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