Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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=<name>` runs one app, `NSH_COMPAT_DUMP=1` prints the texts each step of a script adds and removes,
`NSH_COMPAT_APP=<name>` selects apps whose unqualified name starts with `<name>`; `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-<name>` and `example-<name>` 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 <selector> macos.backdrop material=hudWindow blending=behindWindow`
and `style <selector> glassTintColor #ff000080`. Backdrop members at their defaults are omitted;
an explicitly requested default material is `{}`, while a removed material has no `backdrop` key.
Expand Down
44 changes: 37 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,19 @@ fn record_breaks(
/// example (`examples/<app>/`, its script `tests/compat/examples/<app>.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=<name>`: those whose name starts
/// so).
fn checkout_apps() -> Vec<CheckoutApp> {
Expand All @@ -228,6 +237,7 @@ fn checkout_apps() -> Vec<CheckoutApp> {
if dir.join("script.txt").is_file() {
out.push(CheckoutApp {
name: release_name(&dir),
example: false,
script: dir.join("script.txt"),
source: dir,
});
Expand All @@ -242,6 +252,7 @@ fn checkout_apps() -> Vec<CheckoutApp> {
};
out.push(CheckoutApp {
name: name.to_owned(),
example: true,
source: nsh.join("examples").join(name),
script,
});
Expand Down Expand Up @@ -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"])
Expand Down Expand Up @@ -311,19 +321,20 @@ fn copy_dir(from: &Path, to: &Path) -> Result<(), String> {
Ok(())
}

/// Builds the checkout's apps into `root/<app>/`, side by side; the
/// directories built, or what failed.
/// Builds cases and examples into separate `root/case-<app>/` and
/// `root/example-<app>/` directories; the directories built, or what failed.
fn build_checkout_apps(root: &Path) -> Result<Vec<(String, PathBuf)>, String> {
let apps = checkout_apps();
let results: Vec<_> = std::thread::scope(|s| {
let runs: Vec<_> = apps
.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();
Expand Down Expand Up @@ -389,6 +400,9 @@ pub fn record(version: &str) -> Result<PathBuf, String> {
/// 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() {
Expand Down Expand Up @@ -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}"));
Expand Down
Loading