qt 6.5.1 original

This commit is contained in:
kleuter
2023-10-29 23:33:08 +01:00
parent 71d22ab6b0
commit 85d238dfda
21202 changed files with 5499099 additions and 0 deletions

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
add_subdirectory(qtconcurrentfilter)
add_subdirectory(qtconcurrentiteratekernel)
add_subdirectory(qtconcurrentfiltermapgenerated)
add_subdirectory(qtconcurrentmap)
add_subdirectory(qtconcurrentmedian)
if(NOT INTEGRITY)
add_subdirectory(qtconcurrentrun)
add_subdirectory(qtconcurrenttask)
endif()
add_subdirectory(qtconcurrentthreadengine)

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qtconcurrentfilter Test:
#####################################################################
qt_internal_add_test(tst_qtconcurrentfilter
SOURCES
tst_qtconcurrentfilter.cpp
LIBRARIES
Qt::Concurrent
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qtconcurrentfiltermapgenerated Test:
#####################################################################
qt_internal_add_test(tst_qtconcurrentfiltermapgenerated
SOURCES
tst_qtconcurrent_selected_tests.cpp
tst_qtconcurrentfiltermapgenerated.cpp tst_qtconcurrentfiltermapgenerated.h
LIBRARIES
Qt::Concurrent
)

View File

@ -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.

View File

@ -0,0 +1,268 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#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)
{
if (!movedFrom)
return el.isOdd();
else
return -1;
}
};
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

View File

@ -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)
+ ";"
)

View File

@ -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")

View File

@ -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_()

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -0,0 +1,347 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#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);
}

View File

@ -0,0 +1,59 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#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)

View File

@ -0,0 +1,27 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#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)
};

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qtconcurrentiteratekernel Test:
#####################################################################
qt_internal_add_test(tst_qtconcurrentiteratekernel
SOURCES
tst_qtconcurrentiteratekernel.cpp
LIBRARIES
Qt::Concurrent
)

View File

