• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

google / OpenSK / 1947671397

pending completion
1947671397

Pull #439

github

GitHub
Merge cd5538621 into 7c1ddcda0
Pull Request #439: roll back fuzzing install script, documentation instead

12877 of 14481 relevant lines covered (88.92%)

522337.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

49.7
/libraries/persistent_store/src/buffer.rs
1
// Copyright 2019-2020 Google LLC
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//      http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
//! Flash storage for testing.
16
//!
17
//! [`BufferStorage`] implements the flash [`Storage`] interface but doesn't interface with an
18
//! actual flash storage. Instead it uses a buffer in memory to represent the storage state.
19

20
use crate::{Storage, StorageError, StorageIndex, StorageResult};
21
use alloc::borrow::Borrow;
22
use alloc::boxed::Box;
23
use alloc::vec;
24

25
/// Simulates a flash storage using a buffer in memory.
26
///
27
/// This buffer storage can be used in place of an actual flash storage. It is particularly useful
28
/// for tests and fuzzing, for which it has dedicated functionalities.
29
///
30
/// This storage tracks how many times words are written between page erase cycles, how many times
31
/// pages are erased, and whether an operation flips bits in the wrong direction. Operations panic
32
/// if those conditions are broken (optional). This storage also permits to interrupt operations for
33
/// inspection or to corrupt the operation.
34
#[derive(Clone)]
35
pub struct BufferStorage {
36
    /// Content of the storage.
37
    storage: Box<[u8]>,
38

39
    /// Options of the storage.
40
    options: BufferOptions,
41

42
    /// Number of times a word was written since the last time its page was erased.
43
    word_writes: Box<[usize]>,
44

45
    /// Number of times a page was erased.
46
    page_erases: Box<[usize]>,
47

48
    /// Interruption state.
49
    interruption: Interruption,
50
}
51

52
/// Options of a buffer storage.
53
#[derive(Clone, Debug)]
54
pub struct BufferOptions {
55
    /// Size of a word in bytes.
56
    pub word_size: usize,
57

58
    /// Size of a page in bytes.
59
    pub page_size: usize,
60

61
    /// How many times a word can be written between page erase cycles.
62
    pub max_word_writes: usize,
63

64
    /// How many times a page can be erased.
65
    pub max_page_erases: usize,
66

67
    /// Whether the storage should check the flash invariant.
68
    ///
69
    /// When set, the following conditions would panic:
70
    /// - A bit is written from 0 to 1.
71
    /// - A word is written more than [`Self::max_word_writes`].
72
    /// - A page is erased more than [`Self::max_page_erases`].
73
    pub strict_mode: bool,
74
}
75

76
/// Corrupts a slice given actual and expected value.
77
///
78
/// A corruption function is called exactly once and takes 2 arguments:
79
/// - A mutable slice representing the storage before the interrupted operation.
80
/// - A shared slice representing what the storage would have been if the operation was not
81
///   interrupted.
82
///
83
/// The corruption function may flip an arbitrary number of bits in the mutable slice, but may only
84
/// flip bits that differ between both slices.
85
pub type BufferCorruptFunction<'a> = Box<dyn FnOnce(&mut [u8], &[u8]) + 'a>;
86

