From ca15b18860a095195c078d82dcad5d739d606a2b Mon Sep 17 00:00:00 2001 From: jichangjun Date: Tue, 1 Sep 2020 21:56:56 +0800 Subject: [PATCH 1/6] goc profile: add coverpkg flag --- cmd/diff_test.go | 4 ---- cmd/profile.go | 18 +++++++++++++----- pkg/cover/server.go | 25 ++++++++++++++++++++++++- pkg/cover/server_test.go | 9 +++++++++ 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/cmd/diff_test.go b/cmd/diff_test.go index 5fa4670..d8a6426 100644 --- a/cmd/diff_test.go +++ b/cmd/diff_test.go @@ -104,7 +104,3 @@ func TestDoDiffForLocalProfiles(t *testing.T) { } } - -func TestDoDiffUnderProw(t *testing.T) { - -} diff --git a/cmd/profile.go b/cmd/profile.go index 16e98fd..7c84d47 100644 --- a/cmd/profile.go +++ b/cmd/profile.go @@ -19,11 +19,11 @@ package cmd import ( "bytes" "fmt" - log "github.com/sirupsen/logrus" "io" "os" "github.com/qiniu/goc/pkg/cover" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -74,16 +74,24 @@ goc profile --force }, } -var output string -var force bool -var svrList []string -var addrList []string +var ( + svrList []string // --service flag + addrList []string // --address flag + force bool // --force flag + output string // --output flag + coverPkg []string // --coverpkg flag +) func init() { profileCmd.Flags().StringVarP(&output, "output", "o", "", "download cover profile") profileCmd.Flags().StringSliceVarP(&svrList, "service", "", nil, "service name to fetch profile, see 'goc list' for all services.") 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(&coverPkg, "coverpkg", "", nil, "only output coverage data of the packages matching the patterns") addBasicFlags(profileCmd.Flags()) rootCmd.AddCommand(profileCmd) } + +func filterProfile() { + +} diff --git a/pkg/cover/server.go b/pkg/cover/server.go index 0d09f52..a039588 100644 --- a/pkg/cover/server.go +++ b/pkg/cover/server.go @@ -25,6 +25,7 @@ import ( "net/http" "net/url" "os" + "regexp" "strconv" "github.com/gin-gonic/gin" @@ -83,11 +84,13 @@ type Service struct { Address string `form:"address" json:"address" binding:"required"` } -// ProfileParam is param of profile API (TODO) +// ProfileParam is param of profile API type ProfileParam struct { Force bool `form:"force"` Service []string `form:"service" json:"service"` Address []string `form:"address" json:"address"` + + CoverPkg []string } //listServices list all the registered services @@ -138,6 +141,7 @@ func profile(c *gin.Context) { c.JSON(http.StatusExpectationFailed, gin.H{"error": "invalid param"}) return } + serviceList := removeDuplicateElement(c.QueryArray("service")) addressList := removeDuplicateElement(c.QueryArray("address")) allInfos := DefaultStore.GetAll() @@ -183,6 +187,25 @@ func profile(c *gin.Context) { } } +// filterProfile output profiles of the packages matching the coverPkg +func filterProfile(coverPkg []string, profiles []*cover.Profile) ([]*cover.Profile, error) { + var out = make([]*cover.Profile, 0) + + for _, profile := range profiles { + for _, pattern := range coverPkg { + matched, err := regexp.MatchString(pattern, profile.FileName) + if err != nil { + return nil, fmt.Errorf("filterProfile failed with pattern %s for profile %s", pattern, profile.FileName) + } + if matched { + out = append(out, profile) + } + } + } + + return out, nil +} + func clear(c *gin.Context) { svrsUnderTest := DefaultStore.GetAll() for svc, addrs := range svrsUnderTest { diff --git a/pkg/cover/server_test.go b/pkg/cover/server_test.go index 806c63a..4df5c45 100644 --- a/pkg/cover/server_test.go +++ b/pkg/cover/server_test.go @@ -214,3 +214,12 @@ func TestInitService(t *testing.T) { assert.Equal(t, http.StatusInternalServerError, w.Code) assert.Contains(t, w.Body.String(), "lala error") } + +func TestFilterProfile(t *testing.T) { + // var tcs = []struct { + // pattern []string + // profile []*cover.Profile + // expected []*cover.Profile + // err error + // }{} +} From d648bef19e2e3892d6eaa0d44ac4e76a1eb27c8d Mon Sep 17 00:00:00 2001 From: jichangjun Date: Wed, 2 Sep 2020 22:21:16 +0800 Subject: [PATCH 2/6] add case for filterProfile func --- pkg/cover/server.go | 2 +- pkg/cover/server_test.go | 81 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 76 insertions(+), 7 deletions(-) diff --git a/pkg/cover/server.go b/pkg/cover/server.go index a039588..ac9b0f0 100644 --- a/pkg/cover/server.go +++ b/pkg/cover/server.go @@ -187,7 +187,7 @@ func profile(c *gin.Context) { } } -// filterProfile output profiles of the packages matching the coverPkg +// filterProfile filters profiles of the packages matching the coverPkg func filterProfile(coverPkg []string, profiles []*cover.Profile) ([]*cover.Profile, error) { var out = make([]*cover.Profile, 0) diff --git a/pkg/cover/server_test.go b/pkg/cover/server_test.go index 4df5c45..3754849 100644 --- a/pkg/cover/server_test.go +++ b/pkg/cover/server_test.go @@ -6,11 +6,13 @@ import ( "net/http/httptest" "net/url" "os" + "reflect" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "golang.org/x/tools/cover" ) // MockStore is mock store mainly for unittest @@ -216,10 +218,77 @@ func TestInitService(t *testing.T) { } func TestFilterProfile(t *testing.T) { - // var tcs = []struct { - // pattern []string - // profile []*cover.Profile - // expected []*cover.Profile - // err error - // }{} + var tcs = []struct { + name string + pattern []string + input []*cover.Profile + output []*cover.Profile + expectErr bool + }{ + { + name: "valid test", + 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: "some/fancy/gopath/a.go", + }, + { + FileName: "some/fancy/gopath/b/a.go", + }, + { + FileName: "a/fancy/gopath/a.go", + }, + { + FileName: "b/a/fancy/gopath/a.go", + }, + }, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + out, err := filterProfile(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 { + res = append(res, *p) + } + + return fmt.Sprintf("%#v", res) } From 3f29fd78ca26843ae5e42839e9890de1a423fc9d Mon Sep 17 00:00:00 2001 From: jichangjun Date: Fri, 4 Sep 2020 21:16:42 +0800 Subject: [PATCH 3/6] redesign profile api --- cmd/profile.go | 26 ++++++++++---------- pkg/cover/client.go | 38 +++++++++++++++++------------- pkg/cover/client_test.go | 2 +- pkg/cover/server.go | 51 ++++++++++++++++++++++++++-------------- pkg/cover/server_test.go | 37 ++++++++++++++++++++++++++--- 5 files changed, 103 insertions(+), 51 deletions(-) diff --git a/cmd/profile.go b/cmd/profile.go index 7c84d47..503f49f 100644 --- a/cmd/profile.go +++ b/cmd/profile.go @@ -44,14 +44,18 @@ goc profile --service=service1,service2,service3 # Get coverage counter of several specified addresses. You can get all available addresses from command 'goc list'. Use 'service' and 'address' flag at the same time may cause ambiguity, please use them separately. goc profile --address=address1,address2,address3 +# Only get the coverage data of files matching the special patterns +goc profile --coverfile=pattern1,pattern2,pattern3 + # Force fetching all available profiles. goc profile --force `, Run: func(cmd *cobra.Command, args []string) { p := cover.ProfileParam{ - Force: force, - Service: svrList, - Address: addrList, + Force: force, + Service: svrList, + Address: addrList, + CoverFilePatterns: coverFilePatterns, } res, err := cover.NewWorker(center).Profile(p) if err != nil { @@ -75,11 +79,11 @@ goc profile --force } var ( - svrList []string // --service flag - addrList []string // --address flag - force bool // --force flag - output string // --output flag - coverPkg []string // --coverpkg flag + svrList []string // --service flag + addrList []string // --address flag + force bool // --force flag + output string // --output flag + coverFilePatterns []string // --coverfile flag ) func init() { @@ -87,11 +91,7 @@ func init() { profileCmd.Flags().StringSliceVarP(&svrList, "service", "", nil, "service name to fetch profile, see 'goc list' for all services.") 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(&coverPkg, "coverpkg", "", nil, "only output coverage data of the packages matching the patterns") + profileCmd.Flags().StringSliceVarP(&coverFilePatterns, "coverfile", "", nil, "only output coverage data of the files matching the patterns") addBasicFlags(profileCmd.Flags()) rootCmd.AddCommand(profileCmd) } - -func filterProfile() { - -} diff --git a/pkg/cover/client.go b/pkg/cover/client.go index 361d62d..c9332e7 100644 --- a/pkg/cover/client.go +++ b/pkg/cover/client.go @@ -17,13 +17,14 @@ package cover import ( + "bytes" + "encoding/json" "fmt" "io" "io/ioutil" "net" "net/http" "net/url" - "strconv" "strings" log "github.com/sirupsen/logrus" @@ -76,35 +77,34 @@ func (c *client) RegisterService(srv Service) ([]byte, error) { return nil, fmt.Errorf("invalid service name") } u := fmt.Sprintf("%s%s?name=%s&address=%s", c.Host, CoverRegisterServiceAPI, srv.Name, srv.Address) - _, res, err := c.do("POST", u, nil) + _, res, err := c.do("POST", u, "", nil) return res, err } func (c *client) ListServices() ([]byte, error) { u := fmt.Sprintf("%s%s", c.Host, CoverServicesListAPI) - _, services, err := c.do("GET", u, nil) + _, services, err := c.do("GET", u, "", nil) if err != nil && isNetworkError(err) { - _, services, err = c.do("GET", u, nil) + _, services, err = c.do("GET", u, "", nil) } return services, err } func (c *client) Profile(param ProfileParam) ([]byte, error) { - u := fmt.Sprintf("%s%s?force=%s", c.Host, CoverProfileAPI, strconv.FormatBool(param.Force)) + u := fmt.Sprintf("%s%s", c.Host, CoverProfileAPI) if len(param.Service) != 0 && len(param.Address) != 0 { return nil, fmt.Errorf("use 'service' flag and 'address' flag at the same time may cause ambiguity, please use them separately") } - for _, svr := range param.Service { - u = u + "&service=" + svr + body, err := json.Marshal(param) + if err != nil { + return nil, fmt.Errorf("json.Marshal failed, param: %v, err:%v", param, err) } - for _, addr := range param.Address { - u = u + "&address=" + addr - } - res, profile, err := c.do("GET", u, nil) + + res, profile, err := c.do("POST", u, "application/json", bytes.NewReader(body)) if err != nil && isNetworkError(err) { - res, profile, err = c.do("GET", u, nil) + res, profile, err = c.do("POST", u, "application/json", bytes.NewReader(body)) } if err == nil && res.StatusCode != 200 { err = fmt.Errorf(string(profile)) @@ -114,29 +114,35 @@ func (c *client) Profile(param ProfileParam) ([]byte, error) { func (c *client) Clear() ([]byte, error) { u := fmt.Sprintf("%s%s", c.Host, CoverProfileClearAPI) - _, resp, err := c.do("POST", u, nil) + _, resp, err := c.do("POST", u, "", nil) if err != nil && isNetworkError(err) { - _, resp, err = c.do("POST", u, nil) + _, resp, err = c.do("POST", u, "", nil) } return resp, err } func (c *client) InitSystem() ([]byte, error) { u := fmt.Sprintf("%s%s", c.Host, CoverInitSystemAPI) - _, body, err := c.do("POST", u, nil) + _, body, err := c.do("POST", u, "", nil) return body, err } -func (c *client) do(method, url string, body io.Reader) (*http.Response, []byte, error) { +func (c *client) do(method, url, contentType string, body io.Reader) (*http.Response, []byte, error) { req, err := http.NewRequest(method, url, body) if err != nil { return nil, nil, err } + + if contentType != "" { + req.Header.Set("Content-Type", contentType) + } + res, err := c.client.Do(req) if err != nil { return nil, nil, err } defer res.Body.Close() + responseBody, err := ioutil.ReadAll(res.Body) if err != nil { return res, nil, err diff --git a/pkg/cover/client_test.go b/pkg/cover/client_test.go index 6ce7fee..df0f7e4 100644 --- a/pkg/cover/client_test.go +++ b/pkg/cover/client_test.go @@ -162,6 +162,6 @@ func TestClientDo(t *testing.T) { c := &client{ client: http.DefaultClient, } - _, _, err := c.do(" ", "http://127.0.0.1:7777", nil) // a invalid method + _, _, err := c.do(" ", "http://127.0.0.1:7777", "", nil) // a invalid method assert.Contains(t, err.Error(), "invalid method") } diff --git a/pkg/cover/server.go b/pkg/cover/server.go index ac9b0f0..1c11e92 100644 --- a/pkg/cover/server.go +++ b/pkg/cover/server.go @@ -26,7 +26,6 @@ import ( "net/url" "os" "regexp" - "strconv" "github.com/gin-gonic/gin" log "github.com/sirupsen/logrus" @@ -70,6 +69,7 @@ func GocServer(w io.Writer) *gin.Engine { { v1.POST("/cover/register", registerService) v1.GET("/cover/profile", profile) + v1.POST("/cover/profile", profile) v1.POST("/cover/clear", clear) v1.POST("/cover/init", initSystem) v1.GET("/cover/list", listServices) @@ -86,11 +86,10 @@ type Service struct { // ProfileParam is param of profile API type ProfileParam struct { - Force bool `form:"force"` - Service []string `form:"service" json:"service"` - Address []string `form:"address" json:"address"` - - CoverPkg []string + Force bool `form:"force" json:"force"` + Service []string `form:"service" json:"service"` + Address []string `form:"address" json:"address"` + CoverFilePatterns []string `form:"coverfile" json:"coverfile"` } //listServices list all the registered services @@ -135,17 +134,18 @@ func registerService(c *gin.Context) { return } +// profile API examples: +// POST /v1/cover/profile +// { "force": "true", "service":["a","b"], "address":["c","d"],"coverfile":["e","f"] } func profile(c *gin.Context) { - force, err := strconv.ParseBool(c.Query("force")) - if err != nil { - c.JSON(http.StatusExpectationFailed, gin.H{"error": "invalid param"}) + var body ProfileParam + if err := c.ShouldBind(&body); err != nil { + c.JSON(http.StatusExpectationFailed, gin.H{"error": err.Error()}) return } - serviceList := removeDuplicateElement(c.QueryArray("service")) - addressList := removeDuplicateElement(c.QueryArray("address")) allInfos := DefaultStore.GetAll() - filterAddrList, err := filterAddrs(serviceList, addressList, force, allInfos) + filterAddrList, err := filterAddrs(body.Service, body.Address, body.Force, allInfos) if err != nil { c.JSON(http.StatusExpectationFailed, gin.H{"error": err.Error()}) return @@ -155,13 +155,15 @@ func profile(c *gin.Context) { for _, addr := range filterAddrList { pp, err := NewWorker(addr).Profile(ProfileParam{}) if err != nil { - if force { + if body.Force { log.Warnf("get profile from [%s] failed, error: %s", addr, err.Error()) continue } - c.JSON(http.StatusExpectationFailed, gin.H{"error": err.Error()}) + + c.JSON(http.StatusExpectationFailed, gin.H{"error": fmt.Sprintf("failed to get profile from %s, error %s", addr, err.Error())}) return } + profile, err := convertProfile(pp) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) @@ -181,24 +183,32 @@ func profile(c *gin.Context) { return } + if len(body.CoverFilePatterns) > 0 { + merged, err = filterProfile(body.CoverFilePatterns, merged) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to filter profile based on the patterns: %v, error: %v", body.CoverFilePatterns, err)}) + return + } + } + if err := cov.DumpProfile(merged, c.Writer); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } } -// filterProfile filters profiles of the packages matching the coverPkg -func filterProfile(coverPkg []string, profiles []*cover.Profile) ([]*cover.Profile, error) { +// filterProfile filters profiles of the packages matching the coverFile pattern +func filterProfile(coverFile []string, profiles []*cover.Profile) ([]*cover.Profile, error) { var out = make([]*cover.Profile, 0) - for _, profile := range profiles { - for _, pattern := range coverPkg { + for _, pattern := range coverFile { matched, err := regexp.MatchString(pattern, profile.FileName) if err != nil { return nil, fmt.Errorf("filterProfile failed with pattern %s for profile %s", pattern, profile.FileName) } if matched { out = append(out, profile) + break // no need to check again for the file } } } @@ -261,9 +271,11 @@ func filterAddrs(serviceList, addressList []string, force bool, allInfos map[str for _, addr := range allInfos { addressAll = append(addressAll, addr...) } + if len(serviceList) != 0 && len(addressList) != 0 { return nil, fmt.Errorf("use 'service' flag and 'address' flag at the same time may cause ambiguity, please use them separately") } + // Add matched services to map for _, name := range serviceList { if addr, ok := allInfos[name]; ok { @@ -275,6 +287,7 @@ func filterAddrs(serviceList, addressList []string, force bool, allInfos map[str } log.Warnf("service [%s] not found", name) } + // Add matched addresses to map for _, addr := range addressList { if contains(addressAll, addr) { @@ -286,9 +299,11 @@ func filterAddrs(serviceList, addressList []string, force bool, allInfos map[str } log.Warnf("address [%s] not found", addr) } + if len(addressList) == 0 && len(serviceList) == 0 { filterAddrList = addressAll } + // Return all servers when all param is nil return filterAddrList, nil } diff --git a/pkg/cover/server_test.go b/pkg/cover/server_test.go index 3754849..2eb5d72 100644 --- a/pkg/cover/server_test.go +++ b/pkg/cover/server_test.go @@ -180,7 +180,7 @@ func TestProfileService(t *testing.T) { router.ServeHTTP(w, req) assert.Equal(t, http.StatusExpectationFailed, w.Code) - assert.Contains(t, w.Body.String(), "invalid param") + assert.Contains(t, w.Body.String(), "invalid syntax") } func TestClearService(t *testing.T) { @@ -226,7 +226,7 @@ func TestFilterProfile(t *testing.T) { expectErr bool }{ { - name: "valid test", + name: "normal path", pattern: []string{"some/fancy/gopath", "a/fancy/gopath"}, input: []*cover.Profile{ { @@ -260,6 +260,38 @@ func TestFilterProfile(t *testing.T) { }, }, }, + { + 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/a.go", + }, + { + FileName: "a/fancy/gopath/a.go", + }, + { + FileName: "b/a/fancy/gopath/a.go", + }, + }, + }, } for _, tc := range tcs { @@ -279,7 +311,6 @@ func TestFilterProfile(t *testing.T) { if !reflect.DeepEqual(out, tc.output) { t.Errorf("Mismatched results. \nExpected: %s\nActual:%s", stringifyCoverProfile(tc.output), stringifyCoverProfile(out)) } - }) } } From c00f871f80d479b8969d678c6db96e0bb5c594f5 Mon Sep 17 00:00:00 2001 From: jichangjun Date: Sat, 5 Sep 2020 16:27:23 +0800 Subject: [PATCH 4/6] add e2e for goc profile with coverfile flag --- pkg/cover/server.go | 2 +- tests/profile.bats | 20 +++++++++++++++++++ tests/samples/run_for_several_seconds/a/a.go | 6 ++++++ tests/samples/run_for_several_seconds/b/b.go | 6 ++++++ tests/samples/run_for_several_seconds/main.go | 5 +++++ 5 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/samples/run_for_several_seconds/a/a.go create mode 100644 tests/samples/run_for_several_seconds/b/b.go diff --git a/pkg/cover/server.go b/pkg/cover/server.go index 1c11e92..e952d02 100644 --- a/pkg/cover/server.go +++ b/pkg/cover/server.go @@ -204,7 +204,7 @@ func filterProfile(coverFile []string, profiles []*cover.Profile) ([]*cover.Prof for _, pattern := range coverFile { matched, err := regexp.MatchString(pattern, profile.FileName) if err != nil { - return nil, fmt.Errorf("filterProfile failed with pattern %s for profile %s", pattern, profile.FileName) + return nil, fmt.Errorf("filterProfile failed with pattern %s for profile %s, err: %v", pattern, profile.FileName, err) } if matched { out = append(out, profile) diff --git a/tests/profile.bats b/tests/profile.bats index 6dfbd30..d4bc91e 100755 --- a/tests/profile.bats +++ b/tests/profile.bats @@ -74,6 +74,26 @@ setup() { run cat test-profile.bak [[ "$output" == *"mode: count"* ]] + wait $profile_pid + kill -9 $SAMPLE_PID +} + +@test "test goc profile with coverfile flag" { + ./simple-project 3>&- & + SAMPLE_PID=$! + sleep 2 + + wait_profile_backend "profile3" & + profile_pid=$! + + run gocc profile --center=http://127.0.0.1:60001 --coverfile="a.go$,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"* ]] # 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 diff --git a/tests/samples/run_for_several_seconds/a/a.go b/tests/samples/run_for_several_seconds/a/a.go new file mode 100644 index 0000000..6bd3ef3 --- /dev/null +++ b/tests/samples/run_for_several_seconds/a/a.go @@ -0,0 +1,6 @@ +package a + +// Say Hello A +func Say() { + println("Hello A") +} diff --git a/tests/samples/run_for_several_seconds/b/b.go b/tests/samples/run_for_several_seconds/b/b.go new file mode 100644 index 0000000..ff8954c --- /dev/null +++ b/tests/samples/run_for_several_seconds/b/b.go @@ -0,0 +1,6 @@ +package b + +// Say Hello B +func Say() { + println("Hello B") +} diff --git a/tests/samples/run_for_several_seconds/main.go b/tests/samples/run_for_several_seconds/main.go index f8e8cbc..c17eea6 100644 --- a/tests/samples/run_for_several_seconds/main.go +++ b/tests/samples/run_for_several_seconds/main.go @@ -3,9 +3,14 @@ package main import ( "fmt" "time" + + "example.com/simple-project/a" + "example.com/simple-project/b" ) func main() { fmt.Println("hello") + a.Say() + b.Say() time.Sleep(time.Second * 15) } From ff8f840b140f595310f773b48965cadcce7e2bcc Mon Sep 17 00:00:00 2001 From: jichangjun Date: Sat, 5 Sep 2020 18:32:55 +0800 Subject: [PATCH 5/6] add case for profile api with coverfile flag --- pkg/cover/client.go | 1 + pkg/cover/client_test.go | 110 ++++++++++++++++++++++++--------------- pkg/cover/server.go | 15 +----- pkg/cover/server_test.go | 5 -- 4 files changed, 69 insertions(+), 62 deletions(-) diff --git a/pkg/cover/client.go b/pkg/cover/client.go index c9332e7..04123e0 100644 --- a/pkg/cover/client.go +++ b/pkg/cover/client.go @@ -106,6 +106,7 @@ func (c *client) Profile(param ProfileParam) ([]byte, error) { if err != nil && isNetworkError(err) { res, profile, err = c.do("POST", u, "application/json", bytes.NewReader(body)) } + if err == nil && res.StatusCode != 200 { err = fmt.Errorf(string(profile)) } diff --git a/pkg/cover/client_test.go b/pkg/cover/client_test.go index df0f7e4..ab06402 100644 --- a/pkg/cover/client_test.go +++ b/pkg/cover/client_test.go @@ -33,10 +33,10 @@ func TestClientAction(t *testing.T) { var client = NewWorker(ts.URL) // mock profile server - profileMockResponse := "mode: count\nmockService/main.go:30.13,48.33 13 1" + profileMockResponse := []byte("mode: count\nmockService/main.go:30.13,48.33 13 1\nb/b.go:30.13,48.33 13 1") profileSuccessMockSvr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) - w.Write([]byte(profileMockResponse)) + w.Write(profileMockResponse) })) defer profileSuccessMockSvr.Close() @@ -60,65 +60,89 @@ func TestClientAction(t *testing.T) { assert.Contains(t, string(res), src.Address) assert.Contains(t, string(res), src.Name) - // get porfile from goc server - profileItems := []struct { - service Service - param ProfileParam - res string + // get profile from goc server + tcs := []struct { + name string + service Service + param ProfileParam + expected string + expectedErr bool }{ { - service: Service{Name: "serviceOK", Address: profileSuccessMockSvr.URL}, - param: ProfileParam{Force: false, Service: []string{"serviceOK"}, Address: []string{profileSuccessMockSvr.URL}}, - res: "use 'service' flag and 'address' flag at the same time may cause ambiguity, please use them separately", + name: "both service and address existed", + service: Service{Name: "serviceOK", Address: profileSuccessMockSvr.URL}, + param: ProfileParam{Force: false, Service: []string{"serviceOK"}, Address: []string{profileSuccessMockSvr.URL}}, + expectedErr: true, }, { - service: Service{Name: "serviceOK", Address: profileSuccessMockSvr.URL}, - param: ProfileParam{}, - res: profileMockResponse, + name: "valid test with no service flag provied", + service: Service{Name: "serviceOK", Address: profileSuccessMockSvr.URL}, + param: ProfileParam{}, + expected: "mockService/main.go:30.13,48.33 13 1", }, { - service: Service{Name: "serviceOK", Address: profileSuccessMockSvr.URL}, - param: ProfileParam{Service: []string{"serviceOK"}}, - res: profileMockResponse, + name: "valid test with service flag provied", + service: Service{Name: "serviceOK", Address: profileSuccessMockSvr.URL}, + param: ProfileParam{Service: []string{"serviceOK"}}, + expected: "mockService/main.go:30.13,48.33 13 1", }, { - service: Service{Name: "serviceOK", Address: profileSuccessMockSvr.URL}, - param: ProfileParam{Address: []string{profileSuccessMockSvr.URL}}, - res: profileMockResponse, + name: "valid test with address flag provied", + service: Service{Name: "serviceOK", Address: profileSuccessMockSvr.URL}, + param: ProfileParam{Address: []string{profileSuccessMockSvr.URL}}, + expected: "mockService/main.go:30.13,48.33 13 1", }, { - service: Service{Name: "serviceOK", Address: profileSuccessMockSvr.URL}, - param: ProfileParam{Service: []string{"unknown"}}, - res: "service [unknown] not found", + service: Service{Name: "serviceOK", Address: profileSuccessMockSvr.URL}, + param: ProfileParam{Service: []string{"unknown"}}, + expected: "service [unknown] not found", + expectedErr: true, }, { - service: Service{Name: "serviceErr", Address: profileErrMockSvr.URL}, - res: "bad mode line: error", + service: Service{Name: "serviceErr", Address: profileErrMockSvr.URL}, + expected: "bad mode line: error", + expectedErr: true, }, { - service: Service{Name: "serviceNotExist", Address: "http://172.0.0.2:7777"}, - res: "connection refused", + service: Service{Name: "serviceNotExist", Address: "http://172.0.0.2:7777"}, + expected: "connection refused", + expectedErr: true, }, { - service: Service{Name: "serviceNotExist", Address: "http://172.0.0.2:7777"}, - param: ProfileParam{Force: true}, - res: "no profiles", + service: Service{Name: "serviceNotExist", Address: "http://172.0.0.2:7777"}, + param: ProfileParam{Force: true}, + expected: `{"message":"no profiles"}`, + }, + { + name: "valid test with coverfile flag provied", + service: Service{Name: "serviceOK", Address: profileSuccessMockSvr.URL}, + param: ProfileParam{CoverFilePatterns: []string{"b.go$"}}, + expected: "b/b.go", }, } - for _, item := range profileItems { - // init server - _, err = client.InitSystem() - assert.NoError(t, err) - // register server - res, err = client.RegisterService(item.service) - assert.NoError(t, err) - assert.Contains(t, string(res), "success") - res, err = client.Profile(item.param) - if err != nil { - assert.Contains(t, err.Error(), item.res) - } else { - assert.Contains(t, string(res), item.res) - } + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + // init server + _, err = client.InitSystem() + assert.NoError(t, err) + // register server + res, err = client.RegisterService(tc.service) + assert.NoError(t, err) + assert.Contains(t, string(res), "success") + res, err = client.Profile(tc.param) + if err != nil { + if !tc.expectedErr { + t.Errorf("unexpected err got: %v", err) + } + return + } + + if tc.expectedErr { + t.Errorf("Expected an error, but got value %s", string(res)) + } + + assert.Regexp(t, tc.expected, string(res)) + }) } // init system and check service again diff --git a/pkg/cover/server.go b/pkg/cover/server.go index e952d02..6746ae9 100644 --- a/pkg/cover/server.go +++ b/pkg/cover/server.go @@ -173,7 +173,7 @@ func profile(c *gin.Context) { } if len(mergedProfiles) == 0 { - c.JSON(http.StatusOK, "no profiles") + c.JSON(http.StatusOK, gin.H{"message": "no profiles"}) return } @@ -307,16 +307,3 @@ func filterAddrs(serviceList, addressList []string, force bool, allInfos map[str // Return all servers when all param is nil return filterAddrList, nil } - -// removeDuplicateElement remove duplicate element in slice -func removeDuplicateElement(addrs []string) []string { - result := make([]string, 0, len(addrs)) - temp := map[string]struct{}{} - for _, item := range addrs { - if _, ok := temp[item]; !ok { - temp[item] = struct{}{} - result = append(result, item) - } - } - return result -} diff --git a/pkg/cover/server_test.go b/pkg/cover/server_test.go index 2eb5d72..d53fb61 100644 --- a/pkg/cover/server_test.go +++ b/pkg/cover/server_test.go @@ -110,11 +110,6 @@ func TestFilterAddrs(t *testing.T) { } } -func TestRemoveDuplicateElement(t *testing.T) { - strArr := []string{"a", "a", "b"} - assert.Equal(t, removeDuplicateElement(strArr), []string{"a", "b"}) -} - func TestRegisterService(t *testing.T) { router := GocServer(os.Stdout) From a02988fb3a9557ea0ce473a870ef8670aeb3e054 Mon Sep 17 00:00:00 2001 From: jichangjun Date: Sat, 5 Sep 2020 18:49:29 +0800 Subject: [PATCH 6/6] add more case for filterProfile method --- pkg/cover/client_test.go | 2 +- pkg/cover/server_test.go | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/cover/client_test.go b/pkg/cover/client_test.go index ab06402..1663599 100644 --- a/pkg/cover/client_test.go +++ b/pkg/cover/client_test.go @@ -36,7 +36,7 @@ func TestClientAction(t *testing.T) { profileMockResponse := []byte("mode: count\nmockService/main.go:30.13,48.33 13 1\nb/b.go:30.13,48.33 13 1") profileSuccessMockSvr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) - w.Write(profileMockResponse) + _, _ = w.Write(profileMockResponse) })) defer profileSuccessMockSvr.Close() diff --git a/pkg/cover/server_test.go b/pkg/cover/server_test.go index d53fb61..62b75d4 100644 --- a/pkg/cover/server_test.go +++ b/pkg/cover/server_test.go @@ -287,6 +287,16 @@ func TestFilterProfile(t *testing.T) { }, }, }, + { + name: "with invalid regular expression", + pattern: []string{"(?!a)"}, + input: []*cover.Profile{ + { + FileName: "some/fancy/gopath/a.go", + }, + }, + expectErr: true, + }, } for _, tc := range tcs {