wumpus_hunter/
lib.rs

1//! Run test suite of a project repeatedly, to find flaky tests that
2//! only sometimes fail.
3//!
4//! It is surprisingly easy to accidentally implement an automated
5//! test so that it sometimes fails. This might be, for example, when
6//! there is a race condition in the test, or in the code under test.
7//! The wumpus hunter helps find such tests, by running them
8//! repeatedly.
9//!
10//! The wumpus hunter is configured to run a test command for a given
11//! git repository and branch in that repository. It clones the
12//! repository, and pulls updates, and runs the command, either for a
13//! given number of times, or indefinitely.
14//!
15//! The wumpus hunter is primarily a command line program, but is
16//! exposed also as a library in the hope it can be useful for
17//! building other useful tools.
18
19#![deny(missing_docs)]
20
21use std::{path::PathBuf, time::SystemTime};
22
23use radicle::git::Oid;
24
25use clap::Parser;
26use time::{macros::format_description, OffsetDateTime};
27
28pub mod report;
29pub mod run;
30pub(crate) mod runlog;
31pub mod spec;
32
33const CSS: &str = include_str!("wumpus.css");
34const STATS_TXT: &str = "stats.txt";
35const SUCCESS: &str = "SUCCESS";
36const FAILURE: &str = "FAILURE";
37
38/// The command line arguments for the wumpus hunter.
39#[derive(Debug, Parser)]
40#[clap(version)]
41pub struct Args {
42    /// The sub-command.
43    #[clap(subcommand)]
44    pub cmd: Cmd,
45}
46
47/// The sub-commands for the wumpus hunter.
48#[derive(Debug, Parser)]
49#[allow(missing_docs)]
50pub enum Cmd {
51    Run(run::Run),
52    Report(ReportCmd),
53    DummyLog(DummyLog),
54}
55
56/// Sub-command to produce a dummy HTML report.
57///
58/// This is only useful for testing changes to the wumpus hunter
59/// report generation.
60#[derive(Debug, Parser)]
61pub struct ReportCmd {
62    stats: PathBuf,
63    spec: PathBuf,
64    git: PathBuf,
65}
66
67impl ReportCmd {
68    /// Run the `report` sub-command.
69    pub fn run(&self) -> anyhow::Result<()> {
70        let spec = spec::Spec::from_file(&self.spec)?;
71        let report = report::Report::new(&spec.description, &self.stats)?;
72        print!("{}", report.as_html(&spec, &self.git));
73        Ok(())
74    }
75}
76
77pub(crate) fn timestamp(when: &SystemTime) -> String {
78    let fmt = format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]Z");
79    OffsetDateTime::from(*when).format(fmt).ok().unwrap()
80}
81
82/// Sub-command to produce a dummy HTML run log.
83///
84/// This is only useful for testing changes to the wumpus hunter
85/// report generation.
86#[derive(Debug, Parser)]
87pub struct DummyLog {}
88
89impl DummyLog {
90    /// Run the `report` sub-command.
91    pub fn run(&self) {
92        let mut run_log = runlog::RunLog::default();
93        run_log.url("https://liw.fi");
94        run_log.git_ref("master");
95        run_log.git_commit(Oid::try_from("06f52d8a538e79b09bd8252a1346542ed179cd60").unwrap());
96        run_log.runcmd(
97            &["git", "clean", "-fdx"],
98            0,
99            "This is stdout",
100            "This is stderr",
101        );
102        println!("{}", run_log.as_html());
103    }
104}