Skip to content

Instantly share code, notes, and snippets.

@bbengfort
Created August 14, 2025 20:19
Show Gist options
  • Save bbengfort/8e8ec1a7472b5b7f90a4a72c452ef167 to your computer and use it in GitHub Desktop.
Save bbengfort/8e8ec1a7472b5b7f90a4a72c452ef167 to your computer and use it in GitHub Desktop.

Revisions

  1. bbengfort created this gist Aug 14, 2025.
    75 changes: 75 additions & 0 deletions gocover.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,75 @@
    #!/usr/bin/env python3
    # Runs go coverage tools and generates a table of coverage per package.

    import os
    import argparse
    import subprocess

    from itertools import groupby
    from tabulate import tabulate


    def gotest(package="./...", profile="coverage.out"):
    cmd = [
    "go", "test", "-covermode=count",
    "-coverprofile=" + profile,
    package
    ]

    subprocess.run(cmd)


    def read_coverage(profile="coverage.out"):
    with open(profile, "r") as f:
    for line in f:
    if line.startswith("mode:"):
    continue

    parts = line.split()
    parts[0] = os.path.dirname(parts[0].split(":")[0])
    parts[1] = int(parts[1])
    parts[2] = int(parts[2])
    yield parts


    def coverage(profile="coverage.out"):
    for pkg, group in groupby(read_coverage(profile), key=lambda x: x[0]):
    group = list(group)
    total = sum(x[1] for x in group)
    covered = sum(x[1] for x in group if x[2] > 0)

    yield (
    pkg,
    covered,
    total,
    f"{covered / total if total > 0 else 0:0.2%}",
    )


    def main(args):
    # run go test
    gotest()

    # run go tool cover
    cov = list(coverage())
    print(tabulate(cov, headers=["Package", "Covered", "Statements", "Coverage"], tablefmt="markdown")) # noqa

    # cleanup
    os.remove("coverage.out")


    if __name__ == "__main__":
    opts = {}

    parser = argparse.ArgumentParser(
    description="runs go coverage tools and gernerates a package coverage table", # noqa
    epilog="yeh, this should really be a golang program ... le sigh"
    )

    for pargs, kwargs in opts.items():
    if isinstance(pargs, str):
    pargs = (pargs,)
    parser.add_argument(*pargs, **kwargs)

    args = parser.parse_args()
    main(args)