authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-04-26 18:47:11+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-04-28 18:35:01+02:00
logd305a6fb550d68f4ee57bbb86f482969b819653b
treeb153e13e20d1ffd8fb820e34e84a7714212265ed
parentd8d12d51ecd9e893755ced927c79eb9a6c036b31

test: parse all of manifest in TestManifest abstraction


1 files changed, 98 insertions(+), 90 deletions(-)

src/test.zig+98-90
...@@ -215,7 +215,37 @@ const TestManifest = struct {...@@ -215,7 +215,37 @@ const TestManifest = struct {
215 }215 }
216216
217 fn parse(arena: Allocator, bytes: []const u8) !TestManifest {217 fn parse(arena: Allocator, bytes: []const u8) !TestManifest {
218 var it = std.mem.tokenize(u8, bytes, "\r\n");218 // The manifest is the last contiguous block of comments in the file
219 // We scan for the beginning by searching backward for the first non-empty line that does not start with "//"
220 var start: ?usize = null;
221 var end: usize = bytes.len;
222 if (bytes.len > 0) {
223 var cursor: usize = bytes.len - 1;
224 while (true) {
225 // Move to beginning of line
226 while (cursor > 0 and bytes[cursor - 1] != '\n') cursor -= 1;
227
228 if (std.mem.startsWith(u8, bytes[cursor..], "//")) {
229 start = cursor; // Contiguous comment line, include in manifest
230 } else {
231 if (start != null) break; // Encountered non-comment line, end of manifest
232
233 // We ignore all-whitespace lines following the comment block, but anything else
234 // means that there is no manifest present.
235 if (std.mem.trim(u8, bytes[cursor..end], " \r\n\t").len == 0) {
236 end = cursor;
237 } else break; // If it's not whitespace, there is no manifest
238 }
239
240 // Move to previous line
241 if (cursor != 0) cursor -= 1 else break;
242 }
243 }
244
245 const actual_start = start orelse return error.MissingTestManifest;
246 const manifest_bytes = bytes[actual_start..end];
247
248 var it = std.mem.tokenize(u8, manifest_bytes, "\r\n");
219249
220 // First line is the test type250 // First line is the test type
221 const tt: Type = blk: {251 const tt: Type = blk: {
...@@ -250,20 +280,29 @@ const TestManifest = struct {...@@ -250,20 +280,29 @@ const TestManifest = struct {
250 }280 }
251281
252 // Finally, trailing is expected output282 // Finally, trailing is expected output
253 manifest.trailing_bytes = bytes[it.index..];283 manifest.trailing_bytes = manifest_bytes[it.index..];
254284
255 return manifest;285 return manifest;
256 }286 }
257287
258 fn getConfigValues(288 fn getConfigForKey(
259 self: TestManifest,289 self: TestManifest,
260 key: []const u8,290 key: []const u8,
261 comptime T: type,291 comptime T: type,
262 parse_fn: anytype,292 parse_fn: fn ([]const u8) ?T,
263 ) ?ConfigValueIterator(T, @TypeOf(parse_fn)) {293 ) ConfigValueIterator(T, @TypeOf(parse_fn)) {
264 const bytes = self.config_map.get(key) orelse return null;294 const delimiter = ",";
295 var inner: std.mem.SplitIterator(u8) = if (self.config_map.get(key)) |bytes| .{
296 .buffer = bytes,
297 .delimiter = delimiter,
298 .index = 0,
299 } else .{
300 .buffer = undefined,
301 .delimiter = delimiter,
302 .index = null,
303 };
265 return ConfigValueIterator(T, @TypeOf(parse_fn)){304 return ConfigValueIterator(T, @TypeOf(parse_fn)){
266 .inner = std.mem.split(u8, bytes, ","),305 .inner = inner,
267 .parse_fn = parse_fn,306 .parse_fn = parse_fn,
268 };307 };
269 }308 }
...@@ -958,93 +997,62 @@ pub const TestContext = struct {...@@ -958,93 +997,62 @@ pub const TestContext = struct {
958 const max_file_size = 10 * 1024 * 1024;997 const max_file_size = 10 * 1024 * 1024;
959 const src = try dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0);998 const src = try dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0);
960999
961 // The manifest is the last contiguous block of comments in the file1000 // Parse the manifest
962 // We scan for the beginning by searching backward for the first non-empty line that does not start with "//"1001 var manifest = try TestManifest.parse(ctx.arena, src);
963 var manifest_start: ?usize = null;1002 const strategy = manifest.getConfigForKey("strategy", Strategy, Strategy.parse).next().?;
964 var manifest_end: usize = src.len;1003 const backend = manifest.getConfigForKey("backend", Backend, Backend.parse).next().?;
965 if (src.len > 0) {1004
966 var cursor: usize = src.len - 1;1005 switch (manifest.@"type") {
967 while (true) {1006 .@"error" => {
968 // Move to beginning of line1007 const case = opt_case orelse case: {
969 while (cursor > 0 and src[cursor - 1] != '\n') cursor -= 1;1008 const case = try ctx.cases.addOne();
9701009 case.* = .{
971 if (std.mem.startsWith(u8, src[cursor..], "//")) {1010 .name = "none",
972 manifest_start = cursor; // Contiguous comment line, include in manifest1011 .target = .{},
973 } else {1012 .backend = backend,
974 if (manifest_start != null) break; // Encountered non-comment line, end of manifest1013 .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator),
9751014 .is_test = false,
976 // We ignore all-whitespace lines following the comment block, but anything else1015 .output_mode = .Obj,
977 // means that there is no manifest present.1016 .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator),
978 if (std.mem.trim(u8, src[cursor..manifest_end], " \r\n\t").len == 0) {
979 manifest_end = cursor;
980 } else break; // If it's not whitespace, there is no manifest
981 }
982
983 // Move to previous line
984 if (cursor != 0) cursor -= 1 else break;
985 }
986 }
987
988 if (manifest_start) |start| {
989 // Parse the manifest
990 var mani = try TestManifest.parse(ctx.arena, src[start..manifest_end]);
991 const strategy = mani.getConfigValues("strategy", Strategy, Strategy.parse).?.next().?;
992 const backend = mani.getConfigValues("backend", Backend, Backend.parse).?.next().?;
993
994 switch (mani.@"type") {
995 .@"error" => {
996 const case = opt_case orelse case: {
997 const case = try ctx.cases.addOne();
998 case.* = .{
999 .name = "none",
1000 .target = .{},
1001 .backend = backend,
1002 .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator),
1003 .is_test = false,
1004 .output_mode = .Obj,
1005 .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator),
1006 };
1007 opt_case = case;
1008 break :case case;
1009 };1017 };
1010 const errors = try mani.trailingAlloc(ctx.arena);1018 opt_case = case;
1019 break :case case;
1020 };
1021 const errors = try manifest.trailingAlloc(ctx.arena);
10111022
1012 switch (strategy) {1023 switch (strategy) {
1013 .independent => {1024 .independent => {
1014 case.addError(src, errors);1025 case.addError(src, errors);
1015 },1026 },
1016 .incremental => {1027 .incremental => {
1017 case.addErrorNamed("update", src, errors);1028 case.addErrorNamed("update", src, errors);
1018 },1029 },
1019 }1030 }
1020 },1031 },
1021 .run => {1032 .run => {
1022 const case = opt_case orelse case: {1033 const case = opt_case orelse case: {
1023 const case = try ctx.cases.addOne();1034 const case = try ctx.cases.addOne();
1024 case.* = .{1035 case.* = .{
1025 .name = "none",1036 .name = "none",
1026 .target = .{},1037 .target = .{},
1027 .backend = backend,1038 .backend = backend,
1028 .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator),1039 .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator),
1029 .is_test = false,1040 .is_test = false,
1030 .output_mode = .Exe,1041 .output_mode = .Exe,
1031 .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator),1042 .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator),
1032 };
1033 opt_case = case;
1034 break :case case;
1035 };1043 };
1044 opt_case = case;
1045 break :case case;
1046 };
10361047
1037 var output = std.ArrayList(u8).init(ctx.arena);1048 var output = std.ArrayList(u8).init(ctx.arena);
1038 var trailing_it = mani.trailing();1049 var trailing_it = manifest.trailing();
1039 while (trailing_it.next()) |line| {1050 while (trailing_it.next()) |line| {
1040 try output.appendSlice(line);1051 try output.appendSlice(line);
1041 }1052 }
1042 case.addCompareOutput(src, output.toOwnedSlice());1053 case.addCompareOutput(src, output.toOwnedSlice());
1043 },1054 },
1044 .cli => @panic("TODO cli tests"),1055 .cli => @panic("TODO cli tests"),
1045 }
1046 } else {
1047 return error.MissingManifest;
1048 }1056 }
1049 }1057 }
1050 }1058 }