| author | |
| committer | |
| log | 1ff73a8e69a09b3bc993cffb755cc2ca98c7040b |
| tree | e01062057af7af8e7b144666b106f110b8e2fc1c |
| parent | d12f1f5b4954d893111c05420060354537a1485c |
5 files changed, 451 insertions(+), 494 deletions(-)
CMakeLists.txt+1-16| ... | @@ -63,14 +63,6 @@ set(ZIG_SOURCES | ... | @@ -63,14 +63,6 @@ set(ZIG_SOURCES |
| 63 | "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp" | 63 | "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp" |
| 64 | ) | 64 | ) |
| 65 | 65 | ||
| 66 | set(TEST_SOURCES | ||
| 67 | "${CMAKE_SOURCE_DIR}/src/buffer.cpp" | ||
| 68 | "${CMAKE_SOURCE_DIR}/src/util.cpp" | ||
| 69 | "${CMAKE_SOURCE_DIR}/src/os.cpp" | ||
| 70 | "${CMAKE_SOURCE_DIR}/src/error.cpp" | ||
| 71 | "${CMAKE_SOURCE_DIR}/test/run_tests.cpp" | ||
| 72 | ) | ||
| 73 | |||
| 74 | set(C_HEADERS | 66 | set(C_HEADERS |
| 75 | "${CMAKE_SOURCE_DIR}/c_headers/Intrin.h" | 67 | "${CMAKE_SOURCE_DIR}/c_headers/Intrin.h" |
| 76 | "${CMAKE_SOURCE_DIR}/c_headers/__stddef_max_align_t.h" | 68 | "${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 | ... | @@ -248,19 +240,12 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/special/test_runner.zig" DESTINATION "${Z |
| 248 | install(FILES "${CMAKE_SOURCE_DIR}/std/special/zigrt.zig" DESTINATION "${ZIG_STD_DEST}/special") | 240 | install(FILES "${CMAKE_SOURCE_DIR}/std/special/zigrt.zig" DESTINATION "${ZIG_STD_DEST}/special") |
| 249 | install(FILES "${CMAKE_SOURCE_DIR}/std/target.zig" DESTINATION "${ZIG_STD_DEST}") | 241 | install(FILES "${CMAKE_SOURCE_DIR}/std/target.zig" DESTINATION "${ZIG_STD_DEST}") |
| 250 | 242 | ||
| 251 | add_executable(run_tests ${TEST_SOURCES}) | ||
| 252 | target_link_libraries(run_tests) | ||
| 253 | set_target_properties(run_tests PROPERTIES | ||
| 254 | COMPILE_FLAGS ${EXE_CFLAGS} | ||
| 255 | LINK_FLAGS ${EXE_LDFLAGS} | ||
| 256 | ) | ||
| 257 | |||
| 258 | if (ZIG_TEST_COVERAGE) | 243 | if (ZIG_TEST_COVERAGE) |
| 259 | add_custom_target(coverage | 244 | add_custom_target(coverage |
| 260 | DEPENDS run_tests | 245 | DEPENDS run_tests |
| 261 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} | 246 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} |
| 262 | COMMAND lcov --directory . --zerocounters --rc lcov_branch_coverage=1 | 247 | COMMAND lcov --directory . --zerocounters --rc lcov_branch_coverage=1 |
| 263 | COMMAND ./run_tests | 248 | COMMAND ./zig build --build-file ../build.zig test |
| 264 | COMMAND lcov --directory . --capture --output-file coverage.info --rc lcov_branch_coverage=1 | 249 | COMMAND lcov --directory . --capture --output-file coverage.info --rc lcov_branch_coverage=1 |
| 265 | COMMAND lcov --remove coverage.info '/usr/*' --output-file coverage.info.cleaned --rc lcov_branch_coverage=1 | 250 | COMMAND lcov --remove coverage.info '/usr/*' --output-file coverage.info.cleaned --rc lcov_branch_coverage=1 |
| 266 | COMMAND genhtml -o coverage coverage.info.cleaned --rc lcov_branch_coverage=1 | 251 | COMMAND genhtml -o coverage coverage.info.cleaned --rc lcov_branch_coverage=1 |
build.zig+1| ... | @@ -44,4 +44,5 @@ pub fn build(b: &Builder) { | ... | @@ -44,4 +44,5 @@ pub fn build(b: &Builder) { |
| 44 | test_step.dependOn(tests.addCompileErrorTests(b, test_filter)); | 44 | test_step.dependOn(tests.addCompileErrorTests(b, test_filter)); |
| 45 | test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter)); | 45 | test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter)); |
| 46 | test_step.dependOn(tests.addDebugSafetyTests(b, test_filter)); | 46 | test_step.dependOn(tests.addDebugSafetyTests(b, test_filter)); |
| 47 | test_step.dependOn(tests.addParseHTests(b, test_filter)); | ||
| 47 | } | 48 | } |
test/parseh.zig created+243| ... | @@ -0,0 +1,243 @@ | ||
| 1 | const tests = @import("tests.zig"); | ||
| 2 | |||
| 3 | pub fn addCases(cases: &tests.ParseHContext) { | ||
| 4 | cases.addAllowWarnings("simple data types", | ||
| 5 | \\#include <stdint.h> | ||
| 6 | \\int foo(char a, unsigned char b, signed char c); | ||
| 7 | \\int foo(char a, unsigned char b, signed char c); // test a duplicate prototype | ||
| 8 | \\void bar(uint8_t a, uint16_t b, uint32_t c, uint64_t d); | ||
| 9 | \\void baz(int8_t a, int16_t b, int32_t c, int64_t d); | ||
| 10 | , | ||
| 11 | \\pub extern fn foo(a: u8, b: u8, c: i8) -> c_int; | ||
| 12 | , | ||
| 13 | \\pub extern fn bar(a: u8, b: u16, c: u32, d: u64); | ||
| 14 | , | ||
| 15 | \\pub extern fn baz(a: i8, b: i16, c: i32, d: i64); | ||
| 16 | ); | ||
| 17 | |||
| 18 | cases.add("noreturn attribute", | ||
| 19 | \\void foo(void) __attribute__((noreturn)); | ||
| 20 | , | ||
| 21 | \\pub extern fn foo() -> noreturn; | ||
| 22 | ); | ||
| 23 | |||
| 24 | cases.add("enums", | ||
| 25 | \\enum Foo { | ||
| 26 | \\ FooA, | ||
| 27 | \\ FooB, | ||
| 28 | \\ Foo1, | ||
| 29 | \\}; | ||
| 30 | , | ||
| 31 | \\pub const enum_Foo = extern enum { | ||
| 32 | \\ A, | ||
| 33 | \\ B, | ||
| 34 | \\ @"1", | ||
| 35 | \\}; | ||
| 36 | , | ||
| 37 | \\pub const FooA = 0; | ||
| 38 | , | ||
| 39 | \\pub const FooB = 1; | ||
| 40 | , | ||
| 41 | \\pub const Foo1 = 2; | ||
| 42 | , | ||
| 43 | \\pub const Foo = enum_Foo | ||
| 44 | ); | ||
| 45 | |||
| 46 | cases.add("restrict -> noalias", | ||
| 47 | \\void foo(void *restrict bar, void *restrict); | ||
| 48 | , | ||
| 49 | \\pub extern fn foo(noalias bar: ?&c_void, noalias arg1: ?&c_void); | ||
| 50 | ); | ||
| 51 | |||
| 52 | cases.add("simple struct", | ||
| 53 | \\struct Foo { | ||
| 54 | \\ int x; | ||
| 55 | \\ char *y; | ||
| 56 | \\}; | ||
| 57 | , | ||
| 58 | \\const struct_Foo = extern struct { | ||
| 59 | \\ x: c_int, | ||
| 60 | \\ y: ?&u8, | ||
| 61 | \\}; | ||
| 62 | , | ||
| 63 | \\pub const Foo = struct_Foo; | ||
| 64 | ); | ||
| 65 | |||
| 66 | cases.add("qualified struct and enum", | ||
| 67 | \\struct Foo { | ||
| 68 | \\ int x; | ||
| 69 | \\ int y; | ||
| 70 | \\}; | ||
| 71 | \\enum Bar { | ||
| 72 | \\ BarA, | ||
| 73 | \\ BarB, | ||
| 74 | \\}; | ||
| 75 | \\void func(struct Foo *a, enum Bar **b); | ||
| 76 | , | ||
| 77 | \\pub const struct_Foo = extern struct { | ||
| 78 | \\ x: c_int, | ||
| 79 | \\ y: c_int, | ||
| 80 | \\}; | ||
| 81 | , | ||
| 82 | \\pub const enum_Bar = extern enum { | ||
| 83 | \\ A, | ||
| 84 | \\ B, | ||
| 85 | \\}; | ||
| 86 | , | ||
| 87 | \\pub const BarA = 0; | ||
| 88 | , | ||
| 89 | \\pub const BarB = 1; | ||
| 90 | , | ||
| 91 | \\pub extern fn func(a: ?&struct_Foo, b: ?&?&enum_Bar); | ||
| 92 | , | ||
| 93 | \\pub const Foo = struct_Foo; | ||
| 94 | , | ||
| 95 | \\pub const Bar = enum_Bar; | ||
| 96 | ); | ||
| 97 | |||
| 98 | cases.add("constant size array", | ||
| 99 | \\void func(int array[20]); | ||
| 100 | , | ||
| 101 | \\pub extern fn func(array: ?&c_int); | ||
| 102 | ); | ||
| 103 | |||
| 104 | cases.add("self referential struct with function pointer", | ||
| 105 | \\struct Foo { | ||
| 106 | \\ void (*derp)(struct Foo *foo); | ||
| 107 | \\}; | ||
| 108 | , | ||
| 109 | \\pub const struct_Foo = extern struct { | ||
| 110 | \\ derp: ?extern fn(?&struct_Foo), | ||
| 111 | \\}; | ||
| 112 | , | ||
| 113 | \\pub const Foo = struct_Foo; | ||
| 114 | ); | ||
| 115 | |||
| 116 | cases.add("struct prototype used in func", | ||
| 117 | \\struct Foo; | ||
| 118 | \\struct Foo *some_func(struct Foo *foo, int x); | ||
| 119 | , | ||
| 120 | \\pub const struct_Foo = @OpaqueType(); | ||
| 121 | , | ||
| 122 | \\pub extern fn some_func(foo: ?&struct_Foo, x: c_int) -> ?&struct_Foo; | ||
| 123 | , | ||
| 124 | \\pub const Foo = struct_Foo; | ||
| 125 | ); | ||
| 126 | |||
| 127 | cases.add("#define a char literal", | ||
| 128 | \\#define A_CHAR 'a' | ||
| 129 | , | ||
| 130 | \\pub const A_CHAR = 97; | ||
| 131 | ); | ||
| 132 | |||
| 133 | cases.add("#define an unsigned integer literal", | ||
| 134 | \\#define CHANNEL_COUNT 24 | ||
| 135 | , | ||
| 136 | \\pub const CHANNEL_COUNT = 24; | ||
| 137 | ); | ||
| 138 | |||
| 139 | cases.add("#define referencing another #define", | ||
| 140 | \\#define THING2 THING1 | ||
| 141 | \\#define THING1 1234 | ||
| 142 | , | ||
| 143 | \\pub const THING1 = 1234; | ||
| 144 | , | ||
| 145 | \\pub const THING2 = THING1; | ||
| 146 | ); | ||
| 147 | |||
| 148 | cases.add("variables", | ||
| 149 | \\extern int extern_var; | ||
| 150 | \\static const int int_var = 13; | ||
| 151 | , | ||
| 152 | \\pub extern var extern_var: c_int; | ||
| 153 | , | ||
| 154 | \\pub const int_var: c_int = 13; | ||
| 155 | ); | ||
| 156 | |||
| 157 | cases.add("circular struct definitions", | ||
| 158 | \\struct Bar; | ||
| 159 | \\ | ||
| 160 | \\struct Foo { | ||
| 161 | \\ struct Bar *next; | ||
| 162 | \\}; | ||
| 163 | \\ | ||
| 164 | \\struct Bar { | ||
| 165 | \\ struct Foo *next; | ||
| 166 | \\}; | ||
| 167 | , | ||
| 168 | \\pub const struct_Bar = extern struct { | ||
| 169 | \\ next: ?&struct_Foo, | ||
| 170 | \\}; | ||
| 171 | , | ||
| 172 | \\pub const struct_Foo = extern struct { | ||
| 173 | \\ next: ?&struct_Bar, | ||
| 174 | \\}; | ||
| 175 | ); | ||
| 176 | |||
| 177 | cases.add("typedef void", | ||
| 178 | \\typedef void Foo; | ||
| 179 | \\Foo fun(Foo *a); | ||
| 180 | , | ||
| 181 | \\pub const Foo = c_void; | ||
| 182 | , | ||
| 183 | \\pub extern fn fun(a: ?&c_void); | ||
| 184 | ); | ||
| 185 | |||
| 186 | cases.add("generate inline func for #define global extern fn", | ||
| 187 | \\extern void (*fn_ptr)(void); | ||
| 188 | \\#define foo fn_ptr | ||
| 189 | \\ | ||
| 190 | \\extern char (*fn_ptr2)(int, float); | ||
| 191 | \\#define bar fn_ptr2 | ||
| 192 | , | ||
| 193 | \\pub extern var fn_ptr: ?extern fn(); | ||
| 194 | , | ||
| 195 | \\pub fn foo(); | ||
| 196 | , | ||
| 197 | \\pub extern var fn_ptr2: ?extern fn(c_int, f32) -> u8; | ||
| 198 | , | ||
| 199 | \\pub fn bar(arg0: c_int, arg1: f32) -> u8; | ||
| 200 | ); | ||
| 201 | |||
| 202 | cases.add("#define string", | ||
| 203 | \\#define foo "a string" | ||
| 204 | , | ||
| 205 | \\pub const foo: &const u8 = &(c str lit); | ||
| 206 | ); | ||
| 207 | |||
| 208 | cases.add("__cdecl doesn't mess up function pointers", | ||
| 209 | \\void foo(void (__cdecl *fn_ptr)(void)); | ||
| 210 | , | ||
| 211 | \\pub extern fn foo(fn_ptr: ?extern fn()); | ||
| 212 | ); | ||
| 213 | |||
| 214 | cases.add("comment after integer literal", | ||
| 215 | \\#define SDL_INIT_VIDEO 0x00000020 /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ | ||
| 216 | , | ||
| 217 | \\pub const SDL_INIT_VIDEO = 32; | ||
| 218 | ); | ||
| 219 | |||
| 220 | cases.add("zig keywords in C code", | ||
| 221 | \\struct comptime { | ||
| 222 | \\ int defer; | ||
| 223 | \\}; | ||
| 224 | , | ||
| 225 | \\pub const struct_comptime = extern struct { | ||
| 226 | \\ @"defer": c_int, | ||
| 227 | \\}; | ||
| 228 | , | ||
| 229 | \\pub const @"comptime" = struct_comptime; | ||
| 230 | ); | ||
| 231 | |||
| 232 | cases.add("macro defines string literal with octal", | ||
| 233 | \\#define FOO "aoeu\023 derp" | ||
| 234 | \\#define FOO2 "aoeu\0234 derp" | ||
| 235 | \\#define FOO_CHAR '\077' | ||
| 236 | , | ||
| 237 | \\pub const FOO: &const u8 = &(c str lit); | ||
| 238 | , | ||
| 239 | \\pub const FOO2: &const u8 = &(c str lit); | ||
| 240 | , | ||
| 241 | \\pub const FOO_CHAR = 63; | ||
| 242 | ); | ||
| 243 | } | ||
test/run_tests.cpp deleted-472| ... | @@ -1,472 +0,0 @@ | ||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 Andrew Kelley | ||
| 3 | * | ||
| 4 | * This file is part of zig, which is MIT licensed. | ||
| 5 | * See http://opensource.org/licenses/MIT | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "list.hpp" | ||
| 9 | #include "buffer.hpp" | ||
| 10 | #include "os.hpp" | ||
| 11 | #include "error.hpp" | ||
| 12 | #include "config.h" | ||
| 13 | |||
| 14 | #include <stdio.h> | ||
| 15 | #include <stdarg.h> | ||
| 16 | |||
| 17 | enum TestSpecial { | ||
| 18 | TestSpecialNone, | ||
| 19 | TestSpecialLinkStep, | ||
| 20 | }; | ||
| 21 | |||
| 22 | struct TestSourceFile { | ||
| 23 | const char *relative_path; | ||
| 24 | const char *source_code; | ||
| 25 | }; | ||
| 26 | |||
| 27 | enum AllowWarnings { | ||
| 28 | AllowWarningsNo, | ||
| 29 | AllowWarningsYes, | ||
| 30 | }; | ||
| 31 | |||
| 32 | struct TestCase { | ||
| 33 | const char *case_name; | ||
| 34 | const char *output; | ||
| 35 | ZigList<TestSourceFile> source_files; | ||
| 36 | ZigList<const char *> compile_errors; | ||
| 37 | ZigList<const char *> compiler_args; | ||
| 38 | ZigList<const char *> linker_args; | ||
| 39 | ZigList<const char *> program_args; | ||
| 40 | bool is_parseh; | ||
| 41 | TestSpecial special; | ||
| 42 | bool is_release_mode; | ||
| 43 | AllowWarnings allow_warnings; | ||
| 44 | }; | ||
| 45 | |||
| 46 | static ZigList<TestCase*> test_cases = {0}; | ||
| 47 | static const char *tmp_source_path = ".tmp_source.zig"; | ||
| 48 | static const char *tmp_h_path = ".tmp_header.h"; | ||
| 49 | |||
| 50 | #if defined(_WIN32) | ||
| 51 | static const char *tmp_exe_path = "./.tmp_exe.exe"; | ||
| 52 | static const char *zig_exe = "./zig.exe"; | ||
| 53 | #define NL "\r\n" | ||
| 54 | #else | ||
| 55 | static const char *tmp_exe_path = "./.tmp_exe"; | ||
| 56 | static const char *zig_exe = "./zig"; | ||
| 57 | #define NL "\n" | ||
| 58 | #endif | ||
| 59 | |||
| 60 | static TestCase *add_parseh_case(const char *case_name, AllowWarnings allow_warnings, | ||
| 61 | const char *source, size_t count, ...) | ||
| 62 | { | ||
| 63 | va_list ap; | ||
| 64 | va_start(ap, count); | ||
| 65 | |||
| 66 | TestCase *test_case = allocate<TestCase>(1); | ||
| 67 | test_case->case_name = case_name; | ||
| 68 | test_case->is_parseh = true; | ||
| 69 | test_case->allow_warnings = allow_warnings; | ||
| 70 | |||
| 71 | test_case->source_files.resize(1); | ||
| 72 | test_case->source_files.at(0).relative_path = tmp_h_path; | ||
| 73 | test_case->source_files.at(0).source_code = source; | ||
| 74 | |||
| 75 | for (size_t i = 0; i < count; i += 1) { | ||
| 76 | const char *arg = va_arg(ap, const char *); | ||
| 77 | test_case->compile_errors.append(arg); | ||
| 78 | } | ||
| 79 | |||
| 80 | test_case->compiler_args.append("parseh"); | ||
| 81 | test_case->compiler_args.append(tmp_h_path); | ||
| 82 | //test_case->compiler_args.append("--verbose"); | ||
| 83 | |||
| 84 | test_cases.append(test_case); | ||
| 85 | |||
| 86 | va_end(ap); | ||
| 87 | return test_case; | ||
| 88 | } | ||
| 89 | |||
| 90 | ////////////////////////////////////////////////////////////////////////////// | ||
| 91 | |||
| 92 | static void add_parseh_test_cases(void) { | ||
| 93 | add_parseh_case("simple data types", AllowWarningsYes, R"SOURCE( | ||
| 94 | #include <stdint.h> | ||
| 95 | int foo(char a, unsigned char b, signed char c); | ||
| 96 | int foo(char a, unsigned char b, signed char c); // test a duplicate prototype | ||
| 97 | void bar(uint8_t a, uint16_t b, uint32_t c, uint64_t d); | ||
| 98 | void baz(int8_t a, int16_t b, int32_t c, int64_t d); | ||
| 99 | )SOURCE", 3, | ||
| 100 | "pub extern fn foo(a: u8, b: u8, c: i8) -> c_int;", | ||
| 101 | "pub extern fn bar(a: u8, b: u16, c: u32, d: u64);", | ||
| 102 | "pub extern fn baz(a: i8, b: i16, c: i32, d: i64);"); | ||
| 103 | |||
| 104 | add_parseh_case("noreturn attribute", AllowWarningsNo, R"SOURCE( | ||
| 105 | void foo(void) __attribute__((noreturn)); | ||
| 106 | )SOURCE", 1, R"OUTPUT(pub extern fn foo() -> noreturn;)OUTPUT"); | ||
| 107 | |||
| 108 | add_parseh_case("enums", AllowWarningsNo, R"SOURCE( | ||
| 109 | enum Foo { | ||
| 110 | FooA, | ||
| 111 | FooB, | ||
| 112 | Foo1, | ||
| 113 | }; | ||
| 114 | )SOURCE", 5, R"(pub const enum_Foo = extern enum { | ||
| 115 | A, | ||
| 116 | B, | ||
| 117 | @"1", | ||
| 118 | };)", | ||
| 119 | R"(pub const FooA = 0;)", | ||
| 120 | R"(pub const FooB = 1;)", | ||
| 121 | R"(pub const Foo1 = 2;)", | ||
| 122 | R"(pub const Foo = enum_Foo;)"); | ||
| 123 | |||
| 124 | add_parseh_case("restrict -> noalias", AllowWarningsNo, R"SOURCE( | ||
| 125 | void foo(void *restrict bar, void *restrict); | ||
| 126 | )SOURCE", 1, R"OUTPUT(pub extern fn foo(noalias bar: ?&c_void, noalias arg1: ?&c_void);)OUTPUT"); | ||
| 127 | |||
| 128 | add_parseh_case("simple struct", AllowWarningsNo, R"SOURCE( | ||
| 129 | struct Foo { | ||
| 130 | int x; | ||
| 131 | char *y; | ||
| 132 | }; | ||
| 133 | )SOURCE", 2, | ||
| 134 | R"OUTPUT(const struct_Foo = extern struct { | ||
| 135 | x: c_int, | ||
| 136 | y: ?&u8, | ||
| 137 | };)OUTPUT", R"OUTPUT(pub const Foo = struct_Foo;)OUTPUT"); | ||
| 138 | |||
| 139 | add_parseh_case("qualified struct and enum", AllowWarningsNo, R"SOURCE( | ||
| 140 | struct Foo { | ||
| 141 | int x; | ||
| 142 | int y; | ||
| 143 | }; | ||
| 144 | enum Bar { | ||
| 145 | BarA, | ||
| 146 | BarB, | ||
| 147 | }; | ||
| 148 | void func(struct Foo *a, enum Bar **b); | ||
| 149 | )SOURCE", 7, R"OUTPUT(pub const struct_Foo = extern struct { | ||
| 150 | x: c_int, | ||
| 151 | y: c_int, | ||
| 152 | };)OUTPUT", R"OUTPUT(pub const enum_Bar = extern enum { | ||
| 153 | A, | ||
| 154 | B, | ||
| 155 | };)OUTPUT", | ||
| 156 | R"OUTPUT(pub const BarA = 0;)OUTPUT", | ||
| 157 | R"OUTPUT(pub const BarB = 1;)OUTPUT", | ||
| 158 | "pub extern fn func(a: ?&struct_Foo, b: ?&?&enum_Bar);", | ||
| 159 | R"OUTPUT(pub const Foo = struct_Foo;)OUTPUT", | ||
| 160 | R"OUTPUT(pub const Bar = enum_Bar;)OUTPUT"); | ||
| 161 | |||
| 162 | add_parseh_case("constant size array", AllowWarningsNo, R"SOURCE( | ||
| 163 | void func(int array[20]); | ||
| 164 | )SOURCE", 1, "pub extern fn func(array: ?&c_int);"); | ||
| 165 | |||
| 166 | |||
| 167 | add_parseh_case("self referential struct with function pointer", | ||
| 168 | AllowWarningsNo, R"SOURCE( | ||
| 169 | struct Foo { | ||
| 170 | void (*derp)(struct Foo *foo); | ||
| 171 | }; | ||
| 172 | )SOURCE", 2, R"OUTPUT(pub const struct_Foo = extern struct { | ||
| 173 | derp: ?extern fn(?&struct_Foo), | ||
| 174 | };)OUTPUT", R"OUTPUT(pub const Foo = struct_Foo;)OUTPUT"); | ||
| 175 | |||
| 176 | |||
| 177 | add_parseh_case("struct prototype used in func", AllowWarningsNo, R"SOURCE( | ||
| 178 | struct Foo; | ||
| 179 | struct Foo *some_func(struct Foo *foo, int x); | ||
| 180 | )SOURCE", 3, R"OUTPUT(pub const struct_Foo = @OpaqueType();)OUTPUT", | ||
| 181 | R"OUTPUT(pub extern fn some_func(foo: ?&struct_Foo, x: c_int) -> ?&struct_Foo;)OUTPUT", | ||
| 182 | R"OUTPUT(pub const Foo = struct_Foo;)OUTPUT"); | ||
| 183 | |||
| 184 | |||
| 185 | add_parseh_case("#define a char literal", AllowWarningsNo, R"SOURCE( | ||
| 186 | #define A_CHAR 'a' | ||
| 187 | )SOURCE", 1, R"OUTPUT(pub const A_CHAR = 97;)OUTPUT"); | ||
| 188 | |||
| 189 | |||
| 190 | add_parseh_case("#define an unsigned integer literal", AllowWarningsNo, | ||
| 191 | R"SOURCE( | ||
| 192 | #define CHANNEL_COUNT 24 | ||
| 193 | )SOURCE", 1, R"OUTPUT(pub const CHANNEL_COUNT = 24;)OUTPUT"); | ||
| 194 | |||
| 195 | |||
| 196 | add_parseh_case("#define referencing another #define", AllowWarningsNo, | ||
| 197 | R"SOURCE( | ||
| 198 | #define THING2 THING1 | ||
| 199 | #define THING1 1234 | ||
| 200 | )SOURCE", 2, | ||
| 201 | "pub const THING1 = 1234;", | ||
| 202 | "pub const THING2 = THING1;"); | ||
| 203 | |||
| 204 | |||
| 205 | add_parseh_case("variables", AllowWarningsNo, R"SOURCE( | ||
| 206 | extern int extern_var; | ||
| 207 | static const int int_var = 13; | ||
| 208 | )SOURCE", 2, | ||
| 209 | "pub extern var extern_var: c_int;", | ||
| 210 | "pub const int_var: c_int = 13;"); | ||
| 211 | |||
| 212 | |||
| 213 | add_parseh_case("circular struct definitions", AllowWarningsNo, R"SOURCE( | ||
| 214 | struct Bar; | ||
| 215 | |||
| 216 | struct Foo { | ||
| 217 | struct Bar *next; | ||
| 218 | }; | ||
| 219 | |||
| 220 | struct Bar { | ||
| 221 | struct Foo *next; | ||
| 222 | }; | ||
| 223 | )SOURCE", 2, | ||
| 224 | R"SOURCE(pub const struct_Bar = extern struct { | ||
| 225 | next: ?&struct_Foo, | ||
| 226 | };)SOURCE", | ||
| 227 | R"SOURCE(pub const struct_Foo = extern struct { | ||
| 228 | next: ?&struct_Bar, | ||
| 229 | };)SOURCE"); | ||
| 230 | |||
| 231 | |||
| 232 | add_parseh_case("typedef void", AllowWarningsNo, R"SOURCE( | ||
| 233 | typedef void Foo; | ||
| 234 | Foo fun(Foo *a); | ||
| 235 | )SOURCE", 2, | ||
| 236 | "pub const Foo = c_void;", | ||
| 237 | "pub extern fn fun(a: ?&c_void);"); | ||
| 238 | |||
| 239 | add_parseh_case("generate inline func for #define global extern fn", AllowWarningsNo, | ||
| 240 | R"SOURCE( | ||
| 241 | extern void (*fn_ptr)(void); | ||
| 242 | #define foo fn_ptr | ||
| 243 | |||
| 244 | extern char (*fn_ptr2)(int, float); | ||
| 245 | #define bar fn_ptr2 | ||
| 246 | )SOURCE", 4, | ||
| 247 | "pub extern var fn_ptr: ?extern fn();", | ||
| 248 | "pub fn foo();", | ||
| 249 | "pub extern var fn_ptr2: ?extern fn(c_int, f32) -> u8;", | ||
| 250 | "pub fn bar(arg0: c_int, arg1: f32) -> u8;"); | ||
| 251 | |||
| 252 | |||
| 253 | add_parseh_case("#define string", AllowWarningsNo, R"SOURCE( | ||
| 254 | #define foo "a string" | ||
| 255 | )SOURCE", 1, "pub const foo: &const u8 = &(c str lit);"); | ||
| 256 | |||
| 257 | add_parseh_case("__cdecl doesn't mess up function pointers", AllowWarningsNo, R"SOURCE( | ||
| 258 | void foo(void (__cdecl *fn_ptr)(void)); | ||
| 259 | )SOURCE", 1, "pub extern fn foo(fn_ptr: ?extern fn());"); | ||
| 260 | |||
| 261 | add_parseh_case("comment after integer literal", AllowWarningsNo, R"SOURCE( | ||
| 262 | #define SDL_INIT_VIDEO 0x00000020 /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ | ||
| 263 | )SOURCE", 1, "pub const SDL_INIT_VIDEO = 32;"); | ||
| 264 | |||
| 265 | add_parseh_case("zig keywords in C code", AllowWarningsNo, R"SOURCE( | ||
| 266 | struct comptime { | ||
| 267 | int defer; | ||
| 268 | }; | ||
| 269 | )SOURCE", 2, R"(pub const struct_comptime = extern struct { | ||
| 270 | @"defer": c_int, | ||
| 271 | };)", R"(pub const @"comptime" = struct_comptime;)"); | ||
| 272 | |||
| 273 | add_parseh_case("macro defines string literal with octal", AllowWarningsNo, R"SOURCE( | ||
| 274 | #define FOO "aoeu\023 derp" | ||
| 275 | #define FOO2 "aoeu\0234 derp" | ||
| 276 | #define FOO_CHAR '\077' | ||
| 277 | )SOURCE", 3, | ||
| 278 | R"(pub const FOO: &const u8 = &(c str lit);)", | ||
| 279 | R"(pub const FOO2: &const u8 = &(c str lit);)", | ||
| 280 | R"(pub const FOO_CHAR = 63;)"); | ||
| 281 | } | ||
| 282 | |||
| 283 | static void print_compiler_invocation(TestCase *test_case) { | ||
| 284 | printf("%s", zig_exe); | ||
| 285 | for (size_t i = 0; i < test_case->compiler_args.length; i += 1) { | ||
| 286 | printf(" %s", test_case->compiler_args.at(i)); | ||
| 287 | } | ||
| 288 | printf("\n"); | ||
| 289 | } | ||
| 290 | |||
| 291 | static void print_linker_invocation(TestCase *test_case) { | ||
| 292 | printf("%s", zig_exe); | ||
| 293 | for (size_t i = 0; i < test_case->linker_args.length; i += 1) { | ||
| 294 | printf(" %s", test_case->linker_args.at(i)); | ||
| 295 | } | ||
| 296 | printf("\n"); | ||
| 297 | } | ||
| 298 | |||
| 299 | |||
| 300 | static void print_exe_invocation(TestCase *test_case) { | ||
| 301 | printf("%s", tmp_exe_path); | ||
| 302 | for (size_t i = 0; i < test_case->program_args.length; i += 1) { | ||
| 303 | printf(" %s", test_case->program_args.at(i)); | ||
| 304 | } | ||
| 305 | printf("\n"); | ||
| 306 | } | ||
| 307 | |||
| 308 | static void run_test(TestCase *test_case) { | ||
| 309 | for (size_t i = 0; i < test_case->source_files.length; i += 1) { | ||
| 310 | TestSourceFile *test_source = &test_case->source_files.at(i); | ||
| 311 | os_write_file( | ||
| 312 | buf_create_from_str(test_source->relative_path), | ||
| 313 | buf_create_from_str(test_source->source_code)); | ||
| 314 | } | ||
| 315 | |||
| 316 | Buf zig_stderr = BUF_INIT; | ||
| 317 | Buf zig_stdout = BUF_INIT; | ||
| 318 | int err; | ||
| 319 | Termination term; | ||
| 320 | if ((err = os_exec_process(zig_exe, test_case->compiler_args, &term, &zig_stderr, &zig_stdout))) { | ||
| 321 | fprintf(stderr, "Unable to exec %s: %s\n", zig_exe, err_str(err)); | ||
| 322 | } | ||
| 323 | |||
| 324 | if (!test_case->is_parseh && test_case->compile_errors.length) { | ||
| 325 | if (term.how != TerminationIdClean || term.code != 0) { | ||
| 326 | for (size_t i = 0; i < test_case->compile_errors.length; i += 1) { | ||
| 327 | const char *err_text = test_case->compile_errors.at(i); | ||
| 328 | if (!strstr(buf_ptr(&zig_stderr), err_text)) { | ||
| 329 | printf("\n"); | ||
| 330 | printf("========= Expected this compile error: =========\n"); | ||
| 331 | printf("%s\n", err_text); | ||
| 332 | printf("================================================\n"); | ||
| 333 | print_compiler_invocation(test_case); | ||
| 334 | printf("%s\n", buf_ptr(&zig_stderr)); | ||
| 335 | exit(1); | ||
| 336 | } | ||
| 337 | } | ||
| 338 | return; // success | ||
| 339 | } else { | ||
| 340 | printf("\nCompile failed with return code 0 (Expected failure):\n"); | ||
| 341 | print_compiler_invocation(test_case); | ||
| 342 | printf("%s\n", buf_ptr(&zig_stderr)); | ||
| 343 | exit(1); | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
| 347 | if (term.how != TerminationIdClean || term.code != 0) { | ||
| 348 | printf("\nCompile failed:\n"); | ||
| 349 | print_compiler_invocation(test_case); | ||
| 350 | printf("%s\n", buf_ptr(&zig_stderr)); | ||
| 351 | exit(1); | ||
| 352 | } | ||
| 353 | |||
| 354 | if (test_case->is_parseh) { | ||
| 355 | if (buf_len(&zig_stderr) > 0) { | ||
| 356 | printf("\nparseh emitted warnings:\n"); | ||
| 357 | printf("------------------------------\n"); | ||
| 358 | print_compiler_invocation(test_case); | ||
| 359 | printf("%s\n", buf_ptr(&zig_stderr)); | ||
| 360 | printf("------------------------------\n"); | ||
| 361 | if (test_case->allow_warnings == AllowWarningsNo) { | ||
| 362 | exit(1); | ||
| 363 | } | ||
| 364 | } | ||
| 365 | |||
| 366 | for (size_t i = 0; i < test_case->compile_errors.length; i += 1) { | ||
| 367 | const char *output = test_case->compile_errors.at(i); | ||
| 368 | |||
| 369 | if (!strstr(buf_ptr(&zig_stdout), output)) { | ||
| 370 | printf("\n"); | ||
| 371 | printf("========= Expected this output: =========\n"); | ||
| 372 | printf("%s\n", output); | ||
| 373 | printf("================================================\n"); | ||
| 374 | print_compiler_invocation(test_case); | ||
| 375 | printf("%s\n", buf_ptr(&zig_stdout)); | ||
| 376 | exit(1); | ||
| 377 | } | ||
| 378 | } | ||
| 379 | } else { | ||
| 380 | if (test_case->special == TestSpecialLinkStep) { | ||
| 381 | Buf link_stderr = BUF_INIT; | ||
| 382 | Buf link_stdout = BUF_INIT; | ||
| 383 | int err; | ||
| 384 | Termination term; | ||
| 385 | if ((err = os_exec_process(zig_exe, test_case->linker_args, &term, &link_stderr, &link_stdout))) { | ||
| 386 | fprintf(stderr, "Unable to exec %s: %s\n", zig_exe, err_str(err)); | ||
| 387 | } | ||
| 388 | |||
| 389 | if (term.how != TerminationIdClean || term.code != 0) { | ||
| 390 | printf("\nLink failed:\n"); | ||
| 391 | print_linker_invocation(test_case); | ||
| 392 | printf("%s\n", buf_ptr(&zig_stderr)); | ||
| 393 | exit(1); | ||
| 394 | } | ||
| 395 | } | ||
| 396 | |||
| 397 | Buf program_stderr = BUF_INIT; | ||
| 398 | Buf program_stdout = BUF_INIT; | ||
| 399 | os_exec_process(tmp_exe_path, test_case->program_args, &term, &program_stderr, &program_stdout); | ||
| 400 | |||
| 401 | if (term.how != TerminationIdClean || term.code != 0) { | ||
| 402 | printf("\nProgram exited with error\n"); | ||
| 403 | print_compiler_invocation(test_case); | ||
| 404 | print_exe_invocation(test_case); | ||
| 405 | printf("%s\n", buf_ptr(&program_stderr)); | ||
| 406 | exit(1); | ||
| 407 | } | ||
| 408 | |||
| 409 | if (test_case->output != nullptr && !buf_eql_str(&program_stdout, test_case->output)) { | ||
| 410 | printf("\n"); | ||
| 411 | print_compiler_invocation(test_case); | ||
| 412 | print_exe_invocation(test_case); | ||
| 413 | printf("==== Test failed. Expected output: ====\n"); | ||
| 414 | printf("%s\n", test_case->output); | ||
| 415 | printf("========= Actual output: ==============\n"); | ||
| 416 | printf("%s\n", buf_ptr(&program_stdout)); | ||
| 417 | printf("=======================================\n"); | ||
| 418 | exit(1); | ||
| 419 | } | ||
| 420 | } | ||
| 421 | |||
| 422 | for (size_t i = 0; i < test_case->source_files.length; i += 1) { | ||
| 423 | TestSourceFile *test_source = &test_case->source_files.at(i); | ||
| 424 | remove(test_source->relative_path); | ||
| 425 | } | ||
| 426 | } | ||
| 427 | |||
| 428 | static void run_all_tests(const char *grep_text) { | ||
| 429 | for (size_t i = 0; i < test_cases.length; i += 1) { | ||
| 430 | TestCase *test_case = test_cases.at(i); | ||
| 431 | if (grep_text != nullptr && strstr(test_case->case_name, grep_text) == nullptr) { | ||
| 432 | continue; | ||
| 433 | } | ||
| 434 | |||
| 435 | printf("Test %zu/%zu %s...", i + 1, test_cases.length, test_case->case_name); | ||
| 436 | fflush(stdout); | ||
| 437 | run_test(test_case); | ||
| 438 | printf("OK\n"); | ||
| 439 | } | ||
| 440 | printf("%zu tests passed.\n", test_cases.length); | ||
| 441 | } | ||
| 442 | |||
| 443 | static void cleanup(void) { | ||
| 444 | remove(tmp_source_path); | ||
| 445 | remove(tmp_h_path); | ||
| 446 | remove(tmp_exe_path); | ||
| 447 | } | ||
| 448 | |||
| 449 | static int usage(const char *arg0) { | ||
| 450 | fprintf(stderr, "Usage: %s [--grep text]\n", arg0); | ||
| 451 | return 1; | ||
| 452 | } | ||
| 453 | |||
| 454 | int main(int argc, char **argv) { | ||
| 455 | const char *grep_text = nullptr; | ||
| 456 | for (int i = 1; i < argc; i += 1) { | ||
| 457 | const char *arg = argv[i]; | ||
| 458 | if (i + 1 >= argc) { | ||
| 459 | return usage(argv[0]); | ||
| 460 | } else { | ||
| 461 | i += 1; | ||
| 462 | if (strcmp(arg, "--grep") == 0) { | ||
| 463 | grep_text = argv[i]; | ||
| 464 | } else { | ||
| 465 | return usage(argv[0]); | ||
| 466 | } | ||
| 467 | } | ||
| 468 | } | ||
| 469 | add_parseh_test_cases(); | ||
| 470 | run_all_tests(grep_text); | ||
| 471 | cleanup(); | ||
| 472 | } | ||
test/tests.zig+206-6| ... | @@ -10,13 +10,14 @@ const mem = std.mem; | ... | @@ -10,13 +10,14 @@ const mem = std.mem; |
| 10 | const fmt = std.fmt; | 10 | const fmt = std.fmt; |
| 11 | const List = std.list.List; | 11 | const List = std.list.List; |
| 12 | 12 | ||
| 13 | error TestFailed; | 13 | const compare_output = @import("compare_output.zig"); |
| 14 | const build_examples = @import("build_examples.zig"); | ||
| 15 | const compile_errors = @import("compile_errors.zig"); | ||
| 16 | const assemble_and_link = @import("assemble_and_link.zig"); | ||
| 17 | const debug_safety = @import("debug_safety.zig"); | ||
| 18 | const parseh = @import("parseh.zig"); | ||
| 14 | 19 | ||
| 15 | pub const compare_output = @import("compare_output.zig"); | 20 | error TestFailed; |
| 16 | pub const build_examples = @import("build_examples.zig"); | ||
| 17 | pub const compile_errors = @import("compile_errors.zig"); | ||
| 18 | pub const assemble_and_link = @import("assemble_and_link.zig"); | ||
| 19 | pub const debug_safety = @import("debug_safety.zig"); | ||
| 20 | 21 | ||
| 21 | pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step { | 22 | pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step { |
| 22 | const cases = %%b.allocator.create(CompareOutputContext); | 23 | const cases = %%b.allocator.create(CompareOutputContext); |
| ... | @@ -88,6 +89,20 @@ pub fn addAssembleAndLinkTests(b: &build.Builder, test_filter: ?[]const u8) -> & | ... | @@ -88,6 +89,20 @@ pub fn addAssembleAndLinkTests(b: &build.Builder, test_filter: ?[]const u8) -> & |
| 88 | return cases.step; | 89 | return cases.step; |
| 89 | } | 90 | } |
| 90 | 91 | ||
| 92 | pub fn addParseHTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step { | ||
| 93 | const cases = %%b.allocator.create(ParseHContext); | ||
| 94 | *cases = ParseHContext { | ||
| 95 | .b = b, | ||
| 96 | .step = b.step("test-parseh", "Run the C header file parsing tests"), | ||
| 97 | .test_index = 0, | ||
| 98 | .test_filter = test_filter, | ||
| 99 | }; | ||
| 100 | |||
| 101 | parseh.addCases(cases); | ||
| 102 | |||
| 103 | return cases.step; | ||
| 104 | } | ||
| 105 | |||
| 91 | pub const CompareOutputContext = struct { | 106 | pub const CompareOutputContext = struct { |
| 92 | b: &build.Builder, | 107 | b: &build.Builder, |
| 93 | step: &build.Step, | 108 | step: &build.Step, |
| ... | @@ -645,3 +660,188 @@ pub const BuildExamplesContext = struct { | ... | @@ -645,3 +660,188 @@ pub const BuildExamplesContext = struct { |
| 645 | } | 660 | } |
| 646 | } | 661 | } |
| 647 | }; | 662 | }; |
| 663 | |||
| 664 | pub const ParseHContext = struct { | ||
| 665 | b: &build.Builder, | ||
| 666 | step: &build.Step, | ||
| 667 | test_index: usize, | ||
| 668 | test_filter: ?[]const u8, | ||
| 669 | |||
| 670 | const TestCase = struct { | ||
| 671 | name: []const u8, | ||
| 672 | sources: List(SourceFile), | ||
| 673 | expected_lines: List([]const u8), | ||
| 674 | allow_warnings: bool, | ||
| 675 | |||
| 676 | const SourceFile = struct { | ||
| 677 | filename: []const u8, | ||
| 678 | source: []const u8, | ||
| 679 | }; | ||
| 680 | |||
| 681 | pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) { | ||
| 682 | %%self.sources.append(SourceFile { | ||
| 683 | .filename = filename, | ||
| 684 | .source = source, | ||
| 685 | }); | ||
| 686 | } | ||
| 687 | |||
| 688 | pub fn addExpectedError(self: &TestCase, text: []const u8) { | ||
| 689 | %%self.expected_lines.append(text); | ||
| 690 | } | ||
| 691 | }; | ||
| 692 | |||
| 693 | const ParseHCmpOutputStep = struct { | ||
| 694 | step: build.Step, | ||
| 695 | context: &ParseHContext, | ||
| 696 | name: []const u8, | ||
| 697 | test_index: usize, | ||
| 698 | case: &const TestCase, | ||
| 699 | |||
| 700 | pub fn create(context: &ParseHContext, name: []const u8, case: &const TestCase) -> &ParseHCmpOutputStep { | ||
| 701 | const allocator = context.b.allocator; | ||
| 702 | const ptr = %%allocator.create(ParseHCmpOutputStep); | ||
| 703 | *ptr = ParseHCmpOutputStep { | ||
| 704 | .step = build.Step.init("ParseHCmpOutput", allocator, make), | ||
| 705 | .context = context, | ||
| 706 | .name = name, | ||
| 707 | .test_index = context.test_index, | ||
| 708 | .case = case, | ||
| 709 | }; | ||
| 710 | context.test_index += 1; | ||
| 711 | return ptr; | ||
| 712 | } | ||
| 713 | |||
| 714 | fn make(step: &build.Step) -> %void { | ||
| 715 | const self = @fieldParentPtr(ParseHCmpOutputStep, "step", step); | ||
| 716 | const b = self.context.b; | ||
| 717 | |||
| 718 | const root_src = %%os.path.join(b.allocator, "test_artifacts", self.case.sources.items[0].filename); | ||
| 719 | |||
| 720 | var zig_args = List([]const u8).init(b.allocator); | ||
| 721 | %%zig_args.append("parseh"); | ||
| 722 | %%zig_args.append(b.pathFromRoot(root_src)); | ||
| 723 | |||
| 724 | %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name); | ||
| 725 | |||
| 726 | if (b.verbose) { | ||
| 727 | printInvocation(b.zig_exe, zig_args.toSliceConst()); | ||
| 728 | } | ||
| 729 | |||
| 730 | var child = os.ChildProcess.spawn(b.zig_exe, zig_args.toSliceConst(), &b.env_map, | ||
| 731 | StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err| | ||
| 732 | { | ||
| 733 | debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err)); | ||
| 734 | }; | ||
| 735 | |||
| 736 | const term = child.wait() %% |err| { | ||
| 737 | debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err)); | ||
| 738 | }; | ||
| 739 | switch (term) { | ||
| 740 | Term.Clean => |code| { | ||
| 741 | if (code != 0) { | ||
| 742 | %%io.stderr.printf("Compilation failed with exit code {}\n", code); | ||
| 743 | return error.TestFailed; | ||
| 744 | } | ||
| 745 | }, | ||
| 746 | Term.Signal => |code| { | ||
| 747 | %%io.stderr.printf("Compilation failed with signal {}\n", code); | ||
| 748 | return error.TestFailed; | ||
| 749 | }, | ||
| 750 | else => { | ||
| 751 | %%io.stderr.printf("Compilation terminated unexpectedly\n"); | ||
| 752 | return error.TestFailed; | ||
| 753 | }, | ||
| 754 | }; | ||
| 755 | |||
| 756 | var stdout_buf = %%Buffer0.initEmpty(b.allocator); | ||
| 757 | var stderr_buf = %%Buffer0.initEmpty(b.allocator); | ||
| 758 | |||
| 759 | %%(??child.stdout).readAll(&stdout_buf); | ||
| 760 | %%(??child.stderr).readAll(&stderr_buf); | ||
| 761 | |||
| 762 | const stdout = stdout_buf.toSliceConst(); | ||
| 763 | const stderr = stderr_buf.toSliceConst(); | ||
| 764 | |||
| 765 | if (stderr.len != 0 and !self.case.allow_warnings) { | ||
| 766 | %%io.stderr.printf( | ||
| 767 | \\====== parseh emitted warnings: ============ | ||
| 768 | \\{} | ||
| 769 | \\============================================ | ||
| 770 | \\ | ||
| 771 | , stderr); | ||
| 772 | return error.TestFailed; | ||
| 773 | } | ||
| 774 | |||
| 775 | for (self.case.expected_lines.toSliceConst()) |expected_line| { | ||
| 776 | if (mem.indexOf(u8, stdout, expected_line) == null) { | ||
| 777 | %%io.stderr.printf( | ||
| 778 | \\ | ||
| 779 | \\========= Expected this output: ================ | ||
| 780 | \\{} | ||
| 781 | \\================================================ | ||
| 782 | \\{} | ||
| 783 | \\ | ||
| 784 | , expected_line, stdout); | ||
| 785 | return error.TestFailed; | ||
| 786 | } | ||
| 787 | } | ||
| 788 | %%io.stderr.printf("OK\n"); | ||
| 789 | } | ||
| 790 | }; | ||
| 791 | |||
| 792 | fn printInvocation(exe_path: []const u8, args: []const []const u8) { | ||
| 793 | %%io.stderr.printf("{}", exe_path); | ||
| 794 | for (args) |arg| { | ||
| 795 | %%io.stderr.printf(" {}", arg); | ||
| 796 | } | ||
| 797 | %%io.stderr.printf("\n"); | ||
| 798 | } | ||
| 799 | |||
| 800 | pub fn create(self: &ParseHContext, allow_warnings: bool, name: []const u8, | ||
| 801 | source: []const u8, expected_lines: ...) -> &TestCase | ||
| 802 | { | ||
| 803 | const tc = %%self.b.allocator.create(TestCase); | ||
| 804 | *tc = TestCase { | ||
| 805 | .name = name, | ||
| 806 | .sources = List(TestCase.SourceFile).init(self.b.allocator), | ||
| 807 | .expected_lines = List([]const u8).init(self.b.allocator), | ||
| 808 | .allow_warnings = allow_warnings, | ||
| 809 | }; | ||
| 810 | tc.addSourceFile("source.h", source); | ||
| 811 | comptime var arg_i = 0; | ||
| 812 | inline while (arg_i < expected_lines.len; arg_i += 1) { | ||
| 813 | // TODO mem.dupe is because of issue #336 | ||
| 814 | tc.addExpectedError(%%mem.dupe(self.b.allocator, u8, expected_lines[arg_i])); | ||
| 815 | } | ||
| 816 | return tc; | ||
| 817 | } | ||
| 818 | |||
| 819 | pub fn add(self: &ParseHContext, name: []const u8, source: []const u8, expected_lines: ...) { | ||
| 820 | const tc = self.create(false, name, source, expected_lines); | ||
| 821 | self.addCase(tc); | ||
| 822 | } | ||
| 823 | |||
| 824 | pub fn addAllowWarnings(self: &ParseHContext, name: []const u8, source: []const u8, expected_lines: ...) { | ||
| 825 | const tc = self.create(true, name, source, expected_lines); | ||
| 826 | self.addCase(tc); | ||
| 827 | } | ||
| 828 | |||
| 829 | pub fn addCase(self: &ParseHContext, case: &const TestCase) { | ||
| 830 | const b = self.b; | ||
| 831 | |||
| 832 | const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "parseh {}", case.name); | ||
| 833 | if (const filter ?= self.test_filter) { | ||
| 834 | if (mem.indexOf(u8, annotated_case_name, filter) == null) | ||
| 835 | return; | ||
| 836 | } | ||
| 837 | |||
| 838 | const parseh_and_cmp = ParseHCmpOutputStep.create(self, annotated_case_name, case); | ||
| 839 | self.step.dependOn(&parseh_and_cmp.step); | ||
| 840 | |||
| 841 | for (case.sources.toSliceConst()) |src_file| { | ||
| 842 | const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename); | ||
| 843 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); | ||
| 844 | parseh_and_cmp.step.dependOn(&write_src.step); | ||
| 845 | } | ||
| 846 | } | ||
| 847 | }; |