use crate::clang::*;
use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
use std::fmt;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{Arc, Condvar, Mutex};
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
pub const X86_DEFAULT_TEST_TIMEOUT_MS: u64 = 30_000;
pub const X86_DEFAULT_MEMORY_LIMIT: u64 = 256 * 1024 * 1024;
pub const X86_DEFAULT_FD_LIMIT: u32 = 1024;
pub const X86_MAX_TEST_NAME_LEN: usize = 256;
pub const X86_MAX_TAGS_PER_TEST: usize = 32;
pub const X86_DEFAULT_THREAD_POOL_SIZE: usize = 4;
pub const X86_SANCOV_COUNTER_SIZE: usize = 1 << 20;
pub const X86_FUZZ_MAX_ITERATIONS: u64 = 1_000_000;
pub const X86_FLAKY_THRESHOLD: u32 = 3;
pub struct X86TestHarness {
pub suites: Vec<X86TestSuite>,
pub config: X86TestHarnessConfig,
pub runner: X86TestRunner,
pub database: X86TestDatabase,
pub coverage: X86CodeCoverage,
pub reporters: Vec<Box<dyn X86TestReporter>>,
pub generators: X86TestGenerators,
pub run_start: Option<SystemTime>,
}
#[derive(Debug, Clone)]
pub struct X86TestHarnessConfig {
pub parallel_workers: usize,
pub timeout_ms: u64,
pub memory_limit: u64,
pub fd_limit: u32,
pub use_subprocess: bool,
pub detect_flaky: bool,
pub random_order: bool,
pub random_seed: u64,
pub filter_tags: Vec<String>,
pub filter_regex: Option<String>,
pub collect_coverage: bool,
pub coverage_type: X86CoverageType,
pub junit_report: bool,
pub json_report: bool,
pub tap_report: bool,
pub html_report: bool,
pub markdown_report: bool,
pub report_dir: Option<PathBuf>,
pub target_arch: X86TestArch,
pub target_triple: String,
pub c_standard: CLangStandard,
pub cpp_standard: CppStandard,
pub opt_level: u8,
pub extra_flags: Vec<String>,
pub stop_on_failure: bool,
pub shuffle: bool,
pub verbose: bool,
pub quiet: bool,
pub color: bool,
}
impl Default for X86TestHarnessConfig {
fn default() -> Self {
Self {
parallel_workers: X86_DEFAULT_THREAD_POOL_SIZE,
timeout_ms: X86_DEFAULT_TEST_TIMEOUT_MS,
memory_limit: X86_DEFAULT_MEMORY_LIMIT,
fd_limit: X86_DEFAULT_FD_LIMIT,
use_subprocess: false,
detect_flaky: true,
random_order: false,
random_seed: 42,
filter_tags: Vec::new(),
filter_regex: None,
collect_coverage: false,
coverage_type: X86CoverageType::None,
junit_report: false,
json_report: false,
tap_report: false,
html_report: false,
markdown_report: false,
report_dir: None,
target_arch: X86TestArch::X86_64,
target_triple: "x86_64-unknown-linux-gnu".into(),
c_standard: CLangStandard::C17,
cpp_standard: CppStandard::Cpp17,
opt_level: 2,
extra_flags: Vec::new(),
stop_on_failure: false,
shuffle: false,
verbose: false,
quiet: false,
color: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86TestArch {
X86_32,
X86_64,
X86X32,
}
impl fmt::Display for X86TestArch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::X86_32 => write!(f, "i386"),
Self::X86_64 => write!(f, "x86_64"),
Self::X86X32 => write!(f, "x86_x32"),
}
}
}
impl X86TestHarness {
pub fn new() -> Self {
Self {
suites: Vec::new(),
config: X86TestHarnessConfig::default(),
runner: X86TestRunner::new(),
database: X86TestDatabase::new(),
coverage: X86CodeCoverage::new(),
reporters: Vec::new(),
generators: X86TestGenerators::new(),
run_start: None,
}
}
pub fn with_config(config: X86TestHarnessConfig) -> Self {
let mut harness = Self::new();
let timeout = config.timeout_ms;
let workers = config.parallel_workers;
let random = config.random_order;
let seed = config.random_seed;
harness.config = config;
harness.runner.set_timeout(timeout);
harness.runner.set_parallelism(workers);
if random {
harness.runner.set_random_order(seed);
}
harness
}
pub fn register_suite(&mut self, suite: X86TestSuite) {
self.suites.push(suite);
}
pub fn register_test(&mut self, suite_name: &str, test: X86TestCase) {
if let Some(suite) = self.suites.iter_mut().find(|s| s.name == suite_name) {
suite.tests.push(test);
} else {
let mut suite = X86TestSuite::new(suite_name);
suite.tests.push(test);
self.suites.push(suite);
}
}
pub fn discover_tests(&mut self, dir: &Path) -> io::Result<usize> {
let discovered = self.runner.discover(dir)?;
for (suite_name, tests) in discovered {
self.register_suite(X86TestSuite {
name: suite_name,
tests,
setup: None,
teardown: None,
setup_testcase: None,
teardown_testcase: None,
tags: Vec::new(),
description: String::new(),
});
}
Ok(self.suites.iter().map(|s| s.tests.len()).sum())
}
pub fn run_all(&mut self) -> X86TestRunResult {
self.run_start = Some(SystemTime::now());
let mut filtered_suites: Vec<X86TestSuite> = Vec::new();
for suite in &self.suites {
let mut filtered = suite.clone();
filtered.tests = suite
.tests
.iter()
.filter(|t| self.matches_filter(t))
.cloned()
.collect();
if !filtered.tests.is_empty() {
filtered_suites.push(filtered);
}
}
let runner_result = self.runner.run_suites(&filtered_suites, &self.config);
for result in &runner_result.results {
self.database.record_result(result);
}
if self.config.collect_coverage {
self.coverage.collect();
self.coverage.generate_report(&self.config);
}
for reporter in &self.reporters {
reporter.generate(&runner_result, &self.config);
}
runner_result
}
pub fn run_tagged(&mut self, tag: &str) -> X86TestRunResult {
let mut config = self.config.clone();
config.filter_tags = vec![tag.to_string()];
let saved = std::mem::replace(&mut self.config, config);
let result = self.run_all();
self.config = saved;
result
}
pub fn run_named(&mut self, name: &str) -> X86TestRunResult {
let mut config = self.config.clone();
config.filter_regex = Some(format!("^{}$", regex::escape(name)));
let saved = std::mem::replace(&mut self.config, config);
let result = self.run_all();
self.config = saved;
result
}
fn matches_filter(&self, test: &X86TestCase) -> bool {
if !self.config.filter_tags.is_empty() {
let has_tag = self
.config
.filter_tags
.iter()
.any(|t| test.tags.contains(t));
if !has_tag && !test.tags.contains(&"all".to_string()) {
return false;
}
}
if let Some(ref regex_str) = self.config.filter_regex {
if let Ok(re) = regex::Regex::new(regex_str) {
if !re.is_match(&test.name) {
return false;
}
}
}
true
}
pub fn print_summary(&self, result: &X86TestRunResult) {
if self.config.quiet {
return;
}
let duration = if let Some(start) = self.run_start {
start.elapsed().unwrap_or_default()
} else {
Duration::default()
};
println!("{}", "=".repeat(72));
println!(
" X86 Test Harness Summary — {} tests in {:.2}s",
result.summary.total,
duration.as_secs_f64()
);
println!("{}", "=".repeat(72));
if self.config.color {
println!(" \x1b[32mPassed: {}\x1b[0m", result.summary.passed);
if result.summary.failed > 0 {
println!(" \x1b[31mFailed: {}\x1b[0m", result.summary.failed);
}
if result.summary.skipped > 0 {
println!(" \x1b[33mSkipped: {}\x1b[0m", result.summary.skipped);
}
if result.summary.xfailed > 0 {
println!(" \x1b[35mXFailed: {}\x1b[0m", result.summary.xfailed);
}
if result.summary.flaky > 0 {
println!(" \x1b[36mFlaky: {}\x1b[0m", result.summary.flaky);
}
} else {
println!(" Passed: {}", result.summary.passed);
println!(" Failed: {}", result.summary.failed);
println!(" Skipped: {}", result.summary.skipped);
println!(" XFailed: {}", result.summary.xfailed);
println!(" Flaky: {}", result.summary.flaky);
}
if !result.summary.all_pass() {
println!();
println!(" Failed tests:");
for res in &result.results {
if res.status == X86TestStatus::Failed {
println!(" - {}: {}", res.name, res.message);
}
}
}
println!();
}
pub fn total_tests(&self) -> usize {
self.suites.iter().map(|s| s.tests.len()).sum()
}
pub fn reset(&mut self) {
self.run_start = None;
self.runner.reset();
}
}
impl Default for X86TestHarness {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for X86TestHarness {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86TestHarness")
.field("suites", &self.suites.len())
.field("total_tests", &self.total_tests())
.field("config", &self.config)
.finish()
}
}
#[derive(Clone)]
pub struct X86TestSuite {
pub name: String,
pub tests: Vec<X86TestCase>,
pub setup: Option<fn()>,
pub teardown: Option<fn()>,
pub setup_testcase: Option<fn()>,
pub teardown_testcase: Option<fn()>,
pub tags: Vec<String>,
pub description: String,
}
impl X86TestSuite {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
tests: Vec::new(),
setup: None,
teardown: None,
setup_testcase: None,
teardown_testcase: None,
tags: Vec::new(),
description: String::new(),
}
}
pub fn add_test(&mut self, test: X86TestCase) {
self.tests.push(test);
}
pub fn set_setup(&mut self, f: fn()) {
self.setup = Some(f);
}
pub fn set_teardown(&mut self, f: fn()) {
self.teardown = Some(f);
}
pub fn set_setup_testcase(&mut self, f: fn()) {
self.setup_testcase = Some(f);
}
pub fn set_teardown_testcase(&mut self, f: fn()) {
self.teardown_testcase = Some(f);
}
pub fn add_tag(&mut self, tag: &str) {
self.tags.push(tag.to_string());
}
pub fn test_count(&self) -> usize {
self.tests.len()
}
pub fn is_empty(&self) -> bool {
self.tests.is_empty()
}
}
impl fmt::Debug for X86TestSuite {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86TestSuite")
.field("name", &self.name)
.field("tests", &self.tests.len())
.field("tags", &self.tags)
.finish()
}
}
#[derive(Clone)]
pub struct X86TestCase {
pub name: String,
pub source_code: Option<String>,
pub run_command: Option<String>,
pub expected_return: i32,
pub expected_stdout: Option<String>,
pub expected_stderr: Option<String>,
pub tags: Vec<String>,
pub category: Option<String>,
pub priority: i32,
pub is_xfail: bool,
pub xfail_reason: Option<String>,
pub requires: Vec<String>,
pub unsupported_on: Vec<String>,
pub dependencies: Vec<String>,
pub timeout_override: Option<u64>,
pub disabled: bool,
pub disable_reason: Option<String>,
pub fixture_class: Option<String>,
pub param_values: Vec<String>,
pub compile_mode: X86CompileMode,
pub filecheck_directives: Vec<String>,
}
impl X86TestCase {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
source_code: None,
run_command: None,
expected_return: 0,
expected_stdout: None,
expected_stderr: None,
tags: Vec::new(),
category: None,
priority: 0,
is_xfail: false,
xfail_reason: None,
requires: Vec::new(),
unsupported_on: Vec::new(),
dependencies: Vec::new(),
timeout_override: None,
disabled: false,
disable_reason: None,
fixture_class: None,
param_values: Vec::new(),
compile_mode: X86CompileMode::CompileAndRun,
filecheck_directives: Vec::new(),
}
}
pub fn with_source(mut self, source: &str) -> Self {
self.source_code = Some(source.to_string());
self
}
pub fn with_expected_stdout(mut self, expected: &str) -> Self {
self.expected_stdout = Some(expected.to_string());
self
}
pub fn with_return(mut self, ret: i32) -> Self {
self.expected_return = ret;
self
}
pub fn with_tag(mut self, tag: &str) -> Self {
self.tags.push(tag.to_string());
self
}
pub fn with_xfail(mut self, reason: &str) -> Self {
self.is_xfail = true;
self.xfail_reason = Some(reason.to_string());
self
}
pub fn with_disable(mut self, reason: &str) -> Self {
self.disabled = true;
self.disable_reason = Some(reason.to_string());
self
}
pub fn with_dependency(mut self, dep: &str) -> Self {
self.dependencies.push(dep.to_string());
self
}
pub fn with_category(mut self, cat: &str) -> Self {
self.category = Some(cat.to_string());
self
}
pub fn with_compile_mode(mut self, mode: X86CompileMode) -> Self {
self.compile_mode = mode;
self
}
pub fn with_filecheck(mut self, directive: &str) -> Self {
self.filecheck_directives.push(directive.to_string());
self
}
pub fn is_supported_on(&self, arch: &str) -> bool {
!self.unsupported_on.iter().any(|u| u == arch)
}
}
impl fmt::Debug for X86TestCase {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86TestCase")
.field("name", &self.name)
.field("category", &self.category)
.field("tags", &self.tags)
.field("disabled", &self.disabled)
.field("is_xfail", &self.is_xfail)
.finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86CompileMode {
CompileAndRun,
CompileAndLink,
CompileToBitcode,
CompileToAssembly,
CompileToObject,
SyntaxOnly,
PreprocessOnly,
FileCheck,
Roundtrip,
}
impl fmt::Display for X86CompileMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86CompileMode::CompileAndRun => write!(f, "compile-and-run"),
X86CompileMode::CompileAndLink => write!(f, "compile-and-link"),
X86CompileMode::CompileToBitcode => write!(f, "compile-to-bc"),
X86CompileMode::CompileToAssembly => write!(f, "compile-to-asm"),
X86CompileMode::CompileToObject => write!(f, "compile-to-obj"),
X86CompileMode::SyntaxOnly => write!(f, "syntax-only"),
X86CompileMode::PreprocessOnly => write!(f, "preprocess-only"),
X86CompileMode::FileCheck => write!(f, "filecheck"),
X86CompileMode::Roundtrip => write!(f, "roundtrip"),
}
}
}
#[derive(Clone)]
pub struct X86TestResult {
pub name: String,
pub suite: String,
pub status: X86TestStatus,
pub message: String,
pub duration_ms: u64,
pub timestamp: u64,
pub stdout: String,
pub stderr: String,
pub exit_code: i32,
pub tags: Vec<String>,
pub assertion_count: u32,
pub assertion_passed: u32,
pub memory_used: u64,
pub coverage_data: Option<X86CoverageData>,
pub is_flaky: bool,
pub flaky_history: Vec<X86TestStatus>,
}
impl X86TestResult {
pub fn pass(name: &str, suite: &str, duration_ms: u64) -> Self {
Self {
name: name.to_string(),
suite: suite.to_string(),
status: X86TestStatus::Passed,
message: String::new(),
duration_ms,
timestamp: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64,
stdout: String::new(),
stderr: String::new(),
exit_code: 0,
tags: Vec::new(),
assertion_count: 0,
assertion_passed: 0,
memory_used: 0,
coverage_data: None,
is_flaky: false,
flaky_history: Vec::new(),
}
}
pub fn fail(name: &str, suite: &str, message: &str, duration_ms: u64) -> Self {
Self {
name: name.to_string(),
suite: suite.to_string(),
status: X86TestStatus::Failed,
message: message.to_string(),
duration_ms,
timestamp: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64,
stdout: String::new(),
stderr: String::new(),
exit_code: 1,
tags: Vec::new(),
assertion_count: 0,
assertion_passed: 0,
memory_used: 0,
coverage_data: None,
is_flaky: false,
flaky_history: Vec::new(),
}
}
pub fn is_pass(&self) -> bool {
matches!(self.status, X86TestStatus::Passed | X86TestStatus::XFailed)
}
pub fn is_fail(&self) -> bool {
matches!(self.status, X86TestStatus::Failed)
}
pub fn with_flaky_history(mut self, history: Vec<X86TestStatus>) -> Self {
self.is_flaky = !history.is_empty() && history.iter().any(|s| *s != self.status);
self.flaky_history = history;
self
}
}
impl fmt::Display for X86TestResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let status_str = match self.status {
X86TestStatus::Passed => "PASS",
X86TestStatus::Failed => "FAIL",
X86TestStatus::Skipped => "SKIP",
X86TestStatus::XFailed => "XFAIL",
X86TestStatus::XPassed => "XPASS",
X86TestStatus::Timeout => "TIMEOUT",
X86TestStatus::Crashed => "CRASH",
X86TestStatus::Disabled => "DISABLED",
X86TestStatus::Flaky => "FLAKY",
};
write!(
f,
"[{}] {}::{} ({:.3}s)",
status_str,
self.suite,
self.name,
self.duration_ms as f64 / 1000.0
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86TestStatus {
Passed,
Failed,
Skipped,
XFailed,
XPassed,
Timeout,
Crashed,
Disabled,
Flaky,
}
impl fmt::Display for X86TestStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86TestStatus::Passed => write!(f, "PASSED"),
X86TestStatus::Failed => write!(f, "FAILED"),
X86TestStatus::Skipped => write!(f, "SKIPPED"),
X86TestStatus::XFailed => write!(f, "XFAILED"),
X86TestStatus::XPassed => write!(f, "XPASSED"),
X86TestStatus::Timeout => write!(f, "TIMEOUT"),
X86TestStatus::Crashed => write!(f, "CRASHED"),
X86TestStatus::Disabled => write!(f, "DISABLED"),
X86TestStatus::Flaky => write!(f, "FLAKY"),
}
}
}
#[derive(Clone)]
pub struct X86TestRunResult {
pub results: Vec<X86TestResult>,
pub summary: X86TestSummary,
pub total_duration_ms: u64,
pub error: Option<String>,
}
impl X86TestRunResult {
pub fn new() -> Self {
Self {
results: Vec::new(),
summary: X86TestSummary::new(),
total_duration_ms: 0,
error: None,
}
}
pub fn add_result(&mut self, result: X86TestResult) {
self.summary.update(&result);
self.total_duration_ms += result.duration_ms;
self.results.push(result);
}
pub fn all_passed(&self) -> bool {
self.summary.all_pass()
}
pub fn pass_rate(&self) -> f64 {
self.summary.pass_rate()
}
pub fn failures(&self) -> Vec<&X86TestResult> {
self.results
.iter()
.filter(|r| r.status == X86TestStatus::Failed)
.collect()
}
pub fn flaky_tests(&self) -> Vec<&X86TestResult> {
self.results.iter().filter(|r| r.is_flaky).collect()
}
}
impl fmt::Display for X86TestRunResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "X86 Test Run Result:")?;
writeln!(f, " {}", self.summary)?;
for result in &self.results {
if result.status != X86TestStatus::Passed {
writeln!(f, " {}", result)?;
}
}
Ok(())
}
}
#[derive(Clone, Default)]
pub struct X86TestSummary {
pub total: usize,
pub passed: usize,
pub failed: usize,
pub skipped: usize,
pub xfailed: usize,
pub xpassed: usize,
pub timeout: usize,
pub crashed: usize,
pub disabled: usize,
pub flaky: usize,
pub total_duration_ms: u64,
}
impl X86TestSummary {
pub fn new() -> Self {
Self::default()
}
pub fn update(&mut self, result: &X86TestResult) {
self.total += 1;
match result.status {
X86TestStatus::Passed => self.passed += 1,
X86TestStatus::Failed => self.failed += 1,
X86TestStatus::Skipped => self.skipped += 1,
X86TestStatus::XFailed => self.xfailed += 1,
X86TestStatus::XPassed => self.xpassed += 1,
X86TestStatus::Timeout => self.timeout += 1,
X86TestStatus::Crashed => self.crashed += 1,
X86TestStatus::Disabled => self.disabled += 1,
X86TestStatus::Flaky => self.flaky += 1,
}
self.total_duration_ms += result.duration_ms;
}
pub fn all_pass(&self) -> bool {
self.failed == 0 && self.timeout == 0 && self.crashed == 0 && self.xpassed == 0
}
pub fn pass_rate(&self) -> f64 {
if self.total == 0 {
return 100.0;
}
let effective_total = self.total - self.skipped - self.disabled;
if effective_total == 0 {
return 100.0;
}
(self.passed + self.xfailed) as f64 / effective_total as f64 * 100.0
}
pub fn merge(&mut self, other: &X86TestSummary) {
self.total += other.total;
self.passed += other.passed;
self.failed += other.failed;
self.skipped += other.skipped;
self.xfailed += other.xfailed;
self.xpassed += other.xpassed;
self.timeout += other.timeout;
self.crashed += other.crashed;
self.disabled += other.disabled;
self.flaky += other.flaky;
self.total_duration_ms += other.total_duration_ms;
}
}
impl fmt::Display for X86TestSummary {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Total: {} | Passed: {} | Failed: {} | Skipped: {} | XFailed: {} | Flaky: {} | Time: {:.2}s",
self.total,
self.passed,
self.failed,
self.skipped,
self.xfailed,
self.flaky,
self.total_duration_ms as f64 / 1000.0
)
}
}
pub struct X86TestRunner {
pub thread_pool: Option<X86ThreadPool>,
pub timeout_ms: u64,
pub parallelism: usize,
pub random_seed: u64,
pub random_order: bool,
pub use_subprocess: bool,
pub memory_limit: u64,
pub fd_limit: u32,
pub verbose: bool,
pub tests_executed: AtomicU64,
pub tests_passed: AtomicU64,
}
impl X86TestRunner {
pub fn new() -> Self {
Self {
thread_pool: None,
timeout_ms: X86_DEFAULT_TEST_TIMEOUT_MS,
parallelism: X86_DEFAULT_THREAD_POOL_SIZE,
random_seed: 42,
random_order: false,
use_subprocess: false,
memory_limit: X86_DEFAULT_MEMORY_LIMIT,
fd_limit: X86_DEFAULT_FD_LIMIT,
verbose: false,
tests_executed: AtomicU64::new(0),
tests_passed: AtomicU64::new(0),
}
}
pub fn set_timeout(&mut self, ms: u64) {
self.timeout_ms = ms;
}
pub fn set_parallelism(&mut self, workers: usize) {
self.parallelism = workers;
}
pub fn set_random_order(&mut self, seed: u64) {
self.random_order = true;
self.random_seed = seed;
}
pub fn set_subprocess_isolation(&mut self, enabled: bool) {
self.use_subprocess = enabled;
}
pub fn discover(&self, dir: &Path) -> io::Result<HashMap<String, Vec<X86TestCase>>> {
let mut suites: HashMap<String, Vec<X86TestCase>> = HashMap::new();
if !dir.is_dir() {
return Ok(suites);
}
for entry in std::fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if path.is_file() {
if let Some(ext) = path.extension() {
let ext = ext.to_string_lossy().to_lowercase();
if matches!(
ext.as_str(),
"c" | "cc" | "cpp" | "cxx" | "c++" | "h" | "hpp"
) {
if let Ok(content) = std::fs::read_to_string(&path) {
let file_stem = path
.file_stem()
.unwrap_or_default()
.to_string_lossy()
.to_string();
let discovered = self.discover_in_source(&content, &file_stem);
if !discovered.is_empty() {
suites.insert(file_stem, discovered);
}
}
}
}
} else if path.is_dir() {
let sub = self.discover(&path)?;
for (k, v) in sub {
suites.entry(k).or_default().extend(v);
}
}
}
Ok(suites)
}
fn discover_in_source(&self, content: &str, suite_name: &str) -> Vec<X86TestCase> {
let mut tests = Vec::new();
for pattern in &["TEST(", "TEST_F(", "TEST_P("] {
let mut pos = 0;
while let Some(idx) = content[pos..].find(pattern) {
let start = pos + idx + pattern.len();
let line = &content[start..];
if let Some(close) = line.find(')') {
let args = &line[..close];
let parts: Vec<&str> = args.split(',').collect();
if parts.len() >= 2 {
let name = parts[1].trim().trim_matches('"');
let mut test = X86TestCase::new(name);
test.source_code = Some(content.to_string());
test.category = Some(suite_name.to_string());
test.tags.push("gtest".to_string());
tests.push(test);
}
}
pos = start + 1;
if pos >= content.len() {
break;
}
}
}
for pattern in &["TEST_CASE(", "SCENARIO("] {
let mut pos = 0;
while let Some(idx) = content[pos..].find(pattern) {
let start = pos + idx + pattern.len();
let line = &content[start..];
if let Some(close) = line.find(')') {
let name = &line[..close].trim().trim_matches('"');
let mut test = X86TestCase::new(name);
test.source_code = Some(content.to_string());
test.category = Some(suite_name.to_string());
test.tags.push("catch2".to_string());
tests.push(test);
}
pos = start + 1;
if pos >= content.len() {
break;
}
}
}
let mut pos = 0;
while let Some(idx) = content[pos..].find("BOOST_AUTO_TEST_CASE(") {
let start = pos + idx + 21;
let line = &content[start..];
if let Some(close) = line.find(')') {
let name = &line[..close].trim();
let mut test = X86TestCase::new(name);
test.source_code = Some(content.to_string());
test.category = Some(suite_name.to_string());
test.tags.push("boost".to_string());
tests.push(test);
}
pos = start + 1;
if pos >= content.len() {
break;
}
}
let mut pos = 0;
while let Some(idx) = content[pos..].find("CU_add_test(") {
let start = pos + idx + 12;
let line = &content[start..];
if let Some(close) = line.find(',') {
let name = &line[..close].trim().trim_matches('"');
let mut test = X86TestCase::new(name);
test.source_code = Some(content.to_string());
test.category = Some(suite_name.to_string());
test.tags.push("cunit".to_string());
tests.push(test);
}
pos = start + 1;
if pos >= content.len() {
break;
}
}
let mut pos = 0;
while let Some(idx) = content[pos..].find("START_TEST(") {
let start = pos + idx + 11;
let line = &content[start..];
if let Some(close) = line.find(')') {
let name = &line[..close].trim();
let mut test = X86TestCase::new(name);
test.source_code = Some(content.to_string());
test.category = Some(suite_name.to_string());
test.tags.push("check".to_string());
tests.push(test);
}
pos = start + 1;
if pos >= content.len() {
break;
}
}
tests
}
pub fn run_suites(
&self,
suites: &[X86TestSuite],
config: &X86TestHarnessConfig,
) -> X86TestRunResult {
let mut result = X86TestRunResult::new();
let mut all_tests: Vec<(String, X86TestCase)> = Vec::new();
for suite in suites {
for test in &suite.tests {
if test.disabled {
let mut disabled_result = X86TestResult::pass(&test.name, &suite.name, 0);
disabled_result.status = X86TestStatus::Disabled;
disabled_result.message = test
.disable_reason
.clone()
.unwrap_or_else(|| "Test disabled".to_string());
result.add_result(disabled_result);
continue;
}
if !test.requires.is_empty() {
let mut has_all = true;
for req in &test.requires {
if !self.feature_available(req) {
has_all = false;
break;
}
}
if !has_all {
let mut skip_result = X86TestResult::pass(&test.name, &suite.name, 0);
skip_result.status = X86TestStatus::Skipped;
skip_result.message =
format!("Required features not available: {:?}", test.requires);
result.add_result(skip_result);
continue;
}
}
if !test.is_supported_on(&config.target_arch.to_string()) {
let mut skip_result = X86TestResult::pass(&test.name, &suite.name, 0);
skip_result.status = X86TestStatus::Skipped;
skip_result.message = format!("Unsupported on target: {}", config.target_arch);
result.add_result(skip_result);
continue;
}
all_tests.push((suite.name.clone(), test.clone()));
}
}
if config.shuffle || self.random_order {
self.shuffle_tests(&mut all_tests, self.random_seed);
} else {
all_tests.sort_by(|a, b| {
b.1.priority
.cmp(&a.1.priority)
.then_with(|| a.1.name.cmp(&b.1.name))
});
}
if self.parallelism > 1 && !self.use_subprocess {
self.run_parallel(suites, &all_tests, config, &mut result);
} else {
self.run_sequential(suites, &all_tests, config, &mut result);
}
result
}
fn run_sequential(
&self,
suites: &[X86TestSuite],
tests: &[(String, X86TestCase)],
config: &X86TestHarnessConfig,
result: &mut X86TestRunResult,
) {
for (suite_name, test) in tests {
let suite = suites.iter().find(|s| s.name == *suite_name);
let test_result = self.run_single(suite, test, config);
self.tests_executed.fetch_add(1, Ordering::Relaxed);
if test_result.status == X86TestStatus::Passed {
self.tests_passed.fetch_add(1, Ordering::Relaxed);
}
result.add_result(test_result);
if config.stop_on_failure && result.summary.failed > 0 {
break;
}
}
}
fn run_parallel(
&self,
suites: &[X86TestSuite],
tests: &[(String, X86TestCase)],
config: &X86TestHarnessConfig,
result: &mut X86TestRunResult,
) {
let pool = X86ThreadPool::new(self.parallelism);
let results = Arc::new(Mutex::new(Vec::new()));
let suites_arc = Arc::new(Mutex::new(
suites
.iter()
.map(|s| (s.name.clone(), s.clone()))
.collect::<HashMap<_, _>>(),
));
let config_arc = Arc::new(config.clone());
let mut handles = Vec::new();
for (suite_name, test) in tests.iter() {
let results_clone = Arc::clone(&results);
let suites_clone = Arc::clone(&suites_arc);
let config_clone = Arc::clone(&config_arc);
let suite_name_clone = suite_name.clone();
let test_clone = test.clone();
let handle = pool.spawn(move || {
let suites_map = suites_clone.lock().unwrap();
let suite = suites_map.get(&suite_name_clone);
let suite_ref = suite.cloned();
drop(suites_map);
let test_result = X86TestRunner::run_single_static(
suite_ref.as_ref(),
&test_clone,
&config_clone,
);
let mut results_guard = results_clone.lock().unwrap();
results_guard.push(test_result);
});
handles.push(handle);
}
for handle in handles {
let _ = pool.join(handle);
}
let mut final_results = results.lock().unwrap();
for test_result in final_results.drain(..) {
self.tests_executed.fetch_add(1, Ordering::Relaxed);
if test_result.status == X86TestStatus::Passed {
self.tests_passed.fetch_add(1, Ordering::Relaxed);
}
result.add_result(test_result);
}
pool.shutdown();
}
fn run_single_static(
suite: Option<&X86TestSuite>,
test: &X86TestCase,
config: &X86TestHarnessConfig,
) -> X86TestResult {
let runner = X86TestRunner::new();
runner.run_single(suite, test, config)
}
pub fn run_single(
&self,
suite: Option<&X86TestSuite>,
test: &X86TestCase,
config: &X86TestHarnessConfig,
) -> X86TestResult {
let suite_name = suite.map(|s| s.name.as_str()).unwrap_or("default");
let start = Instant::now();
if let Some(suite) = suite {
if let Some(setup) = suite.setup {
setup();
}
if let Some(setup_tc) = suite.setup_testcase {
setup_tc();
}
}
let (status, message, exit_code, stdout, stderr) = match test.compile_mode {
X86CompileMode::CompileAndRun => self.execute_compile_and_run(test, config),
X86CompileMode::SyntaxOnly => self.execute_syntax_only(test, config),
X86CompileMode::CompileToAssembly => self.execute_compile_to_asm(test, config),
X86CompileMode::CompileToObject => self.execute_compile_to_obj(test, config),
X86CompileMode::CompileToBitcode => self.execute_compile_to_bc(test, config),
X86CompileMode::FileCheck => self.execute_filecheck(test, config),
X86CompileMode::Roundtrip => self.execute_roundtrip(test, config),
_ => self.execute_compile_and_run(test, config),
};
if let Some(suite) = suite {
if let Some(teardown_tc) = suite.teardown_testcase {
teardown_tc();
}
if let Some(teardown) = suite.teardown {
teardown();
}
}
let duration_ms = start.elapsed().as_millis() as u64;
let final_status = if status == X86TestStatus::Failed && test.is_xfail {
X86TestStatus::XFailed
} else if status == X86TestStatus::Passed && test.is_xfail {
X86TestStatus::XPassed
} else {
status
};
X86TestResult {
name: test.name.clone(),
suite: suite_name.to_string(),
status: final_status,
message,
duration_ms,
timestamp: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64,
stdout,
stderr,
exit_code,
tags: test.tags.clone(),
assertion_count: 0,
assertion_passed: 0,
memory_used: 0,
coverage_data: None,
is_flaky: false,
flaky_history: Vec::new(),
}
}
fn execute_compile_and_run(
&self,
test: &X86TestCase,
config: &X86TestHarnessConfig,
) -> (X86TestStatus, String, i32, String, String) {
let source = match &test.source_code {
Some(s) => s.clone(),
None => {
return (
X86TestStatus::Failed,
"No source code provided".to_string(),
-1,
String::new(),
String::new(),
);
}
};
let compile_result = self.simulate_compile(&source, config);
if !compile_result.success {
return (
X86TestStatus::Failed,
format!("Compilation failed: {}", compile_result.stderr),
compile_result.exit_code,
compile_result.stdout,
compile_result.stderr,
);
}
if test.expected_return != 0 {
let (run_stdout, run_stderr, run_code) = self.simulate_run(&source, test);
let stdout_ok = match &test.expected_stdout {
Some(expected) => run_stdout.contains(expected),
None => true,
};
let stderr_ok = match &test.expected_stderr {
Some(expected) => run_stderr.contains(expected),
None => true,
};
if run_code == test.expected_return && stdout_ok && stderr_ok {
return (
X86TestStatus::Passed,
String::new(),
run_code,
run_stdout,
run_stderr,
);
} else {
return (
X86TestStatus::Failed,
format!(
"Expected return {} but got {}. stdout_ok={}, stderr_ok={}",
test.expected_return, run_code, stdout_ok, stderr_ok
),
run_code,
run_stdout,
run_stderr,
);
}
}
(
X86TestStatus::Passed,
String::new(),
0,
compile_result.stdout,
compile_result.stderr,
)
}
fn execute_syntax_only(
&self,
test: &X86TestCase,
config: &X86TestHarnessConfig,
) -> (X86TestStatus, String, i32, String, String) {
let source = match &test.source_code {
Some(s) => s.clone(),
None => {
return (
X86TestStatus::Failed,
"No source code provided".to_string(),
-1,
String::new(),
String::new(),
);
}
};
let result = self.simulate_compile(&source, config);
if result.success {
(
X86TestStatus::Passed,
String::new(),
0,
result.stdout,
result.stderr,
)
} else {
(
X86TestStatus::Failed,
format!("Syntax check failed: {}", result.stderr),
result.exit_code,
result.stdout,
result.stderr,
)
}
}
fn execute_compile_to_asm(
&self,
test: &X86TestCase,
config: &X86TestHarnessConfig,
) -> (X86TestStatus, String, i32, String, String) {
let source = match &test.source_code {
Some(s) => s.clone(),
None => {
return (
X86TestStatus::Failed,
"No source code provided".to_string(),
-1,
String::new(),
String::new(),
);
}
};
let result = self.simulate_compile_to_asm(&source, config);
if result.success {
(
X86TestStatus::Passed,
String::new(),
0,
result.stdout,
result.stderr,
)
} else {
(
X86TestStatus::Failed,
format!("Assembly generation failed: {}", result.stderr),
result.exit_code,
result.stdout,
result.stderr,
)
}
}
fn execute_compile_to_obj(
&self,
test: &X86TestCase,
config: &X86TestHarnessConfig,
) -> (X86TestStatus, String, i32, String, String) {
let source = match &test.source_code {
Some(s) => s.clone(),
None => {
return (
X86TestStatus::Failed,
"No source code provided".to_string(),
-1,
String::new(),
String::new(),
);
}
};
let result = self.simulate_compile_to_obj(&source, config);
if result.success {
(
X86TestStatus::Passed,
String::new(),
0,
result.stdout,
result.stderr,
)
} else {
(
X86TestStatus::Failed,
format!("Object generation failed: {}", result.stderr),
result.exit_code,
result.stdout,
result.stderr,
)
}
}
fn execute_compile_to_bc(
&self,
test: &X86TestCase,
config: &X86TestHarnessConfig,
) -> (X86TestStatus, String, i32, String, String) {
let source = match &test.source_code {
Some(s) => s.clone(),
None => {
return (
X86TestStatus::Failed,
"No source code provided".to_string(),
-1,
String::new(),
String::new(),
);
}
};
let result = self.simulate_compile_to_bc(&source, config);
if result.success {
(
X86TestStatus::Passed,
String::new(),
0,
result.stdout,
result.stderr,
)
} else {
(
X86TestStatus::Failed,
format!("Bitcode generation failed: {}", result.stderr),
result.exit_code,
result.stdout,
result.stderr,
)
}
}
fn execute_filecheck(
&self,
test: &X86TestCase,
config: &X86TestHarnessConfig,
) -> (X86TestStatus, String, i32, String, String) {
let source = match &test.source_code {
Some(s) => s.clone(),
None => {
return (
X86TestStatus::Failed,
"No source code provided".to_string(),
-1,
String::new(),
String::new(),
);
}
};
let output = self.simulate_compile_to_asm(&source, config);
if !output.success {
return (
X86TestStatus::Failed,
format!("Compilation for FileCheck failed: {}", output.stderr),
output.exit_code,
output.stdout,
output.stderr,
);
}
let asm_output = output.stdout.clone();
let mut all_checks_passed = true;
let mut failures: Vec<String> = Vec::new();
for directive in &test.filecheck_directives {
if directive.starts_with("CHECK:") {
let pattern = directive[6..].trim();
if !asm_output.contains(pattern) {
all_checks_passed = false;
failures.push(format!("CHECK pattern not found: {}", pattern));
}
} else if directive.starts_with("CHECK-NOT:") {
let pattern = directive[10..].trim();
if asm_output.contains(pattern) {
all_checks_passed = false;
failures.push(format!("CHECK-NOT pattern found: {}", pattern));
}
} else if directive.starts_with("CHECK-NEXT:") {
let pattern = directive[11..].trim();
let lines: Vec<&str> = asm_output.lines().collect();
let mut found = false;
let mut prev_matched = false;
for line in &lines {
if prev_matched && line.contains(pattern) {
found = true;
break;
}
prev_matched = !lines.is_empty();
}
if !found {
all_checks_passed = false;
failures.push(format!("CHECK-NEXT pattern not found: {}", pattern));
}
}
}
if all_checks_passed {
(
X86TestStatus::Passed,
String::new(),
0,
asm_output,
String::new(),
)
} else {
(
X86TestStatus::Failed,
format!("FileCheck failures: {}", failures.join("; ")),
1,
asm_output,
failures.join("\n"),
)
}
}
fn execute_roundtrip(
&self,
test: &X86TestCase,
config: &X86TestHarnessConfig,
) -> (X86TestStatus, String, i32, String, String) {
let source = match &test.source_code {
Some(s) => s.clone(),
None => {
return (
X86TestStatus::Failed,
"No source code provided".to_string(),
-1,
String::new(),
String::new(),
);
}
};
let asm1 = self.simulate_compile_to_asm(&source, config);
if !asm1.success {
return (
X86TestStatus::Failed,
format!("First compilation failed: {}", asm1.stderr),
asm1.exit_code,
asm1.stdout,
asm1.stderr,
);
}
let obj = self.simulate_assemble(&asm1.stdout, config);
if !obj.success {
return (
X86TestStatus::Failed,
format!("Assembly failed: {}", obj.stderr),
obj.exit_code,
obj.stdout,
obj.stderr,
);
}
let asm2 = self.simulate_disassemble(&obj.stdout, config);
if !asm2.success {
return (
X86TestStatus::Failed,
format!("Disassembly failed: {}", asm2.stderr),
asm2.exit_code,
asm2.stdout,
asm2.stderr,
);
}
let stripped1 = self.strip_asm_comments(&asm1.stdout);
let stripped2 = self.strip_asm_comments(&asm2.stdout);
if stripped1.trim() == stripped2.trim() {
(
X86TestStatus::Passed,
String::new(),
0,
asm1.stdout,
String::new(),
)
} else {
(
X86TestStatus::Failed,
"Roundtrip mismatch".to_string(),
1,
format!("Before:\n{}\nAfter:\n{}", stripped1, stripped2),
String::new(),
)
}
}
fn simulate_compile(
&self,
source: &str,
_config: &X86TestHarnessConfig,
) -> SimulatedCompileResult {
let errors = self.check_basic_syntax(source);
if !errors.is_empty() {
return SimulatedCompileResult {
success: false,
stdout: String::new(),
stderr: errors.join("\n"),
exit_code: 1,
};
}
if source.contains("// expected-error") || source.contains("// expected-warning") {
let expected_errors: Vec<&str> = source
.lines()
.filter(|l| l.contains("expected-error"))
.collect();
return SimulatedCompileResult {
success: false,
stdout: String::new(),
stderr: format!("Expected errors found: {}", expected_errors.len()),
exit_code: 1,
};
}
SimulatedCompileResult {
success: true,
stdout: String::new(),
stderr: String::new(),
exit_code: 0,
}
}
fn check_basic_syntax(&self, source: &str) -> Vec<String> {
let mut errors = Vec::new();
let mut brace_count: i32 = 0;
let mut paren_count: i32 = 0;
let mut bracket_count: i32 = 0;
for (i, ch) in source.chars().enumerate() {
match ch {
'{' => brace_count += 1,
'}' => brace_count -= 1,
'(' => paren_count += 1,
')' => paren_count -= 1,
'[' => bracket_count += 1,
']' => bracket_count -= 1,
_ => {}
}
if brace_count < 0 {
errors.push(format!("Unmatched '}}' at position {}", i));
brace_count = 0;
}
if paren_count < 0 {
errors.push(format!("Unmatched ')' at position {}", i));
paren_count = 0;
}
if bracket_count < 0 {
errors.push(format!("Unmatched ']' at position {}", i));
bracket_count = 0;
}
}
if brace_count > 0 {
errors.push(format!("{} unmatched '{{'", brace_count));
}
if paren_count > 0 {
errors.push(format!("{} unmatched '('", paren_count));
}
if bracket_count > 0 {
errors.push(format!("{} unmatched '['", bracket_count));
}
errors
}
fn simulate_compile_to_asm(
&self,
source: &str,
config: &X86TestHarnessConfig,
) -> SimulatedCompileResult {
let compile = self.simulate_compile(source, config);
if !compile.success {
return compile;
}
let asm = self.generate_x86_asm(source, config);
SimulatedCompileResult {
success: true,
stdout: asm,
stderr: String::new(),
exit_code: 0,
}
}
fn simulate_compile_to_obj(
&self,
source: &str,
config: &X86TestHarnessConfig,
) -> SimulatedCompileResult {
self.simulate_compile_to_asm(source, config)
}
fn simulate_compile_to_bc(
&self,
source: &str,
config: &X86TestHarnessConfig,
) -> SimulatedCompileResult {
let compile = self.simulate_compile(source, config);
if !compile.success {
return compile;
}
let ir = self.generate_llvm_ir(source, config);
SimulatedCompileResult {
success: true,
stdout: ir,
stderr: String::new(),
exit_code: 0,
}
}
fn simulate_run(&self, source: &str, _test: &X86TestCase) -> (String, String, i32) {
let return_val = self.extract_return_value(source);
let stdout = self.extract_printf_output(source);
(stdout, String::new(), return_val.unwrap_or(0))
}
fn simulate_assemble(
&self,
asm: &str,
_config: &X86TestHarnessConfig,
) -> SimulatedCompileResult {
SimulatedCompileResult {
success: true,
stdout: format!("; assembled from:\n{}", asm),
stderr: String::new(),
exit_code: 0,
}
}
fn simulate_disassemble(
&self,
_obj: &str,
_config: &X86TestHarnessConfig,
) -> SimulatedCompileResult {
SimulatedCompileResult {
success: true,
stdout: _obj.to_string(),
stderr: String::new(),
exit_code: 0,
}
}
fn strip_asm_comments(&self, asm: &str) -> String {
asm.lines()
.map(|line| {
if let Some(idx) = line.find(';') {
if idx == 0 || line.as_bytes().get(idx - 1) == Some(&b' ') {
line[..idx].trim_end().to_string()
} else {
line.to_string()
}
} else if let Some(idx) = line.find('#') {
if idx == 0 || line.as_bytes().get(idx - 1) == Some(&b' ') {
line[..idx].trim_end().to_string()
} else {
line.to_string()
}
} else {
line.to_string()
}
})
.collect::<Vec<_>>()
.join("\n")
}
fn generate_x86_asm(&self, source: &str, config: &X86TestHarnessConfig) -> String {
let mut asm = String::new();
asm.push_str(&format!(
"\t.text\n\t.file\t\"test.c\"\n\t.globl\tmain\n\t.type\tmain, @function\nmain:\n"
));
asm.push_str("\tpushq\t%rbp\n");
asm.push_str("\tmovq\t%rsp, %rbp\n");
if let Some(val) = self.extract_return_value(source) {
asm.push_str(&format!("\tmovl\t${}, %eax\n", val));
} else {
asm.push_str("\txorl\t%eax, %eax\n");
}
asm.push_str("\tpopq\t%rbp\n");
asm.push_str("\tretq\n");
asm.push_str(&format!("\t.size\tmain, .-main\n"));
if config.verbose {
asm.push_str(&format!(
"\t.ident\t\"clang version 18.0.0 (x86_64-unknown-linux-gnu)\"\n"
));
}
asm
}
fn generate_llvm_ir(&self, source: &str, config: &X86TestHarnessConfig) -> String {
let mut ir = String::new();
let target = if config.target_arch == X86TestArch::X86_64 {
"x86_64-unknown-linux-gnu"
} else {
"i386-unknown-linux-gnu"
};
ir.push_str(&format!("; ModuleID = 'test.c'\n"));
ir.push_str(&format!("target triple = \"{}\"\n\n", target));
ir.push_str("define i32 @main() {\n");
ir.push_str("entry:\n");
if let Some(val) = self.extract_return_value(source) {
ir.push_str(&format!(" ret i32 {}\n", val));
} else {
ir.push_str(" ret i32 0\n");
}
ir.push_str("}\n");
ir
}
fn extract_return_value(&self, source: &str) -> Option<i32> {
for line in source.lines() {
let trimmed = line.trim();
if trimmed.starts_with("return ") {
let expr = trimmed[7..].trim_end_matches(';').trim();
if let Ok(val) = expr.parse::<i32>() {
return Some(val);
}
if let Some(result) = self.eval_simple_expr(expr) {
return Some(result);
}
}
}
None
}
fn extract_printf_output(&self, source: &str) -> String {
let mut output = String::new();
for line in source.lines() {
let trimmed = line.trim();
if trimmed.starts_with("printf(\"") {
if let Some(end) = trimmed.find("\")") {
let format_str = &trimmed[8..end];
output.push_str(format_str);
}
} else if trimmed.starts_with("puts(\"") {
if let Some(end) = trimmed.find("\")") {
let s = &trimmed[6..end];
output.push_str(s);
}
}
}
output
}
fn eval_simple_expr(&self, expr: &str) -> Option<i32> {
let expr = expr.trim();
if let Some(idx) = expr.find('+') {
let left = expr[..idx].trim();
let right = expr[idx + 1..].trim();
let l = left.parse::<i32>().ok()?;
let r = right.parse::<i32>().ok()?;
return Some(l + r);
}
if let Some(idx) = expr.rfind('-') {
if idx > 0 {
let left = expr[..idx].trim();
let right = expr[idx + 1..].trim();
let l = left.parse::<i32>().ok()?;
let r = right.parse::<i32>().ok()?;
return Some(l - r);
}
}
if expr.contains('*') {
let parts: Vec<&str> = expr.split('*').collect();
if parts.len() == 2 {
let l = parts[0].trim().parse::<i32>().ok()?;
let r = parts[1].trim().parse::<i32>().ok()?;
return Some(l * r);
}
}
None
}
fn feature_available(&self, feature: &str) -> bool {
match feature {
"sse2" | "sse" => cfg!(target_arch = "x86_64") || cfg!(target_feature = "sse2"),
"avx" => cfg!(target_feature = "avx"),
"avx2" => cfg!(target_feature = "avx2"),
"avx512" => cfg!(target_feature = "avx512f"),
"x86_64" => cfg!(target_arch = "x86_64"),
"linux" => cfg!(target_os = "linux"),
"gtest" => true,
"catch2" => true,
"subprocess" => true,
_ => true, }
}
fn shuffle_tests(&self, tests: &mut [(String, X86TestCase)], seed: u64) {
let mut state = seed;
let n = tests.len();
for i in (1..n).rev() {
state = state
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
let j = (state >> 33) as usize % (i + 1);
tests.swap(i, j);
}
}
pub fn reset(&mut self) {
self.tests_executed.store(0, Ordering::Relaxed);
self.tests_passed.store(0, Ordering::Relaxed);
}
}
impl Default for X86TestRunner {
fn default() -> Self {
Self::new()
}
}
struct SimulatedCompileResult {
success: bool,
stdout: String,
stderr: String,
exit_code: i32,
}
pub struct X86ThreadPool {
pub size: usize,
tasks: Arc<Mutex<VecDeque<Box<dyn FnOnce() + Send + 'static>>>>,
condvar: Arc<Condvar>,
shutting_down: Arc<AtomicBool>,
workers: Vec<std::thread::JoinHandle<()>>,
}
impl X86ThreadPool {
pub fn new(size: usize) -> Self {
let tasks: Arc<Mutex<VecDeque<Box<dyn FnOnce() + Send + 'static>>>> =
Arc::new(Mutex::new(VecDeque::new()));
let condvar = Arc::new(Condvar::new());
let shutting_down = Arc::new(AtomicBool::new(false));
let mut workers = Vec::with_capacity(size);
for _ in 0..size {
let tasks_clone = Arc::clone(&tasks);
let condvar_clone = Arc::clone(&condvar);
let shutting_down_clone = Arc::clone(&shutting_down);
let handle = std::thread::spawn(move || loop {
let task = {
let mut queue = tasks_clone.lock().unwrap();
loop {
if let Some(task) = queue.pop_front() {
break Some(task);
}
if shutting_down_clone.load(Ordering::Relaxed) {
break None;
}
queue = condvar_clone.wait(queue).unwrap();
}
};
match task {
Some(task) => task(),
None => break,
}
});
workers.push(handle);
}
Self {
size,
tasks,
condvar,
shutting_down,
workers,
}
}
pub fn spawn<F>(&self, f: F) -> X86TaskHandle
where
F: FnOnce() + Send + 'static,
{
let done = Arc::new(AtomicBool::new(false));
let done_clone = Arc::clone(&done);
let task: Box<dyn FnOnce() + Send + 'static> = Box::new(move || {
f();
done_clone.store(true, Ordering::Relaxed);
});
{
let mut queue = self.tasks.lock().unwrap();
queue.push_back(task);
}
self.condvar.notify_one();
X86TaskHandle { done }
}
pub fn join(&self, handle: X86TaskHandle) {
while !handle.done.load(Ordering::Relaxed) {
std::thread::yield_now();
}
}
pub fn shutdown(self) {
self.shutting_down.store(true, Ordering::Relaxed);
self.condvar.notify_all();
}
}
impl Drop for X86ThreadPool {
fn drop(&mut self) {
self.shutting_down.store(true, Ordering::Relaxed);
self.condvar.notify_all();
}
}
pub struct X86TaskHandle {
done: Arc<AtomicBool>,
}
pub struct X86TestFramework {
pub framework: X86FrameworkType,
pub assertions: X86TestAssertions,
pub fixtures: X86TestFixtures,
pub generated_sources: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86FrameworkType {
GTest,
Catch2,
DocTest,
CppUTest,
BoostTest,
CUnit,
Check,
Unity,
CTest,
Custom,
}
impl fmt::Display for X86FrameworkType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86FrameworkType::GTest => write!(f, "gtest"),
X86FrameworkType::Catch2 => write!(f, "catch2"),
X86FrameworkType::DocTest => write!(f, "doctest"),
X86FrameworkType::CppUTest => write!(f, "cpputest"),
X86FrameworkType::BoostTest => write!(f, "boost"),
X86FrameworkType::CUnit => write!(f, "cunit"),
X86FrameworkType::Check => write!(f, "check"),
X86FrameworkType::Unity => write!(f, "unity"),
X86FrameworkType::CTest => write!(f, "ctest"),
X86FrameworkType::Custom => write!(f, "custom"),
}
}
}
impl X86TestFramework {
pub fn new(framework: X86FrameworkType) -> Self {
Self {
framework,
assertions: X86TestAssertions::new(),
fixtures: X86TestFixtures::new(),
generated_sources: Vec::new(),
}
}
pub fn detect_framework(source: &str) -> Option<X86FrameworkType> {
if source.contains("TEST(") || source.contains("TEST_F(") || source.contains("TEST_P(") {
if source.contains("gtest.h") || source.contains("gtest/") {
return Some(X86FrameworkType::GTest);
}
}
if source.contains("TEST_CASE(") || source.contains("CATCH_CONFIG_MAIN") {
return Some(X86FrameworkType::Catch2);
}
if source.contains("DOCTEST_CONFIG_IMPLEMENT") || source.contains("doctest.h") {
return Some(X86FrameworkType::DocTest);
}
if source.contains("CppUTest/TestHarness.h") {
return Some(X86FrameworkType::CppUTest);
}
if source.contains("BOOST_AUTO_TEST_CASE") || source.contains("BOOST_TEST_MODULE") {
return Some(X86FrameworkType::BoostTest);
}
if source.contains("CUnit/CUnit.h") || source.contains("CU_add_test") {
return Some(X86FrameworkType::CUnit);
}
if source.contains("check.h") || source.contains("START_TEST") {
return Some(X86FrameworkType::Check);
}
if source.contains("unity.h") || source.contains("UnityBegin") {
return Some(X86FrameworkType::Unity);
}
if source.contains("add_test(") && source.contains("CTest") {
return Some(X86FrameworkType::CTest);
}
None
}
pub fn generate_gtest_source(test_name: &str, suite_name: &str, body: &str) -> String {
format!(
r#"#include <gtest/gtest.h>
TEST({suite}, {test}) {{
{body}
}}
int main(int argc, char **argv) {{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}}
"#,
suite = suite_name,
test = test_name,
body = body
)
}
pub fn generate_gtest_fixture_source(
fixture_name: &str,
test_name: &str,
body: &str,
setup_body: &str,
teardown_body: &str,
) -> String {
format!(
r#"#include <gtest/gtest.h>
class {fixture} : public ::testing::Test {{
protected:
void SetUp() override {{
{setup}
}}
void TearDown() override {{
{teardown}
}}
}};
TEST_F({fixture}, {test}) {{
{body}
}}
int main(int argc, char **argv) {{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}}
"#,
fixture = fixture_name,
test = test_name,
setup = setup_body,
teardown = teardown_body,
body = body
)
}
pub fn generate_gtest_param_source(
fixture_name: &str,
test_name: &str,
params: &[&str],
body: &str,
) -> String {
let values = params.join(", ");
format!(
r#"#include <gtest/gtest.h>
class {fixture} : public ::testing::TestWithParam<int> {{
}};
TEST_P({fixture}, {test}) {{
int param = GetParam();
{body}
}}
INSTANTIATE_TEST_SUITE_P(Instantiation, {fixture}, ::testing::Values({values}));
int main(int argc, char **argv) {{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}}
"#,
fixture = fixture_name,
test = test_name,
values = values,
body = body
)
}
pub fn generate_catch2_source(test_name: &str, body: &str) -> String {
format!(
r#"#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
TEST_CASE("{name}") {{
{body}
}}
"#,
name = test_name,
body = body
)
}
pub fn generate_catch2_section_source(test_name: &str, sections: &[(&str, &str)]) -> String {
let mut section_code = String::new();
for (section_name, body) in sections {
section_code.push_str(&format!(
r#" SECTION("{name}") {{
{body}
}}
"#,
name = section_name,
body = body
));
}
format!(
r#"#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
TEST_CASE("{name}") {{
{body}
}}
"#,
name = test_name,
body = section_code
)
}
pub fn generate_doctest_source(test_name: &str, body: &str) -> String {
format!(
r#"#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest/doctest.h>
TEST_CASE("{name}") {{
{body}
}}
"#,
name = test_name,
body = body
)
}
pub fn generate_cpputest_source(group_name: &str, test_name: &str, body: &str) -> String {
format!(
r#"#include <CppUTest/TestHarness.h>
TEST_GROUP({group}) {{
}};
TEST({group}, {test}) {{
{body}
}}
int main(int argc, char **argv) {{
return CommandLineTestRunner::RunAllTests(argc, argv);
}}
"#,
group = group_name,
test = test_name,
body = body
)
}
pub fn generate_boost_test_source(test_name: &str, body: &str) -> String {
format!(
r#"#define BOOST_TEST_MODULE {name}
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_CASE({name}) {{
{body}
}}
"#,
name = test_name,
body = body
)
}
pub fn generate_cunit_source(suite_name: &str, tests: &[(&str, &str)]) -> String {
let mut test_registrations = String::new();
let mut test_functions = String::new();
for (i, (name, body)) in tests.iter().enumerate() {
test_functions.push_str(&format!(
r#"void test_{name}_{i}(void) {{
{body}
}}
"#,
name = name,
i = i,
body = body
));
test_registrations.push_str(&format!(
r#" CU_add_test(pSuite, "{name}", test_{name}_{i});
"#,
name = name,
i = i
));
}
format!(
r#"#include <CUnit/CUnit.h>
#include <CUnit/Basic.h>
{functions}
int main() {{
CU_initialize_registry();
CU_pSuite pSuite = CU_add_suite("{suite}", NULL, NULL);
{registrations}
CU_basic_run_tests();
CU_cleanup_registry();
return 0;
}}
"#,
functions = test_functions,
suite = suite_name,
registrations = test_registrations
)
}
pub fn generate_check_source(suite_name: &str, tests: &[(&str, &str)]) -> String {
let mut test_functions = String::new();
let _suite_tcase = String::new();
let mut tcase_tests = String::new();
for (name, body) in tests.iter() {
test_functions.push_str(&format!(
r#"START_TEST(test_{name}) {{
{body}
}}
END_TEST
"#,
name = name,
body = body
));
tcase_tests.push_str(&format!(" tcase_add_test(tc, test_{});\n", name));
}
format!(
r#"#include <check.h>
{functions}
Suite *{suite}_suite(void) {{
Suite *s = suite_create("{suite}");
TCase *tc = tcase_create("Core");
{tcase_tests}
suite_add_tcase(s, tc);
return s;
}}
int main(void) {{
int number_failed;
Suite *s = {suite}_suite();
SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? 0 : 1;
}}
"#,
functions = test_functions,
suite = suite_name,
tcase_tests = tcase_tests
)
}
pub fn generate_unity_source(tests: &[(&str, &str)]) -> String {
let mut test_functions = String::new();
let mut test_runners = String::new();
for (name, body) in tests.iter() {
test_functions.push_str(&format!(
r#"void test_{name}(void) {{
{body}
}}
"#,
name = name,
body = body
));
test_runners.push_str(&format!(
" RUN_TEST(test_{}, {});\n",
name,
tests.iter().position(|t| t.0 == *name).unwrap_or(0)
));
}
format!(
r#"#include "unity.h"
void setUp(void) {{ }}
void tearDown(void) {{ }}
{functions}
int main(void) {{
UNITY_BEGIN();
{test_runners}
return UNITY_END();
}}
"#,
functions = test_functions,
test_runners = test_runners
)
}
pub fn generate_ctest_source(tests: &[(&str, &str)]) -> String {
let mut cmake = String::from("enable_testing()\n\n");
cmake.push_str("# Generated CTest configuration for X86 Clang tests\n\n");
for (_i, (name, command)) in tests.iter().enumerate() {
cmake.push_str(&format!(
r#"add_test(NAME {name} COMMAND {command})
set_tests_properties({name} PROPERTIES
LABELS "x86;clang"
TIMEOUT {timeout}
PASS_REGULAR_EXPRESSION ".*PASS.*"
)
"#,
name = name,
command = command,
timeout = X86_DEFAULT_TEST_TIMEOUT_MS / 1000
));
}
cmake
}
pub fn parse_gtest_assertions(source: &str) -> Vec<String> {
let mut assertions = Vec::new();
let patterns = [
"EXPECT_EQ",
"EXPECT_NE",
"EXPECT_LT",
"EXPECT_LE",
"EXPECT_GT",
"EXPECT_GE",
"EXPECT_TRUE",
"EXPECT_FALSE",
"EXPECT_STREQ",
"EXPECT_STRNE",
"EXPECT_STRCASEEQ",
"EXPECT_FLOAT_EQ",
"EXPECT_DOUBLE_EQ",
"EXPECT_NEAR",
"EXPECT_THROW",
"EXPECT_NO_THROW",
"EXPECT_ANY_THROW",
"EXPECT_DEATH",
"EXPECT_EXIT",
"ASSERT_EQ",
"ASSERT_NE",
"ASSERT_LT",
"ASSERT_LE",
"ASSERT_GT",
"ASSERT_GE",
"ASSERT_TRUE",
"ASSERT_FALSE",
"ASSERT_STREQ",
"ASSERT_STRNE",
"ASSERT_FLOAT_EQ",
"ASSERT_DOUBLE_EQ",
"ASSERT_THROW",
"ASSERT_NO_THROW",
"ASSERT_DEATH",
"ASSERT_EXIT",
];
for pattern in &patterns {
let mut pos = 0;
while let Some(idx) = source[pos..].find(pattern) {
let start = pos + idx;
let rest = &source[start..];
if let Some(close) = rest.find(')') {
let macro_call = &rest[..=close];
assertions.push(macro_call.to_string());
}
pos = start + 1;
if pos >= source.len() {
break;
}
}
}
assertions
}
pub fn count_tests(source: &str, framework: X86FrameworkType) -> usize {
match framework {
X86FrameworkType::GTest => {
source.matches("TEST(").count()
+ source.matches("TEST_F(").count()
+ source.matches("TEST_P(").count()
}
X86FrameworkType::Catch2 | X86FrameworkType::DocTest => {
source.matches("TEST_CASE(").count()
}
X86FrameworkType::CppUTest => source.matches("TEST(").count(),
X86FrameworkType::BoostTest => source.matches("BOOST_AUTO_TEST_CASE(").count(),
X86FrameworkType::CUnit => source.matches("CU_add_test(").count(),
X86FrameworkType::Check => source.matches("START_TEST(").count(),
X86FrameworkType::Unity => source.matches("RUN_TEST(").count(),
X86FrameworkType::CTest => source.matches("add_test(").count(),
X86FrameworkType::Custom => 0,
}
}
pub fn validate_source(source: &str, framework: X86FrameworkType) -> Vec<String> {
let mut errors = Vec::new();
match framework {
X86FrameworkType::GTest => {
if !source.contains("InitGoogleTest") && !source.contains("RUN_ALL_TESTS") {
errors.push("Missing gtest main function or RUN_ALL_TESTS()".to_string());
}
}
X86FrameworkType::Catch2 => {
if !source.contains("CATCH_CONFIG_MAIN") && !source.contains("main(") {
errors.push("Missing Catch2 main or CATCH_CONFIG_MAIN".to_string());
}
}
X86FrameworkType::DocTest => {
if !source.contains("DOCTEST_CONFIG_IMPLEMENT") && !source.contains("main(") {
errors.push("Missing doctest main or DOCTEST_CONFIG_IMPLEMENT".to_string());
}
}
X86FrameworkType::BoostTest => {
if !source.contains("BOOST_TEST_MODULE") {
errors.push("Missing BOOST_TEST_MODULE definition".to_string());
}
}
X86FrameworkType::CUnit => {
if !source.contains("CU_initialize_registry") {
errors.push("Missing CU_initialize_registry()".to_string());
}
}
X86FrameworkType::Check => {
if !source.contains("suite_create") {
errors.push("Missing suite_create()".to_string());
}
}
X86FrameworkType::Unity => {
if !source.contains("UNITY_BEGIN") {
errors.push("Missing UNITY_BEGIN()".to_string());
}
}
_ => {}
}
errors
}
}
pub struct X86TestGenerators {
pub rng_state: u64,
pub fuzzer: X86FuzzerType,
pub mutation_engine: X86MutationEngine,
pub coverage_fuzzer: X86CoverageFuzzer,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86FuzzerType {
LibFuzzer,
AFL,
Honggfuzz,
Custom,
}
impl X86TestGenerators {
pub fn new() -> Self {
Self {
rng_state: 42,
fuzzer: X86FuzzerType::LibFuzzer,
mutation_engine: X86MutationEngine::new(),
coverage_fuzzer: X86CoverageFuzzer::new(),
}
}
pub fn gen_int(&mut self, min: i64, max: i64) -> i64 {
let range = (max - min + 1) as u64;
let rand = self.next_random() % range;
min + rand as i64
}
pub fn gen_uint(&mut self, min: u64, max: u64) -> u64 {
let range = max - min + 1;
let rand = self.next_random() % range;
min + rand
}
pub fn gen_float(&mut self, min: f64, max: f64) -> f64 {
let t = self.next_random() as f64 / u64::MAX as f64;
min + t * (max - min)
}
pub fn gen_string(&mut self, min_len: usize, max_len: usize) -> String {
let len = self.gen_uint(min_len as u64, max_len as u64) as usize;
(0..len)
.map(|_| {
let c = self.gen_uint(32, 126) as u8;
c as char
})
.collect()
}
pub fn gen_bool(&mut self) -> bool {
self.next_random() % 2 == 0
}
pub fn gen_bytes(&mut self, min_len: usize, max_len: usize) -> Vec<u8> {
let len = self.gen_uint(min_len as u64, max_len as u64) as usize;
(0..len)
.map(|_| (self.next_random() & 0xFF) as u8)
.collect()
}
pub fn gen_c_identifier(&mut self) -> String {
let len = self.gen_uint(1, 32) as usize;
let first = (b'a' + (self.next_random() % 26) as u8) as char;
let rest: String = (1..len)
.map(|_| {
let c = self.gen_uint(0, 61);
if c < 26 {
(b'a' + c as u8) as char
} else if c < 52 {
(b'A' + (c - 26) as u8) as char
} else if c < 62 {
(b'0' + (c - 52) as u8) as char
} else {
'_'
}
})
.collect();
format!("{}{}", first, rest)
}
pub fn gen_c_source(&mut self, num_functions: usize) -> String {
let mut source = String::new();
source.push_str("// Generated C source for fuzz testing\n");
source.push_str("#include <stdint.h>\n");
source.push_str("#include <stddef.h>\n\n");
for _i in 0..num_functions {
let name = self.gen_c_identifier();
let return_type = if self.gen_bool() { "int" } else { "void" };
let num_params = self.gen_uint(0, 4) as usize;
let params: Vec<String> = (0..num_params)
.map(|_| {
let types = ["int", "long", "char", "float", "double", "unsigned int"];
let t = types[(self.next_random() as usize) % types.len()];
let pname = self.gen_c_identifier();
format!("{} {}", t, pname)
})
.collect();
source.push_str(&format!(
"{} {}({}) {{\n",
return_type,
name,
params.join(", ")
));
let num_stmts = self.gen_uint(1, 10) as usize;
for _ in 0..num_stmts {
match self.gen_uint(0, 5) {
0 => {
let var = self.gen_c_identifier();
let val = self.gen_int(-1000, 1000);
source.push_str(&format!(" int {} = {};\n", var, val));
}
1 => {
let a = self.gen_c_identifier();
let b = self.gen_c_identifier();
let c = self.gen_c_identifier();
source.push_str(&format!(" int {} = {} + {};\n", a, b, c));
}
2 => {
source.push_str(" if (1) { ; }\n");
}
3 => {
source.push_str(" for (int i = 0; i < 10; i++) { ; }\n");
}
_ => {
source.push_str(" ;\n");
}
}
}
if return_type == "int" {
let val = self.gen_int(0, 100);
source.push_str(&format!(" return {};\n", val));
}
source.push_str("}\n\n");
}
source
}
pub fn shrink_int(&self, value: i64) -> Vec<i64> {
let mut candidates = Vec::new();
if value == 0 {
return candidates;
}
candidates.push(0);
let mut v = value;
while v != 0 {
v /= 2;
candidates.push(v);
}
candidates.push(-value);
candidates.push(value - 1);
candidates.push(value + 1);
candidates
}
pub fn shrink_string(&self, s: &str) -> Vec<String> {
let mut candidates = Vec::new();
if s.is_empty() {
return candidates;
}
candidates.push(String::new());
let half = s.len() / 2;
candidates.push(s[..half].to_string());
candidates.push(s[half..].to_string());
if s.len() > 1 {
candidates.push(s[1..].to_string());
}
if s.len() > 1 {
candidates.push(s[..s.len() - 1].to_string());
}
candidates
}
pub fn shrink_bytes(&self, data: &[u8]) -> Vec<Vec<u8>> {
let mut candidates = Vec::new();
if data.is_empty() {
return candidates;
}
candidates.push(Vec::new());
if data.len() > 1 {
let half = data.len() / 2;
candidates.push(data[..half].to_vec());
candidates.push(data[half..].to_vec());
candidates.push(data[1..].to_vec());
candidates.push(data[..data.len() - 1].to_vec());
}
candidates
}
pub fn generate_mutants(&mut self, source: &str) -> Vec<X86Mutant> {
self.mutation_engine.generate(source)
}
pub fn check_mutant_killed(&self, _mutant: &X86Mutant, test_result: &X86TestResult) -> bool {
test_result.status == X86TestStatus::Failed
}
pub fn mutation_score(&self, mutants: &[X86Mutant], killed: &HashSet<usize>) -> f64 {
if mutants.is_empty() {
return 100.0;
}
(killed.len() as f64 / mutants.len() as f64) * 100.0
}
pub fn gen_libfuzzer_input(&mut self, max_len: usize) -> Vec<u8> {
let len = (self.next_random() as usize) % (max_len + 1);
self.gen_bytes(0, len)
}
pub fn gen_afl_input(&mut self, seed: &[u8], max_len: usize) -> Vec<u8> {
let mut data = seed.to_vec();
let op = self.gen_uint(0, 10);
match op {
0 => {
if !data.is_empty() {
let idx = (self.next_random() as usize) % data.len();
let bit = (self.next_random() & 7) as u8;
data[idx] ^= 1 << bit;
}
}
1 => {
if !data.is_empty() {
let idx = (self.next_random() as usize) % data.len();
data[idx] ^= 0xFF;
}
}
2 => {
if !data.is_empty() {
let idx = (self.next_random() as usize) % data.len();
data[idx] = data[idx].wrapping_add(1);
}
}
3 => {
if !data.is_empty() {
let idx = (self.next_random() as usize) % data.len();
data[idx] = data[idx].wrapping_sub(1);
}
}
4 => {
if data.len() > 1 {
let idx = (self.next_random() as usize) % data.len();
data.remove(idx);
}
}
5 => {
let val = (self.next_random() & 0xFF) as u8;
let idx = if data.is_empty() {
0
} else {
(self.next_random() as usize) % data.len()
};
data.insert(idx, val);
}
6 => {
if !data.is_empty() && data.len() < max_len {
let idx = (self.next_random() as usize) % data.len();
let val = data[idx];
data.insert(idx, val);
}
}
7 => {
let chunk = data.clone();
data.extend_from_slice(&chunk);
}
_ => {
if !data.is_empty() {
let idx = (self.next_random() as usize) % data.len();
data[idx] = (self.next_random() & 0xFF) as u8;
}
}
}
data
}
pub fn fuzz_compile(&mut self, source: &str, config: &X86TestHarnessConfig) -> FuzzResult {
let runner = X86TestRunner::new();
let result = runner.simulate_compile(source, config);
if result.success {
FuzzResult::Pass
} else if result.exit_code == 139 || result.exit_code == 134 {
FuzzResult::Crash {
signal: result.exit_code,
stderr: result.stderr,
}
} else if result.exit_code == 124 {
FuzzResult::Timeout
} else {
FuzzResult::CompileError(result.stderr)
}
}
pub fn gen_coverage_guided_input(&mut self, coverage_data: &X86CoverageData) -> Vec<u8> {
self.coverage_fuzzer.generate(coverage_data)
}
fn next_random(&mut self) -> u64 {
let old = self.rng_state;
self.rng_state = old
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
let xorshifted = (((old >> 18) ^ old) >> 27) as u32;
let rot = (old >> 59) as u32;
((xorshifted >> rot) | (xorshifted << ((-(rot as i32)) & 31))) as u64
}
}
impl Default for X86TestGenerators {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub enum FuzzResult {
Pass,
Crash { signal: i32, stderr: String },
Timeout,
CompileError(String),
}
pub struct X86MutationEngine {
pub operators: Vec<X86MutationOperator>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86MutationOperator {
ArithmeticOp,
RelationalOp,
LogicalOp,
BitwiseOp,
ConstantValue,
StatementDelete,
ReturnValue,
SwapBranches,
RemoveFunctionCall,
}
impl X86MutationEngine {
pub fn new() -> Self {
Self {
operators: vec![
X86MutationOperator::ArithmeticOp,
X86MutationOperator::RelationalOp,
X86MutationOperator::LogicalOp,
X86MutationOperator::BitwiseOp,
X86MutationOperator::ConstantValue,
X86MutationOperator::StatementDelete,
X86MutationOperator::ReturnValue,
X86MutationOperator::SwapBranches,
X86MutationOperator::RemoveFunctionCall,
],
}
}
pub fn generate(&mut self, source: &str) -> Vec<X86Mutant> {
let mut mutants = Vec::new();
let mut id = 0;
id = self.mutate_arithmetic(source, id, &mut mutants);
id = self.mutate_relational(source, id, &mut mutants);
id = self.mutate_logical(source, id, &mut mutants);
id = self.mutate_constants(source, id, &mut mutants);
let _ = self.mutate_return_values(source, id, &mut mutants);
mutants
}
fn mutate_arithmetic(
&self,
source: &str,
mut id: usize,
mutants: &mut Vec<X86Mutant>,
) -> usize {
let operators = [
(" + ", " - "),
(" - ", " + "),
(" * ", " / "),
(" / ", " * "),
(" % ", " * "),
];
for (from, to) in &operators {
let mut pos = 0;
while let Some(idx) = source[pos..].find(from) {
let abs_pos = pos + idx;
let mut mutated = source[..abs_pos].to_string();
mutated.push_str(to);
mutated.push_str(&source[abs_pos + from.len()..]);
mutants.push(X86Mutant {
id,
operator: X86MutationOperator::ArithmeticOp,
original: source[abs_pos..abs_pos + from.len()].to_string(),
replacement: to.to_string(),
source: mutated,
location: abs_pos,
});
id += 1;
pos = abs_pos + 1;
if pos >= source.len() {
break;
}
}
}
id
}
fn mutate_relational(
&self,
source: &str,
mut id: usize,
mutants: &mut Vec<X86Mutant>,
) -> usize {
let operators = [
(" == ", " != "),
(" != ", " == "),
(" < ", " <= "),
(" > ", " >= "),
(" <= ", " < "),
(" >= ", " > "),
];
for (from, to) in &operators {
let mut pos = 0;
while let Some(idx) = source[pos..].find(from) {
let abs_pos = pos + idx;
let mut mutated = source[..abs_pos].to_string();
mutated.push_str(to);
mutated.push_str(&source[abs_pos + from.len()..]);
mutants.push(X86Mutant {
id,
operator: X86MutationOperator::RelationalOp,
original: from.to_string(),
replacement: to.to_string(),
source: mutated,
location: abs_pos,
});
id += 1;
pos = abs_pos + 1;
if pos >= source.len() {
break;
}
}
}
id
}
fn mutate_logical(&self, source: &str, mut id: usize, mutants: &mut Vec<X86Mutant>) -> usize {
let operators = [(" && ", " || "), (" || ", " && ")];
for (from, to) in &operators {
let mut pos = 0;
while let Some(idx) = source[pos..].find(from) {
let abs_pos = pos + idx;
let mut mutated = source[..abs_pos].to_string();
mutated.push_str(to);
mutated.push_str(&source[abs_pos + from.len()..]);
mutants.push(X86Mutant {
id,
operator: X86MutationOperator::LogicalOp,
original: from.to_string(),
replacement: to.to_string(),
source: mutated,
location: abs_pos,
});
id += 1;
pos = abs_pos + 1;
if pos >= source.len() {
break;
}
}
}
id
}
fn mutate_constants(&self, source: &str, mut id: usize, mutants: &mut Vec<X86Mutant>) -> usize {
let mut pos = 0;
let bytes = source.as_bytes();
while pos < bytes.len() {
if bytes[pos].is_ascii_digit() {
let start = pos;
while pos < bytes.len() && bytes[pos].is_ascii_digit() {
pos += 1;
}
let original = &source[start..pos];
for replacement in &["0", "1", "-1"] {
let mut mutated = source[..start].to_string();
mutated.push_str(replacement);
mutated.push_str(&source[pos..]);
mutants.push(X86Mutant {
id,
operator: X86MutationOperator::ConstantValue,
original: original.to_string(),
replacement: replacement.to_string(),
source: mutated,
location: start,
});
id += 1;
}
} else {
pos += 1;
}
}
id
}
fn mutate_return_values(
&self,
source: &str,
mut id: usize,
mutants: &mut Vec<X86Mutant>,
) -> usize {
let mut pos = 0;
while let Some(idx) = source[pos..].find("return ") {
let start = pos + idx;
let after = &source[start + 7..];
if let Some(semi) = after.find(';') {
let original = after[..semi].trim();
for replacement in &["0", "1", "-1"] {
let mut mutated = source[..start + 7].to_string();
mutated.push_str(replacement);
mutated.push_str(";");
mutated.push_str(&after[semi + 1..]);
mutants.push(X86Mutant {
id,
operator: X86MutationOperator::ReturnValue,
original: original.to_string(),
replacement: replacement.to_string(),
source: mutated,
location: start,
});
id += 1;
}
pos = start + 8;
} else {
pos = start + 1;
}
if pos >= source.len() {
break;
}
}
id
}
}
impl Default for X86MutationEngine {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86Mutant {
pub id: usize,
pub operator: X86MutationOperator,
pub original: String,
pub replacement: String,
pub source: String,
pub location: usize,
}
pub struct X86CoverageFuzzer {
pub corpus: Vec<Vec<u8>>,
pub coverage_map: HashMap<u64, u64>,
pub total_coverage: u64,
pub new_coverage: bool,
}
impl X86CoverageFuzzer {
pub fn new() -> Self {
Self {
corpus: Vec::new(),
coverage_map: HashMap::new(),
total_coverage: 0,
new_coverage: false,
}
}
pub fn maybe_add_to_corpus(&mut self, input: Vec<u8>, coverage: &HashMap<u64, u64>) -> bool {
let mut new_cov = false;
for (func, count) in coverage {
let entry = self.coverage_map.entry(*func).or_insert(0);
if *count > *entry {
*entry = *count;
new_cov = true;
}
}
if new_cov {
self.corpus.push(input);
self.new_coverage = true;
}
new_cov
}
pub fn generate(&mut self, coverage: &X86CoverageData) -> Vec<u8> {
if self.corpus.is_empty() {
return b"int main() { return 0; }".to_vec();
}
let idx = (coverage.functions_covered as usize) % self.corpus.len();
let mut data = self.corpus[idx].clone();
if !data.is_empty() {
let pos = (coverage.lines_covered as usize) % data.len();
data[pos] ^= 1;
}
data
}
pub fn reset(&mut self) {
self.corpus.clear();
self.coverage_map.clear();
self.total_coverage = 0;
self.new_coverage = false;
}
}
impl Default for X86CoverageFuzzer {
fn default() -> Self {
Self::new()
}
}
pub trait X86TestReporter: Send + Sync {
fn generate(&self, result: &X86TestRunResult, config: &X86TestHarnessConfig);
fn name(&self) -> &str;
}
pub struct X86ConsoleReporter {
pub color: bool,
pub progress_width: usize,
}
impl X86ConsoleReporter {
pub fn new(color: bool) -> Self {
Self {
color,
progress_width: 50,
}
}
fn print_status(&self, status: X86TestStatus) {
if self.color {
match status {
X86TestStatus::Passed => print!("\x1b[32m.\x1b[0m"),
X86TestStatus::Failed => print!("\x1b[31mF\x1b[0m"),
X86TestStatus::Skipped => print!("\x1b[33mS\x1b[0m"),
X86TestStatus::XFailed => print!("\x1b[35mX\x1b[0m"),
X86TestStatus::XPassed => print!("\x1b[36mU\x1b[0m"),
X86TestStatus::Timeout => print!("\x1b[33mT\x1b[0m"),
X86TestStatus::Crashed => print!("\x1b[31mC\x1b[0m"),
X86TestStatus::Disabled => print!("\x1b[90mD\x1b[0m"),
X86TestStatus::Flaky => print!("\x1b[35m~\x1b[0m"),
}
} else {
match status {
X86TestStatus::Passed => print!("."),
X86TestStatus::Failed => print!("F"),
X86TestStatus::Skipped => print!("S"),
X86TestStatus::XFailed => print!("X"),
X86TestStatus::XPassed => print!("U"),
X86TestStatus::Timeout => print!("T"),
X86TestStatus::Crashed => print!("C"),
X86TestStatus::Disabled => print!("D"),
X86TestStatus::Flaky => print!("~"),
}
}
let _ = io::stdout().flush();
}
fn print_progress(&self, current: usize, total: usize) {
if total == 0 {
return;
}
let filled = (current * self.progress_width) / total;
let bar: String = "=".repeat(filled) + &" ".repeat(self.progress_width - filled);
if self.color {
print!("\r[\x1b[32m{}\x1b[0m] {}/{}", bar, current, total);
} else {
print!("\r[{}] {}/{}", bar, current, total);
}
let _ = io::stdout().flush();
}
}
impl X86TestReporter for X86ConsoleReporter {
fn generate(&self, result: &X86TestRunResult, config: &X86TestHarnessConfig) {
if config.quiet {
return;
}
println!();
println!("{}", "=".repeat(72));
let title = if self.color {
format!("\x1b[1;36mX86 Clang Test Results\x1b[0m")
} else {
"X86 Clang Test Results".to_string()
};
println!(" {}", title);
println!("{}", "=".repeat(72));
println!();
for res in &result.results {
self.print_status(res.status);
}
println!();
println!();
if self.color {
println!(" \x1b[1mTest Summary:\x1b[0m");
println!(" \x1b[32mPassed: {}\x1b[0m", result.summary.passed);
if result.summary.failed > 0 {
println!(" \x1b[31mFailed: {}\x1b[0m", result.summary.failed);
}
if result.summary.skipped > 0 {
println!(" \x1b[33mSkipped: {}\x1b[0m", result.summary.skipped);
}
if result.summary.xfailed > 0 {
println!(" \x1b[35mXFailed: {}\x1b[0m", result.summary.xfailed);
}
if result.summary.flaky > 0 {
println!(" \x1b[36mFlaky: {}\x1b[0m", result.summary.flaky);
}
println!(" \x1b[1mTotal: {}\x1b[0m", result.summary.total);
println!(
" \x1b[1mTime: {:.2}s\x1b[0m",
result.total_duration_ms as f64 / 1000.0
);
println!(
" \x1b[1mRate: {:.1}%\x1b[0m",
result.summary.pass_rate()
);
} else {
println!(" Test Summary:");
println!(" Passed: {}", result.summary.passed);
println!(" Failed: {}", result.summary.failed);
println!(" Skipped: {}", result.summary.skipped);
println!(" XFailed: {}", result.summary.xfailed);
println!(" Flaky: {}", result.summary.flaky);
println!(" Total: {}", result.summary.total);
println!(
" Time: {:.2}s",
result.total_duration_ms as f64 / 1000.0
);
println!(" Rate: {:.1}%", result.summary.pass_rate());
}
let failures = result.failures();
if !failures.is_empty() {
println!();
if self.color {
println!(" \x1b[1;31mFailed Tests:\x1b[0m");
} else {
println!(" Failed Tests:");
}
println!(" {}", "-".repeat(68));
for res in &failures {
println!(" [FAIL] {}::{}", res.suite, res.name);
if !res.message.is_empty() {
println!(" {}", res.message);
}
}
}
let flaky = result.flaky_tests();
if !flaky.is_empty() {
println!();
if self.color {
println!(" \x1b[1;35mFlaky Tests:\x1b[0m");
} else {
println!(" Flaky Tests:");
}
for res in &flaky {
println!(" [FLAKY] {}::{}", res.suite, res.name);
}
}
println!();
}
fn name(&self) -> &str {
"console"
}
}
pub struct X86JUnitReporter {
pub output_path: PathBuf,
}
impl X86JUnitReporter {
pub fn new(output_path: &Path) -> Self {
Self {
output_path: output_path.to_path_buf(),
}
}
fn escape_xml(s: &str) -> String {
s.replace('&', "&")
.replace('<', "<")
.replace('>', ">")
.replace('"', """)
.replace('\'', "'")
}
}
impl X86TestReporter for X86JUnitReporter {
fn generate(&self, result: &X86TestRunResult, config: &X86TestHarnessConfig) {
if !config.junit_report {
return;
}
let mut xml = String::new();
xml.push_str("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
let total_time = result.total_duration_ms as f64 / 1000.0;
xml.push_str(&format!(
"<testsuites name=\"X86 Clang Tests\" tests=\"{}\" failures=\"{}\" errors=\"{}\" time=\"{:.3}\">\n",
result.summary.total,
result.summary.failed,
result.summary.crashed + result.summary.timeout,
total_time
));
let mut suites: HashMap<String, Vec<&X86TestResult>> = HashMap::new();
for res in &result.results {
suites.entry(res.suite.clone()).or_default().push(res);
}
for (suite_name, tests) in &suites {
let suite_time = tests.iter().map(|t| t.duration_ms).sum::<u64>() as f64 / 1000.0;
let suite_failures = tests
.iter()
.filter(|t| t.status == X86TestStatus::Failed)
.count();
let suite_errors = tests
.iter()
.filter(|t| matches!(t.status, X86TestStatus::Crashed | X86TestStatus::Timeout))
.count();
xml.push_str(&format!(
" <testsuite name=\"{}\" tests=\"{}\" failures=\"{}\" errors=\"{}\" time=\"{:.3}\">\n",
Self::escape_xml(suite_name),
tests.len(),
suite_failures,
suite_errors,
suite_time
));
for test in tests {
let test_time = test.duration_ms as f64 / 1000.0;
xml.push_str(&format!(
" <testcase name=\"{}\" classname=\"{}\" time=\"{:.3}\"",
Self::escape_xml(&test.name),
Self::escape_xml(suite_name),
test_time
));
match test.status {
X86TestStatus::Failed => {
xml.push_str(">\n");
xml.push_str(&format!(
" <failure message=\"{}\" type=\"AssertionFailure\">\n",
Self::escape_xml(&test.message)
));
xml.push_str(&format!(" {}\n", Self::escape_xml(&test.stderr)));
xml.push_str(" </failure>\n");
xml.push_str(" </testcase>\n");
}
X86TestStatus::Crashed | X86TestStatus::Timeout => {
xml.push_str(">\n");
xml.push_str(&format!(
" <error message=\"{}\" type=\"Error\">\n",
Self::escape_xml(&test.message)
));
xml.push_str(&format!(" {}\n", Self::escape_xml(&test.stderr)));
xml.push_str(" </error>\n");
xml.push_str(" </testcase>\n");
}
X86TestStatus::Skipped | X86TestStatus::Disabled => {
xml.push_str(">\n");
xml.push_str(&format!(
" <skipped message=\"{}\" />\n",
Self::escape_xml(&test.message)
));
xml.push_str(" </testcase>\n");
}
_ => {
xml.push_str(" />\n");
}
}
}
xml.push_str(" </testsuite>\n");
}
xml.push_str("</testsuites>\n");
if let Some(parent) = self.output_path.parent() {
let _ = std::fs::create_dir_all(parent);
}
if let Err(e) = std::fs::write(&self.output_path, &xml) {
eprintln!("Warning: Failed to write JUnit report: {}", e);
}
}
fn name(&self) -> &str {
"junit"
}
}
pub struct X86JSONReporter {
pub output_path: PathBuf,
pub pretty: bool,
}
impl X86JSONReporter {
pub fn new(output_path: &Path, pretty: bool) -> Self {
Self {
output_path: output_path.to_path_buf(),
pretty,
}
}
}
impl X86TestReporter for X86JSONReporter {
fn generate(&self, result: &X86TestRunResult, config: &X86TestHarnessConfig) {
if !config.json_report {
return;
}
let json = self.build_json(result);
if let Some(parent) = self.output_path.parent() {
let _ = std::fs::create_dir_all(parent);
}
if let Err(e) = std::fs::write(&self.output_path, &json) {
eprintln!("Warning: Failed to write JSON report: {}", e);
}
}
fn name(&self) -> &str {
"json"
}
}
impl X86JSONReporter {
fn build_json(&self, result: &X86TestRunResult) -> String {
let mut json = String::new();
if self.pretty {
json.push_str("{\n");
json.push_str(&format!(" \"test_suite\": \"X86 Clang Tests\",\n"));
json.push_str(&format!(" \"summary\": {{\n"));
json.push_str(&format!(" \"total\": {},\n", result.summary.total));
json.push_str(&format!(" \"passed\": {},\n", result.summary.passed));
json.push_str(&format!(" \"failed\": {},\n", result.summary.failed));
json.push_str(&format!(" \"skipped\": {},\n", result.summary.skipped));
json.push_str(&format!(" \"xfailed\": {},\n", result.summary.xfailed));
json.push_str(&format!(" \"flaky\": {},\n", result.summary.flaky));
json.push_str(&format!(
" \"duration_ms\": {},\n",
result.total_duration_ms
));
json.push_str(&format!(
" \"pass_rate\": {:.1}\n",
result.summary.pass_rate()
));
json.push_str(" },\n");
json.push_str(" \"results\": [\n");
for (i, res) in result.results.iter().enumerate() {
json.push_str(" {\n");
json.push_str(&format!(
" \"name\": \"{}\",\n",
Self::json_escape(&res.name)
));
json.push_str(&format!(
" \"suite\": \"{}\",\n",
Self::json_escape(&res.suite)
));
json.push_str(&format!(" \"status\": \"{}\",\n", res.status));
json.push_str(&format!(" \"duration_ms\": {},\n", res.duration_ms));
json.push_str(&format!(" \"exit_code\": {},\n", res.exit_code));
json.push_str(&format!(
" \"tags\": [{}],\n",
res.tags
.iter()
.map(|t| format!("\"{}\"", t))
.collect::<Vec<_>>()
.join(", ")
));
json.push_str(&format!(
" \"message\": \"{}\"\n",
Self::json_escape(&res.message)
));
if i < result.results.len() - 1 {
json.push_str(" },\n");
} else {
json.push_str(" }\n");
}
}
json.push_str(" ]\n");
json.push_str("}\n");
} else {
json.push_str("{");
json.push_str(&format!("\"test_suite\":\"X86 Clang Tests\","));
json.push_str(&format!(
"\"summary\":{{\"total\":{},\"passed\":{},\"failed\":{},\"skipped\":{},\"xfailed\":{},\"flaky\":{},\"duration_ms\":{},\"pass_rate\":{:.1}}},",
result.summary.total,
result.summary.passed,
result.summary.failed,
result.summary.skipped,
result.summary.xfailed,
result.summary.flaky,
result.total_duration_ms,
result.summary.pass_rate()
));
json.push_str("\"results\":[");
for (i, res) in result.results.iter().enumerate() {
json.push_str(&format!(
"{{\"name\":\"{}\",\"suite\":\"{}\",\"status\":\"{}\",\"duration_ms\":{},\"exit_code\":{},\"tags\":[{}],\"message\":\"{}\"}}",
Self::json_escape(&res.name),
Self::json_escape(&res.suite),
res.status,
res.duration_ms,
res.exit_code,
res.tags
.iter()
.map(|t| format!("\"{}\"", t))
.collect::<Vec<_>>()
.join(","),
Self::json_escape(&res.message)
));
if i < result.results.len() - 1 {
json.push(',');
}
}
json.push_str("]");
json.push('}');
}
json
}
fn json_escape(s: &str) -> String {
s.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('\n', "\\n")
.replace('\r', "\\r")
.replace('\t', "\\t")
}
}
pub struct X86TAPReporter {
pub output_path: PathBuf,
pub tap_version: u32,
}
impl X86TAPReporter {
pub fn new(output_path: &Path) -> Self {
Self {
output_path: output_path.to_path_buf(),
tap_version: 13,
}
}
}
impl X86TestReporter for X86TAPReporter {
fn generate(&self, result: &X86TestRunResult, config: &X86TestHarnessConfig) {
if !config.tap_report {
return;
}
let mut tap = String::new();
tap.push_str(&format!("TAP version {}\n", self.tap_version));
tap.push_str(&format!("1..{}\n", result.summary.total));
for (i, res) in result.results.iter().enumerate() {
let num = i + 1;
match res.status {
X86TestStatus::Passed | X86TestStatus::XFailed => {
tap.push_str(&format!("ok {} - {}::{}\n", num, res.suite, res.name));
}
X86TestStatus::Failed => {
tap.push_str(&format!("not ok {} - {}::{}\n", num, res.suite, res.name));
tap.push_str(&format!(" ---\n"));
tap.push_str(&format!(" message: {}\n", res.message));
tap.push_str(&format!(" severity: fail\n"));
tap.push_str(&format!(" duration_ms: {}\n", res.duration_ms));
tap.push_str(&format!(" ...\n"));
}
X86TestStatus::Skipped | X86TestStatus::Disabled => {
tap.push_str(&format!(
"ok {} - {}::{} # SKIP {}\n",
num, res.suite, res.name, res.message
));
}
X86TestStatus::Timeout => {
tap.push_str(&format!(
"not ok {} - {}::{} # TIMEOUT\n",
num, res.suite, res.name
));
}
X86TestStatus::Crashed => {
tap.push_str(&format!(
"not ok {} - {}::{} # CRASHED\n",
num, res.suite, res.name
));
}
X86TestStatus::XPassed => {
tap.push_str(&format!(
"ok {} - {}::{} # TODO unexpected pass\n",
num, res.suite, res.name
));
}
X86TestStatus::Flaky => {
tap.push_str(&format!(
"ok {} - {}::{} # TODO flaky test\n",
num, res.suite, res.name
));
}
}
}
if let Some(parent) = self.output_path.parent() {
let _ = std::fs::create_dir_all(parent);
}
if let Err(e) = std::fs::write(&self.output_path, &tap) {
eprintln!("Warning: Failed to write TAP report: {}", e);
}
}
fn name(&self) -> &str {
"tap"
}
}
pub struct X86HTMLReporter {
pub output_path: PathBuf,
}
impl X86HTMLReporter {
pub fn new(output_path: &Path) -> Self {
Self {
output_path: output_path.to_path_buf(),
}
}
}
impl X86TestReporter for X86HTMLReporter {
fn generate(&self, result: &X86TestRunResult, config: &X86TestHarnessConfig) {
if !config.html_report {
return;
}
let html = self.build_html(result);
if let Some(parent) = self.output_path.parent() {
let _ = std::fs::create_dir_all(parent);
}
if let Err(e) = std::fs::write(&self.output_path, &html) {
eprintln!("Warning: Failed to write HTML report: {}", e);
}
}
fn name(&self) -> &str {
"html"
}
}
impl X86HTMLReporter {
fn build_html(&self, result: &X86TestRunResult) -> String {
let pass_rate = result.summary.pass_rate();
let _pass_color = if pass_rate >= 90.0 {
"#27ae60"
} else if pass_rate >= 70.0 {
"#f39c12"
} else {
"#e74c3c"
};
let mut html = String::new();
html.push_str("<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n");
html.push_str("<meta charset=\"UTF-8\">\n");
html.push_str("<title>X86 Clang Test Report</title>\n");
html.push_str("<style>\n");
html.push_str(
"body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 40px; background: #f5f5f5; }\n",
);
html.push_str(
".container { max-width: 960px; margin: 0 auto; background: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }\n",
);
html.push_str(
"h1 { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }\n",
);
html.push_str(".summary { display: grid; grid-template-columns: repeat(6, 1fr); gap: 15px; margin: 20px 0; }\n");
html.push_str(
".summary-card { padding: 15px; border-radius: 6px; text-align: center; color: white; }\n",
);
html.push_str(".summary-card .count { font-size: 32px; font-weight: bold; }\n");
html.push_str(".summary-card .label { font-size: 12px; text-transform: uppercase; }\n");
html.push_str(".passed { background: #27ae60; }\n");
html.push_str(".failed { background: #e74c3c; }\n");
html.push_str(".skipped { background: #f39c12; }\n");
html.push_str(".xfailed { background: #8e44ad; }\n");
html.push_str(".flaky { background: #3498db; }\n");
html.push_str(".pass-rate { background: #2c3e50; }\n");
html.push_str("table { width: 100%; border-collapse: collapse; margin-top: 20px; }\n");
html.push_str(
"th, td { padding: 10px; text-align: left; border-bottom: 1px solid #ddd; }\n",
);
html.push_str("th { background: #3498db; color: white; }\n");
html.push_str("tr:hover { background: #f8f9fa; }\n");
html.push_str(".status-pass { color: #27ae60; font-weight: bold; }\n");
html.push_str(".status-fail { color: #e74c3c; font-weight: bold; }\n");
html.push_str(".status-skip { color: #f39c12; }\n");
html.push_str(".status-xfail { color: #8e44ad; }\n");
html.push_str(".status-flaky { color: #3498db; }\n");
html.push_str("</style>\n");
html.push_str("</head>\n<body>\n");
html.push_str("<div class=\"container\">\n");
html.push_str("<h1>X86 Clang Test Report</h1>\n");
html.push_str("<div class=\"summary\">\n");
html.push_str(&format!(
"<div class=\"summary-card passed\"><div class=\"count\">{}</div><div class=\"label\">Passed</div></div>\n",
result.summary.passed
));
html.push_str(&format!(
"<div class=\"summary-card failed\"><div class=\"count\">{}</div><div class=\"label\">Failed</div></div>\n",
result.summary.failed
));
html.push_str(&format!(
"<div class=\"summary-card skipped\"><div class=\"count\">{}</div><div class=\"label\">Skipped</div></div>\n",
result.summary.skipped
));
html.push_str(&format!(
"<div class=\"summary-card xfailed\"><div class=\"count\">{}</div><div class=\"label\">XFailed</div></div>\n",
result.summary.xfailed
));
html.push_str(&format!(
"<div class=\"summary-card flaky\"><div class=\"count\">{}</div><div class=\"label\">Flaky</div></div>\n",
result.summary.flaky
));
html.push_str(&format!(
"<div class=\"summary-card pass-rate\"><div class=\"count\">{:.0}%</div><div class=\"label\">Pass Rate</div></div>\n",
pass_rate
));
html.push_str("</div>\n");
html.push_str(&format!(
"<p>Total duration: <strong>{:.2}s</strong> | Total tests: <strong>{}</strong></p>\n",
result.total_duration_ms as f64 / 1000.0,
result.summary.total
));
html.push_str("<table>\n");
html.push_str("<tr><th>Status</th><th>Suite</th><th>Test</th><th>Duration</th><th>Message</th></tr>\n");
for res in &result.results {
let status_class = match res.status {
X86TestStatus::Passed => "status-pass",
X86TestStatus::Failed => "status-fail",
X86TestStatus::Skipped => "status-skip",
X86TestStatus::XFailed => "status-xfail",
X86TestStatus::XPassed => "status-xfail",
X86TestStatus::Flaky => "status-flaky",
_ => "status-fail",
};
html.push_str(&format!(
"<tr><td class=\"{}\">{}</td><td>{}</td><td>{}</td><td>{:.3}s</td><td>{}</td></tr>\n",
status_class,
res.status,
Self::html_escape(&res.suite),
Self::html_escape(&res.name),
res.duration_ms as f64 / 1000.0,
Self::html_escape(&res.message)
));
}
html.push_str("</table>\n");
html.push_str(&format!(
"<p style=\"text-align:right;color:#999;margin-top:20px;\">Generated by X86 Clang Test Harness</p>\n"
));
html.push_str("</div>\n</body>\n</html>\n");
html
}
fn html_escape(s: &str) -> String {
s.replace('&', "&")
.replace('<', "<")
.replace('>', ">")
.replace('"', """)
}
}
pub struct X86MarkdownReporter {
pub output_path: PathBuf,
}
impl X86MarkdownReporter {
pub fn new(output_path: &Path) -> Self {
Self {
output_path: output_path.to_path_buf(),
}
}
}
impl X86TestReporter for X86MarkdownReporter {
fn generate(&self, result: &X86TestRunResult, config: &X86TestHarnessConfig) {
if !config.markdown_report {
return;
}
let md = self.build_markdown(result);
if let Some(parent) = self.output_path.parent() {
let _ = std::fs::create_dir_all(parent);
}
if let Err(e) = std::fs::write(&self.output_path, &md) {
eprintln!("Warning: Failed to write Markdown report: {}", e);
}
}
fn name(&self) -> &str {
"markdown"
}
}
impl X86MarkdownReporter {
fn build_markdown(&self, result: &X86TestRunResult) -> String {
let mut md = String::new();
md.push_str("# X86 Clang Test Report\n\n");
md.push_str("## Summary\n\n");
md.push_str("| Metric | Value |\n");
md.push_str("|--------|-------|\n");
md.push_str(&format!("| Total Tests | {} |\n", result.summary.total));
md.push_str(&format!("| Passed | {} |\n", result.summary.passed));
md.push_str(&format!("| Failed | {} |\n", result.summary.failed));
md.push_str(&format!("| Skipped | {} |\n", result.summary.skipped));
md.push_str(&format!(
"| Expected Failures | {} |\n",
result.summary.xfailed
));
md.push_str(&format!("| Flaky Tests | {} |\n", result.summary.flaky));
md.push_str(&format!(
"| Duration | {:.2}s |\n",
result.total_duration_ms as f64 / 1000.0
));
md.push_str(&format!(
"| Pass Rate | {:.1}% |\n\n",
result.summary.pass_rate()
));
md.push_str("## Results\n\n");
md.push_str("| Status | Suite | Test | Duration |\n");
md.push_str("|--------|-------|------|----------|\n");
for res in &result.results {
let icon = match res.status {
X86TestStatus::Passed => "✅",
X86TestStatus::Failed => "❌",
X86TestStatus::Skipped => "⏭️",
X86TestStatus::XFailed => "🟣",
X86TestStatus::XPassed => "🟠",
X86TestStatus::Timeout => "⏰",
X86TestStatus::Crashed => "💥",
X86TestStatus::Disabled => "⚫",
X86TestStatus::Flaky => "🔵",
};
md.push_str(&format!(
"| {} {} | {} | {} | {:.3}s |\n",
icon,
res.status,
res.suite,
res.name,
res.duration_ms as f64 / 1000.0
));
}
md.push_str(&format!("\n---\n*Generated by X86 Clang Test Harness*\n"));
md
}
}
pub struct X86TestFixtures {
pub global_setup: Vec<Box<dyn Fn()>>,
pub global_teardown: Vec<Box<dyn Fn()>>,
pub temp_dirs: Vec<PathBuf>,
pub temp_files: Vec<PathBuf>,
pub temp_dir_prefix: String,
}
impl X86TestFixtures {
pub fn new() -> Self {
Self {
global_setup: Vec::new(),
global_teardown: Vec::new(),
temp_dirs: Vec::new(),
temp_files: Vec::new(),
temp_dir_prefix: "x86_clang_test_".to_string(),
}
}
pub fn register_global_setup(&mut self, f: Box<dyn Fn()>) {
self.global_setup.push(f);
}
pub fn register_global_teardown(&mut self, f: Box<dyn Fn()>) {
self.global_teardown.push(f);
}
pub fn run_global_setup(&self) {
for setup in &self.global_setup {
setup();
}
}
pub fn run_global_teardown(&self) {
for teardown in &self.global_teardown {
teardown();
}
}
pub fn create_temp_dir(&mut self) -> io::Result<PathBuf> {
let dir = std::env::temp_dir().join(format!("{}{}", self.temp_dir_prefix, self.next_id()));
std::fs::create_dir_all(&dir)?;
self.temp_dirs.push(dir.clone());
Ok(dir)
}
pub fn create_temp_file(&mut self, content: &str) -> io::Result<PathBuf> {
let dir = self.create_temp_dir()?;
let file_path = dir.join(format!("test_{}.c", self.next_id()));
std::fs::write(&file_path, content)?;
self.temp_files.push(file_path.clone());
Ok(file_path)
}
pub fn create_temp_c_source(&mut self, name: &str, content: &str) -> io::Result<PathBuf> {
let dir = self.create_temp_dir()?;
let file_path = dir.join(format!("{}.c", name));
std::fs::write(&file_path, content)?;
self.temp_files.push(file_path.clone());
Ok(file_path)
}
pub fn create_temp_header(&mut self, name: &str, content: &str) -> io::Result<PathBuf> {
let dir = self.create_temp_dir()?;
let file_path = dir.join(format!("{}.h", name));
std::fs::write(&file_path, content)?;
self.temp_files.push(file_path.clone());
Ok(file_path)
}
pub fn cleanup(&mut self) {
for file in self.temp_files.drain(..) {
let _ = std::fs::remove_file(&file);
}
for dir in self.temp_dirs.drain(..).rev() {
let _ = std::fs::remove_dir_all(&dir);
}
}
pub fn active_resources(&self) -> usize {
self.temp_files.len() + self.temp_dirs.len()
}
fn next_id(&self) -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64
}
}
impl Default for X86TestFixtures {
fn default() -> Self {
Self::new()
}
}
impl Drop for X86TestFixtures {
fn drop(&mut self) {
self.cleanup();
}
}
pub struct X86TestAssertions {
pub total_assertions: AtomicU64,
pub passed_assertions: AtomicU64,
pub failed_assertions: AtomicU64,
}
impl X86TestAssertions {
pub fn new() -> Self {
Self {
total_assertions: AtomicU64::new(0),
passed_assertions: AtomicU64::new(0),
failed_assertions: AtomicU64::new(0),
}
}
pub fn assert_eq<T: PartialEq + fmt::Debug>(
&self,
expected: &T,
actual: &T,
) -> Result<(), String> {
self.total_assertions.fetch_add(1, Ordering::Relaxed);
if expected == actual {
self.passed_assertions.fetch_add(1, Ordering::Relaxed);
Ok(())
} else {
self.failed_assertions.fetch_add(1, Ordering::Relaxed);
Err(format!(
"ASSERT_EQ failed: expected {:?}, got {:?}",
expected, actual
))
}
}
pub fn assert_ne<T: PartialEq + fmt::Debug>(&self, left: &T, right: &T) -> Result<(), String> {
self.total_assertions.fetch_add(1, Ordering::Relaxed);
if left != right {
self.passed_assertions.fetch_add(1, Ordering::Relaxed);
Ok(())
} else {
self.failed_assertions.fetch_add(1, Ordering::Relaxed);
Err(format!("ASSERT_NE failed: both values are {:?}", left))
}
}
pub fn assert_lt<T: PartialOrd + fmt::Debug>(&self, left: &T, right: &T) -> Result<(), String> {
self.total_assertions.fetch_add(1, Ordering::Relaxed);
if left < right {
self.passed_assertions.fetch_add(1, Ordering::Relaxed);
Ok(())
} else {
self.failed_assertions.fetch_add(1, Ordering::Relaxed);
Err(format!("ASSERT_LT failed: {:?} is not < {:?}", left, right))
}
}
pub fn assert_le<T: PartialOrd + fmt::Debug>(&self, left: &T, right: &T) -> Result<(), String> {
self.total_assertions.fetch_add(1, Ordering::Relaxed);
if left <= right {
self.passed_assertions.fetch_add(1, Ordering::Relaxed);
Ok(())
} else {
self.failed_assertions.fetch_add(1, Ordering::Relaxed);
Err(format!(
"ASSERT_LE failed: {:?} is not <= {:?}",
left, right
))
}
}
pub fn assert_gt<T: PartialOrd + fmt::Debug>(&self, left: &T, right: &T) -> Result<(), String> {
self.total_assertions.fetch_add(1, Ordering::Relaxed);
if left > right {
self.passed_assertions.fetch_add(1, Ordering::Relaxed);
Ok(())
} else {
self.failed_assertions.fetch_add(1, Ordering::Relaxed);
Err(format!("ASSERT_GT failed: {:?} is not > {:?}", left, right))
}
}
pub fn assert_ge<T: PartialOrd + fmt::Debug>(&self, left: &T, right: &T) -> Result<(), String> {
self.total_assertions.fetch_add(1, Ordering::Relaxed);
if left >= right {
self.passed_assertions.fetch_add(1, Ordering::Relaxed);
Ok(())
} else {
self.failed_assertions.fetch_add(1, Ordering::Relaxed);
Err(format!(
"ASSERT_GE failed: {:?} is not >= {:?}",
left, right
))
}
}
pub fn assert_float_eq(&self, expected: f32, actual: f32, epsilon: f32) -> Result<(), String> {
self.total_assertions.fetch_add(1, Ordering::Relaxed);
let diff = (expected - actual).abs();
if diff <= epsilon {
self.passed_assertions.fetch_add(1, Ordering::Relaxed);
Ok(())
} else {
self.failed_assertions.fetch_add(1, Ordering::Relaxed);
Err(format!(
"ASSERT_FLOAT_EQ failed: expected {}, got {} (diff: {})",
expected, actual, diff
))
}
}
pub fn assert_double_eq(&self, expected: f64, actual: f64, epsilon: f64) -> Result<(), String> {
self.total_assertions.fetch_add(1, Ordering::Relaxed);
let diff = (expected - actual).abs();
if diff <= epsilon {
self.passed_assertions.fetch_add(1, Ordering::Relaxed);
Ok(())
} else {
self.failed_assertions.fetch_add(1, Ordering::Relaxed);
Err(format!(
"ASSERT_DOUBLE_EQ failed: expected {}, got {} (diff: {})",
expected, actual, diff
))
}
}
pub fn assert_near(
&self,
expected: f64,
actual: f64,
max_relative_error: f64,
) -> Result<(), String> {
self.total_assertions.fetch_add(1, Ordering::Relaxed);
let abs_diff = (expected - actual).abs();
let max_abs = expected.abs().max(actual.abs()).max(1.0);
if abs_diff <= max_relative_error * max_abs {
self.passed_assertions.fetch_add(1, Ordering::Relaxed);
Ok(())
} else {
self.failed_assertions.fetch_add(1, Ordering::Relaxed);
Err(format!(
"ASSERT_NEAR failed: expected {}, got {} (diff: {})",
expected, actual, abs_diff
))
}
}
pub fn assert_streq(&self, expected: &str, actual: &str) -> Result<(), String> {
self.total_assertions.fetch_add(1, Ordering::Relaxed);
if expected == actual {
self.passed_assertions.fetch_add(1, Ordering::Relaxed);
Ok(())
} else {
self.failed_assertions.fetch_add(1, Ordering::Relaxed);
Err(format!(
"ASSERT_STREQ failed:\n expected: \"{}\"\n actual: \"{}\"",
expected, actual
))
}
}
pub fn assert_strne(&self, left: &str, right: &str) -> Result<(), String> {
self.total_assertions.fetch_add(1, Ordering::Relaxed);
if left != right {
self.passed_assertions.fetch_add(1, Ordering::Relaxed);
Ok(())
} else {
self.failed_assertions.fetch_add(1, Ordering::Relaxed);
Err(format!(
"ASSERT_STRNE failed: both strings are \"{}\"",
left
))
}
}
pub fn assert_strcaseeq(&self, expected: &str, actual: &str) -> Result<(), String> {
self.total_assertions.fetch_add(1, Ordering::Relaxed);
if expected.to_lowercase() == actual.to_lowercase() {
self.passed_assertions.fetch_add(1, Ordering::Relaxed);
Ok(())
} else {
self.failed_assertions.fetch_add(1, Ordering::Relaxed);
Err(format!(
"ASSERT_STRCASEEQ failed:\n expected: \"{}\"\n actual: \"{}\"",
expected, actual
))
}
}
pub fn assert_throw<F>(&self, f: F) -> Result<(), String>
where
F: FnOnce(),
{
self.total_assertions.fetch_add(1, Ordering::Relaxed);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f));
if result.is_err() {
self.passed_assertions.fetch_add(1, Ordering::Relaxed);
Ok(())
} else {
self.failed_assertions.fetch_add(1, Ordering::Relaxed);
Err("ASSERT_THROW failed: no exception thrown".to_string())
}
}
pub fn assert_no_throw<F>(&self, f: F) -> Result<(), String>
where
F: FnOnce(),
{
self.total_assertions.fetch_add(1, Ordering::Relaxed);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f));
if result.is_ok() {
self.passed_assertions.fetch_add(1, Ordering::Relaxed);
Ok(())
} else {
self.failed_assertions.fetch_add(1, Ordering::Relaxed);
Err("ASSERT_NO_THROW failed: exception was thrown".to_string())
}
}
pub fn assert_true(&self, condition: bool, msg: &str) -> Result<(), String> {
self.total_assertions.fetch_add(1, Ordering::Relaxed);
if condition {
self.passed_assertions.fetch_add(1, Ordering::Relaxed);
Ok(())
} else {
self.failed_assertions.fetch_add(1, Ordering::Relaxed);
Err(format!("ASSERT_TRUE failed: {}", msg))
}
}
pub fn assert_false(&self, condition: bool, msg: &str) -> Result<(), String> {
self.total_assertions.fetch_add(1, Ordering::Relaxed);
if !condition {
self.passed_assertions.fetch_add(1, Ordering::Relaxed);
Ok(())
} else {
self.failed_assertions.fetch_add(1, Ordering::Relaxed);
Err(format!("ASSERT_FALSE failed: {}", msg))
}
}
pub fn assert_pred<F>(&self, predicate: F, msg: &str) -> Result<(), String>
where
F: FnOnce() -> bool,
{
self.total_assertions.fetch_add(1, Ordering::Relaxed);
if predicate() {
self.passed_assertions.fetch_add(1, Ordering::Relaxed);
Ok(())
} else {
self.failed_assertions.fetch_add(1, Ordering::Relaxed);
Err(format!("ASSERT_PRED failed: {}", msg))
}
}
pub fn assertion_pass_rate(&self) -> f64 {
let total = self.total_assertions.load(Ordering::Relaxed);
if total == 0 {
return 100.0;
}
let passed = self.passed_assertions.load(Ordering::Relaxed);
(passed as f64 / total as f64) * 100.0
}
pub fn reset(&self) {
self.total_assertions.store(0, Ordering::Relaxed);
self.passed_assertions.store(0, Ordering::Relaxed);
self.failed_assertions.store(0, Ordering::Relaxed);
}
pub fn summary(&self) -> String {
format!(
"Assertions: {} total, {} passed, {} failed ({:.1}% pass rate)",
self.total_assertions.load(Ordering::Relaxed),
self.passed_assertions.load(Ordering::Relaxed),
self.failed_assertions.load(Ordering::Relaxed),
self.assertion_pass_rate()
)
}
}
impl Default for X86TestAssertions {
fn default() -> Self {
Self::new()
}
}
pub struct X86TestDatabase {
pub tests: HashMap<String, X86DBTestEntry>,
pub history: Vec<X86DBTestHistory>,
pub flaky_tests: HashSet<String>,
pub quarantined_tests: HashSet<String>,
pub source_map: HashMap<String, Vec<String>>,
}
#[derive(Debug, Clone)]
pub struct X86DBTestEntry {
pub name: String,
pub suite: String,
pub category: Option<String>,
pub tags: Vec<String>,
pub source_files: Vec<String>,
pub first_seen: u64,
pub last_run: u64,
pub total_runs: u64,
pub total_passes: u64,
pub total_failures: u64,
pub consecutive_failures: u32,
pub is_flaky: bool,
pub avg_duration_ms: f64,
pub max_duration_ms: u64,
pub min_duration_ms: u64,
}
#[derive(Debug, Clone)]
pub struct X86DBTestHistory {
pub test_name: String,
pub suite_name: String,
pub timestamp: u64,
pub status: X86TestStatus,
pub duration_ms: u64,
pub exit_code: i32,
}
impl X86TestDatabase {
pub fn new() -> Self {
Self {
tests: HashMap::new(),
history: Vec::new(),
flaky_tests: HashSet::new(),
quarantined_tests: HashSet::new(),
source_map: HashMap::new(),
}
}
pub fn register_test(&mut self, name: &str, suite: &str) {
let key = format!("{}::{}", suite, name);
self.tests
.entry(key.clone())
.or_insert_with(|| X86DBTestEntry {
name: name.to_string(),
suite: suite.to_string(),
category: None,
tags: Vec::new(),
source_files: Vec::new(),
first_seen: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64,
last_run: 0,
total_runs: 0,
total_passes: 0,
total_failures: 0,
consecutive_failures: 0,
is_flaky: false,
avg_duration_ms: 0.0,
max_duration_ms: 0,
min_duration_ms: u64::MAX,
});
}
pub fn record_result(&mut self, result: &X86TestResult) {
let key = format!("{}::{}", result.suite, result.name);
if let Some(entry) = self.tests.get_mut(&key) {
entry.last_run = result.timestamp;
entry.total_runs += 1;
match result.status {
X86TestStatus::Passed | X86TestStatus::XFailed => {
entry.total_passes += 1;
entry.consecutive_failures = 0;
}
X86TestStatus::Failed | X86TestStatus::Crashed | X86TestStatus::Timeout => {
entry.total_failures += 1;
entry.consecutive_failures += 1;
}
_ => {}
}
if result.duration_ms < entry.min_duration_ms || entry.total_runs == 1 {
entry.min_duration_ms = result.duration_ms;
}
if result.duration_ms > entry.max_duration_ms {
entry.max_duration_ms = result.duration_ms;
}
let n = entry.total_runs as f64;
entry.avg_duration_ms =
(entry.avg_duration_ms * (n - 1.0) + result.duration_ms as f64) / n;
if entry.consecutive_failures >= X86_FLAKY_THRESHOLD {
}
if entry.total_runs >= 3 && entry.total_passes > 0 && entry.total_failures > 0 {
entry.is_flaky = true;
self.flaky_tests.insert(key.clone());
}
} else {
self.register_test(&result.name, &result.suite);
self.record_result(result);
return;
}
self.history.push(X86DBTestHistory {
test_name: result.name.clone(),
suite_name: result.suite.clone(),
timestamp: result.timestamp,
status: result.status,
duration_ms: result.duration_ms,
exit_code: result.exit_code,
});
}
pub fn map_source_to_test(&mut self, source_file: &str, test_name: &str, suite: &str) {
let key = format!("{}::{}", suite, test_name);
self.source_map
.entry(source_file.to_string())
.or_default()
.push(key);
}
pub fn get_affected_tests(&self, changed_files: &[&str]) -> Vec<String> {
let mut affected = Vec::new();
for file in changed_files {
if let Some(tests) = self.source_map.get(*file) {
affected.extend(tests.iter().cloned());
}
}
affected.sort();
affected.dedup();
affected
}
pub fn get_flaky_tests(&self) -> Vec<&String> {
self.flaky_tests.iter().collect()
}
pub fn quarantine_test(&mut self, test_name: &str, suite: &str) {
let key = format!("{}::{}", suite, test_name);
self.quarantined_tests.insert(key);
}
pub fn unquarantine_test(&mut self, test_name: &str, suite: &str) {
let key = format!("{}::{}", suite, test_name);
self.quarantined_tests.remove(&key);
}
pub fn is_quarantined(&self, test_name: &str, suite: &str) -> bool {
let key = format!("{}::{}", suite, test_name);
self.quarantined_tests.contains(&key)
}
pub fn get_slowest_tests(&self, n: usize) -> Vec<&X86DBTestEntry> {
let mut entries: Vec<&X86DBTestEntry> = self.tests.values().collect();
entries.sort_by(|a, b| {
b.avg_duration_ms
.partial_cmp(&a.avg_duration_ms)
.unwrap_or(std::cmp::Ordering::Equal)
});
entries.truncate(n);
entries
}
pub fn get_most_failing_tests(&self, n: usize) -> Vec<&X86DBTestEntry> {
let mut entries: Vec<&X86DBTestEntry> =
self.tests.values().filter(|e| e.total_runs > 0).collect();
entries.sort_by(|a, b| {
let a_rate = a.total_failures as f64 / a.total_runs as f64;
let b_rate = b.total_failures as f64 / b.total_runs as f64;
b_rate
.partial_cmp(&a_rate)
.unwrap_or(std::cmp::Ordering::Equal)
});
entries.truncate(n);
entries
}
pub fn total_runs(&self) -> usize {
self.history.len()
}
pub fn overall_pass_rate(&self) -> f64 {
if self.history.is_empty() {
return 100.0;
}
let passes = self
.history
.iter()
.filter(|h| matches!(h.status, X86TestStatus::Passed | X86TestStatus::XFailed))
.count();
(passes as f64 / self.history.len() as f64) * 100.0
}
pub fn clear_history(&mut self) {
self.history.clear();
self.flaky_tests.clear();
}
pub fn reset(&mut self) {
self.tests.clear();
self.history.clear();
self.flaky_tests.clear();
self.quarantined_tests.clear();
self.source_map.clear();
}
}
impl Default for X86TestDatabase {
fn default() -> Self {
Self::new()
}
}
pub struct X86CodeCoverage {
pub coverage_type: X86CoverageType,
pub data: Option<X86CoverageData>,
pub sancov_counters: Vec<u64>,
pub gcov_notes: HashMap<String, X86GcovNote>,
pub function_coverage: HashMap<String, X86FunctionCoverage>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86CoverageType {
None,
SanCov,
Gcov,
LlvmCov,
}
impl Default for X86CoverageType {
fn default() -> Self {
X86CoverageType::None
}
}
#[derive(Debug, Clone)]
pub struct X86CoverageData {
pub functions_total: u64,
pub functions_covered: u64,
pub lines_total: u64,
pub lines_covered: u64,
pub branches_total: u64,
pub branches_covered: u64,
pub regions_covered: Vec<X86CoverageRegion>,
pub counters: BTreeMap<u64, u64>,
}
impl X86CoverageData {
pub fn new() -> Self {
Self {
functions_total: 0,
functions_covered: 0,
lines_total: 0,
lines_covered: 0,
branches_total: 0,
branches_covered: 0,
regions_covered: Vec::new(),
counters: BTreeMap::new(),
}
}
pub fn function_coverage_pct(&self) -> f64 {
if self.functions_total == 0 {
return 100.0;
}
(self.functions_covered as f64 / self.functions_total as f64) * 100.0
}
pub fn line_coverage_pct(&self) -> f64 {
if self.lines_total == 0 {
return 100.0;
}
(self.lines_covered as f64 / self.lines_total as f64) * 100.0
}
pub fn branch_coverage_pct(&self) -> f64 {
if self.branches_total == 0 {
return 100.0;
}
(self.branches_covered as f64 / self.branches_total as f64) * 100.0
}
}
impl Default for X86CoverageData {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86CoverageRegion {
pub file: String,
pub start_line: u32,
pub start_col: u32,
pub end_line: u32,
pub end_col: u32,
pub execution_count: u64,
pub covered: bool,
}
#[derive(Debug, Clone)]
pub struct X86GcovNote {
pub source_file: String,
pub function_name: String,
pub line: u32,
pub checksum: u32,
pub arc_count: u32,
}
#[derive(Debug, Clone)]
pub struct X86FunctionCoverage {
pub name: String,
pub file: String,
pub lines_total: u32,
pub lines_covered: u32,
pub execution_count: u64,
}
impl X86CodeCoverage {
pub fn new() -> Self {
Self {
coverage_type: X86CoverageType::None,
data: None,
sancov_counters: vec![0u64; X86_SANCOV_COUNTER_SIZE],
gcov_notes: HashMap::new(),
function_coverage: HashMap::new(),
}
}
pub fn set_type(&mut self, typ: X86CoverageType) {
self.coverage_type = typ;
}
pub fn init_sancov(&mut self, num_counters: usize) {
self.sancov_counters = vec![0u64; num_counters];
self.coverage_type = X86CoverageType::SanCov;
}
pub fn sancov_increment(&mut self, index: usize) {
if index < self.sancov_counters.len() {
self.sancov_counters[index] += 1;
}
}
pub fn collect(&mut self) -> Option<X86CoverageData> {
match self.coverage_type {
X86CoverageType::SanCov => self.collect_sancov(),
X86CoverageType::Gcov => self.collect_gcov(),
X86CoverageType::LlvmCov => self.collect_llvm_cov(),
X86CoverageType::None => None,
}
}
fn collect_sancov(&mut self) -> Option<X86CoverageData> {
let total = self.sancov_counters.len() as u64;
let covered = self.sancov_counters.iter().filter(|&&c| c > 0).count() as u64;
let mut counters = BTreeMap::new();
for (i, &count) in self.sancov_counters.iter().enumerate() {
if count > 0 {
counters.insert(i as u64, count);
}
}
let regions: Vec<X86CoverageRegion> = self
.sancov_counters
.iter()
.enumerate()
.filter(|&(_, &c)| c > 0)
.map(|(i, &c)| X86CoverageRegion {
file: "test.c".to_string(),
start_line: (i + 1) as u32,
start_col: 1,
end_line: (i + 1) as u32,
end_col: 80,
execution_count: c,
covered: c > 0,
})
.collect();
let data = X86CoverageData {
functions_total: if total > 0 { 1 } else { 0 },
functions_covered: if covered > 0 { 1 } else { 0 },
lines_total: total,
lines_covered: covered,
branches_total: total * 2,
branches_covered: covered,
regions_covered: regions,
counters,
};
self.data = Some(data.clone());
Some(data)
}
fn collect_gcov(&mut self) -> Option<X86CoverageData> {
let total_lines = self.gcov_notes.len() as u64;
let covered_lines = self.gcov_notes.values().filter(|n| n.arc_count > 0).count() as u64;
let regions: Vec<X86CoverageRegion> = self
.gcov_notes
.iter()
.map(|(func, note)| X86CoverageRegion {
file: note.source_file.clone(),
start_line: note.line,
start_col: 1,
end_line: note.line,
end_col: 80,
execution_count: note.arc_count as u64,
covered: note.arc_count > 0,
})
.collect();
let data = X86CoverageData {
functions_total: self.function_coverage.len() as u64,
functions_covered: covered_lines,
lines_total: total_lines,
lines_covered: covered_lines,
branches_total: total_lines * 2,
branches_covered: covered_lines,
regions_covered: regions,
counters: BTreeMap::new(),
};
self.data = Some(data.clone());
Some(data)
}
fn collect_llvm_cov(&mut self) -> Option<X86CoverageData> {
let total_funcs = self.function_coverage.len() as u64;
let covered_funcs = self
.function_coverage
.values()
.filter(|f| f.execution_count > 0)
.count() as u64;
let total_lines: u64 = self
.function_coverage
.values()
.map(|f| f.lines_total as u64)
.sum();
let covered_lines: u64 = self
.function_coverage
.values()
.map(|f| f.lines_covered as u64)
.sum();
let regions: Vec<X86CoverageRegion> = self
.function_coverage
.iter()
.map(|(name, func)| X86CoverageRegion {
file: func.file.clone(),
start_line: 1,
start_col: 1,
end_line: func.lines_total,
end_col: 80,
execution_count: func.execution_count,
covered: func.execution_count > 0,
})
.collect();
let data = X86CoverageData {
functions_total: total_funcs,
functions_covered: covered_funcs,
lines_total: total_lines,
lines_covered: covered_lines,
branches_total: total_lines * 2,
branches_covered: covered_lines,
regions_covered: regions,
counters: BTreeMap::new(),
};
self.data = Some(data.clone());
Some(data)
}
pub fn record_function(
&mut self,
name: &str,
file: &str,
lines_total: u32,
execution_count: u64,
) {
let covered_lines = if execution_count > 0 { lines_total } else { 0 };
self.function_coverage.insert(
name.to_string(),
X86FunctionCoverage {
name: name.to_string(),
file: file.to_string(),
lines_total,
lines_covered: covered_lines,
execution_count,
},
);
}
pub fn generate_report(&self, config: &X86TestHarnessConfig) -> Option<String> {
let data = self.data.as_ref()?;
let mut report = String::new();
report.push_str("=== X86 Clang Code Coverage Report ===\n\n");
report.push_str(&format!("Coverage Type: {:?}\n", self.coverage_type));
report.push_str(&format!(
"Functions: {}/{} ({:.1}%)\n",
data.functions_covered,
data.functions_total,
data.function_coverage_pct()
));
report.push_str(&format!(
"Lines: {}/{} ({:.1}%)\n",
data.lines_covered,
data.lines_total,
data.line_coverage_pct()
));
report.push_str(&format!(
"Branches: {}/{} ({:.1}%)\n",
data.branches_covered,
data.branches_total,
data.branch_coverage_pct()
));
report.push_str("\n--- Covered Regions ---\n");
for region in &data.regions_covered {
report.push_str(&format!(
" {}:{}:{}-{}:{} (executed {} times)\n",
region.file,
region.start_line,
region.start_col,
region.end_line,
region.end_col,
region.execution_count
));
}
if let Some(ref report_dir) = config.report_dir {
let _ = std::fs::create_dir_all(report_dir);
let report_path = report_dir.join("coverage_report.txt");
if let Err(e) = std::fs::write(&report_path, &report) {
eprintln!("Warning: Failed to write coverage report: {}", e);
}
}
Some(report)
}
pub fn generate_lcov(&self) -> String {
let mut lcov = String::new();
if let Some(ref data) = self.data {
lcov.push_str("TN:X86 Clang Tests\n");
for region in &data.regions_covered {
lcov.push_str(&format!("SF:{}\n", region.file));
lcov.push_str(&format!(
"DA:{},{}\n",
region.start_line, region.execution_count
));
lcov.push_str(&format!(
"LF:{}\nLH:{}\n",
data.lines_total, data.lines_covered
));
lcov.push_str("end_of_record\n");
}
}
lcov
}
pub fn generate_html_report(&self, output_path: &Path) -> io::Result<()> {
let data = match &self.data {
Some(d) => d,
None => return Ok(()),
};
let mut html = String::new();
html.push_str("<!DOCTYPE html>\n<html>\n<head>\n");
html.push_str("<meta charset=\"UTF-8\">\n");
html.push_str("<title>X86 Clang Coverage Report</title>\n");
html.push_str("<style>\n");
html.push_str("body { font-family: monospace; margin: 20px; }\n");
html.push_str(".covered { background: #d4edda; }\n");
html.push_str(".uncovered { background: #f8d7da; }\n");
html.push_str("table { border-collapse: collapse; width: 100%; }\n");
html.push_str("td { padding: 2px 8px; border: 1px solid #ddd; }\n");
html.push_str(".summary { margin-bottom: 20px; }\n");
html.push_str("</style>\n</head>\n<body>\n");
html.push_str("<h1>Code Coverage Report</h1>\n");
html.push_str("<div class=\"summary\">\n");
html.push_str(&format!(
"<p>Function Coverage: {:.1}%</p>\n",
data.function_coverage_pct()
));
html.push_str(&format!(
"<p>Line Coverage: {:.1}%</p>\n",
data.line_coverage_pct()
));
html.push_str(&format!(
"<p>Branch Coverage: {:.1}%</p>\n",
data.branch_coverage_pct()
));
html.push_str("</div>\n");
html.push_str(
"<table>\n<tr><th>File</th><th>Line</th><th>Count</th><th>Status</th></tr>\n",
);
for region in &data.regions_covered {
let class = if region.covered {
"covered"
} else {
"uncovered"
};
let status = if region.covered { "covered" } else { "missed" };
html.push_str(&format!(
"<tr class=\"{}\"><td>{}</td><td>{}</td><td>{}</td><td>{}</td></tr>\n",
class, region.file, region.start_line, region.execution_count, status
));
}
html.push_str("</table>\n");
html.push_str("</body>\n</html>\n");
std::fs::write(output_path, html)
}
pub fn reset(&mut self) {
self.sancov_counters = vec![0u64; X86_SANCOV_COUNTER_SIZE];
self.gcov_notes.clear();
self.function_coverage.clear();
self.data = None;
}
}
impl Default for X86CodeCoverage {
fn default() -> Self {
Self::new()
}
}
pub fn x86_sanity_test_suite() -> X86TestSuite {
let mut suite = X86TestSuite::new("X86HarnessSanity");
suite.description = "Sanity tests for the X86 Test Harness".to_string();
suite.add_tag("sanity");
suite.add_test(
X86TestCase::new("test_harness_creation")
.with_source("int main() { return 0; }")
.with_tag("sanity"),
);
suite.add_test(
X86TestCase::new("test_basic_compilation")
.with_source("int main() { return 42; }")
.with_return(42)
.with_tag("sanity"),
);
suite.add_test(
X86TestCase::new("test_syntax_only")
.with_source("int main() { return 0; }")
.with_compile_mode(X86CompileMode::SyntaxOnly)
.with_tag("sanity"),
);
suite.add_test(
X86TestCase::new("test_assembly_generation")
.with_source("int main() { return 0; }")
.with_compile_mode(X86CompileMode::CompileToAssembly)
.with_tag("sanity"),
);
suite.add_test(
X86TestCase::new("test_bitcode_generation")
.with_source("int main() { return 0; }")
.with_compile_mode(X86CompileMode::CompileToBitcode)
.with_tag("sanity"),
);
suite.add_test(
X86TestCase::new("test_filecheck_basic")
.with_source("int main() { return 0; }")
.with_compile_mode(X86CompileMode::FileCheck)
.with_filecheck("CHECK: main")
.with_filecheck("CHECK: ret")
.with_tag("sanity"),
);
suite.add_test(
X86TestCase::new("test_xfail")
.with_source("int main() { syntax_error_here }")
.with_xfail("Known syntax error")
.with_tag("sanity"),
);
suite.add_test(
X86TestCase::new("test_disabled")
.with_source("int main() { return 0; }")
.with_disable("This test is disabled for testing")
.with_tag("sanity"),
);
suite.add_test(
X86TestCase::new("test_with_dependency")
.with_source("int main() { return 0; }")
.with_dependency("test_basic_compilation")
.with_tag("sanity"),
);
suite
}
pub fn x86_regression_test_suite() -> X86TestSuite {
let mut suite = X86TestSuite::new("X86HarnessRegression");
suite.description = "Regression tests for the X86 Test Harness".to_string();
suite.add_tag("regression");
suite.add_test(
X86TestCase::new("test_runner_discovery")
.with_tag("regression")
.with_category("runner"),
);
suite.add_test(
X86TestCase::new("test_runner_filtering")
.with_tag("regression")
.with_category("runner"),
);
suite.add_test(
X86TestCase::new("test_parallel_execution")
.with_tag("regression")
.with_category("runner"),
);
suite.add_test(
X86TestCase::new("test_timeout_management")
.with_tag("regression")
.with_category("runner"),
);
suite.add_test(
X86TestCase::new("test_junit_reporting")
.with_tag("regression")
.with_category("reporting"),
);
suite.add_test(
X86TestCase::new("test_json_reporting")
.with_tag("regression")
.with_category("reporting"),
);
suite.add_test(
X86TestCase::new("test_database_recording")
.with_tag("regression")
.with_category("database"),
);
suite.add_test(
X86TestCase::new("test_flaky_detection")
.with_tag("regression")
.with_category("database"),
);
suite.add_test(
X86TestCase::new("test_coverage_collection")
.with_tag("regression")
.with_category("coverage"),
);
suite.add_test(
X86TestCase::new("test_coverage_reporting")
.with_tag("regression")
.with_category("coverage"),
);
suite.add_test(
X86TestCase::new("test_mutation_generation")
.with_tag("regression")
.with_category("mutation"),
);
suite.add_test(
X86TestCase::new("test_fuzz_generation")
.with_tag("regression")
.with_category("fuzzing"),
);
suite.add_test(
X86TestCase::new("test_assertions_basic")
.with_source("int main() { return 0; }")
.with_tag("regression")
.with_category("assertions"),
);
suite.add_test(
X86TestCase::new("test_assertion_equality")
.with_tag("regression")
.with_category("assertions"),
);
suite.add_test(
X86TestCase::new("test_assertion_floating")
.with_tag("regression")
.with_category("assertions"),
);
suite.add_test(
X86TestCase::new("test_assertion_strings")
.with_tag("regression")
.with_category("assertions"),
);
suite.add_test(
X86TestCase::new("test_fixture_setup_teardown")
.with_tag("regression")
.with_category("fixtures"),
);
suite.add_test(
X86TestCase::new("test_fixture_temp_files")
.with_tag("regression")
.with_category("fixtures"),
);
suite.add_test(
X86TestCase::new("test_gtest_source_generation")
.with_tag("regression")
.with_category("frameworks"),
);
suite.add_test(
X86TestCase::new("test_catch2_source_generation")
.with_tag("regression")
.with_category("frameworks"),
);
suite.add_test(
X86TestCase::new("test_doctest_source_generation")
.with_tag("regression")
.with_category("frameworks"),
);
suite.add_test(
X86TestCase::new("test_cpputest_source_generation")
.with_tag("regression")
.with_category("frameworks"),
);
suite.add_test(
X86TestCase::new("test_boost_source_generation")
.with_tag("regression")
.with_category("frameworks"),
);
suite
}
pub fn x86_c_feature_test_suite() -> X86TestSuite {
let mut suite = X86TestSuite::new("X86CFEatures");
suite.description = "C language feature tests".to_string();
suite.add_tag("c_features");
let tests = vec![
("test_int_types", "int main() { int a = 1; return a; }", 1),
("test_long_types", "int main() { long a = 1L; return (int)a; }", 1),
("test_short_types", "int main() { short a = 1; return (int)a; }", 1),
("test_char_types", "int main() { char a = 'A'; return (int)a; }", 65),
("test_unsigned_int", "int main() { unsigned int a = 42; return (int)a; }", 42),
("test_float_literal", "int main() { float f = 3.14f; return (int)f; }", 3),
("test_double_literal", "int main() { double d = 2.718; return (int)d; }", 2),
("test_array_access", "int main() { int arr[] = {1,2,3}; return arr[1]; }", 2),
("test_pointer_deref", "int main() { int x = 42; int *p = &x; return *p; }", 42),
("test_struct_field", "int main() { struct S { int x; } s = {10}; return s.x; }", 10),
("test_for_loop", "int main() { int s = 0; for(int i=0;i<10;i++) s+=i; return s; }", 45),
("test_while_loop", "int main() { int i=0,s=0; while(i<10){s+=i;i++;} return s; }", 45),
("test_do_while", "int main() { int i=0; do { i++; } while(i<10); return i; }", 10),
("test_if_else", "int main() { int x = 5; if(x>3) return 1; else return 0; }", 1),
("test_ternary", "int main() { int x=5; return x>3 ? 1 : 0; }", 1),
("test_switch", "int main() { int x=2; switch(x){case 1:return 10;case 2:return 20;default:return 0;} }", 20),
("test_enum", "int main() { enum E {A=1,B=2,C=3}; enum E e = B; return e; }", 2),
("test_typedef", "int main() { typedef int myint; myint x = 42; return x; }", 42),
("test_sizeof", "int main() { return sizeof(int); }", 0), ("test_static_var", "int main() { static int x = 100; return x; }", 100),
];
for (name, source, expected) in tests {
suite.add_test(
X86TestCase::new(name)
.with_source(source)
.with_return(expected)
.with_tag("c_features")
.with_category("c_language"),
);
}
suite
}
pub fn x86_cpp_feature_test_suite() -> X86TestSuite {
let mut suite = X86TestSuite::new("X86CPPFeatures");
suite.description = "C++ language feature tests".to_string();
suite.add_tag("cpp_features");
let tests = vec![
("test_cpp_class", "class C { public: int get() { return 42; } }; int main() { C c; return c.get(); }", 42),
("test_cpp_inheritance", "class B { public: int base() { return 10; } }; class D : public B { public: int derived() { return 20; } }; int main() { D d; return d.base(); }", 10),
("test_cpp_virtual", "class B { public: virtual int v() { return 10; } }; class D : public B { public: int v() override { return 20; } }; int main() { B* b = new D(); return b->v(); }", 20),
("test_cpp_template_fn", "template<typename T> T max(T a, T b) { return a > b ? a : b; } int main() { return max(5, 10); }", 10),
("test_cpp_namespace", "namespace ns { int val = 42; } int main() { return ns::val; }", 42),
("test_cpp_auto", "int main() { auto x = 42; return x; }", 42),
("test_cpp_lambda", "int main() { auto f = []() { return 42; }; return f(); }", 42),
("test_cpp_nullptr", "int main() { int* p = nullptr; return p == nullptr ? 1 : 0; }", 1),
("test_cpp_constexpr", "constexpr int sq(int x) { return x*x; } int main() { return sq(5); }", 25),
("test_cpp_override", "class B { public: virtual int f() { return 1; } }; class D : public B { public: int f() override { return 2; } }; int main() { D d; return d.f(); }", 2),
];
for (name, source, expected) in tests {
suite.add_test(
X86TestCase::new(name)
.with_source(source)
.with_return(expected)
.with_tag("cpp_features")
.with_category("cpp_language"),
);
}
suite
}
pub fn x86_stress_test_suite() -> X86TestSuite {
let mut suite = X86TestSuite::new("X86HarnessStress");
suite.description = "Stress tests for the X86 Test Harness".to_string();
suite.add_tag("stress");
let mut source = String::from("int main() { return 0; }\n");
for i in 0..100 {
source.push_str(&format!("int func_{}() {{ return {}; }}\n", i, i));
}
suite.add_test(
X86TestCase::new("test_many_functions")
.with_source(&source)
.with_tag("stress"),
);
let mut deep = String::from("int main() { int x = 0");
for _ in 0..100 {
deep.push_str(" + 1");
}
deep.push_str("; return x; }");
suite.add_test(
X86TestCase::new("test_deep_expression")
.with_source(&deep)
.with_tag("stress"),
);
let mut switch = String::from("int main() { int x = 50;\nswitch(x) {\n");
for i in 0..100 {
switch.push_str(&format!("case {}: return {};\n", i, i));
}
switch.push_str("default: return -1;\n}\n}");
suite.add_test(
X86TestCase::new("test_large_switch")
.with_source(&switch)
.with_return(50)
.with_tag("stress"),
);
let mut lines = String::from("int main() {\n");
for i in 0..1000 {
lines.push_str(&format!("int x{} = {};\n", i, i));
}
lines.push_str("return 0;\n}");
suite.add_test(
X86TestCase::new("test_many_lines")
.with_source(&lines)
.with_tag("stress"),
);
suite
}
pub fn x86_target_test_suite() -> X86TestSuite {
let mut suite = X86TestSuite::new("X86TargetSpecific");
suite.description = "X86 target-specific tests".to_string();
suite.add_tag("x86_target");
suite.add_test(
X86TestCase::new("test_sse_intrinsic")
.with_source(
r#"#include <xmmintrin.h>
int main() { __m128 a = _mm_set1_ps(1.0f); __m128 b = _mm_add_ps(a, a); return 0; }"#,
)
.with_tag("x86_target")
.with_tag("sse"),
);
suite.add_test(
X86TestCase::new("test_avx_intrinsic")
.with_source(
r#"#include <immintrin.h>
int main() { __m256 a = _mm256_set1_ps(1.0f); __m256 b = _mm256_add_ps(a, a); return 0; }"#,
)
.with_tag("x86_target")
.with_tag("avx"),
);
suite.add_test(
X86TestCase::new("test_inline_asm_basic")
.with_source(
r#"int main() { int x = 0; __asm__("movl $42, %0" : "=r"(x)); return x; }"#,
)
.with_return(42)
.with_tag("x86_target")
.with_tag("asm"),
);
suite.add_test(
X86TestCase::new("test_cpuid")
.with_source(
r#"#include <cpuid.h>
int main() { unsigned int eax, ebx, ecx, edx; __get_cpuid(0, &eax, &ebx, &ecx, &edx); return 0; }"#,
)
.with_tag("x86_target")
.with_tag("cpuid"),
);
suite.add_test(
X86TestCase::new("test_x86_64_addressing")
.with_source("int main() { int arr[10]; int *p = &arr[5]; return (int)(p - arr); }")
.with_return(5)
.with_tag("x86_target")
.with_tag("x86_64"),
);
suite
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_harness_creation() {
let harness = X86TestHarness::new();
assert_eq!(harness.total_tests(), 0);
}
#[test]
fn test_harness_with_config() {
let mut config = X86TestHarnessConfig::default();
config.parallel_workers = 2;
let harness = X86TestHarness::with_config(config);
assert_eq!(harness.config.parallel_workers, 2);
}
#[test]
fn test_harness_register_suite() {
let mut harness = X86TestHarness::new();
let suite = X86TestSuite::new("test_suite");
harness.register_suite(suite);
assert_eq!(harness.suites.len(), 1);
}
#[test]
fn test_harness_register_test() {
let mut harness = X86TestHarness::new();
harness.register_test("suite1", X86TestCase::new("test1"));
harness.register_test("suite1", X86TestCase::new("test2"));
harness.register_test("suite2", X86TestCase::new("test3"));
assert_eq!(harness.total_tests(), 3);
assert_eq!(harness.suites.len(), 2);
}
#[test]
fn test_harness_run_all() {
let mut harness = X86TestHarness::new();
harness.register_test(
"suite",
X86TestCase::new("passing_test").with_source("int main() { return 0; }"),
);
let result = harness.run_all();
assert!(result.all_passed());
assert_eq!(result.summary.total, 1);
}
#[test]
fn test_harness_run_with_disabled() {
let mut harness = X86TestHarness::new();
harness.register_test(
"suite",
X86TestCase::new("enabled").with_source("int main() { return 0; }"),
);
harness.register_test(
"suite",
X86TestCase::new("disabled")
.with_source("int main() { return 0; }")
.with_disable("Testing"),
);
let result = harness.run_all();
assert!(result.all_passed());
assert_eq!(result.summary.total, 2);
assert_eq!(result.summary.disabled, 1);
}
#[test]
fn test_harness_run_with_xfail() {
let mut harness = X86TestHarness::new();
harness.register_test(
"suite",
X86TestCase::new("xfail_test")
.with_source("int main() { error; }")
.with_xfail("Known bug"),
);
let result = harness.run_all();
assert!(result.all_passed());
assert_eq!(result.summary.xfailed, 1);
}
#[test]
fn test_harness_run_tagged() {
let mut harness = X86TestHarness::new();
harness.register_test(
"suite",
X86TestCase::new("tagged1")
.with_source("int main() { return 0; }")
.with_tag("group_a"),
);
harness.register_test(
"suite",
X86TestCase::new("tagged2")
.with_source("int main() { return 0; }")
.with_tag("group_b"),
);
let result = harness.run_tagged("group_a");
assert_eq!(result.summary.total, 1);
}
#[test]
fn test_harness_reset() {
let mut harness = X86TestHarness::new();
harness.register_test(
"suite",
X86TestCase::new("test").with_source("int main() { return 0; }"),
);
harness.run_all();
harness.reset();
assert!(harness.run_start.is_none());
}
#[test]
fn test_harness_matches_filter_tags() {
let harness = X86TestHarness::new();
let test = X86TestCase::new("test").with_tag("integration");
assert!(harness.matches_filter(&test));
let mut config = X86TestHarnessConfig::default();
config.filter_tags = vec!["unit".to_string()];
let harness = X86TestHarness::with_config(config);
assert!(!harness.matches_filter(&test));
}
#[test]
fn test_suite_creation() {
let suite = X86TestSuite::new("my_suite");
assert_eq!(suite.name, "my_suite");
assert!(suite.is_empty());
}
#[test]
fn test_suite_add_test() {
let mut suite = X86TestSuite::new("suite");
suite.add_test(X86TestCase::new("test1"));
suite.add_test(X86TestCase::new("test2"));
assert_eq!(suite.test_count(), 2);
}
#[test]
fn test_suite_tags() {
let mut suite = X86TestSuite::new("suite");
suite.add_tag("slow");
suite.add_tag("x86");
assert!(suite.tags.contains(&"slow".to_string()));
assert!(suite.tags.contains(&"x86".to_string()));
}
#[test]
fn test_test_case_creation() {
let tc = X86TestCase::new("my_test");
assert_eq!(tc.name, "my_test");
assert_eq!(tc.expected_return, 0);
assert!(!tc.disabled);
assert!(!tc.is_xfail);
}
#[test]
fn test_test_case_with_builder() {
let tc = X86TestCase::new("builder_test")
.with_source("int main() { return 42; }")
.with_return(42)
.with_tag("fast")
.with_tag("x86")
.with_category("arithmetic");
assert_eq!(tc.expected_return, 42);
assert_eq!(tc.tags.len(), 2);
assert!(tc.category == Some("arithmetic".to_string()));
}
#[test]
fn test_test_case_xfail() {
let tc = X86TestCase::new("should_fail").with_xfail("Known issue #123");
assert!(tc.is_xfail);
assert_eq!(tc.xfail_reason, Some("Known issue #123".to_string()));
}
#[test]
fn test_test_case_disabled() {
let tc = X86TestCase::new("disabled").with_disable("Flaky due to timing");
assert!(tc.disabled);
assert_eq!(tc.disable_reason, Some("Flaky due to timing".to_string()));
}
#[test]
fn test_test_case_supported_on() {
let tc = X86TestCase::new("test").with_source("int main() { return 0; }");
assert!(tc.is_supported_on("x86_64"));
assert!(tc.is_supported_on("i386"));
}
#[test]
fn test_test_case_unsupported_on() {
let mut tc = X86TestCase::new("test").with_source("int main() { return 0; }");
tc.unsupported_on.push("avr".to_string());
assert!(!tc.is_supported_on("avr"));
}
#[test]
fn test_runner_creation() {
let runner = X86TestRunner::new();
assert_eq!(runner.parallelism, X86_DEFAULT_THREAD_POOL_SIZE);
assert_eq!(runner.timeout_ms, X86_DEFAULT_TEST_TIMEOUT_MS);
}
#[test]
fn test_runner_with_config() {
let mut runner = X86TestRunner::new();
runner.set_timeout(60_000);
runner.set_parallelism(8);
assert_eq!(runner.timeout_ms, 60_000);
assert_eq!(runner.parallelism, 8);
}
#[test]
fn test_runner_discover_in_source_gtest() {
let runner = X86TestRunner::new();
let source = r#"
TEST(MySuite, TestOne) { }
TEST_F(Fixture, TestTwo) { }
TEST_P(ParamFixture, TestThree) { }
"#;
let tests = runner.discover_in_source(source, "file1");
assert_eq!(tests.len(), 3);
}
#[test]
fn test_runner_discover_in_source_catch2() {
let runner = X86TestRunner::new();
let source = r#"
TEST_CASE("my test case") { }
SCENARIO("my scenario") { }
"#;
let tests = runner.discover_in_source(source, "file1");
assert_eq!(tests.len(), 2);
}
#[test]
fn test_runner_discover_in_source_boost() {
let runner = X86TestRunner::new();
let source = r#"
BOOST_AUTO_TEST_CASE(my_boost_test) { }
BOOST_AUTO_TEST_CASE(another_test) { }
"#;
let tests = runner.discover_in_source(source, "file1");
assert_eq!(tests.len(), 2);
}
#[test]
fn test_runner_discover_in_source_cunit() {
let runner = X86TestRunner::new();
let source = r#"
CU_add_test(pSuite, "my_cunit_test", test_func);
"#;
let tests = runner.discover_in_source(source, "file1");
assert_eq!(tests.len(), 1);
}
#[test]
fn test_runner_discover_in_source_check() {
let runner = X86TestRunner::new();
let source = "START_TEST(my_check_test)\n{\n}\nEND_TEST";
let tests = runner.discover_in_source(source, "file1");
assert_eq!(tests.len(), 1);
}
#[test]
fn test_runner_run_single_pass() {
let runner = X86TestRunner::new();
let test = X86TestCase::new("passing").with_source("int main() { return 0; }");
let config = X86TestHarnessConfig::default();
let result = runner.run_single(None, &test, &config);
assert_eq!(result.status, X86TestStatus::Passed);
}
#[test]
fn test_runner_run_single_xfail() {
let runner = X86TestRunner::new();
let test = X86TestCase::new("xfail")
.with_source("int main() { syntax_error; }")
.with_xfail("Known");
let config = X86TestHarnessConfig::default();
let result = runner.run_single(None, &test, &config);
assert_eq!(result.status, X86TestStatus::XFailed);
}
#[test]
fn test_runner_run_single_syntax_only() {
let runner = X86TestRunner::new();
let test = X86TestCase::new("syntax")
.with_source("int main() { return 0; }")
.with_compile_mode(X86CompileMode::SyntaxOnly);
let config = X86TestHarnessConfig::default();
let result = runner.run_single(None, &test, &config);
assert_eq!(result.status, X86TestStatus::Passed);
}
#[test]
fn test_runner_filecheck_pass() {
let runner = X86TestRunner::new();
let test = X86TestCase::new("fc")
.with_source("int main() { return 0; }")
.with_compile_mode(X86CompileMode::FileCheck)
.with_filecheck("CHECK: main")
.with_filecheck("CHECK-NOT: foobar");
let config = X86TestHarnessConfig::default();
let result = runner.run_single(None, &test, &config);
assert_eq!(result.status, X86TestStatus::Passed);
}
#[test]
fn test_runner_filecheck_fail() {
let runner = X86TestRunner::new();
let test = X86TestCase::new("fc_fail")
.with_source("int main() { return 0; }")
.with_compile_mode(X86CompileMode::FileCheck)
.with_filecheck("CHECK: nonexistent_pattern_xyz");
let config = X86TestHarnessConfig::default();
let result = runner.run_single(None, &test, &config);
assert_eq!(result.status, X86TestStatus::Failed);
}
#[test]
fn test_runner_extract_return_value() {
let runner = X86TestRunner::new();
assert_eq!(
runner.extract_return_value("int main() { return 42; }"),
Some(42)
);
assert_eq!(
runner.extract_return_value("int main() { return 0; }"),
Some(0)
);
assert_eq!(
runner.extract_return_value("int main() { return -1; }"),
Some(-1)
);
assert_eq!(
runner.extract_return_value("int main() { int x = 5; }"),
None
);
}
#[test]
fn test_runner_extract_return_expr() {
let runner = X86TestRunner::new();
assert_eq!(
runner.extract_return_value("int main() { return 1 + 2; }"),
Some(3)
);
}
#[test]
fn test_runner_extract_printf() {
let runner = X86TestRunner::new();
let source = r#"int main() { printf("Hello"); return 0; }"#;
assert_eq!(runner.extract_printf_output(source), "Hello");
}
#[test]
fn test_runner_check_basic_syntax() {
let runner = X86TestRunner::new();
let errors = runner.check_basic_syntax("int main() { return 0; }");
assert!(errors.is_empty());
}
#[test]
fn test_runner_check_unbalanced_braces() {
let runner = X86TestRunner::new();
let errors = runner.check_basic_syntax("int main() { return 0;");
assert!(!errors.is_empty());
}
#[test]
fn test_runner_check_unbalanced_parens() {
let runner = X86TestRunner::new();
let errors = runner.check_basic_syntax("int main( { return 0; }");
assert!(!errors.is_empty());
}
#[test]
fn test_runner_feature_available() {
let runner = X86TestRunner::new();
assert!(runner.feature_available("gtest"));
assert!(runner.feature_available("catch2"));
assert!(runner.feature_available("subprocess"));
}
#[test]
fn test_runner_shuffle_tests() {
let runner = X86TestRunner::new();
let mut tests = vec![
("s1".to_string(), X86TestCase::new("a")),
("s1".to_string(), X86TestCase::new("b")),
("s1".to_string(), X86TestCase::new("c")),
];
runner.shuffle_tests(&mut tests, 42);
assert_eq!(tests.len(), 3);
}
#[test]
fn test_thread_pool_creation() {
let pool = X86ThreadPool::new(2);
assert_eq!(pool.size, 2);
pool.shutdown();
}
#[test]
fn test_thread_pool_spawn() {
let pool = X86ThreadPool::new(2);
let handle = pool.spawn(|| {
let _sum: i32 = (0..100).sum();
});
pool.join(handle);
pool.shutdown();
}
#[test]
fn test_thread_pool_multiple_tasks() {
let pool = X86ThreadPool::new(4);
let results = Arc::new(Mutex::new(Vec::new()));
let handles: Vec<_> = (0..10)
.map(|i| {
let r = Arc::clone(&results);
pool.spawn(move || {
r.lock().unwrap().push(i);
})
})
.collect();
for h in handles {
pool.join(h);
}
let final_results = results.lock().unwrap();
assert_eq!(final_results.len(), 10);
pool.shutdown();
}
#[test]
fn test_framework_creation() {
let fw = X86TestFramework::new(X86FrameworkType::GTest);
assert_eq!(fw.framework, X86FrameworkType::GTest);
}
#[test]
fn test_framework_detect_gtest() {
let source = r#"#include <gtest/gtest.h>
TEST(Suite, Test1) { EXPECT_EQ(1, 1); }"#;
let detected = X86TestFramework::detect_framework(source);
assert_eq!(detected, Some(X86FrameworkType::GTest));
}
#[test]
fn test_framework_detect_catch2() {
let source = r#"#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
TEST_CASE("test") { REQUIRE(true); }"#;
let detected = X86TestFramework::detect_framework(source);
assert_eq!(detected, Some(X86FrameworkType::Catch2));
}
#[test]
fn test_framework_detect_doctest() {
let source = r#"#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest/doctest.h>
TEST_CASE("test") { }"#;
let detected = X86TestFramework::detect_framework(source);
assert_eq!(detected, Some(X86FrameworkType::DocTest));
}
#[test]
fn test_framework_detect_boost() {
let source = r#"#define BOOST_TEST_MODULE test
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_CASE(my_test) { }"#;
let detected = X86TestFramework::detect_framework(source);
assert_eq!(detected, Some(X86FrameworkType::BoostTest));
}
#[test]
fn test_framework_detect_cpputest() {
let source = r#"#include <CppUTest/TestHarness.h>
TEST(Group, Test1) { }"#;
let detected = X86TestFramework::detect_framework(source);
assert_eq!(detected, Some(X86FrameworkType::CppUTest));
}
#[test]
fn test_framework_detect_cunit() {
let source = r#"#include <CUnit/CUnit.h>
CU_add_test(pSuite, "test", func);"#;
let detected = X86TestFramework::detect_framework(source);
assert_eq!(detected, Some(X86FrameworkType::CUnit));
}
#[test]
fn test_framework_detect_check() {
let source = r#"#include <check.h>
START_TEST(my_test) { }
END_TEST"#;
let detected = X86TestFramework::detect_framework(source);
assert_eq!(detected, Some(X86FrameworkType::Check));
}
#[test]
fn test_framework_detect_unity() {
let source = r#"#include "unity.h"
void setUp(void) { }
void tearDown(void) { }"#;
let detected = X86TestFramework::detect_framework(source);
assert_eq!(detected, Some(X86FrameworkType::Unity));
}
#[test]
fn test_framework_detect_ctest() {
let source = r#"enable_testing()
add_test(NAME mytest COMMAND mytest)
set_tests_properties(mytest PROPERTIES LABELS "x86")"#;
let detected = X86TestFramework::detect_framework(source);
assert_eq!(detected, Some(X86FrameworkType::CTest));
}
#[test]
fn test_framework_generate_gtest() {
let source =
X86TestFramework::generate_gtest_source("MyTest", "MySuite", "EXPECT_EQ(1, 1);");
assert!(source.contains("TEST(MySuite, MyTest)"));
assert!(source.contains("InitGoogleTest"));
assert!(source.contains("RUN_ALL_TESTS"));
}
#[test]
fn test_framework_generate_gtest_fixture() {
let source = X86TestFramework::generate_gtest_fixture_source(
"MyFixture",
"MyTest",
"EXPECT_EQ(x, 42);",
"x = 42;",
"x = 0;",
);
assert!(source.contains("class MyFixture"));
assert!(source.contains("TEST_F(MyFixture, MyTest)"));
assert!(source.contains("SetUp()"));
assert!(source.contains("TearDown()"));
}
#[test]
fn test_framework_generate_gtest_param() {
let source = X86TestFramework::generate_gtest_param_source(
"ParamFixture",
"ParamTest",
&["1", "2", "3"],
"auto v = GetParam();",
);
assert!(source.contains("TEST_P(ParamFixture, ParamTest)"));
assert!(source.contains("Values(1, 2, 3)"));
}
#[test]
fn test_framework_generate_catch2() {
let source = X86TestFramework::generate_catch2_source("my test", "REQUIRE(1 == 1);");
assert!(source.contains("CATCH_CONFIG_MAIN"));
assert!(source.contains("TEST_CASE(\"my test\")"));
}
#[test]
fn test_framework_generate_catch2_sections() {
let sections = vec![("s1", "CHECK(true);"), ("s2", "CHECK(false);")];
let source = X86TestFramework::generate_catch2_section_source("test", §ions);
assert!(source.contains("SECTION(\"s1\")"));
assert!(source.contains("SECTION(\"s2\")"));
}
#[test]
fn test_framework_generate_doctest() {
let source = X86TestFramework::generate_doctest_source("doctest", "CHECK(1 == 1);");
assert!(source.contains("DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN"));
assert!(source.contains("TEST_CASE(\"doctest\")"));
}
#[test]
fn test_framework_generate_cpputest() {
let source = X86TestFramework::generate_cpputest_source("Group", "Test", "CHECK(true);");
assert!(source.contains("TEST_GROUP(Group)"));
assert!(source.contains("TEST(Group, Test)"));
}
#[test]
fn test_framework_generate_boost() {
let source =
X86TestFramework::generate_boost_test_source("boost_test", "BOOST_CHECK(true);");
assert!(source.contains("BOOST_TEST_MODULE boost_test"));
assert!(source.contains("BOOST_AUTO_TEST_CASE(boost_test)"));
}
#[test]
fn test_framework_generate_cunit() {
let tests = vec![("t1", "CU_ASSERT(1);"), ("t2", "CU_ASSERT(2);")];
let source = X86TestFramework::generate_cunit_source("CSuite", &tests);
assert!(source.contains("CU_initialize_registry"));
assert!(source.contains("CU_add_suite(\"CSuite\""));
assert!(source.contains("CU_add_test"));
}
#[test]
fn test_framework_generate_check() {
let tests = vec![("t1", "ck_assert_int_eq(1, 1);")];
let source = X86TestFramework::generate_check_source("CSuite", &tests);
assert!(source.contains("Suite *CSuite_suite"));
assert!(source.contains("START_TEST(test_t1)"));
}
#[test]
fn test_framework_generate_unity() {
let tests = vec![("t1", "TEST_ASSERT(1);")];
let source = X86TestFramework::generate_unity_source(&tests);
assert!(source.contains("UNITY_BEGIN()"));
assert!(source.contains("UNITY_END()"));
assert!(source.contains("test_t1"));
}
#[test]
fn test_framework_generate_ctest() {
let tests = vec![("mytest", "./mytest_binary")];
let source = X86TestFramework::generate_ctest_source(&tests);
assert!(source.contains("add_test(NAME mytest"));
assert!(source.contains("set_tests_properties"));
}
#[test]
fn test_framework_parse_assertions() {
let source = r#"
TEST(Suite, Test) {
EXPECT_EQ(1, 2);
ASSERT_TRUE(cond);
EXPECT_FLOAT_EQ(1.0f, 2.0f);
ASSERT_DEATH(func(), "msg");
}
"#;
let assertions = X86TestFramework::parse_gtest_assertions(source);
assert!(assertions.iter().any(|a| a.contains("EXPECT_EQ")));
assert!(assertions.iter().any(|a| a.contains("ASSERT_TRUE")));
assert!(assertions.iter().any(|a| a.contains("EXPECT_FLOAT_EQ")));
}
#[test]
fn test_framework_count_tests_gtest() {
let source = "TEST(a,b) TEST(c,d) TEST_F(e,f) TEST_P(g,h)";
assert_eq!(
X86TestFramework::count_tests(source, X86FrameworkType::GTest),
4
);
}
#[test]
fn test_framework_count_tests_catch2() {
let source = "TEST_CASE(\"a\") TEST_CASE(\"b\")";
assert_eq!(
X86TestFramework::count_tests(source, X86FrameworkType::Catch2),
2
);
}
#[test]
fn test_framework_validate_gtest_missing_main() {
let source = "TEST(a,b) { }";
let errors = X86TestFramework::validate_source(source, X86FrameworkType::GTest);
assert!(!errors.is_empty());
}
#[test]
fn test_framework_validate_gtest_valid() {
let source = r#"TEST(a,b) { }
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}"#;
let errors = X86TestFramework::validate_source(source, X86FrameworkType::GTest);
assert!(errors.is_empty());
}
#[test]
fn test_generators_creation() {
let r#gen = X86TestGenerators::new();
assert_eq!(r#gen.rng_state, 42);
}
#[test]
fn test_generators_gen_int() {
let mut r#gen = X86TestGenerators::new();
let val = r#gen.gen_int(0, 100);
assert!(val >= 0 && val <= 100);
}
#[test]
fn test_generators_gen_uint() {
let mut r#gen = X86TestGenerators::new();
let val = r#gen.gen_uint(10, 20);
assert!(val >= 10 && val <= 20);
}
#[test]
fn test_generators_gen_float() {
let mut r#gen = X86TestGenerators::new();
let val = r#gen.gen_float(0.0, 1.0);
assert!(val >= 0.0 && val <= 1.0);
}
#[test]
fn test_generators_gen_string() {
let mut r#gen = X86TestGenerators::new();
let s = r#gen.gen_string(5, 10);
assert!(s.len() >= 5 && s.len() <= 10);
}
#[test]
fn test_generators_gen_bool() {
let mut r#gen = X86TestGenerators::new();
let b = r#gen.gen_bool();
assert!(b == true || b == false);
}
#[test]
fn test_generators_gen_bytes() {
let mut r#gen = X86TestGenerators::new();
let data = r#gen.gen_bytes(10, 20);
assert!(data.len() >= 10 && data.len() <= 20);
}
#[test]
fn test_generators_gen_c_identifier() {
let mut r#gen = X86TestGenerators::new();
let id = r#gen.gen_c_identifier();
assert!(!id.is_empty());
assert!(id.chars().next().unwrap().is_ascii_lowercase());
}
#[test]
fn test_generators_gen_c_source() {
let mut r#gen = X86TestGenerators::new();
let source = r#gen.gen_c_source(5);
assert!(source.contains("#include"));
assert!(source.contains("int ") || source.contains("void "));
}
#[test]
fn test_generators_shrink_int() {
let r#gen = X86TestGenerators::new();
let candidates = r#gen.shrink_int(100);
assert!(!candidates.is_empty());
assert!(candidates.contains(&0));
}
#[test]
fn test_generators_shrink_string() {
let r#gen = X86TestGenerators::new();
let candidates = r#gen.shrink_string("hello world");
assert!(!candidates.is_empty());
assert!(candidates.contains(&String::new()));
}
#[test]
fn test_generators_shrink_bytes() {
let r#gen = X86TestGenerators::new();
let data = vec![1, 2, 3, 4, 5];
let candidates = r#gen.shrink_bytes(&data);
assert!(!candidates.is_empty());
}
#[test]
fn test_mutation_engine_generate() {
let source = "int main() { int a = 1 + 2; return a; }";
let mut engine = X86MutationEngine::new();
let mutants = engine.generate(source);
assert!(!mutants.is_empty());
}
#[test]
fn test_mutation_engine_arithmetic() {
let source = "int f() { return 1 + 1; }";
let mut engine = X86MutationEngine::new();
let mutants = engine.generate(source);
let has_arith = mutants
.iter()
.any(|m| m.operator == X86MutationOperator::ArithmeticOp);
assert!(has_arith);
}
#[test]
fn test_mutation_score() {
let r#gen = X86TestGenerators::new();
let mutants = vec![
X86Mutant {
id: 0,
operator: X86MutationOperator::ArithmeticOp,
original: "+".to_string(),
replacement: "-".to_string(),
source: String::new(),
location: 0,
},
X86Mutant {
id: 1,
operator: X86MutationOperator::ConstantValue,
original: "1".to_string(),
replacement: "0".to_string(),
source: String::new(),
location: 0,
},
];
let mut killed = HashSet::new();
killed.insert(0);
let score = r#gen.mutation_score(&mutants, &killed);
assert_eq!(score, 50.0);
}
#[test]
fn test_coverage_fuzzer() {
let mut fuzzer = X86CoverageFuzzer::new();
assert!(fuzzer.corpus.is_empty());
let cov_data = X86CoverageData::new();
let input = fuzzer.generate(&cov_data);
assert!(!input.is_empty());
}
#[test]
fn test_coverage_fuzzer_add_to_corpus() {
let mut fuzzer = X86CoverageFuzzer::new();
let mut coverage = HashMap::new();
coverage.insert(100, 10);
let added = fuzzer.maybe_add_to_corpus(vec![1, 2, 3], &coverage);
assert!(added);
assert_eq!(fuzzer.corpus.len(), 1);
}
#[test]
fn test_console_reporter() {
let reporter = X86ConsoleReporter::new(true);
assert_eq!(reporter.name(), "console");
assert!(reporter.color);
}
#[test]
fn test_junit_reporter() {
let path = std::env::temp_dir().join("test_junit.xml");
let reporter = X86JUnitReporter::new(&path);
assert_eq!(reporter.name(), "junit");
}
#[test]
fn test_json_reporter() {
let path = std::env::temp_dir().join("test_json.json");
let reporter = X86JSONReporter::new(&path, true);
assert_eq!(reporter.name(), "json");
assert!(reporter.pretty);
}
#[test]
fn test_json_escape() {
assert_eq!(
X86JSONReporter::json_escape("hello\nworld"),
"hello\\nworld"
);
assert_eq!(X86JSONReporter::json_escape("say \"hi\""), "say \\\"hi\\\"");
}
#[test]
fn test_tap_reporter() {
let path = std::env::temp_dir().join("test_tap.tap");
let reporter = X86TAPReporter::new(&path);
assert_eq!(reporter.name(), "tap");
assert_eq!(reporter.tap_version, 13);
}
#[test]
fn test_html_reporter() {
let path = std::env::temp_dir().join("test_report.html");
let reporter = X86HTMLReporter::new(&path);
assert_eq!(reporter.name(), "html");
}
#[test]
fn test_html_escape() {
assert_eq!(X86HTMLReporter::html_escape("<script>"), "<script>");
assert_eq!(X86HTMLReporter::html_escape("a & b"), "a & b");
}
#[test]
fn test_markdown_reporter() {
let path = std::env::temp_dir().join("test_report.md");
let reporter = X86MarkdownReporter::new(&path);
assert_eq!(reporter.name(), "markdown");
}
#[test]
fn test_junit_xml_escape() {
assert_eq!(
X86JUnitReporter::escape_xml("<test & case>"),
"<test & case>"
);
}
#[test]
fn test_fixtures_creation() {
let fixtures = X86TestFixtures::new();
assert_eq!(fixtures.active_resources(), 0);
}
#[test]
fn test_fixtures_create_temp_dir() {
let mut fixtures = X86TestFixtures::new();
let result = fixtures.create_temp_dir();
assert!(result.is_ok());
let dir = result.unwrap();
assert!(dir.exists());
}
#[test]
fn test_fixtures_create_temp_file() {
let mut fixtures = X86TestFixtures::new();
let result = fixtures.create_temp_file("test content");
assert!(result.is_ok());
let file = result.unwrap();
assert!(file.exists());
let content = std::fs::read_to_string(&file).unwrap();
assert_eq!(content, "test content");
}
#[test]
fn test_fixtures_create_temp_c_source() {
let mut fixtures = X86TestFixtures::new();
let result = fixtures.create_temp_c_source("myfile", "int x;");
assert!(result.is_ok());
let file = result.unwrap();
assert!(file.file_name().unwrap().to_string_lossy().ends_with(".c"));
}
#[test]
fn test_fixtures_create_temp_header() {
let mut fixtures = X86TestFixtures::new();
let result = fixtures.create_temp_header("header", "#pragma once");
assert!(result.is_ok());
let file = result.unwrap();
assert!(file.file_name().unwrap().to_string_lossy().ends_with(".h"));
}
#[test]
fn test_fixtures_cleanup() {
let mut fixtures = X86TestFixtures::new();
let dir = fixtures.create_temp_dir().unwrap();
let path = dir.clone();
assert!(path.exists());
fixtures.cleanup();
assert!(!path.exists());
assert_eq!(fixtures.active_resources(), 0);
}
#[test]
fn test_fixtures_global_setup_teardown() {
let state = Arc::new(AtomicBool::new(false));
let state_clone = Arc::clone(&state);
let mut fixtures = X86TestFixtures::new();
let s_clone = Arc::clone(&state);
fixtures.register_global_setup(Box::new(move || {
s_clone.store(true, Ordering::Relaxed);
}));
fixtures.run_global_setup();
assert!(state.load(Ordering::Relaxed));
}
#[test]
fn test_assertions_eq_pass() {
let assertions = X86TestAssertions::new();
let result = assertions.assert_eq(&42, &42);
assert!(result.is_ok());
}
#[test]
fn test_assertions_eq_fail() {
let assertions = X86TestAssertions::new();
let result = assertions.assert_eq(&1, &2);
assert!(result.is_err());
}
#[test]
fn test_assertions_ne_pass() {
let assertions = X86TestAssertions::new();
let result = assertions.assert_ne(&1, &2);
assert!(result.is_ok());
}
#[test]
fn test_assertions_ne_fail() {
let assertions = X86TestAssertions::new();
let result = assertions.assert_ne(&42, &42);
assert!(result.is_err());
}
#[test]
fn test_assertions_lt() {
let assertions = X86TestAssertions::new();
assert!(assertions.assert_lt(&1, &2).is_ok());
assert!(assertions.assert_lt(&2, &1).is_err());
assert!(assertions.assert_lt(&2, &2).is_err());
}
#[test]
fn test_assertions_le() {
let assertions = X86TestAssertions::new();
assert!(assertions.assert_le(&1, &2).is_ok());
assert!(assertions.assert_le(&2, &2).is_ok());
assert!(assertions.assert_le(&2, &1).is_err());
}
#[test]
fn test_assertions_gt() {
let assertions = X86TestAssertions::new();
assert!(assertions.assert_gt(&2, &1).is_ok());
assert!(assertions.assert_gt(&1, &2).is_err());
}
#[test]
fn test_assertions_ge() {
let assertions = X86TestAssertions::new();
assert!(assertions.assert_ge(&2, &1).is_ok());
assert!(assertions.assert_ge(&2, &2).is_ok());
}
#[test]
fn test_assertions_float_eq() {
let assertions = X86TestAssertions::new();
assert!(assertions.assert_float_eq(1.0, 1.0, 0.001).is_ok());
assert!(assertions.assert_float_eq(1.0, 1.1, 0.001).is_err());
}
#[test]
fn test_assertions_double_eq() {
let assertions = X86TestAssertions::new();
assert!(assertions.assert_double_eq(1.0, 1.0, 1e-9).is_ok());
assert!(assertions.assert_double_eq(1.0, 1.1, 1e-9).is_err());
}
#[test]
fn test_assertions_near() {
let assertions = X86TestAssertions::new();
assert!(assertions.assert_near(1.0, 1.001, 0.01).is_ok());
assert!(assertions.assert_near(1.0, 2.0, 0.01).is_err());
}
#[test]
fn test_assertions_streq() {
let assertions = X86TestAssertions::new();
assert!(assertions.assert_streq("hello", "hello").is_ok());
assert!(assertions.assert_streq("hello", "world").is_err());
}
#[test]
fn test_assertions_strne() {
let assertions = X86TestAssertions::new();
assert!(assertions.assert_strne("hello", "world").is_ok());
assert!(assertions.assert_strne("hello", "hello").is_err());
}
#[test]
fn test_assertions_strcaseeq() {
let assertions = X86TestAssertions::new();
assert!(assertions.assert_strcaseeq("HELLO", "hello").is_ok());
assert!(assertions.assert_strcaseeq("HELLO", "world").is_err());
}
#[test]
fn test_assertions_true_false() {
let assertions = X86TestAssertions::new();
assert!(assertions.assert_true(true, "should pass").is_ok());
assert!(assertions.assert_true(false, "should fail").is_err());
assert!(assertions.assert_false(false, "should pass").is_ok());
assert!(assertions.assert_false(true, "should fail").is_err());
}
#[test]
fn test_assertions_pred() {
let assertions = X86TestAssertions::new();
assert!(assertions.assert_pred(|| 1 + 1 == 2, "math works").is_ok());
assert!(assertions
.assert_pred(|| 1 + 1 == 3, "math broken")
.is_err());
}
#[test]
fn test_assertions_no_throw() {
let assertions = X86TestAssertions::new();
assert!(assertions.assert_no_throw(|| {}).is_ok());
}
#[test]
fn test_assertions_throw() {
let assertions = X86TestAssertions::new();
let result = assertions.assert_throw(|| {
panic!("on purpose");
});
assert!(result.is_ok());
}
#[test]
fn test_assertions_summary() {
let assertions = X86TestAssertions::new();
let _ = assertions.assert_eq(&1, &1);
let _ = assertions.assert_eq(&1, &2);
let summary = assertions.summary();
assert!(summary.contains("total"));
assert!(summary.contains("passed"));
assert!(summary.contains("failed"));
}
#[test]
fn test_assertions_pass_rate() {
let assertions = X86TestAssertions::new();
let _ = assertions.assert_eq(&1, &1);
let _ = assertions.assert_eq(&1, &1);
let _ = assertions.assert_eq(&1, &2);
let rate = assertions.assertion_pass_rate();
assert!((rate - 66.666).abs() < 1.0);
}
#[test]
fn test_database_creation() {
let db = X86TestDatabase::new();
assert_eq!(db.total_runs(), 0);
}
#[test]
fn test_database_register_test() {
let mut db = X86TestDatabase::new();
db.register_test("test1", "suite1");
assert!(db.tests.contains_key("suite1::test1"));
}
#[test]
fn test_database_record_result() {
let mut db = X86TestDatabase::new();
let result = X86TestResult::pass("test1", "suite1", 100);
db.record_result(&result);
assert_eq!(db.total_runs(), 1);
}
#[test]
fn test_database_multiple_results() {
let mut db = X86TestDatabase::new();
for i in 0..10 {
let result = X86TestResult::pass("test1", "suite1", i * 10);
db.record_result(&result);
}
let entry = db.tests.get("suite1::test1").unwrap();
assert_eq!(entry.total_runs, 10);
assert_eq!(entry.total_passes, 10);
}
#[test]
fn test_database_flaky_detection() {
let mut db = X86TestDatabase::new();
let results = vec![
X86TestResult::pass("flaky", "suite", 10),
X86TestResult::fail("flaky", "suite", "oops", 10),
];
for r in &results {
db.record_result(r);
}
}
#[test]
fn test_database_quarantine() {
let mut db = X86TestDatabase::new();
db.quarantine_test("bad_test", "suite");
assert!(db.is_quarantined("bad_test", "suite"));
db.unquarantine_test("bad_test", "suite");
assert!(!db.is_quarantined("bad_test", "suite"));
}
#[test]
fn test_database_source_map() {
let mut db = X86TestDatabase::new();
db.map_source_to_test("foo.c", "test_foo", "suite");
db.map_source_to_test("bar.c", "test_bar", "suite");
let affected = db.get_affected_tests(&["foo.c"]);
assert!(affected.contains(&"suite::test_foo".to_string()));
assert!(!affected.contains(&"suite::test_bar".to_string()));
}
#[test]
fn test_database_slowest_tests() {
let mut db = X86TestDatabase::new();
for i in 0..5 {
let result = X86TestResult::pass(&format!("test{}", i), "suite", (i as u64 + 1) * 100);
db.record_result(&result);
}
let slowest = db.get_slowest_tests(3);
assert_eq!(slowest.len(), 3);
}
#[test]
fn test_database_overall_pass_rate() {
let mut db = X86TestDatabase::new();
db.record_result(&X86TestResult::pass("a", "s", 1));
db.record_result(&X86TestResult::pass("b", "s", 2));
db.record_result(&X86TestResult::fail("c", "s", "err", 3));
let rate = db.overall_pass_rate();
assert!((rate - 66.666).abs() < 1.0);
}
#[test]
fn test_database_reset() {
let mut db = X86TestDatabase::new();
db.register_test("test1", "suite1");
db.record_result(&X86TestResult::pass("test1", "suite1", 1));
db.reset();
assert_eq!(db.total_runs(), 0);
assert!(db.tests.is_empty());
}
#[test]
fn test_coverage_creation() {
let coverage = X86CodeCoverage::new();
assert_eq!(coverage.sancov_counters.len(), X86_SANCOV_COUNTER_SIZE);
}
#[test]
fn test_coverage_init_sancov() {
let mut coverage = X86CodeCoverage::new();
coverage.init_sancov(100);
assert_eq!(coverage.sancov_counters.len(), 100);
}
#[test]
fn test_coverage_sancov_increment() {
let mut coverage = X86CodeCoverage::new();
coverage.init_sancov(10);
coverage.sancov_increment(5);
coverage.sancov_increment(5);
assert_eq!(coverage.sancov_counters[5], 2);
}
#[test]
fn test_coverage_collect_sancov() {
let mut coverage = X86CodeCoverage::new();
coverage.init_sancov(10);
coverage.sancov_increment(0);
coverage.sancov_increment(1);
coverage.sancov_increment(1);
let data = coverage.collect();
assert!(data.is_some());
let data = data.unwrap();
assert_eq!(data.lines_covered, 2);
assert_eq!(data.lines_total, 10);
}
#[test]
fn test_coverage_data_percentages() {
let data = X86CoverageData {
functions_total: 10,
functions_covered: 8,
lines_total: 100,
lines_covered: 75,
branches_total: 50,
branches_covered: 40,
regions_covered: vec![],
counters: BTreeMap::new(),
};
assert_eq!(data.function_coverage_pct(), 80.0);
assert_eq!(data.line_coverage_pct(), 75.0);
assert_eq!(data.branch_coverage_pct(), 80.0);
}
#[test]
fn test_coverage_record_function() {
let mut coverage = X86CodeCoverage::new();
coverage.record_function("main", "test.c", 10, 5);
let func = coverage.function_coverage.get("main").unwrap();
assert_eq!(func.name, "main");
assert_eq!(func.lines_total, 10);
assert_eq!(func.execution_count, 5);
}
#[test]
fn test_coverage_lcov_output() {
let mut coverage = X86CodeCoverage::new();
coverage.init_sancov(5);
coverage.sancov_increment(0);
coverage.collect();
let lcov = coverage.generate_lcov();
assert!(lcov.contains("TN:"));
assert!(lcov.contains("SF:"));
}
#[test]
fn test_coverage_reset() {
let mut coverage = X86CodeCoverage::new();
coverage.init_sancov(10);
coverage.sancov_increment(0);
coverage.reset();
assert!(coverage.sancov_counters.iter().all(|&c| c == 0));
assert!(coverage.data.is_none());
}
#[test]
fn test_result_pass() {
let result = X86TestResult::pass("t", "s", 100);
assert!(result.is_pass());
assert!(!result.is_fail());
}
#[test]
fn test_result_fail() {
let result = X86TestResult::fail("t", "s", "failed", 100);
assert!(!result.is_pass());
assert!(result.is_fail());
}
#[test]
fn test_result_xfail_is_pass() {
let mut result = X86TestResult::fail("t", "s", "expected", 100);
result.status = X86TestStatus::XFailed;
assert!(result.is_pass());
}
#[test]
fn test_result_display() {
let result = X86TestResult::pass("my_test", "my_suite", 1500);
let display = format!("{}", result);
assert!(display.contains("PASS"));
assert!(display.contains("my_suite"));
assert!(display.contains("my_test"));
}
#[test]
fn test_result_with_flaky_history() {
let result = X86TestResult::pass("t", "s", 100)
.with_flaky_history(vec![X86TestStatus::Failed, X86TestStatus::Passed]);
assert!(result.is_flaky);
}
#[test]
fn test_summary_new() {
let summary = X86TestSummary::new();
assert_eq!(summary.total, 0);
}
#[test]
fn test_summary_update_pass() {
let mut summary = X86TestSummary::new();
summary.update(&X86TestResult::pass("t", "s", 10));
assert_eq!(summary.total, 1);
assert_eq!(summary.passed, 1);
}
#[test]
fn test_summary_update_fail() {
let mut summary = X86TestSummary::new();
summary.update(&X86TestResult::fail("t", "s", "err", 10));
assert_eq!(summary.failed, 1);
}
#[test]
fn test_summary_all_pass() {
let mut summary = X86TestSummary::new();
summary.passed = 10;
assert!(summary.all_pass());
summary.failed = 1;
assert!(!summary.all_pass());
}
#[test]
fn test_summary_pass_rate() {
let mut summary = X86TestSummary::new();
summary.total = 10;
summary.passed = 8;
summary.failed = 2;
assert_eq!(summary.pass_rate(), 80.0);
}
#[test]
fn test_summary_merge() {
let mut s1 = X86TestSummary::new();
s1.passed = 5;
let mut s2 = X86TestSummary::new();
s2.passed = 3;
s1.merge(&s2);
assert_eq!(s1.passed, 8);
}
#[test]
fn test_summary_display() {
let summary = X86TestSummary::new();
let display = format!("{}", summary);
assert!(display.contains("Total:"));
}
#[test]
fn test_run_result_new() {
let result = X86TestRunResult::new();
assert!(result.all_passed());
assert_eq!(result.pass_rate(), 100.0);
}
#[test]
fn test_run_result_add() {
let mut result = X86TestRunResult::new();
result.add_result(X86TestResult::pass("a", "s", 1));
result.add_result(X86TestResult::fail("b", "s", "err", 2));
assert_eq!(result.summary.total, 2);
assert!(!result.all_passed());
}
#[test]
fn test_run_result_failures() {
let mut result = X86TestRunResult::new();
result.add_result(X86TestResult::pass("a", "s", 1));
result.add_result(X86TestResult::fail("b", "s", "err", 1));
result.add_result(X86TestResult::fail("c", "s", "err2", 1));
assert_eq!(result.failures().len(), 2);
}
#[test]
fn test_compile_mode_display() {
assert_eq!(
format!("{}", X86CompileMode::CompileAndRun),
"compile-and-run"
);
assert_eq!(format!("{}", X86CompileMode::SyntaxOnly), "syntax-only");
assert_eq!(format!("{}", X86CompileMode::FileCheck), "filecheck");
}
#[test]
fn test_arch_display() {
assert_eq!(format!("{}", X86TestArch::X86_32), "i386");
assert_eq!(format!("{}", X86TestArch::X86_64), "x86_64");
assert_eq!(format!("{}", X86TestArch::X86_X32), "x86_x32");
}
#[test]
fn test_status_display() {
assert_eq!(format!("{}", X86TestStatus::Passed), "PASSED");
assert_eq!(format!("{}", X86TestStatus::Failed), "FAILED");
assert_eq!(format!("{}", X86TestStatus::Skipped), "SKIPPED");
}
#[test]
fn test_framework_type_display() {
assert_eq!(format!("{}", X86FrameworkType::GTest), "gtest");
assert_eq!(format!("{}", X86FrameworkType::Catch2), "catch2");
assert_eq!(format!("{}", X86FrameworkType::DocTest), "doctest");
assert_eq!(format!("{}", X86FrameworkType::CppUTest), "cpputest");
assert_eq!(format!("{}", X86FrameworkType::BoostTest), "boost");
assert_eq!(format!("{}", X86FrameworkType::CUnit), "cunit");
assert_eq!(format!("{}", X86FrameworkType::Check), "check");
assert_eq!(format!("{}", X86FrameworkType::Unity), "unity");
assert_eq!(format!("{}", X86FrameworkType::CTest), "ctest");
}
#[test]
fn test_config_default() {
let config = X86TestHarnessConfig::default();
assert_eq!(config.parallel_workers, X86_DEFAULT_THREAD_POOL_SIZE);
assert_eq!(config.timeout_ms, X86_DEFAULT_TEST_TIMEOUT_MS);
assert_eq!(config.target_arch, X86TestArch::X86_64);
}
#[test]
fn test_sanity_suite_creation() {
let suite = x86_sanity_test_suite();
assert_eq!(suite.name, "X86HarnessSanity");
assert!(suite.tags.contains(&"sanity".to_string()));
assert!(!suite.is_empty());
}
#[test]
fn test_regression_suite_creation() {
let suite = x86_regression_test_suite();
assert!(!suite.is_empty());
}
#[test]
fn test_c_feature_suite_creation() {
let suite = x86_c_feature_test_suite();
assert!(suite.test_count() >= 10);
}
#[test]
fn test_cpp_feature_suite_creation() {
let suite = x86_cpp_feature_test_suite();
assert!(suite.test_count() >= 5);
}
#[test]
fn test_stress_suite_creation() {
let suite = x86_stress_test_suite();
assert!(!suite.is_empty());
}
#[test]
fn test_x86_target_suite_creation() {
let suite = x86_target_test_suite();
assert!(!suite.is_empty());
}
#[test]
fn test_full_harness_workflow() {
let mut harness = X86TestHarness::new();
harness.register_suite(x86_sanity_test_suite());
harness.register_suite(x86_c_feature_test_suite());
let result = harness.run_all();
assert!(result.summary.total > 0);
harness.print_summary(&result);
}
#[test]
fn test_harness_with_reporters() {
let mut harness = X86TestHarness::new();
harness.register_test(
"suite",
X86TestCase::new("passing").with_source("int main() { return 0; }"),
);
let temp_dir = std::env::temp_dir().join("x86_test_reports");
let _ = std::fs::create_dir_all(&temp_dir);
harness.reporters.push(Box::new(X86JUnitReporter::new(
&temp_dir.join("results.xml"),
)));
harness.reporters.push(Box::new(X86JSONReporter::new(
&temp_dir.join("results.json"),
false,
)));
harness
.reporters
.push(Box::new(X86TAPReporter::new(&temp_dir.join("results.tap"))));
harness.reporters.push(Box::new(X86HTMLReporter::new(
&temp_dir.join("results.html"),
)));
harness.reporters.push(Box::new(X86MarkdownReporter::new(
&temp_dir.join("results.md"),
)));
let mut config = harness.config.clone();
config.junit_report = true;
config.json_report = true;
config.tap_report = true;
config.html_report = true;
config.markdown_report = true;
config.report_dir = Some(temp_dir.clone());
harness.config = config;
let result = harness.run_all();
assert!(result.all_passed());
let _ = std::fs::remove_dir_all(&temp_dir);
}
#[test]
fn test_database_regression_selection() {
let mut db = X86TestDatabase::new();
db.map_source_to_test("src/lexer.c", "test_lex", "lexer_tests");
db.map_source_to_test("src/parser.c", "test_parse", "parser_tests");
db.map_source_to_test("src/codegen.c", "test_cg", "codegen_tests");
let affected = db.get_affected_tests(&["src/lexer.c"]);
assert_eq!(affected.len(), 1);
assert!(affected.contains(&"lexer_tests::test_lex".to_string()));
let affected = db.get_affected_tests(&["src/lexer.c", "src/codegen.c"]);
assert_eq!(affected.len(), 2);
}
#[test]
fn test_generators_fuzz_compile() {
let mut r#gen = X86TestGenerators::new();
let config = X86TestHarnessConfig::default();
let result = r#gen.fuzz_compile("int main() { return 0; }", &config);
assert!(matches!(result, FuzzResult::Pass));
}
#[test]
fn test_generators_fuzz_compile_error() {
let mut r#gen = X86TestGenerators::new();
let config = X86TestHarnessConfig::default();
let result = r#gen.fuzz_compile("int main() { ", &config);
assert!(matches!(result, FuzzResult::CompileError(_)));
}
#[test]
fn test_framework_detect_fallback() {
let source = "int main() { return 0; }";
let detected = X86TestFramework::detect_framework(source);
assert_eq!(detected, None);
}
#[test]
fn test_framework_validate_catch2_missing_main() {
let source = "TEST_CASE(\"test\") { }";
let errors = X86TestFramework::validate_source(source, X86FrameworkType::Catch2);
assert!(errors.is_empty() || !errors.is_empty());
}
#[test]
fn test_framework_validate_cunit_missing_init() {
let source = "CU_add_test(NULL, \"t\", NULL);";
let errors = X86TestFramework::validate_source(source, X86FrameworkType::CUnit);
assert!(!errors.is_empty());
}
#[test]
fn test_runner_asm_generation() {
let runner = X86TestRunner::new();
let config = X86TestHarnessConfig::default();
let asm = runner.generate_x86_asm("int main() { return 42; }", &config);
assert!(asm.contains("main:"));
assert!(asm.contains("retq"));
}
#[test]
fn test_runner_ir_generation() {
let runner = X86TestRunner::new();
let config = X86TestHarnessConfig::default();
let ir = runner.generate_llvm_ir("int main() { return 42; }", &config);
assert!(ir.contains("@main"));
assert!(ir.contains("ret i32"));
}
#[test]
fn test_runner_strip_asm_comments() {
let runner = X86TestRunner::new();
let asm = "mov rax, rbx ; this is a comment\nret # another";
let stripped = runner.strip_asm_comments(asm);
assert!(!stripped.contains("comment"));
}
#[test]
fn test_mutation_engine_relational() {
let source = "int f() { return 1 == 1; }";
let mut engine = X86MutationEngine::new();
let mutants = engine.generate(source);
let has_rel = mutants
.iter()
.any(|m| m.operator == X86MutationOperator::RelationalOp);
assert!(has_rel);
}
#[test]
fn test_mutation_engine_logical() {
let source = "int f() { return 1 && 0; }";
let mut engine = X86MutationEngine::new();
let mutants = engine.generate(source);
let has_log = mutants
.iter()
.any(|m| m.operator == X86MutationOperator::LogicalOp);
assert!(has_log);
}
#[test]
fn test_mutation_engine_constants() {
let source = "int f() { return 42; }";
let mut engine = X86MutationEngine::new();
let mutants = engine.generate(source);
let has_const = mutants
.iter()
.any(|m| m.operator == X86MutationOperator::ConstantValue);
assert!(has_const);
}
#[test]
fn test_mutation_engine_return() {
let source = "int f() { int x = 5; return x; }";
let mut engine = X86MutationEngine::new();
let mutants = engine.generate(source);
let has_ret = mutants
.iter()
.any(|m| m.operator == X86MutationOperator::ReturnValue);
assert!(has_ret);
}
#[test]
fn test_html_reporter_build() {
let mut result = X86TestRunResult::new();
result.add_result(X86TestResult::pass("t1", "s1", 100));
result.add_result(X86TestResult::fail("t2", "s1", "err", 200));
let path = std::env::temp_dir().join("test_coverage.html");
let reporter = X86HTMLReporter::new(&path);
let html = reporter.build_html(&result);
assert!(html.contains("<!DOCTYPE html>"));
assert!(html.contains("X86 Clang Test Report"));
}
#[test]
fn test_markdown_reporter_build() {
let mut result = X86TestRunResult::new();
result.add_result(X86TestResult::pass("t1", "s1", 100));
let path = std::env::temp_dir().join("test_coverage.md");
let reporter = X86MarkdownReporter::new(&path);
let md = reporter.build_markdown(&result);
assert!(md.contains("# X86 Clang Test Report"));
assert!(md.contains("## Summary"));
}
#[test]
fn test_json_reporter_build() {
let mut result = X86TestRunResult::new();
result.add_result(X86TestResult::pass("t1", "s1", 100));
let path = std::env::temp_dir().join("test_coverage.json");
let reporter = X86JSONReporter::new(&path, false);
let json = reporter.build_json(&result);
assert!(json.contains("test_suite"));
assert!(json.contains("results"));
}
#[test]
fn test_database_most_failing() {
let mut db = X86TestDatabase::new();
for _ in 0..5 {
db.record_result(&X86TestResult::pass("good", "s", 1));
}
for _ in 0..3 {
db.record_result(&X86TestResult::fail("bad", "s", "err", 1));
}
let failing = db.get_most_failing_tests(5);
assert!(!failing.is_empty());
}
#[test]
fn test_coverage_html_report() {
let mut coverage = X86CodeCoverage::new();
coverage.init_sancov(5);
coverage.sancov_increment(0);
coverage.sancov_increment(2);
coverage.collect();
let path = std::env::temp_dir().join("coverage_report.html");
let result = coverage.generate_html_report(&path);
assert!(result.is_ok());
let _ = std::fs::remove_file(&path);
}
#[test]
fn test_eval_simple_expr() {
let runner = X86TestRunner::new();
assert_eq!(runner.eval_simple_expr("1 + 2"), Some(3));
assert_eq!(runner.eval_simple_expr("10 - 3"), Some(7));
assert_eq!(runner.eval_simple_expr("4 * 5"), Some(20));
assert_eq!(runner.eval_simple_expr("abc"), None);
}
#[test]
fn test_harness_record_result_one_shot() {
let mut db = X86TestDatabase::new();
let result = X86TestResult::pass("single", "run", 42);
db.record_result(&result);
let entry = db.tests.get("run::single");
assert!(entry.is_some());
assert_eq!(entry.unwrap().total_runs, 1);
}
}