Skip to content

Commit ae2c5e7

Browse files
authored
Merge pull request #3 from qa-dev/concurrency-tests
concurrency tests
2 parents 9d70494 + 01dff73 commit ae2c5e7

File tree

12 files changed

+321
-12
lines changed

12 files changed

+321
-12
lines changed

.travis.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ go:
44
- 1.8.x
55

66
install: make get-deps prepare
7-
script: go test ./...
7+
script:
8+
- make test
89
services:
910
- mysql
1011

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ ENV CONFIG_PATH ./config.json
77

88
RUN make
99

10-
CMD ["service-entrypoint"]
10+
CMD ["jsonwire-grid"]
1111

1212
EXPOSE 4444

Makefile

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export CONFIG_PATH=./config.json
2+
TEST_CONFIG_PATH=./config-test.json
23
all: get-deps build
34

45
.PHONY: help build fmt clean run test coverage check vet lint doc cfpush
@@ -11,7 +12,8 @@ help:
1112
@echo "run - start application"
1213

1314
build: prepare
14-
go build -o ${GOPATH}/bin/service-entrypoint
15+
@echo "Install jsonwire-grid"
16+
go install github.com/qa-dev/jsonwire-grid
1517

1618
fmt:
1719
go fmt
@@ -25,4 +27,27 @@ get-deps:
2527
prepare: fmt gen
2628

2729
run: build
28-
${GOPATH}/bin/service-entrypoint
30+
@echo "Start jsonwire-grid"
31+
jsonwire-grid
32+
33+
test:
34+
go test ./...
35+
36+
concurrency-test-prepare: build
37+
@echo "Install jsonwire-grid"
38+
go install github.com/qa-dev/jsonwire-grid/testing/webdriver-node-mock
39+
@echo "Install webdriver-node-mock"
40+
go install github.com/qa-dev/jsonwire-grid/testing/webdriver-mock-creator
41+
@echo "Install webdriver-mock-creator"
42+
go install github.com/qa-dev/jsonwire-grid/testing/webdriver-concurrency-test
43+
@echo "Kill all running jsonwire-grid"
44+
killall -9 jsonwire-grid &
45+
@echo "Wait 1s"
46+
sleep 1
47+
@echo "Start jsonwire-grid"
48+
CONFIG_PATH=$(TEST_CONFIG_PATH) jsonwire-grid &
49+
50+
concurrency-test: concurrency-test-prepare
51+
@echo "Start webdriver-concurrency-test"
52+
webdriver-concurrency-test
53+

config-sample.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
},
55
"db": {
66
"implementation": "mysql",
7-
"connection": "root:@(localhost:3306)/grid?tx_isolation=SERIALIZABLE&parseTime=true"
7+
"connection": "root:@(localhost:3306)/grid?tx_isolation=SERIALIZABLE&parseTime=true&interpolateParams=true"
88
},
99
"grid": {
1010
"port": 4444,

config-test.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"logger": {
3+
"level": 3
4+
},
5+
"db": {
6+
"implementation": "mysql",
7+
"connection": "root:@(127.0.0.1:3306)/grid?tx_isolation=SERIALIZABLE&parseTime=true&interpolateParams=true"
8+
},
9+
"grid": {
10+
"port": 4444,
11+
"strategy_list": [
12+
{
13+
"type": "default",
14+
"limit": 0
15+
}
16+
],
17+
"busy_node_duration": "15m",
18+
"reserved_node_duration": "5m"
19+
},
20+
"statsd": {
21+
"host": "statsd-host",
22+
"port": 8126,
23+
"protocol": "udp",
24+
"prefix": "products.tests.qa.debug-dev.jsonwire-grid.",
25+
"enable": false
26+
}
27+
}

