creator_simctl/
launch.rs

1//! Supporting types for the `simctl launch` subcommand.
2
3use std::ffi::OsStr;
4use std::fmt::Display;
5use std::path::Path;
6use std::process::Stdio;
7
8use super::{Device, Result, Validate};
9
10/// Builder that can be used to customize the launch of an application.
11#[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    /// Indicates whether the application should wait for a debugger to attach.
25    pub fn wait_for_debugger(&mut self, wait: bool) -> &mut Launch<'a> {
26        self.wait_for_debugger = wait;
27        self
28    }
29
30    /// Indicates whether the output should be written to a console with PTY.
31    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    /// Writes stdout to the given path.
39    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    /// Writes stderr to the given path.
49    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    /// Adds an argument that will be passed to the program.
59    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    /// Adds an environment variable that will be made available to the program.
68    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    /// Executes the launch.
79    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    /// Returns a builder that can be used to customize the launch of an app
118    /// with the given bundle ID on this device.
119    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}