From 695c8f756b7ef12c4e8993720503b9c6d2242689 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Jan 2020 22:45:48 -0500 Subject: [PATCH] add test harness for "run translated C" tests --- build.zig | 1 + src/main.cpp | 1 - test/run_translated_c.zig | 24 +++++ test/src/run_translated_c.zig | 180 ++++++++++++++++++++++++++++++++++ test/tests.zig | 19 ++++ 5 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 test/run_translated_c.zig create mode 100644 test/src/run_translated_c.zig diff --git a/build.zig b/build.zig index 0788e2e762..979379d9f3 100644 --- a/build.zig +++ b/build.zig @@ -137,6 +137,7 @@ pub fn build(b: *Builder) !void { test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes)); test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes)); test_step.dependOn(tests.addTranslateCTests(b, test_filter)); + test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter)); test_step.dependOn(tests.addGenHTests(b, test_filter)); test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes)); test_step.dependOn(docs_step); diff --git a/src/main.cpp b/src/main.cpp index 43a89b4efe..ea9f089072 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -43,7 +43,6 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " libc [paths_file] Display native libc paths file or validate one\n" " run [source] [-- [args]] create executable and run immediately\n" " translate-c [source] convert c code to zig code\n" - " translate-c-2 [source] experimental self-hosted translate-c\n" " targets list available compilation targets\n" " test [source] create and run a test build\n" " version print version number and exit\n" diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig new file mode 100644 index 0000000000..4bfe22d366 --- /dev/null +++ b/test/run_translated_c.zig @@ -0,0 +1,24 @@ +const tests = @import("tests.zig"); + +pub fn addCases(cases: *tests.RunTranslatedCContext) void { + cases.add("hello world", + \\#define _NO_CRT_STDIO_INLINE 1 + \\#include + \\int main(int argc, char **argv) { + \\ printf("hello, world!\n"); + \\ return 0; + \\} + , "hello, world!\n"); + + cases.add("anon struct init", + \\#include + \\struct {int a; int b;} x = {1, 2}; + \\int main(int argc, char **argv) { + \\ x.a += 2; + \\ x.b += 1; + \\ if (x.a != 3) abort(); + \\ if (x.b != 3) abort(); + \\ return 0; + \\} + , ""); +} diff --git a/test/src/run_translated_c.zig b/test/src/run_translated_c.zig new file mode 100644 index 0000000000..ae09e32dbc --- /dev/null +++ b/test/src/run_translated_c.zig @@ -0,0 +1,180 @@ +// 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; + } + }; + + const DoEverythingStep = struct { + step: build.Step, + context: *RunTranslatedCContext, + name: []const u8, + case: *const TestCase, + test_index: usize, + + pub fn create( + context: *RunTranslatedCContext, + name: []const u8, + case: *const TestCase, + ) *DoEverythingStep { + const allocator = context.b.allocator; + const ptr = allocator.create(DoEverythingStep) catch unreachable; + ptr.* = DoEverythingStep{ + .context = context, + .name = name, + .case = case, + .test_index = context.test_index, + .step = build.Step.init("RunTranslatedC", allocator, make), + }; + context.test_index += 1; + return ptr; + } + + fn make(step: *build.Step) !void { + const self = @fieldParentPtr(DoEverythingStep, "step", step); + const b = self.context.b; + + warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name }); + // translate from c to zig + const translated_c_code = blk: { + var zig_args = ArrayList([]const u8).init(b.allocator); + defer zig_args.deinit(); + + const rel_c_filename = try fs.path.join(b.allocator, &[_][]const u8{ + b.cache_root, + self.case.sources.toSliceConst()[0].filename, + }); + + try zig_args.append(b.zig_exe); + try zig_args.append("translate-c"); + try zig_args.append("-lc"); + try zig_args.append(b.pathFromRoot(rel_c_filename)); + + break :blk try b.exec(zig_args.toSliceConst()); + }; + + // write stdout to a file + + const translated_c_path = try fs.path.join(b.allocator, + &[_][]const u8{ b.cache_root, "translated_c.zig" }); + try fs.cwd().writeFile(translated_c_path, translated_c_code); + + // zig run the result + const run_stdout = blk: { + var zig_args = ArrayList([]const u8).init(b.allocator); + defer zig_args.deinit(); + + try zig_args.append(b.zig_exe); + try zig_args.append("-lc"); + try zig_args.append("run"); + try zig_args.append(translated_c_path); + + break :blk try b.exec(zig_args.toSliceConst()); + }; + // compare stdout + if (!mem.eql(u8, self.case.expected_stdout, run_stdout)) { + warn( + \\ + \\========= Expected this output: ========= + \\{} + \\========= But found: ==================== + \\{} + \\ + , .{ self.case.expected_stdout, run_stdout }); + return error.TestFailed; + } + + warn("OK\n", .{}); + } + }; + + 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 do_everything_step = DoEverythingStep.create(self, annotated_case_name, case); + self.step.dependOn(&do_everything_step.step); + + for (case.sources.toSliceConst()) |src_file| { + const expanded_src_path = fs.path.join( + b.allocator, + &[_][]const u8{ b.cache_root, src_file.filename }, + ) catch unreachable; + const write_src = b.addWriteFile(expanded_src_path, src_file.source); + do_everything_step.step.dependOn(&write_src.step); + } + } +}; + diff --git a/test/tests.zig b/test/tests.zig index 4672359802..4636eb8132 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -14,6 +14,7 @@ const builtin = @import("builtin"); const Mode = builtin.Mode; const LibExeObjStep = build.LibExeObjStep; +// Cases const compare_output = @import("compare_output.zig"); const standalone = @import("standalone.zig"); const stack_traces = @import("stack_traces.zig"); @@ -21,8 +22,12 @@ const compile_errors = @import("compile_errors.zig"); const assemble_and_link = @import("assemble_and_link.zig"); const runtime_safety = @import("runtime_safety.zig"); const translate_c = @import("translate_c.zig"); +const run_translated_c = @import("run_translated_c.zig"); const gen_h = @import("gen_h.zig"); +// Implementations +pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTranslatedCContext; + const TestTarget = struct { target: Target = .Native, mode: builtin.Mode = .Debug, @@ -383,6 +388,20 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St return cases.step; } +pub fn addRunTranslatedCTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { + const cases = b.allocator.create(RunTranslatedCContext) catch unreachable; + cases.* = .{ + .b = b, + .step = b.step("test-run-translated-c", "Run the Run-Translated-C tests"), + .test_index = 0, + .test_filter = test_filter, + }; + + run_translated_c.addCases(cases); + + return cases.step; +} + pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { const cases = b.allocator.create(GenHContext) catch unreachable; cases.* = GenHContext{