use std::path::PathBuf;
const CARGO_TOML: &str = include_str!("../Cargo.toml");
const CARGO_CONFIG: &str = include_str!("../.cargo/config.toml");
const EXAMPLES_README: &str = include_str!("../examples/README.md");
const GATE: &str = "examples";
fn repo_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}
fn table<'a>(toml: &'a str, header: &str) -> Option<&'a str> {
let start = toml.find(&format!("\n{header}\n"))? + 1 + header.len() + 1;
let rest = &toml[start..];
let end = rest
.match_indices("\n[")
.next()
.map(|(i, _)| i + 1)
.unwrap_or(rest.len());
Some(&rest[..end])
}
fn value<'a>(body: &'a str, key: &str) -> Option<&'a str> {
body.lines().find_map(|line| {
let line = line.trim();
let rest = line.strip_prefix(key)?.trim_start();
let rest = rest.strip_prefix('=')?.trim();
Some(rest.split('#').next().unwrap_or(rest).trim())
})
}
struct Example {
name: String,
path: String,
required_features: Option<String>,
}
fn declared_examples() -> Vec<Example> {
let mut examples = Vec::new();
let mut lines = CARGO_TOML.lines().peekable();
while let Some(line) = lines.next() {
if line.trim() != "[[example]]" {
continue;
}
let mut body = String::new();
while let Some(next) = lines.peek() {
if next.trim_start().starts_with('[') {
break;
}
body.push_str(lines.next().expect("peeked"));
body.push('\n');
}
examples.push(Example {
name: value(&body, "name")
.unwrap_or_else(|| panic!("an [[example]] declares no name:\n{body}"))
.trim_matches('"')
.to_string(),
path: value(&body, "path")
.unwrap_or_else(|| panic!("an [[example]] declares no path:\n{body}"))
.trim_matches('"')
.to_string(),
required_features: value(&body, "required-features").map(str::to_string),
});
}
assert!(
!examples.is_empty(),
"no [[example]] blocks parsed out of Cargo.toml at all — the parser has \
stopped matching, not the manifest stopped declaring"
);
examples
}
fn autodiscoverable_paths() -> Vec<String> {
let dir = repo_root().join("examples");
let mut found = Vec::new();
for entry in std::fs::read_dir(&dir).expect("examples/ is not readable") {
let entry = entry.expect("examples/ entry is not readable");
let file_type = entry.file_type().expect("examples/ entry has no file type");
let name = entry.file_name().to_string_lossy().into_owned();
if file_type.is_file() && name.ends_with(".rs") {
found.push(format!("examples/{name}"));
} else if file_type.is_dir() && entry.path().join("main.rs").is_file() {
found.push(format!("examples/{name}/main.rs"));
}
}
assert!(
!found.is_empty(),
"no example sources found under examples/ — the scan is broken, since \
the manifest declares some"
);
found.sort();
found
}
#[test]
fn dev_builds_carry_line_tables_only() {
let dev = table(CARGO_TOML, "[profile.dev]").expect("Cargo.toml has no [profile.dev] table");
let debug = value(dev, "debug").expect("[profile.dev] sets no debug level");
assert_eq!(
debug, "\"line-tables-only\"",
"[profile.dev] debug is `{debug}`. Full debug info for this crate and \
for gpui beneath it is the bulk of what `ld` holds, and several links \
run at once under cargo's default -j; raising this is what got them \
OOM-killed. A debugging session asks for the detail on that build \
alone with RUSTFLAGS=\"-Cdebuginfo=2\"."
);
}
#[test]
fn the_wildcard_package_override_does_not_restore_debug_info() {
let over = table(CARGO_TOML, "[profile.dev.package.\"*\"]").expect(
"Cargo.toml has no [profile.dev.package.\"*\"] table — if it was removed on \
purpose, remove this test with it",
);
match value(over, "debug") {
None => {}
Some("0") | Some("false") | Some("\"none\"") => {}
Some(other) => panic!(
"[profile.dev.package.\"*\"] sets debug = {other}, which puts \
dependency debug info — gpui's above all — back in front of the \
linker and undoes [profile.dev] debug = \"line-tables-only\""
),
}
}
#[test]
fn linux_targets_keep_debug_info_out_of_the_linked_image() {
let targets = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"];
for target in targets {
let body = table(CARGO_CONFIG, &format!("[target.{target}]"))
.unwrap_or_else(|| panic!(".cargo/config.toml has no [target.{target}] table"));
let flags = value(body, "rustflags")
.unwrap_or_else(|| panic!("[target.{target}] sets no rustflags"));
assert!(
flags.contains("split-debuginfo=unpacked"),
"[target.{target}] rustflags is {flags}, without \
-Csplit-debuginfo=unpacked. On Linux the dev default is `off`, so \
every byte of DWARF is copied through `ld` into the linked image."
);
}
}
#[test]
fn split_debuginfo_stays_scoped_to_the_targets_that_can_take_it() {
assert!(
!CARGO_TOML.contains("split-debuginfo"),
"Cargo.toml sets split-debuginfo. A profile cannot be cfg-gated and \
`unpacked` is a hard error on windows-msvc: it belongs in \
.cargo/config.toml under the Linux targets, which is where the rest \
of this module expects it."
);
for (i, line) in CARGO_CONFIG.lines().enumerate() {
let line = line.trim();
if line.starts_with('[') {
assert!(
line.starts_with("[target."),
".cargo/config.toml line {} opens `{line}`. Everything in this \
file is target-scoped on purpose; a [build] rustflags table \
would apply -Csplit-debuginfo=unpacked to windows-msvc too, \
where it is a hard error.",
i + 1
);
}
}
}
#[test]
fn every_example_is_gated_behind_the_examples_feature() {
let expected = format!("[\"{GATE}\"]");
for example in declared_examples() {
let Example {
name,
required_features,
..
} = &example;
let actual = required_features.as_deref().unwrap_or("nothing");
assert_eq!(
actual, expected,
"example `{name}` requires {actual}, not {expected}. An ungated \
example is linked by `cargo test` and by \
`cargo {{build,check}} --all-targets`, and each link is a full \
link of gpui — eight at cargo's default -j is what the kernel \
killed."
);
}
}
#[test]
fn the_gate_feature_exists_and_enables_nothing() {
let features = table(CARGO_TOML, "[features]").expect("Cargo.toml has no [features] table");
let gate = value(features, GATE).unwrap_or_else(|| {
panic!(
"[features] declares no `{GATE}`, so every example's \
required-features names a feature that does not exist and cargo \
refuses the manifest"
)
});
assert_eq!(
gate, "[]",
"the `{GATE}` feature enables {gate}. It exists only to be required by \
the example targets; giving it dependencies would make \
`--features {GATE}` mean something else as well."
);
}
#[test]
fn no_example_is_left_to_autodiscovery() {
let declared: Vec<String> = declared_examples().into_iter().map(|e| e.path).collect();
for path in autodiscoverable_paths() {
assert!(
declared.contains(&path),
"{path} is an example target that no [[example]] block declares, so \
cargo autodiscovers it — and an autodiscovered target cannot carry \
required-features. It is linked by `cargo test` and \
`--all-targets` no matter what the other seven do. Declare it in \
Cargo.toml with required-features = [\"{GATE}\"]."
);
}
}
#[test]
fn every_declared_example_path_exists() {
for Example { name, path, .. } in declared_examples() {
assert!(
repo_root().join(&path).is_file(),
"example `{name}` points at {path}, which is not a file. A \
[[example]] with a dead path fails the whole manifest, so this \
fails first and says which one."
);
}
}
#[test]
fn the_examples_readme_runs_examples_through_the_gate() {
let commands: Vec<&str> = EXAMPLES_README
.lines()
.map(str::trim)
.filter(|line| {
line.starts_with("cargo run --example") || line.starts_with("cargo build --example")
})
.collect();
assert!(
!commands.is_empty(),
"examples/README.md documents no `cargo run --example` command at all — \
either the scan broke or the one place that says how to run these is gone"
);
for command in commands {
assert!(
command.contains(&format!("--features {GATE}"))
|| command.contains(&format!("--features \"{GATE}"))
|| command.contains(&format!("{GATE},"))
|| command.contains(&format!(",{GATE}")),
"examples/README.md documents `{command}`, which does not pass \
--features {GATE}. Since the gate landed, that command fails with \
`no example target named ...` — a reader following the README \
gets an error, not a window."
);
}
}