Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix hangs #49

Merged
merged 9 commits into from
Feb 7, 2017
11 changes: 11 additions & 0 deletions BPSampleApp/BPSampleApp/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
// Override point for customization after application launch.

// This is a sample app for Bluepill testing.
if (getenv("_BP_TEST_CRASH_ON_LAUNCH")) {
char *p = NULL;
NSLog(@"CRASHING AT USER'S REQUEST");
strcpy(p, "I know this will crash my app");
}
if (getenv("_BP_TEST_HANG_ON_LAUNCH")) {
NSLog(@"HANGING AT USER'S REQUEST");
while(1) {
sleep(10);
}
}
return YES;
}

Expand Down
34 changes: 25 additions & 9 deletions Bluepill-cli/BPInstanceTests/BluepillTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ - (void)setUp {
self.config.jsonOutput = NO;
self.config.headlessMode = NO;
self.config.junitOutput = NO;
self.config.testing_NoAppWillRun = YES;
NSString *path = @"testScheme.xcscheme";
self.config.schemePath = [[[NSBundle bundleForClass:[self class]] resourcePath] stringByAppendingPathComponent:path];
[BPUtils quietMode:YES];
Expand Down Expand Up @@ -78,17 +79,32 @@ - (void)tearDown {
[super tearDown];
}

// This is a template to run Voyager's tests
- (void)testVoyager {
// self.config.outputDirectory = @"/Users/khu/tmp/simulator";
// self.config.schemePath = @"/Users/khu/ios/mntf-ios-sample-app_trunk/./mntf-ios-sample-app.xcodeproj/xcshareddata/xcschemes/mntf-ios-sample-app-ui-tests.xcscheme";
// self.config.testCasesToRun = @[@"MNTFSampleAppUITests/testRotate", @"MNTFSampleAppUITests/testScrollToAnIndex"];
// self.config.appBundlePath =
// @"/Users/khu/ios/mntf-ios-sample-app_trunk/build/mntf-ios-sample-app/Build/Products/Debug-iphonesimulator/mntf-ios-sample-app.app";
// self.config.testBundlePath = @"/Users/khu/ios/mntf-ios-sample-app_trunk/build/mntf-ios-sample-app/Build/Products/Debug-iphonesimulator/mntf-ios-sample-app.app/PlugIns/mntf-ios-sample-app-UITests.xctest";
// BPExitStatus exitCode = [[[Bluepill alloc ] initWithConfiguration:self.config] run];
- (void)testAppThatCrashesOnLaunch {
NSString *testBundlePath = [BPTestHelper sampleAppBalancingTestsBunldePath];
self.config.testBundlePath = testBundlePath;
self.config.testing_CrashAppOnLaunch = YES;
self.config.testing_NoAppWillRun = NO;
BPExitStatus exitCode = [[[Bluepill alloc ] initWithConfiguration:self.config] run];
NSLog(@"%ld", (long)exitCode);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove these NSLog (and the one in the next function).

XCTAssert(exitCode == BPExitStatusAppCrashed);

self.config.testing_NoAppWillRun = YES;
}

- (void)testAppThatHangsOnLaunch {
NSString *testBundlePath = [BPTestHelper sampleAppBalancingTestsBunldePath];
self.config.testBundlePath = testBundlePath;
self.config.testing_HangAppOnLaunch = YES;
self.config.testing_NoAppWillRun = NO;
self.config.stuckTimeout = @3;
BPExitStatus exitCode = [[[Bluepill alloc] initWithConfiguration:self.config] run];
NSLog(@"%ld", (long)exitCode);
XCTAssert(exitCode == BPExitStatusTestTimeout);

self.config.testing_NoAppWillRun = YES;
}


- (void)testRunningOnlyCertainTestcases {
NSString *testBundlePath = [BPTestHelper sampleAppBalancingTestsBunldePath];
self.config.testBundlePath = testBundlePath;
Expand Down
3 changes: 2 additions & 1 deletion Bluepill-cli/Bluepill-cli/Bluepill/BPStats.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@
- (void)addTestFailure;
- (void)addTestError;
- (void)addSimulatorCrash;
- (void)addApplicationCrash;
- (void)addRetry;
- (void)addTestRuntimeTimeout;
- (void)addTestBPExitStatusTestTimeout;
- (void)addTestOutputTimeout;
- (void)addSimulatorCreateFailure;
- (void)addSimulatorDeleteFailure;
- (void)addSimulatorInstallFailure;
Expand Down
14 changes: 10 additions & 4 deletions Bluepill-cli/Bluepill-cli/Bluepill/BPStats.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ @interface BPStats()
@property (nonatomic, assign) NSInteger testsTotal;
@property (nonatomic, assign) NSInteger testFailures;
@property (nonatomic, assign) NSInteger testErrors;
@property (nonatomic, assign) NSInteger crashes;
@property (nonatomic, assign) NSInteger simCrashes;
@property (nonatomic, assign) NSInteger appCrashes;
@property (nonatomic, assign) NSInteger retries;
@property (nonatomic, assign) NSInteger runtimeTimeout;
@property (nonatomic, assign) NSInteger outputTimeout;
Expand Down Expand Up @@ -133,7 +134,11 @@ - (void)addTestError {
}

- (void)addSimulatorCrash {
self.crashes++;
self.simCrashes++;
}

- (void)addApplicationCrash {
self.appCrashes++;
}

- (void)addRetry {
Expand All @@ -144,7 +149,7 @@ - (void)addTestRuntimeTimeout {
self.runtimeTimeout++;
}

- (void)addTestBPExitStatusTestTimeout {
- (void)addTestOutputTimeout {
self.outputTimeout++;
}

Expand Down Expand Up @@ -188,7 +193,8 @@ - (void)generateFullReportWithWriter:(BPWriter *)writer exitCode:(int)exitCode {
[writer writeLine:@"Timeout due to test run-time: %d", self.runtimeTimeout];
[writer writeLine:@"Timeout due to no output: %d", self.outputTimeout];
[writer writeLine:@"Retries: %d", self.retries];
[writer writeLine:@"Simulator Crashes: %d", self.crashes];
[writer writeLine:@"Application Crashes: %d", self.appCrashes];
[writer writeLine:@"Simulator Crashes: %d", self.simCrashes];
[writer writeLine:@"Simulator Creation Failures: %d", self.simulatorCreateFailures];
[writer writeLine:@"Simulator Deletion Failures: %d", self.simulatorDeleteFailures];
[writer writeLine:@"App Install Failures: %d", self.simulatorInstallFailures];
Expand Down
14 changes: 14 additions & 0 deletions Bluepill-cli/Bluepill-cli/Bluepill/Bluepill.m
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,20 @@ - (void)checkProcessWithContext:(BPExecutionContext *)context {
return;
}

// This check should be last after all of the more specific tests
// It checks if the app is even running, which it must be at this point
// If it's not running and we passed the above checks (e.g., the tests are not yet completed)
// then it must mean the app has crashed.
// However, we have a short-circuit for tests because those may not actually run any app
if (!isRunning && context.pid > 0 && !self.config.testing_NoAppWillRun) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The testing_NoAppWillRun variable is for the parsing tests right? I would then maybe also use it in the kill() below where I am just looking for self.appPID == 0 for the same case.

// The tests ended before they even got started or the process is gone for some other reason
[[BPStats sharedStats] endTimer:RUN_TESTS(context.attemptNumber)];
[BPUtils printInfo:ERROR withString:@"Application crashed before tests started!"];
[[BPStats sharedStats] addApplicationCrash];
[self deleteSimulatorWithContext:context andStatus:BPExitStatusAppCrashed];
return;
}

NEXT([self checkProcessWithContext:context]);
}

Expand Down
21 changes: 15 additions & 6 deletions Bluepill-cli/Bluepill-cli/Simulator/SimulatorMonitor.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

typedef NS_ENUM(NSInteger, SimulatorState) {
Idle,
AppLaunched,
Running,
Completed
};
Expand Down Expand Up @@ -172,6 +173,10 @@ - (void)onTestSuiteEnded:(NSString *)testSuiteName
- (void)onOutputReceived:(NSString *)output {
NSDate *currentTime = [NSDate date];

if (self.simulatorState == Idle) {
self.simulatorState = AppLaunched;
}

self.currentOutputId++; // Increment the Output ID for this instance since we've moved on to the next bit of output

__block NSUInteger previousOutputId = self.currentOutputId;
Expand All @@ -186,20 +191,24 @@ - (void)onOutputReceived:(NSString *)output {
forTestName:(__self.currentTestName ?: __self.previousTestName)
inClass:(__self.currentClassName ?: __self.previousClassName)];
__self.exitStatus = BPExitStatusAppCrashed;
[[BPStats sharedStats] addSimulatorCrash];
[[BPStats sharedStats] addApplicationCrash];
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(__self.maxTimeWithNoOutput * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (__self.currentOutputId == previousOutputId && __self.simulatorState == Running) {
if (__self.currentOutputId == previousOutputId && (__self.simulatorState == Running || __self.simulatorState == AppLaunched)) {
NSString *testClass = (__self.currentClassName ?: __self.previousClassName);
NSString *testName = (__self.currentTestName ?: __self.previousTestName);
[BPUtils printInfo:TIMEOUT withString:@" %10.6fs waiting for output from %@/%@",
__self.maxTimeWithNoOutput, testClass, testName];
if (testClass == nil && testName == nil && (__self.simulatorState == AppLaunched || __self.simulatorState == Idle)) {
[BPUtils printInfo:ERROR withString:@"It appears that tests have not yet started. The test app has frozen prior to the first test."];
} else {
[BPUtils printInfo:TIMEOUT withString:@" %10.6fs waiting for output from %@/%@",
__self.maxTimeWithNoOutput, testClass, testName];
[[BPStats sharedStats] endTimer:[NSString stringWithFormat:TEST_CASE_FORMAT, [BPStats sharedStats].attemptNumber, testClass, testName]];
}
[__self stopTestsWithErrorMessage:@"Timed out waiting for the test to produce output. Test was aboorted."
forTestName:testName
inClass:testClass];
__self.exitStatus = BPExitStatusTestTimeout;
[[BPStats sharedStats] endTimer:[NSString stringWithFormat:TEST_CASE_FORMAT, [BPStats sharedStats].attemptNumber, testClass, testName]];
[[BPStats sharedStats] addTestBPExitStatusTestTimeout];
[[BPStats sharedStats] addTestOutputTimeout];
}
});
self.lastOutput = currentTime;
Expand Down
7 changes: 7 additions & 0 deletions Bluepill-cli/Bluepill-cli/Simulator/SimulatorRunner.m
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,13 @@ - (void)launchApplicationAndExecuteTestsWithParser:(BPTreeParser *)parser andCom
config:self.config]];
[appLaunchEnvironment addEntriesFromDictionary:argsAndEnv[@"env"]];

if (self.config.testing_CrashAppOnLaunch) {
appLaunchEnvironment[@"_BP_TEST_CRASH_ON_LAUNCH"] = @"YES";
}
if (self.config.testing_HangAppOnLaunch) {
appLaunchEnvironment[@"_BP_TEST_HANG_ON_LAUNCH"] = @"YES";
}

// Intercept stdout, stderr and post as simulator-output events
NSString *stdout_stderr = [NSString stringWithFormat:@"%@/tmp/stdout_stderr_%@", self.device.dataPath, [[self.device UDID] UUIDString]];
NSString *simStdoutPath = [BPUtils mkstemp:stdout_stderr withError:nil];
Expand Down
10 changes: 10 additions & 0 deletions Source/Shared/BPConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@

@interface BPConfiguration : NSObject <NSCopying>

/*
* WARNING: Any fields you add here need to be explicitly handled in the copyWithZone
* and mutableCopyWithZone methods. Yeah, it's stupid, we should fix it.
*/

@property (nonatomic, strong) NSString *appBundlePath;
@property (nonatomic, strong) NSString *testBundlePath;
@property (nonatomic, strong) NSArray *additionalTestBundles;
Expand Down Expand Up @@ -47,6 +52,11 @@
@property (nonatomic, strong) NSNumber *listTestsOnly;
@property (nonatomic) BOOL quiet;

// These fields are for testing.
@property (nonatomic) BOOL testing_CrashAppOnLaunch;
@property (nonatomic) BOOL testing_HangAppOnLaunch;
@property (nonatomic) BOOL testing_NoAppWillRun;

// Generated fields
@property (nonatomic, strong) NSString *xcodePath;

Expand Down
3 changes: 3 additions & 0 deletions Source/Shared/BPConfiguration.m
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,9 @@ - (id)mutableCopyWithZone:(NSZone *)zone {
newConfig.simDeviceType = self.simDeviceType;
#endif
newConfig.xcodePath = self.xcodePath;
newConfig.testing_CrashAppOnLaunch = self.testing_CrashAppOnLaunch;
newConfig.testing_HangAppOnLaunch = self.testing_HangAppOnLaunch;
newConfig.testing_NoAppWillRun = self.testing_NoAppWillRun;

return newConfig;
}
Expand Down