diff options
Diffstat (limited to 'tests/auto/concurrent/qtconcurrentfiltermapgenerated')
14 files changed, 1697 insertions, 0 deletions
diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/.gitignore b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/.gitignore new file mode 100644 index 0000000000..6e6bf470c9 --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/.gitignore @@ -0,0 +1 @@ +tst_qtconcurrentfiltermapgenerated diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/CMakeLists.txt b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/CMakeLists.txt new file mode 100644 index 0000000000..12545702eb --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qtconcurrentfiltermapgenerated Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtconcurrentfiltermapgenerated LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qtconcurrentfiltermapgenerated + SOURCES + tst_qtconcurrent_selected_tests.cpp + tst_qtconcurrentfiltermapgenerated.cpp tst_qtconcurrentfiltermapgenerated.h + LIBRARIES + Qt::Concurrent +) diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/README.txt b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/README.txt new file mode 100644 index 0000000000..63766a2a56 --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/README.txt @@ -0,0 +1,13 @@ +This directory contains a generator for unit tests for QtConcurrent. + +The subdirectory 'generator' contains the generator. Run the file +"generate_gui.py" therein. +Python3.8 and PySide2 are required. + +The generator writes on each click a testcase into the file +tst_qtconcurrentfiltermapgenerated.cpp +and +tst_qtconcurrentfiltermapgenerated.h. + +Testcases which should be preserved can be copy-pasted into +tst_qtconcurrent_selected_tests.cpp. diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generation_helpers.h b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generation_helpers.h new file mode 100644 index 0000000000..aaa0d85002 --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generation_helpers.h @@ -0,0 +1,265 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef QTBASE_GENERATION_HELPERS_H +#define QTBASE_GENERATION_HELPERS_H + +#include "qglobal.h" + +#include <vector> + +struct tag_input +{ +}; +struct tag_mapped +{ +}; +struct tag_reduction +{ +}; + +template<typename tag> +struct SequenceItem +{ + SequenceItem() = default; + // bool as a stronger "explicit": should never be called inside of QtConcurrent + SequenceItem(int val, bool) : value(val) { } + + bool operator==(const SequenceItem<tag> &other) const { return value == other.value; } + bool isOdd() const { return value & 1; } + void multiplyByTwo() { value *= 2; } + + int value = 0; +}; + +template<typename tag> +struct NoConstructSequenceItem +{ + NoConstructSequenceItem() = delete; + // bool as a stronger "explicit": should never be called inside of QtConcurrent + NoConstructSequenceItem(int val, bool) : value(val) { } + + bool operator==(const NoConstructSequenceItem<tag> &other) const + { + return value == other.value; + } + bool isOdd() const { return value & 1; } + void multiplyByTwo() { value *= 2; } + + int value = 0; +}; + +template<typename tag> +struct MoveOnlySequenceItem +{ + MoveOnlySequenceItem() = default; + ~MoveOnlySequenceItem() = default; + MoveOnlySequenceItem(const MoveOnlySequenceItem &) = delete; + MoveOnlySequenceItem(MoveOnlySequenceItem &&other) : value(other.value) { other.value = -1; } + MoveOnlySequenceItem &operator=(const MoveOnlySequenceItem &) = delete; + MoveOnlySequenceItem &operator=(MoveOnlySequenceItem &&other) + { + value = other.value; + other.value = -1; + } + + // bool as a stronger "explicit": should never be called inside of QtConcurrent + MoveOnlySequenceItem(int val, bool) : value(val) { } + + bool operator==(const MoveOnlySequenceItem<tag> &other) const { return value == other.value; } + bool isOdd() const { return value & 1; } + void multiplyByTwo() { value *= 2; } + + int value = 0; +}; + +template<typename tag> +struct MoveOnlyNoConstructSequenceItem +{ + MoveOnlyNoConstructSequenceItem() = delete; + ~MoveOnlyNoConstructSequenceItem() = default; + MoveOnlyNoConstructSequenceItem(const MoveOnlyNoConstructSequenceItem &) = delete; + MoveOnlyNoConstructSequenceItem(MoveOnlyNoConstructSequenceItem &&other) : value(other.value) + { + other.value = -1; + } + MoveOnlyNoConstructSequenceItem &operator=(const MoveOnlyNoConstructSequenceItem &) = delete; + MoveOnlyNoConstructSequenceItem &operator=(MoveOnlyNoConstructSequenceItem &&other) + { + value = other.value; + other.value = -1; + } + + // bool as a stronger "explicit": should never be called inside of QtConcurrent + MoveOnlyNoConstructSequenceItem(int val, bool) : value(val) { } + + bool operator==(const MoveOnlyNoConstructSequenceItem<tag> &other) const + { + return value == other.value; + } + bool isOdd() const { return value & 1; } + void multiplyByTwo() { value *= 2; } + + int value = 0; +}; + +template<typename T> +bool myfilter(const T &el) +{ + return el.isOdd(); +} + +template<typename T> +class MyFilter +{ +public: + bool operator()(const T &el) { return el.isOdd(); } +}; + +template<typename T> +class MyMoveOnlyFilter +{ + bool movedFrom = false; + +public: + MyMoveOnlyFilter() = default; + MyMoveOnlyFilter(const MyMoveOnlyFilter<T> &) = delete; + MyMoveOnlyFilter &operator=(const MyMoveOnlyFilter<T> &) = delete; + + MyMoveOnlyFilter(MyMoveOnlyFilter<T> &&other) { other.movedFrom = true; } + MyMoveOnlyFilter &operator=(MyMoveOnlyFilter<T> &&other) { other.movedFrom = true; } + + bool operator()(const T &el) + { + return movedFrom || el.isOdd(); + } +}; + +template<typename From, typename To> +To myMap(const From &f) +{ + return To(f.value * 2, true); +} + +template<typename T> +void myInplaceMap(T &el) +{ + el.multiplyByTwo(); +} + +template<typename From, typename To> +class MyMap +{ +public: + To operator()(const From &el) { return To(el.value * 2, true); } +}; + +template<typename T> +class MyInplaceMap +{ +public: + void operator()(T &el) { el.multiplyByTwo(); } +}; + +template<typename From, typename To> +class MyMoveOnlyMap +{ + bool movedFrom = false; + +public: + MyMoveOnlyMap() = default; + MyMoveOnlyMap(const MyMoveOnlyMap<From, To> &) = delete; + MyMoveOnlyMap &operator=(const MyMoveOnlyMap<From, To> &) = delete; + + MyMoveOnlyMap(MyMoveOnlyMap<From, To> &&other) { other.movedFrom = true; } + MyMoveOnlyMap &operator=(MyMoveOnlyMap<From, To> &&other) { other.movedFrom = true; } + + To operator()(const From &el) + { + if (!movedFrom) + return To(el.value * 2, true); + else + return To(-1, true); + } +}; + +template<typename T> +class MyMoveOnlyInplaceMap +{ + bool movedFrom = false; + +public: + MyMoveOnlyInplaceMap() = default; + MyMoveOnlyInplaceMap(const MyMoveOnlyInplaceMap<T> &) = delete; + MyMoveOnlyInplaceMap &operator=(const MyMoveOnlyInplaceMap<T> &) = delete; + + MyMoveOnlyInplaceMap(MyMoveOnlyInplaceMap<T> &&other) { other.movedFrom = true; } + MyMoveOnlyInplaceMap &operator=(MyMoveOnlyInplaceMap<T> &&other) { other.movedFrom = true; } + + void operator()(T &el) + { + if (!movedFrom) + el.multiplyByTwo(); + } +}; + +template<typename From, typename To> +void myReduce(To &sum, const From &val) +{ + sum.value += val.value; +} + +template<typename From, typename To> +class MyReduce +{ +public: + void operator()(To &sum, const From &val) { sum.value += val.value; } +}; + +template<typename From, typename To> +class MyMoveOnlyReduce +{ + bool movedFrom = false; + +public: + MyMoveOnlyReduce() = default; + MyMoveOnlyReduce(const MyMoveOnlyReduce<From, To> &) = delete; + MyMoveOnlyReduce &operator=(const MyMoveOnlyReduce<From, To> &) = delete; + + MyMoveOnlyReduce(MyMoveOnlyReduce<From, To> &&other) { other.movedFrom = true; } + MyMoveOnlyReduce &operator=(MyMoveOnlyReduce<From, To> &&other) { other.movedFrom = true; } + + void operator()(To &sum, const From &val) + { + if (!movedFrom) + sum.value += val.value; + } +}; + +QT_BEGIN_NAMESPACE + +// pretty printing +template<typename tag> +char *toString(const SequenceItem<tag> &i) +{ + using QTest::toString; + return toString(QString::number(i.value)); +} + +// pretty printing +template<typename T> +char *toString(const std::vector<T> &vec) +{ + using QTest::toString; + QString result(""); + for (const auto &i : vec) { + result.append(QString::number(i.value) + ", "); + } + if (result.size()) + result.chop(2); + return toString(result); +} + +QT_END_NAMESPACE + +#endif // QTBASE_GENERATION_HELPERS_H diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/function_signature.py b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/function_signature.py new file mode 100644 index 0000000000..d87c8a2e9b --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/function_signature.py @@ -0,0 +1,138 @@ +# Copyright (C) 2020 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + + +from option_management import need_separate_output_sequence + + +# ["hallo", "welt"] -> "halloWelt" +def build_camel_case(*args): + assert all(isinstance(x, str) for x in args) + if len(args) == 0: + return "" + if len(args) == 1: + return args[0] + return args[0] + "".join(x.capitalize() for x in args[1:]) + + +def build_function_name(options): + result = [] + for part in ["blocking", "filter", "map", "reduce"]: + if options[part]: + result.append(part) + + def make_potentially_passive(verb): + if verb == "map": + return "mapped" + if verb == "blocking": + return "blocking" + + if verb[-1] == "e": + verb += "d" + else: + verb += "ed" + + return verb + + if not options["inplace"]: + result = [make_potentially_passive(x) for x in result] + + result = build_camel_case(*result) + return result + + +def build_blocking_return_type(options): + if options["inplace"]: + if options["filter"] and options["iterators"] and not options["reduce"]: + return "Iterator" # have to mark new end + else: + return "void" + else: + if options["reduce"]: + return "ResultType" + + if need_separate_output_sequence(options): + return "OutputSequence" + else: + return "Sequence" + + +def build_return_type(options): + if options["blocking"]: + return build_blocking_return_type(options) + else: + return f"QFuture<{build_blocking_return_type(options)}>" + + +def build_template_argument_list(options): + required_types = [] + if options["reduce"]: + required_types.append("typename ResultType") + + need_output_sequence = need_separate_output_sequence(options) + if need_output_sequence: + required_types.append("OutputSequence") + + if options["iterators"]: + required_types.append("Iterator") + else: + if need_output_sequence: + required_types.append("InputSequence") + else: + required_types.append("Sequence") + + # Functors + if options["filter"]: + required_types.append("KeepFunctor") + + if options["map"]: + required_types.append("MapFunctor") + + if options["reduce"]: + required_types.append("ReduceFunctor") + + if options["initialvalue"]: + required_types.append("reductionitemtype") + + return "template<" + ", ".join(["typename "+x for x in required_types]) + ">" + + +def build_argument_list(options): + required_arguments = [] + if options["pool"]: + required_arguments.append("QThreadPool* pool") + + if options["iterators"]: + required_arguments.append("Iterator begin") + required_arguments.append("Iterator end") + else: + if options["inplace"]: + required_arguments.append("Sequence & sequence") + else: + if need_separate_output_sequence(options): + required_arguments.append("InputSequence && sequence") + else: + required_arguments.append("const Sequence & sequence") + + if options["filter"]: + required_arguments.append("KeepFunctor filterFunction") + + if options["map"]: + required_arguments.append("MapFunctor function") + + if options["reduce"]: + required_arguments.append("ReduceFunctor reduceFunction") + if options["initialvalue"]: + required_arguments.append("reductionitemtype && initialValue") + + required_arguments.append("ReduceOptions") + + return "(" + ", ".join(required_arguments) + ")" + + +def build_function_signature(options): + return (build_template_argument_list(options) + "\n" + + build_return_type( + options) + " " + build_function_name(options) + build_argument_list(options) + + ";" + ) diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generate_excel.py b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generate_excel.py new file mode 100644 index 0000000000..d05e31fc21 --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generate_excel.py @@ -0,0 +1,46 @@ +# Copyright (C) 2020 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import pandas as pd +from option_management import function_describing_options +from function_signature import build_function_signature + + +def generate_excel_file_of_functions(filename): + olist = [] + for options in function_describing_options(): + # filter out unneccesary cases: + if options["reduce"] and options["inplace"]: + # we cannot do a reduction in-place + options["comment"] = "reduce-inplace:nonsense" + options["signature"] = "" + + elif options["initialvalue"] and not options["reduce"]: + options["comment"] = "initial-noreduce:nonsense" + options["signature"] = "" + + elif not options["reduce"] and not options["map"] and not options["filter"]: + # no operation at all + options["comment"] = "noop" + options["signature"] = "" + + else: + options["comment"] = "" + if options["map"] and options["filter"]: + options["implemented"] = "no:filter+map" + elif not options["map"] and not options["filter"]: + options["implemented"] = "no:nofilter+nomap" + elif options["inplace"] and options["iterators"] and options["filter"]: + options["implemented"] = "no:inplace+iterator+filter" + else: + options["implemented"] = "yes" + + options["signature"] = build_function_signature(options) + + olist.append(options) + + df = pd.DataFrame(olist) + df.to_excel(filename) + + +generate_excel_file_of_functions("functions.xls") diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generate_gui.py b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generate_gui.py new file mode 100644 index 0000000000..54c1285e74 --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generate_gui.py @@ -0,0 +1,156 @@ +# Copyright (C) 2020 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import importlib +import sys +import PySide2 +from PySide2.QtCore import Signal +from PySide2.QtWidgets import QVBoxLayout, QRadioButton, QGroupBox, QWidget, QApplication, QPlainTextEdit, QHBoxLayout + +import generate_testcase +from helpers import insert_testcases_into_file +from option_management import (Option, OptionManager, testcase_describing_options, function_describing_options, + skip_function_description, disabled_testcase_describing_options, + skip_testcase_description) + + +class MyRadioButton(QRadioButton): + def __init__(self, value): + super(MyRadioButton, self).__init__(text=str(value)) + self.value = value + + self.toggled.connect(lambda x: x and self.activated.emit(self.value)) + + activated = Signal(object) + + +class OptionSelector(QGroupBox): + def __init__(self, parent: QWidget, option: Option): + super(OptionSelector, self).__init__(title=option.name, parent=parent) + self.layout = QVBoxLayout() + self.setLayout(self.layout) + + self.radiobuttons = [] + for val in option.possible_options: + rb = MyRadioButton(val) + self.layout.addWidget(rb) + rb.activated.connect(lambda value: self.valueSelected.emit(option.name, value)) + self.radiobuttons.append(rb) + + self.radiobuttons[0].setChecked(True) + + valueSelected = Signal(str, object) + + +class OptionsSelector(QGroupBox): + def __init__(self, parent: QWidget, option_manager: OptionManager): + super(OptionsSelector, self).__init__(title=option_manager.name, parent=parent) + self.vlayout = QVBoxLayout() + self.setLayout(self.vlayout) + self.layout1 = QHBoxLayout() + self.layout2 = QHBoxLayout() + self.layout3 = QHBoxLayout() + self.vlayout.addLayout(self.layout1) + self.vlayout.addLayout(self.layout2) + self.vlayout.addLayout(self.layout3) + self.disabledOptions = [] + + self.selectors = {} + for option in option_manager.options.values(): + os = OptionSelector(parent=self, option=option) + if "type" in option.name: + self.layout2.addWidget(os) + elif "passing" in option.name: + self.layout3.addWidget(os) + else: + self.layout1.addWidget(os) + os.valueSelected.connect(self._handle_slection) + self.selectors[option.name] = os + + self.selectedOptionsDict = {option.name: option.possible_options[0] for option in + option_manager.options.values()} + + def get_current_option_set(self): + return {k: v for k, v in self.selectedOptionsDict.items() if k not in self.disabledOptions} + + def _handle_slection(self, name: str, value: object): + self.selectedOptionsDict[name] = value + self.optionsSelected.emit(self.get_current_option_set()) + + def set_disabled_options(self, options): + self.disabledOptions = options + for name, selector in self.selectors.items(): + if name in self.disabledOptions: + selector.setEnabled(False) + else: + selector.setEnabled(True) + + optionsSelected = Signal(dict) + + +class MainWindow(QWidget): + def __init__(self): + super(MainWindow, self).__init__() + self.layout = QVBoxLayout() + self.setLayout(self.layout) + + self.functionSelector = OptionsSelector(parent=self, option_manager=function_describing_options()) + self.layout.addWidget(self.functionSelector) + self.testcaseSelector = OptionsSelector(parent=self, option_manager=testcase_describing_options()) + self.layout.addWidget(self.testcaseSelector) + + self.plainTextEdit = QPlainTextEdit() + self.plainTextEdit.setReadOnly(True) + self.layout.addWidget(self.plainTextEdit) + self.plainTextEdit.setFont(PySide2.QtGui.QFont("Fira Code", 8)) + + # temp + self.functionSelector.optionsSelected.connect(lambda o: self._handle_function_change()) + self.testcaseSelector.optionsSelected.connect(lambda o: self._handle_testcase_change()) + + self._handle_function_change() + + def _handle_function_change(self): + options = self.functionSelector.get_current_option_set() + if m := skip_function_description(options): + self.plainTextEdit.setPlainText(m) + return + + options_to_disable = disabled_testcase_describing_options(options) + self.testcaseSelector.set_disabled_options(options_to_disable) + + options.update(self.testcaseSelector.get_current_option_set()) + if m := skip_testcase_description(options): + self.plainTextEdit.setPlainText(m) + return + + self._generate_new_testcase() + + def _handle_testcase_change(self): + options = self.functionSelector.get_current_option_set() + options.update(self.testcaseSelector.get_current_option_set()) + if m := skip_testcase_description(options): + self.plainTextEdit.setPlainText(m) + return + + self._generate_new_testcase() + + def _generate_new_testcase(self): + foptions = self.functionSelector.get_current_option_set() + toptions = self.testcaseSelector.get_current_option_set() + importlib.reload(generate_testcase) + testcase = generate_testcase.generate_testcase(foptions, toptions) + self.plainTextEdit.setPlainText(testcase[1]) + filename = "../tst_qtconcurrentfiltermapgenerated.cpp" + insert_testcases_into_file(filename, [testcase]) + filename = "../tst_qtconcurrentfiltermapgenerated.h" + insert_testcases_into_file(filename, [testcase]) + + +if __name__ == "__main__": + app = QApplication(sys.argv) + + m = MainWindow() + m.show() + + app.exec_() diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generate_testcase.py b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generate_testcase.py new file mode 100644 index 0000000000..d35a7e9065 --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generate_testcase.py @@ -0,0 +1,366 @@ +# Copyright (C) 2020 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +import textwrap +import time +from subprocess import Popen, PIPE + +from function_signature import build_function_signature, build_function_name +from option_management import need_separate_output_sequence, qt_quirk_case + + +def InputSequenceItem(toptions): + if toptions["inputitemtype"] == "standard": + return "SequenceItem<tag_input>" + if toptions["inputitemtype"] == "noconstruct": + return "NoConstructSequenceItem<tag_input>" + if toptions["inputitemtype"] == "moveonly": + return "MoveOnlySequenceItem<tag_input>" + if toptions["inputitemtype"] == "moveonlynoconstruct": + return "MoveOnlyNoConstructSequenceItem<tag_input>" + assert False + + +def InputSequence(toptions): + item = InputSequenceItem(toptions) + if toptions["inputsequence"] == "standard": + return "std::vector<{}>".format(item) + if toptions["inputsequence"] == "moveonly": + return "MoveOnlyVector<{}>".format(item) + assert False + + +def InputSequenceInitializationString(toptions): + t = InputSequenceItem(toptions) + # construct IILE + mystring = ("[](){" + InputSequence(toptions) + " result;\n" + + "\n".join("result.push_back({}({}, true));".format(t, i) for i in range(1, 7)) + + "\n return result; }()") + return mystring + + +def OutputSequenceItem(toptions): + if toptions["map"] and (toptions["inplace"] or toptions["maptype"] == "same"): + return InputSequenceItem(toptions) + + if toptions["map"]: + if toptions["mappeditemtype"] == "standard": + return "SequenceItem<tag_mapped>" + if toptions["mappeditemtype"] == "noconstruct": + return "NoConstructSequenceItem<tag_mapped>" + if toptions["mappeditemtype"] == "moveonly": + return "MoveOnlySequenceItem<tag_mapped>" + if toptions["mappeditemtype"] == "moveonlynoconstruct": + return "MoveOnlyNoConstructSequenceItem<tag_mapped>" + assert(False) + else: + return InputSequenceItem(toptions) + + +def ReducedItem(toptions): + if toptions["reductiontype"] == "same": + return OutputSequenceItem(toptions) + else: + if toptions["reductionitemtype"] == "standard": + return "SequenceItem<tag_reduction>" + if toptions["reductionitemtype"] == "noconstruct": + return "NoConstructSequenceItem<tag_reduction>" + if toptions["reductionitemtype"] == "moveonly": + return "MoveOnlySequenceItem<tag_reduction>" + if toptions["reductionitemtype"] == "moveonlynoconstruct": + return "MoveOnlyNoConstructSequenceItem<tag_reduction>" + assert(False) + + +def OutputSequence(toptions): + item = OutputSequenceItem(toptions) + # quirk of qt: not a QFuture<Sequence> is returned + if qt_quirk_case(toptions): + return "QList<{}>".format(item) + + needs_extra = need_separate_output_sequence(toptions) + if not needs_extra: + return InputSequence(toptions) + + + if toptions["outputsequence"] == "standard": + return "std::vector<{}>".format(item) + if toptions["outputsequence"] == "moveonly": + return "MoveOnlyVector<{}>".format(item) + assert False + + +def resultData(toptions): + result = [1, 2, 3, 4, 5, 6] + if toptions["filter"]: + result = filter(lambda x: x % 2 == 1, result) + if toptions["map"]: + result = map(lambda x: 2 * x, result) + if toptions["reduce"]: + result = sum(result) + return result + + +def OutputSequenceInitializationString(toptions): + t = OutputSequenceItem(toptions) + # construct IILE + mystring = ("[](){" + OutputSequence(toptions) + " result;\n" + + "\n".join("result.push_back({}({}, true));".format(t, i) for i in resultData(toptions)) + + "\n return result; }()") + return mystring + + +def OutputScalarInitializationString(toptions): + result = resultData(toptions) + assert isinstance(result, int) + return ReducedItem(toptions) + "(" + str(result) + ", true)" + + +def FilterInitializationString(toptions): + item = InputSequenceItem(toptions) + if toptions["filterfunction"] == "function": + return "myfilter<{}>".format(item) + if toptions["filterfunction"] == "functor": + return "MyFilter<{}>{{}}".format(item) + if toptions["filterfunction"] == "memberfunction": + return "&{}::isOdd".format(item) + if toptions["filterfunction"] == "lambda": + return "[](const {}& x){{ return myfilter<{}>(x); }}".format(item, item) + if toptions["filterfunction"] == "moveonlyfunctor": + return "MyMoveOnlyFilter<{}>{{}}".format(item) + assert False + + +def MapInitializationString(toptions): + oldtype = InputSequenceItem(toptions) + newtype = OutputSequenceItem(toptions) + if toptions["inplace"]: + assert oldtype == newtype + if toptions["mapfunction"] == "function": + return "myInplaceMap<{}>".format(oldtype) + if toptions["mapfunction"] == "functor": + return "MyInplaceMap<{}>{{}}".format(oldtype) + if toptions["mapfunction"] == "memberfunction": + return "&{}::multiplyByTwo".format(oldtype) + if toptions["mapfunction"] == "lambda": + return "[]({}& x){{ return myInplaceMap<{}>(x); }}".format(oldtype, oldtype) + if toptions["mapfunction"] == "moveonlyfunctor": + return "MyMoveOnlyInplaceMap<{}>{{}}".format(oldtype) + assert False + else: + if toptions["mapfunction"] == "function": + return "myMap<{f},{t}>".format(f=oldtype, t=newtype) + if toptions["mapfunction"] == "functor": + return "MyMap<{f},{t}>{{}}".format(f=oldtype, t=newtype) + if toptions["mapfunction"] == "memberfunction": + return "&{}::multiplyByTwo".format(newtype) + if toptions["mapfunction"] == "lambda": + return "[](const {f}& x){{ return myMap<{f},{t}>(x); }}".format(f=oldtype, t=newtype) + if toptions["mapfunction"] == "moveonlyfunctor": + return "MyMoveOnlyMap<{f},{t}>{{}}".format(f=oldtype, t=newtype) + assert False + + +def ReductionInitializationString(toptions): + elementtype = OutputSequenceItem(toptions) + sumtype = ReducedItem(toptions) + + if toptions["reductionfunction"] == "function": + return "myReduce<{f},{t}>".format(f=elementtype, t=sumtype) + if toptions["reductionfunction"] == "functor": + return "MyReduce<{f},{t}>{{}}".format(f=elementtype, t=sumtype) + if toptions["reductionfunction"] == "lambda": + return "[]({t}& sum, const {f}& x){{ return myReduce<{f},{t}>(sum, x); }}".format(f=elementtype, t=sumtype) + if toptions["reductionfunction"] == "moveonlyfunctor": + return "MyMoveOnlyReduce<{f},{t}>{{}}".format(f=elementtype, t=sumtype) + assert False + + +def ReductionInitialvalueInitializationString(options): + return ReducedItem(options) + "(0, true)" + + +def function_template_args(options): + args = [] + out_s = OutputSequence(options) + in_s = InputSequence(options) + if options["reduce"] and options["reductionfunction"] == "lambda": + args.append(ReducedItem(options)) + elif out_s != in_s: + if not qt_quirk_case(options): + args.append(out_s) + + if len(args): + return "<" + ", ".join(args) + ">" + + return "" + + +numcall = 0 + + +def generate_testcase(function_options, testcase_options): + options = {**function_options, **testcase_options} + + option_description = "\n".join(" {}={}".format( + a, b) for a, b in testcase_options.items()) + option_description = textwrap.indent(option_description, " "*12) + function_signature = textwrap.indent(build_function_signature(function_options), " "*12) + testcase_name = "_".join("{}_{}".format(x, y) for x, y in options.items()) + global numcall + numcall += 1 + testcase_name = "test" + str(numcall) + function_name = build_function_name(options) + + arguments = [] + + template_args = function_template_args(options) + + # care about the thread pool + if options["pool"]: + pool_initialization = """QThreadPool pool; + pool.setMaxThreadCount(1);""" + arguments.append("&pool") + else: + pool_initialization = "" + + # care about the input sequence + input_sequence_initialization_string = InputSequenceInitializationString(options) + + if "inputsequencepassing" in options and options["inputsequencepassing"] == "lvalue" or options["inplace"]: + input_sequence_initialization = "auto input_sequence = " + \ + input_sequence_initialization_string + ";" + arguments.append("input_sequence") + elif "inputsequencepassing" in options and options["inputsequencepassing"] == "rvalue": + input_sequence_initialization = "" + arguments.append(input_sequence_initialization_string) + else: + input_sequence_initialization = "auto input_sequence = " + \ + input_sequence_initialization_string + ";" + arguments.append("input_sequence.begin()") + arguments.append("input_sequence.end()") + + # care about the map: + if options["map"]: + map_initialization_string = MapInitializationString(options) + if options["mapfunctionpassing"] == "lvalue": + map_initialization = "auto map = " + map_initialization_string + ";" + arguments.append("map") + elif options["mapfunctionpassing"] == "rvalue": + map_initialization = "" + arguments.append(map_initialization_string) + else: + assert False + else: + map_initialization = "" + + # care about the filter + if options["filter"]: + filter_initialization_string = FilterInitializationString(options) + if options["filterfunctionpassing"] == "lvalue": + filter_initialization = "auto filter = " + filter_initialization_string + ";" + arguments.append("filter") + elif options["filterfunctionpassing"] == "rvalue": + filter_initialization = "" + arguments.append(filter_initialization_string) + else: + assert (False) + else: + filter_initialization = "" + + reduction_initialvalue_initialization = "" + # care about reduction + if options["reduce"]: + reduction_initialization_expression = ReductionInitializationString(options) + if options["reductionfunctionpassing"] == "lvalue": + reduction_initialization = "auto reductor = " + reduction_initialization_expression + ";" + arguments.append("reductor") + elif options["reductionfunctionpassing"] == "rvalue": + reduction_initialization = "" + arguments.append(reduction_initialization_expression) + else: + assert (False) + + # initialvalue: + if options["initialvalue"]: + reduction_initialvalue_initialization_expression = ReductionInitialvalueInitializationString(options) + if options["reductioninitialvaluepassing"] == "lvalue": + reduction_initialvalue_initialization = "auto initialvalue = " + reduction_initialvalue_initialization_expression + ";" + arguments.append("initialvalue") + elif options["reductioninitialvaluepassing"] == "rvalue": + reduction_initialvalue_initialization = "" + arguments.append(reduction_initialvalue_initialization_expression) + else: + assert (False) + + if options["reductionoptions"] == "UnorderedReduce": + arguments.append("QtConcurrent::UnorderedReduce") + elif options["reductionoptions"] == "OrderedReduce": + arguments.append("QtConcurrent::OrderedReduce") + elif options["reductionoptions"] == "SequentialReduce": + arguments.append("QtConcurrent::SequentialReduce") + else: + assert options["reductionoptions"] == "unspecified" + else: + reduction_initialization = "" + + # what is the expected result + if options["filter"]: + if not options["reduce"]: + expected_result_expression = OutputSequenceInitializationString(options) + else: + expected_result_expression = OutputScalarInitializationString(options) + elif options["map"]: + if not options["reduce"]: + expected_result_expression = OutputSequenceInitializationString(options) + else: + expected_result_expression = OutputScalarInitializationString(options) + + wait_result_expression = "" + if options["inplace"]: + if options["blocking"]: + result_accepting = "" + result_variable = "input_sequence" + else: + result_accepting = "auto future = " + result_variable = "input_sequence" + wait_result_expression = "future.waitForFinished();" + elif options["blocking"]: + result_accepting = "auto result = " + result_variable = "result" + else: + if not options["reduce"]: + result_accepting = "auto result = " + result_variable = "result.results()" + else: + result_accepting = "auto result = " + result_variable = "result.takeResult()" + + arguments_passing = ", ".join(arguments) + final_string = f""" + void tst_QtConcurrentFilterMapGenerated::{testcase_name}() + {{ + /* test for +{function_signature} + + with +{option_description} + */ + + {pool_initialization} + {input_sequence_initialization} + {filter_initialization} + {map_initialization} + {reduction_initialization} + {reduction_initialvalue_initialization} + + {result_accepting}QtConcurrent::{function_name}{template_args}({arguments_passing}); + + auto expected_result = {expected_result_expression}; + {wait_result_expression} + QCOMPARE({result_variable}, expected_result); + }} + """ + p = Popen(["clang-format"], stdin=PIPE, stdout=PIPE, stderr=PIPE) + final_string = p.communicate(final_string.encode())[0].decode() + + return (f" void {testcase_name}();\n", final_string) diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generator_main.py b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generator_main.py new file mode 100644 index 0000000000..87cb4c7bc4 --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generator_main.py @@ -0,0 +1,33 @@ +# Copyright (C) 2020 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +from option_management import function_describing_options, skip_function_description, testcase_describing_options +from generate_testcase import generate_testcase +from helpers import insert_testcases_into_file +filename = "../tst_qtconcurrentfiltermapgenerated.cpp" + +testcases = [] +counter = 0 +for fo in function_describing_options(): + if skip_function_description(fo): + continue + + if not ( + fo["blocking"] + and fo["filter"] + # and not fo["map"] + and fo["reduce"] + and not fo["inplace"] + and not fo["iterators"] + and not fo["initialvalue"] + and not fo["pool"] + ): + continue + + for to in testcase_describing_options(fo): + print("generate test") + testcases.append(generate_testcase(fo, to)) + counter += 1 + +print(counter) +insert_testcases_into_file(filename, testcases) diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/helpers.py b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/helpers.py new file mode 100644 index 0000000000..fbe969789c --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/helpers.py @@ -0,0 +1,31 @@ +# Copyright (C) 2020 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + + +def insert_testcases_into_file(filename, testcases): + # assume testcases is an array of tuples of (declaration, definition) + with open(filename) as f: + inputlines = f.readlines() + outputlines = [] + skipping = False + for line in inputlines: + if not skipping: + outputlines.append(line) + else: + if "END_GENERATED" in line: + outputlines.append(line) + skipping = False + + if "START_GENERATED_SLOTS" in line: + # put in testcases + outputlines += [t[0] for t in testcases] + skipping = True + + if "START_GENERATED_IMPLEMENTATIONS" in line: + # put in testcases + outputlines += [t[1] for t in testcases] + skipping = True + + if outputlines != inputlines: + with open(filename, "w") as f: + f.writelines(outputlines) diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/option_management.py b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/option_management.py new file mode 100644 index 0000000000..6a1fc87f9f --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/option_management.py @@ -0,0 +1,195 @@ +# Copyright (C) 2020 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import itertools + + +class Option: + def __init__(self, name, possible_options): + self.name = name + self.possible_options = possible_options + + +class OptionManager: + def __init__(self, name): + self.options = {} + self.name = name + + def add_option(self, option: Option): + self.options[option.name] = option + + def iterate(self): + for x in itertools.product(*[ + [(name, x) for x in self.options[name]] + for name in self.options.keys() + ]): + yield dict(x) + + +def function_describing_options(): + om = OptionManager("function options") + + om.add_option(Option("blocking", [True, False])) + om.add_option(Option("filter", [True, False])) + om.add_option(Option("map", [False, True])) + om.add_option(Option("reduce", [False, True])) + om.add_option(Option("inplace", [True, False])) + om.add_option(Option("iterators", [False, True])) + om.add_option(Option("initialvalue", [False, True])) + om.add_option(Option("pool", [True, False])) + + return om + + +def skip_function_description(options): + if options["reduce"] and options["inplace"]: + return "we cannot do a reduction in-place" + + if options["initialvalue"] and not options["reduce"]: + return "without reduction, we do not need an initial value" + + if not options["reduce"] and not options["map"] and not options["filter"]: + return "no operation at all" + + # the following things are skipped because Qt does not support them + if options["filter"] and options["map"]: + return "Not supported by Qt: both map and filter operation" + + if not options["filter"] and not options["map"]: + return "Not supported by Qt: no map and no filter operation" + + if options["inplace"] and options["iterators"] and options["filter"]: + return "Not supported by Qt: filter operation in-place with iterators" + + return False + + +def qt_quirk_case(options): + # whenever a function should return a QFuture<Sequence>, + # it returns a QFuture<item> instead + if options["inplace"] or options["reduce"] or options["blocking"]: + return False + + return True + + +def need_separate_output_sequence(options): + # do we need an output sequence? + if not (options["inplace"] or options["reduce"]): + # transforming a sequence into a sequence + if options["iterators"] or options["map"]: + return True + + return False + + +def testcase_describing_options(): + om = OptionManager("testcase options") + + om.add_option(Option("inputsequence", ["standard", "moveonly"])) + om.add_option(Option("inputsequencepassing", ["lvalue", "rvalue"])) + om.add_option(Option("inputitemtype", ["standard", "noconstruct", "moveonly", "moveonlynoconstruct"])) + + om.add_option(Option("outputsequence", ["standard", "moveonly"])) + + om.add_option(Option("maptype", ["same", "different"])) + om.add_option(Option("mappeditemtype", ["standard", "noconstruct", "moveonly", "moveonlynoconstruct"])) + + om.add_option(Option("reductiontype", ["same", "different"])) + + om.add_option(Option("reductionitemtype", [ + "standard", "noconstruct", "moveonly", "moveonlynoconstruct"])) + + om.add_option(Option("filterfunction", ["functor", "function", "memberfunction", "lambda", "moveonlyfunctor"])) + om.add_option(Option("filterfunctionpassing", ["lvalue", "rvalue"])) + + om.add_option(Option("mapfunction", ["functor", "function", "memberfunction", "lambda", "moveonlyfunctor"])) + om.add_option(Option("mapfunctionpassing", ["lvalue", "rvalue"])) + + om.add_option(Option("reductionfunction", ["functor", "function", "lambda", "moveonlyfunctor"])) + om.add_option(Option("reductionfunctionpassing", ["lvalue", "rvalue"])) + + om.add_option(Option("reductioninitialvaluepassing", ["lvalue", "rvalue"])) + + om.add_option(Option("reductionoptions", [ + "unspecified", "UnorderedReduce", "OrderedReduce", "SequentialReduce"])) + + return om + + +def disabled_testcase_describing_options(options): + disabled_options = [] + + if options["inplace"] or options["iterators"]: + disabled_options.append("inputsequencepassing") + + if not need_separate_output_sequence(options): + disabled_options.append("outputsequence") + + if not options["map"]: + disabled_options.append("mappeditemtype") + + if options["map"] and options["inplace"]: + disabled_options.append("mappeditemtype") + + if not options["filter"]: + disabled_options.append("filterfunction") + disabled_options.append("filterfunctionpassing") + + if not options["map"]: + disabled_options.append("mapfunction") + disabled_options.append("mapfunctionpassing") + + if not options["reduce"]: + disabled_options.append("reductionfunction") + disabled_options.append("reductionfunctionpassing") + + if not options["reduce"]: + disabled_options.append("reductiontype") + disabled_options.append("reductioninitialvaluepassing") + disabled_options.append("reductionoptions") + disabled_options.append("reductionitemtype") + + if not options["initialvalue"]: + disabled_options.append("reductioninitialvaluepassing") + + if not options["map"]: + disabled_options.append("maptype") + else: + if options["inplace"]: + disabled_options.append("maptype") + + return disabled_options + + +def skip_testcase_description(options): + if ( + "maptype" in options and + options["maptype"] == "same" and + "inputitemtype" in options and "mappeditemtype" in options and + (options["inputitemtype"] != options["mappeditemtype"]) + ): + return ("Not possible: map should map to same type, " + "but mapped item type should differ from input item type.") + + if ( + "reductiontype" in options and + options["reductiontype"] == "same"): + # we have to check that this is possible + if ("mappeditemtype" in options and "reductionitemtype" in options + and (options["mappeditemtype"] != options["reductionitemtype"]) + ): + return ("Not possible: should reduce in the same type, " + "but reduction item type should differ from mapped item type.") + if ("mappeditemtype" not in options + and (options["inputitemtype"] != options["reductionitemtype"])): + return ("Not possible: should reduce in the same type, " + "but reduction item type should differ from input item type.") + + if ( + options["map"] and not options["inplace"] + and options["mapfunction"] == "memberfunction" + ): + return "map with memberfunction only available for in-place map" + + return False diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/tst_qtconcurrent_selected_tests.cpp b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/tst_qtconcurrent_selected_tests.cpp new file mode 100644 index 0000000000..edb7cce4c9 --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/tst_qtconcurrent_selected_tests.cpp @@ -0,0 +1,347 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "tst_qtconcurrentfiltermapgenerated.h" + +void tst_QtConcurrentFilterMapGenerated::mapReduceThroughDifferentTypes() +{ + /* test for + template<typename typename ResultType, typename Iterator, typename MapFunctor, typename + ReduceFunctor, typename reductionitemtype> ResultType blockingMappedReduced(Iterator begin, + Iterator end, MapFunctor function, ReduceFunctor reduceFunction, reductionitemtype && + initialValue, ReduceOptions); + + with + inputsequence=standard + inputitemtype=standard + maptype=different + mappeditemtype=standard + reductiontype=different + reductionitemtype=standard + mapfunction=function + mapfunctionpassing=lvalue + reductionfunction=function + reductionfunctionpassing=lvalue + reductioninitialvaluepassing=lvalue + reductionoptions=unspecified + */ + + auto input_sequence = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + return result; + }(); + + auto map = myMap<SequenceItem<tag_input>, SequenceItem<tag_mapped>>; + auto reductor = myReduce<SequenceItem<tag_mapped>, SequenceItem<tag_reduction>>; + auto initialvalue = SequenceItem<tag_reduction>(0, true); + + auto result = QtConcurrent::blockingMappedReduced(input_sequence.begin(), input_sequence.end(), + map, reductor, initialvalue); + + auto expected_result = SequenceItem<tag_reduction>(42, true); + QCOMPARE(result, expected_result); +} + +void tst_QtConcurrentFilterMapGenerated::moveOnlyFilterObject() +{ + /* test for + template<typename Sequence, typename KeepFunctor> + void blockingFilter(QThreadPool* pool, Sequence & sequence, KeepFunctor filterFunction); + + with + inputsequence=standard + inputitemtype=standard + filterfunction=moveonlyfunctor + filterfunctionpassing=rvalue + */ + + QThreadPool pool; + pool.setMaxThreadCount(1); + auto input_sequence = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + return result; + }(); + + QtConcurrent::blockingFilter(&pool, input_sequence, + MyMoveOnlyFilter<SequenceItem<tag_input>> {}); + + auto expected_result = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + return result; + }(); + + QCOMPARE(input_sequence, expected_result); +} + +void tst_QtConcurrentFilterMapGenerated::moveOnlyMapObject() +{ + /* test for + template<typename Sequence, typename MapFunctor> + void blockingMap(QThreadPool* pool, Sequence & sequence, MapFunctor function); + + with + inputsequence=standard + inputitemtype=standard + mapfunction=moveonlyfunctor + mapfunctionpassing=rvalue + */ + + QThreadPool pool; + pool.setMaxThreadCount(1); + auto input_sequence = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + return result; + }(); + + QtConcurrent::blockingMap(&pool, input_sequence, + MyMoveOnlyInplaceMap<SequenceItem<tag_input>> {}); + + auto expected_result = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + result.push_back(SequenceItem<tag_input>(8, true)); + result.push_back(SequenceItem<tag_input>(10, true)); + result.push_back(SequenceItem<tag_input>(12, true)); + return result; + }(); + + QCOMPARE(input_sequence, expected_result); +} + +void tst_QtConcurrentFilterMapGenerated::moveOnlyReduceObject() +{ + /* test for + template<typename typename ResultType, typename Sequence, typename MapFunctor, typename + ReduceFunctor> ResultType blockingMappedReduced(QThreadPool* pool, const Sequence & sequence, + MapFunctor function, ReduceFunctor reduceFunction, ReduceOptions); + + with + inputsequence=standard + inputsequencepassing=lvalue + inputitemtype=standard + maptype=same + mappeditemtype=standard + reductiontype=same + reductionitemtype=standard + mapfunction=functor + mapfunctionpassing=lvalue + reductionfunction=moveonlyfunctor + reductionfunctionpassing=rvalue + reductionoptions=unspecified + */ + + QThreadPool pool; + pool.setMaxThreadCount(1); + auto input_sequence = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + return result; + }(); + + auto map = MyMap<SequenceItem<tag_input>, SequenceItem<tag_input>> {}; + + auto result = QtConcurrent::blockingMappedReduced<SequenceItem<tag_input>>( + &pool, input_sequence, map, + MyMoveOnlyReduce<SequenceItem<tag_input>, SequenceItem<tag_input>> {}); + + auto expected_result = SequenceItem<tag_input>(42, true); + + QCOMPARE(result, expected_result); +} + +void tst_QtConcurrentFilterMapGenerated::functorAsReduction() +{ + /* test for + template<typename typename ResultType, typename Sequence, typename KeepFunctor, typename + ReduceFunctor, typename reductionitemtype> ResultType blockingFilteredReduced(QThreadPool* pool, + const Sequence & sequence, KeepFunctor filterFunction, ReduceFunctor reduceFunction, + reductionitemtype && initialValue, ReduceOptions); + + with + inputsequence=standard + inputsequencepassing=lvalue + inputitemtype=standard + reductiontype=same + reductionitemtype=standard + filterfunction=functor + filterfunctionpassing=lvalue + reductionfunction=functor + reductionfunctionpassing=lvalue + reductioninitialvaluepassing=lvalue + reductionoptions=unspecified + */ + + QThreadPool pool; + pool.setMaxThreadCount(1); + auto input_sequence = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + return result; + }(); + auto filter = MyFilter<SequenceItem<tag_input>> {}; + + auto reductor = MyReduce<SequenceItem<tag_input>, SequenceItem<tag_input>> {}; + auto initialvalue = SequenceItem<tag_input>(0, true); + + auto result = QtConcurrent::blockingFilteredReduced<SequenceItem<tag_input>>( + &pool, input_sequence, filter, reductor, initialvalue); + + auto expected_result = SequenceItem<tag_input>(9, true); + + QCOMPARE(result, expected_result); +} + +void tst_QtConcurrentFilterMapGenerated::moveOnlyReductionItem() +{ + /* test for + template<typename typename ResultType, typename Sequence, typename KeepFunctor, typename + ReduceFunctor, typename reductionitemtype> ResultType blockingFilteredReduced(QThreadPool* pool, + const Sequence & sequence, KeepFunctor filterFunction, ReduceFunctor reduceFunction, + reductionitemtype && initialValue, ReduceOptions); + + with + inputsequence=standard + inputsequencepassing=lvalue + inputitemtype=standard + reductiontype=different + reductionitemtype=moveonly + filterfunction=moveonlyfunctor + filterfunctionpassing=rvalue + reductionfunction=function + reductionfunctionpassing=lvalue + reductioninitialvaluepassing=rvalue + reductionoptions=unspecified + */ +/* TODO: does not work yet + QThreadPool pool; + pool.setMaxThreadCount(1); + auto input_sequence = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + return result; + }(); + + auto reductor = myReduce<SequenceItem<tag_input>, MoveOnlySequenceItem<tag_reduction>>; + + auto result = QtConcurrent::blockingFilteredReduced( + &pool, input_sequence, MyMoveOnlyFilter<SequenceItem<tag_input>> {}, reductor, + MoveOnlySequenceItem<tag_reduction>(0, true)); + + auto expected_result = MoveOnlySequenceItem<tag_reduction>(9, true); + + QCOMPARE(result, expected_result);*/ +} + +void tst_QtConcurrentFilterMapGenerated::noDefaultConstructorItemMapped() +{ + /* test for + template<typename typename ResultType, typename Sequence, typename MapFunctor, typename + ReduceFunctor, typename reductionitemtype> ResultType blockingMappedReduced(QThreadPool* pool, + const Sequence & sequence, MapFunctor function, ReduceFunctor reduceFunction, reductionitemtype + && initialValue, ReduceOptions); + + with + inputsequence=standard + inputsequencepassing=lvalue + inputitemtype=standard + maptype=same + mappeditemtype=standard + reductiontype=different + reductionitemtype=noconstruct + mapfunction=functor + mapfunctionpassing=lvalue + reductionfunction=function + reductionfunctionpassing=lvalue + reductioninitialvaluepassing=lvalue + reductionoptions=unspecified + */ + + QThreadPool pool; + pool.setMaxThreadCount(1); + auto input_sequence = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + return result; + }(); + + auto map = MyMap<SequenceItem<tag_input>, SequenceItem<tag_input>> {}; + auto reductor = myReduce<SequenceItem<tag_input>, NoConstructSequenceItem<tag_reduction>>; + auto initialvalue = NoConstructSequenceItem<tag_reduction>(0, true); + + auto result = + QtConcurrent::blockingMappedReduced(&pool, input_sequence, map, reductor, initialvalue); + + auto expected_result = NoConstructSequenceItem<tag_reduction>(42, true); + + QCOMPARE(result, expected_result); +} + +void tst_QtConcurrentFilterMapGenerated::noDefaultConstructorItemFiltered() +{ + QThreadPool pool; + pool.setMaxThreadCount(1); + auto input_sequence = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + return result; + }(); + + auto filter = MyFilter<SequenceItem<tag_input>> {}; + auto reductor = myReduce<SequenceItem<tag_input>, NoConstructSequenceItem<tag_reduction>>; + auto initialvalue = NoConstructSequenceItem<tag_reduction>(0, true); + + auto result = QtConcurrent::blockingFilteredReduced(&pool, input_sequence, filter, reductor, + initialvalue); + + auto expected_result = NoConstructSequenceItem<tag_reduction>(9, true); + + QCOMPARE(result, expected_result); +} diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/tst_qtconcurrentfiltermapgenerated.cpp b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/tst_qtconcurrentfiltermapgenerated.cpp new file mode 100644 index 0000000000..089ca3f867 --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/tst_qtconcurrentfiltermapgenerated.cpp @@ -0,0 +1,59 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +#include <qtconcurrentfilter.h> +#include <qtconcurrentmap.h> +#include <QCoreApplication> +#include <QList> +#include <QTest> + +#include "../testhelper_functions.h" +#include "generation_helpers.h" + +#include "tst_qtconcurrentfiltermapgenerated.h" + +using namespace QtConcurrent; + +// START_GENERATED_IMPLEMENTATIONS (see generate_tests.py) + +void tst_QtConcurrentFilterMapGenerated::test1() +{ + /* test for + template<typename Sequence, typename KeepFunctor> + void blockingFilter(QThreadPool* pool, Sequence & sequence, KeepFunctor filterFunction); + + with + inputsequence=standard + inputitemtype=standard + filterfunction=functor + filterfunctionpassing=lvalue + */ + + QThreadPool pool; + pool.setMaxThreadCount(1); + auto input_sequence = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + return result; + }(); + auto filter = MyFilter<SequenceItem<tag_input>> {}; + + QtConcurrent::blockingFilter(&pool, input_sequence, filter); + + auto expected_result = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + return result; + }(); + + QCOMPARE(input_sequence, expected_result); +} +// END_GENERATED_IMPLEMENTATION (see generate_tests.py) + +QTEST_MAIN(tst_QtConcurrentFilterMapGenerated) diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/tst_qtconcurrentfiltermapgenerated.h b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/tst_qtconcurrentfiltermapgenerated.h new file mode 100644 index 0000000000..31b62ac4fd --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/tst_qtconcurrentfiltermapgenerated.h @@ -0,0 +1,27 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +#include <qtconcurrentfilter.h> +#include <qtconcurrentmap.h> +#include <QTest> + +#include "generation_helpers.h" + +using namespace QtConcurrent; + +class tst_QtConcurrentFilterMapGenerated : public QObject +{ + Q_OBJECT + +private slots: + void mapReduceThroughDifferentTypes(); + void moveOnlyFilterObject(); + void moveOnlyMapObject(); + void moveOnlyReduceObject(); + void functorAsReduction(); + void moveOnlyReductionItem(); + void noDefaultConstructorItemMapped(); + void noDefaultConstructorItemFiltered(); + // START_GENERATED_SLOTS (see generate_tests.py) + void test1(); + // END_GENERATED_SLOTS (see generate_tests.py) +}; |