aboutsummaryrefslogtreecommitdiff
path: root/test/src/compare_output.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2020-01-05 02:01:28 -0500
committerAndrew Kelley <andrew@ziglang.org>2020-01-05 02:19:22 -0500
commita690a5085ddbfb540cf07db146645a9f8a4e92f6 (patch)
tree267f1c2908cd0125178d85adc41c7c81109d608e /test/src/compare_output.zig
parent14fcfe29817c03c3cac023b045433ea7abe4bd47 (diff)
downloadzig-a690a5085ddbfb540cf07db146645a9f8a4e92f6.tar.gz
zig-a690a5085ddbfb540cf07db146645a9f8a4e92f6.zip
rework and improve some of the zig build steps
* `RunStep` moved to lib/std/build/run.zig and gains ability to compare output and exit code against expected values. Multiple redundant locations in the test harness code are replaced to use `RunStep`. * `WriteFileStep` moved to lib/std/build/write_file.zig and gains ability to write more than one file into the cache directory, for when the files need to be relative to each other. This makes usage of `WriteFileStep` no longer problematic when parallelizing zig build. * Added `CheckFileStep`, which can be used to validate that the output of another step produced a valid file. Multiple redundant locations in the test harness code are replaced to use `CheckFileStep`. * Added `TranslateCStep`. This exposes `zig translate-c` to the build system, which is likely to be rarely useful by most Zig users; however Zig's own test suite uses it both for translate-c tests and for run-translated-c tests. * Refactored ad-hoc code to handle source files coming from multiple kinds of sources, into `std.build.FileSource`. * Added `std.build.Builder.addExecutableFromWriteFileStep`. * Added `std.build.Builder.addExecutableSource`. * Added `std.build.Builder.addWriteFiles`. * Added `std.build.Builder.addTranslateC`. * Added `std.build.LibExeObjStep.addCSourceFileSource`. * Added `std.build.LibExeObjStep.addAssemblyFileFromWriteFileStep`. * Added `std.build.LibExeObjStep.addAssemblyFileSource`. * Exposed `std.fs.base64_encoder`.
Diffstat (limited to 'test/src/compare_output.zig')
-rw-r--r--test/src/compare_output.zig165
1 files changed, 165 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);
+ },
+ }
+ }
+};