@@ -12,6 +12,7 @@ import (
12
12
"net/url"
13
13
"reflect"
14
14
"strings"
15
+ "sync"
15
16
"testing"
16
17
"time"
17
18
@@ -366,7 +367,7 @@ func TestTimeoutWithFullEchoStack(t *testing.T) {
366
367
t .Run (tc .name , func (t * testing.T ) {
367
368
e := echo .New ()
368
369
369
- buf := new (bytes. Buffer )
370
+ buf := new (coroutineSafeBuffer )
370
371
e .Logger .SetOutput (buf )
371
372
372
373
// NOTE: timeout middleware is first as it changes Response.Writer and causes data race for logger middleware if it is not first
@@ -419,6 +420,36 @@ func TestTimeoutWithFullEchoStack(t *testing.T) {
419
420
}
420
421
}
421
422
423
+ // as we are spawning multiple coroutines - one for http server, one for request, one by timeout middleware, one by testcase
424
+ // we are accessing logger (writing/reading) from multiple coroutines and causing dataraces (most often reported on macos)
425
+ // we could be writing to logger in logger middleware and at the same time our tests is getting logger buffer contents
426
+ // in testcase coroutine.
427
+ type coroutineSafeBuffer struct {
428
+ bytes.Buffer
429
+ lock sync.RWMutex
430
+ }
431
+
432
+ func (b * coroutineSafeBuffer ) Write (p []byte ) (n int , err error ) {
433
+ b .lock .Lock ()
434
+ defer b .lock .Unlock ()
435
+
436
+ return b .Buffer .Write (p )
437
+ }
438
+
439
+ func (b * coroutineSafeBuffer ) Bytes () []byte {
440
+ b .lock .RLock ()
441
+ defer b .lock .RUnlock ()
442
+
443
+ return b .Buffer .Bytes ()
444
+ }
445
+
446
+ func (b * coroutineSafeBuffer ) String () string {
447
+ b .lock .RLock ()
448
+ defer b .lock .RUnlock ()
449
+
450
+ return b .Buffer .String ()
451
+ }
452
+
422
453
func startServer (e * echo.Echo ) (* http.Server , string , error ) {
423
454
l , err := net .Listen ("tcp" , ":0" )
424
455
if err != nil {
0 commit comments