diff --git a/ci/ios/deploy_and_run_on_ios_simulator.rs b/ci/ios/deploy_and_run_on_ios_simulator.rs index 97ef63437d7f1fec6ba46665e7d42f781c1f608a..b14615036d02ac8d6fb5707a3ef562143ce0ad81 100644 --- a/ci/ios/deploy_and_run_on_ios_simulator.rs +++ b/ci/ios/deploy_and_run_on_ios_simulator.rs @@ -7,52 +7,68 @@ // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. -// + // This is a script to deploy and execute a binary on an iOS simulator. -// The primary use of this is to be able to run unit tests on the simulator and retrieve the results. +// The primary use of this is to be able to run unit tests on the simulator and +// retrieve the results. // -// To do this through Cargo instead, use Dinghy (https://github.com/snipsco/dinghy): -// cargo dinghy install, then cargo dinghy test. +// To do this through Cargo instead, use Dinghy +// (https://github.com/snipsco/dinghy): cargo dinghy install, then cargo dinghy +// test. use std::env; -use std::fs::File; +use std::fs::{self, File}; use std::io::Write; use std::path::Path; use std::process; use std::process::Command; +macro_rules! t { + ($e:expr) => (match $e { + Ok(e) => e, + Err(e) => panic!("{} failed with: {}", stringify!($e), e), + }) +} + // Step one: Wrap as an app fn package_as_simulator_app(crate_name: &str, test_binary_path: &Path) { println!("Packaging simulator app"); - Command::new("rm").arg("-rf").arg("ios_simulator_app").status().unwrap(); - Command::new("mkdir").arg("ios_simulator_app").check_status(); - Command::new("cp").arg(test_binary_path).arg(["ios_simulator_app/", crate_name].join("")).check_status(); - let mut f = File::create("ios_simulator_app/Info.plist").unwrap(); - f.write_all(&[ - "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", - "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">", - "<plist version=\"1.0\">", - " <dict>", - " <key>CFBundleExecutable</key>", - &[" <string>", crate_name, "</string>"].join(""), - " <key>CFBundleIdentifier</key>", - " <string>com.rust.unittests</string>", - " </dict>", - "</plist>"].join("\n").into_bytes()).unwrap(); + drop(fs::remove_dir_all("ios_simulator_app")); + t!(fs::create_dir("ios_simulator_app")); + t!(fs::copy(test_binary_path, + Path::new("ios_simulator_app").join(crate_name))); + + let mut f = t!(File::create("ios_simulator_app/Info.plist")); + t!(f.write_all(format!(r#" + <?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE plist PUBLIC + "-//Apple//DTD PLIST 1.0//EN" + "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + <plist version="1.0"> + <dict> + <key>CFBundleExecutable</key> + <string>{}</string> + <key>CFBundleIdentifier</key> + <string>com.rust.unittests</string> + </dict> + </plist> + "#, crate_name).as_bytes())); } // Step two: Start the iOS simulator fn start_simulator() { println!("Looking for iOS simulator"); - let output = Command::new("xcrun").arg("simctl").arg("list").output().unwrap(); + let output = t!(Command::new("xcrun").arg("simctl").arg("list").output()); + assert!(output.status.success()); let mut simulator_exists = false; let mut simulator_booted = false; let mut found_rust_sim = false; - let stdout = String::from_utf8(output.stdout).unwrap(); + let stdout = t!(String::from_utf8(output.stdout)); for line in stdout.lines() { if line.contains("rust_ios") { if found_rust_sim { - panic!("Duplicate rust_ios simulators found. Please double-check xcrun simctl list."); + panic!("Duplicate rust_ios simulators found. Please \ + double-check xcrun simctl list."); } simulator_exists = true; simulator_booted = line.contains("(Booted)"); @@ -62,43 +78,69 @@ fn start_simulator() { if simulator_exists == false { println!("Creating iOS simulator"); - Command::new("xcrun").arg("simctl").arg("create").arg("rust_ios") - .arg("com.apple.CoreSimulator.SimDeviceType.iPhone-SE").arg("com.apple.CoreSimulator.SimRuntime.iOS-10-2").check_status(); + Command::new("xcrun") + .arg("simctl") + .arg("create") + .arg("rust_ios") + .arg("com.apple.CoreSimulator.SimDeviceType.iPhone-SE") + .arg("com.apple.CoreSimulator.SimRuntime.iOS-10-2") + .check_status(); } else if simulator_booted == true { println!("Shutting down already-booted simulator"); - Command::new("xcrun").arg("simctl").arg("shutdown").arg("rust_ios").check_status(); + Command::new("xcrun") + .arg("simctl") + .arg("shutdown") + .arg("rust_ios") + .check_status(); } println!("Starting iOS simulator"); - // We can't uninstall the app (if present) as that will hang if the simulator isn't completely booted;just erase the simulator instead. + // We can't uninstall the app (if present) as that will hang if the + // simulator isn't completely booted; just erase the simulator instead. Command::new("xcrun").arg("simctl").arg("erase").arg("rust_ios").check_status(); Command::new("xcrun").arg("simctl").arg("boot").arg("rust_ios").check_status(); } // Step three: Install the app -fn install_app_to_simulator() { +fn install_app_to_simulator() { println!("Installing app to simulator"); - Command::new("xcrun").arg("simctl").arg("install").arg("booted").arg("ios_simulator_app/").check_status(); + Command::new("xcrun") + .arg("simctl") + .arg("install") + .arg("booted") + .arg("ios_simulator_app/") + .check_status(); } // Step four: Run the app fn run_app_on_simulator() { println!("Running app"); - let output = Command::new("xcrun").arg("simctl").arg("launch").arg("--console").arg("booted").arg("com.rust.unittests").output().unwrap(); - let mut test_run_passed = false; - let stdout = String::from_utf8(output.stdout).unwrap(); - for line in stdout.lines() { - println!("{}", line); - - if test_run_passed == false { - // Based on all.rs test output - test_run_passed = line.contains("PASSED") && line.contains("tests"); - } - } + let output = t!(Command::new("xcrun") + .arg("simctl") + .arg("launch") + .arg("--console") + .arg("booted") + .arg("com.rust.unittests") + .output()); + + println!("stdout --\n{}\n", String::from_utf8_lossy(&output.stdout)); + println!("stderr --\n{}\n", String::from_utf8_lossy(&output.stderr)); + + let stdout = String::from_utf8_lossy(&output.stdout); + let passed = stdout.lines() + .find(|l| l.contains("PASSED")) + .map(|l| l.contains("tests")) + .unwrap_or(false); println!("Shutting down simulator"); - Command::new("xcrun").arg("simctl").arg("shutdown").arg("rust_ios").check_status(); - assert!(test_run_passed); + Command::new("xcrun") + .arg("simctl") + .arg("shutdown") + .arg("rust_ios") + .check_status(); + if !passed { + panic!("tests didn't pass"); + } } trait CheckStatus { @@ -107,17 +149,18 @@ trait CheckStatus { impl CheckStatus for Command { fn check_status(&mut self) { - assert!(self.status().unwrap().success()); + println!("\trunning: {:?}", self); + assert!(t!(self.status()).success()); } } -pub fn main() { +fn main() { let args: Vec<String> = env::args().collect(); if args.len() != 2 { - println!("Usage: {:?} executable", Path::new(&args[0]).file_name().unwrap()); + println!("Usage: {} <executable>", args[0]); process::exit(-1); } - + let test_binary_path = Path::new(&args[1]); let crate_name = test_binary_path.file_name().unwrap(); @@ -125,4 +168,4 @@ pub fn main() { start_simulator(); install_app_to_simulator(); run_app_on_simulator(); -} \ No newline at end of file +}