@ -0,0 +1,251 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QThread>
#include <QSet>
struct TestIterator
{
TestIterator(int i)
:i(i) { }
int operator-(const TestIterator &other)
{
return i - other.i;
}
TestIterator& operator++()
{
++i;
return *this;
}
bool operator!=(const TestIterator &other) const
{
return i != other.i;
}
int i;
};
#include <qiterator.h>
namespace std {
template <>
struct iterator_traits<TestIterator>
{
typedef random_access_iterator_tag iterator_category;
};
int distance(TestIterator &a, TestIterator &b)
{
return b - a;
}
}
#include <qtconcurrentiteratekernel.h>
#include <QTest>
using namespace QtConcurrent;
class tst_QtConcurrentIterateKernel: public QObject
{
Q_OBJECT
private slots:
// "for" iteration tests:
void instantiate();
void cancel();
void stresstest();
void noIterations();
void throttling();
void multipleResults();
};
QAtomicInt iterations;
class PrintFor : public IterateKernel<TestIterator, void>
{
public:
PrintFor(TestIterator begin, TestIterator end) : IterateKernel<TestIterator, void>(QThreadPool::globalInstance(), begin, end) { iterations.storeRelaxed(0); }
bool runIterations(TestIterator/*beginIterator*/, int begin, int end, void *) override
{
iterations.fetchAndAddRelaxed(end - begin);
#ifdef PRINT
qDebug() << QThread::currentThread() << "iteration" << begin << "to" << end << "(exclusive)";
#endif
return false;
}
bool runIteration(TestIterator it, int index , void *result) override
{
return runIterations(it, index, index + 1, result);
}
};
class SleepPrintFor : public IterateKernel<TestIterator, void>
{
public:
SleepPrintFor(TestIterator begin, TestIterator end) : IterateKernel<TestIterator, void>(QThreadPool::globalInstance(), begin, end) { iterations.storeRelaxed(0); }
inline bool runIterations(TestIterator/*beginIterator*/, int begin, int end, void *) override
{
QTest::qSleep(200);
iterations.fetchAndAddRelaxed(end - begin);
#ifdef PRINT
qDebug() << QThread::currentThread() << "iteration" << begin << "to" << end << "(exclusive)";
#endif
return false;
}
bool runIteration(TestIterator it, int index , void *result) override
{
return runIterations(it, index, index + 1, result);
}
};
void tst_QtConcurrentIterateKernel::instantiate()
{
auto future = startThreadEngine(new PrintFor(0, 40)).startAsynchronously();
future.waitForFinished();
QCOMPARE(iterations.loadRelaxed(), 40);
}
void tst_QtConcurrentIterateKernel::cancel()
{
{
QFuture<void> f = startThreadEngine(new SleepPrintFor(0, 40)).startAsynchronously();
f.cancel();
f.waitForFinished();
QVERIFY(f.isCanceled());
// the threads might run one iteration each before they are canceled.
QVERIFY2(iterations.loadRelaxed() <= QThread::idealThreadCount(),
(QByteArray::number(iterations.loadRelaxed()) + ' ' + QByteArray::number(QThread::idealThreadCount())));
}
}
QAtomicInt counter;
class CountFor : public IterateKernel<TestIterator, void>
{
public:
CountFor(TestIterator begin, TestIterator end) : IterateKernel<TestIterator, void>(QThreadPool::globalInstance(), begin, end) { iterations.storeRelaxed(0); }
inline bool runIterations(TestIterator/*beginIterator*/, int begin, int end, void *) override
{
counter.fetchAndAddRelaxed(end - begin);
return false;
}
bool runIteration(TestIterator it, int index , void *result) override
{
return runIterations(it, index, index + 1, result);
}
};
void tst_QtConcurrentIterateKernel::stresstest()
{
const int iterations = 1000;
const int times = 50;
for (int i = 0; i < times; ++i) {
counter.storeRelaxed(0);
// ThreadEngine will delete f when it finishes
auto f = new CountFor(0, iterations);
auto future = f->startAsynchronously();
future.waitForFinished();
QCOMPARE(counter.loadRelaxed(), iterations);
}
}
void tst_QtConcurrentIterateKernel::noIterations()
{
const int times = 20000;
for (int i = 0; i < times; ++i) {
auto future = startThreadEngine(new IterateKernel<TestIterator, void>(
QThreadPool::globalInstance(), 0, 0))
.startAsynchronously();
future.waitForFinished();
}
}
QMutex threadsMutex;
QSet<QThread *> threads;
class ThrottleFor : public IterateKernel<TestIterator, void>
{
public:
// this class throttles between iterations 100 and 200,
// and then records how many threads that run between
// iterations 140 and 160.
ThrottleFor(TestIterator begin, TestIterator end) : IterateKernel<TestIterator, void>(QThreadPool::globalInstance(), begin, end) { iterations.storeRelaxed(0); throttling = false; }
inline bool runIterations(TestIterator/*beginIterator*/, int begin, int end, void *) override
{
if (200 >= begin && 200 < end) {
throttling = false;
}
iterations.fetchAndAddRelaxed(end - begin);
QThread *thread = QThread::currentThread();
if (begin > 140 && end < 160) {
QMutexLocker locker(&threadsMutex);
threads.insert(thread);
}
if (100 >= begin && 100 < end) {
throttling = true;
}
QTest::qWait(1);
return false;
}
bool runIteration(TestIterator it, int index , void *result) override
{
return runIterations(it, index, index + 1, result);
}
bool shouldThrottleThread() override
{
const int load = iterations.loadRelaxed();
return (load > 100 && load < 200);
}
bool throttling;
};
void tst_QtConcurrentIterateKernel::throttling()
{
const int totalIterations = 400;
iterations.storeRelaxed(0);
threads.clear();
// ThreadEngine will delete f when it finishes
auto f = new ThrottleFor(0, totalIterations);
auto future = f->startAsynchronously();
future.waitForFinished();
QCOMPARE(iterations.loadRelaxed(), totalIterations);
QCOMPARE(threads.size(), 1);
}
class MultipleResultsFor : public IterateKernel<TestIterator, int>
{
public:
MultipleResultsFor(TestIterator begin, TestIterator end) : IterateKernel<TestIterator, int>(QThreadPool::globalInstance(), begin, end) { }
inline bool runIterations(TestIterator, int begin, int end, int *results) override
{
for (int i = begin; i < end; ++i)
results[i - begin] = i;
return true;
}
};
void tst_QtConcurrentIterateKernel::multipleResults()
{
QFuture<int> f = startThreadEngine(new MultipleResultsFor(0, 10)).startAsynchronously();
QCOMPARE(f.results().size() , 10);
QCOMPARE(f.resultAt(0), 0);
QCOMPARE(f.resultAt(5), 5);
QCOMPARE(f.resultAt(9), 9);
f.waitForFinished();
}
QTEST_MAIN(tst_QtConcurrentIterateKernel)
#include "tst_qtconcurrentiteratekernel.moc"

