creator_simctl/
io.rs

1//! Supporting types for the `simctl io` subcommand.
2
3use std::process::Stdio;
4
5use super::{Device, Result, Validate};
6
7/// Distinguishes the display for devices that have multiple.
8#[derive(Copy, Clone, Debug, Eq, PartialEq)]
9pub enum Display {
10    /// Indicates the internal display. The internal display is the "main
11    /// display" embedded in the hardware. This is supported by iOS and watchOS.
12    Internal,
13
14    /// Indicates the external display. The external display is a connected
15    /// display. This is supported by the iOS simulator (although it's currently
16    /// not possible to setup such an external display through this library) and
17    /// tvOS, where it's the only available display because the hardware itself
18    /// obviously doesn't have a display.
19    External,
20}
21
22/// Controls the masking behavior that is used when taking a screenshot on
23/// simulators of devices that feature rounded corners or a notch.
24#[derive(Copy, Clone, Debug, Eq, PartialEq)]
25pub enum Mask {
26    /// Returns the unmasked frame buffer (without any masking applied).
27    Ignored,
28
29    /// Applies a mask to the alpha channel of a screenshot (i.e. the rounded
30    /// corners and/or notch will be transparent).
31    Alpha,
32
33    /// Replaces colors outside of the mask with black (i.e. the rounded corners
34    /// and/or notch will be black).
35    Black,
36}
37
38/// Controls the encoding that will be used to write a screenshot to the buffer
39/// that is returned.
40#[derive(Copy, Clone, Debug, Eq, PartialEq)]
41pub enum ImageType {
42    /// Returns a PNG-encoded image.
43    Png,
44
45    /// Returns a TIFF-encoded image.
46    Tiff,
47
48    /// Returns a BMP-encoded image.
49    Bmp,
50
51    /// Returns a GIF-encoded image.
52    Gif,
53
54    /// Returns a JPEG-encoded image.
55    Jpeg,
56}
57
58/// Wrapper around the `simctl io` subcommand.
59pub struct IO {
60    device: Device,
61}
62
63impl Device {
64    /// Returns a wrapper around the `simctl io` subcommand.
65    pub fn io(&self) -> IO {
66        IO {
67            device: self.clone(),
68        }
69    }
70}
71
72impl IO {
73    /// Takes a screenshot of the given display, with the given mask and returns
74    /// a buffer of the image encoded using the given type.
75    pub fn screenshot(
76        &self,
77        image_type: ImageType,
78        display: Display,
79        mask: Mask,
80    ) -> Result<Vec<u8>> {
81        let image_type = match image_type {
82            ImageType::Png => "png",
83            ImageType::Tiff => "tiff",
84            ImageType::Bmp => "bmp",
85            ImageType::Gif => "gif",
86            ImageType::Jpeg => "jpeg",
87        };
88
89        let display = match display {
90            Display::Internal => "internal",
91            Display::External => "external",
92        };
93
94        let mask = match mask {
95            Mask::Ignored => "ignored",
96            Mask::Alpha => "alpha",
97            Mask::Black => "black",
98        };
99
100        let output = self
101            .device
102            .simctl()
103            .command("io")
104            .arg(&self.device.udid)
105            .arg("screenshot")
106            .arg(format!("--type={}", image_type))
107            .arg(format!("--display={}", display))
108            .arg(format!("--mask={}", mask))
109            .arg("-")
110            .stdout(Stdio::piped())
111            .output()?;
112
113        let output = output.validate_with_output()?;
114
115        Ok(output.stdout)
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use serial_test::serial;
122
123    use super::*;
124    use crate::mock;
125
126    #[test]
127    #[serial]
128    fn test_screenshot() -> Result<()> {
129        mock::device()?.boot()?;
130
131        // The screenshot service often does not yet run immediately after
132        // booting, so we might need to retry a couple of times.
133        for i in 0..5 {
134            match mock::device()?
135                .io()
136                .screenshot(ImageType::Png, Display::Internal, Mask::Ignored)
137            {
138                Ok(_) => break,
139                Err(_) if i < 4 => continue,
140                Err(error) => return Err(error),
141            }
142        }
143
144        mock::device()?.shutdown()?;
145
146        Ok(())
147    }
148}