Skip to content

Commit

Permalink
Factor out support for running helper server apps in Darwin tests. (#…
Browse files Browse the repository at this point in the history
…33506)

Switches MTROTAProviderTests to using the new factored-out bits.  Other tests
will be switched to those in a separate PR.
  • Loading branch information
bzbarsky-apple authored and pull[bot] committed Aug 27, 2024
1 parent 99c9bcb commit 61d4ac8
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 119 deletions.
131 changes: 13 additions & 118 deletions src/darwin/Framework/CHIPTests/MTROTAProviderTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@

#import "MTRDeviceTestDelegate.h"
#import "MTRErrorTestUtils.h"
#import "MTRTestCase.h"
#import "MTRTestKeys.h"
#import "MTRTestResetCommissioneeHelper.h"
#import "MTRTestServerAppRunner.h"
#import "MTRTestStorage.h"

// system dependencies
Expand Down Expand Up @@ -73,35 +75,21 @@

static NSString * kUpdatedSoftwareVersionString_10 = @"10.0";

// kOtaRequestorBasePort gets the discriminator added to it to figure out the
// port the ota-requestor app should be using. This ensures that apps with
// distinct discriminators use distinct ports.
static const uint16_t kOtaRequestorBasePort = 5542 - 1111;

@class MTROTARequestorAppRunner;

@interface MTROTAProviderTests : XCTestCase
- (NSTask *)createTaskForPath:(NSString *)path;
@interface MTROTAProviderTests : MTRTestCase
- (NSString *)createImageFromRawImage:(NSString *)rawImage withVersion:(NSNumber *)version;
- (MTRDevice *)commissionDeviceWithPayload:(NSString *)payloadString nodeID:(NSNumber *)nodeID;
- (void)registerRunningRequestor:(MTROTARequestorAppRunner *)requestor;
@end

static unsigned sAppRunnerIndex = 1;

@interface MTROTARequestorAppRunner : NSObject
@interface MTROTARequestorAppRunner : MTRTestServerAppRunner
@property (nonatomic, copy) NSString * downloadFilePath;

- (instancetype)initWithPayload:(NSString *)payload testcase:(MTROTAProviderTests *)testcase;
- (MTRDevice *)commissionWithNodeID:(NSNumber *)nodeID;
@end

@implementation MTROTARequestorAppRunner {
unsigned _uniqueIndex;
NSTask * _appTask;
MTROTAProviderTests * _testcase;
NSString * _payload;
MTRDevice * commissionedDevice;
}

- (MTRDevice *)commissionWithNodeID:(NSNumber *)nodeID
Expand All @@ -111,67 +99,24 @@ - (MTRDevice *)commissionWithNodeID:(NSNumber *)nodeID

- (instancetype)initWithPayload:(NSString *)payload testcase:(MTROTAProviderTests *)testcase
{
if (!(self = [super init])) {
return nil;
}

_uniqueIndex = sAppRunnerIndex++;
_testcase = testcase;
_payload = payload;
_downloadFilePath = [NSString stringWithFormat:@"/tmp/chip-ota-requestor-downloaded-image%u", _uniqueIndex];

NSError * error;
__auto_type * parsedPayload = [MTRSetupPayload setupPayloadWithOnboardingPayload:payload error:&error];
XCTAssertNotNil(parsedPayload);
XCTAssertNil(error);

XCTAssertFalse(parsedPayload.hasShortDiscriminator);

__auto_type * discriminator = parsedPayload.discriminator;

_appTask = [testcase createTaskForPath:@"out/debug/ota-requestor-app/chip-ota-requestor-app"];

__auto_type * arguments = @[
@"--interface-id",
@"-1",
@"--secured-device-port",
[NSString stringWithFormat:@"%u", kOtaRequestorBasePort + discriminator.unsignedShortValue],
@"--discriminator",
[NSString stringWithFormat:@"%u", discriminator.unsignedShortValue],
@"--KVS",
[NSString stringWithFormat:@"/tmp/chip-ota-requestor-kvs%u", _uniqueIndex],
__auto_type * downloadFilePath = [NSString stringWithFormat:@"/tmp/chip-ota-requestor-downloaded-image%u", [MTRTestServerAppRunner nextUniqueIndex]];
__auto_type * extraArguments = @[
@"--otaDownloadPath",
_downloadFilePath,
downloadFilePath,
@"--autoApplyImage",
];

[_appTask setArguments:arguments];

NSString * outFile = [NSString stringWithFormat:@"/tmp/darwin/framework-tests/ota-requestor-app-%u.log", _uniqueIndex];
NSString * errorFile = [NSString stringWithFormat:@"/tmp/darwin/framework-tests/ota-requestor-app-err-%u.log", _uniqueIndex];

// Make sure the files exist.
[[NSFileManager defaultManager] createFileAtPath:outFile contents:nil attributes:nil];
[[NSFileManager defaultManager] createFileAtPath:errorFile contents:nil attributes:nil];

_appTask.standardOutput = [NSFileHandle fileHandleForWritingAtPath:outFile];
_appTask.standardError = [NSFileHandle fileHandleForWritingAtPath:errorFile];

[_appTask launchAndReturnError:&error];
XCTAssertNil(error);

NSLog(@"Started requestor with arguments %@ stdout=%@ and stderr=%@", arguments, outFile, errorFile);
if (!(self = [super initWithAppName:@"ota-requestor" arguments:extraArguments payload:payload testcase:testcase])) {
return nil;
}

[_testcase registerRunningRequestor:self];
_testcase = testcase;
_payload = payload;
_downloadFilePath = downloadFilePath;

return self;
}

- (void)terminate
{
[_appTask terminate];
}

@end

@interface MTROTAProviderTestControllerDelegate : NSObject <MTRDeviceControllerDelegate>
Expand Down Expand Up @@ -579,7 +524,6 @@ - (instancetype)initWithRawImagePath:(NSString *)rawImagePath

@implementation MTROTAProviderTests {
NSMutableSet<NSNumber *> * _commissionedNodeIDs;
NSMutableSet<MTROTARequestorAppRunner *> * _runningRequestors;
}

+ (void)tearDown
Expand All @@ -605,7 +549,6 @@ - (void)setUp
}

_commissionedNodeIDs = [[NSMutableSet alloc] init];
_runningRequestors = [[NSMutableSet alloc] init];

XCTAssertNil(sOTAProviderDelegate.queryImageHandler);
XCTAssertNil(sOTAProviderDelegate.applyUpdateRequestHandler);
Expand Down Expand Up @@ -637,12 +580,6 @@ - (void)tearDown
ResetCommissionee(device, dispatch_get_main_queue(), self, kTimeoutInSeconds);
}

for (MTROTARequestorAppRunner * runner in _runningRequestors) {
[runner terminate];
}
// Break cycle.
_runningRequestors = nil;

if (sController != nil) {
[sController shutdown];
XCTAssertFalse([sController isRunning]);
Expand Down Expand Up @@ -686,11 +623,6 @@ - (MTRDevice *)commissionDeviceWithPayload:(NSString *)payloadString nodeID:(NSN
return [MTRDevice deviceWithNodeID:nodeID controller:sController];
}

- (void)registerRunningRequestor:(MTROTARequestorAppRunner *)requestor
{
[_runningRequestors addObject:requestor];
}

- (void)initStack
{
sStackInitRan = YES;
Expand All @@ -717,43 +649,6 @@ + (void)shutdownStack
[[MTRDeviceControllerFactory sharedInstance] stopControllerFactory];
}

/**
* Given a path relative to the Matter root, create an absolute path to the file.
*/
- (NSString *)absolutePathFor:(NSString *)matterRootRelativePath
{
// Start with the absolute path to our file, then remove the suffix that
// comes after the path to the Matter SDK root.
NSString * pathToTest = [NSString stringWithUTF8String:__FILE__];
NSMutableArray * pathComponents = [[NSMutableArray alloc] init];
[pathComponents addObject:[pathToTest substringToIndex:(pathToTest.length - @"src/darwin/Framework/CHIPTests/MTROTAProviderTests.m".length)]];
[pathComponents addObjectsFromArray:[matterRootRelativePath pathComponents]];
return [NSString pathWithComponents:pathComponents];
}

/**
* Create a task given a path relative to the Matter root.
*/
- (NSTask *)createTaskForPath:(NSString *)path
{
NSTask * task = [[NSTask alloc] init];
[task setLaunchPath:[self absolutePathFor:path]];
return task;
}

/**
* Runs a task to completion and makes sure it succeeds.
*/
- (void)runTask:(NSTask *)task
{
NSError * launchError;
[task launchAndReturnError:&launchError];
XCTAssertNil(launchError);

[task waitUntilExit];
XCTAssertEqual([task terminationStatus], 0);
}

/**
* Returns path to the raw image.
*/
Expand Down
31 changes: 31 additions & 0 deletions src/darwin/Framework/CHIPTests/TestHelpers/MTRTestCase.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,44 @@

#import <XCTest/XCTest.h>

#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
#define HAVE_NSTASK 0
#else
#define HAVE_NSTASK 1
#endif

NS_ASSUME_NONNULL_BEGIN

@interface MTRTestCase : XCTestCase
// It would be nice to do the leak-detection automatically, but running "leaks"
// on every single sub-test is slow, and some of our tests seem to have leaks
// outside Matter.framework. So have it be opt-in for now, and improve later.
@property (nonatomic) BOOL detectLeaks;

#if HAVE_NSTASK
/**
* Create an NSTask for the given path. Path should be relative to the Matter
* SDK root.
*/
- (NSTask *)createTaskForPath:(NSString *)path;

/**
* Run a task to completion and make sure it succeeds.
*/
- (void)runTask:(NSTask *)task;

/**
* Launch a task. The task will be automatically terminated when the testcase
* tearDown happens.
*/
- (void)launchTask:(NSTask *)task;
#endif // HAVE_NSTASK

/**
* Get an absolute path from a path relative to the Matter SDK root.
*/
- (NSString *)absolutePathFor:(NSString *)matterRootRelativePath;

@end

NS_ASSUME_NONNULL_END
59 changes: 58 additions & 1 deletion src/darwin/Framework/CHIPTests/TestHelpers/MTRTestCase.mm
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,18 @@

#import "MTRTestCase.h"

@implementation MTRTestCase
@implementation MTRTestCase {
#if HAVE_NSTASK
NSMutableSet<NSTask *> * _runningTasks;
#endif // NSTask
}

- (void)setUp
{
#if HAVE_NSTASK
_runningTasks = [[NSMutableSet alloc] init];
#endif // HAVE_NSTASK
}

/**
* Unfortunately, doing this in "+ (void)tearDown" (the global suite teardown)
Expand All @@ -36,7 +47,53 @@ - (void)tearDown
}
#endif

#if HAVE_NSTASK
for (NSTask * task in _runningTasks) {
[task terminate];
}
_runningTasks = nil;
#endif // HAVE_NSTASK

[super tearDown];
}

#if HAVE_NSTASK
- (NSTask *)createTaskForPath:(NSString *)path
{
NSTask * task = [[NSTask alloc] init];
[task setLaunchPath:[self absolutePathFor:path]];
return task;
}

- (void)runTask:(NSTask *)task
{
NSError * launchError;
[task launchAndReturnError:&launchError];
XCTAssertNil(launchError);

[task waitUntilExit];
XCTAssertEqual([task terminationStatus], 0);
}

- (void)launchTask:(NSTask *)task
{
NSError * launchError;
[task launchAndReturnError:&launchError];
XCTAssertNil(launchError);

[_runningTasks addObject:task];
}
#endif // HAVE_NSTASK

- (NSString *)absolutePathFor:(NSString *)matterRootRelativePath
{
// Start with the absolute path to our file, then remove the suffix that
// comes after the path to the Matter SDK root.
NSString * pathToTest = [NSString stringWithUTF8String:__FILE__];
NSMutableArray * pathComponents = [[NSMutableArray alloc] init];
[pathComponents addObject:[pathToTest substringToIndex:(pathToTest.length - @"src/darwin/Framework/CHIPTests/TestHelpers/MTRTestCase.mm".length)]];
[pathComponents addObjectsFromArray:[matterRootRelativePath pathComponents]];
return [NSString pathWithComponents:pathComponents];
}

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Copyright (c) 2024 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.
*/

#import <Foundation/Foundation.h>

@class MTRTestCase;

NS_ASSUME_NONNULL_BEGIN

/**
* A representation of a server application instance.
*
* Server applications are assumed to be compiled into out/debug/${APPNAME}-app,
* with the binary being out/debug/${APPNAME}-app/chip-${APPNAME}-app.
*/
@interface MTRTestServerAppRunner : NSObject

/**
* Initialize the app runner with the given app name, arguments, setup payload, and testcase
* instance.
*
* The payload will be used to determine the discriminator and passcode
* arguments the app should use, in addition to the provided arguments.
* Discriminators should be at least 1111 (see documentation for port below).
*
* The --KVS argument for the app will be automatically set to something of the
* format "/tmp/chip-${APPNAME}-kvs${UNIQUE_ID}".
*
* The port argument for the app will be determined automatically, by
* subtracting 1111 from the discriminator and adding 5542 (so as not to collide
* with any existing Matter things running on 5540/5541).
*/
- (instancetype)initWithAppName:(NSString *)name arguments:(NSArray<NSString *> *)arguments payload:(NSString *)payload testcase:(MTRTestCase *)testcase;

/**
* Get the unique index that will be used for the next initialization. This
* allows including that index in the arguments provided.
*
* TODO: Should we scan the provided arguments for %u and replace with the index
* instead?
*/
+ (unsigned)nextUniqueIndex;

@end

NS_ASSUME_NONNULL_END
Loading

0 comments on commit 61d4ac8

Please sign in to comment.