mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
296 lines
6.9 KiB
Rust
296 lines
6.9 KiB
Rust
#![cfg(test)]
|
|
|
|
use indoc::indoc;
|
|
|
|
#[cfg(target_os = "linux")]
|
|
static BUILD_ONCE: std::sync::Once = std::sync::Once::new();
|
|
|
|
#[cfg(all(target_os = "linux"))]
|
|
fn build_host() {
|
|
use roc_build::link::preprocessed_host_filename;
|
|
use roc_linker::build_and_preprocess_host;
|
|
|
|
let platform_main_roc = std::env::current_dir()
|
|
.unwrap()
|
|
.join("zig-platform/main.roc");
|
|
|
|
// tests always run on the host
|
|
let target = target_lexicon::Triple::host();
|
|
|
|
// the preprocessed host is stored beside the platform's main.roc
|
|
let preprocessed_host_path =
|
|
platform_main_roc.with_file_name(preprocessed_host_filename(&target).unwrap());
|
|
|
|
build_and_preprocess_host(
|
|
roc_mono::ir::OptLevel::Normal,
|
|
&target,
|
|
&platform_main_roc,
|
|
&preprocessed_host_path,
|
|
vec![String::from("mainForHost")],
|
|
vec![],
|
|
);
|
|
}
|
|
|
|
fn valgrind_test(source: &str) {
|
|
#[cfg(target_os = "linux")]
|
|
{
|
|
valgrind_test_linux(source)
|
|
}
|
|
|
|
#[cfg(not(target_os = "linux"))]
|
|
{
|
|
let _ = source;
|
|
}
|
|
}
|
|
|
|
#[cfg(target_os = "linux")]
|
|
fn valgrind_test_linux(source: &str) {
|
|
use roc_cli::build::BuiltFile;
|
|
|
|
// the host is identical for all tests so we only want to build it once
|
|
BUILD_ONCE.call_once(build_host);
|
|
|
|
let pf = std::env::current_dir()
|
|
.unwrap()
|
|
.join("zig-platform/main.roc");
|
|
|
|
assert!(pf.exists(), "cannot find platform {:?}", &pf);
|
|
|
|
let mut app_module_source = format!(
|
|
indoc::indoc!(
|
|
r#"
|
|
app "test"
|
|
packages {{ pf: "{}" }}
|
|
imports []
|
|
provides [main] to pf
|
|
|
|
main =
|
|
"#
|
|
),
|
|
pf.to_str().unwrap()
|
|
);
|
|
|
|
for line in source.lines() {
|
|
app_module_source.push_str(" ");
|
|
app_module_source.push_str(line);
|
|
app_module_source.push('\n');
|
|
}
|
|
|
|
let temp_dir = tempfile::tempdir().unwrap();
|
|
let app_module_path = temp_dir.path().join("app.roc");
|
|
|
|
let arena = bumpalo::Bump::new();
|
|
let assume_prebuilt = true;
|
|
let res_binary_path = roc_cli::build::build_str_test(
|
|
&arena,
|
|
&app_module_path,
|
|
&app_module_source,
|
|
assume_prebuilt,
|
|
);
|
|
|
|
match res_binary_path {
|
|
Ok(BuiltFile {
|
|
binary_path,
|
|
problems,
|
|
total_time: _,
|
|
expect_metadata: _,
|
|
}) => {
|
|
if problems.exit_code() != 0 {
|
|
panic!("there are problems")
|
|
}
|
|
|
|
run_with_valgrind(&binary_path);
|
|
}
|
|
Err(roc_cli::build::BuildFileError::LoadingProblem(
|
|
roc_load::LoadingProblem::FormattedReport(report),
|
|
)) => {
|
|
eprintln!("{}", report);
|
|
panic!("");
|
|
}
|
|
Err(e) => panic!("{:?}", e),
|
|
}
|
|
|
|
drop(temp_dir)
|
|
}
|
|
|
|
#[allow(unused)]
|
|
fn run_with_valgrind(binary_path: &std::path::Path) {
|
|
use cli_utils::helpers::{extract_valgrind_errors, ValgrindError, ValgrindErrorXWhat};
|
|
|
|
// If possible, report the generated executable name relative to the current dir.
|
|
let generated_filename = binary_path
|
|
.strip_prefix(std::env::current_dir().unwrap())
|
|
.unwrap_or(binary_path)
|
|
.to_str()
|
|
.unwrap();
|
|
|
|
let (valgrind_out, raw_xml) =
|
|
cli_utils::helpers::run_with_valgrind([], &[generated_filename.to_string()]);
|
|
|
|
if valgrind_out.status.success() {
|
|
let memory_errors = extract_valgrind_errors(&raw_xml).unwrap_or_else(|err| {
|
|
panic!(
|
|
indoc!(
|
|
r#"
|
|
failed to parse the `valgrind` xml output:
|
|
|
|
Error was:
|
|
|
|
{:?}
|
|
|
|
valgrind xml was:
|
|
|
|
{}
|
|
|
|
valgrind stdout was:
|
|
|
|
{}
|
|
|
|
valgrind stderr was:
|
|
|
|
{}
|
|
"#
|
|
),
|
|
err, raw_xml, valgrind_out.stdout, valgrind_out.stderr
|
|
);
|
|
});
|
|
|
|
if !memory_errors.is_empty() {
|
|
for error in memory_errors {
|
|
let ValgrindError {
|
|
kind,
|
|
what: _,
|
|
xwhat,
|
|
} = error;
|
|
println!("Valgrind Error: {}\n", kind);
|
|
|
|
if let Some(ValgrindErrorXWhat {
|
|
text,
|
|
leakedbytes: _,
|
|
leakedblocks: _,
|
|
}) = xwhat
|
|
{
|
|
println!(" {}", text);
|
|
}
|
|
}
|
|
panic!("Valgrind found memory errors");
|
|
}
|
|
} else {
|
|
let exit_code = match valgrind_out.status.code() {
|
|
Some(code) => format!("exit code {}", code),
|
|
None => "no exit code".to_string(),
|
|
};
|
|
|
|
panic!(
|
|
"`valgrind` exited with {}. valgrind stdout was: \"{}\"\n\nvalgrind stderr was: \"{}\"",
|
|
exit_code, valgrind_out.stdout, valgrind_out.stderr
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn list_concat_consumes_first_argument() {
|
|
valgrind_test("List.concat (List.withCapacity 1024) [1,2,3] |> List.len |> Num.toStr");
|
|
}
|
|
|
|
#[test]
|
|
fn list_concat_consumes_second_argument() {
|
|
valgrind_test(indoc!(
|
|
r#"
|
|
(
|
|
a : List U8
|
|
a = []
|
|
b = List.reserve [] 11
|
|
List.concat a b
|
|
|> List.len
|
|
|> Num.toStr
|
|
)
|
|
"#
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn str_capacity_concat() {
|
|
valgrind_test(r#"Str.withCapacity 42 |> Str.concat "foobar""#);
|
|
}
|
|
|
|
#[test]
|
|
fn append_scalar() {
|
|
valgrind_test(indoc!(
|
|
r#"
|
|
Str.appendScalar "abcd" 'A'
|
|
|> Result.withDefault ""
|
|
"#
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn split_not_present() {
|
|
valgrind_test(indoc!(
|
|
r#"
|
|
Str.split (Str.concat "a string that is stored on the heap" "!") "\n"
|
|
|> Str.joinWith ""
|
|
"#
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn str_concat_first_argument_not_unique() {
|
|
valgrind_test(indoc!(
|
|
r#"
|
|
(
|
|
str1 = Str.reserve "" 48
|
|
str2 = "a"
|
|
|
|
out = Str.concat str1 str2
|
|
if Bool.false then
|
|
out
|
|
else
|
|
str1
|
|
)
|
|
"#
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn list_concat_empty_list_zero_sized_type() {
|
|
valgrind_test(indoc!(
|
|
r#"
|
|
(
|
|
a = List.reserve [] 11
|
|
b = []
|
|
List.concat a b
|
|
|> List.len
|
|
|> Num.toStr
|
|
)
|
|
"#
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn str_trim_right_capacity() {
|
|
valgrind_test(indoc!(
|
|
r#"
|
|
(
|
|
str = "a" |> Str.reserve 30
|
|
out = str |> Str.trimRight
|
|
|
|
if out == "" then "A" else "B"
|
|
)
|
|
"#
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn str_trim_left_capacity() {
|
|
valgrind_test(indoc!(
|
|
r#"
|
|
(
|
|
str = " a" |> Str.reserve 30
|
|
out = str |> Str.trimLeft
|
|
|
|
if out == "" then "A" else "B"
|
|
)
|
|
"#
|
|
));
|
|
}
|