From b627ac3940de64f1a9e20d6222de319dcf8b600e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20Mondaini=20Calv=C3=A3o?= Date: Fri, 19 Jun 2026 09:07:58 -0300 Subject: [PATCH] =?UTF-8?q?fix(tests):=20recursively=20collect=20nested=20?= =?UTF-8?q?zspec=20tests=20=E2=80=94=2090=20dead=20tests=20now=20run=20(#1?= =?UTF-8?q?4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tests/root.zig used std.testing.refAllDecls, which is one level deep: it referenced the imported test files but NOT the doubly-nested `pub const FooSpec = struct { pub const group = struct { test ... } }` zspec groups. Under Zig 0.16 a test block is only collected once its container is analyzed, so every nested spec test was silently never compiled OR run — `zig build test` reported only the single root-level #13 regression test and passed even with failing assertions. Add a local refAllDeclsRecursive (Zig 0.16 dropped the std one) so every nested group is analyzed and its tests collected. This surfaced that the dead tests were masking 0.16-stale code in the library itself: * src/a_star.zig — PriorityQueue moved to the unmanaged API (init->initContext, add->push(allocator), removeOrNull->pop, deinit(allocator)) * src/z_index_buckets.zig — ArrayListUnmanaged init {} -> .empty * tests/floyd_warshall_optimized_test.zig — same ArrayListUnmanaged init Result: 1 -> 91 tests run, all green; force-failing any nested test now fails `zig build test`. version 0.7.2 -> 0.7.3. --- build.zig.zon | 2 +- src/a_star.zig | 10 +++++----- src/z_index_buckets.zig | 2 +- tests/floyd_warshall_optimized_test.zig | 4 ++-- tests/root.zig | 25 ++++++++++++++++++++++++- 5 files changed, 33 insertions(+), 10 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index f7cc801..c696d43 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,6 @@ .{ .name = .zig_utils, - .version = "0.7.2", + .version = "0.7.3", .fingerprint = 0x6dc482caf73c4a75, .minimum_zig_version = "0.16.0", diff --git a/src/a_star.zig b/src/a_star.zig index e421ee4..6ceba59 100644 --- a/src/a_star.zig +++ b/src/a_star.zig @@ -227,15 +227,15 @@ pub fn AStar(comptime WeightType: type) type { var closed_set = try std.DynamicBitSet.initEmpty(self.allocator, n); defer closed_set.deinit(); - var open_set = std.PriorityQueue(PQNode, void, PQNode.compare).init(self.allocator, {}); - defer open_set.deinit(); + var open_set = std.PriorityQueue(PQNode, void, PQNode.compare).initContext({}); + defer open_set.deinit(self.allocator); // Initialize source g_score[source] = 0; const h = self.calculateHeuristic(source, dest); - try open_set.add(.{ .vertex = source, .f_score = h }); + try open_set.push(self.allocator, .{ .vertex = source, .f_score = h }); - while (open_set.removeOrNull()) |current| { + while (open_set.pop()) |current| { if (current.vertex == dest) { // Reconstruct path var node = dest; @@ -273,7 +273,7 @@ pub fn AStar(comptime WeightType: type) type { g_score[edge.to] = tentative_g; const f = @as(f32, @floatFromInt(tentative_g)) + self.calculateHeuristic(edge.to, dest); - try open_set.add(.{ .vertex = edge.to, .f_score = f }); + try open_set.push(self.allocator, .{ .vertex = edge.to, .f_score = f }); } } } diff --git a/src/z_index_buckets.zig b/src/z_index_buckets.zig index 10e6f28..7cd0bb2 100644 --- a/src/z_index_buckets.zig +++ b/src/z_index_buckets.zig @@ -38,7 +38,7 @@ pub fn ZIndexBuckets(comptime T: type, comptime ZIndexType: type) type { pub fn init(allocator: std.mem.Allocator) Self { return Self{ - .buckets = [_]Bucket{.{}} ** bucket_count, + .buckets = [_]Bucket{.empty} ** bucket_count, .allocator = allocator, .total_count = 0, }; diff --git a/tests/floyd_warshall_optimized_test.zig b/tests/floyd_warshall_optimized_test.zig index 01c3570..05ee671 100644 --- a/tests/floyd_warshall_optimized_test.zig +++ b/tests/floyd_warshall_optimized_test.zig @@ -115,7 +115,7 @@ pub const FloydWarshallOptimizedSpec = struct { fw.generate(); - var path = std.ArrayListUnmanaged(u32){}; + var path = std.ArrayListUnmanaged(u32).empty; defer path.deinit(allocator); try fw.setPathWithMappingUnmanaged(allocator, &path, 10, 40); @@ -152,7 +152,7 @@ pub const FloydWarshallOptimizedSpec = struct { fw.next[idx_a * size + idx_d] = idx_b; // from 10 toward 40 -> 20 fw.next[idx_b * size + idx_d] = idx_a; // from 20 toward 40 -> back to 10 - var path = std.ArrayListUnmanaged(u32){}; + var path = std.ArrayListUnmanaged(u32).empty; defer path.deinit(allocator); // Must TERMINATE with an error, not hang. diff --git a/tests/root.zig b/tests/root.zig index 01bdf6b..1a7ec52 100644 --- a/tests/root.zig +++ b/tests/root.zig @@ -12,8 +12,31 @@ pub const a_star_test = @import("a_star_test.zig"); pub const heuristics_test = @import("heuristics_test.zig"); pub const zon_coercion_test = @import("zon_coercion_test.zig"); +// std's `refAllDecls` is ONE level deep — it references the imported test +// structs above, but NOT the `pub const FooSpec = struct { pub const group = +// struct { test "..." {} } }` groups nested inside them. Under Zig 0.16 a +// `test` block is only collected into the test binary once its containing +// struct is semantically analyzed, so every doubly-nested zspec test was +// silently never run, and `zig build test` passed even with a failing +// assertion (#14). Recurse into every nested container type so each group +// struct is analyzed and its tests get collected. (Zig 0.16 dropped +// `std.testing.refAllDeclsRecursive`, hence the local copy.) +fn refAllDeclsRecursive(comptime T: type) void { + if (!@import("builtin").is_test) return; + inline for (comptime std.meta.declarations(T)) |decl| { + const field = @field(T, decl.name); + if (@TypeOf(field) == type) { + switch (@typeInfo(field)) { + .@"struct", .@"enum", .@"union", .@"opaque" => refAllDeclsRecursive(field), + else => {}, + } + } + _ = &@field(T, decl.name); + } +} + test { - std.testing.refAllDecls(@This()); + refAllDeclsRecursive(@This()); } // Regression for #13 — kept at the test ROOT (top-level) on purpose: the