87
impl BufferStorage {
88
    /// Creates a buffer storage.
89
    ///
90
    /// # Panics
91
    ///
92
    /// The following preconditions must hold:
93
    /// - `options.word_size` must be a power of two.
94
    /// - `options.page_size` must be a power of two.
95
    /// - `options.page_size` must be word-aligned.
96
    /// - `storage.len()` must be page-aligned.
97
    pub fn new(storage: Box<[u8]>, options: BufferOptions) -> BufferStorage {
665✔
98
        assert!(options.word_size.is_power_of_two());
665✔
99
        assert!(options.page_size.is_power_of_two());
665✔
100
        let num_words = storage.len() / options.word_size;
665✔
101
        let num_pages = storage.len() / options.page_size;
665✔
102
        let buffer = BufferStorage {
665✔
103
            storage,
665✔
104
            options,
665✔
105
            word_writes: vec![0; num_words].into_boxed_slice(),
665✔
106
            page_erases: vec![0; num_pages].into_boxed_slice(),
665✔
107
            interruption: Interruption::Ready,
665✔
108
        };
665✔
109
        assert!(buffer.is_word_aligned(buffer.options.page_size));
665✔
110
        assert!(buffer.is_page_aligned(buffer.storage.len()));
665✔
111
        buffer
665✔
112
    }
665✔
113

114
    /// Arms an interruption after a given delay.
115
    ///
116
    /// Before each subsequent mutable operation (write or erase), the delay is decremented if
117
    /// positive. If the delay is elapsed, the operation is saved and an error is returned.
118
    /// Subsequent operations will panic until either of:
119
    /// - The interrupted operation is [corrupted](BufferStorage::corrupt_operation).
120
    /// - The interruption is [reset](BufferStorage::reset_interruption).
121
    ///
122
    /// # Panics
123
    ///
124
    /// Panics if an interruption is already armed.
125
    pub fn arm_interruption(&mut self, delay: usize) {
126
        self.interruption.arm(delay);
127
    }
128

129
    /// Disarms an interruption that did not trigger.
130
    ///
131
    /// Returns the remaining delay.
132
    ///
133
    /// # Panics
134
    ///
135
    /// Panics if any of the following conditions hold:
136
    /// - An interruption was not [armed](BufferStorage::arm_interruption).
137
    /// - An interruption was armed and it has triggered.
138
    pub fn disarm_interruption(&mut self) -> usize {
139
        self.interruption.get().err().unwrap()
140
    }
141

142
    /// Resets an interruption regardless of triggering.
143
    ///
144
    /// # Panics
145
    ///
146
    /// Panics if an interruption was not [armed](BufferStorage::arm_interruption).
147
    pub fn reset_interruption(&mut self) {
148
        let _ = self.interruption.get();
149
    }
150

151
    /// Corrupts an interrupted operation.
152
    ///
153
    /// Applies the corruption function to the storage. Counters are updated accordingly:
154
    /// - If a word is fully written, its counter is incremented regardless of whether other words
155
    ///   of the same operation have been fully written.
156
    /// - If a page is fully erased, its counter is incremented (and its word counters are reset).
157
    ///
158
    /// # Panics
159
    ///
160
    /// Panics if any of the following conditions hold:
161
    /// - An interruption was not [armed](BufferStorage::arm_interruption).
162
    /// - An interruption was armed but did not trigger.
163
    /// - The corruption function corrupts more bits than allowed.
164
    /// - The interrupted operation itself would have panicked.
165
    pub fn corrupt_operation(&mut self, corrupt: BufferCorruptFunction) {
×
166
        let operation = self.interruption.get().unwrap();
×
167
        let range = self.operation_range(&operation).unwrap();
×
168
        let mut before = self.storage[range.clone()].to_vec().into_boxed_slice();
×
169
        match operation {
×
170
            BufferOperation::Write { value: after, .. } => {
×
171
                corrupt(&mut before, &after);
×
172
                self.incr_word_writes(range.start, &before, &after);
×
173
            }
×
174
            BufferOperation::Erase { page } => {
×
175
                let after = vec![0xff; self.page_size()].into_boxed_slice();
×
176
                corrupt(&mut before, &after);
×
177
                if before == after {
×
178
                    self.incr_page_erases(page);
×
179
                }
×
180
            }
181
        };
182
        self.storage[range].copy_from_slice(&before);
×
183
    }
×
184

185
    /// Returns the number of times a word was written.
186
    pub fn get_word_writes(&self, word: usize) -> usize {
187
        self.word_writes[word]
188
    }
189

190
    /// Returns the number of times a page was erased.
191
    pub fn get_page_erases(&self, page: usize) -> usize {
192
        self.page_erases[page]
193
    }
194

195
    /// Sets the number of times a page was erased.
196
    pub fn set_page_erases(&mut self, page: usize, cycle: usize) {
197
        self.page_erases[page] = cycle;
198
    }
199

200
    /// Returns whether a number is word-aligned.
201
    fn is_word_aligned(&self, x: usize) -> bool {
34,455✔
202
        x & (self.options.word_size - 1) == 0
34,455✔
203
    }
34,455✔
204

205
    /// Returns whether a number is page-aligned.
206
    fn is_page_aligned(&self, x: usize) -> bool {
665✔
207
        x & (self.options.page_size - 1) == 0
665✔
208
    }
665✔
209

210
    /// Updates the counters as if a page was erased.
211
    ///
212
    /// The page counter of that page is incremented and the word counters of that page are reset.
213
    ///
214
    /// # Panics
215
    ///
216
    /// Panics if the [maximum number of erase cycles per page](BufferOptions::max_page_erases) is
217
    /// reached.
218
    fn incr_page_erases(&mut self, page: usize) {
×
219
        // Check that pages are not erased too many times.
220
        if self.options.strict_mode {
×
221
            assert!(self.page_erases[page] < self.max_page_erases());
×
222
        }
×
223
        self.page_erases[page] += 1;
×
224
        let num_words = self.page_size() / self.word_size();
×
225
        for word in 0..num_words {
×
226
            self.word_writes[page * num_words + word] = 0;
×
227
        }
×
228
    }
×
229

230
    /// Updates the word counters as if a partial write occurred.
231
    ///
232
    /// The partial write is described as if `complete` was supposed to be written to the storage
233
    /// starting at byte `index`, but actually only `value` was written. Word counters are
234
    /// incremented only if their value would change and they would be completely written.
235
    ///
236
    /// # Preconditions
237
    ///
238
    /// - `index` must be word-aligned.
239
    /// - `value` and `complete` must have the same word-aligned length.
240
    ///
241
    /// # Panics
242
    ///
243
    /// Panics if the [maximum number of writes per word](BufferOptions::max_word_writes) is
244
    /// reached.
245
    fn incr_word_writes(&mut self, index: usize, value: &[u8], complete: &[u8]) {
16,895✔
246
        let word_size = self.word_size();
16,895✔
247
        for i in 0..value.len() / word_size {
118,205✔
248
            let range = core::ops::Range {
118,205✔
249
                start: i * word_size,
118,205✔
250
                end: (i + 1) * word_size,
118,205✔
251
            };
118,205✔
252
            // Partial word writes do not count.
118,205✔
253
            if value[range.clone()] != complete[range.clone()] {
118,205✔
254
                continue;
255
            }
118,205✔
256
            // Words are written only if necessary.
118,205✔
257
            if value[range.clone()] == self.storage[index..][range] {
118,205✔
258
                continue;
259
            }
118,205✔
260
            let word = index / word_size + i;
118,205✔
261
            // Check that words are not written too many times.
118,205✔
262
            if self.options.strict_mode {
118,205✔
263
                assert!(self.word_writes[word] < self.max_word_writes());
118,205✔
264
            }
×
265
            self.word_writes[word] += 1;
118,205✔
266
        }
267
    }
16,895✔
268

269
    /// Returns the storage range of an operation.
270
    fn operation_range(
16,895✔
271
        &self,
16,895✔
272
        operation: &BufferOperation<impl Borrow<[u8]>>,
16,895✔
273
    ) -> StorageResult<core::ops::Range<usize>> {
16,895✔
274
        match *operation {
16,895✔
275
            BufferOperation::Write { index, ref value } => index.range(value.borrow().len(), self),
16,895✔
276
            BufferOperation::Erase { page } => {
×
277
                StorageIndex { page, byte: 0 }.range(self.page_size(), self)
×
278
            }
279
        }
280
    }
16,895✔
281
}
282

