#![allow(clippy::expect_used, clippy::panic, clippy::unwrap_used)]
use zerodds_ast::Ast;
use zerodds_lang_rules::adapter::c::c_items;
use zerodds_lang_rules::rule;
use zerodds_lang_rules::{ModelGraph, apply};
fn declarations(idl: &str) -> String {
let parsed = zerodds_idl_parse::parse(idl);
assert!(
parsed.diagnostics().is_empty(),
"{:?}",
parsed.diagnostics()
);
let lowered = zerodds_idl_lower::lower(&parsed.syntax(), "test.idl");
assert!(lowered.diagnostics.is_empty(), "{:?}", lowered.diagnostics);
let mut ast: Ast = lowered.ast;
ast.resolve_all_by_name().expect("resolve");
let graph = ModelGraph::of(&ast);
let applied = apply(&graph, &rule::c::catalogue()).expect("apply");
let decls = c_items(&ast, &applied).expect("materialise");
zerodds_lang_render::c::file(&zerodds_lang_cst::c::File {
doc: zerodds_lang_cst::common::Doc::none(),
guard: None,
includes: Vec::new(),
decls,
})
}
const SAMPLE: &str = "module m { struct Inner { long a; }; \
@final struct Sample { @external Inner nested; @external long a; long b; }; };";
#[test]
fn an_aggregate_with_an_external_member_gets_the_four_functions() {
let generated = declarations(SAMPLE);
for function in [
"zd_init_m_Sample",
"zd_fini_m_Sample",
"zd_copy_m_Sample",
"zd_copy_with_m_Sample",
] {
assert!(generated.contains(function), "{function}:\n{generated}");
}
assert!(!generated.contains("zd_init_m_Inner"), "{generated}");
}
#[test]
fn the_initialiser_sets_the_external_pointers_to_null() {
let generated = declarations(SAMPLE);
assert!(generated.contains("v->nested = NULL;"), "{generated}");
assert!(generated.contains("v->a = NULL;"), "{generated}");
}
#[test]
fn the_destructor_deletes_what_a_non_null_pointer_references() {
let generated = declarations(SAMPLE);
assert!(
generated.contains("if (v->nested != NULL) {"),
"{generated}"
);
assert!(generated.contains("free(v->nested);"), "{generated}");
assert!(generated.contains("free(v->a);"), "{generated}");
}
#[test]
fn the_copy_allocates_a_null_destination_and_fills_it() {
let generated = declarations(SAMPLE);
assert!(
generated.contains("dst->nested = (m_Inner *)malloc(sizeof(m_Inner));"),
"{generated}"
);
assert!(
generated.contains("(*dst->nested) = (*src->nested);"),
"{generated}"
);
assert!(generated.contains("return false;"), "{generated}");
}
#[test]
fn the_copy_has_the_modes_of_table_45() {
let generated = declarations(SAMPLE);
for name in [
"ZD_COPY_SHALLOW",
"ZD_COPY_DEEP",
"ZD_COPY_REUSE",
"ZD_COPY_REPLACE",
"ZD_COPY_RELEASE",
] {
assert!(generated.contains(name), "{name}:\n{generated}");
}
assert!(
generated.contains("ZD_COPY_DEEP, ZD_COPY_REUSE"),
"{generated}"
);
}
#[test]
fn an_aggregate_that_holds_one_inline_calls_the_functions_of_it() {
let generated = declarations(
"module m { struct Inner { @external long a; }; \
@final struct Outer { Inner held; long z; }; };",
);
assert!(generated.contains("zd_init_m_Outer"), "{generated}");
assert!(
generated.contains("zd_init_m_Inner(&v->held);"),
"{generated}"
);
assert!(
generated.contains("zd_fini_m_Inner(&v->held);"),
"{generated}"
);
}
#[test]
fn an_external_branch_of_a_union_is_released_by_the_live_discriminator() {
let generated = declarations(
"module m { struct Inner { long a; }; \
union Choice switch (long) { case 1: @external Inner nested; case 2: long plain; }; };",
);
assert!(generated.contains("zd_fini_m_Choice"), "{generated}");
assert!(generated.contains("v->_d == (int32_t)1"), "{generated}");
assert!(generated.contains("free(v->_u.nested);"), "{generated}");
}
#[test]
fn a_model_without_an_external_member_gets_nothing() {
let generated = declarations("module m { struct Plain { long a; string s; }; };");
assert!(!generated.contains("ZD_LIFECYCLE_DEFINED"), "{generated}");
assert!(!generated.contains("zd_init_"), "{generated}");
assert!(!generated.contains("#include <stdlib.h>"), "{generated}");
}
#[test]
fn an_external_string_is_a_pointer_the_initialiser_clears_and_nothing_releases() {
let generated =
declarations("module m { @final struct Holder { @external string s; long z; }; };");
assert!(generated.contains("zd_init_m_Holder"), "{generated}");
assert!(generated.contains("v->s = NULL;"), "{generated}");
assert!(!generated.contains("free(v->s);"), "{generated}");
}
#[test]
fn an_optional_member_is_a_pointer_the_functions_initialise_release_and_copy() {
let generated = declarations(
"module m { @final struct O { @optional long a; @optional string s; long z; }; };",
);
assert!(generated.contains("zd_init_m_O"), "{generated}");
assert!(generated.contains("v->a = NULL;"), "{generated}");
assert!(generated.contains("v->s = NULL;"), "{generated}");
assert!(generated.contains("free(v->a);"), "{generated}");
assert!(!generated.contains("free(v->s);"), "{generated}");
assert!(
generated.contains("dst->a = (int32_t *)malloc(sizeof(int32_t));"),
"{generated}"
);
assert!(generated.contains("dst->s = src->s;"), "{generated}");
}