use super::*;
const BINDING_CRATE: &str = "demo-swift";
const RAW_CORE_H: &str = "#include <stdint.h>\n\
#include <stdbool.h>\n\
typedef struct RustStr { uint8_t* const start; uintptr_t len; } RustStr;\n\
typedef struct __private__FfiSlice { void* const start; uintptr_t len; } __private__FfiSlice;\n\
void* __swift_bridge__null_pointer(void);\n\
\n\
typedef struct __private__OptionU8 { uint8_t val; bool is_some; } __private__OptionU8;\n\
typedef struct __private__OptionBool { bool val; bool is_some; } __private__OptionBool;\n";
const RAW_CRATE_H: &str = "void* __swift_bridge__$Vec_Widget$new();\n\
void __swift_bridge__$Vec_Widget$_free(void* const vec);\n\
__private__OptionU8 __swift_bridge__$Vec_Widget$pop(void* const vec);\n\
struct RustStr __swift_bridge__$Widget$label(void* self);\n\
__private__OptionBool __swift_bridge__$Widget$enabled(void* self);\n";
const FORMATTED_UMBRELLA_HEADER: &str = r#"#ifndef RUST_BRIDGE_C_H
#define RUST_BRIDGE_C_H
// Auto-generated by alef — do not edit by hand.
// alef:hash:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
// Concatenates SwiftBridgeCore.h and demo-swift.h produced by
// `cargo build -p demo-swift` via swift_bridge_build.
#include <stdbool.h>
#include <stdint.h>
typedef struct RustStr {
uint8_t *const start;
uintptr_t len;
} RustStr;
typedef struct __private__FfiSlice {
void *const start;
uintptr_t len;
} __private__FfiSlice;
void *__swift_bridge__null_pointer(void);
typedef struct __private__OptionU8 {
uint8_t val;
bool is_some;
} __private__OptionU8;
typedef struct __private__OptionBool {
bool val;
bool is_some;
} __private__OptionBool;
void *__swift_bridge__$Vec_Widget$new();
void __swift_bridge__$Vec_Widget$_free(void *const vec);
__private__OptionU8 __swift_bridge__$Vec_Widget$pop(void *const vec);
struct RustStr __swift_bridge__$Widget$label(void *self);
__private__OptionBool __swift_bridge__$Widget$enabled(void *self);
#endif /* RUST_BRIDGE_C_H */
"#;
fn raw_umbrella_header() -> String {
assemble(BINDING_CRATE, RAW_CORE_H, RAW_CRATE_H)
}
#[test]
fn raw_and_formatted_umbrella_headers_declare_the_same_c() {
assert!(
declares_the_same_c(&raw_umbrella_header(), FORMATTED_UMBRELLA_HEADER),
"clang-format's rewrite of the same swift-bridge output must not read as a change"
);
}
#[test]
fn a_dropped_declaration_is_not_the_same_c() {
let truncated = FORMATTED_UMBRELLA_HEADER.replace(
"__private__OptionBool __swift_bridge__$Widget$enabled(void *self);\n",
"",
);
assert_ne!(
truncated, FORMATTED_UMBRELLA_HEADER,
"sanity: the replace must have removed a line"
);
assert!(
!declares_the_same_c(&raw_umbrella_header(), &truncated),
"a header missing a __swift_bridge__$ declaration must not compare equal"
);
}
#[test]
fn a_dropped_core_typedef_is_not_the_same_c() {
let without_core = FORMATTED_UMBRELLA_HEADER.replace(
"typedef struct __private__FfiSlice {\n void *const start;\n uintptr_t len;\n} __private__FfiSlice;\n",
"",
);
assert_ne!(
without_core, FORMATTED_UMBRELLA_HEADER,
"sanity: the replace must have removed the typedef"
);
assert!(
!declares_the_same_c(&raw_umbrella_header(), &without_core),
"a header missing a core typedef must not compare equal"
);
}
#[test]
fn a_swapped_field_type_is_not_the_same_c() {
let swapped = FORMATTED_UMBRELLA_HEADER.replace(
"typedef struct __private__OptionU8 {\n uint8_t val;\n bool is_some;\n}",
"typedef struct __private__OptionU8 {\n bool val;\n uint8_t is_some;\n}",
);
assert_ne!(
swapped, FORMATTED_UMBRELLA_HEADER,
"sanity: the replace must have swapped the field types"
);
assert!(
!declares_the_same_c(&raw_umbrella_header(), &swapped),
"reusing the same tokens in a different order within a declaration is a real change"
);
}
#[test]
fn a_complete_assembly_has_no_undefined_core_types() {
assert_eq!(undefined_core_types(&raw_umbrella_header()), Vec::<String>::new());
assert_eq!(undefined_core_types(FORMATTED_UMBRELLA_HEADER), Vec::<String>::new());
}
#[test]
fn an_empty_core_header_leaves_core_types_undefined() {
let partial = assemble(BINDING_CRATE, "", RAW_CRATE_H);
assert!(
is_populated(&partial),
"sanity: the partial header still carries real declarations"
);
assert_eq!(
undefined_core_types(&partial),
vec![
"RustStr".to_string(),
"__private__OptionBool".to_string(),
"__private__OptionU8".to_string(),
]
);
}
#[test]
fn a_binding_crate_using_no_core_types_is_not_flagged() {
let plain = assemble(BINDING_CRATE, "", "void __swift_bridge__$Widget$_free(void* self);\n");
assert_eq!(
undefined_core_types(&plain),
Vec::<String>::new(),
"core types no declaration references must not be demanded"
);
}
#[test]
fn resolve_fresh_preserves_a_committed_header_declaring_the_same_c() {
let resolved = resolve_fresh(BINDING_CRATE, RAW_CORE_H, RAW_CRATE_H, Some(FORMATTED_UMBRELLA_HEADER))
.expect("a complete assembly must resolve");
assert_eq!(
resolved, FORMATTED_UMBRELLA_HEADER,
"the committed header must survive byte-for-byte when its C is unchanged"
);
}
#[test]
fn resolve_fresh_rewrites_when_the_declarations_change() {
let extended = format!("{RAW_CRATE_H}void __swift_bridge__$Widget$reset(void* self);\n");
let resolved = resolve_fresh(BINDING_CRATE, RAW_CORE_H, &extended, Some(FORMATTED_UMBRELLA_HEADER))
.expect("a complete assembly must resolve");
assert!(
resolved.contains("__swift_bridge__$Widget$reset"),
"a genuinely new declaration must reach the header; got:\n{resolved}"
);
}
#[test]
fn resolve_fresh_keeps_the_committed_header_when_the_assembly_is_partial() {
let resolved = resolve_fresh(BINDING_CRATE, "", RAW_CRATE_H, Some(FORMATTED_UMBRELLA_HEADER))
.expect("a partial assembly with a committed fallback must not fail the run");
assert_eq!(
resolved, FORMATTED_UMBRELLA_HEADER,
"a partial assembly must never replace a populated committed header"
);
}
#[test]
fn resolve_fresh_refuses_a_partial_assembly_with_no_committed_fallback() {
let error = resolve_fresh(BINDING_CRATE, "", RAW_CRATE_H, None)
.expect_err("a partial assembly with nothing to fall back on must fail loudly");
let message = format!("{error:#}");
assert!(
message.contains("refusing to write an incomplete RustBridgeC.h"),
"the failure must name what it refused; got: {message}"
);
assert!(
message.contains("RustStr"),
"the failure must name the undefined type so the cause is actionable; got: {message}"
);
}
#[test]
fn resolve_fresh_does_not_treat_a_placeholder_as_a_committed_fallback() {
let placeholder = "#ifndef RUST_BRIDGE_C_H\n#define RUST_BRIDGE_C_H\ntypedef struct RustStr { uint8_t* const start; uintptr_t len; } RustStr;\n#endif\n";
assert!(
!is_populated(placeholder),
"sanity: the placeholder must not look populated"
);
resolve_fresh(BINDING_CRATE, "", RAW_CRATE_H, Some(placeholder))
.expect_err("a placeholder must not absorb a partial assembly");
}
#[test]
fn a_plain_struct_tag_definition_counts_as_defining_the_type() {
let core = "struct __private__ResultPtrAndPtr {\n bool is_ok;\n void *ok_or_err;\n};\n";
let crate_h = "struct __private__ResultPtrAndPtr __swift_bridge__$Widget$parse(void *self);\n";
assert_eq!(
undefined_core_types(&assemble(BINDING_CRATE, core, crate_h)),
Vec::<String>::new(),
"a plain `struct NAME {{ .. }};` definition must satisfy references to NAME"
);
assert_eq!(
undefined_core_types(&assemble(BINDING_CRATE, "", crate_h)),
vec!["__private__ResultPtrAndPtr".to_string()],
"without the definition the same reference must be reported"
);
}