Skip to content

Commit 2001340

Browse files
authored
fix: the test suite data manipulate is not thread safe (#168)
* fix: the test suite data manipulate is not thread safe * add more unit tests
1 parent 2e23e82 commit 2001340

File tree

8 files changed

+182
-14
lines changed

8 files changed

+182
-14
lines changed

.github/testing/core.yaml

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,65 @@ items:
88
request:
99
api: /GetSuites
1010
method: POST
11+
- name: createSuite
12+
request:
13+
api: /CreateTestSuite
14+
method: POST
15+
body: |
16+
{"name": "{{randAlpha 6}}"}
1117
- name: suite
1218
request:
1319
api: /GetTestSuite
1420
method: POST
1521
body: |
16-
{"name": "test"}
22+
{"name": "{{randAlpha 6}}"}
1723
expect:
1824
bodyFieldsExpect:
1925
name: ""
2026
api: ""
21-
- name: get-testcase-not-found
27+
- name: UpdateTestSuite
2228
request:
23-
api: /GetTestCase
29+
api: /UpdateTestSuite
2430
method: POST
2531
body: |
26-
{"name": "test"}
32+
{
33+
"name": "{{index (keys .suites.data) 0}}",
34+
"api": "{{randAlpha 6}}"}
35+
}
36+
- name: DeleteTestSuiteNotFound
37+
request:
38+
api: /DeleteTestSuite
39+
method: POST
40+
body: |
41+
{"name": "{{randAlpha 6}}"}
2742
expect:
2843
statusCode: 500
44+
45+
- name: ListTestCase
46+
request:
47+
api: /ListTestCase
48+
method: POST
49+
body: |
50+
{"name": "{{index (keys .suites.data) (randInt 0 (len (keys .suites.data)))}}"}
51+
- name: list-testcases-not-found
52+
request:
53+
api: /ListTestCase
54+
method: POST
55+
body: |
56+
{"name": "{{randAlpha 6}}"}
57+
expect:
2958
bodyFieldsExpect:
30-
code: 2
31-
- name: list-testcases
59+
name: ""
60+
- name: GetSuggestedAPIs-no-testsuite-found
61+
request:
62+
api: /GetSuggestedAPIs
63+
method: POST
64+
body: |
65+
{"name": "{{randAlpha 6}}"}
66+
expect:
67+
verify:
68+
- len(data.data) == 0
69+
- name: get-testcase-not-found
3270
request:
3371
api: /GetTestCase
3472
method: POST
@@ -67,9 +105,16 @@ items:
67105
request:
68106
api: /GetVersion
69107
method: POST
70-
- name: secrets
108+
- name: GetSecrets
71109
request:
72110
api: /GetSecrets
73111
method: POST
74112
expect:
75113
statusCode: 500
114+
115+
- name: DeleteTestSuite
116+
request:
117+
api: /DeleteTestSuite
118+
method: POST
119+
body: |
120+
{"name": "{{index (keys .suites.data) 0}}"}

.github/workflows/build.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
sudo atest service install
3434
sudo atest service restart
3535
sudo atest service status
36-
atest run -p .github/testing/core.yaml
36+
atest run -p .github/testing/core.yaml --request-ignore-error
3737
sudo atest service status
3838
3939
Build:

CONTRIBUTION.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,14 @@ make install-precheck
1313
```shell
1414
git ls-files | xargs cloc
1515
```
16+
17+
## pprof
18+
19+
```
20+
go tool pprof -http=:9999 http://localhost:8080/debug/pprof/heap
21+
```
22+
23+
Other usage of this:
24+
* `/debug/pprof/heap?gc=1`
25+
* `/debug/pprof/heap?seconds=10`
26+
* `/debug/pprof/goroutine/?debug=0`

