Commit 10525b869d

Andrew Kelley <superjoe30@gmail.com>
2017-04-19 07:45:46
convert build examples tests to zig build system
1 parent 37b9a2e
test/build_examples.zig
@@ -0,0 +1,60 @@
+const std = @import("std");
+const build = std.build;
+const mem = std.mem;
+const fmt = std.fmt;
+
+pub fn addBuildExampleTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
+    const cases = %%b.allocator.create(BuildExamplesContext);
+    *cases = BuildExamplesContext {
+        .b = b,
+        .step = b.step("test-build-examples", "Build the examples"),
+        .test_index = 0,
+        .test_filter = test_filter,
+    };
+
+    cases.add("example/hello_world/hello.zig");
+    cases.addC("example/hello_world/hello_libc.zig");
+    cases.add("example/cat/main.zig");
+    cases.add("example/guess_number/main.zig");
+
+    return cases.step;
+}
+
+const BuildExamplesContext = struct {
+    b: &build.Builder,
+    step: &build.Step,
+    test_index: usize,
+    test_filter: ?[]const u8,
+
+    pub fn addC(self: &BuildExamplesContext, root_src: []const u8) {
+        self.addAllArgs(root_src, true);
+    }
+
+    pub fn add(self: &BuildExamplesContext, root_src: []const u8) {
+        self.addAllArgs(root_src, false);
+    }
+
+    pub fn addAllArgs(self: &BuildExamplesContext, root_src: []const u8, link_libc: bool) {
+        const b = self.b;
+
+        for ([]bool{false, true}) |release| {
+            const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "build {} ({})",
+                root_src, if (release) "release" else "debug");
+            if (const filter ?= self.test_filter) {
+                if (mem.indexOf(u8, annotated_case_name, filter) == null)
+                    continue;
+            }
+
+            const exe = b.addExecutable("test", root_src);
+            exe.setRelease(release);
+            if (link_libc) {
+                exe.linkLibrary("c");
+            }
+
+            const log_step = b.addLog("PASS {}\n", annotated_case_name);
+            log_step.step.dependOn(&exe.step);
+
+            self.step.dependOn(&log_step.step);
+        }
+    }
+};
test/compare_output.zig
@@ -0,0 +1,587 @@
+const std = @import("std");
+const debug = std.debug;
+const build = std.build;
+const os = std.os;
+const StdIo = os.ChildProcess.StdIo;
+const Term = os.ChildProcess.Term;
+const Buffer0 = std.cstr.Buffer0;
+const io = std.io;
+const mem = std.mem;
+const fmt = std.fmt;
+const List = std.list.List;
+
+error TestFailed;
+
+pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
+    const cases = %%b.allocator.create(CompareOutputContext);
+    *cases = CompareOutputContext {
+        .b = b,
+        .compare_output_tests = b.step("test-compare-output", "Run the compare output tests"),
+        .test_index = 0,
+        .test_filter = test_filter,
+    };
+
+    cases.addC("hello world with libc",
+        \\const c = @cImport(@cInclude("stdio.h"));
+        \\export fn main(argc: c_int, argv: &&u8) -> c_int {
+        \\    _ = c.puts(c"Hello, world!");
+        \\    return 0;
+        \\}
+    , "Hello, world!" ++ os.line_sep);
+
+    cases.addCase({
+        var tc = cases.create("multiple files with private function",
+            \\use @import("std").io;
+            \\use @import("foo.zig");
+            \\
+            \\pub fn main() -> %void {
+            \\    privateFunction();
+            \\    %%stdout.printf("OK 2\n");
+            \\}
+            \\
+            \\fn privateFunction() {
+            \\    printText();
+            \\}
+        , "OK 1\nOK 2\n");
+
+        tc.addSourceFile("foo.zig",
+            \\use @import("std").io;
+            \\
+            \\// purposefully conflicting function with main.zig
+            \\// but it's private so it should be OK
+            \\fn privateFunction() {
+            \\    %%stdout.printf("OK 1\n");
+            \\}
+            \\
+            \\pub fn printText() {
+            \\    privateFunction();
+            \\}
+        );
+
+        tc
+    });
+
+    cases.addCase({
+        var tc = cases.create("import segregation",
+            \\use @import("foo.zig");
+            \\use @import("bar.zig");
+            \\
+            \\pub fn main() -> %void {
+            \\    foo_function();
+            \\    bar_function();
+            \\}
+        , "OK\nOK\n");
+
+        tc.addSourceFile("foo.zig",
+            \\use @import("std").io;
+            \\pub fn foo_function() {
+            \\    %%stdout.printf("OK\n");
+            \\}
+        );
+
+        tc.addSourceFile("bar.zig",
+            \\use @import("other.zig");
+            \\use @import("std").io;
+            \\
+            \\pub fn bar_function() {
+            \\    if (foo_function()) {
+            \\        %%stdout.printf("OK\n");
+            \\    }
+            \\}
+        );
+
+        tc.addSourceFile("other.zig",
+            \\pub fn foo_function() -> bool {
+            \\    // this one conflicts with the one from foo
+            \\    return true;
+            \\}
+        );
+
+        tc
+    });
+
+    cases.addCase({
+        var tc = cases.create("two files use import each other",
+            \\use @import("a.zig");
+            \\
+            \\pub fn main() -> %void {
+            \\    ok();
+            \\}
+        , "OK\n");
+
+        tc.addSourceFile("a.zig",
+            \\use @import("b.zig");
+            \\const io = @import("std").io;
+            \\
+            \\pub const a_text = "OK\n";
+            \\
+            \\pub fn ok() {
+            \\    %%io.stdout.printf(b_text);
+            \\}
+        );
+
+        tc.addSourceFile("b.zig",
+            \\use @import("a.zig");
+            \\
+            \\pub const b_text = a_text;
+        );
+
+        tc
+    });
+
+    cases.add("hello world without libc",
+        \\const io = @import("std").io;
+        \\
+        \\pub fn main() -> %void {
+        \\    %%io.stdout.printf("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a'));
+        \\}
+    , "Hello, world!\n0012 012 a\n");
+
+    cases.addC("number literals",
+        \\const c = @cImport(@cInclude("stdio.h"));
+        \\
+        \\export fn main(argc: c_int, argv: &&u8) -> c_int {
+        \\    _ = c.printf(c"0: %llu\n",
+        \\             u64(0));
+        \\    _ = c.printf(c"320402575052271: %llu\n",
+        \\         u64(320402575052271));
+        \\    _ = c.printf(c"0x01236789abcdef: %llu\n",
+        \\         u64(0x01236789abcdef));
+        \\    _ = c.printf(c"0xffffffffffffffff: %llu\n",
+        \\         u64(0xffffffffffffffff));
+        \\    _ = c.printf(c"0x000000ffffffffffffffff: %llu\n",
+        \\         u64(0x000000ffffffffffffffff));
+        \\    _ = c.printf(c"0o1777777777777777777777: %llu\n",
+        \\         u64(0o1777777777777777777777));
+        \\    _ = c.printf(c"0o0000001777777777777777777777: %llu\n",
+        \\         u64(0o0000001777777777777777777777));
+        \\    _ = c.printf(c"0b1111111111111111111111111111111111111111111111111111111111111111: %llu\n",
+        \\         u64(0b1111111111111111111111111111111111111111111111111111111111111111));
+        \\    _ = c.printf(c"0b0000001111111111111111111111111111111111111111111111111111111111111111: %llu\n",
+        \\         u64(0b0000001111111111111111111111111111111111111111111111111111111111111111));
+        \\
+        \\    _ = c.printf(c"\n");
+        \\
+        \\    _ = c.printf(c"0.0: %a\n",
+        \\         f64(0.0));
+        \\    _ = c.printf(c"0e0: %a\n",
+        \\         f64(0e0));
+        \\    _ = c.printf(c"0.0e0: %a\n",
+        \\         f64(0.0e0));
+        \\    _ = c.printf(c"000000000000000000000000000000000000000000000000000000000.0e0: %a\n",
+        \\         f64(000000000000000000000000000000000000000000000000000000000.0e0));
+        \\    _ = c.printf(c"0.000000000000000000000000000000000000000000000000000000000e0: %a\n",
+        \\         f64(0.000000000000000000000000000000000000000000000000000000000e0));
+        \\    _ = c.printf(c"0.0e000000000000000000000000000000000000000000000000000000000: %a\n",
+        \\         f64(0.0e000000000000000000000000000000000000000000000000000000000));
+        \\    _ = c.printf(c"1.0: %a\n",
+        \\         f64(1.0));
+        \\    _ = c.printf(c"10.0: %a\n",
+        \\         f64(10.0));
+        \\    _ = c.printf(c"10.5: %a\n",
+        \\         f64(10.5));
+        \\    _ = c.printf(c"10.5e5: %a\n",
+        \\         f64(10.5e5));
+        \\    _ = c.printf(c"10.5e+5: %a\n",
+        \\         f64(10.5e+5));
+        \\    _ = c.printf(c"50.0e-2: %a\n",
+        \\         f64(50.0e-2));
+        \\    _ = c.printf(c"50e-2: %a\n",
+        \\         f64(50e-2));
+        \\
+        \\    _ = c.printf(c"\n");
+        \\
+        \\    _ = c.printf(c"0x1.0: %a\n",
+        \\         f64(0x1.0));
+        \\    _ = c.printf(c"0x10.0: %a\n",
+        \\         f64(0x10.0));
+        \\    _ = c.printf(c"0x100.0: %a\n",
+        \\         f64(0x100.0));
+        \\    _ = c.printf(c"0x103.0: %a\n",
+        \\         f64(0x103.0));
+        \\    _ = c.printf(c"0x103.7: %a\n",
+        \\         f64(0x103.7));
+        \\    _ = c.printf(c"0x103.70: %a\n",
+        \\         f64(0x103.70));
+        \\    _ = c.printf(c"0x103.70p4: %a\n",
+        \\         f64(0x103.70p4));
+        \\    _ = c.printf(c"0x103.70p5: %a\n",
+        \\         f64(0x103.70p5));
+        \\    _ = c.printf(c"0x103.70p+5: %a\n",
+        \\         f64(0x103.70p+5));
+        \\    _ = c.printf(c"0x103.70p-5: %a\n",
+        \\         f64(0x103.70p-5));
+        \\
+        \\    _ = c.printf(c"\n");
+        \\
+        \\    _ = c.printf(c"0b10100.00010e0: %a\n",
+        \\         f64(0b10100.00010e0));
+        \\    _ = c.printf(c"0o10700.00010e0: %a\n",
+        \\         f64(0o10700.00010e0));
+        \\
+        \\    return 0;
+        \\}
+    ,
+        \\0: 0
+        \\320402575052271: 320402575052271
+        \\0x01236789abcdef: 320402575052271
+        \\0xffffffffffffffff: 18446744073709551615
+        \\0x000000ffffffffffffffff: 18446744073709551615
+        \\0o1777777777777777777777: 18446744073709551615
+        \\0o0000001777777777777777777777: 18446744073709551615
+        \\0b1111111111111111111111111111111111111111111111111111111111111111: 18446744073709551615
+        \\0b0000001111111111111111111111111111111111111111111111111111111111111111: 18446744073709551615
+        \\
+        \\0.0: 0x0p+0
+        \\0e0: 0x0p+0
+        \\0.0e0: 0x0p+0
+        \\000000000000000000000000000000000000000000000000000000000.0e0: 0x0p+0
+        \\0.000000000000000000000000000000000000000000000000000000000e0: 0x0p+0
+        \\0.0e000000000000000000000000000000000000000000000000000000000: 0x0p+0
+        \\1.0: 0x1p+0
+        \\10.0: 0x1.4p+3
+        \\10.5: 0x1.5p+3
+        \\10.5e5: 0x1.0059p+20
+        \\10.5e+5: 0x1.0059p+20
+        \\50.0e-2: 0x1p-1
+        \\50e-2: 0x1p-1
+        \\
+        \\0x1.0: 0x1p+0
+        \\0x10.0: 0x1p+4
+        \\0x100.0: 0x1p+8
+        \\0x103.0: 0x1.03p+8
+        \\0x103.7: 0x1.037p+8
+        \\0x103.70: 0x1.037p+8
+        \\0x103.70p4: 0x1.037p+12
+        \\0x103.70p5: 0x1.037p+13
+        \\0x103.70p+5: 0x1.037p+13
+        \\0x103.70p-5: 0x1.037p+3
+        \\
+        \\0b10100.00010e0: 0x1.41p+4
+        \\0o10700.00010e0: 0x1.1c0001p+12
+        \\
+    );
+
+    cases.add("order-independent declarations",
+        \\const io = @import("std").io;
+        \\const z = io.stdin_fileno;
+        \\const x : @typeOf(y) = 1234;
+        \\const y : u16 = 5678;
+        \\pub fn main() -> %void {
+        \\    var x_local : i32 = print_ok(x);
+        \\}
+        \\fn print_ok(val: @typeOf(x)) -> @typeOf(foo) {
+        \\    %%io.stdout.printf("OK\n");
+        \\    return 0;
+        \\}
+        \\const foo : i32 = 0;
+    , "OK\n");
+
+    cases.addC("expose function pointer to C land",
+        \\const c = @cImport(@cInclude("stdlib.h"));
+        \\
+        \\export fn compare_fn(a: ?&const c_void, b: ?&const c_void) -> c_int {
+        \\    const a_int = @ptrcast(&i32, a ?? unreachable);
+        \\    const b_int = @ptrcast(&i32, b ?? unreachable);
+        \\    if (*a_int < *b_int) {
+        \\        -1
+        \\    } else if (*a_int > *b_int) {
+        \\        1
+        \\    } else {
+        \\        c_int(0)
+        \\    }
+        \\}
+        \\
+        \\export fn main() -> c_int {
+        \\    var array = []u32 { 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 };
+        \\
+        \\    c.qsort(@ptrcast(&c_void, &array[0]), c_ulong(array.len), @sizeOf(i32), compare_fn);
+        \\
+        \\    for (array) |item, i| {
+        \\        if (item != i) {
+        \\            c.abort();
+        \\        }
+        \\    }
+        \\
+        \\    return 0;
+        \\}
+    , "");
+
+    cases.addC("casting between float and integer types",
+        \\const c = @cImport(@cInclude("stdio.h"));
+        \\export fn main(argc: c_int, argv: &&u8) -> c_int {
+        \\    const small: f32 = 3.25;
+        \\    const x: f64 = small;
+        \\    const y = i32(x);
+        \\    const z = f64(y);
+        \\    _ = c.printf(c"%.2f\n%d\n%.2f\n%.2f\n", x, y, z, f64(-0.4));
+        \\    return 0;
+        \\}
+    , "3.25\n3\n3.00\n-0.40\n");
+
+    cases.add("same named methods in incomplete struct",
+        \\const io = @import("std").io;
+        \\
+        \\const Foo = struct {
+        \\    field1: Bar,
+        \\
+        \\    fn method(a: &const Foo) -> bool { true }
+        \\};
+        \\
+        \\const Bar = struct {
+        \\    field2: i32,
+        \\
+        \\    fn method(b: &const Bar) -> bool { true }
+        \\};
+        \\
+        \\pub fn main() -> %void {
+        \\    const bar = Bar {.field2 = 13,};
+        \\    const foo = Foo {.field1 = bar,};
+        \\    if (!foo.method()) {
+        \\        %%io.stdout.printf("BAD\n");
+        \\    }
+        \\    if (!bar.method()) {
+        \\        %%io.stdout.printf("BAD\n");
+        \\    }
+        \\    %%io.stdout.printf("OK\n");
+        \\}
+    , "OK\n");
+
+    cases.add("defer with only fallthrough",
+        \\const io = @import("std").io;
+        \\pub fn main() -> %void {
+        \\    %%io.stdout.printf("before\n");
+        \\    defer %%io.stdout.printf("defer1\n");
+        \\    defer %%io.stdout.printf("defer2\n");
+        \\    defer %%io.stdout.printf("defer3\n");
+        \\    %%io.stdout.printf("after\n");
+        \\}
+    , "before\nafter\ndefer3\ndefer2\ndefer1\n");
+
+    cases.add("defer with return",
+        \\const io = @import("std").io;
+        \\const os = @import("std").os;
+        \\pub fn main() -> %void {
+        \\    %%io.stdout.printf("before\n");
+        \\    defer %%io.stdout.printf("defer1\n");
+        \\    defer %%io.stdout.printf("defer2\n");
+        \\    if (os.args.count() == 1) return;
+        \\    defer %%io.stdout.printf("defer3\n");
+        \\    %%io.stdout.printf("after\n");
+        \\}
+    , "before\ndefer2\ndefer1\n");
+
+    cases.add("%defer and it fails",
+        \\const io = @import("std").io;
+        \\pub fn main() -> %void {
+        \\    do_test() %% return;
+        \\}
+        \\fn do_test() -> %void {
+        \\    %%io.stdout.printf("before\n");
+        \\    defer %%io.stdout.printf("defer1\n");
+        \\    %defer %%io.stdout.printf("deferErr\n");
+        \\    %return its_gonna_fail();
+        \\    defer %%io.stdout.printf("defer3\n");
+        \\    %%io.stdout.printf("after\n");
+        \\}
+        \\error IToldYouItWouldFail;
+        \\fn its_gonna_fail() -> %void {
+        \\    return error.IToldYouItWouldFail;
+        \\}
+    , "before\ndeferErr\ndefer1\n");
+
+    cases.add("%defer and it passes",
+        \\const io = @import("std").io;
+        \\pub fn main() -> %void {
+        \\    do_test() %% return;
+        \\}
+        \\fn do_test() -> %void {
+        \\    %%io.stdout.printf("before\n");
+        \\    defer %%io.stdout.printf("defer1\n");
+        \\    %defer %%io.stdout.printf("deferErr\n");
+        \\    %return its_gonna_pass();
+        \\    defer %%io.stdout.printf("defer3\n");
+        \\    %%io.stdout.printf("after\n");
+        \\}
+        \\fn its_gonna_pass() -> %void { }
+    , "before\nafter\ndefer3\ndefer1\n");
+
+    cases.addCase({
+        var tc = cases.create("@embedFile",
+            \\const foo_txt = @embedFile("foo.txt");
+            \\const io = @import("std").io;
+            \\
+            \\pub fn main() -> %void {
+            \\    %%io.stdout.printf(foo_txt);
+            \\}
+        , "1234\nabcd\n");
+
+        tc.addSourceFile("foo.txt", "1234\nabcd\n");
+
+        tc
+    });
+
+    return cases.compare_output_tests;
+}
+
+const CompareOutputContext = struct {
+    b: &build.Builder,
+    compare_output_tests: &build.Step,
+    test_index: usize,
+    test_filter: ?[]const u8,
+
+    const TestCase = struct {
+        name: []const u8,
+        sources: List(SourceFile),
+        expected_output: []const u8,
+        link_libc: bool,
+
+        const SourceFile = struct {
+            filename: []const u8,
+            source: []const u8,
+        };
+
+        pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) {
+            %%self.sources.append(SourceFile {
+                .filename = filename,
+                .source = source,
+            });
+        }
+    };
+
+    pub fn create(self: &CompareOutputContext, name: []const u8, source: []const u8,
+        expected_output: []const u8) -> TestCase
+    {
+        var tc = TestCase {
+            .name = name,
+            .sources = List(TestCase.SourceFile).init(self.b.allocator),
+            .expected_output = expected_output,
+            .link_libc = false,
+        };
+        tc.addSourceFile("source.zig", source);
+        return tc;
+    }
+
+    pub fn addC(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) {
+        var tc = self.create(name, source, expected_output);
+        tc.link_libc = true;
+        self.addCase(tc);
+    }
+
+    pub fn add(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) {
+        const tc = self.create(name, source, expected_output);
+        self.addCase(tc);
+    }
+
+    pub fn addCase(self: &CompareOutputContext, case: &const TestCase) {
+        const b = self.b;
+
+        const root_src = %%os.path.join(b.allocator, "test_artifacts", case.sources.items[0].filename);
+        const exe_path = %%os.path.join(b.allocator, "test_artifacts", "test");
+
+        for ([]bool{false, true}) |release| {
+            const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "{} ({})",
+                case.name, if (release) "release" else "debug");
+            if (const filter ?= self.test_filter) {
+                if (mem.indexOf(u8, annotated_case_name, filter) == null)
+                    continue;
+            }
+
+            const exe = b.addExecutable("test", root_src);
+            exe.setOutputPath(exe_path);
+            exe.setRelease(release);
+            if (case.link_libc) {
+                exe.linkLibrary("c");
+            }
+
+            for (case.sources.toSliceConst()) |src_file| {
+                const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename);
+                const write_src = b.addWriteFile(expanded_src_path, src_file.source);
+                exe.step.dependOn(&write_src.step);
+            }
+
+            const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name,
+                case.expected_output);
+            run_and_cmp_output.step.dependOn(&exe.step);
+
+            self.compare_output_tests.dependOn(&run_and_cmp_output.step);
+        }
+    }
+};
+
+const RunCompareOutputStep = struct {
+    step: build.Step,
+    context: &CompareOutputContext,
+    exe_path: []const u8,
+    name: []const u8,
+    expected_output: []const u8,
+    test_index: usize,
+
+    pub fn create(context: &CompareOutputContext, exe_path: []const u8,
+        name: []const u8, expected_output: []const u8) -> &RunCompareOutputStep
+    {
+        const allocator = context.b.allocator;
+        const ptr = %%allocator.create(RunCompareOutputStep);
+        *ptr = RunCompareOutputStep {
+            .context = context,
+            .exe_path = exe_path,
+            .name = name,
+            .expected_output = expected_output,
+            .test_index = context.test_index,
+            .step = build.Step.init("RunCompareOutput", allocator, make),
+        };
+        context.test_index += 1;
+        return ptr;
+    }
+
+    fn make(step: &build.Step) -> %void {
+        const self = @fieldParentPtr(RunCompareOutputStep, "step", step);
+        const b = self.context.b;
+
+        const full_exe_path = b.pathFromRoot(self.exe_path);
+
+        %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
+
+        var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, &b.env_map,
+            StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err|
+        {
+            debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
+        };
+
+        const term = child.wait() %% |err| {
+            debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
+        };
+        switch (term) {
+            Term.Clean => |code| {
+                if (code != 0) {
+                    %%io.stderr.printf("Process {} exited with error code {}\n", full_exe_path, code);
+                    return error.TestFailed;
+                }
+            },
+            else => {
+                %%io.stderr.printf("Process {} terminated unexpectedly\n", full_exe_path);
+                return error.TestFailed;
+            },
+        };
+
+        var stdout = %%Buffer0.initEmpty(b.allocator);
+        var stderr = %%Buffer0.initEmpty(b.allocator);
+
+        %%(??child.stdout).readAll(&stdout);
+        %%(??child.stderr).readAll(&stderr);
+
+        if (!mem.eql(u8, self.expected_output, stdout.toSliceConst())) {
+            %%io.stderr.printf(
+                \\
+                \\========= Expected this output: =========
+                \\{}
+                \\================================================
+                \\{}
+                \\
+            , self.expected_output, stdout.toSliceConst());
+            return error.TestFailed;
+        }
+        %%io.stderr.printf("OK\n");
+    }
+};
+
test/run_tests.cpp
@@ -272,13 +272,6 @@ static TestCase *add_example_compile_libc(const char *root_source_file) {
 
 ////////////////////////////////////////////////////////////////////////////////////
 
-static void add_build_examples(void) {
-    add_example_compile("example/hello_world/hello.zig");
-    add_example_compile_libc("example/hello_world/hello_libc.zig");
-    add_example_compile("example/cat/main.zig");
-    add_example_compile("example/guess_number/main.zig");
-}
-
 
 ////////////////////////////////////////////////////////////////////////////////////
 
@@ -2615,7 +2608,6 @@ int main(int argc, char **argv) {
             }
         }
     }
