Backlog/testing/test-all.zig

185 lines
5.9 KiB
Zig

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;