aboutsummaryrefslogtreecommitdiff
path: root/test/src
diff options
context:
space:
mode:
Diffstat (limited to 'test/src')
-rw-r--r--test/src/compare_output.zig165
-rw-r--r--test/src/run_translated_c.zig104
-rw-r--r--test/src/translate_c.zig109
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);
+ }
+};