mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2025-07-02 23:35:28 +08:00
qt 6.5.1 original
This commit is contained in:
13
tests/auto/concurrent/CMakeLists.txt
Normal file
13
tests/auto/concurrent/CMakeLists.txt
Normal 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)
|
13
tests/auto/concurrent/qtconcurrentfilter/CMakeLists.txt
Normal file
13
tests/auto/concurrent/qtconcurrentfilter/CMakeLists.txt
Normal 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
|
||||
)
|
1548
tests/auto/concurrent/qtconcurrentfilter/tst_qtconcurrentfilter.cpp
Normal file
1548
tests/auto/concurrent/qtconcurrentfilter/tst_qtconcurrentfilter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
)
|
@ -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.
|
@ -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
|
@ -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)
|
||||
+ ";"
|
||||
)
|
@ -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")
|
@ -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_()
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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
|
@ -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);
|
||||
}
|
@ -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)
|
@ -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)
|
||||
};
|
@ -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
|
||||
)
|
@ -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"
|
16
tests/auto/concurrent/qtconcurrentmap/CMakeLists.txt
Normal file
16
tests/auto/concurrent/qtconcurrentmap/CMakeLists.txt
Normal 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:
|
||||
#####################################################################
|
2172
tests/auto/concurrent/qtconcurrentmap/tst_qtconcurrentmap.cpp
Normal file
2172
tests/auto/concurrent/qtconcurrentmap/tst_qtconcurrentmap.cpp
Normal file
File diff suppressed because it is too large
Load Diff
13
tests/auto/concurrent/qtconcurrentmedian/CMakeLists.txt
Normal file
13
tests/auto/concurrent/qtconcurrentmedian/CMakeLists.txt
Normal 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
|
||||
)
|
@ -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"
|
17
tests/auto/concurrent/qtconcurrentrun/CMakeLists.txt
Normal file
17
tests/auto/concurrent/qtconcurrentrun/CMakeLists.txt
Normal 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:
|
||||
#####################################################################
|
1601
tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp
Normal file
1601
tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp
Normal file
File diff suppressed because it is too large
Load Diff
13
tests/auto/concurrent/qtconcurrenttask/CMakeLists.txt
Normal file
13
tests/auto/concurrent/qtconcurrenttask/CMakeLists.txt
Normal 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
|
||||
)
|
196
tests/auto/concurrent/qtconcurrenttask/tst_qtconcurrenttask.cpp
Normal file
196
tests/auto/concurrent/qtconcurrenttask/tst_qtconcurrenttask.cpp
Normal 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"
|
@ -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
|
||||
)
|
@ -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"
|
183
tests/auto/concurrent/testhelper_functions.h
Normal file
183
tests/auto/concurrent/testhelper_functions.h
Normal 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
|
Reference in New Issue
Block a user