-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit dbbd1be
Showing
18 changed files
with
559 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
name: CI | ||
|
||
on: | ||
push: | ||
tags: | ||
- v* | ||
branches: | ||
- main | ||
pull_request: | ||
|
||
jobs: | ||
lint-and-test: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- uses: actions/setup-go@v4 | ||
with: | ||
go-version: '1.21' | ||
cache: true | ||
- run: go test ./... -race -coverprofile=coverage.txt -covermode=atomic | ||
- name: Upload coverage to Codecov | ||
uses: codecov/codecov-action@v3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# 📑 coverdiff | ||
[![ci](https://github.com/konradreiche/coverdiff/actions/workflows/ci.yaml/badge.svg)](https://github.com/konradreiche/coverdiff/actions) [![codecov](https://codecov.io/gh/konradreiche/coverdiff/graph/badge.svg?token=kXoAXWhLJS)](https://codecov.io/gh/konradreiche/coverdiff) | ||
|
||
Print your Go test coverage line-by-line in the form of a code diff, highlighting each line. This tool takes inspiration from Go's HTML presentation of test coverage but brings it to the terminal instead. | ||
|
||
[![asciicast](https://asciinema.org/a/627967.svg)](https://asciinema.org/a/627967) | ||
|
||
## Getting Started | ||
|
||
``` | ||
go install github.com/konradreiche/coverdiff | ||
``` | ||
|
||
## Usage | ||
|
||
``` | ||
Usage: | ||
coverdiff [file] | ||
Flags: | ||
-h, --help print help text | ||
Examples: | ||
go test -cover -coverprofile=coverage.out | ||
cat coverage.out | coverdiff | ||
go test -cover -coverprofile >(coverdiff) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
ignore: | ||
- "testdata/**/*" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package main | ||
|
||
import ( | ||
"io" | ||
"os" | ||
|
||
"golang.org/x/tools/cover" | ||
) | ||
|
||
func command(stdin io.Reader, stdout io.Writer) error { | ||
// parse coverage profiles from stdin or file path if provided | ||
profiles, err := parseCoverProfiles(stdin) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// find path that points to module root which will be needed to construct an | ||
// absolute file path to the Go source files to generate a diff for | ||
moduleInfo, err := findModuleInfo() | ||
if err != nil { | ||
return err | ||
} | ||
return printDiff(stdout, profiles, moduleInfo.modulePath) | ||
} | ||
|
||
func parseCoverProfiles(stdin io.Reader) ([]*cover.Profile, error) { | ||
if len(os.Args) > 1 && os.Args[1] != "-" { | ||
profiles, err := cover.ParseProfiles(os.Args[1]) | ||
return profiles, err | ||
} | ||
profiles, err := cover.ParseProfilesFromReader(stdin) | ||
return profiles, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
) | ||
|
||
func TestCommand(t *testing.T) { | ||
t.Run("from-stdin-pipe", func(t *testing.T) { | ||
args := os.Args | ||
t.Cleanup(func() { os.Args = args }) | ||
|
||
// override os.Args which contains flags from the test binary | ||
os.Args = []string{ | ||
"coverdiff", | ||
} | ||
|
||
var stdout bytes.Buffer | ||
stdin := bytes.NewBufferString(readFile(t, "testdata/coverage.out")) | ||
if err := command(stdin, &stdout); err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
got := stdout.String() | ||
want := readFile(t, "testdata/coverdiff.out") | ||
if got != want { | ||
t.Errorf("got len=%d, want len=%d", len(got), len(want)) | ||
} | ||
}) | ||
|
||
t.Run("from-file", func(t *testing.T) { | ||
os.Args[1] = filepath.Join(projectPath, "testdata/coverage.out") | ||
|
||
var stdout bytes.Buffer | ||
if err := command(nil, &stdout); err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
got := stdout.String() | ||
want := readFile(t, "testdata/coverdiff.out") | ||
if got != want { | ||
t.Errorf("got len=%d, want len=%d", len(got), len(want)) | ||
} | ||
}) | ||
|
||
t.Run("outside-go-module", func(t *testing.T) { | ||
changeDir(t, "..") | ||
|
||
var stdout bytes.Buffer | ||
err := command(nil, &stdout) | ||
|
||
got := err.Error() | ||
want := "findModuleDir: no go.mod file found" | ||
if got != want { | ||
t.Errorf("got %s, want: %s", got, want) | ||
} | ||
}) | ||
} | ||
|
||
func readFile(tb testing.TB, name string) string { | ||
tb.Helper() | ||
b, err := os.ReadFile(name) | ||
if err != nil { | ||
tb.Fatal(err) | ||
} | ||
return string(b) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"os" | ||
"strings" | ||
|
||
"golang.org/x/tools/cover" | ||
) | ||
|
||
func printDiff(stdout io.Writer, profiles []*cover.Profile, modulePath string) error { | ||
for _, profile := range profiles { | ||
// create file path using absolute path to module | ||
filepath := strings.ReplaceAll(profile.FileName, modulePath+"/", "") | ||
|
||
b, err := os.ReadFile(filepath) | ||
if err != nil { | ||
return fmt.Errorf("os.ReadFile: %w", err) | ||
} | ||
lines := strings.Split(string(b), "\n") | ||
|
||
// track coverage mapping source code line to profile block | ||
blockByLine := make(map[int][]cover.ProfileBlock) | ||
|
||
for _, block := range profile.Blocks { | ||
for i := block.StartLine; i <= block.EndLine; i++ { | ||
// handle coverage of blocks by skipping coverage for only one line | ||
// otherwise we will print duplicate lines | ||
if len(blockByLine[i]) == 1 { | ||
continue | ||
} | ||
blockByLine[i] = append(blockByLine[i], block) | ||
} | ||
} | ||
|
||
// print diff file headers | ||
fmt.Fprintf(stdout, "diff --git a/%s b/%s\n", filepath, filepath) | ||
fmt.Fprintf(stdout, "--- a/%s\n", filepath) | ||
fmt.Fprintf(stdout, "+++ b/%s\n", filepath) | ||
|
||
// print diff index header | ||
fmt.Fprintf(stdout, "@@ -%d,%d +%d,%d @@ %s\n", 0, 0, len(lines), 0, lines[0]) | ||
|
||
// print all lines regardless of coverage | ||
for i, line := range lines[1:] { | ||
blocks, ok := blockByLine[i+2] | ||
if !ok { | ||
fmt.Fprintln(stdout, line) | ||
continue | ||
} | ||
for _, block := range blocks { | ||
if block.Count == 1 { | ||
fmt.Fprintf(stdout, "+%s\n", line) | ||
} else { | ||
fmt.Fprintf(stdout, "-%s\n", line) | ||
} | ||
} | ||
} | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"path/filepath" | ||
"runtime" | ||
"runtime/debug" | ||
"testing" | ||
|
||
"golang.org/x/tools/cover" | ||
) | ||
|
||
var ( | ||
_, b, _, _ = runtime.Caller(0) | ||
projectPath = filepath.Dir(b) | ||
) | ||
|
||
func TestPrintDiff(t *testing.T) { | ||
profiles, err := cover.ParseProfiles("testdata/coverage.out") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
bi, ok := debug.ReadBuildInfo() | ||
if !ok { | ||
t.Fatal("binary build information not available") | ||
} | ||
|
||
var b bytes.Buffer | ||
if err := printDiff(&b, profiles, bi.Path); err != nil { | ||
t.Fatal(err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
module github.com/konradreiche/coverdiff | ||
|
||
go 1.21.5 | ||
|
||
require ( | ||
golang.org/x/mod v0.14.0 | ||
golang.org/x/tools v0.16.0 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= | ||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= | ||
golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= | ||
golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
) | ||
|
||
const usage = `📑 coverdiff - print Go test coverage as diff | ||
Usage: | ||
coverdiff [file] | ||
Flags: | ||
-h, --help print help text | ||
coverdiff is a tool designed to process the cover profile output of Go and | ||
display the coverage as a diff, similar to Go's -html option but optimized for | ||
terminal convenience. You can provide the cover profile as a file or pass it | ||
through standard input. | ||
Examples: | ||
go test -cover -coverprofile=coverage.out | ||
cat coverage.out | coverdiff | ||
go test -cover -coverprofile >(coverdiff) | ||
` | ||
|
||
func printUsage(stderr io.Writer) func() { | ||
// return func() to conform with the required type of flag.CommandLine.Usage | ||
return func() { | ||
fmt.Fprint(stderr, usage) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"testing" | ||
) | ||
|
||
func TestPrintUsage(t *testing.T) { | ||
var stderr bytes.Buffer | ||
printUsage(&stderr)() | ||
|
||
got := stderr.String() | ||
want := usage | ||
if got != want { | ||
t.Errorf("got %s, want %s", got, want) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"os" | ||
) | ||
|
||
func main() { | ||
flag.CommandLine.Usage = printUsage(os.Stderr) | ||
flag.Parse() | ||
|
||
if err := command(os.Stdin, os.Stdout); err != nil { | ||
fmt.Fprintln(os.Stderr, fmt.Errorf("coverdiff: %w", err)) | ||
fmt.Fprintln(os.Stderr, "Use coverdiff --help to display help text") | ||
os.Exit(1) | ||
} | ||
} |
Oops, something went wrong.