common: Implement Logger::remove_recording().
authorTilman Sauerbeck <tilman@code-monkey.de>
Tue, 11 Feb 2020 18:56:37 +0000 (19:56 +0100)
committerTilman Sauerbeck <tilman@code-monkey.de>
Tue, 18 Feb 2020 20:12:14 +0000 (21:12 +0100)
src/common/logger.rs
test/logger_test.rs

index 8a1c05cd75f89b676635b7a37c991cde1c84688b..9b498ce64a5cec5e7f76be504fab5395a80ac41c 100644 (file)
@@ -38,6 +38,7 @@ const NUM_SECTORS: usize = MEMORY_SIZE / SECTOR_SIZE;
 #[derive(Clone, Copy, PartialEq, Debug)]
 pub enum Error {
     NoSuchRecording,
+    StorageError,
 }
 
 enum SectorFlag {
@@ -666,4 +667,40 @@ impl<'a> Logger<'a> {
             Err(Error::NoSuchRecording)
         }
     }
+
+    ///
+    /// Remove recording @p recording_id.
+    pub fn remove_recording(&mut self, recording_id: u16) -> Result<(), Error> {
+        if let Some(found_index) = (0..NUM_SECTORS).find(|&index| {
+            let sector_header = &self.sector_header[index as usize];
+
+            sector_header.recording_id == recording_id &&
+            sector_header.starts_recording()
+        }) {
+            let mut next_sector = found_index as usize;
+
+            for _ in 0..NUM_SECTORS {
+                let address = next_sector * SECTOR_SIZE;
+
+                if let Err(_) = self.storage.erase(address) {
+                    return Err(Error::StorageError);
+                }
+
+                // Mark this sector as eligible for the next recording
+                // and ensure it won't be picked up by list_recordings().
+                self.read_sector_header(next_sector);
+
+                next_sector += 1;
+                next_sector &= NUM_SECTORS - 1;
+
+                if !self.sector_header[next_sector].belongs_to(recording_id) {
+                    break;
+                }
+            }
+
+            Ok(())
+        } else {
+            Err(Error::NoSuchRecording)
+        }
+    }
 }
index a254f505db960e7c35959de6a215185503d69aef..784c04955cc567938e852abfbd7d3b8e8b0bb43c 100644 (file)
@@ -514,3 +514,61 @@ fn list_recording1_multi_sector() {
     assert_eq!(String::from_utf8(expected_bytes.to_vec()).unwrap(),
                String::from_utf8(listing_raw[0..num_bytes_read].to_vec()).unwrap());
 }
+
+// Verifies that Logger::remove_recording() reports an error if there is no
+// recording with the given ID.
+#[test]
+fn remove_recording0() {
+    let mut fake_storage = FakeStorage::new();
+
+    let mut logger = Logger::new(&mut fake_storage);
+    logger.init();
+
+    let result = logger.remove_recording(42);
+    assert_eq!(LoggerError::NoSuchRecording, result.unwrap_err());
+}
+
+// Verifies that Logger::remove_recording() clears consecutive sectors
+// belonging to the recording identified by the given ID.
+#[test]
+fn remove_recording1() {
+    let mut fake_storage = FakeStorage::new();
+
+    // Mark first sector as in use.
+    let header0 = [
+        0x01, 0x00, 0x01, 0x00,
+        0xa2, 0xdb, 0x14, 0x5e,
+    ];
+
+    // Mark second sector as in use by the same recording.
+    let header1 = [
+        0x03, 0x00, 0x01, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+    ];
+
+    // Mark third sector as in use by another recording.
+    let header2 = [
+        0x01, 0x00, 0x02, 0x00,
+        0xa2, 0xdb, 0x14, 0x5f,
+    ];
+
+    fake_storage.actual[0..header0.len()].copy_from_slice(&header0);
+    fake_storage.actual[4096..4096 + header1.len()].copy_from_slice(&header1);
+    fake_storage.actual[8192..8192 + header2.len()].copy_from_slice(&header2);
+
+    let mut logger = Logger::new(&mut fake_storage);
+    logger.init();
+
+    assert!(logger.remove_recording(1).is_ok());
+
+    let cleared = [
+        0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff,
+    ];
+
+    fake_storage.expected[0..cleared.len()].copy_from_slice(&cleared);
+    fake_storage.expected[4096..4096 + header1.len()].copy_from_slice(&cleared);
+    fake_storage.expected[8192..8192 + header2.len()].copy_from_slice(&header2);
+
+    assert_eq!(fake_storage.expected, fake_storage.actual);
+}