Skip to content

Commit ad4b509

Browse files
committed
tests: Add --github-summary to generate formatted markdown file
Signed-off-by: Matej Hrica <[email protected]>
1 parent e587220 commit ad4b509

File tree

1 file changed

+94
-21
lines changed

1 file changed

+94
-21
lines changed

tests/runner/src/main.rs

Lines changed: 94 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,19 @@ use clap::Parser;
33
use nix::sys::resource::{getrlimit, setrlimit, Resource};
44
use std::env;
55
use std::fs::{self, File};
6+
use std::io::Write;
67
use std::panic::catch_unwind;
78
use std::path::{Path, PathBuf};
89
use std::process::{Command, Stdio};
910
use tempdir::TempDir;
1011
use test_cases::{test_cases, Test, TestCase, TestSetup};
1112

13+
struct TestResult {
14+
name: String,
15+
passed: bool,
16+
log_path: PathBuf,
17+
}
18+
1219
fn get_test(name: &str) -> anyhow::Result<Box<dyn Test>> {
1320
let tests = test_cases();
1421
tests
@@ -36,7 +43,7 @@ fn run_single_test(
3643
base_dir: &Path,
3744
keep_all: bool,
3845
max_name_len: usize,
39-
) -> anyhow::Result<bool> {
46+
) -> anyhow::Result<TestResult> {
4047
let executable = env::current_exe().context("Failed to detect current executable")?;
4148
let test_dir = base_dir.join(test_case);
4249
fs::create_dir(&test_dir).context("Failed to create test directory")?;
@@ -68,22 +75,76 @@ fn run_single_test(
6875
test.check(child);
6976
});
7077

71-
match result {
72-
Ok(()) => {
73-
eprintln!("OK");
74-
if !keep_all {
75-
let _ = fs::remove_dir_all(&test_dir);
76-
}
77-
Ok(true)
78-
}
79-
Err(_e) => {
80-
eprintln!("FAIL");
81-
Ok(false)
78+
let passed = result.is_ok();
79+
if passed {
80+
eprintln!("OK");
81+
if !keep_all {
82+
let _ = fs::remove_dir_all(&test_dir);
8283
}
84+
} else {
85+
eprintln!("FAIL");
86+
}
87+
88+
Ok(TestResult {
89+
name: test_case.to_string(),
90+
passed,
91+
log_path,
92+
})
93+
}
94+
95+
fn write_github_summary(
96+
results: &[TestResult],
97+
num_ok: usize,
98+
num_tests: usize,
99+
) -> anyhow::Result<()> {
100+
let summary_path = env::var("GITHUB_STEP_SUMMARY")
101+
.context("GITHUB_STEP_SUMMARY environment variable not set")?;
102+
103+
let mut file = fs::OpenOptions::new()
104+
.create(true)
105+
.append(true)
106+
.open(&summary_path)
107+
.context("Failed to open GITHUB_STEP_SUMMARY")?;
108+
109+
let all_passed = num_ok == num_tests;
110+
let status = if all_passed { "✅" } else { "❌" };
111+
112+
writeln!(
113+
file,
114+
"## {status} Integration Tests ({num_ok}/{num_tests} passed)\n"
115+
)?;
116+
117+
for result in results {
118+
let icon = if result.passed { "✅" } else { "❌" };
119+
let log_content = fs::read_to_string(&result.log_path).unwrap_or_default();
120+
121+
writeln!(file, "<details>")?;
122+
writeln!(file, "<summary>{icon} {}</summary>\n", result.name)?;
123+
writeln!(file, "```")?;
124+
// Limit log size to avoid huge summaries (2 MiB limit)
125+
const MAX_LOG_SIZE: usize = 2 * 1024 * 1024;
126+
let truncated = if log_content.len() > MAX_LOG_SIZE {
127+
format!(
128+
"... (truncated, showing last 1 MiB) ...\n{}",
129+
&log_content[log_content.len() - MAX_LOG_SIZE..]
130+
)
131+
} else {
132+
log_content
133+
};
134+
writeln!(file, "{truncated}")?;
135+
writeln!(file, "```")?;
136+
writeln!(file, "</details>\n")?;
83137
}
138+
139+
Ok(())
84140
}
85141

86-
fn run_tests(test_case: &str, base_dir: Option<PathBuf>, keep_all: bool) -> anyhow::Result<()> {
142+
fn run_tests(
143+
test_case: &str,
144+
base_dir: Option<PathBuf>,
145+
keep_all: bool,
146+
github_summary: bool,
147+
) -> anyhow::Result<()> {
87148
// Create the base directory - either use provided path or create a temp one
88149
let base_dir = match base_dir {
89150
Some(path) => {
@@ -95,22 +156,29 @@ fn run_tests(test_case: &str, base_dir: Option<PathBuf>, keep_all: bool) -> anyh
95156
.into_path(),
96157
};
97158

98-
let mut num_tests = 1;
99-
let mut num_ok: usize = 0;
159+
let mut results: Vec<TestResult> = Vec::new();
100160

101161
if test_case == "all" {
102162
let all_tests = test_cases();
103-
num_tests = all_tests.len();
104163
let max_name_len = all_tests.iter().map(|t| t.name.len()).max().unwrap_or(0);
105164

106165
for TestCase { name, test: _ } in all_tests {
107-
num_ok +=
108-
run_single_test(name, &base_dir, keep_all, max_name_len).context(name)? as usize;
166+
results.push(run_single_test(name, &base_dir, keep_all, max_name_len).context(name)?);
109167
}
110168
} else {
111169
let max_name_len = test_case.len();
112-
num_ok += run_single_test(test_case, &base_dir, keep_all, max_name_len)
113-
.context(test_case.to_string())? as usize;
170+
results.push(
171+
run_single_test(test_case, &base_dir, keep_all, max_name_len)
172+
.context(test_case.to_string())?,
173+
);
174+
}
175+
176+
let num_tests = results.len();
177+
let num_ok = results.iter().filter(|r| r.passed).count();
178+
179+
// Write GitHub Actions summary if requested
180+
if github_summary {
181+
write_github_summary(&results, num_ok, num_tests)?;
114182
}
115183

116184
let num_failures = num_tests - num_ok;
@@ -140,6 +208,9 @@ enum CliCommand {
140208
/// Keep all test artifacts even on success
141209
#[arg(long)]
142210
keep_all: bool,
211+
/// Write test results to GitHub Actions job summary ($GITHUB_STEP_SUMMARY)
212+
#[arg(long)]
213+
github_summary: bool,
143214
},
144215
StartVm {
145216
#[arg(long)]
@@ -155,6 +226,7 @@ impl Default for CliCommand {
155226
test_case: "all".to_string(),
156227
base_dir: None,
157228
keep_all: false,
229+
github_summary: false,
158230
}
159231
}
160232
}
@@ -175,6 +247,7 @@ fn main() -> anyhow::Result<()> {
175247
test_case,
176248
base_dir,
177249
keep_all,
178-
} => run_tests(&test_case, base_dir, keep_all),
250+
github_summary,
251+
} => run_tests(&test_case, base_dir, keep_all, github_summary),
179252
}
180253
}

0 commit comments

Comments
 (0)