Commit f94cbab3ac

mlugg <mlugg@mlugg.co.uk>
2023-02-18 07:28:47
Add test coverage for some module structures
1 parent b8a96ba
src/test.zig
@@ -583,6 +583,11 @@ pub const TestContext = struct {
         path: []const u8,
     };
 
+    pub const DepModule = struct {
+        name: []const u8,
+        path: []const u8,
+    };
+
     pub const Backend = enum {
         stage1,
         stage2,
@@ -611,6 +616,7 @@ pub const TestContext = struct {
         link_libc: bool = false,
 
         files: std.ArrayList(File),
+        deps: std.ArrayList(DepModule),
 
         result: anyerror!void = {},
 
@@ -618,6 +624,13 @@ pub const TestContext = struct {
             case.files.append(.{ .path = name, .src = src }) catch @panic("out of memory");
         }
 
+        pub fn addDepModule(case: *Case, name: []const u8, path: []const u8) void {
+            case.deps.append(.{
+                .name = name,
+                .path = path,
+            }) catch @panic("out of memory");
+        }
+
         /// Adds a subcase in which the module is updated with `src`, and a C
         /// header is generated.
         pub fn addHeader(self: *Case, src: [:0]const u8, result: [:0]const u8) void {
@@ -767,6 +780,7 @@ pub const TestContext = struct {
             .updates = std.ArrayList(Update).init(ctx.cases.allocator),
             .output_mode = .Exe,
             .files = std.ArrayList(File).init(ctx.arena),
+            .deps = std.ArrayList(DepModule).init(ctx.arena),
         }) catch @panic("out of memory");
         return &ctx.cases.items[ctx.cases.items.len - 1];
     }
@@ -787,6 +801,7 @@ pub const TestContext = struct {
             .updates = std.ArrayList(Update).init(ctx.cases.allocator),
             .output_mode = .Exe,
             .files = std.ArrayList(File).init(ctx.arena),
+            .deps = std.ArrayList(DepModule).init(ctx.arena),
             .link_libc = true,
         }) catch @panic("out of memory");
         return &ctx.cases.items[ctx.cases.items.len - 1];
@@ -801,6 +816,7 @@ pub const TestContext = struct {
             .updates = std.ArrayList(Update).init(ctx.cases.allocator),
             .output_mode = .Exe,
             .files = std.ArrayList(File).init(ctx.arena),
+            .deps = std.ArrayList(DepModule).init(ctx.arena),
             .backend = .llvm,
             .link_libc = true,
         }) catch @panic("out of memory");
@@ -818,6 +834,7 @@ pub const TestContext = struct {
             .updates = std.ArrayList(Update).init(ctx.cases.allocator),
             .output_mode = .Obj,
             .files = std.ArrayList(File).init(ctx.arena),
+            .deps = std.ArrayList(DepModule).init(ctx.arena),
         }) catch @panic("out of memory");
         return &ctx.cases.items[ctx.cases.items.len - 1];
     }
@@ -834,6 +851,7 @@ pub const TestContext = struct {
             .output_mode = .Exe,
             .is_test = true,
             .files = std.ArrayList(File).init(ctx.arena),
+            .deps = std.ArrayList(DepModule).init(ctx.arena),
         }) catch @panic("out of memory");
         return &ctx.cases.items[ctx.cases.items.len - 1];
     }
@@ -858,6 +876,7 @@ pub const TestContext = struct {
             .updates = std.ArrayList(Update).init(ctx.cases.allocator),
             .output_mode = .Obj,
             .files = std.ArrayList(File).init(ctx.arena),
+            .deps = std.ArrayList(DepModule).init(ctx.arena),
         }) catch @panic("out of memory");
         return &ctx.cases.items[ctx.cases.items.len - 1];
     }
@@ -1145,6 +1164,7 @@ pub const TestContext = struct {
                                 .output_mode = output_mode,
                                 .link_libc = backend == .llvm,
                                 .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator),
+                                .deps = std.ArrayList(DepModule).init(ctx.cases.allocator),
                             });
                             try cases.append(next);
                         }
@@ -1498,7 +1518,24 @@ pub const TestContext = struct {
             .root_src_directory = .{ .path = tmp_dir_path, .handle = tmp.dir },
             .root_src_path = tmp_src_path,
         };
