allocator: std.mem.Allocator, modules: ModuleDirList, testResultsQueue: core.RingQueue(ModuleResults), const ModuleDirList = std.ArrayList(ModuleDir); const ModuleDir = struct { name: []u8, modulePath: []u8, pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void { allocator.free(self.name); allocator.free(self.modulePath); } }; const ModuleResults = struct { success: bool = true, log: std.ArrayListUnmanaged(u8) = .{}, logError: std.ArrayListUnmanaged(u8) = .{}, pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void { self.log.deinit(allocator); self.logError.deinit(allocator); } }; pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "testAll"); pub fn init(allocator: std.mem.Allocator) !*@This() { const self = try allocator.create(@This()); self.* = .{ .allocator = allocator, .testResultsQueue = try core.RingQueue(ModuleResults).init(allocator, 4096), .modules = ModuleDirList.init(allocator), }; return self; } pub fn prepare(self: *@This()) !void { const baseDirs: []const []const u8 = &.{ "../lib", "../engine" }; for (baseDirs) |basePath| { const baseDir = try std.fs.cwd().openDir(basePath, .{ .iterate = true }); var iter = baseDir.iterate(); while (try iter.next()) |entry| { if (entry.kind == .directory) { { const modDir = try baseDir.openDir(entry.name, .{}); modDir.access("build.zig.zon", .{}) catch { core.engine_log("skipping module [{s}] because missing build.zig.zon ", .{entry.name}); continue; }; } const module: ModuleDir = .{ .name = try self.allocator.dupe(u8, entry.name), .modulePath = try std.fmt.allocPrint(self.allocator, "{s}/{s}", .{ basePath, entry.name }), }; try self.modules.append(module); } } } // create a task list and kick off tasks for each module { const L = struct { module: *const ModuleDir, program: *Program, allocator: std.mem.Allocator, pub fn func(ctx: @This(), job: *core.JobContext) void { _ = job; // core.engine_log("starting test job: {s} {s}", ctx.module.*); const argv: []const []const u8 = &.{ "zig", "build", "test" }; const cwdPath = std.fs.cwd().realpathAlloc(ctx.allocator, ctx.module.modulePath) catch unreachable; defer ctx.allocator.free(cwdPath); const result = std.process.Child.run(.{ .argv = argv, .allocator = ctx.allocator, .cwd = cwdPath, }) catch unreachable; defer ctx.allocator.free(result.stdout); defer ctx.allocator.free(result.stderr); var success: bool = true; switch (result.term) { .Exited => |value| { if (value != 0) { success = false; } }, .Signal => { success = false; }, .Stopped => { success = false; // no-op should be ok? }, .Unknown => { unreachable; }, } if (success) { core.engine_log("tests for module [{s}] passed!", .{ctx.module.name}); } else { core.engine_err("tests for module [{s}] failed!", .{ctx.module.name}); core.engine_err("failure {s}: ", .{result.stderr}); } var moduleResults: ModuleResults = .{ .success = success, }; moduleResults.log.appendSlice(ctx.allocator, result.stdout) catch unreachable; moduleResults.logError.appendSlice(ctx.allocator, result.stderr) catch unreachable; ctx.program.testResultsQueue.pushLocked(moduleResults) catch unreachable; } }; for (self.modules.items) |*mod| { try core.dispatchJob(L{ .module = mod, .program = self, .allocator = self.allocator }); } } } pub fn tick(self: *@This(), _: f64) void { std.Thread.sleep(10_000_000); var successCount: u32 = 0; if (self.testResultsQueue.count() == self.modules.items.len) { // all tests complete, tear it down boys. while (self.testResultsQueue.popFromUnlocked()) |t| { var copy = t; copy.deinit(self.allocator); if (t.success) { successCount += 1; } } core.engine_log("[{d} of {d} tests passed]", .{ successCount, self.modules.items.len }); core.exitNow(); } } pub fn deinit(self: *@This()) void { self.testResultsQueue.deinit(); for (self.modules.items) |*mod| { mod.deinit(self.allocator); } self.modules.deinit(); self.allocator.destroy(self); } pub fn main() anyerror!void { try backlog.initializeAndRunStandardProgram(@This(), .{ .name = "Hello World", .enabledModules = .{ .platform = false, .graphics = false, .ui = false, .papyrus = false, .vkImgui = false, .assets = false, }, }); } const Program = @This(); const std = @import("std"); const backlog = @import("Backlog"); const core = backlog.core; const script = core.script;