Factor our common logic in Darwin OTA tests. (#27300)
The whole BDX driver bit can be shared across tests.
diff --git a/.github/workflows/darwin.yaml b/.github/workflows/darwin.yaml
index 8f6e387..f724c01 100644
--- a/.github/workflows/darwin.yaml
+++ b/.github/workflows/darwin.yaml
@@ -132,7 +132,6 @@
timeout-minutes: 10
run: |
scripts/examples/gn_build_example.sh examples/ota-requestor-app/linux out/debug chip_config_network_layer_ble=false non_spec_compliant_ota_action_delay_floor=0 chip_device_config_device_software_version=5 chip_device_config_device_software_version_string='"5.0"'
- src/app/ota_image_tool.py create -v 0xFF01 -p 0x8001 -vn 5 -vs "5.0" -da sha256 out/debug/chip-ota-requestor-app /tmp/ota-test005-image
cp out/debug/chip-ota-requestor-app /tmp/ota-test005-raw-image
- name: Build example OTA Requestor
timeout-minutes: 10
diff --git a/src/darwin/Framework/CHIPTests/MTROTAProviderTests.m b/src/darwin/Framework/CHIPTests/MTROTAProviderTests.m
index 7a7b943..0b84c22 100644
--- a/src/darwin/Framework/CHIPTests/MTROTAProviderTests.m
+++ b/src/darwin/Framework/CHIPTests/MTROTAProviderTests.m
@@ -35,7 +35,7 @@
static const uint16_t kPairingTimeoutInSeconds = 10;
static const uint16_t kTimeoutInSeconds = 3;
-static const uint16_t kTimeoutWithUpdateInSeconds = 10;
+static const uint16_t kTimeoutWithUpdateInSeconds = 60;
static const uint64_t kDeviceId1 = 0x12341234;
static const uint64_t kDeviceId2 = 0x12341235;
// NOTE: These onboarding payloads are for the chip-ota-requestor-app, not chip-all-clusters-app
@@ -255,18 +255,11 @@
}];
}
-- (void)respondWithDiscontinueToApplyUpdateRequestWithCompletion:(ApplyUpdateRequestCompletion)completion
+- (void)respondToApplyUpdateRequestWithAction:(MTROTASoftwareUpdateProviderOTAApplyUpdateAction)action
+ completion:(ApplyUpdateRequestCompletion)completion
{
__auto_type * params = [[MTROTASoftwareUpdateProviderClusterApplyUpdateResponseParams alloc] init];
- params.action = @(MTROTASoftwareUpdateProviderOTAApplyUpdateActionDiscontinue);
- params.delayedActionTime = @(0);
- completion(params, nil);
-}
-
-- (void)respondWithProceedToApplyUpdateRequestWithCompletion:(ApplyUpdateRequestCompletion)completion
-{
- __auto_type * params = [[MTROTASoftwareUpdateProviderClusterApplyUpdateResponseParams alloc] init];
- params.action = @(MTROTASoftwareUpdateProviderOTAApplyUpdateActionProceed);
+ params.action = @(action);
params.delayedActionTime = @(0);
completion(params, nil);
}
@@ -308,6 +301,172 @@
static MTROTAProviderDelegateImpl * sOTAProviderDelegate;
+/**
+ * Helper that, given a raw image, creates an image-with-header from it, drives
+ * a BDX transfer, etc.
+ */
+@interface MTROTAProviderTransferChecker : NSObject
+
+- (instancetype)initWithRawImagePath:(NSString *)rawImagePath
+ nodeID:(NSNumber *)nodeID
+ applyUpdateAction:(MTROTASoftwareUpdateProviderOTAApplyUpdateAction)applyUpdateAction
+ testcase:(XCTestCase *)testcase;
+
+@property (nonatomic, readonly) XCTestExpectation * queryExpectation;
+@property (nonatomic, readonly) XCTestExpectation * bdxBeginExpectation;
+@property (nonatomic, readonly) XCTestExpectation * bdxQueryExpectation;
+@property (nonatomic, readonly) XCTestExpectation * bdxEndExpectation;
+@property (nonatomic, readonly) XCTestExpectation * applyUpdateRequestExpectation;
+@property (nonatomic, readonly) XCTestExpectation * notifyUpdateAppliedExpectation;
+@end
+
+@implementation MTROTAProviderTransferChecker
+
+- (instancetype)initWithRawImagePath:(NSString *)rawImagePath
+ nodeID:(NSNumber *)nodeID
+ applyUpdateAction:(MTROTASoftwareUpdateProviderOTAApplyUpdateAction)applyUpdateAction
+ testcase:(XCTestCase *)testcase
+{
+ if (!(self = [super init])) {
+ return nil;
+ }
+
+ _queryExpectation = [testcase expectationWithDescription:@"handleQueryImageForNodeID called"];
+ _bdxBeginExpectation = [testcase expectationWithDescription:@"handleBDXTransferSessionBeginForNodeID called"];
+ _bdxQueryExpectation = [testcase expectationWithDescription:@"handleBDXQueryForNodeID called"];
+ _bdxEndExpectation = [testcase expectationWithDescription:@"handleBDXTransferSessionEndForNodeID called"];
+ _applyUpdateRequestExpectation = [testcase expectationWithDescription:@"handleApplyUpdateRequestForNodeID called"];
+ _notifyUpdateAppliedExpectation = [testcase expectationWithDescription:@"handleNotifyUpdateAppliedForNodeID called"];
+
+ NSString * imagePath = [rawImagePath stringByReplacingOccurrencesOfString:@"raw-image" withString:@"image"];
+
+ // Find the right absolute path to our ota_image_tool.py script. PWD should
+ // point to our src/darwin/Framework, while the script is in
+ // src/app/ota_image_tool.py.
+ NSString * pwd = [[NSProcessInfo processInfo] environment][@"PWD"];
+ NSString * imageToolPath = [NSString
+ pathWithComponents:@[ [pwd substringToIndex:(pwd.length - @"darwin/Framework".length)], @"app", @"ota_image_tool.py" ]];
+
+ NSTask * task = [[NSTask alloc] init];
+ [task setLaunchPath:imageToolPath];
+ [task setArguments:@[
+ @"create", @"-v", @"0xFFF1", @"-p", @"0x8001", @"-vn", [kUpdatedSoftwareVersion stringValue], @"-vs",
+ kUpdatedSoftwareVersionString, @"-da", @"sha256", rawImagePath, imagePath
+ ]];
+ NSError * launchError = nil;
+ [task launchAndReturnError:&launchError];
+ XCTAssertNil(launchError);
+ [task waitUntilExit];
+ XCTAssertEqual([task terminationStatus], 0);
+
+ NSData * updateToken = [sOTAProviderDelegate generateUpdateToken];
+
+ __block NSFileHandle * readHandle;
+ __block uint64_t imageSize;
+ __block uint32_t lastBlockIndex = UINT32_MAX;
+
+ sOTAProviderDelegate.queryImageHandler = ^(NSNumber * nodeID, MTRDeviceController * controller,
+ MTROTASoftwareUpdateProviderClusterQueryImageParams * params, QueryImageCompletion completion) {
+ XCTAssertEqualObjects(nodeID, nodeID);
+ XCTAssertEqual(controller, sController);
+
+ sOTAProviderDelegate.queryImageHandler = nil;
+ [sOTAProviderDelegate respondAvailableWithDelay:@(0) uri:imagePath updateToken:updateToken completion:completion];
+ [self.queryExpectation fulfill];
+ };
+ sOTAProviderDelegate.transferBeginHandler = ^(NSNumber * nodeID, MTRDeviceController * controller, NSString * fileDesignator,
+ NSNumber * offset, MTRStatusCompletion completion) {
+ XCTAssertEqualObjects(nodeID, nodeID);
+ XCTAssertEqual(controller, sController);
+ XCTAssertEqualObjects(fileDesignator, imagePath);
+ XCTAssertEqualObjects(offset, @(0));
+
+ readHandle = [NSFileHandle fileHandleForReadingAtPath:fileDesignator];
+ XCTAssertNotNil(readHandle);
+
+ NSError * endSeekError;
+ XCTAssertTrue([readHandle seekToEndReturningOffset:&imageSize error:&endSeekError]);
+ XCTAssertNil(endSeekError);
+
+ sOTAProviderDelegate.transferBeginHandler = nil;
+ [sOTAProviderDelegate respondSuccess:completion];
+ [self.bdxBeginExpectation fulfill];
+ };
+ sOTAProviderDelegate.blockQueryHandler = ^(NSNumber * nodeID, MTRDeviceController * controller, NSNumber * blockSize,
+ NSNumber * blockIndex, NSNumber * bytesToSkip, BlockQueryCompletion completion) {
+ XCTAssertEqualObjects(nodeID, nodeID);
+ XCTAssertEqual(controller, sController);
+ XCTAssertEqualObjects(blockSize, @(1024)); // Seems to always be 1024.
+ XCTAssertEqualObjects(blockIndex, @(lastBlockIndex + 1));
+ XCTAssertEqualObjects(bytesToSkip, @(0)); // Don't expect to see skips here.
+ // Make sure we actually end up with multiple blocks.
+ XCTAssertTrue(blockSize.unsignedLongLongValue < imageSize);
+
+ XCTAssertNotNil(readHandle);
+ uint64_t offset = blockSize.unsignedLongLongValue * blockIndex.unsignedLongLongValue;
+ NSError * seekError = nil;
+ [readHandle seekToOffset:offset error:&seekError];
+ XCTAssertNil(seekError);
+
+ NSError * readError = nil;
+ NSData * data = [readHandle readDataUpToLength:blockSize.unsignedLongValue error:&readError];
+ XCTAssertNil(readError);
+ XCTAssertNotNil(data);
+
+ BOOL isEOF = offset + blockSize.unsignedLongValue >= imageSize;
+
+ ++lastBlockIndex;
+
+ if (isEOF) {
+ sOTAProviderDelegate.blockQueryHandler = nil;
+ }
+
+ completion(data, isEOF);
+
+ if (isEOF) {
+ [self.bdxQueryExpectation fulfill];
+ }
+ };
+ sOTAProviderDelegate.transferEndHandler = ^(NSNumber * nodeID, MTRDeviceController * controller, NSError * _Nullable error) {
+ XCTAssertEqualObjects(nodeID, nodeID);
+ XCTAssertEqual(controller, sController);
+ XCTAssertNil(error);
+
+ sOTAProviderDelegate.transferEndHandler = nil;
+ [self.bdxEndExpectation fulfill];
+ };
+ sOTAProviderDelegate.applyUpdateRequestHandler = ^(NSNumber * nodeID, MTRDeviceController * controller,
+ MTROTASoftwareUpdateProviderClusterApplyUpdateRequestParams * params, ApplyUpdateRequestCompletion completion) {
+ XCTAssertEqualObjects(nodeID, nodeID);
+ XCTAssertEqual(controller, sController);
+ XCTAssertEqualObjects(params.updateToken, updateToken);
+ XCTAssertEqualObjects(params.newVersion, kUpdatedSoftwareVersion); // TODO: Factor this out better!
+
+ XCTAssertTrue([[NSFileManager defaultManager] contentsEqualAtPath:rawImagePath andPath:kOtaDownloadedFilePath1]);
+
+ sOTAProviderDelegate.applyUpdateRequestHandler = nil;
+ [sOTAProviderDelegate respondToApplyUpdateRequestWithAction:applyUpdateAction completion:completion];
+ [self.applyUpdateRequestExpectation fulfill];
+ };
+
+ if (applyUpdateAction == MTROTASoftwareUpdateProviderOTAApplyUpdateActionProceed) {
+ sOTAProviderDelegate.notifyUpdateAppliedHandler = ^(NSNumber * nodeID, MTRDeviceController * controller,
+ MTROTASoftwareUpdateProviderClusterNotifyUpdateAppliedParams * params, MTRStatusCompletion completion) {
+ XCTAssertEqualObjects(nodeID, nodeID);
+ XCTAssertEqual(controller, sController);
+ XCTAssertEqualObjects(params.updateToken, updateToken);
+ XCTAssertEqualObjects(params.softwareVersion, kUpdatedSoftwareVersion);
+
+ sOTAProviderDelegate.notifyUpdateAppliedHandler = nil;
+ [sOTAProviderDelegate respondSuccess:completion];
+ [self.notifyUpdateAppliedExpectation fulfill];
+ };
+ }
+
+ return self;
+}
+@end
+
@interface MTROTAProviderTests : XCTestCase
@end
@@ -625,15 +784,6 @@
// that the update does not actually proceed.
__auto_type * device = sConnectedDevice1;
- XCTestExpectation * queryExpectation = [self expectationWithDescription:@"handleQueryImageForNodeID called"];
- XCTestExpectation * bdxBeginExpectation = [self expectationWithDescription:@"handleBDXTransferSessionBeginForNodeID called"];
- XCTestExpectation * bdxQueryExpectation = [self expectationWithDescription:@"handleBDXQueryForNodeID called"];
- XCTestExpectation * bdxEndExpectation = [self expectationWithDescription:@"handleBDXTransferSessionEndForNodeID called"];
- XCTestExpectation * applyUpdateRequestExpectation =
- [self expectationWithDescription:@"handleApplyUpdateRequestForNodeID called"];
-
- NSData * updateToken = [sOTAProviderDelegate generateUpdateToken];
-
// First, create an image. Make it at least 4096 bytes long, so we get
// multiple BDX blocks going.
const size_t rawImageSize = 4112;
@@ -644,142 +794,37 @@
[fakeImage appendData:rawImagePiece];
}
NSString * rawImagePath = @"/tmp/ota-test004-raw-image";
- NSString * imagePath = @"/tmp/ota-test004-image";
[[NSFileManager defaultManager] createFileAtPath:rawImagePath contents:fakeImage attributes:nil];
- // Find the right absolute path to our ota_image_tool.py script. PWD should
- // point to our src/darwin/Framework, while the script is in
- // src/app/ota_image_tool.py.
- NSString * pwd = [[NSProcessInfo processInfo] environment][@"PWD"];
- NSString * imageToolPath = [NSString
- pathWithComponents:@[ [pwd substringToIndex:(pwd.length - @"darwin/Framework".length)], @"app", @"ota_image_tool.py" ]];
-
- NSTask * task = [[NSTask alloc] init];
- [task setLaunchPath:imageToolPath];
- [task setArguments:@[
- @"create", @"-v", @"0xFFF1", @"-p", @"0x8001", @"-vn", [kUpdatedSoftwareVersion stringValue], @"-vs",
- kUpdatedSoftwareVersionString, @"-da", @"sha256", rawImagePath, imagePath
- ]];
- NSError * launchError = nil;
- [task launchAndReturnError:&launchError];
- XCTAssertNil(launchError);
- [task waitUntilExit];
- XCTAssertEqual([task terminationStatus], 0);
-
- __block NSFileHandle * readHandle;
- __block uint64_t imageSize;
- __block uint32_t lastBlockIndex = UINT32_MAX;
-
- // TODO: Maybe we should move more of this logic into sOTAProviderDelegate
- // or some other helper, once we have multiple tests sending images? For
- // example, we could have something where you can do one of two things:
- //
- // 1) register a "raw image" with it, and it generates the
- // image-with header.
- // 2) register a pre-generated image with it and it uses "ota_image_tool.py
- // extract" to extract the raw image.
- //
- // Once that's done the helper could track the transfer state for a
- // particular image, etc, with us just forwarding our notifications to it.
- sOTAProviderDelegate.queryImageHandler = ^(NSNumber * nodeID, MTRDeviceController * controller,
- MTROTASoftwareUpdateProviderClusterQueryImageParams * params, QueryImageCompletion completion) {
- XCTAssertEqualObjects(nodeID, @(kDeviceId1));
- XCTAssertEqual(controller, sController);
-
- sOTAProviderDelegate.queryImageHandler = nil;
- [sOTAProviderDelegate respondAvailableWithDelay:@(0) uri:imagePath updateToken:updateToken completion:completion];
- [queryExpectation fulfill];
- };
- sOTAProviderDelegate.transferBeginHandler = ^(NSNumber * nodeID, MTRDeviceController * controller, NSString * fileDesignator,
- NSNumber * offset, MTRStatusCompletion completion) {
- XCTAssertEqualObjects(nodeID, @(kDeviceId1));
- XCTAssertEqual(controller, sController);
- XCTAssertEqualObjects(fileDesignator, imagePath);
- XCTAssertEqualObjects(offset, @(0));
-
- readHandle = [NSFileHandle fileHandleForReadingAtPath:fileDesignator];
- XCTAssertNotNil(readHandle);
-
- NSError * endSeekError;
- XCTAssertTrue([readHandle seekToEndReturningOffset:&imageSize error:&endSeekError]);
- XCTAssertNil(endSeekError);
-
- sOTAProviderDelegate.transferBeginHandler = nil;
- [sOTAProviderDelegate respondSuccess:completion];
- [bdxBeginExpectation fulfill];
- };
- sOTAProviderDelegate.blockQueryHandler = ^(NSNumber * nodeID, MTRDeviceController * controller, NSNumber * blockSize,
- NSNumber * blockIndex, NSNumber * bytesToSkip, BlockQueryCompletion completion) {
- XCTAssertEqualObjects(nodeID, @(kDeviceId1));
- XCTAssertEqual(controller, sController);
- XCTAssertEqualObjects(blockSize, @(1024)); // Seems to always be 1024.
- XCTAssertEqualObjects(blockIndex, @(lastBlockIndex + 1));
- XCTAssertEqualObjects(bytesToSkip, @(0)); // Don't expect to see skips here.
- // Make sure we actually end up with multiple blocks.
- XCTAssertTrue(blockSize.unsignedLongLongValue < rawImageSize);
-
- XCTAssertNotNil(readHandle);
- uint64_t offset = blockSize.unsignedLongLongValue * blockIndex.unsignedLongLongValue;
- NSError * seekError = nil;
- [readHandle seekToOffset:offset error:&seekError];
- XCTAssertNil(seekError);
-
- NSError * readError = nil;
- NSData * data = [readHandle readDataUpToLength:blockSize.unsignedLongValue error:&readError];
- XCTAssertNil(readError);
- XCTAssertNotNil(data);
-
- BOOL isEOF = offset + blockSize.unsignedLongValue >= imageSize;
-
- ++lastBlockIndex;
-
- if (isEOF) {
- sOTAProviderDelegate.blockQueryHandler = nil;
- }
-
- completion(data, isEOF);
-
- if (isEOF) {
- [bdxQueryExpectation fulfill];
- }
- };
- sOTAProviderDelegate.transferEndHandler = ^(NSNumber * nodeID, MTRDeviceController * controller, NSError * _Nullable error) {
- XCTAssertEqualObjects(nodeID, @(kDeviceId1));
- XCTAssertEqual(controller, sController);
- XCTAssertNil(error);
-
- sOTAProviderDelegate.transferEndHandler = nil;
- [bdxEndExpectation fulfill];
- };
- sOTAProviderDelegate.applyUpdateRequestHandler = ^(NSNumber * nodeID, MTRDeviceController * controller,
- MTROTASoftwareUpdateProviderClusterApplyUpdateRequestParams * params, ApplyUpdateRequestCompletion completion) {
- XCTAssertEqualObjects(nodeID, @(kDeviceId1));
- XCTAssertEqual(controller, sController);
- XCTAssertEqualObjects(params.updateToken, updateToken);
- XCTAssertEqualObjects(params.newVersion, kUpdatedSoftwareVersion); // TODO: Factor this out better!
-
- XCTAssertTrue([[NSFileManager defaultManager] contentsEqualAtPath:rawImagePath andPath:kOtaDownloadedFilePath1]);
-
- sOTAProviderDelegate.applyUpdateRequestHandler = nil;
- [sOTAProviderDelegate respondWithDiscontinueToApplyUpdateRequestWithCompletion:completion];
- [applyUpdateRequestExpectation fulfill];
- };
+ __auto_type * checker =
+ [[MTROTAProviderTransferChecker alloc] initWithRawImagePath:rawImagePath
+ nodeID:@(kDeviceId1)
+ applyUpdateAction:MTROTASoftwareUpdateProviderOTAApplyUpdateActionDiscontinue
+ testcase:self];
+ // We do not expect the update to actually be applied here.
+ checker.notifyUpdateAppliedExpectation.inverted = YES;
// Advertise ourselves as an OTA provider.
XCTestExpectation * announceResponseExpectation = [self announceProviderToDevice:device];
// Make sure we get our callbacks in order. Give it a bit more time, because
// we want to allow time for the BDX download.
- [self waitForExpectations:@[ queryExpectation, bdxBeginExpectation, bdxQueryExpectation ]
+ [self waitForExpectations:@[ checker.queryExpectation, checker.bdxBeginExpectation, checker.bdxQueryExpectation ]
timeout:(kTimeoutWithUpdateInSeconds) enforceOrder:YES];
// Nothing really defines the ordering of bdxEndExpectation and
// applyUpdateRequestExpectation with respect to each other, and nothing
// defines the ordering of announceResponseExpectation with respect to _any_
// of the above expectations.
- [self waitForExpectations:@[ bdxEndExpectation, applyUpdateRequestExpectation, announceResponseExpectation ]
+ [self waitForExpectations:@[ checker.bdxEndExpectation, checker.applyUpdateRequestExpectation, announceResponseExpectation ]
timeout:kTimeoutInSeconds];
+
+ // We are not expecting checker.notifyUpdateAppliedExpectation to actually
+ // be called fulfilled, but we still need to wait on it here. Since we set
+ // inverted = YES, on it, this is basically a no-op, except for making
+ // XCTest not complain about unwaited expectations.
+ [self waitForExpectations:@[ checker.notifyUpdateAppliedExpectation ] timeout:kTimeoutInSeconds];
}
// TODO: Enable this test when PR #26040 is merged. Currently the poll interval causes delays in the BDX transfer and
@@ -799,149 +844,33 @@
// 7) When device invokes ApplyUpdateRequest, respond with Proceed so that the update proceeds
// 8) Wait for the app to restart and wait for the NotifyUpdateApplied message to confirm the app has updated to the new version
- // These are the paths where the raw ota image file and the raw image file will be generated
- // as a pre-requisite to running the test.
+ // This test expects a pre-generated raw image at otaRawImagePath.
NSString * otaRawImagePath = @"/tmp/ota-test005-raw-image";
- NSString * otaImagePath = @"/tmp/ota-test005-image";
// Check if the ota raw image exists at kOtaRawImagePath
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:otaRawImagePath]);
- // Check if the ota image file is created at kOtaImagePath.
- XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:otaImagePath]);
-
__auto_type * device = sConnectedDevice1;
- XCTestExpectation * queryExpectation = [self expectationWithDescription:@"handleQueryImageForNodeID called"];
- XCTestExpectation * bdxBeginExpectation = [self expectationWithDescription:@"handleBDXTransferSessionBeginForNodeID called"];
- XCTestExpectation * bdxQueryExpectation = [self expectationWithDescription:@"handleBDXQueryForNodeID called"];
- XCTestExpectation * bdxEndExpectation = [self expectationWithDescription:@"handleBDXTransferSessionEndForNodeID called"];
- XCTestExpectation * applyUpdateRequestExpectation =
- [self expectationWithDescription:@"handleApplyUpdateRequestForNodeID called"];
- XCTestExpectation * notifyUpdateAppliedExpectation =
- [self expectationWithDescription:@"handleNotifyUpdateAppliedForNodeID called"];
-
- NSData * updateToken = [sOTAProviderDelegate generateUpdateToken];
-
- __block NSFileHandle * readHandle;
- __block uint64_t imageSize;
- __block uint32_t lastBlockIndex = UINT32_MAX;
-
- // TODO: Maybe we should move more of this logic into sOTAProviderDelegate
- // or some other helper, once we have multiple tests sending images? For
- // example, we could have something where you can do one of two things:
- //
- // 1) register a "raw image" with it, and it generates the
- // image-with header.
- // 2) register a pre-generated image with it and it uses "ota_image_tool.py
- // extract" to extract the raw image.
- //
- // Once that's done the helper could track the transfer state for a
- // particular image, etc, with us just forwarding our notifications to it.
- sOTAProviderDelegate.queryImageHandler = ^(NSNumber * nodeID, MTRDeviceController * controller,
- MTROTASoftwareUpdateProviderClusterQueryImageParams * params, QueryImageCompletion completion) {
- XCTAssertEqualObjects(nodeID, @(kDeviceId1));
- XCTAssertEqual(controller, sController);
-
- sOTAProviderDelegate.queryImageHandler = nil;
- [sOTAProviderDelegate respondAvailableWithDelay:@(0) uri:otaImagePath updateToken:updateToken completion:completion];
- [queryExpectation fulfill];
- };
- sOTAProviderDelegate.transferBeginHandler = ^(NSNumber * nodeID, MTRDeviceController * controller, NSString * fileDesignator,
- NSNumber * offset, MTRStatusCompletion completion) {
- XCTAssertEqualObjects(nodeID, @(kDeviceId1));
- XCTAssertEqual(controller, sController);
- XCTAssertEqualObjects(fileDesignator, otaImagePath);
- XCTAssertEqualObjects(offset, @(0));
-
- readHandle = [NSFileHandle fileHandleForReadingAtPath:fileDesignator];
- XCTAssertNotNil(readHandle);
-
- NSError * endSeekError;
- XCTAssertTrue([readHandle seekToEndReturningOffset:&imageSize error:&endSeekError]);
- XCTAssertNil(endSeekError);
-
- sOTAProviderDelegate.transferBeginHandler = nil;
- [sOTAProviderDelegate respondSuccess:completion];
- [bdxBeginExpectation fulfill];
- };
- sOTAProviderDelegate.blockQueryHandler = ^(NSNumber * nodeID, MTRDeviceController * controller, NSNumber * blockSize,
- NSNumber * blockIndex, NSNumber * bytesToSkip, BlockQueryCompletion completion) {
- XCTAssertEqualObjects(nodeID, @(kDeviceId1));
- XCTAssertEqual(controller, sController);
- XCTAssertEqualObjects(blockSize, @(1024)); // Seems to always be 1024.
- XCTAssertEqualObjects(blockIndex, @(lastBlockIndex + 1));
- XCTAssertEqualObjects(bytesToSkip, @(0)); // Don't expect to see skips here.
-
- XCTAssertNotNil(readHandle);
- uint64_t offset = blockSize.unsignedLongLongValue * blockIndex.unsignedLongLongValue;
- NSError * seekError = nil;
- [readHandle seekToOffset:offset error:&seekError];
- XCTAssertNil(seekError);
-
- NSError * readError = nil;
- NSData * data = [readHandle readDataUpToLength:blockSize.unsignedLongValue error:&readError];
- XCTAssertNil(readError);
- XCTAssertNotNil(data);
-
- BOOL isEOF = offset + blockSize.unsignedLongValue >= imageSize;
-
- ++lastBlockIndex;
-
- if (isEOF) {
- sOTAProviderDelegate.blockQueryHandler = nil;
- }
-
- completion(data, isEOF);
-
- if (isEOF) {
- [bdxQueryExpectation fulfill];
- }
- };
- sOTAProviderDelegate.transferEndHandler = ^(NSNumber * nodeID, MTRDeviceController * controller, NSError * _Nullable error) {
- XCTAssertEqualObjects(nodeID, @(kDeviceId1));
- XCTAssertEqual(controller, sController);
- XCTAssertNil(error);
-
- sOTAProviderDelegate.transferEndHandler = nil;
- [bdxEndExpectation fulfill];
- };
- sOTAProviderDelegate.applyUpdateRequestHandler = ^(NSNumber * nodeID, MTRDeviceController * controller,
- MTROTASoftwareUpdateProviderClusterApplyUpdateRequestParams * params, ApplyUpdateRequestCompletion completion) {
- XCTAssertEqualObjects(nodeID, @(kDeviceId1));
- XCTAssertEqual(controller, sController);
- XCTAssertEqualObjects(params.updateToken, updateToken);
- XCTAssertEqualObjects(params.newVersion, kUpdatedSoftwareVersion); // TODO: Factor this out better!
-
- XCTAssertTrue([[NSFileManager defaultManager] contentsEqualAtPath:otaRawImagePath andPath:kOtaDownloadedFilePath1]);
-
- sOTAProviderDelegate.applyUpdateRequestHandler = nil;
- [sOTAProviderDelegate respondWithProceedToApplyUpdateRequestWithCompletion:completion];
- [applyUpdateRequestExpectation fulfill];
- };
- sOTAProviderDelegate.notifyUpdateAppliedHandler = ^(NSNumber * nodeID, MTRDeviceController * controller,
- MTROTASoftwareUpdateProviderClusterNotifyUpdateAppliedParams * params, MTRStatusCompletion completion) {
- XCTAssertEqualObjects(nodeID, @(kDeviceId1));
- XCTAssertEqual(controller, sController);
- XCTAssertEqualObjects(params.updateToken, updateToken);
- XCTAssertEqualObjects(params.softwareVersion, kUpdatedSoftwareVersion);
-
- sOTAProviderDelegate.notifyUpdateAppliedHandler = nil;
- [sOTAProviderDelegate respondSuccess:completion];
- [notifyUpdateAppliedExpectation fulfill];
- };
+ __auto_type * checker =
+ [[MTROTAProviderTransferChecker alloc] initWithRawImagePath:otaRawImagePath
+ nodeID:@(kDeviceId1)
+ applyUpdateAction:MTROTASoftwareUpdateProviderOTAApplyUpdateActionProceed
+ testcase:self];
// Advertise ourselves as an OTA provider.
XCTestExpectation * announceResponseExpectation = [self announceProviderToDevice:device];
// Make sure we get our callbacks in order. Give it a bit more time, because
// we want to allow time for the BDX download.
- [self waitForExpectations:@[ queryExpectation, bdxBeginExpectation, bdxQueryExpectation, bdxEndExpectation ]
+ [self waitForExpectations:@[
+ checker.queryExpectation, checker.bdxBeginExpectation, checker.bdxQueryExpectation, checker.bdxEndExpectation
+ ]
timeout:(kTimeoutWithUpdateInSeconds) enforceOrder:YES];
// Nothing really defines the ordering of bdxEndExpectation and
// applyUpdateRequestExpectation with respect to each other.
- [self waitForExpectations:@[ applyUpdateRequestExpectation, notifyUpdateAppliedExpectation ]
+ [self waitForExpectations:@[ checker.applyUpdateRequestExpectation, checker.notifyUpdateAppliedExpectation ]
timeout:kTimeoutInSeconds
enforceOrder:YES];