use crate::*;
#[test]
fn test_compile_valid_program() {
let source =
"program test\nfn main() {\n let x: Field = pub_read()\n pub_write(x + 1)\n}";
let result = compile(source, "test.tri");
assert!(result.is_ok());
let tasm = result.unwrap();
assert!(tasm.contains("read_io 1"));
assert!(tasm.contains("write_io 1"));
}
#[test]
fn test_compile_type_error_returns_err() {
let source = "program test\nfn main() {\n let x: U32 = pub_read()\n}";
let result = compile(source, "test.tri");
assert!(result.is_err());
}
#[test]
fn test_deeply_nested_if() {
let source = r#"program test
fn main() {
let x: Field = pub_read()
if x == 0 {
if x == 1 {
if x == 2 {
if x == 3 {
if x == 4 {
pub_write(x)
}
}
}
}
}
}
"#;
assert!(compile(source, "test.tri").is_ok());
}
#[test]
fn test_deeply_nested_for() {
let source = r#"program test
fn main() {
let mut s: Field = 0
for i in 0..3 bounded 3 {
for j in 0..3 bounded 3 {
for k in 0..3 bounded 3 {
s = s + 1
}
}
}
pub_write(s)
}
"#;
let result = compile(source, "test.tri");
assert!(result.is_ok());
let tasm = result.unwrap();
assert!(tasm.contains("write_io 1"));
}
#[test]
fn test_many_variables_spill() {
// Force stack spilling by having many live variables
let source = r#"program test
fn main() {
let a: Field = pub_read()
let b: Field = pub_read()
let c: Field = pub_read()
let d: Field = pub_read()
let e: Field = pub_read()
let f: Field = pub_read()
let g: Field = pub_read()
let h: Field = pub_read()
let i: Field = pub_read()
let j: Field = pub_read()
let k: Field = pub_read()
let l: Field = pub_read()
let m: Field = pub_read()
let n: Field = pub_read()
let o: Field = pub_read()
let p: Field = pub_read()
let q: Field = pub_read()
let r: Field = pub_read()
pub_write(a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r)
}
"#;
let result = compile(source, "test.tri");
assert!(
result.is_ok(),
"should handle 18 live variables with spilling"
);
let tasm = result.unwrap();
// All 18 variables contribute to the output โ verify the sum reaches write_io
assert!(
tasm.contains("write_io 1"),
"18-variable sum should produce write_io"
);
}
#[test]
fn test_chain_of_function_calls() {
let source = r#"program test
fn add1(x: Field) -> Field {
x + 1
}
fn add2(x: Field) -> Field {
add1(add1(x))
}
fn add4(x: Field) -> Field {
add2(add2(x))
}
fn main() {
let x: Field = pub_read()
pub_write(add4(add4(x)))
}
"#;
let result = compile(source, "test.tri");
assert!(result.is_ok());
}
#[test]
fn test_all_binary_operators() {
let source = r#"program test
fn main() {
let a: Field = pub_read()
let b: Field = pub_read()
let sum: Field = a + b
let prod: Field = a * b
let eq: Bool = a == b
let (hi, lo) = split(a)
let lt: Bool = hi < lo
let band: U32 = hi & lo
let bxor: U32 = hi ^ lo
let (q, r) = hi /% lo
pub_write(sum)
pub_write(prod)
}
"#;
assert!(compile(source, "test.tri").is_ok());
}
#[test]
fn test_struct_with_digest_field() {
let source = r#"program test
struct AuthData {
owner: Digest,
nonce: Field,
}
fn main() {
let d: Digest = divine5()
let auth: AuthData = AuthData { owner: d, nonce: 42 }
pub_write(auth.nonce)
}
"#;
assert!(compile(source, "test.tri").is_ok());
}
#[test]
fn test_xfield_operations() {
// *. operator is XField * Field -> XField (scalar multiplication)
let source = r#"program test
fn main() {
let a: XField = xfield(1, 2, 3)
let s: Field = pub_read()
let c: XField = a *. s
let d: XField = xinvert(c)
pub_write(0)
}
"#;
assert!(compile(source, "test.tri").is_ok());
}
#[test]
fn test_tail_expression() {
let source = r#"program test
fn double(x: Field) -> Field {
x + x
}
fn main() {
pub_write(double(pub_read()))
}
"#;
assert!(compile(source, "test.tri").is_ok());
}
#[test]
fn test_multiple_return_paths() {
let source = r#"program test
fn abs_diff(a: Field, b: Field) -> Field {
if a == b {
return 0
}
a + b
}
fn main() {
pub_write(abs_diff(pub_read(), pub_read()))
}
"#;
assert!(compile(source, "test.tri").is_ok());
}
#[test]
fn test_events_emit_and_seal() {
let source = r#"program test
event Transfer {
from: Field,
to: Field,
amount: Field,
}
event Commitment {
value: Field,
}
fn main() {
let a: Field = pub_read()
let b: Field = pub_read()
let c: Field = pub_read()
// Open reveal: tag + 3 fields written directly
reveal Transfer { from: a, to: b, amount: c }
// Sealed: hash(tag, value, 0...) written as digest
seal Commitment { value: a }
}
"#;
let tasm = compile(source, "events.tri").expect("events program should compile");
// reveal Transfer: push 0, write_io 1, [field], write_io 1 ร 3
// Total write_io 1 from reveal: 4 (tag + 3 fields)
let write_io_1 = tasm.lines().filter(|l| l.trim() == "write_io 1").count();
assert!(
write_io_1 >= 4,
"expected >= 4 write_io 1 (reveal tag + 3 fields), got {}",
write_io_1
);
// seal Commitment: hash + write_io 5
assert!(tasm.contains("hash"), "seal should contain hash");
assert!(tasm.contains("write_io 5"), "seal should write_io 5");
eprintln!("Events TASM:\n{}", tasm);
}
#[test]
fn test_error_max_nesting_depth() {
// Generate deeply nested blocks via nested if statements.
// Each `if true { ... }` adds one nesting level; 260 > MAX_NESTING_DEPTH (256).
// The parser recurses to depth 256 before the guard triggers, which
// needs more stack than the default test-thread provides in debug
// builds. Run the actual work on a thread with an explicit 16 MB stack.
let handle = std::thread::Builder::new()
.stack_size(16 * 1024 * 1024)
.spawn(|| {
let depth = 260u32;
let mut src = String::from("program t\nfn main() {\n");
for _ in 0..depth {
src.push_str("if true {\n");
}
src.push_str("pub_write(0)\n");
for _ in 0..depth {
src.push_str("}\n");
}
src.push_str("}\n");
let (tokens, _comments, lex_errs) = crate::lexer::Lexer::new(&src, 0).tokenize();
assert!(lex_errs.is_empty(), "lex errors: {:?}", lex_errs);
let result = crate::parser::Parser::new(tokens).parse_file();
assert!(
result.is_err(),
"deeply nested input should produce an error"
);
let diags = result.unwrap_err();
let has_depth = diags.iter().any(|d| d.message.contains("nesting depth"));
assert!(
has_depth,
"should report nesting depth exceeded, got: {:?}",
diags.iter().map(|d| &d.message).collect::<Vec<_>>()
);
})
.expect("failed to spawn test thread");
handle.join().expect("test thread panicked");
}
#[test]
fn test_coin_compiles() {
let path = std::path::Path::new("os/neptune/standards/coin.tri");
if !path.exists() {
return;
}
let tasm = compile_project(path).expect("coin program should compile");
// Verify all 5 operations are in the TASM output
assert!(tasm.contains("__pay:"), "missing pay function");
assert!(tasm.contains("__mint:"), "missing mint function");
assert!(tasm.contains("__burn:"), "missing burn function");
assert!(tasm.contains("__lock:"), "missing lock function");
assert!(tasm.contains("__update:"), "missing update function");
// Verify helper functions
assert!(tasm.contains("__hash_leaf:"), "missing hash_leaf function");
assert!(
tasm.contains("__hash_config:"),
"missing hash_config function"
);
// hash_metadata is defined but never called โ DCE correctly removes it
assert!(
tasm.contains("__verify_auth:"),
"missing verify_auth function"
);
assert!(
tasm.contains("__verify_config:"),
"missing verify_config function"
);
// Verify hash operations are emitted (leaf/config/auth + seal nullifiers)
// hash_metadata is DCE'd (unused), so count is lower than pre-DCE
let hash_count = tasm.lines().filter(|l| l.trim() == "hash").count();
assert!(
hash_count >= 5,
"expected at least 5 hash ops, got {}",
hash_count
);
// Verify seal produces write_io 5 (nullifier commitments in pay and burn)
assert!(
tasm.contains("write_io 5"),
"seal should produce write_io 5"
);
// Verify assertions are present (security checks)
let assert_count = tasm
.lines()
.filter(|l| l.trim().starts_with("assert"))
.count();
assert!(
assert_count >= 6,
"expected at least 6 assertions, got {}",
assert_count
);
// Verify Merkle root authentication is present
assert!(
tasm.contains("merkle_step"),
"should authenticate leaves against Merkle root"
);
eprintln!(
"Token TASM: {} lines, {} instructions",
tasm.lines().count(),
tasm.lines()
.filter(|l| l.starts_with(" ") && !l.trim().is_empty())
.count()
);
}
#[test]
fn test_card_compiles() {
let path = std::path::Path::new("os/neptune/standards/card.tri");
if !path.exists() {
return;
}
let tasm = compile_project(path).expect("card program should compile");
// Verify all 5 PLUMB operations
assert!(tasm.contains("__pay:"), "missing pay function");
assert!(tasm.contains("__mint:"), "missing mint function");
assert!(tasm.contains("__burn:"), "missing burn function");
assert!(tasm.contains("__lock:"), "missing lock function");
assert!(tasm.contains("__update:"), "missing update function");
// Verify helper functions
assert!(tasm.contains("__hash_leaf:"), "missing hash_leaf function");
assert!(
tasm.contains("__hash_config:"),
"missing hash_config function"
);
assert!(
tasm.contains("__verify_auth:"),
"missing verify_auth function"
);
assert!(
tasm.contains("__verify_config:"),
"missing verify_config function"
);
// Verify Merkle root authentication is present
assert!(
tasm.contains("merkle_step"),
"should authenticate leaves against Merkle root"
);
}
trident/src/api/tests/compile.rs
ฯ 0.0%
use crate::*;