use std::process::Command;
fn packaged_files(package: &str) -> Vec<String> {
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
let output = Command::new(cargo)
.args([
"package",
"--list",
"--quiet",
"--allow-dirty",
"-p",
package,
])
.current_dir(env!("CARGO_MANIFEST_DIR"))
.output()
.expect("run `cargo package --list`");
assert!(
output.status.success(),
"`cargo package --list` failed:\n{}",
String::from_utf8_lossy(&output.stderr)
);
String::from_utf8(output.stdout)
.expect("utf8 file list")
.lines()
.map(str::trim)
.filter(|l| !l.is_empty())
.map(str::to_owned)
.collect()
}
#[test]
fn public_docs_are_tag_pinned_and_excluded_from_the_package() {
let files = packaged_files("tuika");
let bundled_docs: Vec<&String> = files.iter().filter(|f| f.starts_with("docs/")).collect();
assert!(
bundled_docs.is_empty(),
"public docs must stay in the tagged repository, not the crate: {bundled_docs:?}"
);
let readme = std::fs::read_to_string("README.md").expect("read root README");
let version = env!("CARGO_PKG_VERSION");
let expected_prefix = format!("https://github.com/everruns/tuika/blob/v{version}/docs/");
let doc_links: Vec<&str> = readme
.split("](")
.skip(1)
.filter_map(|rest| rest.split(')').next())
.filter(|destination| destination.contains("docs/"))
.collect();
assert!(!doc_links.is_empty(), "README must link to the public docs");
assert!(
doc_links
.iter()
.all(|destination| destination.starts_with(&expected_prefix)),
"README doc links must be pinned to v{version}: {doc_links:?}"
);
}
#[test]
fn repository_machinery_is_excluded_from_the_package() {
let files = packaged_files("tuika");
const INTERNAL_PREFIXES: [&str; 5] =
[".agents/", ".claude/", ".github/", "knowledge/", "scripts/"];
const INTERNAL_FILES: [&str; 6] = [
"AGENTS.md",
"CLAUDE.md",
"CODE_OF_CONDUCT.md",
"CONTRIBUTING.md",
"SECURITY.md",
"rust-toolchain.toml",
];
let leaked: Vec<&String> = files
.iter()
.filter(|f| {
INTERNAL_PREFIXES.iter().any(|p| f.starts_with(p))
|| INTERNAL_FILES.iter().any(|n| f == n)
})
.collect();
assert!(
leaked.is_empty(),
"repository-only files must not ship in the crate (see Cargo.toml `exclude`): {leaked:?}"
);
let nested: Vec<&String> = files.iter().filter(|f| f.starts_with("crates/")).collect();
assert!(
nested.is_empty(),
"nested workspace packages must not ship inside tuika's crate: {nested:?}"
);
}
#[test]
fn generated_site_is_excluded_from_the_package() {
let files = packaged_files("tuika");
let leaked: Vec<&String> = files.iter().filter(|f| f.starts_with("site/")).collect();
assert!(
leaked.is_empty(),
"the generated documentation site must not ship in the crate: {leaked:?}"
);
}
#[test]
fn benchmark_results_are_excluded_from_every_package() {
for package in [
"tuika",
"tuika-charts",
"tuika-codeformatters",
"tuika-html",
"tuika-mermaid",
] {
let files = packaged_files(package);
let leaked: Vec<&String> = files
.iter()
.filter(|path| {
path.starts_with("target/")
|| (path.starts_with("benches/") && path.ends_with(".json"))
})
.collect();
assert!(
leaked.is_empty(),
"benchmark results must not ship in {package}: {leaked:?}"
);
}
}
#[test]
fn root_readme_assets_are_tag_pinned_and_excluded() {
let files = packaged_files("tuika");
let has = |p: &str| files.iter().any(|f| f == p);
let readme = std::fs::read_to_string("README.md").expect("read root README");
let version = env!("CARGO_PKG_VERSION");
for asset in ["logo.svg", "docs/hero.gif", "docs/demos/image.svg"] {
let url = format!("https://raw.githubusercontent.com/everruns/tuika/v{version}/{asset}");
assert!(
readme.contains(&url),
"README must pin {asset} to v{version}"
);
}
assert!(
!readme.contains("docs/split-footer.gif"),
"split-footer recording belongs in the focused guide, not the root README"
);
for asset in [
"logo.svg",
"docs/hero.gif",
"docs/demos/image.svg",
"docs/split-footer.gif",
] {
assert!(!has(asset), "absolute README asset must not ship: {asset}");
}
assert!(has("src/lib.rs"), "library source must ship");
assert!(has("Cargo.toml"), "manifest must ship");
assert!(has("README.md"), "README must ship");
}
#[test]
fn alternate_logo_assets_are_repository_only() {
let files = packaged_files("tuika");
for asset in [
"logo-dark.svg",
"logo-mono.svg",
"logo.png",
"logo-dark.png",
"logo-mono.png",
] {
assert!(
!files.iter().any(|f| f == asset),
"non-README logo asset must not ship: {asset}"
);
}
}
#[test]
fn codeformatters_ships_source_but_not_its_demo_recording() {
let files = packaged_files("tuika-codeformatters");
let gifs: Vec<&String> = files.iter().filter(|f| f.ends_with(".gif")).collect();
assert!(
gifs.is_empty(),
"demo recordings must not ship in tuika-codeformatters (see its Cargo.toml `exclude`): {gifs:?}"
);
let has = |p: &str| files.iter().any(|f| f == p);
assert!(has("src/lib.rs"), "library source must ship");
assert!(
has("examples/highlight_file.rs"),
"the file-viewer example documented in the README must ship"
);
assert!(has("Cargo.toml"), "manifest must ship");
assert!(has("README.md"), "README must ship");
}
#[test]
fn html_keeps_the_demo_its_readme_embeds() {
let files = packaged_files("tuika-html");
let has = |p: &str| files.iter().any(|f| f == p);
for asset in [
"examples/html_markdown/html.png",
"examples/html_view/html_view.png",
] {
assert!(
has(asset),
"the demos the crates.io README embeds by relative path must ship: {asset}"
);
}
assert!(has("src/lib.rs"), "library source must ship");
assert!(has("Cargo.toml"), "manifest must ship");
assert!(has("README.md"), "README must ship");
}
#[test]
fn mermaid_keeps_the_recording_its_readme_embeds() {
let files = packaged_files("tuika-mermaid");
let has = |p: &str| files.iter().any(|f| f == p);
assert!(
has("examples/mermaid_markdown/mermaid.gif"),
"the recording the crates.io README embeds by relative path must ship"
);
assert!(has("src/lib.rs"), "library source must ship");
assert!(has("Cargo.toml"), "manifest must ship");
assert!(has("README.md"), "README must ship");
}
#[test]
fn charts_ships_source_and_readme_without_repository_demo() {
let files = packaged_files("tuika-charts");
let has = |p: &str| files.iter().any(|f| f == p);
assert!(has("src/lib.rs"), "library source must ship");
assert!(has("examples/charts.rs"), "runnable example must ship");
assert!(
!files.iter().any(|path| path.starts_with("docs/charts/")),
"repository-hosted chart demos must not ship"
);
assert!(has("Cargo.toml"), "manifest must ship");
assert!(has("README.md"), "README must ship");
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
for kind in ["line", "area", "bar", "scatter", "step"] {
for renderer in ["cells", "graphics"] {
let path = root.join(format!("docs/charts/{kind}-{renderer}.png"));
let metadata = path
.metadata()
.unwrap_or_else(|_| panic!("missing generated chart demo: {}", path.display()));
assert!(
metadata.len() > 0,
"generated chart demo is empty: {}",
path.display()
);
}
}
}