Repository: qiniu/goc
Pull request #112 (2020-09-11 07:46:13)

Clear coverage by service&addr
1. add service & address to goc clear command: goc clear --service=xx --address=xxx
2. add unit test & e2e test
3. add go1.15 to ci


Patch:
diff --git a/.github/workflows/style_check.yml b/.github/workflows/style_check.yml
index ebc98ba..568d0d4 100644
--- a/.github/workflows/style_check.yml
+++ b/.github/workflows/style_check.yml
@@ -13,7 +13,7 @@ jobs:
     name: vet and gofmt
     strategy:
       matrix:
-        go-version: [1.13.x, 1.14.x]
+        go-version: [1.13.x, 1.14.x, 1.15.x]
     runs-on: ubuntu-latest
     steps:
       - name: Install Go
diff --git a/.github/workflows/ut_check.yml b/.github/workflows/ut_check.yml
index 0b9d311..417d0d2 100644
--- a/.github/workflows/ut_check.yml
+++ b/.github/workflows/ut_check.yml
@@ -13,7 +13,7 @@ jobs:
     name: go test
     strategy:
       matrix:
-        go-version: [1.13.x, 1.14.x]
+        go-version: [1.13.x, 1.14.x, 1.15.x]
     runs-on: ubuntu-latest
     steps:
       - name: Install Go
diff --git a/cmd/clear.go b/cmd/clear.go
index 65cfa6e..2e5aea2 100644
--- a/cmd/clear.go
+++ b/cmd/clear.go
@@ -18,9 +18,10 @@ package cmd
 
 import (
 	"fmt"
-	log "github.com/sirupsen/logrus"
 	"os"
 
+	log "github.com/sirupsen/logrus"
+
 	"github.com/qiniu/goc/pkg/cover"
 	"github.com/spf13/cobra"
 )