-    add_build_examples();
     add_debug_safety_test_cases();
     add_compile_failure_test_cases();
     add_parse_error_tests();
test/tests.zig
@@ -1,587 +1,2 @@
-const std = @import("std");
-const debug = std.debug;
-const build = std.build;
-const os = std.os;
-const StdIo = os.ChildProcess.StdIo;
-const Term = os.ChildProcess.Term;
-const Buffer0 = std.cstr.Buffer0;
-const io = std.io;
-const mem = std.mem;
-const fmt = std.fmt;
-const List = std.list.List;
-
-error TestFailed;
-
-pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
-    const cases = %%b.allocator.create(CompareOutputContext);
-    *cases = CompareOutputContext {
-        .b = b,
-        .compare_output_tests = b.step("test-compare-output", "Run the compare output tests"),
-        .test_index = 0,
-        .test_filter = test_filter,
-    };
-
-    cases.addC("hello world with libc",
-        \\const c = @cImport(@cInclude("stdio.h"));
-        \\export fn main(argc: c_int, argv: &&u8) -> c_int {
-        \\    _ = c.puts(c"Hello, world!");
-        \\    return 0;
-        \\}
-    , "Hello, world!" ++ os.line_sep);
-
-    cases.addCase({
-        var tc = cases.create("multiple files with private function",
-            \\use @import("std").io;
-            \\use @import("foo.zig");
-            \\
-            \\pub fn main() -> %void {
-            \\    privateFunction();
-            \\    %%stdout.printf("OK 2\n");
-            \\}
-            \\
-            \\fn privateFunction() {
-            \\    printText();
-            \\}
-        , "OK 1\nOK 2\n");
-
-        tc.addSourceFile("foo.zig",
-            \\use @import("std").io;
-            \\
-            \\// purposefully conflicting function with main.zig
-            \\// but it's private so it should be OK
-            \\fn privateFunction() {
-            \\    %%stdout.printf("OK 1\n");
-            \\}
-            \\
-            \\pub fn printText() {
-            \\    privateFunction();
-            \\}
-        );
-
-        tc
-    });
-
-    cases.addCase({
-        var tc = cases.create("import segregation",
-            \\use @import("foo.zig");
-            \\use @import("bar.zig");
-            \\
-            \\pub fn main() -> %void {
-            \\    foo_function();
-            \\    bar_function();
-            \\}
-        , "OK\nOK\n");
-
-        tc.addSourceFile("foo.zig",
-            \\use @import("std").io;
-            \\pub fn foo_function() {
-            \\    %%stdout.printf("OK\n");
-            \\}
-        );
-
-        tc.addSourceFile("bar.zig",
-            \\use @import("other.zig");
-            \\use @import("std").io;
-            \\
-            \\pub fn bar_function() {
-            \\    if (foo_function()) {
-            \\        %%stdout.printf("OK\n");
-            \\    }
-            \\}
-        );
-
-        tc.addSourceFile("other.zig",
-            \\pub fn foo_function() -> bool {
-            \\    // this one conflicts with the one from foo
-            \\    return true;
-            \\}
-        );
-
-        tc
-    });
-
-    cases.addCase({
-        var tc = cases.create("two files use import each other",
-            \\use @import("a.zig");
-            \\
-            \\pub fn main() -> %void {
-            \\    ok();
-            \\}
-        , "OK\n");
-
-        tc.addSourceFile("a.zig",
-            \\use @import("b.zig");
-            \\const io = @import("std").io;
-            \\
-            \\pub const a_text = "OK\n";
-            \\
-            \\pub fn ok() {
-            \\    %%io.stdout.printf(b_text);
-            \\}
-        );
-
-        tc.addSourceFile("b.zig",
-            \\use @import("a.zig");
-            \\
-            \\pub const b_text = a_text;
-        );
-
-        tc
-    });
-
-    cases.add("hello world without libc",
-        \\const io = @import("std").io;
-        \\
-        \\pub fn main() -> %void {
-        \\    %%io.stdout.printf("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a'));
-        \\}
-    , "Hello, world!\n0012 012 a\n");
-
-    cases.addC("number literals",
-        \\const c = @cImport(@cInclude("stdio.h"));
-        \\
-        \\export fn main(argc: c_int, argv: &&u8) -> c_int {
-        \\    _ = c.printf(c"0: %llu\n",
-        \\             u64(0));
-        \\    _ = c.printf(c"320402575052271: %llu\n",
-        \\         u64(320402575052271));
-        \\    _ = c.printf(c"0x01236789abcdef: %llu\n",
-        \\         u64(0x01236789abcdef));
-        \\    _ = c.printf(c"0xffffffffffffffff: %llu\n",
-        \\         u64(0xffffffffffffffff));
-        \\    _ = c.printf(c"0x000000ffffffffffffffff: %llu\n",
-        \\         u64(0x000000ffffffffffffffff));
-        \\    _ = c.printf(c"0o1777777777777777777777: %llu\n",
-        \\         u64(0o1777777777777777777777));
-        \\    _ = c.printf(c"0o0000001777777777777777777777: %llu\n",
-        \\         u64(0o0000001777777777777777777777));
-        \\    _ = c.printf(c"0b1111111111111111111111111111111111111111111111111111111111111111: %llu\n",
-        \\         u64(0b1111111111111111111111111111111111111111111111111111111111111111));
-        \\    _ = c.printf(c"0b0000001111111111111111111111111111111111111111111111111111111111111111: %llu\n",
-        \\         u64(0b0000001111111111111111111111111111111111111111111111111111111111111111));
-        \\
-        \\    _ = c.printf(c"\n");
-        \\
-        \\    _ = c.printf(c"0.0: %a\n",
-        \\         f64(0.0));
-        \\    _ = c.printf(c"0e0: %a\n",
-        \\         f64(0e0));
-        \\    _ = c.printf(c"0.0e0: %a\n",
-        \\         f64(0.0e0));
-        \\    _ = c.printf(c"000000000000000000000000000000000000000000000000000000000.0e0: %a\n",
-        \\         f64(000000000000000000000000000000000000000000000000000000000.0e0));
-        \\    _ = c.printf(c"0.000000000000000000000000000000000000000000000000000000000e0: %a\n",
-        \\         f64(0.000000000000000000000000000000000000000000000000000000000e0));
-        \\    _ = c.printf(c"0.0e000000000000000000000000000000000000000000000000000000000: %a\n",
-        \\         f64(0.0e000000000000000000000000000000000000000000000000000000000));
-        \\    _ = c.printf(c"1.0: %a\n",
-        \\         f64(1.0));
-        \\    _ = c.printf(c"10.0: %a\n",
-        \\         f64(10.0));
-        \\    _ = c.printf(c"10.5: %a\n",
-        \\         f64(10.5));
-        \\    _ = c.printf(c"10.5e5: %a\n",
-        \\         f64(10.5e5));
-        \\    _ = c.printf(c"10.5e+5: %a\n",
-        \\         f64(10.5e+5));
-        \\    _ = c.printf(c"50.0e-2: %a\n",
-        \\         f64(50.0e-2));
-        \\    _ = c.printf(c"50e-2: %a\n",
-        \\         f64(50e-2));
-        \\
-        \\    _ = c.printf(c"\n");
-        \\
-        \\    _ = c.printf(c"0x1.0: %a\n",
-        \\         f64(0x1.0));
-        \\    _ = c.printf(c"0x10.0: %a\n",
-        \\         f64(0x10.0));
-        \\    _ = c.printf(c"0x100.0: %a\n",
-        \\         f64(0x100.0));
-        \\    _ = c.printf(c"0x103.0: %a\n",
-        \\         f64(0x103.0));
-        \\    _ = c.printf(c"0x103.7: %a\n",
-        \\         f64(0x103.7));
-        \\    _ = c.printf(c"0x103.70: %a\n",
-        \\         f64(0x103.70));
-        \\    _ = c.printf(c"0x103.70p4: %a\n",
-        \\         f64(0x103.70p4));
-        \\    _ = c.printf(c"0x103.70p5: %a\n",
-        \\         f64(0x103.70p5));
-        \\    _ = c.printf(c"0x103.70p+5: %a\n",
-        \\         f64(0x103.70p+5));
-        \\    _ = c.printf(c"0x103.70p-5: %a\n",
-        \\         f64(0x103.70p-5));
-        \\
-        \\    _ = c.printf(c"\n");
-        \\
-        \\    _ = c.printf(c"0b10100.00010e0: %a\n",
-        \\         f64(0b10100.00010e0));
-        \\    _ = c.printf(c"0o10700.00010e0: %a\n",
-        \\         f64(0o10700.00010e0));
-        \\
-        \\    return 0;
-        \\}
-    ,
-        \\0: 0
-        \\320402575052271: 320402575052271
-        \\0x01236789abcdef: 320402575052271
-        \\0xffffffffffffffff: 18446744073709551615
-        \\0x000000ffffffffffffffff: 18446744073709551615
-        \\0o1777777777777777777777: 18446744073709551615
-        \\0o0000001777777777777777777777: 18446744073709551615
-        \\0b1111111111111111111111111111111111111111111111111111111111111111: 18446744073709551615
-        \\0b0000001111111111111111111111111111111111111111111111111111111111111111: 18446744073709551615
-        \\
-        \\0.0: 0x0p+0
-        \\0e0: 0x0p+0
-        \\0.0e0: 0x0p+0
-        \\000000000000000000000000000000000000000000000000000000000.0e0: 0x0p+0
-        \\0.000000000000000000000000000000000000000000000000000000000e0: 0x0p+0
-        \\0.0e000000000000000000000000000000000000000000000000000000000: 0x0p+0
-        \\1.0: 0x1p+0
-        \\10.0: 0x1.4p+3
-        \\10.5: 0x1.5p+3
-        \\10.5e5: 0x1.0059p+20
-        \\10.5e+5: 0x1.0059p+20
-        \\50.0e-2: 0x1p-1
-        \\50e-2: 0x1p-1
-        \\
-        \\0x1.0: 0x1p+0
-        \\0x10.0: 0x1p+4
-        \\0x100.0: 0x1p+8
-        \\0x103.0: 0x1.03p+8
-        \\0x103.7: 0x1.037p+8
-        \\0x103.70: 0x1.037p+8
-        \\0x103.70p4: 0x1.037p+12
-        \\0x103.70p5: 0x1.037p+13
-        \\0x103.70p+5: 0x1.037p+13
-        \\0x103.70p-5: 0x1.037p+3
-        \\
-        \\0b10100.00010e0: 0x1.41p+4
-        \\0o10700.00010e0: 0x1.1c0001p+12
-        \\
-    );
-
-    cases.add("order-independent declarations",
-        \\const io = @import("std").io;
-        \\const z = io.stdin_fileno;
-        \\const x : @typeOf(y) = 1234;
-        \\const y : u16 = 5678;
-        \\pub fn main() -> %void {
-        \\    var x_local : i32 = print_ok(x);
-        \\}
-        \\fn print_ok(val: @typeOf(x)) -> @typeOf(foo) {
-        \\    %%io.stdout.printf("OK\n");
-        \\    return 0;
-        \\}
-        \\const foo : i32 = 0;
-    , "OK\n");
-
-    cases.addC("expose function pointer to C land",
-        \\const c = @cImport(@cInclude("stdlib.h"));
-        \\
-        \\export fn compare_fn(a: ?&const c_void, b: ?&const c_void) -> c_int {
-        \\    const a_int = @ptrcast(&i32, a ?? unreachable);
-        \\    const b_int = @ptrcast(&i32, b ?? unreachable);
-        \\    if (*a_int < *b_int) {
-        \\        -1
-        \\    } else if (*a_int > *b_int) {
-        \\        1
-        \\    } else {
-        \\        c_int(0)
-        \\    }
-        \\}
-        \\
-        \\export fn main() -> c_int {
-        \\    var array = []u32 { 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 };
-        \\
-        \\    c.qsort(@ptrcast(&c_void, &array[0]), c_ulong(array.len), @sizeOf(i32), compare_fn);
-        \\
-        \\    for (array) |item, i| {
-        \\        if (item != i) {
-        \\            c.abort();
-        \\        }
-        \\    }
-        \\
-        \\    return 0;
-        \\}
-    , "");
-
-    cases.addC("casting between float and integer types",
-        \\const c = @cImport(@cInclude("stdio.h"));
-        \\export fn main(argc: c_int, argv: &&u8) -> c_int {
-        \\    const small: f32 = 3.25;
-        \\    const x: f64 = small;
-        \\    const y = i32(x);
-        \\    const z = f64(y);
-        \\    _ = c.printf(c"%.2f\n%d\n%.2f\n%.2f\n", x, y, z, f64(-0.4));
-        \\    return 0;
-        \\}
-    , "3.25\n3\n3.00\n-0.40\n");
-
-    cases.add("same named methods in incomplete struct",
-        \\const io = @import("std").io;
-        \\
-        \\const Foo = struct {
-        \\    field1: Bar,
-        \\
-        \\    fn method(a: &const Foo) -> bool { true }
-        \\};
-        \\
-        \\const Bar = struct {
-        \\    field2: i32,
-        \\
-        \\    fn method(b: &const Bar) -> bool { true }
-        \\};
-        \\
-        \\pub fn main() -> %void {
-        \\    const bar = Bar {.field2 = 13,};
-        \\    const foo = Foo {.field1 = bar,};
-        \\    if (!foo.method()) {
-        \\        %%io.stdout.printf("BAD\n");
-        \\    }
-        \\    if (!bar.method()) {
-        \\        %%io.stdout.printf("BAD\n");
-        \\    }
-        \\    %%io.stdout.printf("OK\n");
-        \\}
-    , "OK\n");
-
-    cases.add("defer with only fallthrough",
-        \\const io = @import("std").io;
-        \\pub fn main() -> %void {
-        \\    %%io.stdout.printf("before\n");
-        \\    defer %%io.stdout.printf("defer1\n");
-        \\    defer %%io.stdout.printf("defer2\n");
-        \\    defer %%io.stdout.printf("defer3\n");
-        \\    %%io.stdout.printf("after\n");
-        \\}
-    , "before\nafter\ndefer3\ndefer2\ndefer1\n");
-
-    cases.add("defer with return",
-        \\const io = @import("std").io;
-        \\const os = @import("std").os;
-        \\pub fn main() -> %void {
-        \\    %%io.stdout.printf("before\n");
-        \\    defer %%io.stdout.printf("defer1\n");
-        \\    defer %%io.stdout.printf("defer2\n");
-        \\    if (os.args.count() == 1) return;
-        \\    defer %%io.stdout.printf("defer3\n");
-        \\    %%io.stdout.printf("after\n");
-        \\}
-    , "before\ndefer2\ndefer1\n");
-
-    cases.add("%defer and it fails",
-        \\const io = @import("std").io;
-        \\pub fn main() -> %void {
-        \\    do_test() %% return;
-        \\}
-        \\fn do_test() -> %void {
-        \\    %%io.stdout.printf("before\n");
-        \\    defer %%io.stdout.printf("defer1\n");
-        \\    %defer %%io.stdout.printf("deferErr\n");
-        \\    %return its_gonna_fail();
-        \\    defer %%io.stdout.printf("defer3\n");
-        \\    %%io.stdout.printf("after\n");
-        \\}
-        \\error IToldYouItWouldFail;
-        \\fn its_gonna_fail() -> %void {
-        \\    return error.IToldYouItWouldFail;
-        \\}
-    , "before\ndeferErr\ndefer1\n");
-
-    cases.add("%defer and it passes",
-        \\const io = @import("std").io;
-        \\pub fn main() -> %void {
-        \\    do_test() %% return;
-        \\}
-        \\fn do_test() -> %void {
-        \\    %%io.stdout.printf("before\n");
-        \\    defer %%io.stdout.printf("defer1\n");
-        \\    %defer %%io.stdout.printf("deferErr\n");
-        \\    %return its_gonna_pass();
-        \\    defer %%io.stdout.printf("defer3\n");
-        \\    %%io.stdout.printf("after\n");
-        \\}
-        \\fn its_gonna_pass() -> %void { }
-    , "before\nafter\ndefer3\ndefer1\n");
-
-    cases.addCase({
-        var tc = cases.create("@embedFile",
-            \\const foo_txt = @embedFile("foo.txt");
-            \\const io = @import("std").io;
-            \\
-            \\pub fn main() -> %void {
-            \\    %%io.stdout.printf(foo_txt);
-            \\}
-        , "1234\nabcd\n");
-
-        tc.addSourceFile("foo.txt", "1234\nabcd\n");
-
-        tc
-    });
-
-    return cases.compare_output_tests;
-}
-
-const CompareOutputContext = struct {
-    b: &build.Builder,
-    compare_output_tests: &build.Step,
-    test_index: usize,
-    test_filter: ?[]const u8,
-
-    const TestCase = struct {
-        name: []const u8,
-        sources: List(SourceFile),
-        expected_output: []const u8,
-        link_libc: bool,
-
-        const SourceFile = struct {
-            filename: []const u8,
-            source: []const u8,
-        };
-
-        pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) {
-            %%self.sources.append(SourceFile {
-                .filename = filename,
-                .source = source,
-            });
-        }
-    };
-
-    pub fn create(self: &CompareOutputContext, name: []const u8, source: []const u8,
-        expected_output: []const u8) -> TestCase
-    {
-        var tc = TestCase {
-            .name = name,
-            .sources = List(TestCase.SourceFile).init(self.b.allocator),
-            .expected_output = expected_output,
-            .link_libc = false,
-        };
-        tc.addSourceFile("source.zig", source);
-        return tc;
-    }
-
-    pub fn addC(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) {
-        var tc = self.create(name, source, expected_output);
-        tc.link_libc = true;
-        self.addCase(tc);
-    }
-
-    pub fn add(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) {
-        const tc = self.create(name, source, expected_output);
-        self.addCase(tc);
-    }
-
-    pub fn addCase(self: &CompareOutputContext, case: &const TestCase) {
-        const b = self.b;
-
-        const root_src = %%os.path.join(b.allocator, "test_artifacts", case.sources.items[0].filename);
-        const exe_path = %%os.path.join(b.allocator, "test_artifacts", "test");
-
-        for ([]bool{false, true}) |release| {
-            const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "{} ({})",
-                case.name, if (release) "release" else "debug");
-            if (const filter ?= self.test_filter) {
-                if (mem.indexOf(u8, annotated_case_name, filter) == null)
-                    continue;
-            }
-
-            const exe = b.addExecutable("test", root_src);
-            exe.setOutputPath(exe_path);
-            exe.setRelease(release);
-            if (case.link_libc) {
-                exe.linkLibrary("c");
-            }
-
-            for (case.sources.toSliceConst()) |src_file| {
-                const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename);
-                const write_src = b.addWriteFile(expanded_src_path, src_file.source);
-                exe.step.dependOn(&write_src.step);
-            }
-
-            const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name,
-                case.expected_output);
-            run_and_cmp_output.step.dependOn(&exe.step);
-
-            self.compare_output_tests.dependOn(&run_and_cmp_output.step);
-        }
-    }
-};
-
-const RunCompareOutputStep = struct {
-    step: build.Step,
-    context: &CompareOutputContext,
-    exe_path: []const u8,
-    name: []const u8,
-    expected_output: []const u8,
-    test_index: usize,
-
-    pub fn create(context: &CompareOutputContext, exe_path: []const u8,
-        name: []const u8, expected_output: []const u8) -> &RunCompareOutputStep
-    {
-        const allocator = context.b.allocator;
-        const ptr = %%allocator.create(RunCompareOutputStep);
-        *ptr = RunCompareOutputStep {
-            .context = context,
-            .exe_path = exe_path,
-            .name = name,
-            .expected_output = expected_output,
-            .test_index = context.test_index,
-            .step = build.Step.init("RunCompareOutput", allocator, make),
-        };
-        context.test_index += 1;
-        return ptr;
-    }
-
-    fn make(step: &build.Step) -> %void {
-        const self = @fieldParentPtr(RunCompareOutputStep, "step", step);
-        const b = self.context.b;
-
-        const full_exe_path = b.pathFromRoot(self.exe_path);
-
-        %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
-
-        var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, &b.env_map,
-            StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err|
-        {
-            debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
-        };
-
-        const term = child.wait() %% |err| {
-            debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
-        };
-        switch (term) {
-            Term.Clean => |code| {
-                if (code != 0) {
-                    %%io.stderr.printf("Process {} exited with error code {}\n", full_exe_path, code);
-                    return error.TestFailed;
-                }
-            },
-            else => {
-                %%io.stderr.printf("Process {} terminated unexpectedly\n", full_exe_path);
-                return error.TestFailed;
-            },
-        };
-
-        var stdout = %%Buffer0.initEmpty(b.allocator);
-        var stderr = %%Buffer0.initEmpty(b.allocator);
-
-        %%(??child.stdout).readAll(&stdout);
-        %%(??child.stderr).readAll(&stderr);
-
-        if (!mem.eql(u8, self.expected_output, stdout.toSliceConst())) {
-            %%io.stderr.printf(
-                \\
-                \\========= Expected this output: =========
-                \\{}
-                \\================================================
-                \\{}
-                \\
-            , self.expected_output, stdout.toSliceConst());
-            return error.TestFailed;
-        }
-        %%io.stderr.printf("OK\n");
-    }
-};
-
+pub const addCompareOutputTests = @import("compare_output.zig").addCompareOutputTests;
+pub const addBuildExampleTests = @import("build_examples.zig").addBuildExampleTests;
build.zig
@@ -10,27 +10,23 @@ pub fn build(b: &Builder) {
     const run_tests_cmd = b.addCommand(b.out_dir, b.env_map, "./run_tests", [][]const u8{});
     run_tests_cmd.step.dependOn(&run_tests_exe.step);
 
-    const self_hosted_tests_debug_nolibc = b.addTest("test/self_hosted.zig");
-
-    const self_hosted_tests_release_nolibc = b.addTest("test/self_hosted.zig");
-    self_hosted_tests_release_nolibc.setRelease(true);
-
-    const self_hosted_tests_debug_libc = b.addTest("test/self_hosted.zig");
-    self_hosted_tests_debug_libc.linkLibrary("c");
-
-    const self_hosted_tests_release_libc = b.addTest("test/self_hosted.zig");
-    self_hosted_tests_release_libc.setRelease(true);
-    self_hosted_tests_release_libc.linkLibrary("c");
-
     const self_hosted_tests = b.step("test-self-hosted", "Run the self-hosted tests");
-    self_hosted_tests.dependOn(&self_hosted_tests_debug_nolibc.step);
-    self_hosted_tests.dependOn(&self_hosted_tests_release_nolibc.step);
-    self_hosted_tests.dependOn(&self_hosted_tests_debug_libc.step);
-    self_hosted_tests.dependOn(&self_hosted_tests_release_libc.step);
+    for ([]bool{false, true}) |release| {
+        for ([]bool{false, true}) |link_libc| {
+            const these_tests = b.addTest("test/self_hosted.zig");
+            // TODO add prefix to test names
+            // TODO pass test_filter to these_tests
+            these_tests.setRelease(release);
+            if (link_libc) {
+                these_tests.linkLibrary("c");
+            }
+            self_hosted_tests.dependOn(&these_tests.step);
+        }
+    }
 
     test_step.dependOn(self_hosted_tests);
     //test_step.dependOn(&run_tests_cmd.step);
 
     test_step.dependOn(tests.addCompareOutputTests(b, test_filter));
-    //test_step.dependOn(tests.addBuildExampleTests(b, test_filter));
+    test_step.dependOn(tests.addBuildExampleTests(b, test_filter));
 }