zig 0.12.0: bring all of runStandaloneServer under test
This commit is contained in:
		
							parent
							
								
									1053c8f002
								
							
						
					
					
						commit
						c3e1ffc2ef
					
				
					 2 changed files with 53 additions and 44 deletions
				
			
		| 
						 | 
					@ -96,6 +96,7 @@ pub fn build(b: *std.Build) !void {
 | 
				
			||||||
        // Creates steps for unit testing. This only builds the test executable
 | 
					        // Creates steps for unit testing. This only builds the test executable
 | 
				
			||||||
        // but does not run it.
 | 
					        // but does not run it.
 | 
				
			||||||
        const exe_tests = b.addTest(.{
 | 
					        const exe_tests = b.addTest(.{
 | 
				
			||||||
 | 
					            .name = "test: as executable",
 | 
				
			||||||
            .root_source_file = .{ .path = "src/test.zig" },
 | 
					            .root_source_file = .{ .path = "src/test.zig" },
 | 
				
			||||||
            .target = b.resolveTargetQuery(t),
 | 
					            .target = b.resolveTargetQuery(t),
 | 
				
			||||||
            .optimize = optimize,
 | 
					            .optimize = optimize,
 | 
				
			||||||
| 
						 | 
					@ -115,6 +116,7 @@ pub fn build(b: *std.Build) !void {
 | 
				
			||||||
        // in the future. Scaleway, for instance, is another system that works
 | 
					        // in the future. Scaleway, for instance, is another system that works
 | 
				
			||||||
        // via shared library
 | 
					        // via shared library
 | 
				
			||||||
        const lib_tests = b.addTest(.{
 | 
					        const lib_tests = b.addTest(.{
 | 
				
			||||||
 | 
					            .name = "test: as library",
 | 
				
			||||||
            .root_source_file = .{ .path = "src/flexilib.zig" },
 | 
					            .root_source_file = .{ .path = "src/flexilib.zig" },
 | 
				
			||||||
            .target = b.resolveTargetQuery(t),
 | 
					            .target = b.resolveTargetQuery(t),
 | 
				
			||||||
            .optimize = optimize,
 | 
					            .optimize = optimize,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,4 @@
 | 
				
			||||||
 | 
					const builtin = @import("builtin");
 | 
				
			||||||
const std = @import("std");
 | 
					const std = @import("std");
 | 
				
			||||||
const build_options = @import("universal_lambda_build_options");
 | 
					const build_options = @import("universal_lambda_build_options");
 | 
				
			||||||
const flexilib = @import("flexilib-interface");
 | 
					const flexilib = @import("flexilib-interface");
 | 
				
			||||||
| 
						 | 
					@ -46,7 +47,8 @@ fn runStandaloneServerParent(allocator: ?std.mem.Allocator, event_handler: inter
 | 
				
			||||||
    while (argi.next()) |a| {
 | 
					    while (argi.next()) |a| {
 | 
				
			||||||
        if (std.mem.eql(u8, child_arg, a)) {
 | 
					        if (std.mem.eql(u8, child_arg, a)) {
 | 
				
			||||||
            // This should never actually return
 | 
					            // This should never actually return
 | 
				
			||||||
            return try runStandaloneServer(allocator, event_handler);
 | 
					            try runStandaloneServer(allocator, event_handler, 8080); // TODO: configurable port
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        try al.append(a);
 | 
					        try al.append(a);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -66,27 +68,25 @@ fn runStandaloneServerParent(allocator: ?std.mem.Allocator, event_handler: inter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Will create a web server and marshall all requests back to our event handler
 | 
					/// Will create a web server and marshall all requests back to our event handler
 | 
				
			||||||
/// To keep things simple, we'll have this on a single thread, at least for now
 | 
					/// To keep things simple, we'll have this on a single thread, at least for now
 | 
				
			||||||
fn runStandaloneServer(allocator: ?std.mem.Allocator, event_handler: interface.HandlerFn) !u8 {
 | 
					fn runStandaloneServer(allocator: ?std.mem.Allocator, event_handler: interface.HandlerFn, port: u16) !void {
 | 
				
			||||||
    // TODO: Get this under test. It doesn't work in zig 0.12.0
 | 
					 | 
				
			||||||
    const alloc = allocator orelse std.heap.page_allocator;
 | 
					    const alloc = allocator orelse std.heap.page_allocator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var arena = std.heap.ArenaAllocator.init(alloc);
 | 
					    var arena = std.heap.ArenaAllocator.init(alloc);
 | 
				
			||||||
    defer arena.deinit();
 | 
					    defer arena.deinit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var aa = arena.allocator();
 | 
					    var aa = arena.allocator();
 | 
				
			||||||
    var server = std.http.Server.init(aa, .{ .reuse_address = true });
 | 
					    const address = try std.net.Address.parseIp("127.0.0.1", port);
 | 
				
			||||||
    defer server.deinit();
 | 
					    var net_server = try address.listen(.{ .reuse_address = true });
 | 
				
			||||||
    const address = try std.net.Address.parseIp("127.0.0.1", 8080); // TODO: allow config
 | 
					    const server_port = net_server.listen_address.in.getPort();
 | 
				
			||||||
    try server.listen(address);
 | 
					    _ = try std.fmt.bufPrint(&server_url, "http://127.0.0.1:{d}", .{server_port});
 | 
				
			||||||
    const server_port = server.socket.listen_address.in.getPort();
 | 
					    log.info("server listening at {s}", .{server_url});
 | 
				
			||||||
    var uri: ["http://127.0.0.1:99999".len]u8 = undefined;
 | 
					    if (builtin.is_test) server_ready = true;
 | 
				
			||||||
    _ = try std.fmt.bufPrint(&uri, "http://127.0.0.1:{d}", .{server_port});
 | 
					 | 
				
			||||||
    log.info("server listening at {s}", .{uri});
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // No threads, maybe later
 | 
					    // No threads, maybe later
 | 
				
			||||||
    //log.info("starting server thread, tid {d}", .{std.Thread.getCurrentId()});
 | 
					    //log.info("starting server thread, tid {d}", .{std.Thread.getCurrentId()});
 | 
				
			||||||
    while (true) {
 | 
					    while (remaining_requests == null or remaining_requests.? > 0) {
 | 
				
			||||||
        defer {
 | 
					        defer {
 | 
				
			||||||
 | 
					            if (remaining_requests) |*r| r.* -= 1;
 | 
				
			||||||
            if (!arena.reset(.{ .retain_with_limit = 1024 * 1024 })) {
 | 
					            if (!arena.reset(.{ .retain_with_limit = 1024 * 1024 })) {
 | 
				
			||||||
                // reallocation failed, arena is degraded
 | 
					                // reallocation failed, arena is degraded
 | 
				
			||||||
                log.warn("Arena reset failed and is degraded. Resetting arena", .{});
 | 
					                log.warn("Arena reset failed and is degraded. Resetting arena", .{});
 | 
				
			||||||
| 
						 | 
					@ -95,19 +95,20 @@ fn runStandaloneServer(allocator: ?std.mem.Allocator, event_handler: interface.H
 | 
				
			||||||
                aa = arena.allocator();
 | 
					                aa = arena.allocator();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const builtin = @import("builtin");
 | 
					        const supports_getrusage = builtin.os.tag != .windows and @hasDecl(std.posix.system, "rusage"); // Is Windows it?
 | 
				
			||||||
        const supports_getrusage = builtin.os.tag != .windows and @hasDecl(std.os.system, "rusage"); // Is Windows it?
 | 
					        var rss: if (supports_getrusage) std.posix.rusage else void = undefined;
 | 
				
			||||||
        var rss: if (supports_getrusage) std.os.rusage else void = undefined;
 | 
					 | 
				
			||||||
        if (supports_getrusage and builtin.mode == .Debug)
 | 
					        if (supports_getrusage and builtin.mode == .Debug)
 | 
				
			||||||
            rss = std.os.getrusage(std.os.rusage.SELF);
 | 
					            rss = std.posix.getrusage(std.posix.rusage.SELF);
 | 
				
			||||||
        processRequest(aa, &server, event_handler) catch |e| {
 | 
					        if (builtin.is_test) bytes_allocated = arena.queryCapacity();
 | 
				
			||||||
 | 
					        processRequest(aa, &net_server, event_handler) catch |e| {
 | 
				
			||||||
            log.err("Unexpected error processing request: {any}", .{e});
 | 
					            log.err("Unexpected error processing request: {any}", .{e});
 | 
				
			||||||
            if (@errorReturnTrace()) |trace| {
 | 
					            if (@errorReturnTrace()) |trace| {
 | 
				
			||||||
                std.debug.dumpStackTrace(trace.*);
 | 
					                std.debug.dumpStackTrace(trace.*);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					        if (builtin.is_test) bytes_allocated = arena.queryCapacity() - bytes_allocated;
 | 
				
			||||||
        if (supports_getrusage and builtin.mode == .Debug) { // and  debug mode) {
 | 
					        if (supports_getrusage and builtin.mode == .Debug) { // and  debug mode) {
 | 
				
			||||||
            const rusage = std.os.getrusage(std.os.rusage.SELF);
 | 
					            const rusage = std.posix.getrusage(std.posix.rusage.SELF);
 | 
				
			||||||
            log.debug(
 | 
					            log.debug(
 | 
				
			||||||
                "Request complete, max RSS of process: {d}M. Incremental: {d}K, User: {d}μs, System: {d}μs",
 | 
					                "Request complete, max RSS of process: {d}M. Incremental: {d}K, User: {d}μs, System: {d}μs",
 | 
				
			||||||
                .{
 | 
					                .{
 | 
				
			||||||
| 
						 | 
					@ -121,10 +122,11 @@ fn runStandaloneServer(allocator: ?std.mem.Allocator, event_handler: interface.H
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return 0;
 | 
					    return;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn processRequest(aa: std.mem.Allocator, server: *std.net.Server, event_handler: interface.HandlerFn) !void {
 | 
					fn processRequest(aa: std.mem.Allocator, server: *std.net.Server, event_handler: interface.HandlerFn) !void {
 | 
				
			||||||
 | 
					    // This function is under test, but not the standalone server itself
 | 
				
			||||||
    var connection = try server.accept();
 | 
					    var connection = try server.accept();
 | 
				
			||||||
    defer connection.stream.close();
 | 
					    defer connection.stream.close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -189,43 +191,48 @@ test {
 | 
				
			||||||
    // TODO: Do we want build files here too?
 | 
					    // TODO: Do we want build files here too?
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn testRequest(request_bytes: []const u8, event_handler: interface.HandlerFn) !void {
 | 
					var server_ready = false;
 | 
				
			||||||
    const allocator = std.testing.allocator;
 | 
					var remaining_requests: ?usize = null;
 | 
				
			||||||
    var arena = std.heap.ArenaAllocator.init(allocator);
 | 
					var server_url: ["http://127.0.0.1:99999".len]u8 = undefined;
 | 
				
			||||||
    defer arena.deinit();
 | 
					var bytes_allocated: usize = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const address = try std.net.Address.parseIp("127.0.0.1", 0);
 | 
					fn testRequest(method: std.http.Method, target: []const u8, event_handler: interface.HandlerFn) !void {
 | 
				
			||||||
    var http_server = try address.listen(.{ .reuse_address = true });
 | 
					    remaining_requests = 1;
 | 
				
			||||||
    const server_port = http_server.listen_address.in.getPort();
 | 
					    defer remaining_requests = null;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    var al = std.ArrayList(u8).init(allocator);
 | 
					 | 
				
			||||||
    defer al.deinit();
 | 
					 | 
				
			||||||
    const writer = al.writer();
 | 
					 | 
				
			||||||
    _ = writer;
 | 
					 | 
				
			||||||
    const aa = arena.allocator();
 | 
					 | 
				
			||||||
    const bytes_allocated: usize = 0;
 | 
					 | 
				
			||||||
    // pre-warm
 | 
					 | 
				
			||||||
    const server_thread = try std.Thread.spawn(
 | 
					    const server_thread = try std.Thread.spawn(
 | 
				
			||||||
        .{},
 | 
					        .{},
 | 
				
			||||||
        processRequest,
 | 
					        runStandaloneServer,
 | 
				
			||||||
        .{ aa, &http_server, event_handler },
 | 
					        .{ null, event_handler, 0 },
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					    var client = std.http.Client{ .allocator = std.testing.allocator };
 | 
				
			||||||
 | 
					    defer client.deinit();
 | 
				
			||||||
 | 
					    defer server_ready = false;
 | 
				
			||||||
 | 
					    while (!server_ready) std.time.sleep(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const stream = try std.net.tcpConnectToHost(allocator, "127.0.0.1", server_port);
 | 
					    const url = try std.mem.concat(std.testing.allocator, u8, &[_][]const u8{ server_url[0..], target });
 | 
				
			||||||
    defer stream.close();
 | 
					    defer std.testing.allocator.free(url);
 | 
				
			||||||
    _ = try stream.writeAll(request_bytes[0..]);
 | 
					    log.debug("fetch from url: {s}", .{url});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var response_data = std.ArrayList(u8).init(std.testing.allocator);
 | 
				
			||||||
 | 
					    defer response_data.deinit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const resp = try client.fetch(.{
 | 
				
			||||||
 | 
					        .response_storage = .{ .dynamic = &response_data },
 | 
				
			||||||
 | 
					        .method = method,
 | 
				
			||||||
 | 
					        .location = .{ .url = url },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    server_thread.join();
 | 
					    server_thread.join();
 | 
				
			||||||
    log.debug("Bytes allocated during request: {d}", .{arena.queryCapacity() - bytes_allocated});
 | 
					    log.debug("Bytes allocated during request: {d}", .{bytes_allocated});
 | 
				
			||||||
    log.debug("Stdout: {s}", .{al.items});
 | 
					    log.debug("Response status: {}", .{resp.status});
 | 
				
			||||||
 | 
					    log.debug("Response: {s}", .{response_data.items});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn testGet(comptime path: []const u8, event_handler: interface.HandlerFn) !void {
 | 
					fn testGet(comptime path: []const u8, event_handler: interface.HandlerFn) !void {
 | 
				
			||||||
    try testRequest("GET " ++ path ++ " HTTP/1.1\r\n" ++
 | 
					    try testRequest(.GET, path, event_handler);
 | 
				
			||||||
        "Accept: */*\r\n" ++
 | 
					 | 
				
			||||||
        "\r\n", event_handler);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
test "can make a request" {
 | 
					test "can make a request" {
 | 
				
			||||||
 | 
					    // std.testing.log_level = .debug;
 | 
				
			||||||
    if (@import("builtin").os.tag == .wasi) return error.SkipZigTest;
 | 
					    if (@import("builtin").os.tag == .wasi) return error.SkipZigTest;
 | 
				
			||||||
    const HandlerClosure = struct {
 | 
					    const HandlerClosure = struct {
 | 
				
			||||||
        var data_received: []const u8 = undefined;
 | 
					        var data_received: []const u8 = undefined;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue