Skip to content

Commit ab47cb9

Browse files
committed
better test web server management
1 parent ae8298b commit ab47cb9

File tree

1 file changed

+73
-15
lines changed

1 file changed

+73
-15
lines changed

src/aws.zig

Lines changed: 73 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,7 +1533,7 @@ const TestOptions = struct {
15331533
request_target: []const u8 = undefined,
15341534
request_headers: []std.http.Header = undefined,
15351535
test_server_runtime_uri: ?[]u8 = null,
1536-
server_ready: bool = false,
1536+
server_ready: std.Thread.Semaphore = .{},
15371537
requests_processed: usize = 0,
15381538

15391539
const Self = @This();
@@ -1588,11 +1588,18 @@ const TestOptions = struct {
15881588
return error.HeaderOrValueNotFound;
15891589
}
15901590
fn waitForReady(self: *Self) !void {
1591-
// Set 1 minute timeout...this is way longer than necessary
1592-
var remaining_iters: isize = std.time.ns_per_min / 100;
1593-
while (!self.server_ready and remaining_iters > 0) : (remaining_iters -= 1)
1594-
std.time.sleep(100);
1595-
if (!self.server_ready) return error.TestServerTimeoutWaitingForReady;
1591+
// Set 10s timeout...this is way longer than necessary
1592+
log.debug("waiting for ready", .{});
1593+
try self.server_ready.timedWait(1000 * std.time.ns_per_ms);
1594+
// var deadline = std.Thread.Futex.Deadline.init(1000 * std.time.ns_per_ms);
1595+
// if (self.futex_word.load(.acquire) != 0) return;
1596+
// log.debug("futex zero", .{});
1597+
// // note that this seems backwards from the documentation...
1598+
// deadline.wait(self.futex_word, 1) catch {
1599+
// log.err("futex value {d}", .{self.futex_word.load(.acquire)});
1600+
// return error.TestServerTimeoutWaitingForReady;
1601+
// };
1602+
log.debug("the wait is over!", .{});
15961603
}
15971604
};
15981605

