From 6da686b9e0fbd83514e0cb2283ff921705f434c8 Mon Sep 17 00:00:00 2001 From: Evstigneev Denis Date: Sun, 15 Oct 2023 19:53:11 +0300 Subject: [PATCH] add calculate and merge coverage files --- go.mod | 5 +- go.sum | 1 + internal/analyzer/coverage/coverage.go | 94 +++++++++++++++++++++ internal/analyzer/coverage/coverage_test.go | 36 ++++++++ internal/analyzer/coverage/git.go | 70 +++++++++++++++ 5 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 internal/analyzer/coverage/coverage.go create mode 100644 internal/analyzer/coverage/coverage_test.go create mode 100644 internal/analyzer/coverage/git.go diff --git a/go.mod b/go.mod index cbaa451..995d1f7 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c github.com/jmoiron/sqlx v1.3.1 github.com/pkg/errors v0.9.1 + github.com/stretchr/testify v1.8.3 go.unistack.org/micro-client-http/v4 v4.0.2 go.unistack.org/micro-codec-json/v4 v4.0.0 go.unistack.org/micro-codec-jsonpb/v4 v4.0.0 @@ -25,6 +26,7 @@ require ( go.unistack.org/protoc-gen-go-micro/v4 v4.0.7 golang.org/x/mod v0.12.0 golang.org/x/sync v0.3.0 + golang.org/x/tools v0.11.0 google.golang.org/protobuf v1.31.0 modernc.org/sqlite v1.21.0 ) @@ -37,6 +39,7 @@ require ( github.com/acomagu/bufpipe v1.0.4 // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/cloudflare/circl v1.3.3 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/fatih/structtag v1.2.0 // indirect @@ -76,6 +79,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rs/zerolog v1.30.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect @@ -92,7 +96,6 @@ require ( golang.org/x/sys v0.11.0 // indirect golang.org/x/text v0.12.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.11.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 0f1c959..38b99f4 100644 --- a/go.sum +++ b/go.sum @@ -987,6 +987,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= diff --git a/internal/analyzer/coverage/coverage.go b/internal/analyzer/coverage/coverage.go new file mode 100644 index 0000000..368418b --- /dev/null +++ b/internal/analyzer/coverage/coverage.go @@ -0,0 +1,94 @@ +package coverage + +import ( + "context" + "fmt" + "git.unistack.org/unistack-org/pkgdash/internal/models" + "golang.org/x/tools/cover" + "io" + "strings" +) + +func Analyze(ctx context.Context, dataCoverage io.Reader, pack models.Package) (float64, error) { + calculcate, err := calculateFiles(dataCoverage) + if err != nil { + return 0, err + } + + mapCover := make(map[string]float64) + { + tree, err := GetTreeFromGit(ctx, pack.URL) + if err != nil { + return 0, err + } + list, err := tree.GoFileList("") + if err != nil { + return 0, err + } + + for _, f := range list { + mapCover[f] = 0.0 + } + } + + cur := len(mapCover) + + for _, d := range calculcate.Files { + file := strings.TrimPrefix(d.Name, pack.Name+"/") + mapCover[file] = d.Coverage + } + + // check) + if len(mapCover) != cur { + fmt.Printf("add new keys, was: %d, has: %d", cur, len(mapCover)) + } + + //TODO add calculate full + + return 0, nil +} + +type Data struct { + Files []*calculateFile + Set bool +} + +type calculateFile struct { + Name string + Coverage float64 +} + +func calculateFiles(coverSrc io.Reader) (d *Data, err error) { + profiles, err := cover.ParseProfilesFromReader(coverSrc) + if err != nil { + return nil, err + } + + d = new(Data) + for _, profile := range profiles { + fn := profile.FileName + if profile.Mode == "set" { + d.Set = true + } + + d.Files = append(d.Files, &calculateFile{ + Name: fn, + Coverage: percentCovered(profile), + }) + } + return d, err +} + +func percentCovered(p *cover.Profile) float64 { + var total, covered int64 + for _, b := range p.Blocks { + total += int64(b.NumStmt) + if b.Count > 0 { + covered += int64(b.NumStmt) + } + } + if total == 0 { + return 0 + } + return float64(covered) / float64(total) * 100 +} diff --git a/internal/analyzer/coverage/coverage_test.go b/internal/analyzer/coverage/coverage_test.go new file mode 100644 index 0000000..ee00bd7 --- /dev/null +++ b/internal/analyzer/coverage/coverage_test.go @@ -0,0 +1,36 @@ +package coverage + +import ( + "context" + "git.unistack.org/unistack-org/pkgdash/internal/models" + "github.com/stretchr/testify/assert" + "os" + "testing" +) + +func Test_Calculate(t *testing.T) { + file, err := os.Open("cover_test.out") + assert.Nil(t, err) + defer func() { + assert.Nil(t, file.Close()) + }() + + dataFiles, err := calculateFiles(file) + assert.Nil(t, err) + assert.NotNil(t, dataFiles) +} + +func Test_Analyze(t *testing.T) { + file, err := os.Open("cover_test.out") + assert.Nil(t, err) + defer func() { + assert.Nil(t, file.Close()) + }() + + analyze, err := Analyze(context.Background(), file, models.Package{ + Name: "go.unistack.org/micro/v4", + URL: "https://git.unistack.org/unistack-org/micro.git", + }) + assert.Nil(t, err) + assert.Equal(t, analyze, 0.0) +} diff --git a/internal/analyzer/coverage/git.go b/internal/analyzer/coverage/git.go new file mode 100644 index 0000000..19212d8 --- /dev/null +++ b/internal/analyzer/coverage/git.go @@ -0,0 +1,70 @@ +package coverage + +import ( + "context" + "errors" + "fmt" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/filemode" + "github.com/go-git/go-git/v5/plumbing/object" + "github.com/go-git/go-git/v5/storage/memory" + "os" + "regexp" + "strings" +) + +var ( + fileNil = errors.New("file pointer is nil") +) + +func GetTreeFromGit(ctx context.Context, url string) (*Tree, error) { + cloneOpts := &git.CloneOptions{ + URL: url, + Progress: os.Stdout, + } + + repo, err := git.CloneContext(ctx, memory.NewStorage(), nil, cloneOpts) + if err != nil { + return nil, err + } + + ref, err := repo.Head() + if err != nil { + return nil, fmt.Errorf("failed to get head: %v", err) + } + + commit, err := repo.CommitObject(ref.Hash()) + if err != nil { + return nil, fmt.Errorf("failed to get commit: %v", err) + } + + tree, err := commit.Tree() + + return &Tree{tree}, err +} + +type Tree struct { + *object.Tree +} + +func (t Tree) GoFileList(pattern string) ([]string, error) { + matcher, err := regexp.Compile(pattern) + if err != nil { + return nil, err + } + + var list []string + err = t.Files().ForEach(func(file *object.File) error { + if file == nil { + return fileNil + } + if file.Mode == filemode.Regular && strings.HasSuffix(file.Name, ".go") && !strings.HasSuffix(file.Name, "_test.go") && matcher.MatchString(file.Name) { + list = append(list, file.Name) + } + return nil + }) + if err != nil { + return nil, err + } + return list, err +}