1use std::ffi::OsStr;
4use std::fmt::Display;
5use std::path::Path;
6use std::process::Stdio;
7
8use super::{Device, Result, Validate};
9
10#[derive(Debug)]
12pub struct Launch<'a> {
13 device: Device,
14 bundle_id: &'a str,
15 wait_for_debugger: bool,
16 use_pty: Option<bool>,
17 stdout: Option<&'a Path>,
18 stderr: Option<&'a Path>,
19 args: Vec<&'a OsStr>,
20 envs: Vec<(String, &'a OsStr)>,
21}
22
23impl<'a> Launch<'a> {
24 pub fn wait_for_debugger(&mut self, wait: bool) -> &mut Launch<'a> {
26 self.wait_for_debugger = wait;
27 self
28 }
29
30 pub fn use_pty(&mut self, use_pty: bool) -> &mut Launch<'a> {
32 self.use_pty = Some(use_pty);
33 self.stdout = None;
34 self.stderr = None;
35 self
36 }
37
38 pub fn stdout<P>(&mut self, path: &'a P) -> &mut Launch<'a>
40 where
41 P: AsRef<Path>,
42 {
43 self.use_pty = None;
44 self.stdout = Some(path.as_ref());
45 self
46 }
47
48 pub fn stderr<P>(&mut self, path: &'a P) -> &mut Launch<'a>
50 where
51 P: AsRef<Path>,
52 {
53 self.use_pty = None;
54 self.stderr = Some(path.as_ref());
55 self
56 }
57
58 pub fn arg<S>(&mut self, arg: &'a S) -> &mut Launch<'a>
60 where
61 S: AsRef<OsStr>,
62 {
63 self.args.push(arg.as_ref());
64 self
65 }
66
67 pub fn env<K, V>(&mut self, key: K, value: &'a V) -> &mut Launch<'a>
69 where
70 K: Display,
71 V: AsRef<OsStr>,
72 {
73 self.envs
74 .push((format!("SIMCTL_CHILD_{}", key), value.as_ref()));
75 self
76 }
77
78 pub fn exec(&mut self) -> Result<()> {
80 let mut command = self.device.simctl().command("launch");
81
82 if self.wait_for_debugger {
83 command.arg("--wait-for-debugger");
84 }
85
86 if let Some(use_pty) = self.use_pty {
87 match use_pty {
88 true => command.arg("--console-pty"),
89 false => command.arg("--console"),
90 };
91 }
92
93 if let Some(stdout) = self.stdout {
94 command.arg(format!("--stdout={}", stdout.display()));
95 } else {
96 command.stdout(Stdio::inherit());
97 }
98
99 if let Some(stderr) = self.stderr {
100 command.arg(format!("--stderr={}", stderr.display()));
101 } else {
102 command.stderr(Stdio::inherit());
103 }
104
105 command.envs(self.envs.iter().map(|(k, v)| (k, v)));
106
107 command.arg(&self.device.udid);
108 command.arg(self.bundle_id);
109
110 command.args(&self.args);
111
112 command.output()?.validate()
113 }
114}
115
116impl Device {
117 pub fn launch<'a>(&self, bundle_id: &'a str) -> Launch<'a> {
120 Launch {
121 device: self.clone(),
122 bundle_id,
123 wait_for_debugger: false,
124 use_pty: Some(false),
125 stdout: None,
126 stderr: None,
127 args: vec![],
128 envs: vec![],
129 }
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use crate::mock;
137
138 use serial_test::serial;
139
140 #[test]
141 #[serial]
142 fn test_launch() -> Result<()> {
143 mock::device()?.boot()?;
144
145 let path = "/dev/zero";
146
147 mock::device()?
148 .launch("com.apple.mobilesafari")
149 .stdout(&path)
150 .stderr(&path)
151 .exec()?;
152
153 mock::device()?.shutdown()?;
154
155 Ok(())
156 }
157}