refactor(netxlite/iox): group tests and avoid races (#484)

Part of https://github.com/ooni/probe/issues/1591
This commit is contained in:
Simone Basso 2021-09-07 22:41:34 +02:00 committed by GitHub
parent 1472f7530b
commit 9e82e37ab8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -5,130 +5,182 @@ import (
"errors" "errors"
"io" "io"
"strings" "strings"
"sync"
"testing" "testing"
"time"
"github.com/ooni/probe-cli/v3/internal/netxlite/mocks" "github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
) )
func TestReadAllContextCommonCase(t *testing.T) { func TestReadAllContext(t *testing.T) {
r := strings.NewReader("deadbeef") t.Run("with success and background context", func(t *testing.T) {
ctx := context.Background() r := strings.NewReader("deadbeef")
out, err := ReadAllContext(ctx, r) ctx := context.Background()
if err != nil { out, err := ReadAllContext(ctx, r)
t.Fatal(err) if err != nil {
} t.Fatal(err)
if len(out) != 8 { }
t.Fatal("not the expected number of bytes") if len(out) != 8 {
} t.Fatal("not the expected number of bytes")
}
})
t.Run("with failure and background context", func(t *testing.T) {
expected := errors.New("mocked error")
r := &mocks.Reader{
MockRead: func(b []byte) (int, error) {
return 0, expected
},
}
ctx := context.Background()
out, err := ReadAllContext(ctx, r)
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
if len(out) != 0 {
t.Fatal("not the expected number of bytes")
}
})
t.Run("with success and cancelled context", func(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
sigch := make(chan interface{})
r := &mocks.Reader{
MockRead: func(b []byte) (int, error) {
defer wg.Done()
<-sigch
// "When Read encounters an error or end-of-file condition
// after successfully reading n > 0 bytes, it returns
// the number of bytes read. It may return the (non-nil)
// error from the same call or return the error (and n == 0)
// from a subsequent call.""
//
// See https://pkg.go.dev/io#Reader
return len(b), io.EOF
},
}
ctx, cancel := context.WithCancel(context.Background())
cancel() // fail immediately
out, err := ReadAllContext(ctx, r)
if !errors.Is(err, context.Canceled) {
t.Fatal("not the error we expected", err)
}
if len(out) != 0 {
t.Fatal("not the expected number of bytes")
}
close(sigch)
wg.Wait()
})
t.Run("with failure and cancelled context", func(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
sigch := make(chan interface{})
expected := errors.New("mocked error")
r := &mocks.Reader{
MockRead: func(b []byte) (int, error) {
defer wg.Done()
<-sigch
return 0, expected
},
}
ctx, cancel := context.WithCancel(context.Background())
cancel() // fail immediately
out, err := ReadAllContext(ctx, r)
if !errors.Is(err, context.Canceled) {
t.Fatal("not the error we expected", err)
}
if len(out) != 0 {
t.Fatal("not the expected number of bytes")
}
close(sigch)
wg.Wait()
})
} }
func TestReadAllContextWithError(t *testing.T) { func TestCopyContext(t *testing.T) {
expected := errors.New("mocked error") t.Run("with success and background context", func(t *testing.T) {
r := &mocks.Reader{ r := strings.NewReader("deadbeef")
MockRead: func(b []byte) (int, error) { ctx := context.Background()
return 0, expected out, err := CopyContext(ctx, io.Discard, r)
}, if err != nil {
} t.Fatal(err)
ctx := context.Background() }
out, err := ReadAllContext(ctx, r) if out != 8 {
if !errors.Is(err, expected) { t.Fatal("not the expected number of bytes")
t.Fatal("not the error we expected", err) }
} })
if len(out) != 0 {
t.Fatal("not the expected number of bytes")
}
}
func TestReadAllContextWithCancelledContext(t *testing.T) { t.Run("with failure and background context", func(t *testing.T) {
r := strings.NewReader("deadbeef") expected := errors.New("mocked error")
ctx, cancel := context.WithCancel(context.Background()) r := &mocks.Reader{
cancel() // fail immediately MockRead: func(b []byte) (int, error) {
out, err := ReadAllContext(ctx, r) return 0, expected
if !errors.Is(err, context.Canceled) { },
t.Fatal("not the error we expected", err) }
} ctx := context.Background()
if len(out) != 0 { out, err := CopyContext(ctx, io.Discard, r)
t.Fatal("not the expected number of bytes") if !errors.Is(err, expected) {
} t.Fatal("not the error we expected", err)
} }
if out != 0 {
t.Fatal("not the expected number of bytes")
}
})
func TestReadAllContextWithErrorAndCancelledContext(t *testing.T) { t.Run("with success and cancelled context", func(t *testing.T) {
expected := errors.New("mocked error") wg := &sync.WaitGroup{}
r := &mocks.Reader{ wg.Add(1)
MockRead: func(b []byte) (int, error) { sigch := make(chan interface{})
time.Sleep(time.Millisecond) r := &mocks.Reader{
return 0, expected MockRead: func(b []byte) (int, error) {
}, defer wg.Done()
} <-sigch
ctx, cancel := context.WithCancel(context.Background()) // "When Read encounters an error or end-of-file condition
cancel() // fail immediately // after successfully reading n > 0 bytes, it returns
out, err := ReadAllContext(ctx, r) // the number of bytes read. It may return the (non-nil)
if !errors.Is(err, context.Canceled) { // error from the same call or return the error (and n == 0)
t.Fatal("not the error we expected", err) // from a subsequent call.""
} //
if len(out) != 0 { // See https://pkg.go.dev/io#Reader
t.Fatal("not the expected number of bytes") return len(b), io.EOF
} },
} }
ctx, cancel := context.WithCancel(context.Background())
cancel() // fail immediately
out, err := CopyContext(ctx, io.Discard, r)
if !errors.Is(err, context.Canceled) {
t.Fatal("not the error we expected", err)
}
if out != 0 {
t.Fatal("not the expected number of bytes")
}
close(sigch)
wg.Wait()
})
func TestCopyContextCommonCase(t *testing.T) { t.Run("with failure and cancelled context", func(t *testing.T) {
r := strings.NewReader("deadbeef") wg := &sync.WaitGroup{}
ctx := context.Background() wg.Add(1)
out, err := CopyContext(ctx, io.Discard, r) sigch := make(chan interface{})
if err != nil { expected := errors.New("mocked error")
t.Fatal(err) r := &mocks.Reader{
} MockRead: func(b []byte) (int, error) {
if out != 8 { defer wg.Done()
t.Fatal("not the expected number of bytes") <-sigch
} return 0, expected
} },
}
func TestCopyContextWithError(t *testing.T) { ctx, cancel := context.WithCancel(context.Background())
expected := errors.New("mocked error") cancel() // fail immediately
r := &mocks.Reader{ out, err := CopyContext(ctx, io.Discard, r)
MockRead: func(b []byte) (int, error) { if !errors.Is(err, context.Canceled) {
return 0, expected t.Fatal("not the error we expected", err)
}, }
} if out != 0 {
ctx := context.Background() t.Fatal("not the expected number of bytes")
out, err := CopyContext(ctx, io.Discard, r) }
if !errors.Is(err, expected) { close(sigch)
t.Fatal("not the error we expected", err) wg.Wait()
} })
if out != 0 {
t.Fatal("not the expected number of bytes")
}
}
func TestCopyContextWithCancelledContext(t *testing.T) {
r := strings.NewReader("deadbeef")
ctx, cancel := context.WithCancel(context.Background())
cancel() // fail immediately
out, err := CopyContext(ctx, io.Discard, r)
if !errors.Is(err, context.Canceled) {
t.Fatal("not the error we expected", err)
}
if out != 0 {
t.Fatal("not the expected number of bytes")
}
}
func TestCopyContextWithErrorAndCancelledContext(t *testing.T) {
expected := errors.New("mocked error")
r := &mocks.Reader{
MockRead: func(b []byte) (int, error) {
time.Sleep(time.Millisecond)
return 0, expected
},
}
ctx, cancel := context.WithCancel(context.Background())
cancel() // fail immediately
out, err := CopyContext(ctx, io.Discard, r)
if !errors.Is(err, context.Canceled) {
t.Fatal("not the error we expected", err)
}
if out != 0 {
t.Fatal("not the expected number of bytes")
}
} }