Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 54 additions & 13 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -131,43 +131,84 @@ pub fn build(b: *std.Build) void {

b.installArtifact(lib);

const lib_unit_tests = b.addTest(.{ .root_module = lib_mod });
const test_runner = b.path("test_runner.zig");

const lib_unit_tests = b.addTest(.{
.root_module = lib_mod,
.test_runner = .{ .path = test_runner, .mode = .simple },
});
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);

const ascii_unit_tests = b.addTest(.{ .root_module = ascii_mod });
const ascii_unit_tests = b.addTest(.{
.root_module = ascii_mod,
.test_runner = .{ .path = test_runner, .mode = .simple },
});
const run_ascii_unit_tests = b.addRunArtifact(ascii_unit_tests);

const ccc_unit_tests = b.addTest(.{ .root_module = ccc_mod });
const ccc_unit_tests = b.addTest(.{
.root_module = ccc_mod,
.test_runner = .{ .path = test_runner, .mode = .simple },
});
const run_ccc_unit_tests = b.addRunArtifact(ccc_unit_tests);

const cea_unit_tests = b.addTest(.{ .root_module = cea_mod });
const cea_unit_tests = b.addTest(.{
.root_module = cea_mod,
.test_runner = .{ .path = test_runner, .mode = .simple },
});
const run_cea_unit_tests = b.addRunArtifact(cea_unit_tests);

const collator_unit_tests = b.addTest(.{ .root_module = collator_mod });
const collator_unit_tests = b.addTest(.{
.root_module = collator_mod,
.test_runner = .{ .path = test_runner, .mode = .simple },
});
const run_collator_unit_tests = b.addRunArtifact(collator_unit_tests);

const consts_unit_tests = b.addTest(.{ .root_module = consts_mod });
const consts_unit_tests = b.addTest(.{
.root_module = consts_mod,
.test_runner = .{ .path = test_runner, .mode = .simple },
});
const run_consts_unit_tests = b.addRunArtifact(consts_unit_tests);

const decode_unit_tests = b.addTest(.{ .root_module = decode_mod });
const decode_unit_tests = b.addTest(.{
.root_module = decode_mod,
.test_runner = .{ .path = test_runner, .mode = .simple },
});
const run_decode_unit_tests = b.addRunArtifact(decode_unit_tests);

const load_unit_tests = b.addTest(.{ .root_module = load_mod });
const load_unit_tests = b.addTest(.{
.root_module = load_mod,
.test_runner = .{ .path = test_runner, .mode = .simple },
});
const run_load_unit_tests = b.addRunArtifact(load_unit_tests);

const normalize_unit_tests = b.addTest(.{ .root_module = normalize_mod });
const normalize_unit_tests = b.addTest(.{
.root_module = normalize_mod,
.test_runner = .{ .path = test_runner, .mode = .simple },
});
const run_normalize_unit_tests = b.addRunArtifact(normalize_unit_tests);

const prefix_unit_tests = b.addTest(.{ .root_module = prefix_mod });
const prefix_unit_tests = b.addTest(.{
.root_module = prefix_mod,
.test_runner = .{ .path = test_runner, .mode = .simple },
});
const run_prefix_unit_tests = b.addRunArtifact(prefix_unit_tests);

const sort_key_unit_tests = b.addTest(.{ .root_module = sort_key_mod });
const sort_key_unit_tests = b.addTest(.{
.root_module = sort_key_mod,
.test_runner = .{ .path = test_runner, .mode = .simple },
});
const run_sort_key_unit_tests = b.addRunArtifact(sort_key_unit_tests);

const types_unit_tests = b.addTest(.{ .root_module = types_mod });
const types_unit_tests = b.addTest(.{
.root_module = types_mod,
.test_runner = .{ .path = test_runner, .mode = .simple },
});
const run_types_unit_tests = b.addRunArtifact(types_unit_tests);

const util_unit_tests = b.addTest(.{ .root_module = util_mod });
const util_unit_tests = b.addTest(.{
.root_module = util_mod,
.test_runner = .{ .path = test_runner, .mode = .simple },
});
const run_util_unit_tests = b.addRunArtifact(util_unit_tests);

const test_step = b.step("test", "Run unit tests");
Expand Down
20 changes: 10 additions & 10 deletions src/decode.zig
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ fn expectDecoded(input: []const u8, expected: []const u32) !void {
try testing.expectEqualSlices(u32, expected, result.items);
}

