use pymute::mutants::{find_mutants, MutationType};
use pymute::runner;
use rand::{seq::IteratorRandom, SeedableRng};
use rand_chacha::ChaCha8Rng;
use clap::Parser;
use std::path::PathBuf;
#[derive(Debug, Parser)]
#[command(author, version, about, long_about = None)]
#[command(propagate_version = true)]
pub struct Cli {
root: PathBuf,
#[arg(short, long)]
#[arg(default_value = "**/*.py")]
modules: String,
#[arg(short, long)]
#[arg(default_value = ".")]
tests: String,
#[arg(short, long)]
num_threads: Option<usize>,
#[arg(short, long)]
#[arg(value_enum)]
#[arg(default_value_t = runner::OutputLevel::Missed)]
output_level: runner::OutputLevel,
#[arg(short, long)]
#[arg(value_enum)]
#[arg(default_value_t = runner::Runner::Pytest)]
runner: runner::Runner,
#[arg(short, long)]
#[arg(value_enum)]
environment: Option<String>,
#[arg(long)]
max_mutants: Option<usize>,
#[arg(long)]
#[arg(value_enum)]
#[arg(default_values_t = [
MutationType::MathOps,
MutationType::Conjunctions,
MutationType::Booleans,
MutationType::ControlFlow,
MutationType::CompOps,
MutationType::Numbers,
], value_delimiter=',')]
mutation_types: Vec<MutationType>,
#[arg(short, long)]
inplace: bool,
#[arg(short, long)]
list: bool,
#[arg(short, long)]
#[arg(default_value = "42")]
seed: u64,
}
fn main() {
let args = Cli::parse();
let modules: PathBuf = [&args.root, &args.modules.into()].iter().collect();
let mutants = match args.max_mutants {
Some(max) => {
let mut rng = ChaCha8Rng::seed_from_u64(args.seed);
find_mutants(
modules
.into_os_string()
.to_str()
.expect("Invalid Glob Expression?"),
&args.mutation_types,
)
.expect("Failed to find mutants!")
.into_iter()
.choose_multiple(&mut rng, max)
.into_iter()
.collect()
}
None => find_mutants(
modules
.into_os_string()
.to_str()
.expect("Invalid Glob Expression?"),
&args.mutation_types,
)
.expect("Failed to find mutants!"),
};
if args.list {
for mutant in &mutants {
println!("{mutant}");
}
return;
}
let _n_mutants = mutants.len();
if args.inplace {
runner::run_mutants_inplace(
&args.root,
&mutants,
&args.runner,
&args.tests,
&args.environment,
&args.output_level,
&args.num_threads,
)
} else if let Some(n) = args.num_threads {
rayon::ThreadPoolBuilder::new()
.num_threads(n)
.build_global()
.expect("Failed to set the number of threads using rayon.");
runner::run_mutants(
&args.root,
&mutants,
&args.runner,
&args.tests,
&args.environment,
&args.output_level,
);
} else {
rayon::ThreadPoolBuilder::new()
.num_threads(1)
.build_global()
.expect("Failed to set the number of threads using rayon.");
runner::run_mutants(
&args.root,
&mutants,
&args.runner,
&args.tests,
&args.environment,
&args.output_level,
);
}
}