tree-sitter/xtask/src/test.rs
Max Brunsfeld 201b41cf11
feat: add 'reserved word' construct
Co-authored-by: Amaan Qureshi <amaanq12@gmail.com>
2024-12-23 03:06:32 -05:00

139 lines
4.6 KiB
Rust

use std::{
env,
path::Path,
process::{Command, Stdio},
};
use anyhow::{anyhow, Result};
use regex::Regex;
use crate::{bail_on_err, Test};
pub fn run(args: &Test) -> Result<()> {
let test_flags = if args.address_sanitizer {
env::set_var("CFLAGS", "-fsanitize=undefined,address");
// When the Tree-sitter C library is compiled with the address sanitizer, the address
// sanitizer runtime library needs to be linked into the final test executable. When
// using Xcode clang, the Rust linker doesn't know where to find that library, so we
// need to specify linker flags directly.
let output = Command::new("cc").arg("-print-runtime-dir").output()?;
bail_on_err(&output, "Failed to get clang runtime dir")?;
let runtime_dir = String::from_utf8(output.stdout)?;
if runtime_dir.contains("/Xcode.app/") {
env::set_var(
"RUSTFLAGS",
format!(
"-C link-arg=-L{runtime_dir} -C link-arg=-lclang_rt.asan_osx_dynamic -C link-arg=-Wl,-rpath,{runtime_dir}"
),
);
}
// Specify a `--target` explicitly. This is required for address sanitizer support.
let output = Command::new("rustup")
.arg("show")
.arg("active-toolchain")
.output()?;
bail_on_err(&output, "Failed to get active Rust toolchain")?;
let toolchain = String::from_utf8(output.stdout)?;
let re = Regex::new(r"(stable|beta|nightly)-([_a-z0-9-]+).*")?;
let captures = re
.captures(&toolchain)
.ok_or_else(|| anyhow!("Failed to parse toolchain '{toolchain}'"))?;
let current_target = captures.get(2).unwrap().as_str();
format!("--target={current_target}")
} else {
String::new()
};
if let Some(language) = &args.language {
env::set_var("TREE_SITTER_LANGUAGE", language);
}
if let Some(example) = &args.example {
env::set_var("TREE_SITTER_EXAMPLE_INCLUDE", example);
}
if let Some(seed) = args.seed {
env::set_var("TREE_SITTER_SEED", seed.to_string());
}
if let Some(iterations) = args.iterations {
env::set_var("TREE_SITTER_ITERATIONS", iterations.to_string());
}
if args.debug {
env::set_var("TREE_SITTER_LOG", "1");
}
if args.debug_graph {
env::set_var("TREE_SITTER_LOG_GRAPHS", "1");
}
if args.g {
let cargo_cmd = Command::new("cargo")
.arg("test")
.arg(test_flags)
.arg("--no-run")
.arg("--message-format=json")
.stdout(Stdio::piped())
.spawn()?;
let jq_cmd = Command::new("jq")
.arg("-rs")
.arg(r#"map(select(.target.name == "tree_sitter_cli" and .executable))[0].executable"#)
.stdin(cargo_cmd.stdout.unwrap())
.output()?;
let test_binary = String::from_utf8(jq_cmd.stdout)?;
let mut lldb_cmd = Command::new("lldb");
lldb_cmd.arg(test_binary.trim()).arg("--").args(&args.args);
bail_on_err(
&lldb_cmd.spawn()?.wait_with_output()?,
&format!("Failed to run {lldb_cmd:?}"),
)?;
} else {
let mut cargo_cmd = Command::new("cargo");
cargo_cmd.arg("test");
if args.wasm {
cargo_cmd.arg("--features").arg("wasm");
}
if !test_flags.is_empty() {
cargo_cmd.arg(test_flags);
}
cargo_cmd.args(&args.args);
if args.nocapture {
cargo_cmd.arg("--").arg("--nocapture");
}
bail_on_err(
&cargo_cmd.spawn()?.wait_with_output()?,
&format!("Failed to run {cargo_cmd:?}"),
)?;
}
Ok(())
}
pub fn run_wasm() -> Result<()> {
std::env::set_current_dir("lib/binding_web")?;
let node_modules_dir = Path::new("node_modules");
let npm = if cfg!(target_os = "windows") {
"npm.cmd"
} else {
"npm"
};
if !node_modules_dir.join("chai").exists() || !node_modules_dir.join("mocha").exists() {
println!("Installing test dependencies...");
let output = Command::new(npm).arg("install").output()?;
bail_on_err(&output, "Failed to install test dependencies")?;
}
let child = Command::new(npm).arg("test").spawn()?;
let output = child.wait_with_output()?;
bail_on_err(&output, &format!("Failed to run `{npm} test`"))?;
// Display test results
let output = String::from_utf8_lossy(&output.stdout);
for line in output.lines() {
println!("{line}");
}
Ok(())
}