Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix panic in reflect #4

Merged
merged 2 commits into from
Apr 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion async_task.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,23 @@ func Start(ctx context.Context, task AsyncFunc) *TaskStatus {
return record
}

// isErrorReallyError do extra error check
// - Nil Pointer to a Type (that implement error)
// - Zero Value of a Type (that implement error)
func isErrorReallyError(err error) bool {
v := reflect.ValueOf(err)
if v.Type().Kind() == reflect.Ptr &&
v.IsNil() {
return false
}

if v.Type().Kind() == reflect.Struct &&
v.IsZero() {
return false
}
return true
}

func runAndTrackTask(record *TaskStatus, task func(ctx context.Context) (interface{}, error)) {
defer record.waitGroup.Done()
defer func() {
Expand All @@ -150,7 +167,7 @@ func runAndTrackTask(record *TaskStatus, task func(ctx context.Context) (interfa
// incase some team use pointer typed error (implement Error() string on a pointer type)
// which can break err check (but nil point assigned to error result to non-nil error)
// check out TestPointerErrorCase in error_test.go
reflect.ValueOf(err).IsNil() {
!isErrorReallyError(err) {
record.finish(StateCompleted, result, nil)
return
}
Expand Down
11 changes: 10 additions & 1 deletion async_task_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func TestEasyCase(t *testing.T) {
result = rawResult.(int)
assert.Equal(t, result, 9)

assert.True(t, elapsed.Microseconds() < 2, "Second wait should take more than 2 millisecond")
assert.True(t, elapsed.Microseconds() < 2, "Second wait should return immediately")
}

func TestCancelFunc(t *testing.T) {
Expand Down Expand Up @@ -150,6 +150,15 @@ func TestConsistentResultAfterTimeout(t *testing.T) {
rawResult, err = t1.Wait()
assert.Equal(t, asynctask.ErrTimeout, err, "should return reason of error")
assert.Nil(t, rawResult, "didn't expect resule on canceled task")

// invoke WaitWithTimeout again,
start := time.Now()
rawResult, err = t1.WaitWithTimeout(1 * time.Second)
elapsed := time.Since(start)
assert.Equal(t, asynctask.StateCanceled, t1.State(), "t1 should remain canceled even after routine finish")
assert.Equal(t, asynctask.ErrTimeout, err, "should return reason of error")
assert.Nil(t, rawResult, "didn't expect resule on canceled task")
assert.True(t, elapsed.Microseconds() < 2, "Second wait should return immediately")
}

func TestCompletedTask(t *testing.T) {
Expand Down
28 changes: 28 additions & 0 deletions error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ import (
"github.com/stretchr/testify/assert"
)

type structError struct{}

func (pe structError) Error() string {
return "Error from struct type"
}

type pointerError struct{}

func (pe *pointerError) Error() string {
Expand Down Expand Up @@ -77,3 +83,25 @@ func TestPointerErrorCase(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, result, "Done")
}

func TestStructErrorCase(t *testing.T) {
t.Parallel()

// nil point of a type that implement error
var se structError
// pass this nil pointer to error interface
var err error = se
// now you get a non-nil error
assert.False(t, err == nil, "reason this test is needed")

ctx := newTestContext(t)
tsk := asynctask.Start(ctx, func(ctx context.Context) (interface{}, error) {
time.Sleep(100 * time.Millisecond)
var se structError
return "Done", se
})

result, err := tsk.Wait()
assert.NoError(t, err)
assert.Equal(t, result, "Done")
}