|
1 | 1 | const std = @import("std"); |
| 2 | +const clap = @import("clap"); |
| 3 | + |
2 | 4 | const HttpParser = @import("./httpfile/parser.zig"); |
3 | 5 | const Client = @import("./httpfile/http_client.zig"); |
4 | 6 | const AssertionChecker = @import("./httpfile/assertion_checker.zig"); |
5 | | -const clap = @import("clap"); |
| 7 | +const TestReporter = @import("./reporters/test_reporter.zig"); |
6 | 8 |
|
7 | 9 | pub fn main() !void { |
8 | 10 | var debug = std.heap.DebugAllocator(.{}){}; |
9 | 11 | defer _ = debug.deinit(); |
10 | 12 |
|
| 13 | + const threads = std.process.parseEnvVarInt("HTTP_THREAD_COUNT", usize, 10) catch 1; |
| 14 | + |
11 | 15 | const allocator = debug.allocator(); |
12 | 16 |
|
13 | 17 | const params = comptime clap.parseParamsComptime( |
@@ -66,51 +70,65 @@ pub fn main() !void { |
66 | 70 | } |
67 | 71 | } |
68 | 72 |
|
69 | | - var test_count: usize = 0; |
70 | | - var test_pass: usize = 0; |
71 | | - var test_fail: usize = 0; |
72 | 73 | // TODO: This is simple, but completely serial. Ideally, we'd span this across multiple threads. |
73 | | - for (files.items) |pos| { |
74 | | - test_count += 1; |
75 | | - var has_failure = false; |
| 74 | + |
| 75 | + var pool: std.Thread.Pool = undefined; |
| 76 | + try pool.init(.{ |
| 77 | + .allocator = allocator, |
| 78 | + .n_jobs = threads, |
| 79 | + }); |
| 80 | + defer pool.deinit(); |
| 81 | + |
| 82 | + var wg: std.Thread.WaitGroup = .{}; |
| 83 | + var reporter = TestReporter.BasicReporter.init(); |
| 84 | + |
| 85 | + for (files.items) |path| { |
76 | 86 | // TODO: Each one gets its own areana? |
77 | | - std.io.getStdOut().writer().print("Running test {d}: {s}\n", .{ test_count, pos }) catch |err| { |
78 | | - std.debug.print("Error writing to stdout: {}\n", .{err}); |
79 | | - return err; |
80 | | - }; |
81 | | - var items = try HttpParser.parseFile(allocator, pos); |
82 | | - const owned_items = try items.toOwnedSlice(); |
83 | | - defer allocator.free(owned_items); |
84 | | - var client = Client.HttpClient.init(allocator); |
85 | | - defer client.deinit(); |
86 | | - for (owned_items) |*owned_item| { |
87 | | - defer owned_item.deinit(allocator); |
88 | | - var responses = try client.execute(owned_item); |
89 | | - defer responses.deinit(); |
90 | | - // Check assertions |
91 | | - AssertionChecker.check(owned_item, responses) catch { |
92 | | - has_failure = true; |
93 | | - break; |
94 | | - }; |
95 | | - } |
96 | | - if (!has_failure) { |
97 | | - test_pass += 1; |
98 | | - } else { |
99 | | - test_fail += 1; |
100 | | - } |
| 87 | + pool.spawnWg(&wg, runTest, .{ allocator, &reporter, path }); |
101 | 88 | } |
102 | 89 |
|
103 | | - std.io.getStdOut().writer().print( |
104 | | - \\ |
105 | | - \\All {d} tests ran successfully! |
106 | | - \\ |
107 | | - \\Pass: {d} |
108 | | - \\Fail: {d} |
109 | | - \\ |
110 | | - , .{ test_count, test_pass, test_fail }) catch |err| { |
111 | | - std.debug.print("Error writing to stdout: {}\n", .{err}); |
112 | | - return err; |
| 90 | + wg.wait(); |
| 91 | + reporter.report(std.io.getStdOut().writer()); |
| 92 | +} |
| 93 | + |
| 94 | +fn runTest(allocator: std.mem.Allocator, reporter: *TestReporter.BasicReporter, path: []const u8) void { |
| 95 | + var has_failure = false; |
| 96 | + |
| 97 | + reporter.incTestCount(); |
| 98 | + var items = HttpParser.parseFile(allocator, path) catch |err| { |
| 99 | + reporter.incTestInvalid(); |
| 100 | + std.debug.print("Failed to parse file {s}: {s}\n", .{ path, @errorName(err) }); |
| 101 | + return; |
| 102 | + }; |
| 103 | + const owned_items = items.toOwnedSlice() catch |err| { |
| 104 | + // TODO: This seems like an US error, not an invalid test error. |
| 105 | + reporter.incTestInvalid(); |
| 106 | + std.debug.print("Failed to convert items to owned slice in file {s}: {s}\n", .{ path, @errorName(err) }); |
| 107 | + return; |
113 | 108 | }; |
| 109 | + defer allocator.free(owned_items); |
| 110 | + |
| 111 | + var client = Client.HttpClient.init(allocator); |
| 112 | + defer client.deinit(); |
| 113 | + for (owned_items) |*owned_item| { |
| 114 | + defer owned_item.deinit(allocator); |
| 115 | + var responses = client.execute(owned_item) catch |err| { |
| 116 | + reporter.incTestInvalid(); |
| 117 | + std.debug.print("Failed to execute request in file {s}: {s}\n", .{ path, @errorName(err) }); |
| 118 | + return; |
| 119 | + }; |
| 120 | + defer responses.deinit(); |
| 121 | + // Check assertions |
| 122 | + AssertionChecker.check(owned_item, responses) catch { |
| 123 | + has_failure = true; |
| 124 | + break; |
| 125 | + }; |
| 126 | + } |
| 127 | + if (!has_failure) { |
| 128 | + reporter.incTestPass(); |
| 129 | + } else { |
| 130 | + reporter.incTestFail(); |
| 131 | + } |
114 | 132 | } |
115 | 133 |
|
116 | 134 | // List all HTTP files in the given directory and its subdirectories |
|
0 commit comments