Skip to content

Commit fb5b7bb

Browse files
committed
Write e2e tests for uds and named pipes
1 parent f0cfe1a commit fb5b7bb

File tree

5 files changed

+617
-7
lines changed

5 files changed

+617
-7
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bin_tests/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ serde_json = { version = "1.0" }
2020
strum = { version = "0.26.2", features = ["derive"] }
2121
libc = "0.2"
2222
nix = { version = "0.29", features = ["signal", "socket"] }
23+
hex = "0.4"
24+
25+
[target.'cfg(windows)'.dependencies]
26+
tokio = { version = "1.0", features = ["net"] }
2327

2428
[lib]
2529
bench = false

bin_tests/tests/crashtracker_bin_test.rs

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,17 @@ fn test_crash_tracking_errors_intake_crash_ping() {
147147
test_crash_tracking_errors_intake_dual_upload(BuildProfile::Release, "donothing", "null_deref");
148148
}
149149

150+
#[test]
151+
#[cfg_attr(miri, ignore)]
152+
#[cfg(unix)]
153+
fn test_crash_tracking_errors_intake_uds_socket() {
154+
test_crash_tracking_bin_with_errors_intake_uds(
155+
BuildProfile::Release,
156+
"donothing",
157+
"null_deref",
158+
);
159+
}
160+
150161
// This test is disabled for now on x86_64 musl and macos
151162
// It seems that on aarch64 musl, libc has CFI which allows
152163
// unwinding passed the signal frame.
@@ -864,6 +875,165 @@ fn test_crash_tracking_errors_intake_dual_upload(
864875
assert_telemetry_message(&crash_telemetry, crash_typ);
865876
}
866877

