Automatically generate unit tests for QtConcurrent

There are many different ways to to call map and filter functions
in QtConcurrent. This patch adds a python script to generate
all possible combinations.

Change-Id: I61ed1758601e219c5852e8cc939c5feebb23d2f6
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
This commit is contained in:
Andreas Buhr 2020-10-09 00:02:00 +02:00
parent 3553f8119b
commit 223e409efb
21 changed files with 1948 additions and 46 deletions

View File

@ -133,7 +133,7 @@ public:
void finish() override
{
reducer.finish(reduce, reducedResult);
sequence = reducedResult;
sequence = std::move(reducedResult);
}
inline bool shouldThrottleThread() override

View File

@ -1,6 +1,7 @@
# Generated from concurrent.pro.
add_subdirectory(qtconcurrentfilter)
add_subdirectory(qtconcurrentfiltermapgenerated)
add_subdirectory(qtconcurrentiteratekernel)
add_subdirectory(qtconcurrentmap)
add_subdirectory(qtconcurrentmedian)

View File

@ -2,6 +2,7 @@ TEMPLATE=subdirs
SUBDIRS=\
qtconcurrentfilter \
qtconcurrentiteratekernel \
qtconcurrentfiltermapgenerated \
qtconcurrentmap \
qtconcurrentmedian \
qtconcurrentrun \

View File

@ -265,13 +265,13 @@ void tst_QtConcurrentFilter::filtered()
{
// move only types sequences
auto future = QtConcurrent::filtered(MoveOnlyVector({ 1, 2, 3, 4 }), keepEvenIntegers);
auto future = QtConcurrent::filtered(MoveOnlyVector<int>({ 1, 2, 3, 4 }), keepEvenIntegers);
QCOMPARE(future.results(), QList<int>({ 2, 4 }));
#if 0
// does not work yet
auto result = QtConcurrent::blockingFiltered(
MoveOnlyVector({ 1, 2, 3, 4 }), keepEvenIntegers);
MoveOnlyVector<int>({ 1, 2, 3, 4 }), keepEvenIntegers);
QCOMPARE(result, std::vector<int>({ 2, 4 }));
#endif
}
@ -341,15 +341,15 @@ void tst_QtConcurrentFilter::filteredThreadPool()
{
// move-only sequences
auto future = QtConcurrent::filtered(
&pool, MoveOnlyVector({ 1, 2, 3, 4 }), keepEvenIntegers);
auto future = QtConcurrent::filtered(&pool, MoveOnlyVector<int>({ 1, 2, 3, 4 }),
keepEvenIntegers);
QCOMPARE(future.results(), QList<int>({ 2, 4 }));
#if 0
// does not work yet
auto result =
QtConcurrent::blockingFiltered(
&pool, MoveOnlyVector({ 1, 2, 3, 4 }), keepEvenIntegers);
&pool, MoveOnlyVector<int>({ 1, 2, 3, 4 }), keepEvenIntegers);
QCOMPARE(result, std::vector<int>({ 2, 4 }));
#endif
}
@ -550,11 +550,11 @@ void tst_QtConcurrentFilter::filteredReduced()
{
// move only sequences
auto future = QtConcurrent::filteredReduced(MoveOnlyVector({ 1, 2, 3, 4 }),
auto future = QtConcurrent::filteredReduced(MoveOnlyVector<int>({ 1, 2, 3, 4 }),
keepEvenIntegers, intSumReduce);
QCOMPARE(future.result(), intSum);
auto result = QtConcurrent::blockingFilteredReduced(MoveOnlyVector({ 1, 2, 3, 4 }),
auto result = QtConcurrent::blockingFilteredReduced(MoveOnlyVector<int>({ 1, 2, 3, 4 }),
keepEvenIntegers, intSumReduce);
QCOMPARE(result, intSum);
}
@ -649,12 +649,12 @@ void tst_QtConcurrentFilter::filteredReducedThreadPool()
{
// move only sequences
auto future = QtConcurrent::filteredReduced(&pool, MoveOnlyVector({ 1, 2, 3, 4 }),
auto future = QtConcurrent::filteredReduced(&pool, MoveOnlyVector<int>({ 1, 2, 3, 4 }),
keepOddIntegers, intSumReduce);
QCOMPARE(future.result(), intSum);
auto result = QtConcurrent::blockingFilteredReduced(&pool, MoveOnlyVector({ 1, 2, 3, 4 }),
keepOddIntegers, intSumReduce);
auto result = QtConcurrent::blockingFilteredReduced(
&pool, MoveOnlyVector<int>({ 1, 2, 3, 4 }), keepOddIntegers, intSumReduce);
QCOMPARE(result, intSum);
}
}
@ -923,12 +923,12 @@ void tst_QtConcurrentFilter::filteredReducedInitialValue()
{
// move only sequences
auto future = QtConcurrent::filteredReduced(MoveOnlyVector({ 1, 2, 3, 4 }),
auto future = QtConcurrent::filteredReduced(MoveOnlyVector<int>({ 1, 2, 3, 4 }),
keepEvenIntegers, intSumReduce, intInitial);
QCOMPARE(future.result(), intSum);
auto result = QtConcurrent::blockingFilteredReduced(
MoveOnlyVector({ 1, 2, 3, 4 }), keepEvenIntegers, intSumReduce, intInitial);
MoveOnlyVector<int>({ 1, 2, 3, 4 }), keepEvenIntegers, intSumReduce, intInitial);
QCOMPARE(result, intSum);
}
}
@ -1034,12 +1034,13 @@ void tst_QtConcurrentFilter::filteredReducedInitialValueThreadPool()
{
// move only sequences
auto future = QtConcurrent::filteredReduced(&pool, MoveOnlyVector({ 1, 2, 3, 4 }),
auto future = QtConcurrent::filteredReduced(&pool, MoveOnlyVector<int>({ 1, 2, 3, 4 }),
keepOddIntegers, intSumReduce, intInitial);
QCOMPARE(future.result(), intSum);
auto result = QtConcurrent::blockingFilteredReduced(
&pool, MoveOnlyVector({ 1, 2, 3, 4 }), keepOddIntegers, intSumReduce, intInitial);
auto result =
QtConcurrent::blockingFilteredReduced(&pool, MoveOnlyVector<int>({ 1, 2, 3, 4 }),
keepOddIntegers, intSumReduce, intInitial);
QCOMPARE(result, intSum);
}
}

View File

@ -0,0 +1 @@
tst_qtconcurrentfiltermapgenerated

View File

@ -0,0 +1,12 @@
#####################################################################
## tst_qtconcurrentfiltermapgenerated Test:
#####################################################################
qt_internal_add_test(tst_qtconcurrentfiltermapgenerated
SOURCES
tst_qtconcurrentfiltermapgenerated.cpp
tst_qtconcurrentfiltermapgenerated.h
tst_qtconcurrent_selected_tests.cpp
PUBLIC_LIBRARIES
Qt::Concurrent
)

View File

@ -0,0 +1,13 @@
This directory contains a generator for unit tests for QtConcurrent.
The subdirectory 'generator' contains the generator. Run the file
"generate_gui.py" therein.
Python3.8 and PySide2 are required.
The generator writes on each click a testcase into the file
tst_qtconcurrentfiltermapgenerated.cpp
and
tst_qtconcurrentfiltermapgenerated.h.
Testcases which should be preserved can be copy-pasted into
tst_qtconcurrent_selected_tests.cpp.

View File

@ -0,0 +1,293 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QTBASE_GENERATION_HELPERS_H
#define QTBASE_GENERATION_HELPERS_H
#include "qglobal.h"
#include <vector>
struct tag_input
{
};
struct tag_mapped
{
};
struct tag_reduction
{
};
template<typename tag>
struct SequenceItem
{
SequenceItem() = default;
// bool as a stronger "explicit": should never be called inside of QtConcurrent
SequenceItem(int val, bool) : value(val) { }
bool operator==(const SequenceItem<tag> &other) const { return value == other.value; }
bool isOdd() const { return value & 1; }
void multiplyByTwo() { value *= 2; }
int value = 0;
};
template<typename tag>
struct NoConstructSequenceItem
{
NoConstructSequenceItem() = delete;
// bool as a stronger "explicit": should never be called inside of QtConcurrent
NoConstructSequenceItem(int val, bool) : value(val) { }
bool operator==(const NoConstructSequenceItem<tag> &other) const
{
return value == other.value;
}
bool isOdd() const { return value & 1; }
void multiplyByTwo() { value *= 2; }
int value = 0;
};
template<typename tag>
struct MoveOnlySequenceItem
{
MoveOnlySequenceItem() = default;
~MoveOnlySequenceItem() = default;
MoveOnlySequenceItem(const MoveOnlySequenceItem &) = delete;
MoveOnlySequenceItem(MoveOnlySequenceItem &&other) : value(other.value) { other.value = -1; }
MoveOnlySequenceItem &operator=(const MoveOnlySequenceItem &) = delete;
MoveOnlySequenceItem &operator=(MoveOnlySequenceItem &&other)
{
value = other.value;
other.value = -1;
}
// bool as a stronger "explicit": should never be called inside of QtConcurrent
MoveOnlySequenceItem(int val, bool) : value(val) { }
bool operator==(const MoveOnlySequenceItem<tag> &other) const { return value == other.value; }
bool isOdd() const { return value & 1; }
void multiplyByTwo() { value *= 2; }
int value = 0;
};
template<typename tag>
struct MoveOnlyNoConstructSequenceItem
{
MoveOnlyNoConstructSequenceItem() = delete;
~MoveOnlyNoConstructSequenceItem() = default;
MoveOnlyNoConstructSequenceItem(const MoveOnlyNoConstructSequenceItem &) = delete;
MoveOnlyNoConstructSequenceItem(MoveOnlyNoConstructSequenceItem &&other) : value(other.value)
{
other.value = -1;
}
MoveOnlyNoConstructSequenceItem &operator=(const MoveOnlyNoConstructSequenceItem &) = delete;
MoveOnlyNoConstructSequenceItem &operator=(MoveOnlyNoConstructSequenceItem &&other)
{
value = other.value;
other.value = -1;
}
// bool as a stronger "explicit": should never be called inside of QtConcurrent
MoveOnlyNoConstructSequenceItem(int val, bool) : value(val) { }
bool operator==(const MoveOnlyNoConstructSequenceItem<tag> &other) const
{
return value == other.value;
}
bool isOdd() const { return value & 1; }
void multiplyByTwo() { value *= 2; }
int value = 0;
};
template<typename T>
bool myfilter(const T &el)
{
return el.isOdd();
}
template<typename T>
class MyFilter
{
public:
bool operator()(const T &el) { return el.isOdd(); }
};
template<typename T>
class MyMoveOnlyFilter
{
bool movedFrom = false;
public:
MyMoveOnlyFilter() = default;
MyMoveOnlyFilter(const MyMoveOnlyFilter<T> &) = delete;
MyMoveOnlyFilter &operator=(const MyMoveOnlyFilter<T> &) = delete;
MyMoveOnlyFilter(MyMoveOnlyFilter<T> &&other) { other.movedFrom = true; }
MyMoveOnlyFilter &operator=(MyMoveOnlyFilter<T> &&other) { other.movedFrom = true; }
bool operator()(const T &el)
{
if (!movedFrom)
return el.isOdd();
else
return -1;
}
};
template<typename From, typename To>
To myMap(const From &f)
{
return To(f.value * 2, true);
}
template<typename T>
void myInplaceMap(T &el)
{
el.multiplyByTwo();
}
template<typename From, typename To>
class MyMap
{
public:
To operator()(const From &el) { return To(el.value * 2, true); }
};
template<typename T>
class MyInplaceMap
{
public:
void operator()(T &el) { el.multiplyByTwo(); }
};
template<typename From, typename To>
class MyMoveOnlyMap
{
bool movedFrom = false;
public:
MyMoveOnlyMap() = default;
MyMoveOnlyMap(const MyMoveOnlyMap<From, To> &) = delete;
MyMoveOnlyMap &operator=(const MyMoveOnlyMap<From, To> &) = delete;
MyMoveOnlyMap(MyMoveOnlyMap<From, To> &&other) { other.movedFrom = true; }
MyMoveOnlyMap &operator=(MyMoveOnlyMap<From, To> &&other) { other.movedFrom = true; }
To operator()(const From &el)
{
if (!movedFrom)
return To(el.value * 2, true);
else
return To(-1, true);
}
};
template<typename T>
class MyMoveOnlyInplaceMap
{
bool movedFrom = false;
public:
MyMoveOnlyInplaceMap() = default;
MyMoveOnlyInplaceMap(const MyMoveOnlyInplaceMap<T> &) = delete;
MyMoveOnlyInplaceMap &operator=(const MyMoveOnlyInplaceMap<T> &) = delete;
MyMoveOnlyInplaceMap(MyMoveOnlyInplaceMap<T> &&other) { other.movedFrom = true; }
MyMoveOnlyInplaceMap &operator=(MyMoveOnlyInplaceMap<T> &&other) { other.movedFrom = true; }
void operator()(T &el)
{
if (!movedFrom)
el.multiplyByTwo();
}
};
template<typename From, typename To>
void myReduce(To &sum, const From &val)
{
sum.value += val.value;
}
template<typename From, typename To>
class MyReduce
{
public:
void operator()(To &sum, const From &val) { sum.value += val.value; }
};
template<typename From, typename To>
class MyMoveOnlyReduce
{
bool movedFrom = false;
public:
MyMoveOnlyReduce() = default;
MyMoveOnlyReduce(const MyMoveOnlyReduce<From, To> &) = delete;
MyMoveOnlyReduce &operator=(const MyMoveOnlyReduce<From, To> &) = delete;
MyMoveOnlyReduce(MyMoveOnlyReduce<From, To> &&other) { other.movedFrom = true; }
MyMoveOnlyReduce &operator=(MyMoveOnlyReduce<From, To> &&other) { other.movedFrom = true; }
void operator()(To &sum, const From &val)
{
if (!movedFrom)
sum.value += val.value;
}
};
QT_BEGIN_NAMESPACE
// pretty printing
template<typename tag>
char *toString(const SequenceItem<tag> &i)
{
using QTest::toString;
return toString(QString::number(i.value));
}
// pretty printing
template<typename T>
char *toString(const std::vector<T> &vec)
{
using QTest::toString;
QString result("");
for (const auto &i : vec) {
result.append(QString::number(i.value) + ", ");
}
if (result.size())
result.chop(2);
return toString(result);
}
QT_END_NAMESPACE
#endif // QTBASE_GENERATION_HELPERS_H

View File

@ -0,0 +1,163 @@
#############################################################################
#
# Copyright (C) 2020 The Qt Company Ltd.
# Contact: https://www.qt.io/licensing/
#
# This file is part of the test suite of the Qt Toolkit.
#
# $QT_BEGIN_LICENSE:GPL-EXCEPT$
# Commercial License Usage
# Licensees holding valid commercial Qt licenses may use this file in
# accordance with the commercial license agreement provided with the
# Software or, alternatively, in accordance with the terms contained in
# a written agreement between you and The Qt Company. For licensing terms
# and conditions see https://www.qt.io/terms-conditions. For further
# information use the contact form at https://www.qt.io/contact-us.
#
# GNU General Public License Usage
# Alternatively, this file may be used under the terms of the GNU
# General Public License version 3 as published by the Free Software
# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
# included in the packaging of this file. Please review the following
# information to ensure the GNU General Public License requirements will
# be met: https://www.gnu.org/licenses/gpl-3.0.html.
#
# $QT_END_LICENSE$
#
#############################################################################
from option_management import need_separate_output_sequence
# ["hallo", "welt"] -> "halloWelt"
def build_camel_case(*args):
assert all(isinstance(x, str) for x in args)
if len(args) == 0:
return ""
if len(args) == 1:
return args[0]
return args[0] + "".join(x.capitalize() for x in args[1:])
def build_function_name(options):
result = []
for part in ["blocking", "filter", "map", "reduce"]:
if options[part]:
result.append(part)
def make_potentially_passive(verb):
if verb == "map":
return "mapped"
if verb == "blocking":
return "blocking"
if verb[-1] == "e":
verb += "d"
else:
verb += "ed"
return verb
if not options["inplace"]:
result = [make_potentially_passive(x) for x in result]
result = build_camel_case(*result)
return result
def build_blocking_return_type(options):
if options["inplace"]:
if options["filter"] and options["iterators"] and not options["reduce"]:
return "Iterator" # have to mark new end
else:
return "void"
else:
if options["reduce"]:
return "ResultType"
if need_separate_output_sequence(options):
return "OutputSequence"
else:
return "Sequence"
def build_return_type(options):
if options["blocking"]:
return build_blocking_return_type(options)
else:
return f"QFuture<{build_blocking_return_type(options)}>"
def build_template_argument_list(options):
required_types = []
if options["reduce"]:
required_types.append("typename ResultType")
need_output_sequence = need_separate_output_sequence(options)
if need_output_sequence:
required_types.append("OutputSequence")
if options["iterators"]:
required_types.append("Iterator")
else:
if need_output_sequence:
required_types.append("InputSequence")
else:
required_types.append("Sequence")
# Functors
if options["filter"]:
required_types.append("KeepFunctor")
if options["map"]:
required_types.append("MapFunctor")
if options["reduce"]:
required_types.append("ReduceFunctor")
if options["initialvalue"]:
required_types.append("reductionitemtype")
return "template<" + ", ".join(["typename "+x for x in required_types]) + ">"
def build_argument_list(options):
required_arguments = []
if options["pool"]:
required_arguments.append("QThreadPool* pool")
if options["iterators"]:
required_arguments.append("Iterator begin")
required_arguments.append("Iterator end")
else:
if options["inplace"]:
required_arguments.append("Sequence & sequence")
else:
if need_separate_output_sequence(options):
required_arguments.append("InputSequence && sequence")
else:
required_arguments.append("const Sequence & sequence")
if options["filter"]:
required_arguments.append("KeepFunctor filterFunction")
if options["map"]:
required_arguments.append("MapFunctor function")
if options["reduce"]:
required_arguments.append("ReduceFunctor reduceFunction")
if options["initialvalue"]:
required_arguments.append("reductionitemtype && initialValue")
required_arguments.append("ReduceOptions")
return "(" + ", ".join(required_arguments) + ")"
def build_function_signature(options):
return (build_template_argument_list(options) + "\n" +
build_return_type(
options) + " " + build_function_name(options) + build_argument_list(options)
+ ";"
)

View File

@ -0,0 +1,71 @@
#############################################################################
#
# Copyright (C) 2020 The Qt Company Ltd.
# Contact: https://www.qt.io/licensing/
#
# This file is part of the test suite of the Qt Toolkit.
#
# $QT_BEGIN_LICENSE:GPL-EXCEPT$
# Commercial License Usage
# Licensees holding valid commercial Qt licenses may use this file in
# accordance with the commercial license agreement provided with the
# Software or, alternatively, in accordance with the terms contained in
# a written agreement between you and The Qt Company. For licensing terms
# and conditions see https://www.qt.io/terms-conditions. For further
# information use the contact form at https://www.qt.io/contact-us.
#
# GNU General Public License Usage
# Alternatively, this file may be used under the terms of the GNU
# General Public License version 3 as published by the Free Software
# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
# included in the packaging of this file. Please review the following
# information to ensure the GNU General Public License requirements will
# be met: https://www.gnu.org/licenses/gpl-3.0.html.
#
# $QT_END_LICENSE$
#
#############################################################################
import pandas as pd
from option_management import function_describing_options
from function_signature import build_function_signature
def generate_excel_file_of_functions(filename):
olist = []
for options in function_describing_options():
# filter out unneccesary cases:
if options["reduce"] and options["inplace"]:
# we cannot do a reduction in-place
options["comment"] = "reduce-inplace:nonsense"
options["signature"] = ""
elif options["initialvalue"] and not options["reduce"]:
options["comment"] = "initial-noreduce:nonsense"
options["signature"] = ""
elif not options["reduce"] and not options["map"] and not options["filter"]:
# no operation at all
options["comment"] = "noop"
options["signature"] = ""
else:
options["comment"] = ""
if options["map"] and options["filter"]:
options["implemented"] = "no:filter+map"
elif not options["map"] and not options["filter"]:
options["implemented"] = "no:nofilter+nomap"
elif options["inplace"] and options["iterators"] and options["filter"]:
options["implemented"] = "no:inplace+iterator+filter"
else:
options["implemented"] = "yes"
options["signature"] = build_function_signature(options)
olist.append(options)
df = pd.DataFrame(olist)
df.to_excel(filename)
generate_excel_file_of_functions("functions.xls")

View File

@ -0,0 +1,181 @@
#############################################################################
#
# Copyright (C) 2020 The Qt Company Ltd.
# Contact: https://www.qt.io/licensing/
#
# This file is part of the test suite of the Qt Toolkit.
#
# $QT_BEGIN_LICENSE:GPL-EXCEPT$
# Commercial License Usage
# Licensees holding valid commercial Qt licenses may use this file in
# accordance with the commercial license agreement provided with the
# Software or, alternatively, in accordance with the terms contained in
# a written agreement between you and The Qt Company. For licensing terms
# and conditions see https://www.qt.io/terms-conditions. For further
# information use the contact form at https://www.qt.io/contact-us.
#
# GNU General Public License Usage
# Alternatively, this file may be used under the terms of the GNU
# General Public License version 3 as published by the Free Software
# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
# included in the packaging of this file. Please review the following
# information to ensure the GNU General Public License requirements will
# be met: https://www.gnu.org/licenses/gpl-3.0.html.
#
# $QT_END_LICENSE$
#
#############################################################################
import importlib
import sys
import PySide2
from PySide2.QtCore import Signal
from PySide2.QtWidgets import QVBoxLayout, QRadioButton, QGroupBox, QWidget, QApplication, QPlainTextEdit, QHBoxLayout
import generate_testcase
from helpers import insert_testcases_into_file
from option_management import (Option, OptionManager, testcase_describing_options, function_describing_options,
skip_function_description, disabled_testcase_describing_options,
skip_testcase_description)
class MyRadioButton(QRadioButton):
def __init__(self, value):
super(MyRadioButton, self).__init__(text=str(value))
self.value = value
self.toggled.connect(lambda x: x and self.activated.emit(self.value))
activated = Signal(object)
class OptionSelector(QGroupBox):
def __init__(self, parent: QWidget, option: Option):
super(OptionSelector, self).__init__(title=option.name, parent=parent)
self.layout = QVBoxLayout()
self.setLayout(self.layout)
self.radiobuttons = []
for val in option.possible_options:
rb = MyRadioButton(val)
self.layout.addWidget(rb)
rb.activated.connect(lambda value: self.valueSelected.emit(option.name, value))
self.radiobuttons.append(rb)
self.radiobuttons[0].setChecked(True)
valueSelected = Signal(str, object)
class OptionsSelector(QGroupBox):
def __init__(self, parent: QWidget, option_manager: OptionManager):
super(OptionsSelector, self).__init__(title=option_manager.name, parent=parent)
self.vlayout = QVBoxLayout()
self.setLayout(self.vlayout)
self.layout1 = QHBoxLayout()
self.layout2 = QHBoxLayout()
self.layout3 = QHBoxLayout()
self.vlayout.addLayout(self.layout1)
self.vlayout.addLayout(self.layout2)
self.vlayout.addLayout(self.layout3)
self.disabledOptions = []
self.selectors = {}
for option in option_manager.options.values():
os = OptionSelector(parent=self, option=option)
if "type" in option.name:
self.layout2.addWidget(os)
elif "passing" in option.name:
self.layout3.addWidget(os)
else:
self.layout1.addWidget(os)
os.valueSelected.connect(self._handle_slection)
self.selectors[option.name] = os
self.selectedOptionsDict = {option.name: option.possible_options[0] for option in
option_manager.options.values()}
def get_current_option_set(self):
return {k: v for k, v in self.selectedOptionsDict.items() if k not in self.disabledOptions}
def _handle_slection(self, name: str, value: object):
self.selectedOptionsDict[name] = value
self.optionsSelected.emit(self.get_current_option_set())
def set_disabled_options(self, options):
self.disabledOptions = options
for name, selector in self.selectors.items():
if name in self.disabledOptions:
selector.setEnabled(False)
else:
selector.setEnabled(True)
optionsSelected = Signal(dict)
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.layout = QVBoxLayout()
self.setLayout(self.layout)
self.functionSelector = OptionsSelector(parent=self, option_manager=function_describing_options())
self.layout.addWidget(self.functionSelector)
self.testcaseSelector = OptionsSelector(parent=self, option_manager=testcase_describing_options())
self.layout.addWidget(self.testcaseSelector)
self.plainTextEdit = QPlainTextEdit()
self.plainTextEdit.setReadOnly(True)
self.layout.addWidget(self.plainTextEdit)
self.plainTextEdit.setFont(PySide2.QtGui.QFont("Fira Code", 8))
# temp
self.functionSelector.optionsSelected.connect(lambda o: self._handle_function_change())
self.testcaseSelector.optionsSelected.connect(lambda o: self._handle_testcase_change())
self._handle_function_change()
def _handle_function_change(self):
options = self.functionSelector.get_current_option_set()
if m := skip_function_description(options):
self.plainTextEdit.setPlainText(m)
return
options_to_disable = disabled_testcase_describing_options(options)
self.testcaseSelector.set_disabled_options(options_to_disable)
options.update(self.testcaseSelector.get_current_option_set())
if m := skip_testcase_description(options):
self.plainTextEdit.setPlainText(m)
return
self._generate_new_testcase()
def _handle_testcase_change(self):
options = self.functionSelector.get_current_option_set()
options.update(self.testcaseSelector.get_current_option_set())
if m := skip_testcase_description(options):
self.plainTextEdit.setPlainText(m)
return
self._generate_new_testcase()
def _generate_new_testcase(self):
foptions = self.functionSelector.get_current_option_set()
toptions = self.testcaseSelector.get_current_option_set()
importlib.reload(generate_testcase)
testcase = generate_testcase.generate_testcase(foptions, toptions)
self.plainTextEdit.setPlainText(testcase[1])
filename = "../tst_qtconcurrentfiltermapgenerated.cpp"
insert_testcases_into_file(filename, [testcase])
filename = "../tst_qtconcurrentfiltermapgenerated.h"
insert_testcases_into_file(filename, [testcase])
if __name__ == "__main__":
app = QApplication(sys.argv)
m = MainWindow()
m.show()
app.exec_()

View File

@ -0,0 +1,391 @@
#############################################################################
#
# Copyright (C) 2020 The Qt Company Ltd.
# Contact: https://www.qt.io/licensing/
#
# This file is part of the test suite of the Qt Toolkit.
#
# $QT_BEGIN_LICENSE:GPL-EXCEPT$
# Commercial License Usage
# Licensees holding valid commercial Qt licenses may use this file in
# accordance with the commercial license agreement provided with the
# Software or, alternatively, in accordance with the terms contained in
# a written agreement between you and The Qt Company. For licensing terms
# and conditions see https://www.qt.io/terms-conditions. For further
# information use the contact form at https://www.qt.io/contact-us.
#
# GNU General Public License Usage
# Alternatively, this file may be used under the terms of the GNU
# General Public License version 3 as published by the Free Software
# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
# included in the packaging of this file. Please review the following
# information to ensure the GNU General Public License requirements will
# be met: https://www.gnu.org/licenses/gpl-3.0.html.
#
# $QT_END_LICENSE$
#
#############################################################################
import textwrap
import time
from subprocess import Popen, PIPE
from function_signature import build_function_signature, build_function_name
from option_management import need_separate_output_sequence, qt_quirk_case
def InputSequenceItem(toptions):
if toptions["inputitemtype"] == "standard":
return "SequenceItem<tag_input>"
if toptions["inputitemtype"] == "noconstruct":
return "NoConstructSequenceItem<tag_input>"
if toptions["inputitemtype"] == "moveonly":
return "MoveOnlySequenceItem<tag_input>"
if toptions["inputitemtype"] == "moveonlynoconstruct":
return "MoveOnlyNoConstructSequenceItem<tag_input>"
assert False
def InputSequence(toptions):
item = InputSequenceItem(toptions)
if toptions["inputsequence"] == "standard":
return "std::vector<{}>".format(item)
if toptions["inputsequence"] == "moveonly":
return "MoveOnlyVector<{}>".format(item)
assert False
def InputSequenceInitializationString(toptions):
t = InputSequenceItem(toptions)
# construct IILE
mystring = ("[](){" + InputSequence(toptions) + " result;\n"
+ "\n".join("result.push_back({}({}, true));".format(t, i) for i in range(1, 7))
+ "\n return result; }()")
return mystring
def OutputSequenceItem(toptions):
if toptions["map"] and (toptions["inplace"] or toptions["maptype"] == "same"):
return InputSequenceItem(toptions)
if toptions["map"]:
if toptions["mappeditemtype"] == "standard":
return "SequenceItem<tag_mapped>"
if toptions["mappeditemtype"] == "noconstruct":
return "NoConstructSequenceItem<tag_mapped>"
if toptions["mappeditemtype"] == "moveonly":
return "MoveOnlySequenceItem<tag_mapped>"
if toptions["mappeditemtype"] == "moveonlynoconstruct":
return "MoveOnlyNoConstructSequenceItem<tag_mapped>"
assert(False)
else:
return InputSequenceItem(toptions)
def ReducedItem(toptions):
if toptions["reductiontype"] == "same":
return OutputSequenceItem(toptions)
else:
if toptions["reductionitemtype"] == "standard":
return "SequenceItem<tag_reduction>"
if toptions["reductionitemtype"] == "noconstruct":
return "NoConstructSequenceItem<tag_reduction>"
if toptions["reductionitemtype"] == "moveonly":
return "MoveOnlySequenceItem<tag_reduction>"
if toptions["reductionitemtype"] == "moveonlynoconstruct":
return "MoveOnlyNoConstructSequenceItem<tag_reduction>"
assert(False)
def OutputSequence(toptions):
item = OutputSequenceItem(toptions)
# quirk of qt: not a QFuture<Sequence> is returned
if qt_quirk_case(toptions):
return "QList<{}>".format(item)
needs_extra = need_separate_output_sequence(toptions)
if not needs_extra:
return InputSequence(toptions)
if toptions["outputsequence"] == "standard":
return "std::vector<{}>".format(item)
if toptions["outputsequence"] == "moveonly":
return "MoveOnlyVector<{}>".format(item)
assert False
def resultData(toptions):
result = [1, 2, 3, 4, 5, 6]
if toptions["filter"]:
result = filter(lambda x: x % 2 == 1, result)
if toptions["map"]:
result = map(lambda x: 2 * x, result)
if toptions["reduce"]:
result = sum(result)
return result
def OutputSequenceInitializationString(toptions):
t = OutputSequenceItem(toptions)
# construct IILE
mystring = ("[](){" + OutputSequence(toptions) + " result;\n"
+ "\n".join("result.push_back({}({}, true));".format(t, i) for i in resultData(toptions))
+ "\n return result; }()")
return mystring
def OutputScalarInitializationString(toptions):
result = resultData(toptions)
assert isinstance(result, int)
return ReducedItem(toptions) + "(" + str(result) + ", true)"
def FilterInitializationString(toptions):
item = InputSequenceItem(toptions)
if toptions["filterfunction"] == "function":
return "myfilter<{}>".format(item)
if toptions["filterfunction"] == "functor":
return "MyFilter<{}>{{}}".format(item)
if toptions["filterfunction"] == "memberfunction":
return "&{}::isOdd".format(item)
if toptions["filterfunction"] == "lambda":
return "[](const {}& x){{ return myfilter<{}>(x); }}".format(item, item)
if toptions["filterfunction"] == "moveonlyfunctor":
return "MyMoveOnlyFilter<{}>{{}}".format(item)
assert False
def MapInitializationString(toptions):
oldtype = InputSequenceItem(toptions)
newtype = OutputSequenceItem(toptions)
if toptions["inplace"]:
assert oldtype == newtype
if toptions["mapfunction"] == "function":
return "myInplaceMap<{}>".format(oldtype)
if toptions["mapfunction"] == "functor":
return "MyInplaceMap<{}>{{}}".format(oldtype)
if toptions["mapfunction"] == "memberfunction":
return "&{}::multiplyByTwo".format(oldtype)
if toptions["mapfunction"] == "lambda":
return "[]({}& x){{ return myInplaceMap<{}>(x); }}".format(oldtype, oldtype)
if toptions["mapfunction"] == "moveonlyfunctor":
return "MyMoveOnlyInplaceMap<{}>{{}}".format(oldtype)
assert False
else:
if toptions["mapfunction"] == "function":
return "myMap<{f},{t}>".format(f=oldtype, t=newtype)
if toptions["mapfunction"] == "functor":
return "MyMap<{f},{t}>{{}}".format(f=oldtype, t=newtype)
if toptions["mapfunction"] == "memberfunction":
return "&{}::multiplyByTwo".format(newtype)
if toptions["mapfunction"] == "lambda":
return "[](const {f}& x){{ return myMap<{f},{t}>(x); }}".format(f=oldtype, t=newtype)
if toptions["mapfunction"] == "moveonlyfunctor":
return "MyMoveOnlyMap<{f},{t}>{{}}".format(f=oldtype, t=newtype)
assert False
def ReductionInitializationString(toptions):
elementtype = OutputSequenceItem(toptions)
sumtype = ReducedItem(toptions)
if toptions["reductionfunction"] == "function":
return "myReduce<{f},{t}>".format(f=elementtype, t=sumtype)
if toptions["reductionfunction"] == "functor":
return "MyReduce<{f},{t}>{{}}".format(f=elementtype, t=sumtype)
if toptions["reductionfunction"] == "lambda":
return "[]({t}& sum, const {f}& x){{ return myReduce<{f},{t}>(sum, x); }}".format(f=elementtype, t=sumtype)
if toptions["reductionfunction"] == "moveonlyfunctor":
return "MyMoveOnlyReduce<{f},{t}>{{}}".format(f=elementtype, t=sumtype)
assert False
def ReductionInitialvalueInitializationString(options):
return ReducedItem(options) + "(0, true)"
def function_template_args(options):
args = []
out_s = OutputSequence(options)
in_s = InputSequence(options)
if options["reduce"] and options["reductionfunction"] == "lambda":
args.append(ReducedItem(options))
elif out_s != in_s:
if not qt_quirk_case(options):
args.append(out_s)
if len(args):
return "<" + ", ".join(args) + ">"
return ""
numcall = 0
def generate_testcase(function_options, testcase_options):
options = {**function_options, **testcase_options}
option_description = "\n".join(" {}={}".format(
a, b) for a, b in testcase_options.items())
option_description = textwrap.indent(option_description, " "*12)
function_signature = textwrap.indent(build_function_signature(function_options), " "*12)
testcase_name = "_".join("{}_{}".format(x, y) for x, y in options.items())
global numcall
numcall += 1
testcase_name = "test" + str(numcall)
function_name = build_function_name(options)
arguments = []
template_args = function_template_args(options)
# care about the thread pool
if options["pool"]:
pool_initialization = """QThreadPool pool;
pool.setMaxThreadCount(1);"""
arguments.append("&pool")
else:
pool_initialization = ""
# care about the input sequence
input_sequence_initialization_string = InputSequenceInitializationString(options)
if "inputsequencepassing" in options and options["inputsequencepassing"] == "lvalue" or options["inplace"]:
input_sequence_initialization = "auto input_sequence = " + \
input_sequence_initialization_string + ";"
arguments.append("input_sequence")
elif "inputsequencepassing" in options and options["inputsequencepassing"] == "rvalue":
input_sequence_initialization = ""
arguments.append(input_sequence_initialization_string)
else:
input_sequence_initialization = "auto input_sequence = " + \
input_sequence_initialization_string + ";"
arguments.append("input_sequence.begin()")
arguments.append("input_sequence.end()")
# care about the map:
if options["map"]:
map_initialization_string = MapInitializationString(options)
if options["mapfunctionpassing"] == "lvalue":
map_initialization = "auto map = " + map_initialization_string + ";"
arguments.append("map")
elif options["mapfunctionpassing"] == "rvalue":
map_initialization = ""
arguments.append(map_initialization_string)
else:
assert False
else:
map_initialization = ""
# care about the filter
if options["filter"]:
filter_initialization_string = FilterInitializationString(options)
if options["filterfunctionpassing"] == "lvalue":
filter_initialization = "auto filter = " + filter_initialization_string + ";"
arguments.append("filter")
elif options["filterfunctionpassing"] == "rvalue":
filter_initialization = ""
arguments.append(filter_initialization_string)
else:
assert (False)
else:
filter_initialization = ""
reduction_initialvalue_initialization = ""
# care about reduction
if options["reduce"]:
reduction_initialization_expression = ReductionInitializationString(options)
if options["reductionfunctionpassing"] == "lvalue":
reduction_initialization = "auto reductor = " + reduction_initialization_expression + ";"
arguments.append("reductor")
elif options["reductionfunctionpassing"] == "rvalue":
reduction_initialization = ""
arguments.append(reduction_initialization_expression)
else:
assert (False)
# initialvalue:
if options["initialvalue"]:
reduction_initialvalue_initialization_expression = ReductionInitialvalueInitializationString(options)
if options["reductioninitialvaluepassing"] == "lvalue":
reduction_initialvalue_initialization = "auto initialvalue = " + reduction_initialvalue_initialization_expression + ";"
arguments.append("initialvalue")
elif options["reductioninitialvaluepassing"] == "rvalue":
reduction_initialvalue_initialization = ""
arguments.append(reduction_initialvalue_initialization_expression)
else:
assert (False)
if options["reductionoptions"] == "UnorderedReduce":
arguments.append("QtConcurrent::UnorderedReduce")
elif options["reductionoptions"] == "OrderedReduce":
arguments.append("QtConcurrent::OrderedReduce")
elif options["reductionoptions"] == "SequentialReduce":
arguments.append("QtConcurrent::SequentialReduce")
else:
assert options["reductionoptions"] == "unspecified"
else:
reduction_initialization = ""
# what is the expected result
if options["filter"]:
if not options["reduce"]:
expected_result_expression = OutputSequenceInitializationString(options)
else:
expected_result_expression = OutputScalarInitializationString(options)
elif options["map"]:
if not options["reduce"]:
expected_result_expression = OutputSequenceInitializationString(options)
else:
expected_result_expression = OutputScalarInitializationString(options)
wait_result_expression = ""
if options["inplace"]:
if options["blocking"]:
result_accepting = ""
result_variable = "input_sequence"
else:
result_accepting = "auto future = "
result_variable = "input_sequence"
wait_result_expression = "future.waitForFinished();"
elif options["blocking"]:
result_accepting = "auto result = "
result_variable = "result"
else:
if not options["reduce"]:
result_accepting = "auto result = "
result_variable = "result.results()"
else:
result_accepting = "auto result = "
result_variable = "result.takeResult()"
arguments_passing = ", ".join(arguments)
final_string = f"""
void tst_QtConcurrentFilterMapGenerated::{testcase_name}()
{{
/* test for
{function_signature}
with
{option_description}
*/
{pool_initialization}
{input_sequence_initialization}
{filter_initialization}
{map_initialization}
{reduction_initialization}
{reduction_initialvalue_initialization}
{result_accepting}QtConcurrent::{function_name}{template_args}({arguments_passing});
auto expected_result = {expected_result_expression};
{wait_result_expression}
QCOMPARE({result_variable}, expected_result);
}}
"""
p = Popen(["clang-format"], stdin=PIPE, stdout=PIPE, stderr=PIPE)
final_string = p.communicate(final_string.encode())[0].decode()
return (f" void {testcase_name}();\n", final_string)

View File

@ -0,0 +1,58 @@
#############################################################################
#
# Copyright (C) 2020 The Qt Company Ltd.
# Contact: https://www.qt.io/licensing/
#
# This file is part of the test suite of the Qt Toolkit.
#
# $QT_BEGIN_LICENSE:GPL-EXCEPT$
# Commercial License Usage
# Licensees holding valid commercial Qt licenses may use this file in
# accordance with the commercial license agreement provided with the
# Software or, alternatively, in accordance with the terms contained in
# a written agreement between you and The Qt Company. For licensing terms
# and conditions see https://www.qt.io/terms-conditions. For further
# information use the contact form at https://www.qt.io/contact-us.
#
# GNU General Public License Usage
# Alternatively, this file may be used under the terms of the GNU
# General Public License version 3 as published by the Free Software
# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
# included in the packaging of this file. Please review the following
# information to ensure the GNU General Public License requirements will
# be met: https://www.gnu.org/licenses/gpl-3.0.html.
#
# $QT_END_LICENSE$
#
#############################################################################
from option_management import function_describing_options, skip_function_description, testcase_describing_options
from generate_testcase import generate_testcase
from helpers import insert_testcases_into_file
filename = "../tst_qtconcurrentfiltermapgenerated.cpp"
testcases = []
counter = 0
for fo in function_describing_options():
if skip_function_description(fo):
continue
if not (
fo["blocking"]
and fo["filter"]
# and not fo["map"]
and fo["reduce"]
and not fo["inplace"]
and not fo["iterators"]
and not fo["initialvalue"]
and not fo["pool"]
):
continue
for to in testcase_describing_options(fo):
print("generate test")
testcases.append(generate_testcase(fo, to))
counter += 1
print(counter)
insert_testcases_into_file(filename, testcases)

View File

@ -0,0 +1,56 @@
#############################################################################
#
# Copyright (C) 2020 The Qt Company Ltd.
# Contact: https://www.qt.io/licensing/
#
# This file is part of the test suite of the Qt Toolkit.
#
# $QT_BEGIN_LICENSE:GPL-EXCEPT$
# Commercial License Usage
# Licensees holding valid commercial Qt licenses may use this file in
# accordance with the commercial license agreement provided with the
# Software or, alternatively, in accordance with the terms contained in
# a written agreement between you and The Qt Company. For licensing terms
# and conditions see https://www.qt.io/terms-conditions. For further
# information use the contact form at https://www.qt.io/contact-us.
#
# GNU General Public License Usage
# Alternatively, this file may be used under the terms of the GNU
# General Public License version 3 as published by the Free Software
# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
# included in the packaging of this file. Please review the following
# information to ensure the GNU General Public License requirements will
# be met: https://www.gnu.org/licenses/gpl-3.0.html.
#
# $QT_END_LICENSE$
#
#############################################################################
def insert_testcases_into_file(filename, testcases):
# assume testcases is an array of tuples of (declaration, definition)
with open(filename) as f:
inputlines = f.readlines()
outputlines = []
skipping = False
for line in inputlines:
if not skipping:
outputlines.append(line)
else:
if "END_GENERATED" in line:
outputlines.append(line)
skipping = False
if "START_GENERATED_SLOTS" in line:
# put in testcases
outputlines += [t[0] for t in testcases]
skipping = True
if "START_GENERATED_IMPLEMENTATIONS" in line:
# put in testcases
outputlines += [t[1] for t in testcases]
skipping = True
if outputlines != inputlines:
with open(filename, "w") as f:
f.writelines(outputlines)

View File

@ -0,0 +1,220 @@
#############################################################################
#
# Copyright (C) 2020 The Qt Company Ltd.
# Contact: https://www.qt.io/licensing/
#
# This file is part of the test suite of the Qt Toolkit.
#
# $QT_BEGIN_LICENSE:GPL-EXCEPT$
# Commercial License Usage
# Licensees holding valid commercial Qt licenses may use this file in
# accordance with the commercial license agreement provided with the
# Software or, alternatively, in accordance with the terms contained in
# a written agreement between you and The Qt Company. For licensing terms
# and conditions see https://www.qt.io/terms-conditions. For further
# information use the contact form at https://www.qt.io/contact-us.
#
# GNU General Public License Usage
# Alternatively, this file may be used under the terms of the GNU
# General Public License version 3 as published by the Free Software
# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
# included in the packaging of this file. Please review the following
# information to ensure the GNU General Public License requirements will
# be met: https://www.gnu.org/licenses/gpl-3.0.html.
#
# $QT_END_LICENSE$
#
#############################################################################
import itertools
class Option:
def __init__(self, name, possible_options):
self.name = name
self.possible_options = possible_options
class OptionManager:
def __init__(self, name):
self.options = {}
self.name = name
def add_option(self, option: Option):
self.options[option.name] = option
def iterate(self):
for x in itertools.product(*[
[(name, x) for x in self.options[name]]
for name in self.options.keys()
]):
yield dict(x)
def function_describing_options():
om = OptionManager("function options")
om.add_option(Option("blocking", [True, False]))
om.add_option(Option("filter", [True, False]))
om.add_option(Option("map", [False, True]))
om.add_option(Option("reduce", [False, True]))
om.add_option(Option("inplace", [True, False]))
om.add_option(Option("iterators", [False, True]))
om.add_option(Option("initialvalue", [False, True]))
om.add_option(Option("pool", [True, False]))
return om
def skip_function_description(options):
if options["reduce"] and options["inplace"]:
return "we cannot do a reduction in-place"
if options["initialvalue"] and not options["reduce"]:
return "without reduction, we do not need an initial value"
if not options["reduce"] and not options["map"] and not options["filter"]:
return "no operation at all"
# the following things are skipped because Qt does not support them
if options["filter"] and options["map"]:
return "Not supported by Qt: both map and filter operation"
if not options["filter"] and not options["map"]:
return "Not supported by Qt: no map and no filter operation"
if options["inplace"] and options["iterators"] and options["filter"]:
return "Not supported by Qt: filter operation in-place with iterators"
return False
def qt_quirk_case(options):
# whenever a function should return a QFuture<Sequence>,
# it returns a QFuture<item> instead
if options["inplace"] or options["reduce"] or options["blocking"]:
return False
return True
def need_separate_output_sequence(options):
# do we need an output sequence?
if not (options["inplace"] or options["reduce"]):
# transforming a sequence into a sequence
if options["iterators"] or options["map"]:
return True
return False
def testcase_describing_options():
om = OptionManager("testcase options")
om.add_option(Option("inputsequence", ["standard", "moveonly"]))
om.add_option(Option("inputsequencepassing", ["lvalue", "rvalue"]))
om.add_option(Option("inputitemtype", ["standard", "noconstruct", "moveonly", "moveonlynoconstruct"]))
om.add_option(Option("outputsequence", ["standard", "moveonly"]))
om.add_option(Option("maptype", ["same", "different"]))
om.add_option(Option("mappeditemtype", ["standard", "noconstruct", "moveonly", "moveonlynoconstruct"]))
om.add_option(Option("reductiontype", ["same", "different"]))
om.add_option(Option("reductionitemtype", [
"standard", "noconstruct", "moveonly", "moveonlynoconstruct"]))
om.add_option(Option("filterfunction", ["functor", "function", "memberfunction", "lambda", "moveonlyfunctor"]))
om.add_option(Option("filterfunctionpassing", ["lvalue", "rvalue"]))
om.add_option(Option("mapfunction", ["functor", "function", "memberfunction", "lambda", "moveonlyfunctor"]))
om.add_option(Option("mapfunctionpassing", ["lvalue", "rvalue"]))
om.add_option(Option("reductionfunction", ["functor", "function", "lambda", "moveonlyfunctor"]))
om.add_option(Option("reductionfunctionpassing", ["lvalue", "rvalue"]))
om.add_option(Option("reductioninitialvaluepassing", ["lvalue", "rvalue"]))
om.add_option(Option("reductionoptions", [
"unspecified", "UnorderedReduce", "OrderedReduce", "SequentialReduce"]))
return om
def disabled_testcase_describing_options(options):
disabled_options = []
if options["inplace"] or options["iterators"]:
disabled_options.append("inputsequencepassing")
if not need_separate_output_sequence(options):
disabled_options.append("outputsequence")
if not options["map"]:
disabled_options.append("mappeditemtype")
if options["map"] and options["inplace"]:
disabled_options.append("mappeditemtype")
if not options["filter"]:
disabled_options.append("filterfunction")
disabled_options.append("filterfunctionpassing")
if not options["map"]:
disabled_options.append("mapfunction")
disabled_options.append("mapfunctionpassing")
if not options["reduce"]:
disabled_options.append("reductionfunction")
disabled_options.append("reductionfunctionpassing")
if not options["reduce"]:
disabled_options.append("reductiontype")
disabled_options.append("reductioninitialvaluepassing")
disabled_options.append("reductionoptions")
disabled_options.append("reductionitemtype")
if not options["initialvalue"]:
disabled_options.append("reductioninitialvaluepassing")
if not options["map"]:
disabled_options.append("maptype")
else:
if options["inplace"]:
disabled_options.append("maptype")
return disabled_options
def skip_testcase_description(options):
if (
"maptype" in options and
options["maptype"] == "same" and
"inputitemtype" in options and "mappeditemtype" in options and
(options["inputitemtype"] != options["mappeditemtype"])
):
return ("Not possible: map should map to same type, "
"but mapped item type should differ from input item type.")
if (
"reductiontype" in options and
options["reductiontype"] == "same"):
# we have to check that this is possible
if ("mappeditemtype" in options and "reductionitemtype" in options
and (options["mappeditemtype"] != options["reductionitemtype"])
):
return ("Not possible: should reduce in the same type, "
"but reduction item type should differ from mapped item type.")
if ("mappeditemtype" not in options
and (options["inputitemtype"] != options["reductionitemtype"])):
return ("Not possible: should reduce in the same type, "
"but reduction item type should differ from input item type.")
if (
options["map"] and not options["inplace"]
and options["mapfunction"] == "memberfunction"
):
return "map with memberfunction only available for in-place map"
return False

View File

@ -0,0 +1,5 @@
CONFIG += testcase
TARGET = tst_qtconcurrentfiltermapgenerated
QT = core testlib concurrent
SOURCES = tst_qtconcurrentfiltermapgenerated.cpp tst_qtconcurrent_selected_tests.cpp
HEADERS = tst_qtconcurrentfiltermapgenerated.h

View File

@ -0,0 +1,296 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#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);*/
}

