Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor to support importing from go #12

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
.PHONY: build test inttest clean

build:
GOOS=linux GOARCH=amd64 go build -o bin/gcov2lcov-linux-amd64 .
GOOS=windows GOARCH=amd64 go build -o bin/gcov2lcov-win-amd64 .
GOOS=darwin GOARCH=amd64 go build -o bin/gcov2lcov-darwin-amd64 .
GOOS=linux GOARCH=amd64 go build -o bin/gcov2lcov-linux-amd64 ./cmd
GOOS=windows GOARCH=amd64 go build -o bin/gcov2lcov-win-amd64 ./cmd
GOOS=darwin GOARCH=amd64 go build -o bin/gcov2lcov-darwin-amd64 ./cmd

test:
go test ./... -coverprofile coverage.out
Expand Down
68 changes: 68 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// gcov2lcov - convert golang coverage files to the lcov format.
//
// Copyright (c) 2019 Jan Delgado
// Copyright (c) 2019 Richard S Allinson
//
// Credits:
// This tool is based on covfmt (https://github.com/ricallinson/covfmt) and
// uses some parts of goveralls (https://github.com/mattn/goveralls).
//
package main

import (
"flag"
"github.com/jandelgado/gcov2lcov"
"log"
"os"
)

func main() {
os.Exit(gcovmain())
}

func gcovmain() int {
infileName := flag.String("infile", "", "go coverage file to read, default: <stdin>")
outfileName := flag.String("outfile", "", "lcov file to write, default: <stdout>")
useAbsoluteSourcePath := flag.Bool("use-absolute-source-path", false,
"use absolute paths for source file in lcov output, default: false")
flag.Parse()
if len(flag.Args()) > 0 {
flag.Usage()
return 1
}

infile := os.Stdin
outfile := os.Stdout
var err error
if *infileName != "" {
infile, err = os.Open(*infileName)
if err != nil {
log.Printf("error opening input file: %v\n", err)
return 2
}
defer infile.Close()
}
if *outfileName != "" {
outfile, err = os.Create(*outfileName)
if err != nil {
log.Printf("error opening output file: %v\n", err)
return 3
}
defer outfile.Close()
}

var pathResolverFunc gcov2lcov.PathResolver
if *useAbsoluteSourcePath {
pathResolverFunc = gcov2lcov.AbsolutePathResolver
} else {
pathResolverFunc = gcov2lcov.RelativePathResolver
}

err = gcov2lcov.ConvertCoverage(infile, outfile, pathResolverFunc)
if err != nil {
log.Printf("error: convert: %v", err)
return 4
}
return 0
}

106 changes: 30 additions & 76 deletions main.go → gcov2lcov.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@
// This tool is based on covfmt (https://github.com/ricallinson/covfmt) and
// uses some parts of goveralls (https://github.com/mattn/goveralls).
//
package main
package gcov2lcov