cmd/run_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func TestRunSuite(t *testing.T) {
5050
}}
5151
for _, tt := range tests {
5252
t.Run(tt.name, func(t *testing.T) {
53-
defer gock.Clean()
53+
defer gock.Off()
5454
util.MakeSureNotNil(tt.prepare)()
5555
ctx := getDefaultContext()
5656
opt := newDiscardRunOption()
@@ -141,7 +141,8 @@ func TestRunCommand(t *testing.T) {
141141
}}
142142
for _, tt := range tests {
143143
t.Run(tt.name, func(t *testing.T) {
144-
defer gock.Clean()
144+
defer gock.Off()
145+
145146
buf := new(bytes.Buffer)
146147
util.MakeSureNotNil(tt.prepare)()
147148
root := &cobra.Command{Use: "root"}

cmd/server.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"time"
1414

1515
_ "embed"
16+
pprof "net/http/pprof"
1617

1718
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
1819
template "github.com/linuxsuren/api-testing/pkg/render"
@@ -124,6 +125,7 @@ func (o *serverOption) runE(cmd *cobra.Command, args []string) (err error) {
124125
mux.HandlePath(http.MethodGet, "/", frontEndHandlerWithLocation(o.consolePath))
125126
mux.HandlePath(http.MethodGet, "/assets/{asset}", frontEndHandlerWithLocation(o.consolePath))
126127
mux.HandlePath(http.MethodGet, "/healthz", frontEndHandlerWithLocation(o.consolePath))
128+
debugHandler(mux)
127129
o.httpServer.WithHandler(mux)
128130
log.Printf("HTTP server listening at %v", httplis.Addr())
129131
err = o.httpServer.Serve(httplis)
@@ -165,6 +167,25 @@ func frontEndHandlerWithLocation(consolePath string) func(w http.ResponseWriter,
165167
}
166168
}
167169

170+
func debugHandler(mux *runtime.ServeMux) {
171+
mux.HandlePath(http.MethodGet, "/debug/pprof/{sub}", func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
172+
switch sub := pathParams["sub"]; sub {
173+
case "cmdline":
174+
pprof.Cmdline(w, r)
175+
case "profile":
176+
pprof.Profile(w, r)
177+
case "symbol":
178+
pprof.Symbol(w, r)
179+
case "trace":
180+
pprof.Trace(w, r)
181+
case "allocs", "block", "goroutine", "heap", "mutex", "threadcreate":
182+
pprof.Index(w, r)
183+
case "":
184+
pprof.Index(w, r)
185+
}
186+
})
187+
}
188+
168189
type gRPCServer interface {
169190
Serve(lis net.Listener) error
170191
grpc.ServiceRegistrar

cmd/server_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ package cmd
22

33
import (
44
"bytes"
5+
"fmt"
6+
"net"
57
"net/http"
68
"strings"
79
"testing"
810

11+
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
912
"github.com/linuxsuren/api-testing/pkg/server"
1013
"github.com/linuxsuren/api-testing/pkg/util"
1114
fakeruntime "github.com/linuxsuren/go-fake-runtime"
@@ -97,6 +100,43 @@ func TestFrontEndHandlerWithLocation(t *testing.T) {
97100
handler(resp, req, map[string]string{})
98101
assert.Equal(t, "ok", resp.GetBody().String())
99102
})
103+
104+
t.Run("pprof", func(t *testing.T) {
105+
apis := []string{"", "cmdline", "symbol",
106+
"trace", "profile",
107+
"allocs", "block", "goroutine", "heap", "mutex", "threadcreate"}
108+
109+
mu := runtime.NewServeMux()
110+
debugHandler(mu)
111+
112+
ready := make(chan struct{})
113+
var err error
114+
var listen net.Listener
115+
var port string
116+
go func() {
117+
listen, err = net.Listen("tcp", ":0")
118+
assert.NoError(t, err)
119+
120+
addr := listen.Addr().String()
121+
items := strings.Split(addr, ":")
122+
port = items[len(items)-1]
123+
124+
ready <- struct{}{}
125+
server := http.Server{Addr: addr, Handler: mu}
126+
server.Serve(listen)
127+
}()
128+
129+
<-ready
130+
defer listen.Close()
131+
132+
for _, name := range apis {
133+
// gock.Off()
134+
135+
resp, err := http.Get(fmt.Sprintf("http://localhost:%s/debug/pprof/%s?seconds=1", port, name))
136+
assert.NoError(t, err)
137+
assert.Equal(t, http.StatusOK, resp.StatusCode)
138+
}
139+
})
100140
}
101141

102142
type fakeResponseWriter struct {

docs/manifests/podman.yaml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
apiVersion: v1
2+
kind: Pod
3+
metadata:
4+
labels:
5+
app: api-testing
6+
name: api-testing
7+
spec:
8+
containers:
9+
- image: ghcr.io/linuxsuren/api-testing:master
10+
name: web
11+
ports:
12+
- containerPort: 8080
13+
hostPort: 8080
14+
protocol: TCP
15+
volumeMounts:
16+
- mountPath: /root/.config/atest/
17+
name: config
18+
tty: true
19+
workingDir: /
20+
- image: ghcr.io/linuxsuren/api-testing:master
21+
name: extension-orm
22+
command: [atest-store-orm]
23+
tty: true
24+
workingDir: /
25+
- image: ghcr.io/linuxsuren/api-testing:master
26+
name: extension-s3
27+
command: [atest-store-s3]
28+
tty: true
29+
workingDir: /
30+
- image: ghcr.io/linuxsuren/api-testing:master
31+
name: extension-git
32+
command: [atest-store-git]
33+
tty: true
34+
workingDir: /
35+
volumes:
36+
- name: config
37+
hostPath:
38+
path: /root/.config/atest/
39+
type: DirectoryOrCreate

pkg/testing/loader_file.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"os"
66
"path"
77
"path/filepath"
8+
"sync"
89

910
"github.com/linuxsuren/api-testing/pkg/util"
1011
)
@@ -13,15 +14,17 @@ type fileLoader struct {
1314
paths []string
1415
index int
1516
parent string
17+
18+
lock *sync.RWMutex
1619
}
1720

1821
// NewFileLoader creates the instance of file loader
1922
func NewFileLoader() Loader {
20-
return &fileLoader{index: -1}
23+
return &fileLoader{index: -1, lock: &sync.RWMutex{}}
2124
}
2225

2326
func NewFileWriter(parent string) Writer {
24-
return &fileLoader{index: -1, parent: parent}
27+
return &fileLoader{index: -1, parent: parent, lock: &sync.RWMutex{}}
2528
}
2629

2730
// HasMore returns if there are more test cases
@@ -38,6 +41,9 @@ func (l *fileLoader) Load() (data []byte, err error) {
3841

3942
// Put adds the test case path
4043
func (l *fileLoader) Put(item string) (err error) {
44+
l.lock.Lock()
45+
defer l.lock.Unlock()
46+
4147
if l.parent == "" {
4248
l.parent = path.Dir(item)
4349
}
@@ -47,9 +53,8 @@ func (l *fileLoader) Put(item string) (err error) {
4753
if files, err = filepath.Glob(pattern); err == nil {
4854
l.paths = append(l.paths, files...)
4955
}
50-
fmt.Println(pattern, "pattern", files)
56+
fmt.Println(pattern, "pattern", len(files))
5157
}
52-
fmt.Println(l.paths, item, l.parent, err)
5358
return
5459
}
5560

@@ -133,6 +138,9 @@ func (l *fileLoader) CreateSuite(name, api string) (err error) {
133138
}
134139

135140
func (l *fileLoader) GetSuite(name string) (suite *TestSuite, absPath string, err error) {
141+
l.lock.RLock()
142+
defer l.lock.RUnlock()
143+
136144
for i := range l.paths {
137145
suitePath := l.paths[i]
138146
if absPath, err = filepath.Abs(suitePath); err != nil {
@@ -165,6 +173,9 @@ func (l *fileLoader) UpdateSuite(suite TestSuite) (err error) {
165173
}
166174

167175
func (l *fileLoader) DeleteSuite(name string) (err error) {
176+
l.lock.Lock()
177+
defer l.lock.Unlock()
178+
168179
found := false
169180
for i := range l.paths {
170181
suitePath := l.paths[i]

0 commit comments

Comments
 (0)