summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorFabian Kosmale <fabian.kosmale@qt.io>2020-06-19 13:58:53 +0200
committerFabian Kosmale <fabian.kosmale@qt.io>2020-06-25 14:11:56 +0200
commite18a060c034beec6c7f4a22044255d9fb55d091a (patch)
tree068400f49e62abc8c0c85e31ae7c1c73afee5647 /tests
parent6a24ac7c4e0a7737b0cd0738c4e6fe8b9ac589ca (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.cpp131
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"