diff --git a/qa-test/src/bin/embassy_i2c_bmp180_calibration_data.rs b/qa-test/src/bin/embassy_i2c_bmp180_calibration_data.rs index e89b8231e7..45df473b52 100644 --- a/qa-test/src/bin/embassy_i2c_bmp180_calibration_data.rs +++ b/qa-test/src/bin/embassy_i2c_bmp180_calibration_data.rs @@ -12,6 +12,7 @@ //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% FEATURES: embassy-generic-timers +//% TAG: bmp180 #![no_std] #![no_main] diff --git a/qa-test/src/bin/i2c_bmp180_calibration_data.rs b/qa-test/src/bin/i2c_bmp180_calibration_data.rs index 91070eb77b..8f7557f32f 100644 --- a/qa-test/src/bin/i2c_bmp180_calibration_data.rs +++ b/qa-test/src/bin/i2c_bmp180_calibration_data.rs @@ -7,6 +7,7 @@ //! - SCL => GPIO5 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% TAG: bmp180 #![no_std] #![no_main] diff --git a/qa-test/src/bin/qspi_flash.rs b/qa-test/src/bin/qspi_flash.rs index 93c28416ff..261aebbf64 100644 --- a/qa-test/src/bin/qspi_flash.rs +++ b/qa-test/src/bin/qspi_flash.rs @@ -23,6 +23,7 @@ //! register is set. //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% TAG: flashchip #![no_std] #![no_main] diff --git a/qa-test/src/bin/ram.rs b/qa-test/src/bin/ram.rs index 4fd04a3e83..3dce40f318 100644 --- a/qa-test/src/bin/ram.rs +++ b/qa-test/src/bin/ram.rs @@ -46,16 +46,14 @@ fn main() -> ! { function_in_ram as *const () ); unsafe { - println!("SOME_INITED_DATA {:x?}", SOME_INITED_DATA); - println!("SOME_PERSISTENT_DATA {:x?}", SOME_PERSISTENT_DATA); - println!("SOME_ZEROED_DATA {:x?}", SOME_ZEROED_DATA); + assert_eq!(&[0xaa, 0xbb], &SOME_INITED_DATA); + assert_eq!(&[0; 8], &SOME_ZEROED_DATA); SOME_INITED_DATA[0] = 0xff; SOME_ZEROED_DATA[0] = 0xff; - println!("SOME_INITED_DATA {:x?}", SOME_INITED_DATA); - println!("SOME_PERSISTENT_DATA {:x?}", SOME_PERSISTENT_DATA); - println!("SOME_ZEROED_DATA {:x?}", SOME_ZEROED_DATA); + assert_eq!(&[0xff, 0xbb], &SOME_INITED_DATA); + assert_eq!(&[0xff, 0, 0, 0, 0, 0, 0, 0], &SOME_ZEROED_DATA); if SOME_PERSISTENT_DATA[1] == 0xff { SOME_PERSISTENT_DATA[1] = 0; @@ -69,17 +67,22 @@ fn main() -> ! { "RTC_FAST function located at {:p}", function_in_rtc_ram as *const () ); - println!("Result {}", function_in_rtc_ram()); + assert_eq!(42, function_in_rtc_ram()); + + println!("Restarting in ~ 10 seconds."); + let mut i = 10; loop { - function_in_ram(); + assert_eq!(42, function_in_rtc_ram()); + function_in_ram(i); + i = i.saturating_sub(1); delay.delay(1.secs()); } } #[ram] -fn function_in_ram() { - println!("Hello world!"); +fn function_in_ram(count: u32) { + println!("{}", count); } #[ram(rtc_fast)] diff --git a/qa-test/src/bin/spi_halfduplex_read_manufacturer_id.rs b/qa-test/src/bin/spi_halfduplex_read_manufacturer_id.rs index 3972410f60..a77284e53b 100644 --- a/qa-test/src/bin/spi_halfduplex_read_manufacturer_id.rs +++ b/qa-test/src/bin/spi_halfduplex_read_manufacturer_id.rs @@ -23,6 +23,7 @@ //! register is set. //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% TAG: flashchip #![no_std] #![no_main] diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index dd1c37e47e..078b48ac4f 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -9,6 +9,7 @@ anyhow = "1.0.93" basic-toml = "0.1.9" chrono = "0.4.38" clap = { version = "4.5.20", features = ["derive", "wrap_help"] } +console = "0.15.10" csv = "1.3.1" env_logger = "0.11.5" esp-metadata = { path = "../esp-metadata", features = ["clap"] } diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index fde169615b..6eefb5b04a 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -59,14 +59,24 @@ pub struct Metadata { example_path: PathBuf, chip: Chip, feature_set: Vec, + tag: Option, + description: Option, } impl Metadata { - pub fn new(example_path: &Path, chip: Chip, feature_set: Vec) -> Self { + pub fn new( + example_path: &Path, + chip: Chip, + feature_set: Vec, + tag: Option, + description: Option, + ) -> Self { Self { example_path: example_path.to_path_buf(), chip, feature_set, + tag, + description, } } @@ -93,6 +103,16 @@ impl Metadata { pub fn supports_chip(&self, chip: Chip) -> bool { self.chip == chip } + + /// Optional tag of the example. + pub fn tag(&self) -> Option { + self.tag.clone() + } + + /// Optional description of the example. + pub fn description(&self) -> Option { + self.description.clone() + } } #[derive(Debug, Clone, Copy, Display, ValueEnum)] @@ -203,6 +223,17 @@ pub fn load_examples(path: &Path, action: CargoAction) -> Result> let mut chips = Chip::iter().collect::>(); let mut feature_sets = Vec::new(); let mut chip_features = HashMap::new(); + let mut tag = None; + let mut description = None; + + // collect `//!` as description + for line in text.lines().filter(|line| line.starts_with("//!")) { + let line = line.trim_start_matches("//!"); + let mut descr: String = description.unwrap_or_default(); + descr.push_str(line); + descr.push('\n'); + description = Some(descr); + } // We will indicate metadata lines using the `//%` prefix: for line in text.lines().filter(|line| line.starts_with("//%")) { @@ -250,6 +281,8 @@ pub fn load_examples(path: &Path, action: CargoAction) -> Result> for chip in chips { chip_features.insert(chip, values.clone()); } + } else if key.starts_with("TAG") { + tag = Some(value.to_string()); } else { log::warn!("Unrecognized metadata key '{key}', ignoring"); } @@ -274,7 +307,13 @@ pub fn load_examples(path: &Path, action: CargoAction) -> Result> feature_set.sort(); } - examples.push(Metadata::new(&path, *chip, feature_set.clone())); + examples.push(Metadata::new( + &path, + *chip, + feature_set.clone(), + tag.clone(), + description.clone(), + )); } } } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 49dac201df..f1c62f83d7 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -244,7 +244,8 @@ fn examples(workspace: &Path, mut args: ExampleArgs, action: CargoAction) -> Res // Execute the specified action: match action { CargoAction::Build => build_examples(args, examples, &package_path), - CargoAction::Run => run_example(args, examples, &package_path), + CargoAction::Run if args.example.is_some() => run_example(args, examples, &package_path), + CargoAction::Run => run_examples(args, examples, &package_path), } } @@ -318,6 +319,79 @@ fn run_example(args: ExampleArgs, examples: Vec, package_path: &Path) Ok(()) } +fn run_examples(args: ExampleArgs, examples: Vec, package_path: &Path) -> Result<()> { + // Determine the appropriate build target for the given package and chip: + let target = target_triple(args.package, &args.chip)?; + + // Filter the examples down to only the binaries we're interested in + let mut examples: Vec = examples + .iter() + .filter(|ex| ex.supports_chip(args.chip)) + .cloned() + .collect(); + examples.sort_by_key(|ex| ex.tag()); + + let console = console::Term::stdout(); + + for example in examples { + let mut skip = false; + + log::info!("Running example '{}'", example.name()); + if let Some(description) = example.description() { + log::info!( + "\n\n{}\n\nPress ENTER to run example, `s` to skip", + description.trim() + ); + } else { + log::info!("\n\nPress ENTER to run example, `s` to skip"); + } + + loop { + let key = console.read_key(); + + match key { + Ok(console::Key::Enter) => break, + Ok(console::Key::Char('s')) => { + skip = true; + break; + } + _ => (), + } + } + + if !skip { + while !skip + && xtask::execute_app( + package_path, + args.chip, + target, + &example, + CargoAction::Run, + 1, + args.debug, + ) + .is_err() + { + log::info!("Failed to run example. Retry or skip? (r/s)"); + loop { + let key = console.read_key(); + + match key { + Ok(console::Key::Char('r')) => break, + Ok(console::Key::Char('s')) => { + skip = true; + break; + } + _ => (), + } + } + } + } + } + + Ok(()) +} + fn tests(workspace: &Path, args: TestArgs, action: CargoAction) -> Result<()> { // Absolute path of the 'hil-test' package's root: let package_path = xtask::windows_safe_path(&workspace.join("hil-test"));