@@ -37,7 +38,11 @@ goc clear
 goc clear --center=http://192.168.1.1:8080
 `,
 	Run: func(cmd *cobra.Command, args []string) {
-		res, err := cover.NewWorker(center).Clear()
+		p := cover.ProfileParam{
+			Service: svrList,
+			Address: addrList,
+		}
+		res, err := cover.NewWorker(center).Clear(p)
 		if err != nil {
 			log.Fatalf("call host %v failed, err: %v, response: %v", center, err, string(res))
 		}
@@ -47,5 +52,7 @@ goc clear --center=http://192.168.1.1:8080
 
 func init() {
 	addBasicFlags(clearCmd.Flags())
+	clearCmd.Flags().StringSliceVarP(&svrList, "service", "", nil, "service name to clear profile, see 'goc list' for all services.")
+	clearCmd.Flags().StringSliceVarP(&addrList, "address", "", nil, "address to clear profile, see 'goc list' for all addresses.")
 	rootCmd.AddCommand(clearCmd)
 }
diff --git a/pkg/cover/client.go b/pkg/cover/client.go
index 04123e0..c45166f 100644
--- a/pkg/cover/client.go
+++ b/pkg/cover/client.go
@@ -33,7 +33,7 @@ import (
 // Action provides methods to contact with the covered service under test
 type Action interface {
 	Profile(param ProfileParam) ([]byte, error)
-	Clear() ([]byte, error)
+	Clear(param ProfileParam) ([]byte, error)
 	InitSystem() ([]byte, error)
 	ListServices() ([]byte, error)
 	RegisterService(svr Service) ([]byte, error)
@@ -97,10 +97,9 @@ func (c *client) Profile(param ProfileParam) ([]byte, error) {
 		return nil, fmt.Errorf("use 'service' flag and 'address' flag at the same time may cause ambiguity, please use them separately")
 	}
 
-	body, err := json.Marshal(param)
-	if err != nil {
-		return nil, fmt.Errorf("json.Marshal failed, param: %v, err:%v", param, err)
-	}
+	// the json.Marshal function can return two types of errors: UnsupportedTypeError or UnsupportedValueError
+	// so no need to check here
+	body, _ := json.Marshal(param)
 
 	res, profile, err := c.do("POST", u, "application/json", bytes.NewReader(body))
 	if err != nil && isNetworkError(err) {
@@ -113,11 +112,18 @@ func (c *client) Profile(param ProfileParam) ([]byte, error) {
 	return profile, err
 }
 
-func (c *client) Clear() ([]byte, error) {
+func (c *client) Clear(param ProfileParam) ([]byte, error) {
 	u := fmt.Sprintf("%s%s", c.Host, CoverProfileClearAPI)
-	_, resp, err := c.do("POST", u, "", nil)
+	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")
+	}
+
+	// the json.Marshal function can return two types of errors: UnsupportedTypeError or UnsupportedValueError
+	// so no need to check here
+	body, _ := json.Marshal(param)
+	_, resp, err := c.do("POST", u, "application/json", bytes.NewReader(body))
 	if err != nil && isNetworkError(err) {
-		_, resp, err = c.do("POST", u, "", nil)
+		_, resp, err = c.do("POST", u, "application/json", bytes.NewReader(body))
 	}
 	return resp, err
 }
diff --git a/pkg/cover/server.go b/pkg/cover/server.go
index 6746ae9..93a50c1 100644
--- a/pkg/cover/server.go
+++ b/pkg/cover/server.go
@@ -217,17 +217,26 @@ func filterProfile(coverFile []string, profiles []*cover.Profile) ([]*cover.Prof
 }
 
 func clear(c *gin.Context) {
+	var body ProfileParam
+	if err := c.ShouldBind(&body); err != nil {
+		c.JSON(http.StatusExpectationFailed, gin.H{"error": err.Error()})
+		return
+	}
 	svrsUnderTest := DefaultStore.GetAll()
-	for svc, addrs := range svrsUnderTest {
-		for _, addr := range addrs {
-			pp, err := NewWorker(addr).Clear()
-			if err != nil {
-				c.JSON(http.StatusExpectationFailed, gin.H{"error": err.Error()})
-				return
-			}
-			fmt.Fprintf(c.Writer, "Register service %s: %s coverage counter %s", svc, addr, string(pp))
+	filterAddrList, err := filterAddrs(body.Service, body.Address, true, svrsUnderTest)
+	if err != nil {
+		c.JSON(http.StatusExpectationFailed, gin.H{"error": err.Error()})
+		return
+	}
+	for _, addr := range filterAddrList {
+		pp, err := NewWorker(addr).Clear(ProfileParam{})
+		if err != nil {
+			c.JSON(http.StatusExpectationFailed, gin.H{"error": err.Error()})
+			return
 		}
+		fmt.Fprintf(c.Writer, "Register service %s coverage counter %s", addr, string(pp))
 	}
+
 }
 
 func initSystem(c *gin.Context) {


Test patch:
diff --git a/.github/workflows/e2e_test_check.yml b/.github/workflows/e2e_test_check.yml
index 4739346..fe0ea5d 100644
--- a/.github/workflows/e2e_test_check.yml
+++ b/.github/workflows/e2e_test_check.yml
@@ -41,7 +41,7 @@ jobs:
     needs: job_1
     strategy:
       matrix:
-        go-version: [1.11.x, 1.12.x, 1.13.x, 1.14.x]
+        go-version: [1.11.x, 1.12.x, 1.13.x, 1.14.x, 1.15.x]
     runs-on: ubuntu-latest
     steps:
       - name: Install Go
diff --git a/pkg/cover/client_test.go b/pkg/cover/client_test.go
index 1663599..e5cc340 100644
--- a/pkg/cover/client_test.go
+++ b/pkg/cover/client_test.go
@@ -189,3 +189,16 @@ func TestClientDo(t *testing.T) {
 	_, _, err := c.do(" ", "http://127.0.0.1:7777", "", nil) // a invalid method
 	assert.Contains(t, err.Error(), "invalid method")
 }
+
+func TestClientClearWithInvalidParam(t *testing.T) {
+	p := ProfileParam{
+		Service: []string{"goc"},
+		Address: []string{"http://127.0.0.1:777"},
+	}
+	c := &client{
+		client: http.DefaultClient,
+	}
+	_, err := c.Clear(p)
+	assert.Error(t, err)
+	assert.Contains(t, err.Error(), "use 'service' flag and 'address' flag at the same time may cause ambiguity, please use them separately")
+}
diff --git a/pkg/cover/server_test.go b/pkg/cover/server_test.go
index 62b75d4..fb7feb7 100644
--- a/pkg/cover/server_test.go
+++ b/pkg/cover/server_test.go
@@ -1,6 +1,8 @@
 package cover
 
 import (
+	"bytes"
+	"encoding/json"
 	"fmt"
 	"net/http"
 	"net/http/httptest"
@@ -186,13 +188,36 @@ func TestClearService(t *testing.T) {
 
 	router := GocServer(os.Stdout)
 
-	// get profile with invalid force parameter
+	// clear profile with non-exist port
 	w := httptest.NewRecorder()
-	req, _ := http.NewRequest("POST", "/v1/cover/clear", nil)
+	req, _ := http.NewRequest("POST", "/v1/cover/clear", bytes.NewBuffer([]byte(`{}`)))
+	req.Header.Set("Content-Type", "application/json")
 	router.ServeHTTP(w, req)
 
 	assert.Equal(t, http.StatusExpectationFailed, w.Code)
 	assert.Contains(t, w.Body.String(), "invalid port")
+
+	// clear profile with invalid service
+	w = httptest.NewRecorder()
+	req, _ = http.NewRequest("POST", "/v1/cover/clear", nil)
+	req.Header.Set("Content-Type", "application/json")
+	router.ServeHTTP(w, req)
+	assert.Equal(t, http.StatusExpectationFailed, w.Code)
+	assert.Contains(t, w.Body.String(), "invalid request")
+
+	// clear profile with service and address set at at the same time
+	p := ProfileParam{
+		Service: []string{"goc"},
+		Address: []string{"http://127.0.0.1:3333"},
+	}
+	encoded, err := json.Marshal(p)
+	assert.NoError(t, err)
+	w = httptest.NewRecorder()
+	req, _ = http.NewRequest("POST", "/v1/cover/clear", bytes.NewBuffer(encoded))
+	req.Header.Set("Content-Type", "application/json")
+	router.ServeHTTP(w, req)
+	assert.Equal(t, http.StatusExpectationFailed, w.Code)
+	assert.Contains(t, w.Body.String(), "use 'service' flag and 'address' flag at the same time may cause ambiguity, please use them separately")
 }
 
 func TestInitService(t *testing.T) {
diff --git a/tests/clear.bats b/tests/clear.bats
index 24583c7..f8cb637 100755
--- a/tests/clear.bats
+++ b/tests/clear.bats
@@ -38,6 +38,7 @@ setup_file() {
 }
 
 teardown_file() {
+    rm *_profile_listen_addr
     kill -9 $GOC_PID
     kill -9 $GOCC_PID
     kill -9 $SAMPLE_PID
@@ -65,4 +66,37 @@ teardown_file() {
     [[ "$output" == *"coverage counter clear call successfully"* ]]
 
     wait $profile_pid
+}
+
+@test "test clear by service name" {
+    goc build --output=./test-service
+    ./test-service 3>&- &
+    TEST_SERVICE=$!
+    sleep 1
+
+    # clear by wrong service name
+    run goc clear --service="test-servicej"
+    [ "$status" -eq 0 ]
+    [ "$output" = "" ]
+
+    # check by goc profile, as the last step is wrong
+    # the coverage count should be 1
+    run goc profile --coverfile="simple-project/a/a.go" --force
+    info clear3 output: $output
+    [ "$status" -eq 0 ]
+    [[ "$output" =~ "example.com/simple-project/a/a.go:4.12,6.2 1 1" ]]
+
+    # clear by right service name
+    run goc clear --service="test-service"
+    [ "$status" -eq 0 ]
+    [[ "$output" =~ "coverage counter clear call successfully" ]]
+
+    # check by goc profile, the coverage count should be reset to 0
+    run goc profile --coverfile="simple-project/a/a.go" --force
+    info clear4 output: $output
+    [ "$status" -eq 0 ]
+    [[ "$output" =~ "example.com/simple-project/a/a.go:4.12,6.2 1 0" ]]
+
+
+    kill -9 $TEST_SERVICE
 }
\ No newline at end of file