use polydat::dsl::compile::compile_gk;
use polydat::dsl::ast::BindingModifier;
use polydat::node::Value;
use polydat::kernel::Construction;
use polydat::subcontext::GkMatter;
#[test]
fn materialize_wiring_from_outer_wires_constants() {
let outer = compile_gk(r#"
input cycle: u64
dim := 128
count := 1000
"#).unwrap();
let inner_program = compile_gk(r#"
input cycle: u64
extern dim: u64
extern count: u64
"#).unwrap().program().clone();
let mut inner = outer.subscope(GkMatter::builder().program(inner_program).build().unwrap()).unwrap();
let dim_idx = inner.program().find_input("dim").unwrap();
let count_idx = inner.program().find_input("count").unwrap();
assert_eq!(inner.state().get_input(dim_idx).as_u64(), 128);
assert_eq!(inner.state().get_input(count_idx).as_u64(), 1000);
}
#[test]
fn materialize_wiring_from_outer_only_matches_by_name() {
let outer = compile_gk(r#"
input cycle: u64
dim := 128
"#).unwrap();
let inner_program = compile_gk(r#"
input cycle: u64
extern offset: u64
"#).unwrap().program().clone();
let mut inner = outer.subscope(GkMatter::builder().program(inner_program).build().unwrap()).unwrap();
let idx = inner.program().find_input("offset").unwrap();
assert!(matches!(inner.state().get_input(idx), Value::None));
}
#[test]
fn materialize_wiring_from_outer_does_not_affect_coordinates() {
let outer = compile_gk(r#"
input cycle: u64
dim := 128
"#).unwrap();
let inner_program = compile_gk(r#"
input cycle: u64
extern dim: u64
h := hash(cycle)
"#).unwrap().program().clone();
let mut inner = outer.subscope(GkMatter::builder().program(inner_program).build().unwrap()).unwrap();
inner.set_inputs(&[42]);
let v1 = inner.pull("h").as_u64();
inner.set_inputs(&[43]);
let v2 = inner.pull("h").as_u64();
assert_ne!(v1, v2, "different cycles should produce different hashes");
}
#[test]
fn scope_values_extracts_bound_inputs() {
let outer = compile_gk(r#"
input cycle: u64
dim := 128
count := 500
"#).unwrap();
let inner_program = compile_gk(r#"
input cycle: u64
extern dim: u64
extern count: u64
"#).unwrap().program().clone();
let inner = outer.subscope(GkMatter::builder().program(inner_program).build().unwrap()).unwrap();
let values = inner.scope_values();
let dim_val = values.iter().find(|(name, _)| name == "dim");
assert!(dim_val.is_some(), "scope_values should include dim");
assert_eq!(dim_val.unwrap().1.as_u64(), 128);
}
#[test]
fn scope_values_empty_when_no_externs() {
let outer = compile_gk(r#"
input cycle: u64
dim := 128
"#).unwrap();
let inner_program = compile_gk(r#"
input cycle: u64
h := hash(cycle)
"#).unwrap().program().clone();
let inner = outer.subscope(GkMatter::builder().program(inner_program).build().unwrap()).unwrap();
let values = inner.scope_values();
for (_, val) in &values {
assert!(matches!(val, Value::U64(0)),
"only coordinate defaults expected, got {:?}", val);
}
}
#[test]
fn inner_scope_shadows_outer_binding() {
let outer = compile_gk(r#"
input cycle: u64
dim := 128
"#).unwrap();
assert_eq!(outer.get_constant("dim").unwrap().as_u64(), 128);
let inner = compile_gk(r#"
input cycle: u64
dim := 256
"#).unwrap();
assert_eq!(inner.get_constant("dim").unwrap().as_u64(), 256);
let inner2_program = compile_gk(r#"
input cycle: u64
dim := 256
"#).unwrap().program().clone();
let inner2 = outer.subscope(GkMatter::builder().program(inner2_program).build().unwrap()).unwrap();
assert_eq!(inner2.get_constant("dim").unwrap().as_u64(), 256);
}
#[test]
fn shared_modifier_survives_compilation_pipeline() {
let kernel = compile_gk(r#"
input cycle: u64
shared running_total := 0
shared error_count := 0
normal_val := hash(cycle)
"#).unwrap();
let prog = kernel.program();
assert_eq!(prog.output_modifier("running_total"), BindingModifier::SHARED);
assert_eq!(prog.output_modifier("error_count"), BindingModifier::SHARED);
assert_eq!(prog.output_modifier("normal_val"), BindingModifier::NONE);
let mut shared = prog.shared_outputs();
shared.sort();
assert_eq!(shared, vec!["error_count", "running_total"]);
}
#[test]
fn shared_literal_constant_folds() {
let kernel = compile_gk(r#"
input cycle: u64
shared budget := 100
"#).unwrap();
assert_eq!(kernel.program().output_modifier("budget"), BindingModifier::SHARED);
assert_eq!(kernel.lookup("budget").unwrap().as_u64(), 100);
}
#[test]
fn final_modifier_survives_compilation_pipeline() {
let kernel = compile_gk(r#"
input cycle: u64
const dataset := "example"
const dim := 128
mutable_val := hash(cycle)
"#).unwrap();
let prog = kernel.program();
assert_eq!(prog.output_modifier("dataset"), BindingModifier::CONST);
assert_eq!(prog.output_modifier("dim"), BindingModifier::CONST);
assert_eq!(prog.output_modifier("mutable_val"), BindingModifier::NONE);
let mut finals = prog.const_outputs();
finals.sort();
assert_eq!(finals, vec!["dataset", "dim"]);
}
#[test]
fn const_literal_constant_folds() {
let kernel = compile_gk(r#"
input cycle: u64
const max_dim := 512
"#).unwrap();
assert_eq!(kernel.program().output_modifier("max_dim"), BindingModifier::CONST);
assert_eq!(kernel.get_constant("max_dim").unwrap().as_u64(), 512);
}
#[test]
fn extern_wired_into_hash() {
let src = r#"
input cycle: u64
extern seed: u64
result := hash(seed)
"#;
let mut kernel = compile_gk(src).unwrap();
let idx = kernel.program().find_input("seed").unwrap();
kernel.state().set_input(idx, Value::U64(42));
kernel.set_inputs(&[0]);
let v1 = kernel.pull("result").as_u64();
kernel.state().set_input(idx, Value::U64(99));
kernel.set_inputs(&[0]);
let v2 = kernel.pull("result").as_u64();
assert_ne!(v1, v2, "different seeds should produce different hashes");
}
#[test]
fn extern_in_binary_expression() {
let src = r#"
input cycle: u64
extern multiplier: u64
result := cycle * multiplier
"#;
let mut kernel = compile_gk(src).unwrap();
let idx = kernel.program().find_input("multiplier").unwrap();
kernel.state().set_input(idx, Value::U64(7));
kernel.set_inputs(&[6]);
assert_eq!(kernel.pull("result").as_u64(), 42);
}
#[test]
fn extern_in_function_chain() {
let src = r#"
input cycle: u64
extern base: u64
h := hash(base)
result := mod(h, 100)
"#;
let mut kernel = compile_gk(src).unwrap();
let idx = kernel.program().find_input("base").unwrap();
kernel.state().set_input(idx, Value::U64(42));
kernel.set_inputs(&[0]);
let v = kernel.pull("result").as_u64();
assert!(v < 100);
}
#[test]
fn extern_and_coordinate_mixed() {
let src = r#"
input cycle: u64
extern offset: u64
result := hash(cycle) + offset
"#;
let mut kernel = compile_gk(src).unwrap();
let idx = kernel.program().find_input("offset").unwrap();
kernel.state().set_input(idx, Value::U64(1000));
kernel.set_inputs(&[42]);
let v1 = kernel.pull("result").as_u64();
kernel.state().set_input(idx, Value::U64(2000));
kernel.set_inputs(&[42]);
let v2 = kernel.pull("result").as_u64();
assert_eq!(v2 - v1, 1000, "offset difference should be reflected");
}
#[test]
fn full_scope_pipeline_outer_to_inner() {
let outer = compile_gk(r#"
input cycle: u64
dim := 128
base_count := 10000
"#).unwrap();
let inner_program = compile_gk(r#"
input cycle: u64
extern dim: u64
extern base_count: u64
id := hash(cycle) + base_count
"#).unwrap().program().clone();
let mut inner = outer.subscope(GkMatter::builder().program(inner_program).build().unwrap()).unwrap();
inner.set_inputs(&[0]);
let dim_val = inner.pull("dim").as_u64();
assert_eq!(dim_val, 128);
inner.set_inputs(&[42]);
let id = inner.pull("id").as_u64();
assert!(id >= 10000, "id should include base_count offset, got {id}");
}
#[test]
fn scope_pipeline_with_shared_and_final() {
let outer = compile_gk(r#"
input cycle: u64
shared error_budget := 100
const max_dim := 256
normal := hash(cycle)
"#).unwrap();
let prog = outer.program();
assert_eq!(prog.output_modifier("error_budget"), BindingModifier::SHARED);
assert_eq!(prog.output_modifier("max_dim"), BindingModifier::CONST);
assert_eq!(prog.output_modifier("normal"), BindingModifier::NONE);
let inner_program = compile_gk(r#"
input cycle: u64
extern error_budget: u64
extern max_dim: u64
"#).unwrap().program().clone();
let mut inner = outer.subscope(GkMatter::builder().program(inner_program).build().unwrap()).unwrap();
let eb_idx = inner.program().find_input("error_budget").unwrap();
let md_idx = inner.program().find_input("max_dim").unwrap();
assert_eq!(inner.state().get_input(eb_idx).as_u64(), 100);
assert_eq!(inner.state().get_input(md_idx).as_u64(), 256);
}
#[test]
fn sequential_inner_scopes_are_independent() {
let outer = compile_gk(r#"
input cycle: u64
seed := 42
"#).unwrap();
let inner1_program = compile_gk(r#"
input cycle: u64
extern seed: u64
h := hash(seed)
"#).unwrap().program().clone();
let mut inner1 = outer.subscope(GkMatter::builder().program(inner1_program).build().unwrap()).unwrap();
inner1.set_inputs(&[0]);
let v1 = inner1.pull("h").as_u64();
let inner2_program = compile_gk(r#"
input cycle: u64
extern seed: u64
h := hash(seed)
"#).unwrap().program().clone();
let mut inner2 = outer.subscope(GkMatter::builder().program(inner2_program).build().unwrap()).unwrap();
inner2.set_inputs(&[0]);
let v2 = inner2.pull("h").as_u64();
assert_eq!(v1, v2, "identical inner scopes with same outer should be deterministic");
}
#[test]
fn all_kernels_have_diagnostic_context() {
let src = r#"
input cycle: u64
h := hash(cycle)
"#;
let kernel = compile_gk(src).unwrap();
let source = kernel.program().source();
assert!(source.contains("hash(cycle)"), "source should be preserved");
}
#[test]
fn extern_kernel_has_source() {
let src = r#"
input cycle: u64
extern dim: u64
h := hash(dim)
"#;
let kernel = compile_gk(src).unwrap();
assert!(kernel.program().source().contains("extern dim"));
}
#[test]
fn extern_default_u64_literal() {
let kernel = compile_gk(r#"
input cycle: u64
extern counter: u64 = 42
"#).unwrap();
assert_eq!(kernel.lookup("counter").unwrap().as_u64(), 42);
}
#[test]
fn extern_default_u64_zero() {
let kernel = compile_gk(r#"
input cycle: u64
extern counter: u64 = 0
"#).unwrap();
assert_eq!(kernel.lookup("counter").unwrap().as_u64(), 0);
}
#[test]
fn extern_default_f64_float_literal() {
let kernel = compile_gk(r#"
input cycle: u64
extern temperature: f64 = 3.14
"#).unwrap();
assert_eq!(kernel.lookup("temperature").unwrap().as_f64(), 3.14);
}
#[test]
fn extern_default_f64_int_literal_widens() {
let kernel = compile_gk(r#"
input cycle: u64
extern threshold: f64 = 5
"#).unwrap();
assert_eq!(kernel.lookup("threshold").unwrap().as_f64(), 5.0);
}
#[test]
fn extern_default_string_literal() {
let kernel = compile_gk(r#"
input cycle: u64
extern name: String = "guest"
"#).unwrap();
match kernel.lookup("name").unwrap() {
Value::Str(s) => assert_eq!(&*s, "guest"),
other => panic!("expected Str, got {other:?}"),
}
}
#[test]
fn extern_default_no_default_starts_unset() {
let kernel = compile_gk(r#"
input cycle: u64
extern unset: u64
"#).unwrap();
assert!(kernel.lookup("unset").is_none(),
"unset extern should not resolve via lookup");
}
#[test]
fn extern_default_visible_through_passthrough_output() {
let kernel = compile_gk(r#"
input cycle: u64
extern budget: u64 = 100
"#).unwrap();
let v = kernel.lookup("budget").expect("budget should resolve");
assert_eq!(v.as_u64(), 100);
}
#[test]
fn extern_default_function_call_rejected() {
let err = compile_gk(r#"
input cycle: u64
extern x: u64 = hash(0)
"#).expect_err("function call default must error");
assert!(err.contains("extern 'x' default"),
"error should name the extern: {err}");
assert!(err.contains("literal"),
"error should explain that literals are required: {err}");
}
#[test]
fn extern_default_identifier_rejected() {
let err = compile_gk(r#"
input cycle: u64
extern x: u64 = somewhere
"#).expect_err("identifier default must error");
assert!(err.contains("extern 'x' default"), "error: {err}");
}
#[test]
fn extern_default_type_mismatch_string_for_u64_rejected() {
let err = compile_gk(r#"
input cycle: u64
extern n: u64 = "not a number"
"#).expect_err("string default for u64 port must error");
assert!(err.contains("extern 'n' default"), "error: {err}");
}
#[test]
fn extern_default_type_mismatch_float_for_u64_rejected() {
let err = compile_gk(r#"
input cycle: u64
extern n: u64 = 1.5
"#).expect_err("float default for u64 port must error");
assert!(err.contains("extern 'n' default"), "error: {err}");
}
#[test]
fn extern_default_negative_for_u64_rejected_with_clear_message() {
let err = compile_gk(r#"
input cycle: u64
extern n: u64 = -5
"#).expect_err("negative literal default for u64 must error");
assert!(err.contains("extern 'n' default"), "error: {err}");
}
#[test]
fn extern_default_bool_true_works() {
let kernel = compile_gk(r#"
input cycle: u64
extern enabled: bool = true
"#).unwrap();
match kernel.lookup("enabled").unwrap() {
Value::Bool(true) => {}
other => panic!("expected Bool(true), got {other:?}"),
}
}
#[test]
fn extern_default_bool_false_works() {
let kernel = compile_gk(r#"
input cycle: u64
extern enabled: bool = false
"#).unwrap();
match kernel.lookup("enabled").unwrap() {
Value::Bool(false) => {}
other => panic!("expected Bool(false), got {other:?}"),
}
}
#[test]
fn shared_init_compiles_to_slot_with_initial_value() {
let kernel = compile_gk(r#"
input cycle: u64
shared counter := 0
"#).unwrap();
assert_eq!(kernel.program().output_modifier("counter"),
BindingModifier::SHARED);
assert!(kernel.program().find_input("counter").is_some(),
"shared literal-init must create an input slot");
assert_eq!(kernel.lookup("counter").unwrap().as_u64(), 0);
}
#[test]
fn shared_inner_write_propagates_to_outer_via_cell() {
let outer = compile_gk(r#"
input cycle: u64
shared counter := 5
"#).unwrap();
let inner_program = compile_gk(r#"
input cycle: u64
extern counter: u64
"#).unwrap().program().clone();
let mut inner = outer.subscope(GkMatter::builder().program(inner_program).build().unwrap()).unwrap();
assert_eq!(inner.lookup("counter").unwrap().as_u64(), 5);
let inner_idx = inner.program().find_input("counter").unwrap();
inner.state().set_input(inner_idx, Value::U64(42));
assert_eq!(outer.lookup("counter").unwrap().as_u64(), 42,
"outer's cell-aware lookup must reflect inner's write");
}
#[test]
fn shared_two_inners_see_each_others_writes_via_cell() {
let outer = compile_gk(r#"
input cycle: u64
shared budget := 100
"#).unwrap();
let a_program = compile_gk(r#"
input cycle: u64
extern budget: u64
"#).unwrap().program().clone();
let b_program = compile_gk(r#"
input cycle: u64
extern budget: u64
"#).unwrap().program().clone();
let mut a = outer.subscope(GkMatter::builder().program(a_program).build().unwrap()).unwrap();
let b = outer.subscope(GkMatter::builder().program(b_program).build().unwrap()).unwrap();
assert_eq!(a.lookup("budget").unwrap().as_u64(), 100);
assert_eq!(b.lookup("budget").unwrap().as_u64(), 100);
let a_idx = a.program().find_input("budget").unwrap();
a.state().set_input(a_idx, Value::U64(99));
assert_eq!(b.lookup("budget").unwrap().as_u64(), 99,
"second inner kernel must see the first's write through the shared cell");
}
#[test]
fn shared_last_write_wins_under_concurrent_writers() {
let outer = compile_gk(r#"
input cycle: u64
shared status := "init"
"#).unwrap();
let a_program = compile_gk(r#"
input cycle: u64
extern status: String
"#).unwrap().program().clone();
let b_program = compile_gk(r#"
input cycle: u64
extern status: String
"#).unwrap().program().clone();
let mut a = outer.subscope(GkMatter::builder().program(a_program).build().unwrap()).unwrap();
let mut b = outer.subscope(GkMatter::builder().program(b_program).build().unwrap()).unwrap();
let a_idx = a.program().find_input("status").unwrap();
let b_idx = b.program().find_input("status").unwrap();
a.state().set_input(a_idx, Value::Str("from-a".into()));
b.state().set_input(b_idx, Value::Str("from-b".into()));
a.state().set_input(a_idx, Value::Str("from-a-again".into()));
let expected = Value::Str("from-a-again".into());
assert_eq!(a.lookup("status").unwrap(), expected);
assert_eq!(b.lookup("status").unwrap(), expected);
}
#[test]
fn shared_non_literal_init_rejected() {
let err = compile_gk(r#"
input cycle: u64
shared rolling := hash(cycle)
"#).expect_err("non-literal shared const must error");
assert!(err.contains("shared binding 'rolling'"), "error: {err}");
assert!(err.contains("literal initial value"), "error: {err}");
}
#[test]
fn const_with_unbound_interpolation_falls_through_to_outer() {
let outer = compile_gk(r#"
input cycle: u64
const X := "DEFAULT"
"#).unwrap();
let inner_program = compile_gk(r#"
input cycle: u64
extern Y: str
const X := "{Y}"
"#).unwrap().program().clone();
let inner = outer.subscope(
GkMatter::builder().program(inner_program).build().unwrap()
).unwrap();
assert_eq!(
inner.lookup("X").map(|v| v.as_str().to_string()),
Some("DEFAULT".to_string()),
"inner X should fall through to outer DEFAULT; got: {:?}",
inner.lookup("X"),
);
}
#[test]
fn three_scope_chain_transitive_fall_through() {
let workload = compile_gk(r#"
input cycle: u64
const X := "WORKLOAD_DEFAULT"
"#).unwrap();
let middle_program = compile_gk(r#"
input cycle: u64
extern undef_in_middle: str
const X := "{undef_in_middle}"
"#).unwrap().program().clone();
let middle = workload.subscope(
GkMatter::builder().program(middle_program).build().unwrap()
).unwrap();
assert_eq!(
middle.lookup("X").map(|v| v.as_str().to_string()),
Some("WORKLOAD_DEFAULT".to_string()),
"middle.lookup(X) should fall through to workload default",
);
let inner_program = compile_gk(r#"
input cycle: u64
extern X: str
"#).unwrap().program().clone();
let inner = middle.subscope(
GkMatter::builder().program(inner_program).build().unwrap()
).unwrap();
assert_eq!(
inner.lookup("X").map(|v| v.as_str().to_string()),
Some("WORKLOAD_DEFAULT".to_string()),
"inner.lookup(X) should reach workload default through middle's transparent shadow",
);
}
#[test]
fn pure_literal_const_does_not_auto_extern() {
let kernel = compile_gk(r#"
input cycle: u64
const x := 42
"#).unwrap();
assert!(kernel.program().find_input("x").is_none(),
"pure-literal const must not get an auto-extern input slot");
assert_eq!(kernel.get_constant("x").unwrap().as_u64(), 42);
}
#[test]
fn const_with_bound_interpolation_shadows_outer() {
let outer = compile_gk(r#"
input cycle: u64
const X := "DEFAULT"
const Y := "OVERRIDE"
"#).unwrap();
let inner_program = compile_gk(r#"
input cycle: u64
extern Y: str
const X := "{Y}"
"#).unwrap().program().clone();
let inner = outer.subscope(
GkMatter::builder().program(inner_program).build().unwrap()
).unwrap();
assert_eq!(
inner.lookup("X").map(|v| v.as_str().to_string()),
Some("OVERRIDE".to_string()),
);
}