github.com/supragya/TendermintConnector@v0.0.0-20210619045051-113e32b84fb1/_deprecated_chains/cosmos/libs/common/async_test.go (about)

     1  package common
     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{}, err error, abort bool) {
    20  			atomic.AddInt32(counter, 1)
    21  			return -1 * i, nil, false
    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  		if !ok {
    35  			assert.Fail(t, "Task #%v did not complete.", i)
    36  			failedTasks++
    37  		} else if taskResult.Error != nil {
    38  			assert.Fail(t, "Task should not have errored but got %v", taskResult.Error)
    39  			failedTasks++
    40  		} else if !assert.Equal(t, -1*i, taskResult.Value.(int)) {
    41  			assert.Fail(t, "Task should have returned %v but got %v", -1*i, taskResult.Value.(int))
    42  			failedTasks++
    43  		} else {
    44  			// Good!
    45  		}
    46  	}
    47  	assert.Equal(t, failedTasks, 0, "No task should have failed")
    48  	assert.Nil(t, trs.FirstError(), "There should be no errors")
    49  	assert.Equal(t, 0, trs.FirstValue(), "First value should be 0")
    50  }
    51  
    52  func TestParallelAbort(t *testing.T) {
    53  
    54  	var flow1 = make(chan struct{}, 1)
    55  	var flow2 = make(chan struct{}, 1)
    56  	var flow3 = make(chan struct{}, 1) // Cap must be > 0 to prevent blocking.
    57  	var flow4 = make(chan struct{}, 1)
    58  
    59  	// Create tasks.
    60  	var tasks = []Task{
    61  		func(i int) (res interface{}, err error, abort bool) {
    62  			assert.Equal(t, i, 0)
    63  			flow1 <- struct{}{}
    64  			return 0, nil, false
    65  		},
    66  		func(i int) (res interface{}, err error, abort bool) {
    67  			assert.Equal(t, i, 1)
    68  			flow2 <- <-flow1
    69  			return 1, errors.New("some error"), false
    70  		},
    71  		func(i int) (res interface{}, err error, abort bool) {
    72  			assert.Equal(t, i, 2)
    73  			flow3 <- <-flow2
    74  			return 2, nil, true
    75  		},
    76  		func(i int) (res interface{}, err error, abort bool) {
    77  			assert.Equal(t, i, 3)
    78  			<-flow4
    79  			return 3, nil, false
    80  		},
    81  	}
    82  
    83  	// Run in parallel.
    84  	var taskResultSet, ok = Parallel(tasks...)
    85  	assert.False(t, ok, "ok should be false since we aborted task #2.")
    86  
    87  	// Verify task #3.
    88  	// Initially taskResultSet.chz[3] sends nothing since flow4 didn't send.
    89  	waitTimeout(t, taskResultSet.chz[3], "Task #3")
    90  
    91  	// Now let the last task (#3) complete after abort.
    92  	flow4 <- <-flow3
    93  
    94  	// Wait until all tasks have returned or panic'd.
    95  	taskResultSet.Wait()
    96  
    97  	// Verify task #0, #1, #2.
    98  	checkResult(t, taskResultSet, 0, 0, nil, nil)
    99  	checkResult(t, taskResultSet, 1, 1, errors.New("some error"), nil)
   100  	checkResult(t, taskResultSet, 2, 2, nil, nil)
   101  	checkResult(t, taskResultSet, 3, 3, nil, nil)
   102  }
   103  
   104  func TestParallelRecover(t *testing.T) {
   105  
   106  	// Create tasks.
   107  	var tasks = []Task{
   108  		func(i int) (res interface{}, err error, abort bool) {
   109  			return 0, nil, false
   110  		},
   111  		func(i int) (res interface{}, err error, abort bool) {
   112  			return 1, errors.New("some error"), false
   113  		},
   114  		func(i int) (res interface{}, err error, abort bool) {
   115  			panic(2)
   116  		},
   117  	}
   118  
   119  	// Run in parallel.
   120  	var taskResultSet, ok = Parallel(tasks...)
   121  	assert.False(t, ok, "ok should be false since we panic'd in task #2.")
   122  
   123  	// Verify task #0, #1, #2.
   124  	checkResult(t, taskResultSet, 0, 0, nil, nil)
   125  	checkResult(t, taskResultSet, 1, 1, errors.New("some error"), nil)
   126  	checkResult(t, taskResultSet, 2, nil, nil, 2)
   127  }
   128  
   129  // Wait for result
   130  func checkResult(t *testing.T, taskResultSet *TaskResultSet, index int, val interface{}, err error, pnk interface{}) {
   131  	taskResult, ok := taskResultSet.LatestResult(index)
   132  	taskName := fmt.Sprintf("Task #%v", index)
   133  	assert.True(t, ok, "TaskResultCh unexpectedly closed for %v", taskName)
   134  	assert.Equal(t, val, taskResult.Value, taskName)
   135  	if err != nil {
   136  		assert.Equal(t, err, taskResult.Error, taskName)
   137  	} else if pnk != nil {
   138  		assert.Equal(t, pnk, taskResult.Error.(Error).Data(), taskName)
   139  	} else {
   140  		assert.Nil(t, taskResult.Error, taskName)
   141  	}
   142  }
   143  
   144  // Wait for timeout (no result)
   145  func waitTimeout(t *testing.T, taskResultCh TaskResultCh, taskName string) {
   146  	select {
   147  	case _, ok := <-taskResultCh:
   148  		if !ok {
   149  			assert.Fail(t, "TaskResultCh unexpectedly closed (%v)", taskName)
   150  		} else {
   151  			assert.Fail(t, "TaskResultCh unexpectedly returned for %v", taskName)
   152  		}
   153  	case <-time.After(1 * time.Second):
   154  		// Good!
   155  	}
   156  }