From ab408ca0f7724e57147bf5597ece078df272d8b2 Mon Sep 17 00:00:00 2001 From: Brendan Dennis Date: Sun, 29 Jan 2023 13:16:41 +1030 Subject: [PATCH 01/19] Fixed 'toOwnedSlice' calls returning error unions not 'trying' Fixed references to 'field_type' being renamed to 'type' Checked all tests now pass --- src/parse.zig | 2 +- src/yaml.zig | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/parse.zig b/src/parse.zig index c635927..3804163 100644 --- a/src/parse.zig +++ b/src/parse.zig @@ -288,7 +288,7 @@ pub const Tree = struct { } self.source = source; - self.tokens = tokens.toOwnedSlice(); + self.tokens = try tokens.toOwnedSlice(); var it = TokenIterator{ .buffer = self.tokens }; var parser = Parser{ diff --git a/src/yaml.zig b/src/yaml.zig index d55600b..8c63a59 100644 --- a/src/yaml.zig +++ b/src/yaml.zig @@ -180,7 +180,7 @@ pub const Value = union(enum) { out_list.appendAssumeCapacity(value); } - return Value{ .list = out_list.toOwnedSlice() }; + return Value{ .list = try out_list.toOwnedSlice() }; } else if (node.cast(Node.Value)) |value| { const raw = tree.getRaw(node.start, node.end); @@ -221,7 +221,7 @@ pub const Value = union(enum) { } } - return Value{ .list = list.toOwnedSlice() }; + return Value{ .list = try list.toOwnedSlice() }; } else { var map = Map.init(arena); errdefer map.deinit(); @@ -275,7 +275,7 @@ pub const Value = union(enum) { } } - return Value{ .list = list.toOwnedSlice() }; + return Value{ .list = try list.toOwnedSlice() }; }, else => { @compileError("Unhandled type: {s}" ++ @typeName(@TypeOf(input))); @@ -399,7 +399,7 @@ pub const Yaml = struct { if (union_info.tag_type) |_| { inline for (union_info.fields) |field| { - if (self.parseValue(field.field_type, value)) |u_value| { + if (self.parseValue(field.type, value)) |u_value| { return @unionInit(T, field.name, u_value); } else |err| { if (@as(@TypeOf(err) || error{TypeMismatch}, err) != error.TypeMismatch) return err; @@ -426,16 +426,16 @@ pub const Yaml = struct { break :blk map.get(field_name); }; - if (@typeInfo(field.field_type) == .Optional) { - @field(parsed, field.name) = try self.parseOptional(field.field_type, value); + if (@typeInfo(field.type) == .Optional) { + @field(parsed, field.name) = try self.parseOptional(field.type, value); continue; } const unwrapped = value orelse { - log.err("missing struct field: {s}: {s}", .{ field.name, @typeName(field.field_type) }); + log.err("missing struct field: {s}: {s}", .{ field.name, @typeName(field.type) }); return error.StructFieldMissing; }; - @field(parsed, field.name) = try self.parseValue(field.field_type, unwrapped); + @field(parsed, field.name) = try self.parseValue(field.type, unwrapped); } return parsed; From 447ab0f3b54e313566994eea6797e150382eaf29 Mon Sep 17 00:00:00 2001 From: Brendan Dennis Date: Mon, 30 Jan 2023 00:07:33 +1030 Subject: [PATCH 02/19] Added in prototype test generator that takes the 'data' folder produced by the 1.2 YAML test cases and autogenerates zig test cases --- build.zig | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/build.zig b/build.zig index fba118f..9bc8bb9 100644 --- a/build.zig +++ b/build.zig @@ -38,4 +38,132 @@ pub fn build(b: *std.build.Builder) void { e2e_tests.setBuildMode(mode); e2e_tests.addPackagePath("yaml", "src/yaml.zig"); test_step.dependOn(&e2e_tests.step); + + const cwd = std.fs.cwd(); + if(cwd.access("data",.{})) { + std.debug.print("Found 'data' directory with YAML tests. Attempting to generate test cases\n",.{}); + + const gen = GenerateStep.init(b,"yamlTest.zig"); + test_step.dependOn(&gen.step); + var full_yaml_tests = b.addTest("zig-cache/yamlTest.zig"); + test_step.dependOn(&full_yaml_tests.step); + } else |_| { + std.debug.print("No 'data' directory with YAML tests provided\n",.{}); + } } + + +const path = std.fs.path; +const Builder = std.build.Builder; +const Step = std.build.Step; +const Allocator = std.mem.Allocator; + +const preamble = + \\// This file is generated from the YAML 1.2 test database. + \\ + \\const std = @import("std"); + \\const mem = std.mem; + \\const testing = std.testing; + \\ + \\const Allocator = mem.Allocator; + \\const Yaml = @import("yaml").Yaml; + \\ + \\const gpa = testing.allocator; + \\ + \\fn loadFromFile(file_path: []const u8) !Yaml { + \\ const file = try std.fs.cwd().openFile(file_path, .{}); + \\ defer file.close(); + \\ + \\ const source = try file.readToEndAlloc(gpa, std.math.maxInt(u32)); + \\ defer gpa.free(source); + \\ + \\ return Yaml.load(gpa, source); + \\} + \\ +; + +pub const GenerateStep = struct { + step: Step, + builder: *Builder, + + output_file: std.build.GeneratedFile, + + /// Initialize a Vulkan generation step, for `builder`. `spec_path` is the path to + /// vk.xml, relative to the project root. The generated bindings will be placed at + /// `out_path`, which is relative to the zig-cache directory. + pub fn init(builder: *Builder, out_path: []const u8) *GenerateStep { + const self = builder.allocator.create(GenerateStep) catch unreachable; + const full_out_path = path.join(builder.allocator, &[_][]const u8{ + builder.build_root, + builder.cache_root, + out_path, + }) catch unreachable; + + self.* = .{ + .step = Step.init(.custom, "yaml-test-generate", builder.allocator, make), + .builder = builder, + .output_file = .{ + .step = &self.step, + .path = full_out_path, + }, + }; + return self; + } + + /// Internal build function. This reads `vk.xml`, and passes it to `generate`, which then generates + /// the final bindings. The resulting generated bindings are not formatted, which is why an ArrayList + /// writer is passed instead of a file writer. This is then formatted into standard formatting + /// by parsing it and rendering with `std.zig.parse` and `std.zig.render` respectively. + fn make(step: *Step) !void { + const self = @fieldParentPtr(GenerateStep, "step", step); + const cwd = std.fs.cwd(); + + var out_buffer = std.ArrayList(u8).init(self.builder.allocator); + + const writer = out_buffer.writer(); + + try writer.writeAll(preamble); + + + //read the tags, follow the links, generate the tests + const root_data_dir = path.join(self.builder.allocator, &[_][]const u8{ + self.builder.build_root, + "data", + }) catch unreachable; + + const tagdir = try std.fs.openDirAbsolute(root_data_dir, .{}); + + var itdir = try tagdir.openIterableDir("tags",.{}); + + var walker = try itdir.walk(self.builder.allocator); + defer walker.deinit(); + loop: { + while (walker.next()) |entry| { + if (entry) |e| { + try emitTestForTag(self.builder.allocator, writer, e.path, e); + } else { + break :loop; + } + } else |err| { + std.debug.print("err: {}", .{err}); + break :loop; + } + } + + try out_buffer.append(0); + const src = out_buffer.items[0 .. out_buffer.items.len - 1 :0]; + const dir = path.dirname(self.output_file.path.?).?; + try cwd.makePath(dir); + try cwd.writeFile(self.output_file.path.?, src); + } + + fn emitTestForTag(allocator: Allocator, writer: anytype, name: []const u8, dir: std.fs.IterableDir.Walker.WalkerEntry) !void { + try writer.writeAll("test \""); + try writer.writeAll(name); + try writer.writeAll("\" {\n"); + _ = dir; + _ = allocator; + try writer.writeAll(" try testing.expect(true);\n"); + try writer.writeAll("}\n\n"); + } +}; From 197656a9366b0b11755d365ee9ead8bb8c590f39 Mon Sep 17 00:00:00 2001 From: Brendan Dennis Date: Mon, 30 Jan 2023 00:57:03 +1030 Subject: [PATCH 03/19] Parser no longer crashes and can load all files from the test suite --- build.zig | 44 +++++++++++++++++++++++++++++++++++++++++--- src/parse.zig | 26 ++++++++++++++++---------- 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/build.zig b/build.zig index 9bc8bb9..5239739 100644 --- a/build.zig +++ b/build.zig @@ -46,6 +46,7 @@ pub fn build(b: *std.build.Builder) void { const gen = GenerateStep.init(b,"yamlTest.zig"); test_step.dependOn(&gen.step); var full_yaml_tests = b.addTest("zig-cache/yamlTest.zig"); + full_yaml_tests.addPackagePath("yaml", "src/yaml.zig"); test_step.dependOn(&full_yaml_tests.step); } else |_| { std.debug.print("No 'data' directory with YAML tests provided\n",.{}); @@ -161,9 +162,46 @@ pub const GenerateStep = struct { try writer.writeAll("test \""); try writer.writeAll(name); try writer.writeAll("\" {\n"); - _ = dir; - _ = allocator; - try writer.writeAll(" try testing.expect(true);\n"); + + const error_file_path = path.join(allocator, &[_][]const u8{ + "data/tags", + dir.path, + "error", + }) catch unreachable; + + const cwd = std.fs.cwd(); + var has_error_file: bool = undefined; + if(cwd.access(error_file_path,.{})) { + has_error_file = true; + } else |_| { + has_error_file = false; + } + + + const input_file_path = path.join(allocator, &[_][]const u8{ + "data/tags", + dir.path, + "in.yaml", + }) catch unreachable; + + try writer.writeAll("if(loadFromFile(\""); + try writer.writeAll(input_file_path); + try writer.writeAll("\")) |yaml_const| {\n"); + try writer.writeAll(" var yaml = yaml_const;\n"); + try writer.writeAll(" yaml.deinit();\n"); + try writer.writeAll(" try testing.expect(true);\n"); + try writer.writeAll("} else |_| {\n"); + + //check if we were expecting a problem or not + if(has_error_file) { + try writer.writeAll(" try testing.expect(true);\n"); + } else { + try writer.writeAll(" try testing.expect(false);\n"); + } + + try writer.writeAll("}\n"); + + try writer.writeAll("}\n\n"); } }; diff --git a/src/parse.zig b/src/parse.zig index 3804163..f122d84 100644 --- a/src/parse.zig +++ b/src/parse.zig @@ -372,7 +372,7 @@ const Parser = struct { // Parse header const explicit_doc: bool = if (self.eatToken(.doc_start, &.{})) |doc_pos| explicit_doc: { - if (self.getCol(doc_pos) > 0) return error.MalformedYaml; + if (try self.getCol(doc_pos) > 0) return error.MalformedYaml; if (self.eatToken(.tag, &.{ .new_line, .comment })) |_| { node.directive = try self.expectToken(.literal, &.{ .new_line, .comment }); } @@ -393,13 +393,13 @@ const Parser = struct { footer: { if (self.eatToken(.doc_end, &.{})) |pos| { if (!explicit_doc) return error.UnexpectedToken; - if (self.getCol(pos) > 0) return error.MalformedYaml; + if (try self.getCol(pos) > 0) return error.MalformedYaml; node.base.end = pos; break :footer; } if (self.eatToken(.doc_start, &.{})) |pos| { if (!explicit_doc) return error.UnexpectedToken; - if (self.getCol(pos) > 0) return error.MalformedYaml; + if (try self.getCol(pos) > 0) return error.MalformedYaml; self.token_it.seekBy(-1); node.base.end = pos - 1; break :footer; @@ -434,14 +434,14 @@ const Parser = struct { log.debug("(map) begin {s}@{d}", .{ @tagName(self.tree.tokens[node.base.start].id), node.base.start }); - const col = self.getCol(node.base.start); + const col = try self.getCol(node.base.start); while (true) { self.eatCommentsAndSpace(&.{}); // Parse key const key_pos = self.token_it.pos; - if (self.getCol(key_pos) < col) { + if (try self.getCol(key_pos) < col) { break; } @@ -471,11 +471,11 @@ const Parser = struct { }; if (val) |v| { - if (self.getCol(v.start) < self.getCol(key_pos)) { + if (try self.getCol(v.start) < try self.getCol(key_pos)) { return error.MalformedYaml; } if (v.cast(Node.Value)) |_| { - if (self.getCol(v.start) == self.getCol(key_pos)) { + if (try self.getCol(v.start) == try self.getCol(key_pos)) { return error.MalformedYaml; } } @@ -658,8 +658,12 @@ const Parser = struct { return self.line_cols.get(index).?.line; } - fn getCol(self: *Parser, index: TokenIndex) usize { - return self.line_cols.get(index).?.col; + fn getCol(self: *Parser, index: TokenIndex) ParseError!usize { + if(self.line_cols.get(index)) |index_actual| { + return index_actual.col; + } else { + return ParseError.UnexpectedEof; + } } fn parseSingleQuoted(self: *Parser, node: *Node.Value, raw: []const u8) ParseError!void { @@ -697,7 +701,9 @@ const Parser = struct { } fn parseDoubleQuoted(self: *Parser, node: *Node.Value, raw: []const u8) ParseError!void { - assert(raw[0] == '"' and raw[raw.len - 1] == '"'); + if((raw[0] == '"' and raw[raw.len - 1] == '"') == false) { + return ParseError.Unhandled; + } const raw_no_quotes = raw[1 .. raw.len - 1]; try node.string_value.ensureTotalCapacity(self.allocator, raw_no_quotes.len); From 4868608c4a24ac9f89873765530b05c77a91b6f8 Mon Sep 17 00:00:00 2001 From: Brendan Dennis Date: Mon, 30 Jan 2023 01:02:00 +1030 Subject: [PATCH 04/19] Updated build.zig to search in the test folder for the 'data' folder which contains the tests --- build.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build.zig b/build.zig index 5239739..ee3ca09 100644 --- a/build.zig +++ b/build.zig @@ -40,7 +40,7 @@ pub fn build(b: *std.build.Builder) void { test_step.dependOn(&e2e_tests.step); const cwd = std.fs.cwd(); - if(cwd.access("data",.{})) { + if(cwd.access("test/data",.{})) { std.debug.print("Found 'data' directory with YAML tests. Attempting to generate test cases\n",.{}); const gen = GenerateStep.init(b,"yamlTest.zig"); @@ -129,7 +129,7 @@ pub const GenerateStep = struct { //read the tags, follow the links, generate the tests const root_data_dir = path.join(self.builder.allocator, &[_][]const u8{ self.builder.build_root, - "data", + "test/data", }) catch unreachable; const tagdir = try std.fs.openDirAbsolute(root_data_dir, .{}); @@ -164,7 +164,7 @@ pub const GenerateStep = struct { try writer.writeAll("\" {\n"); const error_file_path = path.join(allocator, &[_][]const u8{ - "data/tags", + "test/data/tags", dir.path, "error", }) catch unreachable; @@ -179,7 +179,7 @@ pub const GenerateStep = struct { const input_file_path = path.join(allocator, &[_][]const u8{ - "data/tags", + "test/data/tags", dir.path, "in.yaml", }) catch unreachable; From 089d66fa9cc54595da917e0ae862f696b1dad02f Mon Sep 17 00:00:00 2001 From: Brendan Dennis Date: Mon, 30 Jan 2023 01:17:30 +1030 Subject: [PATCH 05/19] Updated some formatting and ensured reverse expect is set for error present case --- build.zig | 45 ++++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/build.zig b/build.zig index ee3ca09..c074a22 100644 --- a/build.zig +++ b/build.zig @@ -141,7 +141,7 @@ pub const GenerateStep = struct { loop: { while (walker.next()) |entry| { if (entry) |e| { - try emitTestForTag(self.builder.allocator, writer, e.path, e); + if(emitTestForTag(self.builder.allocator, writer, e.path, e)) |_| {} else |_| {} } else { break :loop; } @@ -159,9 +159,6 @@ pub const GenerateStep = struct { } fn emitTestForTag(allocator: Allocator, writer: anytype, name: []const u8, dir: std.fs.IterableDir.Walker.WalkerEntry) !void { - try writer.writeAll("test \""); - try writer.writeAll(name); - try writer.writeAll("\" {\n"); const error_file_path = path.join(allocator, &[_][]const u8{ "test/data/tags", @@ -184,22 +181,36 @@ pub const GenerateStep = struct { "in.yaml", }) catch unreachable; - try writer.writeAll("if(loadFromFile(\""); + try cwd.access(input_file_path,.{}); + + try writer.writeAll("test \""); + try writer.writeAll(name); + try writer.writeAll("\" {\n"); + + + try writer.writeAll(" if(loadFromFile(\""); try writer.writeAll(input_file_path); try writer.writeAll("\")) |yaml_const| {\n"); - try writer.writeAll(" var yaml = yaml_const;\n"); - try writer.writeAll(" yaml.deinit();\n"); - try writer.writeAll(" try testing.expect(true);\n"); - try writer.writeAll("} else |_| {\n"); - - //check if we were expecting a problem or not - if(has_error_file) { - try writer.writeAll(" try testing.expect(true);\n"); - } else { - try writer.writeAll(" try testing.expect(false);\n"); - } + try writer.writeAll(" var yaml = yaml_const;\n"); + try writer.writeAll(" yaml.deinit();\n"); + + //check if we were expecting a problem or not + if(has_error_file) { + try writer.writeAll(" try testing.expect(false);\n"); + } else { + try writer.writeAll(" try testing.expect(true);\n"); + } + + try writer.writeAll(" } else |_| {\n"); + + //check if we were expecting a problem or not + if(has_error_file) { + try writer.writeAll(" try testing.expect(true);\n"); + } else { + try writer.writeAll(" try testing.expect(false);\n"); + } - try writer.writeAll("}\n"); + try writer.writeAll(" }\n"); try writer.writeAll("}\n\n"); From aabd21bdf56f50a744cc3403479c559160c62eb9 Mon Sep 17 00:00:00 2001 From: Brendan Dennis Date: Mon, 30 Jan 2023 01:19:20 +1030 Subject: [PATCH 06/19] Removed headers from functions hacked in from vk.zig generator --- build.zig | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/build.zig b/build.zig index c074a22..3ca092b 100644 --- a/build.zig +++ b/build.zig @@ -89,9 +89,7 @@ pub const GenerateStep = struct { output_file: std.build.GeneratedFile, - /// Initialize a Vulkan generation step, for `builder`. `spec_path` is the path to - /// vk.xml, relative to the project root. The generated bindings will be placed at - /// `out_path`, which is relative to the zig-cache directory. + /// Create the builder, which will generate the YAML test file for us pub fn init(builder: *Builder, out_path: []const u8) *GenerateStep { const self = builder.allocator.create(GenerateStep) catch unreachable; const full_out_path = path.join(builder.allocator, &[_][]const u8{ @@ -111,10 +109,7 @@ pub const GenerateStep = struct { return self; } - /// Internal build function. This reads `vk.xml`, and passes it to `generate`, which then generates - /// the final bindings. The resulting generated bindings are not formatted, which is why an ArrayList - /// writer is passed instead of a file writer. This is then formatted into standard formatting - /// by parsing it and rendering with `std.zig.parse` and `std.zig.render` respectively. + /// Walk the 'data' dir, follow the symlinks, emit the file into the cache fn make(step: *Step) !void { const self = @fieldParentPtr(GenerateStep, "step", step); const cwd = std.fs.cwd(); From a3457c04915ff72cf827ceafd8220492179ee936 Mon Sep 17 00:00:00 2001 From: Brendan Dennis Date: Wed, 1 Feb 2023 16:48:56 +1030 Subject: [PATCH 07/19] Moved the test generator code into test/generator.zig Updated the tests to the improved format as suggested. --- build.zig | 159 +-------------------------------------- test/generator.zig | 180 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+), 158 deletions(-) create mode 100644 test/generator.zig diff --git a/build.zig b/build.zig index 3ca092b..e37d3e6 100644 --- a/build.zig +++ b/build.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const GenerateStep = @import("test/generator.zig").GenerateStep; pub fn build(b: *std.build.Builder) void { const mode = b.standardReleaseOptions(); @@ -52,162 +53,4 @@ pub fn build(b: *std.build.Builder) void { std.debug.print("No 'data' directory with YAML tests provided\n",.{}); } } - -const path = std.fs.path; -const Builder = std.build.Builder; -const Step = std.build.Step; -const Allocator = std.mem.Allocator; - -const preamble = - \\// This file is generated from the YAML 1.2 test database. - \\ - \\const std = @import("std"); - \\const mem = std.mem; - \\const testing = std.testing; - \\ - \\const Allocator = mem.Allocator; - \\const Yaml = @import("yaml").Yaml; - \\ - \\const gpa = testing.allocator; - \\ - \\fn loadFromFile(file_path: []const u8) !Yaml { - \\ const file = try std.fs.cwd().openFile(file_path, .{}); - \\ defer file.close(); - \\ - \\ const source = try file.readToEndAlloc(gpa, std.math.maxInt(u32)); - \\ defer gpa.free(source); - \\ - \\ return Yaml.load(gpa, source); - \\} - \\ -; - -pub const GenerateStep = struct { - step: Step, - builder: *Builder, - - output_file: std.build.GeneratedFile, - - /// Create the builder, which will generate the YAML test file for us - pub fn init(builder: *Builder, out_path: []const u8) *GenerateStep { - const self = builder.allocator.create(GenerateStep) catch unreachable; - const full_out_path = path.join(builder.allocator, &[_][]const u8{ - builder.build_root, - builder.cache_root, - out_path, - }) catch unreachable; - - self.* = .{ - .step = Step.init(.custom, "yaml-test-generate", builder.allocator, make), - .builder = builder, - .output_file = .{ - .step = &self.step, - .path = full_out_path, - }, - }; - return self; - } - - /// Walk the 'data' dir, follow the symlinks, emit the file into the cache - fn make(step: *Step) !void { - const self = @fieldParentPtr(GenerateStep, "step", step); - const cwd = std.fs.cwd(); - - var out_buffer = std.ArrayList(u8).init(self.builder.allocator); - - const writer = out_buffer.writer(); - - try writer.writeAll(preamble); - - - //read the tags, follow the links, generate the tests - const root_data_dir = path.join(self.builder.allocator, &[_][]const u8{ - self.builder.build_root, - "test/data", - }) catch unreachable; - - const tagdir = try std.fs.openDirAbsolute(root_data_dir, .{}); - - var itdir = try tagdir.openIterableDir("tags",.{}); - - var walker = try itdir.walk(self.builder.allocator); - defer walker.deinit(); - loop: { - while (walker.next()) |entry| { - if (entry) |e| { - if(emitTestForTag(self.builder.allocator, writer, e.path, e)) |_| {} else |_| {} - } else { - break :loop; - } - } else |err| { - std.debug.print("err: {}", .{err}); - break :loop; - } - } - - try out_buffer.append(0); - const src = out_buffer.items[0 .. out_buffer.items.len - 1 :0]; - const dir = path.dirname(self.output_file.path.?).?; - try cwd.makePath(dir); - try cwd.writeFile(self.output_file.path.?, src); - } - - fn emitTestForTag(allocator: Allocator, writer: anytype, name: []const u8, dir: std.fs.IterableDir.Walker.WalkerEntry) !void { - - const error_file_path = path.join(allocator, &[_][]const u8{ - "test/data/tags", - dir.path, - "error", - }) catch unreachable; - - const cwd = std.fs.cwd(); - var has_error_file: bool = undefined; - if(cwd.access(error_file_path,.{})) { - has_error_file = true; - } else |_| { - has_error_file = false; - } - - - const input_file_path = path.join(allocator, &[_][]const u8{ - "test/data/tags", - dir.path, - "in.yaml", - }) catch unreachable; - - try cwd.access(input_file_path,.{}); - - try writer.writeAll("test \""); - try writer.writeAll(name); - try writer.writeAll("\" {\n"); - - - try writer.writeAll(" if(loadFromFile(\""); - try writer.writeAll(input_file_path); - try writer.writeAll("\")) |yaml_const| {\n"); - try writer.writeAll(" var yaml = yaml_const;\n"); - try writer.writeAll(" yaml.deinit();\n"); - - //check if we were expecting a problem or not - if(has_error_file) { - try writer.writeAll(" try testing.expect(false);\n"); - } else { - try writer.writeAll(" try testing.expect(true);\n"); - } - - try writer.writeAll(" } else |_| {\n"); - - //check if we were expecting a problem or not - if(has_error_file) { - try writer.writeAll(" try testing.expect(true);\n"); - } else { - try writer.writeAll(" try testing.expect(false);\n"); - } - - try writer.writeAll(" }\n"); - - - try writer.writeAll("}\n\n"); - } -}; diff --git a/test/generator.zig b/test/generator.zig new file mode 100644 index 0000000..cba5451 --- /dev/null +++ b/test/generator.zig @@ -0,0 +1,180 @@ + +const std = @import("std"); +const path = std.fs.path; +const Builder = std.build.Builder; +const Step = std.build.Step; +const Allocator = std.mem.Allocator; + +const preamble = + \\// This file is generated from the YAML 1.2 test database. + \\ + \\const std = @import("std"); + \\const mem = std.mem; + \\const testing = std.testing; + \\ + \\const Allocator = mem.Allocator; + \\const Yaml = @import("yaml").Yaml; + \\ + \\const gpa = testing.allocator; + \\ + \\fn loadFromFile(file_path: []const u8) !Yaml { + \\ const file = try std.fs.cwd().openFile(file_path, .{}); + \\ defer file.close(); + \\ + \\ const source = try file.readToEndAlloc(gpa, std.math.maxInt(u32)); + \\ defer gpa.free(source); + \\ + \\ return Yaml.load(gpa, source); + \\} + \\ +; + +pub const GenerateStep = struct { + step: Step, + builder: *Builder, + + output_file: std.build.GeneratedFile, + + /// Create the builder, which will generate the YAML test file for us + pub fn init(builder: *Builder, out_path: []const u8) *GenerateStep { + const self = builder.allocator.create(GenerateStep) catch unreachable; + const full_out_path = path.join(builder.allocator, &[_][]const u8{ + builder.build_root, + builder.cache_root, + out_path, + }) catch unreachable; + + self.* = .{ + .step = Step.init(.custom, "yaml-test-generate", builder.allocator, make), + .builder = builder, + .output_file = .{ + .step = &self.step, + .path = full_out_path, + }, + }; + return self; + } + + + /// Walk the 'data' dir, follow the symlinks, emit the file into the cache + fn make(step: *Step) !void { + const self = @fieldParentPtr(GenerateStep, "step", step); + const cwd = std.fs.cwd(); + + var out_buffer = std.ArrayList(u8).init(self.builder.allocator); + + const writer = out_buffer.writer(); + + try writer.writeAll(preamble); + + + //read the tags, follow the links, generate the tests + const root_data_dir = path.join(self.builder.allocator, &[_][]const u8{ + self.builder.build_root, + "test/data", + }) catch unreachable; + + const tagdir = try std.fs.openDirAbsolute(root_data_dir, .{}); + + var itdir = try tagdir.openIterableDir("tags",.{}); + + var walker = try itdir.walk(self.builder.allocator); + defer walker.deinit(); + loop: { + while (walker.next()) |entry| { + if (entry) |e| { + if(emitTestForTag(self.builder.allocator, writer, e.path, e)) |_| {} else |_| {} + } else { + break :loop; + } + } else |err| { + std.debug.print("err: {}", .{err}); + break :loop; + } + } + + try out_buffer.append(0); + const src = out_buffer.items[0 .. out_buffer.items.len - 1 :0]; + const dir = path.dirname(self.output_file.path.?).?; + try cwd.makePath(dir); + try cwd.writeFile(self.output_file.path.?, src); + } + + fn canAccess(file_path: []const u8) bool { + const cwd = std.fs.cwd(); + if(cwd.access(file_path,.{})) { + return true; + } else |_| { + return false; + } + } + + fn emitTestForTag(allocator: Allocator, writer: anytype, name: []const u8, dir: std.fs.IterableDir.Walker.WalkerEntry) !void { + + const error_file_path = path.join(allocator, &[_][]const u8{ + "test/data/tags", + dir.path, + "error", + }) catch unreachable; + + const has_error_file: bool = canAccess(error_file_path); + + const input_file_path = path.join(allocator, &[_][]const u8{ + "test/data/tags", + dir.path, + "in.yaml", + }) catch unreachable; + + //if we cannot acces the input file here, we may as well bail + //possibly the directory structure changed, submit bug report? + const cwd = std.fs.cwd(); + try cwd.access(input_file_path,.{}); + + //we have access to the input file at the path specified, + //we have also determined if we expect an error or not + //we can now emit the basic test case + + try emitFunctionStart(writer, name); + + //the presence of an error file means our parser/tokeniser SHOULD get an error + if(has_error_file) { + try emitErrorIsSuccessCase(writer,input_file_path); + } + //otherwise we expect the parsing to succeed correctly + else { + try emitErrorIsFailureCase(writer,input_file_path); + } + + try emitFunctionFinish(writer); + + } + + fn emitFunctionStart(writer: anytype, name: []const u8) !void { + try writer.writeAll("test \""); + try writer.writeAll(name); + try writer.writeAll("\" {\n"); + } + + fn emitFunctionFinish(writer: anytype) !void { + try writer.writeAll("}\n\n"); + } + + fn emitErrorIsSuccessCase(writer: anytype, name: []const u8) !void { + //Write: var yaml = loadFromFile("PATH/TO/FILE/in.yaml") catch return; + try writer.writeAll("var yaml = loadFromFile(\""); + try writer.writeAll(name); + try writer.writeAll("\") catch return;\n"); + //write rest of function + try writer.writeAll("defer yaml.deinit();\n"); + try writer.writeAll("return error.UnexpectedSuccess;\n"); + } + + fn emitErrorIsFailureCase(writer: anytype, name: []const u8) !void { + //Write: var yaml = loadFromFile("PATH/TO/FILE/in.yaml") catch return error.Failed; + try writer.writeAll("var yaml = loadFromFile(\""); + try writer.writeAll(name); + try writer.writeAll("\") catch return;\n"); + //write rest of function + try writer.writeAll("defer yaml.deinit();\n"); + } +}; From bbfc09e91cb8515af2957ef7354864cbdbb70ee8 Mon Sep 17 00:00:00 2001 From: Brendan Dennis Date: Wed, 1 Feb 2023 16:57:52 +1030 Subject: [PATCH 08/19] Added whitespace indentation to the test functions Updated Readme to include information about tests Fixed missing error return for cases that we expect success on --- README.md | 28 ++++++++++++++++++++++++++++ test/generator.zig | 13 +++++++------ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b54f5ac..f0eb836 100644 --- a/README.md +++ b/README.md @@ -88,3 +88,31 @@ nested: wick: john doe finally: [ 8.17, 19.78, 17, 21 ] ``` + +## Testing against YAML 1.2 compatibility tests + +To test against the YAML 1.2 compatibility tests, first obtain the tests from https://github.com/yaml/yaml-test-suite. +Follow the instructions to obtain the `data` directory which contains all of the test cases. +Copy this directory into the `test` folder where `generator.zig` is located. +Running `zig build test` will now generate test cases for the YAML 1.2 compatibility tests. They can be found in the zig-cache in a file named `yamlTest.zig`. + +The test cases have the following format. + +For a test that should parse correctly. + +``` +test "indent/SKE5" { + var yaml = loadFromFile("test/data/tags/indent/SKE5/in.yaml") catch return error.Failed; + defer yaml.deinit(); +} +``` + +For a test that should fail to parse. + +``` +test "indent/EW3V" { + var yaml = loadFromFile("test/data/tags/indent/EW3V/in.yaml") catch return; + defer yaml.deinit(); + return error.UnexpectedSuccess; +} +``` diff --git a/test/generator.zig b/test/generator.zig index cba5451..df5e468 100644 --- a/test/generator.zig +++ b/test/generator.zig @@ -27,6 +27,7 @@ const preamble = \\ return Yaml.load(gpa, source); \\} \\ + \\ ; pub const GenerateStep = struct { @@ -161,20 +162,20 @@ pub const GenerateStep = struct { fn emitErrorIsSuccessCase(writer: anytype, name: []const u8) !void { //Write: var yaml = loadFromFile("PATH/TO/FILE/in.yaml") catch return; - try writer.writeAll("var yaml = loadFromFile(\""); + try writer.writeAll(" var yaml = loadFromFile(\""); try writer.writeAll(name); try writer.writeAll("\") catch return;\n"); //write rest of function - try writer.writeAll("defer yaml.deinit();\n"); - try writer.writeAll("return error.UnexpectedSuccess;\n"); + try writer.writeAll(" defer yaml.deinit();\n"); + try writer.writeAll(" return error.UnexpectedSuccess;\n"); } fn emitErrorIsFailureCase(writer: anytype, name: []const u8) !void { //Write: var yaml = loadFromFile("PATH/TO/FILE/in.yaml") catch return error.Failed; - try writer.writeAll("var yaml = loadFromFile(\""); + try writer.writeAll(" var yaml = loadFromFile(\""); try writer.writeAll(name); - try writer.writeAll("\") catch return;\n"); + try writer.writeAll("\") catch return error.Failed;\n"); //write rest of function - try writer.writeAll("defer yaml.deinit();\n"); + try writer.writeAll(" defer yaml.deinit();\n"); } }; From 944cdca4ad16927a67225ccbba04de2aaf265a35 Mon Sep 17 00:00:00 2001 From: Brendan Dennis Date: Wed, 1 Feb 2023 17:14:20 +1030 Subject: [PATCH 09/19] Updated some comments in the generator --- test/generator.zig | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/generator.zig b/test/generator.zig index df5e468..a97ddb3 100644 --- a/test/generator.zig +++ b/test/generator.zig @@ -63,27 +63,30 @@ pub const GenerateStep = struct { const cwd = std.fs.cwd(); var out_buffer = std.ArrayList(u8).init(self.builder.allocator); - const writer = out_buffer.writer(); - + try writer.writeAll(preamble); - //read the tags, follow the links, generate the tests const root_data_dir = path.join(self.builder.allocator, &[_][]const u8{ self.builder.build_root, "test/data", }) catch unreachable; - + + //open the root directory, which 'should' contain the tags folder. const tagdir = try std.fs.openDirAbsolute(root_data_dir, .{}); + //then open the tags subdirectory for iterating var itdir = try tagdir.openIterableDir("tags",.{}); + //we now want to walk the directory, including the symlinked folders, there should be no loops + //unsure how the walker might handle loops.. var walker = try itdir.walk(self.builder.allocator); defer walker.deinit(); loop: { while (walker.next()) |entry| { if (entry) |e| { + //for any valid entry, we can emit a test, if(emitTestForTag(self.builder.allocator, writer, e.path, e)) |_| {} else |_| {} } else { break :loop; @@ -94,6 +97,7 @@ pub const GenerateStep = struct { } } + //our buffer now has all the tests, we can dump it out to the file try out_buffer.append(0); const src = out_buffer.items[0 .. out_buffer.items.len - 1 :0]; const dir = path.dirname(self.output_file.path.?).?; @@ -101,6 +105,8 @@ pub const GenerateStep = struct { try cwd.writeFile(self.output_file.path.?, src); } + //access returns an error or is void, this will make it a bool + //behaviour of some of the tests is determined by the presence (as opposed to contents) of a file fn canAccess(file_path: []const u8) bool { const cwd = std.fs.cwd(); if(cwd.access(file_path,.{})) { From d46d9b4b374b59932bba84ab244ca9758a89b274 Mon Sep 17 00:00:00 2001 From: Brendan Dennis Date: Wed, 1 Feb 2023 17:28:14 +1030 Subject: [PATCH 10/19] Updated formatting to include the 'test purpose' string loaded from '===' file as a comment header. This will also be useful in future failure messages. --- test/generator.zig | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/test/generator.zig b/test/generator.zig index a97ddb3..88e65dc 100644 --- a/test/generator.zig +++ b/test/generator.zig @@ -136,12 +136,24 @@ pub const GenerateStep = struct { //possibly the directory structure changed, submit bug report? const cwd = std.fs.cwd(); try cwd.access(input_file_path,.{}); - + + //load the header file that contains test information + const header_file_path = path.join(allocator, &[_][]const u8{ + "test/data/tags", + dir.path, + "===" + }) catch unreachable; + + const file = try std.fs.cwd().openFile(header_file_path, .{}); + defer file.close(); + const header_source = try file.readToEndAlloc(allocator, std.math.maxInt(u32)); + defer allocator.free(header_source); + //we have access to the input file at the path specified, //we have also determined if we expect an error or not //we can now emit the basic test case - try emitFunctionStart(writer, name); + try emitFunctionStart(writer, name, header_source); //the presence of an error file means our parser/tokeniser SHOULD get an error if(has_error_file) { @@ -156,14 +168,17 @@ pub const GenerateStep = struct { } - fn emitFunctionStart(writer: anytype, name: []const u8) !void { + fn emitFunctionStart(writer: anytype, name: []const u8, details: []const u8) !void { + try writer.writeAll("//"); + try writer.writeAll(details); + try writer.writeAll("\n"); try writer.writeAll("test \""); try writer.writeAll(name); try writer.writeAll("\" {\n"); } fn emitFunctionFinish(writer: anytype) !void { - try writer.writeAll("}\n\n"); + try writer.writeAll("}\n\n\n"); } fn emitErrorIsSuccessCase(writer: anytype, name: []const u8) !void { From 99b4f567ca53452c45541203280a0e74c2fe639b Mon Sep 17 00:00:00 2001 From: Brendan Dennis Date: Wed, 1 Feb 2023 19:12:54 +1030 Subject: [PATCH 11/19] Added an option -DspecificYAML which accepts a list of tests to allow generating only specific tests to run against, which should help reduce the amount of excess messages when running the test cases. --- build.zig | 7 ++++++- test/generator.zig | 38 +++++++++++++++++++++++++++++++++----- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/build.zig b/build.zig index e37d3e6..c25ceea 100644 --- a/build.zig +++ b/build.zig @@ -5,6 +5,9 @@ pub fn build(b: *std.build.Builder) void { const mode = b.standardReleaseOptions(); const enable_logging = b.option(bool, "log", "Whether to enable logging") orelse false; + const create_only_yaml_tests = b.option([]const []const u8, "specificYAML", "generate only YAML tests matching this eg -DspecificYAML{\"comment/6HB6\",\"EW3V\"}") orelse &[_][]const u8{}; + + const enable_silent_yaml = b.option(bool, "silentYAML", "all YAML tests will pass, failures will be logged cleanly") orelse false; const lib = b.addStaticLibrary("yaml", "src/yaml.zig"); lib.setBuildMode(mode); @@ -20,6 +23,8 @@ pub fn build(b: *std.build.Builder) void { const example_opts = b.addOptions(); example.addOptions("build_options", example_opts); example_opts.addOption(bool, "enable_logging", enable_logging); + example_opts.addOption(bool, "enable_silent_yaml", enable_silent_yaml); + example_opts.addOption([]const []const u8, "gen_tests_only", create_only_yaml_tests); example.install(); @@ -44,7 +49,7 @@ pub fn build(b: *std.build.Builder) void { if(cwd.access("test/data",.{})) { std.debug.print("Found 'data' directory with YAML tests. Attempting to generate test cases\n",.{}); - const gen = GenerateStep.init(b,"yamlTest.zig"); + const gen = GenerateStep.init(b,"yamlTest.zig",create_only_yaml_tests,enable_silent_yaml); test_step.dependOn(&gen.step); var full_yaml_tests = b.addTest("zig-cache/yamlTest.zig"); full_yaml_tests.addPackagePath("yaml", "src/yaml.zig"); diff --git a/test/generator.zig b/test/generator.zig index 88e65dc..06f6990 100644 --- a/test/generator.zig +++ b/test/generator.zig @@ -33,11 +33,13 @@ const preamble = pub const GenerateStep = struct { step: Step, builder: *Builder, + build_for_only: []const []const u8, + silent_mode: bool, output_file: std.build.GeneratedFile, /// Create the builder, which will generate the YAML test file for us - pub fn init(builder: *Builder, out_path: []const u8) *GenerateStep { + pub fn init(builder: *Builder, out_path: []const u8, build_only: []const []const u8, silent_mode: bool) *GenerateStep { const self = builder.allocator.create(GenerateStep) catch unreachable; const full_out_path = path.join(builder.allocator, &[_][]const u8{ builder.build_root, @@ -48,6 +50,8 @@ pub const GenerateStep = struct { self.* = .{ .step = Step.init(.custom, "yaml-test-generate", builder.allocator, make), .builder = builder, + .build_for_only = build_only, + .silent_mode = silent_mode, .output_file = .{ .step = &self.step, .path = full_out_path, @@ -86,8 +90,30 @@ pub const GenerateStep = struct { loop: { while (walker.next()) |entry| { if (entry) |e| { - //for any valid entry, we can emit a test, - if(emitTestForTag(self.builder.allocator, writer, e.path, e)) |_| {} else |_| {} + //check if we are omitting tests. + var emit_tests: bool = true; + //if we have specified some items in the 'build for only' array + if (self.build_for_only.len > 0) { + emit_tests = false; + //then we need to iterate them + for (self.build_for_only) |permitted_test| { + //check if we can needle/haystack without crashing + //std probably needs this check and should return false if the preconditions are not met? + //maybe this is done already in a wrapper function + if(e.path.len >= permitted_test.len) { + //check if we have a match and should emit + const index = std.mem.indexOfPosLinear(u8,e.path,0,permitted_test); + if(index != null) { + emit_tests = true; + } + } + } + } + + //for any valid entry, we can emit a test, + if(emit_tests) { + if(emitTestForTag(self.builder.allocator, writer, e.path, e,self.silent_mode)) |_| {} else |_| {} + } } else { break :loop; } @@ -116,8 +142,10 @@ pub const GenerateStep = struct { } } - fn emitTestForTag(allocator: Allocator, writer: anytype, name: []const u8, dir: std.fs.IterableDir.Walker.WalkerEntry) !void { - + fn emitTestForTag(allocator: Allocator, writer: anytype, name: []const u8, dir: std.fs.IterableDir.Walker.WalkerEntry, silent: bool) !void { + + _ = silent; + const error_file_path = path.join(allocator, &[_][]const u8{ "test/data/tags", dir.path, From 4e06b7707e7c574c5733352a2a1625047bf4fc14 Mon Sep 17 00:00:00 2001 From: Brendan Dennis Date: Wed, 1 Feb 2023 19:26:39 +1030 Subject: [PATCH 12/19] Updated Readme to include example of using the specificYAML command --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index f0eb836..ec661de 100644 --- a/README.md +++ b/README.md @@ -116,3 +116,20 @@ test "indent/EW3V" { return error.UnexpectedSuccess; } ``` + +running zig build test with the following option + +``` +-DspecificYAML="string" +-DspecificYAML={"stringOne","stringTwo"} +``` + +will generate only tests that match those strings. This can be used to specify the particular test or test groups you want to generate tests for. For example running +`-DspecificYAML={"comment/6HB6","5T43"}` will produce the following tests. + +``` +test "comment/6HB6" +test "mapping/5T43" +test "scalar/5T43" +test "flow/5T43" +``` From 08232c18c3a3517665fca992aa0641c7e2f72b53 Mon Sep 17 00:00:00 2001 From: Brendan Dennis Date: Wed, 1 Feb 2023 22:35:54 +1030 Subject: [PATCH 13/19] Added -DsilentYAML=true to silence failing calls in test cases, this effectively supresses the call stack report on error and just logs success/failure as a one liner. Allows for quicker review of pass/fail --- test/generator.zig | 110 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 84 insertions(+), 26 deletions(-) diff --git a/test/generator.zig b/test/generator.zig index 06f6990..1c4fce7 100644 --- a/test/generator.zig +++ b/test/generator.zig @@ -144,8 +144,6 @@ pub const GenerateStep = struct { fn emitTestForTag(allocator: Allocator, writer: anytype, name: []const u8, dir: std.fs.IterableDir.Walker.WalkerEntry, silent: bool) !void { - _ = silent; - const error_file_path = path.join(allocator, &[_][]const u8{ "test/data/tags", dir.path, @@ -183,19 +181,96 @@ pub const GenerateStep = struct { try emitFunctionStart(writer, name, header_source); - //the presence of an error file means our parser/tokeniser SHOULD get an error - if(has_error_file) { - try emitErrorIsSuccessCase(writer,input_file_path); - } - //otherwise we expect the parsing to succeed correctly - else { - try emitErrorIsFailureCase(writer,input_file_path); + //it means silent for Zig test. so the test will always pass, but we will log the information + //about the actual success or failure of the test, so we can determine what we need to do + //to get compliance. + if(silent) { + try emitDetailed(writer,input_file_path,has_error_file); + } else { + + //the presence of an error file means our parser/tokeniser SHOULD get an error + if(has_error_file) { + try emitErrorIsSuccessCase(writer,input_file_path); + } + //otherwise we expect the parsing to succeed correctly + else { + try emitErrorIsFailureCase(writer,input_file_path); + } } try emitFunctionFinish(writer); } +//constant text for the detailed load success/fail cases + +const verbose_loadfile = + \\ if(loadFromFile(" +; +const verbose_expect_error = + \\")) |it_parsed| { + \\ var yml = it_parsed; + \\ yml.deinit(); + \\ std.debug.print("fail\n",.{}); + \\ } else |_| { + \\ std.debug.print("success\n",.{}); + \\ } +; + +const verbose_expect_no_error = + \\")) |it_parsed| { + \\ var yml = it_parsed; + \\ yml.deinit(); + \\ std.debug.print("success\n",.{}); + \\ } else |_| { + \\ std.debug.print("fail\n",.{}); + \\ } +; + + + fn emitDetailed(writer: anytype, name: []const u8, has_error: bool) !void { + try writer.writeAll(verbose_loadfile); + try writer.writeAll(name); + if(has_error) { + try writer.writeAll(verbose_expect_error); + } else { + try writer.writeAll(verbose_expect_no_error); + } + } + +//constant text for the standard load success/fail cases + +const loadfile = + \\ var yaml = loadFromFile(" +; + +const endErrorSuccess = + \\") catch return; + \\ defer yaml.deinit(); + \\ return error.UnexpectedSuccess; + \\ +; + +const endErrorIsFailure = + \\") catch return error.Failed; + \\ defer yaml.deinit(); + \\ +; + + fn emitErrorIsSuccessCase(writer: anytype, name: []const u8) !void { + try writer.writeAll(loadfile); + try writer.writeAll(name); + try writer.writeAll(endErrorSuccess); + } + + fn emitErrorIsFailureCase(writer: anytype, name: []const u8) !void { + try writer.writeAll(loadfile); + try writer.writeAll(name); + try writer.writeAll(endErrorIsFailure); + } + + + //function start and finish fn emitFunctionStart(writer: anytype, name: []const u8, details: []const u8) !void { try writer.writeAll("//"); try writer.writeAll(details); @@ -209,22 +284,5 @@ pub const GenerateStep = struct { try writer.writeAll("}\n\n\n"); } - fn emitErrorIsSuccessCase(writer: anytype, name: []const u8) !void { - //Write: var yaml = loadFromFile("PATH/TO/FILE/in.yaml") catch return; - try writer.writeAll(" var yaml = loadFromFile(\""); - try writer.writeAll(name); - try writer.writeAll("\") catch return;\n"); - //write rest of function - try writer.writeAll(" defer yaml.deinit();\n"); - try writer.writeAll(" return error.UnexpectedSuccess;\n"); - } - fn emitErrorIsFailureCase(writer: anytype, name: []const u8) !void { - //Write: var yaml = loadFromFile("PATH/TO/FILE/in.yaml") catch return error.Failed; - try writer.writeAll(" var yaml = loadFromFile(\""); - try writer.writeAll(name); - try writer.writeAll("\") catch return error.Failed;\n"); - //write rest of function - try writer.writeAll(" defer yaml.deinit();\n"); - } }; From 7d4ffc301dd9d2d343836ae2c75fcb29962d2817 Mon Sep 17 00:00:00 2001 From: Brendan Dennis Date: Wed, 1 Feb 2023 22:57:12 +1030 Subject: [PATCH 14/19] Improved silent test function to report pass/fail with error, but as a single line --- test/generator.zig | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/generator.zig b/test/generator.zig index 1c4fce7..f3a7f78 100644 --- a/test/generator.zig +++ b/test/generator.zig @@ -205,6 +205,7 @@ pub const GenerateStep = struct { //constant text for the detailed load success/fail cases const verbose_loadfile = + \\ var failed: bool = false; \\ if(loadFromFile(" ; const verbose_expect_error = @@ -212,9 +213,14 @@ const verbose_expect_error = \\ var yml = it_parsed; \\ yml.deinit(); \\ std.debug.print("fail\n",.{}); + \\ failed = true; \\ } else |_| { \\ std.debug.print("success\n",.{}); \\ } + \\ + \\ if(failed) { + \\ return error.Failed; + \\ } ; const verbose_expect_no_error = @@ -224,6 +230,10 @@ const verbose_expect_no_error = \\ std.debug.print("success\n",.{}); \\ } else |_| { \\ std.debug.print("fail\n",.{}); + \\ failed = true; + \\ } + \\ if(failed) { + \\ return error.Failed; \\ } ; From 73a9c8ad72e54cf1c6c126c1644ec8db49ad8254 Mon Sep 17 00:00:00 2001 From: Brendan Dennis Date: Wed, 1 Feb 2023 22:59:12 +1030 Subject: [PATCH 15/19] Removed unneccesary log in failure case of silent test output --- test/generator.zig | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/generator.zig b/test/generator.zig index f3a7f78..9d4660e 100644 --- a/test/generator.zig +++ b/test/generator.zig @@ -212,7 +212,6 @@ const verbose_expect_error = \\")) |it_parsed| { \\ var yml = it_parsed; \\ yml.deinit(); - \\ std.debug.print("fail\n",.{}); \\ failed = true; \\ } else |_| { \\ std.debug.print("success\n",.{}); @@ -229,7 +228,6 @@ const verbose_expect_no_error = \\ yml.deinit(); \\ std.debug.print("success\n",.{}); \\ } else |_| { - \\ std.debug.print("fail\n",.{}); \\ failed = true; \\ } \\ if(failed) { From d917b725b254e401b31ce829d4acc56b71509fbd Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 4 Feb 2023 23:16:55 +0100 Subject: [PATCH 16/19] zig fmt --- src/parse.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parse.zig b/src/parse.zig index f122d84..4bc6eba 100644 --- a/src/parse.zig +++ b/src/parse.zig @@ -659,7 +659,7 @@ const Parser = struct { } fn getCol(self: *Parser, index: TokenIndex) ParseError!usize { - if(self.line_cols.get(index)) |index_actual| { + if (self.line_cols.get(index)) |index_actual| { return index_actual.col; } else { return ParseError.UnexpectedEof; @@ -701,7 +701,7 @@ const Parser = struct { } fn parseDoubleQuoted(self: *Parser, node: *Node.Value, raw: []const u8) ParseError!void { - if((raw[0] == '"' and raw[raw.len - 1] == '"') == false) { + if ((raw[0] == '"' and raw[raw.len - 1] == '"') == false) { return ParseError.Unhandled; } From d8fc8337e54ed25d4c634bd607597ff8d69702f4 Mon Sep 17 00:00:00 2001 From: Brendan Dennis Date: Sun, 5 Feb 2023 21:33:19 +1030 Subject: [PATCH 17/19] Updated the formatting in the generator.zig to canonical --- test/generator.zig | 166 +++++++++++++++++++++------------------------ 1 file changed, 77 insertions(+), 89 deletions(-) diff --git a/test/generator.zig b/test/generator.zig index 9d4660e..6405d7b 100644 --- a/test/generator.zig +++ b/test/generator.zig @@ -1,4 +1,3 @@ - const std = @import("std"); const path = std.fs.path; const Builder = std.build.Builder; @@ -29,7 +28,7 @@ const preamble = \\ \\ ; - + pub const GenerateStep = struct { step: Step, builder: *Builder, @@ -60,31 +59,30 @@ pub const GenerateStep = struct { return self; } - /// Walk the 'data' dir, follow the symlinks, emit the file into the cache fn make(step: *Step) !void { const self = @fieldParentPtr(GenerateStep, "step", step); const cwd = std.fs.cwd(); - + var out_buffer = std.ArrayList(u8).init(self.builder.allocator); const writer = out_buffer.writer(); - + try writer.writeAll(preamble); - + //read the tags, follow the links, generate the tests const root_data_dir = path.join(self.builder.allocator, &[_][]const u8{ self.builder.build_root, "test/data", }) catch unreachable; - + //open the root directory, which 'should' contain the tags folder. const tagdir = try std.fs.openDirAbsolute(root_data_dir, .{}); - + //then open the tags subdirectory for iterating - var itdir = try tagdir.openIterableDir("tags",.{}); - + var itdir = try tagdir.openIterableDir("tags", .{}); + //we now want to walk the directory, including the symlinked folders, there should be no loops - //unsure how the walker might handle loops.. + //unsure how the walker might handle loops.. var walker = try itdir.walk(self.builder.allocator); defer walker.deinit(); loop: { @@ -100,10 +98,10 @@ pub const GenerateStep = struct { //check if we can needle/haystack without crashing //std probably needs this check and should return false if the preconditions are not met? //maybe this is done already in a wrapper function - if(e.path.len >= permitted_test.len) { + if (e.path.len >= permitted_test.len) { //check if we have a match and should emit - const index = std.mem.indexOfPosLinear(u8,e.path,0,permitted_test); - if(index != null) { + const index = std.mem.indexOfPosLinear(u8, e.path, 0, permitted_test); + if (index != null) { emit_tests = true; } } @@ -111,18 +109,18 @@ pub const GenerateStep = struct { } //for any valid entry, we can emit a test, - if(emit_tests) { - if(emitTestForTag(self.builder.allocator, writer, e.path, e,self.silent_mode)) |_| {} else |_| {} + if (emit_tests) { + if (emitTestForTag(self.builder.allocator, writer, e.path, e, self.silent_mode)) |_| {} else |_| {} } } else { - break :loop; + break :loop; } } else |err| { std.debug.print("err: {}", .{err}); break :loop; } } - + //our buffer now has all the tests, we can dump it out to the file try out_buffer.append(0); const src = out_buffer.items[0 .. out_buffer.items.len - 1 :0]; @@ -135,40 +133,35 @@ pub const GenerateStep = struct { //behaviour of some of the tests is determined by the presence (as opposed to contents) of a file fn canAccess(file_path: []const u8) bool { const cwd = std.fs.cwd(); - if(cwd.access(file_path,.{})) { + if (cwd.access(file_path, .{})) { return true; } else |_| { return false; } } - + fn emitTestForTag(allocator: Allocator, writer: anytype, name: []const u8, dir: std.fs.IterableDir.Walker.WalkerEntry, silent: bool) !void { - const error_file_path = path.join(allocator, &[_][]const u8{ "test/data/tags", dir.path, "error", }) catch unreachable; - + const has_error_file: bool = canAccess(error_file_path); - + const input_file_path = path.join(allocator, &[_][]const u8{ "test/data/tags", dir.path, "in.yaml", }) catch unreachable; - + //if we cannot acces the input file here, we may as well bail - //possibly the directory structure changed, submit bug report? + //possibly the directory structure changed, submit bug report? const cwd = std.fs.cwd(); - try cwd.access(input_file_path,.{}); + try cwd.access(input_file_path, .{}); //load the header file that contains test information - const header_file_path = path.join(allocator, &[_][]const u8{ - "test/data/tags", - dir.path, - "===" - }) catch unreachable; + const header_file_path = path.join(allocator, &[_][]const u8{ "test/data/tags", dir.path, "===" }) catch unreachable; const file = try std.fs.cwd().openFile(header_file_path, .{}); defer file.close(); @@ -184,86 +177,84 @@ pub const GenerateStep = struct { //it means silent for Zig test. so the test will always pass, but we will log the information //about the actual success or failure of the test, so we can determine what we need to do //to get compliance. - if(silent) { - try emitDetailed(writer,input_file_path,has_error_file); - } else { + if (silent) { + try emitDetailed(writer, input_file_path, has_error_file); + } else { //the presence of an error file means our parser/tokeniser SHOULD get an error - if(has_error_file) { - try emitErrorIsSuccessCase(writer,input_file_path); + if (has_error_file) { + try emitErrorIsSuccessCase(writer, input_file_path); } //otherwise we expect the parsing to succeed correctly else { - try emitErrorIsFailureCase(writer,input_file_path); + try emitErrorIsFailureCase(writer, input_file_path); } } - + try emitFunctionFinish(writer); - } -//constant text for the detailed load success/fail cases - -const verbose_loadfile = - \\ var failed: bool = false; - \\ if(loadFromFile(" -; -const verbose_expect_error = - \\")) |it_parsed| { - \\ var yml = it_parsed; - \\ yml.deinit(); - \\ failed = true; - \\ } else |_| { - \\ std.debug.print("success\n",.{}); - \\ } - \\ - \\ if(failed) { - \\ return error.Failed; - \\ } -; + //constant text for the detailed load success/fail cases -const verbose_expect_no_error = - \\")) |it_parsed| { - \\ var yml = it_parsed; - \\ yml.deinit(); - \\ std.debug.print("success\n",.{}); - \\ } else |_| { - \\ failed = true; - \\ } - \\ if(failed) { - \\ return error.Failed; - \\ } -; + const verbose_loadfile = + \\ var failed: bool = false; + \\ if(loadFromFile(" + ; + const verbose_expect_error = + \\")) |it_parsed| { + \\ var yml = it_parsed; + \\ yml.deinit(); + \\ failed = true; + \\ } else |_| { + \\ std.debug.print("success\n",.{}); + \\ } + \\ + \\ if(failed) { + \\ return error.Failed; + \\ } + ; + const verbose_expect_no_error = + \\")) |it_parsed| { + \\ var yml = it_parsed; + \\ yml.deinit(); + \\ std.debug.print("success\n",.{}); + \\ } else |_| { + \\ failed = true; + \\ } + \\ if(failed) { + \\ return error.Failed; + \\ } + ; fn emitDetailed(writer: anytype, name: []const u8, has_error: bool) !void { try writer.writeAll(verbose_loadfile); try writer.writeAll(name); - if(has_error) { + if (has_error) { try writer.writeAll(verbose_expect_error); } else { try writer.writeAll(verbose_expect_no_error); } } -//constant text for the standard load success/fail cases + //constant text for the standard load success/fail cases -const loadfile = - \\ var yaml = loadFromFile(" -; + const loadfile = + \\ var yaml = loadFromFile(" + ; -const endErrorSuccess = - \\") catch return; - \\ defer yaml.deinit(); - \\ return error.UnexpectedSuccess; - \\ -; + const endErrorSuccess = + \\") catch return; + \\ defer yaml.deinit(); + \\ return error.UnexpectedSuccess; + \\ + ; -const endErrorIsFailure = - \\") catch return error.Failed; - \\ defer yaml.deinit(); - \\ -; + const endErrorIsFailure = + \\") catch return error.Failed; + \\ defer yaml.deinit(); + \\ + ; fn emitErrorIsSuccessCase(writer: anytype, name: []const u8) !void { try writer.writeAll(loadfile); @@ -277,7 +268,6 @@ const endErrorIsFailure = try writer.writeAll(endErrorIsFailure); } - //function start and finish fn emitFunctionStart(writer: anytype, name: []const u8, details: []const u8) !void { try writer.writeAll("//"); @@ -291,6 +281,4 @@ const endErrorIsFailure = fn emitFunctionFinish(writer: anytype) !void { try writer.writeAll("}\n\n\n"); } - - }; From f42bb3b9f8f5b9cb7e70d5299c2a0b4da1f9bbe5 Mon Sep 17 00:00:00 2001 From: Brendan Dennis Date: Tue, 28 Feb 2023 23:03:54 +1030 Subject: [PATCH 18/19] Updated to new zig syntax Build file seeems to be working but i don't think the .test CompileStep works anymore. bug? --- build.zig | 76 +++++++++++++++++++++++++++++++++++++--------- src/yaml.zig | 14 ++++----- test/generator.zig | 8 ++--- test/test.zig | 26 ++++++++-------- 4 files changed, 86 insertions(+), 38 deletions(-) diff --git a/build.zig b/build.zig index c25ceea..2830ff3 100644 --- a/build.zig +++ b/build.zig @@ -1,24 +1,40 @@ const std = @import("std"); const GenerateStep = @import("test/generator.zig").GenerateStep; -pub fn build(b: *std.build.Builder) void { - const mode = b.standardReleaseOptions(); +pub fn build(b: *std.Build) void { + const optimize = b.standardOptimizeOption(.{}); + const target = b.standardTargetOptions(.{}); const enable_logging = b.option(bool, "log", "Whether to enable logging") orelse false; const create_only_yaml_tests = b.option([]const []const u8, "specificYAML", "generate only YAML tests matching this eg -DspecificYAML{\"comment/6HB6\",\"EW3V\"}") orelse &[_][]const u8{}; const enable_silent_yaml = b.option(bool, "silentYAML", "all YAML tests will pass, failures will be logged cleanly") orelse false; - const lib = b.addStaticLibrary("yaml", "src/yaml.zig"); - lib.setBuildMode(mode); + const lib = b.addStaticLibrary(.{ + .name = "yaml", + .root_source_file = .{.path = "src/yaml.zig"}, + .target = target, + .optimize = optimize, + }); lib.install(); - var yaml_tests = b.addTest("src/yaml.zig"); - yaml_tests.setBuildMode(mode); + var yaml_tests = b.addTest(.{ + .name = "yaml", + .kind = .test_exe, + .root_source_file = .{.path = "src/yaml.zig"}, + .target = target, + .optimize = optimize, + }); + + const example = b.addExecutable(.{ + .name = "yaml", + .root_source_file = .{.path = "examples/yaml.zig"}, + .target = target, + .optimize = optimize, + }); - const example = b.addExecutable("yaml", "examples/yaml.zig"); - example.setBuildMode(mode); - example.addPackagePath("yaml", "src/yaml.zig"); + var yaml_module = module(b,"./"); + example.addModule("yaml", yaml_module); const example_opts = b.addOptions(); example.addOptions("build_options", example_opts); @@ -40,9 +56,14 @@ pub fn build(b: *std.build.Builder) void { const test_step = b.step("test", "Run library tests"); test_step.dependOn(&yaml_tests.step); - var e2e_tests = b.addTest("test/test.zig"); - e2e_tests.setBuildMode(mode); - e2e_tests.addPackagePath("yaml", "src/yaml.zig"); + var e2e_tests = b.addTest(.{ + .name = "e2e_tests", + .kind = .test_exe, + .root_source_file = .{.path = "test/test.zig"}, + .target = target, + .optimize = optimize, + }); + e2e_tests.addModule("yaml", yaml_module); test_step.dependOn(&e2e_tests.step); const cwd = std.fs.cwd(); @@ -51,11 +72,38 @@ pub fn build(b: *std.build.Builder) void { const gen = GenerateStep.init(b,"yamlTest.zig",create_only_yaml_tests,enable_silent_yaml); test_step.dependOn(&gen.step); - var full_yaml_tests = b.addTest("zig-cache/yamlTest.zig"); - full_yaml_tests.addPackagePath("yaml", "src/yaml.zig"); + var full_yaml_tests = b.addTest(.{ + .name = "full_yaml_tests", + .kind = .test_exe, + .root_source_file = .{.path = "zig-cache/yamlTest.zig"}, + .target = target, + .optimize = optimize, + }); + full_yaml_tests.addModule("yaml", yaml_module); test_step.dependOn(&full_yaml_tests.step); } else |_| { std.debug.print("No 'data' directory with YAML tests provided\n",.{}); } } +var cached_pkg: ?*std.Build.Module = null; + +pub fn module(b: *std.Build, path: []const u8) *std.Build.Module { + if (cached_pkg == null) { + + const yaml_path = std.fs.path.join(b.allocator,&[_][]const u8{ + path, + "src/yaml.zig" + }) catch unreachable; + + const yaml_module = b.createModule(.{ + .source_file = .{ .path = yaml_path }, + .dependencies = &.{ }, + }); + + cached_pkg = yaml_module; + } + + return cached_pkg.?; +} + diff --git a/src/yaml.zig b/src/yaml.zig index 8c63a59..954deb5 100644 --- a/src/yaml.zig +++ b/src/yaml.zig @@ -74,7 +74,7 @@ pub const Value = union(enum) { const first = list[0]; if (first.isCompound()) { - for (list) |elem, i| { + for (list, 0..list.len) |elem, i| { try writer.writeByteNTimes(' ', args.indentation); try writer.writeAll("- "); try elem.stringify(writer, .{ @@ -89,7 +89,7 @@ pub const Value = union(enum) { } try writer.writeAll("[ "); - for (list) |elem, i| { + for (list, 0..list.len) |elem, i| { try elem.stringify(writer, args); if (i < len - 1) { try writer.writeAll(", "); @@ -350,7 +350,7 @@ pub const Yaml = struct { switch (@typeInfo(T)) { .Array => |info| { var parsed: T = undefined; - for (self.docs.items) |doc, i| { + for (self.docs.items, 0..self.docs.items.len) |doc, i| { parsed[i] = try self.parseValue(info.child, doc); } return parsed; @@ -359,7 +359,7 @@ pub const Yaml = struct { switch (info.size) { .Slice => { var parsed = try self.arena.allocator().alloc(info.child, self.docs.items.len); - for (self.docs.items) |doc, i| { + for (self.docs.items, 0..self.docs.items.len) |doc, i| { parsed[i] = try self.parseValue(info.child, doc); } return parsed; @@ -452,7 +452,7 @@ pub const Yaml = struct { } var parsed = try arena.alloc(ptr_info.child, value.list.len); - for (value.list) |elem, i| { + for (value.list, 0..value.list.len) |elem, i| { parsed[i] = try self.parseValue(ptr_info.child, elem); } return parsed; @@ -466,7 +466,7 @@ pub const Yaml = struct { if (array_info.len != list.len) return error.ArraySizeMismatch; var parsed: T = undefined; - for (list) |elem, i| { + for (list, 0..list.len) |elem, i| { parsed[i] = try self.parseValue(array_info.child, elem); } @@ -474,7 +474,7 @@ pub const Yaml = struct { } pub fn stringify(self: Yaml, writer: anytype) !void { - for (self.docs.items) |doc, i| { + for (self.docs.items, 0..self.docs.items.len) |doc, i| { try writer.writeAll("---"); if (self.tree.?.getDirective(i)) |directive| { try writer.print(" !{s}", .{directive}); diff --git a/test/generator.zig b/test/generator.zig index 6405d7b..40e891b 100644 --- a/test/generator.zig +++ b/test/generator.zig @@ -1,6 +1,6 @@ const std = @import("std"); const path = std.fs.path; -const Builder = std.build.Builder; +const Builder = std.Build; const Step = std.build.Step; const Allocator = std.mem.Allocator; @@ -41,8 +41,8 @@ pub const GenerateStep = struct { pub fn init(builder: *Builder, out_path: []const u8, build_only: []const []const u8, silent_mode: bool) *GenerateStep { const self = builder.allocator.create(GenerateStep) catch unreachable; const full_out_path = path.join(builder.allocator, &[_][]const u8{ - builder.build_root, - builder.cache_root, + builder.build_root.path.?, + builder.cache_root.path.?, out_path, }) catch unreachable; @@ -71,7 +71,7 @@ pub const GenerateStep = struct { //read the tags, follow the links, generate the tests const root_data_dir = path.join(self.builder.allocator, &[_][]const u8{ - self.builder.build_root, + self.builder.build_root.path.?, "test/data", }) catch unreachable; diff --git a/test/test.zig b/test/test.zig index b4810d7..8db9976 100644 --- a/test/test.zig +++ b/test/test.zig @@ -32,15 +32,15 @@ test "simple" { if (self.numbers.len != other.numbers.len) return false; if (self.finally.len != other.finally.len) return false; - for (self.names) |lhs, i| { + for (self.names, 0..self.names.len) |lhs, i| { if (!mem.eql(u8, lhs, other.names[i])) return false; } - for (self.numbers) |lhs, i| { + for (self.numbers, 0..self.numbers.len) |lhs, i| { if (lhs != other.numbers[i]) return false; } - for (self.finally) |lhs, i| { + for (self.finally, 0..self.finally.len) |lhs, i| { if (lhs != other.finally[i]) return false; } @@ -96,7 +96,7 @@ const LibTbd = struct { if (self.tbd_version != other.tbd_version) return false; if (self.targets.len != other.targets.len) return false; - for (self.targets) |target, i| { + for (self.targets, 0..self.targets.len) |target, i| { if (!mem.eql(u8, target, other.targets[i])) return false; } @@ -118,17 +118,17 @@ const LibTbd = struct { if (reexported_libraries.len != o_reexported_libraries.len) return false; - for (reexported_libraries) |reexport, i| { + for (reexported_libraries, 0..reexported_libraries.len) |reexport, i| { const o_reexport = o_reexported_libraries[i]; if (reexport.targets.len != o_reexport.targets.len) return false; if (reexport.libraries.len != o_reexport.libraries.len) return false; - for (reexport.targets) |target, j| { + for (reexport.targets, 0..reexport.targets.len) |target, j| { const o_target = o_reexport.targets[j]; if (!mem.eql(u8, target, o_target)) return false; } - for (reexport.libraries) |library, j| { + for (reexport.libraries, 0..reexport.libraries.len) |library, j| { const o_library = o_reexport.libraries[j]; if (!mem.eql(u8, library, o_library)) return false; } @@ -140,11 +140,11 @@ const LibTbd = struct { if (parent_umbrella.len != o_parent_umbrella.len) return false; - for (parent_umbrella) |pumbrella, i| { + for (parent_umbrella, 0..parent_umbrella.len) |pumbrella, i| { const o_pumbrella = o_parent_umbrella[i]; if (pumbrella.targets.len != o_pumbrella.targets.len) return false; - for (pumbrella.targets) |target, j| { + for (pumbrella.targets, 0..pumbrella.targets.len) |target, j| { const o_target = o_pumbrella.targets[j]; if (!mem.eql(u8, target, o_target)) return false; } @@ -155,17 +155,17 @@ const LibTbd = struct { if (self.exports.len != other.exports.len) return false; - for (self.exports) |exp, i| { + for (self.exports, 0..self.exports.len) |exp, i| { const o_exp = other.exports[i]; if (exp.targets.len != o_exp.targets.len) return false; if (exp.symbols.len != o_exp.symbols.len) return false; - for (exp.targets) |target, j| { + for (exp.targets, 0..exp.targets.len) |target, j| { const o_target = o_exp.targets[j]; if (!mem.eql(u8, target, o_target)) return false; } - for (exp.symbols) |symbol, j| { + for (exp.symbols, 0..exp.symbols.len) |symbol, j| { const o_symbol = o_exp.symbols[j]; if (!mem.eql(u8, symbol, o_symbol)) return false; } @@ -304,7 +304,7 @@ test "multi lib tbd" { }, }; - for (result) |lib, i| { + for (result, 0..result.len) |lib, i| { try testing.expect(lib.eql(expected[i])); } } From 70a89fb69f4a5e5a6b0fc7379fc118591782036b Mon Sep 17 00:00:00 2001 From: Brendan Dennis Date: Thu, 9 Mar 2023 21:44:46 +1030 Subject: [PATCH 19/19] Added enable-spec-tests flag Updated test module dependencies and set kind to correct enum variant --- README.md | 2 +- build.zig | 63 +++++++++++++++++++++++++--------------------- src/yaml.zig | 2 +- src/yaml/test.zig | 2 +- test/generator.zig | 1 - 5 files changed, 37 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index ec661de..5a30861 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ finally: [ 8.17, 19.78, 17, 21 ] To test against the YAML 1.2 compatibility tests, first obtain the tests from https://github.com/yaml/yaml-test-suite. Follow the instructions to obtain the `data` directory which contains all of the test cases. Copy this directory into the `test` folder where `generator.zig` is located. -Running `zig build test` will now generate test cases for the YAML 1.2 compatibility tests. They can be found in the zig-cache in a file named `yamlTest.zig`. +Running `zig build test -Denable-spec-tests` will now generate test cases for the YAML 1.2 compatibility tests. They can be found in the zig-cache in a file named `yamlTest.zig`. The test cases have the following format. diff --git a/build.zig b/build.zig index 2830ff3..a974781 100644 --- a/build.zig +++ b/build.zig @@ -12,7 +12,7 @@ pub fn build(b: *std.Build) void { const lib = b.addStaticLibrary(.{ .name = "yaml", - .root_source_file = .{.path = "src/yaml.zig"}, + .root_source_file = .{ .path = "src/yaml.zig" }, .target = target, .optimize = optimize, }); @@ -20,21 +20,22 @@ pub fn build(b: *std.Build) void { var yaml_tests = b.addTest(.{ .name = "yaml", - .kind = .test_exe, - .root_source_file = .{.path = "src/yaml.zig"}, + .kind = .@"test", + .root_source_file = .{ .path = "src/yaml/test.zig" }, .target = target, .optimize = optimize, - }); - + }); + const example = b.addExecutable(.{ .name = "yaml", - .root_source_file = .{.path = "examples/yaml.zig"}, + .root_source_file = .{ .path = "examples/yaml.zig" }, .target = target, .optimize = optimize, }); - var yaml_module = module(b,"./"); + var yaml_module = module(b, "./"); example.addModule("yaml", yaml_module); + yaml_tests.addModule("yaml", yaml_module); const example_opts = b.addOptions(); example.addOptions("build_options", example_opts); @@ -53,36 +54,45 @@ pub fn build(b: *std.Build) void { const run_step = b.step("run", "Run example program parser"); run_step.dependOn(&run_cmd.step); - const test_step = b.step("test", "Run library tests"); + var test_step = b.step("test", "Run library tests"); test_step.dependOn(&yaml_tests.step); var e2e_tests = b.addTest(.{ .name = "e2e_tests", - .kind = .test_exe, - .root_source_file = .{.path = "test/test.zig"}, + .kind = .@"test", + .root_source_file = .{ .path = "test/test.zig" }, .target = target, .optimize = optimize, - }); + }); e2e_tests.addModule("yaml", yaml_module); test_step.dependOn(&e2e_tests.step); + const enable_macos_sdk = b.option(bool, "enable-spec-tests", "Attempt to generate and run the full yaml 1.2 test suite.") orelse false; + + if (enable_macos_sdk == false) { + return; + } + const cwd = std.fs.cwd(); - if(cwd.access("test/data",.{})) { - std.debug.print("Found 'data' directory with YAML tests. Attempting to generate test cases\n",.{}); - - const gen = GenerateStep.init(b,"yamlTest.zig",create_only_yaml_tests,enable_silent_yaml); + if (cwd.access("test/data", .{})) { + std.debug.print("Found 'data' directory with YAML tests. Attempting to generate test cases\n", .{}); + + const gen = GenerateStep.init(b, "yamlTest.zig", create_only_yaml_tests, enable_silent_yaml); test_step.dependOn(&gen.step); + var full_yaml_tests = b.addTest(.{ - .name = "full_yaml_tests", - .kind = .test_exe, - .root_source_file = .{.path = "zig-cache/yamlTest.zig"}, - .target = target, - .optimize = optimize, - }); + .name = "full_yaml_tests", + .kind = .@"test", + .root_source_file = .{ .path = "zig-cache/yamlTest.zig" }, + .target = target, + .optimize = optimize, + }); + full_yaml_tests.addModule("yaml", yaml_module); + test_step.dependOn(&full_yaml_tests.step); } else |_| { - std.debug.print("No 'data' directory with YAML tests provided\n",.{}); + std.debug.print("No 'data' directory with YAML tests provided\n", .{}); } } @@ -90,15 +100,11 @@ var cached_pkg: ?*std.Build.Module = null; pub fn module(b: *std.Build, path: []const u8) *std.Build.Module { if (cached_pkg == null) { - - const yaml_path = std.fs.path.join(b.allocator,&[_][]const u8{ - path, - "src/yaml.zig" - }) catch unreachable; + const yaml_path = std.fs.path.join(b.allocator, &[_][]const u8{ path, "src/yaml.zig" }) catch unreachable; const yaml_module = b.createModule(.{ .source_file = .{ .path = yaml_path }, - .dependencies = &.{ }, + .dependencies = &.{}, }); cached_pkg = yaml_module; @@ -106,4 +112,3 @@ pub fn module(b: *std.Build, path: []const u8) *std.Build.Module { return cached_pkg.?; } - diff --git a/src/yaml.zig b/src/yaml.zig index 954deb5..54da23b 100644 --- a/src/yaml.zig +++ b/src/yaml.zig @@ -503,5 +503,5 @@ pub fn stringify(allocator: Allocator, input: anytype, writer: anytype) !void { test { std.testing.refAllDecls(Tokenizer); std.testing.refAllDecls(parse); - _ = @import("yaml/test.zig"); + //_ = @import("yaml/test.zig"); } diff --git a/src/yaml/test.zig b/src/yaml/test.zig index 8db9435..0e99a87 100644 --- a/src/yaml/test.zig +++ b/src/yaml/test.zig @@ -2,7 +2,7 @@ const std = @import("std"); const mem = std.mem; const testing = std.testing; -const yaml_mod = @import("../yaml.zig"); +const yaml_mod = @import("yaml"); const Yaml = yaml_mod.Yaml; test "simple list" { diff --git a/test/generator.zig b/test/generator.zig index 40e891b..2467fc5 100644 --- a/test/generator.zig +++ b/test/generator.zig @@ -41,7 +41,6 @@ pub const GenerateStep = struct { pub fn init(builder: *Builder, out_path: []const u8, build_only: []const []const u8, silent_mode: bool) *GenerateStep { const self = builder.allocator.create(GenerateStep) catch unreachable; const full_out_path = path.join(builder.allocator, &[_][]const u8{ - builder.build_root.path.?, builder.cache_root.path.?, out_path, }) catch unreachable;