Skip to content

Commit 9d11fbc

Browse files
authored
Merge pull request #363 from se7entyse7en/gitbase-multi-version-testing
Gitbase multi version testing
2 parents 0328827 + acd1537 commit 9d11fbc

File tree

590 files changed

+116777
-52
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

590 files changed

+116777
-52
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ main
2727
# CI
2828
.ci/
2929
build/
30+
regression-testing-cache/

Makefile

+29-5
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,46 @@ $(MAKEFILE):
1717

1818
-include $(MAKEFILE)
1919

20-
GOTEST_INTEGRATION = $(GOTEST) -timeout 20m -parallel 1 -count 1 -tags=integration -ldflags "$(LD_FLAGS)"
20+
GOTEST_BASE = $(GOTEST) -timeout 20m -parallel 1 -count 1 -ldflags "$(LD_FLAGS)"
21+
GOTEST_INTEGRATION = $(GOTEST_BASE) -tags=integration
22+
GOTEST_REGRESSION = $(GOTEST_BASE) -tags=regression
2123

2224
OS := $(shell uname)
2325

2426
ifeq ($(OS),Darwin)
2527
test-integration-clean:
26-
$(eval TMPDIR_TEST := $(PWD)/integration-test-tmp)
27-
$(eval GOTEST_INTEGRATION := TMPDIR=$(TMPDIR_TEST) $(GOTEST_INTEGRATION))
28-
rm -rf $(TMPDIR_TEST)
29-
mkdir $(TMPDIR_TEST)
28+
$(eval TMPDIR_INTEGRATION_TEST := $(PWD)/integration-test-tmp)
29+
$(eval GOTEST_INTEGRATION := TMPDIR=$(TMPDIR_INTEGRATION_TEST) $(GOTEST_INTEGRATION))
30+
rm -rf $(TMPDIR_INTEGRATION_TEST)
31+
mkdir $(TMPDIR_INTEGRATION_TEST)
3032
else
3133
test-integration-clean:
3234
endif
3335

36+
ifeq ($(OS),Darwin)
37+
test-regression-clean:
38+
$(eval TMPDIR_REGRESSION_TEST := $(PWD)/regression-test-tmp)
39+
$(eval GOTEST_REGRESSION := TMPDIR=$(TMPDIR_REGRESSION_TEST) $(GOTEST_REGRESSION))
40+
rm -rf $(TMPDIR_REGRESSION_TEST)
41+
mkdir $(TMPDIR_REGRESSION_TEST)
42+
else
43+
test-regression-clean:
44+
endif
45+
3446
test-integration-no-build: test-integration-clean
3547
TEST_PRUNE_WITH_IMAGE=false $(GOTEST_INTEGRATION) github.com/src-d/engine/cmdtests/
3648
$(GOTEST_INTEGRATION) github.com/src-d/engine/cmdtests/ -run TestPruneTestSuite/TestRunningContainersWithImages
3749

3850
test-integration: clean build docker-build test-integration-no-build
51+
52+
test-regression-usage:
53+
@echo
54+
@echo "Usage: \`PREV_ENGINE_VERSION=<first engine version to compare (default: 'latest')> CURR_ENGINE_VERSION=<second engine version to compare (default: 'local:HEAD')> make test-regression\`"
55+
@echo "Examples:"
56+
@echo "- \`make test-regression\` # tests that latest version is forward-compatible with current (HEAD) version"
57+
@echo "- \`PREV_ENGINE_VERSION=v0.10.0 make test-regression\` # tests that v0.10.0 version is forward-compatible with current (HEAD) version"
58+
@echo "- \`PREV_ENGINE_VERSION=v0.10.0 CURR_ENGINE_VERSION=v0.11.0 make test-regression\` # tests that v0.10.0 version is forward-compatible with v0.11.0 version"
59+
@echo
60+
61+
test-regression: test-regression-usage test-regression-clean
62+
$(GOTEST_REGRESSION) github.com/src-d/engine/cmdtests/

