use crate::compile;
use crate::download;
use crate::types::*;
use crate::ui;
use crate::version;
use log::debug;
use semver::Version as SemverVersion;
pub fn run_tests<F>(mut matrix: TestMatrix, mut on_result: F) -> Result<Vec<TestResult>, String>
where
F: FnMut(&TestResult),
{
debug!("Starting test execution for {} test pairs", matrix.test_count());
let baseline_count = matrix.base_versions.iter().filter(|v| v.is_baseline).count();
debug_assert!(baseline_count == 1, "Expected exactly 1 baseline, found {}", baseline_count);
for base_spec in &mut matrix.base_versions {
if let Version::Latest = base_spec.crate_ref.version {
let latest = version::resolve_latest_version(&base_spec.crate_ref.name, false)
.map_err(|e| format!("Failed to resolve latest version for {}: {}", base_spec.crate_ref.name, e))?;
base_spec.crate_ref.version = Version::Semver(latest);
}
}
let mut results = Vec::new();
for idx in 0..matrix.dependents.len() {
if let Version::Latest = matrix.dependents[idx].crate_ref.version {
let name = matrix.dependents[idx].crate_ref.name.clone();
let latest = version::resolve_latest_version(&name, false)
.map_err(|e| format!("Failed to resolve latest version for {}: {}", name, e))?;
matrix.dependents[idx].crate_ref.version = Version::Semver(latest);
}
let dependent_spec = &matrix.dependents[idx];
let dependent = &dependent_spec.crate_ref;
let baseline_result = {
let baseline_spec = matrix
.base_versions
.iter()
.find(|v| v.is_baseline)
.ok_or_else(|| "No baseline version found".to_string())?;
debug!("Testing BASELINE {} against {}", baseline_spec.crate_ref.display(), dependent.display());
let execution = run_single_test(baseline_spec, dependent_spec, &matrix)?;
TestResult {
base_version: baseline_spec.crate_ref.clone(),
dependent: dependent.clone(),
execution,
baseline: None, }
};
let baseline_passed = baseline_result.execution.is_success();
let baseline_fetch_passed = baseline_result.execution.fetch.success;
let baseline_check_passed = baseline_result.execution.check.as_ref().map(|c| c.success);
let baseline_test_passed = baseline_result.execution.test.as_ref().map(|t| t.success);
let baseline_spec_requirement = baseline_result.execution.original_requirement.clone();
on_result(&baseline_result); results.push(baseline_result);
for base_spec in matrix.base_versions.iter().filter(|v| !v.is_baseline) {
let base_version = &base_spec.crate_ref;
debug!("Testing {} against {}", base_version.display(), dependent.display());
let execution =
run_single_test_with_spec(base_spec, dependent_spec, &matrix, baseline_spec_requirement.clone())?;
let result = TestResult {
base_version: base_version.clone(),
dependent: dependent.clone(),
execution,
baseline: Some(BaselineComparison {
baseline_passed,
baseline_version: matrix
.base_versions
.iter()
.find(|v| v.is_baseline)
.map(|v| v.crate_ref.version.display())
.unwrap_or_else(|| "unknown".to_string()),
baseline_fetch_passed,
baseline_check_passed,
baseline_test_passed,
}),
};
on_result(&result); results.push(result);
}
}
Ok(results)
}
fn run_single_test(
base_spec: &VersionSpec,
dependent_spec: &VersionSpec,
matrix: &TestMatrix,
) -> Result<compile::ThreeStepResult, String> {
run_single_test_with_spec(base_spec, dependent_spec, matrix, None)
}
fn run_single_test_with_spec(
base_spec: &VersionSpec,
dependent_spec: &VersionSpec,
matrix: &TestMatrix,
original_requirement: Option<String>,
) -> Result<compile::ThreeStepResult, String> {
let base_version = &base_spec.crate_ref;
let dependent = &dependent_spec.crate_ref;
let base_version_str = match &base_version.version {
Version::Semver(v) => v.clone(),
_ => return Err("Version not resolved".to_string()),
};
let dependent_version_str = match &dependent.version {
Version::Semver(v) => v.clone(),
_ => return Err("Dependent version not resolved".to_string()),
};
let dependent_path = match &dependent.source {
CrateSource::Local { path } => path.clone(),
CrateSource::Registry => {
let vers = SemverVersion::parse(&dependent_version_str).map_err(|e| format!("Invalid semver: {}", e))?;
let crate_handle = download::get_crate_handle(&dependent.name, &vers)
.map_err(|e| format!("Failed to download {}: {}", dependent.name, e))?;
let dest = matrix.staging_dir.join(format!("{}-{}", dependent.name, dependent_version_str));
if !dest.exists() {
std::fs::create_dir_all(&dest).map_err(|e| format!("Failed to create staging dir: {}", e))?;
crate_handle
.unpack_source_to(&dest)
.map_err(|e| format!("Failed to unpack {}: {}", dependent.name, e))?;
}
dest
}
CrateSource::Git { .. } => {
return Err("Git sources not yet implemented".to_string());
}
};
let test_config = compile::TestConfig::new(dependent_path.as_path(), &matrix.base_crate)
.with_skip_flags(matrix.skip_check, matrix.skip_test)
.with_version_info(
Some(base_version_str.clone()),
base_spec.override_mode == OverrideMode::Force,
original_requirement, )
.with_patch_transitive(matrix.patch_transitive);
let override_path = if base_spec.override_mode != OverrideMode::None {
match &base_version.source {
CrateSource::Local { path } => {
let dir_path =
if path.ends_with("Cargo.toml") { path.parent().unwrap().to_path_buf() } else { path.clone() };
Some(dir_path)
}
CrateSource::Registry => {
let base_vers =
SemverVersion::parse(&base_version_str).map_err(|e| format!("Invalid semver for base: {}", e))?;
let crate_handle = download::get_crate_handle(&base_version.name, &base_vers)
.map_err(|e| format!("Failed to download {}: {}", base_version.name, e))?;
let dest = matrix.staging_dir.join(format!("{}-{}", base_version.name, base_version_str));
if !dest.exists() {
std::fs::create_dir_all(&dest).map_err(|e| format!("Failed to create staging dir: {}", e))?;
crate_handle
.unpack_source_to(&dest)
.map_err(|e| format!("Failed to unpack {}: {}", base_version.name, e))?;
}
Some(dest)
}
CrateSource::Git { .. } => {
return Err("Git sources not yet implemented".to_string());
}
}
} else {
None
};
let test_config = if let Some(ref path) = override_path {
test_config.with_override_path(path)
} else {
test_config
};
let result = compile::run_three_step_ict(test_config).map_err(|e| format!("Test execution failed: {}", e))?;
result.debug_assert_consistent();
Ok(result)
}
#[cfg(test)]
#[path = "runner_test.rs"]
mod runner_test;