View File

@ -0,0 +1,16 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qtconcurrentmap Test:
#####################################################################
qt_internal_add_test(tst_qtconcurrentmap
SOURCES
tst_qtconcurrentmap.cpp
LIBRARIES
Qt::Concurrent
)
## Scopes:
#####################################################################

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qtconcurrentmedian Test:
#####################################################################
qt_internal_add_test(tst_qtconcurrentmedian
SOURCES
tst_qtconcurrentmedian.cpp
LIBRARIES
Qt::Concurrent
)

View File

@ -0,0 +1,61 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <qtconcurrentmedian.h>
#include <QTest>
class tst_QtConcurrentMedian: public QObject
{
Q_OBJECT
private slots:
void median_data();
void median();
};
void tst_QtConcurrentMedian::median_data()
{
QTest::addColumn<QList<double> >("values");
QTest::addColumn<double>("expectedMedian");
QTest::newRow("size=1")
<< (QList<double>() << 1.0)
<< 0.0; // six 0.0 in front of the actual value
QTest::newRow("size=2")
<< (QList<double>() << 3.0 << 2.0)
<< 0.0; // five 0.0 in front of the actual value
QTest::newRow("size=3")
<< (QList<double>() << 3.0 << 1.0 << 2.0)
<< 0.0; // four 0.0 in front of the actual value
QTest::newRow("size=4")
<< (QList<double>() << 3.0 << 1.0 << 2.0 << 4.0)
<< 1.0; // three 0.0 in front of the first actual value, pick 1.0
QTest::newRow("size=5")
<< (QList<double>() << 3.0 << 1.0 << 2.0 << 3.0 << 1.0)
<< 1.0; // two 0.0 in front of the first actual value, pick 1.0
QTest::newRow("size=6")
<< (QList<double>() << 3.0 << 1.0 << 2.0 << 3.0 << 1.0 << 2.0)
<< 2.0; // one 0.0 in front of the first actual value, pick 2.0
QTest::newRow("size=7")
<< QList<double> { 207089.0, 202585.0, 180067.0, 157549.0, 211592.0, 216096.0, 207089.0 }
<< 207089.0;
}
void tst_QtConcurrentMedian::median()
{
QFETCH(QList<double> , values);
QFETCH(double, expectedMedian);
QtConcurrent::Median m;
foreach (double value, values)
m.addValue(value);
QCOMPARE(m.median(), expectedMedian);
}
QTEST_MAIN(tst_QtConcurrentMedian)
#include "tst_qtconcurrentmedian.moc"

View File

@ -0,0 +1,17 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qtconcurrentrun Test:
#####################################################################
qt_internal_add_test(tst_qtconcurrentrun
SOURCES
tst_qtconcurrentrun.cpp
LIBRARIES
Qt::Concurrent
Qt::TestPrivate
)
## Scopes:
#####################################################################

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qtconcurrenttask Test:
#####################################################################
qt_internal_add_test(tst_qtconcurrenttask
SOURCES
tst_qtconcurrenttask.cpp
LIBRARIES
Qt::Concurrent
)

View File

@ -0,0 +1,196 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <qtconcurrenttask.h>
#include <QTest>
#include <QSemaphore>
#include <random>
class tst_QtConcurrentTask : public QObject
{
Q_OBJECT
private Q_SLOTS:
void taskWithFreeFunction();
void taskWithClassMethod();
void taskWithCallableObject();
void taskWithLambda();
void taskWithArguments();
void useCustomThreadPool();
void setPriority_data();
void setPriority();
void adjustAllSettings();
void ignoreFutureResult();
void withPromise();
};
using namespace QtConcurrent;
void tst_QtConcurrentTask::taskWithFreeFunction()
{
QVariant value(42);
auto result = task(&qvariant_cast<int>)
.withArguments(value)
.spawn()
.result();
QCOMPARE(result, 42);
}
void tst_QtConcurrentTask::taskWithClassMethod()
{
QString result("foobar");
task(&QString::chop).withArguments(&result, 3).spawn().waitForFinished();
QCOMPARE(result, "foo");
}
void tst_QtConcurrentTask::taskWithCallableObject()
{
QCOMPARE(task(std::plus<int>())
.withArguments(40, 2)
.spawn()
.result(),
42);
}
void tst_QtConcurrentTask::taskWithLambda()
{
QCOMPARE(task([]{ return 42; }).spawn().result(), 42);
}
void tst_QtConcurrentTask::taskWithArguments()
{
auto result = task([](int arg1, int arg2){ return arg1 + arg2; })
.withArguments(40, 2)
.spawn()
.result();
QCOMPARE(result, 42);
}
void tst_QtConcurrentTask::useCustomThreadPool()
{
QThreadPool pool;
int result = 0;
task([&]{ result = 42; }).onThreadPool(pool).spawn().waitForFinished();
QCOMPARE(result, 42);
}
void tst_QtConcurrentTask::setPriority_data()
{
QTest::addColumn<bool>("runWithPromise");
QTest::addRow("without promise") << false;
QTest::addRow("with promise") << true;
}
void tst_QtConcurrentTask::setPriority()
{
QFETCH(bool, runWithPromise);
QThreadPool pool;
pool.setMaxThreadCount(1);
QSemaphore sem;
QList<QFuture<void>> futureResults;
futureResults << task([&]{ sem.acquire(); })
.onThreadPool(pool)
.spawn();
const int tasksCount = 10;
QList<int> priorities(tasksCount);
std::iota(priorities.begin(), priorities.end(), 1);
auto seed = std::random_device {}();
std::shuffle(priorities.begin(), priorities.end(), std::default_random_engine(seed));
qDebug() << "Generated priorities list" << priorities << "using seed" << seed;
QList<int> actual;
for (int priority : priorities) {
if (runWithPromise) {
futureResults << task([priority, &actual] (QPromise<void> &) { actual << priority; })
.onThreadPool(pool)
.withPriority(priority)
.spawn();
} else {
futureResults << task([priority, &actual] { actual << priority; })
.onThreadPool(pool)
.withPriority(priority)
.spawn();
}
}
sem.release();
pool.waitForDone();
for (const auto &f : futureResults)
QVERIFY(f.isFinished());
QList<int> expected(priorities);
std::sort(expected.begin(), expected.end(), std::greater<>());
QCOMPARE(actual, expected);
}
void tst_QtConcurrentTask::adjustAllSettings()
{
QThreadPool pool;
pool.setMaxThreadCount(1);
const int priority = 10;
QList<int> result;
auto append = [&](auto &&...args){ (result << ... << args); };
task(std::move(append))
.withArguments(1, 2, 3)
.onThreadPool(pool)
.withPriority(priority)
.spawn()
.waitForFinished();
QCOMPARE(result, QList<int>({ 1, 2, 3 }));
}
void tst_QtConcurrentTask::ignoreFutureResult()
{
QThreadPool pool;
std::atomic_int value = 0;
for (std::size_t i = 0; i < 10; ++i)
task([&value]{ ++value; })
.onThreadPool(pool)
.spawn(FutureResult::Ignore);
pool.waitForDone();
QCOMPARE(value, 10);
}
void incrementWithPromise(QPromise<int> &promise, int i)
{
promise.addResult(i + 1);
}
void return0WithPromise(QPromise<int> &promise)
{
promise.addResult(0);
}
void tst_QtConcurrentTask::withPromise()
{
QCOMPARE(task(&return0WithPromise).spawn().result(), 0);
QCOMPARE(task(&return0WithPromise).withPriority(7).spawn().result(), 0);
QCOMPARE(task(&incrementWithPromise).withArguments(1).spawn().result(), 2);
QCOMPARE(task(&incrementWithPromise).withArguments(1).withPriority(7).spawn().result(), 2);
}
QTEST_MAIN(tst_QtConcurrentTask)
#include "tst_qtconcurrenttask.moc"

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qtconcurrentthreadengine Test:
#####################################################################
qt_internal_add_test(tst_qtconcurrentthreadengine
SOURCES
tst_qtconcurrentthreadengine.cpp
LIBRARIES
Qt::Concurrent
)

View File

