@@ -3746,11 +3746,16 @@ - (void)test035_TestMTRDeviceSubscriptionNotEstablishedOverXPC
3746
3746
}
3747
3747
3748
3748
- (NSArray<NSDictionary<NSString *, id> *> *)_testAttributeReportWithValue:(unsigned int)testValue
3749
+ {
3750
+ return [self _testAttributeReportWithValue:testValue dataVersion:testValue];
3751
+ }
3752
+
3753
+ - (NSArray<NSDictionary<NSString *, id> *> *)_testAttributeReportWithValue:(unsigned int)testValue dataVersion:(unsigned int)dataVersion
3749
3754
{
3750
3755
return @[ @{
3751
3756
MTRAttributePathKey : [MTRAttributePath attributePathWithEndpointID:@(0) clusterID:@(MTRClusterIDTypeLevelControlID) attributeID:@(MTRAttributeIDTypeClusterLevelControlAttributeCurrentLevelID)],
3752
3757
MTRDataKey : @ {
3753
- MTRDataVersionKey : @(testValue ),
3758
+ MTRDataVersionKey : @(dataVersion ),
3754
3759
MTRTypeKey : MTRUnsignedIntegerValueType,
3755
3760
MTRValueKey : @(testValue),
3756
3761
}
@@ -3809,7 +3814,7 @@ - (void)test036_TestStorageBehaviorConfiguration
3809
3814
[device setDelegate:delegate queue:queue];
3810
3815
3811
3816
// Use a counter that will be incremented for each report as the value.
3812
- unsigned int currentTestValue = 1;
3817
+ __block unsigned int currentTestValue = 1;
3813
3818
3814
3819
// Initial setup: Inject report and see that the attribute persisted. No delay is
3815
3820
// expected for the first (priming) report.
@@ -3928,54 +3933,84 @@ - (void)test036_TestStorageBehaviorConfiguration
3928
3933
XCTAssertLessThan(reportToPersistenceDelay, baseTestDelayTime * 2 * 5 * 1.3);
3929
3934
3930
3935
// Test 4: test reporting excessively, and see that persistence does not happen until
3931
- // reporting frequency goes back above the threshold
3932
- reportEndTime = nil;
3933
- dataPersistedTime = nil;
3934
- XCTestExpectation * dataPersisted4 = [self expectationWithDescription:@"data persisted 4"];
3935
- delegate.onClusterDataPersisted = ^{
3936
- os_unfair_lock_lock(&lock);
3937
- if (!dataPersistedTime) {
3938
- dataPersistedTime = [NSDate now];
3939
- }
3940
- os_unfair_lock_unlock(&lock);
3941
- [dataPersisted4 fulfill];
3942
- };
3943
-
3944
- // Set report times with short delay and check that the multiplier is engaged
3945
- [device unitTestSetMostRecentReportTimes:[NSMutableArray arrayWithArray:@[
3946
- [NSDate dateWithTimeIntervalSinceNow:-(baseTestDelayTime * 0.1 * 4)],
3947
- [NSDate dateWithTimeIntervalSinceNow:-(baseTestDelayTime * 0.1 * 3)],
3948
- [NSDate dateWithTimeIntervalSinceNow:-(baseTestDelayTime * 0.1 * 2)],
3949
- [NSDate dateWithTimeIntervalSinceNow:-(baseTestDelayTime * 0.1)],
3950
- ]]];
3951
-
3952
- // Inject report that makes MTRDevice detect the device is reporting excessively
3953
- [device unitTestInjectAttributeReport:[self _testAttributeReportWithValue:currentTestValue++] fromSubscription:YES];
3936
+ // reporting frequency goes back below the threshold
3937
+ __auto_type excessiveReportTest = ^(unsigned int testId, NSArray<NSDictionary<NSString *, id> *> * (^reportGenerator)(void), bool expectPersistence) {
3938
+ reportEndTime = nil;
3939
+ dataPersistedTime = nil;
3940
+ XCTestExpectation * dataPersisted = [self expectationWithDescription:[NSString stringWithFormat:@"data persisted %u", testId]];
3941
+ dataPersisted.inverted = !expectPersistence;
3942
+ delegate.onClusterDataPersisted = ^{
3943
+ os_unfair_lock_lock(&lock);
3944
+ if (!dataPersistedTime) {
3945
+ dataPersistedTime = [NSDate now];
3946
+ }
3947
+ os_unfair_lock_unlock(&lock);
3948
+ [dataPersisted fulfill];
3949
+ };
3954
3950
3955
- // Now keep reporting excessively for base delay time max times max multiplier, plus a bit more
3956
- NSDate * excessiveStartTime = [NSDate now];
3957
- for (;;) {
3958
- usleep((useconds_t) (baseTestDelayTime * 0.1 * USEC_PER_SEC));
3959
- [device unitTestInjectAttributeReport:[self _testAttributeReportWithValue:currentTestValue++] fromSubscription:YES];
3960
- NSTimeInterval elapsed = -[excessiveStartTime timeIntervalSinceNow];
3961
- if (elapsed > (baseTestDelayTime * 2 * 5 * 1.2)) {
3962
- break;
3951
+ // Set report times with short delay and check that the multiplier is engaged
3952
+ [device unitTestSetMostRecentReportTimes:[NSMutableArray arrayWithArray:@[
3953
+ [NSDate dateWithTimeIntervalSinceNow:-(baseTestDelayTime * 0.1 * 4)],
3954
+ [NSDate dateWithTimeIntervalSinceNow:-(baseTestDelayTime * 0.1 * 3)],
3955
+ [NSDate dateWithTimeIntervalSinceNow:-(baseTestDelayTime * 0.1 * 2)],
3956
+ [NSDate dateWithTimeIntervalSinceNow:-(baseTestDelayTime * 0.1)],
3957
+ ]]];
3958
+
3959
+ // Inject report that makes MTRDevice detect the device is reporting excessively
3960
+ [device unitTestInjectAttributeReport:reportGenerator() fromSubscription:YES];
3961
+
3962
+ // Now keep reporting excessively for base delay time max times max multiplier, plus a bit more
3963
+ NSDate * excessiveStartTime = [NSDate now];
3964
+ for (;;) {
3965
+ usleep((useconds_t) (baseTestDelayTime * 0.1 * USEC_PER_SEC));
3966
+ [device unitTestInjectAttributeReport:reportGenerator() fromSubscription:YES];
3967
+ NSTimeInterval elapsed = -[excessiveStartTime timeIntervalSinceNow];
3968
+ if (elapsed > (baseTestDelayTime * 2 * 5 * 1.2)) {
3969
+ break;
3970
+ }
3963
3971
}
3964
- }
3965
3972
3966
- // Check that persistence has not happened because it's now turned off
3967
- XCTAssertNil(dataPersistedTime);
3973
+ // Check that persistence has not happened because it's now turned off
3974
+ XCTAssertNil(dataPersistedTime);
3968
3975
3969
- // Now force report times to large number, to simulate time passage
3970
- [device unitTestSetMostRecentReportTimes:[NSMutableArray arrayWithArray:@[
3971
- [NSDate dateWithTimeIntervalSinceNow:-(baseTestDelayTime * 10)],
3972
- ]]];
3976
+ // Now force report times to large number, to simulate time passage
3977
+ [device unitTestSetMostRecentReportTimes:[NSMutableArray arrayWithArray:@[
3978
+ [NSDate dateWithTimeIntervalSinceNow:-(baseTestDelayTime * 10)],
3979
+ ]]];
3973
3980
3974
- // And inject a report to trigger MTRDevice to recalculate that this device is no longer
3975
- // reporting excessively
3976
- [device unitTestInjectAttributeReport:[self _testAttributeReportWithValue:currentTestValue++] fromSubscription:YES];
3981
+ // And inject a report to trigger MTRDevice to recalculate that this device is no longer
3982
+ // reporting excessively
3983
+ [device unitTestInjectAttributeReport:reportGenerator() fromSubscription:YES];
3984
+
3985
+ [self waitForExpectations:@[ dataPersisted ] timeout:60];
3986
+ };
3977
3987
3978
- [self waitForExpectations:@[ dataPersisted4 ] timeout:60];
3988
+ excessiveReportTest(
3989
+ 4, ^{
3990
+ return [self _testAttributeReportWithValue:currentTestValue++];
3991
+ }, true);
3992
+
3993
+ // Test 5: test reporting excessively with the same value and different data
3994
+ // versions, and see that persistence does not happen until reporting
3995
+ // frequency goes back below the threshold.
3996
+ __block __auto_type dataVersion = currentTestValue;
3997
+ // We incremented currentTestValue after injecting the last report. Make sure all the new
3998
+ // reports use that last-reported value.
3999
+ __auto_type lastReportedValue = currentTestValue - 1;
4000
+ excessiveReportTest(
4001
+ 5, ^{
4002
+ return [self _testAttributeReportWithValue:lastReportedValue dataVersion:dataVersion++];
4003
+ }, true);
4004
+
4005
+ // Test 6: test reporting excessively with the same value and same data
4006
+ // version, and see that persistence does not happen at all.
4007
+ // We incremented dataVersion after injecting the last report. Make sure all the new
4008
+ // reports use that last-reported value.
4009
+ __block __auto_type lastReportedDataVersion = dataVersion - 1;
4010
+ excessiveReportTest(
4011
+ 6, ^{
4012
+ return [self _testAttributeReportWithValue:lastReportedValue dataVersion:lastReportedDataVersion];
4013
+ }, false);
3979
4014
3980
4015
delegate.onReportEnd = nil;
3981
4016
delegate.onClusterDataPersisted = nil;
0 commit comments