Skip to content

Commit ff244b9

Browse files
committed
Add bin tests for windows
1 parent 60eaab4 commit ff244b9

File tree

4 files changed

+159
-9
lines changed

4 files changed

+159
-9
lines changed

bin_tests/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ bench = false
3131
[[bin]]
3232
name = "test_the_tests"
3333
bench = false
34+
target = "cfg(unix)"
3435

3536
[[bin]]
3637
name = "crashtracker_receiver"

bin_tests/src/bin/crashing_test_app.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
// Copyright 2023-Present Datadog, Inc. https://www.datadoghq.com/
22
// SPDX-License-Identifier: Apache-2.0
33

4-
#[cfg(not(unix))]
5-
fn main() {}
6-
7-
#[cfg(unix)]
84
fn main() -> anyhow::Result<()> {
95
unix::main()
106
}
117

12-
#[cfg(unix)]
138
mod unix {
149
use anyhow::ensure;
1510
use anyhow::Context;
Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
// Copyright 2023-Present Datadog, Inc. https://www.datadoghq.com/
22
// SPDX-License-Identifier: Apache-2.0
33

4-
#[cfg(not(unix))]
5-
fn main() {}
6-
7-
#[cfg(unix)]
84
fn main() -> anyhow::Result<()> {
95
datadog_crashtracker::receiver_entry_point_stdin()
106
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
// Copyright 2023-Present Datadog, Inc. https://www.datadoghq.com/
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#![cfg(windows)]
5+
6+
use std::collections::HashMap;
7+
use std::io::{Read, Write};
8+
use std::path::Path;
9+
use std::process;
10+
use std::{fs, path::PathBuf};
11+
12+
use anyhow::Context;
13+
use bin_tests::{build_artifacts, ArtifactType, ArtifactsBuild, BuildProfile};
14+
use serde_json::Value;
15+
16+
// This test is disabled for now on x86_64 musl and macos
17+
// It seems that on aarch64 musl, libc has CFI which allows
18+
// unwinding passed the signal frame.
19+
#[test]
20+
#[cfg_attr(miri, ignore)]
21+
fn test_crasht_tracking_validate_callstack() {
22+
test_crash_tracking_callstack()
23+
}
24+
25+
fn test_crash_tracking_callstack() {
26+
let (_, crashtracker_receiver) = setup_crashtracking_crates(BuildProfile::Release);
27+
28+
let crashing_app = ArtifactsBuild {
29+
name: "crashing_test_app".to_owned(),
30+
// compile in debug so we avoid inlining
31+
// and can check the callchain
32+
build_profile: BuildProfile::Debug,
33+
artifact_type: ArtifactType::Bin,
34+
triple_target: None,
35+
};
36+
37+
let fixtures = setup_test_fixtures(&[&crashtracker_receiver, &crashing_app]);
38+
39+
let mut p = process::Command::new(&fixtures.artifacts[&crashing_app])
40+
.arg(format!("file://{}", fixtures.crash_profile_path.display()))
41+
.arg(fixtures.artifacts[&crashtracker_receiver].as_os_str())
42+
.arg(&fixtures.output_dir)
43+
.spawn()
44+
.unwrap();
45+
46+
let exit_status = bin_tests::timeit!("exit after signal", {
47+
eprintln!("Waiting for exit");
48+
p.wait().unwrap()
49+
});
50+
assert!(!exit_status.success());
51+
52+
let stderr_path = format!("{0}/out.stderr", fixtures.output_dir.display());
53+
let stderr = fs::read(stderr_path)
54+
.context("reading crashtracker stderr")
55+
.unwrap();
56+
let stdout_path = format!("{0}/out.stdout", fixtures.output_dir.display());
57+
let stdout = fs::read(stdout_path)
58+
.context("reading crashtracker stdout")
59+
.unwrap();
60+
let s = String::from_utf8(stderr);
61+
assert!(
62+
matches!(
63+
s.as_deref(),
64+
Ok("") | Ok("Failed to fully receive crash. Exit state was: StackTrace([])\n")
65+
| Ok("Failed to fully receive crash. Exit state was: InternalError(\"{\\\"ip\\\": \\\"\")\n"),
66+
),
67+
"got {s:?}"
68+
);
69+
assert_eq!(Ok(""), String::from_utf8(stdout).as_deref());
70+
71+
let crash_profile = fs::read(fixtures.crash_profile_path)
72+
.context("reading crashtracker profiling payload")
73+
.unwrap();
74+
let crash_payload = serde_json::from_slice::<serde_json::Value>(&crash_profile)
75+
.context("deserializing crashtracker profiling payload to json")
76+
.unwrap();
77+
78+
// Note: in Release, we do not have the crate and module name prepended to the function name
79+
// Here we compile the crashing app in Debug.
80+
let mut expected_functions = Vec::new();
81+
// It seems that on arm/arm64, fn3 is inlined in fn2, so not present.
82+
// Add fn3 only for x86_64 arch
83+
#[cfg(target_arch = "x86_64")]
84+
expected_functions.push("crashing_test_app::unix::fn3");
85+
expected_functions.extend_from_slice(&[
86+
"crashing_test_app::unix::fn2",
87+
"crashing_test_app::unix::fn1",
88+
"crashing_test_app::unix::main",
89+
"crashing_test_app::main",
90+
]);
91+
92+
let crashing_callstack = &crash_payload["error"]["stack"]["frames"];
93+
assert!(
94+
crashing_callstack.as_array().unwrap().len() >= expected_functions.len(),
95+
"crashing thread callstacks does have less frames than expected. Current: {}, Expected: {}",
96+
crashing_callstack.as_array().unwrap().len(),
97+
expected_functions.len()
98+
);
99+
100+
let function_names: Vec<&str> = crashing_callstack
101+
.as_array()
102+
.unwrap()
103+
.iter()
104+
.map(|f| f["function"].as_str().unwrap_or(""))
105+
.collect();
106+
107+
for (expected, actual) in expected_functions.iter().zip(function_names.iter()) {
108+
assert_eq!(expected, actual);
109+
}
110+
}
111+
112+
struct TestFixtures<'a> {
113+
tmpdir: tempfile::TempDir,
114+
crash_profile_path: PathBuf,
115+
crash_telemetry_path: PathBuf,
116+
output_dir: PathBuf,
117+
118+
artifacts: HashMap<&'a ArtifactsBuild, PathBuf>,
119+
}
120+
121+
fn setup_test_fixtures<'a>(crates: &[&'a ArtifactsBuild]) -> TestFixtures<'a> {
122+
let artifacts = build_artifacts(crates).unwrap();
123+
124+
let tmpdir = tempfile::TempDir::new().unwrap();
125+
let dirpath = tmpdir.path();
126+
TestFixtures {
127+
crash_profile_path: extend_path(dirpath, "crash"),
128+
crash_telemetry_path: extend_path(dirpath, "crash.telemetry"),
129+
output_dir: dirpath.to_path_buf(),
130+
131+
artifacts,
132+
tmpdir,
133+
}
134+
}
135+
136+
fn setup_crashtracking_crates(
137+
crash_tracking_receiver_profile: BuildProfile,
138+
) -> (ArtifactsBuild, ArtifactsBuild) {
139+
let crashtracker_bin = ArtifactsBuild {
140+
name: "crashtracker_bin_test".to_owned(),
141+
build_profile: crash_tracking_receiver_profile,
142+
artifact_type: ArtifactType::Bin,
143+
triple_target: None,
144+
};
145+
let crashtracker_receiver = ArtifactsBuild {
146+
name: "crashtracker_receiver".to_owned(),
147+
build_profile: crash_tracking_receiver_profile,
148+
artifact_type: ArtifactType::Bin,
149+
triple_target: None,
150+
};
151+
(crashtracker_bin, crashtracker_receiver)
152+
}
153+
154+
fn extend_path<T: AsRef<Path>>(parent: &Path, path: T) -> PathBuf {
155+
let mut parent = parent.to_path_buf();
156+
parent.push(path);
157+
parent
158+
}

0 commit comments

Comments
 (0)