deflake-rs 0.1.0

cargo-deflake is a command that detects flaky tests based on what tests fail and what code has changed
mod test_interface;
use clap::Parser;
use cli::Cli;
use deflake::Deflake;
use std::{fs, thread::available_parallelism, time::Instant};
use test_interface::{TestCollector, TestMeta, TestResult, TestResults, TestRunner};
mod ast;
mod cli;
mod cov;
mod deflake;
mod git;

static VERSION: &str = "1.0.0";

fn exit(args: &Cli, results: &TestResults, meta: TestMeta) -> ! {
    if let Some(output_file) = &args.output {
        let s = results.serialise(meta);
        fs::write(output_file, s).expect("ERR: Could not write results to file");
    }

    let exit_code = match results.has_failure() {
        true => 1,
        false => 0,
    };
    std::process::exit(exit_code);
}

fn main() {
    let args = Cli::parse();
    //dbg!(&args);

    TestRunner::setup();

    let now = Instant::now();
    println!("Building and collecting tests...");
    let tests = TestCollector::get_tests(&args);

    //let perf_time = now.elapsed().as_millis();
    //println!("PERF: Time getting tests = {}", perf_time);

    println!("Done!");
    TestRunner::cleanup();

    if args.build_only {
        println!("There are {} tests", tests.len());
        std::process::exit(0);
    }

    let batch_size = match args.batch_size {
        Some(s) => s.into(),
        None => match available_parallelism() {
            Ok(i) => i.into(),
            Err(_) => 4,
        },
    };

    let mut results = TestRunner::exec_batch(&tests, batch_size, args.timeout);
    let test_exec_time = now.elapsed().as_millis();

    let mut meta = TestMeta {
        deflaker_version: VERSION.to_string(),
        execution_time: test_exec_time,
        tests: tests.len(),
        deflake_time: None,
    };

    if !results.has_failure() || args.no_deflake || args.no_coverage {
        if args.no_deflake {
            println!("NOTE: not classifying flaky tests")
        }

        let fails = results.failures();
        if fails.len() > 0 {
            println!("Failing tests:");
            for (id, fail) in results.failures() {
                println!(
                    "{} - {} {}",
                    id,
                    fail.0.stdout_path(),
                    if fail.1 == TestResult::Timeout {
                        "(timeout)"
                    } else {
                        ""
                    }
                );
            }
        } else {
            println!("No failures!");
        }
        exit(&args, &results, meta);
    }

    let failures = results.failures_no_timeout();
    let flaky = Deflake::run(&args, failures);
    for flake in flaky {
        results.reclassify(flake, TestResult::Flaky);
    }

    let whole_time = now.elapsed().as_millis();
    meta.deflake_time = Some(whole_time);

    // ------- Printing of results
    println!("Failing tests:");
    for (id, fail) in results.failures() {
        println!("{} - {}", id, fail.0.stdout_path());
    }

    println!("\nFlaky tests:");
    for (id, flake) in results.flaky() {
        println!("{} - {}", id, flake.0.stdout_path());
    }

    exit(&args, &results, meta);
}