#![allow(
clippy::expect_used,
clippy::unwrap_used,
clippy::panic,
clippy::print_stderr,
clippy::print_stdout,
clippy::field_reassign_with_default,
clippy::manual_flatten,
clippy::collapsible_if,
clippy::empty_line_after_doc_comments,
clippy::uninlined_format_args,
clippy::drop_non_drop,
missing_docs
)]
use zerodds_idl::config::ParserConfig;
use zerodds_idl_java::{JavaGenOptions, generate_java_files};
fn gen_java_concat(src: &str) -> String {
let opts = JavaGenOptions {
emit_typesupport: false,
..Default::default()
};
let ast = zerodds_idl::parse(src, &ParserConfig::default()).expect("parse");
let files = generate_java_files(&ast, &opts).expect("gen");
let mut out = String::new();
for f in files {
out.push_str(&f.source);
out.push('\n');
}
out
}
#[test]
fn module_becomes_lowercase_package_path() {
let java = gen_java_concat("module MyMod { struct S { long x; }; };");
assert!(java.contains("package mymod") || java.contains("S"));
}
#[test]
fn pascal_case_for_class_names() {
let java = gen_java_concat("struct my_struct { long x; };");
assert!(java.contains("MyStruct") || java.contains("my_struct"));
}
#[test]
fn camel_case_for_member_accessors() {
let java = gen_java_concat("struct S { long my_field; };");
assert!(java.contains("getMyField") || java.contains("myField") || java.contains("my_field"));
}
#[test]
fn all_uppercase_for_constants_via_idl_default() {
let java = gen_java_concat("const long MAX_SIZE = 100;");
assert!(java.contains("MAX_SIZE"));
}
#[test]
fn out_param_uses_holder_pattern_in_service_interface() {
let java = gen_java_concat(
r#"
@service
interface Calc {
void compute(in long a, out long result);
};
"#,
);
assert!(
java.contains("Holder") || java.contains("result"),
"Holder oder out-param fehlt:\n{java}"
);
}
#[test]
fn standalone_constant_emits_holder_class() {
let java = gen_java_concat("const long MAX = 100;");
assert!(java.contains("MAX"));
assert!(java.contains("100"));
}
#[test]
fn unbounded_sequence_emits_list_or_typed_seq() {
let java = gen_java_concat("struct S { sequence<long> values; };");
assert!(java.contains("List") || java.contains("Seq"));
}
#[test]
fn bounded_sequence_keeps_bound_marker() {
let java = gen_java_concat("struct S { sequence<long, 10> values; };");
assert!(
java.contains("List") || java.contains("Bounded") || java.contains("10"),
"bound-marker fehlt:\n{java}"
);
}
#[test]
fn string_member_uses_java_lang_string() {
let java = gen_java_concat("struct S { string name; };");
assert!(java.contains("String"));
}
#[test]
fn wstring_member_uses_java_lang_string() {
let java = gen_java_concat("struct S { wstring name; };");
assert!(java.contains("String"));
}
#[test]
fn struct_emits_public_class() {
let java = gen_java_concat("struct S { long x; };");
assert!(java.contains("class S") || java.contains("record S"));
}
#[test]
fn union_emits_class_with_discriminator() {
let java = gen_java_concat(
r#"
union U switch (long) {
case 1: long a;
case 2: float b;
};
"#,
);
assert!(java.contains("U"), "union U fehlt:\n{java}");
}
#[test]
fn enum_emits_java_enum() {
let java = gen_java_concat("enum Color { RED, GREEN, BLUE };");
assert!(java.contains("enum Color"));
}
#[test]
fn array_member_uses_java_array_or_list() {
let java = gen_java_concat("struct S { long values[3]; };");
assert!(java.contains("[]") || java.contains("List") || java.contains("values"));
}
#[test]
fn typedef_emits_alias_class_or_inline() {
let java = gen_java_concat("typedef long MyLong;");
assert!(java.contains("MyLong"));
}
#[test]
fn union_with_octet_discriminator_supported() {
let java = gen_java_concat(
r#"
union U switch (octet) {
case 1: long a;
};
"#,
);
assert!(java.contains("U"));
}
#[test]
fn idl_map_emits_java_map() {
let parse = zerodds_idl::parse(
"struct S { map<string, long> entries; };",
&ParserConfig::default(),
);
if let Ok(ast) = parse {
let res = generate_java_files(&ast, &JavaGenOptions::default());
if let Ok(files) = res {
let out: String = files.iter().map(|f| f.source.as_str()).collect();
assert!(
out.contains("Map") || out.contains("entries"),
"map-mapping fehlt:\n{out}"
);
}
}
}
#[test]
fn verbatim_annotation_with_java_language_inlines_text() {
let java = gen_java_concat(
r#"
@verbatim(language="java", placement=BEFORE_DECLARATION, text="// pre-decl marker")
struct PlainStruct { long x; };
"#,
);
assert!(java.contains("PlainStruct"));
assert!(
java.contains("// pre-decl marker"),
"@verbatim BEFORE_DECLARATION fehlt:\n{java}"
);
let pos_marker = java.find("// pre-decl marker").unwrap_or(usize::MAX);
let pos_class = java.find("public class PlainStruct").unwrap_or(usize::MAX);
assert!(
pos_marker < pos_class,
"Marker muss vor `public class` stehen:\n{java}"
);
}
#[test]
fn verbatim_annotation_with_after_declaration_placement() {
let java = gen_java_concat(
r#"
@verbatim(language="java", placement=AFTER_DECLARATION, text="// trailer")
struct S { long x; };
"#,
);
let pos_close = java.rfind('}').unwrap_or(0);
let pos_marker = java.find("// trailer").unwrap_or(usize::MAX);
assert!(
pos_marker != usize::MAX && pos_marker > pos_close,
"AFTER_DECLARATION verbatim muss nach Class-Block stehen:\n{java}"
);
}
#[test]
fn non_service_interface_emits_java_interface() {
let java = gen_java_concat(
r#"
interface Calc {
long add(in long a, in long b);
readonly attribute long version;
};
"#,
);
assert!(
java.contains("public interface Calc"),
"Java interface fehlt:\n{java}"
);
assert!(java.contains("get_version"));
}
#[test]
fn any_member_emits_java_object() {
let java = gen_java_concat(r#"struct M { any value; };"#);
assert!(
java.contains("Object"),
"Object-Mapping fuer any fehlt:\n{java}"
);
}
#[test]
fn valuetype_emits_two_classes_abstract_and_concrete() {
let parse = zerodds_idl::parse(
r#"valuetype VT { public long x; };"#,
&ParserConfig::default(),
);
if let Ok(ast) = parse {
let files = generate_java_files(&ast, &JavaGenOptions::default()).expect("ok");
let combined: String = files.iter().map(|f| f.source.clone()).collect();
assert!(
combined.contains("public abstract class VTAbstract"),
"VTAbstract fehlt:\n{combined}"
);
assert!(combined.contains("public class VT extends VTAbstract"));
assert!(combined.contains("get_x"));
assert!(combined.contains("set_x"));
}
}
#[test]
fn valuetype_private_state_emits_protected_accessor() {
let parse = zerodds_idl::parse(
r#"valuetype VT { private long secret; };"#,
&ParserConfig::default(),
);
if let Ok(ast) = parse {
let files = generate_java_files(&ast, &JavaGenOptions::default()).expect("ok");
let combined: String = files.iter().map(|f| f.source.clone()).collect();
assert!(
combined.contains("protected abstract"),
"private->protected:\n{combined}"
);
}
}
#[test]
fn valuetype_factory_emits_void_method() {
let parse = zerodds_idl::parse(
r#"valuetype VT { public long x; factory create(in long x); };"#,
&ParserConfig::default(),
);
if let Ok(ast) = parse {
let files = generate_java_files(&ast, &JavaGenOptions::default()).expect("ok");
let combined: String = files.iter().map(|f| f.source.clone()).collect();
assert!(
combined.contains("public abstract void create"),
"factory->void:\n{combined}"
);
}
}
#[test]
fn fixed_member_emits_java_bigdecimal() {
let java = gen_java_concat(r#"struct M { fixed<10,2> price; };"#);
assert!(
java.contains("java.math.BigDecimal"),
"BigDecimal-Mapping fehlt:\n{java}"
);
}
#[test]
fn shared_member_emits_shared_annotation() {
let java = gen_java_concat(
r#"
struct WithShared {
@shared long ptr;
};
"#,
);
assert!(
java.contains("@org.zerodds.types.Shared"),
"@Shared-Annotation fehlt:\n{java}"
);
}
#[test]
fn verbatim_annotation_other_language_skipped_in_java() {
let java = gen_java_concat(
r#"
@verbatim(language="c++", placement=BEFORE_DECLARATION, text="// cpp only")
struct S { long x; };
"#,
);
assert!(
!java.contains("// cpp only"),
"C++-verbatim darf nicht in Java-Output:\n{java}"
);
}
#[test]
fn fixed_returns_unsupported_or_parse_error() {
let parse = zerodds_idl::parse("struct S { fixed<5,2> price; };", &ParserConfig::default());
if let Ok(ast) = parse {
let res = generate_java_files(&ast, &JavaGenOptions::default());
let _ = res; }
}
#[test]
fn any_returns_object_or_parse_error() {
let parse = zerodds_idl::parse("struct S { any value; };", &ParserConfig::default());
if let Ok(ast) = parse {
let files = generate_java_files(&ast, &JavaGenOptions::default()).expect("ok");
let combined: String = files.iter().map(|f| f.source.clone()).collect();
assert!(combined.contains("Object"));
}
}