Skip to content

Commit 325c6d9

Browse files
committed
fix(tests): add test coverage for vulkan generation
Signed-off-by: Steven Noonan <steven@uplinklabs.net>
1 parent d9cebe2 commit 325c6d9

4 files changed

Lines changed: 963 additions & 1 deletion

File tree

src/resolve.rs

Lines changed: 301 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ pub struct EnumGroup {
164164
pub values: Vec<FlatEnum>,
165165
}
166166

167-
#[derive(Debug, Serialize, Clone, Copy)]
167+
#[derive(Debug, Serialize, Clone, Copy, PartialEq, Eq)]
168168
pub struct PfnRange {
169169
/// Index into featArray or extArray.
170170
pub extension: u16,
@@ -2045,4 +2045,304 @@ mod tests {
20452045
// glX — capital X matters for generated member names.
20462046
assert_eq!(api_name_prefix("glx"), "glX");
20472047
}
2048+
2049+
// ---- Protection lattice ----
2050+
2051+
#[test]
2052+
fn protection_guarded_absorbs_unconditional() {
2053+
// A guarded state that encounters an unprotected extension becomes
2054+
// unconditional (absorbing element of the lattice).
2055+
let mut p = Protection::Guarded(vec!["VK_USE_PLATFORM_WIN32_KHR".to_string()]);
2056+
p.add_extension(&[]); // unprotected extension
2057+
assert!(p.is_unconditional());
2058+
}
2059+
2060+
#[test]
2061+
fn protection_unconditional_stays_unconditional() {
2062+
// Once unconditional, adding a guarded extension can't re-guard it.
2063+
let mut p = Protection::Unconditional;
2064+
p.add_extension(&["VK_USE_PLATFORM_XLIB_KHR".to_string()]);
2065+
assert!(p.is_unconditional());
2066+
}
2067+
2068+
#[test]
2069+
fn protection_guarded_unions_guards() {
2070+
let mut p = Protection::new_guarded();
2071+
p.add_extension(&["VK_USE_PLATFORM_WIN32_KHR".to_string()]);
2072+
p.add_extension(&["VK_USE_PLATFORM_XLIB_KHR".to_string()]);
2073+
let v = p.into_vec();
2074+
assert_eq!(v.len(), 2);
2075+
// into_vec sorts
2076+
assert_eq!(v[0], "VK_USE_PLATFORM_WIN32_KHR");
2077+
assert_eq!(v[1], "VK_USE_PLATFORM_XLIB_KHR");
2078+
}
2079+
2080+
#[test]
2081+
fn protection_guarded_deduplicates() {
2082+
let mut p = Protection::new_guarded();
2083+
p.add_extension(&["VK_USE_PLATFORM_WIN32_KHR".to_string()]);
2084+
p.add_extension(&["VK_USE_PLATFORM_WIN32_KHR".to_string()]);
2085+
let v = p.into_vec();
2086+
assert_eq!(v.len(), 1);
2087+
}
2088+
2089+
#[test]
2090+
fn protection_empty_guarded_into_vec_is_empty() {
2091+
// A Guarded with no guards is treated as unconditional (empty Vec).
2092+
let p = Protection::new_guarded();
2093+
assert!(p.into_vec().is_empty());
2094+
}
2095+
2096+
#[test]
2097+
fn protection_unconditional_into_vec_is_empty() {
2098+
let p = Protection::Unconditional;
2099+
assert!(p.into_vec().is_empty());
2100+
}
2101+
2102+
// ---- indices_to_ranges ----
2103+
2104+
#[test]
2105+
fn indices_to_ranges_empty() {
2106+
assert!(indices_to_ranges(0, &[]).is_empty());
2107+
}
2108+
2109+
#[test]
2110+
fn indices_to_ranges_single_element() {
2111+
let r = indices_to_ranges(5, &[42]);
2112+
assert_eq!(
2113+
r,
2114+
vec![PfnRange {
2115+
extension: 5,
2116+
start: 42,
2117+
count: 1
2118+
}]
2119+
);
2120+
}
2121+
2122+
#[test]
2123+
fn indices_to_ranges_fully_contiguous() {
2124+
// A contiguous run should produce a single range.
2125+
let r = indices_to_ranges(0, &[10, 11, 12, 13, 14]);
2126+
assert_eq!(
2127+
r,
2128+
vec![PfnRange {
2129+
extension: 0,
2130+
start: 10,
2131+
count: 5
2132+
}]
2133+
);
2134+
}
2135+
2136+
#[test]
2137+
fn indices_to_ranges_single_gap() {
2138+
// [3, 4, 5, 10, 11] → two ranges.
2139+
let r = indices_to_ranges(1, &[3, 4, 5, 10, 11]);
2140+
assert_eq!(
2141+
r,
2142+
vec![
2143+
PfnRange {
2144+
extension: 1,
2145+
start: 3,
2146+
count: 3
2147+
},
2148+
PfnRange {
2149+
extension: 1,
2150+
start: 10,
2151+
count: 2
2152+
},
2153+
]
2154+
);
2155+
}
2156+
2157+
#[test]
2158+
fn indices_to_ranges_all_disjoint() {
2159+
// No contiguous pairs → one range per element.
2160+
let r = indices_to_ranges(2, &[0, 5, 10]);
2161+
assert_eq!(
2162+
r,
2163+
vec![
2164+
PfnRange {
2165+
extension: 2,
2166+
start: 0,
2167+
count: 1
2168+
},
2169+
PfnRange {
2170+
extension: 2,
2171+
start: 5,
2172+
count: 1
2173+
},
2174+
PfnRange {
2175+
extension: 2,
2176+
start: 10,
2177+
count: 1
2178+
},
2179+
]
2180+
);
2181+
}
2182+
2183+
#[test]
2184+
fn indices_to_ranges_multiple_gaps() {
2185+
let r = indices_to_ranges(0, &[1, 2, 5, 6, 7, 20]);
2186+
assert_eq!(
2187+
r,
2188+
vec![
2189+
PfnRange {
2190+
extension: 0,
2191+
start: 1,
2192+
count: 2
2193+
},
2194+
PfnRange {
2195+
extension: 0,
2196+
start: 5,
2197+
count: 3
2198+
},
2199+
PfnRange {
2200+
extension: 0,
2201+
start: 20,
2202+
count: 1
2203+
},
2204+
]
2205+
);
2206+
}
2207+
2208+
// ---- ext_short_name / version_short_name ----
2209+
2210+
#[test]
2211+
fn ext_short_name_strips_gl_prefix() {
2212+
assert_eq!(ext_short_name("GL_ARB_sync"), "ARB_sync");
2213+
}
2214+
2215+
#[test]
2216+
fn ext_short_name_strips_vk_prefix() {
2217+
assert_eq!(ext_short_name("VK_KHR_swapchain"), "KHR_swapchain");
2218+
}
2219+
2220+
#[test]
2221+
fn ext_short_name_strips_egl_prefix() {
2222+
assert_eq!(
2223+
ext_short_name("EGL_KHR_platform_wayland"),
2224+
"KHR_platform_wayland"
2225+
);
2226+
}
2227+
2228+
#[test]
2229+
fn ext_short_name_unknown_prefix_unchanged() {
2230+
assert_eq!(ext_short_name("UNKNOWN_foo_bar"), "UNKNOWN_foo_bar");
2231+
}
2232+
2233+
#[test]
2234+
fn version_short_name_gl() {
2235+
assert_eq!(version_short_name("GL_VERSION_3_3", "gl"), "VERSION_3_3");
2236+
}
2237+
2238+
#[test]
2239+
fn version_short_name_gles2() {
2240+
// GLES uses "GL_" prefix in the XML feature name.
2241+
assert_eq!(
2242+
version_short_name("GL_ES_VERSION_3_0", "gles2"),
2243+
"ES_VERSION_3_0"
2244+
);
2245+
}
2246+
2247+
#[test]
2248+
fn version_short_name_vk() {
2249+
assert_eq!(version_short_name("VK_VERSION_1_3", "vk"), "VERSION_1_3");
2250+
}
2251+
2252+
#[test]
2253+
fn version_short_name_unknown_api_no_strip() {
2254+
assert_eq!(
2255+
version_short_name("CUSTOM_VERSION_1_0", "custom"),
2256+
"CUSTOM_VERSION_1_0"
2257+
);
2258+
}
2259+
2260+
// ---- is_gl_auto_excluded ----
2261+
2262+
#[test]
2263+
fn gl_auto_excluded_known_names() {
2264+
assert!(is_gl_auto_excluded("stddef"));
2265+
assert!(is_gl_auto_excluded("khrplatform"));
2266+
assert!(is_gl_auto_excluded("inttypes"));
2267+
}
2268+
2269+
#[test]
2270+
fn gl_auto_excluded_other_names() {
2271+
assert!(!is_gl_auto_excluded("GLuint"));
2272+
assert!(!is_gl_auto_excluded("GLenum"));
2273+
}
2274+
2275+
// ---- topo_sort_typedefs: cycle fallback ----
2276+
2277+
#[test]
2278+
fn topo_sort_typedefs_simple_dependency_order() {
2279+
// B depends on A (A appears in B's raw_c), so A should come first.
2280+
let types = vec![
2281+
TypeDef {
2282+
name: "B".to_string(),
2283+
raw_c: "typedef struct { A member; } B;".to_string(),
2284+
category: "struct".to_string(),
2285+
protect: vec![],
2286+
},
2287+
TypeDef {
2288+
name: "A".to_string(),
2289+
raw_c: "typedef struct { int x; } A;".to_string(),
2290+
category: "struct".to_string(),
2291+
protect: vec![],
2292+
},
2293+
];
2294+
let sorted = topo_sort_typedefs(types);
2295+
let a_pos = sorted.iter().position(|t| t.name == "A").unwrap();
2296+
let b_pos = sorted.iter().position(|t| t.name == "B").unwrap();
2297+
assert!(a_pos < b_pos, "A must precede B");
2298+
}
2299+
2300+
#[test]
2301+
fn topo_sort_typedefs_cycle_does_not_panic() {
2302+
// A references B, B references A — a cycle. The fallback path
2303+
// must produce *some* valid output without panicking.
2304+
let types = vec![
2305+
TypeDef {
2306+
name: "A".to_string(),
2307+
raw_c: "typedef struct { B* ptr; } A;".to_string(),
2308+
category: "struct".to_string(),
2309+
protect: vec![],
2310+
},
2311+
TypeDef {
2312+
name: "B".to_string(),
2313+
raw_c: "typedef struct { A* ptr; } B;".to_string(),
2314+
category: "struct".to_string(),
2315+
protect: vec![],
2316+
},
2317+
];
2318+
let sorted = topo_sort_typedefs(types);
2319+
// Both must appear exactly once.
2320+
assert_eq!(sorted.len(), 2);
2321+
assert!(sorted.iter().any(|t| t.name == "A"));
2322+
assert!(sorted.iter().any(|t| t.name == "B"));
2323+
}
2324+
2325+
#[test]
2326+
fn topo_sort_typedefs_non_scannable_categories_ignored() {
2327+
// "define" category bodies are not scanned for deps, so even though
2328+
// D's raw_c mentions C, no edge is created and insertion order is kept.
2329+
let types = vec![
2330+
TypeDef {
2331+
name: "D".to_string(),
2332+
raw_c: "#define D C".to_string(),
2333+
category: "define".to_string(),
2334+
protect: vec![],
2335+
},
2336+
TypeDef {
2337+
name: "C".to_string(),
2338+
raw_c: "typedef int C;".to_string(),
2339+
category: "basetype".to_string(),
2340+
protect: vec![],
2341+
},
2342+
];
2343+
let sorted = topo_sort_typedefs(types);
2344+
// No dep edge was created, so original order is preserved.
2345+
assert_eq!(sorted[0].name, "D");
2346+
assert_eq!(sorted[1].name, "C");
2347+
}
20482348
}

0 commit comments

Comments
 (0)