diff --git a/cmd/profile.go b/cmd/profile.go index 7dfe51e..e6fdf6c 100644 --- a/cmd/profile.go +++ b/cmd/profile.go @@ -56,6 +56,7 @@ goc profile --force Service: svrList, Address: addrList, CoverFilePatterns: coverFilePatterns, + SkipFilePatterns: skipFilePatterns, } res, err := cover.NewWorker(center).Profile(p) if err != nil { @@ -84,6 +85,7 @@ var ( force bool // --force flag output string // --output flag coverFilePatterns []string // --coverfile flag + skipFilePatterns []string // --skipfile flag ) func init() { @@ -92,6 +94,7 @@ func init() { profileCmd.Flags().StringSliceVarP(&addrList, "address", "", nil, "address to fetch profile, see 'goc list' for all addresses.") profileCmd.Flags().BoolVarP(&force, "force", "f", false, "force fetching all available profiles") profileCmd.Flags().StringSliceVarP(&coverFilePatterns, "coverfile", "", nil, "only output coverage data of the files matching the patterns") + profileCmd.Flags().StringSliceVarP(&skipFilePatterns, "skipfile", "", nil, "skip the files matching the patterns when outputing coverage data") addBasicFlags(profileCmd.Flags()) rootCmd.AddCommand(profileCmd) } diff --git a/docs/images/wechat.png b/docs/images/wechat.png index 6485ae2..c839152 100644 Binary files a/docs/images/wechat.png and b/docs/images/wechat.png differ diff --git a/pkg/cover/client_test.go b/pkg/cover/client_test.go index f858872..4313dfe 100644 --- a/pkg/cover/client_test.go +++ b/pkg/cover/client_test.go @@ -127,6 +127,18 @@ func TestClientAction(t *testing.T) { param: ProfileParam{CoverFilePatterns: []string{"b.go$"}}, expected: "b/b.go", }, + { + name: "valid test with skipfile flag provided", + service: ServiceUnderTest{Name: "serviceOK", Address: profileSuccessMockSvr.URL}, + param: ProfileParam{SkipFilePatterns: []string{"b.go$"}}, + expected: "main.go", + }, + { + name: "valid test with both skipfile and coverfile flags provided", + service: ServiceUnderTest{Name: "serviceOK", Address: profileSuccessMockSvr.URL}, + param: ProfileParam{SkipFilePatterns: []string{"main.go"}, CoverFilePatterns: []string{".go$"}}, + expected: "b.go", + }, } for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { diff --git a/pkg/cover/server.go b/pkg/cover/server.go index b8420b6..72e946b 100644 --- a/pkg/cover/server.go +++ b/pkg/cover/server.go @@ -108,6 +108,7 @@ type ProfileParam struct { Service []string `form:"service" json:"service"` Address []string `form:"address" json:"address"` CoverFilePatterns []string `form:"coverfile" json:"coverfile"` + SkipFilePatterns []string `form:"skipfile" json:"skipfile"` } //listServices list all the registered services @@ -209,6 +210,14 @@ func (s *server) profile(c *gin.Context) { } } + if len(body.SkipFilePatterns) > 0 { + merged, err = skipProfile(body.SkipFilePatterns, merged) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to skip profile based on the patterns: %v, error: %v", body.SkipFilePatterns, err)}) + return + } + } + if err := cov.DumpProfile(merged, c.Writer); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return @@ -234,6 +243,31 @@ func filterProfile(coverFile []string, profiles []*cover.Profile) ([]*cover.Prof return out, nil } +// skipProfile skips profiles of the packages matching the skipFile pattern +func skipProfile(skipFile []string, profiles []*cover.Profile) ([]*cover.Profile, error) { + var out = make([]*cover.Profile, 0) + for _, profile := range profiles { + var shouldSkip bool + for _, pattern := range skipFile { + matched, err := regexp.MatchString(pattern, profile.FileName) + if err != nil { + return nil, fmt.Errorf("filterProfile failed with pattern %s for profile %s, err: %v", pattern, profile.FileName, err) + } + + if matched { + shouldSkip = true + break // no need to check again for the file + } + } + + if !shouldSkip { + out = append(out, profile) + } + } + + return out, nil +} + func (s *server) clear(c *gin.Context) { var body ProfileParam if err := c.ShouldBind(&body); err != nil { diff --git a/pkg/cover/server_test.go b/pkg/cover/server_test.go index bc8756b..5ccbaaa 100644 --- a/pkg/cover/server_test.go +++ b/pkg/cover/server_test.go @@ -421,6 +421,102 @@ func TestFilterProfile(t *testing.T) { } } +func TestSkipProfile(t *testing.T) { + var tcs = []struct { + name string + pattern []string + input []*cover.Profile + output []*cover.Profile + expectErr bool + }{ + { + name: "normal path", + pattern: []string{"some/fancy/gopath", "a/fancy/gopath"}, + input: []*cover.Profile{ + { + FileName: "some/fancy/gopath/a.go", + }, + { + FileName: "some/fancy/gopath/b/a.go", + }, + { + FileName: "a/fancy/gopath/a.go", + }, + { + FileName: "b/fancy/gopath/a.go", + }, + { + FileName: "b/a/fancy/gopath/a.go", + }, + }, + output: []*cover.Profile{ + { + FileName: "b/fancy/gopath/a.go", + }, + }, + }, + { + name: "with regular expression", + pattern: []string{"fancy/gopath/a.go$", "^b/a/"}, + input: []*cover.Profile{ + { + FileName: "some/fancy/gopath/a.go", + }, + { + FileName: "some/fancy/gopath/b/a.go", + }, + { + FileName: "a/fancy/gopath/a.go", + }, + { + FileName: "b/fancy/gopath/c/a.go", + }, + { + FileName: "b/a/fancy/gopath/a.go", + }, + }, + output: []*cover.Profile{ + { + FileName: "some/fancy/gopath/b/a.go", + }, + { + FileName: "b/fancy/gopath/c/a.go", + }, + }, + }, + { + name: "with invalid regular expression", + pattern: []string{"(?!a)"}, + input: []*cover.Profile{ + { + FileName: "some/fancy/gopath/a.go", + }, + }, + expectErr: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + out, err := skipProfile(tc.pattern, tc.input) + if err != nil { + if !tc.expectErr { + t.Errorf("Unexpected error: %v", err) + } + return + } + + if tc.expectErr { + t.Errorf("Expected an error, but got value %s", stringifyCoverProfile(out)) + } + + if !reflect.DeepEqual(out, tc.output) { + t.Errorf("Mismatched results. \nExpected: %s\nActual:%s", stringifyCoverProfile(tc.output), stringifyCoverProfile(out)) + } + }) + } +} + func stringifyCoverProfile(profiles []*cover.Profile) string { res := make([]cover.Profile, 0, len(profiles)) for _, p := range profiles { diff --git a/tests/profile.bats b/tests/profile.bats index 6a64bf2..f4a4b7c 100755 --- a/tests/profile.bats +++ b/tests/profile.bats @@ -128,6 +128,26 @@ setup() { [ "$status" -eq 0 ] [[ "$output" == *"mode: count"* ]] + wait $profile_pid + kill -9 $SAMPLE_PID +} + +@test "test goc profile with coverfile and skipfile flags" { + ./simple-project 3>&- & + SAMPLE_PID=$! + sleep 2 + + wait_profile_backend "profile6" & + profile_pid=$! + + run gocc profile --center=http://127.0.0.1:60001 --coverfile="a.go$,b.go$" --skipfile="b.go$" --debug --debugcisyncfile ci-sync.bak; + info $output + [ "$status" -eq 0 ] + [[ "$output" == *"mode: count"* ]] + [[ "$output" == *"a.go"* ]] # contains a.go file + [[ "$output" != *"b.go"* ]] # not contains b.go file + [[ "$output" != *"main.go"* ]] # not contains main.go file + wait $profile_pid kill -9 $SAMPLE_PID } \ No newline at end of file