878+
#[cfg(unix)]
879+
fn test_crash_tracking_bin_with_errors_intake_uds(
880+
crash_tracking_receiver_profile: BuildProfile,
881+
mode: &str,
882+
crash_typ: &str,
883+
) {
884+
let (crashtracker_bin, crashtracker_receiver) =
885+
setup_crashtracking_crates(crash_tracking_receiver_profile);
886+
let fixtures = setup_test_fixtures(&[&crashtracker_receiver, &crashtracker_bin]);
887+
888+
// Try to create the standard agent UDS socket for testing
889+
let socket_path = std::path::Path::new("/var/run/datadog/apm.socket");
890+
891+
// Create directory if it doesn't exist
892+
if let Some(parent) = socket_path.parent() {
893+
if std::fs::create_dir_all(parent).is_err() {
894+
// Skip test if we can't create the directory (permission issues)
895+
eprintln!("Skipping UDS test - cannot create /var/run/datadog directory");
896+
return;
897+
}
898+
}
899+
900+
// Remove socket if it exists from a previous run
901+
let _ = std::fs::remove_file(socket_path);
902+
903+
// Create the Unix socket at the standard agent location
904+
let listener = match std::os::unix::net::UnixListener::bind(socket_path) {
905+
Ok(l) => l,
906+
Err(_) => {
907+
eprintln!("Skipping UDS test - cannot create socket at /var/run/datadog/apm.socket");
908+
return;
909+
}
910+
};
911+
912+
let mut p = process::Command::new(&fixtures.artifacts[&crashtracker_bin])
913+
.arg("") // Empty endpoint so both use agent detection
914+
.arg(fixtures.artifacts[&crashtracker_receiver].as_os_str())
915+
.arg(&fixtures.output_dir)
916+
.arg(mode)
917+
.arg(crash_typ)
918+
// Don't set DD_TRACE_AGENT_URL - let it auto-detect the UDS socket
919+
.spawn()
920+
.unwrap();
921+
922+
let exit_status = bin_tests::timeit!("exit after signal", {
923+
eprintln!("Waiting for exit");
924+
p.wait().unwrap()
925+
});
926+
927+
match crash_typ {
928+
"kill_sigabrt" | "kill_sigill" | "null_deref" | "raise_sigabrt" | "raise_sigill" => {
929+
assert!(!exit_status.success())
930+
}
931+
"kill_sigbus" | "kill_sigsegv" | "raise_sigbus" | "raise_sigsegv" => {
932+
assert!(exit_status.success())
933+
}
934+
_ => unreachable!("{crash_typ} shouldn't happen"),
935+
}
936+
937+
// Handle HTTP requests on the Unix socket - expect 4 requests total
938+
// 2 from telemetry (crash ping + crash report) and 2 from errors intake (crash ping + crash
939+
// report)
940+
let (mut stream1, _) = listener.accept().unwrap();
941+
let body1 = read_http_request_body(&mut stream1);
942+
stream1
943+
.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")
944+
.unwrap();
945+
946+
let (mut stream2, _) = listener.accept().unwrap();
947+
let body2 = read_http_request_body(&mut stream2);
948+
stream2
949+
.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")
950+
.unwrap();
951+
952+
let (mut stream3, _) = listener.accept().unwrap();
953+
let body3 = read_http_request_body(&mut stream3);
954+
stream3
955+
.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")
956+
.unwrap();
957+
958+
let (mut stream4, _) = listener.accept().unwrap();
959+
let body4 = read_http_request_body(&mut stream4);
960+
stream4
961+
.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")
962+
.unwrap();
963+
964+
let all_bodies = [body1, body2, body3, body4];
965+
966+
// Separate crash pings from crash reports
967+
let mut crash_pings = Vec::new();
968+
let mut crash_reports = Vec::new();
969+
970+
for (i, body) in all_bodies.iter().enumerate() {
971+
if body.contains("is_crash_ping:true") {
972+
crash_pings.push((i + 1, body));
973+
} else if body.contains("is_crash:true") {
974+
crash_reports.push((i + 1, body));
975+
}
976+
}
977+
978+
assert_eq!(
979+
crash_pings.len(),
980+
2,
981+
"Expected 2 crash pings (telemetry + errors intake), got {}",
982+
crash_pings.len()
983+
);
984+
assert_eq!(
985+
crash_reports.len(),
986+
2,
987+
"Expected 2 crash reports (telemetry + errors intake), got {}",
988+
crash_reports.len()
989+
);
990+
991+
// Find telemetry requests (contain api_version and request_type)
992+
let telemetry_crash_ping = crash_pings
993+
.iter()
994+
.find(|(_, body)| body.contains("api_version") && body.contains("request_type"))
995+
.expect("Should have telemetry crash ping");
996+
validate_crash_ping_telemetry(telemetry_crash_ping.1);
997+
998+
let telemetry_crash_report = crash_reports
999+
.iter()
1000+
.find(|(_, body)| body.contains("api_version") && body.contains("request_type"))
1001+
.expect("Should have telemetry crash report");
1002+
assert_telemetry_message(telemetry_crash_report.1.as_bytes(), crash_typ);
1003+
1004+
// Find errors intake requests (contain ddsource: crashtracker but no api_version)
1005+
let errors_crash_ping = crash_pings
1006+
.iter()
1007+
.find(|(_, body)| {
1008+
body.contains("\"ddsource\":\"crashtracker\"") && !body.contains("api_version")
1009+
})
1010+
.expect("Should have errors intake crash ping");
1011+
1012+
let errors_crash_report = crash_reports
1013+
.iter()
1014+
.find(|(_, body)| {
1015+
body.contains("\"ddsource\":\"crashtracker\"") && !body.contains("api_version")
1016+
})
1017+
.expect("Should have errors intake crash report");
1018+
1019+
// Parse and validate errors intake payloads
1020+
let errors_ping_payload: serde_json::Value = serde_json::from_str(errors_crash_ping.1).unwrap();
1021+
let errors_report_payload: serde_json::Value =
1022+
serde_json::from_str(errors_crash_report.1).unwrap();
1023+
1024+
// Validate errors intake crash ping (is_crash: false)
1025+
assert_eq!(errors_ping_payload["ddsource"], "crashtracker");
1026+
assert_eq!(errors_ping_payload["error"]["is_crash"], false);
1027+
1028+
// Validate errors intake crash report (is_crash: true)
1029+
assert_eq!(errors_report_payload["ddsource"], "crashtracker");
1030+
assert_eq!(errors_report_payload["error"]["is_crash"], true);
1031+
1032+
// Clean up
1033+
drop(listener);
1034+
let _ = std::fs::remove_file(socket_path);
1035+
}
1036+
8671037
fn assert_errors_intake_payload(payload: &Value, crash_typ: &str) {
8681038
// Validate basic structure
8691039
assert_eq!(payload["ddsource"], "crashtracker");

0 commit comments

Comments
 (0)