#[test]
fn test_heredoc_execution() {
let source = r#"
fn main() {
print_multiline();
}
fn print_multiline() {}
"#;
let config = Config::default();
let shell = transpile(source, &config).unwrap();
let mut file = NamedTempFile::new().expect("Failed to create temp file");
file.write_all(shell.as_bytes())
.expect("Failed to write shell script");
let output = Command::new("sh")
.arg(file.path())
.output()
.expect("Failed to execute shell script");
assert!(
output.status.success() || output.status.code() == Some(127),
"Script should execute. Exit code: {:?}, stderr: {}",
output.status.code(),
String::from_utf8_lossy(&output.stderr)
);
}
#[test]
fn test_process_id_purification_baseline() {
let source = r#"
fn main() {
use_fixed_id();
}
fn use_fixed_id() {}
"#;
let config = Config::default();
let result = transpile(source, &config);
assert!(
result.is_ok(),
"Should transpile fixed ID function: {:?}",
result.err()
);
let shell = result.unwrap();
eprintln!("Generated shell for fixed ID:\n{}", shell);
assert!(
shell.contains("use_fixed_id"),
"Should use fixed identifier, not $$"
);
let main_section = shell.split("main() {").nth(1).unwrap_or("");
let main_body = main_section.split("}").next().unwrap_or("");
assert!(
!main_body.contains("$$"),
"Main function should NOT contain $$ (trap cleanup is OK, but user code shouldn't use $$)"
);
}
#[test]
#[ignore] fn test_process_id_rejection() {
let source = r#"
fn main() {
let pid = std::process::id();
println!("PID: {}", pid);
}
"#;
let config = Config::default();
let result = transpile(source, &config);
assert!(
result.is_err(),
"std::process::id() should be rejected as non-deterministic"
);
}
#[test]
fn test_process_id_execution() {
let source = r#"
fn main() {
use_session_id("test-session");
}
fn use_session_id(id: &str) {}
"#;
let config = Config::default();
let shell = transpile(source, &config).unwrap();
let mut file = NamedTempFile::new().expect("Failed to create temp file");
file.write_all(shell.as_bytes())
.expect("Failed to write shell script");
let output = Command::new("sh")
.arg(file.path())
.output()
.expect("Failed to execute shell script");
assert!(
output.status.success() || output.status.code() == Some(127),
"Script should execute with fixed ID"
);
}
#[test]
fn test_background_pid_purification_baseline() {
let source = r#"
fn main() {
run_sync();
}
fn run_sync() {}
"#;
let config = Config::default();
let result = transpile(source, &config);
assert!(
result.is_ok(),
"Should transpile sync function: {:?}",
result.err()
);
let shell = result.unwrap();
eprintln!("Generated shell for sync execution:\n{}", shell);
assert!(
shell.contains("run_sync"),
"Should call function synchronously"
);
assert!(
!shell.contains(" &") && !shell.contains("$!"),
"Should NOT contain background job operators (non-deterministic)"
);
}
#[test]
#[ignore] fn test_background_async_rejection() {
let source = r#"
async fn background_task() {
// Some work
}
fn main() {
background_task();
}
"#;
let config = Config::default();
let result = transpile(source, &config);
assert!(
result.is_err(),
"async functions should be rejected as non-deterministic"
);
}
#[test]
fn test_background_sync_execution() {
let source = r#"
fn main() {
task1();
task2();
}
fn task1() {}
fn task2() {}
"#;
let config = Config::default();
let shell = transpile(source, &config).unwrap();
let mut file = NamedTempFile::new().expect("Failed to create temp file");
file.write_all(shell.as_bytes())
.expect("Failed to write shell script");
let output = Command::new("sh")
.arg(file.path())
.output()
.expect("Failed to execute shell script");
assert!(
output.status.success() || output.status.code() == Some(127),
"Script should execute synchronously"
);
}
#[test]
fn test_random_purification_baseline() {
let source = r#"
fn main() {
use_seed(42);
}
fn use_seed(seed: i32) {}
"#;
let config = Config::default();
let result = transpile(source, &config);
assert!(
result.is_ok(),
"Should transpile seed function: {:?}",
result.err()
);
let shell = result.unwrap();
eprintln!("Generated shell for deterministic seed:\n{}", shell);
assert!(
shell.contains("use_seed") && shell.contains("42"),
"Should use deterministic seed"
);
assert!(
!shell.contains("$RANDOM") && !shell.contains("RANDOM"),
"Should NOT contain $RANDOM (non-deterministic)"
);
}
#[test]
#[ignore] fn test_random_crate_rejection() {
let source = r#"
fn main() {
let num = rand::random::<u32>();
println!("{}", num);
}
"#;
let config = Config::default();
let result = transpile(source, &config);
assert!(
result.is_err(),
"rand crate usage should be rejected as non-deterministic"
);
}
#[test]
fn test_random_deterministic_execution() {
let source = r#"
fn main() {
use_value(12345);
}
fn use_value(val: i32) {}
"#;
let config = Config::default();
let shell = transpile(source, &config).unwrap();
let mut file = NamedTempFile::new().expect("Failed to create temp file");
file.write_all(shell.as_bytes())
.expect("Failed to write shell script");
let output = Command::new("sh")
.arg(file.path())
.output()
.expect("Failed to execute shell script");
assert!(
output.status.success() || output.status.code() == Some(127),
"Script should execute with deterministic value"
);
}
#[test]
fn test_session6_commands_execution() {
let source = r#"
fn main() {
print_heredoc();
use_fixed_id();
run_sync();
use_seed(42);
}
fn print_heredoc() {}
fn use_fixed_id() {}
fn run_sync() {}
fn use_seed(seed: i32) {}
"#;
let config = Config::default();
let shell = transpile(source, &config).unwrap();
eprintln!("Generated combined shell script:\n{}", shell);
assert!(shell.contains("print_heredoc"), "Should call print_heredoc");
assert!(shell.contains("use_fixed_id"), "Should call use_fixed_id");
assert!(shell.contains("run_sync"), "Should call run_sync");
assert!(shell.contains("use_seed"), "Should call use_seed");
let main_section = shell.split("main() {").nth(1).unwrap_or("");
let main_body = main_section.split("}").next().unwrap_or("");
assert!(!main_body.contains("$$"), "Main should NOT contain $$");
assert!(!main_body.contains("$!"), "Main should NOT contain $!");
assert!(
!main_body.contains("$RANDOM"),
"Main should NOT contain $RANDOM"
);
assert!(
!main_body.contains(" &"),
"Main should NOT contain background &"
);
let mut file = NamedTempFile::new().expect("Failed to create temp file");
file.write_all(shell.as_bytes())
.expect("Failed to write shell script");
let output = Command::new("sh")
.arg(file.path())
.output()
.expect("Failed to execute shell script");
eprintln!("Exit code: {:?}", output.status.code());
eprintln!("Stderr: {}", String::from_utf8_lossy(&output.stderr));
}
#[test]
fn test_exit_status_baseline() {
let source = r#"
fn main() {
get_status();
}
fn get_status() -> i32 { 0 }
"#;
let config = Config::default();
let result = transpile(source, &config);
assert!(
result.is_ok(),
"Should transpile exit status function: {:?}",
result.err()
);
let shell = result.unwrap();
eprintln!("Generated shell for exit status:\n{}", shell);
assert!(
shell.contains("get_status"),
"Should transpile get_status function"
);
}
#[test]
#[ignore] fn test_exit_status_capture() {
let source = r#"
fn main() {
run_command();
let status = last_exit_code();
check_status(status);
}
fn run_command() {}
fn last_exit_code() -> i32 { 0 }
fn check_status(code: i32) {}
"#;
let config = Config::default();
let shell = transpile(source, &config).unwrap();
assert!(shell.contains("$?"), "Should use $? to capture exit status");
}
#[test]
fn test_exit_status_param_execution() {
let source = r#"
fn main() {
check_result(0);
}
fn check_result(code: i32) {}
"#;
let config = Config::default();
let shell = transpile(source, &config).unwrap();
let mut file = NamedTempFile::new().expect("Failed to create temp file");
file.write_all(shell.as_bytes())
.expect("Failed to write shell script");
let output = Command::new("sh")
.arg(file.path())
.output()
.expect("Failed to execute shell script");
assert!(
output.status.success() || output.status.code() == Some(127),
"Script should execute"
);
}