Fix sending of timed invoke via MTRBaseDevice invokeCommandWithEndpointId. (#21804)
* Fix CommandSender to error out early if it detects that its "am I a timed
invoke" state does not match whether a timed invoke timeout was provided.
This should prevent invalid message sequences from being sent by accident.
* Move the one actual test from MTRClustersTests to MTRDeviceTests.
* Enable running MTRDeviceTests in CI.
* Add a test to MTRDeviceTests that does a timed invoke.
* Fix incorrect initialization of CommandSender in invokeCommandWithEndpointId
The tests being enabled fail without that last item.
diff --git a/src/app/CommandSender.cpp b/src/app/CommandSender.cpp
index 7e074df..0b6bad6 100644
--- a/src/app/CommandSender.cpp
+++ b/src/app/CommandSender.cpp
@@ -76,6 +76,15 @@
mExchangeCtx->SetResponseTimeout(timeout.ValueOr(session->ComputeRoundTripTimeout(app::kExpectedIMProcessingTime)));
+ if (mTimedRequest != mTimedInvokeTimeoutMs.HasValue())
+ {
+ ChipLogError(
+ DataManagement,
+ "Inconsistent timed request state in CommandSender: mTimedRequest (%d) != mTimedInvokeTimeoutMs.HasValue() (%d)",
+ mTimedRequest, mTimedInvokeTimeoutMs.HasValue());
+ return CHIP_ERROR_INCORRECT_STATE;
+ }
+
if (mTimedInvokeTimeoutMs.HasValue())
{
ReturnErrorOnFailure(TimedRequest::Send(mExchangeCtx.Get(), mTimedInvokeTimeoutMs.Value()));
diff --git a/src/darwin/Framework/CHIP/MTRBaseDevice.mm b/src/darwin/Framework/CHIP/MTRBaseDevice.mm
index 9b3f105..c3e060d 100644
--- a/src/darwin/Framework/CHIP/MTRBaseDevice.mm
+++ b/src/darwin/Framework/CHIP/MTRBaseDevice.mm
@@ -1146,7 +1146,8 @@
decoder->SetOnDoneCallback(onDoneCb);
- auto commandSender = chip::Platform::MakeUnique<app::CommandSender>(decoder.get(), &exchangeManager, false);
+ bool isTimedRequest = (timeoutMs != nil);
+ auto commandSender = chip::Platform::MakeUnique<app::CommandSender>(decoder.get(), &exchangeManager, isTimedRequest);
VerifyOrReturnError(commandSender != nullptr, CHIP_ERROR_NO_MEMORY);
ReturnErrorOnFailure(commandSender->AddRequestData(commandPath, MTRDataValueDictionaryDecodableType(commandFields),
diff --git a/src/darwin/Framework/CHIPTests/MTRClustersTests.m b/src/darwin/Framework/CHIPTests/MTRClustersTests.m
deleted file mode 100644
index bdab9a5..0000000
--- a/src/darwin/Framework/CHIPTests/MTRClustersTests.m
+++ /dev/null
@@ -1,208 +0,0 @@
-//
-// MTRClustersTests.m
-// MTRClustersTests
-/*
- *
- * Copyright (c) 2022 Project CHIP Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// THIS FILE IS GENERATED BY ZAP
-
-// module headers
-#import <Matter/Matter.h>
-
-#import "MTRErrorTestUtils.h"
-#import "MTRTestKeys.h"
-#import "MTRTestStorage.h"
-
-// system dependencies
-#import <XCTest/XCTest.h>
-
-const uint16_t kPairingTimeoutInSeconds = 10;
-const uint16_t kCASESetupTimeoutInSeconds = 30;
-const uint16_t kTimeoutInSeconds = 20;
-const uint64_t nodeId = 0x12344321;
-const uint32_t kSetupPINCode = 20202021;
-const uint16_t kRemotePort = 5540;
-const uint16_t kLocalPort = 5541;
-NSString * kAddress = @"::1";
-static uint16_t kTestVendorId = 0xFFF1u;
-
-// Singleton controller we use.
-static MTRDeviceController * sController = nil;
-
-@interface MTRToolPairingDelegate : NSObject <MTRDevicePairingDelegate>
-@property (nonatomic, strong) XCTestExpectation * expectation;
-@end
-
-@implementation MTRToolPairingDelegate
-- (id)initWithExpectation:(XCTestExpectation *)expectation
-{
- self = [super init];
- if (self) {
- _expectation = expectation;
- }
- return self;
-}
-
-- (void)onPairingComplete:(NSError *)error
-{
- XCTAssertEqual(error.code, 0);
- NSError * commissionError = nil;
- [sController commissionDevice:nodeId commissioningParams:[[MTRCommissioningParameters alloc] init] error:&commissionError];
- XCTAssertNil(commissionError);
-
- // Keep waiting for onCommissioningComplete
-}
-
-- (void)onCommissioningComplete:(NSError *)error
-{
- XCTAssertEqual(error.code, 0);
- [_expectation fulfill];
- _expectation = nil;
-}
-
-- (void)onAddressUpdated:(NSError *)error
-{
- XCTAssertEqual(error.code, 0);
- [_expectation fulfill];
- _expectation = nil;
-}
-@end
-
-@interface MTRClustersTests : XCTestCase
-@end
-
-@implementation MTRClustersTests
-
-- (void)setUp
-{
- [super setUp];
- [self setContinueAfterFailure:NO];
-}
-
-- (void)testInitStack
-{
- XCTestExpectation * expectation = [self expectationWithDescription:@"Pairing Complete"];
-
- __auto_type * factory = [MTRControllerFactory sharedInstance];
- XCTAssertNotNil(factory);
-
- __auto_type * storage = [[MTRTestStorage alloc] init];
- __auto_type * factoryParams = [[MTRControllerFactoryParams alloc] initWithStorage:storage];
- factoryParams.port = @(kLocalPort);
- // For our first startup, use a dummy paaCerts array.
- factoryParams.paaCerts = [[NSArray alloc] init];
-
- BOOL ok = [factory startup:factoryParams];
- XCTAssertTrue(ok);
-
- // Test that things still work right if we then shut down and
- // restart the factory.
- [factory shutdown];
- factoryParams.paaCerts = nil;
- ok = [factory startup:factoryParams];
- XCTAssertTrue(ok);
-
- __auto_type * testKeys = [[MTRTestKeys alloc] init];
- XCTAssertNotNil(testKeys);
-
- __auto_type * params = [[MTRDeviceControllerStartupParams alloc] initWithSigningKeypair:testKeys fabricId:1 ipk:testKeys.ipk];
- params.vendorId = @(kTestVendorId);
-
- MTRDeviceController * controller = [factory startControllerOnNewFabric:params];
- XCTAssertNotNil(controller);
-
- sController = controller;
-
- MTRToolPairingDelegate * pairing = [[MTRToolPairingDelegate alloc] initWithExpectation:expectation];
- dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.pairing", DISPATCH_QUEUE_SERIAL);
-
- [controller setPairingDelegate:pairing queue:callbackQueue];
-
- NSError * error;
- [controller pairDevice:nodeId address:kAddress port:kRemotePort setupPINCode:kSetupPINCode error:&error];
- XCTAssertEqual(error.code, 0);
-
- [self waitForExpectationsWithTimeout:kPairingTimeoutInSeconds handler:nil];
-
- __block XCTestExpectation * connectionExpectation = [self expectationWithDescription:@"CASE established"];
- [controller getBaseDevice:nodeId
- queue:dispatch_get_main_queue()
- completionHandler:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) {
- XCTAssertEqual(error.code, 0);
- [connectionExpectation fulfill];
- connectionExpectation = nil;
- }];
- [self waitForExpectationsWithTimeout:kCASESetupTimeoutInSeconds handler:nil];
-}
-
-- (void)testShutdownStack
-{
- MTRDeviceController * controller = sController;
- XCTAssertNotNil(controller);
-
- [controller shutdown];
- XCTAssertFalse([controller isRunning]);
-
- [[MTRControllerFactory sharedInstance] shutdown];
- XCTAssertFalse([[MTRControllerFactory sharedInstance] isRunning]);
-}
-
-- (void)testReuseChipClusterObject
-{
- MTRDeviceController * controller = sController;
- XCTAssertNotNil(controller);
-
- __block MTRBaseDevice * device;
- __block XCTestExpectation * connectionExpectation = [self expectationWithDescription:@"CASE established"];
- [controller getBaseDevice:nodeId
- queue:dispatch_get_main_queue()
- completionHandler:^(MTRBaseDevice * _Nullable retrievedDevice, NSError * _Nullable error) {
- XCTAssertEqual(error.code, 0);
- [connectionExpectation fulfill];
- connectionExpectation = nil;
- device = retrievedDevice;
- }];
- [self waitForExpectationsWithTimeout:kCASESetupTimeoutInSeconds handler:nil];
-
- XCTestExpectation * expectation = [self expectationWithDescription:@"ReuseMTRClusterObjectFirstCall"];
-
- dispatch_queue_t queue = dispatch_get_main_queue();
- MTRBaseClusterTestCluster * cluster = [[MTRBaseClusterTestCluster alloc] initWithDevice:device endpoint:1 queue:queue];
- XCTAssertNotNil(cluster);
-
- [cluster testWithCompletionHandler:^(NSError * err) {
- NSLog(@"ReuseMTRClusterObject test Error: %@", err);
- XCTAssertEqual(err.code, 0);
- [expectation fulfill];
- }];
-
- [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil];
-
- expectation = [self expectationWithDescription:@"ReuseMTRClusterObjectSecondCall"];
-
- // Reuse the MTRCluster Object for multiple times.
-
- [cluster testWithCompletionHandler:^(NSError * err) {
- NSLog(@"ReuseMTRClusterObject test Error: %@", err);
- XCTAssertEqual(err.code, 0);
- [expectation fulfill];
- }];
-
- [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil];
-}
-
-@end
diff --git a/src/darwin/Framework/CHIPTests/MTRDeviceTests.m b/src/darwin/Framework/CHIPTests/MTRDeviceTests.m
index 60e732f..84782d2 100644
--- a/src/darwin/Framework/CHIPTests/MTRDeviceTests.m
+++ b/src/darwin/Framework/CHIPTests/MTRDeviceTests.m
@@ -347,6 +347,51 @@
[self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil];
}
+- (void)test004_InvokeTimedCommand
+{
+#if MANUAL_INDIVIDUAL_TEST
+ [self initStack];
+ [self waitForCommissionee];
+#endif
+ XCTestExpectation * expectation = [self expectationWithDescription:@"invoke Off command"];
+
+ MTRBaseDevice * device = GetConnectedDevice();
+ dispatch_queue_t queue = dispatch_get_main_queue();
+
+ NSDictionary * fields = @{
+ @"type" : @"Structure",
+ @"value" : @[],
+ };
+ [device invokeCommandWithEndpointId:@1
+ clusterId:@6
+ commandId:@0
+ commandFields:fields
+ timedInvokeTimeout:@10000
+ clientQueue:queue
+ completion:^(id _Nullable values, NSError * _Nullable error) {
+ NSLog(@"invoke command: Off values: %@, error: %@", values, error);
+
+ XCTAssertNil(error);
+
+ {
+ XCTAssertTrue([values isKindOfClass:[NSArray class]]);
+ NSArray * resultArray = values;
+ for (NSDictionary * result in resultArray) {
+ MTRCommandPath * path = result[@"commandPath"];
+ XCTAssertEqual([path.endpoint unsignedIntegerValue], 1);
+ XCTAssertEqual([path.cluster unsignedIntegerValue], 6);
+ XCTAssertEqual([path.command unsignedIntegerValue], 0);
+ XCTAssertNil(result[@"error"]);
+ }
+ XCTAssertEqual([resultArray count], 1);
+ }
+
+ [expectation fulfill];
+ }];
+
+ [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil];
+}
+
static void (^globalReportHandler)(id _Nullable values, NSError * _Nullable error) = nil;
- (void)test005_Subscribe
@@ -1048,6 +1093,55 @@
}
#endif
+- (void)test013_ReuseChipClusterObject
+{
+#if MANUAL_INDIVIDUAL_TEST
+ [self initStack];
+ [self waitForCommissionee];
+#endif
+
+ MTRDeviceController * controller = sController;
+ XCTAssertNotNil(controller);
+
+ __block MTRBaseDevice * device;
+ __block XCTestExpectation * connectionExpectation = [self expectationWithDescription:@"CASE established"];
+ [controller getBaseDevice:kDeviceId
+ queue:dispatch_get_main_queue()
+ completionHandler:^(MTRBaseDevice * _Nullable retrievedDevice, NSError * _Nullable error) {
+ XCTAssertEqual(error.code, 0);
+ [connectionExpectation fulfill];
+ connectionExpectation = nil;
+ device = retrievedDevice;
+ }];
+ [self waitForExpectationsWithTimeout:kCASESetupTimeoutInSeconds handler:nil];
+
+ XCTestExpectation * expectation = [self expectationWithDescription:@"ReuseMTRClusterObjectFirstCall"];
+
+ dispatch_queue_t queue = dispatch_get_main_queue();
+ MTRBaseClusterTestCluster * cluster = [[MTRBaseClusterTestCluster alloc] initWithDevice:device endpoint:1 queue:queue];
+ XCTAssertNotNil(cluster);
+
+ [cluster testWithCompletionHandler:^(NSError * err) {
+ NSLog(@"ReuseMTRClusterObject test Error: %@", err);
+ XCTAssertEqual(err.code, 0);
+ [expectation fulfill];
+ }];
+
+ [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil];
+
+ expectation = [self expectationWithDescription:@"ReuseMTRClusterObjectSecondCall"];
+
+ // Reuse the MTRCluster Object for multiple times.
+
+ [cluster testWithCompletionHandler:^(NSError * err) {
+ NSLog(@"ReuseMTRClusterObject test Error: %@", err);
+ XCTAssertEqual(err.code, 0);
+ [expectation fulfill];
+ }];
+
+ [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil];
+}
+
- (void)test900_SubscribeAllAttributes
{
#if MANUAL_INDIVIDUAL_TEST
diff --git a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj
index 8ba1247..b271e45 100644
--- a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj
+++ b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj
@@ -9,7 +9,6 @@
/* Begin PBXBuildFile section */
1E5801C328941C050033A199 /* MTRTestOTAProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E748B3828941A44008A1BE8 /* MTRTestOTAProvider.m */; };
1E85730C265519AE0050A4D9 /* callback-stub.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1E857307265519AE0050A4D9 /* callback-stub.cpp */; };
- 1EB41B7B263C4CC60048E4C1 /* MTRClustersTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EB41B7A263C4CC60048E4C1 /* MTRClustersTests.m */; };
1EC3238D271999E2002A8BF0 /* cluster-objects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1EC3238C271999E2002A8BF0 /* cluster-objects.cpp */; };
1EC4CE5D25CC26E900D7304F /* MTRBaseClusters.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1EC4CE5925CC26E900D7304F /* MTRBaseClusters.mm */; };
1EC4CE6425CC276600D7304F /* MTRBaseClusters.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC4CE6325CC276600D7304F /* MTRBaseClusters.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -142,7 +141,6 @@
1E748B3828941A44008A1BE8 /* MTRTestOTAProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTRTestOTAProvider.m; sourceTree = "<group>"; };
1E748B3928941A45008A1BE8 /* MTRTestOTAProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRTestOTAProvider.h; sourceTree = "<group>"; };
1E857307265519AE0050A4D9 /* callback-stub.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "callback-stub.cpp"; path = "../../../../zzz_generated/controller-clusters/zap-generated/callback-stub.cpp"; sourceTree = "<group>"; };
- 1EB41B7A263C4CC60048E4C1 /* MTRClustersTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTRClustersTests.m; sourceTree = "<group>"; };
1EC3238C271999E2002A8BF0 /* cluster-objects.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "cluster-objects.cpp"; path = "../../../../zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp"; sourceTree = "<group>"; };
1EC4CE5925CC26E900D7304F /* MTRBaseClusters.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MTRBaseClusters.mm; path = "zap-generated/MTRBaseClusters.mm"; sourceTree = "<group>"; };
1EC4CE6325CC276600D7304F /* MTRBaseClusters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MTRBaseClusters.h; path = "zap-generated/MTRBaseClusters.h"; sourceTree = "<group>"; };
@@ -447,7 +445,6 @@
51E24E72274E0DAC007CCF6E /* MTRErrorTestUtils.mm */,
51C8E3F72825CDB600D47D00 /* MTRTestKeys.m */,
51D10D2D2808E2CA00E8CA3D /* MTRTestStorage.m */,
- 1EB41B7A263C4CC60048E4C1 /* MTRClustersTests.m */,
99C65E0F267282F1003402F6 /* MTRControllerTests.m */,
5AE6D4E327A99041001F2493 /* MTRDeviceTests.m */,
5A6FEC9C27B5E48800F25F42 /* MTRXPCProtocolTests.m */,
@@ -712,7 +709,6 @@
files = (
51D10D2E2808E2CA00E8CA3D /* MTRTestStorage.m in Sources */,
7596A8512878709F004DAE0E /* MTRAsyncCallbackQueueTests.m in Sources */,
- 1EB41B7B263C4CC60048E4C1 /* MTRClustersTests.m in Sources */,
997DED1A26955D0200975E97 /* MTRThreadOperationalDatasetTests.mm in Sources */,
51C8E3F82825CDB600D47D00 /* MTRTestKeys.m in Sources */,
99C65E10267282F1003402F6 /* MTRControllerTests.m in Sources */,
diff --git a/src/darwin/Framework/Matter.xcodeproj/xcshareddata/xcschemes/Matter Framework Tests.xcscheme b/src/darwin/Framework/Matter.xcodeproj/xcshareddata/xcschemes/Matter Framework Tests.xcscheme
index a0fb421..d376af2 100644
--- a/src/darwin/Framework/Matter.xcodeproj/xcshareddata/xcschemes/Matter Framework Tests.xcscheme
+++ b/src/darwin/Framework/Matter.xcodeproj/xcshareddata/xcschemes/Matter Framework Tests.xcscheme
@@ -39,9 +39,6 @@
</BuildableReference>
<SkippedTests>
<Test
- Identifier = "MTRDeviceTests">
- </Test>
- <Test
Identifier = "MTRRemoteDeviceSampleTests">
</Test>
<Test