use depyler_core::DepylerPipeline;
#[test]
fn test_list_method_append() {
let pipeline = DepylerPipeline::new();
let python_code = r#"
def test_list():
items = [1, 2, 3]
items.append(4)
return items
"#;
let rust_code = pipeline.transpile(python_code).unwrap();
println!("Generated list append code:\n{}", rust_code);
assert!(
rust_code.contains(".push("),
"list.append() should transpile to .push()"
);
}
#[test]
fn test_dict_method_get_with_default() {
let pipeline = DepylerPipeline::new();
let python_code = r#"
def test_dict():
data = {"key": "value"}
result = data.get("key", "default")
return result
"#;
let rust_code = pipeline.transpile(python_code).unwrap();
println!("Generated dict.get() code:\n{}", rust_code);
assert!(
rust_code.contains(".get(") && rust_code.contains("unwrap_or"),
"dict.get(key, default) should use .get().unwrap_or()"
);
}
#[test]
fn test_string_method_upper() {
let pipeline = DepylerPipeline::new();
let python_code = r#"
def test_str():
text = "hello"
return text.upper()
"#;
let rust_code = pipeline.transpile(python_code).unwrap();
println!("Generated string.upper() code:\n{}", rust_code);
assert!(
rust_code.contains(".to_uppercase()"),
"str.upper() should transpile to .to_uppercase()"
);
}
#[test]
fn test_floor_division_semantics() {
let pipeline = DepylerPipeline::new();
let python_code = r#"
def floor_div(a: int, b: int) -> int:
return a // b
"#;
let rust_code = pipeline.transpile(python_code).unwrap();
println!("Generated floor division code:\n{}", rust_code);
assert!(
rust_code.contains("needs_adjustment") || rust_code.contains("signs_differ"),
"Floor division should implement Python semantics"
);
}
#[test]
fn test_power_operation_with_negative_exponent() {
let pipeline = DepylerPipeline::new();
let python_code = r#"
def power_calc():
return 2 ** -1
"#;
let rust_code = pipeline.transpile(python_code).unwrap();
println!("Generated power with negative exp code:\n{}", rust_code);
assert!(
rust_code.contains(".powf(") || rust_code.contains("as f64"),
"Negative exponent should use float power"
);
}
#[test]
fn test_set_literals_generate_hashset() {
let pipeline = DepylerPipeline::new();
let python_code = r#"
def set_create():
a = {1, 2, 3}
return a
"#;
let rust_code = pipeline.transpile(python_code).unwrap();
println!("Generated set creation code:\n{}", rust_code);
assert!(
rust_code.contains("HashSet"),
"Set literals should generate HashSet"
);
}
#[test]
fn test_slice_with_step() {
let pipeline = DepylerPipeline::new();
let python_code = r#"
def slice_test():
arr = [1, 2, 3, 4, 5, 6]
return arr[::2]
"#;
let rust_code = pipeline.transpile(python_code).unwrap();
println!("Generated slice with step code:\n{}", rust_code);
assert!(
rust_code.contains(".step_by(") || rust_code.contains("step"),
"Slice with step should use .step_by()"
);
}
#[test]
fn test_slice_negative_step() {
let pipeline = DepylerPipeline::new();
let python_code = r#"
def reverse_slice():
arr = [1, 2, 3, 4, 5]
return arr[::-1]
"#;
let rust_code = pipeline.transpile(python_code).unwrap();
println!("Generated reverse slice code:\n{}", rust_code);
assert!(
rust_code.contains(".rev()"),
"Slice [::-1] should use .rev()"
);
}
#[test]
fn test_list_comprehension_with_filter() {
let pipeline = DepylerPipeline::new();
let python_code = r#"
def list_comp():
return [x * 2 for x in range(10) if x > 5]
"#;
let rust_code = pipeline.transpile(python_code).unwrap();
println!("Generated list comprehension code:\n{}", rust_code);
assert!(
rust_code.contains(".filter(") && rust_code.contains(".map("),
"List comprehension with condition should use .filter().map()"
);
}
#[cfg(test)]
mod property_tests {
use super::*;
use proptest::prelude::*;
proptest! {
#![proptest_config(ProptestConfig::with_cases(10))]
#[test]
fn prop_integer_binary_operations_transpile(a in -100i32..100i32, b in 1i32..100i32) {
let pipeline = DepylerPipeline::new();
let python_code = format!(r#"
def binary_ops():
return {} + {}
"#, a, b);
let result = pipeline.transpile(&python_code);
prop_assert!(result.is_ok(), "Binary operation transpilation failed: {:?}", result.err());
}
#[test]
fn prop_list_operations_always_generate_vec(size in 1usize..10) {
let pipeline = DepylerPipeline::new();
let elements = (0..size).map(|i| i.to_string()).collect::<Vec<_>>().join(", ");
let python_code = format!(r#"
def make_list():
return [{}]
"#, elements);
let result = pipeline.transpile(&python_code);
prop_assert!(result.is_ok(), "List transpilation failed");
let rust_code = result.unwrap();
prop_assert!(
rust_code.contains("vec!") || rust_code.contains("vec !"),
"List should generate vec! macro"
);
}
#[test]
fn prop_dict_operations_require_hashmap(pairs in 0usize..5) {
let pipeline = DepylerPipeline::new();
let items = (0..pairs)
.map(|i| format!(r#""key{}": {}"#, i, i))
.collect::<Vec<_>>()
.join(", ");
let python_code = format!(r#"
def make_dict():
return {{{}}}
"#, items);
let result = pipeline.transpile(&python_code);
prop_assert!(result.is_ok(), "Dict transpilation failed");
let rust_code = result.unwrap();
prop_assert!(
rust_code.contains("HashMap"),
"Dict should require HashMap"
);
}
#[test]
fn prop_range_calls_generate_valid_ranges(n in 1usize..20) {
let pipeline = DepylerPipeline::new();
let python_code = format!(r#"
def use_range():
total = 0
for i in range({}):
total = total + i
return total
"#, n);
let result = pipeline.transpile(&python_code);
prop_assert!(result.is_ok(), "range() transpilation failed");
let rust_code = result.unwrap();
prop_assert!(
rust_code.contains("..") || rust_code.contains("range"),
"range() should generate Rust range syntax"
);
}
}
}
#[cfg(test)]
mod mutation_tests {
use super::*;
#[test]
fn test_mutation_method_dispatch_correctness() {
let pipeline = DepylerPipeline::new();
let python_code = r#"
def test_list_methods():
items = []
items.append(1)
items.append(2)
return items
"#;
let rust_code = pipeline.transpile(python_code).unwrap();
assert!(
rust_code.matches(".push(").count() == 2,
"MUTATION KILL: Must use .push() exactly 2 times for 2 append calls (found {} times)",
rust_code.matches(".push(").count()
);
assert!(
rust_code.contains(".push(1)") && rust_code.contains(".push(2)"),
"MUTATION KILL: Must pass correct arguments to .push()"
);
}
#[test]
fn test_mutation_floor_division_semantics() {
let pipeline = DepylerPipeline::new();
let python_code = r#"
def floor_div_test(a: int, b: int) -> int:
return a // b
"#;
let rust_code = pipeline.transpile(python_code).unwrap();
assert!(
rust_code.contains("needs_adjustment") || rust_code.contains("signs_differ"),
"MUTATION KILL: Must include Python floor division adjustment logic"
);
assert!(
rust_code.contains("r_nonzero") || rust_code.contains("r != 0"),
"MUTATION KILL: Must check remainder for adjustment decision"
);
assert!(
rust_code.contains("negative") || rust_code.contains("< 0"),
"MUTATION KILL: Must check signs for floor division"
);
}
#[test]
fn test_mutation_comprehension_filter_order() {
let pipeline = DepylerPipeline::new();
let python_code = r#"
def filtered_comp():
return [x * 2 for x in range(10) if x > 5]
"#;
let rust_code = pipeline.transpile(python_code).unwrap();
assert!(
rust_code.contains(".filter("),
"MUTATION KILL: Must include .filter() for comprehension condition"
);
assert!(
rust_code.contains(".map("),
"MUTATION KILL: Must include .map() for element transformation"
);
assert!(
rust_code.contains(".collect::<Vec<_>>()"),
"MUTATION KILL: Must collect into Vec for list comprehension"
);
let filter_pos = rust_code.find(".filter(").expect(".filter( must exist");
let map_pos = rust_code.find(".map(").expect(".map( must exist");
assert!(
filter_pos < map_pos,
"MUTATION KILL: .filter() must appear before .map() in chain (filter at {}, map at {})",
filter_pos,
map_pos
);
}
#[test]
fn test_mutation_set_creation() {
let pipeline = DepylerPipeline::new();
let python_code = r#"
def make_set():
s = {1, 2, 3}
return s
"#;
let rust_code = pipeline.transpile(python_code).unwrap();
assert!(
rust_code.contains("HashSet"),
"MUTATION KILL: Set literals must use HashSet"
);
assert!(
rust_code.matches(".insert(").count() >= 3,
"MUTATION KILL: Must use .insert() for each set element (found {} times)",
rust_code.matches(".insert(").count()
);
assert!(
rust_code.contains("use std::collections::HashSet"),
"MUTATION KILL: Must import HashSet for set literals"
);
}
}
#[test]
fn test_counter_builtin_conversion() {
let pipeline = DepylerPipeline::new();
let python_code = r#"
from collections import Counter
def count_items(items):
return Counter(items)
"#;
let rust_code = pipeline.transpile(python_code).unwrap();
println!("Generated Counter() code:\n{}", rust_code);
assert!(
!rust_code.contains("HashMap::new(items)") && !rust_code.contains("HashMap(items)"),
"Counter(items) should NOT use HashMap::new(items) or HashMap(items)"
);
assert!(
rust_code.contains(".collect::<HashMap<") || rust_code.contains(".fold("),
"Counter(items) should use .collect() or .fold() to count elements"
);
}
#[test]
fn test_dict_builtin_conversion() {
let pipeline = DepylerPipeline::new();
let python_code = r#"
def convert_to_dict(mapping):
return dict(mapping)
"#;
let rust_code = pipeline.transpile(python_code).unwrap();
println!("Generated dict() code:\n{}", rust_code);
assert!(
rust_code.contains(".collect::<HashMap<") || rust_code.contains("HashMap::from"),
"dict(mapping) should use .collect() or HashMap::from()"
);
}
#[test]
fn test_dict_empty_constructor() {
let pipeline = DepylerPipeline::new();
let python_code = r#"
def make_empty_dict():
return dict()
"#;
let rust_code = pipeline.transpile(python_code).unwrap();
println!("Generated dict() empty code:\n{}", rust_code);
assert!(
rust_code.contains("HashMap::new()"),
"dict() with no args should generate HashMap::new()"
);
}
#[test]
fn test_deque_builtin_conversion() {
let pipeline = DepylerPipeline::new();
let python_code = r#"
from collections import deque
def make_deque(items):
return deque(items)
"#;
let rust_code = pipeline.transpile(python_code).unwrap();
println!("Generated deque() code:\n{}", rust_code);
assert!(
!rust_code.contains("VecDeque(items)"),
"deque(items) should NOT use VecDeque(items)"
);
assert!(
rust_code.contains("VecDeque::from(") || rust_code.contains(".collect::<VecDeque<"),
"deque(items) should use VecDeque::from() or .collect()"
);
}
#[test]
fn test_list_builtin_conversion() {
let pipeline = DepylerPipeline::new();
let python_code = r#"
def convert_to_list(iterable):
return list(iterable)
"#;
let rust_code = pipeline.transpile(python_code).unwrap();
println!("Generated list() code:\n{}", rust_code);
assert!(
rust_code.contains(".collect::<Vec<") || rust_code.contains("Vec::from") || rust_code.contains(".to_vec()"),
"list(iterable) should use .collect(), Vec::from(), or .to_vec()"
);
}
#[test]
fn test_list_empty_constructor() {
let pipeline = DepylerPipeline::new();
let python_code = r#"
def make_empty_list():
return list()
"#;
let rust_code = pipeline.transpile(python_code).unwrap();
println!("Generated list() empty code:\n{}", rust_code);
assert!(
rust_code.contains("Vec::new()") || rust_code.contains("vec![]") || rust_code.contains("vec ! []"),
"list() with no args should generate Vec::new() or vec![]"
);
}