deqp-runner 0.16.1

A VK-GL-CTS/dEQP wrapper program to parallelize it across CPUs and report results against a baseline.
Documentation
use anyhow::Result;
use deqp_runner::gtest_command::GTestCommand;
use deqp_runner::mock_gtest::MockGTest;
use deqp_runner::{
    parallel_test, process_results, CommandLineRunOptions, TestCommand, TestConfiguration,
};
use std::path::PathBuf;
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
#[structopt(
    author = "Emma Anholt <emma@anholt.net>",
    about = "Runs gtest in parallel (gtest binary must be single-threaded)"
)]
struct Opts {
    #[structopt(subcommand)]
    subcmd: SubCommand,
}

#[derive(Debug, StructOpt)]
#[allow(clippy::large_enum_variant)]
enum SubCommand {
    #[structopt(name = "run")]
    Run(Run),

    #[structopt(
        name = "mock-gtest",
        help = "gtest-runner internal mock gtest binary for testing"
    )]
    MockGTest(MockGTest),
}

#[derive(Debug, StructOpt)]
pub struct Run {
    #[structopt(long, help = "path to gtest binary")]
    gtest: PathBuf,

    #[structopt(flatten)]
    common: CommandLineRunOptions,

    #[structopt(
        long,
        default_value = "500",
        help = "Starting number of tests to include in each bin invocation (mitigates startup overhead)"
    )]
    tests_per_group: usize,

    #[structopt(
        long,
        default_value = "0",
        help = "Minimum number of tests to scale down to in each bin invocation (defaults to 0 to match tests_per_group)"
    )]
    min_tests_per_group: usize,

    #[structopt(help = "arguments to gtest binary")]
    gtest_args: Vec<String>,
}

fn main() -> Result<()> {
    let opts = Opts::from_args();

    match opts.subcmd {
        SubCommand::Run(run) => {
            run.common.setup()?;

            let include_filter = run.common.includes_regex()?;

            let gtest = GTestCommand {
                bin: run.gtest,
                config: TestConfiguration::from_cli(&run.common)?,
                args: run.gtest_args,
            };

            let tests = gtest
                .list_tests()?
                .into_iter()
                .skip(run.common.sub_config.fraction_start - 1)
                .step_by(run.common.sub_config.fraction)
                .filter(|x| include_filter.is_match(x.name()))
                .collect();

            let groups = gtest.split_tests_to_groups(
                tests,
                run.tests_per_group,
                run.min_tests_per_group,
                &run.common.sub_config,
                &[include_filter],
            )?;

            println!(
                "Running gtest on {} threads in {}-test groups",
                rayon::current_num_threads(),
                if let Some((_, tests)) = groups.get(0) {
                    tests.len()
                } else {
                    0
                }
            );

            let results = parallel_test(std::io::stdout(), groups)?;
            process_results(&results, &run.common.output_dir, run.common.summary_limit)?;
        }

        SubCommand::MockGTest(mock) => {
            stderrlog::new().module(module_path!()).init().unwrap();

            mock.run();
        }
    }

    Ok(())
}