github.com/xmidt-org/webpa-common@v1.11.9/concurrent/runnable_test.go (about)

     1  package concurrent
     2  
     3  import (
     4  	"errors"
     5  	"os"
     6  	"sync"
     7  	"sync/atomic"
     8  	"testing"
     9  	"time"
    10  )
    11  
    12  // success returns a closure that simulates a successfully started task
    13  func success(t *testing.T, runCount *uint32) Runnable {
    14  	return RunnableFunc(func(waitGroup *sync.WaitGroup, shutdown <-chan struct{}) error {
    15  		atomic.AddUint32(runCount, 1)
    16  		waitGroup.Add(1)
    17  
    18  		// simulates some longrunning task ...
    19  		go func() {
    20  			defer waitGroup.Done()
    21  			<-shutdown
    22  		}()
    23  
    24  		return nil
    25  	})
    26  }
    27  
    28  // fail returns a closure that simulates a task that failed to start
    29  func fail(t *testing.T, runCount *uint32) Runnable {
    30  	return RunnableFunc(func(waitGroup *sync.WaitGroup, shutdown <-chan struct{}) error {
    31  		atomic.AddUint32(runCount, 1)
    32  		return errors.New("Expected error")
    33  	})
    34  }
    35  
    36  func TestRunnableSetRun(t *testing.T) {
    37  	var actualRunCount uint32
    38  	success := success(t, &actualRunCount)
    39  	fail := fail(t, &actualRunCount)
    40  
    41  	var testData = []struct {
    42  		runnable         RunnableSet
    43  		expectedRunCount uint32
    44  	}{
    45  		{nil, 0},
    46  		{RunnableSet{}, 0},
    47  		{RunnableSet{success}, 1},
    48  		{RunnableSet{fail}, 1},
    49  		{RunnableSet{success, success}, 2},
    50  		{RunnableSet{success, fail}, 2},
    51  		{RunnableSet{success, fail, success}, 2},
    52  		{RunnableSet{success, fail, fail}, 2},
    53  		{RunnableSet{success, fail, success}, 2},
    54  		{RunnableSet{success, success, fail, success, success, fail}, 3},
    55  		{RunnableSet{success, success, success, success, fail}, 5},
    56  		{RunnableSet{success, success, success, success, success}, 5},
    57  	}
    58  
    59  	for _, record := range testData {
    60  		actualRunCount = 0
    61  		waitGroup := &sync.WaitGroup{}
    62  		shutdown := make(chan struct{})
    63  		record.runnable.Run(waitGroup, shutdown)
    64  		close(shutdown)
    65  
    66  		if !WaitTimeout(waitGroup, time.Second*2) {
    67  			t.Errorf("Blocked on WaitGroup longer than the timeout")
    68  		}
    69  
    70  		if record.expectedRunCount != actualRunCount {
    71  			t.Errorf(
    72  				"Expected Run to be called %d time(s), but instead was called %d time(s)",
    73  				record.expectedRunCount,
    74  				actualRunCount,
    75  			)
    76  		}
    77  	}
    78  }
    79  
    80  func TestExecuteSuccess(t *testing.T) {
    81  	var actualRunCount uint32
    82  	success := success(t, &actualRunCount)
    83  	waitGroup, shutdown, err := Execute(success)
    84  	if actualRunCount != 1 {
    85  		t.Error("Execute() did not invoke Run()")
    86  	}
    87  
    88  	if err != nil {
    89  		t.Fatalf("Execute() failed: %v", err)
    90  	}
    91  
    92  	if waitGroup == nil {
    93  		t.Fatal("Execute() returned a nil WaitGroup")
    94  	}
    95  
    96  	if shutdown == nil {
    97  		t.Fatal("Execute() returned a nil shutdown channel")
    98  	}
    99  
   100  	close(shutdown)
   101  
   102  	if !WaitTimeout(waitGroup, time.Second*2) {
   103  		t.Errorf("Blocked on WaitGroup longer than the timeout")
   104  	}
   105  }
   106  
   107  func TestExecuteFail(t *testing.T) {
   108  	var actualRunCount uint32
   109  	fail := fail(t, &actualRunCount)
   110  	waitGroup, shutdown, err := Execute(fail)
   111  	if actualRunCount != 1 {
   112  		t.Error("Execute() did not invoke Run()")
   113  	}
   114  
   115  	if err == nil {
   116  		t.Error("Execute() should have returned an error")
   117  	}
   118  
   119  	if waitGroup == nil {
   120  		t.Fatal("Execute() returned a nil WaitGroup")
   121  	}
   122  
   123  	if shutdown == nil {
   124  		t.Fatal("Execute() returned a nil shutdown channel")
   125  	}
   126  
   127  	close(shutdown)
   128  
   129  	if !WaitTimeout(waitGroup, time.Second*2) {
   130  		t.Errorf("Blocked on WaitGroup longer than the timeout")
   131  	}
   132  }
   133  
   134  func TestAwaitSuccess(t *testing.T) {
   135  	testWaitGroup := &sync.WaitGroup{}
   136  	testWaitGroup.Add(1)
   137  	success := RunnableFunc(func(waitGroup *sync.WaitGroup, shutdown <-chan struct{}) error {
   138  		waitGroup.Add(1)
   139  		go func() {
   140  			defer waitGroup.Done()
   141  			defer testWaitGroup.Done()
   142  			<-shutdown
   143  		}()
   144  
   145  		return nil
   146  	})
   147  
   148  	signals := make(chan os.Signal, 1)
   149  	go func() {
   150  		Await(success, signals)
   151  	}()
   152  
   153  	// simulate a ctrl+c
   154  	signals <- os.Interrupt
   155  
   156  	if !WaitTimeout(testWaitGroup, time.Second*2) {
   157  		t.Errorf("Blocked on WaitGroup longer than the timeout")
   158  	}
   159  }
   160  
   161  func TestAwaitFail(t *testing.T) {
   162  	testWaitGroup := &sync.WaitGroup{}
   163  	testWaitGroup.Add(1)
   164  	fail := RunnableFunc(func(waitGroup *sync.WaitGroup, shutdown <-chan struct{}) error {
   165  		defer testWaitGroup.Done()
   166  		return errors.New("Expected error")
   167  	})
   168  
   169  	signals := make(chan os.Signal, 1)
   170  	go func() {
   171  		Await(fail, signals)
   172  	}()
   173  
   174  	// simulate a ctrl+c
   175  	signals <- os.Interrupt
   176  
   177  	if !WaitTimeout(testWaitGroup, time.Second*2) {
   178  		t.Errorf("Blocked on WaitGroup longer than the timeout")
   179  	}
   180  }