From aa1c9472c922d5ef9711cf5720d29d5fb00f5791 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Wed, 31 Jan 2024 21:23:25 -0500 Subject: [PATCH] ci: test status now in json Signed-off-by: Kent Overstreet --- ci-web/Cargo.lock | 36 ++++++------- ci-web/Cargo.toml | 5 +- ci-web/src/bin/cgi.rs | 7 +-- ci-web/src/bin/gen-commit-summary.rs | 3 +- ci-web/src/lib.rs | 76 ++++++++++++++++++---------- ci-web/src/testresult.capnp | 1 + lib/supervisor.c | 29 +++++------ tests/prelude.sh | 14 ++++- 8 files changed, 101 insertions(+), 70 deletions(-) diff --git a/ci-web/Cargo.lock b/ci-web/Cargo.lock index 4bc9e4c0..87041f85 100644 --- a/ci-web/Cargo.lock +++ b/ci-web/Cargo.lock @@ -157,6 +157,7 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", "windows-targets", ] @@ -174,14 +175,13 @@ dependencies = [ "die", "file-lock", "git2", - "glob", "libc", "memoize", - "multimap", "querystring", "regex", "serde", "serde_derive", + "serde_json", "toml", ] @@ -289,12 +289,6 @@ dependencies = [ "url", ] -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - [[package]] name = "heck" version = "0.4.1" @@ -455,15 +449,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" -dependencies = [ - "serde", -] - [[package]] name = "num-traits" version = "0.2.17" @@ -562,6 +547,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + [[package]] name = "serde" version = "1.0.196" @@ -582,6 +573,17 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "serde_json" +version = "1.0.113" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "strsim" version = "0.10.0" diff --git a/ci-web/Cargo.toml b/ci-web/Cargo.toml index 3a28d1cb..85938249 100644 --- a/ci-web/Cargo.toml +++ b/ci-web/Cargo.toml @@ -11,7 +11,6 @@ build = "src/build.rs" cgi = "0.6" git2 = "0.16" querystring = "1.1.0" -multimap = "0.8.3" die = "0.2.0" libc = "0.2" toml = "0.5.9" @@ -19,12 +18,12 @@ serde = "1.0.145" serde_derive = "1.0.145" regex = "1" memoize = "0.3.1" -glob = "0.3.0" clap = { version = "4.0.32", features = ["derive"] } file-lock = "2.1.6" capnp = "0.19.*" anyhow = "1.0.71" -chrono = "0.4.26" +chrono = { version = "0.4.26", features = ["serde"] } +serde_json = "1.0.113" [build-dependencies] capnpc = "0.19.*" diff --git a/ci-web/src/bin/cgi.rs b/ci-web/src/bin/cgi.rs index 31137ca4..e6b6bf0f 100644 --- a/ci-web/src/bin/cgi.rs +++ b/ci-web/src/bin/cgi.rs @@ -13,7 +13,7 @@ const STYLESHEET: &str = "bootstrap.min.css"; fn filter_results(r: TestResultsMap, tests_matching: &Regex) -> TestResultsMap { r.iter() .filter(|i| tests_matching.is_match(&i.0) ) - .map(|(k, v)| (k.clone(), *v)) + .map(|(k, v)| (k.clone(), v.clone())) .collect() } @@ -45,9 +45,9 @@ fn commit_get_results(ci: &Ci, commit: &git2::Commit) -> CommitResults { let tests = commitdir_get_results_filtered(ci, &id); CommitResults { - id: id, + id, message: commit.message().unwrap().to_string(), - tests: tests, + tests, } } @@ -254,6 +254,7 @@ fn ci_commit(ci: &Ci) -> cgi::Response { writeln!(&mut out, "", result.status.table_class()).unwrap(); writeln!(&mut out, " {} ", name).unwrap(); writeln!(&mut out, " {} ", result.status.to_str()).unwrap(); + writeln!(&mut out, " {} ", result.msg.unwrap_or("".to_string())).unwrap(); writeln!(&mut out, " {}s ", result.duration).unwrap(); writeln!(&mut out, " log ", &commit_id, name).unwrap(); writeln!(&mut out, " full log ", &commit_id, name).unwrap(); diff --git a/ci-web/src/bin/gen-commit-summary.rs b/ci-web/src/bin/gen-commit-summary.rs index 8ec43d1b..63e6ea67 100644 --- a/ci-web/src/bin/gen-commit-summary.rs +++ b/ci-web/src/bin/gen-commit-summary.rs @@ -18,5 +18,6 @@ fn main() { } let ktestrc = ktestrc.unwrap(); - commit_update_results_from_fs(&ktestrc, &args.commit); + let commit_dir = ktestrc.output_dir.join(&args.commit); + commit_update_results_from_fs(&commit_dir); } diff --git a/ci-web/src/lib.rs b/ci-web/src/lib.rs index 83553288..877925da 100644 --- a/ci-web/src/lib.rs +++ b/ci-web/src/lib.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; use std::fs::read_to_string; use std::fs::File; use std::io::prelude::*; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use serde_derive::Deserialize; use toml; use anyhow; @@ -58,17 +58,19 @@ pub use testresult_capnp::test_result::Status as TestStatus; impl TestStatus { fn from_str(status: &str) -> TestStatus { + let status = status.to_lowercase(); + if status.is_empty() { TestStatus::Inprogress - } else if status.contains("IN PROGRESS") { + } else if status.contains("in progress") { TestStatus::Inprogress - } else if status.contains("PASSED") { + } else if status.contains("passed") { TestStatus::Passed - } else if status.contains("FAILED") { + } else if status.contains("failed") { TestStatus::Failed - } else if status.contains("NOTRUN") { + } else if status.contains("notrun") { TestStatus::Notrun - } else if status.contains("NOT STARTED") { + } else if status.contains("not started") { TestStatus::Notstarted } else { TestStatus::Unknown @@ -98,35 +100,54 @@ impl TestStatus { } } -#[derive(Copy, Clone, Debug)] +#[derive(Clone, Debug, Deserialize)] +pub struct TestResultJson { + pub status: String, + pub msg: Option, + pub duration: u64, +} + +#[derive(Clone, Debug)] pub struct TestResult { - pub status: TestStatus, - pub starttime: DateTime, - pub duration: u64, + pub status: TestStatus, + pub msg: Option, + pub starttime: DateTime, + pub duration: u64, } pub type TestResultsMap = BTreeMap; -fn commitdir_get_results_fs(ktestrc: &Ktestrc, commit_id: &String) -> TestResultsMap { +fn commitdir_get_results_fs(commit_dir: &Path) -> TestResultsMap { fn read_test_result(testdir: &std::fs::DirEntry) -> Option { let mut f = File::open(&testdir.path().join("status")).ok()?; - let mut status = String::new(); - f.read_to_string(&mut status).ok()?; + let mut status_str = String::new(); + f.read_to_string(&mut status_str).ok()?; + + let status_str = status_str.trim(); + + println!("status_str: {:?}", status_str); + + let status_json: TestResultJson = serde_json::from_str(&status_str).ok()?; + + println!("status_json: {:?}", status_json); Some(TestResult { - status: TestStatus::from_str(&status), - starttime: f.metadata().ok()?.modified().ok()?.into(), - duration: read_to_string(&testdir.path().join("duration")).unwrap_or("0".to_string()).parse().unwrap_or(0), + status: TestStatus::from_str(&status_json.status), + msg: status_json.msg, + starttime: f.metadata().ok()?.modified().ok()?.into(), + duration: status_json.duration, }) } let mut results = BTreeMap::new(); - let results_dir = ktestrc.output_dir.join(commit_id).read_dir(); + let results_dir = commit_dir.read_dir(); if let Ok(results_dir) = results_dir { for d in results_dir.filter_map(|i| i.ok()) { - if let Some(r) = read_test_result(&d) { + let r = read_test_result(&d); + println!("{:?}", r); + if let Some(r) = r { results.insert(d.file_name().into_string().unwrap(), r); } } @@ -138,7 +159,7 @@ fn commitdir_get_results_fs(ktestrc: &Ktestrc, commit_id: &String) -> TestResult use testresult_capnp::test_results; use capnp::serialize; -fn results_to_capnp(ktestrc: &Ktestrc, commit_id: &String, results_in: &TestResultsMap) -> anyhow::Result<()> { +fn results_to_capnp(commit_dir: &Path, results_in: &TestResultsMap) -> anyhow::Result<()> { let mut message = capnp::message::Builder::new_default(); let results = message.init_root::(); let mut result_list = results.init_entries(results_in.len().try_into().unwrap()); @@ -151,8 +172,8 @@ fn results_to_capnp(ktestrc: &Ktestrc, commit_id: &String, results_in: &TestResu result.set_status(result_in.status); } - let fname = ktestrc.output_dir.join(commit_id.clone() + ".capnp"); - let fname_new = ktestrc.output_dir.join(commit_id.clone() + ".capnp.new"); + let fname = commit_dir.with_extension("capnp"); + let fname_new = commit_dir.with_extension("capnp.new"); let mut out = File::create(&fname_new)?; @@ -163,10 +184,10 @@ fn results_to_capnp(ktestrc: &Ktestrc, commit_id: &String, results_in: &TestResu Ok(()) } -pub fn commit_update_results_from_fs(ktestrc: &Ktestrc, commit_id: &String) { - let results = commitdir_get_results_fs(&ktestrc, commit_id); +pub fn commit_update_results_from_fs(commit_dir: &Path) { + let results = commitdir_get_results_fs(commit_dir); - results_to_capnp(ktestrc, commit_id, &results) + results_to_capnp(commit_dir, &results) .map_err(|e| eprintln!("error generating capnp: {}", e)).ok(); } @@ -180,9 +201,10 @@ pub fn commitdir_get_results(ktestrc: &Ktestrc, commit_id: &String) -> anyhow::R let mut results = BTreeMap::new(); for e in entries { let r = TestResult { - status: e.get_status()?, - starttime: Utc.timestamp_opt(e.get_starttime(), 0).unwrap(), - duration: e.get_duration() + status: e.get_status()?, + msg: e.get_msg()?.to_string().ok(), + starttime: Utc.timestamp_opt(e.get_starttime(), 0).unwrap(), + duration: e.get_duration() }; results.insert(e.get_name()?.to_string()?, r); diff --git a/ci-web/src/testresult.capnp b/ci-web/src/testresult.capnp index 56999548..7bc49b77 100644 --- a/ci-web/src/testresult.capnp +++ b/ci-web/src/testresult.capnp @@ -13,6 +13,7 @@ struct TestResult { notstarted @4; unknown @5; } + msg @4: Text; } struct TestResults { diff --git a/lib/supervisor.c b/lib/supervisor.c index f1d95cc4..f357cf07 100644 --- a/lib/supervisor.c +++ b/lib/supervisor.c @@ -149,11 +149,9 @@ static char *test_is_starting(const char *line) return ret; } -static bool test_is_ending(char *line) +static const char *test_is_ending(const char *line) { - return str_starts_with(line, "========= PASSED ") || - str_starts_with(line, "========= FAILED ") || - str_starts_with(line, "========= NOTRUN"); + return str_starts_with(line, "========= FINISH "); } static FILE *popen_with_pid(char *argv[], pid_t *child) @@ -224,13 +222,10 @@ static void test_start(char *new_test, struct timespec now) set_timeout(default_timeout); } -static void test_end(struct timespec now) +static void test_end(void) { - write_test_file("duration", "%li", now.tv_sec - current_test_start.tv_sec); - fclose(current_test_log); current_test_log = NULL; - set_timeout(default_timeout); } @@ -327,19 +322,17 @@ int main(int argc, char *argv[]) set_timeout(default_timeout); again: while ((len = getline(&line, &n, childf)) >= 0) { - struct timespec now = xclock_gettime(CLOCK_MONOTONIC); - strim(line); - char *output = mprintf("%.5lu %s\n", now.tv_sec - start.tv_sec, line); - read_watchdog(line); - char *new_test = test_is_starting(line); + struct timespec now = xclock_gettime(CLOCK_MONOTONIC); + char *output = mprintf("%.5lu %s\n", now.tv_sec - start.tv_sec, line); /* If a test is starting, close logfile for previous test: */ + char *new_test = test_is_starting(line); if (current_test_log && new_test) - test_end(now); + test_end(); if (new_test) test_start(new_test, now); @@ -349,9 +342,11 @@ int main(int argc, char *argv[]) fputs(output, logfile); fputs(output, stdout); - if (current_test_log && test_is_ending(line)) { - write_test_file("status", "%s\n", line); - test_end(now); + const char *statusline; + if (current_test_log && + (statusline = test_is_ending(line))) { + write_test_file("status", "%s\n", statusline); + test_end(); } if (exit_on_failure && str_starts_with(line, "TEST FAILED")) diff --git a/tests/prelude.sh b/tests/prelude.sh index 46ff822b..598b2048 100644 --- a/tests/prelude.sh +++ b/tests/prelude.sh @@ -237,6 +237,8 @@ run_tests() local start=$(date '+%s') local ret=0 + + rm -f /tmp/ktest-test-msg (set -e; run_test $i) ret=$? local finish=$(date '+%s') @@ -249,12 +251,20 @@ run_tests() echo if [[ $ret = 0 ]]; then - echo "========= PASSED $i in $(($finish - $start))s" + local status=passed tests_passed+=($i) else - echo "========= FAILED $i in $(($finish - $start))s" + local status=failed tests_failed+=($i) + fi + + if [[ ! -f /tmp/ktest-test-msg ]]; then + echo "========= FINISH { \"status\": \"$status\", \"duration\": $(($finish - $start)) }" + else + echo "========= FINISH { \"status\": \"$status\", \"duration\": $(($finish - $start)), \"statusline\": \"$(cat /tmp/ktest-test-msg)\" }" + fi + if [[ $ret != 0 ]]; then # Try to clean up after a failed test so we can run the rest of # the tests - unless failfast is enabled, or there was only one # test to run: