convert debug safety tests to zig build system

This commit is contained in:
Andrew Kelley 2017-04-19 14:41:59 -04:00
parent d1e01e43d3
commit 9b7f438882
4 changed files with 415 additions and 336 deletions

View File

@ -39,4 +39,5 @@ pub fn build(b: &Builder) {
test_step.dependOn(tests.addBuildExampleTests(b, test_filter));
test_step.dependOn(tests.addCompileErrorTests(b, test_filter));
test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter));
test_step.dependOn(tests.addDebugSafetyTests(b, test_filter));
}

234
test/debug_safety.zig Normal file
View File

@ -0,0 +1,234 @@
const tests = @import("tests.zig");
pub fn addCases(cases: &tests.CompareOutputContext) {
cases.addDebugSafety("calling panic",
\\pub fn panic(message: []const u8) -> noreturn {
\\ @breakpoint();
\\ while (true) {}
\\}
\\pub fn main() -> %void {
\\ @panic("oh no");
\\}
);
cases.addDebugSafety("out of bounds slice access",
\\pub fn panic(message: []const u8) -> noreturn {
\\ @breakpoint();
\\ while (true) {}
\\}
\\pub fn main() -> %void {
\\ const a = []i32{1, 2, 3, 4};
\\ baz(bar(a));
\\}
\\fn bar(a: []const i32) -> i32 {
\\ a[4]
\\}
\\fn baz(a: i32) { }
);
cases.addDebugSafety("integer addition overflow",
\\pub fn panic(message: []const u8) -> noreturn {
\\ @breakpoint();
\\ while (true) {}
\\}
\\error Whatever;
\\pub fn main() -> %void {
\\ const x = add(65530, 10);
\\ if (x == 0) return error.Whatever;
\\}
\\fn add(a: u16, b: u16) -> u16 {
\\ a + b
\\}
);
cases.addDebugSafety("integer subtraction overflow",
\\pub fn panic(message: []const u8) -> noreturn {
\\ @breakpoint();
\\ while (true) {}
\\}
\\error Whatever;
\\pub fn main() -> %void {
\\ const x = sub(10, 20);
\\ if (x == 0) return error.Whatever;
\\}
\\fn sub(a: u16, b: u16) -> u16 {
\\ a - b
\\}
);
cases.addDebugSafety("integer multiplication overflow",
\\pub fn panic(message: []const u8) -> noreturn {
\\ @breakpoint();
\\ while (true) {}
\\}
\\error Whatever;
\\pub fn main() -> %void {
\\ const x = mul(300, 6000);
\\ if (x == 0) return error.Whatever;
\\}
\\fn mul(a: u16, b: u16) -> u16 {
\\ a * b
\\}
);
cases.addDebugSafety("integer negation overflow",
\\pub fn panic(message: []const u8) -> noreturn {
\\ @breakpoint();
\\ while (true) {}
\\}
\\error Whatever;
\\pub fn main() -> %void {
\\ const x = neg(-32768);
\\ if (x == 32767) return error.Whatever;
\\}
\\fn neg(a: i16) -> i16 {
\\ -a
\\}
);
cases.addDebugSafety("signed integer division overflow",
\\pub fn panic(message: []const u8) -> noreturn {
\\ @breakpoint();
\\ while (true) {}
\\}
\\error Whatever;
\\pub fn main() -> %void {
\\ const x = div(-32768, -1);
\\ if (x == 32767) return error.Whatever;
\\}
\\fn div(a: i16, b: i16) -> i16 {
\\ a / b
\\}
);
cases.addDebugSafety("signed shift left overflow",
\\pub fn panic(message: []const u8) -> noreturn {
\\ @breakpoint();
\\ while (true) {}
\\}
\\error Whatever;
\\pub fn main() -> %void {
\\ const x = shl(-16385, 1);
\\ if (x == 0) return error.Whatever;
\\}
\\fn shl(a: i16, b: i16) -> i16 {
\\ a << b
\\}
);
cases.addDebugSafety("unsigned shift left overflow",
\\pub fn panic(message: []const u8) -> noreturn {
\\ @breakpoint();
\\ while (true) {}
\\}
\\error Whatever;
\\pub fn main() -> %void {
\\ const x = shl(0b0010111111111111, 3);
\\ if (x == 0) return error.Whatever;
\\}
\\fn shl(a: u16, b: u16) -> u16 {
\\ a << b
\\}
);
cases.addDebugSafety("integer division by zero",
\\pub fn panic(message: []const u8) -> noreturn {
\\ @breakpoint();
\\ while (true) {}
\\}
\\error Whatever;
\\pub fn main() -> %void {
\\ const x = div0(999, 0);
\\}
\\fn div0(a: i32, b: i32) -> i32 {
\\ a / b
\\}
);
cases.addDebugSafety("exact division failure",
\\pub fn panic(message: []const u8) -> noreturn {
\\ @breakpoint();
\\ while (true) {}
\\}
\\error Whatever;
\\pub fn main() -> %void {
\\ const x = divExact(10, 3);
\\ if (x == 0) return error.Whatever;
\\}
\\fn divExact(a: i32, b: i32) -> i32 {
\\ @divExact(a, b)
\\}
);
cases.addDebugSafety("cast []u8 to bigger slice of wrong size",
\\pub fn panic(message: []const u8) -> noreturn {
\\ @breakpoint();
\\ while (true) {}
\\}
\\error Whatever;
\\pub fn main() -> %void {
\\ const x = widenSlice([]u8{1, 2, 3, 4, 5});
\\ if (x.len == 0) return error.Whatever;
\\}
\\fn widenSlice(slice: []const u8) -> []const i32 {
\\ ([]const i32)(slice)
\\}
);
cases.addDebugSafety("value does not fit in shortening cast",
\\pub fn panic(message: []const u8) -> noreturn {
\\ @breakpoint();
\\ while (true) {}
\\}
\\error Whatever;
\\pub fn main() -> %void {
\\ const x = shorten_cast(200);
\\ if (x == 0) return error.Whatever;
\\}
\\fn shorten_cast(x: i32) -> i8 {
\\ i8(x)
\\}
);
cases.addDebugSafety("signed integer not fitting in cast to unsigned integer",
\\pub fn panic(message: []const u8) -> noreturn {
\\ @breakpoint();
\\ while (true) {}
\\}
\\error Whatever;
\\pub fn main() -> %void {
\\ const x = unsigned_cast(-10);
\\ if (x == 0) return error.Whatever;
\\}
\\fn unsigned_cast(x: i32) -> u32 {
\\ u32(x)
\\}
);
cases.addDebugSafety("unwrap error",
\\pub fn panic(message: []const u8) -> noreturn {
\\ @breakpoint();
\\ while (true) {}
\\}
\\error Whatever;
\\pub fn main() -> %void {
\\ %%bar();
\\}
\\fn bar() -> %void {
\\ return error.Whatever;
\\}
);
cases.addDebugSafety("cast integer to error and no code matches",
\\pub fn panic(message: []const u8) -> noreturn {
\\ @breakpoint();
\\ while (true) {}
\\}
\\pub fn main() -> %void {
\\ _ = bar(9999);
\\}
\\fn bar(x: u32) -> error {
\\ return error(x);
\\}
);
}

