Skip to content

Commit

Permalink
refactor: use hilprobe library
Browse files Browse the repository at this point in the history
Using probe-rs CLI makes test library simpler. We don't need
fancy control over the probe.
  • Loading branch information
lulf committed Jan 16, 2025
1 parent 5738850 commit 0364234
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 589 deletions.
20 changes: 10 additions & 10 deletions examples/tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,22 @@ tokio = { version = "1", default-features = false, features = [
"time",
"rt-multi-thread",
"macros",
"process"
] }
pretty_env_logger = "0.5.0"
reqwest = "0.12"
hilbench-agent = "0.1.0"
embedded-io-adapters = { version = "0.6.1", features = ["tokio-1"] }
embedded-io-async = { version = "0.6.1" }
embedded-io = { version = "0.6.1" }
embassy-sync = { version = "0.6" }
tokio-serial = "5.4"
critical-section = { version = "1", features = ["std"] }
probe-rs = "0.25.0"
embassy-sync = "0.6"
tokio-serial = "5.4"
tokio-util = "0.7"
rand = "0.8.5"
heapless = "0.8.0"
anyhow = "1"
object = "0.32.2"
defmt-decoder = { version = "0.3.9", features = ["unstable"] }
bytes = "1.5.0"
backtrace = "0.3.69"
pretty_env_logger = "0.5.0"
pin-project-lite = "0.2.13"
chrono = { version = "0.4.31", features = ["serde"] }
tempfile = "3.15"

[patch.crates-io]
hilbench-agent = { git = "https://github.com/lulf/hilbench.git", rev = "700693cec2f813967f6717341296828d4c2971ae" }
31 changes: 31 additions & 0 deletions examples/tests/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,33 @@
use anyhow::anyhow;
use hilbench_agent::ProbeConfig;
use probe::DeviceUnderTest;

pub mod probe;
pub mod serial;

pub struct TestContext {
pub serial_adapters: Vec<String>,
pub probe_config: ProbeConfig,
}

impl TestContext {
pub fn new() -> Self {
let serial_adapters = serial::find_controllers();
let config = std::env::var("PROBE_CONFIG").unwrap();
log::info!("Using probe config {}", config);
let probe_config = serde_json::from_str(&config).unwrap();

Self {
serial_adapters,
probe_config,
}
}

pub fn find_dut(&self, labels: &[(&str, &str)]) -> Result<DeviceUnderTest<'static>, anyhow::Error> {
let selector = hilbench_agent::init(self.probe_config.clone());
let target = selector
.select(labels)
.ok_or(anyhow!("Unable to find DUT for {:?}", labels))?;
Ok(DeviceUnderTest::new(target))
}
}
184 changes: 64 additions & 120 deletions examples/tests/src/probe/mod.rs
Original file line number Diff line number Diff line change
@@ -1,136 +1,80 @@
use probe_rs::flashing::Format;
use probe_rs::probe::list::Lister;
use probe_rs::probe::DebugProbeSelector;
use probe_rs::{Permissions, Session};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::OnceLock;
use tokio::sync::oneshot;
use tokio::task::spawn_blocking;

mod run;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ProbeConfig {
pub targets: Vec<TargetConfig>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct TargetConfig {
pub chip: String,
pub probe: String,
pub labels: HashMap<String, String>,
}

static SELECTOR: OnceLock<ProbeSelector> = OnceLock::new();

pub fn init(config: ProbeConfig) -> &'static ProbeSelector {
SELECTOR.get_or_init(|| ProbeSelector::new(config))
}

pub struct ProbeSelector {
targets: Vec<(AtomicBool, TargetConfig)>,
}

#[derive(Debug)]
pub struct Target<'d> {
config: TargetConfig,
taken: &'d AtomicBool,
use std::io::Write;
use std::process::Stdio;
use tokio::io::AsyncBufReadExt;
use tokio::io::BufReader;
use tokio::process::Command;
use tokio::select;
use tokio_util::sync::CancellationToken;

