extern crate std;
use super::*;
use std::{vec, vec::Vec};
const OTADATA: u32 = 0xD000;
const OTA_0: u32 = 0x10000;
const OTA_1: u32 = 0x20000;
const OTA_SIZE: u32 = 0x10000;
struct MemFlash(Vec<u8>);
impl ReadStorage for MemFlash {
type Error = ();
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> core::result::Result<(), ()> {
let src = self
.0
.get(offset as usize..offset as usize + bytes.len())
.ok_or(())?;
bytes.copy_from_slice(src);
Ok(())
}
fn capacity(&self) -> usize {
self.0.len()
}
}
impl Storage for MemFlash {
fn write(&mut self, offset: u32, bytes: &[u8]) -> core::result::Result<(), ()> {
let dst = self
.0
.get_mut(offset as usize..offset as usize + bytes.len())
.ok_or(())?;
dst.copy_from_slice(bytes);
Ok(())
}
}
fn part_entry(p_type: u8, subtype: u8, offset: u32, size: u32) -> [u8; 32] {
let mut e = [0u8; 32];
e[0..4].copy_from_slice(&[0xAA, 0x50, p_type, subtype]);
e[4..8].copy_from_slice(&offset.to_le_bytes());
e[8..12].copy_from_slice(&size.to_le_bytes());
e
}
fn flash_with(entries: &[[u8; 32]]) -> MemFlash {
let mut flash = vec![0xFF; 0x30000];
for (i, e) in entries.iter().enumerate() {
let at = PART_OFFSET as usize + i * 32;
flash[at..at + 32].copy_from_slice(e);
}
MemFlash(flash)
}
fn default_flash() -> MemFlash {
flash_with(&[
part_entry(1, 2, 0x9000, 0x4000), part_entry(1, 0, OTADATA, 0x2000),
part_entry(0, 0x10, OTA_0, OTA_SIZE),
part_entry(0, 0x11, OTA_1, OTA_SIZE),
part_entry(0, 0x20, 0x2_0000, 0x1000), ])
}
fn entry_at(ota: &mut Ota<MemFlash>, slot: u8) -> EspOtaSelectEntry {
ota.read_boot_entry(slot).unwrap()
}
#[test]
fn crc_matches_esp_idf() {
assert_eq!(crc32_le(0, b"123456789"), 0xCBF43926);
assert!(helpers::is_crc_seq_correct(1, 0x4743989A));
assert!(helpers::is_crc_seq_correct(2, 0x55F63774));
assert!(!helpers::is_crc_seq_correct(2, 0x4743989A));
assert!(!helpers::is_crc_seq_correct(
u32::MAX,
crc32_le(u32::MAX, &[0xFF; 4])
));
}
#[test]
fn helpers_do_not_panic_on_bad_input() {
assert_eq!(helpers::seq_to_part(5, 0), 0);
assert_eq!(helpers::seq_to_part(0, 2), 0);
assert_eq!(helpers::seq_to_part(4, 2), 1);
}
#[test]
fn entry_parsing_accepts_any_state_value() {
let mut bytes = [0xAB; 32];
bytes[24..28].copy_from_slice(&7u32.to_le_bytes());
let entry = EspOtaSelectEntry::from_bytes(&bytes);
assert_eq!(entry.ota_state, OtaImgState::Undefined);
assert_eq!(entry.seq_label, [0xAB; 20]);
let mut entry = entry;
entry.ota_state = OtaImgState::Valid;
let parsed = EspOtaSelectEntry::from_bytes(&entry.to_bytes());
assert_eq!(parsed.ota_state, OtaImgState::Valid);
assert_eq!(parsed.seq, entry.seq);
}
#[test]
fn partition_table_parsing() {
let ota = Ota::new(default_flash()).unwrap();
assert_eq!(
ota.get_partitions(),
&[(OTA_0, OTA_SIZE), (OTA_1, OTA_SIZE)]
);
assert_eq!(ota.pinfo.otadata_offset, OTADATA);
let no_otadata = flash_with(&[
part_entry(0, 0x10, OTA_0, OTA_SIZE),
part_entry(0, 0x11, OTA_1, OTA_SIZE),
]);
assert_eq!(
Ota::new(no_otadata).err(),
Some(Error::NoOtadata)
);
let out_of_order = flash_with(&[
part_entry(1, 0, OTADATA, 0x2000),
part_entry(0, 0x11, OTA_1, OTA_SIZE),
part_entry(0, 0x10, OTA_0, OTA_SIZE),
]);
assert_eq!(
Ota::new(out_of_order).err(),
Some(Error::PartitionOrder)
);
}
#[test]
fn full_update_writes_otadata() {
let image: Vec<u8> = (0..5000u32).map(|i| i as u8).collect();
let crc = crc32_le(0, &image);
let mut ota = Ota::new(default_flash()).unwrap();
assert_eq!(ota.write(&image), Err(Error::NotStarted));
assert_eq!(
ota.begin(OTA_SIZE + 1, crc),
Err(Error::PartitionTooSmall)
);
ota.begin(image.len() as u32, crc).unwrap();
assert_eq!(ota.write(&image[..1000]), Ok(false));
assert_eq!(ota.finish(true), Err(Error::Incomplete));
let mut rest = image[1000..].to_vec();
rest.extend_from_slice(&[0x55; 100]);
assert_eq!(ota.write(&rest), Ok(true));
let progress = ota.progress().unwrap();
assert_eq!((progress.percent(), progress.permille()), (100, 1000));
assert_eq!(ota.verify(), Ok(true));
ota.finish(true).unwrap();
assert_eq!(&ota.flash.0[OTA_0 as usize..][..image.len()], &image[..]);
let entry = entry_at(&mut ota, 2);
assert_eq!(entry.seq, 1);
assert_eq!(entry.ota_state, OtaImgState::New);
assert_eq!(entry_at(&mut ota, 1).seq, 0);
}
#[test]
fn wrong_crc_does_not_touch_otadata() {
let mut ota = Ota::new(default_flash()).unwrap();
ota.begin(4, 1234).unwrap();
assert_eq!(ota.write(&[1, 2, 3, 4]), Ok(true));
assert_eq!(ota.finish(false), Err(Error::Crc));
assert!(
ota.flash.0[OTADATA as usize..][..0x2000]
.iter()
.all(|&b| b == 0xFF)
);
}
#[test]
fn boot_seq_follows_esp_idf() {
let mut ota = Ota::new(default_flash()).unwrap();
ota.set_boot_partition(0, OtaImgState::New)
.unwrap();
assert_eq!(
(entry_at(&mut ota, 1).seq, entry_at(&mut ota, 2).seq),
(0, 1)
);
ota.set_boot_partition(1, OtaImgState::New)
.unwrap();
assert_eq!(
(entry_at(&mut ota, 1).seq, entry_at(&mut ota, 2).seq),
(2, 1)
);
ota.set_boot_partition(0, OtaImgState::New)
.unwrap();
assert_eq!(
(entry_at(&mut ota, 1).seq, entry_at(&mut ota, 2).seq),
(2, 3)
);
ota.set_boot_partition(0, OtaImgState::New)
.unwrap();
assert_eq!(
(entry_at(&mut ota, 1).seq, entry_at(&mut ota, 2).seq),
(3, 3)
);
assert_eq!(
ota.set_boot_partition(2, OtaImgState::New),
Err(Error::NotEnoughPartitions)
);
}
#[test]
fn resume_validates_input() {
let image = [7u8; 300];
let crc = crc32_le(0, &image);
let mut ota = Ota::new(default_flash()).unwrap();
assert_eq!(
ota.resume(
&ResumeInfo {
size: 10,
remaining: 20,
target_crc: 0,
last_crc: 0
},
false
),
Err(Error::InvalidResumeInfo)
);
ota.begin(300, crc).unwrap();
ota.write(&image[..100]).unwrap();
let info = ota.resume_info().unwrap();
assert_eq!(ota.progress().unwrap().percent(), 33);
let mut ota = Ota::new(MemFlash(ota.flash.0)).unwrap();
assert_eq!(
ota.resume(
&ResumeInfo {
last_crc: info.last_crc ^ 1,
..info
},
true
),
Err(Error::Crc)
);
ota.resume(&info, true).unwrap();
assert_eq!(ota.write(&image[100..]), Ok(true));
ota.finish(false).unwrap();
}