View File

@ -40,7 +40,6 @@ struct TestCase {
bool is_parseh;
TestSpecial special;
bool is_release_mode;
bool is_debug_safety;
AllowWarnings allow_warnings;
};
@ -58,26 +57,6 @@ static const char *zig_exe = "./zig";
#define NL "\n"
#endif
static void add_debug_safety_case(const char *case_name, const char *source) {
TestCase *test_case = allocate<TestCase>(1);
test_case->is_debug_safety = true;
test_case->case_name = buf_ptr(buf_sprintf("%s", case_name));
test_case->source_files.resize(1);
test_case->source_files.at(0).relative_path = tmp_source_path;
test_case->source_files.at(0).source_code = source;
test_case->compiler_args.append("build_exe");
test_case->compiler_args.append(tmp_source_path);
test_case->compiler_args.append("--name");
test_case->compiler_args.append("test");
test_case->compiler_args.append("--output");
test_case->compiler_args.append(tmp_exe_path);
test_cases.append(test_case);
}
static TestCase *add_parseh_case(const char *case_name, AllowWarnings allow_warnings,
const char *source, size_t count, ...)
{
@ -107,240 +86,6 @@ static TestCase *add_parseh_case(const char *case_name, AllowWarnings allow_warn
va_end(ap);
return test_case;
}
//////////////////////////////////////////////////////////////////////////////
static void add_debug_safety_test_cases(void) {
add_debug_safety_case("calling panic", R"SOURCE(
pub fn panic(message: []const u8) -> noreturn {
@breakpoint();
while (true) {}
}
pub fn main() -> %void {
@panic("oh no");
}
)SOURCE");
add_debug_safety_case("out of bounds slice access", R"SOURCE(
pub fn panic(message: []const u8) -> noreturn {
@breakpoint();
while (true) {}
}
pub fn main() -> %void {
const a = []i32{1, 2, 3, 4};
baz(bar(a));
}
fn bar(a: []const i32) -> i32 {
a[4]
}
fn baz(a: i32) { }
)SOURCE");
add_debug_safety_case("integer addition overflow", R"SOURCE(
pub fn panic(message: []const u8) -> noreturn {
@breakpoint();
while (true) {}
}
error Whatever;
pub fn main() -> %void {
const x = add(65530, 10);
if (x == 0) return error.Whatever;
}
fn add(a: u16, b: u16) -> u16 {
a + b
}
)SOURCE");
add_debug_safety_case("integer subtraction overflow", R"SOURCE(
pub fn panic(message: []const u8) -> noreturn {
@breakpoint();
while (true) {}
}
error Whatever;
pub fn main() -> %void {
const x = sub(10, 20);
if (x == 0) return error.Whatever;
}
fn sub(a: u16, b: u16) -> u16 {
a - b
}
)SOURCE");
add_debug_safety_case("integer multiplication overflow", R"SOURCE(
pub fn panic(message: []const u8) -> noreturn {
@breakpoint();
while (true) {}
}
error Whatever;
pub fn main() -> %void {
const x = mul(300, 6000);
if (x == 0) return error.Whatever;
}
fn mul(a: u16, b: u16) -> u16 {
a * b
}
)SOURCE");
add_debug_safety_case("integer negation overflow", R"SOURCE(
pub fn panic(message: []const u8) -> noreturn {
@breakpoint();
while (true) {}
}
error Whatever;
pub fn main() -> %void {
const x = neg(-32768);
if (x == 32767) return error.Whatever;
}
fn neg(a: i16) -> i16 {
-a
}
)SOURCE");
add_debug_safety_case("signed integer division overflow", R"SOURCE(
pub fn panic(message: []const u8) -> noreturn {
@breakpoint();
while (true) {}
}
error Whatever;
pub fn main() -> %void {
const x = div(-32768, -1);
if (x == 32767) return error.Whatever;
}
fn div(a: i16, b: i16) -> i16 {
a / b
}
)SOURCE");
add_debug_safety_case("signed shift left overflow", R"SOURCE(
pub fn panic(message: []const u8) -> noreturn {
@breakpoint();
while (true) {}
}
error Whatever;
pub fn main() -> %void {
const x = shl(-16385, 1);
if (x == 0) return error.Whatever;
}
fn shl(a: i16, b: i16) -> i16 {
a << b
}
)SOURCE");
add_debug_safety_case("unsigned shift left overflow", R"SOURCE(
pub fn panic(message: []const u8) -> noreturn {
@breakpoint();
while (true) {}
}
error Whatever;
pub fn main() -> %void {
const x = shl(0b0010111111111111, 3);
if (x == 0) return error.Whatever;
}
fn shl(a: u16, b: u16) -> u16 {
a << b
}
)SOURCE");
add_debug_safety_case("integer division by zero", R"SOURCE(
pub fn panic(message: []const u8) -> noreturn {
@breakpoint();
while (true) {}
}
error Whatever;
pub fn main() -> %void {
const x = div0(999, 0);
}
fn div0(a: i32, b: i32) -> i32 {
a / b
}
)SOURCE");
add_debug_safety_case("exact division failure", R"SOURCE(
pub fn panic(message: []const u8) -> noreturn {
@breakpoint();
while (true) {}
}
error Whatever;
pub fn main() -> %void {
const x = divExact(10, 3);
if (x == 0) return error.Whatever;
}
fn divExact(a: i32, b: i32) -> i32 {
@divExact(a, b)
}
)SOURCE");
add_debug_safety_case("cast []u8 to bigger slice of wrong size", R"SOURCE(
pub fn panic(message: []const u8) -> noreturn {
@breakpoint();
while (true) {}
}
error Whatever;
pub fn main() -> %void {
const x = widenSlice([]u8{1, 2, 3, 4, 5});
if (x.len == 0) return error.Whatever;
}
fn widenSlice(slice: []const u8) -> []const i32 {
([]const i32)(slice)
}
)SOURCE");
add_debug_safety_case("value does not fit in shortening cast", R"SOURCE(
pub fn panic(message: []const u8) -> noreturn {
@breakpoint();
while (true) {}
}
error Whatever;
pub fn main() -> %void {
const x = shorten_cast(200);
if (x == 0) return error.Whatever;
}
fn shorten_cast(x: i32) -> i8 {
i8(x)
}
)SOURCE");
add_debug_safety_case("signed integer not fitting in cast to unsigned integer", R"SOURCE(
pub fn panic(message: []const u8) -> noreturn {
@breakpoint();
while (true) {}
}
error Whatever;
pub fn main() -> %void {
const x = unsigned_cast(-10);
if (x == 0) return error.Whatever;
}
fn unsigned_cast(x: i32) -> u32 {
u32(x)
}
)SOURCE");
add_debug_safety_case("unwrap error", R"SOURCE(
pub fn panic(message: []const u8) -> noreturn {
@breakpoint();
while (true) {}
}
error Whatever;
pub fn main() -> %void {
%%bar();
}
fn bar() -> %void {
return error.Whatever;
}
)SOURCE");
add_debug_safety_case("cast integer to error and no code matches", R"SOURCE(
pub fn panic(message: []const u8) -> noreturn {
@breakpoint();
while (true) {}
}
pub fn main() -> %void {
_ = bar(9999);
}
fn bar(x: u32) -> error {
return error(x);
}
)SOURCE");
}
//////////////////////////////////////////////////////////////////////////////
@ -653,43 +398,24 @@ static void run_test(TestCase *test_case) {
Buf program_stdout = BUF_INIT;
os_exec_process(tmp_exe_path, test_case->program_args, &term, &program_stderr, &program_stdout);
if (test_case->is_debug_safety) {
int debug_trap_signal = 5;
if (term.how != TerminationIdSignaled || term.code != debug_trap_signal) {
if (term.how == TerminationIdClean) {
printf("\nProgram expected to hit debug trap (signal %d) but exited with return code %d\n",
debug_trap_signal, term.code);
} else if (term.how == TerminationIdSignaled) {
printf("\nProgram expected to hit debug trap (signal %d) but signaled with code %d\n",
debug_trap_signal, term.code);
} else {
printf("\nProgram expected to hit debug trap (signal %d) exited in an unexpected way\n",
debug_trap_signal);
}
print_compiler_invocation(test_case);
print_exe_invocation(test_case);
exit(1);
}
} else {
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 (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);
}
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);
}
}
@ -740,7 +466,6 @@ int main(int argc, char **argv) {
}
}
}
add_debug_safety_test_cases();
add_parseh_test_cases();
run_all_tests(grep_text);
cleanup();

