Skip to content

Commit

Permalink
Record device logs using simctl
Browse files Browse the repository at this point in the history
  • Loading branch information
RainNapper committed Nov 20, 2020
1 parent 9f171b4 commit 9583582
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 4 deletions.
53 changes: 50 additions & 3 deletions bluepill/tests/BPIntegrationTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ @interface BPIntegrationTests : XCTestCase

@implementation BPIntegrationTests

- (BPConfiguration *)generateConfigWithVideoDir:(NSString *)videoDir {
- (BPConfiguration *)generateConfigWithOutputDir:(NSString *)outputDir withVideoDir:(NSString *)videoDir {
NSString *hostApplicationPath = [BPTestHelper sampleAppPath];
NSString *testBundlePath = [BPTestHelper sampleAppBalancingTestsBundlePath];
BPConfiguration *config = [[BPConfiguration alloc] initWithProgram:BP_MASTER];
Expand All @@ -36,14 +36,17 @@ - (BPConfiguration *)generateConfigWithVideoDir:(NSString *)videoDir {
config.deviceType = @BP_DEFAULT_DEVICE_TYPE;
config.headlessMode = YES;
config.quiet = [BPUtils isBuildScript];
if (outputDir != nil) {
config.outputDirectory = outputDir;
}
if (videoDir != nil) {
config.videosDirectory = videoDir;
}
return config;
}

- (BPConfiguration *)generateConfig {
return [self generateConfigWithVideoDir: nil];
return [self generateConfigWithOutputDir:nil withVideoDir:nil];
}

- (void)setUp {
Expand Down Expand Up @@ -231,7 +234,7 @@ - (void)testTwoBPInstancesWithVideo {

NSString* videoDirName = @"my_videos";
NSString *videoPath = [path stringByAppendingPathComponent:videoDirName];
BPConfiguration *config = [self generateConfigWithVideoDir:videoPath];
BPConfiguration *config = [self generateConfigWithOutputDir:path withVideoDir:videoPath];
config.numSims = @2;
config.errorRetriesCount = @1;
config.failureTolerance = @0;
Expand Down Expand Up @@ -278,4 +281,48 @@ - (void)testTwoBPInstancesWithVideo {
XCTAssertTrue(hasTest2);
}


- (void)testTwoBPInstancesForDeviceLogs {
NSError *mkdtempError;
NSString *path = [BPUtils mkdtemp:@"bpout" withError:&mkdtempError];
XCTAssertNil(mkdtempError);

BPConfiguration *config = [self generateConfigWithOutputDir:path withVideoDir:nil];
config.numSims = @2;
config.errorRetriesCount = @1;
config.failureTolerance = @0;
// This looks backwards but we want the main app to be the runner
// and the sampleApp is launched from the callback.
config.testBundlePath = [BPTestHelper sampleAppUITestBundlePath];
config.testRunnerAppPath = [BPTestHelper sampleAppPath];
config.appBundlePath = [BPTestHelper sampleAppUITestRunnerPath];

NSError *err;
BPApp *app = [BPApp appWithConfig:config
withError:&err];
NSString *bpPath = [BPTestHelper bpExecutablePath];

BPRunner *runner = [BPRunner BPRunnerWithConfig:config withBpPath:bpPath];
XCTAssert(runner != nil);
int rc = [runner runWithBPXCTestFiles:app.testBundles];
XCTAssert(rc == 0);
XCTAssert([runner.nsTaskList count] == 0);

NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *bp1Output = [fileManager contentsOfDirectoryAtPath:[path stringByAppendingPathComponent:@"BP-1"] error:nil];
NSArray *bp2Output = [fileManager contentsOfDirectoryAtPath:[path stringByAppendingPathComponent:@"BP-2"] error:nil];

NSString *testClass = @"BPSampleAppUITests";
NSSet *filenameSet1 = [NSSet setWithArray:bp1Output];
XCTAssertTrue([filenameSet1 containsObject:@"1-simulator.log"]);
NSString *testDeviceLog1 = [NSString stringWithFormat:@"%@__%@__1_system.log", testClass, @"testExample"];
XCTAssertTrue([filenameSet1 containsObject:testDeviceLog1]);

NSSet *filenameSet2 = [NSSet setWithArray:bp2Output];
XCTAssertTrue(([filenameSet2 containsObject:@"1-simulator.log"]));
NSString *testDeviceLog2 = [NSString stringWithFormat:@"%@__%@__1_system.log", testClass, @"testExample2"];
XCTAssertTrue([filenameSet2 containsObject:testDeviceLog2]);
}


@end
65 changes: 64 additions & 1 deletion bp/src/BPTestBundleConnection.m
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ @interface BPTestBundleConnection()<XCTestManager_IDEInterface>
@property (nonatomic, strong) NSString *bundleID;
@property (nonatomic, assign) pid_t appProcessPID;
@property (nonatomic, nullable) NSTask *recordVideoTask;
//@property (nonatomic, nullable) NSPipe *recordVideoPipe;
@property (nonatomic, nullable) NSTask *recordDeviceLogsTask;


@end
Expand Down Expand Up @@ -88,6 +88,7 @@ - (void)connect {
[connection registerDisconnectHandler:^{
// This is called when the task is abruptly terminated (e.g. if the test times out)
[self stopVideoRecording:YES];
[self stopDeviceLogRecording:YES];
[BPUtils printInfo:INFO withString:@"DTXConnection disconnected."];
}];
[connection
Expand Down Expand Up @@ -275,6 +276,59 @@ - (void)stopVideoRecording:(BOOL)forced
self.recordVideoTask = nil;
}

#pragma mark - Device Log Recording

- (BOOL)shouldRecordDeviceLog {
return self.context.config.outputDirectory != nil;
}

static inline NSString* getOutputDeviceLogForTestClass(NSString *directory, NSString *testClass, NSString *method, NSInteger attemptNumber) {
return [NSString stringWithFormat:@"%@/%@__%@__%ld_system.log", directory, testClass, method, (long)attemptNumber];
}

- (void)startDeviceLogRecordingForTestClass:(NSString *)testClass method:(NSString *)method {
[self stopDeviceLogRecording:YES];
NSString *deviceLogForTestPath = getOutputDeviceLogForTestClass(self.context.config.outputDirectory, testClass, method, self.context.attemptNumber);
NSString *command = [NSString stringWithFormat:@"xcrun simctl spawn %@ log stream --style compact --process PinterestDevelopmentEG2 > %@", [self.simulator UDID], deviceLogForTestPath];
NSTask *task = [BPUtils buildShellTaskForCommand:command];
self.recordDeviceLogsTask = task;
[task launch];
[BPUtils printInfo:INFO withString:@"Started recording device logs to %@", deviceLogForTestPath];
[BPUtils printInfo:DEBUGINFO withString:@"Started recording device with pid %d and command: %@", [task processIdentifier], [BPUtils getCommandStringForTask:task]];
}

- (void)stopDeviceLogRecording:(BOOL)forced {
NSTask *task = self.recordDeviceLogsTask;
if (task == nil) {
if (!forced) {
[BPUtils printInfo:ERROR withString: @"Tried to end device log task normally, but there was no task."];
}
return;
}

if (forced) {
[BPUtils printInfo:ERROR withString: @"Found dangling device log recording task. Stopping it."];
}

if (![task isRunning]) {
[BPUtils printInfo:ERROR withString:@"Device log task exists but it was not running!"];
} else {
[BPUtils printInfo:INFO withString:@"Stopping device log recording."];
[BPUtils printInfo:DEBUGINFO withString:@"Stopping device log recording task with pid %d and command: %@", [task processIdentifier], [BPUtils getCommandStringForTask:task]];
[task interrupt];
[task waitUntilExit];
}


NSString *filePath = [[task arguments].lastObject componentsSeparatedByString:@" "].lastObject;
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
[BPUtils printInfo:ERROR withString:@"Device log recording file missing, expected at path %@!", filePath];
}

self.recordDeviceLogsTask = nil;
}


#pragma mark - XCTestManager_IDEInterface protocol

#pragma mark Process Launch Delegation
Expand Down Expand Up @@ -343,6 +397,9 @@ - (id)_XCT_testCaseDidStartForTestClass:(NSString *)testClass method:(NSString *
if ([self shouldRecordVideo]) {
[self startVideoRecordingForTestClass:testClass method:method];
}
if ([self shouldRecordDeviceLog]) {
[self startDeviceLogRecordingForTestClass:testClass method:method];
}
return nil;
}

Expand All @@ -367,6 +424,9 @@ - (id)_XCT_testCaseDidFinishForTestClass:(NSString *)testClass method:(NSString
if ([self shouldRecordVideo]) {
[self stopVideoRecording:NO];
}
if ([self shouldRecordDeviceLog]) {
[self stopDeviceLogRecording:NO];
}
return nil;
}

Expand All @@ -376,6 +436,9 @@ - (id)_XCT_testSuite:(NSString *)arg1 didFinishAt:(NSString *)time runCount:(NSN
if ([self shouldRecordVideo]) {
[self stopVideoRecording:YES];
}
if ([self shouldRecordDeviceLog]) {
[self stopDeviceLogRecording:YES];
}
return nil;
}

Expand Down

0 comments on commit 9583582

Please sign in to comment.