use decy_core::transpile;
use proptest::prelude::*;
fn value_strategy() -> impl Strategy<Value = i32> {
-1000i32..=1000
}
fn null_scenario_strategy() -> impl Strategy<Value = bool> {
any::<bool>()
}
proptest! {
#[test]
fn prop_null_check_transpiles(
value in value_strategy()
) {
let c_code = format!(
r#"
#include <stdlib.h>
int main() {{
int* ptr = (int*)malloc(sizeof(int));
if (ptr == 0) {{
return 1;
}}
*ptr = {};
free(ptr);
return 0;
}}
"#,
value
);
let result = transpile(&c_code);
prop_assert!(result.is_ok(), "NULL check should transpile: {:?}", result.err());
}
}
proptest! {
#[test]
fn prop_null_initialization_transpiles(
_ in 0..10usize
) {
let c_code = r#"
int main() {
int* ptr = 0;
if (ptr == 0) {
return 1;
}
return 0;
}
"#;
let result = transpile(c_code);
prop_assert!(result.is_ok(), "NULL initialization should transpile: {:?}", result.err());
}
}
proptest! {
#[test]
fn prop_function_return_null_transpiles(
return_null in null_scenario_strategy()
) {
let condition = if return_null { "0" } else { "1" };
let c_code = format!(
r#"
#include <stdlib.h>
int* get_ptr(int cond) {{
if (cond == 0) {{
return 0;
}}
int* ptr = (int*)malloc(sizeof(int));
return ptr;
}}
int main() {{
int* ptr = get_ptr({});
if (ptr != 0) {{
free(ptr);
}}
return 0;
}}
"#,
condition
);
let result = transpile(&c_code);
prop_assert!(result.is_ok(), "Function return NULL should transpile: {:?}", result.err());
}
}
proptest! {
#[test]
fn prop_defensive_null_check_transpiles(
value in value_strategy()
) {
let c_code = format!(
r#"
int safe_deref(int* ptr) {{
if (ptr == 0) {{
return -1;
}}
return *ptr;
}}
int main() {{
int val = {};
int result = safe_deref(&val);
return result;
}}
"#,
value
);
let result = transpile(&c_code);
prop_assert!(result.is_ok(), "Defensive NULL check should transpile: {:?}", result.err());
}
}
proptest! {
#[test]
fn prop_unsafe_density_below_target(
value in value_strategy()
) {
let c_code = format!(
r#"
#include <stdlib.h>
int main() {{
int* ptr = (int*)malloc(sizeof(int));
if (ptr == 0) {{
return 1;
}}
*ptr = {};
int result = *ptr;
free(ptr);
return result;
}}
"#,
value
);
let result = transpile(&c_code).expect("Should transpile");
let unsafe_count = result.matches("unsafe").count();
let lines = result.lines().count();
let unsafe_per_1000 = if lines > 0 {
(unsafe_count as f64 / lines as f64) * 1000.0
} else {
0.0
};
prop_assert!(
unsafe_per_1000 <= 100.0,
"Unsafe per 1000 LOC should be <=100, got {:.2}",
unsafe_per_1000
);
}
}
proptest! {
#[test]
fn prop_generated_code_balanced(
_ in 0..10usize
) {
let c_code = r#"
int main() {
int* ptr = 0;
if (ptr != 0) {
*ptr = 42;
}
return 0;
}
"#;
let result = transpile(c_code).expect("Should transpile");
let open_braces = result.matches('{').count();
let close_braces = result.matches('}').count();
prop_assert_eq!(
open_braces, close_braces,
"Braces should be balanced: {} open, {} close",
open_braces, close_braces
);
}
}
proptest! {
#[test]
fn prop_transpilation_deterministic(
value in value_strategy()
) {
let c_code = format!(
r#"
int main() {{
int val = {};
int* ptr = &val;
if (ptr != 0) {{
return *ptr;
}}
return 0;
}}
"#,
value
);
let result1 = transpile(&c_code).expect("Should transpile (1)");
let result2 = transpile(&c_code).expect("Should transpile (2)");
prop_assert_eq!(
result1, result2,
"Transpilation should be deterministic"
);
}
}
proptest! {
#[test]
fn prop_null_coalescing_transpiles(
default_value in value_strategy()
) {
let c_code = format!(
r#"
int main() {{
int* ptr = 0;
int value = (ptr != 0) ? *ptr : {};
return value;
}}
"#,
default_value
);
let result = transpile(&c_code);
prop_assert!(result.is_ok(), "NULL coalescing should transpile: {:?}", result.err());
}
}