import (
"bufio"
"errors"
"flag"
"go/build"
"io"
"log"
Expand Down Expand Up @@ -41,6 +40,33 @@ type cacheEntry struct {

var pkgCache = map[string]cacheEntry{}

type PathResolver func(name string) (string, error)

func ConvertCoverage(in io.Reader, out io.Writer, pathResolverFunc PathResolver) error {
blocks, err := parseCoverage(in, pathResolverFunc)
if err != nil {
return err
}
return writeLcov(blocks, out)
}

func AbsolutePathResolver(name string) (string, error) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the name is a little bit misleading and should have been named something like IdentityPathResolver since the function does not actually change or make a path absolute (as can be seen in the test)

return name, nil
}

func RelativePathResolver(name string) (string, error) {
name, err := findFile(name)
if err != nil {
return "", err
}

if dir, ok := findRepositoryRoot(name); ok {
filename := strings.TrimPrefix(name, dir+string(os.PathSeparator))
return filename, nil
}
return name, nil
}

// given a module+file spec (e.g. github.com/jandelgado/gcov2lcov/main.go),
// strip of the module name and return the file name (e.g. main.go).
func findFile(filePath string) (string, error) {
Expand Down Expand Up @@ -73,18 +99,6 @@ func findRepositoryRoot(dir string) (string, bool) {
return findRepositoryRoot(nextdir)
}

func getSourceFileName(name string) string {
return name
}

func getCoverallsSourceFileName(name string) string {
if dir, ok := findRepositoryRoot(name); ok {
filename := strings.TrimPrefix(name, dir+string(os.PathSeparator))
return filename
}
return name
}

func keysOfMap(m map[int]int) []int {
keys := make([]int, len(m))
i := 0
Expand Down Expand Up @@ -203,7 +217,7 @@ func parseCoverageLine(line string) (string, *block, error) {
return path[0], b, err
}

func parseCoverage(coverage io.Reader, pathResolverFunc func(string) string) (map[string][]*block, error) {
func parseCoverage(coverage io.Reader, pathResolverFunc PathResolver) (map[string][]*block, error) {
scanner := bufio.NewScanner(coverage)
blocks := map[string][]*block{}
for scanner.Scan() {
Expand All @@ -212,14 +226,12 @@ func parseCoverage(coverage io.Reader, pathResolverFunc func(string) string) (ma
continue
}
if f, b, err := parseCoverageLine(line); err == nil {
f, err := findFile(f)
f, err = pathResolverFunc(f)
if err != nil {
log.Printf("warn: %v", err)
continue
}

f = pathResolverFunc(f)

// Make sure the filePath is a key in the map.
if _, found := blocks[f]; !found {
blocks[f] = []*block{}
Expand All @@ -235,61 +247,3 @@ func parseCoverage(coverage io.Reader, pathResolverFunc func(string) string) (ma
}
return blocks, nil
}

func convertCoverage(in io.Reader, out io.Writer, pathResolverFunc func(string) string) error {
blocks, err := parseCoverage(in, pathResolverFunc)
if err != nil {
return err
}
return writeLcov(blocks, out)
}

func main() {
os.Exit(gcovmain())
}

func gcovmain() int {
infileName := flag.String("infile", "", "go coverage file to read, default: <stdin>")
outfileName := flag.String("outfile", "", "lcov file to write, default: <stdout>")
useAbsoluteSourcePath := flag.Bool("use-absolute-source-path", false,
"use absolute paths for source file in lcov output, default: false")
flag.Parse()
if len(flag.Args()) > 0 {
flag.Usage()
return 1
}

infile := os.Stdin
outfile := os.Stdout
var err error
if *infileName != "" {
infile, err = os.Open(*infileName)
if err != nil {
log.Printf("error opening input file: %v\n", err)
return 2
}
defer infile.Close()
}
if *outfileName != "" {
outfile, err = os.Create(*outfileName)
if err != nil {
log.Printf("error opening output file: %v\n", err)
return 3
}
defer outfile.Close()
}

var pathResolverFunc func(string) string
if *useAbsoluteSourcePath {
pathResolverFunc = getSourceFileName
} else {
pathResolverFunc = getCoverallsSourceFileName
}

err = convertCoverage(infile, outfile, pathResolverFunc)
if err != nil {
log.Printf("error: convert: %v", err)
return 4
}
return 0
}
39 changes: 17 additions & 22 deletions main_test.go → gcov2lcov_test.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
// gcov2lcov - convert golang coverage files to the lcov format.
// (c) 2019 Jan Delgado
package main
package gcov2lcov

import (
"bytes"
"os"
"github.com/stretchr/testify/assert"
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

func TestKeysOfMapReturnsAllKeysOfMap(t *testing.T) {
Expand All @@ -35,11 +31,11 @@ func TestParseCoverageLineFailsOnInvalidLines(t *testing.T) {
}

func TestParseCoverageLineOfParsesValidLineCorrectly(t *testing.T) {
line := "github.com/jandelgado/gcov2lcov/main.go:6.14,8.3 2 1"
line := "github.com/jandelgado/gcov2lcov/cmd/main.go:6.14,8.3 2 1"
file, b, err := parseCoverageLine(line)

assert.Nil(t, err)
assert.Equal(t, "github.com/jandelgado/gcov2lcov/main.go", file)
assert.Equal(t, "github.com/jandelgado/gcov2lcov/cmd/main.go", file)
assert.Equal(t, 6, b.startLine)
assert.Equal(t, 14, b.startChar)
assert.Equal(t, 8, b.endLine)
Expand All @@ -53,17 +49,17 @@ func TestParseCoverage(t *testing.T) {
// note: in this integrative test, the package path must match the actual
// repository name of this project.
cov := `mode: set
github.com/jandelgado/gcov2lcov/main.go:6.14,8.3 2 1`
github.com/jandelgado/gcov2lcov/cmd/main.go:6.14,8.3 2 1`

reader := strings.NewReader(cov)
res, err := parseCoverage(reader, getCoverallsSourceFileName)
res, err := parseCoverage(reader, RelativePathResolver)

assert.NoError(t, err)
assert.Equal(t, 1, len(res))
for k, blks := range res {
assert.Equal(t, 1, len(blks))
b := blks[0]
assert.Equal(t, "main.go", k)
assert.Equal(t, "cmd/main.go", k)
assert.Equal(t, 6, b.startLine)
assert.Equal(t, 14, b.startChar)
assert.Equal(t, 8, b.endLine)
Expand All @@ -78,16 +74,16 @@ func TestConvertCoverage(t *testing.T) {
// repository name of this project. Format:
// name.go:line.column,line.column numberOfStatements count
cov := `mode: set
github.com/jandelgado/gcov2lcov/main.go:6.14,8.3 2 1
github.com/jandelgado/gcov2lcov/main.go:7.14,9.3 2 0
github.com/jandelgado/gcov2lcov/main.go:10.1,11.10 2 2`
github.com/jandelgado/gcov2lcov/cmd/main.go:6.14,8.3 2 1
github.com/jandelgado/gcov2lcov/cmd/main.go:7.14,9.3 2 0
github.com/jandelgado/gcov2lcov/cmd/main.go:10.1,11.10 2 2`

in := strings.NewReader(cov)
out := bytes.NewBufferString("")
err := convertCoverage(in, out, getCoverallsSourceFileName)
err := ConvertCoverage(in, out, RelativePathResolver)

expected := `TN:
SF:main.go
SF:cmd/main.go
DA:6,1
DA:7,1
DA:8,1
Expand All @@ -103,12 +99,11 @@ end_of_record
}

func TestPathResolverFunc(t *testing.T) {
pwd, err := os.Getwd()
name, err := RelativePathResolver("github.com/jandelgado/gcov2lcov/cmd/main.go")
assert.NoError(t, err)
assert.Equal(t, "cmd/main.go", name)

name := getCoverallsSourceFileName(pwd + "/main.go")
assert.Equal(t, "main.go", name)

name = getSourceFileName(pwd + "/main.go")
assert.Equal(t, pwd+"/main.go", name)
name, err = AbsolutePathResolver("github.com/jandelgado/gcov2lcov/cmd/main.go")
assert.NoError(t, err)
assert.Equal(t, "github.com/jandelgado/gcov2lcov/cmd/main.go", name)
}