283
impl Storage for BufferStorage {
284
    fn word_size(&self) -> usize {
17,560✔
285
        self.options.word_size
17,560✔
286
    }
17,560✔
287

288
    fn page_size(&self) -> usize {
13,626,760✔
289
        self.options.page_size
13,626,760✔
290
    }
13,626,760✔
291

292
    fn num_pages(&self) -> usize {
6,935,660✔
293
        self.storage.len() / self.options.page_size
6,935,660✔
294
    }
6,935,660✔
295

296
    fn max_word_writes(&self) -> usize {
118,870✔
297
        self.options.max_word_writes
118,870✔
298
    }
118,870✔
299

300
    fn max_page_erases(&self) -> usize {
1,330✔
301
        self.options.max_page_erases
1,330✔
302
    }
1,330✔
303

304
    fn read_slice(&self, index: StorageIndex, length: usize) -> StorageResult<&[u8]> {
6,935,855✔
305
        Ok(&self.storage[index.range(length, self)?])
6,935,855✔
306
    }
6,935,855✔
307

308
    fn write_slice(&mut self, index: StorageIndex, value: &[u8]) -> StorageResult<()> {
16,895✔
309
        if !self.is_word_aligned(index.byte) || !self.is_word_aligned(value.len()) {
16,895✔
310
            return Err(StorageError::NotAligned);
×
311
        }
16,895✔
312
        let operation = BufferOperation::Write { index, value };
16,895✔
313
        let range = self.operation_range(&operation)?;
16,895✔
314
        // Interrupt operation if armed and delay expired.
315
        self.interruption.tick(&operation)?;
16,895✔
316
        // Check and update counters.
317
        self.incr_word_writes(range.start, value, value);
16,895✔
318
        // Check that bits are correctly flipped.
16,895✔
319
        if self.options.strict_mode {
16,895✔
320
            for (byte, &val) in range.clone().zip(value.iter()) {
472,405✔
321
                assert_eq!(self.storage[byte] & val, val);
472,405✔
322
            }
323
        }
×
324
        // Write to the storage.
325
        self.storage[range].copy_from_slice(value);
16,895✔
326
        Ok(())
16,895✔
327
    }
16,895✔
328

329
    fn erase_page(&mut self, page: usize) -> StorageResult<()> {
×
330
        let operation = BufferOperation::Erase { page };
×
331
        let range = self.operation_range(&operation)?;
×
332
        // Interrupt operation if armed and delay expired.
333
        self.interruption.tick(&operation)?;
×
334
        // Check and update counters.
335
        self.incr_page_erases(page);
×
336
        // Write to the storage.
337
        for byte in &mut self.storage[range] {
×
338
            *byte = 0xff;
×
339
        }
×
340
        Ok(())
×
341
    }
×
342
}
343