@@ -1620,9 +1627,9 @@ fn threadMain(options: *TestOptions) !void {
16201627
// var aa = arena.allocator();
16211628
// We're in control of all requests/responses, so this flag will tell us
16221629
// when it's time to shut down
1623-
defer options.server_ready = true; // In case remaining_requests = 0, we don't want to wait forever
1624-
while (options.server_remaining_requests > 0) {
1625-
options.server_remaining_requests -= 1;
1630+
if (options.server_remaining_requests == 0)
1631+
options.server_ready.post(); // This will cause the wait for server to return
1632+
while (options.server_remaining_requests > 0) : (options.server_remaining_requests -= 1) {
16261633
processRequest(options, &http_server) catch |e| {
16271634
log.err("Unexpected error processing request: {any}", .{e});
16281635
if (@errorReturnTrace()) |trace| {
@@ -1633,12 +1640,13 @@ fn threadMain(options: *TestOptions) !void {
16331640
}
16341641

16351642
fn processRequest(options: *TestOptions, net_server: *std.net.Server) !void {
1636-
options.server_ready = true;
1637-
errdefer options.server_ready = false;
16381643
log.debug(
16391644
"tid {d} (server): server waiting to accept. requests remaining: {d}",
1640-
.{ std.Thread.getCurrentId(), options.server_remaining_requests + 1 },
1645+
.{ std.Thread.getCurrentId(), options.server_remaining_requests },
16411646
);
1647+
// options.futex_word.store(1, .release);
1648+
// errdefer options.futex_word.store(0, .release);
1649+
options.server_ready.post();
16421650
var connection = try net_server.accept();
16431651
defer connection.stream.close();
16441652
var read_buffer: [1024 * 16]u8 = undefined;
@@ -1657,8 +1665,6 @@ fn processRequest(options: *TestOptions, net_server: *std.net.Server) !void {
16571665
}
16581666

16591667
fn serveRequest(options: *TestOptions, request: *std.http.Server.Request) !void {
1660-
options.server_ready = false;
1661-
16621668
options.requests_processed += 1;
16631669
options.request_body = try (try request.reader()).readAllAlloc(options.allocator, std.math.maxInt(usize));
16641670
options.request_method = request.head.method;
@@ -1728,7 +1734,8 @@ const TestSetup = struct {
17281734
// Not sure why we're getting sprayed here, but we have an arena allocator, and this
17291735
// is testing, so yolo
17301736
awshttp.endpoint_override = self.request_options.test_server_runtime_uri;
1731-
log.debug("endpoint override set to {?s}", .{awshttp.endpoint_override});
1737+
if (awshttp.endpoint_override == null) return error.TestSetupStartFailure;
1738+
std.log.debug("endpoint override set to {?s}", .{awshttp.endpoint_override});
17321739
self.creds = aws_auth.Credentials.init(
17331740
self.allocator,
17341741
try self.allocator.dupe(u8, "ACCESS"),
@@ -1746,6 +1753,27 @@ const TestSetup = struct {
17461753
}
17471754

17481755
fn stop(self: *Self) void {
1756+
if (self.request_options.server_remaining_requests > 0)
1757+
if (test_error_log_enabled)
1758+
std.log.err(
1759+
"Test server has {d} request(s) remaining to issue! Draining",
1760+
.{self.request_options.server_remaining_requests},
1761+
)
1762+
else
1763+
std.log.info(
1764+
"Test server has {d} request(s) remaining to issue! Draining",
1765+
.{self.request_options.server_remaining_requests},
1766+
);
1767+
1768+
var rr = self.request_options.server_remaining_requests;
1769+
while (rr > 0) : (rr -= 1) {
1770+
std.log.debug("rr: {d}", .{self.request_options.server_remaining_requests});
1771+
// We need to drain all remaining requests, otherwise the server
1772+
// will hang indefinitely
1773+
var client = std.http.Client{ .allocator = self.allocator };
1774+
defer client.deinit();
1775+
_ = client.fetch(.{ .location = .{ .url = self.request_options.test_server_runtime_uri.? } }) catch unreachable;
1776+
}
17491777
self.server_thread.join();
17501778
}
17511779

@@ -2391,3 +2419,33 @@ test "json_1_1: ECR timestamps" {
23912419
// try std.testing.expectEqual(@as(i64, 1.73859841557E9), call.response.authorization_data.?[0].expires_at.?);
23922420
try std.testing.expectEqual(@as(f128, 1.7385984915E9), call.response.authorization_data.?[0].expires_at.?);
23932421
}
2422+
var test_error_log_enabled = true;
2423+
test "test server timeout works" {
2424+
// const old = std.testing.log_level;
2425+
// defer std.testing.log_level = old;
2426+
// std.testing.log_level = .debug;
2427+
// defer std.testing.log_level = old;
2428+
// std.testing.log_level = .debug;
2429+
test_error_log_enabled = false;
2430+
defer test_error_log_enabled = true;
2431+
std.log.debug("test start", .{});
2432+
const allocator = std.testing.allocator;
2433+
var test_harness = TestSetup.init(.{
2434+
.allocator = allocator,
2435+
.server_response =
2436+
\\{}
2437+
,
2438+
.server_response_headers = &.{
2439+
.{ .name = "Content-Type", .value = "application/json" },
2440+
.{ .name = "x-amzn-RequestId", .value = "QBI72OUIN8U9M9AG6PCSADJL4JVV4KQNSO5AEMVJF66Q9ASUAAJG" },
2441+
},
2442+
});
2443+
defer test_harness.deinit();
2444+
defer test_harness.creds.deinit(); // Usually this gets done during the call,
2445+
// but we're purposely not making a call
2446+
// here, so we have to deinit() manually
2447+
_ = try test_harness.start();
2448+
std.log.debug("harness started", .{});
2449+
test_harness.stop();
2450+
std.log.debug("test complete", .{});
2451+
}

0 commit comments

Comments
 (0)