mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 23:23:28 +00:00
Start unifying test and code
Depends on https://github.com/lightpanda-io/browser/pull/993 There's currently 3 ways to execute a page: 1 - page.navigate (as used in both the 'fetch' and 'serve' commands) 2 - jsRunner as used in unit tests 3 - main_wpt as used in the WPT runner Both jsRunner and main_wpt replicate the page.navigate code, but in their own hack-ish way. main_wpt re-implements the DOM walking in order to extract and execute <script> tags, as well as the needed page lifecycle events. This PR replaces the existing main_wpt loader with a call to page.navigate. To support this, a test HTTP server was added. (The test HTTP server is extracted from the existing unit test test server, and re-used between the two). There are benefits to this approach: 1 - The code is simpler 2 - More of the actual code and flow is tested 3 - There's 1 way to do things (page.navigate) 4 - Having an HTTP server might unlock some WPT tests Technically, we're replacing file IO with network IO i.e. http requests). This has potential downsides: 1 - The tests might be more brittle 2 - The tests might be slower I think we need to run it for a while to see if we get flaky behavior. The goal for following PRs is to bring this unification to the jsRunner.
This commit is contained in:
117
src/TestHTTPServer.zig
Normal file
117
src/TestHTTPServer.zig
Normal file
@@ -0,0 +1,117 @@
|
||||
const std = @import("std");
|
||||
|
||||
const TestHTTPServer = @This();
|
||||
|
||||
shutdown: bool,
|
||||
listener: ?std.net.Server,
|
||||
handler: Handler,
|
||||
|
||||
const Handler = *const fn (req: *std.http.Server.Request) anyerror!void;
|
||||
|
||||
pub fn init(handler: Handler) TestHTTPServer {
|
||||
return .{
|
||||
.shutdown = true,
|
||||
.listener = null,
|
||||
.handler = handler,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *TestHTTPServer) void {
|
||||
self.shutdown = true;
|
||||
if (self.listener) |*listener| {
|
||||
listener.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
std.debug.print("TestHTTPServer asked to serve an unknown file type: {s}\n", .{file_path});
|
||||
return "text/html";
|
||||
}
|
||||
Reference in New Issue
Block a user