~alva/zig-bare

a17ae6a098f8c7ebe2a36d6d4a34862a81c48f8e — Alva 4 months ago f87375e
Test compile errors
2 files changed, 212 insertions(+), 38 deletions(-)

M src/bare.zig
M src/test.zig
M src/bare.zig => src/bare.zig +3 -3
@@ 106,7 106,7 @@ pub const Reader = struct {
        var s: T = undefined;

        if (ti.fields.len < 1)
            @compileError("structs must have at least one field");
            @compileError("structs must have 1 or more fields");

        inline for (ti.fields) |f|
            @field(s, f.name) = try self.read(f.field_type, stream);


@@ 134,7 134,7 @@ pub const Reader = struct {
        var i: usize = 0;

        if (ti.len < 1)
            @compileError("array length must be > 0");
            @compileError("array length must be at least 1");

        while (i < ti.len) : (i += 1)
            buf[i] = try self.read(ti.child, stream);


@@ 280,7 280,7 @@ pub const Writer = struct {
        const ti = @typeInfo(@TypeOf(value)).Struct;

        if (ti.fields.len < 1)
            @compileError("structs must have at least one field");
            @compileError("structs must have 1 or more fields");

        inline for (ti.fields) |f|
            try self.write(@field(value, f.name), stream);

M src/test.zig => src/test.zig +209 -35
@@ 16,8 16,6 @@ const expectEqualSlices = testing.expectEqualSlices;
const expectError = testing.expectError;
const warn = std.debug.warn;

// TODO: Test that expected compile errors actually result in compile errors.

test "read variable uint" {
    const x = try Reader.init(testing.allocator).readVarUint(io.fixedBufferStream("\x2a").inStream());
    expectEqual(x, 42);


@@ 130,7 128,14 @@ test "read struct 6" {
}

test "read struct with void members" {
    // Compile error.
    try testCompileError(
        \\pub fn main() void {
        \\  var fbs = io.fixedBufferStream("lol");
        \\  const Foo = struct { a: u8, b: void };
        \\  _ = try Reader.init(testing.allocator).read(Foo, fbs.inStream());
        \\}
        , "unsupported type"
    );
}

test "read enum" {


@@ 204,7 209,14 @@ test "read map[string]u8" {
}

test "read map[u8]void" {
    // Compile error.
    try testCompileError(
        \\pub fn main() void {
        \\  var fbs = io.fixedBufferStream("lol");
        \\  const HM = std.AutoHashMap(u8, void);
        \\  _ = try Reader.init(testing.allocator).read(HM, fbs.inStream());
        \\}
        , "unsupported type"
    );
}

test "read tagged union" {


@@ 214,11 226,23 @@ test "read tagged union" {
}

test "read void" {
    // Compile error.
    try testCompileError(
        \\pub fn main() void {
        \\  var fbs = io.fixedBufferStream("lol");
        \\  _ = try Reader.init(testing.allocator).read(void, fbs.inStream());
        \\}
        , "unsupported type"
    );
}

test "read unsupported type" {
    // This is a compile error now.
    try testCompileError(
        \\pub fn main() void {
        \\  var fbs = io.fixedBufferStream("lol");
        \\  _ = try Reader.init(testing.allocator).read(u7, fbs.inStream());
        \\}
        , "unsupported type"
    );
}

test "write variable uint" {


@@ 270,7 294,15 @@ test "write struct" {
}

test "write struct with void members" {
    // Compile error.
    try testCompileError(
        \\pub fn main() void {
        \\  const Foo = struct { x: u8, y: void };
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  try Writer.init().write(Foo{ .x = 1, .y = {} }, fbs.outStream());
        \\}
        , "unsupported type"
    );
}

test "write enum" {


@@ 306,7 338,15 @@ test "write optional u8 null" {
}

test "write optional void" {
    // This is a compile error now.
    try testCompileError(
        \\pub fn main() void {
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  const foo: ?void = null;
        \\  try Writer.init().write(foo, fbs.outStream());
        \\}
        , "unsupported type"
    );
}

test "write u8 array" {


@@ 319,8 359,16 @@ test "write u8 array" {
    expectEqualSlices(u8, fbs.getWritten()[0..], expected[0..]);
}

test "write u8 array of void" {
    // Compile error.
test "write array of void" {
    try testCompileError(
        \\pub fn main() void {
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  const foo: [4]void = .{ {}, {}, {}, {} };
        \\  try Writer.init().write(foo, fbs.outStream());
        \\}
        , "unsupported type"
    );
}

test "write slice" {


@@ 337,7 385,15 @@ test "write slice" {
}

test "write slice of void" {
    // Compile error.
    try testCompileError(
        \\pub fn main() void {
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  const foo: []const void = &[_]void{ {}, {} };
        \\  try Writer.init().write(foo, fbs.outStream());
        \\}
        , "unsupported type"
    );
}

test "write map[u8]u8" {


@@ 375,7 431,15 @@ test "write map[string]u8" {
}

test "write map[u8]void" {
    // Compile error.
    try testCompileError(
        \\pub fn main() void {
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  const foo = std.AutoHashMap(u8, void);
        \\  try Writer.init().write(foo, fbs.outStream());
        \\}
        , "unsupported type"
    );
}

test "write tagged union" {


@@ 404,11 468,26 @@ test "write tagged union with void member active" {
}

test "write void" {
    // Compile error.
    try testCompileError(
        \\pub fn main() void {
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  try Writer.init().write(void, fbs.outStream());
        \\}
        , "unsupported type"
    );
}

test "write unsupported type" {
    // This is a compile error now.
    try testCompileError(
        \\pub fn main() void {
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  const foo: u7 = 7;
        \\  try Writer.init().write(foo, fbs.outStream());
        \\}
        , "unsupported type"
    );
}

test "round trip variable uint" {


@@ 535,34 614,129 @@ test "bare.Reader frees its memory" {
}

test "invariant: fixed-length array must have length > 0 (read)" {
    // This is a compile error now.
    try testCompileError(
        \\pub fn main() void {
        \\  var fbs = io.fixedBufferStream("lol");
        \\  try Reader.init(testing.allocator).read([0]u8, fbs.inStream());
        \\}
        , "array length must be at least 1"
    );
}

test "invariant: fixed-length array must have length > 0 (write)" {
    // This is a compile error now.
    try testCompileError(
        \\pub fn main() void {
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  var foo = [0]u8{};
        \\  try Writer.init().write(foo, fbs.outStream());
        \\}
        , "array length must be at least 1"
    );
}

test "invariant: structs must have at least 1 field (read)" {
    // This is a compile error now.
    try testCompileError(
        \\pub fn main() void {
        \\  const Foo = struct {};
        \\  var fbs = io.fixedBufferStream("lol");
        \\  try Reader.init(testing.allocator).read(Foo, fbs.inStream());
        \\}
        , "structs must have 1 or more fields"
    );
}

test "invariant: structs must have at least 1 field (write)" {
    // This is a compile error now.
}

test "invariant: unions must have at least 1 field" {
    // Zig does not accept something like the following:
    // ```
    // const Tag = enum {};
    // const Foo = union(Tag) {};
    // ```
    // So this invariant is enforced by the language.
}

test "invariant: hashmap keys must be of primitive type (read)" {
    // This is a compile error now.
}

test "invariant: hashmap keys must be of primitive type (write)" {
    // This is a compile error now.
    try testCompileError(
        \\pub fn main() void {
        \\  const Foo = struct {};
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  try Writer.init().write(Foo{}, fbs.outStream());
        \\}
        , "structs must have 1 or more fields"
    );
}

test "invariant: unions must have at least 1 field (read)" {
    try testCompileError(
        \\pub fn main() void {
        \\  const Foo = union(enum) {};
        \\  var fbs = io.fixedBufferStream("lol");
        \\  try Reader.init(testing.allocator).read(Foo{}, fbs.inStream());
        \\}
        , "unions must have 1 or more fields"
    );
}

test "invariant: unions must have at least 1 field (write)" {
    try testCompileError(
        \\pub fn main() void {
        \\  const Foo = union(enum) {};
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  try Writer.init().write(Foo{}, fbs.outStream());
        \\}
        , "unions must have 1 or more fields"
    );
}

test "invariant: hashmap keys must be of primitive type" {
    try testCompileError(
        \\pub fn main() void {
        \\  const hm = std.AutoHashMap([64]u8, void).init(testing.allocator);
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  try Writer.init().write(hm, fbs.outStream());
        \\}
        , "unsupported hashmap key type"
    );
}

const fs = std.fs;

const boilerplate =
    \\const std = @import("std");
    \\const io = std.io;
    \\const math = std.math;
    \\const mem = std.mem;
    \\const testing = std.testing;
    \\
    \\const bare = @import("zig-bare.zig");
    \\const Reader = bare.Reader;
    \\const Writer = bare.Writer;
;

fn testCompileError(comptime code: []const u8, comptime expected_error: []const u8) !void {
    const fname = "/tmp/zig-bare-test.zig";
    var cwd = fs.cwd();
    try cwd.copyFile("src/bare.zig", cwd, "/tmp/zig-bare.zig", .{});
    var tmpfile = try cwd.createFile(fname, .{ .read = true, .truncate = true });
    defer tmpfile.close();
    _ = try tmpfile.write(boilerplate);
    _ = try tmpfile.write(code);
    var proc = try std.ChildProcess.init(&[_][]const u8{ "zig", "build-exe", fname }, testing.allocator);
    defer proc.deinit();
    proc.stderr_behavior = .Pipe;
    proc.stdout_behavior = .Pipe;
    try proc.spawn();
    const stderr = try proc.stderr.?.inStream().readAllAlloc(testing.allocator, 0x1000);
    const term = try proc.wait();
//    warn("{}\n", .{ t });
    defer testing.allocator.free(stderr);
    try checkErrorMessage(stderr, expected_error);
    expectEqual(term.Exited, 1);
}

fn checkErrorMessage(haystack: []const u8, message: []const u8) !void {
    const errorstart = "error: ";
    const idx = mem.indexOf(u8, haystack, errorstart) orelse {
        expect(false);
        return;
    };
    const errorslice = haystack[idx + errorstart.len..];
    if (!mem.startsWith(u8, errorslice, message)) {
        warn("\nunexpected output: {}\n", .{ errorslice });
        expect(false);
    }
}