zig build test --json to get duration/memory stats

This commit is contained in:
Karl Seguin
2025-04-15 18:49:39 +08:00
parent 5816443ad3
commit 180359e148
31 changed files with 134 additions and 36 deletions

View File

@@ -67,7 +67,7 @@ pub const Attr = struct {
const testing = @import("../../testing.zig");
test "Browser.DOM.Attribute" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -103,7 +103,7 @@ pub const CharacterData = struct {
const testing = @import("../../testing.zig");
test "Browser.DOM.CharacterData" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -41,7 +41,7 @@ pub const Comment = struct {
const testing = @import("../../testing.zig");
test "Browser.DOM.Comment" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -254,7 +254,7 @@ pub const Document = struct {
const testing = @import("../../testing.zig");
test "Browser.DOM.Document" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -37,7 +37,7 @@ pub const DocumentFragment = struct {
const testing = @import("../../testing.zig");
test "Browser.DOM.DocumentFragment" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -361,7 +361,7 @@ pub const Element = struct {
const testing = @import("../../testing.zig");
test "Browser.DOM.Element" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -115,7 +115,7 @@ pub const EventTarget = struct {
const testing = @import("../../testing.zig");
test "Browser.DOM.EventTarget" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -167,7 +167,7 @@ pub const DOMException = struct {
const testing = @import("../../testing.zig");
test "Browser.DOM.Exception" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
const err = "Failed to execute 'appendChild' on 'Node': The new child element contains the parent.";

View File

@@ -468,7 +468,7 @@ pub const HTMLCollection = struct {
const testing = @import("../../testing.zig");
test "Browser.DOM.HTMLCollection" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -88,7 +88,7 @@ pub const DOMImplementation = struct {
const testing = @import("../../testing.zig");
test "Browser.DOM.Implementation" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -346,7 +346,7 @@ const EventHandler = struct {
const testing = @import("../../testing.zig");
test "Browser.DOM.MutationObserver" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -77,7 +77,7 @@ pub const NamedNodeMap = struct {
const testing = @import("../../testing.zig");
test "Browser.DOM.NamedNodeMap" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -408,7 +408,7 @@ pub const Node = struct {
const testing = @import("../../testing.zig");
test "Browser.DOM.node" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.exec(

View File

@@ -171,7 +171,7 @@ pub const NodeList = struct {
const testing = @import("../../testing.zig");
test "Browser.DOM.NodeList" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -49,7 +49,7 @@ pub const ProcessingInstruction = struct {
const testing = @import("../../testing.zig");
test "Browser.DOM.ProcessingInstruction" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -62,7 +62,7 @@ pub const Text = struct {
const testing = @import("../../testing.zig");
test "Browser.DOM.Text" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -107,7 +107,7 @@ pub const DOMTokenList = struct {
const testing = @import("../../testing.zig");
test "Browser.DOM.TokenList" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -147,7 +147,7 @@ pub const EventHandler = struct {
const testing = @import("../../testing.zig");
test "Browser.Event" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -219,7 +219,7 @@ pub const HTMLDocument = struct {
const testing = @import("../../testing.zig");
test "Browser.HTML.Document" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -913,7 +913,7 @@ pub fn toInterface(comptime T: type, e: *parser.Element) !T {
const testing = @import("../../testing.zig");
test "Browser.HTML.Element" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -98,7 +98,7 @@ pub const History = struct {
const testing = @import("../../testing.zig");
test "Browser.HTML.History" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -97,7 +97,7 @@ pub const Location = struct {
const testing = @import("../../testing.zig");
test "Browser.HTML.Location" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -89,7 +89,7 @@ pub const Navigator = struct {
const testing = @import("../../testing.zig");
test "Browser.HTML.Navigator" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -14,7 +14,7 @@ pub const source = @embedFile("fetch.js");
const testing = @import("../../testing.zig");
test "Browser.fetch" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try @import("polyfill.zig").load(testing.allocator, runner.executor);

View File

@@ -217,7 +217,7 @@ pub const Bottle = struct {
const testing = @import("../../testing.zig");
test "Browser.Storage.LocalStorage" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -265,7 +265,7 @@ pub const URLSearchParams = struct {
const testing = @import("../../testing.zig");
test "Browser.URL" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -69,7 +69,7 @@ pub const ProgressEvent = struct {
const testing = @import("../../testing.zig");
test "Browser.XHR.ProgressEvent" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -818,7 +818,7 @@ pub const XMLHttpRequest = struct {
const testing = @import("../../testing.zig");
test "Browser.XHR.XMLHttpRequest" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -47,7 +47,7 @@ pub const XMLSerializer = struct {
const testing = @import("../../testing.zig");
test "Browser.XMLSerializer" {
var runner = try testing.jsRunner(testing.allocator, .{});
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{

View File

@@ -34,6 +34,9 @@ pub const std_options = std.Options{
.side_channels_mitigations = .none,
};
pub var js_runner_duration: usize = 0;
pub var tracking_allocator = TrackingAllocator.init(std.testing.allocator);
pub fn main() !void {
var mem: [8192]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&mem);
@@ -171,27 +174,28 @@ pub fn main() !void {
// TODO: at the very least, `browser` should return real stats
if (json_stats) {
const stats = tracking_allocator.stats();
try std.json.stringify(&.{
.{ .name = "browser", .bench = .{
.duration = 3180096049,
.alloc_nb = 6,
.realloc_nb = 278,
.alloc_size = 24711226,
.duration = js_runner_duration,
.alloc_nb = stats.allocation_count,
.realloc_nb = stats.reallocation_count,
.alloc_size = stats.allocated_bytes,
} },
.{ .name = "libdom", .bench = .{
.duration = 3180096049,
.duration = js_runner_duration,
.alloc_nb = 0,
.realloc_nb = 0,
.alloc_size = 0,
} },
.{ .name = "v8", .bench = .{
.duration = 3180096049,
.duration = js_runner_duration,
.alloc_nb = 0,
.realloc_nb = 0,
.alloc_size = 0,
} },
.{ .name = "main", .bench = .{
.duration = 3180096049,
.duration = js_runner_duration,
.alloc_nb = 0,
.realloc_nb = 0,
.alloc_size = 0,
@@ -375,3 +379,93 @@ fn isSetup(t: std.builtin.TestFn) bool {
fn isTeardown(t: std.builtin.TestFn) bool {
return std.mem.endsWith(u8, t.name, "tests:afterAll");
}
pub const TrackingAllocator = struct {
parent_allocator: Allocator,
free_count: usize = 0,
allocated_bytes: usize = 0,
allocation_count: usize = 0,
reallocation_count: usize = 0,
const Stats = struct {
allocated_bytes: usize,
allocation_count: usize,
reallocation_count: usize,
};
fn init(parent_allocator: Allocator) TrackingAllocator {
return .{
.parent_allocator = parent_allocator,
};
}
pub fn stats(self: *const TrackingAllocator) Stats {
return .{
.allocated_bytes = self.allocated_bytes,
.allocation_count = self.allocation_count,
.reallocation_count = self.reallocation_count,
};
}
pub fn allocator(self: *TrackingAllocator) Allocator {
return .{
.ptr = self,
.vtable = &.{
.alloc = alloc,
.resize = resize,
.free = free,
.remap = remap,
}
};
}
fn alloc(
ctx: *anyopaque,
len: usize,
alignment: std.mem.Alignment,
return_address: usize,
) ?[*]u8 {
const self: *TrackingAllocator = @ptrCast(@alignCast(ctx));
const result = self.parent_allocator.rawAlloc(len, alignment, return_address);
self.allocation_count += 1;
self.allocated_bytes += len;
return result;
}
fn resize(
ctx: *anyopaque,
old_mem: []u8,
alignment: std.mem.Alignment,
new_len: usize,
ra: usize,
) bool {
const self: *TrackingAllocator = @ptrCast(@alignCast(ctx));
const result = self.parent_allocator.rawResize(old_mem, alignment, new_len, ra);
self.reallocation_count += 1; // TODO: only if result is not null?
return result;
}
fn free(
ctx: *anyopaque,
old_mem: []u8,
alignment: std.mem.Alignment,
ra: usize,
) void {
const self: *TrackingAllocator = @ptrCast(@alignCast(ctx));
self.parent_allocator.rawFree(old_mem, alignment, ra);
self.free_count += 1;
}
fn remap(
ctx: *anyopaque,
memory: []u8,
alignment: std.mem.Alignment,
new_len: usize,
ret_addr: usize,
) ?[*]u8 {
const self: *TrackingAllocator = @ptrCast(@alignCast(ctx));
const result = self.parent_allocator.rawRemap(memory, alignment, new_len, ret_addr);
self.reallocation_count += 1; // TODO: only if result is not null?
return result;
}
};

View File

@@ -351,6 +351,7 @@ fn isJsonValue(a: std.json.Value, b: std.json.Value) bool {
}
}
pub const tracking_allocator = @import("root").tracking_allocator.allocator();
pub const JsRunner = struct {
const URL = @import("url.zig").URL;
const Env = @import("browser/env.zig").Env;
@@ -443,6 +444,8 @@ pub const JsRunner = struct {
const RunOpts = struct {};
pub const Case = std.meta.Tuple(&.{ []const u8, []const u8 });
pub fn testCases(self: *JsRunner, cases: []const Case, _: RunOpts) !void {
const start = try std.time.Instant.now();
for (cases, 0..) |case, i| {
var try_catch: Env.TryCatch = undefined;
try_catch.init(self.executor);
@@ -455,6 +458,7 @@ pub const JsRunner = struct {
return err;
};
try self.loop.run();
@import("root").js_runner_duration += std.time.Instant.since(try std.time.Instant.now(), start);
const actual = try value.toString(self.arena);
if (std.mem.eql(u8, case.@"1", actual) == false) {