-        defer main_pkg.table.deinit(allocator);
+        defer {
+            var it = main_pkg.table.iterator();
+            while (it.next()) |kv| {
+                allocator.free(kv.key_ptr.*);
+                kv.value_ptr.*.destroy(allocator);
+            }
+            main_pkg.table.deinit(allocator);
+        }
+
+        for (case.deps.items) |dep| {
+            var pkg = try Package.create(
+                allocator,
+                tmp_dir_path,
+                dep.path,
+            );
+            errdefer pkg.destroy(allocator);
+            try main_pkg.add(allocator, dep.name, pkg);
+        }
 
         const bin_name = try std.zig.binNameAlloc(arena, .{
             .root_name = "test_case",
test/stage2/nvptx.zig
@@ -97,6 +97,7 @@ pub fn addPtx(
         .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator),
         .output_mode = .Obj,
         .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator),
+        .deps = std.ArrayList(TestContext.DepModule).init(ctx.cases.allocator),
         .link_libc = false,
         .backend = .llvm,
         // Bug in Debug mode
test/standalone/dep_diamond/bar.zig
@@ -0,0 +1,1 @@
+pub const shared = @import("shared");
test/standalone/dep_diamond/build.zig
@@ -0,0 +1,28 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) void {
+    const optimize = b.standardOptimizeOption(.{});
+
+    const shared = b.createModule(.{
+        .source_file = .{ .path = "shared.zig" },
+    });
+
+    const exe = b.addExecutable(.{
+        .name = "test",
+        .root_source_file = .{ .path = "test.zig" },
+        .optimize = optimize,
+    });
+    exe.addAnonymousModule("foo", .{
+        .source_file = .{ .path = "foo.zig" },
+        .dependencies = &.{.{ .name = "shared", .module = shared }},
+    });
+    exe.addAnonymousModule("bar", .{
+        .source_file = .{ .path = "bar.zig" },
+        .dependencies = &.{.{ .name = "shared", .module = shared }},
+    });
+
+    const run = exe.run();
+
+    const test_step = b.step("test", "Test it");
+    test_step.dependOn(&run.step);
+}
test/standalone/dep_diamond/foo.zig
@@ -0,0 +1,1 @@
+pub const shared = @import("shared");
test/standalone/dep_diamond/shared.zig
@@ -0,0 +1,1 @@
+// (empty)
test/standalone/dep_diamond/test.zig
@@ -0,0 +1,7 @@
+const foo = @import("foo");
+const bar = @import("bar");
+const assert = @import("std").debug.assert;
+
+pub fn main() void {
+    assert(foo.shared == bar.shared);
+}
test/standalone/dep_mutually_recursive/bar.zig
@@ -0,0 +1,6 @@
+const assert = @import("std").debug.assert;
+pub const foo = @import("foo");
+
+comptime {
+    assert(foo.bar == @This());
+}
test/standalone/dep_mutually_recursive/build.zig
@@ -0,0 +1,26 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) void {
+    const optimize = b.standardOptimizeOption(.{});
+
+    const foo = b.createModule(.{
+        .source_file = .{ .path = "foo.zig" },
+    });
+    const bar = b.createModule(.{
+        .source_file = .{ .path = "bar.zig" },
+    });
+    foo.dependencies.put("bar", bar) catch @panic("OOM");
+    bar.dependencies.put("foo", foo) catch @panic("OOM");
+
+    const exe = b.addExecutable(.{
+        .name = "test",
+        .root_source_file = .{ .path = "test.zig" },
+        .optimize = optimize,
+    });
+    exe.addModule("foo", foo);
+
+    const run = exe.run();
+
+    const test_step = b.step("test", "Test it");
+    test_step.dependOn(&run.step);
+}
test/standalone/dep_mutually_recursive/foo.zig
@@ -0,0 +1,6 @@
+const assert = @import("std").debug.assert;
+pub const bar = @import("bar");
+
+comptime {
+    assert(bar.foo == @This());
+}
test/standalone/dep_mutually_recursive/test.zig
@@ -0,0 +1,7 @@
+const foo = @import("foo");
+const assert = @import("std").debug.assert;
+
+pub fn main() void {
+    assert(foo == foo.bar.foo);
+    assert(foo == foo.bar.foo.bar.foo);
+}
test/standalone/dep_recursive/build.zig
@@ -0,0 +1,22 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) void {
+    const optimize = b.standardOptimizeOption(.{});
+
+    const foo = b.createModule(.{
+        .source_file = .{ .path = "foo.zig" },
+    });
+    foo.dependencies.put("foo", foo) catch @panic("OOM");
+
+    const exe = b.addExecutable(.{
+        .name = "test",
+        .root_source_file = .{ .path = "test.zig" },
+        .optimize = optimize,
+    });
+    exe.addModule("foo", foo);
+
+    const run = exe.run();
+
+    const test_step = b.step("test", "Test it");
+    test_step.dependOn(&run.step);
+}
test/standalone/dep_recursive/foo.zig
@@ -0,0 +1,6 @@
+const assert = @import("std").debug.assert;
+pub const foo = @import("foo");
+
+comptime {
+    assert(foo == @This());
+}
test/standalone/dep_recursive/test.zig
@@ -0,0 +1,8 @@
+const foo = @import("foo");
+const shared = @import("shared");
+const assert = @import("std").debug.assert;
+
+pub fn main() void {
+    assert(foo == foo.foo);
+    assert(foo == foo.foo.foo);
+}
test/standalone/dep_shared_builtin/build.zig
@@ -0,0 +1,19 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) void {
+    const optimize = b.standardOptimizeOption(.{});
+
+    const exe = b.addExecutable(.{
+        .name = "test",
+        .root_source_file = .{ .path = "test.zig" },
+        .optimize = optimize,
+    });
+    exe.addAnonymousModule("foo", .{
+        .source_file = .{ .path = "foo.zig" },
+    });
+
+    const run = exe.run();
+
+    const test_step = b.step("test", "Test it");
+    test_step.dependOn(&run.step);
+}
test/standalone/dep_shared_builtin/foo.zig
@@ -0,0 +1,3 @@
+pub const std = @import("std");
+pub const builtin = @import("builtin");
+pub const root = @import("root");
test/standalone/dep_shared_builtin/test.zig
@@ -0,0 +1,11 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const root = @import("root");
+const foo = @import("foo");
+
+pub fn main() void {
+    std.debug.assert(root == @This());
+    std.debug.assert(std == foo.std);
+    std.debug.assert(builtin == foo.builtin);
+    std.debug.assert(root == foo.root);
+}
test/standalone/dep_triangle/build.zig
@@ -0,0 +1,25 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) void {
+    const optimize = b.standardOptimizeOption(.{});
+
+    const shared = b.createModule(.{
+        .source_file = .{ .path = "shared.zig" },
+    });
+
+    const exe = b.addExecutable(.{
+        .name = "test",
+        .root_source_file = .{ .path = "test.zig" },
+        .optimize = optimize,
+    });
+    exe.addAnonymousModule("foo", .{
+        .source_file = .{ .path = "foo.zig" },
+        .dependencies = &.{.{ .name = "shared", .module = shared }},
+    });
+    exe.addModule("shared", shared);
+
+    const run = exe.run();
+
+    const test_step = b.step("test", "Test it");
+    test_step.dependOn(&run.step);
+}
test/standalone/dep_triangle/foo.zig
@@ -0,0 +1,1 @@
+pub const shared = @import("shared");
test/standalone/dep_triangle/shared.zig
@@ -0,0 +1,1 @@
+// (empty)
test/standalone/dep_triangle/test.zig
@@ -0,0 +1,7 @@
+const foo = @import("foo");
+const shared = @import("shared");
+const assert = @import("std").debug.assert;
+
+pub fn main() void {
+    assert(foo.shared == shared);
+}
test/compile_errors.zig
@@ -288,4 +288,26 @@ pub fn addCases(ctx: *TestContext) !void {
     //, &[_][]const u8{
     //    "tmp.zig:4:1: error: unable to inline function",
     //});
+
+    {
+        const case = ctx.obj("file in multiple modules", .{});
+        case.backend = .stage2;
+
+        case.addSourceFile("foo.zig",
+            \\const dummy = 0;
+        );
+
+        case.addDepModule("foo", "foo.zig");
+
+        case.addError(
+            \\comptime {
+            \\    _ = @import("foo");
+            \\    _ = @import("foo.zig");
+            \\}
+        , &[_][]const u8{
+            ":1:1: error: file exists in multiple modules",
+            ":1:1: note: root of module root.foo",
+            ":3:17: note: imported from module root",
+        });
+    }
 }
test/standalone.zig
@@ -107,4 +107,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
     cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{});
     cases.addBuildFile("test/standalone/issue_12588/build.zig", .{});
     cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{});
+
+    cases.addBuildFile("test/standalone/dep_diamond/build.zig", .{});
+    cases.addBuildFile("test/standalone/dep_triangle/build.zig", .{});
+    cases.addBuildFile("test/standalone/dep_recursive/build.zig", .{});
+    cases.addBuildFile("test/standalone/dep_mutually_recursive/build.zig", .{});
+    cases.addBuildFile("test/standalone/dep_shared_builtin/build.zig", .{});
 }