diff --git a/.github/workflows/wpt.yml b/.github/workflows/wpt.yml index ecea3cc4..38405591 100644 --- a/.github/workflows/wpt.yml +++ b/.github/workflows/wpt.yml @@ -15,11 +15,11 @@ on: workflow_dispatch: jobs: - wpt: - name: web platform tests json output + wpt-build-release: + name: zig build release runs-on: ubuntu-latest - timeout-minutes: 90 + timeout-minutes: 15 steps: - uses: actions/checkout@v6 @@ -30,11 +30,85 @@ jobs: - uses: ./.github/actions/install - - name: build wpt - run: zig build -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=ReleaseFast -- version + - name: zig build release + run: zig build -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=ReleaseFast -Dcpu=x86_64 -Dgit_commit=$(git rev-parse --short ${{ github.sha }}) + + - name: upload artifact + uses: actions/upload-artifact@v4 + with: + name: lightpanda-build-release + path: | + zig-out/bin/lightpanda + retention-days: 1 + + wpt-build-runner: + name: build wpt runner + + runs-on: ubuntu-latest + timeout-minutes: 90 + + steps: + - uses: actions/checkout@v6 + with: + repository: 'lightpanda-io/demo' + fetch-depth: 0 + + - run: | + cd ./wptrunner + CGO_ENABLED=0 go build + + - name: upload artifact + uses: actions/upload-artifact@v4 + with: + name: wptrunner + path: | + wptrunner/wptrunner + retention-days: 1 + + run-wpt: + name: web platform tests json output + needs: + - wpt-build-release + - wpt-build-runner + + # use a self host runner. + runs-on: lpd-bench-hetzner + timeout-minutes: 90 + + steps: + - uses: actions/checkout@v6 + with: + ref: fork + repository: 'lightpanda-io/wpt' + fetch-depth: 0 + + # The hosts are configured manually on the self host runner. + # - name: create custom hosts + # run: ./wpt make-hosts-file | sudo tee -a /etc/hosts + + - name: generate manifest + run: ./wpt manifest + + - name: download lightpanda release + uses: actions/download-artifact@v4 + with: + name: lightpanda-build-release + + - run: chmod a+x ./lightpanda + + - name: download wptrunner + uses: actions/download-artifact@v4 + with: + name: wptrunner + + - run: chmod a+x ./wptrunner - name: run test with json output - run: zig-out/bin/lightpanda-wpt --json > wpt.json + run: | + ./wpt serve 2> /dev/null & echo $! > WPT.pid + sleep 10s + ./wptrunner -lpd-path ./lightpanda -json -concurrency 1 > wpt.json + kill `cat WPT.pid` - name: write commit run: | @@ -51,7 +125,7 @@ jobs: perf-fmt: name: perf-fmt - needs: wpt + needs: run-wpt runs-on: ubuntu-latest timeout-minutes: 15 diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index cc314b9b..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "tests/wpt"] - path = tests/wpt - url = https://github.com/lightpanda-io/wpt diff --git a/Makefile b/Makefile index 0e34a5e7..72dbcb7d 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ help: # $(ZIG) commands # ------------ -.PHONY: build build-v8-snapshot build-dev run run-release shell test bench wpt data end2end +.PHONY: build build-v8-snapshot build-dev run run-release shell test bench data end2end ## Build v8 snapshot build-v8-snapshot: @@ -82,15 +82,6 @@ shell: @printf "\033[36mBuilding shell...\033[0m\n" @$(ZIG) build shell || (printf "\033[33mBuild ERROR\033[0m\n"; exit 1;) -## Run WPT tests -wpt: - @printf "\033[36mBuilding wpt...\033[0m\n" - @$(ZIG) build wpt -- $(filter-out $@,$(MAKECMDGOALS)) || (printf "\033[33mBuild ERROR\033[0m\n"; exit 1;) - -wpt-summary: - @printf "\033[36mBuilding wpt...\033[0m\n" - @$(ZIG) build wpt -- --summary $(filter-out $@,$(MAKECMDGOALS)) || (printf "\033[33mBuild ERROR\033[0m\n"; exit 1;) - ## Test - `grep` is used to filter out the huge compile command on build ifeq ($(OS), macos) test: diff --git a/README.md b/README.md index d7c78d16..92e36cad 100644 --- a/README.md +++ b/README.md @@ -281,35 +281,75 @@ make end2end Lightpanda is tested against the standardized [Web Platform Tests](https://web-platform-tests.org/). -The relevant tests cases are committed in a [dedicated repository](https://github.com/lightpanda-io/wpt) which is fetched by the `make install-submodule` command. - -All the tests cases executed are located in the `tests/wpt` sub-directory. +We use [a fork](https://github.com/lightpanda-io/wpt/tree/fork) including a custom +[`testharnessreport.js`](https://github.com/lightpanda-io/wpt/commit/01a3115c076a3ad0c84849dbbf77a6e3d199c56f). For reference, you can easily execute a WPT test case with your browser via [wpt.live](https://wpt.live). +#### Configure WPT HTTP server + +To run the test, you must clone the repository, configure the custom hosts and generate the +`MANIFEST.json` file. + +Clone the repository with the `fork` branch. +``` +git clone -b fork --depth=1 git@github.com:lightpanda-io/wpt.git +``` + +Enter into the `wpt/` dir. + +Install custom domains in your `/etc/hosts` +``` +./wpt make-hosts-file | sudo tee -a /etc/hosts +``` + +Generate `MANIFEST.json` +``` +./wpt manifest +``` +Use the [WPT's setup +guide](https://web-platform-tests.org/running-tests/from-local-system.html) for +details. + #### Run WPT test suite -To run all the tests: +An external [Go](https://go.dev) runner is provided by +[github.com/lightpanda-io/demo/](https://github.com/lightpanda-io/demo/) +repository, located into `wptrunner/` dir. +You need to clone the project first. + +First start the WPT's HTTP server from your `wpt/` clone dir. +``` +./wpt serve +``` + +Run a Lightpanda browser ``` -make wpt +zig build run -- --insecure_disable_tls_host_verification +``` + +Then you can start the wptrunner from the Demo's clone dir: +``` +cd wptrunner && go run . ``` Or one specific test: ``` -make wpt Node-childNodes.html +cd wptrunner && go run . Node-childNodes.html ``` -#### Add a new WPT test case +`wptrunner` command accepts `--summary` and `--json` options modifying output. +Also `--concurrency` define the concurrency limit. -We add new relevant tests cases files when we implemented changes in Lightpanda. +:warning: Running the whole test suite will take a long time. In this case, +it's useful to build in `releaseFast` mode to make tests faster. -To add a new test, copy the file you want from the [WPT -repo](https://github.com/web-platform-tests/wpt) into the `tests/wpt` directory. - -:warning: Please keep the original directory tree structure of `tests/wpt`. +``` +zig build -Doptimize=ReleaseFast run +``` ## Contributing diff --git a/build.zig b/build.zig index cd304454..7e3a2817 100644 --- a/build.zig +++ b/build.zig @@ -146,32 +146,6 @@ pub fn build(b: *Build) !void { const run_step = b.step("legacy_test", "Run the app"); run_step.dependOn(&run_cmd.step); } - - { - // wpt - const exe = b.addExecutable(.{ - .name = "lightpanda-wpt", - .use_llvm = true, - .root_module = b.createModule(.{ - .root_source_file = b.path("src/main_wpt.zig"), - .target = target, - .optimize = optimize, - .sanitize_c = enable_csan, - .sanitize_thread = enable_tsan, - .imports = &.{ - .{ .name = "lightpanda", .module = lightpanda_module }, - }, - }), - }); - b.installArtifact(exe); - - const run_cmd = b.addRunArtifact(exe); - if (b.args) |args| { - run_cmd.addArgs(args); - } - const run_step = b.step("wpt", "Run WPT tests"); - run_step.dependOn(&run_cmd.step); - } } fn linkV8( diff --git a/src/main_wpt.zig b/src/main_wpt.zig deleted file mode 100644 index 3db9d92b..00000000 --- a/src/main_wpt.zig +++ /dev/null @@ -1,616 +0,0 @@ -// Copyright (C) 2023-2025 Lightpanda (Selecy SAS) -// -// Francis Bouvier -// Pierre Tachoire -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -const std = @import("std"); -const lp = @import("lightpanda"); - -const Allocator = std.mem.Allocator; -const ArenaAllocator = std.heap.ArenaAllocator; - -const WPT_DIR = "tests/wpt"; - -// use in custom panic handler -var current_test: ?[]const u8 = null; - -pub fn main() !void { - var gpa: std.heap.DebugAllocator(.{}) = .init; - defer _ = gpa.deinit(); - - const allocator = gpa.allocator(); - - var http_server = try TestHTTPServer.init(); - defer http_server.deinit(); - - { - var wg: std.Thread.WaitGroup = .{}; - wg.startMany(1); - var thrd = try std.Thread.spawn(.{}, TestHTTPServer.run, .{ &http_server, &wg }); - thrd.detach(); - wg.wait(); - } - - // An arena for the runner itself, lives for the duration of the the process - var ra = ArenaAllocator.init(allocator); - defer ra.deinit(); - const runner_arena = ra.allocator(); - - const cmd = try parseArgs(runner_arena); - - var it = try TestIterator.init(allocator, WPT_DIR, cmd.filters); - defer it.deinit(); - - var writer = try Writer.init(allocator, cmd.format); - defer writer.deinit(); - - lp.log.opts.level = .warn; - const config = try lp.Config.init(allocator, "lightpanda-wpt", .{ .serve = .{ - .common = .{ - .tls_verify_host = false, - .user_agent_suffix = "internal-tester", - }, - } }); - defer config.deinit(allocator); - - var app = try lp.App.init(allocator, &config); - defer app.deinit(); - - const http_client = try app.http.createClient(allocator); - defer http_client.deinit(); - - var browser = try lp.Browser.init(app, .{ .http_client = http_client }); - defer browser.deinit(); - - // An arena for running each tests. Is reset after every test. - var test_arena = ArenaAllocator.init(allocator); - defer test_arena.deinit(); - - var i: usize = 0; - while (try it.next()) |test_file| { - defer _ = test_arena.reset(.retain_capacity); - - defer current_test = null; - current_test = test_file; - - var err_out: ?[]const u8 = null; - const result = run( - test_arena.allocator(), - &browser, - test_file, - &err_out, - ) catch |err| blk: { - if (err_out == null) { - err_out = @errorName(err); - } - break :blk null; - }; - try writer.process(test_file, result, err_out); - // if (@mod(i, 10) == 0) { - // std.debug.print("\n\n=== V8 Memory {d}===\n", .{i}); - // browser.env.dumpMemoryStats(); - // } - i += 1; - } - try writer.finalize(); -} - -fn run( - arena: Allocator, - browser: *lp.Browser, - test_file: []const u8, - err_out: *?[]const u8, -) ![]const u8 { - const notification = try lp.Notification.init(browser.allocator); - defer notification.deinit(); - - const session = try browser.newSession(notification); - defer browser.closeSession(); - - const page = try session.createPage(); - defer session.removePage(); - - const url = try std.fmt.allocPrintSentinel(arena, "http://localhost:9582/{s}", .{test_file}, 0); - try page.navigate(url, .{}); - - _ = session.wait(2000); - - var ls: lp.js.Local.Scope = undefined; - page.js.localScope(&ls); - defer ls.deinit(); - - var try_catch: lp.js.TryCatch = undefined; - try_catch.init(&ls.local); - defer try_catch.deinit(); - - // Check the final test status. - ls.local.eval("report.status", "teststatus") catch |err| { - const caught = try_catch.caughtOrError(arena, err); - err_out.* = caught.exception; - return err; - }; - - // return the detailed result. - const value = ls.local.exec("report.log", "report") catch |err| { - const caught = try_catch.caughtOrError(arena, err); - err_out.* = caught.exception; - return err; - }; - - return value.toStringSliceWithAlloc(arena); -} - -const Writer = struct { - format: Format, - allocator: Allocator, - pass_count: usize = 0, - fail_count: usize = 0, - case_pass_count: usize = 0, - case_fail_count: usize = 0, - writer: std.fs.File.Writer, - cases: std.ArrayList(Case) = .{}, - - const Format = enum { json, text, summary, quiet }; - - fn init(allocator: Allocator, format: Format) !Writer { - const out = std.fs.File.stdout(); - var writer = out.writer(&.{}); - - if (format == .json) { - try writer.interface.writeByte('['); - } - - return .{ - .format = format, - .writer = writer, - .allocator = allocator, - }; - } - - fn deinit(self: *Writer) void { - self.cases.deinit(self.allocator); - } - - fn finalize(self: *Writer) !void { - var writer = &self.writer.interface; - if (self.format == .json) { - // When we write a test output, we add a trailing comma to act as - // a separator for the next test. We need to add this dummy entry - // to make it valid json. - // Better option could be to change the formatter to work on JSONL: - // https://github.com/lightpanda-io/perf-fmt/blob/main/wpt/wpt.go - try writer.writeAll("{\"name\":\"empty\",\"pass\": true, \"cases\": []}]"); - } else { - try writer.print("\n==Summary==\nTests: {d}/{d}\nCases: {d}/{d}\n", .{ - self.pass_count, - self.pass_count + self.fail_count, - self.case_pass_count, - self.case_pass_count + self.case_fail_count, - }); - } - } - - fn process(self: *Writer, test_file: []const u8, result_: ?[]const u8, err_: ?[]const u8) !void { - var writer = &self.writer.interface; - if (err_) |err| { - self.fail_count += 1; - switch (self.format) { - .text => return writer.print("Fail\t{s}\n\t{s}\n", .{ test_file, err }), - .summary => return writer.print("Fail 0/0\t{s}\n", .{test_file}), - .json => { - try std.json.Stringify.value(Test{ - .pass = false, - .name = test_file, - .cases = &.{}, - }, .{ .whitespace = .indent_2 }, writer); - return writer.writeByte(','); - }, - .quiet => {}, - } - // just make sure we didn't fall through by mistake - unreachable; - } - - // if we don't have an error, we must have a result - const result = result_ orelse return error.InvalidResult; - - var cases = &self.cases; - cases.clearRetainingCapacity(); // from previous run - - var pass = true; - var case_pass_count: usize = 0; - var case_fail_count: usize = 0; - - var lines = std.mem.splitScalar(u8, result, '\n'); - while (lines.next()) |line| { - if (line.len == 0) { - break; - } - // case names can have | in them, so we can't simply split on | - var case_name = line; - var case_pass = false; // so pessimistic! - var case_message: []const u8 = ""; - - if (std.mem.endsWith(u8, line, "|Pass")) { - case_name = line[0 .. line.len - 5]; - case_pass = true; - case_pass_count += 1; - } else { - // both cases names and messages can have | in them. Our only - // chance to "parse" this is to anchor off the |$Status. - const statuses = [_][]const u8{ "|Fail", "|Timeout", "|Not Run", "|Optional Feature Unsupported" }; - var pos_: ?usize = null; - var message_start: usize = 0; - for (statuses) |status| { - if (std.mem.indexOf(u8, line, status)) |idx| { - pos_ = idx; - message_start = idx + status.len; - break; - } - } - const pos = pos_ orelse { - std.debug.print("invalid result line: {s}\n", .{line}); - return error.InvalidResult; - }; - - case_name = line[0..pos]; - case_message = line[message_start..]; - pass = false; - case_fail_count += 1; - } - - try cases.append(self.allocator, .{ - .name = case_name, - .pass = case_pass, - .message = case_message, - }); - } - - // our global counters - if (pass) { - self.pass_count += 1; - } else { - self.fail_count += 1; - } - self.case_pass_count += case_pass_count; - self.case_fail_count += case_fail_count; - - switch (self.format) { - .summary => try writer.print("{s} {d}/{d}\t{s}\n", .{ statusText(pass), case_pass_count, case_pass_count + case_fail_count, test_file }), - .text => { - try writer.print("{s}\t{s}\n", .{ statusText(pass), test_file }); - for (cases.items) |c| { - try writer.print("\t{s}\t{s}\n", .{ statusText(c.pass), c.name }); - if (c.message) |msg| { - try writer.print("\t\t{s}\n", .{msg}); - } - } - }, - .json => { - try std.json.Stringify.value(Test{ - .pass = pass, - .name = test_file, - .cases = cases.items, - }, .{ .whitespace = .indent_2 }, writer); - // separator, see `finalize` for the hack we use to terminate this - try writer.writeByte(','); - }, - .quiet => {}, - } - } - - fn statusText(pass: bool) []const u8 { - return if (pass) "Pass" else "Fail"; - } -}; - -const Command = struct { - format: Writer.Format, - filters: [][]const u8, -}; - -fn parseArgs(arena: Allocator) !Command { - const usage = - \\usage: {s} [options] [test filter] - \\ Run the Web Test Platform. - \\ - \\ -h, --help Print this help message and exit. - \\ --json result is formatted in JSON. - \\ --summary print a summary result. Incompatible w/ --json or --quiet - \\ --quiet No output. Incompatible w/ --json or --summary - \\ - ; - - var args = try std.process.argsWithAllocator(arena); - - // get the exec name. - const exec_name = args.next().?; - - var format = Writer.Format.text; - var filters: std.ArrayList([]const u8) = .{}; - - while (args.next()) |arg| { - if (std.mem.eql(u8, "-h", arg) or std.mem.eql(u8, "--help", arg)) { - std.debug.print(usage, .{exec_name}); - std.posix.exit(0); - } - - if (std.mem.eql(u8, "--json", arg)) { - format = .json; - } else if (std.mem.eql(u8, "--summary", arg)) { - format = .summary; - } else if (std.mem.eql(u8, "--quiet", arg)) { - format = .quiet; - } else { - try filters.append(arena, arg); - } - } - - return .{ - .format = format, - .filters = filters.items, - }; -} - -const TestIterator = struct { - dir: Dir, - walker: Dir.Walker, - filters: [][]const u8, - read_arena: ArenaAllocator, - - const Dir = std.fs.Dir; - - fn init(allocator: Allocator, root: []const u8, filters: [][]const u8) !TestIterator { - var dir = try std.fs.cwd().openDir(root, .{ .iterate = true, .no_follow = true }); - errdefer dir.close(); - - return .{ - .dir = dir, - .filters = filters, - .walker = try dir.walk(allocator), - .read_arena = ArenaAllocator.init(allocator), - }; - } - - fn deinit(self: *TestIterator) void { - self.walker.deinit(); - self.dir.close(); - self.read_arena.deinit(); - } - - fn next(self: *TestIterator) !?[]const u8 { - NEXT: while (try self.walker.next()) |entry| { - if (entry.kind != .file) { - continue; - } - - if (std.mem.startsWith(u8, entry.path, "resources/")) { - // resources for running the tests themselves, not actual tests - continue; - } - - if (!std.mem.endsWith(u8, entry.basename, ".html") and !std.mem.endsWith(u8, entry.basename, ".htm")) { - continue; - } - - const path = entry.path; - for (self.filters) |filter| { - if (std.mem.indexOf(u8, path, filter) == null) { - continue :NEXT; - } - } - - { - defer _ = self.read_arena.reset(.retain_capacity); - // We need to read the file's content to see if there's a - // "testharness.js" in it. If there isn't, it isn't a test. - // Shame we have to do this. - - const arena = self.read_arena.allocator(); - const full_path = try std.fs.path.join(arena, &.{ WPT_DIR, path }); - const file = try std.fs.cwd().openFile(full_path, .{}); - defer file.close(); - const html = try file.readToEndAlloc(arena, 128 * 1024); - - if (std.mem.indexOf(u8, html, "testharness.js") == null) { - // This isn't a test. A lot of files are helpers/content for tests to - // make use of. - continue :NEXT; - } - } - - return path; - } - - return null; - } -}; - -const Case = struct { - pass: bool, - name: []const u8, - message: ?[]const u8, -}; - -const Test = struct { - pass: bool, - crash: bool = false, - name: []const u8, - cases: []Case, -}; - -const TestHTTPServer = struct { - shutdown: bool, - dir: std.fs.Dir, - listener: ?std.net.Server, - - pub fn init() !TestHTTPServer { - return .{ - .dir = try std.fs.cwd().openDir(WPT_DIR, .{}), - .shutdown = true, - .listener = null, - }; - } - - pub fn deinit(self: *TestHTTPServer) void { - self.shutdown = true; - if (self.listener) |*listener| { - listener.deinit(); - } - self.dir.close(); - } - - pub fn run(self: *TestHTTPServer, wg: *std.Thread.WaitGroup) !void { - const address = try std.net.Address.parseIp("127.0.0.1", 9582); - - self.listener = try address.listen(.{ .reuse_address = true }); - var listener = &self.listener.?; - - wg.finish(); - - while (true) { - const conn = listener.accept() catch |err| { - if (self.shutdown) { - return; - } - return err; - }; - const thrd = try std.Thread.spawn(.{}, handleConnection, .{ self, conn }); - thrd.detach(); - } - } - - fn handleConnection(self: *TestHTTPServer, conn: std.net.Server.Connection) !void { - defer conn.stream.close(); - - var req_buf: [2048]u8 = undefined; - var conn_reader = conn.stream.reader(&req_buf); - var conn_writer = conn.stream.writer(&req_buf); - - var http_server = std.http.Server.init(conn_reader.interface(), &conn_writer.interface); - - while (true) { - var req = http_server.receiveHead() catch |err| switch (err) { - error.ReadFailed => continue, - error.HttpConnectionClosing => continue, - else => { - std.debug.print("Test HTTP Server error: {}\n", .{err}); - return err; - }, - }; - - self.handler(&req) catch |err| { - std.debug.print("test http error '{s}': {}\n", .{ req.head.target, err }); - try req.respond("server error", .{ .status = .internal_server_error }); - return; - }; - } - } - - fn handler(server: *TestHTTPServer, req: *std.http.Server.Request) !void { - const path = req.head.target; - - if (std.mem.eql(u8, path, "/")) { - // There's 1 test that does an XHR request to this, and it just seems - // to want a 200 success. - return req.respond("Hello!", .{}); - } - - // strip out leading '/' to make the path relative - const file = try server.dir.openFile(path[1..], .{}); - defer file.close(); - - const stat = try file.stat(); - var send_buffer: [4096]u8 = undefined; - - var res = try req.respondStreaming(&send_buffer, .{ - .content_length = stat.size, - .respond_options = .{ - .extra_headers = &.{ - .{ .name = "content-type", .value = getContentType(path) }, - }, - }, - }); - - var read_buffer: [4096]u8 = undefined; - var reader = file.reader(&read_buffer); - _ = try res.writer.sendFileAll(&reader, .unlimited); - try res.writer.flush(); - try res.end(); - } - - pub fn sendFile(req: *std.http.Server.Request, file_path: []const u8) !void { - var file = std.fs.cwd().openFile(file_path, .{}) catch |err| switch (err) { - error.FileNotFound => return req.respond("server error", .{ .status = .not_found }), - else => return err, - }; - defer file.close(); - - const stat = try file.stat(); - var send_buffer: [4096]u8 = undefined; - - var res = try req.respondStreaming(&send_buffer, .{ - .content_length = stat.size, - .respond_options = .{ - .extra_headers = &.{ - .{ .name = "content-type", .value = getContentType(file_path) }, - }, - }, - }); - - var read_buffer: [4096]u8 = undefined; - var reader = file.reader(&read_buffer); - _ = try res.writer.sendFileAll(&reader, .unlimited); - try res.writer.flush(); - try res.end(); - } - - fn getContentType(file_path: []const u8) []const u8 { - if (std.mem.endsWith(u8, file_path, ".js")) { - return "application/json"; - } - - if (std.mem.endsWith(u8, file_path, ".html")) { - return "text/html"; - } - - if (std.mem.endsWith(u8, file_path, ".htm")) { - return "text/html"; - } - - if (std.mem.endsWith(u8, file_path, ".xml")) { - // some wpt tests do this - return "text/xml"; - } - - if (std.mem.endsWith(u8, file_path, ".mjs")) { - // mjs are ECMAScript modules - return "application/json"; - } - - std.debug.print("TestHTTPServer asked to serve an unknown file type: {s}\n", .{file_path}); - return "text/html"; - } -}; - -pub const panic = std.debug.FullPanic(struct { - pub fn panicFn(msg: []const u8, first_trace_addr: ?usize) noreturn { - if (current_test) |ct| { - std.debug.print("===panic running: {s}===\n", .{ct}); - } - std.debug.defaultPanic(msg, first_trace_addr); - } -}.panicFn); diff --git a/tests/wpt b/tests/wpt deleted file mode 160000 index 69c6afab..00000000 --- a/tests/wpt +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 69c6afabd893c00c7cb982ba66306e1a68db90c3