avr_tester/
builder.rs

1use crate::AvrTester;
2use std::path::Path;
3use std::time::Duration;
4
5pub struct AvrTesterBuilder {
6    mcu: String,
7    clock: Option<u32>,
8    timeout: Option<Duration>,
9}
10
11impl AvrTesterBuilder {
12    /// Creates `AvrTesterBuilder`.
13    ///
14    /// To avoid typos, it's preferred that you use helper functions such as
15    /// [`AvrTester::atmega328p()`] - this constructor is provided just in case.
16    pub fn new(mcu: impl ToString) -> Self {
17        Self {
18            mcu: mcu.to_string(),
19            clock: None,
20            timeout: None,
21        }
22    }
23
24    /// Specifies AVR's clock.
25    ///
26    /// This value doesn't affect how fast the simulation is run - it's used
27    /// mostly so that [`AvrTester::run_for_s()`] and similar functions know how
28    /// long a second, a millisecond etc. should be.
29    ///
30    /// See:
31    ///
32    /// - [`Self::with_clock_of_1_mhz()`],
33    /// - [`Self::with_clock_of_4_mhz()`],
34    /// - [`Self::with_clock_of_8_mhz()`],
35    /// - [`Self::with_clock_of_16_mhz()`],
36    /// - [`Self::with_clock_of_20_mhz()`],
37    /// - [`Self::with_clock_of_24_mhz()`].
38    pub fn with_clock(mut self, clock: u32) -> Self {
39        self.clock = Some(clock);
40        self
41    }
42
43    /// See: [`Self::with_clock()`].
44    pub fn with_clock_of_1_mhz(self) -> Self {
45        self.with_clock(1_000_000)
46    }
47
48    /// See: [`Self::with_clock()`].
49    pub fn with_clock_of_4_mhz(self) -> Self {
50        self.with_clock(4_000_000)
51    }
52
53    /// See: [`Self::with_clock()`].
54    pub fn with_clock_of_8_mhz(self) -> Self {
55        self.with_clock(8_000_000)
56    }
57
58    /// See: [`Self::with_clock()`].
59    pub fn with_clock_of_12_mhz(self) -> Self {
60        self.with_clock(12_000_000)
61    }
62
63    /// See: [`Self::with_clock()`].
64    pub fn with_clock_of_16_mhz(self) -> Self {
65        self.with_clock(16_000_000)
66    }
67
68    /// See: [`Self::with_clock()`].
69    pub fn with_clock_of_20_mhz(self) -> Self {
70        self.with_clock(20_000_000)
71    }
72
73    /// See: [`Self::with_clock()`].
74    pub fn with_clock_of_24_mhz(self) -> Self {
75        self.with_clock(24_000_000)
76    }
77
78    /// Specifies a timeout (in AVR's time¹) after which calling
79    /// [`AvrTester::run()`] (or a similar function) will panic, aborting the
80    /// test to signal that it has timed out.
81    ///
82    /// This might come handy in tests that wait for AVR to do something:
83    ///
84    /// ```no_run
85    /// # use avr_tester::AvrTester;
86    /// #
87    /// let mut avr = AvrTester::atmega328p()
88    ///     .with_clock_of_16_mhz()
89    ///     .with_timeout_of_s(1)
90    ///     .load("...");
91    ///
92    /// while avr.pins().pb1().is_low() {
93    ///     avr.run_for_ms(1);
94    /// }
95    ///
96    /// /* do something else later */
97    /// ```
98    ///
99    /// ... as otherwise, without specifying the timeout, if the tested firmware
100    /// misbehaves (and e.g. doesn't toggle the expected pin), the test would be
101    /// running in an infinite loop (instead of failing).
102    ///
103    /// ¹ using AVR's time instead of the host's time allows to assert this
104    ///   reliably - whatever timeout you set here will be consistent across all
105    ///   machines.
106    pub fn with_timeout(mut self, timeout: Duration) -> Self {
107        self.timeout = Some(timeout);
108        self
109    }
110
111    /// Specifies a timeout in seconds (of AVR's time).
112    ///
113    /// See: [`Self::with_timeout()`].
114    pub fn with_timeout_of_s(self, s: u64) -> Self {
115        self.with_timeout(Duration::from_secs(s))
116    }
117
118    /// Specifies a timeout in milliseconds (of AVR's time).
119    ///
120    /// See: [`Self::with_timeout()`].
121    pub fn with_timeout_of_ms(self, ms: u64) -> Self {
122        self.with_timeout(Duration::from_millis(ms))
123    }
124
125    /// Specifies a timeout in microseconds (of AVR's time).
126    ///
127    /// See: [`Self::with_timeout()`].
128    pub fn with_timeout_of_us(self, us: u64) -> Self {
129        self.with_timeout(Duration::from_micros(us))
130    }
131
132    /// Loads given firmware (an `*.elf` file) and boots the simulator.
133    #[doc(alias = "build")]
134    pub fn load(self, firmware: impl AsRef<Path>) -> AvrTester {
135        let clock_frequency = self
136            .clock
137            .expect("Clock frequency was not specified; please call `.with_clock()` before");
138
139        let remaining_clock_cycles = self
140            .timeout
141            .map(|timeout| (timeout.as_secs_f32() * (clock_frequency as f32)))
142            .map(|cc| cc as _);
143
144        AvrTester::new(
145            &self.mcu,
146            clock_frequency,
147            firmware,
148            remaining_clock_cycles,
149        )
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    fn target() -> AvrTesterBuilder {
158        AvrTesterBuilder::new("some-random-avr")
159    }
160
161    #[test]
162    fn with_clock() {
163        let target = target().with_clock(123);
164
165        assert_eq!(Some(123), target.clock);
166    }
167
168    #[test]
169    fn with_clock_of_1_mhz() {
170        let target = target().with_clock_of_1_mhz();
171
172        assert_eq!(Some(1_000_000), target.clock);
173    }
174
175    #[test]
176    fn with_clock_of_4_mhz() {
177        let target = target().with_clock_of_4_mhz();
178
179        assert_eq!(Some(4_000_000), target.clock);
180    }
181
182    #[test]
183    fn with_clock_of_8_mhz() {
184        let target = target().with_clock_of_8_mhz();
185
186        assert_eq!(Some(8_000_000), target.clock);
187    }
188
189    #[test]
190    fn with_clock_of_12_mhz() {
191        let target = target().with_clock_of_12_mhz();
192
193        assert_eq!(Some(12_000_000), target.clock);
194    }
195
196    #[test]
197    fn with_clock_of_16_mhz() {
198        let target = target().with_clock_of_16_mhz();
199
200        assert_eq!(Some(16_000_000), target.clock);
201    }
202
203    #[test]
204    fn with_clock_of_20_mhz() {
205        let target = target().with_clock_of_20_mhz();
206
207        assert_eq!(Some(20_000_000), target.clock);
208    }
209
210    #[test]
211    fn with_clock_of_24_mhz() {
212        let target = target().with_clock_of_24_mhz();
213
214        assert_eq!(Some(24_000_000), target.clock);
215    }
216
217    #[test]
218    fn with_timeout() {
219        let target = target().with_timeout(Duration::from_secs(321));
220
221        // Note that the actual timeouting logic is already covered by through
222        // our integration tests - in here we just want to assert that the field
223        // is stored correctly:
224        assert_eq!(Some(Duration::from_secs(321)), target.timeout);
225    }
226
227    #[test]
228    fn with_timeout_of_s() {
229        let target = target().with_timeout_of_s(123);
230
231        assert_eq!(Some(Duration::from_secs(123)), target.timeout);
232    }
233
234    #[test]
235    fn with_timeout_of_ms() {
236        let target = target().with_timeout_of_ms(123);
237
238        assert_eq!(Some(Duration::from_millis(123)), target.timeout);
239    }
240
241    #[test]
242    fn with_timeout_of_us() {
243        let target = target().with_timeout_of_us(123);
244
245        assert_eq!(Some(Duration::from_micros(123)), target.timeout);
246    }
247}