convert parseh tests to zig build system
parent
d12f1f5b49
commit
1ff73a8e69
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
);
|
||||
}
|
|
@ -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();
|
||||
}
|
212
test/tests.zig
212
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue