Proper way to get zig code coverage

Hello Members,

I am having an issue with code coverage for one of my Zig projects; tests were executed, but coverage shows 0% for most combinations. Reaching out to you, folks, to get some insights to get the issue corrected. Appreciate it if anyone can guide me to get this properly enabled.

I am using zig init a bare project for this discussion, with the following coverage command enabled.

main.zig

const std = @import("std");
const zig_coverage = @import("zig_coverage");

pub fn main() !void {
    // Prints to stderr, ignoring potential errors.
    std.debug.print("All your {s} are belong to us.\n", .{"codebase"});
    try zig_coverage.bufferedPrint();
}

test "simple test" {
    const gpa = std.testing.allocator;
    var list: std.ArrayList(i32) = .empty;
    defer list.deinit(gpa); // Try commenting this out and see if zig detects the memory leak!
    try list.append(gpa, 42);
    try std.testing.expectEqual(@as(i32, 42), list.pop());
}

test "fuzz example" {
    const Context = struct {
        fn testOne(context: @This(), input: []const u8) anyerror!void {
            _ = context;
            // Try passing `--fuzz` to `zig build test` and see if it manages to fail this test case!
            try std.testing.expect(!std.mem.eql(u8, "canyoufindme", input));
        }
    };
  

build.zig

const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .Debug });

    const mod = b.addModule("zig_coverage", .{
        .root_source_file = b.path("src/root.zig"),
        .target = target,
    });

    const exe = b.addExecutable(.{
        .name = "zig_coverage",
        .root_module = b.createModule(.{
            .root_source_file = b.path("src/main.zig"),
            .target = target,
            .optimize = optimize,
            .imports = &.{
                .{ .name = "zig_coverage", .module = mod },
            },
        }),
    });

    b.installArtifact(exe);

    const run_step = b.step("run", "Run the app");

    const run_cmd = b.addRunArtifact(exe);
    run_step.dependOn(&run_cmd.step);

    run_cmd.step.dependOn(b.getInstallStep());

    if (b.args) |args| {
        run_cmd.addArgs(args);
    }

    const mod_tests = b.addTest(.{
        .root_module = mod,
    });

    const run_mod_tests = b.addRunArtifact(mod_tests);

    const exe_tests = b.addTest(.{
        .root_module = exe.root_module,
    });

    const test_step = b.step("test", "Run tests");
    test_step.dependOn(&run_mod_tests.step);

    const coverage = b.option(bool, "coverage", "enable code coverage using kcov") orelse false;
    if (coverage) {
        const mkdir_kcov = b.addSystemCommand(&.{ "mkdir", "-p", "zig-out/kcov" });
        const run_kcov = b.addSystemCommand(&.{
            "kcov",
            "--include-pattern=src/",
            "--exclude-pattern=/usr/include,/usr/lib",
            "zig-out/kcov",
        });
        run_kcov.addArtifactArg(exe_tests);
        run_kcov.step.dependOn(&mkdir_kcov.step);

        test_step.dependOn(&run_mod_tests.step);
        test_step.dependOn(&run_kcov.step);
    } else {
        const run_exe_tests = b.addRunArtifact(exe_tests);
        test_step.dependOn(&run_mod_tests.step);
        test_step.dependOn(&run_exe_tests.step);
    }

Test summary

❯ zig build test -Dcoverage --summary all
All 2 tests passed.
1 fuzz tests found.

Build Summary: 6/6 steps succeeded; 1/1 tests passed
test success
├─ run test 1 passed 506us MaxRSS:2M
│  └─ compile test Debug native cached 20ms MaxRSS:33M
├─ run test (+1 more reused dependencies)
└─ run kcov success 119ms MaxRSS:112M
   ├─ compile test Debug native cached 12ms MaxRSS:33M
   └─ run mkdir success 2ms MaxRSS:2M

kcov output:

Why exactly should the coverage here be greater than 0? Here is an example using kcov GitHub - charlesrocket/ghext: Compact git HEAD hash extractor · GitHub.

Thank you so much for your kind words and direction!

I was expecting to run those two tests that would cover the existing root/main code and preview coverage of 100%

Anyway, after a closer look at your build.zig, I found I was wrong in adding --include-path in place of the --include-pattern src directory, and get this fixed.

const run_kcov = b.addSystemCommand(&.{
            "kcov",
            "--include-path=src/",
            "zig-out/kcov",
        });

Default project zig init coverage:

The test project coverage: