diff --git a/README.md b/README.md index 0867af4..850328f 100644 --- a/README.md +++ b/README.md @@ -44,10 +44,15 @@ The tests: `closed.txt`; - `released_apps_run`: every app each release built still starts and does what its script says. -`NSH_COMPAT_APP=` runs one app, `NSH_COMPAT_DUMP=1` prints the texts each step of a script adds and removes, +`NSH_COMPAT_APP=` selects apps whose unqualified name starts with ``; `desktop` selects both its case +and example. `NSH_COMPAT_DUMP=1` prints the texts each step of a script adds and removes, `NSH_COMPAT_KEEP_GOING=1` goes on after a failed step at the script's next case (its next `tap id:case-…`) and reports the first failure of each case at the end, so one run shows every case that fails. +Cases and examples build concurrently into separate `case-` and `example-` directories. Each owns +its `.build` staging directory, so equally named apps cannot overwrite each other's bundle, metadata or script. +Each checkout run replaces its generated output directory before building; frozen release records are unchanged. + Material assertions use `prop macos.backdrop material=hudWindow blending=behindWindow` and `style glassTintColor #ff000080`. Backdrop members at their defaults are omitted; an explicitly requested default material is `{}`, while a removed material has no `backdrop` key. diff --git a/src/lib.rs b/src/lib.rs index b099e84..e763d3f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -207,10 +207,19 @@ fn record_breaks( /// example (`examples//`, its script `tests/compat/examples/.txt`). struct CheckoutApp { name: String, + example: bool, source: PathBuf, script: PathBuf, } +impl CheckoutApp { + /// Cases and examples can share a logical name, but never build files. + fn build_name(&self) -> String { + let kind = if self.example { "example" } else { "case" }; + format!("{kind}-{}", self.name) + } +} + /// The checkout's apps (`NSH_COMPAT_APP=`: those whose name starts /// so). fn checkout_apps() -> Vec { @@ -228,6 +237,7 @@ fn checkout_apps() -> Vec { if dir.join("script.txt").is_file() { out.push(CheckoutApp { name: release_name(&dir), + example: false, script: dir.join("script.txt"), source: dir, }); @@ -242,6 +252,7 @@ fn checkout_apps() -> Vec { }; out.push(CheckoutApp { name: name.to_owned(), + example: true, source: nsh.join("examples").join(name), script, }); @@ -272,8 +283,7 @@ fn build(app: &CheckoutApp, out: &Path) -> Result<(), String> { nsh_dir().display() )); } - let staging = out.with_extension("build"); - let _ = std::fs::remove_dir_all(&staging); + let staging = out.join(".build"); let _ = std::fs::remove_dir_all(out); let built = Command::new(&bin) .args(["build", "--release", "-o"]) @@ -311,8 +321,8 @@ fn copy_dir(from: &Path, to: &Path) -> Result<(), String> { Ok(()) } -/// Builds the checkout's apps into `root//`, side by side; the -/// directories built, or what failed. +/// Builds cases and examples into separate `root/case-/` and +/// `root/example-/` directories; the directories built, or what failed. fn build_checkout_apps(root: &Path) -> Result, String> { let apps = checkout_apps(); let results: Vec<_> = std::thread::scope(|s| { @@ -320,10 +330,11 @@ fn build_checkout_apps(root: &Path) -> Result, String> { .iter() .map(|app| { s.spawn(move || { - let dir = root.join(&app.name); + let name = app.build_name(); + let dir = root.join(&name); build(app, &dir) - .map(|()| (app.name.clone(), dir)) - .map_err(|e| format!("{}: {e}", app.name)) + .map_err(|e| format!("{name}: {e}")) + .map(|()| (name, dir)) }) }) .collect(); @@ -389,6 +400,9 @@ pub fn record(version: &str) -> Result { /// and their `covers` claims hold. pub fn checkout_apps_run() -> Result<(), String> { let root = work_dir().join("checkout"); + if root.exists() { + std::fs::remove_dir_all(&root).map_err(|e| e.to_string())?; + } let built = build_checkout_apps(&root)?; let failed = run_apps(&built); if !failed.is_empty() { @@ -465,6 +479,22 @@ pub fn any_release() -> bool { mod tests { use super::*; + #[test] + fn case_and_example_build_names_do_not_collide() { + let mut names = std::collections::HashSet::new(); + for name in ["desktop", "example-desktop", "desktop.build"] { + for example in [false, true] { + let app = CheckoutApp { + name: name.into(), + example, + source: PathBuf::new(), + script: PathBuf::new(), + }; + assert!(names.insert(app.build_name())); + } + } + } + #[test] fn checkout_apps_run() { super::checkout_apps_run().unwrap_or_else(|e| panic!("{e}"));