#![cfg(any(
not(any(
feature = "parser_tests",
feature = "analyzer_tests",
feature = "codegen_tests",
feature = "interpreter_tests",
feature = "conformance_tests",
feature = "integration_tests",
)),
feature = "integration_tests",
))]
use std::io::Write;
use std::process::Command;
fn compile_wj_project(files: &[(&str, &str)]) -> std::collections::HashMap<String, String> {
let dir = tempfile::tempdir().expect("Failed to create temp dir");
let src_dir = dir.path().join("src");
let out_dir = dir.path().join("out");
std::fs::create_dir_all(&src_dir).unwrap();
std::fs::create_dir_all(&out_dir).unwrap();
for (name, content) in files {
let file_path = src_dir.join(name);
if let Some(parent) = file_path.parent() {
std::fs::create_dir_all(parent).unwrap();
}
let mut file = std::fs::File::create(&file_path).unwrap();
file.write_all(content.as_bytes()).unwrap();
}
let output = Command::new(env!("CARGO_BIN_EXE_wj"))
.args([
"build",
src_dir.to_str().unwrap(),
"--no-cargo",
"-o",
out_dir.to_str().unwrap(),
])
.current_dir(env!("CARGO_MANIFEST_DIR"))
.output()
.expect("Failed to run wj");
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout);
eprintln!("Compilation stderr:\n{}", stderr);
eprintln!("Compilation stdout:\n{}", stdout);
}
let mut results = std::collections::HashMap::new();
fn collect_rs_files(
dir: &std::path::Path,
base: &std::path::Path,
results: &mut std::collections::HashMap<String, String>,
) {
if let Ok(entries) = std::fs::read_dir(dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
collect_rs_files(&path, base, results);
} else if path.extension().is_some_and(|e| e == "rs") {
let relative = path
.strip_prefix(base)
.unwrap()
.to_string_lossy()
.to_string();
if let Ok(content) = std::fs::read_to_string(&path) {
results.insert(relative, content);
}
}
}
}
}
collect_rs_files(&out_dir, &out_dir, &mut results);
results
}
#[test]
fn test_cross_module_no_clone_on_i32_field_via_borrowed_iter() {
let files = &[
(
"mod.wj",
r#"
pub mod item
pub mod trading
"#,
),
(
"item.wj",
r#"
pub struct Item {
pub id: string,
pub name: string,
}
pub struct ItemStack {
pub item: Item,
pub quantity: i32,
}
impl ItemStack {
pub fn new(item: Item, quantity: i32) -> ItemStack {
ItemStack { item: item, quantity: quantity }
}
}
"#,
),
(
"trading.wj",
r#"
use item::ItemStack
pub fn total_quantity(stacks: Vec<ItemStack>) -> i32 {
let mut total = 0
for stack in &stacks {
total = total + stack.quantity
}
total
}
pub fn has_enough(stacks: Vec<ItemStack>, min_qty: i32) -> bool {
for stack in &stacks {
if stack.quantity < min_qty {
return false
}
}
true
}
"#,
),
];
let generated = compile_wj_project(files);
let trading_rs = generated
.iter()
.find(|(k, _)| k.contains("trading"))
.map(|(_, v)| v.clone())
.expect("trading.rs not found in output");
println!("Generated trading.rs:\n{}", trading_rs);
assert!(
!trading_rs.contains("stack.quantity.clone()"),
"Should not clone i32 field 'quantity' accessed through borrowed iterator in multi-file build.\nGenerated:\n{}",
trading_rs
);
}
#[test]
fn test_cross_module_no_clone_on_f32_field_via_borrowed_iter() {
let files = &[
(
"mod.wj",
r#"
pub mod math
pub mod physics
"#,
),
(
"math.wj",
r#"
pub struct Vector2 {
pub x: f32,
pub y: f32,
}
"#,
),
(
"physics.wj",
r#"
use math::Vector2
pub fn sum_positions(points: Vec<Vector2>) -> f32 {
let mut total = 0.0
for p in &points {
total = total + p.x + p.y
}
total
}
"#,
),
];
let generated = compile_wj_project(files);
let physics_rs = generated
.iter()
.find(|(k, _)| k.contains("physics"))
.map(|(_, v)| v.clone())
.expect("physics.rs not found in output");
println!("Generated physics.rs:\n{}", physics_rs);
assert!(
!physics_rs.contains("p.x.clone()"),
"Should not clone f32 field 'x' in multi-file build.\nGenerated:\n{}",
physics_rs
);
assert!(
!physics_rs.contains("p.y.clone()"),
"Should not clone f32 field 'y' in multi-file build.\nGenerated:\n{}",
physics_rs
);
}