Commit 46db5e2a44
src/test.zig
@@ -237,10 +237,10 @@ const TestManifest = struct {
}
};
- fn ConfigValueIterator(comptime T: type, comptime ParseFn: type) type {
+ fn ConfigValueIterator(comptime T: type) type {
return struct {
inner: std.mem.SplitIterator(u8),
- parse_fn: ParseFn,
+ parse_fn: ParseFn(T),
fn next(self: *@This()) ?T {
const next_raw = self.inner.next() orelse return null;
@@ -320,28 +320,34 @@ const TestManifest = struct {
return manifest;
}
- fn getConfigForKey(
+ fn getConfigForKeyCustomParser(
self: TestManifest,
key: []const u8,
comptime T: type,
- parse_fn: fn ([]const u8) ?T,
- ) ConfigValueIterator(T, @TypeOf(parse_fn)) {
- const delimiter = ",";
- var inner: std.mem.SplitIterator(u8) = if (self.config_map.get(key)) |bytes| .{
- .buffer = bytes,
- .delimiter = delimiter,
- .index = 0,
- } else .{
- .buffer = undefined,
- .delimiter = delimiter,
- .index = null,
- };
- return ConfigValueIterator(T, @TypeOf(parse_fn)){
- .inner = inner,
+ parse_fn: ParseFn(T),
+ ) ConfigValueIterator(T) {
+ const bytes = self.config_map.get(key) orelse TestManifestConfigDefaults.get(self.@"type", key);
+ return ConfigValueIterator(T){
+ .inner = std.mem.split(u8, bytes, ","),
.parse_fn = parse_fn,
};
}
+ fn getConfigForKey(
+ self: TestManifest,
+ key: []const u8,
+ comptime T: type,
+ ) ConfigValueIterator(T) {
+ return self.getConfigForKeyCustomParser(key, T, getDefaultParser(T));
+ }
+
+ fn getConfigForKeyAssertSingle(self: TestManifest, key: []const u8, comptime T: type) T {
+ var it = self.getConfigForKey(key, T);
+ const res = it.next().?;
+ assert(it.next() == null);
+ return res;
+ }
+
fn trailing(self: TestManifest) TrailingIterator {
return .{
.inner = std.mem.tokenize(u8, self.trailing_bytes, "\r\n"),
@@ -356,6 +362,40 @@ const TestManifest = struct {
}
return out.toOwnedSlice();
}
+
+ fn ParseFn(comptime T: type) type {
+ return fn ([]const u8) ?T;
+ }
+
+ fn getDefaultParser(comptime T: type) ParseFn(T) {
+ switch (@typeInfo(T)) {
+ .Int => return struct {
+ fn parse(str: []const u8) ?T {
+ return std.fmt.parseInt(T, str, 0) catch null;
+ }
+ }.parse,
+ .Bool => return struct {
+ fn parse(str: []const u8) ?T {
+ const as_int = std.fmt.parseInt(u1, str, 0) catch return null;
+ return as_int > 0;
+ }
+ }.parse,
+ .Enum => return struct {
+ fn parse(str: []const u8) ?T {
+ return std.meta.stringToEnum(T, str);
+ }
+ }.parse,
+ .Struct => if (comptime std.mem.eql(u8, @typeName(T), "CrossTarget")) return struct {
+ fn parse(str: []const u8) ?T {
+ var opts = CrossTarget.ParseOptions{
+ .arch_os_abi = str,
+ };
+ return CrossTarget.parse(opts) catch null;
+ }
+ }.parse else @compileError("no default parser for " ++ @typeName(T)),
+ else => @compileError("no default parser for " ++ @typeName(T)),
+ }
+ }
};
pub const TestContext = struct {
@@ -401,10 +441,6 @@ pub const TestContext = struct {
stage1,
stage2,
llvm,
-
- fn parse(str: []const u8) ?Backend {
- return std.meta.stringToEnum(Backend, str);
- }
};
/// A `Case` consists of a list of `Update`. The same `Compilation` is used for each
@@ -899,7 +935,7 @@ pub const TestContext = struct {
pub fn addTestCasesFromDir(ctx: *TestContext, dir: std.fs.Dir, strategy: Strategy) void {
var current_file: []const u8 = "none";
- addTestCasesFromDirInner(ctx, dir, strategy, ¤t_file) catch |err| {
+ ctx.addTestCasesFromDirInner(dir, strategy, ¤t_file) catch |err| {
std.debug.panic("test harness failed to process file '{s}': {s}\n", .{
current_file, @errorName(err),
});
@@ -974,11 +1010,10 @@ pub const TestContext = struct {
/// that if any errors occur the caller knows it happened during this file.
current_file: *[]const u8,
) !void {
- var opt_case: ?*Case = null;
+ var cases = std.ArrayList(*Case).init(ctx.arena);
var it = dir.iterate();
var filenames = std.ArrayList([]const u8).init(ctx.arena);
- defer filenames.deinit();
while (try it.next()) |entry| {
if (entry.kind != .File) continue;
@@ -1021,7 +1056,7 @@ pub const TestContext = struct {
if (new_parts.test_index != null and new_parts.test_index.? != 0) return error.InvalidIncrementalTestIndex;
if (strategy == .independent)
- opt_case = null; // Generate a new independent test case for this update
+ cases.clearRetainingCapacity(); // Generate a new independent test case for this update
}
}
prev_filename = filename;
@@ -1032,59 +1067,53 @@ pub const TestContext = struct {
// Parse the manifest
var manifest = try TestManifest.parse(ctx.arena, src);
- switch (manifest.@"type") {
- .@"error" => {
- const case = opt_case orelse case: {
- const case = try ctx.cases.addOne();
- const backend = manifest.getConfigForKey("backend", Backend, Backend.parse).next().?;
- case.* = .{
- .name = "none",
- .target = .{},
- .backend = backend,
- .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator),
- .is_test = false,
- .output_mode = .Obj,
- .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator),
- };
- opt_case = case;
- break :case case;
- };
- const errors = try manifest.trailingAlloc(ctx.arena);
+ if (cases.items.len == 0) {
+ var backends = manifest.getConfigForKey("backend", Backend);
+ var targets = manifest.getConfigForKey("target", CrossTarget);
+ const is_test = manifest.getConfigForKeyAssertSingle("is_test", bool);
+ const output_mode = manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode);
- switch (strategy) {
- .independent => {
- case.addError(src, errors);
- },
- .incremental => {
- case.addErrorNamed("update", src, errors);
- },
- }
- },
- .run => {
- const case = opt_case orelse case: {
+ // Cross-product to get all possible test combinations
+ while (backends.next()) |backend| {
+ while (targets.next()) |target| {
const case = try ctx.cases.addOne();
- const backend = manifest.getConfigForKey("backend", Backend, Backend.parse).next().?;
case.* = .{
.name = "none",
- .target = .{},
+ .target = target,
.backend = backend,
.updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator),
- .is_test = false,
- .output_mode = .Exe,
+ .is_test = is_test,
+ .output_mode = output_mode,
.files = std.ArrayList(TestContext.File).init(ctx.cases.allocator),
};
- opt_case = case;
- break :case case;
- };
-
- var output = std.ArrayList(u8).init(ctx.arena);
- var trailing_it = manifest.trailing();
- while (trailing_it.next()) |line| {
- try output.appendSlice(line);
+ try cases.append(case);
}
- case.addCompareOutput(src, output.toOwnedSlice());
- },
- .cli => @panic("TODO cli tests"),
+ }
+ }
+
+ for (cases.items) |case| {
+ switch (manifest.@"type") {
+ .@"error" => {
+ const errors = try manifest.trailingAlloc(ctx.arena);
+ switch (strategy) {
+ .independent => {
+ case.addError(src, errors);
+ },
+ .incremental => {
+ case.addErrorNamed("update", 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);
+ }
+ case.addCompareOutput(src, output.toOwnedSlice());
+ },
+ .cli => @panic("TODO cli tests"),
+ }
}
}
}
test/incremental/add.0.zig
@@ -7,5 +7,4 @@ fn add(a: u32, b: u32) void {
}
// run
-// backend=stage2
//