@ -0,0 +1,426 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <qtconcurrentthreadengine.h>
#include <qexception.h>
#include <QThread>
#include <QElapsedTimer>
#include <QTest>
#include <QSet>
using namespace QtConcurrent;
class tst_QtConcurrentThreadEngine: public QObject
{
Q_OBJECT
private slots:
void runDirectly();
void result();
void runThroughStarter();
void cancel();
void throttle();
void threadCount();
void multipleResults();
void stresstest();
void cancelQueuedSlowUser();
#ifndef QT_NO_EXCEPTIONS
void exceptions();
#endif
};
class PrintUser : public ThreadEngine<void>
{
public:
PrintUser() : ThreadEngine(QThreadPool::globalInstance()) {}
ThreadFunctionResult threadFunction() override
{
QTest::qSleep(50);
QTest::qSleep(100);
return ThreadFinished;
}
};
void tst_QtConcurrentThreadEngine::runDirectly()
{
PrintUser *engine = new PrintUser();
QFuture<void> f = engine->startAsynchronously();
f.waitForFinished();
}
class StringResultUser : public ThreadEngine<QString>
{
public:
typedef QString ResultType;
StringResultUser()
: ThreadEngine(QThreadPool::globalInstance())
, done(false) { }
bool shouldStartThread() override
{
return !done;
}
ThreadFunctionResult threadFunction() override
{
done = true;
return ThreadFinished;
}
QString *result() override
{
foo = "Foo";
return &foo;
}
QString foo;
bool done;
};
void tst_QtConcurrentThreadEngine::result()
{
// ThreadEngine will delete 'engine' when it finishes
auto engine = new StringResultUser();
auto future = engine->startAsynchronously();
QCOMPARE(future.result(), QString("Foo"));
}
class VoidResultUser : public ThreadEngine<void>
{
public:
VoidResultUser() : ThreadEngine(QThreadPool::globalInstance()) {}
bool shouldStartThread() override
{
return !done;
}
ThreadFunctionResult threadFunction() override
{
done = true;
return ThreadFinished;
}
void *result() override
{
return 0;
}
bool done;
};
void tst_QtConcurrentThreadEngine::runThroughStarter()
{
ThreadEngineStarter<QString> starter = startThreadEngine(new StringResultUser());
QFuture<QString> f = starter.startAsynchronously();
QCOMPARE(f.result(), QString("Foo"));
}
class CancelUser : public ThreadEngine<void>
{
public:
CancelUser() : ThreadEngine(QThreadPool::globalInstance()) {}
void *result() override
{
return 0;
}
ThreadFunctionResult threadFunction() override
{
while (this->isCanceled() == false)
{
QTest::qSleep(10);
}
return ThreadFinished;
}
};
void tst_QtConcurrentThreadEngine::cancel()
{
{
CancelUser *engine = new CancelUser();
QFuture<void> f = engine->startAsynchronously();
f.cancel();
f.waitForFinished();
}
{
CancelUser *engine = new CancelUser();
QFuture<void> f = engine->startAsynchronously();
QTest::qSleep(10);
f.cancel();
f.waitForFinished();
}
}
QAtomicInt count;
class ThrottleAlwaysUser : public ThreadEngine<void>
{
public:
ThrottleAlwaysUser()
: ThreadEngine(QThreadPool::globalInstance())
{
count.storeRelaxed(initialCount = 100);
finishing = false;
}
bool shouldStartThread() override
{
return !finishing;
}
ThreadFunctionResult threadFunction() override
{
forever {
const int local = count.loadRelaxed();
if (local == 0) {
finishing = true;
return ThreadFinished;
}
if (count.testAndSetOrdered(local, local - 1))
break;
}
return ThrottleThread;
}
bool finishing;
int initialCount;
};
// Test that a user task with a thread function that always
// want to be throttled still completes. The thread engine
// should make keep one thread running at all times.
void tst_QtConcurrentThreadEngine::throttle()
{
const int repeats = 10;
for (int i = 0; i < repeats; ++i) {
QFuture<void> f = (new ThrottleAlwaysUser())->startAsynchronously();
f.waitForFinished();
QCOMPARE(count.loadRelaxed(), 0);
}
}
QSet<QThread *> threads;
QMutex mutex;
class ThreadCountUser : public ThreadEngine<void>
{
public:
ThreadCountUser(bool finishImmediately = false)
: ThreadEngine(QThreadPool::globalInstance())
{
threads.clear();
finishing = finishImmediately;
}
bool shouldStartThread() override
{
return !finishing;
}
ThreadFunctionResult threadFunction() override
{
{
QMutexLocker lock(&mutex);
threads.insert(QThread::currentThread());
}
QTest::qSleep(10);
finishing = true;
return ThreadFinished;
}
bool finishing;
};
void tst_QtConcurrentThreadEngine::threadCount()
{
const int repeats = 10;
for (int i = 0; i < repeats; ++i) {
(new ThreadCountUser())->startAsynchronously().waitForFinished();
const auto count = threads.size();
const auto maxThreadCount = QThreadPool::globalInstance()->maxThreadCount();
QVERIFY(count <= maxThreadCount);
QVERIFY(!threads.contains(QThread::currentThread()));
}
// Set the finish flag immediately, this should give us one thread only.
for (int i = 0; i < repeats; ++i) {
(new ThreadCountUser(true /*finishImmediately*/))->startAsynchronously().waitForFinished();
const auto count = threads.size();
QCOMPARE(count, 1);
QVERIFY(!threads.contains(QThread::currentThread()));
}
}
class MultipleResultsUser : public ThreadEngine<int>
{
public:
MultipleResultsUser() : ThreadEngine(QThreadPool::globalInstance()) {}
bool shouldStartThread() override
{
return false;
}
ThreadFunctionResult threadFunction() override
{
for (int i = 0; i < 10; ++i)
this->reportResult(&i);
return ThreadFinished;
}
};
void tst_QtConcurrentThreadEngine::multipleResults()
{
MultipleResultsUser *engine = new MultipleResultsUser();
QFuture<int> f = engine->startAsynchronously();
QCOMPARE(f.results().size() , 10);
QCOMPARE(f.resultAt(0), 0);
QCOMPARE(f.resultAt(5), 5);
QCOMPARE(f.resultAt(9), 9);
f.waitForFinished();
}
class NoThreadsUser : public ThreadEngine<void>
{
public:
bool shouldStartThread() override
{
return false;
}
ThreadFunctionResult threadFunction() override
{
return ThreadFinished;
}
void *result() override
{
return 0;
}
};
void tst_QtConcurrentThreadEngine::stresstest()
{
const int times = 20000;
for (int i = 0; i < times; ++i) {
VoidResultUser *engine = new VoidResultUser();
engine->startAsynchronously().waitForFinished();
}
for (int i = 0; i < times; ++i) {
VoidResultUser *engine = new VoidResultUser();
engine->startAsynchronously();
}
for (int i = 0; i < times; ++i) {
VoidResultUser *engine = new VoidResultUser();
engine->startAsynchronously().waitForFinished();
}
}
const int sleepTime = 20;
class SlowUser : public ThreadEngine<void>
{
public:
SlowUser() : ThreadEngine(QThreadPool::globalInstance()) {}
bool shouldStartThread() override { return false; }
ThreadFunctionResult threadFunction() override { QTest::qSleep(sleepTime); return ThreadFinished; }
};
void tst_QtConcurrentThreadEngine::cancelQueuedSlowUser()
{
const int times = 100;
QElapsedTimer t;
t.start();
{
QList<QFuture<void> > futures;
for (int i = 0; i < times; ++i) {
SlowUser *engine = new SlowUser();
futures.append(engine->startAsynchronously());
}
foreach(QFuture<void> future, futures)
future.cancel();
}
QVERIFY(t.elapsed() < (sleepTime * times) / 2);
}
#ifndef QT_NO_EXCEPTIONS
class QtConcurrentExceptionThrower : public ThreadEngine<void>
{
public:
QtConcurrentExceptionThrower(QThread *blockThread = nullptr)
: ThreadEngine(QThreadPool::globalInstance())
{
this->blockThread = blockThread;
}
ThreadFunctionResult threadFunction() override
{
QTest::qSleep(50);
throw QException();
return ThreadFinished;
}
QThread *blockThread;
};
class IntExceptionThrower : public ThreadEngine<void>
{
public:
IntExceptionThrower(QThread *blockThread = nullptr)
: ThreadEngine(QThreadPool::globalInstance())
{
this->blockThread = blockThread;
}
ThreadFunctionResult threadFunction() override
{
QTest::qSleep(50);
throw int();
return ThreadFinished;
}
QThread *blockThread;
};
void tst_QtConcurrentThreadEngine::exceptions()
{
{
bool caught = false;
try {
QtConcurrentExceptionThrower *e = new QtConcurrentExceptionThrower();
QFuture<void> f = e->startAsynchronously();
f.waitForFinished();
} catch (const QException &) {
caught = true;
}
QVERIFY2(caught, "did not get exception");
}
{
bool caught = false;
try {
IntExceptionThrower *e = new IntExceptionThrower();
QFuture<void> f = e->startAsynchronously();
f.waitForFinished();
} catch (const QUnhandledException &ex) {
// Make sure the exception info is not lost
try {
if (ex.exception())
std::rethrow_exception(ex.exception());
} catch (int) {
caught = true;
}
}
QVERIFY2(caught, "did not get exception");
}
}
#endif
QTEST_MAIN(tst_QtConcurrentThreadEngine)
#include "tst_qtconcurrentthreadengine.moc"

