diff options
author | Fabian Kosmale <fabian.kosmale@qt.io> | 2020-06-19 13:58:53 +0200 |
---|---|---|
committer | Fabian Kosmale <fabian.kosmale@qt.io> | 2020-06-25 14:11:56 +0200 |
commit | e18a060c034beec6c7f4a22044255d9fb55d091a (patch) | |
tree | 068400f49e62abc8c0c85e31ae7c1c73afee5647 /tests | |
parent | 6a24ac7c4e0a7737b0cd0738c4e6fe8b9ac589ca (diff) |
QNotifiedProperty: Add guard callback
A guard callback is a predicate which takes the new value set by
setValue or computed as the result of a binding expression. If it
returns false, the value is discarded and the old value is kept.
Note that due to lazyness, when setting a binding, we still notify
everyone as the binding is only evaluated on demand, and the guard can
thus only run when someone actually queries the value.
Note further that a guard is allowed to modify the value that is passed
to it (e.g. to clamp it to a certain range).
Task-number: QTBUG-85032
Change-Id: I3551e4357fe5780fb75da80bf8be208ec152dc2a
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp index 7200bf176e..53d361ca26 100644 --- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp +++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp @@ -73,6 +73,7 @@ private slots: void propertyAlias(); void notifiedProperty(); void notifiedPropertyWithOldValueCallback(); + void notifiedPropertyWithGuard(); }; void tst_QProperty::functorBinding() @@ -876,6 +877,136 @@ void tst_QProperty::notifiedPropertyWithOldValueCallback() QCOMPARE(instance.property.value(), 4); } +struct ClassWithNotifiedPropertyWithGuard +{ + using This = ClassWithNotifiedPropertyWithGuard; + QVector<int> recordedValues; + + void callback() { recordedValues << property.value(); } + void callback2() { recordedValues << property2.value(); } + void callback3() {} + bool trivialGuard(int ) {return true;} + bool reject42(int newValue) {return newValue != 42;} + bool bound(int &newValue) { newValue = qBound<int>(0, newValue, 100); return true; } + + QNotifiedProperty<int, &This::callback, &This::trivialGuard> property; + QNotifiedProperty<int, &This::callback2, &This::reject42> property2; + QNotifiedProperty<int, &This::callback3, &This::bound> property3; +}; + +void tst_QProperty::notifiedPropertyWithGuard() +{ + { + // property with guard that returns always true is the same as using no guard + ClassWithNotifiedPropertyWithGuard instance; + + std::array<QProperty<int>, 5> otherProperties = { + QProperty<int>([&]() { return instance.property + 1; }), + QProperty<int>([&]() { return instance.property + 2; }), + QProperty<int>([&]() { return instance.property + 3; }), + QProperty<int>([&]() { return instance.property + 4; }), + QProperty<int>([&]() { return instance.property + 5; }), + }; + + auto check = [&] { + const int val = instance.property.value(); + for (int i = 0; i < int(otherProperties.size()); ++i) + QCOMPARE(otherProperties[i].value(), val + i + 1); + }; + + QVERIFY(instance.recordedValues.isEmpty()); + check(); + + instance.property.setValue(&instance, 42); + QCOMPARE(instance.recordedValues.count(), 1); + QCOMPARE(instance.recordedValues.at(0), 42); + instance.recordedValues.clear(); + check(); + + instance.property.setValue(&instance, 42); + QVERIFY(instance.recordedValues.isEmpty()); + check(); + + int subscribedCount = 0; + QProperty<int> injectedValue(100); + instance.property.setBinding(&instance, [&injectedValue]() { return injectedValue.value(); }); + auto subscriber = [&] { ++subscribedCount; }; + std::array<QPropertyChangeHandler<decltype (subscriber)>, 10> subscribers = { + instance.property.subscribe(subscriber), + instance.property.subscribe(subscriber), + instance.property.subscribe(subscriber), + instance.property.subscribe(subscriber), + instance.property.subscribe(subscriber), + instance.property.subscribe(subscriber), + instance.property.subscribe(subscriber), + instance.property.subscribe(subscriber), + instance.property.subscribe(subscriber), + instance.property.subscribe(subscriber) + }; + + QCOMPARE(subscribedCount, 10); + subscribedCount = 0; + + QCOMPARE(instance.property.value(), 100); + QCOMPARE(instance.recordedValues.count(), 1); + QCOMPARE(instance.recordedValues.at(0), 100); + instance.recordedValues.clear(); + check(); + QCOMPARE(subscribedCount, 0); + + injectedValue = 200; + QCOMPARE(instance.property.value(), 200); + QCOMPARE(instance.recordedValues.count(), 1); + QCOMPARE(instance.recordedValues.at(0), 200); + instance.recordedValues.clear(); + check(); + QCOMPARE(subscribedCount, 10); + subscribedCount = 0; + + injectedValue = 400; + QCOMPARE(instance.property.value(), 400); + QCOMPARE(instance.recordedValues.count(), 1); + QCOMPARE(instance.recordedValues.at(0), 400); + instance.recordedValues.clear(); + check(); + QCOMPARE(subscribedCount, 10); + } + + { + // Values can be rejected + ClassWithNotifiedPropertyWithGuard instance2; + + instance2.property2.setValue(&instance2, 1); + instance2.property2.setBinding(&instance2, [](){return 42;}); + instance2.property2.setValue(&instance2, 2); + instance2.property2.setBinding(&instance2, [](){return 3;}); + instance2.property2.setValue(&instance2, 42); + // Note that we get 1 twice + // This is an unfortunate result of the lazyness used for bindings + // When we call setBinding, the binding does not get evaluated, but we + // call the callback in notify; the callback will in our case query the + // properties' value. At that point we then evaluate the binding, and + // notice that the value is in fact disallowed. Thus we return the old + // value. + QVector<int> expected {1, 1, 2, 3}; + QCOMPARE(instance2.recordedValues, expected); + } + + { + // guard can modify incoming values + ClassWithNotifiedPropertyWithGuard instance3; + instance3.property3.setValue(&instance3, 5); + int i1 = 5; + QCOMPARE(instance3.property3.value(), i1); + instance3.property3.setBinding(&instance3, [](){return 255;}); + QCOMPARE(instance3.property3.value(), 100); + const int i2 = -1; + instance3.property3.setValue(&instance3, i2); + QCOMPARE(instance3.property3.value(), 0); + } +} + + QTEST_MAIN(tst_QProperty); #include "tst_qproperty.moc" |