344
impl core::fmt::Display for BufferStorage {
345
    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
×
346
        let num_pages = self.num_pages();
×
347
        let num_words = self.page_size() / self.word_size();
×
348
        let num_bytes = self.word_size();
×
349
        for page in 0..num_pages {
×
350
            write!(f, "[{}]", self.page_erases[page])?;
×
351
            for word in 0..num_words {
×
352
                write!(f, " [{}]", self.word_writes[page * num_words + word])?;
×
353
                for byte in 0..num_bytes {
×
354
                    let index = (page * num_words + word) * num_bytes + byte;
×
355
                    write!(f, "{:02x}", self.storage[index])?;
×
356
                }
357
            }
358
            writeln!(f)?;
×
359
        }
360
        Ok(())
×
361
    }
×
362
}
363

364
/// Represents a storage operation.
365
///
366
/// It is polymorphic over the ownership of the byte slice to avoid unnecessary copies.
367
#[derive(Clone, Debug, PartialEq, Eq)]
×
368
enum BufferOperation<ByteSlice: Borrow<[u8]>> {
369
    /// Represents a write operation.
370
    Write {
371
        /// The storage index at which the write should occur.
372
        index: StorageIndex,
373

374
        /// The slice that should be written.
375
        value: ByteSlice,
376
    },
377

378
    /// Represents an erase operation.
379
    Erase {
380
        /// The page that should be erased.
381
        page: usize,
382
    },
383
}
384

385
/// Represents a storage operation owning its byte slices.
386
type OwnedBufferOperation = BufferOperation<Box<[u8]>>;
387

388
/// Represents a storage operation sharing its byte slices.
389
type SharedBufferOperation<'a> = BufferOperation<&'a [u8]>;
390