cmdtests/commander.go

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// +build integration regression
2+
3+
package cmdtests
4+
5+
import (
6+
"path"
7+
"runtime"
8+
"time"
9+
10+
"gotest.tools/icmd"
11+
)
12+
13+
type Commander struct {
14+
bin string
15+
}
16+
17+
func NewCommander(bin string) *Commander {
18+
return &Commander{bin: bin}
19+
}
20+
21+
func (s *Commander) Bin() string {
22+
return s.bin
23+
}
24+
25+
func (s *Commander) RunCmd(cmd string, args []string, cmdOperators ...icmd.CmdOp) *icmd.Result {
26+
args = append([]string{cmd}, args...)
27+
return icmd.RunCmd(icmd.Command(s.bin, args...), cmdOperators...)
28+
}
29+
30+
func (s *Commander) RunCommand(cmd string, args ...string) *icmd.Result {
31+
return s.RunCmd(cmd, args)
32+
}
33+
34+
func (s *Commander) StartCommand(cmd string, args []string, cmdOperators ...icmd.CmdOp) *icmd.Result {
35+
args = append([]string{cmd}, args...)
36+
return icmd.StartCmd(icmd.Command(s.bin, args...))
37+
}
38+
39+
func (s *Commander) Wait(timeout time.Duration, r *icmd.Result) *icmd.Result {
40+
return icmd.WaitOnCmd(timeout, r)
41+
}
42+
43+
// RunInit runs srcd init with workdir and custom config for integration tests
44+
func (s *Commander) RunInit(workdir string) *icmd.Result {
45+
return s.RunInitWithTimeout(workdir, 0)
46+
}
47+
48+
// RunInitWithTimeout runs srcd init with workdir and custom config for integration tests with timeout
49+
func (s *Commander) RunInitWithTimeout(workdir string, timeout time.Duration) *icmd.Result {
50+
_, filename, _, _ := runtime.Caller(0)
51+
configFile := path.Join(path.Dir(filename), "..", "integration-testing-config.yaml")
52+
return s.RunCmd("init", []string{workdir, "--config", configFile}, icmd.WithTimeout(timeout))
53+
}

cmdtests/common.go

+163-30
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// +build integration
1+
// +build integration regression
22

33
package cmdtests
44

@@ -7,19 +7,19 @@ import (
77
"io/ioutil"
88
"log"
99
"os"
10+
"reflect"
1011
"regexp"
1112
"runtime"
13+
"sort"
1214
"strings"
1315
"time"
1416

1517
"github.com/src-d/engine/docker"
1618
"github.com/stretchr/testify/suite"
17-
"gotest.tools/icmd"
1819
)
1920

2021
// TODO (carlosms) this could be build/bin, workaround for https://github.com/src-d/ci/issues/97
2122
var srcdBin = fmt.Sprintf("../build/engine_%s_%s/srcd", runtime.GOOS, runtime.GOARCH)
22-
var configFile = "../integration-testing-config.yaml"
2323

