use crate::*;
#[test]
fn test_analyze_costs_valid() {
let source = "program test\nfn main() {\n pub_write(pub_read())\n}";
let result = analyze_costs(source, "test.tri");
assert!(result.is_ok());
let cost = result.unwrap();
assert!(cost.total.get(0) > 0);
assert!(cost.padded_height.is_power_of_two());
}
#[test]
fn test_analyze_costs_type_error() {
let source = "program test\nfn main() {\n let x: U32 = pub_read()\n}";
assert!(analyze_costs(source, "test.tri").is_err());
}
#[test]
fn test_coin_cost_analysis() {
let path = std::path::Path::new("os/neptune/standards/coin.tri");
if !path.exists() {
return;
}
let cost = analyze_costs_project(path, &CompileOptions::default())
.expect("cost analysis should succeed");
// Processor table should be nonzero
assert!(cost.total.get(0) > 0);
// Token uses hash heavily (leaf hashing, config hashing, auth verification)
assert!(cost.total.get(1) > 0, "token should have hash table cost");
// Token uses u32 range checks for balance verification
assert!(
cost.total.get(2) > 0,
"token should have u32 table cost for range checks"
);
// Padded height should be reasonable (power of 2)
assert!(cost.padded_height.is_power_of_two());
assert!(
cost.padded_height <= 4096,
"padded height {} seems too high",
cost.padded_height
);
// Should have functions for all 5 operations
let fn_names: Vec<&str> = cost.functions.iter().map(|f| f.name.as_str()).collect();
assert!(fn_names.contains(&"pay"), "missing pay cost");
assert!(fn_names.contains(&"mint"), "missing mint cost");
assert!(fn_names.contains(&"burn"), "missing burn cost");
assert!(fn_names.contains(&"lock"), "missing lock cost");
assert!(fn_names.contains(&"update"), "missing update cost");
// Helper functions should appear (hash_config and verify_config
// moved to plumb.tri, but hash_metadata remains local)
assert!(
fn_names.contains(&"hash_metadata"),
"missing hash_metadata cost"
);
assert!(fn_names.contains(&"hash_leaf"), "missing hash_leaf cost");
eprintln!(
"Token cost: padded_height={}, cc={}, hash={}, u32={}",
cost.padded_height,
cost.total.get(0),
cost.total.get(1),
cost.total.get(2)
);
eprintln!("{}", cost.format_report());
}
#[test]
fn test_annotate_source_valid() {
let source =
"program test\n\nfn main() {\n let x: Field = pub_read()\n pub_write(x)\n}\n";
let result = annotate_source(source, "test.tri");
assert!(result.is_ok(), "annotate_source should succeed");
let annotated = result.unwrap();
// Should contain line numbers
assert!(annotated.contains("1 |"), "should have line 1");
assert!(annotated.contains("3 |"), "should have line 3");
// Should contain cost annotations (brackets with cc= or jump=)
assert!(
annotated.contains("["),
"should contain cost annotation brackets"
);
assert!(annotated.contains("cc="), "should contain cc= cost marker");
// fn main() line should show call overhead (jump stack)
let line3 = annotated.lines().find(|l| l.contains("fn main()"));
assert!(line3.is_some(), "should have fn main() line");
let line3 = line3.unwrap();
assert!(
line3.contains("jump="),
"fn main() should show jump stack cost from call overhead"
);
}
#[test]
fn test_annotate_source_shows_hash_cost() {
let source = "program test\n\nfn main() {\n let d: Digest = divine5()\n let (d0, d1, d2, d3, d4) = d\n let h: Digest = hash(d0, d1, d2, d3, d4, 0, 0, 0, 0, 0)\n pub_write(0)\n}\n";
let result = annotate_source(source, "test.tri");
assert!(result.is_ok(), "annotate_source should succeed");
let annotated = result.unwrap();
// The hash line should show hash cost
let hash_line = annotated.lines().find(|l| l.contains("hash("));
assert!(hash_line.is_some(), "should have hash() line");
let hash_line = hash_line.unwrap();
assert!(
hash_line.contains("hash="),
"hash() line should show hash cost, got: {}",
hash_line
);
}
#[test]
fn test_cost_json_roundtrip_integration() {
let source = "program test\nfn helper(x: Field) -> Field {\n x + x\n}\nfn main() {\n let x: Field = pub_read()\n pub_write(helper(x))\n}";
let cost_result = analyze_costs(source, "test.tri").expect("should analyze");
let json = cost_result.to_json();
// Verify JSON structure
assert!(json.contains("\"functions\""), "JSON should have functions");
assert!(json.contains("\"total\""), "JSON should have total");
assert!(
json.contains("\"padded_height\""),
"JSON should have padded_height"
);
assert!(json.contains("\"main\""), "JSON should have main function");
assert!(
json.contains("\"helper\""),
"JSON should have helper function"
);
// Round-trip
let parsed =
cost::ProgramCost::from_json(&json).expect("should parse JSON back to ProgramCost");
for i in 0..parsed.total.count as usize {
assert_eq!(parsed.total.get(i), cost_result.total.get(i));
}
assert_eq!(parsed.padded_height, cost_result.padded_height);
}
#[test]
fn test_comparison_formatting_integration() {
let source_v1 =
"program test\nfn main() {\n let x: Field = pub_read()\n pub_write(x)\n}";
let source_v2 = "program test\nfn main() {\n let x: Field = pub_read()\n let y: Field = pub_read()\n pub_write(x + y)\n}";
let cost_v1 = analyze_costs(source_v1, "test.tri").expect("v1 should analyze");
let cost_v2 = analyze_costs(source_v2, "test.tri").expect("v2 should analyze");
let comparison = cost_v1.format_comparison(&cost_v2);
assert!(
comparison.contains("Cost comparison:"),
"should have header"
);
assert!(comparison.contains("TOTAL"), "should have TOTAL row");
assert!(
comparison.contains("Padded height:"),
"should have padded height row"
);
assert!(
comparison.contains("main"),
"should show main function in comparison"
);
// v2 has more operations, so delta should be positive
assert!(
comparison.contains("+"),
"v2 should have higher cost than v1, showing + delta"
);
}
trident/src/api/tests/cost.rs
ฯ 0.0%
use crate::*;