Skip to content

Commit

Permalink
Fix TSAN issue with test values in unit test
Browse files Browse the repository at this point in the history
  • Loading branch information
jtung-apple committed May 24, 2024
1 parent 683e48b commit d8f14fa
Showing 1 changed file with 23 additions and 30 deletions.
53 changes: 23 additions & 30 deletions src/darwin/Framework/CHIPTests/MTRDeviceTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -3668,6 +3668,18 @@ - (void)test035_TestMTRDeviceSubscriptionNotEstablishedOverXPC
XCTAssertEqual([device _getInternalState], MTRInternalDeviceStateUnsubscribed);
}

- (NSArray<NSDictionary<NSString *, id> *> *)testAttributeReportWithValue:(unsigned int)testValue
{
return @[ @{
MTRAttributePathKey : [MTRAttributePath attributePathWithEndpointID:@(0) clusterID:@(MTRClusterIDTypeLevelControlID) attributeID:@(MTRAttributeIDTypeClusterLevelControlAttributeCurrentLevelID)],
MTRDataKey : @ {
MTRDataVersionKey : @(testValue),
MTRTypeKey : MTRUnsignedIntegerValueType,
MTRValueKey : @(testValue),
}
} ];
}

- (void)test036_TestStorageBehaviorConfiguration
{
// Use separate queue for timing sensitive test
Expand Down Expand Up @@ -3719,21 +3731,11 @@ - (void)test036_TestStorageBehaviorConfiguration

[device setDelegate:delegate queue:queue];

// Use a mutable dictionary so the data value can be changed between reports
// Use a mutable dictionary so the data value can be easily changed between reports
unsigned int currentTestValue = 1;
NSMutableDictionary * mutableResponseValue = [NSMutableDictionary dictionaryWithDictionary:@{
MTRAttributePathKey : [MTRAttributePath attributePathWithEndpointID:@(0) clusterID:@(MTRClusterIDTypeLevelControlID) attributeID:@(MTRAttributeIDTypeClusterLevelControlAttributeCurrentLevelID)],
MTRDataKey : @ {
MTRDataVersionKey : @(currentTestValue),
MTRTypeKey : MTRUnsignedIntegerValueType,
MTRValueKey : @(currentTestValue),
}
}];

NSArray<NSDictionary<NSString *, id> *> * attributeReport = @[ mutableResponseValue ];

// Test 1: Inject report and see that the attribute persisted, with a delay
[device unitTestInjectAttributeReport:attributeReport fromSubscription:YES];
[device unitTestInjectAttributeReport:[self testAttributeReportWithValue:currentTestValue++] fromSubscription:YES];

[self waitForExpectations:@[ dataPersisted1 ] timeout:60];

Expand All @@ -3759,25 +3761,20 @@ - (void)test036_TestStorageBehaviorConfiguration
// Test 2: Inject multiple reports with delay and see that the attribute persisted eventually
reportEndTime = nil;
dataPersistedTime = nil;
mutableResponseValue[MTRDataKey] = @{ MTRDataVersionKey : @(++currentTestValue), MTRTypeKey : MTRUnsignedIntegerValueType, MTRValueKey : @(currentTestValue) };
[device unitTestInjectAttributeReport:attributeReport fromSubscription:YES];
[device unitTestInjectAttributeReport:[self testAttributeReportWithValue:currentTestValue++] fromSubscription:YES];

double frequentReportMultiplier = 0.5;
usleep((useconds_t) (baseTestDelayTime * frequentReportMultiplier * USEC_PER_SEC));
mutableResponseValue[MTRDataKey] = @{ MTRDataVersionKey : @(++currentTestValue), MTRTypeKey : MTRUnsignedIntegerValueType, MTRValueKey : @(currentTestValue) };
[device unitTestInjectAttributeReport:attributeReport fromSubscription:YES];
[device unitTestInjectAttributeReport:[self testAttributeReportWithValue:currentTestValue++] fromSubscription:YES];

usleep((useconds_t) (baseTestDelayTime * frequentReportMultiplier * USEC_PER_SEC));
mutableResponseValue[MTRDataKey] = @{ MTRDataVersionKey : @(++currentTestValue), MTRTypeKey : MTRUnsignedIntegerValueType, MTRValueKey : @(currentTestValue) };
[device unitTestInjectAttributeReport:attributeReport fromSubscription:YES];
[device unitTestInjectAttributeReport:[self testAttributeReportWithValue:currentTestValue++] fromSubscription:YES];

usleep((useconds_t) (baseTestDelayTime * frequentReportMultiplier * USEC_PER_SEC));
mutableResponseValue[MTRDataKey] = @{ MTRDataVersionKey : @(++currentTestValue), MTRTypeKey : MTRUnsignedIntegerValueType, MTRValueKey : @(currentTestValue) };
[device unitTestInjectAttributeReport:attributeReport fromSubscription:YES];
[device unitTestInjectAttributeReport:[self testAttributeReportWithValue:currentTestValue++] fromSubscription:YES];

usleep((useconds_t) (baseTestDelayTime * frequentReportMultiplier * USEC_PER_SEC));
mutableResponseValue[MTRDataKey] = @{ MTRDataVersionKey : @(++currentTestValue), MTRTypeKey : MTRUnsignedIntegerValueType, MTRValueKey : @(currentTestValue) };
[device unitTestInjectAttributeReport:attributeReport fromSubscription:YES];
[device unitTestInjectAttributeReport:[self testAttributeReportWithValue:currentTestValue++] fromSubscription:YES];

// At this point, the threshold for reportToPersistenceDelayTimeMax should have hit, and persistence
// should have happened with timer running down to persist again with the 5th report above. Need to
Expand Down Expand Up @@ -3819,8 +3816,7 @@ - (void)test036_TestStorageBehaviorConfiguration
]]];

