@@ -5,24 +5,225 @@ package cmd
5
5
import (
6
6
"bytes"
7
7
"context"
8
+ "fmt"
8
9
"io"
9
10
"io/ioutil"
10
11
"log"
11
12
"os"
12
13
"os/exec"
13
14
"path"
14
15
"path/filepath"
16
+ "regexp"
15
17
"strings"
16
18
"testing"
17
19
"time"
18
20
21
+ "github.com/kr/pty"
19
22
cmdtest "github.com/src-d/engine/cmd/test-utils"
20
-
23
+ "github.com/src-d/engine/components"
24
+ "github.com/src-d/engine/docker"
21
25
"github.com/stretchr/testify/assert"
22
26
"github.com/stretchr/testify/require"
23
27
"github.com/stretchr/testify/suite"
24
28
)
25
29
30
+ var showTablesOutput = sqlOutput (`+--------------+
31
+ | Table |
32
+ +--------------+
33
+ | blobs |
34
+ | commit_blobs |
35
+ | commit_files |
36
+ | commit_trees |
37
+ | commits |
38
+ | files |
39
+ | ref_commits |
40
+ | refs |
41
+ | remotes |
42
+ | repositories |
43
+ | tree_entries |
44
+ +--------------+
45
+ ` )
46
+
47
+ var showRepoTableDescOutput = sqlOutput (`+---------------+------+
48
+ | name | type |
49
+ +---------------+------+
50
+ | repository_id | TEXT |
51
+ +---------------+------+
52
+ ` )
53
+
54
+ type SQLREPLTestSuite struct {
55
+ cmdtest.IntegrationSuite
56
+ testDir string
57
+ }
58
+
59
+ func TestSQLREPLTestSuite (t * testing.T ) {
60
+ s := SQLREPLTestSuite {}
61
+ suite .Run (t , & s )
62
+ }
63
+
64
+ func (s * SQLREPLTestSuite ) SetupTest () {
65
+ var err error
66
+ s .testDir , err = ioutil .TempDir ("" , "sql-repl-test" )
67
+ if err != nil {
68
+ log .Fatal (err )
69
+ }
70
+ }
71
+
72
+ func (s * SQLREPLTestSuite ) TearDownTest () {
73
+ s .RunCommand (context .Background (), "prune" )
74
+ os .RemoveAll (s .testDir )
75
+ }
76
+
77
+ func (s * SQLREPLTestSuite ) TestREPL () {
78
+ // When it is not in a terminal, the command reads stdin and exits.
79
+ // You can see it running:
80
+ // $ echo "show tables;" | ./srcd sql
81
+ // So this test does not really interacts with the REPL prompt, but we still
82
+ // test that the code that processes each read line is working as expected.
83
+
84
+ require := s .Require ()
85
+
86
+ var out , in bytes.Buffer
87
+
88
+ command := s .CommandContext (context .TODO (), "sql" )
89
+ command .Stdout = & out
90
+ command .Stderr = & out
91
+ command .Stdin = & in
92
+
93
+ io .WriteString (& in , "show tables;\n " )
94
+ io .WriteString (& in , "describe table repositories;\n " )
95
+
96
+ err := command .Start ()
97
+ require .NoError (err )
98
+
99
+ err = command .Wait ()
100
+ require .NoError (err )
101
+
102
+ require .Contains (out .String (), showTablesOutput )
103
+ require .Contains (out .String (), showRepoTableDescOutput )
104
+ }
105
+
106
+ func (s * SQLREPLTestSuite ) TestInteractiveREPL () {
107
+ require := s .Require ()
108
+
109
+ command , stdin , ch := s .runInteractiveRepl ()
110
+
111
+ res := s .runInteractiveQuery (stdin , "show tables;\n " , ch )
112
+ require .Contains (res , showTablesOutput )
113
+
114
+ res = s .runInteractiveQuery (stdin , "describe table repositories;\n " , ch )
115
+ require .Contains (res , showRepoTableDescOutput )
116
+
117
+ require .NoError (s .exitInteractiveAndWait (10 * time .Second , stdin , ch ))
118
+ require .NoError (s .waitMysqlCliContainerStopped (10 * time .Second ))
119
+
120
+ command .Wait ()
121
+ }
122
+
123
+ func (s * SQLREPLTestSuite ) runInteractiveRepl () (* exec.Cmd , * os.File , <- chan string ) {
124
+ s .T ().Helper ()
125
+
126
+ command := s .CommandContext (context .TODO (), "sql" )
127
+
128
+ ch := make (chan string )
129
+ cr := cmdtest .NewChannelWriter (ch )
130
+
131
+ command .Stdout = cr
132
+ command .Stderr = cr
133
+
134
+ f , err := pty .Start (command )
135
+ if err != nil {
136
+ panic (err )
137
+ }
138
+
139
+ mysqlCliReady := make (chan struct {})
140
+
141
+ go func () {
142
+ for s := range ch {
143
+ lines := strings .Split (s , "\n " )
144
+ for _ , l := range lines {
145
+ if strings .HasPrefix (l , "mysql>" ) {
146
+ mysqlCliReady <- struct {}{}
147
+ return
148
+ }
149
+ }
150
+ }
151
+ }()
152
+
153
+ <- mysqlCliReady
154
+
155
+ return command , f , ch
156
+ }
157
+
158
+ func (s * SQLREPLTestSuite ) runInteractiveQuery (stdin * os.File , query string , ch <- chan string ) string {
159
+ io .WriteString (stdin , query )
160
+
161
+ var res strings.Builder
162
+ for c := range ch {
163
+ fmt .Printf ("Message: %s\n " , c )
164
+ res .WriteString (c )
165
+ if s .containsSQLOutput (res .String ()) {
166
+ break
167
+ }
168
+ }
169
+
170
+ return res .String ()
171
+ }
172
+
173
+ func (s * SQLREPLTestSuite ) exitInteractiveAndWait (timeout time.Duration , stdin * os.File , ch <- chan string ) error {
174
+ io .WriteString (stdin , "exit;\n " )
175
+
176
+ done := make (chan struct {})
177
+ go func () {
178
+ for c := range ch {
179
+ if strings .Contains (c , "Bye" ) {
180
+ done <- struct {}{}
181
+ // don't return in order to consume all output and let the process exit
182
+ }
183
+ }
184
+ }()
185
+
186
+ select {
187
+ case <- done :
188
+ stdin .Close ()
189
+ return nil
190
+ case <- time .After (timeout ):
191
+ return fmt .Errorf ("timeout of %v elapsed while waiting to exit" , timeout )
192
+ }
193
+ }
194
+
195
+ func (s * SQLREPLTestSuite ) waitMysqlCliContainerStopped (timeout time.Duration ) error {
196
+ done := make (chan struct {})
197
+ go func () {
198
+ for {
199
+ running , err := docker .IsRunning (components .MysqlCli .Name , "" )
200
+ if ! running {
201
+ done <- struct {}{}
202
+ return
203
+ }
204
+
205
+ if err != nil {
206
+ panic (err )
207
+ }
208
+
209
+ time .Sleep (timeout / 5 )
210
+ }
211
+ }()
212
+
213
+ select {
214
+ case <- done :
215
+ return nil
216
+ case <- time .After (timeout ):
217
+ return fmt .Errorf ("timeout of %v elapsed while waiting to stop container" , timeout )
218
+ }
219
+ }
220
+
221
+ func (s * SQLREPLTestSuite ) containsSQLOutput (out string ) bool {
222
+ sep := regexp .MustCompile (`\+[-]+\+` )
223
+ matches := sep .FindAllStringIndex (out , - 1 )
224
+ return len (matches ) == 3
225
+ }
226
+
26
227
type SQLTestSuite struct {
27
228
cmdtest.IntegrationSuite
28
229
testDir string
@@ -46,23 +247,6 @@ func (s *SQLTestSuite) TearDownTest() {
46
247
os .RemoveAll (s .testDir )
47
248
}
48
249
49
- var showTablesOutput = sqlOutput (`+--------------+
50
- | Table |
51
- +--------------+
52
- | blobs |
53
- | commit_blobs |
54
- | commit_files |
55
- | commit_trees |
56
- | commits |
57
- | files |
58
- | ref_commits |
59
- | refs |
60
- | remotes |
61
- | repositories |
62
- | tree_entries |
63
- +--------------+
64
- ` )
65
-
66
250
func (s * SQLTestSuite ) TestInit () {
67
251
require := s .Require ()
68
252
@@ -165,55 +349,6 @@ func (s *SQLTestSuite) TestWrongQuery() {
165
349
}
166
350
}
167
351
168
- func (s * SQLTestSuite ) TestREPL () {
169
- // When it is not in a terminal, the command reads stdin and exits.
170
- // You can see it running:
171
- // $ echo "show tables;" | ./srcd sql
172
- // So this test does not really interacts with the REPL prompt, but we still
173
- // test that the code that processes each read line is working as expected.
174
-
175
- require := s .Require ()
176
-
177
- var out , in bytes.Buffer
178
-
179
- command := s .CommandContext (context .TODO (), "sql" )
180
- command .Stdout = & out
181
- command .Stderr = & out
182
- command .Stdin = & in
183
-
184
- io .WriteString (& in , "show tables;\n " )
185
- io .WriteString (& in , "describe table repositories;\n " )
186
-
187
- err := command .Start ()
188
- require .NoError (err )
189
-
190
- err = command .Wait ()
191
- require .NoError (err )
192
-
193
- expected := sqlOutput (`+--------------+
194
- | Table |
195
- +--------------+
196
- | blobs |
197
- | commit_blobs |
198
- | commit_files |
199
- | commit_trees |
200
- | commits |
201
- | files |
202
- | ref_commits |
203
- | refs |
204
- | remotes |
205
- | repositories |
206
- | tree_entries |
207
- +--------------+
208
- +---------------+------+
209
- | name | type |
210
- +---------------+------+
211
- | repository_id | TEXT |
212
- +---------------+------+` )
213
-
214
- require .Contains (out .String (), expected )
215
- }
216
-
217
352
func (s * SQLTestSuite ) TestIndexesWorkdirChange () {
218
353
require := s .Require ()
219
354
0 commit comments