test "decode 4-byte code point" {
test "decode 4-byte scalar" {
const testing = std.testing;
const alloc = testing.allocator;

Expand All @@ -107,7 +107,7 @@ test "decode 4-byte code point" {
try testing.expectEqual(0x1BC9E, result.items[0]);
}

test "invalid sequence does not consume following ASCII bytes" {
test "invalid sequence does not consume following ASCII" {
const input = [_]u8{ 0xC2, 0x41, 0x42 };
const expected = [_]u32{ REPLACEMENT, 0x41, 0x42 };

Expand All @@ -121,7 +121,7 @@ test "invalid sequence restarts from next valid starter" {
try expectDecoded(&input, &expected);
}

test "non-shortest form sequences become replacement characters" {
test "overlong sequences become replacements" {
const input = [_]u8{ 0xC0, 0xAF, 0xE0, 0x80, 0xBF, 0xF0, 0x81, 0x82, 0x41 };
const expected = [_]u32{
REPLACEMENT, REPLACEMENT, REPLACEMENT, REPLACEMENT, REPLACEMENT,
Expand All @@ -131,7 +131,7 @@ test "non-shortest form sequences become replacement characters" {
try expectDecoded(&input, &expected);
}

test "surrogate UTF-8 sequences become replacement characters" {
test "surrogate sequences become replacements" {
const input = [_]u8{ 0xED, 0xA0, 0x80, 0xED, 0xBF, 0xBF, 0xED, 0xAF, 0x41 };
const expected = [_]u32{
REPLACEMENT, REPLACEMENT, REPLACEMENT, REPLACEMENT, REPLACEMENT,
Expand All @@ -141,7 +141,7 @@ test "surrogate UTF-8 sequences become replacement characters" {
try expectDecoded(&input, &expected);
}

test "other ill-formed sequences become replacement characters" {
test "other ill-formed sequences become replacements" {
const input = [_]u8{ 0xF4, 0x91, 0x92, 0x93, 0xFF, 0x41, 0x80, 0xBF, 0x42 };
const expected = [_]u32{
REPLACEMENT, REPLACEMENT, REPLACEMENT, REPLACEMENT, REPLACEMENT, 0x41,
Expand All @@ -151,14 +151,14 @@ test "other ill-formed sequences become replacement characters" {
try expectDecoded(&input, &expected);
}

test "truncated sequences become replacement characters" {
test "truncated sequences become replacements" {
const input = [_]u8{ 0xE1, 0x80, 0xE2, 0xF0, 0x91, 0x92, 0xF1, 0xBF, 0x41 };
const expected = [_]u32{ REPLACEMENT, REPLACEMENT, REPLACEMENT, REPLACEMENT, 0x41 };

try expectDecoded(&input, &expected);
}

test "decode boundary valid UTF-8 scalars" {
test "decode UTF-8 scalar edge values" {
const input = [_]u8{
0x00, 0x7F, 0xC2, 0x80, 0xDF, 0xBF, 0xE0, 0xA0,
0x80, 0xEF, 0xBF, 0xBF, 0xF0, 0x90, 0x80, 0x80,
Expand All @@ -169,21 +169,21 @@ test "decode boundary valid UTF-8 scalars" {
try expectDecoded(&input, &expected);
}

test "decode mixed valid and invalid UTF-8 stream" {
test "decode mixed valid and invalid stream" {
const input = [_]u8{ 0x41, 0xC2, 0x42, 0xE2, 0x82, 0xAC, 0x80, 0xF0, 0x9F, 0x98, 0x80 };
const expected = [_]u32{ 0x41, REPLACEMENT, 0x42, 0x20AC, REPLACEMENT, 0x1F600 };

try expectDecoded(&input, &expected);
}

test "out of range UTF-8 prefix does not consume following bytes" {
test "out-of-range prefix does not consume following bytes" {
const input = [_]u8{ 0xF4, 0x90, 0x80, 0x80 };
const expected = [_]u32{ REPLACEMENT, REPLACEMENT, REPLACEMENT, REPLACEMENT };

try expectDecoded(&input, &expected);
}

test "truncated sequences at EOF emit one replacement each" {
test "truncated sequence at EOF emits one replacement" {
try expectDecoded(&[_]u8{0xC2}, &[_]u32{REPLACEMENT});
try expectDecoded(&[_]u8{ 0xE1, 0x80 }, &[_]u32{REPLACEMENT});
try expectDecoded(&[_]u8{ 0xF1, 0x80, 0x80 }, &[_]u32{REPLACEMENT});
Expand Down
69 changes: 69 additions & 0 deletions test_runner.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
const std = @import("std");
const builtin = @import("builtin");

pub fn main(init: std.process.Init) !void {
const io = init.io;

if (builtin.test_functions.len == 0) return;

var pass: usize = 0;
var fail: usize = 0;
var skip: usize = 0;
var leak: usize = 0;

for (builtin.test_functions) |t| {
std.testing.allocator_instance = .{};

const start = std.Io.Clock.Timestamp.now(io, .awake);
const result = t.func();
const end = std.Io.Clock.Timestamp.now(io, .awake);

const ns: u64 = @intCast(start.durationTo(end).raw.nanoseconds);
const ms = @as(f64, @floatFromInt(ns)) / 1_000_000.0;

var status: enum { pass, fail, skip } = .pass;

if (result) |_| {
pass += 1;
} else |err| switch (err) {
error.SkipZigTest => {
skip += 1;
status = .skip;
},
else => {
fail += 1;
status = .fail;

std.debug.print("\nFAIL: {s}: {s}\n", .{ t.name, @errorName(err) });

if (@errorReturnTrace()) |trace| {
std.debug.dumpErrorReturnTrace(trace);
}
},
}

var leaked = false;

if (std.testing.allocator_instance.deinit() == .leak) {
leak += 1;
leaked = true;
status = .fail;
std.debug.print("\nLEAK: {s}\n", .{t.name});
}

const status_str = switch (status) {
.pass => "OK",
.fail => if (leaked) "FAIL (LEAK)" else "FAIL",
.skip => "SKIP",
};

std.debug.print("{s} ... {s} ({d:.2}ms)\n", .{ t.name, status_str, ms });
}

std.debug.print(
"==> {d} passed, {d} failed, {d} skipped, {d} leaked\n\n",
.{ pass, fail, skip, leak },
);

std.process.exit(if (fail == 0 and leak == 0) 0 else 1);
}
Loading