Test all feature sets (#2901)

* Mark interconnect as unstable

* Explicitly set unstable feature in HIL tests

* WIP append feature set name to artifact

* Add name to feature sets, build all combinations

* Fix tests

* Provide a looping executor for stable async tests

* Fix usb serial jtag

* Hide interconnect types
This commit is contained in:
Dániel Buga 2025-01-09 14:58:14 +01:00 committed by GitHub
parent 2b80e4d123
commit 848029b152
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
54 changed files with 307 additions and 191 deletions

View File

@ -121,26 +121,10 @@ jobs:
- name: Build tests - name: Build tests
run: cargo xtask build-tests ${{ matrix.target.soc }} run: cargo xtask build-tests ${{ matrix.target.soc }}
- name: Prepare artifact
run: |
# Create the 'tests' directory if it doesn't exist
mkdir -p tests
# Find ELF files in the specified path and move them to 'tests'
find "hil-test/target/${{ matrix.target.rust-target }}/release/deps/" -type f -exec file {} + | \
grep ELF | \
awk -F: '{print $1}' | \
xargs -I {} mv {} tests
# Rename files in 'tests' by removing everything after the first dash
for file in tests/*-*; do
base_name="$(basename "$file" | cut -d'-' -f1)"
mv "$file" "tests/$base_name"
done
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
name: tests-${{ matrix.target.soc }} name: tests-${{ matrix.target.soc }}
path: /home/runner/work/esp-hal/esp-hal/tests path: /home/runner/work/esp-hal/esp-hal/target/tests/${{ matrix.target.soc }}
if-no-files-found: error if-no-files-found: error
overwrite: true overwrite: true

View File

@ -212,6 +212,7 @@ fn disconnect_peripheral_output_from_pin(pin: &mut AnyPin, signal: gpio::OutputS
/// A configurable input signal between a peripheral and a GPIO pin. /// A configurable input signal between a peripheral and a GPIO pin.
/// ///
/// Multiple input signals can be connected to one pin. /// Multiple input signals can be connected to one pin.
#[instability::unstable]
pub struct InputSignal { pub struct InputSignal {
pin: AnyPin, pin: AnyPin,
is_inverted: bool, is_inverted: bool,
@ -347,6 +348,7 @@ impl DirectInputSignal {
/// A configurable output signal between a peripheral and a GPIO pin. /// A configurable output signal between a peripheral and a GPIO pin.
/// ///
/// Multiple pins can be connected to one output signal. /// Multiple pins can be connected to one output signal.
#[instability::unstable]
pub struct OutputSignal { pub struct OutputSignal {
pin: AnyPin, pin: AnyPin,
is_inverted: bool, is_inverted: bool,
@ -506,6 +508,7 @@ enum InputConnectionInner {
/// This is mainly intended for internal use, but it can be used to connect /// This is mainly intended for internal use, but it can be used to connect
/// peripherals within the MCU without external hardware. /// peripherals within the MCU without external hardware.
#[derive(Clone)] #[derive(Clone)]
#[doc(hidden)] // FIXME: replace with `#[unstable]` when we can mark delegated methods https://github.com/Kobzol/rust-delegate/issues/77
pub struct InputConnection(InputConnectionInner); pub struct InputConnection(InputConnectionInner);
impl Peripheral for InputConnection { impl Peripheral for InputConnection {
@ -614,6 +617,7 @@ enum OutputConnectionInner {
/// ///
/// This is mainly intended for internal use, but it can be used to connect /// This is mainly intended for internal use, but it can be used to connect
/// peripherals within the MCU without external hardware. /// peripherals within the MCU without external hardware.
#[doc(hidden)] // FIXME: replace with `#[unstable]` when we can mark delegated methods https://github.com/Kobzol/rust-delegate/issues/77
pub struct OutputConnection(OutputConnectionInner); pub struct OutputConnection(OutputConnectionInner);
impl Sealed for OutputConnection {} impl Sealed for OutputConnection {}

View File

@ -79,23 +79,20 @@ use crate::{
private::{self, Sealed}, private::{self, Sealed},
}; };
pub mod interconnect;
mod placeholder; mod placeholder;
pub use placeholder::NoPin; pub use placeholder::NoPin;
#[cfg(soc_etm)]
crate::unstable_module! { crate::unstable_module! {
pub mod interconnect;
#[cfg(soc_etm)]
pub mod etm; pub mod etm;
}
#[cfg(lp_io)] #[cfg(lp_io)]
crate::unstable_module! {
pub mod lp_io; pub mod lp_io;
}
#[cfg(all(rtc_io, not(esp32)))] #[cfg(all(rtc_io, not(esp32)))]
crate::unstable_module! {
pub mod rtc_io; pub mod rtc_io;
} }
@ -820,6 +817,7 @@ where
/// ///
/// Peripheral signals allow connecting peripherals together without using /// Peripheral signals allow connecting peripherals together without using
/// external hardware. /// external hardware.
#[instability::unstable]
pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) {
( (
interconnect::InputSignal::new(self.degrade_pin(private::Internal)), interconnect::InputSignal::new(self.degrade_pin(private::Internal)),
@ -1296,6 +1294,8 @@ impl<'d> Output<'d> {
/// ///
/// Peripheral signals allow connecting peripherals together without using /// Peripheral signals allow connecting peripherals together without using
/// external hardware. /// external hardware.
#[inline]
#[instability::unstable]
pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) {
self.pin.split() self.pin.split()
} }
@ -1305,6 +1305,7 @@ impl<'d> Output<'d> {
/// ///
/// The input signal can be passed to peripherals in place of an input pin. /// The input signal can be passed to peripherals in place of an input pin.
#[inline] #[inline]
#[instability::unstable]
pub fn peripheral_input(&self) -> interconnect::InputSignal { pub fn peripheral_input(&self) -> interconnect::InputSignal {
self.pin.peripheral_input() self.pin.peripheral_input()
} }
@ -1315,6 +1316,7 @@ impl<'d> Output<'d> {
/// The output signal can be passed to peripherals in place of an output /// The output signal can be passed to peripherals in place of an output
/// pin. /// pin.
#[inline] #[inline]
#[instability::unstable]
pub fn into_peripheral_output(self) -> interconnect::OutputSignal { pub fn into_peripheral_output(self) -> interconnect::OutputSignal {
self.pin.into_peripheral_output() self.pin.into_peripheral_output()
} }
@ -1439,6 +1441,7 @@ impl<'d> Input<'d> {
/// ///
/// The input signal can be passed to peripherals in place of an input pin. /// The input signal can be passed to peripherals in place of an input pin.
#[inline] #[inline]
#[instability::unstable]
pub fn peripheral_input(&self) -> interconnect::InputSignal { pub fn peripheral_input(&self) -> interconnect::InputSignal {
self.pin.peripheral_input() self.pin.peripheral_input()
} }
@ -1565,6 +1568,8 @@ impl<'d> Input<'d> {
/// ///
/// Peripheral signals allow connecting peripherals together without using /// Peripheral signals allow connecting peripherals together without using
/// external hardware. /// external hardware.
#[inline]
#[instability::unstable]
pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) {
self.pin.split() self.pin.split()
} }
@ -1575,6 +1580,7 @@ impl<'d> Input<'d> {
/// The output signal can be passed to peripherals in place of an output /// The output signal can be passed to peripherals in place of an output
/// pin. /// pin.
#[inline] #[inline]
#[instability::unstable]
pub fn into_peripheral_output(self) -> interconnect::OutputSignal { pub fn into_peripheral_output(self) -> interconnect::OutputSignal {
self.pin.into_peripheral_output() self.pin.into_peripheral_output()
} }
@ -1654,6 +1660,8 @@ impl<'d> OutputOpenDrain<'d> {
/// ///
/// Peripheral signals allow connecting peripherals together without using /// Peripheral signals allow connecting peripherals together without using
/// external hardware. /// external hardware.
#[inline]
#[instability::unstable]
pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) {
self.pin.split() self.pin.split()
} }
@ -1663,6 +1671,7 @@ impl<'d> OutputOpenDrain<'d> {
/// ///
/// The input signal can be passed to peripherals in place of an input pin. /// The input signal can be passed to peripherals in place of an input pin.
#[inline] #[inline]
#[instability::unstable]
pub fn peripheral_input(&self) -> interconnect::InputSignal { pub fn peripheral_input(&self) -> interconnect::InputSignal {
self.pin.peripheral_input() self.pin.peripheral_input()
} }
@ -1673,6 +1682,7 @@ impl<'d> OutputOpenDrain<'d> {
/// The output signal can be passed to peripherals in place of an output /// The output signal can be passed to peripherals in place of an output
/// pin. /// pin.
#[inline] #[inline]
#[instability::unstable]
pub fn into_peripheral_output(self) -> interconnect::OutputSignal { pub fn into_peripheral_output(self) -> interconnect::OutputSignal {
self.pin.into_peripheral_output() self.pin.into_peripheral_output()
} }
@ -1797,6 +1807,7 @@ impl<'d> Flex<'d> {
/// ///
/// The input signal can be passed to peripherals in place of an input pin. /// The input signal can be passed to peripherals in place of an input pin.
#[inline] #[inline]
#[instability::unstable]
pub fn peripheral_input(&self) -> interconnect::InputSignal { pub fn peripheral_input(&self) -> interconnect::InputSignal {
self.pin.degrade_pin(private::Internal).split().0 self.pin.degrade_pin(private::Internal).split().0
} }
@ -1963,6 +1974,8 @@ impl<'d> Flex<'d> {
/// ///
/// Peripheral signals allow connecting peripherals together without using /// Peripheral signals allow connecting peripherals together without using
/// external hardware. /// external hardware.
#[inline]
#[instability::unstable]
pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) {
assert!(self.pin.is_output()); assert!(self.pin.is_output());
self.pin.degrade_pin(private::Internal).split() self.pin.degrade_pin(private::Internal).split()
@ -1974,6 +1987,7 @@ impl<'d> Flex<'d> {
/// The output signal can be passed to peripherals in place of an output /// The output signal can be passed to peripherals in place of an output
/// pin. /// pin.
#[inline] #[inline]
#[instability::unstable]
pub fn into_peripheral_output(self) -> interconnect::OutputSignal { pub fn into_peripheral_output(self) -> interconnect::OutputSignal {
self.split().1 self.split().1
} }
@ -2028,6 +2042,7 @@ pub(crate) mod internal {
/// using external hardware. /// using external hardware.
#[inline] #[inline]
#[allow(unused_braces, reason = "False positive")] #[allow(unused_braces, reason = "False positive")]
#[instability::unstable]
pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) {
handle_gpio_input!(self, target, { target.split() }) handle_gpio_input!(self, target, { target.split() })
} }

View File

@ -197,6 +197,7 @@ cfg-if = "1.0.0"
critical-section = "1.1.3" critical-section = "1.1.3"
defmt = "0.3.8" defmt = "0.3.8"
defmt-rtt = { version = "0.4.1", optional = true } defmt-rtt = { version = "0.4.1", optional = true }
embassy-executor = "0.6.0"
embassy-futures = "0.1.1" embassy-futures = "0.1.1"
embassy-sync = "0.6.0" embassy-sync = "0.6.0"
embassy-time = "0.3.2" embassy-time = "0.3.2"
@ -206,7 +207,7 @@ embedded-hal-async = "1.0.0"
embedded-hal-nb = "1.0.0" embedded-hal-nb = "1.0.0"
esp-alloc = { path = "../esp-alloc", optional = true } esp-alloc = { path = "../esp-alloc", optional = true }
esp-backtrace = { path = "../esp-backtrace", default-features = false, features = ["exception-handler", "defmt", "semihosting"] } esp-backtrace = { path = "../esp-backtrace", default-features = false, features = ["exception-handler", "defmt", "semihosting"] }
esp-hal = { path = "../esp-hal", features = ["digest", "unstable"], optional = true } esp-hal = { path = "../esp-hal", features = ["digest"], optional = true }
esp-hal-embassy = { path = "../esp-hal-embassy", optional = true } esp-hal-embassy = { path = "../esp-hal-embassy", optional = true }
esp-wifi = { path = "../esp-wifi", optional = true, features = ["wifi"] } esp-wifi = { path = "../esp-wifi", optional = true, features = ["wifi"] }
portable-atomic = "1.9.0" portable-atomic = "1.9.0"
@ -220,7 +221,7 @@ digest = { version = "0.10.7", default-features = false }
elliptic-curve = { version = "0.13.8", default-features = false, features = ["sec1"] } elliptic-curve = { version = "0.13.8", default-features = false, features = ["sec1"] }
embassy-executor = { version = "0.6.0", default-features = false } embassy-executor = { version = "0.6.0", default-features = false }
# Add the `embedded-test/defmt` feature for more verbose testing # Add the `embedded-test/defmt` feature for more verbose testing
embedded-test = { version = "0.5.0", git = "https://github.com/probe-rs/embedded-test.git", rev = "7109473", default-features = false } embedded-test = { version = "0.5.0", git = "https://github.com/probe-rs/embedded-test.git", rev = "7109473", default-features = false, features = ["embassy", "external-executor"] }
fugit = "0.3.7" fugit = "0.3.7"
hex-literal = "0.4.1" hex-literal = "0.4.1"
nb = "1.1.0" nb = "1.1.0"
@ -234,7 +235,8 @@ esp-build = { path = "../esp-build" }
esp-metadata = { path = "../esp-metadata" } esp-metadata = { path = "../esp-metadata" }
[features] [features]
default = ["embassy"] default = []
unstable = ["esp-hal/unstable"]
defmt = ["dep:defmt-rtt", "esp-hal/defmt", "embedded-test/defmt"] defmt = ["dep:defmt-rtt", "esp-hal/defmt", "embedded-test/defmt"]
@ -286,14 +288,14 @@ esp32s3 = [
] ]
# Async & Embassy: # Async & Embassy:
embassy = [ embassy = [
"embedded-test/embassy",
"embedded-test/external-executor",
"dep:esp-hal-embassy", "dep:esp-hal-embassy",
] ]
generic-queue = [ generic-queue = [
"embassy",
"embassy-time/generic-queue-64" "embassy-time/generic-queue-64"
] ]
integrated-timers = [ integrated-timers = [
"embassy",
"esp-hal-embassy/integrated-timers", "esp-hal-embassy/integrated-timers",
] ]
octal-psram = ["esp-hal/octal-psram", "esp-alloc"] octal-psram = ["esp-hal/octal-psram", "esp-alloc"]

View File

@ -82,3 +82,42 @@ macro_rules! unconnected_pin {
} }
}}; }};
} }
// A simple looping executor to test async code without esp-hal-embassy (which
// needs `esp-hal/unstable`).
#[cfg(not(feature = "embassy"))]
mod executor {
use core::marker::PhantomData;
use embassy_executor::{raw, Spawner};
#[export_name = "__pender"]
fn __pender(_: *mut ()) {}
pub struct Executor {
inner: raw::Executor,
not_send: PhantomData<*mut ()>,
}
impl Executor {
pub fn new() -> Self {
Self {
inner: raw::Executor::new(core::ptr::null_mut()),
not_send: PhantomData,
}
}
pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
init(self.inner.spawner());
loop {
unsafe { self.inner.poll() };
}
}
}
}
#[cfg(feature = "embassy")]
pub use esp_hal_embassy::Executor;
#[cfg(not(feature = "embassy"))]
pub use executor::Executor;

View File

@ -1,6 +1,7 @@
//! AES Test //! AES Test
//% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,6 +1,7 @@
//! AES DMA Test //! AES DMA Test
//% CHIPS: esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,6 +1,7 @@
//! Clock Monitor Test //! Clock Monitor Test
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,6 +1,7 @@
//! CRC and MD5 Tests //! CRC and MD5 Tests
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,6 +1,7 @@
//! Ensure invariants of locks are upheld. //! Ensure invariants of locks are upheld.
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
// TODO: add multi-core tests // TODO: add multi-core tests

View File

@ -1,6 +1,7 @@
//! Delay Test //! Delay Test
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -4,6 +4,7 @@
//! `embedded_hal_async::delay::DelayNs` trait. //! `embedded_hal_async::delay::DelayNs` trait.
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]
@ -69,7 +70,7 @@ async fn test_async_delay_ms(mut timer: impl DelayNs, duration: u32) {
} }
#[cfg(test)] #[cfg(test)]
#[embedded_test::tests(default_timeout = 2, executor = esp_hal_embassy::Executor::new())] #[embedded_test::tests(default_timeout = 2, executor = hil_test::Executor::new())]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -1,6 +1,7 @@
//! DMA macro tests //! DMA macro tests
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,6 +1,7 @@
//! DMA Mem2Mem Tests //! DMA Mem2Mem Tests
//% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s3 //% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,6 +1,7 @@
//! ECC Test //! ECC Test
//% CHIPS: esp32c2 esp32c6 esp32h2 //% CHIPS: esp32c2 esp32c6 esp32h2
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -2,8 +2,8 @@
//! code. //! code.
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: integrated-timers //% FEATURES(integrated): unstable embassy integrated-timers
//% FEATURES: generic-queue //% FEATURES(generic): unstable embassy generic-queue
#![no_std] #![no_std]
#![no_main] #![no_main]
@ -48,7 +48,7 @@ struct Context {
} }
#[cfg(test)] #[cfg(test)]
#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] #[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())]
mod test { mod test {
use super::*; use super::*;

View File

@ -1,8 +1,8 @@
//! Reproduction and regression test for a sneaky issue. //! Reproduction and regression test for a sneaky issue.
//% CHIPS: esp32 esp32s2 esp32s3 esp32c3 esp32c6 esp32h2 //% CHIPS: esp32 esp32s2 esp32s3 esp32c3 esp32c6 esp32h2
//% FEATURES: integrated-timers //% FEATURES(integrated): unstable embassy integrated-timers
//% FEATURES: generic-queue //% FEATURES(generic): unstable embassy generic-queue
#![no_std] #![no_std]
#![no_main] #![no_main]
@ -75,7 +75,7 @@ async fn interrupt_driven_task(mut i2s_tx: esp_hal::i2s::master::I2sTx<'static,
} }
#[cfg(test)] #[cfg(test)]
#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] #[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())]
mod test { mod test {
use super::*; use super::*;

View File

@ -1,8 +1,8 @@
//! Embassy timer and executor Test //! Embassy timer and executor Test
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: integrated-timers //% FEATURES(integrated): unstable embassy integrated-timers
//% FEATURES: generic-queue //% FEATURES(generic): unstable embassy generic-queue
#![no_std] #![no_std]
#![no_main] #![no_main]
@ -122,7 +122,7 @@ fn set_up_embassy_with_systimer(peripherals: Peripherals) {
} }
#[cfg(test)] #[cfg(test)]
#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] #[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())]
mod test { mod test {
use super::*; use super::*;
use crate::test_cases::*; use crate::test_cases::*;

View File

@ -1,7 +1,7 @@
//! Cp0Disable exception regression test //! Cp0Disable exception regression test
//% CHIPS: esp32 esp32s2 esp32s3 //% CHIPS: esp32 esp32s2 esp32s3
//% FEATURES: esp-wifi esp-alloc //% FEATURES: unstable esp-wifi esp-alloc
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,6 +1,7 @@
//! time::now Test //! time::now Test
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,7 +1,8 @@
//! GPIO Test //! GPIO Test
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: generic-queue //% FEATURES: unstable embassy generic-queue
//% FEATURES(stable):
#![no_std] #![no_std]
#![no_main] #![no_main]
@ -9,14 +10,22 @@
use core::cell::RefCell; use core::cell::RefCell;
use critical_section::Mutex; use critical_section::Mutex;
#[cfg(feature = "unstable")]
use embassy_time::{Duration, Timer};
use esp_hal::{ use esp_hal::{
delay::Delay, delay::Delay,
gpio::{AnyPin, Input, Io, Level, Output, Pin, Pull}, gpio::{AnyPin, Input, Level, Output, OutputOpenDrain, Pin, Pull},
interrupt::InterruptConfigurable,
macros::handler, macros::handler,
};
#[cfg(feature = "unstable")]
use esp_hal::{
gpio::{Event, Flex, Io},
interrupt::InterruptConfigurable,
timer::timg::TimerGroup, timer::timg::TimerGroup,
}; };
use hil_test as _; use hil_test as _;
#[cfg(feature = "unstable")]
use portable_atomic::{AtomicUsize, Ordering};
static COUNTER: Mutex<RefCell<u32>> = Mutex::new(RefCell::new(0)); static COUNTER: Mutex<RefCell<u32>> = Mutex::new(RefCell::new(0));
static INPUT_PIN: Mutex<RefCell<Option<Input>>> = Mutex::new(RefCell::new(None)); static INPUT_PIN: Mutex<RefCell<Option<Input>>> = Mutex::new(RefCell::new(None));
@ -39,27 +48,28 @@ pub fn interrupt_handler() {
} }
#[cfg(test)] #[cfg(test)]
#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] #[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())]
mod tests { mod tests {
use embassy_time::{Duration, Timer};
use esp_hal::gpio::{Event, Flex, OutputOpenDrain};
use portable_atomic::{AtomicUsize, Ordering};
use super::*; use super::*;
#[init] #[init]
fn init() -> Context { fn init() -> Context {
let peripherals = esp_hal::init(esp_hal::Config::default()); let peripherals = esp_hal::init(esp_hal::Config::default());
let mut io = Io::new(peripherals.IO_MUX);
io.set_interrupt_handler(interrupt_handler);
let delay = Delay::new(); let delay = Delay::new();
let (gpio1, gpio2) = hil_test::common_test_pins!(peripherals); let (gpio1, gpio2) = hil_test::common_test_pins!(peripherals);
#[cfg(feature = "unstable")]
{
// Interrupts are unstable
let mut io = Io::new(peripherals.IO_MUX);
io.set_interrupt_handler(interrupt_handler);
// Timers are unstable
let timg0 = TimerGroup::new(peripherals.TIMG0); let timg0 = TimerGroup::new(peripherals.TIMG0);
esp_hal_embassy::init(timg0.timer0); esp_hal_embassy::init(timg0.timer0);
}
Context { Context {
test_gpio1: gpio1.degrade(), test_gpio1: gpio1.degrade(),
@ -69,6 +79,7 @@ mod tests {
} }
#[test] #[test]
#[cfg(feature = "unstable")] // Timers are unstable
async fn async_edge(ctx: Context) { async fn async_edge(ctx: Context) {
let counter = AtomicUsize::new(0); let counter = AtomicUsize::new(0);
let Context { let Context {
@ -147,53 +158,6 @@ mod tests {
assert_eq!(test_gpio2.is_set_high(), true); assert_eq!(test_gpio2.is_set_high(), true);
} }
#[test]
fn gpio_output_embedded_hal_0_2(ctx: Context) {
let test_gpio1 = Input::new(ctx.test_gpio1, Pull::Down);
let mut test_gpio2 = Output::new(ctx.test_gpio2, Level::Low);
fn set<T>(pin: &mut T, state: bool)
where
T: embedded_hal::digital::OutputPin,
{
if state {
pin.set_high().ok();
} else {
pin.set_low().ok();
}
}
fn toggle<T>(pin: &mut T)
where
T: embedded_hal::digital::StatefulOutputPin,
{
pin.toggle().ok();
}
// `StatefulOutputPin`:
assert_eq!(test_gpio2.is_set_low(), true);
assert_eq!(test_gpio2.is_set_high(), false);
assert_eq!(test_gpio1.is_low(), true);
assert_eq!(test_gpio1.is_high(), false);
set(&mut test_gpio2, true);
assert_eq!(test_gpio2.is_set_low(), false);
assert_eq!(test_gpio2.is_set_high(), true);
assert_eq!(test_gpio1.is_low(), false);
assert_eq!(test_gpio1.is_high(), true);
// `ToggleableOutputPin`:
toggle(&mut test_gpio2);
assert_eq!(test_gpio2.is_set_low(), true);
assert_eq!(test_gpio2.is_set_high(), false);
assert_eq!(test_gpio1.is_low(), true);
assert_eq!(test_gpio1.is_high(), false);
toggle(&mut test_gpio2);
assert_eq!(test_gpio2.is_set_low(), false);
assert_eq!(test_gpio2.is_set_high(), true);
assert_eq!(test_gpio1.is_low(), false);
assert_eq!(test_gpio1.is_high(), true);
}
#[test] #[test]
fn gpio_output_embedded_hal_1_0(ctx: Context) { fn gpio_output_embedded_hal_1_0(ctx: Context) {
let test_gpio1 = Input::new(ctx.test_gpio1, Pull::Down); let test_gpio1 = Input::new(ctx.test_gpio1, Pull::Down);
@ -242,6 +206,7 @@ mod tests {
} }
#[test] #[test]
#[cfg(feature = "unstable")] // Interrupts are unstable
fn gpio_interrupt(ctx: Context) { fn gpio_interrupt(ctx: Context) {
let mut test_gpio1 = Input::new(ctx.test_gpio1, Pull::Down); let mut test_gpio1 = Input::new(ctx.test_gpio1, Pull::Down);
let mut test_gpio2 = Output::new(ctx.test_gpio2, Level::Low); let mut test_gpio2 = Output::new(ctx.test_gpio2, Level::Low);
@ -325,6 +290,7 @@ mod tests {
} }
#[test] #[test]
#[cfg(feature = "unstable")]
fn gpio_flex(ctx: Context) { fn gpio_flex(ctx: Context) {
let mut test_gpio1 = Flex::new(ctx.test_gpio1); let mut test_gpio1 = Flex::new(ctx.test_gpio1);
let mut test_gpio2 = Flex::new(ctx.test_gpio2); let mut test_gpio2 = Flex::new(ctx.test_gpio2);
@ -401,6 +367,7 @@ mod tests {
} }
#[test] #[test]
#[cfg(feature = "unstable")]
fn interrupt_executor_is_not_frozen(ctx: Context) { fn interrupt_executor_is_not_frozen(ctx: Context) {
use esp_hal::interrupt::{software::SoftwareInterrupt, Priority}; use esp_hal::interrupt::{software::SoftwareInterrupt, Priority};
use esp_hal_embassy::InterruptExecutor; use esp_hal_embassy::InterruptExecutor;

View File

@ -6,7 +6,7 @@
//! async API works for user handlers automatically. //! async API works for user handlers automatically.
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: integrated-timers //% FEATURES: unstable integrated-timers
#![no_std] #![no_std]
#![no_main] #![no_main]
@ -66,7 +66,7 @@ async fn drive_pins(gpio1: impl Into<AnyPin>, gpio2: impl Into<AnyPin>) -> usize
} }
#[cfg(test)] #[cfg(test)]
#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] #[embedded_test::tests(executor = hil_test::Executor::new())]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -1,6 +1,7 @@
//! I2C test //! I2C test
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -4,7 +4,7 @@
//! with loopback mode enabled). //! with loopback mode enabled).
//% CHIPS: esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: generic-queue //% FEATURES: unstable generic-queue
// FIXME: re-enable on ESP32 when it no longer fails spuriously // FIXME: re-enable on ESP32 when it no longer fails spuriously
#![no_std] #![no_std]
@ -97,7 +97,7 @@ fn enable_loopback() {
} }
#[cfg(test)] #[cfg(test)]
#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] #[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -1,6 +1,7 @@
//! Initialization tests //! Initialization tests
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -3,6 +3,7 @@
//! "Disabled" for now - see https://github.com/esp-rs/esp-hal/pull/1635#issuecomment-2137405251 //! "Disabled" for now - see https://github.com/esp-rs/esp-hal/pull/1635#issuecomment-2137405251
//% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 //% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,6 +1,7 @@
//! LCD_CAM Camera and DPI tests //! LCD_CAM Camera and DPI tests
//% CHIPS: esp32s3 //% CHIPS: esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,6 +1,7 @@
//! lcd_cam i8080 tests //! lcd_cam i8080 tests
//% CHIPS: esp32s3 //% CHIPS: esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,7 +1,7 @@
//! lcd_cam i8080 tests //! lcd_cam i8080 tests
//% CHIPS: esp32s3 //% CHIPS: esp32s3
//% FEATURES: generic-queue //% FEATURES: unstable generic-queue
#![no_std] #![no_std]
#![no_main] #![no_main]
@ -28,7 +28,7 @@ struct Context<'d> {
} }
#[cfg(test)] #[cfg(test)]
#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] #[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -1,6 +1,8 @@
//! PARL_IO TX test //! PARL_IO TX test
//% CHIPS: esp32c6 esp32h2 //% CHIPS: esp32c6 esp32h2
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,7 +1,7 @@
//! PARL_IO TX async test //! PARL_IO TX async test
//% CHIPS: esp32c6 esp32h2 //% CHIPS: esp32c6 esp32h2
//% FEATURES: generic-queue //% FEATURES: unstable generic-queue
#![no_std] #![no_std]
#![no_main] #![no_main]
@ -43,7 +43,7 @@ struct Context {
} }
#[cfg(test)] #[cfg(test)]
#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] #[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())]
mod tests { mod tests {
use defmt::info; use defmt::info;

View File

@ -1,6 +1,7 @@
//! PCNT tests //! PCNT tests
//% CHIPS: esp32 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,6 +1,7 @@
//! QSPI Test Suite //! QSPI Test Suite
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,6 +1,7 @@
//! RMT Loopback Test //! RMT Loopback Test
//% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,6 +1,7 @@
//! RSA Test //! RSA Test
//% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,6 +1,7 @@
//! Async RSA Test //! Async RSA Test
//% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]
@ -48,7 +49,7 @@ const fn compute_mprime(modulus: &U512) -> u32 {
} }
#[cfg(test)] #[cfg(test)]
#[embedded_test::tests(default_timeout = 5, executor = esp_hal_embassy::Executor::new())] #[embedded_test::tests(default_timeout = 5, executor = hil_test::Executor::new())]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -1,19 +1,21 @@
//! SHA Test //! SHA Test
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]
use digest::{Digest, Update}; use digest::{Digest, Update};
#[cfg(not(feature = "esp32"))] #[cfg(not(feature = "esp32"))]
use esp_hal::clock::CpuClock;
#[cfg(not(feature = "esp32"))]
use esp_hal::sha::Sha224; use esp_hal::sha::Sha224;
#[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))]
use esp_hal::sha::{Sha384, Sha512}; use esp_hal::sha::{Sha384, Sha512};
#[cfg(any(feature = "esp32s2", feature = "esp32s3"))] #[cfg(any(feature = "esp32s2", feature = "esp32s3"))]
use esp_hal::sha::{Sha512_224, Sha512_256}; use esp_hal::sha::{Sha512_224, Sha512_256};
use esp_hal::{ use esp_hal::{
clock::CpuClock,
rng::Rng, rng::Rng,
sha::{Sha, Sha1, Sha256, ShaAlgorithm, ShaDigest}, sha::{Sha, Sha1, Sha256, ShaAlgorithm, ShaDigest},
}; };

View File

@ -1,7 +1,7 @@
//! SPI Full Duplex test suite. //! SPI Full Duplex test suite.
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: generic-queue //% FEATURES: unstable generic-queue
// FIXME: add async test cases that don't rely on PCNT // FIXME: add async test cases that don't rely on PCNT
@ -48,7 +48,7 @@ struct Context {
} }
#[cfg(test)] #[cfg(test)]
#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] #[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -1,6 +1,7 @@
//! SPI Half Duplex Read Test //! SPI Half Duplex Read Test
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,6 +1,7 @@
//! SPI Half Duplex Write Test //! SPI Half Duplex Write Test
//% CHIPS: esp32 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,6 +1,7 @@
//! SPI Half Duplex Write Test //! SPI Half Duplex Write Test
//% FEATURES: octal-psram
//% CHIPS: esp32s3 //% CHIPS: esp32s3
//% FEATURES: unstable octal-psram
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -4,6 +4,7 @@
//! testing Mode 1. //! testing Mode 1.
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]
@ -94,7 +95,7 @@ impl BitbangSpi {
} }
#[cfg(test)] #[cfg(test)]
#[embedded_test::tests(default_timeout = 10, executor = esp_hal_embassy::Executor::new())] #[embedded_test::tests(default_timeout = 10, executor = hil_test::Executor::new())]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -2,6 +2,7 @@
// esp32 disabled as it does not have a systimer // esp32 disabled as it does not have a systimer
//% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,6 +1,7 @@
//! TWAI test //! TWAI test
//% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,6 +1,7 @@
//! UART Test //! UART Test
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,7 +1,7 @@
//! UART Test //! UART Test
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: generic-queue //% FEATURES: unstable embassy generic-queue
#![no_std] #![no_std]
#![no_main] #![no_main]
@ -17,7 +17,7 @@ struct Context {
} }
#[cfg(test)] #[cfg(test)]
#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] #[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -1,6 +1,7 @@
//! Misc UART TX/RX regression tests //! Misc UART TX/RX regression tests
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,6 +1,7 @@
//! UART TX/RX Test //! UART TX/RX Test
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]

View File

@ -1,7 +1,7 @@
//! UART TX/RX Async Test //! UART TX/RX Async Test
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: generic-queue //% FEATURES: unstable generic-queue
#![no_std] #![no_std]
#![no_main] #![no_main]
@ -18,7 +18,7 @@ struct Context {
} }
#[cfg(test)] #[cfg(test)]
#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] #[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -1,6 +1,7 @@
//! USB Serial JTAG tests //! USB Serial JTAG tests
//% CHIPS: esp32c3 esp32c6 esp32h2 esp32s3 //% CHIPS: esp32c3 esp32c6 esp32h2 esp32s3
//% FEATURES: unstable
#![no_std] #![no_std]
#![no_main] #![no_main]
@ -8,16 +9,13 @@
#[cfg(test)] #[cfg(test)]
#[embedded_test::tests(default_timeout = 3)] #[embedded_test::tests(default_timeout = 3)]
mod tests { mod tests {
use esp_hal::{timer::timg::TimerGroup, usb_serial_jtag::UsbSerialJtag}; use esp_hal::usb_serial_jtag::UsbSerialJtag;
use hil_test as _; use hil_test as _;
#[test] #[test]
fn creating_peripheral_does_not_break_debug_connection() { fn creating_peripheral_does_not_break_debug_connection() {
let peripherals = esp_hal::init(esp_hal::Config::default()); let peripherals = esp_hal::init(esp_hal::Config::default());
let timg0 = TimerGroup::new(peripherals.TIMG0);
esp_hal_embassy::init(timg0.timer0);
_ = UsbSerialJtag::new(peripherals.USB_DEVICE) _ = UsbSerialJtag::new(peripherals.USB_DEVICE)
.into_async() .into_async()
.split(); .split();

View File

@ -17,5 +17,6 @@ log = "0.4.22"
minijinja = "2.5.0" minijinja = "2.5.0"
semver = { version = "1.0.23", features = ["serde"] } semver = { version = "1.0.23", features = ["serde"] }
serde = { version = "1.0.215", features = ["derive"] } serde = { version = "1.0.215", features = ["derive"] }
serde_json = "1.0.70"
strum = { version = "0.26.3", features = ["derive"] } strum = { version = "0.26.3", features = ["derive"] }
toml_edit = "0.22.22" toml_edit = "0.22.22"

View File

@ -2,27 +2,39 @@
use std::{ use std::{
ffi::OsStr, ffi::OsStr,
path::Path, path::{Path, PathBuf},
process::{Command, Stdio}, process::{Command, Stdio},
}; };
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use serde::{Deserialize, Serialize};
use crate::windows_safe_path; use crate::windows_safe_path;
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum CargoAction { pub enum CargoAction {
Build, Build(PathBuf),
Run, Run,
} }
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Artifact {
pub executable: PathBuf,
}
/// Execute cargo with the given arguments and from the specified directory. /// Execute cargo with the given arguments and from the specified directory.
pub fn run(args: &[String], cwd: &Path) -> Result<()> { pub fn run(args: &[String], cwd: &Path) -> Result<()> {
run_with_env::<[(&str, &str); 0], _, _>(args, cwd, []) run_with_env::<[(&str, &str); 0], _, _>(args, cwd, [], false)?;
Ok(())
} }
/// Execute cargo with the given arguments and from the specified directory. /// Execute cargo with the given arguments and from the specified directory.
pub fn run_with_env<I, K, V>(args: &[String], cwd: &Path, envs: I) -> Result<()> pub fn run_and_capture(args: &[String], cwd: &Path) -> Result<String> {
run_with_env::<[(&str, &str); 0], _, _>(args, cwd, [], true)
}
/// Execute cargo with the given arguments and from the specified directory.
pub fn run_with_env<I, K, V>(args: &[String], cwd: &Path, envs: I, capture: bool) -> Result<String>
where where
I: IntoIterator<Item = (K, V)>, I: IntoIterator<Item = (K, V)>,
K: AsRef<OsStr>, K: AsRef<OsStr>,
@ -38,19 +50,26 @@ where
// using now or in future! // using now or in future!
let cwd = windows_safe_path(cwd); let cwd = windows_safe_path(cwd);
let status = Command::new(get_cargo()) let output = Command::new(get_cargo())
.args(args) .args(args)
.current_dir(cwd) .current_dir(cwd)
.envs(envs) .envs(envs)
.stdout(Stdio::inherit()) .stdout(if capture {
.stderr(Stdio::inherit()) Stdio::piped()
.stdin(Stdio::inherit()) } else {
.status()?; Stdio::inherit()
})
.stderr(if capture {
Stdio::piped()
} else {
Stdio::inherit()
})
.output()?;
// Make sure that we return an appropriate exit code here, as Github Actions // Make sure that we return an appropriate exit code here, as Github Actions
// requires this in order to function correctly: // requires this in order to function correctly:
if status.success() { if output.status.success() {
Ok(()) Ok(String::from_utf8_lossy(&output.stdout).to_string())
} else { } else {
bail!("Failed to execute cargo subcommand") bail!("Failed to execute cargo subcommand")
} }
@ -145,16 +164,16 @@ impl CargoArgsBuilder {
} }
#[must_use] #[must_use]
pub fn build(self) -> Vec<String> { pub fn build(&self) -> Vec<String> {
let mut args = vec![]; let mut args = vec![];
if let Some(toolchain) = self.toolchain { if let Some(ref toolchain) = self.toolchain {
args.push(format!("+{toolchain}")); args.push(format!("+{toolchain}"));
} }
args.push(self.subcommand); args.push(self.subcommand.clone());
if let Some(target) = self.target { if let Some(ref target) = self.target {
args.push(format!("--target={target}")); args.push(format!("--target={target}"));
} }
@ -162,8 +181,8 @@ impl CargoArgsBuilder {
args.push(format!("--features={}", self.features.join(","))); args.push(format!("--features={}", self.features.join(",")));
} }
for arg in self.args { for arg in self.args.iter() {
args.push(arg); args.push(arg.clone());
} }
args args

View File

@ -58,6 +58,7 @@ pub enum Package {
pub struct Metadata { pub struct Metadata {
example_path: PathBuf, example_path: PathBuf,
chip: Chip, chip: Chip,
feature_set_name: String,
feature_set: Vec<String>, feature_set: Vec<String>,
tag: Option<String>, tag: Option<String>,
description: Option<String>, description: Option<String>,
@ -67,6 +68,7 @@ impl Metadata {
pub fn new( pub fn new(
example_path: &Path, example_path: &Path,
chip: Chip, chip: Chip,
feature_set_name: String,
feature_set: Vec<String>, feature_set: Vec<String>,
tag: Option<String>, tag: Option<String>,
description: Option<String>, description: Option<String>,
@ -74,6 +76,7 @@ impl Metadata {
Self { Self {
example_path: example_path.to_path_buf(), example_path: example_path.to_path_buf(),
chip, chip,
feature_set_name,
feature_set, feature_set,
tag, tag,
description, description,
@ -161,6 +164,7 @@ pub fn build_documentation(workspace: &Path, package: Package, chip: Chip) -> Re
&args, &args,
&package_path, &package_path,
[("RUSTDOCFLAGS", "--cfg docsrs --cfg not_really_docsrs")], [("RUSTDOCFLAGS", "--cfg docsrs --cfg not_really_docsrs")],
false,
)?; )?;
let docs_path = windows_safe_path( let docs_path = windows_safe_path(
@ -212,7 +216,7 @@ fn apply_feature_rules(package: &Package, config: &Config) -> Vec<String> {
} }
/// Load all examples at the given path, and parse their metadata. /// Load all examples at the given path, and parse their metadata.
pub fn load_examples(path: &Path, action: CargoAction) -> Result<Vec<Metadata>> { pub fn load_examples(path: &Path) -> Result<Vec<Metadata>> {
let mut examples = Vec::new(); let mut examples = Vec::new();
for entry in fs::read_dir(path)? { for entry in fs::read_dir(path)? {
@ -248,7 +252,7 @@ pub fn load_examples(path: &Path, action: CargoAction) -> Result<Vec<Metadata>>
.split_ascii_whitespace() .split_ascii_whitespace()
.map(|s| Chip::from_str(s, false).unwrap()) .map(|s| Chip::from_str(s, false).unwrap())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
} else if key == "FEATURES" { } else if let Some(feature_set_name) = key.strip_prefix("FEATURES") {
// Base feature set required to run the example. // Base feature set required to run the example.
// If multiple are specified, we compile the same example multiple times. // If multiple are specified, we compile the same example multiple times.
let mut values = value let mut values = value
@ -259,7 +263,20 @@ pub fn load_examples(path: &Path, action: CargoAction) -> Result<Vec<Metadata>>
// Sort the features so they are in a deterministic order: // Sort the features so they are in a deterministic order:
values.sort(); values.sort();
feature_sets.push(values); let feature_set_name = feature_set_name.trim_matches(&['(', ')']).to_string();
if feature_sets
.iter()
.any(|(name, _)| name == &feature_set_name)
{
bail!(
"Duplicate feature set name '{}' in {}",
feature_set_name,
path.display()
);
}
feature_sets.push((feature_set_name, values));
} else if key.starts_with("CHIP-FEATURES(") { } else if key.starts_with("CHIP-FEATURES(") {
// Additional features required for specific chips. // Additional features required for specific chips.
// These are appended to the base feature set(s). // These are appended to the base feature set(s).
@ -289,15 +306,10 @@ pub fn load_examples(path: &Path, action: CargoAction) -> Result<Vec<Metadata>>
} }
if feature_sets.is_empty() { if feature_sets.is_empty() {
feature_sets.push(Vec::new()); feature_sets.push((String::new(), Vec::new()));
} }
if action == CargoAction::Build {
// Only build the first feature set for each example. for (feature_set_name, feature_set) in feature_sets {
// Rebuilding with a different feature set just wastes time because the latter
// one will overwrite the former one(s).
feature_sets.truncate(1);
}
for feature_set in feature_sets {
for chip in &chips { for chip in &chips {
let mut feature_set = feature_set.clone(); let mut feature_set = feature_set.clone();
if let Some(chip_features) = chip_features.get(chip) { if let Some(chip_features) = chip_features.get(chip) {
@ -310,6 +322,7 @@ pub fn load_examples(path: &Path, action: CargoAction) -> Result<Vec<Metadata>>
examples.push(Metadata::new( examples.push(Metadata::new(
&path, &path,
*chip, *chip,
feature_set_name.clone(),
feature_set.clone(), feature_set.clone(),
tag.clone(), tag.clone(),
description.clone(), description.clone(),
@ -331,7 +344,7 @@ pub fn execute_app(
target: &str, target: &str,
app: &Metadata, app: &Metadata,
action: CargoAction, action: CargoAction,
mut repeat: usize, repeat: usize,
debug: bool, debug: bool,
) -> Result<()> { ) -> Result<()> {
log::info!( log::info!(
@ -348,53 +361,80 @@ pub fn execute_app(
let package = app.example_path().strip_prefix(package_path)?; let package = app.example_path().strip_prefix(package_path)?;
log::info!("Package: {}", package.display()); log::info!("Package: {}", package.display());
let (bin, subcommand) = if action == CargoAction::Build {
repeat = 1; // Do not repeat builds in a loop let mut builder = CargoArgsBuilder::default()
let bin = if package.starts_with("src/bin") { .target(target)
.features(&features);
let bin_arg = if package.starts_with("src/bin") {
format!("--bin={}", app.name()) format!("--bin={}", app.name())
} else if package.starts_with("tests") { } else if package.starts_with("tests") {
format!("--test={}", app.name()) format!("--test={}", app.name())
} else { } else {
format!("--example={}", app.name()) format!("--example={}", app.name())
}; };
(bin, "build") builder.add_arg(bin_arg);
} else if package.starts_with("src/bin") {
(format!("--bin={}", app.name()), "run")
} else if package.starts_with("tests") {
(format!("--test={}", app.name()), "test")
} else {
(format!("--example={}", app.name()), "run")
};
let mut builder = CargoArgsBuilder::default() let subcommand = if matches!(action, CargoAction::Build(_)) {
.subcommand(subcommand) "build"
.target(target) } else if package.starts_with("tests") {
.features(&features) "test"
.arg(bin); } else {
"run"
};
builder = builder.subcommand(subcommand);
if !debug { if !debug {
builder.add_arg("--release"); builder.add_arg("--release");
} }
if subcommand == "test" && chip == Chip::Esp32c2 {
builder.add_arg("--").add_arg("--speed").add_arg("15000");
}
// If targeting an Xtensa device, we must use the '+esp' toolchain modifier: // If targeting an Xtensa device, we must use the '+esp' toolchain modifier:
if target.starts_with("xtensa") { if target.starts_with("xtensa") {
builder = builder.toolchain("esp"); builder = builder.toolchain("esp");
builder.add_arg("-Zbuild-std=core,alloc"); builder.add_arg("-Zbuild-std=core,alloc");
} }
if subcommand == "test" && chip == Chip::Esp32c2 {
builder.add_arg("--").add_arg("--speed").add_arg("15000");
}
let args = builder.build(); let args = builder.build();
log::debug!("{args:#?}"); log::debug!("{args:#?}");
if let CargoAction::Build(out_dir) = action {
cargo::run(&args, package_path)?;
// Now that the build has succeeded and we printed the output, we can
// rerun the build again quickly enough to capture JSON. We'll use this to
// copy the binary to the output directory.
builder.add_arg("--message-format=json");
let args = builder.build();
let output = cargo::run_and_capture(&args, package_path)?;
for line in output.lines() {
if let Ok(artifact) = serde_json::from_str::<cargo::Artifact>(line) {
let out_dir = out_dir.join(&chip.to_string());
std::fs::create_dir_all(&out_dir)?;
let basename = app.name();
let name = if app.feature_set_name.is_empty() {
basename
} else {
format!("{}_{}", basename, app.feature_set_name)
};
let output_file = out_dir.join(name);
std::fs::copy(artifact.executable, &output_file)?;
log::info!("Output ready: {}", output_file.display());
}
}
} else {
for i in 0..repeat { for i in 0..repeat {
if repeat != 1 { if repeat != 1 {
log::info!("Run {}/{}", i + 1, repeat); log::info!("Run {}/{}", i + 1, repeat);
} }
cargo::run(&args, package_path)?; cargo::run(&args, package_path)?;
} }
}
Ok(()) Ok(())
} }

View File

@ -181,12 +181,20 @@ fn main() -> Result<()> {
let workspace = std::env::current_dir()?; let workspace = std::env::current_dir()?;
let out_path = Path::new("target");
match Cli::parse() { match Cli::parse() {
Cli::BuildDocumentation(args) => build_documentation(&workspace, args), Cli::BuildDocumentation(args) => build_documentation(&workspace, args),
Cli::BuildDocumentationIndex(args) => build_documentation_index(&workspace, args), Cli::BuildDocumentationIndex(args) => build_documentation_index(&workspace, args),
Cli::BuildExamples(args) => examples(&workspace, args, CargoAction::Build), Cli::BuildExamples(args) => examples(
&workspace,
args,
CargoAction::Build(out_path.join("examples")),
),
Cli::BuildPackage(args) => build_package(&workspace, args), Cli::BuildPackage(args) => build_package(&workspace, args),
Cli::BuildTests(args) => tests(&workspace, args, CargoAction::Build), Cli::BuildTests(args) => {
tests(&workspace, args, CargoAction::Build(out_path.join("tests")))
}
Cli::BumpVersion(args) => bump_version(&workspace, args), Cli::BumpVersion(args) => bump_version(&workspace, args),
Cli::FmtPackages(args) => fmt_packages(&workspace, args), Cli::FmtPackages(args) => fmt_packages(&workspace, args),
Cli::GenerateEfuseFields(args) => generate_efuse_src(&workspace, args), Cli::GenerateEfuseFields(args) => generate_efuse_src(&workspace, args),
@ -227,7 +235,7 @@ fn examples(workspace: &Path, mut args: ExampleArgs, action: CargoAction) -> Res
}; };
// Load all examples which support the specified chip and parse their metadata: // Load all examples which support the specified chip and parse their metadata:
let mut examples = xtask::load_examples(&example_path, action)? let mut examples = xtask::load_examples(&example_path)?
.iter() .iter()
.filter_map(|example| { .filter_map(|example| {
if example.supports_chip(args.chip) { if example.supports_chip(args.chip) {
@ -243,13 +251,18 @@ fn examples(workspace: &Path, mut args: ExampleArgs, action: CargoAction) -> Res
// Execute the specified action: // Execute the specified action:
match action { match action {
CargoAction::Build => build_examples(args, examples, &package_path), CargoAction::Build(out_path) => build_examples(args, examples, &package_path, out_path),
CargoAction::Run if args.example.is_some() => 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), CargoAction::Run => run_examples(args, examples, &package_path),
} }
} }
fn build_examples(args: ExampleArgs, examples: Vec<Metadata>, package_path: &Path) -> Result<()> { fn build_examples(
args: ExampleArgs,
examples: Vec<Metadata>,
package_path: &Path,
out_path: PathBuf,
) -> Result<()> {
// Determine the appropriate build target for the given package and chip: // Determine the appropriate build target for the given package and chip:
let target = target_triple(args.package, &args.chip)?; let target = target_triple(args.package, &args.chip)?;
@ -265,7 +278,7 @@ fn build_examples(args: ExampleArgs, examples: Vec<Metadata>, package_path: &Pat
args.chip, args.chip,
target, target,
example, example,
CargoAction::Build, CargoAction::Build(out_path.clone()),
1, 1,
args.debug, args.debug,
)?; )?;
@ -282,7 +295,7 @@ fn build_examples(args: ExampleArgs, examples: Vec<Metadata>, package_path: &Pat
args.chip, args.chip,
target, target,
example, example,
CargoAction::Build, CargoAction::Build(out_path.clone()),
1, 1,
args.debug, args.debug,
) )
@ -400,7 +413,7 @@ fn tests(workspace: &Path, args: TestArgs, action: CargoAction) -> Result<()> {
let target = target_triple(Package::HilTest, &args.chip)?; let target = target_triple(Package::HilTest, &args.chip)?;
// Load all tests which support the specified chip and parse their metadata: // Load all tests which support the specified chip and parse their metadata:
let mut tests = xtask::load_examples(&package_path.join("tests"), action)? let mut tests = xtask::load_examples(&package_path.join("tests"))?
.into_iter() .into_iter()
.filter(|example| example.supports_chip(args.chip)) .filter(|example| example.supports_chip(args.chip))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -420,7 +433,7 @@ fn tests(workspace: &Path, args: TestArgs, action: CargoAction) -> Result<()> {
args.chip, args.chip,
target, target,
test, test,
action, action.clone(),
args.repeat.unwrap_or(1), args.repeat.unwrap_or(1),
false, false,
)?; )?;
@ -436,7 +449,7 @@ fn tests(workspace: &Path, args: TestArgs, action: CargoAction) -> Result<()> {
args.chip, args.chip,
target, target,
&test, &test,
action, action.clone(),
args.repeat.unwrap_or(1), args.repeat.unwrap_or(1),
false, false,
) )