xref: /aosp_15_r20/bootable/libbootloader/gbl/libefi/mocks/lib.rs (revision 5225e6b173e52d2efc6bcf950c27374fd72adabc)
1*5225e6b1SAndroid Build Coastguard Worker // Copyright 2024, The Android Open Source Project
2*5225e6b1SAndroid Build Coastguard Worker //
3*5225e6b1SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*5225e6b1SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*5225e6b1SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*5225e6b1SAndroid Build Coastguard Worker //
7*5225e6b1SAndroid Build Coastguard Worker //     http://www.apache.org/licenses/LICENSE-2.0
8*5225e6b1SAndroid Build Coastguard Worker //
9*5225e6b1SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*5225e6b1SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*5225e6b1SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*5225e6b1SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*5225e6b1SAndroid Build Coastguard Worker // limitations under the License.
14*5225e6b1SAndroid Build Coastguard Worker 
15*5225e6b1SAndroid Build Coastguard Worker //! UEFI object mocks to support unit tests.
16*5225e6b1SAndroid Build Coastguard Worker //!
17*5225e6b1SAndroid Build Coastguard Worker //! This module aliases mock objects to their standard names, so that code can just unconditionally
18*5225e6b1SAndroid Build Coastguard Worker //! use e.g. `EfiEntry` and in test code it will switch to `MockEfiEntry`.
19*5225e6b1SAndroid Build Coastguard Worker 
20*5225e6b1SAndroid Build Coastguard Worker #![feature(negative_impls)]
21*5225e6b1SAndroid Build Coastguard Worker 
22*5225e6b1SAndroid Build Coastguard Worker pub mod protocol;
23*5225e6b1SAndroid Build Coastguard Worker pub mod utils;
24*5225e6b1SAndroid Build Coastguard Worker 
25*5225e6b1SAndroid Build Coastguard Worker use efi_types::{EfiConfigurationTable, EfiTimerDelay};
26*5225e6b1SAndroid Build Coastguard Worker use liberror::Result;
27*5225e6b1SAndroid Build Coastguard Worker use mockall::mock;
28*5225e6b1SAndroid Build Coastguard Worker use protocol::{
29*5225e6b1SAndroid Build Coastguard Worker     gbl_efi_ab_slot::GblSlotProtocol,
30*5225e6b1SAndroid Build Coastguard Worker     gbl_efi_avb::GblAvbProtocol,
31*5225e6b1SAndroid Build Coastguard Worker     simple_text_output::{passthrough_con_out, MockSimpleTextOutputProtocol},
32*5225e6b1SAndroid Build Coastguard Worker };
33*5225e6b1SAndroid Build Coastguard Worker use std::cell::RefCell;
34*5225e6b1SAndroid Build Coastguard Worker 
35*5225e6b1SAndroid Build Coastguard Worker /// libefi types that can be used in tests as-is.
36*5225e6b1SAndroid Build Coastguard Worker pub use efi::{efi_print, efi_println, DeviceHandle, EventNotify, EventType};
37*5225e6b1SAndroid Build Coastguard Worker 
38*5225e6b1SAndroid Build Coastguard Worker /// Holds state to set up a mock UEFI environment.
39*5225e6b1SAndroid Build Coastguard Worker ///
40*5225e6b1SAndroid Build Coastguard Worker /// Parts of the libefi API surface doesn't translate super well to mocks, so this struct helps
41*5225e6b1SAndroid Build Coastguard Worker /// cover over some of the awkwardness. In particular, APIs that return "views" over what is
42*5225e6b1SAndroid Build Coastguard Worker /// really a singleton object are difficult to mock properly.
43*5225e6b1SAndroid Build Coastguard Worker ///
44*5225e6b1SAndroid Build Coastguard Worker /// For example, the [efi::EfiEntry::system_table()] function returns a full [efi::SystemTable]
45*5225e6b1SAndroid Build Coastguard Worker /// object, not a reference. This means that our mocks have to do the same, and return a new
46*5225e6b1SAndroid Build Coastguard Worker /// [MockSystemTable] object - but this sort of defeats the purpose of mocks, which is to have
47*5225e6b1SAndroid Build Coastguard Worker /// a mock that you can set up expectations ahead of time.
48*5225e6b1SAndroid Build Coastguard Worker ///
49*5225e6b1SAndroid Build Coastguard Worker /// You can get around this in a limited fashion if the code under test only needs to grab the
50*5225e6b1SAndroid Build Coastguard Worker /// system table once; in this case you can use the `return_once()` expectation to move the mock
51*5225e6b1SAndroid Build Coastguard Worker /// around. But this will cause a runtime error if the code under test tries to grab the system
52*5225e6b1SAndroid Build Coastguard Worker /// table more than once, since the mock will have been moved out already. And since grabbing the
53*5225e6b1SAndroid Build Coastguard Worker /// system table is very common, this really restricts what you can do in a test.
54*5225e6b1SAndroid Build Coastguard Worker ///
55*5225e6b1SAndroid Build Coastguard Worker /// [MockEfi] works around this by stashing objects like this in `thread_local` state, and the
56*5225e6b1SAndroid Build Coastguard Worker /// mocks created at runtime just forward all their calls to this shared state. This allows
57*5225e6b1SAndroid Build Coastguard Worker /// expectations to be placed at the EFI system level, and ignore any intermediate "view" mocks
58*5225e6b1SAndroid Build Coastguard Worker /// that get created and destroyed over the course of the test.
59*5225e6b1SAndroid Build Coastguard Worker pub struct MockEfi {
60*5225e6b1SAndroid Build Coastguard Worker     /// The global [MockEfiEntry] to set expectations on.
61*5225e6b1SAndroid Build Coastguard Worker     pub entry: MockEfiEntry,
62*5225e6b1SAndroid Build Coastguard Worker     /// The global [MockSystemTable] to set expectations on.
63*5225e6b1SAndroid Build Coastguard Worker     pub system_table: MockSystemTable,
64*5225e6b1SAndroid Build Coastguard Worker     /// The global [MockBootServices] to set expectations on.
65*5225e6b1SAndroid Build Coastguard Worker     pub boot_services: MockBootServices,
66*5225e6b1SAndroid Build Coastguard Worker     /// The global [MockSimpleTextOutputProtocol] to set expectations on.
67*5225e6b1SAndroid Build Coastguard Worker     pub con_out: MockSimpleTextOutputProtocol,
68*5225e6b1SAndroid Build Coastguard Worker }
69*5225e6b1SAndroid Build Coastguard Worker 
70*5225e6b1SAndroid Build Coastguard Worker thread_local! {
71*5225e6b1SAndroid Build Coastguard Worker     pub(crate) static MOCK_EFI: RefCell<Option<MockEfi>> = RefCell::new(None);
72*5225e6b1SAndroid Build Coastguard Worker }
73*5225e6b1SAndroid Build Coastguard Worker 
74*5225e6b1SAndroid Build Coastguard Worker impl MockEfi {
75*5225e6b1SAndroid Build Coastguard Worker     /// Creates a new [MockEfi].
76*5225e6b1SAndroid Build Coastguard Worker     ///
77*5225e6b1SAndroid Build Coastguard Worker     /// The following expectations will be set by this function, and should generally not be
78*5225e6b1SAndroid Build Coastguard Worker     /// adjusted by the caller:
79*5225e6b1SAndroid Build Coastguard Worker     /// * `entry.system_table()` will automatically forward to `system_table`
80*5225e6b1SAndroid Build Coastguard Worker     /// * `system_table.con_out()` will automatically forward to `con_out`
81*5225e6b1SAndroid Build Coastguard Worker     ///
82*5225e6b1SAndroid Build Coastguard Worker     /// Other than that, callers may set the other expectations as needed on these mocks.
83*5225e6b1SAndroid Build Coastguard Worker     ///
84*5225e6b1SAndroid Build Coastguard Worker     /// Once the mocks are ready, call [install] to install the thread-local state.
new() -> Self85*5225e6b1SAndroid Build Coastguard Worker     pub fn new() -> Self {
86*5225e6b1SAndroid Build Coastguard Worker         let mut entry = MockEfiEntry::default();
87*5225e6b1SAndroid Build Coastguard Worker         entry.expect_system_table().returning(|| passthrough_system_table());
88*5225e6b1SAndroid Build Coastguard Worker 
89*5225e6b1SAndroid Build Coastguard Worker         let mut system_table = MockSystemTable::default();
90*5225e6b1SAndroid Build Coastguard Worker         system_table.expect_boot_services().returning(|| passthrough_boot_services());
91*5225e6b1SAndroid Build Coastguard Worker         system_table.expect_con_out().returning(|| Ok(passthrough_con_out()));
92*5225e6b1SAndroid Build Coastguard Worker 
93*5225e6b1SAndroid Build Coastguard Worker         let boot_services = MockBootServices::default();
94*5225e6b1SAndroid Build Coastguard Worker         let con_out = MockSimpleTextOutputProtocol::default();
95*5225e6b1SAndroid Build Coastguard Worker 
96*5225e6b1SAndroid Build Coastguard Worker         Self { entry, system_table, boot_services, con_out }
97*5225e6b1SAndroid Build Coastguard Worker     }
98*5225e6b1SAndroid Build Coastguard Worker 
99*5225e6b1SAndroid Build Coastguard Worker     /// Installs the [MockEfi] in thread-local state.
100*5225e6b1SAndroid Build Coastguard Worker     ///
101*5225e6b1SAndroid Build Coastguard Worker     /// Only one [MockEfi] can be installed at a time (per thread). Attempting to install a
102*5225e6b1SAndroid Build Coastguard Worker     /// second will panic.
103*5225e6b1SAndroid Build Coastguard Worker     ///
104*5225e6b1SAndroid Build Coastguard Worker     /// Returns an [InstalledMockEfi] which automatically unregisters the state on drop.
install(self) -> InstalledMockEfi105*5225e6b1SAndroid Build Coastguard Worker     pub fn install(self) -> InstalledMockEfi {
106*5225e6b1SAndroid Build Coastguard Worker         MOCK_EFI.with_borrow_mut(|efi| {
107*5225e6b1SAndroid Build Coastguard Worker             // If this error message changes the unittest will need to change as well.
108*5225e6b1SAndroid Build Coastguard Worker             assert!(efi.is_none(), "Only one MockEfi can be installed at a time (per-thread)");
109*5225e6b1SAndroid Build Coastguard Worker             *efi = Some(self)
110*5225e6b1SAndroid Build Coastguard Worker         });
111*5225e6b1SAndroid Build Coastguard Worker         InstalledMockEfi { entry: passthrough_efi_entry() }
112*5225e6b1SAndroid Build Coastguard Worker     }
113*5225e6b1SAndroid Build Coastguard Worker }
114*5225e6b1SAndroid Build Coastguard Worker 
115*5225e6b1SAndroid Build Coastguard Worker /// Scoped wrapper to automatically unregister the global [MockEfi] on drop.
116*5225e6b1SAndroid Build Coastguard Worker pub struct InstalledMockEfi {
117*5225e6b1SAndroid Build Coastguard Worker     entry: MockEfiEntry,
118*5225e6b1SAndroid Build Coastguard Worker }
119*5225e6b1SAndroid Build Coastguard Worker 
120*5225e6b1SAndroid Build Coastguard Worker impl InstalledMockEfi {
121*5225e6b1SAndroid Build Coastguard Worker     /// The user-facing [MockEfiEntry] to use in the code under test.
122*5225e6b1SAndroid Build Coastguard Worker     ///
123*5225e6b1SAndroid Build Coastguard Worker     /// This is a const ref so you cannot place expectations here, all calls will be forwarded to
124*5225e6b1SAndroid Build Coastguard Worker     /// the installed [MockEfi] mocks.
entry(&self) -> &MockEfiEntry125*5225e6b1SAndroid Build Coastguard Worker     pub fn entry(&self) -> &MockEfiEntry {
126*5225e6b1SAndroid Build Coastguard Worker         &self.entry
127*5225e6b1SAndroid Build Coastguard Worker     }
128*5225e6b1SAndroid Build Coastguard Worker }
129*5225e6b1SAndroid Build Coastguard Worker 
130*5225e6b1SAndroid Build Coastguard Worker /// [InstalledMockEfi] uses thread-local state so cannot be sent to another thread.
131*5225e6b1SAndroid Build Coastguard Worker impl !Send for InstalledMockEfi {}
132*5225e6b1SAndroid Build Coastguard Worker 
133*5225e6b1SAndroid Build Coastguard Worker impl Drop for InstalledMockEfi {
drop(&mut self)134*5225e6b1SAndroid Build Coastguard Worker     fn drop(&mut self) {
135*5225e6b1SAndroid Build Coastguard Worker         MOCK_EFI.with_borrow_mut(|efi| *efi = None);
136*5225e6b1SAndroid Build Coastguard Worker     }
137*5225e6b1SAndroid Build Coastguard Worker }
138*5225e6b1SAndroid Build Coastguard Worker 
139*5225e6b1SAndroid Build Coastguard Worker mock! {
140*5225e6b1SAndroid Build Coastguard Worker     /// Mock [efi::EfiEntry].
141*5225e6b1SAndroid Build Coastguard Worker     pub EfiEntry {
142*5225e6b1SAndroid Build Coastguard Worker         /// Returns a [MockSystemTable].
143*5225e6b1SAndroid Build Coastguard Worker         pub fn system_table(&self) -> MockSystemTable;
144*5225e6b1SAndroid Build Coastguard Worker 
145*5225e6b1SAndroid Build Coastguard Worker         /// Returns a real [efi::DeviceHandle], which is data-only so isn't mocked.
146*5225e6b1SAndroid Build Coastguard Worker         pub fn image_handle(&self) -> DeviceHandle;
147*5225e6b1SAndroid Build Coastguard Worker     }
148*5225e6b1SAndroid Build Coastguard Worker }
149*5225e6b1SAndroid Build Coastguard Worker /// Map to the libefi name so code under test can just use one name.
150*5225e6b1SAndroid Build Coastguard Worker pub type EfiEntry = MockEfiEntry;
151*5225e6b1SAndroid Build Coastguard Worker 
152*5225e6b1SAndroid Build Coastguard Worker /// While this mock itself isn't necessarily thread-local, passing through to the thread-local state
153*5225e6b1SAndroid Build Coastguard Worker /// is our primary use case, so we just disallow [Send] entirely.
154*5225e6b1SAndroid Build Coastguard Worker impl !Send for MockEfiEntry {}
155*5225e6b1SAndroid Build Coastguard Worker 
156*5225e6b1SAndroid Build Coastguard Worker /// Returns a [MockEfiEntry] that forwards all calls to `MOCK_EFI`.
passthrough_efi_entry() -> MockEfiEntry157*5225e6b1SAndroid Build Coastguard Worker fn passthrough_efi_entry() -> MockEfiEntry {
158*5225e6b1SAndroid Build Coastguard Worker     let mut entry = MockEfiEntry::default();
159*5225e6b1SAndroid Build Coastguard Worker     entry
160*5225e6b1SAndroid Build Coastguard Worker         .expect_system_table()
161*5225e6b1SAndroid Build Coastguard Worker         .returning(|| MOCK_EFI.with_borrow_mut(|efi| efi.as_mut().unwrap().entry.system_table()));
162*5225e6b1SAndroid Build Coastguard Worker     entry
163*5225e6b1SAndroid Build Coastguard Worker         .expect_image_handle()
164*5225e6b1SAndroid Build Coastguard Worker         .returning(|| MOCK_EFI.with_borrow_mut(|efi| efi.as_mut().unwrap().entry.image_handle()));
165*5225e6b1SAndroid Build Coastguard Worker     entry
166*5225e6b1SAndroid Build Coastguard Worker }
167*5225e6b1SAndroid Build Coastguard Worker 
168*5225e6b1SAndroid Build Coastguard Worker mock! {
169*5225e6b1SAndroid Build Coastguard Worker     /// Mock [efi::SystemTable].
170*5225e6b1SAndroid Build Coastguard Worker     pub SystemTable {
171*5225e6b1SAndroid Build Coastguard Worker         /// Returns a [MockBootServices].
172*5225e6b1SAndroid Build Coastguard Worker         pub fn boot_services(&self) -> MockBootServices;
173*5225e6b1SAndroid Build Coastguard Worker 
174*5225e6b1SAndroid Build Coastguard Worker         /// Returns a [MockRuntimeServices].
175*5225e6b1SAndroid Build Coastguard Worker         pub fn runtime_services(&self) -> MockRuntimeServices;
176*5225e6b1SAndroid Build Coastguard Worker 
177*5225e6b1SAndroid Build Coastguard Worker         /// Returns a [MockSimpleTextOutputProtocol]. This is a singleton protocol which is
178*5225e6b1SAndroid Build Coastguard Worker         /// always-open, as opposed to most protocols which need to be opened explicitly.
179*5225e6b1SAndroid Build Coastguard Worker         pub fn con_out(&self) -> Result<MockSimpleTextOutputProtocol>;
180*5225e6b1SAndroid Build Coastguard Worker 
181*5225e6b1SAndroid Build Coastguard Worker         /// Returns a real [efi::EfiConfigurationTable], which is data-only so isn't mocked.
182*5225e6b1SAndroid Build Coastguard Worker         pub fn configuration_table(&self) -> Option<&'static [EfiConfigurationTable]>;
183*5225e6b1SAndroid Build Coastguard Worker     }
184*5225e6b1SAndroid Build Coastguard Worker }
185*5225e6b1SAndroid Build Coastguard Worker /// Map to the libefi name so code under test can just use one name.
186*5225e6b1SAndroid Build Coastguard Worker pub type SystemTable = MockSystemTable;
187*5225e6b1SAndroid Build Coastguard Worker 
188*5225e6b1SAndroid Build Coastguard Worker /// While this mock itself isn't necessarily thread-local, passing through to the thread-local state
189*5225e6b1SAndroid Build Coastguard Worker /// is our primary use case, so we just disallow [Send] entirely.
190*5225e6b1SAndroid Build Coastguard Worker impl !Send for MockSystemTable {}
191*5225e6b1SAndroid Build Coastguard Worker 
192*5225e6b1SAndroid Build Coastguard Worker /// Returns a [MockSystemTable] that forwards all calls to `MOCK_EFI`.
passthrough_system_table() -> MockSystemTable193*5225e6b1SAndroid Build Coastguard Worker fn passthrough_system_table() -> MockSystemTable {
194*5225e6b1SAndroid Build Coastguard Worker     let mut table = MockSystemTable::default();
195*5225e6b1SAndroid Build Coastguard Worker     table.expect_boot_services().returning(|| {
196*5225e6b1SAndroid Build Coastguard Worker         MOCK_EFI.with_borrow_mut(|efi| efi.as_mut().unwrap().system_table.boot_services())
197*5225e6b1SAndroid Build Coastguard Worker     });
198*5225e6b1SAndroid Build Coastguard Worker     table
199*5225e6b1SAndroid Build Coastguard Worker         .expect_con_out()
200*5225e6b1SAndroid Build Coastguard Worker         .returning(|| MOCK_EFI.with_borrow_mut(|efi| efi.as_mut().unwrap().system_table.con_out()));
201*5225e6b1SAndroid Build Coastguard Worker     table
202*5225e6b1SAndroid Build Coastguard Worker }
203*5225e6b1SAndroid Build Coastguard Worker 
204*5225e6b1SAndroid Build Coastguard Worker mock! {
205*5225e6b1SAndroid Build Coastguard Worker     /// Mock [efi::BootServices].
206*5225e6b1SAndroid Build Coastguard Worker     pub BootServices {
207*5225e6b1SAndroid Build Coastguard Worker         /// Returns an instance of the requested type `T`.
208*5225e6b1SAndroid Build Coastguard Worker         ///
209*5225e6b1SAndroid Build Coastguard Worker         /// This is slightly different than the original API because it's difficult to mock an
210*5225e6b1SAndroid Build Coastguard Worker         /// [efi::Protocol] wrapping [efi::ProtocolInfo]. To simplify, we just return a mock
211*5225e6b1SAndroid Build Coastguard Worker         /// that looks like the protocol object.
212*5225e6b1SAndroid Build Coastguard Worker         pub fn open_protocol<T: 'static>(&self, handle: DeviceHandle) -> Result<T>;
213*5225e6b1SAndroid Build Coastguard Worker 
214*5225e6b1SAndroid Build Coastguard Worker         /// Similar to [open_protocol], returns the type `T`.
215*5225e6b1SAndroid Build Coastguard Worker         pub fn find_first_and_open<T: 'static>(&self) -> Result<T>;
216*5225e6b1SAndroid Build Coastguard Worker 
217*5225e6b1SAndroid Build Coastguard Worker         /// Returns a [MockEvent].
218*5225e6b1SAndroid Build Coastguard Worker         pub fn create_event(
219*5225e6b1SAndroid Build Coastguard Worker             &self,
220*5225e6b1SAndroid Build Coastguard Worker             event_type: EventType,
221*5225e6b1SAndroid Build Coastguard Worker             mut cb: Option<&'static mut EventNotify<'static>>,
222*5225e6b1SAndroid Build Coastguard Worker         ) -> Result<MockEvent>;
223*5225e6b1SAndroid Build Coastguard Worker 
224*5225e6b1SAndroid Build Coastguard Worker         /// Sets a [MockEvent] timer.
225*5225e6b1SAndroid Build Coastguard Worker         pub fn set_timer(
226*5225e6b1SAndroid Build Coastguard Worker             &self,
227*5225e6b1SAndroid Build Coastguard Worker             event: &MockEvent,
228*5225e6b1SAndroid Build Coastguard Worker             delay_type: EfiTimerDelay,
229*5225e6b1SAndroid Build Coastguard Worker             trigger_time: u64,
230*5225e6b1SAndroid Build Coastguard Worker         ) -> Result<()>;
231*5225e6b1SAndroid Build Coastguard Worker     }
232*5225e6b1SAndroid Build Coastguard Worker }
233*5225e6b1SAndroid Build Coastguard Worker /// Map to the libefi name so code under test can just use one name.
234*5225e6b1SAndroid Build Coastguard Worker pub type BootServices = MockBootServices;
235*5225e6b1SAndroid Build Coastguard Worker 
236*5225e6b1SAndroid Build Coastguard Worker /// Returns a [MockBootServices] that forwards all calls to `MOCK_EFI`.
passthrough_boot_services() -> MockBootServices237*5225e6b1SAndroid Build Coastguard Worker fn passthrough_boot_services() -> MockBootServices {
238*5225e6b1SAndroid Build Coastguard Worker     let mut services = MockBootServices::default();
239*5225e6b1SAndroid Build Coastguard Worker     services.expect_find_first_and_open::<GblAvbProtocol>().returning(|| {
240*5225e6b1SAndroid Build Coastguard Worker         MOCK_EFI.with_borrow_mut(|efi| {
241*5225e6b1SAndroid Build Coastguard Worker             efi.as_mut().unwrap().boot_services.find_first_and_open::<GblAvbProtocol>()
242*5225e6b1SAndroid Build Coastguard Worker         })
243*5225e6b1SAndroid Build Coastguard Worker     });
244*5225e6b1SAndroid Build Coastguard Worker     services.expect_find_first_and_open::<GblSlotProtocol>().returning(|| {
245*5225e6b1SAndroid Build Coastguard Worker         MOCK_EFI.with_borrow_mut(|efi| {
246*5225e6b1SAndroid Build Coastguard Worker             efi.as_mut().unwrap().boot_services.find_first_and_open::<GblSlotProtocol>()
247*5225e6b1SAndroid Build Coastguard Worker         })
248*5225e6b1SAndroid Build Coastguard Worker     });
249*5225e6b1SAndroid Build Coastguard Worker 
250*5225e6b1SAndroid Build Coastguard Worker     services
251*5225e6b1SAndroid Build Coastguard Worker }
252*5225e6b1SAndroid Build Coastguard Worker 
253*5225e6b1SAndroid Build Coastguard Worker mock! {
254*5225e6b1SAndroid Build Coastguard Worker     /// Mock [efi::LocatedHandles].
255*5225e6b1SAndroid Build Coastguard Worker     pub LocatedHandles {}
256*5225e6b1SAndroid Build Coastguard Worker }
257*5225e6b1SAndroid Build Coastguard Worker /// Map to the libefi name so code under test can just use one name.
258*5225e6b1SAndroid Build Coastguard Worker pub type LocatedHandles = MockLocatedHandles;
259*5225e6b1SAndroid Build Coastguard Worker 
260*5225e6b1SAndroid Build Coastguard Worker mock! {
261*5225e6b1SAndroid Build Coastguard Worker     /// Mock [efi::Event].
262*5225e6b1SAndroid Build Coastguard Worker     pub Event {}
263*5225e6b1SAndroid Build Coastguard Worker }
264*5225e6b1SAndroid Build Coastguard Worker /// Map to the libefi name so code under test can just use one name.
265*5225e6b1SAndroid Build Coastguard Worker pub type Event = MockEvent;
266*5225e6b1SAndroid Build Coastguard Worker 
267*5225e6b1SAndroid Build Coastguard Worker mock! {
268*5225e6b1SAndroid Build Coastguard Worker     /// Mock [efi::RuntimeServices].
269*5225e6b1SAndroid Build Coastguard Worker     pub RuntimeServices {
270*5225e6b1SAndroid Build Coastguard Worker         /// Performs a cold reset.
271*5225e6b1SAndroid Build Coastguard Worker         pub fn cold_reset(&self);
272*5225e6b1SAndroid Build Coastguard Worker     }
273*5225e6b1SAndroid Build Coastguard Worker }
274*5225e6b1SAndroid Build Coastguard Worker 
275*5225e6b1SAndroid Build Coastguard Worker /// Map to the libefi name so code under test can just use one name.
276*5225e6b1SAndroid Build Coastguard Worker pub type RuntimeServices = MockRuntimeServices;
277*5225e6b1SAndroid Build Coastguard Worker 
278*5225e6b1SAndroid Build Coastguard Worker #[cfg(test)]
279*5225e6b1SAndroid Build Coastguard Worker pub mod test {
280*5225e6b1SAndroid Build Coastguard Worker     use super::*;
281*5225e6b1SAndroid Build Coastguard Worker     use mockall::predicate::eq;
282*5225e6b1SAndroid Build Coastguard Worker     use std::fmt::Write;
283*5225e6b1SAndroid Build Coastguard Worker 
284*5225e6b1SAndroid Build Coastguard Worker     #[test]
efi_state_install()285*5225e6b1SAndroid Build Coastguard Worker     fn efi_state_install() {
286*5225e6b1SAndroid Build Coastguard Worker         MOCK_EFI.with_borrow_mut(|efi| assert!(efi.is_none()));
287*5225e6b1SAndroid Build Coastguard Worker 
288*5225e6b1SAndroid Build Coastguard Worker         // Global should still be `None` until we call `install()`.
289*5225e6b1SAndroid Build Coastguard Worker         let mock_efi = MockEfi::new();
290*5225e6b1SAndroid Build Coastguard Worker         MOCK_EFI.with_borrow_mut(|efi| assert!(efi.is_none()));
291*5225e6b1SAndroid Build Coastguard Worker 
292*5225e6b1SAndroid Build Coastguard Worker         let installed = mock_efi.install();
293*5225e6b1SAndroid Build Coastguard Worker         MOCK_EFI.with_borrow_mut(|efi| assert!(efi.is_some()));
294*5225e6b1SAndroid Build Coastguard Worker 
295*5225e6b1SAndroid Build Coastguard Worker         // Global goes back to `None` once the install goes out of scope.
296*5225e6b1SAndroid Build Coastguard Worker         drop(installed);
297*5225e6b1SAndroid Build Coastguard Worker         MOCK_EFI.with_borrow_mut(|efi| assert!(efi.is_none()));
298*5225e6b1SAndroid Build Coastguard Worker     }
299*5225e6b1SAndroid Build Coastguard Worker 
300*5225e6b1SAndroid Build Coastguard Worker     #[test]
301*5225e6b1SAndroid Build Coastguard Worker     #[should_panic(expected = "Only one MockEfi can be installed at a time (per-thread)")]
efi_state_double_install_fails()302*5225e6b1SAndroid Build Coastguard Worker     fn efi_state_double_install_fails() {
303*5225e6b1SAndroid Build Coastguard Worker         let mock_efi = MockEfi::new();
304*5225e6b1SAndroid Build Coastguard Worker         let mock_efi_2 = MockEfi::new();
305*5225e6b1SAndroid Build Coastguard Worker 
306*5225e6b1SAndroid Build Coastguard Worker         let installed = mock_efi.install();
307*5225e6b1SAndroid Build Coastguard Worker         mock_efi_2.install();
308*5225e6b1SAndroid Build Coastguard Worker 
309*5225e6b1SAndroid Build Coastguard Worker         // Explicit drop to keep it in scope until here.
310*5225e6b1SAndroid Build Coastguard Worker         drop(installed);
311*5225e6b1SAndroid Build Coastguard Worker     }
312*5225e6b1SAndroid Build Coastguard Worker 
313*5225e6b1SAndroid Build Coastguard Worker     #[test]
efi_state_con_out_write_once()314*5225e6b1SAndroid Build Coastguard Worker     fn efi_state_con_out_write_once() {
315*5225e6b1SAndroid Build Coastguard Worker         let mut mock_efi = MockEfi::new();
316*5225e6b1SAndroid Build Coastguard Worker         mock_efi.con_out.expect_write_str().once().with(eq("foo 123")).return_const(Ok(()));
317*5225e6b1SAndroid Build Coastguard Worker 
318*5225e6b1SAndroid Build Coastguard Worker         let installed = mock_efi.install();
319*5225e6b1SAndroid Build Coastguard Worker         let efi_entry = installed.entry();
320*5225e6b1SAndroid Build Coastguard Worker 
321*5225e6b1SAndroid Build Coastguard Worker         assert!(write!(efi_entry.system_table().con_out().unwrap(), "{} {}", "foo", 123).is_ok());
322*5225e6b1SAndroid Build Coastguard Worker     }
323*5225e6b1SAndroid Build Coastguard Worker 
324*5225e6b1SAndroid Build Coastguard Worker     #[test]
efi_state_con_out_write_twice_same_mock()325*5225e6b1SAndroid Build Coastguard Worker     fn efi_state_con_out_write_twice_same_mock() {
326*5225e6b1SAndroid Build Coastguard Worker         let mut mock_efi = MockEfi::new();
327*5225e6b1SAndroid Build Coastguard Worker         mock_efi.con_out.expect_write_str().once().with(eq("foo 123")).return_const(Ok(()));
328*5225e6b1SAndroid Build Coastguard Worker         mock_efi.con_out.expect_write_str().once().with(eq("bar 456")).return_const(Ok(()));
329*5225e6b1SAndroid Build Coastguard Worker 
330*5225e6b1SAndroid Build Coastguard Worker         let installed = mock_efi.install();
331*5225e6b1SAndroid Build Coastguard Worker         let efi_entry = installed.entry();
332*5225e6b1SAndroid Build Coastguard Worker 
333*5225e6b1SAndroid Build Coastguard Worker         let mut con_out = efi_entry.system_table().con_out().unwrap();
334*5225e6b1SAndroid Build Coastguard Worker         assert!(write!(con_out, "{} {}", "foo", 123).is_ok());
335*5225e6b1SAndroid Build Coastguard Worker         assert!(write!(con_out, "{} {}", "bar", 456).is_ok());
336*5225e6b1SAndroid Build Coastguard Worker     }
337*5225e6b1SAndroid Build Coastguard Worker 
338*5225e6b1SAndroid Build Coastguard Worker     #[test]
efi_state_con_out_write_twice_different_mock()339*5225e6b1SAndroid Build Coastguard Worker     fn efi_state_con_out_write_twice_different_mock() {
340*5225e6b1SAndroid Build Coastguard Worker         let mut mock_efi = MockEfi::new();
341*5225e6b1SAndroid Build Coastguard Worker         mock_efi.con_out.expect_write_str().once().with(eq("foo 123")).return_const(Ok(()));
342*5225e6b1SAndroid Build Coastguard Worker         mock_efi.con_out.expect_write_str().once().with(eq("bar 456")).return_const(Ok(()));
343*5225e6b1SAndroid Build Coastguard Worker 
344*5225e6b1SAndroid Build Coastguard Worker         let installed = mock_efi.install();
345*5225e6b1SAndroid Build Coastguard Worker         let efi_entry = installed.entry();
346*5225e6b1SAndroid Build Coastguard Worker 
347*5225e6b1SAndroid Build Coastguard Worker         // Call `write!` on two separate passthrough mocks, both should forward the calls to
348*5225e6b1SAndroid Build Coastguard Worker         // the "real" global mock.
349*5225e6b1SAndroid Build Coastguard Worker         //
350*5225e6b1SAndroid Build Coastguard Worker         // A common instance of this is `efi_print!` which fetches a new `con_out` on every call.
351*5225e6b1SAndroid Build Coastguard Worker         assert!(write!(efi_entry.system_table().con_out().unwrap(), "{} {}", "foo", 123).is_ok());
352*5225e6b1SAndroid Build Coastguard Worker         assert!(write!(efi_entry.system_table().con_out().unwrap(), "{} {}", "bar", 456).is_ok());
353*5225e6b1SAndroid Build Coastguard Worker     }
354*5225e6b1SAndroid Build Coastguard Worker }
355