Coverage Report

Created: 2026-11-12 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/wasefire-store-0.2.4/src/buffer.rs
Line
Count
Source
1
// Copyright 2019 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 alloc::borrow::{Borrow, Cow};
21
use alloc::boxed::Box;
22
use alloc::vec;
23
24
use crate::{Storage, StorageError, StorageIndex, StorageResult};
25
26
/// Simulates a flash storage using a buffer in memory.
27
///
28
/// This buffer storage can be used in place of an actual flash storage. It is particularly useful
29
/// for tests and fuzzing, for which it has dedicated functionalities.
30
///
31
/// This storage tracks how many times words are written between page erase cycles, how many times
32
/// pages are erased, and whether an operation flips bits in the wrong direction. Operations panic
33
/// if those conditions are broken (optional). This storage also permits to interrupt operations for
34
/// inspection or to corrupt the operation.
35
#[derive(Clone)]
36
pub struct BufferStorage {
37
    /// Content of the storage.
38
    storage: Box<[u8]>,
39
40
    /// Options of the storage.
41
    options: BufferOptions,
42
43
    /// Number of times a word was written since the last time its page was erased.
44
    word_writes: Box<[usize]>,
45
46
    /// Number of times a page was erased.
47
    page_erases: Box<[usize]>,
48
49
    /// Interruption state.
50
    interruption: Interruption,
51
}
52
53
/// Options of a buffer storage.
54
#[derive(Clone, Debug)]
55
pub struct BufferOptions {
56
    /// Size of a word in bytes.
57
    pub word_size: usize,
58
59
    /// Size of a page in bytes.
60
    pub page_size: usize,
61
62
    /// How many times a word can be written between page erase cycles.
63
    pub max_word_writes: usize,
64
65
    /// How many times a page can be erased.
66
    pub max_page_erases: usize,
67
68
    /// Whether the storage should check the flash invariant.
69
    ///
70
    /// When set, the following conditions would panic:
71
    /// - A bit is written from 0 to 1.
72
    /// - A word is written more than [`Self::max_word_writes`].
73
    /// - A page is erased more than [`Self::max_page_erases`].
74
    pub strict_mode: bool,
75
}
76
77
/// Corrupts a slice given actual and expected value.
78
///
79
/// A corruption function is called exactly once and takes 2 arguments:
80
/// - A mutable slice representing the storage before the interrupted operation.
81
/// - A shared slice representing what the storage would have been if the operation was not
82
///   interrupted.
83
///
84
/// The corruption function may flip an arbitrary number of bits in the mutable slice, but may only
85
/// flip bits that differ between both slices.
86
pub type BufferCorruptFunction<'a> = Box<dyn FnOnce(&mut [u8], &[u8]) + 'a>;
87
88
impl BufferStorage {
89
    /// Creates a buffer storage.
90
    ///
91
    /// # Panics
92
    ///
93
    /// The following preconditions must hold:
94
    /// - `options.word_size` must be a power of two.
95
    /// - `options.page_size` must be a power of two.
96
    /// - `options.page_size` must be word-aligned.
97
    /// - `storage.len()` must be page-aligned.
98
21.9k
    pub fn new(storage: Box<[u8]>, options: BufferOptions) -> BufferStorage {
99
21.9k
        assert!(options.word_size.is_power_of_two());
100
21.9k
        assert!(options.page_size.is_power_of_two());
101
21.9k
        let num_words = storage.len() / options.word_size;
102
21.9k
        let num_pages = storage.len() / options.page_size;
103
21.9k
        let buffer = BufferStorage {
104
21.9k
            storage,
105
21.9k
            options,
106
21.9k
            word_writes: vec![0; num_words].into_boxed_slice(),
107
21.9k
            page_erases: vec![0; num_pages].into_boxed_slice(),
108
21.9k
            interruption: Interruption::Ready,
109
21.9k
        };
110
21.9k
        assert!(buffer.is_word_aligned(buffer.options.page_size));
111
21.9k
        assert!(buffer.is_page_aligned(buffer.storage.len()));
112
21.9k
        buffer
113
21.9k
    }
114
115
    /// Arms an interruption after a given delay.
116
    ///
117
    /// Before each subsequent mutable operation (write or erase), the delay is decremented if
118
    /// positive. If the delay is elapsed, the operation is saved and an error is returned.
119
    /// Subsequent operations will panic until either of:
120
    /// - The interrupted operation is [corrupted](BufferStorage::corrupt_operation).
121
    /// - The interruption is [reset](BufferStorage::reset_interruption).
122
    ///
123
    /// # Panics
124
    ///
125
    /// Panics if an interruption is already armed.
126
0
    pub fn arm_interruption(&mut self, delay: usize) {
127
0
        self.interruption.arm(delay);
128
0
    }
129
130
    /// Disarms an interruption that did not trigger.
131
    ///
132
    /// Returns the remaining delay.
133
    ///
134
    /// # Panics
135
    ///
136
    /// Panics if any of the following conditions hold:
137
    /// - An interruption was not [armed](BufferStorage::arm_interruption).
138
    /// - An interruption was armed and it has triggered.
139
0
    pub fn disarm_interruption(&mut self) -> usize {
140
0
        self.interruption.get().err().unwrap()
141
0
    }
142
143
    /// Resets an interruption regardless of triggering.
144
    ///
145
    /// # Panics
146
    ///
147
    /// Panics if an interruption was not [armed](BufferStorage::arm_interruption).
148
0
    pub fn reset_interruption(&mut self) {
149
0
        let _ = self.interruption.get();
150
0
    }
151
152
    /// Corrupts an interrupted operation.
153
    ///
154
    /// Applies the corruption function to the storage. Counters are updated accordingly:
155
    /// - If a word is fully written, its counter is incremented regardless of whether other words
156
    ///   of the same operation have been fully written.
157
    /// - If a page is fully erased, its counter is incremented (and its word counters are reset).
158
    ///
159
    /// # Panics
160
    ///
161
    /// Panics if any of the following conditions hold:
162
    /// - An interruption was not [armed](BufferStorage::arm_interruption).
163
    /// - An interruption was armed but did not trigger.
164
    /// - The corruption function corrupts more bits than allowed.
165
    /// - The interrupted operation itself would have panicked.
166
0
    pub fn corrupt_operation(&mut self, corrupt: BufferCorruptFunction) {
167
0
        let operation = self.interruption.get().unwrap();
168
0
        let range = self.operation_range(&operation).unwrap();
169
0
        let mut before = self.storage[range.clone()].to_vec().into_boxed_slice();
170
0
        match operation {
171
0
            BufferOperation::Write { value: after, .. } => {
172
0
                corrupt(&mut before, &after);
173
0
                self.incr_word_writes(range.start, &before, &after);
174
0
            }
175
0
            BufferOperation::Erase { page } => {
176
0
                let after = vec![0xff; self.page_size()].into_boxed_slice();
177
0
                corrupt(&mut before, &after);
178
0
                if before == after {
179
0
                    self.incr_page_erases(page);
180
0
                }
181
            }
182
        };
183
0
        self.storage[range].copy_from_slice(&before);
184
0
    }
185
186
    /// Returns the number of times a word was written.
187
0
    pub fn get_word_writes(&self, word: usize) -> usize {
188
0
        self.word_writes[word]
189
0
    }
190
191
    /// Returns the number of times a page was erased.
192
0
    pub fn get_page_erases(&self, page: usize) -> usize {
193
0
        self.page_erases[page]
194
0
    }
195
196
    /// Sets the number of times a page was erased.
197
0
    pub fn set_page_erases(&mut self, page: usize, cycle: usize) {
198
0
        self.page_erases[page] = cycle;
199
0
    }
200
201
    /// Returns whether a number is word-aligned.
202
112k
    fn is_word_aligned(&self, x: usize) -> bool {
203
112k
        x & (self.options.word_size - 1) == 0
204
112k
    }
205
206
    /// Returns whether a number is page-aligned.
207
21.9k
    fn is_page_aligned(&self, x: usize) -> bool {
208
21.9k
        x & (self.options.page_size - 1) == 0
209
21.9k
    }
210
211
    /// Updates the counters as if a page was erased.
212
    ///
213
    /// The page counter of that page is incremented and the word counters of that page are reset.
214
    ///
215
    /// # Panics
216
    ///
217
    /// Panics if the [maximum number of erase cycles per page](BufferOptions::max_page_erases) is
218
    /// reached.
219
0
    fn incr_page_erases(&mut self, page: usize) {
220
        // Check that pages are not erased too many times.
221
0
        if self.options.strict_mode {
222
0
            assert!(self.page_erases[page] < self.max_page_erases());
223
0
        }
224
0
        self.page_erases[page] += 1;
225
0
        let num_words = self.page_size() / self.word_size();
226
0
        for word in 0 .. num_words {
227
0
            self.word_writes[page * num_words + word] = 0;
228
0
        }
229
0
    }
230
231
    /// Updates the word counters as if a partial write occurred.
232
    ///
233
    /// The partial write is described as if `complete` was supposed to be written to the storage
234
    /// starting at byte `index`, but actually only `value` was written. Word counters are
235
    /// incremented only if their value would change and they would be completely written.
236
    ///
237
    /// # Preconditions
238
    ///
239
    /// - `index` must be word-aligned.
240
    /// - `value` and `complete` must have the same word-aligned length.
241
    ///
242
    /// # Panics
243
    ///
244
    /// Panics if the [maximum number of writes per word](BufferOptions::max_word_writes) is
245
    /// reached.
246
45.1k
    fn incr_word_writes(&mut self, index: usize, value: &[u8], complete: &[u8]) {
247
45.1k
        let word_size = self.word_size();
248
176k
        for i in 0 .. value.len() / word_size {
249
176k
            let range = core::ops::Range { start: i * word_size, end: (i + 1) * word_size };
250
            // Partial word writes do not count.
251
176k
            if value[range.clone()] != complete[range.clone()] {
252
0
                continue;
253
176k
            }
254
            // Words are written only if necessary.
255
176k
            if value[range.clone()] == self.storage[index ..][range] {
256
1.23k
                continue;
257
175k
            }
258
175k
            let word = index / word_size + i;
259
            // Check that words are not written too many times.
260
175k
            if self.options.strict_mode {
261
175k
                assert!(self.word_writes[word] < self.max_word_writes());
262
0
            }
263
175k
            self.word_writes[word] += 1;
264
        }
265
45.1k
    }
266
267
    /// Returns the storage range of an operation.
268
45.1k
    fn operation_range(
269
45.1k
        &self, operation: &BufferOperation<impl Borrow<[u8]>>,
270
45.1k
    ) -> StorageResult<core::ops::Range<usize>> {
271
45.1k
        match *operation {
272
45.1k
            BufferOperation::Write { index, ref value } => index.range(value.borrow().len(), self),
273
0
            BufferOperation::Erase { page } => {
274
0
                StorageIndex { page, byte: 0 }.range(self.page_size(), self)
275
            }
276
        }
277
45.1k
    }
Unexecuted instantiation: <wasefire_store::buffer::BufferStorage>::operation_range::<alloc::boxed::Box<[u8]>>
<wasefire_store::buffer::BufferStorage>::operation_range::<&[u8]>
Line
Count
Source
268
45.1k
    fn operation_range(
269
45.1k
        &self, operation: &BufferOperation<impl Borrow<[u8]>>,
270
45.1k
    ) -> StorageResult<core::ops::Range<usize>> {
271
45.1k
        match *operation {
272
45.1k
            BufferOperation::Write { index, ref value } => index.range(value.borrow().len(), self),
273
0
            BufferOperation::Erase { page } => {
274
0
                StorageIndex { page, byte: 0 }.range(self.page_size(), self)
275
            }
276
        }
277
45.1k
    }
278
}
279
280
impl Storage for BufferStorage {
281
67.1k
    fn word_size(&self) -> usize {
282
67.1k
        self.options.word_size
283
67.1k
    }
284
285
52.5M
    fn page_size(&self) -> usize {
286
52.5M
        self.options.page_size
287
52.5M
    }
288
289
26.3M
    fn num_pages(&self) -> usize {
290
26.3M
        self.storage.len() / self.options.page_size
291
26.3M
    }
292
293
197k
    fn max_word_writes(&self) -> usize {
294
197k
        self.options.max_word_writes
295
197k
    }
296
297
43.9k
    fn max_page_erases(&self) -> usize {
298
43.9k
        self.options.max_page_erases
299
43.9k
    }
300
301
26.2M
    fn read_slice(&self, index: StorageIndex, length: usize) -> StorageResult<Cow<[u8]>> {
302
26.2M
        Ok(Cow::Borrowed(&self.storage[index.range(length, self)?]))
303
26.2M
    }
304
305
45.1k
    fn write_slice(&mut self, index: StorageIndex, value: &[u8]) -> StorageResult<()> {
306
45.1k
        if !self.is_word_aligned(index.byte) || !self.is_word_aligned(value.len()) {
307
0
            return Err(StorageError::NotAligned);
308
45.1k
        }
309
45.1k
        let operation = BufferOperation::Write { index, value };
310
45.1k
        let range = self.operation_range(&operation)?;
311
        // Interrupt operation if armed and delay expired.
312
45.1k
        self.interruption.tick(&operation)?;
313
        // Check and update counters.
314
45.1k
        self.incr_word_writes(range.start, value, value);
315
        // Check that bits are correctly flipped.
316
45.1k
        if self.options.strict_mode {
317
707k
            for (byte, &val) in range.clone().zip(value.iter()) {
318
707k
                assert_eq!(self.storage[byte] & val, val);
319
            }
320
0
        }
321
        // Write to the storage.
322
45.1k
        self.storage[range].copy_from_slice(value);
323
45.1k
        Ok(())
324
45.1k
    }
325
326
0
    fn erase_page(&mut self, page: usize) -> StorageResult<()> {
327
0
        let operation = BufferOperation::Erase { page };
328
0
        let range = self.operation_range(&operation)?;
329
        // Interrupt operation if armed and delay expired.
330
0
        self.interruption.tick(&operation)?;
331
        // Check and update counters.
332
0
        self.incr_page_erases(page);
333
        // Write to the storage.
334
0
        for byte in &mut self.storage[range] {
335
0
            *byte = 0xff;
336
0
        }
337
0
        Ok(())
338
0
    }
339
}
340
341
impl core::fmt::Display for BufferStorage {
342
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
343
0
        let num_pages = self.num_pages();
344
0
        let num_words = self.page_size() / self.word_size();
345
0
        let num_bytes = self.word_size();
346
0
        for page in 0 .. num_pages {
347
0
            write!(f, "[{}]", self.page_erases[page])?;
348
0
            for word in 0 .. num_words {
349
0
                write!(f, " [{}]", self.word_writes[page * num_words + word])?;
350
0
                for byte in 0 .. num_bytes {
351
0
                    let index = (page * num_words + word) * num_bytes + byte;
352
0
                    write!(f, "{:02x}", self.storage[index])?;
353
                }
354
            }
355
0
            writeln!(f)?;
356
        }
357
0
        Ok(())
358
0
    }
359
}
360
361
/// Represents a storage operation.
362
///
363
/// It is polymorphic over the ownership of the byte slice to avoid unnecessary copies.
364
#[derive(Clone, Debug, PartialEq, Eq)]
365
enum BufferOperation<ByteSlice: Borrow<[u8]>> {
366
    /// Represents a write operation.
367
    Write {
368
        /// The storage index at which the write should occur.
369
        index: StorageIndex,
370
371
        /// The slice that should be written.
372
        value: ByteSlice,
373
    },
374
375
    /// Represents an erase operation.
376
    Erase {
377
        /// The page that should be erased.
378
        page: usize,
379
    },
380
}
381
382
/// Represents a storage operation owning its byte slices.
383
type OwnedBufferOperation = BufferOperation<Box<[u8]>>;
384
385
/// Represents a storage operation sharing its byte slices.
386
type SharedBufferOperation<'a> = BufferOperation<&'a [u8]>;
387
388
impl<'a> SharedBufferOperation<'a> {
389
0
    fn to_owned(&self) -> OwnedBufferOperation {
390
0
        match *self {
391
0
            BufferOperation::Write { index, value } => {
392
0
                BufferOperation::Write { index, value: value.to_vec().into_boxed_slice() }
393
            }
394
0
            BufferOperation::Erase { page } => BufferOperation::Erase { page },
395
        }
396
0
    }
397
}
398
399
/// Controls when an operation is interrupted.
400
///
401
/// This can be used to simulate power-offs while the device is writing to the storage or erasing a
402
/// page in the storage.
403
#[derive(Clone)]
404
enum Interruption {
405
    /// Mutable operations have normal behavior.
406
    Ready,
407
408
    /// If the delay is positive, mutable operations decrement it. If the count is zero, mutable
409
    /// operations fail and are saved.
410
    Armed { delay: usize },
411
412
    /// Mutable operations panic.
413
    Saved { operation: OwnedBufferOperation },
414
}
415
416
impl Interruption {
417
    /// Arms an interruption for a given delay.
418
    ///
419
    /// # Panics
420
    ///
421
    /// Panics if an interruption is already armed.
422
0
    fn arm(&mut self, delay: usize) {
423
0
        match self {
424
0
            Interruption::Ready => *self = Interruption::Armed { delay },
425
0
            _ => panic!(),
426
        }
427
0
    }
428
429
    /// Disarms an interruption.
430
    ///
431
    /// Returns the interrupted operation if any, otherwise the remaining delay.
432
    ///
433
    /// # Panics
434
    ///
435
    /// Panics if an interruption was not armed.
436
0
    fn get(&mut self) -> Result<OwnedBufferOperation, usize> {
437
0
        let mut interruption = Interruption::Ready;
438
0
        core::mem::swap(self, &mut interruption);
439
0
        match interruption {
440
0
            Interruption::Armed { delay } => Err(delay),
441
0
            Interruption::Saved { operation } => Ok(operation),
442
0
            _ => panic!(),
443
        }
444
0
    }
445
446
    /// Interrupts an operation if the delay is over.
447
    ///
448
    /// Decrements the delay if positive. Otherwise, the operation is stored and an error is
449
    /// returned to interrupt the operation.
450
    ///
451
    /// # Panics
452
    ///
453
    /// Panics if an operation has already been interrupted and the interruption has not been
454
    /// disarmed.
455
45.1k
    fn tick(&mut self, operation: &SharedBufferOperation) -> StorageResult<()> {
456
0
        match self {
457
45.1k
            Interruption::Ready => (),
458
0
            Interruption::Armed { delay } if *delay == 0 => {
459
0
                let operation = operation.to_owned();
460
0
                *self = Interruption::Saved { operation };
461
0
                return Err(StorageError::CustomError);
462
            }
463
0
            Interruption::Armed { delay } => *delay -= 1,
464
0
            Interruption::Saved { .. } => panic!(),
465
        }
466
45.1k
        Ok(())
467
45.1k
    }
468
}
469
470
#[cfg(test)]
471
mod tests {
472
    use super::*;
473
474
    const NUM_PAGES: usize = 2;
475
    const OPTIONS: BufferOptions = BufferOptions {
476
        word_size: 4,
477
        page_size: 16,
478
        max_word_writes: 2,
479
        max_page_erases: 3,
480
        strict_mode: true,
481
    };
482
    // Those words are decreasing bit patterns. Bits are only changed from 1 to 0 and at least one
483
    // bit is changed.
484
    const BLANK_WORD: &[u8] = &[0xff, 0xff, 0xff, 0xff];
485
    const FIRST_WORD: &[u8] = &[0xee, 0xdd, 0xbb, 0x77];
486
    const SECOND_WORD: &[u8] = &[0xca, 0xc9, 0xa9, 0x65];
487
    const THIRD_WORD: &[u8] = &[0x88, 0x88, 0x88, 0x44];
488
489
    fn new_storage() -> Box<[u8]> {
490
        vec![0xff; NUM_PAGES * OPTIONS.page_size].into_boxed_slice()
491
    }
492
493
    #[test]
494
    fn words_are_decreasing() {
495
        fn assert_is_decreasing(prev: &[u8], next: &[u8]) {
496
            for (&prev, &next) in prev.iter().zip(next.iter()) {
497
                assert_eq!(prev & next, next);
498
                assert!(prev != next);
499
            }
500
        }
501
        assert_is_decreasing(BLANK_WORD, FIRST_WORD);
502
        assert_is_decreasing(FIRST_WORD, SECOND_WORD);
503
        assert_is_decreasing(SECOND_WORD, THIRD_WORD);
504
    }
505
506
    #[test]
507
    fn options_ok() {
508
        let buffer = BufferStorage::new(new_storage(), OPTIONS);
509
        assert_eq!(buffer.word_size(), OPTIONS.word_size);
510
        assert_eq!(buffer.page_size(), OPTIONS.page_size);
511
        assert_eq!(buffer.num_pages(), NUM_PAGES);
512
        assert_eq!(buffer.max_word_writes(), OPTIONS.max_word_writes);
513
        assert_eq!(buffer.max_page_erases(), OPTIONS.max_page_erases);
514
    }
515
516
    #[test]
517
    fn read_write_ok() {
518
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
519
        let index = StorageIndex { page: 0, byte: 0 };
520
        let next_index = StorageIndex { page: 0, byte: 4 };
521
        assert_eq!(buffer.read_slice(index, 4).unwrap(), BLANK_WORD);
522
        buffer.write_slice(index, FIRST_WORD).unwrap();
523
        assert_eq!(buffer.read_slice(index, 4).unwrap(), FIRST_WORD);
524
        assert_eq!(buffer.read_slice(next_index, 4).unwrap(), BLANK_WORD);
525
    }
526
527
    #[test]
528
    fn erase_ok() {
529
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
530
        let index = StorageIndex { page: 0, byte: 0 };
531
        let other_index = StorageIndex { page: 1, byte: 0 };
532
        buffer.write_slice(index, FIRST_WORD).unwrap();
533
        buffer.write_slice(other_index, FIRST_WORD).unwrap();
534
        assert_eq!(buffer.read_slice(index, 4).unwrap(), FIRST_WORD);
535
        assert_eq!(buffer.read_slice(other_index, 4).unwrap(), FIRST_WORD);
536
        buffer.erase_page(0).unwrap();
537
        assert_eq!(buffer.read_slice(index, 4).unwrap(), BLANK_WORD);
538
        assert_eq!(buffer.read_slice(other_index, 4).unwrap(), FIRST_WORD);
539
    }
540
541
    #[test]
542
    fn invalid_range() {
543
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
544
        let index = StorageIndex { page: 0, byte: 12 };
545
        let half_index = StorageIndex { page: 0, byte: 14 };
546
        let over_index = StorageIndex { page: 0, byte: 16 };
547
        let bad_page = StorageIndex { page: 2, byte: 0 };
548
549
        // Reading a word in the storage is ok.
550
        assert!(buffer.read_slice(index, 4).is_ok());
551
        // Reading a half-word in the storage is ok.
552
        assert!(buffer.read_slice(half_index, 2).is_ok());
553
        // Reading even a single byte outside a page is not ok.
554
        assert!(buffer.read_slice(over_index, 1).is_err());
555
        // But reading an empty slice just after a page is ok.
556
        assert!(buffer.read_slice(over_index, 0).is_ok());
557
        // Reading even an empty slice outside the storage is not ok.
558
        assert!(buffer.read_slice(bad_page, 0).is_err());
559
560
        // Writing a word in the storage is ok.
561
        assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
562
        // Writing an unaligned word is not ok.
563
        assert!(buffer.write_slice(half_index, FIRST_WORD).is_err());
564
        // Writing a word outside a page is not ok.
565
        assert!(buffer.write_slice(over_index, FIRST_WORD).is_err());
566
        // But writing an empty slice just after a page is ok.
567
        assert!(buffer.write_slice(over_index, &[]).is_ok());
568
        // Writing even an empty slice outside the storage is not ok.
569
        assert!(buffer.write_slice(bad_page, &[]).is_err());
570
571
        // Only pages in the storage can be erased.
572
        assert!(buffer.erase_page(0).is_ok());
573
        assert!(buffer.erase_page(2).is_err());
574
    }
575
576
    #[test]
577
    fn write_twice_ok() {
578
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
579
        let index = StorageIndex { page: 0, byte: 4 };
580
        assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
581
        assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
582
    }
583
584
    #[test]
585
    fn write_twice_and_once_ok() {
586
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
587
        let index = StorageIndex { page: 0, byte: 0 };
588
        let next_index = StorageIndex { page: 0, byte: 4 };
589
        assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
590
        assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
591
        assert!(buffer.write_slice(next_index, THIRD_WORD).is_ok());
592
    }
593
594
    #[test]
595
    #[should_panic]
596
    fn write_three_times_panics() {
597
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
598
        let index = StorageIndex { page: 0, byte: 4 };
599
        assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
600
        assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
601
        let _ = buffer.write_slice(index, THIRD_WORD);
602
    }
603
604
    #[test]
605
    fn write_twice_then_once_ok() {
606
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
607
        let index = StorageIndex { page: 0, byte: 0 };
608
        assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
609
        assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
610
        assert!(buffer.erase_page(0).is_ok());
611
        assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
612
    }
613
614
    #[test]
615
    fn erase_three_times_ok() {
616
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
617
        assert!(buffer.erase_page(0).is_ok());
618
        assert!(buffer.erase_page(0).is_ok());
619
        assert!(buffer.erase_page(0).is_ok());
620
    }
621
622
    #[test]
623
    fn erase_three_times_and_once_ok() {
624
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
625
        assert!(buffer.erase_page(0).is_ok());
626
        assert!(buffer.erase_page(0).is_ok());
627
        assert!(buffer.erase_page(0).is_ok());
628
        assert!(buffer.erase_page(1).is_ok());
629
    }
630
631
    #[test]
632
    #[should_panic]
633
    fn erase_four_times_panics() {
634
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
635
        assert!(buffer.erase_page(0).is_ok());
636
        assert!(buffer.erase_page(0).is_ok());
637
        assert!(buffer.erase_page(0).is_ok());
638
        let _ = buffer.erase_page(0).is_ok();
639
    }
640
641
    #[test]
642
    #[should_panic]
643
    fn switch_zero_to_one_panics() {
644
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
645
        let index = StorageIndex { page: 0, byte: 0 };
646
        assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
647
        let _ = buffer.write_slice(index, FIRST_WORD);
648
    }
649
650
    #[test]
651
    fn interrupt_delay_ok() {
652
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
653
654
        // Interrupt the second operation.
655
        buffer.arm_interruption(1);
656
657
        // The first operation should not fail.
658
        buffer.write_slice(StorageIndex { page: 0, byte: 0 }, &[0x5c; 8]).unwrap();
659
        // The delay should be decremented.
660
        assert_eq!(buffer.disarm_interruption(), 0);
661
        // The storage should have been modified.
662
        assert_eq!(&buffer.storage[.. 8], &[0x5c; 8]);
663
        assert!(buffer.storage[8 ..].iter().all(|&x| x == 0xff));
664
    }
665
666
    #[test]
667
    fn interrupt_save_ok() {
668
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
669
670
        // Interrupt the second operation.
671
        buffer.arm_interruption(1);
672
673
        // The second operation should fail.
674
        buffer.write_slice(StorageIndex { page: 0, byte: 0 }, &[0x5c; 8]).unwrap();
675
        assert!(buffer.write_slice(StorageIndex { page: 0, byte: 8 }, &[0x93; 8]).is_err());
676
        // The operation should represent the change.
677
        buffer.corrupt_operation(Box::new(|_, value| assert_eq!(value, &[0x93; 8])));
678
        // The storage should not have been modified.
679
        assert_eq!(&buffer.storage[.. 8], &[0x5c; 8]);
680
        assert!(buffer.storage[8 ..].iter().all(|&x| x == 0xff));
681
    }
682
}