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}