use inquest::commands::{
AnalyzeIsolationCommand, Command, FailingCommand, InitCommand, LastCommand, StatsCommand,
};
use inquest::repository::{RepositoryFactory, TestResult, TestRun};
use inquest::ui::UI;
use std::fs;
use std::io::Write;
use tempfile::TempDir;
struct TestUI {
output: Vec<String>,
errors: Vec<String>,
bytes_output: Vec<Vec<u8>>,
}
impl TestUI {
fn new() -> Self {
TestUI {
output: Vec::new(),
errors: Vec::new(),
bytes_output: Vec::new(),
}
}
}
impl UI for TestUI {
fn output(&mut self, message: &str) -> inquest::error::Result<()> {
self.output.push(message.to_string());
Ok(())
}
fn error(&mut self, message: &str) -> inquest::error::Result<()> {
self.errors.push(message.to_string());
Ok(())
}
fn warning(&mut self, message: &str) -> inquest::error::Result<()> {
self.errors.push(format!("Warning: {}", message));
Ok(())
}
fn output_bytes(&mut self, bytes: &[u8]) -> inquest::error::Result<()> {
self.bytes_output.push(bytes.to_vec());
Ok(())
}
}
#[test]
fn test_full_workflow_init_load_last() {
let temp = TempDir::new().unwrap();
let base_path = temp.path().to_string_lossy().to_string();
let mut ui = TestUI::new();
let init_cmd = InitCommand::new(Some(base_path.clone()));
let result = init_cmd.execute(&mut ui);
assert_eq!(result.unwrap(), 0);
assert!(ui.output[0].contains("Initialized"));
assert!(temp.path().join(".testrepository").exists());
assert!(temp.path().join(".testrepository/format").exists());
let mut test_run = TestRun::new("0".to_string());
test_run.timestamp = chrono::DateTime::from_timestamp(1000000000, 0).unwrap();
test_run.add_result(TestResult::success("test1"));
test_run.add_result(TestResult::failure("test2", "Failed"));
test_run.add_result(TestResult::success("test3"));
let factory = inquest::repository::file::FileRepositoryFactory;
let mut repo = factory.open(temp.path()).unwrap();
repo.insert_test_run(test_run).unwrap();
let mut ui = TestUI::new();
let stats_cmd = StatsCommand::new(Some(base_path.clone()));
let result = stats_cmd.execute(&mut ui);
assert_eq!(result.unwrap(), 0);
assert_eq!(ui.output.len(), 6);
assert_eq!(ui.output[0], "Repository Statistics:");
assert_eq!(ui.output[1], " Total test runs: 1");
assert_eq!(ui.output[2], " Latest run: 0");
assert_eq!(ui.output[3], " Tests in latest run: 3");
assert_eq!(ui.output[4], " Failures in latest run: 1");
assert_eq!(ui.output[5], " Total tests executed: 3");
let mut ui = TestUI::new();
let last_cmd = LastCommand::new(Some(base_path.clone()));
let result = last_cmd.execute(&mut ui);
assert_eq!(result.unwrap(), 1);
assert_eq!(ui.output.len(), 8);
assert_eq!(ui.output[0], "Test run: 0");
assert!(ui.output[1].starts_with("Timestamp: "));
assert_eq!(ui.output[2], "Total tests: 3");
assert_eq!(ui.output[3], "Passed: 2");
assert_eq!(ui.output[4], "Failed: 1");
assert_eq!(ui.output[5], "");
assert_eq!(ui.output[6], "Failed tests:");
assert_eq!(ui.output[7], " test2");
assert_eq!(ui.bytes_output.len(), 0);
}
#[test]
fn test_workflow_with_failing_tests() {
let temp = TempDir::new().unwrap();
let base_path = temp.path().to_string_lossy().to_string();
let mut ui = TestUI::new();
let init_cmd = InitCommand::new(Some(base_path.clone()));
init_cmd.execute(&mut ui).unwrap();
let mut run1 = TestRun::new("0".to_string());
run1.timestamp = chrono::DateTime::from_timestamp(1000000000, 0).unwrap();
run1.add_result(TestResult::success("test1"));
run1.add_result(TestResult::failure("test2", "Error"));
run1.add_result(TestResult::failure("test3", "Error"));
let factory = inquest::repository::file::FileRepositoryFactory;
let mut repo = factory.open(temp.path()).unwrap();
repo.insert_test_run_partial(run1, false).unwrap();
let mut ui = TestUI::new();
let failing_cmd = FailingCommand::new(Some(base_path.clone()));
let result = failing_cmd.execute(&mut ui);
assert_eq!(result.unwrap(), 1); assert_eq!(ui.output[0], "2 failing test(s):");
assert!(ui.output[1] == " test2" || ui.output[1] == " test3");
assert!(ui.output[2] == " test2" || ui.output[2] == " test3");
assert_ne!(ui.output[1], ui.output[2]);
let mut run2 = TestRun::new("1".to_string());
run2.timestamp = chrono::DateTime::from_timestamp(1000000001, 0).unwrap();
run2.add_result(TestResult::success("test1"));
run2.add_result(TestResult::success("test2"));
run2.add_result(TestResult::failure("test3", "Still failing"));
repo.insert_test_run_partial(run2, false).unwrap();
let mut ui = TestUI::new();
let failing_cmd = FailingCommand::new(Some(base_path));
let result = failing_cmd.execute(&mut ui);
assert_eq!(result.unwrap(), 1); assert_eq!(ui.output.len(), 2);
assert_eq!(ui.output[0], "1 failing test(s):");
assert_eq!(ui.output[1], " test3");
}
#[test]
fn test_workflow_partial_mode() {
let temp = TempDir::new().unwrap();
let base_path = temp.path().to_string_lossy().to_string();
let mut ui = TestUI::new();
let init_cmd = InitCommand::new(Some(base_path.clone()));
init_cmd.execute(&mut ui).unwrap();
let factory = inquest::repository::file::FileRepositoryFactory;
let mut repo = factory.open(temp.path()).unwrap();
let mut run1 = TestRun::new("0".to_string());
run1.timestamp = chrono::DateTime::from_timestamp(1000000000, 0).unwrap();
run1.add_result(TestResult::failure("test1", "Error"));
run1.add_result(TestResult::failure("test2", "Error"));
run1.add_result(TestResult::success("test3"));
repo.insert_test_run_partial(run1, false).unwrap();
let failing = repo.get_failing_tests().unwrap();
assert_eq!(failing.len(), 2);
let mut run2 = TestRun::new("1".to_string());
run2.timestamp = chrono::DateTime::from_timestamp(1000000001, 0).unwrap();
run2.add_result(TestResult::success("test1"));
repo.insert_test_run_partial(run2, true).unwrap();
let failing = repo.get_failing_tests().unwrap();
assert_eq!(failing.len(), 1);
assert!(failing.iter().any(|id| id.as_str() == "test2"));
assert!(!failing.iter().any(|id| id.as_str() == "test1"));
}
#[test]
fn test_workflow_with_load_list() {
let temp = TempDir::new().unwrap();
let test_list_path = temp.path().join("tests.txt");
let mut file = fs::File::create(&test_list_path).unwrap();
writeln!(file, "test1").unwrap();
writeln!(file, "test3").unwrap();
writeln!(file, "test5").unwrap();
let test_ids = inquest::testlist::parse_list_file(&test_list_path).unwrap();
assert_eq!(test_ids.len(), 3);
assert_eq!(test_ids[0].as_str(), "test1");
assert_eq!(test_ids[1].as_str(), "test3");
assert_eq!(test_ids[2].as_str(), "test5");
}
#[test]
fn test_workflow_times_database() {
let temp = TempDir::new().unwrap();
let base_path = temp.path().to_string_lossy().to_string();
let mut ui = TestUI::new();
let init_cmd = InitCommand::new(Some(base_path));
init_cmd.execute(&mut ui).unwrap();
let factory = inquest::repository::file::FileRepositoryFactory;
let mut repo = factory.open(temp.path()).unwrap();
let mut run = TestRun::new("0".to_string());
run.timestamp = chrono::DateTime::from_timestamp(1000000000, 0).unwrap();
run.add_result(
TestResult::success("test1").with_duration(std::time::Duration::from_secs_f64(1.5)),
);
run.add_result(
TestResult::success("test2").with_duration(std::time::Duration::from_secs_f64(0.3)),
);
repo.insert_test_run(run).unwrap();
let times_path = temp.path().join(".testrepository/times.dbm");
assert!(times_path.exists());
}
#[test]
fn test_workflow_list_flag() {
let temp = TempDir::new().unwrap();
let base_path = temp.path().to_string_lossy().to_string();
let mut ui = TestUI::new();
let init_cmd = InitCommand::new(Some(base_path.clone()));
init_cmd.execute(&mut ui).unwrap();
let factory = inquest::repository::file::FileRepositoryFactory;
let mut repo = factory.open(temp.path()).unwrap();
let mut run = TestRun::new("0".to_string());
run.timestamp = chrono::DateTime::from_timestamp(1000000000, 0).unwrap();
run.add_result(TestResult::failure("test1", "Error"));
run.add_result(TestResult::failure("test2", "Error"));
run.add_result(TestResult::success("test3"));
repo.insert_test_run_partial(run, false).unwrap();
let mut ui = TestUI::new();
let failing_cmd = FailingCommand::with_list_only(Some(base_path));
let result = failing_cmd.execute(&mut ui);
assert_eq!(result.unwrap(), 1);
assert_eq!(ui.output.len(), 2);
assert!(ui.output[0] == "test1" || ui.output[0] == "test2");
assert!(ui.output[1] == "test1" || ui.output[1] == "test2");
assert_ne!(ui.output[0], ui.output[1]);
}
#[test]
fn test_error_handling_no_repository() {
let temp = TempDir::new().unwrap();
let base_path = temp.path().to_string_lossy().to_string();
let mut ui = TestUI::new();
let last_cmd = LastCommand::new(Some(base_path));
let result = last_cmd.execute(&mut ui);
assert!(result.is_err());
}
#[test]
fn test_parallel_execution() {
use inquest::commands::RunCommand;
use inquest::repository::file::FileRepositoryFactory;
let temp = TempDir::new().unwrap();
let base_path = temp.path().to_string_lossy().to_string();
let factory = FileRepositoryFactory;
factory.initialise(temp.path()).unwrap();
let config = r#"
[DEFAULT]
test_command=python3 -c "import sys; import time; sys.stdout.buffer.write(b'\xb3\x29\x00\x16test1\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb3'); sys.stdout.buffer.flush()"
"#;
fs::write(temp.path().join(".testr.conf"), config).unwrap();
let mut ui = TestUI::new();
let cmd = RunCommand::with_all_options(
Some(base_path.clone()),
false, false, false, None, Some(2), false, false, false, false, None, None, );
let _result = cmd.execute(&mut ui);
assert!(!ui.output.is_empty());
}
#[test]
fn test_parallel_execution_with_worker_tags() {
use inquest::partition::partition_tests;
use inquest::repository::TestId;
use std::collections::HashMap;
let test_ids = vec![
TestId::new("test1"),
TestId::new("test2"),
TestId::new("test3"),
TestId::new("test4"),
];
let partitions = partition_tests(&test_ids, &HashMap::new(), 2);
assert_eq!(partitions.len(), 2);
let total_tests: usize = partitions.iter().map(|p| p.len()).sum();
assert_eq!(total_tests, 4);
assert!(!partitions[0].is_empty());
assert!(!partitions[1].is_empty());
}
#[test]
fn test_until_failure_flag_behavior() {
use inquest::commands::RunCommand;
use inquest::repository::file::FileRepositoryFactory;
let temp = TempDir::new().unwrap();
let base_path = temp.path().to_string_lossy().to_string();
let factory = FileRepositoryFactory;
factory.initialise(temp.path()).unwrap();
let config = r#"
[DEFAULT]
test_command=echo ""
"#;
fs::write(temp.path().join(".testr.conf"), config).unwrap();
let cmd = RunCommand::with_all_options(
Some(base_path.clone()),
false, false, false, None, None, true, false, false, false, None, None, );
assert_eq!(cmd.name(), "run");
}
#[test]
fn test_isolated_flag_behavior() {
use inquest::commands::RunCommand;
use inquest::repository::file::FileRepositoryFactory;
let temp = TempDir::new().unwrap();
let base_path = temp.path().to_string_lossy().to_string();
let factory = FileRepositoryFactory;
factory.initialise(temp.path()).unwrap();
let config = r#"
[DEFAULT]
test_command=echo ""
"#;
fs::write(temp.path().join(".testr.conf"), config).unwrap();
let cmd = RunCommand::with_all_options(
Some(base_path.clone()),
false, false, false, None, None, false, true, false, false, None, None, );
assert_eq!(cmd.name(), "run");
}
#[test]
fn test_analyze_isolation_command_no_repository() {
let temp = TempDir::new().unwrap();
let base_path = temp.path().to_string_lossy().to_string();
let mut ui = TestUI::new();
let cmd = AnalyzeIsolationCommand::new(Some(base_path.clone()), "test_example".to_string());
let result = cmd.execute(&mut ui);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Repository not found"));
}
#[test]
fn test_analyze_isolation_command_basic() {
let temp = TempDir::new().unwrap();
let base_path = temp.path().to_string_lossy().to_string();
let mut ui = TestUI::new();
let init_cmd = InitCommand::new(Some(base_path.clone()));
init_cmd.execute(&mut ui).unwrap();
let config = r#"
[DEFAULT]
test_command=printf "test: test_target\nsuccess: test_target\n" | python3 -c "import sys; sys.stdout.buffer.write(b'\xb3)\x00\x00\x01\x1btest: test_target\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\xb3*\x00\x00\x00\x1asuccess: test_target\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05')"
test_list_option=--list
"#;
fs::write(temp.path().join(".testr.conf"), config).unwrap();
let cmd = AnalyzeIsolationCommand::new(Some(base_path), "test_target".to_string());
assert_eq!(cmd.name(), "analyze-isolation");
assert_eq!(cmd.help(), "Analyze test isolation issues using bisection");
}
#[test]
fn test_group_regex_with_parallel_execution() {
use inquest::testcommand::TestCommand;
let temp = TempDir::new().unwrap();
let base_path = temp.path().to_string_lossy().to_string();
let mut ui = TestUI::new();
let init_cmd = InitCommand::new(Some(base_path.clone()));
init_cmd.execute(&mut ui).unwrap();
let config = r#"
[DEFAULT]
test_command=echo ""
test_list_option=--list
group_regex=^([^.]+)\.
"#;
fs::write(temp.path().join(".testr.conf"), config).unwrap();
let test_cmd = TestCommand::from_directory(temp.path()).unwrap();
assert_eq!(
test_cmd.config().group_regex,
Some("^([^.]+)\\.".to_string())
);
}
#[test]
fn test_run_concurrency_callout() {
use inquest::testcommand::TestCommand;
let temp = TempDir::new().unwrap();
let base_path = temp.path().to_string_lossy().to_string();
let mut ui = TestUI::new();
let init_cmd = InitCommand::new(Some(base_path.clone()));
init_cmd.execute(&mut ui).unwrap();
let config = r#"
[DEFAULT]
test_command=echo ""
test_list_option=--list
test_run_concurrency=echo 2
"#;
fs::write(temp.path().join(".testr.conf"), config).unwrap();
let test_cmd = TestCommand::from_directory(temp.path()).unwrap();
let concurrency = test_cmd.get_concurrency().unwrap();
assert_eq!(concurrency, Some(2));
}