blob: fca23b8b051dcfe43951d550df6ad5ef24607395 [file] [log] [blame]
use crate::e2e::*;
#[test]
fn empty_file() -> anyhow::Result<()> {
let space = TestSpace::new()?;
space.write("build.ninja", "")?;
let out = space.run(&mut n2_command(vec![]))?;
assert_eq!(std::str::from_utf8(&out.stdout)?, "n2: no work to do\n");
Ok(())
}
#[test]
fn basic_build() -> anyhow::Result<()> {
let space = TestSpace::new()?;
space.write(
"build.ninja",
&[TOUCH_RULE, "build out: touch in", ""].join("\n"),
)?;
space.write("in", "")?;
space.run_expect(&mut n2_command(vec!["out"]))?;
assert!(space.read("out").is_ok());
Ok(())
}
#[test]
fn create_subdir() -> anyhow::Result<()> {
// Run a build rule that needs a subdir to be automatically created.
let space = TestSpace::new()?;
space.write(
"build.ninja",
&[TOUCH_RULE, "build subdir/out: touch in", ""].join("\n"),
)?;
space.write("in", "")?;
space.run_expect(&mut n2_command(vec!["subdir/out"]))?;
assert!(space.read("subdir/out").is_ok());
Ok(())
}
#[cfg(unix)]
#[test]
fn generate_rsp_file() -> anyhow::Result<()> {
let space = TestSpace::new()?;
space.write(
"build.ninja",
"
rule cat
command = cat ${out}.rsp > ${out}
rspfile = ${out}.rsp
rspfile_content = 1 $in 2 $in_newline 3
rule litter
command = cat make/me/${out}.rsp > ${out}
rspfile = make/me/${out}.rsp
rspfile_content = random stuff
rule touch
command = touch $out
build main: cat foo bar baz in
build foo: litter bar
build bar: touch baz
build baz: touch in
",
)?;
space.write("in", "go!")?;
let _ = space.run_expect(&mut n2_command(vec!["main"]))?;
// The 'main' and 'foo' targets copy the contents of their rsp file to their
// output.
let main_rsp = space.read("main").unwrap();
assert_eq!(main_rsp, b"1 foo bar baz in 2 foo\nbar\nbaz\nin 3");
let foo_rsp = space.read("foo").unwrap();
assert_eq!(foo_rsp, b"random stuff");
// The 'make/me' directory was created when writing an rsp file.
// It should still be there.
let meta = space.metadata("make/me").unwrap();
assert!(meta.is_dir());
// Run again: everything should be up to date.
let out = space.run_expect(&mut n2_command(vec!["main"]))?;
assert_output_contains(&out, "no work");
Ok(())
}
/// Run a task that prints something, and verify it shows up.
#[cfg(unix)]
#[test]
fn spam_output() -> anyhow::Result<()> {
let space = TestSpace::new()?;
space.write(
"build.ninja",
"
rule quiet
description = quiet $out
command = touch $out
rule spam
description = spam $out
command = echo greetz from $out && touch $out
build a: quiet
build b: spam a
build c: quiet b
",
)?;
let out = space.run_expect(&mut n2_command(vec!["c"]))?;
assert_output_contains(
&out,
"quiet a
spam b
greetz from b
quiet c
",
);
Ok(())
}
#[test]
fn specify_build_file() -> anyhow::Result<()> {
let space = TestSpace::new()?;
space.write(
"build_specified.ninja",
&[TOUCH_RULE, "build out: touch in", ""].join("\n"),
)?;
space.write("in", "")?;
space.run_expect(&mut n2_command(vec!["-f", "build_specified.ninja", "out"]))?;
assert!(space.read("out").is_ok());
Ok(())
}
/// Regression test for https://github.com/evmar/n2/issues/44
/// and https://github.com/evmar/n2/issues/46 .
/// Build with the same output listed multiple times.
#[test]
fn repeated_out() -> anyhow::Result<()> {
let space = TestSpace::new()?;
space.write(
"build.ninja",
&[
TOUCH_RULE,
"build dup dup: touch in",
"build out: touch dup",
"",
]
.join("\n"),
)?;
space.write("in", "")?;
space.write("dup", "")?;
let out = space.run_expect(&mut n2_command(vec!["out"]))?;
assert_output_contains(&out, "is repeated in output list");
Ok(())
}
/// Regression test for https://github.com/evmar/n2/issues/55
/// UTF-8 filename.
#[cfg(unix)]
#[test]
fn utf8_filename() -> anyhow::Result<()> {
let space = TestSpace::new()?;
space.write(
"build.ninja",
&[
"
rule echo
description = unicode variable: $in
command = echo unicode command line: $in && touch $out
",
"build out: echo reykjavík.md",
"",
]
.join("\n"),
)?;
space.write("reykjavík.md", "")?;
let out = space.run_expect(&mut n2_command(vec!["out"]))?;
assert_output_contains(&out, "unicode variable: reykjavík.md");
assert_output_contains(&out, "unicode command line: reykjavík.md");
Ok(())
}
#[test]
fn explain() -> anyhow::Result<()> {
let space = TestSpace::new()?;
space.write(
"build.ninja",
&[TOUCH_RULE, "build out: touch in", ""].join("\n"),
)?;
space.write("in", "")?;
let out = space.run_expect(&mut n2_command(vec!["out"]))?;
assert_output_contains(&out, "up to date");
space.write("in", "")?;
let out = space.run_expect(&mut n2_command(vec!["-d", "explain", "out"]))?;
// The main "explain" log line:
assert_output_contains(&out, "explain: build.ninja:6: manifest changed");
// The dump of the file manifest after includes mtimes that we don't want
// to be sensitive to, so just look for some bits we know show up there.
assert_output_contains(&out, "discovered:");
Ok(())
}
/// Meson generates a build step that writes to one of its inputs.
#[test]
fn write_to_input() -> anyhow::Result<()> {
#[cfg(unix)]
let touch_input_command = "touch out in";
#[cfg(windows)]
let touch_input_command = "cmd /c type nul > in && cmd /c type nul > out";
let touch_input_rule = format!(
"
rule touch_in
description = touch out+in
command = {}
",
touch_input_command
);
let space = TestSpace::new()?;
space.write(
"build.ninja",
&[&touch_input_rule, "build out: touch_in in", ""].join("\n"),
)?;
space.write("in", "")?;
space.sub_mtime("in", std::time::Duration::from_secs(1))?;
let out = space.run_expect(&mut n2_command(vec!["out"]))?;
assert_output_contains(&out, "ran 1 task");
// TODO: to support meson, we need this second invocation to not build anything.
let out = space.run_expect(&mut n2_command(vec!["out"]))?;
assert_output_contains(&out, "ran 1 task");
Ok(())
}
#[test]
fn showincludes() -> anyhow::Result<()> {
let space = TestSpace::new()?;
space.write(
"build.ninja",
&[
ECHO_RULE,
"
build out: echo
text = Note: including file: foo
deps = msvc
",
]
.join("\n"),
)?;
space.write("foo", "")?;
let out = space.run_expect(&mut n2_command(vec!["out"]))?;
assert_output_contains(&out, "ran 1 task");
space.write("foo", "")?;
let out = space.run_expect(&mut n2_command(vec!["out"]))?;
assert_output_contains(&out, "ran 1 task");
Ok(())
}
// Repro for issue #83.
#[cfg(unix)]
#[test]
fn eval_twice() -> anyhow::Result<()> {
let space = TestSpace::new()?;
space.write(
"build.ninja",
&[
TOUCH_RULE,
"
var = 123
rule custom
command = $cmd $var
build out: custom
cmd = echo $var hello
",
]
.join("\n"),
)?;
let out = space.run_expect(&mut n2_command(vec!["out"]))?;
assert_output_contains(&out, "echo 123 hello 123");
Ok(())
}
// Repro for issue #84: phony depending on phony.
#[test]
fn phony_depends() -> anyhow::Result<()> {
let space = TestSpace::new()?;
space.write(
"build.ninja",
&[
TOUCH_RULE,
"
build out1: touch
build out2: phony out1
build out3: phony out2
",
]
.join("\n"),
)?;
space.run_expect(&mut n2_command(vec!["out3"]))?;
space.read("out1")?;
Ok(())
}
// builddir controls where .n2_db is written.
#[test]
fn builddir() -> anyhow::Result<()> {
let space = TestSpace::new()?;
space.write(
"build.ninja",
&[
"builddir = foo",
TOUCH_RULE,
"build $builddir/bar: touch",
"",
]
.join("\n"),
)?;
space.run_expect(&mut n2_command(vec!["foo/bar"]))?;
space.read("foo/.n2_db")?;
Ok(())
}
#[test]
fn bad_rule_variable() -> anyhow::Result<()> {
let space = TestSpace::new()?;
space.write(
"build.ninja",
"
rule my_rule
command = touch $out
my_var = foo
build out: my_rule
",
)?;
let out = space.run(&mut n2_command(vec!["out"]))?;
assert_output_contains(&out, "unexpected variable \"my_var\"");
Ok(())
}
#[cfg(unix)]
#[test]
fn deps_evaluate_build_bindings() -> anyhow::Result<()> {
let space = TestSpace::new()?;
space.write(
"build.ninja",
"
rule touch
command = touch $out
rule copy
command = cp $in $out
build foo: copy ${my_dep}
my_dep = bar
build bar: touch
",
)?;
space.run_expect(&mut n2_command(vec!["foo"]))?;
space.read("foo")?;
Ok(())
}
#[cfg(unix)]
#[test]
fn looks_up_values_from_build() -> anyhow::Result<()> {
let space = TestSpace::new()?;
space.write(
"build.ninja",
"
rule copy_rspfile
command = cp $out.rsp $out
rspfile = $out.rsp
build foo: copy_rspfile
rspfile_content = Hello, world!
",
)?;
space.run_expect(&mut n2_command(vec!["foo"]))?;
assert_eq!(space.read("foo")?, b"Hello, world!");
Ok(())
}
#[cfg(unix)]
#[test]
fn build_bindings_arent_recursive() -> anyhow::Result<()> {
let space = TestSpace::new()?;
space.write(
"build.ninja",
"
rule write_file
command = echo $my_var > $out
build foo: write_file
my_var = Hello,$my_var_2 world!
my_var_2 = my_var_2_value
",
)?;
space.run_expect(&mut n2_command(vec!["foo"]))?;
assert_eq!(space.read("foo")?, b"Hello, world!\n");
Ok(())
}
#[cfg(unix)]
#[test]
fn empty_variable_binding() -> anyhow::Result<()> {
let space = TestSpace::new()?;
space.write(
"build.ninja",
"
empty_var =
rule write_file
command = echo $my_var > $out
build foo: write_file
my_var = Hello,$empty_var world!
",
)?;
space.run_expect(&mut n2_command(vec!["foo"]))?;
assert_eq!(space.read("foo")?, b"Hello, world!\n");
Ok(())
}
#[cfg(unix)]
#[test]
fn empty_build_variable() -> anyhow::Result<()> {
let space = TestSpace::new()?;
space.write(
"build.ninja",
"
rule write_file
command = echo $my_var > $out
build foo: write_file
empty =
my_var = Hello, world!
",
)?;
space.run_expect(&mut n2_command(vec!["foo"]))?;
assert_eq!(space.read("foo")?, b"Hello, world!\n");
Ok(())
}