View File

@ -16,6 +16,7 @@ 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");
pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
const cases = %%b.allocator.create(CompareOutputContext);
@ -31,6 +32,20 @@ pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &bu
return cases.step;
}
pub fn addDebugSafetyTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
const cases = %%b.allocator.create(CompareOutputContext);
*cases = CompareOutputContext {
.b = b,
.step = b.step("test-debug-safety", "Run the debug safety tests"),
.test_index = 0,
.test_filter = test_filter,
};
debug_safety.addCases(cases);
return cases.step;
}
pub fn addCompileErrorTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
const cases = %%b.allocator.create(CompileErrorContext);
*cases = CompileErrorContext {
@ -79,12 +94,18 @@ pub const CompareOutputContext = struct {
test_index: usize,
test_filter: ?[]const u8,
const Special = enum {
None,
Asm,
DebugSafety,
};
const TestCase = struct {
name: []const u8,
sources: List(SourceFile),
expected_output: []const u8,
link_libc: bool,
is_asm: bool,
special: Special,
const SourceFile = struct {
filename: []const u8,
@ -175,17 +196,83 @@ pub const CompareOutputContext = struct {
}
};
const DebugSafetyRunStep = struct {
step: build.Step,
context: &CompareOutputContext,
exe_path: []const u8,
name: []const u8,
test_index: usize,
pub fn create(context: &CompareOutputContext, exe_path: []const u8,
name: []const u8) -> &DebugSafetyRunStep
{
const allocator = context.b.allocator;
const ptr = %%allocator.create(DebugSafetyRunStep);
*ptr = DebugSafetyRunStep {
.context = context,
.exe_path = exe_path,
.name = name,
.test_index = context.test_index,
.step = build.Step.init("DebugSafetyRun", allocator, make),
};
context.test_index += 1;
return ptr;
}
fn make(step: &build.Step) -> %void {
const self = @fieldParentPtr(DebugSafetyRunStep, "step", step);
const b = self.context.b;
const full_exe_path = b.pathFromRoot(self.exe_path);
%%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, &b.env_map,
StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err|
{
debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
};
const term = child.wait() %% |err| {
debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
};
const debug_trap_signal: i32 = 5;
switch (term) {
Term.Clean => |code| {
%%io.stderr.printf("\nProgram expected to hit debug trap (signal {}) " ++
"but exited with return code {}\n", debug_trap_signal, code);
return error.TestFailed;
},
Term.Signal => |sig| {
if (sig != debug_trap_signal) {
%%io.stderr.printf("\nProgram expected to hit debug trap (signal {}) " ++
"but instead signaled {}\n", debug_trap_signal, sig);
return error.TestFailed;
}
},
else => {
%%io.stderr.printf("\nProgram expected to hit debug trap (signal {}) " ++
" but exited in an unexpected way\n", debug_trap_signal);
return error.TestFailed;
},
}
%%io.stderr.printf("OK\n");
}
};
pub fn createExtra(self: &CompareOutputContext, name: []const u8, source: []const u8,
expected_output: []const u8, is_asm: bool) -> TestCase
expected_output: []const u8, special: Special) -> TestCase
{
var tc = TestCase {
.name = name,
.sources = List(TestCase.SourceFile).init(self.b.allocator),
.expected_output = expected_output,
.link_libc = false,
.is_asm = is_asm,
.special = special,
};
const root_src_name = if (is_asm) "source.s" else "source.zig";
const root_src_name = if (special == Special.Asm) "source.s" else "source.zig";
tc.addSourceFile(root_src_name, source);
return tc;
}
@ -193,7 +280,7 @@ pub const CompareOutputContext = struct {
pub fn create(self: &CompareOutputContext, name: []const u8, source: []const u8,
expected_output: []const u8) -> TestCase
{
return createExtra(self, name, source, expected_output, false);
return createExtra(self, name, source, expected_output, Special.None);
}
pub fn addC(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) {
@ -208,7 +295,12 @@ pub const CompareOutputContext = struct {
}
pub fn addAsm(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) {
const tc = self.createExtra(name, source, expected_output, true);
const tc = self.createExtra(name, source, expected_output, Special.Asm);
self.addCase(tc);
}
pub fn addDebugSafety(self: &CompareOutputContext, name: []const u8, source: []const u8) {
const tc = self.createExtra(name, source, undefined, Special.DebugSafety);
self.addCase(tc);
}
@ -218,45 +310,74 @@ pub const CompareOutputContext = struct {
const root_src = %%os.path.join(b.allocator, "test_artifacts", case.sources.items[0].filename);
const exe_path = %%os.path.join(b.allocator, "test_artifacts", "test");
if (case.is_asm) {
const obj_path = %%os.path.join(b.allocator, "test_artifacts", "test.o");
const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "assemble-and-link {}", case.name);
if (const filter ?= self.test_filter) {
if (mem.indexOf(u8, annotated_case_name, filter) == null)
return;
}
const obj = b.addAssemble("test", root_src);
obj.setOutputPath(obj_path);
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);
obj.step.dependOn(&write_src.step);
}
const exe = b.addLinkExecutable("test");
exe.step.dependOn(&obj.step);
exe.addObjectFile(obj_path);
exe.setOutputPath(exe_path);
const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name,
case.expected_output);
run_and_cmp_output.step.dependOn(&exe.step);
self.step.dependOn(&run_and_cmp_output.step);
} else {
for ([]bool{false, true}) |release| {
const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "{} ({})",
case.name, if (release) "release" else "debug");
switch (case.special) {
Special.Asm => {
const obj_path = %%os.path.join(b.allocator, "test_artifacts", "test.o");
const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "assemble-and-link {}", case.name);
if (const filter ?= self.test_filter) {
if (mem.indexOf(u8, annotated_case_name, filter) == null)
continue;
return;
}
const obj = b.addAssemble("test", root_src);
obj.setOutputPath(obj_path);
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);
obj.step.dependOn(&write_src.step);
}
const exe = b.addLinkExecutable("test");
exe.step.dependOn(&obj.step);
exe.addObjectFile(obj_path);
exe.setOutputPath(exe_path);
const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name,
case.expected_output);
run_and_cmp_output.step.dependOn(&exe.step);
self.step.dependOn(&run_and_cmp_output.step);
},
Special.None => {
for ([]bool{false, true}) |release| {
const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "{} ({})",
case.name, if (release) "release" else "debug");
if (const filter ?= self.test_filter) {
if (mem.indexOf(u8, annotated_case_name, filter) == null)
continue;
}
const exe = b.addExecutable("test", root_src);
exe.setOutputPath(exe_path);
exe.setRelease(release);
if (case.link_libc) {
exe.linkLibrary("c");
}
for (case.sources.toSliceConst()) |src_file| {
const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename);
const write_src = b.addWriteFile(expanded_src_path, src_file.source);
exe.step.dependOn(&write_src.step);
}
const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name,
case.expected_output);
run_and_cmp_output.step.dependOn(&exe.step);
self.step.dependOn(&run_and_cmp_output.step);
}
},
Special.DebugSafety => {
const obj_path = %%os.path.join(b.allocator, "test_artifacts", "test.o");
const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "debug-safety {}", case.name);
if (const filter ?= self.test_filter) {
if (mem.indexOf(u8, annotated_case_name, filter) == null)
return;
}
const exe = b.addExecutable("test", root_src);
exe.setOutputPath(exe_path);
exe.setRelease(release);
if (case.link_libc) {
exe.linkLibrary("c");
}
@ -267,14 +388,12 @@ pub const CompareOutputContext = struct {
exe.step.dependOn(&write_src.step);
}
const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name,
case.expected_output);
const run_and_cmp_output = DebugSafetyRunStep.create(self, exe_path, annotated_case_name);
run_and_cmp_output.step.dependOn(&exe.step);
self.step.dependOn(&run_and_cmp_output.step);
}
};
},
}
}
};