diff options
Diffstat (limited to 'test/src')
| -rw-r--r-- | test/src/compare_output.zig | 165 | ||||
| -rw-r--r-- | test/src/run_translated_c.zig | 104 | ||||
| -rw-r--r-- | test/src/translate_c.zig | 109 |
3 files changed, 378 insertions, 0 deletions
diff --git a/test/src/compare_output.zig b/test/src/compare_output.zig new file mode 100644 index 0000000000..ae994a0697 --- /dev/null +++ b/test/src/compare_output.zig @@ -0,0 +1,165 @@ +// This is the implementation of the test harness. +// For the actual test cases, see test/compare_output.zig. +const std = @import("std"); +const builtin = std.builtin; +const build = std.build; +const ArrayList = std.ArrayList; +const fmt = std.fmt; +const mem = std.mem; +const fs = std.fs; +const warn = std.debug.warn; +const Mode = builtin.Mode; + +pub const CompareOutputContext = struct { + b: *build.Builder, + step: *build.Step, + test_index: usize, + test_filter: ?[]const u8, + modes: []const Mode, + + const Special = enum { + None, + Asm, + RuntimeSafety, + }; + + const TestCase = struct { + name: []const u8, + sources: ArrayList(SourceFile), + expected_output: []const u8, + link_libc: bool, + special: Special, + cli_args: []const []const u8, + + const SourceFile = struct { + filename: []const u8, + source: []const u8, + }; + + pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { + self.sources.append(SourceFile{ + .filename = filename, + .source = source, + }) catch unreachable; + } + + pub fn setCommandLineArgs(self: *TestCase, args: []const []const u8) void { + self.cli_args = args; + } + }; + + pub fn createExtra(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8, special: Special) TestCase { + var tc = TestCase{ + .name = name, + .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), + .expected_output = expected_output, + .link_libc = false, + .special = special, + .cli_args = &[_][]const u8{}, + }; + const root_src_name = if (special == Special.Asm) "source.s" else "source.zig"; + tc.addSourceFile(root_src_name, source); + return tc; + } + + pub fn create(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) TestCase { + return createExtra(self, name, source, expected_output, Special.None); + } + + pub fn addC(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { + var tc = self.create(name, source, expected_output); + tc.link_libc = true; + self.addCase(tc); + } + + pub fn add(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { + const tc = self.create(name, source, expected_output); + self.addCase(tc); + } + + pub fn addAsm(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { + const tc = self.createExtra(name, source, expected_output, Special.Asm); + self.addCase(tc); + } + + pub fn addRuntimeSafety(self: *CompareOutputContext, name: []const u8, source: []const u8) void { + const tc = self.createExtra(name, source, undefined, Special.RuntimeSafety); + self.addCase(tc); + } + + pub fn addCase(self: *CompareOutputContext, case: TestCase) void { + const b = self.b; + + const write_src = b.addWriteFiles(); + for (case.sources.toSliceConst()) |src_file| { + write_src.add(src_file.filename, src_file.source); + } + + switch (case.special) { + Special.Asm => { + const annotated_case_name = fmt.allocPrint(self.b.allocator, "assemble-and-link {}", .{ + case.name, + }) catch unreachable; + if (self.test_filter) |filter| { + if (mem.indexOf(u8, annotated_case_name, filter) == null) return; + } + + const exe = b.addExecutable("test", null); + exe.addAssemblyFileFromWriteFileStep(write_src, case.sources.toSliceConst()[0].filename); + + const run = exe.run(); + run.addArgs(case.cli_args); + run.expectStdErrEqual(""); + run.expectStdOutEqual(case.expected_output); + + self.step.dependOn(&run.step); + }, + Special.None => { + for (self.modes) |mode| { + const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", .{ + "compare-output", + case.name, + @tagName(mode), + }) catch unreachable; + if (self.test_filter) |filter| { + if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; + } + + const basename = case.sources.toSliceConst()[0].filename; + const exe = b.addExecutableFromWriteFileStep("test", write_src, basename); + exe.setBuildMode(mode); + if (case.link_libc) { + exe.linkSystemLibrary("c"); + } + + const run = exe.run(); + run.addArgs(case.cli_args); + run.expectStdErrEqual(""); + run.expectStdOutEqual(case.expected_output); + + self.step.dependOn(&run.step); + } + }, + Special.RuntimeSafety => { + const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {}", .{case.name}) catch unreachable; + if (self.test_filter) |filter| { + if (mem.indexOf(u8, annotated_case_name, filter) == null) return; + } + + const basename = case.sources.toSliceConst()[0].filename; + const exe = b.addExecutableFromWriteFileStep("test", write_src, basename); + if (case.link_libc) { + exe.linkSystemLibrary("c"); + } + + const run = exe.run(); + run.addArgs(case.cli_args); + run.stderr_action = .ignore; + run.stdout_action = .ignore; + run.expected_exit_code = 126; + + self.step.dependOn(&run.step); + }, + } + } +}; diff --git a/test/src/run_translated_c.zig b/test/src/run_translated_c.zig new file mode 100644 index 0000000000..d136a5fc94 --- /dev/null +++ b/test/src/run_translated_c.zig @@ -0,0 +1,104 @@ +// This is the implementation of the test harness for running translated +// C code. For the actual test cases, see test/run_translated_c.zig. +const std = @import("std"); +const build = std.build; +const ArrayList = std.ArrayList; +const fmt = std.fmt; +const mem = std.mem; +const fs = std.fs; +const warn = std.debug.warn; + +pub const RunTranslatedCContext = struct { + b: *build.Builder, + step: *build.Step, + test_index: usize, + test_filter: ?[]const u8, + + const TestCase = struct { + name: []const u8, + sources: ArrayList(SourceFile), + expected_stdout: []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) void { + self.sources.append(SourceFile{ + .filename = filename, + .source = source, + }) catch unreachable; + } + }; + + pub fn create( + self: *RunTranslatedCContext, + allow_warnings: bool, + filename: []const u8, + name: []const u8, + source: []const u8, + expected_stdout: []const u8, + ) *TestCase { + const tc = self.b.allocator.create(TestCase) catch unreachable; + tc.* = TestCase{ + .name = name, + .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), + .expected_stdout = expected_stdout, + .allow_warnings = allow_warnings, + }; + + tc.addSourceFile(filename, source); + return tc; + } + + pub fn add( + self: *RunTranslatedCContext, + name: []const u8, + source: []const u8, + expected_stdout: []const u8, + ) void { + const tc = self.create(false, "source.c", name, source, expected_stdout); + self.addCase(tc); + } + + pub fn addAllowWarnings( + self: *RunTranslatedCContext, + name: []const u8, + source: []const u8, + expected_stdout: []const u8, + ) void { + const tc = self.create(true, "source.c", name, source, expected_stdout); + self.addCase(tc); + } + + pub fn addCase(self: *RunTranslatedCContext, case: *const TestCase) void { + const b = self.b; + + const annotated_case_name = fmt.allocPrint(self.b.allocator, "run-translated-c {}", .{case.name}) catch unreachable; + if (self.test_filter) |filter| { + if (mem.indexOf(u8, annotated_case_name, filter) == null) return; + } + + const write_src = b.addWriteFiles(); + for (case.sources.toSliceConst()) |src_file| { + write_src.add(src_file.filename, src_file.source); + } + const translate_c = b.addTranslateC(.{ + .write_file = .{ + .step = write_src, + .basename = case.sources.toSliceConst()[0].filename, + }, + }); + const exe = translate_c.addExecutable(); + exe.linkLibC(); + const run = exe.run(); + if (!case.allow_warnings) { + run.expectStdErrEqual(""); + } + run.expectStdOutEqual(case.expected_stdout); + + self.step.dependOn(&run.step); + } +}; diff --git a/test/src/translate_c.zig b/test/src/translate_c.zig new file mode 100644 index 0000000000..6db1e6f181 --- /dev/null +++ b/test/src/translate_c.zig @@ -0,0 +1,109 @@ +// This is the implementation of the test harness. +// For the actual test cases, see test/translate_c.zig. +const std = @import("std"); +const build = std.build; +const ArrayList = std.ArrayList; +const fmt = std.fmt; +const mem = std.mem; +const fs = std.fs; +const warn = std.debug.warn; + +pub const TranslateCContext = struct { + b: *build.Builder, + step: *build.Step, + test_index: usize, + test_filter: ?[]const u8, + + const TestCase = struct { + name: []const u8, + sources: ArrayList(SourceFile), + expected_lines: ArrayList([]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) void { + self.sources.append(SourceFile{ + .filename = filename, + .source = source, + }) catch unreachable; + } + + pub fn addExpectedLine(self: *TestCase, text: []const u8) void { + self.expected_lines.append(text) catch unreachable; + } + }; + + pub fn create( + self: *TranslateCContext, + allow_warnings: bool, + filename: []const u8, + name: []const u8, + source: []const u8, + expected_lines: []const []const u8, + ) *TestCase { + const tc = self.b.allocator.create(TestCase) catch unreachable; + tc.* = TestCase{ + .name = name, + .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), + .expected_lines = ArrayList([]const u8).init(self.b.allocator), + .allow_warnings = allow_warnings, + }; + + tc.addSourceFile(filename, source); + var arg_i: usize = 0; + while (arg_i < expected_lines.len) : (arg_i += 1) { + tc.addExpectedLine(expected_lines[arg_i]); + } + return tc; + } + + pub fn add( + self: *TranslateCContext, + name: []const u8, + source: []const u8, + expected_lines: []const []const u8, + ) void { + const tc = self.create(false, "source.h", name, source, expected_lines); + self.addCase(tc); + } + + pub fn addAllowWarnings( + self: *TranslateCContext, + name: []const u8, + source: []const u8, + expected_lines: []const []const u8, + ) void { + const tc = self.create(true, "source.h", name, source, expected_lines); + self.addCase(tc); + } + + pub fn addCase(self: *TranslateCContext, case: *const TestCase) void { + const b = self.b; + + const translate_c_cmd = "translate-c"; + const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {}", .{ translate_c_cmd, case.name }) catch unreachable; + if (self.test_filter) |filter| { + if (mem.indexOf(u8, annotated_case_name, filter) == null) return; + } + + const write_src = b.addWriteFiles(); + for (case.sources.toSliceConst()) |src_file| { + write_src.add(src_file.filename, src_file.source); + } + + const translate_c = b.addTranslateC(.{ + .write_file = .{ + .step = write_src, + .basename = case.sources.toSliceConst()[0].filename, + }, + }); + + const check_file = translate_c.addCheckFile(case.expected_lines.toSliceConst()); + + self.step.dependOn(&check_file.step); + } +}; |