2424
func init() {
2525
if os.Getenv("SRCD_BIN") != "" {
@@ -29,6 +29,11 @@ func init() {
2929

3030
type IntegrationSuite struct {
3131
suite.Suite
32+
*Commander
33+
}
34+
35+
func NewIntegrationSuite() IntegrationSuite {
36+
return IntegrationSuite{Commander: &Commander{bin: srcdBin}}
3237
}
3338

3439
func (s *IntegrationSuite) SetupTest() {
@@ -40,33 +45,6 @@ func (s *IntegrationSuite) SetupTest() {
4045
s.Require().NoError(r.Error, r.Combined())
4146
}
4247

43-
func (s *IntegrationSuite) Bin() string {
44-
return srcdBin
45-
}
46-
47-
func (s *IntegrationSuite) RunCmd(cmd string, args []string, cmdOperators ...icmd.CmdOp) *icmd.Result {
48-
args = append([]string{cmd}, args...)
49-
return icmd.RunCmd(icmd.Command(srcdBin, args...), cmdOperators...)
50-
}
51-
52-
func (s *IntegrationSuite) RunCommand(cmd string, args ...string) *icmd.Result {
53-
return s.RunCmd(cmd, args)
54-
}
55-
56-
func (s *IntegrationSuite) StartCommand(cmd string, args []string, cmdOperators ...icmd.CmdOp) *icmd.Result {
57-
args = append([]string{cmd}, args...)
58-
return icmd.StartCmd(icmd.Command(srcdBin, args...))
59-
}
60-
61-
func (s *IntegrationSuite) Wait(timeout time.Duration, r *icmd.Result) *icmd.Result {
62-
return icmd.WaitOnCmd(timeout, r)
63-
}
64-
65-
// RunInit runs srcd init with workdir and custom config for integration tests
66-
func (s *IntegrationSuite) RunInit(workdir string) *icmd.Result {
67-
return s.RunCommand("init", workdir, "--config", configFile)
68-
}
69-
7048
var logMsgRegex = regexp.MustCompile(`.*msg="(.+?[^\\])"`)
7149

7250
func (s *IntegrationSuite) ParseLogMessages(memLog string) []string {
@@ -111,6 +89,10 @@ type IntegrationTmpDirSuite struct {
11189
TestDir string
11290
}
11391

92+
func NewIntegrationTmpDirSuite() IntegrationTmpDirSuite {
93+
return IntegrationTmpDirSuite{IntegrationSuite: NewIntegrationSuite()}
94+
}
95+
11496
func (s *IntegrationTmpDirSuite) SetupTest() {
11597
s.IntegrationSuite.SetupTest()
11698

@@ -138,7 +120,89 @@ func (cr *ChannelWriter) Write(b []byte) (int, error) {
138120
return len(b), nil
139121
}
140122

123+
type RegressionSuite struct {
124+
suite.Suite
125+
PrevCmd *Commander
126+
CurrCmd *Commander
127+
}
128+
129+
func NewRegressionSuite(prevBin, currentBin string) RegressionSuite {
130+
return RegressionSuite{
131+
PrevCmd: &Commander{bin: prevBin},
132+
CurrCmd: &Commander{bin: currentBin},
133+
}
134+
}
135+
136+
type SQLOutputTable struct {
137+
Data map[string][]string
138+
cols []string
139+
rowsN int
140+
}
141+
142+
func (s *SQLOutputTable) RequireEqual(o *SQLOutputTable) error {
143+
return s.requireEqual(o, false)
144+
}
145+
146+
func (s *SQLOutputTable) RequireStrictlyEqual(o *SQLOutputTable) error {
147+
return s.requireEqual(o, true)
148+
}
149+
150+
func (s *SQLOutputTable) requireEqual(o *SQLOutputTable, strictEmpty bool) error {
151+
if !strictEmpty && s.rowsN == 0 && o.rowsN == 0 {
152+
return nil
153+
}
154+
155+
if s.rowsN != o.rowsN {
156+
return s.diffErr("rows number", s.rowsN, o.rowsN)
157+
}
158+
159+
if !reflect.DeepEqual(s.cols, o.cols) {
160+
return s.diffErr("columns", s.cols, o.cols)
161+
}
162+
163+
var thisRows []string
164+
var otherRows []string
165+
166+
for i := 0; i < s.rowsN; i++ {
167+
var thisRow []string
168+
var otherRow []string
169+
170+
for _, c := range s.cols {
171+
thisRow = append(thisRow, s.Data[c][i])
172+
otherRow = append(otherRow, o.Data[c][i])
173+
}
174+
175+
thisRows = append(thisRows, strings.Join(thisRow, "|"))
176+
otherRows = append(otherRows, strings.Join(otherRow, "|"))
177+
}
178+
179+
sort.Strings(thisRows)
180+
sort.Strings(otherRows)
181+
182+
eq := reflect.DeepEqual(thisRows, otherRows)
183+
if eq {
184+
return nil
185+
}
186+
187+
return s.diffErr("rows", thisRows, otherRows)
188+
}
189+
190+
func (s *SQLOutputTable) diffErr(what string, this, other interface{}) error {
191+
return fmt.Errorf("Different %s:\n- actual: %v\n- expected: %v",
192+
what, this, other)
193+
}
194+
195+
func AreSQLOutputStrictlyEqual(s1 string, s2 string) error {
196+
return ParseSQLOutput(s1).RequireStrictlyEqual(ParseSQLOutput(s2))
197+
}
198+
199+
func AreSQLOutputEqual(s1 string, s2 string) error {
200+
return ParseSQLOutput(s1).RequireEqual(ParseSQLOutput(s2))
201+
}
202+
141203
var newLineFormatter = regexp.MustCompile(`(\r\n|\r|\n)`)
204+
var lineSepReg = regexp.MustCompile(`^\+[-+]+\+$`)
205+
var lineReg = regexp.MustCompile(`(?:\s+((?:[\w-\s.]+)?)\s+)`)
142206

143207
func normalizeNewLine(s string) string {
144208
return newLineFormatter.ReplaceAllString(s, "\n")
@@ -215,3 +279,72 @@ func (sl *StreamLinifier) Linify(in chan string) chan string {
215279

216280
return out
217281
}
282+
283+
func normalizeColName(s string) string {
284+
normCol := strings.ToUpper(strings.TrimSpace(s))
285+
return strings.Replace(
286+
strings.Replace(normCol, " ", "_", -1),
287+
"-", "_", -1)
288+
}
289+
290+
// ParseSQLOutput parses a string into a `SQLOutputTable` in order to facilitate
291+
// comparisons between different results.
292+
// This has been introduced mainly for two different reasons:
293+
// 1. handling ordering of rows (assuming that in equality checks the order is
294+
// not important). This could be solved also by forcing the queries to have an
295+
// 'ORDER BY' clause.
296+
// 2. during the replacement of the SQL cli, there was a version using our custom
297+
// SQL cli and another using the MySQL one. In one case the headers were all
298+
// capitalized and in the other were not. Additionally, if the header is
299+
// composed by multiple words, the separator in one case is the space and in
300+
// the other is the dash. Moreover, in our SQL cli the newline was '\n' while on
301+
// MySQL is '\r\n'. This latter thing is also a minimal problem as we can simply
302+
// replace all the newlines of the buffer.
303+
func ParseSQLOutput(raw string) *SQLOutputTable {
304+
splitted := strings.Split(normalizeNewLine(raw), "\n")
305+
header := false
306+
body := false
307+
var cols []string
308+
fields := make(map[string][]string)
309+
nRows := 0
310+
for _, s := range splitted {
311+
if !header && !body {
312+
if lineSepReg.MatchString(s) {
313+
header = true
314+
}
315+
316+
continue
317+
}
318+
319+
if header {
320+
if lineSepReg.MatchString(s) {
321+
header = false
322+
body = true
323+
continue
324+
}
325+
326+
for _, match := range lineReg.FindAllStringSubmatch(s, -1) {
327+
cols = append(cols, normalizeColName(match[1]))
328+
}
329+
}
330+
331+
if body {
332+
if lineSepReg.MatchString(s) {
333+
break
334+
}
335+
336+
nRows++
337+
for i, match := range lineReg.FindAllStringSubmatch(s, -1) {
338+
key := cols[i]
339+
fields[key] = append(fields[key], match[1])
340+
}
341+
}
342+
}
343+
344+
sort.Strings(cols)
345+
return &SQLOutputTable{
346+
Data: fields,
347+
cols: cols,
348+
rowsN: nRows,
349+
}
350+
}

cmdtests/components_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ type ComponentsTestSuite struct {
1919
}
2020

2121
func TestComponentsTestSuite(t *testing.T) {
22-
s := ComponentsTestSuite{}
22+
s := ComponentsTestSuite{IntegrationTmpDirSuite: cmdtests.NewIntegrationTmpDirSuite()}
2323
suite.Run(t, &s)
2424
}
2525

0 commit comments

Comments
 (0)