Commit d305a6fb55
Changed files (1)
src
src/test.zig
@@ -215,7 +215,37 @@ const TestManifest = struct {
}
fn parse(arena: Allocator, bytes: []const u8) !TestManifest {
- var it = std.mem.tokenize(u8, bytes, "\r\n");
+ // The manifest is the last contiguous block of comments in the file
+ // We scan for the beginning by searching backward for the first non-empty line that does not start with "//"
+ var start: ?usize = null;
+ var end: usize = bytes.len;
+ if (bytes.len > 0) {
+ var cursor: usize = bytes.len - 1;
+ while (true) {
+ // Move to beginning of line
+ while (cursor > 0 and bytes[cursor - 1] != '\n') cursor -= 1;
+
+ if (std.mem.startsWith(u8, bytes[cursor..], "//")) {
+ start = cursor; // Contiguous comment line, include in manifest
+ } else {
+ if (start != null) break; // Encountered non-comment line, end of manifest
+
+ // We ignore all-whitespace lines following the comment block, but anything else
+ // means that there is no manifest present.
+ if (std.mem.trim(u8, bytes[cursor..end], " \r\n\t").len == 0) {
+ end = cursor;
+ } else break; // If it's not whitespace, there is no manifest
+ }
+
+ // Move to previous line
+ if (cursor != 0) cursor -= 1 else break;
+ }
+ }
+
+ const actual_start = start orelse return error.MissingTestManifest;
+ const manifest_bytes = bytes[actual_start..end];
+
+ var it = std.mem.tokenize(u8, manifest_bytes, "\r\n");
// First line is the test type
const tt: Type = blk: {
@@ -250,20 +280,29 @@ const TestManifest = struct {
}
// Finally, trailing is expected output
- manifest.trailing_bytes = bytes[it.index..];
+ manifest.trailing_bytes = manifest_bytes[it.index..];
return manifest;
}
- fn getConfigValues(
+ fn getConfigForKey(
self: TestManifest,
key: []const u8,
comptime T: type,
- parse_fn: anytype,
- ) ?ConfigValueIterator(T, @TypeOf(parse_fn)) {
- const bytes = self.config_map.get(key) orelse return null;
+ 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 = std.mem.split(u8, bytes, ","),
+ .inner = inner,
.parse_fn = parse_fn,
};
}
@@ -958,93 +997,62 @@ pub const TestContext = struct {
const max_file_size = 10 * 1024 * 1024;
const src = try dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0);
- // The manifest is the last contiguous block of comments in the file
- // We scan for the beginning by searching backward for the first non-empty line that does not start with "//"
- var manifest_start: ?usize = null;
- var manifest_end: usize = src.len;
- if (src.len > 0) {
- var cursor: usize = src.len - 1;
- while (true) {
- // Move to beginning of line
- while (cursor > 0 and src[cursor - 1] != '\n') cursor -= 1;
-
- if (std.mem.startsWith(u8, src[cursor..], "//")) {
- manifest_start = cursor; // Contiguous comment line, include in manifest
- } else {
- if (manifest_start != null) break; // Encountered non-comment line, end of manifest
-
- // We ignore all-whitespace lines following the comment block, but anything else
- // means that there is no manifest present.
- if (std.mem.trim(u8, src[cursor..manifest_end], " \r\n\t").len == 0) {
- manifest_end = cursor;
- } else break; // If it's not whitespace, there is no manifest
- }
-
- // Move to previous line
- if (cursor != 0) cursor -= 1 else break;
- }
- }
-
- if (manifest_start) |start| {
- // Parse the manifest
- var mani = try TestManifest.parse(ctx.arena, src[start..manifest_end]);
- const strategy = mani.getConfigValues("strategy", Strategy, Strategy.parse).?.next().?;
- const backend = mani.getConfigValues("backend", Backend, Backend.parse).?.next().?;
-
- switch (mani.@"type") {
- .@"error" => {
- const case = opt_case orelse case: {
- const case = try ctx.cases.addOne();
- 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;
+ // Parse the manifest
+ var manifest = try TestManifest.parse(ctx.arena, src);
+ const strategy = manifest.getConfigForKey("strategy", Strategy, Strategy.parse).next().?;
+ const backend = manifest.getConfigForKey("backend", Backend, Backend.parse).next().?;
+
+ switch (manifest.@"type") {
+ .@"error" => {
+ const case = opt_case orelse case: {
+ const case = try ctx.cases.addOne();
+ 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),
};
- const errors = try mani.trailingAlloc(ctx.arena);
+ opt_case = case;
+ break :case case;
+ };
+ const errors = try manifest.trailingAlloc(ctx.arena);
- switch (strategy) {
- .independent => {
- case.addError(src, errors);
- },
- .incremental => {
- case.addErrorNamed("update", src, errors);
- },
- }
- },
- .run => {
- const case = opt_case orelse case: {
- const case = try ctx.cases.addOne();
- case.* = .{
- .name = "none",
- .target = .{},
- .backend = backend,
- .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator),
- .is_test = false,
- .output_mode = .Exe,
- .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator),
- };
- opt_case = case;
- break :case case;
+ switch (strategy) {
+ .independent => {
+ case.addError(src, errors);
+ },
+ .incremental => {
+ case.addErrorNamed("update", src, errors);
+ },
+ }
+ },
+ .run => {
+ const case = opt_case orelse case: {
+ const case = try ctx.cases.addOne();
+ case.* = .{
+ .name = "none",
+ .target = .{},
+ .backend = backend,
+ .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator),
+ .is_test = false,
+ .output_mode = .Exe,
+ .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 = mani.trailing();
- while (trailing_it.next()) |line| {
- try output.appendSlice(line);
- }
- case.addCompareOutput(src, output.toOwnedSlice());
- },
- .cli => @panic("TODO cli tests"),
- }
- } else {
- return error.MissingManifest;
+ 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"),
}
}
}