xref: /aosp_15_r20/external/flashrom/util/flashrom_tester/src/tester.rs (revision 0d6140be3aa665ecc836e8907834fcd3e3b018fc)
1*0d6140beSAndroid Build Coastguard Worker //
2*0d6140beSAndroid Build Coastguard Worker // Copyright 2019, Google Inc.
3*0d6140beSAndroid Build Coastguard Worker // All rights reserved.
4*0d6140beSAndroid Build Coastguard Worker //
5*0d6140beSAndroid Build Coastguard Worker // Redistribution and use in source and binary forms, with or without
6*0d6140beSAndroid Build Coastguard Worker // modification, are permitted provided that the following conditions are
7*0d6140beSAndroid Build Coastguard Worker // met:
8*0d6140beSAndroid Build Coastguard Worker //
9*0d6140beSAndroid Build Coastguard Worker //    * Redistributions of source code must retain the above copyright
10*0d6140beSAndroid Build Coastguard Worker // notice, this list of conditions and the following disclaimer.
11*0d6140beSAndroid Build Coastguard Worker //    * Redistributions in binary form must reproduce the above
12*0d6140beSAndroid Build Coastguard Worker // copyright notice, this list of conditions and the following disclaimer
13*0d6140beSAndroid Build Coastguard Worker // in the documentation and/or other materials provided with the
14*0d6140beSAndroid Build Coastguard Worker // distribution.
15*0d6140beSAndroid Build Coastguard Worker //    * Neither the name of Google Inc. nor the names of its
16*0d6140beSAndroid Build Coastguard Worker // contributors may be used to endorse or promote products derived from
17*0d6140beSAndroid Build Coastguard Worker // this software without specific prior written permission.
18*0d6140beSAndroid Build Coastguard Worker //
19*0d6140beSAndroid Build Coastguard Worker // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20*0d6140beSAndroid Build Coastguard Worker // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21*0d6140beSAndroid Build Coastguard Worker // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22*0d6140beSAndroid Build Coastguard Worker // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23*0d6140beSAndroid Build Coastguard Worker // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24*0d6140beSAndroid Build Coastguard Worker // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25*0d6140beSAndroid Build Coastguard Worker // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26*0d6140beSAndroid Build Coastguard Worker // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27*0d6140beSAndroid Build Coastguard Worker // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28*0d6140beSAndroid Build Coastguard Worker // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29*0d6140beSAndroid Build Coastguard Worker // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30*0d6140beSAndroid Build Coastguard Worker //
31*0d6140beSAndroid Build Coastguard Worker // Alternatively, this software may be distributed under the terms of the
32*0d6140beSAndroid Build Coastguard Worker // GNU General Public License ("GPL") version 2 as published by the Free
33*0d6140beSAndroid Build Coastguard Worker // Software Foundation.
34*0d6140beSAndroid Build Coastguard Worker //
35*0d6140beSAndroid Build Coastguard Worker 
36*0d6140beSAndroid Build Coastguard Worker use super::rand_util;
37*0d6140beSAndroid Build Coastguard Worker use super::types;
38*0d6140beSAndroid Build Coastguard Worker use super::utils::{self, LayoutSizes};
39*0d6140beSAndroid Build Coastguard Worker use flashrom::FlashromError;
40*0d6140beSAndroid Build Coastguard Worker use flashrom::{FlashChip, Flashrom};
41*0d6140beSAndroid Build Coastguard Worker use libflashrom::FlashromFlags;
42*0d6140beSAndroid Build Coastguard Worker use serde_json::json;
43*0d6140beSAndroid Build Coastguard Worker use std::fs::File;
44*0d6140beSAndroid Build Coastguard Worker use std::io::Write;
45*0d6140beSAndroid Build Coastguard Worker use std::path::Path;
46*0d6140beSAndroid Build Coastguard Worker use std::path::PathBuf;
47*0d6140beSAndroid Build Coastguard Worker use std::sync::atomic::{AtomicBool, Ordering};
48*0d6140beSAndroid Build Coastguard Worker 
49*0d6140beSAndroid Build Coastguard Worker // type-signature comes from the return type of lib.rs workers.
50*0d6140beSAndroid Build Coastguard Worker type TestError = Box<dyn std::error::Error>;
51*0d6140beSAndroid Build Coastguard Worker pub type TestResult = Result<(), TestError>;
52*0d6140beSAndroid Build Coastguard Worker 
53*0d6140beSAndroid Build Coastguard Worker pub struct TestEnv<'a> {
54*0d6140beSAndroid Build Coastguard Worker     chip_type: FlashChip,
55*0d6140beSAndroid Build Coastguard Worker     /// Flashrom instantiation information.
56*0d6140beSAndroid Build Coastguard Worker     ///
57*0d6140beSAndroid Build Coastguard Worker     /// Where possible, prefer to use methods on the TestEnv rather than delegating
58*0d6140beSAndroid Build Coastguard Worker     /// to the raw flashrom functions.
59*0d6140beSAndroid Build Coastguard Worker     pub cmd: &'a dyn Flashrom,
60*0d6140beSAndroid Build Coastguard Worker     layout: LayoutSizes,
61*0d6140beSAndroid Build Coastguard Worker 
62*0d6140beSAndroid Build Coastguard Worker     pub wp: WriteProtectState<'a>,
63*0d6140beSAndroid Build Coastguard Worker     /// The path to a file containing the flash contents at test start.
64*0d6140beSAndroid Build Coastguard Worker     original_flash_contents: PathBuf,
65*0d6140beSAndroid Build Coastguard Worker     /// The path to a file containing flash-sized random data
66*0d6140beSAndroid Build Coastguard Worker     random_data: PathBuf,
67*0d6140beSAndroid Build Coastguard Worker     /// The path to a file containing layout data.
68*0d6140beSAndroid Build Coastguard Worker     pub layout_file: PathBuf,
69*0d6140beSAndroid Build Coastguard Worker }
70*0d6140beSAndroid Build Coastguard Worker 
71*0d6140beSAndroid Build Coastguard Worker impl<'a> TestEnv<'a> {
create( chip_type: FlashChip, cmd: &'a dyn Flashrom, print_layout: bool, ) -> Result<Self, FlashromError>72*0d6140beSAndroid Build Coastguard Worker     pub fn create(
73*0d6140beSAndroid Build Coastguard Worker         chip_type: FlashChip,
74*0d6140beSAndroid Build Coastguard Worker         cmd: &'a dyn Flashrom,
75*0d6140beSAndroid Build Coastguard Worker         print_layout: bool,
76*0d6140beSAndroid Build Coastguard Worker     ) -> Result<Self, FlashromError> {
77*0d6140beSAndroid Build Coastguard Worker         let rom_sz = cmd.get_size()?;
78*0d6140beSAndroid Build Coastguard Worker         let out = TestEnv {
79*0d6140beSAndroid Build Coastguard Worker             chip_type,
80*0d6140beSAndroid Build Coastguard Worker             cmd,
81*0d6140beSAndroid Build Coastguard Worker             layout: utils::get_layout_sizes(rom_sz)?,
82*0d6140beSAndroid Build Coastguard Worker             wp: WriteProtectState::from_hardware(cmd, chip_type)?,
83*0d6140beSAndroid Build Coastguard Worker             original_flash_contents: "/tmp/flashrom_tester_golden.bin".into(),
84*0d6140beSAndroid Build Coastguard Worker             random_data: "/tmp/random_content.bin".into(),
85*0d6140beSAndroid Build Coastguard Worker             layout_file: create_layout_file(rom_sz, Path::new("/tmp/"), print_layout),
86*0d6140beSAndroid Build Coastguard Worker         };
87*0d6140beSAndroid Build Coastguard Worker         let flags = FlashromFlags::default();
88*0d6140beSAndroid Build Coastguard Worker         info!("Set flags: {}", flags);
89*0d6140beSAndroid Build Coastguard Worker         out.cmd.set_flags(&flags);
90*0d6140beSAndroid Build Coastguard Worker 
91*0d6140beSAndroid Build Coastguard Worker         info!("Stashing golden image for verification/recovery on completion");
92*0d6140beSAndroid Build Coastguard Worker         out.cmd.read_into_file(&out.original_flash_contents)?;
93*0d6140beSAndroid Build Coastguard Worker         out.cmd.verify_from_file(&out.original_flash_contents)?;
94*0d6140beSAndroid Build Coastguard Worker 
95*0d6140beSAndroid Build Coastguard Worker         info!("Generating random flash-sized data");
96*0d6140beSAndroid Build Coastguard Worker         rand_util::gen_rand_testdata(&out.random_data, rom_sz as usize)
97*0d6140beSAndroid Build Coastguard Worker             .map_err(|io_err| format!("I/O error writing random data file: {:#}", io_err))?;
98*0d6140beSAndroid Build Coastguard Worker 
99*0d6140beSAndroid Build Coastguard Worker         Ok(out)
100*0d6140beSAndroid Build Coastguard Worker     }
101*0d6140beSAndroid Build Coastguard Worker 
run_test<T: TestCase>(&mut self, test: T) -> TestResult102*0d6140beSAndroid Build Coastguard Worker     pub fn run_test<T: TestCase>(&mut self, test: T) -> TestResult {
103*0d6140beSAndroid Build Coastguard Worker         let name = test.get_name();
104*0d6140beSAndroid Build Coastguard Worker         info!("Beginning test: {}", name);
105*0d6140beSAndroid Build Coastguard Worker         let out = test.run(self);
106*0d6140beSAndroid Build Coastguard Worker         info!("Completed test: {}; result {:?}", name, out);
107*0d6140beSAndroid Build Coastguard Worker         out
108*0d6140beSAndroid Build Coastguard Worker     }
109*0d6140beSAndroid Build Coastguard Worker 
chip_type(&self) -> FlashChip110*0d6140beSAndroid Build Coastguard Worker     pub fn chip_type(&self) -> FlashChip {
111*0d6140beSAndroid Build Coastguard Worker         // This field is not public because it should be immutable to tests,
112*0d6140beSAndroid Build Coastguard Worker         // so this getter enforces that it is copied.
113*0d6140beSAndroid Build Coastguard Worker         self.chip_type
114*0d6140beSAndroid Build Coastguard Worker     }
115*0d6140beSAndroid Build Coastguard Worker 
116*0d6140beSAndroid Build Coastguard Worker     /// Return the path to a file that contains random data and is the same size
117*0d6140beSAndroid Build Coastguard Worker     /// as the flash chip.
random_data_file(&self) -> &Path118*0d6140beSAndroid Build Coastguard Worker     pub fn random_data_file(&self) -> &Path {
119*0d6140beSAndroid Build Coastguard Worker         &self.random_data
120*0d6140beSAndroid Build Coastguard Worker     }
121*0d6140beSAndroid Build Coastguard Worker 
layout(&self) -> &LayoutSizes122*0d6140beSAndroid Build Coastguard Worker     pub fn layout(&self) -> &LayoutSizes {
123*0d6140beSAndroid Build Coastguard Worker         &self.layout
124*0d6140beSAndroid Build Coastguard Worker     }
125*0d6140beSAndroid Build Coastguard Worker 
126*0d6140beSAndroid Build Coastguard Worker     /// Return true if the current Flash contents are the same as the golden image
127*0d6140beSAndroid Build Coastguard Worker     /// that was present at the start of testing.
is_golden(&self) -> bool128*0d6140beSAndroid Build Coastguard Worker     pub fn is_golden(&self) -> bool {
129*0d6140beSAndroid Build Coastguard Worker         self.cmd
130*0d6140beSAndroid Build Coastguard Worker             .verify_from_file(&self.original_flash_contents)
131*0d6140beSAndroid Build Coastguard Worker             .is_ok()
132*0d6140beSAndroid Build Coastguard Worker     }
133*0d6140beSAndroid Build Coastguard Worker 
134*0d6140beSAndroid Build Coastguard Worker     /// Do whatever is necessary to make the current Flash contents the same as they
135*0d6140beSAndroid Build Coastguard Worker     /// were at the start of testing.
ensure_golden(&mut self) -> Result<(), FlashromError>136*0d6140beSAndroid Build Coastguard Worker     pub fn ensure_golden(&mut self) -> Result<(), FlashromError> {
137*0d6140beSAndroid Build Coastguard Worker         self.wp.set_hw(false)?.set_sw(false)?;
138*0d6140beSAndroid Build Coastguard Worker         self.cmd.write_from_file(&self.original_flash_contents)?;
139*0d6140beSAndroid Build Coastguard Worker         Ok(())
140*0d6140beSAndroid Build Coastguard Worker     }
141*0d6140beSAndroid Build Coastguard Worker 
142*0d6140beSAndroid Build Coastguard Worker     /// Attempt to erase the flash.
erase(&self) -> Result<(), FlashromError>143*0d6140beSAndroid Build Coastguard Worker     pub fn erase(&self) -> Result<(), FlashromError> {
144*0d6140beSAndroid Build Coastguard Worker         self.cmd.erase()?;
145*0d6140beSAndroid Build Coastguard Worker         Ok(())
146*0d6140beSAndroid Build Coastguard Worker     }
147*0d6140beSAndroid Build Coastguard Worker 
148*0d6140beSAndroid Build Coastguard Worker     /// Verify that the current Flash contents are the same as the file at the given
149*0d6140beSAndroid Build Coastguard Worker     /// path.
150*0d6140beSAndroid Build Coastguard Worker     ///
151*0d6140beSAndroid Build Coastguard Worker     /// Returns Err if they are not the same.
verify(&self, contents_path: &Path) -> Result<(), FlashromError>152*0d6140beSAndroid Build Coastguard Worker     pub fn verify(&self, contents_path: &Path) -> Result<(), FlashromError> {
153*0d6140beSAndroid Build Coastguard Worker         self.cmd.verify_from_file(contents_path)?;
154*0d6140beSAndroid Build Coastguard Worker         Ok(())
155*0d6140beSAndroid Build Coastguard Worker     }
156*0d6140beSAndroid Build Coastguard Worker }
157*0d6140beSAndroid Build Coastguard Worker 
158*0d6140beSAndroid Build Coastguard Worker impl<'a> Drop for TestEnv<'a> {
drop(&mut self)159*0d6140beSAndroid Build Coastguard Worker     fn drop(&mut self) {
160*0d6140beSAndroid Build Coastguard Worker         info!("Verifying flash remains unmodified");
161*0d6140beSAndroid Build Coastguard Worker         if !self.is_golden() {
162*0d6140beSAndroid Build Coastguard Worker             warn!("ROM seems to be in a different state at finish; restoring original");
163*0d6140beSAndroid Build Coastguard Worker             if let Err(e) = self.ensure_golden() {
164*0d6140beSAndroid Build Coastguard Worker                 error!("Failed to write back golden image: {:?}", e);
165*0d6140beSAndroid Build Coastguard Worker             }
166*0d6140beSAndroid Build Coastguard Worker         }
167*0d6140beSAndroid Build Coastguard Worker     }
168*0d6140beSAndroid Build Coastguard Worker }
169*0d6140beSAndroid Build Coastguard Worker 
170*0d6140beSAndroid Build Coastguard Worker struct WriteProtect {
171*0d6140beSAndroid Build Coastguard Worker     hw: bool,
172*0d6140beSAndroid Build Coastguard Worker     sw: bool,
173*0d6140beSAndroid Build Coastguard Worker }
174*0d6140beSAndroid Build Coastguard Worker 
175*0d6140beSAndroid Build Coastguard Worker /// RAII handle for setting write protect in either hardware or software.
176*0d6140beSAndroid Build Coastguard Worker ///
177*0d6140beSAndroid Build Coastguard Worker /// Given an instance, the state of either write protect can be modified by calling
178*0d6140beSAndroid Build Coastguard Worker /// `set`. When it goes out of scope, the write protects will be returned
179*0d6140beSAndroid Build Coastguard Worker /// to the state they had then it was created.
180*0d6140beSAndroid Build Coastguard Worker pub struct WriteProtectState<'a> {
181*0d6140beSAndroid Build Coastguard Worker     current: WriteProtect,
182*0d6140beSAndroid Build Coastguard Worker     initial: WriteProtect,
183*0d6140beSAndroid Build Coastguard Worker     cmd: &'a dyn Flashrom,
184*0d6140beSAndroid Build Coastguard Worker     fc: FlashChip,
185*0d6140beSAndroid Build Coastguard Worker }
186*0d6140beSAndroid Build Coastguard Worker 
187*0d6140beSAndroid Build Coastguard Worker impl<'a> WriteProtectState<'a> {
188*0d6140beSAndroid Build Coastguard Worker     /// Initialize a state from the current state of the hardware.
from_hardware(cmd: &'a dyn Flashrom, fc: FlashChip) -> Result<Self, FlashromError>189*0d6140beSAndroid Build Coastguard Worker     pub fn from_hardware(cmd: &'a dyn Flashrom, fc: FlashChip) -> Result<Self, FlashromError> {
190*0d6140beSAndroid Build Coastguard Worker         let hw = Self::get_hw(cmd)?;
191*0d6140beSAndroid Build Coastguard Worker         let sw = Self::get_sw(cmd)?;
192*0d6140beSAndroid Build Coastguard Worker         info!("Initial write protect state: HW={} SW={}", hw, sw);
193*0d6140beSAndroid Build Coastguard Worker 
194*0d6140beSAndroid Build Coastguard Worker         Ok(WriteProtectState {
195*0d6140beSAndroid Build Coastguard Worker             current: WriteProtect { hw, sw },
196*0d6140beSAndroid Build Coastguard Worker             initial: WriteProtect { hw, sw },
197*0d6140beSAndroid Build Coastguard Worker             cmd,
198*0d6140beSAndroid Build Coastguard Worker             fc,
199*0d6140beSAndroid Build Coastguard Worker         })
200*0d6140beSAndroid Build Coastguard Worker     }
201*0d6140beSAndroid Build Coastguard Worker 
202*0d6140beSAndroid Build Coastguard Worker     /// Get the actual hardware write protect state.
get_hw(cmd: &dyn Flashrom) -> Result<bool, String>203*0d6140beSAndroid Build Coastguard Worker     fn get_hw(cmd: &dyn Flashrom) -> Result<bool, String> {
204*0d6140beSAndroid Build Coastguard Worker         if cmd.can_control_hw_wp() {
205*0d6140beSAndroid Build Coastguard Worker             super::utils::get_hardware_wp()
206*0d6140beSAndroid Build Coastguard Worker         } else {
207*0d6140beSAndroid Build Coastguard Worker             Ok(false)
208*0d6140beSAndroid Build Coastguard Worker         }
209*0d6140beSAndroid Build Coastguard Worker     }
210*0d6140beSAndroid Build Coastguard Worker 
211*0d6140beSAndroid Build Coastguard Worker     /// Get the actual software write protect state.
get_sw(cmd: &dyn Flashrom) -> Result<bool, FlashromError>212*0d6140beSAndroid Build Coastguard Worker     fn get_sw(cmd: &dyn Flashrom) -> Result<bool, FlashromError> {
213*0d6140beSAndroid Build Coastguard Worker         let b = cmd.wp_status(true)?;
214*0d6140beSAndroid Build Coastguard Worker         Ok(b)
215*0d6140beSAndroid Build Coastguard Worker     }
216*0d6140beSAndroid Build Coastguard Worker 
217*0d6140beSAndroid Build Coastguard Worker     /// Return true if the current programmer supports setting the hardware
218*0d6140beSAndroid Build Coastguard Worker     /// write protect.
219*0d6140beSAndroid Build Coastguard Worker     ///
220*0d6140beSAndroid Build Coastguard Worker     /// If false, calls to set_hw() will do nothing.
can_control_hw_wp(&self) -> bool221*0d6140beSAndroid Build Coastguard Worker     pub fn can_control_hw_wp(&self) -> bool {
222*0d6140beSAndroid Build Coastguard Worker         self.cmd.can_control_hw_wp()
223*0d6140beSAndroid Build Coastguard Worker     }
224*0d6140beSAndroid Build Coastguard Worker 
225*0d6140beSAndroid Build Coastguard Worker     /// Set the software write protect and check that the state is as expected.
set_sw(&mut self, enable: bool) -> Result<&mut Self, String>226*0d6140beSAndroid Build Coastguard Worker     pub fn set_sw(&mut self, enable: bool) -> Result<&mut Self, String> {
227*0d6140beSAndroid Build Coastguard Worker         info!("set_sw request={}, current={}", enable, self.current.sw);
228*0d6140beSAndroid Build Coastguard Worker         if self.current.sw != enable {
229*0d6140beSAndroid Build Coastguard Worker             self.cmd
230*0d6140beSAndroid Build Coastguard Worker                 .wp_toggle(/* en= */ enable)
231*0d6140beSAndroid Build Coastguard Worker                 .map_err(|e| e.to_string())?;
232*0d6140beSAndroid Build Coastguard Worker         }
233*0d6140beSAndroid Build Coastguard Worker         if Self::get_sw(self.cmd).map_err(|e| e.to_string())? != enable {
234*0d6140beSAndroid Build Coastguard Worker             Err(format!(
235*0d6140beSAndroid Build Coastguard Worker                 "Software write protect did not change state to {} when requested",
236*0d6140beSAndroid Build Coastguard Worker                 enable
237*0d6140beSAndroid Build Coastguard Worker             ))
238*0d6140beSAndroid Build Coastguard Worker         } else {
239*0d6140beSAndroid Build Coastguard Worker             self.current.sw = enable;
240*0d6140beSAndroid Build Coastguard Worker             Ok(self)
241*0d6140beSAndroid Build Coastguard Worker         }
242*0d6140beSAndroid Build Coastguard Worker     }
243*0d6140beSAndroid Build Coastguard Worker 
244*0d6140beSAndroid Build Coastguard Worker     // Set software write protect with a custom range
set_range(&mut self, range: (i64, i64), enable: bool) -> Result<&mut Self, String>245*0d6140beSAndroid Build Coastguard Worker     pub fn set_range(&mut self, range: (i64, i64), enable: bool) -> Result<&mut Self, String> {
246*0d6140beSAndroid Build Coastguard Worker         info!("set_range request={}, current={}", enable, self.current.sw);
247*0d6140beSAndroid Build Coastguard Worker         self.cmd
248*0d6140beSAndroid Build Coastguard Worker             .wp_range(range, enable)
249*0d6140beSAndroid Build Coastguard Worker             .map_err(|e| e.to_string())?;
250*0d6140beSAndroid Build Coastguard Worker         let actual_state = Self::get_sw(self.cmd).map_err(|e| e.to_string())?;
251*0d6140beSAndroid Build Coastguard Worker         if actual_state != enable {
252*0d6140beSAndroid Build Coastguard Worker             Err(format!(
253*0d6140beSAndroid Build Coastguard Worker                 "set_range request={}, real={}",
254*0d6140beSAndroid Build Coastguard Worker                 enable, actual_state
255*0d6140beSAndroid Build Coastguard Worker             ))
256*0d6140beSAndroid Build Coastguard Worker         } else {
257*0d6140beSAndroid Build Coastguard Worker             self.current.sw = enable;
258*0d6140beSAndroid Build Coastguard Worker             Ok(self)
259*0d6140beSAndroid Build Coastguard Worker         }
260*0d6140beSAndroid Build Coastguard Worker     }
261*0d6140beSAndroid Build Coastguard Worker 
262*0d6140beSAndroid Build Coastguard Worker     /// Set the hardware write protect if supported and check that the state is as expected.
set_hw(&mut self, enable: bool) -> Result<&mut Self, String>263*0d6140beSAndroid Build Coastguard Worker     pub fn set_hw(&mut self, enable: bool) -> Result<&mut Self, String> {
264*0d6140beSAndroid Build Coastguard Worker         info!("set_hw request={}, current={}", enable, self.current.hw);
265*0d6140beSAndroid Build Coastguard Worker         if self.can_control_hw_wp() {
266*0d6140beSAndroid Build Coastguard Worker             if self.current.hw != enable {
267*0d6140beSAndroid Build Coastguard Worker                 super::utils::toggle_hw_wp(/* dis= */ !enable)?;
268*0d6140beSAndroid Build Coastguard Worker             }
269*0d6140beSAndroid Build Coastguard Worker             // toggle_hw_wp does check this, but we might not have called toggle_hw_wp so check again.
270*0d6140beSAndroid Build Coastguard Worker             if Self::get_hw(self.cmd)? != enable {
271*0d6140beSAndroid Build Coastguard Worker                 return Err(format!(
272*0d6140beSAndroid Build Coastguard Worker                     "Hardware write protect did not change state to {} when requested",
273*0d6140beSAndroid Build Coastguard Worker                     enable
274*0d6140beSAndroid Build Coastguard Worker                 ));
275*0d6140beSAndroid Build Coastguard Worker             }
276*0d6140beSAndroid Build Coastguard Worker         } else {
277*0d6140beSAndroid Build Coastguard Worker             info!(
278*0d6140beSAndroid Build Coastguard Worker                 "Ignoring attempt to set hardware WP with {:?} programmer",
279*0d6140beSAndroid Build Coastguard Worker                 self.fc
280*0d6140beSAndroid Build Coastguard Worker             );
281*0d6140beSAndroid Build Coastguard Worker         }
282*0d6140beSAndroid Build Coastguard Worker         self.current.hw = enable;
283*0d6140beSAndroid Build Coastguard Worker         Ok(self)
284*0d6140beSAndroid Build Coastguard Worker     }
285*0d6140beSAndroid Build Coastguard Worker 
286*0d6140beSAndroid Build Coastguard Worker     /// Reset the hardware to what it was when this state was created, reporting errors.
287*0d6140beSAndroid Build Coastguard Worker     ///
288*0d6140beSAndroid Build Coastguard Worker     /// This behaves exactly like allowing a state to go out of scope, but it can return
289*0d6140beSAndroid Build Coastguard Worker     /// errors from that process rather than panicking.
close(mut self) -> Result<(), String>290*0d6140beSAndroid Build Coastguard Worker     pub fn close(mut self) -> Result<(), String> {
291*0d6140beSAndroid Build Coastguard Worker         let out = self.drop_internal();
292*0d6140beSAndroid Build Coastguard Worker         // We just ran drop, don't do it again
293*0d6140beSAndroid Build Coastguard Worker         std::mem::forget(self);
294*0d6140beSAndroid Build Coastguard Worker         out
295*0d6140beSAndroid Build Coastguard Worker     }
296*0d6140beSAndroid Build Coastguard Worker 
297*0d6140beSAndroid Build Coastguard Worker     /// Sets both write protects to the state they had when this state was created.
drop_internal(&mut self) -> Result<(), String>298*0d6140beSAndroid Build Coastguard Worker     fn drop_internal(&mut self) -> Result<(), String> {
299*0d6140beSAndroid Build Coastguard Worker         // Toggle both protects back to their initial states.
300*0d6140beSAndroid Build Coastguard Worker         // Software first because we can't change it once hardware is enabled.
301*0d6140beSAndroid Build Coastguard Worker         if self.set_sw(self.initial.sw).is_err() {
302*0d6140beSAndroid Build Coastguard Worker             self.set_hw(false)?;
303*0d6140beSAndroid Build Coastguard Worker             self.set_sw(self.initial.sw)?;
304*0d6140beSAndroid Build Coastguard Worker         }
305*0d6140beSAndroid Build Coastguard Worker         self.set_hw(self.initial.hw)?;
306*0d6140beSAndroid Build Coastguard Worker 
307*0d6140beSAndroid Build Coastguard Worker         Ok(())
308*0d6140beSAndroid Build Coastguard Worker     }
309*0d6140beSAndroid Build Coastguard Worker }
310*0d6140beSAndroid Build Coastguard Worker 
311*0d6140beSAndroid Build Coastguard Worker impl<'a> Drop for WriteProtectState<'a> {
drop(&mut self)312*0d6140beSAndroid Build Coastguard Worker     fn drop(&mut self) {
313*0d6140beSAndroid Build Coastguard Worker         self.drop_internal()
314*0d6140beSAndroid Build Coastguard Worker             .expect("Error while dropping WriteProtectState")
315*0d6140beSAndroid Build Coastguard Worker     }
316*0d6140beSAndroid Build Coastguard Worker }
317*0d6140beSAndroid Build Coastguard Worker 
318*0d6140beSAndroid Build Coastguard Worker pub trait TestCase {
get_name(&self) -> &str319*0d6140beSAndroid Build Coastguard Worker     fn get_name(&self) -> &str;
expected_result(&self) -> TestConclusion320*0d6140beSAndroid Build Coastguard Worker     fn expected_result(&self) -> TestConclusion;
run(&self, env: &mut TestEnv) -> TestResult321*0d6140beSAndroid Build Coastguard Worker     fn run(&self, env: &mut TestEnv) -> TestResult;
322*0d6140beSAndroid Build Coastguard Worker }
323*0d6140beSAndroid Build Coastguard Worker 
324*0d6140beSAndroid Build Coastguard Worker impl<S: AsRef<str>, F: Fn(&mut TestEnv) -> TestResult> TestCase for (S, F) {
get_name(&self) -> &str325*0d6140beSAndroid Build Coastguard Worker     fn get_name(&self) -> &str {
326*0d6140beSAndroid Build Coastguard Worker         self.0.as_ref()
327*0d6140beSAndroid Build Coastguard Worker     }
328*0d6140beSAndroid Build Coastguard Worker 
expected_result(&self) -> TestConclusion329*0d6140beSAndroid Build Coastguard Worker     fn expected_result(&self) -> TestConclusion {
330*0d6140beSAndroid Build Coastguard Worker         TestConclusion::Pass
331*0d6140beSAndroid Build Coastguard Worker     }
332*0d6140beSAndroid Build Coastguard Worker 
run(&self, env: &mut TestEnv) -> TestResult333*0d6140beSAndroid Build Coastguard Worker     fn run(&self, env: &mut TestEnv) -> TestResult {
334*0d6140beSAndroid Build Coastguard Worker         (self.1)(env)
335*0d6140beSAndroid Build Coastguard Worker     }
336*0d6140beSAndroid Build Coastguard Worker }
337*0d6140beSAndroid Build Coastguard Worker 
338*0d6140beSAndroid Build Coastguard Worker impl<T: TestCase + ?Sized> TestCase for &T {
get_name(&self) -> &str339*0d6140beSAndroid Build Coastguard Worker     fn get_name(&self) -> &str {
340*0d6140beSAndroid Build Coastguard Worker         (*self).get_name()
341*0d6140beSAndroid Build Coastguard Worker     }
342*0d6140beSAndroid Build Coastguard Worker 
expected_result(&self) -> TestConclusion343*0d6140beSAndroid Build Coastguard Worker     fn expected_result(&self) -> TestConclusion {
344*0d6140beSAndroid Build Coastguard Worker         (*self).expected_result()
345*0d6140beSAndroid Build Coastguard Worker     }
346*0d6140beSAndroid Build Coastguard Worker 
run(&self, env: &mut TestEnv) -> TestResult347*0d6140beSAndroid Build Coastguard Worker     fn run(&self, env: &mut TestEnv) -> TestResult {
348*0d6140beSAndroid Build Coastguard Worker         (*self).run(env)
349*0d6140beSAndroid Build Coastguard Worker     }
350*0d6140beSAndroid Build Coastguard Worker }
351*0d6140beSAndroid Build Coastguard Worker 
352*0d6140beSAndroid Build Coastguard Worker #[allow(dead_code)]
353*0d6140beSAndroid Build Coastguard Worker #[derive(Copy, Clone, PartialEq, Eq, Debug)]
354*0d6140beSAndroid Build Coastguard Worker pub enum TestConclusion {
355*0d6140beSAndroid Build Coastguard Worker     Pass,
356*0d6140beSAndroid Build Coastguard Worker     Fail,
357*0d6140beSAndroid Build Coastguard Worker     UnexpectedPass,
358*0d6140beSAndroid Build Coastguard Worker     UnexpectedFail,
359*0d6140beSAndroid Build Coastguard Worker }
360*0d6140beSAndroid Build Coastguard Worker 
361*0d6140beSAndroid Build Coastguard Worker pub struct ReportMetaData {
362*0d6140beSAndroid Build Coastguard Worker     pub chip_name: String,
363*0d6140beSAndroid Build Coastguard Worker     pub os_release: String,
364*0d6140beSAndroid Build Coastguard Worker     pub cros_release: String,
365*0d6140beSAndroid Build Coastguard Worker     pub system_info: String,
366*0d6140beSAndroid Build Coastguard Worker     pub bios_info: String,
367*0d6140beSAndroid Build Coastguard Worker }
368*0d6140beSAndroid Build Coastguard Worker 
decode_test_result(res: TestResult, con: TestConclusion) -> (TestConclusion, Option<TestError>)369*0d6140beSAndroid Build Coastguard Worker fn decode_test_result(res: TestResult, con: TestConclusion) -> (TestConclusion, Option<TestError>) {
370*0d6140beSAndroid Build Coastguard Worker     use TestConclusion::*;
371*0d6140beSAndroid Build Coastguard Worker 
372*0d6140beSAndroid Build Coastguard Worker     match (res, con) {
373*0d6140beSAndroid Build Coastguard Worker         (Ok(_), Fail) => (UnexpectedPass, None),
374*0d6140beSAndroid Build Coastguard Worker         (Err(e), Pass) => (UnexpectedFail, Some(e)),
375*0d6140beSAndroid Build Coastguard Worker         _ => (Pass, None),
376*0d6140beSAndroid Build Coastguard Worker     }
377*0d6140beSAndroid Build Coastguard Worker }
378*0d6140beSAndroid Build Coastguard Worker 
create_layout_file(rom_sz: i64, tmp_dir: &Path, print_layout: bool) -> PathBuf379*0d6140beSAndroid Build Coastguard Worker fn create_layout_file(rom_sz: i64, tmp_dir: &Path, print_layout: bool) -> PathBuf {
380*0d6140beSAndroid Build Coastguard Worker     info!("Calculate ROM partition sizes & Create the layout file.");
381*0d6140beSAndroid Build Coastguard Worker     let layout_sizes = utils::get_layout_sizes(rom_sz).expect("Could not partition rom");
382*0d6140beSAndroid Build Coastguard Worker 
383*0d6140beSAndroid Build Coastguard Worker     let layout_file = tmp_dir.join("layout.file");
384*0d6140beSAndroid Build Coastguard Worker     let mut f = File::create(&layout_file).expect("Could not create layout file");
385*0d6140beSAndroid Build Coastguard Worker     let mut buf: Vec<u8> = vec![];
386*0d6140beSAndroid Build Coastguard Worker     utils::construct_layout_file(&mut buf, &layout_sizes).expect("Could not construct layout file");
387*0d6140beSAndroid Build Coastguard Worker 
388*0d6140beSAndroid Build Coastguard Worker     f.write_all(&buf).expect("Writing layout file failed");
389*0d6140beSAndroid Build Coastguard Worker     if print_layout {
390*0d6140beSAndroid Build Coastguard Worker         info!(
391*0d6140beSAndroid Build Coastguard Worker             "Dumping layout file as requested:\n{}",
392*0d6140beSAndroid Build Coastguard Worker             String::from_utf8_lossy(&buf)
393*0d6140beSAndroid Build Coastguard Worker         );
394*0d6140beSAndroid Build Coastguard Worker     }
395*0d6140beSAndroid Build Coastguard Worker     layout_file
396*0d6140beSAndroid Build Coastguard Worker }
397*0d6140beSAndroid Build Coastguard Worker 
run_all_tests<T, TS>( chip: FlashChip, cmd: &dyn Flashrom, ts: TS, terminate_flag: Option<&AtomicBool>, print_layout: bool, ) -> Vec<(String, (TestConclusion, Option<TestError>))> where T: TestCase + Copy, TS: IntoIterator<Item = T>,398*0d6140beSAndroid Build Coastguard Worker pub fn run_all_tests<T, TS>(
399*0d6140beSAndroid Build Coastguard Worker     chip: FlashChip,
400*0d6140beSAndroid Build Coastguard Worker     cmd: &dyn Flashrom,
401*0d6140beSAndroid Build Coastguard Worker     ts: TS,
402*0d6140beSAndroid Build Coastguard Worker     terminate_flag: Option<&AtomicBool>,
403*0d6140beSAndroid Build Coastguard Worker     print_layout: bool,
404*0d6140beSAndroid Build Coastguard Worker ) -> Vec<(String, (TestConclusion, Option<TestError>))>
405*0d6140beSAndroid Build Coastguard Worker where
406*0d6140beSAndroid Build Coastguard Worker     T: TestCase + Copy,
407*0d6140beSAndroid Build Coastguard Worker     TS: IntoIterator<Item = T>,
408*0d6140beSAndroid Build Coastguard Worker {
409*0d6140beSAndroid Build Coastguard Worker     let mut env =
410*0d6140beSAndroid Build Coastguard Worker         TestEnv::create(chip, cmd, print_layout).expect("Failed to set up test environment");
411*0d6140beSAndroid Build Coastguard Worker 
412*0d6140beSAndroid Build Coastguard Worker     let mut results = Vec::new();
413*0d6140beSAndroid Build Coastguard Worker     for t in ts {
414*0d6140beSAndroid Build Coastguard Worker         if terminate_flag
415*0d6140beSAndroid Build Coastguard Worker             .map(|b| b.load(Ordering::Acquire))
416*0d6140beSAndroid Build Coastguard Worker             .unwrap_or(false)
417*0d6140beSAndroid Build Coastguard Worker         {
418*0d6140beSAndroid Build Coastguard Worker             break;
419*0d6140beSAndroid Build Coastguard Worker         }
420*0d6140beSAndroid Build Coastguard Worker 
421*0d6140beSAndroid Build Coastguard Worker         let result = decode_test_result(env.run_test(t), t.expected_result());
422*0d6140beSAndroid Build Coastguard Worker         results.push((t.get_name().into(), result));
423*0d6140beSAndroid Build Coastguard Worker     }
424*0d6140beSAndroid Build Coastguard Worker     results
425*0d6140beSAndroid Build Coastguard Worker }
426*0d6140beSAndroid Build Coastguard Worker 
427*0d6140beSAndroid Build Coastguard Worker #[derive(Debug, PartialEq, Eq, Clone, Copy)]
428*0d6140beSAndroid Build Coastguard Worker pub enum OutputFormat {
429*0d6140beSAndroid Build Coastguard Worker     Pretty,
430*0d6140beSAndroid Build Coastguard Worker     Json,
431*0d6140beSAndroid Build Coastguard Worker }
432*0d6140beSAndroid Build Coastguard Worker 
433*0d6140beSAndroid Build Coastguard Worker impl std::str::FromStr for OutputFormat {
434*0d6140beSAndroid Build Coastguard Worker     type Err = ();
435*0d6140beSAndroid Build Coastguard Worker 
from_str(s: &str) -> Result<Self, Self::Err>436*0d6140beSAndroid Build Coastguard Worker     fn from_str(s: &str) -> Result<Self, Self::Err> {
437*0d6140beSAndroid Build Coastguard Worker         use OutputFormat::*;
438*0d6140beSAndroid Build Coastguard Worker 
439*0d6140beSAndroid Build Coastguard Worker         if s.eq_ignore_ascii_case("pretty") {
440*0d6140beSAndroid Build Coastguard Worker             Ok(Pretty)
441*0d6140beSAndroid Build Coastguard Worker         } else if s.eq_ignore_ascii_case("json") {
442*0d6140beSAndroid Build Coastguard Worker             Ok(Json)
443*0d6140beSAndroid Build Coastguard Worker         } else {
444*0d6140beSAndroid Build Coastguard Worker             Err(())
445*0d6140beSAndroid Build Coastguard Worker         }
446*0d6140beSAndroid Build Coastguard Worker     }
447*0d6140beSAndroid Build Coastguard Worker }
448*0d6140beSAndroid Build Coastguard Worker 
collate_all_test_runs( truns: &[(String, (TestConclusion, Option<TestError>))], meta_data: ReportMetaData, format: OutputFormat, )449*0d6140beSAndroid Build Coastguard Worker pub fn collate_all_test_runs(
450*0d6140beSAndroid Build Coastguard Worker     truns: &[(String, (TestConclusion, Option<TestError>))],
451*0d6140beSAndroid Build Coastguard Worker     meta_data: ReportMetaData,
452*0d6140beSAndroid Build Coastguard Worker     format: OutputFormat,
453*0d6140beSAndroid Build Coastguard Worker ) {
454*0d6140beSAndroid Build Coastguard Worker     match format {
455*0d6140beSAndroid Build Coastguard Worker         OutputFormat::Pretty => {
456*0d6140beSAndroid Build Coastguard Worker             let color = if atty::is(atty::Stream::Stdout) {
457*0d6140beSAndroid Build Coastguard Worker                 types::COLOR
458*0d6140beSAndroid Build Coastguard Worker             } else {
459*0d6140beSAndroid Build Coastguard Worker                 types::NOCOLOR
460*0d6140beSAndroid Build Coastguard Worker             };
461*0d6140beSAndroid Build Coastguard Worker             println!();
462*0d6140beSAndroid Build Coastguard Worker             println!("  =============================");
463*0d6140beSAndroid Build Coastguard Worker             println!("  =====  AVL qual RESULTS  ====");
464*0d6140beSAndroid Build Coastguard Worker             println!("  =============================");
465*0d6140beSAndroid Build Coastguard Worker             println!();
466*0d6140beSAndroid Build Coastguard Worker             println!("  %---------------------------%");
467*0d6140beSAndroid Build Coastguard Worker             println!("   os release: {}", meta_data.os_release);
468*0d6140beSAndroid Build Coastguard Worker             println!("   cros release: {}", meta_data.cros_release);
469*0d6140beSAndroid Build Coastguard Worker             println!("   chip name: {}", meta_data.chip_name);
470*0d6140beSAndroid Build Coastguard Worker             println!("   system info: \n{}", meta_data.system_info);
471*0d6140beSAndroid Build Coastguard Worker             println!("   bios info: \n{}", meta_data.bios_info);
472*0d6140beSAndroid Build Coastguard Worker             println!("  %---------------------------%");
473*0d6140beSAndroid Build Coastguard Worker             println!();
474*0d6140beSAndroid Build Coastguard Worker 
475*0d6140beSAndroid Build Coastguard Worker             for trun in truns.iter() {
476*0d6140beSAndroid Build Coastguard Worker                 let (name, (result, error)) = trun;
477*0d6140beSAndroid Build Coastguard Worker                 if *result != TestConclusion::Pass {
478*0d6140beSAndroid Build Coastguard Worker                     println!(
479*0d6140beSAndroid Build Coastguard Worker                         " {} {}",
480*0d6140beSAndroid Build Coastguard Worker                         style!(format!(" <+> {} test:", name), color.bold, color),
481*0d6140beSAndroid Build Coastguard Worker                         style_dbg!(result, color.red, color)
482*0d6140beSAndroid Build Coastguard Worker                     );
483*0d6140beSAndroid Build Coastguard Worker                     match error {
484*0d6140beSAndroid Build Coastguard Worker                         None => {}
485*0d6140beSAndroid Build Coastguard Worker                         Some(e) => info!(" - {} failure details:\n{}", name, e.to_string()),
486*0d6140beSAndroid Build Coastguard Worker                     };
487*0d6140beSAndroid Build Coastguard Worker                 } else {
488*0d6140beSAndroid Build Coastguard Worker                     println!(
489*0d6140beSAndroid Build Coastguard Worker                         " {} {}",
490*0d6140beSAndroid Build Coastguard Worker                         style!(format!(" <+> {} test:", name), color.bold, color),
491*0d6140beSAndroid Build Coastguard Worker                         style_dbg!(result, color.green, color)
492*0d6140beSAndroid Build Coastguard Worker                     );
493*0d6140beSAndroid Build Coastguard Worker                 }
494*0d6140beSAndroid Build Coastguard Worker             }
495*0d6140beSAndroid Build Coastguard Worker             println!();
496*0d6140beSAndroid Build Coastguard Worker         }
497*0d6140beSAndroid Build Coastguard Worker         OutputFormat::Json => {
498*0d6140beSAndroid Build Coastguard Worker             use serde_json::{Map, Value};
499*0d6140beSAndroid Build Coastguard Worker 
500*0d6140beSAndroid Build Coastguard Worker             let mut all_pass = true;
501*0d6140beSAndroid Build Coastguard Worker             let mut tests = Map::<String, Value>::new();
502*0d6140beSAndroid Build Coastguard Worker             for (name, (result, error)) in truns {
503*0d6140beSAndroid Build Coastguard Worker                 let passed = *result == TestConclusion::Pass;
504*0d6140beSAndroid Build Coastguard Worker                 all_pass &= passed;
505*0d6140beSAndroid Build Coastguard Worker 
506*0d6140beSAndroid Build Coastguard Worker                 let error = match error {
507*0d6140beSAndroid Build Coastguard Worker                     Some(e) => Value::String(format!("{:#?}", e)),
508*0d6140beSAndroid Build Coastguard Worker                     None => Value::Null,
509*0d6140beSAndroid Build Coastguard Worker                 };
510*0d6140beSAndroid Build Coastguard Worker 
511*0d6140beSAndroid Build Coastguard Worker                 assert!(
512*0d6140beSAndroid Build Coastguard Worker                     !tests.contains_key(name),
513*0d6140beSAndroid Build Coastguard Worker                     "Found multiple tests named {:?}",
514*0d6140beSAndroid Build Coastguard Worker                     name
515*0d6140beSAndroid Build Coastguard Worker                 );
516*0d6140beSAndroid Build Coastguard Worker                 tests.insert(
517*0d6140beSAndroid Build Coastguard Worker                     name.into(),
518*0d6140beSAndroid Build Coastguard Worker                     json!({
519*0d6140beSAndroid Build Coastguard Worker                         "pass": passed,
520*0d6140beSAndroid Build Coastguard Worker                         "error": error,
521*0d6140beSAndroid Build Coastguard Worker                     }),
522*0d6140beSAndroid Build Coastguard Worker                 );
523*0d6140beSAndroid Build Coastguard Worker             }
524*0d6140beSAndroid Build Coastguard Worker 
525*0d6140beSAndroid Build Coastguard Worker             let json = json!({
526*0d6140beSAndroid Build Coastguard Worker                 "pass": all_pass,
527*0d6140beSAndroid Build Coastguard Worker                 "metadata": {
528*0d6140beSAndroid Build Coastguard Worker                     "os_release": meta_data.os_release,
529*0d6140beSAndroid Build Coastguard Worker                     "chip_name": meta_data.chip_name,
530*0d6140beSAndroid Build Coastguard Worker                     "system_info": meta_data.system_info,
531*0d6140beSAndroid Build Coastguard Worker                     "bios_info": meta_data.bios_info,
532*0d6140beSAndroid Build Coastguard Worker                 },
533*0d6140beSAndroid Build Coastguard Worker                 "tests": tests,
534*0d6140beSAndroid Build Coastguard Worker             });
535*0d6140beSAndroid Build Coastguard Worker             println!("{:#}", json);
536*0d6140beSAndroid Build Coastguard Worker         }
537*0d6140beSAndroid Build Coastguard Worker     }
538*0d6140beSAndroid Build Coastguard Worker }
539*0d6140beSAndroid Build Coastguard Worker 
540*0d6140beSAndroid Build Coastguard Worker #[cfg(test)]
541*0d6140beSAndroid Build Coastguard Worker mod tests {
542*0d6140beSAndroid Build Coastguard Worker     #[test]
decode_test_result()543*0d6140beSAndroid Build Coastguard Worker     fn decode_test_result() {
544*0d6140beSAndroid Build Coastguard Worker         use super::decode_test_result;
545*0d6140beSAndroid Build Coastguard Worker         use super::TestConclusion::*;
546*0d6140beSAndroid Build Coastguard Worker 
547*0d6140beSAndroid Build Coastguard Worker         let (result, err) = decode_test_result(Ok(()), Pass);
548*0d6140beSAndroid Build Coastguard Worker         assert_eq!(result, Pass);
549*0d6140beSAndroid Build Coastguard Worker         assert!(err.is_none());
550*0d6140beSAndroid Build Coastguard Worker 
551*0d6140beSAndroid Build Coastguard Worker         let (result, err) = decode_test_result(Ok(()), Fail);
552*0d6140beSAndroid Build Coastguard Worker         assert_eq!(result, UnexpectedPass);
553*0d6140beSAndroid Build Coastguard Worker         assert!(err.is_none());
554*0d6140beSAndroid Build Coastguard Worker 
555*0d6140beSAndroid Build Coastguard Worker         let (result, err) = decode_test_result(Err("broken".into()), Pass);
556*0d6140beSAndroid Build Coastguard Worker         assert_eq!(result, UnexpectedFail);
557*0d6140beSAndroid Build Coastguard Worker         assert!(err.is_some());
558*0d6140beSAndroid Build Coastguard Worker 
559*0d6140beSAndroid Build Coastguard Worker         let (result, err) = decode_test_result(Err("broken".into()), Fail);
560*0d6140beSAndroid Build Coastguard Worker         assert_eq!(result, Pass);
561*0d6140beSAndroid Build Coastguard Worker         assert!(err.is_none());
562*0d6140beSAndroid Build Coastguard Worker     }
563*0d6140beSAndroid Build Coastguard Worker 
564*0d6140beSAndroid Build Coastguard Worker     #[test]
output_format_round_trip()565*0d6140beSAndroid Build Coastguard Worker     fn output_format_round_trip() {
566*0d6140beSAndroid Build Coastguard Worker         use super::OutputFormat::{self, *};
567*0d6140beSAndroid Build Coastguard Worker 
568*0d6140beSAndroid Build Coastguard Worker         assert_eq!(format!("{:?}", Pretty).parse::<OutputFormat>(), Ok(Pretty));
569*0d6140beSAndroid Build Coastguard Worker         assert_eq!(format!("{:?}", Json).parse::<OutputFormat>(), Ok(Json));
570*0d6140beSAndroid Build Coastguard Worker     }
571*0d6140beSAndroid Build Coastguard Worker }
572