// Inject final report that makes MTRDevice recalculate delay with multiplier
mutableResponseValue[MTRDataKey] = @{ MTRDataVersionKey : @(++currentTestValue), MTRTypeKey : MTRUnsignedIntegerValueType, MTRValueKey : @(currentTestValue) };
[device unitTestInjectAttributeReport:attributeReport fromSubscription:YES];
[device unitTestInjectAttributeReport:[self testAttributeReportWithValue:currentTestValue++] fromSubscription:YES];

[self waitForExpectations:@[ dataPersisted3 ] timeout:60];

Expand Down Expand Up @@ -3859,15 +3855,13 @@ - (void)test036_TestStorageBehaviorConfiguration
]]];

// Inject report that makes MTRDevice detect the device is reporting excessively
mutableResponseValue[MTRDataKey] = @{ MTRDataVersionKey : @(++currentTestValue), MTRTypeKey : MTRUnsignedIntegerValueType, MTRValueKey : @(currentTestValue) };
[device unitTestInjectAttributeReport:attributeReport fromSubscription:YES];
[device unitTestInjectAttributeReport:[self testAttributeReportWithValue:currentTestValue++] fromSubscription:YES];

// Now keep reporting excessively for base delay time max times max multiplier, plus a bit more
NSDate * excessiveStartTime = [NSDate now];
for (;;) {
usleep((useconds_t) (baseTestDelayTime * 0.1 * USEC_PER_SEC));
mutableResponseValue[MTRDataKey] = @{ MTRDataVersionKey : @(++currentTestValue), MTRTypeKey : MTRUnsignedIntegerValueType, MTRValueKey : @(currentTestValue) };
[device unitTestInjectAttributeReport:attributeReport fromSubscription:YES];
[device unitTestInjectAttributeReport:[self testAttributeReportWithValue:currentTestValue++] fromSubscription:YES];
NSTimeInterval elapsed = -[excessiveStartTime timeIntervalSinceNow];
if (elapsed > (baseTestDelayTime * 2 * 5 * 1.2)) {
break;
Expand All @@ -3884,8 +3878,7 @@ - (void)test036_TestStorageBehaviorConfiguration

// And inject a report to trigger MTRDevice to recalculate that this device is no longer
// reporting excessively
mutableResponseValue[MTRDataKey] = @{ MTRDataVersionKey : @(++currentTestValue), MTRTypeKey : MTRUnsignedIntegerValueType, MTRValueKey : @(currentTestValue) };
[device unitTestInjectAttributeReport:attributeReport fromSubscription:YES];
[device unitTestInjectAttributeReport:[self testAttributeReportWithValue:currentTestValue++] fromSubscription:YES];

[self waitForExpectations:@[ dataPersisted4 ] timeout:60];

Expand Down

0 comments on commit d8f14fa

Please sign in to comment.