@@ -20,6 +20,7 @@ import (
20
20
21
21
"github.com/google/uuid"
22
22
"github.com/stretchr/testify/require"
23
+ "golang.org/x/xerrors"
23
24
24
25
"cdr.dev/slog"
25
26
"cdr.dev/slog/sloggers/slogtest"
@@ -68,19 +69,48 @@ func TestMain(m *testing.M) {
68
69
69
70
var topologies = []integration.TestTopology {
70
71
{
72
+ // Test that DERP over loopback works.
71
73
Name : "BasicLoopbackDERP" ,
72
74
SetupNetworking : integration .SetupNetworkingLoopback ,
73
- StartServer : integration .StartServerBasic ,
74
- StartClient : integration .StartClientBasic ,
75
+ ServerOptions : integration.ServerOptions {} ,
76
+ StartClient : integration .StartClientDERP ,
75
77
RunTests : integration .TestSuite ,
76
78
},
77
79
{
80
+ // Test that DERP over "easy" NAT works. The server, client 1 and client
81
+ // 2 are on different networks with a shared router, and the router
82
+ // masquerades the traffic.
78
83
Name : "EasyNATDERP" ,
79
84
SetupNetworking : integration .SetupNetworkingEasyNAT ,
80
- StartServer : integration .StartServerBasic ,
81
- StartClient : integration .StartClientBasic ,
85
+ ServerOptions : integration.ServerOptions {} ,
86
+ StartClient : integration .StartClientDERP ,
82
87
RunTests : integration .TestSuite ,
83
88
},
89
+ {
90
+ // Test that DERP over WebSocket (as well as DERPForceWebSockets works).
91
+ // This does not test the actual DERP failure detection code and
92
+ // automatic fallback.
93
+ Name : "DERPForceWebSockets" ,
94
+ SetupNetworking : integration .SetupNetworkingEasyNAT ,
95
+ ServerOptions : integration.ServerOptions {
96
+ FailUpgradeDERP : false ,
97
+ DERPWebsocketOnly : true ,
98
+ },
99
+ StartClient : integration .StartClientDERPWebSockets ,
100
+ RunTests : integration .TestSuite ,
101
+ },
102
+ {
103
+ // Test that falling back to DERP over WebSocket works.
104
+ Name : "DERPFallbackWebSockets" ,
105
+ SetupNetworking : integration .SetupNetworkingEasyNAT ,
106
+ ServerOptions : integration.ServerOptions {
107
+ FailUpgradeDERP : true ,
108
+ DERPWebsocketOnly : false ,
109
+ },
110
+ // Use a basic client that will try `Upgrade: derp` first.
111
+ StartClient : integration .StartClientDERP ,
112
+ RunTests : integration .TestSuite ,
113
+ },
84
114
}
85
115
86
116
//nolint:paralleltest,tparallel
@@ -101,19 +131,17 @@ func TestIntegration(t *testing.T) {
101
131
networking := topo .SetupNetworking (t , log )
102
132
103
133
// Fork the three child processes.
104
- serverErrCh , closeServer := startServerSubprocess (t , topo .Name , networking )
134
+ closeServer := startServerSubprocess (t , topo .Name , networking )
105
135
// client1 runs the tests.
106
136
client1ErrCh , _ := startClientSubprocess (t , topo .Name , networking , 1 )
107
- client2ErrCh , closeClient2 := startClientSubprocess (t , topo .Name , networking , 2 )
137
+ _ , closeClient2 := startClientSubprocess (t , topo .Name , networking , 2 )
108
138
109
139
// Wait for client1 to exit.
110
140
require .NoError (t , <- client1ErrCh , "client 1 exited" )
111
141
112
142
// Close client2 and the server.
113
- closeClient2 ()
114
- require .NoError (t , <- client2ErrCh , "client 2 exited" )
115
- closeServer ()
116
- require .NoError (t , <- serverErrCh , "server exited" )
143
+ require .NoError (t , closeClient2 (), "client 2 exited" )
144
+ require .NoError (t , closeServer (), "server exited" )
117
145
})
118
146
}
119
147
}
@@ -138,15 +166,32 @@ func handleTestSubprocess(t *testing.T) {
138
166
139
167
//nolint:parralleltest
140
168
t .Run (testName , func (t * testing.T ) {
141
- log := slogtest .Make (t , nil ).Leveled (slog .LevelDebug )
169
+ logger := slogtest .Make (t , nil ).Leveled (slog .LevelDebug )
142
170
switch * role {
143
171
case "server" :
144
- log = log .Named ("server" )
145
- topo .StartServer (t , log , * serverListenAddr )
172
+ logger = logger .Named ("server" )
173
+
174
+ srv := http.Server {
175
+ Addr : * serverListenAddr ,
176
+ Handler : topo .ServerOptions .Router (t , logger ),
177
+ ReadTimeout : 10 * time .Second ,
178
+ }
179
+ serveDone := make (chan struct {})
180
+ go func () {
181
+ defer close (serveDone )
182
+ err := srv .ListenAndServe ()
183
+ if err != nil && ! xerrors .Is (err , http .ErrServerClosed ) {
184
+ t .Error ("HTTP server error:" , err )
185
+ }
186
+ }()
187
+ t .Cleanup (func () {
188
+ _ = srv .Close ()
189
+ <- serveDone
190
+ })
146
191
// no exit
147
192
148
193
case "client" :
149
- log = log .Named (* clientName )
194
+ logger = logger .Named (* clientName )
150
195
serverURL , err := url .Parse (* clientServerURL )
151
196
require .NoErrorf (t , err , "parse server url %q" , * clientServerURL )
152
197
myID , err := uuid .Parse (* clientMyID )
@@ -156,7 +201,7 @@ func handleTestSubprocess(t *testing.T) {
156
201
157
202
waitForServerAvailable (t , serverURL )
158
203
159
- conn := topo .StartClient (t , log , serverURL , myID , peerID )
204
+ conn := topo .StartClient (t , logger , serverURL , myID , peerID )
160
205
161
206
if * clientRunTests {
162
207
// Wait for connectivity.
@@ -165,7 +210,7 @@ func handleTestSubprocess(t *testing.T) {
165
210
t .Fatalf ("peer %v did not become reachable" , peerIP )
166
211
}
167
212
168
- topo .RunTests (t , log , serverURL , myID , peerID , conn )
213
+ topo .RunTests (t , logger , serverURL , myID , peerID , conn )
169
214
// then exit
170
215
return
171
216
}
@@ -206,16 +251,17 @@ func waitForServerAvailable(t *testing.T, serverURL *url.URL) {
206
251
t .Fatalf ("server did not become available after %v" , timeout )
207
252
}
208
253
209
- func startServerSubprocess (t * testing.T , topologyName string , networking integration.TestNetworking ) ( <- chan error , func ()) {
210
- return startSubprocess (t , "server" , networking .ProcessServer .NetNS , []string {
254
+ func startServerSubprocess (t * testing.T , topologyName string , networking integration.TestNetworking ) func () error {
255
+ _ , closeFn := startSubprocess (t , "server" , networking .ProcessServer .NetNS , []string {
211
256
"--subprocess" ,
212
257
"--test-name=" + topologyName ,
213
258
"--role=server" ,
214
259
"--server-listen-addr=" + networking .ServerListenAddr ,
215
260
})
261
+ return closeFn
216
262
}
217
263
218
- func startClientSubprocess (t * testing.T , topologyName string , networking integration.TestNetworking , clientNumber int ) (<- chan error , func ()) {
264
+ func startClientSubprocess (t * testing.T , topologyName string , networking integration.TestNetworking , clientNumber int ) (<- chan error , func () error ) {
219
265
require .True (t , clientNumber == 1 || clientNumber == 2 )
220
266
221
267
var (
@@ -247,7 +293,13 @@ func startClientSubprocess(t *testing.T, topologyName string, networking integra
247
293
return startSubprocess (t , clientName , netNS , flags )
248
294
}
249
295
250
- func startSubprocess (t * testing.T , processName string , netNS * os.File , flags []string ) (<- chan error , func ()) {
296
+ // startSubprocess starts a subprocess with the given flags and returns a
297
+ // channel that will receive the error when the subprocess exits. The returned
298
+ // function can be used to close the subprocess.
299
+ //
300
+ // Do not call close then wait on the channel. Use the returned value from the
301
+ // function instead in this case.
302
+ func startSubprocess (t * testing.T , processName string , netNS * os.File , flags []string ) (<- chan error , func () error ) {
251
303
name := os .Args [0 ]
252
304
// Always use verbose mode since it gets piped to the parent test anyways.
253
305
args := append (os .Args [1 :], append ([]string {"-test.v=true" }, flags ... )... )
@@ -289,15 +341,15 @@ func startSubprocess(t *testing.T, processName string, netNS *os.File, flags []s
289
341
close (waitErr )
290
342
}()
291
343
292
- closeFn := func () {
344
+ closeFn := func () error {
293
345
_ = cmd .Process .Signal (syscall .SIGTERM )
294
346
select {
295
347
case <- time .After (5 * time .Second ):
296
348
_ = cmd .Process .Kill ()
297
- case <- waitErr :
298
- return
349
+ case err := <- waitErr :
350
+ return err
299
351
}
300
- <- waitErr
352
+ return <- waitErr
301
353
}
302
354
303
355
t .Cleanup (func () {
@@ -310,7 +362,7 @@ func startSubprocess(t *testing.T, processName string, netNS *os.File, flags []s
310
362
default :
311
363
}
312
364
313
- closeFn ()
365
+ _ = closeFn ()
314
366
})
315
367
316
368
return waitErr , closeFn
@@ -338,6 +390,11 @@ func (w *testWriter) Write(p []byte) (n int, err error) {
338
390
// then it's a test result line. We want to capture it and log it later.
339
391
trimmed := strings .TrimSpace (s )
340
392
if strings .HasPrefix (trimmed , "--- PASS" ) || strings .HasPrefix (trimmed , "--- FAIL" ) || trimmed == "PASS" || trimmed == "FAIL" {
393
+ // Also fail the test if we see a FAIL line.
394
+ if strings .Contains (trimmed , "FAIL" ) {
395
+ w .t .Errorf ("subprocess logged test failure: %s: \t %s" , w .name , s )
396
+ }
397
+
341
398
w .capturedLines = append (w .capturedLines , s )
342
399
continue
343
400
}
0 commit comments