Commit 1ff73a8e69

Andrew Kelley <superjoe30@gmail.com>
2017-04-19 22:59:20
convert parseh tests to zig build system
1 parent d12f1f5
test/parseh.zig
@@ -0,0 +1,243 @@
+const tests = @import("tests.zig");
+
+pub fn addCases(cases: &tests.ParseHContext) {
+    cases.addAllowWarnings("simple data types",
+        \\#include <stdint.h>
+        \\int foo(char a, unsigned char b, signed char c);
+        \\int foo(char a, unsigned char b, signed char c); // test a duplicate prototype
+        \\void bar(uint8_t a, uint16_t b, uint32_t c, uint64_t d);
+        \\void baz(int8_t a, int16_t b, int32_t c, int64_t d);
+    ,
+        \\pub extern fn foo(a: u8, b: u8, c: i8) -> c_int;
+    ,
+        \\pub extern fn bar(a: u8, b: u16, c: u32, d: u64);
+    ,
+        \\pub extern fn baz(a: i8, b: i16, c: i32, d: i64);
+    );
+
+    cases.add("noreturn attribute",
+        \\void foo(void) __attribute__((noreturn));
+    ,
+        \\pub extern fn foo() -> noreturn;
+    );
+
+    cases.add("enums",
+        \\enum Foo {
+        \\    FooA,
+        \\    FooB,
+        \\    Foo1,
+        \\};
+    ,
+        \\pub const enum_Foo = extern enum {
+        \\    A,
+        \\    B,
+        \\    @"1",
+        \\};
+    ,
+        \\pub const FooA = 0;
+    ,
+        \\pub const FooB = 1;
+    ,
+        \\pub const Foo1 = 2;
+    ,
+        \\pub const Foo = enum_Foo
+    );
+
+    cases.add("restrict -> noalias",
+        \\void foo(void *restrict bar, void *restrict);
+    ,
+        \\pub extern fn foo(noalias bar: ?&c_void, noalias arg1: ?&c_void);
+    );
+
+    cases.add("simple struct",
+        \\struct Foo {
+        \\    int x;
+        \\    char *y;
+        \\};
+    ,
+        \\const struct_Foo = extern struct {
+        \\    x: c_int,
+        \\    y: ?&u8,
+        \\};
+    ,
+        \\pub const Foo = struct_Foo;
+    );
+
+    cases.add("qualified struct and enum",
+        \\struct Foo {
+        \\    int x;
+        \\    int y;
+        \\};
+        \\enum Bar {
+        \\    BarA,
+        \\    BarB,
+        \\};
+        \\void func(struct Foo *a, enum Bar **b);
+    ,
+        \\pub const struct_Foo = extern struct {
+        \\    x: c_int,
+        \\    y: c_int,
+        \\};
+    ,
+        \\pub const enum_Bar = extern enum {
+        \\    A,
+        \\    B,
+        \\};
+    ,
+        \\pub const BarA = 0;
+    ,
+        \\pub const BarB = 1;
+    ,
+        \\pub extern fn func(a: ?&struct_Foo, b: ?&?&enum_Bar);
+    ,
+        \\pub const Foo = struct_Foo;
+    ,
+        \\pub const Bar = enum_Bar;
+    );
+
+    cases.add("constant size array",
+        \\void func(int array[20]);
+    ,
+        \\pub extern fn func(array: ?&c_int);
+    );
+
+    cases.add("self referential struct with function pointer",
+        \\struct Foo {
+        \\    void (*derp)(struct Foo *foo);
+        \\};
+    ,
+        \\pub const struct_Foo = extern struct {
+        \\    derp: ?extern fn(?&struct_Foo),
+        \\};
+    ,
+        \\pub const Foo = struct_Foo;
+    );
+
+    cases.add("struct prototype used in func",
+        \\struct Foo;
+        \\struct Foo *some_func(struct Foo *foo, int x);
+    ,
+        \\pub const struct_Foo = @OpaqueType();
+    ,
+        \\pub extern fn some_func(foo: ?&struct_Foo, x: c_int) -> ?&struct_Foo;
+    ,
+        \\pub const Foo = struct_Foo;
+    );
+
+    cases.add("#define a char literal",
+        \\#define A_CHAR  'a'
+    ,
+        \\pub const A_CHAR = 97;
+    );
+
+    cases.add("#define an unsigned integer literal",
+        \\#define CHANNEL_COUNT 24
+    ,
+        \\pub const CHANNEL_COUNT = 24;
+    );
+
+    cases.add("#define referencing another #define",
+        \\#define THING2 THING1
+        \\#define THING1 1234
+    ,
+        \\pub const THING1 = 1234;
+    ,
+        \\pub const THING2 = THING1;
+    );
+
+    cases.add("variables",
+        \\extern int extern_var;
+        \\static const int int_var = 13;
+    ,
+        \\pub extern var extern_var: c_int;
+    ,
+        \\pub const int_var: c_int = 13;
+    );
+
+    cases.add("circular struct definitions",
+        \\struct Bar;
+        \\
+        \\struct Foo {
+        \\    struct Bar *next;
+        \\};
+        \\
+        \\struct Bar {
+        \\    struct Foo *next;
+        \\};
+    ,
+        \\pub const struct_Bar = extern struct {
+        \\    next: ?&struct_Foo,
+        \\};
+    ,
+        \\pub const struct_Foo = extern struct {
+        \\    next: ?&struct_Bar,
+        \\};
+    );
+
+    cases.add("typedef void",
+        \\typedef void Foo;
+        \\Foo fun(Foo *a);
+    ,
+        \\pub const Foo = c_void;
+    ,
+        \\pub extern fn fun(a: ?&c_void);
+    );
+
+    cases.add("generate inline func for #define global extern fn",
+        \\extern void (*fn_ptr)(void);
+        \\#define foo fn_ptr
+        \\
+        \\extern char (*fn_ptr2)(int, float);
+        \\#define bar fn_ptr2
+    ,
+        \\pub extern var fn_ptr: ?extern fn();
+    ,
+        \\pub fn foo();
+    ,
+        \\pub extern var fn_ptr2: ?extern fn(c_int, f32) -> u8;
+    ,
+        \\pub fn bar(arg0: c_int, arg1: f32) -> u8;
+    );
+
+    cases.add("#define string",
+        \\#define  foo  "a string"
+    ,
+        \\pub const foo: &const u8 = &(c str lit);
+    );
+
+    cases.add("__cdecl doesn't mess up function pointers",
+        \\void foo(void (__cdecl *fn_ptr)(void));
+    ,
+        \\pub extern fn foo(fn_ptr: ?extern fn());
+    );
+
+    cases.add("comment after integer literal",
+        \\#define SDL_INIT_VIDEO 0x00000020  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+    ,
+        \\pub const SDL_INIT_VIDEO = 32;
+    );
+
+    cases.add("zig keywords in C code",
+        \\struct comptime {
+        \\    int defer;
+        \\};
+    ,
+        \\pub const struct_comptime = extern struct {
+        \\    @"defer": c_int,
+        \\};
+    ,
+        \\pub const @"comptime" = struct_comptime;
+    );
+
+    cases.add("macro defines string literal with octal",
+        \\#define FOO "aoeu\023 derp"
+        \\#define FOO2 "aoeu\0234 derp"
+        \\#define FOO_CHAR '\077'
+    ,
+        \\pub const FOO: &const u8 = &(c str lit);
+    ,
+        \\pub const FOO2: &const u8 = &(c str lit);
+    ,
+        \\pub const FOO_CHAR = 63;
+    );
+}
test/run_tests.cpp
@@ -1,472 +0,0 @@
-/*
- * Copyright (c) 2015 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#include "list.hpp"
-#include "buffer.hpp"
-#include "os.hpp"
-#include "error.hpp"
-#include "config.h"
-
-#include <stdio.h>
-#include <stdarg.h>
-
-enum TestSpecial {
-    TestSpecialNone,
-    TestSpecialLinkStep,
-};
-
-struct TestSourceFile {
-    const char *relative_path;
-    const char *source_code;
-};
-
-enum AllowWarnings {
-    AllowWarningsNo,
-    AllowWarningsYes,
-};
-
-struct TestCase {
-    const char *case_name;
-    const char *output;
-    ZigList<TestSourceFile> source_files;
-    ZigList<const char *> compile_errors;
-    ZigList<const char *> compiler_args;
-    ZigList<const char *> linker_args;
-    ZigList<const char *> program_args;
-    bool is_parseh;
-    TestSpecial special;
-    bool is_release_mode;
-    AllowWarnings allow_warnings;
-};
-
-static ZigList<TestCase*> test_cases = {0};
-static const char *tmp_source_path = ".tmp_source.zig";
-static const char *tmp_h_path = ".tmp_header.h";
-
-#if defined(_WIN32)
-static const char *tmp_exe_path = "./.tmp_exe.exe";
-static const char *zig_exe = "./zig.exe";
-#define NL "\r\n"
-#else
-static const char *tmp_exe_path = "./.tmp_exe";
-static const char *zig_exe = "./zig";
-#define NL "\n"
-#endif
-
-static TestCase *add_parseh_case(const char *case_name, AllowWarnings allow_warnings,
-    const char *source, size_t count, ...)
-{
-    va_list ap;
-    va_start(ap, count);
-
-    TestCase *test_case = allocate<TestCase>(1);
-    test_case->case_name = case_name;
-    test_case->is_parseh = true;
-    test_case->allow_warnings = allow_warnings;
-
-    test_case->source_files.resize(1);
-    test_case->source_files.at(0).relative_path = tmp_h_path;
-    test_case->source_files.at(0).source_code = source;
-
-    for (size_t i = 0; i < count; i += 1) {
-        const char *arg = va_arg(ap, const char *);
-        test_case->compile_errors.append(arg);
-    }
-
-    test_case->compiler_args.append("parseh");
-    test_case->compiler_args.append(tmp_h_path);
-    //test_case->compiler_args.append("--verbose");
-
-    test_cases.append(test_case);
-
-    va_end(ap);
-    return test_case;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-static void add_parseh_test_cases(void) {
-    add_parseh_case("simple data types", AllowWarningsYes, R"SOURCE(
-#include <stdint.h>
-int foo(char a, unsigned char b, signed char c);
-int foo(char a, unsigned char b, signed char c); // test a duplicate prototype
-void bar(uint8_t a, uint16_t b, uint32_t c, uint64_t d);
-void baz(int8_t a, int16_t b, int32_t c, int64_t d);
-    )SOURCE", 3,
-            "pub extern fn foo(a: u8, b: u8, c: i8) -> c_int;",
-            "pub extern fn bar(a: u8, b: u16, c: u32, d: u64);",
-            "pub extern fn baz(a: i8, b: i16, c: i32, d: i64);");
-
-    add_parseh_case("noreturn attribute", AllowWarningsNo, R"SOURCE(
-void foo(void) __attribute__((noreturn));
-    )SOURCE", 1, R"OUTPUT(pub extern fn foo() -> noreturn;)OUTPUT");
-
-    add_parseh_case("enums", AllowWarningsNo, R"SOURCE(
-enum Foo {
-    FooA,
-    FooB,
-    Foo1,
-};
-    )SOURCE", 5, R"(pub const enum_Foo = extern enum {
-    A,
-    B,
-    @"1",
-};)",
-            R"(pub const FooA = 0;)",
-            R"(pub const FooB = 1;)",
-            R"(pub const Foo1 = 2;)",
-            R"(pub const Foo = enum_Foo;)");
-
-    add_parseh_case("restrict -> noalias", AllowWarningsNo, R"SOURCE(
-void foo(void *restrict bar, void *restrict);
-    )SOURCE", 1, R"OUTPUT(pub extern fn foo(noalias bar: ?&c_void, noalias arg1: ?&c_void);)OUTPUT");
-
-    add_parseh_case("simple struct", AllowWarningsNo, R"SOURCE(
-struct Foo {
-    int x;
-    char *y;
-};
-    )SOURCE", 2,
-            R"OUTPUT(const struct_Foo = extern struct {
-    x: c_int,
-    y: ?&u8,
-};)OUTPUT", R"OUTPUT(pub const Foo = struct_Foo;)OUTPUT");
-
-    add_parseh_case("qualified struct and enum", AllowWarningsNo, R"SOURCE(
-struct Foo {
-    int x;
-    int y;
-};
-enum Bar {
-    BarA,
-    BarB,
-};
-void func(struct Foo *a, enum Bar **b);
-    )SOURCE", 7, R"OUTPUT(pub const struct_Foo = extern struct {
-    x: c_int,
-    y: c_int,
-};)OUTPUT", R"OUTPUT(pub const enum_Bar = extern enum {
-    A,
-    B,
-};)OUTPUT",
-            R"OUTPUT(pub const BarA = 0;)OUTPUT",
-            R"OUTPUT(pub const BarB = 1;)OUTPUT",
-            "pub extern fn func(a: ?&struct_Foo, b: ?&?&enum_Bar);",
-    R"OUTPUT(pub const Foo = struct_Foo;)OUTPUT",
-    R"OUTPUT(pub const Bar = enum_Bar;)OUTPUT");
-
-    add_parseh_case("constant size array", AllowWarningsNo, R"SOURCE(
-void func(int array[20]);
-    )SOURCE", 1, "pub extern fn func(array: ?&c_int);");
-
-
-    add_parseh_case("self referential struct with function pointer",
-        AllowWarningsNo, R"SOURCE(
-struct Foo {
-    void (*derp)(struct Foo *foo);
-};
-    )SOURCE", 2, R"OUTPUT(pub const struct_Foo = extern struct {
-    derp: ?extern fn(?&struct_Foo),
-};)OUTPUT", R"OUTPUT(pub const Foo = struct_Foo;)OUTPUT");
-
-
-    add_parseh_case("struct prototype used in func", AllowWarningsNo, R"SOURCE(
-struct Foo;
-struct Foo *some_func(struct Foo *foo, int x);
-    )SOURCE", 3, R"OUTPUT(pub const struct_Foo = @OpaqueType();)OUTPUT",
-        R"OUTPUT(pub extern fn some_func(foo: ?&struct_Foo, x: c_int) -> ?&struct_Foo;)OUTPUT",
-        R"OUTPUT(pub const Foo = struct_Foo;)OUTPUT");
-
-
-    add_parseh_case("#define a char literal", AllowWarningsNo, R"SOURCE(
-#define A_CHAR  'a'
-    )SOURCE", 1, R"OUTPUT(pub const A_CHAR = 97;)OUTPUT");
-
-
-    add_parseh_case("#define an unsigned integer literal", AllowWarningsNo,
-        R"SOURCE(
-#define CHANNEL_COUNT 24
-    )SOURCE", 1, R"OUTPUT(pub const CHANNEL_COUNT = 24;)OUTPUT");
-
-
-    add_parseh_case("#define referencing another #define", AllowWarningsNo,
-        R"SOURCE(
-#define THING2 THING1
-#define THING1 1234
-    )SOURCE", 2,
-            "pub const THING1 = 1234;",
-            "pub const THING2 = THING1;");
-
-
-    add_parseh_case("variables", AllowWarningsNo, R"SOURCE(
-extern int extern_var;
-static const int int_var = 13;
-    )SOURCE", 2,
-            "pub extern var extern_var: c_int;",
-            "pub const int_var: c_int = 13;");
-
-
-    add_parseh_case("circular struct definitions", AllowWarningsNo, R"SOURCE(
-struct Bar;
-
-struct Foo {
-    struct Bar *next;
-};
-
-struct Bar {
-    struct Foo *next;
-};
-    )SOURCE", 2,
-            R"SOURCE(pub const struct_Bar = extern struct {
-    next: ?&struct_Foo,
-};)SOURCE",
-            R"SOURCE(pub const struct_Foo = extern struct {
-    next: ?&struct_Bar,
-};)SOURCE");
-
-
-    add_parseh_case("typedef void", AllowWarningsNo, R"SOURCE(
-typedef void Foo;
-Foo fun(Foo *a);
-    )SOURCE", 2,
-            "pub const Foo = c_void;",
-            "pub extern fn fun(a: ?&c_void);");
-
-    add_parseh_case("generate inline func for #define global extern fn", AllowWarningsNo,
-        R"SOURCE(
-extern void (*fn_ptr)(void);
-#define foo fn_ptr
-
-extern char (*fn_ptr2)(int, float);
-#define bar fn_ptr2
-    )SOURCE", 4,
-            "pub extern var fn_ptr: ?extern fn();",
-            "pub fn foo();",
-            "pub extern var fn_ptr2: ?extern fn(c_int, f32) -> u8;",
-            "pub fn bar(arg0: c_int, arg1: f32) -> u8;");
-
-
-    add_parseh_case("#define string", AllowWarningsNo, R"SOURCE(
-#define  foo  "a string"
-    )SOURCE", 1, "pub const foo: &const u8 = &(c str lit);");
-
-    add_parseh_case("__cdecl doesn't mess up function pointers", AllowWarningsNo, R"SOURCE(
-void foo(void (__cdecl *fn_ptr)(void));
-    )SOURCE", 1, "pub extern fn foo(fn_ptr: ?extern fn());");
-
-    add_parseh_case("comment after integer literal", AllowWarningsNo, R"SOURCE(
-#define SDL_INIT_VIDEO 0x00000020  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
-    )SOURCE", 1, "pub const SDL_INIT_VIDEO = 32;");
-
-    add_parseh_case("zig keywords in C code", AllowWarningsNo, R"SOURCE(
-struct comptime {
-    int defer;
-};
-    )SOURCE", 2, R"(pub const struct_comptime = extern struct {
-    @"defer": c_int,
-};)", R"(pub const @"comptime" = struct_comptime;)");
-
-    add_parseh_case("macro defines string literal with octal", AllowWarningsNo, R"SOURCE(
-#define FOO "aoeu\023 derp"
-#define FOO2 "aoeu\0234 derp"
-#define FOO_CHAR '\077'
-    )SOURCE", 3,
-            R"(pub const FOO: &const u8 = &(c str lit);)",
-            R"(pub const FOO2: &const u8 = &(c str lit);)",
-            R"(pub const FOO_CHAR = 63;)");
-}
-
-static void print_compiler_invocation(TestCase *test_case) {
-    printf("%s", zig_exe);
-    for (size_t i = 0; i < test_case->compiler_args.length; i += 1) {
-        printf(" %s", test_case->compiler_args.at(i));
-    }
-    printf("\n");
-}
-
-static void print_linker_invocation(TestCase *test_case) {
-    printf("%s", zig_exe);
-    for (size_t i = 0; i < test_case->linker_args.length; i += 1) {
-        printf(" %s", test_case->linker_args.at(i));
-    }
-    printf("\n");
-}
-
-
-static void print_exe_invocation(TestCase *test_case) {
-    printf("%s", tmp_exe_path);
-    for (size_t i = 0; i < test_case->program_args.length; i += 1) {
-        printf(" %s", test_case->program_args.at(i));
-    }
-    printf("\n");
-}
-
-static void run_test(TestCase *test_case) {
-    for (size_t i = 0; i < test_case->source_files.length; i += 1) {
-        TestSourceFile *test_source = &test_case->source_files.at(i);
-        os_write_file(
-                buf_create_from_str(test_source->relative_path),
-                buf_create_from_str(test_source->source_code));
-    }
-
-    Buf zig_stderr = BUF_INIT;
-    Buf zig_stdout = BUF_INIT;
-    int err;
-    Termination term;
-    if ((err = os_exec_process(zig_exe, test_case->compiler_args, &term, &zig_stderr, &zig_stdout))) {
-        fprintf(stderr, "Unable to exec %s: %s\n", zig_exe, err_str(err));
-    }
-
-    if (!test_case->is_parseh && test_case->compile_errors.length) {
-        if (term.how != TerminationIdClean || term.code != 0) {
-            for (size_t i = 0; i < test_case->compile_errors.length; i += 1) {
-                const char *err_text = test_case->compile_errors.at(i);
-                if (!strstr(buf_ptr(&zig_stderr), err_text)) {
-                    printf("\n");
-                    printf("========= Expected this compile error: =========\n");
-                    printf("%s\n", err_text);
-                    printf("================================================\n");
-                    print_compiler_invocation(test_case);
-                    printf("%s\n", buf_ptr(&zig_stderr));
-                    exit(1);
-                }
-            }
-            return; // success
-        } else {
-            printf("\nCompile failed with return code 0 (Expected failure):\n");
-            print_compiler_invocation(test_case);
-            printf("%s\n", buf_ptr(&zig_stderr));
-            exit(1);
-        }
-    }
-
-    if (term.how != TerminationIdClean || term.code != 0) {
-        printf("\nCompile failed:\n");
-        print_compiler_invocation(test_case);
-        printf("%s\n", buf_ptr(&zig_stderr));
-        exit(1);
-    }
-
-    if (test_case->is_parseh) {
-        if (buf_len(&zig_stderr) > 0) {
-            printf("\nparseh emitted warnings:\n");
-            printf("------------------------------\n");
-            print_compiler_invocation(test_case);
-            printf("%s\n", buf_ptr(&zig_stderr));
-            printf("------------------------------\n");
-            if (test_case->allow_warnings == AllowWarningsNo) {
-                exit(1);
-            }
-        }
-
-        for (size_t i = 0; i < test_case->compile_errors.length; i += 1) {
-            const char *output = test_case->compile_errors.at(i);
-
-            if (!strstr(buf_ptr(&zig_stdout), output)) {
-                printf("\n");
-                printf("========= Expected this output: =========\n");
-                printf("%s\n", output);
-                printf("================================================\n");
-                print_compiler_invocation(test_case);
-                printf("%s\n", buf_ptr(&zig_stdout));
-                exit(1);
-            }
-        }
-    } else {
-        if (test_case->special == TestSpecialLinkStep) {
-            Buf link_stderr = BUF_INIT;
-            Buf link_stdout = BUF_INIT;
-            int err;
-            Termination term;
-            if ((err = os_exec_process(zig_exe, test_case->linker_args, &term, &link_stderr, &link_stdout))) {
-                fprintf(stderr, "Unable to exec %s: %s\n", zig_exe, err_str(err));
-            }
-
-            if (term.how != TerminationIdClean || term.code != 0) {
-                printf("\nLink failed:\n");
-                print_linker_invocation(test_case);
-                printf("%s\n", buf_ptr(&zig_stderr));
-                exit(1);
-            }
-        }
-
-        Buf program_stderr = BUF_INIT;
-        Buf program_stdout = BUF_INIT;
-        os_exec_process(tmp_exe_path, test_case->program_args, &term, &program_stderr, &program_stdout);
-
-        if (term.how != TerminationIdClean || term.code != 0) {
-            printf("\nProgram exited with error\n");
-            print_compiler_invocation(test_case);
-            print_exe_invocation(test_case);
-            printf("%s\n", buf_ptr(&program_stderr));
-            exit(1);
-        }
-
-        if (test_case->output != nullptr && !buf_eql_str(&program_stdout, test_case->output)) {
-            printf("\n");
-            print_compiler_invocation(test_case);
-            print_exe_invocation(test_case);
-            printf("==== Test failed. Expected output: ====\n");
-            printf("%s\n", test_case->output);
-            printf("========= Actual output: ==============\n");
-            printf("%s\n", buf_ptr(&program_stdout));
-            printf("=======================================\n");
-            exit(1);
-        }
-    }
-
-    for (size_t i = 0; i < test_case->source_files.length; i += 1) {
-        TestSourceFile *test_source = &test_case->source_files.at(i);
-        remove(test_source->relative_path);
-    }
-}
-
-static void run_all_tests(const char *grep_text) {
-    for (size_t i = 0; i < test_cases.length; i += 1) {
-        TestCase *test_case = test_cases.at(i);
-        if (grep_text != nullptr && strstr(test_case->case_name, grep_text) == nullptr) {
-            continue;
-        }
-
-        printf("Test %zu/%zu %s...", i + 1, test_cases.length, test_case->case_name);
-        fflush(stdout);
-        run_test(test_case);
-        printf("OK\n");
-    }
-    printf("%zu tests passed.\n", test_cases.length);
-}
-
-static void cleanup(void) {
-    remove(tmp_source_path);
-    remove(tmp_h_path);
-    remove(tmp_exe_path);
-}
-
-static int usage(const char *arg0) {
-    fprintf(stderr, "Usage: %s [--grep text]\n", arg0);
-    return 1;
-}
-
-int main(int argc, char **argv) {
-    const char *grep_text = nullptr;
-    for (int i = 1; i < argc; i += 1) {
-        const char *arg = argv[i];
-        if (i + 1 >= argc) {
-            return usage(argv[0]);
-        } else {
-            i += 1;
-            if (strcmp(arg, "--grep") == 0) {
-                grep_text = argv[i];
-            } else {
-                return usage(argv[0]);
-            }
-        }
-    }
-    add_parseh_test_cases();
-    run_all_tests(grep_text);
-    cleanup();
-}
test/tests.zig
@@ -10,13 +10,14 @@ const mem = std.mem;
 const fmt = std.fmt;
 const List = std.list.List;
 
-error TestFailed;
+const compare_output = @import("compare_output.zig");
+const build_examples = @import("build_examples.zig");
+const compile_errors = @import("compile_errors.zig");
+const assemble_and_link = @import("assemble_and_link.zig");
+const debug_safety = @import("debug_safety.zig");
+const parseh = @import("parseh.zig");
 
-pub const compare_output = @import("compare_output.zig");
-pub const build_examples = @import("build_examples.zig");
-pub const compile_errors = @import("compile_errors.zig");
-pub const assemble_and_link = @import("assemble_and_link.zig");
-pub const debug_safety = @import("debug_safety.zig");
+error TestFailed;
 
 pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
     const cases = %%b.allocator.create(CompareOutputContext);
@@ -88,6 +89,20 @@ pub fn addAssembleAndLinkTests(b: &build.Builder, test_filter: ?[]const u8) -> &
     return cases.step;
 }
 
+pub fn addParseHTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
+    const cases = %%b.allocator.create(ParseHContext);
+    *cases = ParseHContext {
+        .b = b,
+        .step = b.step("test-parseh", "Run the C header file parsing tests"),
+        .test_index = 0,
+        .test_filter = test_filter,
+    };
+
+    parseh.addCases(cases);
+
+    return cases.step;
+}
+
 pub const CompareOutputContext = struct {
     b: &build.Builder,
     step: &build.Step,
@@ -645,3 +660,188 @@ pub const BuildExamplesContext = struct {
         }
     }
 };
+
+pub const ParseHContext = struct {
+    b: &build.Builder,
+    step: &build.Step,
+    test_index: usize,
+    test_filter: ?[]const u8,
+
+    const TestCase = struct {
+        name: []const u8,
+        sources: List(SourceFile),
+        expected_lines: List([]const u8),
+        allow_warnings: 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 addExpectedError(self: &TestCase, text: []const u8) {
+            %%self.expected_lines.append(text);
+        }
+    };
+
+    const ParseHCmpOutputStep = struct {
+        step: build.Step,
+        context: &ParseHContext,
+        name: []const u8,
+        test_index: usize,
+        case: &const TestCase,
+
+        pub fn create(context: &ParseHContext, name: []const u8, case: &const TestCase) -> &ParseHCmpOutputStep {
+            const allocator = context.b.allocator;
+            const ptr = %%allocator.create(ParseHCmpOutputStep);
+            *ptr = ParseHCmpOutputStep {
+                .step = build.Step.init("ParseHCmpOutput", allocator, make),
+                .context = context,
+                .name = name,
+                .test_index = context.test_index,
+                .case = case,
+            };
+            context.test_index += 1;
+            return ptr;
+        }
+
+        fn make(step: &build.Step) -> %void {
+            const self = @fieldParentPtr(ParseHCmpOutputStep, "step", step);
+            const b = self.context.b;
+
+            const root_src = %%os.path.join(b.allocator, "test_artifacts", self.case.sources.items[0].filename);
+
+            var zig_args = List([]const u8).init(b.allocator);
+            %%zig_args.append("parseh");
+            %%zig_args.append(b.pathFromRoot(root_src));
+
+            %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
+
+            if (b.verbose) {
+                printInvocation(b.zig_exe, zig_args.toSliceConst());
+            }
+
+            var child = os.ChildProcess.spawn(b.zig_exe, zig_args.toSliceConst(), &b.env_map,
+                StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err|
+            {
+                debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));
+            };
+
+            const term = child.wait() %% |err| {
+                debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));
+            };
+            switch (term) {
+                Term.Clean => |code| {
+                    if (code != 0) {
+                        %%io.stderr.printf("Compilation failed with exit code {}\n", code);
+                        return error.TestFailed;
+                    }
+                },
+                Term.Signal => |code| {
+                    %%io.stderr.printf("Compilation failed with signal {}\n", code);
+                    return error.TestFailed;
+                },
+                else => {
+                    %%io.stderr.printf("Compilation terminated unexpectedly\n");
+                    return error.TestFailed;
+                },
+            };
+
+            var stdout_buf = %%Buffer0.initEmpty(b.allocator);
+            var stderr_buf = %%Buffer0.initEmpty(b.allocator);
+
+            %%(??child.stdout).readAll(&stdout_buf);
+            %%(??child.stderr).readAll(&stderr_buf);
+
+            const stdout = stdout_buf.toSliceConst();
+            const stderr = stderr_buf.toSliceConst();
+
+            if (stderr.len != 0 and !self.case.allow_warnings) {
+                %%io.stderr.printf(
+                    \\====== parseh emitted warnings: ============
+                    \\{}
+                    \\============================================
+                    \\
+                , stderr);
+                return error.TestFailed;
+            }
+
+            for (self.case.expected_lines.toSliceConst()) |expected_line| {
+                if (mem.indexOf(u8, stdout, expected_line) == null) {
+                    %%io.stderr.printf(
+                        \\
+                        \\========= Expected this output: ================
+                        \\{}
+                        \\================================================
+                        \\{}
+                        \\
+                    , expected_line, stdout);
+                    return error.TestFailed;
+                }
+            }
+            %%io.stderr.printf("OK\n");
+        }
+    };
+
+    fn printInvocation(exe_path: []const u8, args: []const []const u8) {
+        %%io.stderr.printf("{}", exe_path);
+        for (args) |arg| {
+            %%io.stderr.printf(" {}", arg);
+        }
+        %%io.stderr.printf("\n");
+    }
+
+    pub fn create(self: &ParseHContext, allow_warnings: bool, name: []const u8,
+        source: []const u8, expected_lines: ...) -> &TestCase
+    {
+        const tc = %%self.b.allocator.create(TestCase);
+        *tc = TestCase {
+            .name = name,
+            .sources = List(TestCase.SourceFile).init(self.b.allocator),
+            .expected_lines = List([]const u8).init(self.b.allocator),
+            .allow_warnings = allow_warnings,
+        };
+        tc.addSourceFile("source.h", source);
+        comptime var arg_i = 0;
+        inline while (arg_i < expected_lines.len; arg_i += 1) {
+            // TODO mem.dupe is because of issue #336
+            tc.addExpectedError(%%mem.dupe(self.b.allocator, u8, expected_lines[arg_i]));
+        }
+        return tc;
+    }
+
+    pub fn add(self: &ParseHContext, name: []const u8, source: []const u8, expected_lines: ...) {
+        const tc = self.create(false, name, source, expected_lines);
+        self.addCase(tc);
+    }
+
+    pub fn addAllowWarnings(self: &ParseHContext, name: []const u8, source: []const u8, expected_lines: ...) {
+        const tc = self.create(true, name, source, expected_lines);
+        self.addCase(tc);
+    }
+
+    pub fn addCase(self: &ParseHContext, case: &const TestCase) {
+        const b = self.b;
+
+        const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "parseh {}", case.name);
+        if (const filter ?= self.test_filter) {
+            if (mem.indexOf(u8, annotated_case_name, filter) == null)
+                return;
+        }
+
+        const parseh_and_cmp = ParseHCmpOutputStep.create(self, annotated_case_name, case);
+        self.step.dependOn(&parseh_and_cmp.step);
+
+        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);
+            parseh_and_cmp.step.dependOn(&write_src.step);
+        }
+    }
+};
build.zig
@@ -44,4 +44,5 @@ pub fn build(b: &Builder) {
     test_step.dependOn(tests.addCompileErrorTests(b, test_filter));
     test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter));
     test_step.dependOn(tests.addDebugSafetyTests(b, test_filter));
+    test_step.dependOn(tests.addParseHTests(b, test_filter));
 }
CMakeLists.txt
@@ -63,14 +63,6 @@ set(ZIG_SOURCES
     "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
 )
 
-set(TEST_SOURCES
-    "${CMAKE_SOURCE_DIR}/src/buffer.cpp"
-    "${CMAKE_SOURCE_DIR}/src/util.cpp"
-    "${CMAKE_SOURCE_DIR}/src/os.cpp"
-    "${CMAKE_SOURCE_DIR}/src/error.cpp"
-    "${CMAKE_SOURCE_DIR}/test/run_tests.cpp"
-)
-
 set(C_HEADERS
     "${CMAKE_SOURCE_DIR}/c_headers/Intrin.h"
     "${CMAKE_SOURCE_DIR}/c_headers/__stddef_max_align_t.h"
@@ -248,19 +240,12 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/special/test_runner.zig" DESTINATION "${Z
 install(FILES "${CMAKE_SOURCE_DIR}/std/special/zigrt.zig" DESTINATION "${ZIG_STD_DEST}/special")
 install(FILES "${CMAKE_SOURCE_DIR}/std/target.zig" DESTINATION "${ZIG_STD_DEST}")
 
-add_executable(run_tests ${TEST_SOURCES})
-target_link_libraries(run_tests)
-set_target_properties(run_tests PROPERTIES
-    COMPILE_FLAGS ${EXE_CFLAGS}
-    LINK_FLAGS ${EXE_LDFLAGS}
-)
-
 if (ZIG_TEST_COVERAGE)
     add_custom_target(coverage
         DEPENDS run_tests
         WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
         COMMAND lcov --directory . --zerocounters --rc lcov_branch_coverage=1
-        COMMAND ./run_tests
+        COMMAND ./zig build --build-file ../build.zig test
         COMMAND lcov --directory . --capture --output-file coverage.info --rc lcov_branch_coverage=1
         COMMAND lcov --remove coverage.info '/usr/*' --output-file coverage.info.cleaned --rc lcov_branch_coverage=1
         COMMAND genhtml -o coverage coverage.info.cleaned --rc lcov_branch_coverage=1