#![allow(deprecated)]
use std::io::Write;
use predicates::prelude::*;
use run::engine::LanguageEngine;
use tempfile::NamedTempFile;
fn python_available() -> bool {
run::engine::PythonEngine::new().validate().is_ok()
}
fn rust_available() -> bool {
run::engine::RustEngine::new().validate().is_ok()
}
#[allow(dead_code)]
fn go_available() -> bool {
run::engine::GoEngine::new().validate().is_ok()
}
#[test]
fn hash_source_deterministic() {
let h1 = run::engine::hash_source("fn main() {}");
let h2 = run::engine::hash_source("fn main() {}");
assert_eq!(h1, h2, "same source must produce same hash");
}
#[test]
fn hash_source_different_inputs() {
let h1 = run::engine::hash_source("fn main() {}");
let h2 = run::engine::hash_source("fn main() { 1 }");
assert_ne!(h1, h2, "different source must produce different hash");
}
#[test]
fn hash_source_empty_string() {
let h = run::engine::hash_source("");
assert_ne!(h, 0, "empty string should still produce a valid hash");
}
#[test]
fn cache_lookup_returns_none_for_unknown() {
let hash = run::engine::hash_source("this_is_a_very_unique_test_string_12345");
assert!(
run::engine::cache_lookup("test-cache", hash).is_none(),
"cache should return None for unknown hashes"
);
}
#[test]
fn cache_store_and_retrieve() {
let mut tmp = NamedTempFile::new().expect("create temp file");
writeln!(tmp, "#!/bin/sh\necho cached").expect("write temp");
let path = tmp.path().to_path_buf();
let hash = run::engine::hash_source("cache_store_and_retrieve_test");
let cached = run::engine::cache_store("test-cache", hash, &path);
assert!(cached.is_some(), "cache_store should succeed");
let lookup = run::engine::cache_lookup("test-cache", hash);
assert!(lookup.is_some(), "cache_lookup should find stored entry");
assert!(lookup.unwrap().exists(), "cached path should exist on disk");
}
#[test]
fn rust_inline_uses_cache_on_repeated_run() {
if !rust_available() {
eprintln!("skipping: rustc not available");
return;
}
let engine = run::engine::RustEngine::new();
let code = r#"fn main() { println!("cache_test"); }"#;
let payload = run::engine::ExecutionPayload::Inline {
code: code.to_string(),
args: Vec::new(),
};
let out1 = engine.execute(&payload).expect("first run should succeed");
assert!(out1.success());
assert!(out1.stdout.contains("cache_test"));
let t1 = out1.duration;
let out2 = engine.execute(&payload).expect("second run should succeed");
assert!(out2.success());
assert!(out2.stdout.contains("cache_test"));
eprintln!(
"first: {}ms, second: {}ms",
t1.as_millis(),
out2.duration.as_millis()
);
}
#[test]
fn bench_flag_python() {
if !python_available() {
eprintln!("skipping: python not available");
return;
}
let cmd = assert_cmd::Command::cargo_bin("run")
.expect("binary")
.args(["--bench", "3", "python", "-c", "print('bench')"])
.assert();
cmd.success()
.stdout(predicate::str::contains("bench"))
.stderr(predicate::str::contains("Results (3 runs)"));
}
#[test]
fn bench_flag_requires_positive_iterations() {
if !python_available() {
eprintln!("skipping: python not available");
return;
}
let cmd = assert_cmd::Command::cargo_bin("run")
.expect("binary")
.args(["--bench", "0", "python", "-c", "print('ok')"])
.assert();
cmd.success()
.stdout(predicate::str::contains("ok"))
.stderr(predicate::str::contains("Results (1 runs)"));
}
#[test]
fn watch_flag_requires_file() {
let cmd = assert_cmd::Command::cargo_bin("run")
.expect("binary")
.args(["--watch", "python", "-c", "print('x')"])
.assert();
cmd.failure()
.stderr(predicate::str::contains("--watch requires a file path"));
}
#[test]
fn config_default_is_empty() {
let config = run::config::RunConfig::default();
assert!(config.language.is_none());
assert!(config.timeout.is_none());
assert!(config.timing.is_none());
assert!(config.bench_iterations.is_none());
}
#[test]
fn config_load_from_toml() {
let mut tmp = NamedTempFile::new().expect("create temp");
writeln!(
tmp,
r#"language = "python"
timeout = 30
timing = true
bench_iterations = 10
"#
)
.expect("write");
let config = run::config::RunConfig::load(tmp.path()).expect("parse config");
assert_eq!(config.language.as_deref(), Some("python"));
assert_eq!(config.timeout, Some(30));
assert_eq!(config.timing, Some(true));
assert_eq!(config.bench_iterations, Some(10));
}
#[test]
fn config_load_ignores_unknown_keys() {
let mut tmp = NamedTempFile::new().expect("create temp");
writeln!(tmp, "language = \"go\"\nunknown_key = 42\n").expect("write");
let result = run::config::RunConfig::load(tmp.path());
if let Ok(config) = result {
assert_eq!(config.language.as_deref(), Some("go"));
}
}
#[test]
fn format_duration_millis() {
let engine = run::engine::PythonEngine::new();
if engine.validate().is_err() {
eprintln!("skipping: python not available");
return;
}
let payload = run::engine::ExecutionPayload::Inline {
code: "1+1".to_string(),
args: Vec::new(),
};
let outcome = engine.execute(&payload).expect("should run");
assert!(outcome.success());
assert!(outcome.duration.as_nanos() > 0);
}