391
impl<'a> SharedBufferOperation<'a> {
392
    fn to_owned(&self) -> OwnedBufferOperation {
×
393
        match *self {
×
394
            BufferOperation::Write { index, value } => BufferOperation::Write {
×
395
                index,
×
396
                value: value.to_vec().into_boxed_slice(),
×
397
            },
×
398
            BufferOperation::Erase { page } => BufferOperation::Erase { page },
×
399
        }
400
    }
×
401
}
402

403
/// Controls when an operation is interrupted.
404
///
405
/// This can be used to simulate power-offs while the device is writing to the storage or erasing a
406
/// page in the storage.
407
#[derive(Clone)]
408
enum Interruption {
409
    /// Mutable operations have normal behavior.
410
    Ready,
411

412
    /// If the delay is positive, mutable operations decrement it. If the count is zero, mutable
413
    /// operations fail and are saved.
414
    Armed { delay: usize },
415

416
    /// Mutable operations panic.
417
    Saved { operation: OwnedBufferOperation },
418
}
419

420
impl Interruption {
421
    /// Arms an interruption for a given delay.
422
    ///
423
    /// # Panics
424
    ///
425
    /// Panics if an interruption is already armed.
426
    fn arm(&mut self, delay: usize) {
×
427
        match self {
×
428
            Interruption::Ready => *self = Interruption::Armed { delay },
×
429
            _ => panic!(),
×
430
        }
431
    }
×
432

433
    /// Disarms an interruption.
434
    ///
435
    /// Returns the interrupted operation if any, otherwise the remaining delay.
436
    ///
437
    /// # Panics
438
    ///
439
    /// Panics if an interruption was not armed.
440
    fn get(&mut self) -> Result<OwnedBufferOperation, usize> {
×
441
        let mut interruption = Interruption::Ready;
×
442
        core::mem::swap(self, &mut interruption);
×
443
        match interruption {
×
444
            Interruption::Armed { delay } => Err(delay),
×
445
            Interruption::Saved { operation } => Ok(operation),
×
446
            _ => panic!(),
×
447
        }
448
    }
×
449

450
    /// Interrupts an operation if the delay is over.
451
    ///
452
    /// Decrements the delay if positive. Otherwise, the operation is stored and an error is
453
    /// returned to interrupt the operation.
454
    ///
455
    /// # Panics
456
    ///
457
    /// Panics if an operation has already been interrupted and the interruption has not been
458
    /// disarmed.
459
    fn tick(&mut self, operation: &SharedBufferOperation) -> StorageResult<()> {
16,895✔
460
        match self {
×
461
            Interruption::Ready => (),
16,895✔
462
            Interruption::Armed { delay } if *delay == 0 => {
×
463
                let operation = operation.to_owned();
×
464
                *self = Interruption::Saved { operation };
×
465
                return Err(StorageError::CustomError);
×
466
            }
467
            Interruption::Armed { delay } => *delay -= 1,
×
468
            Interruption::Saved { .. } => panic!(),
×
469
        }
470
        Ok(())
16,895✔
471
    }
16,895✔
472
}
473

474
#[cfg(test)]
475
mod tests {
476
    use super::*;
477

478
    const NUM_PAGES: usize = 2;
479
    const OPTIONS: BufferOptions = BufferOptions {
480
        word_size: 4,
481
        page_size: 16,
482
        max_word_writes: 2,
483
        max_page_erases: 3,
484
        strict_mode: true,
485
    };
486
    // Those words are decreasing bit patterns. Bits are only changed from 1 to 0 and at least one
487
    // bit is changed.
488
    const BLANK_WORD: &[u8] = &[0xff, 0xff, 0xff, 0xff];
489
    const FIRST_WORD: &[u8] = &[0xee, 0xdd, 0xbb, 0x77];
490
    const SECOND_WORD: &[u8] = &[0xca, 0xc9, 0xa9, 0x65];
491
    const THIRD_WORD: &[u8] = &[0x88, 0x88, 0x88, 0x44];
492

493
    fn new_storage() -> Box<[u8]> {
494
        vec![0xff; NUM_PAGES * OPTIONS.page_size].into_boxed_slice()
495
    }
496

497
    #[test]
498
    fn words_are_decreasing() {
499
        fn assert_is_decreasing(prev: &[u8], next: &[u8]) {
500
            for (&prev, &next) in prev.iter().zip(next.iter()) {
501
                assert_eq!(prev & next, next);
502
                assert!(prev != next);
503
            }
504
        }
505
        assert_is_decreasing(BLANK_WORD, FIRST_WORD);
506
        assert_is_decreasing(FIRST_WORD, SECOND_WORD);
507
        assert_is_decreasing(SECOND_WORD, THIRD_WORD);
508
    }
509

510
    #[test]
511
    fn options_ok() {
512
        let buffer = BufferStorage::new(new_storage(), OPTIONS);
513
        assert_eq!(buffer.word_size(), OPTIONS.word_size);
514
        assert_eq!(buffer.page_size(), OPTIONS.page_size);
515
        assert_eq!(buffer.num_pages(), NUM_PAGES);
516
        assert_eq!(buffer.max_word_writes(), OPTIONS.max_word_writes);
517
        assert_eq!(buffer.max_page_erases(), OPTIONS.max_page_erases);
518
    }
519

520
    #[test]
521
    fn read_write_ok() {
522
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
523
        let index = StorageIndex { page: 0, byte: 0 };
524
        let next_index = StorageIndex { page: 0, byte: 4 };
525
        assert_eq!(buffer.read_slice(index, 4).unwrap(), BLANK_WORD);
526
        buffer.write_slice(index, FIRST_WORD).unwrap();
527
        assert_eq!(buffer.read_slice(index, 4).unwrap(), FIRST_WORD);
528
        assert_eq!(buffer.read_slice(next_index, 4).unwrap(), BLANK_WORD);
529
    }
530

531
    #[test]
532
    fn erase_ok() {
533
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
534
        let index = StorageIndex { page: 0, byte: 0 };
535
        let other_index = StorageIndex { page: 1, byte: 0 };
536
        buffer.write_slice(index, FIRST_WORD).unwrap();
537
        buffer.write_slice(other_index, FIRST_WORD).unwrap();
538
        assert_eq!(buffer.read_slice(index, 4).unwrap(), FIRST_WORD);
539
        assert_eq!(buffer.read_slice(other_index, 4).unwrap(), FIRST_WORD);
540
        buffer.erase_page(0).unwrap();
541
        assert_eq!(buffer.read_slice(index, 4).unwrap(), BLANK_WORD);
542
        assert_eq!(buffer.read_slice(other_index, 4).unwrap(), FIRST_WORD);
543
    }
544

545
    #[test]
546
    fn invalid_range() {
547
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
548
        let index = StorageIndex { page: 0, byte: 12 };
549
        let half_index = StorageIndex { page: 0, byte: 14 };
550
        let over_index = StorageIndex { page: 0, byte: 16 };
551
        let bad_page = StorageIndex { page: 2, byte: 0 };
552

553
        // Reading a word in the storage is ok.
554
        assert!(buffer.read_slice(index, 4).is_ok());
555
        // Reading a half-word in the storage is ok.
556
        assert!(buffer.read_slice(half_index, 2).is_ok());
557
        // Reading even a single byte outside a page is not ok.
558
        assert!(buffer.read_slice(over_index, 1).is_err());
559
        // But reading an empty slice just after a page is ok.
560
        assert!(buffer.read_slice(over_index, 0).is_ok());
561
        // Reading even an empty slice outside the storage is not ok.
562
        assert!(buffer.read_slice(bad_page, 0).is_err());
563

564
        // Writing a word in the storage is ok.
565
        assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
566
        // Writing an unaligned word is not ok.
567
        assert!(buffer.write_slice(half_index, FIRST_WORD).is_err());
568
        // Writing a word outside a page is not ok.
569
        assert!(buffer.write_slice(over_index, FIRST_WORD).is_err());
570
        // But writing an empty slice just after a page is ok.
571
        assert!(buffer.write_slice(over_index, &[]).is_ok());
572
        // Writing even an empty slice outside the storage is not ok.
573
        assert!(buffer.write_slice(bad_page, &[]).is_err());
574

575
        // Only pages in the storage can be erased.
576
        assert!(buffer.erase_page(0).is_ok());
577
        assert!(buffer.erase_page(2).is_err());
578
    }
579

580
    #[test]
581
    fn write_twice_ok() {
582
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
583
        let index = StorageIndex { page: 0, byte: 4 };
584
        assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
585
        assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
586
    }
587

588
    #[test]
589
    fn write_twice_and_once_ok() {
590
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
591
        let index = StorageIndex { page: 0, byte: 0 };
592
        let next_index = StorageIndex { page: 0, byte: 4 };
593
        assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
594
        assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
595
        assert!(buffer.write_slice(next_index, THIRD_WORD).is_ok());
596
    }
597

598
    #[test]
599
    #[should_panic]
600
    fn write_three_times_panics() {
601
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
602
        let index = StorageIndex { page: 0, byte: 4 };
603
        assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
604
        assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
605
        let _ = buffer.write_slice(index, THIRD_WORD);
606
    }
607

608
    #[test]
609
    fn write_twice_then_once_ok() {
610
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
611
        let index = StorageIndex { page: 0, byte: 0 };
612
        assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
613
        assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
614
        assert!(buffer.erase_page(0).is_ok());
615
        assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
616
    }
617

618
    #[test]
619
    fn erase_three_times_ok() {
620
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
621
        assert!(buffer.erase_page(0).is_ok());
622
        assert!(buffer.erase_page(0).is_ok());
623
        assert!(buffer.erase_page(0).is_ok());
624
    }
625

626
    #[test]
627
    fn erase_three_times_and_once_ok() {
628
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
629
        assert!(buffer.erase_page(0).is_ok());
630
        assert!(buffer.erase_page(0).is_ok());
631
        assert!(buffer.erase_page(0).is_ok());
632
        assert!(buffer.erase_page(1).is_ok());
633
    }
634

635
    #[test]
636
    #[should_panic]
637
    fn erase_four_times_panics() {
638
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
639
        assert!(buffer.erase_page(0).is_ok());
640
        assert!(buffer.erase_page(0).is_ok());
641
        assert!(buffer.erase_page(0).is_ok());
642
        let _ = buffer.erase_page(0).is_ok();
643
    }
644

645
    #[test]
646
    #[should_panic]
647
    fn switch_zero_to_one_panics() {
648
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
649
        let index = StorageIndex { page: 0, byte: 0 };
650
        assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
651
        let _ = buffer.write_slice(index, FIRST_WORD);
652
    }
653

654
    #[test]
655
    fn interrupt_delay_ok() {
656
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
657

658
        // Interrupt the second operation.
659
        buffer.arm_interruption(1);
660

661
        // The first operation should not fail.
662
        buffer
663
            .write_slice(StorageIndex { page: 0, byte: 0 }, &[0x5c; 8])
664
            .unwrap();
665
        // The delay should be decremented.
666
        assert_eq!(buffer.disarm_interruption(), 0);
667
        // The storage should have been modified.
668
        assert_eq!(&buffer.storage[..8], &[0x5c; 8]);
669
        assert!(buffer.storage[8..].iter().all(|&x| x == 0xff));
670
    }
671

672
    #[test]
673
    fn interrupt_save_ok() {
674
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
675

676
        // Interrupt the second operation.
677
        buffer.arm_interruption(1);
678

679
        // The second operation should fail.
680
        buffer
681
            .write_slice(StorageIndex { page: 0, byte: 0 }, &[0x5c; 8])
682
            .unwrap();
683
        assert!(buffer
684
            .write_slice(StorageIndex { page: 0, byte: 8 }, &[0x93; 8])
685
            .is_err());
686
        // The operation should represent the change.
687
        buffer.corrupt_operation(Box::new(|_, value| assert_eq!(value, &[0x93; 8])));
688
        // The storage should not have been modified.
689
        assert_eq!(&buffer.storage[..8], &[0x5c; 8]);
690
        assert!(buffer.storage[8..].iter().all(|&x| x == 0xff));
691
    }
692
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc