diff --git a/Bluepill-cli/BPInstanceTests/BPTestHelper.h b/Bluepill-cli/BPInstanceTests/BPTestHelper.h index aa8cf1f2..2f4c33ed 100644 --- a/Bluepill-cli/BPInstanceTests/BPTestHelper.h +++ b/Bluepill-cli/BPInstanceTests/BPTestHelper.h @@ -53,6 +53,9 @@ // Return the sample photo path. + (NSString *)samplePhotoPath; +// Return the sample script path. ++ (NSString *)sampleScriptPath; + // Return the path to the Debug iphone-simulator folder + (NSString *)debugIphoneSimulatorPath; diff --git a/Bluepill-cli/BPInstanceTests/BPTestHelper.m b/Bluepill-cli/BPInstanceTests/BPTestHelper.m index c508ade9..037042d8 100644 --- a/Bluepill-cli/BPInstanceTests/BPTestHelper.m +++ b/Bluepill-cli/BPInstanceTests/BPTestHelper.m @@ -64,6 +64,10 @@ + (NSString *)samplePhotoPath { return [[[NSBundle bundleForClass:[self class]] resourcePath] stringByAppendingPathComponent:@"image.png"]; } ++ (NSString *)sampleScriptPath { + return [[[NSBundle bundleForClass:[self class]] resourcePath] stringByAppendingPathComponent:@"test-script.sh"]; +} + + (NSString *)bpExecutablePath { return [[self debugMacOSPath] stringByAppendingPathComponent:@"bp"]; } diff --git a/Bluepill-cli/BPInstanceTests/BluepillTests.m b/Bluepill-cli/BPInstanceTests/BluepillTests.m index 87ac7f6b..dc207e84 100644 --- a/Bluepill-cli/BPInstanceTests/BluepillTests.m +++ b/Bluepill-cli/BPInstanceTests/BluepillTests.m @@ -477,6 +477,38 @@ - (void)testCopySimulatorPreferencesFile { XCTAssert([[NSDictionary alloc] initWithContentsOfURL:preferencesFile] == nil); } +- (void)testRunScript { + self.config.scriptFilePath = [BPTestHelper sampleScriptPath]; + + NSString *testBundlePath = [BPTestHelper sampleAppBalancingTestsBundlePath]; + self.config.testBundlePath = testBundlePath; + self.config.keepSimulator = YES; + + Bluepill *bp = [[Bluepill alloc ] initWithConfiguration:self.config]; + BPExitStatus exitCode = [bp run]; + XCTAssert(exitCode == BPExitStatusTestsAllPassed); + XCTAssertNotNil(bp.test_simulatorUDID); + + NSString *devicePath = bp.test_simulator.device.devicePath; + NSString *deviceID = bp.test_simulator.device.UDID.UUIDString; + + // The test script will create $(DEVICE_ID).txt in the device path + NSString *testFile = [NSString stringWithFormat:@"%@/%@.txt", devicePath, deviceID]; + XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:testFile]); + + self.config.deleteSimUDID = bp.test_simulatorUDID; + XCTAssertNotNil(self.config.deleteSimUDID); + + Bluepill *bp2 = [[Bluepill alloc ] initWithConfiguration:self.config]; + BPExitStatus exitCode2 = [bp2 run]; + XCTAssert(exitCode2 == BPExitStatusSimulatorDeleted); + XCTAssertEqualObjects(self.config.deleteSimUDID, bp2.test_simulatorUDID); + + if ([[NSFileManager defaultManager] fileExistsAtPath:testFile]) { + [[NSFileManager defaultManager] removeItemAtPath:testFile error:nil]; + XCTFail(@"%@ was not deleted when the simulator was deleted", testFile); + } +} - (void)testThatScreenshotAreNotTakenWithFailingTestsSetWithoutConfigOption { NSString *tempDir = NSTemporaryDirectory(); diff --git a/Bluepill-cli/BPInstanceTests/Resource Files/test-script.sh b/Bluepill-cli/BPInstanceTests/Resource Files/test-script.sh new file mode 100755 index 00000000..ff7aabd6 --- /dev/null +++ b/Bluepill-cli/BPInstanceTests/Resource Files/test-script.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +# This is a script to test --script-file + +touch "$BP_DEVICE_PATH/$BP_DEVICE_ID.txt" diff --git a/Bluepill-cli/Bluepill-cli.xcodeproj/project.pbxproj b/Bluepill-cli/Bluepill-cli.xcodeproj/project.pbxproj index 8c24b0b7..34530902 100644 --- a/Bluepill-cli/Bluepill-cli.xcodeproj/project.pbxproj +++ b/Bluepill-cli/Bluepill-cli.xcodeproj/project.pbxproj @@ -73,6 +73,7 @@ B3DAF83C2151CB4100210286 /* BPDeleteSimulatorHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A7E7BBB1DF21749007928F3 /* BPDeleteSimulatorHandler.m */; }; B3DAF83D2151CB7000210286 /* BPHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A7E7BAF1DF2066B007928F3 /* BPHandler.m */; }; B3DAF83E2151CB9700210286 /* BPWaitTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ACE1F6E1DD397F800C0FA73 /* BPWaitTimer.m */; }; + B848CF51217E39060043D826 /* test-script.sh in Resources */ = {isa = PBXBuildFile; fileRef = B848CF4F217E38FB0043D826 /* test-script.sh */; }; BA0096FE1DCA5D810000DD45 /* testConfigRelativePath.json in Resources */ = {isa = PBXBuildFile; fileRef = BA0096FD1DCA5D810000DD45 /* testConfigRelativePath.json */; }; BA0097001DCA61210000DD45 /* BPConfigurationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BA0096FF1DCA61210000DD45 /* BPConfigurationTests.m */; }; BA0C554D1DDAE241009E1377 /* failure_retry_report.xml in Resources */ = {isa = PBXBuildFile; fileRef = BA0C554C1DDAE241009E1377 /* failure_retry_report.xml */; }; @@ -192,6 +193,7 @@ B368E562213F8D2F00B4DEA3 /* bluelibTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = bluelibTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; B368E567213F8D2F00B4DEA3 /* bluelibTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = bluelibTests.m; sourceTree = ""; }; B368E569213F8D2F00B4DEA3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B848CF4F217E38FB0043D826 /* test-script.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "test-script.sh"; sourceTree = ""; }; BA0096FD1DCA5D810000DD45 /* testConfigRelativePath.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = testConfigRelativePath.json; sourceTree = ""; }; BA0096FF1DCA61210000DD45 /* BPConfigurationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPConfigurationTests.m; sourceTree = ""; }; BA0097011DCA626F0000DD45 /* BPConfiguration+Test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "BPConfiguration+Test.h"; sourceTree = ""; }; @@ -496,6 +498,7 @@ BA180A131DBB088100D7D130 /* testScheme.xcscheme */, BA0096FD1DCA5D810000DD45 /* testConfigRelativePath.json */, C94DEF7F8BCA7AB3C9114467 /* simulator-preferences.plist */, + B848CF4F217E38FB0043D826 /* test-script.sh */, ); path = "Resource Files"; sourceTree = ""; @@ -889,6 +892,7 @@ BA4BCFD11DC4888800592FA4 /* hanging_tests.xml in Resources */, BA4800EA1DC2C74600026972 /* BPAppNegativeTests-results.xml in Resources */, 7A4FB8E61DF8AD4F0073F268 /* missed-crash.log in Resources */, + B848CF51217E39060043D826 /* test-script.sh in Resources */, C94DE0BB4360016D3D3061D9 /* simulator-preferences.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Bluepill-cli/Bluepill-cli/Bluepill/Bluepill.m b/Bluepill-cli/Bluepill-cli/Bluepill/Bluepill.m index 55e5f7b1..d193813f 100644 --- a/Bluepill-cli/Bluepill-cli/Bluepill/Bluepill.m +++ b/Bluepill-cli/Bluepill-cli/Bluepill/Bluepill.m @@ -282,6 +282,9 @@ - (void)createSimulatorWithContext:(BPExecutionContext *)context { }; handler.onSuccess = ^{ + if (self.config.scriptFilePath) { + [context.runner runScriptFile:self.config.scriptFilePath]; + } if (self.config.cloneSimulator) { // launch application directly when clone simulator NEXT([__self launchApplicationWithContext:context]); diff --git a/Bluepill-cli/Bluepill-cli/Simulator/BPSimulator.h b/Bluepill-cli/Bluepill-cli/Simulator/BPSimulator.h index 9c98dfcf..e008bf87 100644 --- a/Bluepill-cli/Bluepill-cli/Simulator/BPSimulator.h +++ b/Bluepill-cli/Bluepill-cli/Simulator/BPSimulator.h @@ -48,6 +48,8 @@ - (void)addVideosToSimulator; +- (void)runScriptFile:(NSString *)scriptFilePath; + /*! @discussion create template simulators and install the test hosts @param testBundles include the test hosts need to be installed. diff --git a/Bluepill-cli/Bluepill-cli/Simulator/BPSimulator.m b/Bluepill-cli/Bluepill-cli/Simulator/BPSimulator.m index 15944aa4..cd674eab 100644 --- a/Bluepill-cli/Bluepill-cli/Simulator/BPSimulator.m +++ b/Bluepill-cli/Bluepill-cli/Simulator/BPSimulator.m @@ -254,6 +254,26 @@ - (void)copySimulatorPreferencesFile:(NSString *)newPreferencesFile { } } +- (void)runScriptFile:(NSString *)scriptFilePath { + NSTask *task = [[NSTask alloc] init]; + [task setLaunchPath:scriptFilePath]; + NSMutableDictionary *env = [[NSMutableDictionary alloc] init]; + [env addEntriesFromDictionary:[[NSProcessInfo processInfo] environment]]; + [env setObject:[NSString stringWithFormat:@"%@", self.device.UDID.UUIDString] forKey:@"BP_DEVICE_ID"]; + [env setObject:[NSString stringWithFormat:@"%@", self.device.devicePath] forKey:@"BP_DEVICE_PATH"]; + [task setEnvironment:env]; + + [task launch]; + [task waitUntilExit]; + int status = [task terminationStatus]; + [BPUtils printInfo:INFO withString:@"Script (%@) has finished with exit code %d.", + scriptFilePath, [task terminationStatus]]; + + if (status != 0) { + [BPUtils printInfo:ERROR withString:@"Failed running script: %@", scriptFilePath]; + } +} + - (BOOL)useSimulatorWithDeviceUDID:(NSUUID *)deviceUDID { self.device = [self findDeviceWithConfig:self.config andDeviceID:deviceUDID]; if (!self.device) { diff --git a/Source/Shared/BPConfiguration.h b/Source/Shared/BPConfiguration.h index edc7f477..9a9b6d55 100644 --- a/Source/Shared/BPConfiguration.h +++ b/Source/Shared/BPConfiguration.h @@ -67,6 +67,7 @@ typedef NS_ENUM(NSInteger, BPProgram) { @property (nonatomic, strong) NSString *outputDirectory; @property (nonatomic, strong) NSString *screenshotsDirectory; @property (nonatomic, strong) NSString *simulatorPreferencesFile; +@property (nonatomic, strong) NSString *scriptFilePath; @property (nonatomic) BOOL headlessMode; @property (nonatomic) BOOL cloneSimulator; @property (nonatomic, strong) NSNumber *numSims; diff --git a/Source/Shared/BPConfiguration.m b/Source/Shared/BPConfiguration.m index 33a45dca..06112802 100644 --- a/Source/Shared/BPConfiguration.m +++ b/Source/Shared/BPConfiguration.m @@ -141,6 +141,8 @@ typedef NS_OPTIONS(NSUInteger, BPOptionType) { "Directory where simulator screenshots for failed ui tests will be stored"}, {362, "simulator-preferences-file", BP_MASTER | BP_SLAVE, NO, NO, required_argument, NULL, BP_VALUE | BP_PATH, "simulatorPreferencesFile", "A .GlobalPreferences.plist simulator preferences file to be copied to any newly created simulators before booting"}, + {363, "script-file", BP_MASTER | BP_SLAVE, NO, NO, required_argument, NULL, BP_VALUE | BP_PATH, "scriptFilePath", + "A script that will be called after the simulator is booted, but before tests are run. Can be used to do any setup (e.g. installing certs). The environment will contain $BP_DEVICE_ID with the ID of the simulator and $BP_DEVICE_PATH with its full path. Exit with zero for success and non-zero for failure."}, {0, 0, 0, 0, 0, 0, 0} }; @@ -718,6 +720,24 @@ - (BOOL)validateConfigWithError:(NSError *__autoreleasing *)err { } } + if (self.scriptFilePath) { + if ([[NSFileManager defaultManager] fileExistsAtPath:self.scriptFilePath isDirectory:&isdir]) { + if (isdir) { + BP_SET_ERROR(err, @"%@ is a directory.", self.scriptFilePath); + return NO; + } + + if (![[NSFileManager defaultManager] isExecutableFileAtPath:self.scriptFilePath]) { + BP_SET_ERROR(err, @"%@ is not executable.", self.scriptFilePath); + return NO; + } + + } else { + BP_SET_ERROR(err, @"%@ doesn't exist", self.scriptFilePath); + return NO; + } + } + if (!self.xcTestRunDict && self.schemePath) { if ([[NSFileManager defaultManager] fileExistsAtPath:self.schemePath isDirectory:&isdir]) { if (isdir) {