View File

@ -0,0 +1,183 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef FUNCTIONS_H
#define FUNCTIONS_H
#include <QList>
#include <vector>
bool keepEvenIntegers(const int &x)
{
return (x & 1) == 0;
}
class KeepEvenIntegers
{
public:
bool operator()(const int &x)
{
return (x & 1) == 0;
}
};
class KeepEvenIntegersMoveOnly
{
public:
KeepEvenIntegersMoveOnly() = default;
KeepEvenIntegersMoveOnly(KeepEvenIntegersMoveOnly &&) = default;
KeepEvenIntegersMoveOnly &operator=(KeepEvenIntegersMoveOnly &&other) = default;
KeepEvenIntegersMoveOnly(const KeepEvenIntegersMoveOnly &) = delete;
KeepEvenIntegersMoveOnly &operator=(const KeepEvenIntegersMoveOnly &) = delete;
bool operator()(int x) { return (x & 1) == 0; }
};
class Number
{
int n;
public:
Number()
: n(0)
{ }
Number(int n)
: n(n)
{ }
void multiplyBy2()
{
n *= 2;
}
Number multipliedBy2() const
{
return n * 2;
}
bool isEven() const
{
return (n & 1) == 0;
}
int toInt() const
{
return n;
}
QString toString() const
{
return QString::number(n);
}
Number squared() const
{
return Number(n * n);
}
bool operator==(const Number &other) const
{
return n == other.n;
}
};
bool keepEvenNumbers(const Number &x)
{
return (x.toInt() & 1) == 0;
}
class KeepEvenNumbers
{
public:
bool operator()(const Number &x)
{
return (x.toInt() & 1) == 0;
}
};
void intSumReduce(int &sum, int x)
{
sum += x;
}
class IntSumReduce
{
public:
void operator()(int &sum, int x)
{
sum += x;
}
};
class IntSumReduceMoveOnly
{
public:
IntSumReduceMoveOnly() = default;
IntSumReduceMoveOnly(IntSumReduceMoveOnly &&) = default;
IntSumReduceMoveOnly &operator=(IntSumReduceMoveOnly &&other) = default;
IntSumReduceMoveOnly(const IntSumReduceMoveOnly &) = delete;
IntSumReduceMoveOnly &operator=(const IntSumReduceMoveOnly &) = delete;
void operator()(int &sum, int x) { sum += x; }
};
void numberSumReduce(int &sum, const Number &x)
{
sum += x.toInt();
}
class NumberSumReduce
{
public:
void operator()(int &sum, const Number &x)
{
sum += x.toInt();
}
};
template<typename T>
class MoveOnlyVector
{
public:
using value_type = T;
// rule of six
MoveOnlyVector() = default;
~MoveOnlyVector() = default;
MoveOnlyVector(MoveOnlyVector<T> &&other) = default;
MoveOnlyVector &operator=(MoveOnlyVector<T> &&other) = default;
MoveOnlyVector(const MoveOnlyVector<T> &) = delete;
MoveOnlyVector &operator=(const MoveOnlyVector<T> &) = delete;
// convenience for creation
explicit MoveOnlyVector(const std::vector<T> &v) : data(v) { }
void push_back(T &&el) { data.push_back(el); }
void push_back(const T &el) { data.push_back(el); }
// minimal interface to be usable as a Sequence in QtConcurrent
typedef typename std::vector<T>::const_iterator const_iterator;
typedef typename std::vector<T>::iterator iterator;
const_iterator cbegin() const { return data.cbegin(); }
const_iterator cend() const { return data.cend(); }
iterator begin() { return data.begin(); }
iterator end() { return data.end(); }
const_iterator begin() const { return data.cbegin(); }
const_iterator end() const { return data.cend(); }
bool operator==(const MoveOnlyVector<T> &other) const { return data == other.data; }
private:
std::vector<T> data;
};
struct NonTemplateSequence : public QList<int>
{
NonTemplateSequence() = default;
NonTemplateSequence(std::initializer_list<int> args) : QList(args) { }
};
#endif