github.com/Team-Kujira/tendermint@v0.34.24-indexer/libs/async/async_test.go (about) 1 package async 2 3 import ( 4 "errors" 5 "fmt" 6 "sync/atomic" 7 "testing" 8 "time" 9 10 "github.com/stretchr/testify/assert" 11 ) 12 13 func TestParallel(t *testing.T) { 14 15 // Create tasks. 16 var counter = new(int32) 17 var tasks = make([]Task, 100*1000) 18 for i := 0; i < len(tasks); i++ { 19 tasks[i] = func(i int) (res interface{}, abort bool, err error) { 20 atomic.AddInt32(counter, 1) 21 return -1 * i, false, nil 22 } 23 } 24 25 // Run in parallel. 26 var trs, ok = Parallel(tasks...) 27 assert.True(t, ok) 28 29 // Verify. 30 assert.Equal(t, int(*counter), len(tasks), "Each task should have incremented the counter already") 31 var failedTasks int 32 for i := 0; i < len(tasks); i++ { 33 taskResult, ok := trs.LatestResult(i) 34 switch { 35 case !ok: 36 assert.Fail(t, "Task #%v did not complete.", i) 37 failedTasks++ 38 case taskResult.Error != nil: 39 assert.Fail(t, "Task should not have errored but got %v", taskResult.Error) 40 failedTasks++ 41 case !assert.Equal(t, -1*i, taskResult.Value.(int)): 42 assert.Fail(t, "Task should have returned %v but got %v", -1*i, taskResult.Value.(int)) 43 failedTasks++ 44 } 45 // else { 46 // Good! 47 // } 48 } 49 assert.Equal(t, failedTasks, 0, "No task should have failed") 50 assert.Nil(t, trs.FirstError(), "There should be no errors") 51 assert.Equal(t, 0, trs.FirstValue(), "First value should be 0") 52 } 53 54 func TestParallelAbort(t *testing.T) { 55 56 var flow1 = make(chan struct{}, 1) 57 var flow2 = make(chan struct{}, 1) 58 var flow3 = make(chan struct{}, 1) // Cap must be > 0 to prevent blocking. 59 var flow4 = make(chan struct{}, 1) 60 61 // Create tasks. 62 var tasks = []Task{ 63 func(i int) (res interface{}, abort bool, err error) { 64 assert.Equal(t, i, 0) 65 flow1 <- struct{}{} 66 return 0, false, nil 67 }, 68 func(i int) (res interface{}, abort bool, err error) { 69 assert.Equal(t, i, 1) 70 flow2 <- <-flow1 71 return 1, false, errors.New("some error") 72 }, 73 func(i int) (res interface{}, abort bool, err error) { 74 assert.Equal(t, i, 2) 75 flow3 <- <-flow2 76 return 2, true, nil 77 }, 78 func(i int) (res interface{}, abort bool, err error) { 79 assert.Equal(t, i, 3) 80 <-flow4 81 return 3, false, nil 82 }, 83 } 84 85 // Run in parallel. 86 var taskResultSet, ok = Parallel(tasks...) 87 assert.False(t, ok, "ok should be false since we aborted task #2.") 88 89 // Verify task #3. 90 // Initially taskResultSet.chz[3] sends nothing since flow4 didn't send. 91 waitTimeout(t, taskResultSet.chz[3], "Task #3") 92 93 // Now let the last task (#3) complete after abort. 94 flow4 <- <-flow3 95 96 // Wait until all tasks have returned or panic'd. 97 taskResultSet.Wait() 98 99 // Verify task #0, #1, #2. 100 checkResult(t, taskResultSet, 0, 0, nil, nil) 101 checkResult(t, taskResultSet, 1, 1, errors.New("some error"), nil) 102 checkResult(t, taskResultSet, 2, 2, nil, nil) 103 checkResult(t, taskResultSet, 3, 3, nil, nil) 104 } 105 106 func TestParallelRecover(t *testing.T) { 107 108 // Create tasks. 109 var tasks = []Task{ 110 func(i int) (res interface{}, abort bool, err error) { 111 return 0, false, nil 112 }, 113 func(i int) (res interface{}, abort bool, err error) { 114 return 1, false, errors.New("some error") 115 }, 116 func(i int) (res interface{}, abort bool, err error) { 117 panic(2) 118 }, 119 } 120 121 // Run in parallel. 122 var taskResultSet, ok = Parallel(tasks...) 123 assert.False(t, ok, "ok should be false since we panic'd in task #2.") 124 125 // Verify task #0, #1, #2. 126 checkResult(t, taskResultSet, 0, 0, nil, nil) 127 checkResult(t, taskResultSet, 1, 1, errors.New("some error"), nil) 128 checkResult(t, taskResultSet, 2, nil, nil, fmt.Errorf("panic in task %v", 2).Error()) 129 } 130 131 // Wait for result 132 func checkResult(t *testing.T, taskResultSet *TaskResultSet, index int, 133 val interface{}, err error, pnk interface{}) { 134 taskResult, ok := taskResultSet.LatestResult(index) 135 taskName := fmt.Sprintf("Task #%v", index) 136 assert.True(t, ok, "TaskResultCh unexpectedly closed for %v", taskName) 137 assert.Equal(t, val, taskResult.Value, taskName) 138 switch { 139 case err != nil: 140 assert.Equal(t, err.Error(), taskResult.Error.Error(), taskName) 141 case pnk != nil: 142 assert.Contains(t, taskResult.Error.Error(), pnk, taskName) 143 default: 144 assert.Nil(t, taskResult.Error, taskName) 145 } 146 } 147 148 // Wait for timeout (no result) 149 func waitTimeout(t *testing.T, taskResultCh TaskResultCh, taskName string) { 150 select { 151 case _, ok := <-taskResultCh: 152 if !ok { 153 assert.Fail(t, "TaskResultCh unexpectedly closed (%v)", taskName) 154 } else { 155 assert.Fail(t, "TaskResultCh unexpectedly returned for %v", taskName) 156 } 157 case <-time.After(1 * time.Second): // TODO use deterministic time? 158 // Good! 159 } 160 }