diff options
Diffstat (limited to 'test/src')
| -rw-r--r-- | test/src/Cases.zig | 272 |
1 files changed, 238 insertions, 34 deletions
diff --git a/test/src/Cases.zig b/test/src/Cases.zig index fe1fb6fe34..4969b82033 100644 --- a/test/src/Cases.zig +++ b/test/src/Cases.zig @@ -1,6 +1,7 @@ gpa: Allocator, arena: Allocator, cases: std.ArrayList(Case), +translate: std.ArrayList(Translate), incremental_cases: std.ArrayList(IncrementalCase), pub const IncrementalCase = struct { @@ -36,7 +37,7 @@ pub const Update = struct { Execution: []const u8, /// A header update compiles the input with the equivalent of /// `-femit-h` and tests the produced header against the - /// expected result + /// expected result. Header: []const u8, }, @@ -61,6 +62,11 @@ pub const Backend = enum { llvm, }; +pub const CFrontend = enum { + clang, + aro, +}; + /// A `Case` consists of a list of `Update`. The same `Compilation` is used for each /// update, so each update's source is treated as a single file being /// updated by the test harness and incrementally compiled. @@ -143,6 +149,25 @@ pub const Case = struct { } }; +pub const Translate = struct { + /// The name of the test case. This is shown if a test fails, and + /// otherwise ignored. + name: []const u8, + + input: [:0]const u8, + target: CrossTarget, + link_libc: bool, + c_frontend: CFrontend, + kind: union(enum) { + /// Translate the input, run it and check that it + /// outputs the expected text. + run: []const u8, + /// Translate the input and check that it contains + /// the expected lines of code. + translate: []const []const u8, + }, +}; + pub fn addExe( ctx: *Cases, name: []const u8, @@ -346,9 +371,12 @@ pub fn addCompile( pub fn addFromDir(ctx: *Cases, dir: std.fs.IterableDir) void { var current_file: []const u8 = "none"; ctx.addFromDirInner(dir, ¤t_file) catch |err| { - std.debug.panic("test harness failed to process file '{s}': {s}\n", .{ - current_file, @errorName(err), - }); + std.debug.panicExtra( + @errorReturnTrace(), + @returnAddress(), + "test harness failed to process file '{s}': {s}\n", + .{ current_file, @errorName(err) }, + ); }; } @@ -395,10 +423,44 @@ fn addFromDirInner( const backends = try manifest.getConfigForKeyAlloc(ctx.arena, "backend", Backend); const targets = try manifest.getConfigForKeyAlloc(ctx.arena, "target", CrossTarget); + const c_frontends = try manifest.getConfigForKeyAlloc(ctx.arena, "c_frontend", CFrontend); const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool); const link_libc = try manifest.getConfigForKeyAssertSingle("link_libc", bool); const output_mode = try manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode); + if (manifest.type == .translate_c) { + for (c_frontends) |c_frontend| { + for (targets) |target| { + const output = try manifest.trailingLinesSplit(ctx.arena); + try ctx.translate.append(.{ + .name = std.fs.path.stem(filename), + .c_frontend = c_frontend, + .target = target, + .link_libc = link_libc, + .input = src, + .kind = .{ .translate = output }, + }); + } + } + continue; + } + if (manifest.type == .run_translated_c) { + for (c_frontends) |c_frontend| { + for (targets) |target| { + const output = try manifest.trailingSplit(ctx.arena); + try ctx.translate.append(.{ + .name = std.fs.path.stem(filename), + .c_frontend = c_frontend, + .target = target, + .link_libc = link_libc, + .input = src, + .kind = .{ .run = output }, + }); + } + } + continue; + } + var cases = std.ArrayList(usize).init(ctx.arena); // Cross-product to get all possible test combinations @@ -439,21 +501,15 @@ fn addFromDirInner( case.addCompile(src); }, .@"error" => { - const errors = try manifest.trailingAlloc(ctx.arena); + const errors = try manifest.trailingLines(ctx.arena); case.addError(src, errors); }, .run => { - var output = std.ArrayList(u8).init(ctx.arena); - var trailing_it = manifest.trailing(); - while (trailing_it.next()) |line| { - try output.appendSlice(line); - try output.append('\n'); - } - if (output.items.len > 0) { - try output.resize(output.items.len - 1); - } - case.addCompareOutput(src, try output.toOwnedSlice()); + const output = try manifest.trailingSplit(ctx.arena); + case.addCompareOutput(src, output); }, + .translate_c => @panic("c_frontend specified for compile case"), + .run_translated_c => @panic("c_frontend specified for compile case"), .cli => @panic("TODO cli tests"), } } @@ -468,6 +524,7 @@ pub fn init(gpa: Allocator, arena: Allocator) Cases { return .{ .gpa = gpa, .cases = std.ArrayList(Case).init(gpa), + .translate = std.ArrayList(Translate).init(gpa), .incremental_cases = std.ArrayList(IncrementalCase).init(gpa), .arena = arena, }; @@ -482,7 +539,7 @@ pub fn lowerToBuildSteps( incremental_exe: *std.Build.Step.Compile, ) void { const host = std.zig.system.NativeTargetInfo.detect(.{}) catch |err| - std.debug.panic("unable to detect notive host: {s}\n", .{@errorName(err)}); + std.debug.panic("unable to detect native host: {s}\n", .{@errorName(err)}); for (self.incremental_cases.items) |incr_case| { if (true) { @@ -589,7 +646,7 @@ pub fn lowerToBuildSteps( .Execution => |expected_stdout| no_exec: { const run = if (case.target.ofmt == .c) run_step: { const target_info = std.zig.system.NativeTargetInfo.detect(case.target) catch |err| - std.debug.panic("unable to detect notive host: {s}\n", .{@errorName(err)}); + std.debug.panic("unable to detect target host: {s}\n", .{@errorName(err)}); if (host.getExternalExecutor(&target_info, .{ .link_libc = true }) != .native) { // We wouldn't be able to run the compiled C code. break :no_exec; @@ -623,6 +680,68 @@ pub fn lowerToBuildSteps( .Header => @panic("TODO"), } } + + for (self.translate.items) |*case| switch (case.kind) { + .run => |output| { + const annotated_case_name = b.fmt("run-translated-c {s}", .{case.name}); + if (opt_test_filter) |filter| { + if (std.mem.indexOf(u8, annotated_case_name, filter) == null) return; + } + if (!std.process.can_spawn) { + std.debug.print("Unable to spawn child processes on {s}, skipping test.\n", .{@tagName(builtin.os.tag)}); + continue; // Pass test. + } + + const target_info = std.zig.system.NativeTargetInfo.detect(case.target) catch |err| + std.debug.panic("unable to detect target host: {s}\n", .{@errorName(err)}); + if (host.getExternalExecutor(&target_info, .{ .link_libc = true }) != .native) { + // We wouldn't be able to run the compiled C code. + continue; // Pass test. + } + + const write_src = b.addWriteFiles(); + const file_source = write_src.add("tmp.c", case.input); + + const translate_c = b.addTranslateC(.{ + .source_file = file_source, + .optimize = .Debug, + .target = case.target, + .link_libc = case.link_libc, + .use_clang = case.c_frontend == .clang, + }); + translate_c.step.name = b.fmt("{s} translate-c", .{annotated_case_name}); + + const run_exe = translate_c.addExecutable(.{}); + run_exe.step.name = b.fmt("{s} build-exe", .{annotated_case_name}); + run_exe.linkLibC(); + const run = b.addRunArtifact(run_exe); + run.step.name = b.fmt("{s} run", .{annotated_case_name}); + run.expectStdOutEqual(output); + + parent_step.dependOn(&run.step); + }, + .translate => |output| { + const annotated_case_name = b.fmt("zig translate-c {s}", .{case.name}); + if (opt_test_filter) |filter| { + if (std.mem.indexOf(u8, annotated_case_name, filter) == null) return; + } + + const write_src = b.addWriteFiles(); + const file_source = write_src.add("tmp.c", case.input); + + const translate_c = b.addTranslateC(.{ + .source_file = file_source, + .optimize = .Debug, + .target = case.target, + .link_libc = case.link_libc, + .use_clang = case.c_frontend == .clang, + }); + translate_c.step.name = annotated_case_name; + + const check_file = translate_c.addCheckFile(output); + parent_step.dependOn(&check_file.step); + }, + }; } /// Sort test filenames in-place, so that incremental test cases ("foo.0.zig", @@ -780,7 +899,7 @@ const TestManifestConfigDefaults = struct { if (std.mem.eql(u8, key, "backend")) { return "stage2"; } else if (std.mem.eql(u8, key, "target")) { - if (@"type" == .@"error") { + if (@"type" == .@"error" or @"type" == .translate_c or @"type" == .run_translated_c) { return "native"; } return comptime blk: { @@ -807,12 +926,16 @@ const TestManifestConfigDefaults = struct { .@"error" => "Obj", .run => "Exe", .compile => "Obj", + .translate_c => "Obj", + .run_translated_c => "Obj", .cli => @panic("TODO test harness for CLI tests"), }; } else if (std.mem.eql(u8, key, "is_test")) { - return "0"; + return "false"; } else if (std.mem.eql(u8, key, "link_libc")) { - return "0"; + return "false"; + } else if (std.mem.eql(u8, key, "c_frontend")) { + return "clang"; } else unreachable; } }; @@ -844,6 +967,8 @@ const TestManifest = struct { run, cli, compile, + translate_c, + run_translated_c, }; const TrailingIterator = struct { @@ -912,6 +1037,10 @@ const TestManifest = struct { break :blk .cli; } else if (std.mem.eql(u8, raw, "compile")) { break :blk .compile; + } else if (std.mem.eql(u8, raw, "translate-c")) { + break :blk .translate_c; + } else if (std.mem.eql(u8, raw, "run-translated-c")) { + break :blk .run_translated_c; } else { std.log.warn("unknown test case type requested: {s}", .{raw}); return error.UnknownTestCaseType; @@ -979,7 +1108,21 @@ const TestManifest = struct { }; } - fn trailingAlloc(self: TestManifest, allocator: Allocator) error{OutOfMemory}![]const []const u8 { + fn trailingSplit(self: TestManifest, allocator: Allocator) error{OutOfMemory}![]const u8 { + var out = std.ArrayList(u8).init(allocator); + defer out.deinit(); + var trailing_it = self.trailing(); + while (trailing_it.next()) |line| { + try out.appendSlice(line); + try out.append('\n'); + } + if (out.items.len > 0) { + try out.resize(out.items.len - 1); + } + return try out.toOwnedSlice(); + } + + fn trailingLines(self: TestManifest, allocator: Allocator) error{OutOfMemory}![]const []const u8 { var out = std.ArrayList([]const u8).init(allocator); defer out.deinit(); var it = self.trailing(); @@ -989,6 +1132,28 @@ const TestManifest = struct { return try out.toOwnedSlice(); } + fn trailingLinesSplit(self: TestManifest, allocator: Allocator) error{OutOfMemory}![]const []const u8 { + // Collect output lines split by empty lines + var out = std.ArrayList([]const u8).init(allocator); + defer out.deinit(); + var buf = std.ArrayList(u8).init(allocator); + defer buf.deinit(); + var it = self.trailing(); + while (it.next()) |line| { + if (line.len == 0) { + if (buf.items.len != 0) { + try out.append(try buf.toOwnedSlice()); + buf.items.len = 0; + } + continue; + } + try buf.appendSlice(line); + try buf.append('\n'); + } + try out.append(try buf.toOwnedSlice()); + return try out.toOwnedSlice(); + } + fn ParseFn(comptime T: type) type { return fn ([]const u8) anyerror!T; } @@ -1011,8 +1176,10 @@ const TestManifest = struct { }.parse, .Bool => return struct { fn parse(str: []const u8) anyerror!T { - const as_int = try std.fmt.parseInt(u1, str, 0); - return as_int > 0; + if (std.mem.eql(u8, str, "true")) return true; + if (std.mem.eql(u8, str, "false")) return false; + std.debug.print("{s}\n", .{str}); + return error.InvalidBool; } }.parse, .Enum => return struct { @@ -1124,9 +1291,47 @@ pub fn main() !void { if (cases.items.len == 0) { const backends = try manifest.getConfigForKeyAlloc(arena, "backend", Backend); const targets = try manifest.getConfigForKeyAlloc(arena, "target", CrossTarget); + const c_frontends = try manifest.getConfigForKeyAlloc(ctx.arena, "c_frontend", CFrontend); const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool); + const link_libc = try manifest.getConfigForKeyAssertSingle("link_libc", bool); const output_mode = try manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode); + if (manifest.type == .translate_c) { + for (c_frontends) |c_frontend| { + for (targets) |target| { + const output = try manifest.trailingLinesSplit(ctx.arena); + try ctx.translate.append(.{ + .name = std.fs.path.stem(filename), + .c_frontend = c_frontend, + .target = target, + .is_test = is_test, + .link_libc = link_libc, + .input = src, + .kind = .{ .translate = output }, + }); + } + } + continue; + } + if (manifest.type == .run_translated_c) { + for (c_frontends) |c_frontend| { + for (targets) |target| { + const output = try manifest.trailingSplit(ctx.arena); + try ctx.translate.append(.{ + .name = std.fs.path.stem(filename), + .c_frontend = c_frontend, + .target = target, + .is_test = is_test, + .link_libc = link_libc, + .output = output, + .input = src, + .kind = .{ .run = output }, + }); + } + } + continue; + } + // Cross-product to get all possible test combinations for (backends) |backend| { for (targets) |target| { @@ -1158,7 +1363,7 @@ pub fn main() !void { case.addCompile(src); }, .@"error" => { - const errors = try manifest.trailingAlloc(arena); + const errors = try manifest.trailingLines(arena); switch (strategy) { .independent => { case.addError(src, errors); @@ -1169,17 +1374,11 @@ pub fn main() !void { } }, .run => { - var output = std.ArrayList(u8).init(arena); - var trailing_it = manifest.trailing(); - while (trailing_it.next()) |line| { - try output.appendSlice(line); - try output.append('\n'); - } - if (output.items.len > 0) { - try output.resize(output.items.len - 1); - } - case.addCompareOutput(src, try output.toOwnedSlice()); + const output = try manifest.trailingSplit(ctx.arena); + case.addCompareOutput(src, output); }, + .translate_c => @panic("c_frontend specified for compile case"), + .run_translated_c => @panic("c_frontend specified for compile case"), .cli => @panic("TODO cli tests"), } } @@ -1255,6 +1454,11 @@ fn runCases(self: *Cases, zig_exe_path: []const u8) !void { host, ); } + + for (self.translate.items) |*case| { + _ = case; + @panic("TODO is this even used?"); + } } } |