use hilbench_agent::ProbeConfig;
use hilbench_agent::Target;

pub fn init(config: ProbeConfig) {
hilbench_agent::init(config);
}

pub struct Firmware {
pub data: Vec<u8>,
pub format: Format,
}

impl ProbeSelector {
fn new(config: ProbeConfig) -> Self {
let mut targets = Vec::new();
for t in config.targets {
targets.push((AtomicBool::new(false), t));
}
Self { targets }
}

/// Select a target with the provided labels
pub fn select<'m>(&'m self, labels: &[(&str, &str)]) -> Option<Target<'m>> {
for (taken, config) in &self.targets {
let mut matched = true;
for (key, value) in labels {
let v = config.labels.get(*key);
if let Some(v) = v {
if v != value {
matched = false;
break;
}
}
}
if matched && taken.swap(true, Ordering::Acquire) == false {
return Some(Target {
config: config.clone(),
taken,
});
}
}
None
}
pub struct DeviceUnderTest<'d> {
target: Target<'d>,
token: CancellationToken,
}

impl<'d> Target<'d> {
pub fn flash(self, fw: Firmware) -> Result<TargetRunner<'d>, anyhow::Error> {
let probe = self.config.probe.clone();
let p: DebugProbeSelector = probe.try_into()?;
log::info!("Debug probe selector created");
let t = probe_rs::config::get_target_by_name(&self.config.chip)?;
log::info!("Target created");

let lister = Lister::new();
log::info!("Opening probe");
let probe = lister.open(p)?;

let perms = Permissions::new().allow_erase_all();
log::info!("Attaching probe");
let mut session = probe.attach(t, perms)?;
let mut flasher = run::Flasher::new(fw);
flasher.flash(&mut session)?;
Ok(TargetRunner {
_target: self,
flasher,
session,
})
impl<'d> DeviceUnderTest<'d> {
pub(crate) fn new(target: Target<'d>) -> Self {
Self {
target,
token: CancellationToken::new(),
}
}
}

impl<'d> Drop for Target<'d> {
fn drop(&mut self) {
self.taken.store(false, Ordering::Release);
pub fn token(&self) -> CancellationToken {
self.token.clone()
}
}

pub struct TargetRunner<'d> {
_target: Target<'d>,
flasher: run::Flasher,
session: Session,
}

impl<'d> TargetRunner<'d> {
pub async fn run(mut self, cancel: oneshot::Receiver<()>) -> Result<(), anyhow::Error> {
let result = spawn_blocking(move || {
let mut runner = self.flasher.start(&mut self.session).unwrap();
runner.run(&mut self.session, cancel)
})
.await
.unwrap();
match result {
Ok(halted) => {
if halted {
Err(anyhow::anyhow!("Firmware stopped"))
} else {
Ok(())
pub async fn run(self, fw: Firmware) -> Result<FirmwareLogs, anyhow::Error> {
let mut temp = tempfile::NamedTempFile::new()?;
temp.write_all(&fw.data)?;
let path = temp.path().to_str().unwrap().to_string();
drop(temp);
let mut flasher = Command::new("probe-rs")
.env("RUST_LOG", "info")
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.arg("run")
.arg("--elf")
.arg(&path)
.arg("--chip")
.arg(&self.target.config().chip)
.arg("--probe")
.arg(&self.target.config().probe)
.spawn()
.unwrap();

let stderr = flasher.stderr.as_mut().unwrap();
let mut stderr_reader = BufReader::new(stderr);

let mut lines: Vec<String> = Vec::new();
select! {
_ = self.token.cancelled() => {
flasher.kill().await.unwrap();
}
_ = async {
loop {
let mut line = String::new();
stderr_reader.read_line(&mut line).await.unwrap();
lines.push(line);
}
} => {

}
Err(e) => Err(e.into()),
}
flasher.wait().await.unwrap();
Ok(FirmwareLogs { lines })
}
}

pub struct FirmwareLogs {
pub lines: Vec<String>,
}
Loading

0 comments on commit 0364234

Please sign in to comment.