View File

@ -0,0 +1,84 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <qtconcurrentfilter.h>
#include <qtconcurrentmap.h>
#include <QCoreApplication>
#include <QList>
#include <QtTest/QtTest>
#include "../testhelper_functions.h"
#include "generation_helpers.h"
#include "tst_qtconcurrentfiltermapgenerated.h"
using namespace QtConcurrent;
// START_GENERATED_IMPLEMENTATIONS (see generate_tests.py)
void tst_QtConcurrentFilterMapGenerated::test1()
{
/* test for
template<typename Sequence, typename KeepFunctor>
void blockingFilter(QThreadPool* pool, Sequence & sequence, KeepFunctor filterFunction);
with
inputsequence=standard
inputitemtype=standard
filterfunction=functor
filterfunctionpassing=lvalue
*/
QThreadPool pool;
pool.setMaxThreadCount(1);
auto input_sequence = []() {
std::vector<SequenceItem<tag_input>> result;
result.push_back(SequenceItem<tag_input>(1, true));
result.push_back(SequenceItem<tag_input>(2, true));
result.push_back(SequenceItem<tag_input>(3, true));
result.push_back(SequenceItem<tag_input>(4, true));
result.push_back(SequenceItem<tag_input>(5, true));
result.push_back(SequenceItem<tag_input>(6, true));
return result;
}();
auto filter = MyFilter<SequenceItem<tag_input>> {};
QtConcurrent::blockingFilter(&pool, input_sequence, filter);
auto expected_result = []() {
std::vector<SequenceItem<tag_input>> result;
result.push_back(SequenceItem<tag_input>(1, true));
result.push_back(SequenceItem<tag_input>(3, true));
result.push_back(SequenceItem<tag_input>(5, true));
return result;
}();
QCOMPARE(input_sequence, expected_result);
}
// END_GENERATED_IMPLEMENTATION (see generate_tests.py)
QTEST_MAIN(tst_QtConcurrentFilterMapGenerated)

View File

@ -0,0 +1,50 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <qtconcurrentfilter.h>
#include <qtconcurrentmap.h>
#include <QtTest/QtTest>
#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();
// START_GENERATED_SLOTS (see generate_tests.py)
void test1();
// END_GENERATED_SLOTS (see generate_tests.py)
};

View File

@ -194,11 +194,11 @@ void tst_QtConcurrentMap::map()
// functors take arguments by reference, modifying the move-only sequence in place
{
MoveOnlyVector moveOnlyVector({ 1, 2, 3 });
MoveOnlyVector<int> moveOnlyVector({ 1, 2, 3 });
// functor
QtConcurrent::map(moveOnlyVector, MultiplyBy2InPlace()).waitForFinished();
QCOMPARE(moveOnlyVector, MoveOnlyVector({ 2, 4, 6 }));
QCOMPARE(moveOnlyVector, MoveOnlyVector<int>({ 2, 4, 6 }));
}
#if 0
@ -291,14 +291,14 @@ void tst_QtConcurrentMap::blockingMap()
// functors take arguments by reference, modifying the move-only sequence in place
{
MoveOnlyVector moveOnlyVector({ 1, 2, 3 });
MoveOnlyVector<int> moveOnlyVector({ 1, 2, 3 });
// functor
QtConcurrent::blockingMap(moveOnlyVector, MultiplyBy2InPlace());
QCOMPARE(moveOnlyVector, MoveOnlyVector({ 2, 4, 6 }));
QCOMPARE(moveOnlyVector, MoveOnlyVector<int>({ 2, 4, 6 }));
QtConcurrent::blockingMap(moveOnlyVector.begin(), moveOnlyVector.end(),
MultiplyBy2InPlace());
QCOMPARE(moveOnlyVector, MoveOnlyVector({ 4, 8, 12 }));
QCOMPARE(moveOnlyVector, MoveOnlyVector<int>({ 4, 8, 12 }));
}
// functors don't take arguments by reference, making these no-ops
@ -574,11 +574,11 @@ void tst_QtConcurrentMap::mapped()
{
// move only sequences
auto future = QtConcurrent::mapped(MoveOnlyVector({ 1, 2, 3 }), multiplyBy2);
auto future = QtConcurrent::mapped(MoveOnlyVector<int>({ 1, 2, 3 }), multiplyBy2);
QCOMPARE(future.results(), QList<int>({ 2, 4, 6 }));
auto result = QtConcurrent::blockingMapped<std::vector<int>>(MoveOnlyVector({ 1, 2, 3 }),
multiplyBy2);
auto result = QtConcurrent::blockingMapped<std::vector<int>>(
MoveOnlyVector<int>({ 1, 2, 3 }), multiplyBy2);
QCOMPARE(result, std::vector<int>({ 2, 4, 6 }));
}
}
@ -677,11 +677,11 @@ void tst_QtConcurrentMap::mappedThreadPool()
}
{
// move only sequences
auto future = QtConcurrent::mapped(&pool, MoveOnlyVector({ 1, 2, 3 }), multiplyBy2);
auto future = QtConcurrent::mapped(&pool, MoveOnlyVector<int>({ 1, 2, 3 }), multiplyBy2);
QCOMPARE(future.results(), QList<int>({ 2, 4, 6 }));
auto result = QtConcurrent::blockingMapped<std::vector<int>>(
&pool, MoveOnlyVector({ 1, 2, 3 }), multiplyBy2);
&pool, MoveOnlyVector<int>({ 1, 2, 3 }), multiplyBy2);
QCOMPARE(result, std::vector<int>({ 2, 4, 6 }));
}
}
@ -870,11 +870,11 @@ void tst_QtConcurrentMap::mappedReduced()
{
// move only sequences
auto future =
QtConcurrent::mappedReduced(MoveOnlyVector({ 1, 2, 3 }), intSquare, intSumReduce);
QtConcurrent::mappedReduced(MoveOnlyVector<int>({ 1, 2, 3 }), intSquare, intSumReduce);
QCOMPARE(future.result(), sumOfSquares);
auto result = QtConcurrent::blockingMappedReduced(MoveOnlyVector({ 1, 2, 3 }), intSquare,
intSumReduce);
auto result = QtConcurrent::blockingMappedReduced(MoveOnlyVector<int>({ 1, 2, 3 }),
intSquare, intSumReduce);
QCOMPARE(result, sumOfSquares);
}
}
@ -979,11 +979,11 @@ void tst_QtConcurrentMap::mappedReducedThreadPool()
{
// move only sequences
auto future = QtConcurrent::mappedReduced(&pool, MoveOnlyVector({ 1, 2, 3 }), intCube,
auto future = QtConcurrent::mappedReduced(&pool, MoveOnlyVector<int>({ 1, 2, 3 }), intCube,
intSumReduce);
QCOMPARE(future.result(), sumOfCubes);
auto result = QtConcurrent::blockingMappedReduced(&pool, MoveOnlyVector({ 1, 2, 3 }),
auto result = QtConcurrent::blockingMappedReduced(&pool, MoveOnlyVector<int>({ 1, 2, 3 }),
intCube, intSumReduce);
QCOMPARE(result, sumOfCubes);
}
@ -1216,12 +1216,12 @@ void tst_QtConcurrentMap::mappedReducedInitialValue()
{
// move only sequences
auto future = QtConcurrent::mappedReduced(MoveOnlyVector({ 1, 2, 3 }), intSquare,
auto future = QtConcurrent::mappedReduced(MoveOnlyVector<int>({ 1, 2, 3 }), intSquare,
intSumReduce, intInitial);
QCOMPARE(future.result(), sumOfSquares);
auto result = QtConcurrent::blockingMappedReduced(MoveOnlyVector({ 1, 2, 3 }), intSquare,
intSumReduce, intInitial);
auto result = QtConcurrent::blockingMappedReduced(MoveOnlyVector<int>({ 1, 2, 3 }),
intSquare, intSumReduce, intInitial);
QCOMPARE(result, sumOfSquares);
}
}
@ -1324,11 +1324,11 @@ void tst_QtConcurrentMap::mappedReducedInitialValueThreadPool()
{
// move only sequences
auto future = QtConcurrent::mappedReduced(&pool, MoveOnlyVector({ 1, 2, 3 }), intCube,
auto future = QtConcurrent::mappedReduced(&pool, MoveOnlyVector<int>({ 1, 2, 3 }), intCube,
intSumReduce, intInitial);
QCOMPARE(future.result(), sumOfCubes);
auto result = QtConcurrent::blockingMappedReduced(&pool, MoveOnlyVector({ 1, 2, 3 }),
auto result = QtConcurrent::blockingMappedReduced(&pool, MoveOnlyVector<int>({ 1, 2, 3 }),
intCube, intSumReduce, intInitial);
QCOMPARE(result, sumOfCubes);
}

View File

@ -161,34 +161,39 @@ public:
}
};
template<typename T>
class MoveOnlyVector
{
public:
using value_type = int;
using value_type = T;
// rule of six
MoveOnlyVector() = default;
~MoveOnlyVector() = default;
MoveOnlyVector(MoveOnlyVector &&other) = default;
MoveOnlyVector &operator=(MoveOnlyVector &&other) = default;
MoveOnlyVector(MoveOnlyVector<T> &&other) = default;
MoveOnlyVector &operator=(MoveOnlyVector<T> &&other) = default;
MoveOnlyVector(const MoveOnlyVector &) = delete;
MoveOnlyVector &operator=(const MoveOnlyVector &) = delete;
MoveOnlyVector(const MoveOnlyVector<T> &) = delete;
MoveOnlyVector &operator=(const MoveOnlyVector<T> &) = delete;
// convenience for creation
explicit MoveOnlyVector(const std::vector<int> &v) : data(v) { }
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 std::vector<int>::const_iterator const_iterator;
typedef std::vector<int>::iterator iterator;
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(); }
bool operator==(const MoveOnlyVector &other) const { return data == other.data; }
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<int> data;
std::vector<T> data;
};
#endif