lampbench station / export / label / tm30 / calibrate replace the Python lamp_station.py, lamp_export.py, label_printer.py, tm30.py and calibration.py that lived in HPCS6500-py. The driver is a submodule (HPCS6500/, like mppt-testbench). Workspace crates: tm30 (TM-30-18 port of colour-science 0.4.7), brother-ql (raster protocol, byte-identical to brother_ql) and lamp-label (QR + ID labels for the QL-820NWB). Bundles are byte-identical to lamp_export.py's (golden tests); the station runs end to end in a test against the simulated instrument.
204 lines
5.6 KiB
Rust
204 lines
5.6 KiB
Rust
//! The station end to end: scripted answers on stdin, the simulated
|
|
//! instrument, a throwaway comparison-data repository.
|
|
|
|
use std::io::Write;
|
|
use std::path::Path;
|
|
use std::process::{Command, Stdio};
|
|
|
|
fn git(dir: &Path, args: &[&str]) -> String {
|
|
let o = Command::new("git")
|
|
.arg("-C")
|
|
.arg(dir)
|
|
.args(args)
|
|
.output()
|
|
.unwrap();
|
|
assert!(
|
|
o.status.success(),
|
|
"git {args:?}: {}",
|
|
String::from_utf8_lossy(&o.stderr)
|
|
);
|
|
String::from_utf8_lossy(&o.stdout).to_string()
|
|
}
|
|
|
|
/// A valid EAN-13 with the given 12 leading digits.
|
|
fn ean(d12: &str) -> String {
|
|
let sum: u32 = d12
|
|
.chars()
|
|
.enumerate()
|
|
.map(|(i, c)| c.to_digit(10).unwrap() * if i % 2 == 1 { 3 } else { 1 })
|
|
.sum();
|
|
format!("{d12}{}", (10 - sum % 10) % 10)
|
|
}
|
|
|
|
#[test]
|
|
fn station_measures_commits_and_re_measures() {
|
|
let tmp = std::env::temp_dir().join(format!("lampbench-station-{}", std::process::id()));
|
|
let _ = std::fs::remove_dir_all(&tmp);
|
|
let repo = tmp.join("comparison-data");
|
|
std::fs::create_dir_all(repo.join("lamps")).unwrap();
|
|
git(&repo, &["init", "-q"]);
|
|
git(&repo, &["config", "user.name", "Test"]);
|
|
git(&repo, &["config", "user.email", "test@example.com"]);
|
|
std::fs::write(repo.join("README.md"), "data\n").unwrap();
|
|
git(&repo, &["add", "."]);
|
|
git(&repo, &["commit", "-qm", "init"]);
|
|
|
|
// --interval 0.5 keeps the runs in different seconds, so the re-measure changes measured_at
|
|
let internal = ean("200123456789");
|
|
let answers = [
|
|
// lamp 1: no barcode
|
|
"",
|
|
"philips", // manufacturer -> canonical "Philips"
|
|
"Test A60", // model
|
|
"", // type: led
|
|
"y", // dimmable
|
|
"Warm White", // variant -> lower case
|
|
"", // notes
|
|
"806",
|
|
"2700",
|
|
"8.5",
|
|
"",
|
|
"",
|
|
"60", // rated: flux, CCT, W, Ra, lifetime, equivalent
|
|
"", // insert the lamp
|
|
// lamp 2: retailer-internal EAN (no online lookup)
|
|
&internal,
|
|
"",
|
|
"Shop bulb",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
// re-measure lamp 1, everything pre-filled
|
|
"L0001",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
// a bad barcode, then quit
|
|
"12345",
|
|
"q",
|
|
]
|
|
.join("\n")
|
|
+ "\n";
|
|
|
|
let mut child = Command::new(env!("CARGO_BIN_EXE_lampbench"))
|
|
.args([
|
|
"station",
|
|
"--simulate",
|
|
"--settle",
|
|
"0",
|
|
"--readings",
|
|
"2",
|
|
"--interval",
|
|
"0.5",
|
|
"--no-push",
|
|
"--no-labels",
|
|
"--no-lookup",
|
|
])
|
|
.arg("--data-repo")
|
|
.arg(&repo)
|
|
.arg("--cache")
|
|
.arg(tmp.join("ean-cache.json"))
|
|
.env_remove("HPCS_LABEL_PRINTER")
|
|
.stdin(Stdio::piped())
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
child
|
|
.stdin
|
|
.take()
|
|
.unwrap()
|
|
.write_all(answers.as_bytes())
|
|
.unwrap();
|
|
let out = child.wait_with_output().unwrap();
|
|
let text = String::from_utf8_lossy(&out.stdout);
|
|
assert!(
|
|
out.status.success(),
|
|
"{text}\n{}",
|
|
String::from_utf8_lossy(&out.stderr)
|
|
);
|
|
assert!(text.contains("--- New lamp L0001 ---"), "{text}");
|
|
assert!(
|
|
text.contains("Using canonical brand name: Philips") || text.contains("Manufacturer"),
|
|
"{text}"
|
|
);
|
|
assert!(
|
|
text.contains("Lookup: ? / ? (retailer-internal barcode, not resolvable)"),
|
|
"{text}"
|
|
);
|
|
assert!(text.contains("Re-measuring L0001"), "{text}");
|
|
assert!(text.contains("Not a valid EAN-13/UPC-A: '12345'"), "{text}");
|
|
assert!(text.contains("PSU off"), "{text}");
|
|
assert!(text.trim_end().ends_with("Bye."), "{text}");
|
|
|
|
let m: serde_json::Value = serde_json::from_str(
|
|
&std::fs::read_to_string(repo.join("lamps/L0001/metrics.json")).unwrap(),
|
|
)
|
|
.unwrap();
|
|
assert_eq!(m["manufacturer"], "Philips");
|
|
assert_eq!(m["variant"], "warm white");
|
|
assert_eq!(m["dimmable"], true);
|
|
assert_eq!(m["type"], "led");
|
|
assert_eq!(
|
|
m["rated"],
|
|
serde_json::json!({"Phi_lm": 806, "CCT_K": 2700, "Power_W": 8.5, "W_equiv": 60})
|
|
);
|
|
assert_eq!(m["readings_averaged"], 2);
|
|
assert_eq!(m["supply"]["ac_voltage"], 230.0);
|
|
let m2: serde_json::Value = serde_json::from_str(
|
|
&std::fs::read_to_string(repo.join("lamps/L0002/metrics.json")).unwrap(),
|
|
)
|
|
.unwrap();
|
|
assert_eq!(m2["ean"], internal.as_str());
|
|
assert_eq!(m2["notes"], "retailer-internal barcode, brand unknown");
|
|
assert_eq!(
|
|
std::fs::read_to_string(repo.join("lamps/L0001/spd.csv"))
|
|
.unwrap()
|
|
.lines()
|
|
.count(),
|
|
351
|
|
);
|
|
assert_eq!(
|
|
std::fs::read_to_string(repo.join("lamps/L0001/tm30.csv"))
|
|
.unwrap()
|
|
.lines()
|
|
.count(),
|
|
17
|
|
);
|
|
|
|
let log = git(&repo, &["log", "--format=%s"]);
|
|
let subjects: Vec<&str> = log.lines().collect();
|
|
assert_eq!(
|
|
subjects[0], "Re-measure lamp L0001 (Philips, Test A60, warm white)",
|
|
"{log}"
|
|
);
|
|
assert_eq!(
|
|
subjects[1],
|
|
format!("Add lamp L0002 (Shop bulb, EAN {internal})"),
|
|
"{log}"
|
|
);
|
|
assert_eq!(
|
|
subjects[2], "Add lamp L0001 (Philips, Test A60, warm white)",
|
|
"{log}"
|
|
);
|
|
let _ = std::fs::remove_dir_all(&tmp);
|
|
}
|