2023-11-04 20:32:54 +00:00
|
|
|
#include <QSignalSpy>
|
|
|
|
#include <QTest>
|
|
|
|
|
|
|
|
#include <QJsonArray>
|
|
|
|
#include <QJsonObject>
|
|
|
|
#include <QQmlComponent>
|
|
|
|
#include <QQmlContext>
|
|
|
|
#include <QQmlEngine>
|
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
#include <StatusQ/submodelproxymodel.h>
|
2024-04-29 13:33:40 +00:00
|
|
|
|
2023-12-21 13:46:09 +00:00
|
|
|
#include <TestHelpers/listmodelwrapper.h>
|
2024-04-29 13:33:40 +00:00
|
|
|
#include <TestHelpers/modelsignalsspy.h>
|
|
|
|
#include <TestHelpers/modeltestutils.h>
|
2023-11-04 20:32:54 +00:00
|
|
|
|
|
|
|
class TestSubmodelProxyModel: public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
|
2024-04-29 13:33:40 +00:00
|
|
|
int roleForName(const QHash<int, QByteArray>& roles, const QByteArray& name) const
|
|
|
|
{
|
|
|
|
auto keys = roles.keys(name);
|
|
|
|
|
|
|
|
if (keys.empty())
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return keys.first();
|
|
|
|
}
|
|
|
|
|
2023-11-04 20:32:54 +00:00
|
|
|
private slots:
|
|
|
|
void basicTest() {
|
|
|
|
QQmlEngine engine;
|
|
|
|
QQmlComponent delegate(&engine);
|
|
|
|
|
|
|
|
auto delegateData = R"(
|
|
|
|
import QtQml 2.15
|
|
|
|
QtObject {
|
2024-04-11 22:03:19 +00:00
|
|
|
property var count: submodel.count
|
2023-11-04 20:32:54 +00:00
|
|
|
}
|
|
|
|
)";
|
|
|
|
|
|
|
|
delegate.setData(delegateData, QUrl());
|
|
|
|
|
|
|
|
SubmodelProxyModel model;
|
2024-04-11 22:03:19 +00:00
|
|
|
|
|
|
|
auto source = R"([
|
|
|
|
{ balances: [ { balance: 4 } ], name: "name 1" },
|
|
|
|
{ balances: [ { balance: 4 }, {balance: 43} ], name: "name 2" },
|
|
|
|
{ balances: [], name: "name 3" }
|
|
|
|
])";
|
|
|
|
|
|
|
|
ListModelWrapper sourceModel(engine, source);
|
2023-11-04 20:32:54 +00:00
|
|
|
|
|
|
|
QSignalSpy sourceModelChangedSpy(
|
|
|
|
&model, &SubmodelProxyModel::sourceModelChanged);
|
|
|
|
QSignalSpy delegateChangedSpy(
|
|
|
|
&model, &SubmodelProxyModel::delegateModelChanged);
|
|
|
|
QSignalSpy submodelRoleNameChangedSpy(
|
|
|
|
&model, &SubmodelProxyModel::submodelRoleNameChanged);
|
|
|
|
|
2023-12-21 13:46:09 +00:00
|
|
|
model.setSourceModel(sourceModel);
|
2023-11-04 20:32:54 +00:00
|
|
|
model.setDelegateModel(&delegate);
|
|
|
|
model.setSubmodelRoleName(QStringLiteral("balances"));
|
|
|
|
|
|
|
|
QCOMPARE(sourceModelChangedSpy.count(), 1);
|
|
|
|
QCOMPARE(delegateChangedSpy.count(), 1);
|
|
|
|
QCOMPARE(submodelRoleNameChangedSpy.count(), 1);
|
|
|
|
|
2023-12-21 13:46:09 +00:00
|
|
|
QCOMPARE(model.sourceModel(), sourceModel);
|
2023-11-04 20:32:54 +00:00
|
|
|
QCOMPARE(model.delegateModel(), &delegate);
|
|
|
|
QCOMPARE(model.submodelRoleName(), QStringLiteral("balances"));
|
|
|
|
|
|
|
|
QCOMPARE(model.rowCount(), 3);
|
|
|
|
|
|
|
|
QCOMPARE(model.data(model.index(0, 0), sourceModel.role("name")), "name 1");
|
|
|
|
QVERIFY(model.data(model.index(0, 0),
|
|
|
|
sourceModel.role("balances")).isValid());
|
|
|
|
|
|
|
|
auto object = model.data(model.index(0, 0),
|
|
|
|
sourceModel.role("balances")).value<QObject*>();
|
|
|
|
QVERIFY(object);
|
|
|
|
|
|
|
|
auto context = QQmlEngine::contextForObject(object);
|
|
|
|
|
2024-04-11 22:03:19 +00:00
|
|
|
QVERIFY(context->contextProperty("submodel").value<QObject*>() != nullptr);
|
|
|
|
QCOMPARE(object->property("count"), 1);
|
2023-11-04 20:32:54 +00:00
|
|
|
QCOMPARE(QQmlEngine::objectOwnership(object),
|
2024-04-11 22:03:19 +00:00
|
|
|
QQmlEngine::CppOwnership);
|
|
|
|
}
|
|
|
|
|
2024-04-24 08:28:33 +00:00
|
|
|
void submodelTypeTest() {
|
|
|
|
QQmlEngine engine;
|
|
|
|
QQmlComponent delegate(&engine);
|
|
|
|
|
|
|
|
auto delegateData = R"(
|
|
|
|
import QtQml 2.15
|
|
|
|
QtObject {
|
|
|
|
property var count: submodel.count
|
|
|
|
}
|
|
|
|
)";
|
|
|
|
|
|
|
|
delegate.setData(delegateData, QUrl());
|
|
|
|
|
|
|
|
SubmodelProxyModel model;
|
|
|
|
|
|
|
|
auto source = R"([
|
|
|
|
{ balances: [ { balance: 4 } ], name: "name 1" }
|
|
|
|
])";
|
|
|
|
|
|
|
|
ListModelWrapper sourceModel(engine, source);
|
|
|
|
model.setSourceModel(sourceModel);
|
|
|
|
model.setDelegateModel(&delegate);
|
|
|
|
model.setSubmodelRoleName(QStringLiteral("balances"));
|
|
|
|
|
|
|
|
QCOMPARE(model.rowCount(), 1);
|
|
|
|
|
2024-04-29 13:33:40 +00:00
|
|
|
QVariant balances1 = model.data(model.index(0, 0),
|
2024-04-24 08:28:33 +00:00
|
|
|
sourceModel.role("balances"));
|
2024-04-29 13:33:40 +00:00
|
|
|
QVERIFY(balances1.isValid());
|
2024-04-24 08:28:33 +00:00
|
|
|
|
|
|
|
QVariant balances2 = model.data(model.index(0, 0),
|
|
|
|
sourceModel.role("balances"));
|
2024-04-29 13:33:40 +00:00
|
|
|
QVERIFY(balances2.isValid());
|
2024-04-24 08:28:33 +00:00
|
|
|
|
|
|
|
// SubmodelProxyModel may create proxy objects on demand, then first
|
|
|
|
// call to data(...) returns freshly created object, the next calls
|
|
|
|
// related to the same row should return cached object. It's important
|
|
|
|
// to have QVariant type identical in both cases. E.g. returning raw
|
|
|
|
// pointer in first call and pointer wrapped into QPointer in the next
|
|
|
|
// one leads to problems in UI components in some scenarios even if
|
|
|
|
// those QVariant types are automatically convertible.
|
2024-04-29 13:33:40 +00:00
|
|
|
QCOMPARE(balances2.type(), balances1.type());
|
|
|
|
|
|
|
|
// Check if the same instance is returned.
|
|
|
|
QCOMPARE(balances2.value<QObject*>(), balances1.value<QObject*>());
|
2024-04-24 08:28:33 +00:00
|
|
|
}
|
|
|
|
|
2024-04-11 22:03:19 +00:00
|
|
|
void usingNonObjectSubmodelRoleTest() {
|
|
|
|
QQmlEngine engine;
|
|
|
|
QQmlComponent delegate(&engine);
|
|
|
|
|
|
|
|
auto delegateData = R"(
|
|
|
|
import QtQml 2.15
|
|
|
|
QtObject {
|
|
|
|
property var count: submodel.count
|
|
|
|
}
|
|
|
|
)";
|
|
|
|
|
|
|
|
delegate.setData(delegateData, QUrl());
|
|
|
|
|
|
|
|
SubmodelProxyModel model;
|
|
|
|
|
|
|
|
auto source = R"([
|
|
|
|
{ balances: 1, name: "name 1" },
|
|
|
|
{ balances: 2, name: "name 2" },
|
|
|
|
{ balances: 3, name: "name 3" }
|
|
|
|
])";
|
|
|
|
|
|
|
|
ListModelWrapper sourceModel(engine, source);
|
|
|
|
|
|
|
|
QTest::ignoreMessage(QtWarningMsg,
|
|
|
|
"Submodel must be a QObject-based type!");
|
2023-11-04 20:32:54 +00:00
|
|
|
|
2024-04-11 22:03:19 +00:00
|
|
|
model.setSourceModel(sourceModel);
|
|
|
|
model.setDelegateModel(&delegate);
|
|
|
|
model.setSubmodelRoleName(QStringLiteral("balances"));
|
|
|
|
|
|
|
|
QCOMPARE(model.rowCount(), 3);
|
|
|
|
|
|
|
|
QVERIFY(model.data(model.index(0, 0),
|
|
|
|
sourceModel.role("balances")).isValid());
|
2023-11-04 20:32:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void deletingDelegateTest() {
|
|
|
|
QQmlEngine engine;
|
|
|
|
auto delegate = std::make_unique<QQmlComponent>(&engine);
|
|
|
|
|
|
|
|
delegate->setData(QByteArrayLiteral(R"(
|
|
|
|
import QtQml 2.15
|
|
|
|
|
|
|
|
QtObject {
|
|
|
|
property var sub: submodel
|
|
|
|
}
|
|
|
|
)"), QUrl());
|
|
|
|
|
|
|
|
SubmodelProxyModel model;
|
2023-12-21 13:46:09 +00:00
|
|
|
ListModelWrapper sourceModel(engine, QJsonArray {
|
2023-11-04 20:32:54 +00:00
|
|
|
QJsonObject {{ "balances", 11 }, { "name", "name 1" }},
|
|
|
|
QJsonObject {{ "balances", 12 }, { "name", "name 2" }},
|
|
|
|
QJsonObject {{ "balances", 123}, { "name", "name 3" }}
|
|
|
|
});
|
|
|
|
|
2023-12-21 13:46:09 +00:00
|
|
|
model.setSourceModel(sourceModel);
|
2023-11-04 20:32:54 +00:00
|
|
|
model.setDelegateModel(delegate.get());
|
|
|
|
model.setSubmodelRoleName(QStringLiteral("balances"));
|
|
|
|
|
|
|
|
QSignalSpy delegateModelChangedSpy(
|
|
|
|
&model, &SubmodelProxyModel::delegateModelChanged);
|
|
|
|
QSignalSpy dataChangedSpy(
|
|
|
|
&model, &SubmodelProxyModel::dataChanged);
|
|
|
|
|
|
|
|
delegate.reset();
|
|
|
|
|
|
|
|
QCOMPARE(delegateModelChangedSpy.count(), 1);
|
|
|
|
QCOMPARE(dataChangedSpy.count(), 1);
|
|
|
|
|
|
|
|
QCOMPARE(model.rowCount(), 3);
|
|
|
|
QCOMPARE(model.data(model.index(0, 0),
|
|
|
|
sourceModel.role("balances")), 11);
|
|
|
|
}
|
|
|
|
|
|
|
|
void deletingSourceModelTest() {
|
|
|
|
QQmlEngine engine;
|
|
|
|
QQmlComponent delegate(&engine);
|
|
|
|
|
|
|
|
delegate.setData(QByteArrayLiteral(R"(
|
|
|
|
import QtQml 2.15
|
|
|
|
|
|
|
|
QtObject {
|
|
|
|
property var sub: submodel
|
|
|
|
}
|
|
|
|
)"), QUrl());
|
|
|
|
|
|
|
|
SubmodelProxyModel model;
|
|
|
|
|
2023-12-21 13:46:09 +00:00
|
|
|
auto sourceModel = std::make_unique<ListModelWrapper>(engine,
|
2023-11-04 20:32:54 +00:00
|
|
|
QJsonArray {
|
|
|
|
QJsonObject {{ "balances", 11 }, { "name", "name 1" }},
|
|
|
|
QJsonObject {{ "balances", 12 }, { "name", "name 2" }},
|
|
|
|
QJsonObject {{ "balances", 123}, { "name", "name 3" }}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
model.setSourceModel(sourceModel->model());
|
|
|
|
model.setDelegateModel(&delegate);
|
|
|
|
model.setSubmodelRoleName(QStringLiteral("balances"));
|
|
|
|
|
|
|
|
sourceModel.reset();
|
|
|
|
|
|
|
|
QCOMPARE(model.rowCount(), 0);
|
|
|
|
|
|
|
|
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*"));
|
|
|
|
QCOMPARE(model.data(model.index(0, 0), 0), {});
|
|
|
|
}
|
|
|
|
|
2024-04-29 13:33:40 +00:00
|
|
|
void settingUndefinedSubmodelRoleNameTest() {
|
2023-11-04 20:32:54 +00:00
|
|
|
QQmlEngine engine;
|
|
|
|
auto delegate = std::make_unique<QQmlComponent>(&engine);
|
|
|
|
|
|
|
|
delegate->setData(QByteArrayLiteral(R"(
|
|
|
|
import QtQml 2.15
|
|
|
|
|
|
|
|
QtObject {
|
|
|
|
property var sub: submodel
|
|
|
|
}
|
|
|
|
)"), QUrl());
|
|
|
|
|
|
|
|
SubmodelProxyModel model;
|
2023-12-21 13:46:09 +00:00
|
|
|
ListModelWrapper sourceModel(engine, QJsonArray {
|
2023-11-04 20:32:54 +00:00
|
|
|
QJsonObject {{ "balances", 11 }, { "name", "name 1" }},
|
|
|
|
QJsonObject {{ "balances", 12 }, { "name", "name 2" }},
|
|
|
|
QJsonObject {{ "balances", 123}, { "name", "name 3" }}
|
|
|
|
});
|
|
|
|
|
2023-12-21 13:46:09 +00:00
|
|
|
model.setSourceModel(sourceModel);
|
2023-11-04 20:32:54 +00:00
|
|
|
model.setDelegateModel(delegate.get());
|
|
|
|
|
|
|
|
QTest::ignoreMessage(QtWarningMsg, "Submodel role not found!");
|
|
|
|
|
|
|
|
model.setSubmodelRoleName(QStringLiteral("undefined"));
|
|
|
|
|
|
|
|
QCOMPARE(model.rowCount(), 3);
|
|
|
|
}
|
2024-04-29 13:33:40 +00:00
|
|
|
|
|
|
|
void addingNewRoleToTopLevelModelTest() {
|
|
|
|
QQmlEngine engine;
|
|
|
|
auto delegate = std::make_unique<QQmlComponent>(&engine);
|
|
|
|
|
|
|
|
delegate->setData(QByteArrayLiteral(R"(
|
|
|
|
import QtQml.Models 2.15
|
|
|
|
|
|
|
|
ListModel {
|
|
|
|
id: delegateRoot
|
|
|
|
|
|
|
|
property var sub: submodel
|
|
|
|
|
|
|
|
property int extraValue: submodel.rowCount()
|
|
|
|
readonly property alias extraValueRole: delegateRoot.extraValue
|
|
|
|
}
|
|
|
|
)"), QUrl());
|
|
|
|
|
|
|
|
SubmodelProxyModel model;
|
|
|
|
|
|
|
|
ListModelWrapper sourceModel(engine, R"([
|
|
|
|
{ "balances": [], "name": "name 1" },
|
|
|
|
{ "balances": [ { balance: 1 } ], "name": "name 2" },
|
|
|
|
{ "balances": [], "name": "name 3" }
|
|
|
|
])");
|
|
|
|
|
|
|
|
model.setSourceModel(sourceModel);
|
|
|
|
model.setDelegateModel(delegate.get());
|
|
|
|
model.setSubmodelRoleName(QStringLiteral("balances"));
|
|
|
|
|
|
|
|
ListModelWrapper expected(engine, R"([
|
|
|
|
{ "balances": [], "name": "name 1", "extraValue": 0 },
|
|
|
|
{ "balances": [], "name": "name 2", "extraValue": 1 },
|
|
|
|
{ "balances": [], "name": "name 3", "extraValue": 0 }
|
|
|
|
])");
|
|
|
|
|
|
|
|
QCOMPARE(model.rowCount(), 3);
|
|
|
|
|
|
|
|
auto roles = model.roleNames();
|
|
|
|
QCOMPARE(roles.size(), 3);
|
|
|
|
QVERIFY(isSame(&model, expected));
|
|
|
|
|
|
|
|
ModelSignalsSpy signalsSpy(&model);
|
|
|
|
|
|
|
|
QVariant wrapperVariant = model.data(model.index(0, 0),
|
|
|
|
roleForName(roles, "balances"));
|
|
|
|
QObject* wrapper = wrapperVariant.value<QObject*>();
|
|
|
|
QVERIFY(wrapper != nullptr);
|
|
|
|
wrapper->setProperty("extraValue", 42);
|
|
|
|
|
|
|
|
ListModelWrapper expected2(engine, R"([
|
|
|
|
{ "balances": [], "name": "name 1", "extraValue": 42 },
|
|
|
|
{ "balances": [], "name": "name 2", "extraValue": 1 },
|
|
|
|
{ "balances": [], "name": "name 3", "extraValue": 0 }
|
|
|
|
])");
|
|
|
|
|
|
|
|
// dataChanged signal emission is scheduled to event loop, not called
|
|
|
|
// immediately
|
|
|
|
QCOMPARE(signalsSpy.count(), 0);
|
|
|
|
|
|
|
|
QVERIFY(QTest::qWaitFor([&signalsSpy]() {
|
|
|
|
return signalsSpy.count() == 1;
|
|
|
|
}));
|
|
|
|
|
|
|
|
QCOMPARE(signalsSpy.count(), 1);
|
|
|
|
QCOMPARE(signalsSpy.dataChangedSpy.count(), 1);
|
|
|
|
|
|
|
|
QCOMPARE(signalsSpy.dataChangedSpy.at(0).at(0), model.index(0, 0));
|
|
|
|
QCOMPARE(signalsSpy.dataChangedSpy.at(0).at(1),
|
|
|
|
model.index(model.rowCount() - 1, 0));
|
|
|
|
|
|
|
|
QVector<int> expectedChangedRoles = { roleForName(roles, "extraValue") };
|
|
|
|
QCOMPARE(signalsSpy.dataChangedSpy.at(0).at(2).value<QVector<int>>(),
|
|
|
|
expectedChangedRoles);
|
|
|
|
|
|
|
|
QVERIFY(isSame(&model, expected2));
|
|
|
|
}
|
|
|
|
|
|
|
|
void additionalRoleDataChangedWhenEmptyTest() {
|
|
|
|
QQmlEngine engine;
|
|
|
|
auto delegate = std::make_unique<QQmlComponent>(&engine);
|
|
|
|
|
|
|
|
delegate->setData(QByteArrayLiteral(R"(
|
|
|
|
import QtQml.Models 2.15
|
|
|
|
|
|
|
|
ListModel {
|
|
|
|
property int extraValueRole: 0
|
|
|
|
}
|
|
|
|
)"), QUrl());
|
|
|
|
|
|
|
|
ListModelWrapper sourceModel(engine, R"([
|
|
|
|
{ "balances": [], "name": "name 1" }
|
|
|
|
])");
|
|
|
|
|
|
|
|
SubmodelProxyModel model;
|
|
|
|
model.setSourceModel(sourceModel);
|
|
|
|
model.setDelegateModel(delegate.get());
|
|
|
|
model.setSubmodelRoleName(QStringLiteral("balances"));
|
|
|
|
|
|
|
|
QCOMPARE(model.rowCount(), 1);
|
|
|
|
|
|
|
|
auto roles = model.roleNames();
|
|
|
|
QCOMPARE(roles.size(), 3);
|
|
|
|
|
|
|
|
ModelSignalsSpy signalsSpy(&model);
|
|
|
|
|
|
|
|
QVariant wrapperVariant = model.data(model.index(0, 0),
|
|
|
|
roleForName(roles, "balances"));
|
|
|
|
QObject* wrapper = wrapperVariant.value<QObject*>();
|
|
|
|
QVERIFY(wrapper != nullptr);
|
|
|
|
|
|
|
|
// dataChanged signal emission is scheduled to event loop, not called
|
|
|
|
// immediately. In the meantime the source may be cleared and then no
|
|
|
|
// dataChanged event should be emited.
|
|
|
|
wrapper->setProperty("extraValueRole", 42);
|
|
|
|
|
|
|
|
sourceModel.remove(0);
|
|
|
|
|
|
|
|
QCOMPARE(signalsSpy.count(), 2);
|
|
|
|
QCOMPARE(signalsSpy.rowsAboutToBeRemovedSpy.count(), 1);
|
|
|
|
QCOMPARE(signalsSpy.rowsRemovedSpy.count(), 1);
|
|
|
|
|
|
|
|
QTest::qWait(100);
|
|
|
|
QCOMPARE(signalsSpy.count(), 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
void multipleProxiesTest() {
|
|
|
|
QSKIP("Not implemented yet. The goal is to make the proxy fully "
|
|
|
|
"non-intrusive what will fix the isse pointed in this test.");
|
|
|
|
|
|
|
|
QQmlEngine engine;
|
|
|
|
auto delegate1 = std::make_unique<QQmlComponent>(&engine);
|
|
|
|
|
|
|
|
delegate1->setData(QByteArrayLiteral(R"(
|
|
|
|
import QtQml.Models 2.15
|
|
|
|
|
|
|
|
ListModel {
|
|
|
|
readonly property int myProp: 42
|
|
|
|
}
|
|
|
|
)"), QUrl());
|
|
|
|
|
|
|
|
auto delegate2 = std::make_unique<QQmlComponent>(&engine);
|
|
|
|
|
|
|
|
delegate2->setData(QByteArrayLiteral(R"(
|
|
|
|
import QtQml.Models 2.15
|
|
|
|
|
|
|
|
ListModel {
|
|
|
|
readonly property int myProp: 11
|
|
|
|
}
|
|
|
|
)"), QUrl());
|
|
|
|
|
|
|
|
ListModelWrapper sourceModel(engine, R"([
|
|
|
|
{ "balances": [], "name": "name 1" },
|
|
|
|
{ "balances": [], "name": "name 2" },
|
|
|
|
{ "balances": [], "name": "name 3" }
|
|
|
|
])");
|
|
|
|
|
|
|
|
SubmodelProxyModel model1;
|
|
|
|
model1.setSourceModel(sourceModel);
|
|
|
|
model1.setDelegateModel(delegate1.get());
|
|
|
|
model1.setSubmodelRoleName(QStringLiteral("balances"));
|
|
|
|
|
|
|
|
SubmodelProxyModel model2;
|
|
|
|
model2.setSourceModel(sourceModel);
|
|
|
|
model2.setDelegateModel(delegate2.get());
|
|
|
|
model2.setSubmodelRoleName(QStringLiteral("balances"));
|
|
|
|
|
|
|
|
auto roles = model1.roleNames();
|
|
|
|
QCOMPARE(roles.size(), 2);
|
|
|
|
|
|
|
|
QVariant wrapperVariant1 = model1.data(model1.index(0, 0),
|
|
|
|
roleForName(roles, "balances"));
|
|
|
|
QObject* wrapper1 = wrapperVariant1.value<QObject*>();
|
|
|
|
QCOMPARE(wrapper1->property("myProp"), 42);
|
|
|
|
|
|
|
|
QVariant wrapperVariant2 = model2.data(model2.index(0, 0),
|
|
|
|
roleForName(roles, "balances"));
|
|
|
|
QObject* wrapper2 = wrapperVariant2.value<QObject*>();
|
|
|
|
QCOMPARE(wrapper2->property("myProp"), 11);
|
|
|
|
}
|
2023-11-04 20:32:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
QTEST_MAIN(TestSubmodelProxyModel)
|
|
|
|
#include "tst_SubmodelProxyModel.moc"
|