aboutsummaryrefslogtreecommitdiff
path: root/src/test/tester.zig
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-05-05 19:02:30 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-05-05 19:02:30 -0700
commitd975ab19db14b75b1099470cdf984329537a20aa (patch)
treedfb0845f0e3db99f72ba2217ef0d6b3804592690 /src/test/tester.zig
parent41c9896e11948b91dea278636c226516f7c01f3d (diff)
downloadbun-d975ab19db14b75b1099470cdf984329537a20aa.tar.gz
bun-d975ab19db14b75b1099470cdf984329537a20aa.tar.zst
bun-d975ab19db14b75b1099470cdf984329537a20aa.zip
cool
Former-commit-id: 3708dd44843274f397ae28b2cead39de8f55e6a6
Diffstat (limited to 'src/test/tester.zig')
-rw-r--r--src/test/tester.zig159
1 files changed, 159 insertions, 0 deletions
diff --git a/src/test/tester.zig b/src/test/tester.zig
new file mode 100644
index 000000000..6b3174eac
--- /dev/null
+++ b/src/test/tester.zig
@@ -0,0 +1,159 @@
+const std = @import("std");
+
+const string = []const u8;
+
+const RED = "\x1b[31;1m";
+const GREEN = "\x1b[32;1m";
+const CYAN = "\x1b[36;1m";
+const WHITE = "\x1b[37;1m";
+const DIM = "\x1b[2m";
+const RESET = "\x1b[0m";
+
+pub const Tester = struct {
+ pass: std.ArrayList(Expectation),
+ fail: std.ArrayList(Expectation),
+ allocator: *std.mem.Allocator,
+
+ pub fn t(allocator: *std.mem.Allocator) Tester {
+ return Tester{
+ .allocator = allocator,
+ .pass = std.ArrayList(Expectation).init(allocator),
+ .fail = std.ArrayList(Expectation).init(allocator),
+ };
+ }
+
+ pub const Expectation = struct {
+ expected: string,
+ result: string,
+ source: std.builtin.SourceLocation,
+
+ pub fn init(expected: string, result: string, src: std.builtin.SourceLocation) Expectation {
+ return Expectation{
+ .expected = expected,
+ .result = result,
+ .source = src,
+ };
+ }
+ const PADDING = 0;
+ pub fn print(self: *const @This()) void {
+ var pad = &([_]u8{' '} ** PADDING);
+ var stderr = std.io.getStdErr();
+
+ stderr.writeAll(RESET) catch unreachable;
+ stderr.writeAll(pad) catch unreachable;
+ stderr.writeAll(DIM) catch unreachable;
+ std.fmt.format(stderr.writer(), "{s}:{d}:{d}", .{ self.source.file, self.source.line, self.source.column }) catch unreachable;
+ stderr.writeAll(RESET) catch unreachable;
+ stderr.writeAll("\n") catch unreachable;
+
+ stderr.writeAll(pad) catch unreachable;
+ stderr.writeAll("Expected: ") catch unreachable;
+ stderr.writeAll(RESET) catch unreachable;
+ stderr.writeAll(GREEN) catch unreachable;
+ std.fmt.format(stderr.writer(), "\"{s}\"", .{self.expected}) catch unreachable;
+ stderr.writeAll(GREEN) catch unreachable;
+ stderr.writeAll(RESET) catch unreachable;
+
+ stderr.writeAll("\n") catch unreachable;
+ stderr.writeAll(pad) catch unreachable;
+ stderr.writeAll("Received: ") catch unreachable;
+ stderr.writeAll(RESET) catch unreachable;
+ stderr.writeAll(RED) catch unreachable;
+ std.fmt.format(stderr.writer(), "\"{s}\"", .{self.result}) catch unreachable;
+ stderr.writeAll(RED) catch unreachable;
+ stderr.writeAll(RESET) catch unreachable;
+ stderr.writeAll("\n") catch unreachable;
+ }
+
+ pub fn evaluate_outcome(self: *const @This()) Outcome {
+ for (self.expected) |char, i| {
+ if (char != self.result[i]) {
+ return Outcome.fail;
+ }
+ }
+
+ return Outcome.pass;
+ }
+ };
+
+ pub const Outcome = enum {
+ pass,
+ fail,
+ };
+
+ pub fn expect(tester: *Tester, expected: string, result: string, src: std.builtin.SourceLocation) callconv(.Inline) bool {
+ var expectation = Expectation.init(expected, result, src);
+ switch (expectation.evaluate_outcome()) {
+ .pass => {
+ tester.pass.append(expectation) catch unreachable;
+ return true;
+ },
+ .fail => {
+ tester.fail.append(expectation) catch unreachable;
+ return false;
+ },
+ }
+ }
+
+ const ReportType = enum {
+ none,
+ pass,
+ fail,
+ some_fail,
+
+ pub fn init(tester: *Tester) ReportType {
+ if (tester.fail.items.len == 0 and tester.pass.items.len == 0) {
+ return .none;
+ } else if (tester.fail.items.len == 0) {
+ return .pass;
+ } else if (tester.pass.items.len == 0) {
+ return .fail;
+ } else {
+ return .some_fail;
+ }
+ }
+ };
+
+ pub fn report(tester: *Tester, src: std.builtin.SourceLocation) void {
+ var stderr = std.io.getStdErr();
+
+ if (tester.fail.items.len > 0) {
+ std.fmt.format(stderr.writer(), "\n\n", .{}) catch unreachable;
+ }
+
+ for (tester.fail.items) |item| {
+ item.print();
+ std.fmt.format(stderr.writer(), "\n", .{}) catch unreachable;
+ }
+
+ switch (ReportType.init(tester)) {
+ .none => {
+ std.log.info("No expectations.\n\n", .{});
+ },
+ .pass => {
+ std.fmt.format(stderr.writer(), "{s}All {d} expectations passed.{s}\n", .{ GREEN, tester.pass.items.len, GREEN }) catch unreachable;
+ std.fmt.format(stderr.writer(), RESET, .{}) catch unreachable;
+ std.testing.expect(true);
+ },
+ .fail => {
+ std.fmt.format(stderr.writer(), "{s}All {d} expectations failed.{s}\n\n", .{ RED, tester.fail.items.len, RED }) catch unreachable;
+ std.fmt.format(stderr.writer(), RESET, .{}) catch unreachable;
+ std.testing.expect(false);
+ },
+ .some_fail => {
+ std.fmt.format(stderr.writer(), "{s}{d} failed{s} and {s}{d} passed{s} of {d} expectations{s}\n\n", .{
+ RED,
+ tester.fail.items.len,
+ RED ++ RESET,
+ GREEN,
+ tester.pass.items.len,
+ GREEN ++ RESET,
+ tester.fail.items.len + tester.pass.items.len,
+ RESET,
+ }) catch unreachable;
+ std.fmt.format(stderr.writer(), RESET, .{}) catch unreachable;
+ std.testing.expect(false);
+ },
+ }
+ }
+};