main.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,20 @@ func main() {
7979
http.Handle("/", m.Log(&handlers.UseSession{Pool: poolInstance}))
8080

8181
server := &http.Server{Addr: fmt.Sprintf(":%v", cfg.Grid.Port)}
82+
serverError := make(chan error)
8283
go func() {
8384
err = server.ListenAndServe()
8485
if err != nil {
85-
// todo: норма ли что при закрытии всегда возвращается еррор???
86-
log.Errorf("Listen serve error, %s", err)
86+
// todo: норма ли что при вызове server.Shutdown всегда возвращается еррор???
87+
serverError <- err
8788
}
8889
}()
8990

90-
<-stop
91+
select {
92+
case err = <-serverError:
93+
log.Fatalf("Server error, %s", err)
94+
case <-stop:
95+
}
9196

9297
log.Info("Shutting down the server...")
9398

storage/mysql/factory.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func (f *Factory) Create(config config.Config) (pool.StorageInterface, error) {
2121
return nil, err
2222
}
2323

24-
db.SetMaxIdleConns(0) // this is the root problem! set it to 0 to remove all idle connections
24+
db.SetMaxIdleConns(0) // this is the root problem! set it to 0 to remove all idle connections
2525
db.SetMaxOpenConns(50) // or whatever is appropriate for your setup.
2626

2727
storage := NewMysqlStorage(db)

storage/mysql/storage.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ package mysql
22

33
import (
44
"errors"
5+
"fmt"
56
"github.com/jmoiron/sqlx"
67
"github.com/qa-dev/jsonwire-grid/pool"
78
"sort"
89
"strconv"
9-
"time"
10-
"fmt"
1110
"strings"
11+
"time"
1212
)
1313

1414
type MysqlNodeModel struct {
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"errors"
7+
"flag"
8+
"fmt"
9+
"github.com/qa-dev/jsonwire-grid/jsonwire"
10+
"io/ioutil"
11+
"log"
12+
"net/http"
13+
"os"
14+
"os/exec"
15+
"os/signal"
16+
"strconv"
17+
"sync"
18+
"sync/atomic"
19+
"time"
20+
)
21+
22+
var (
23+
hubUrl *string
24+
level *int
25+
durationStr *string
26+
)
27+
28+
// todo: prototype
29+
func main() {
30+
hubUrl = flag.String("hub", "http://127.0.0.1:4444", "address of hub, default http://127.0.0.1:4444")
31+
level = flag.Int("level", 50, "count parallell conections")
32+
durationStr = flag.String("duration", "30s", "duration of test, string format ex. 12m, see time.ParseDuration()")
33+
mockMaxDuration := flag.Int("mockMaxDuration", 500, "request duration [0 <=duration], default 0")
34+
mockStartPort := flag.Int("mockStartPort", 5000, "mockStartPort")
35+
flag.Parse()
36+
37+
duration, err := time.ParseDuration(*durationStr)
38+
if err != nil {
39+
log.Fatal("Invalid duration")
40+
}
41+
var counter uint64 = 0
42+
43+
stop := make(chan os.Signal)
44+
signal.Notify(stop, os.Interrupt)
45+
wg := sync.WaitGroup{}
46+
isAlive := true
47+
errChan := make(chan error, *level)
48+
49+
cmd := exec.Command(
50+
"webdriver-mock-creator",
51+
fmt.Sprintf("-hub=%v", *hubUrl),
52+
fmt.Sprintf("-startPort=%v", *mockStartPort),
53+
fmt.Sprintf("-maxDuration=%v", *mockMaxDuration),
54+
fmt.Sprintf("-countNodes=%v", *level),
55+
)
56+
57+
err = cmd.Start()
58+
if err != nil {
59+
log.Fatal(err)
60+
}
61+
62+
time.Sleep(time.Millisecond * 500)
63+
64+
go func() {
65+
for i := 1; i <= *level && isAlive; i++ {
66+
time.Sleep(time.Millisecond * 100)
67+
go func() {
68+
wg.Add(1)
69+
defer wg.Done()
70+
for {
71+
if !isAlive {
72+
break
73+
}
74+
err := runScenario()
75+
if err != nil {
76+
errChan <- errors.New("Run scenario, " + err.Error())
77+
}
78+
atomic.AddUint64(&counter, 1)
79+
}
80+
}()
81+
}
82+
}()
83+
84+
select {
85+
case <-time.After(duration):
86+
case err = <-errChan:
87+
case <-stop:
88+
}
89+
90+
isAlive = false
91+
92+
//wait end all running scenarios
93+
wg.Wait()
94+
95+
//wait interrupt child process
96+
cmd.Process.Signal(os.Interrupt)
97+
cmd.Wait()
98+
99+
if err != nil {
100+
log.Fatalf("Tests failed: %v, ", err)
101+
}
102+
log.Printf("Test ok, %v cycles passed", counter)
103+
}
104+
105+
func runScenario() error {
106+
sessionID, err := createSession()
107+
if err != nil {
108+
err = errors.New("Create session, " + err.Error())
109+
return err
110+
}
111+
err = sendAnyRequest(sessionID)
112+
if err != nil {
113+
err = errors.New("Send any request, " + err.Error())
114+
return err
115+
}
116+
err = closeSession(sessionID)
117+
if err != nil {
118+
err = errors.New("Close session, " + err.Error())
119+
return err
120+
}
121+
return nil
122+
}
123+
124+
func createSession() (sessionID string, err error) {
125+
resp, err := http.Post(*hubUrl+"/wd/hub/session", "application/json", bytes.NewBuffer([]byte(`{"desiredCapabilities":{"browserName": "firefox"}}`)))
126+
if err != nil {
127+
err = errors.New("Send request, " + err.Error())
128+
return
129+
}
130+
defer resp.Body.Close()
131+
b, err := ioutil.ReadAll(resp.Body)
132+
if err != nil {
133+
err = errors.New("Read response body, " + err.Error())
134+
return
135+
}
136+
137+
var message jsonwire.NewSession
138+
err = json.Unmarshal(b, &message)
139+
if err != nil {
140+
err = errors.New("Unmarshal json, " + err.Error() + ", given response body=[" + string(b) + "]")
141+
return
142+
}
143+
switch {
144+
case message.SessionId != "":
145+
sessionID = message.SessionId
146+
case message.Value.SessionId != "":
147+
sessionID = message.Value.SessionId
148+
default:
149+
err = errors.New("Field`s SessionId is empty")
150+
return
151+
}
152+
if resp.StatusCode != http.StatusOK {
153+
err = errors.New("Expected status code 200, actual: " + strconv.Itoa(resp.StatusCode) + ", given response body=[" + string(b) + "]")
154+
return
155+
}
156+
return sessionID, nil
157+
}
158+
159+
func sendAnyRequest(sessionID string) (err error) {
160+
resp, err := http.Get(*hubUrl + "/wd/hub/session/" + sessionID + "/url")
161+
if err != nil {
162+
err = errors.New("Send request, " + err.Error())
163+
return
164+
}
165+
defer resp.Body.Close()
166+
if resp.StatusCode != http.StatusOK {
167+
b, _ := ioutil.ReadAll(resp.Body)
168+
err = errors.New("Expected status code 200, actual: " + strconv.Itoa(resp.StatusCode) + ", given response body=[" + string(b) + "]")
169+
return
170+
}
171+
return
172+
}
173+
174+
func closeSession(sessionID string) (err error) {
175+
req, err := http.NewRequest(http.MethodDelete, *hubUrl+"/wd/hub/session/"+sessionID, nil)
176+
if err != nil {
177+
err = errors.New("Create request, " + err.Error())
178+
return
179+
}
180+
resp, err := http.DefaultClient.Do(req)
181+
if err != nil {
182+
err = errors.New("Send request, " + err.Error())
183+
return
184+
}
185+
defer resp.Body.Close()
186+
if resp.StatusCode != http.StatusOK {
187+
b, _ := ioutil.ReadAll(resp.Body)
188+
err = errors.New("Expected status code 200, actual: " + strconv.Itoa(resp.StatusCode) + ", given response body=[" + string(b) + "]")
189+
return
190+
}
191+
return
192+
}

0 commit comments

Comments
 (0)