Skip to content

Commit

Permalink
Assert tests (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
akrylysov authored Nov 8, 2020
1 parent 5c32d7d commit cc107cd
Show file tree
Hide file tree
Showing 3 changed files with 282 additions and 6 deletions.
2 changes: 2 additions & 0 deletions index.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ func (idx *index) bucketIndex(hash uint32) uint32 {
return bidx
}

// TODO: deeply nested callbacks are hard to reason about.
// TODO: rewrite the function to return an iterator if there is no performance implications.
func (idx *index) forEachBucket(startBucketIdx uint32, cb func(bucketHandle) (bool, error)) error {
off := bucketOffset(startBucketIdx)
f := idx.main.MmapFile
Expand Down
29 changes: 23 additions & 6 deletions internal/assert/assert.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,45 @@ func Equal(t testing.TB, expected interface{}, actual interface{}) {
}
}

// Nil fails the test when actual is not nil.
func Nil(t testing.TB, actual interface{}) {
if actual != nil && !reflect.ValueOf(actual).IsNil() {
// https://github.com/golang/go/blob/go1.15/src/reflect/value.go#L1071
var nillableKinds = map[reflect.Kind]bool{
reflect.Chan: true,
reflect.Func: true,
reflect.Map: true,
reflect.Ptr: true,
reflect.UnsafePointer: true,
reflect.Interface: true,
reflect.Slice: true,
}

// Nil fails the test when obj is not nil.
func Nil(t testing.TB, obj interface{}) {
if obj == nil {
return
}
val := reflect.ValueOf(obj)
if !nillableKinds[val.Kind()] || !val.IsNil() {
t.Helper()
t.Fatalf("expected nil; got %+v", actual)
t.Fatalf("expected nil; got %+v", obj)
}
}

const pollingInterval = time.Millisecond * 10 // How often CompleteWithin polls the cond function.

// CompleteWithin fails the test when cond doesn't succeed within waitDur.
func CompleteWithin(t testing.TB, waitDur time.Duration, cond func() bool) {
start := time.Now()
for time.Since(start) < waitDur {
if cond() {
return
}
time.Sleep(time.Millisecond * 10)
time.Sleep(pollingInterval)
}
t.Helper()
t.Fatalf("expected to complete within %v", waitDur)
}

// Panic fails the test when the test doesn't panic.
// Panic fails the test when the test doesn't panic with the expected message.
func Panic(t testing.TB, expectedMessage string, f func()) {
t.Helper()
var message interface{}
Expand Down
257 changes: 257 additions & 0 deletions internal/assert/assert_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
package assert

import (
"fmt"
"sync"
"testing"
"time"
)

func TestEqual(t *testing.T) {
testCases := []struct {
first interface{}
second interface{}
expectedFailed bool
}{
{
first: 1,
second: 1,
expectedFailed: false,
},

{
first: nil,
second: nil,
expectedFailed: false,
},
{
first: "1",
second: "1",
expectedFailed: false,
},
{
first: struct{}{},
second: struct{}{},
expectedFailed: false,
},
{
first: struct{ x int }{x: 1},
second: struct{ x int }{x: 1},
expectedFailed: false,
},
{
first: 1,
second: 2,
expectedFailed: true,
},
{
first: 1,
second: "1",
expectedFailed: true,
},
{
first: 1,
second: 1.0,
expectedFailed: true,
},
{
first: struct{ x int }{x: 1},
second: struct{ x int }{x: 2},
expectedFailed: true,
},
{
first: struct{ x int }{x: 1},
second: struct{ y int }{y: 1},
expectedFailed: true,
},
}

for i, tc := range testCases {
t.Run(fmt.Sprintf("case %d %+v", i, tc), func(t *testing.T) {
mock := &testing.T{}
wg := &sync.WaitGroup{}
wg.Add(1)
// Run the asserting in a goroutine. t.Fatal calls runtime.Goexit.
go func() {
defer wg.Done()
Equal(mock, tc.first, tc.second)
}()
wg.Wait()
failed := mock.Failed()
if tc.expectedFailed != failed {
t.Fatalf("expected to fail: %t; failed: %t", tc.expectedFailed, failed)
}
})
}
}

func TestNil(t *testing.T) {
var nilIntPtr *int
var nilStructPtr *struct{ x int }
var nilSlice []string

testCases := []struct {
obj interface{}
expectedFailed bool
}{
{
obj: nil,
expectedFailed: false,
},
{
obj: nilIntPtr,
expectedFailed: false,
},
{
obj: nilStructPtr,
expectedFailed: false,
},
{
obj: nilSlice,
expectedFailed: false,
},
{
obj: 1,
expectedFailed: true,
},
{
obj: "1",
expectedFailed: true,
},
{
obj: []string{},
expectedFailed: true,
},
{
obj: [2]int{1, 1},
expectedFailed: true,
},
}

for i, tc := range testCases {
t.Run(fmt.Sprintf("case %d %+v", i, tc.obj), func(t *testing.T) {
mock := &testing.T{}
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
Nil(mock, tc.obj)
}()
wg.Wait()
if tc.expectedFailed != mock.Failed() {
t.Fatalf("expected to fail: %t; failed: %t", tc.expectedFailed, mock.Failed())
}
})
}
}

func TestPanic(t *testing.T) {
testCases := []struct {
name string
f func()
expectedFailed bool
}{
{
name: "panic",
f: func() {
panic("message123")
},
expectedFailed: false,
},
{
name: "panic: wrong message",
f: func() {
panic("message456")
},
expectedFailed: true,
},
{
name: "no panic",
f: func() {},
expectedFailed: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mock := &testing.T{}
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
Panic(mock, "message123", tc.f)
}()
wg.Wait()
if tc.expectedFailed != mock.Failed() {
t.Fatalf("expected to fail: %t; failed: %t", tc.expectedFailed, mock.Failed())
}
})
}
}

func TestCompleteWithin(t *testing.T) {
var tc2Tries int
var tc4Tries int
testCases := []struct {
name string
dur time.Duration
cond func() bool
expectedFailed bool
}{
{
name: "completed: first try",
dur: time.Hour,
cond: func() bool {
return true
},
expectedFailed: false,
},
{
name: "completed: second try",
dur: time.Hour,
cond: func() bool {
if tc2Tries == 0 {
tc2Tries++
return false
}
return true
},
expectedFailed: false,
},
{
name: "not completed",
dur: time.Nanosecond,
cond: func() bool {
return false
},
expectedFailed: true,
},
{
name: "not completed: timeout",
dur: time.Nanosecond,
cond: func() bool {
if tc4Tries == 0 {
tc4Tries++
time.Sleep(pollingInterval * 2)
return false
}
return true
},
expectedFailed: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mock := &testing.T{}
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
CompleteWithin(mock, tc.dur, tc.cond)
}()
wg.Wait()
if tc.expectedFailed != mock.Failed() {
t.Fatalf("expected to fail: %t; failed: %t", tc.expectedFailed, mock.Failed())
}
})
}
}

0 comments on commit cc107cd

Please sign in to comment.