@@ -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