github.com/haraldrudell/parl@v0.4.176/wait-group-ch_test.go (about)

     1  /*
     2  © 2021–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package parl
     7  
     8  import (
     9  	"sync"
    10  	"sync/atomic"
    11  	"testing"
    12  	"time"
    13  )
    14  
    15  func TestWaitGroupCh(t *testing.T) {
    16  	var adds0 = 2
    17  	var adds1 = 1
    18  	var currentCountExp0 = 1
    19  	var sExp = "waitGroupCh_count:0(adds:0)"
    20  
    21  	var awaitableCh AwaitableCh
    22  	var isClosed, isExit, isZero bool
    23  	var currentCount, totalAdds int
    24  	var s string
    25  
    26  	// Add() Ch() Count() Done() DoneBool() IsZero() Reset() String()
    27  	// Wait()
    28  	var w WaitGroupCh
    29  	var reset = func() {
    30  		w = WaitGroupCh{}
    31  	}
    32  
    33  	// Ch returns a non-closed channel
    34  	reset()
    35  	w.Add(adds1)
    36  	awaitableCh = w.Ch()
    37  	select {
    38  	case <-awaitableCh:
    39  		isClosed = true
    40  	default:
    41  		isClosed = false
    42  	}
    43  	if isClosed {
    44  		t.Error("Ch is closed")
    45  	}
    46  
    47  	// Ch channel closes
    48  	reset()
    49  	w.Add(adds1)
    50  	awaitableCh = w.Ch()
    51  	w.Done()
    52  	select {
    53  	case <-awaitableCh:
    54  		isClosed = true
    55  	default:
    56  		isClosed = false
    57  	}
    58  	if !isClosed {
    59  		t.Error("Ch not closed")
    60  	}
    61  
    62  	// Count returns zeroes
    63  	reset()
    64  	currentCount, totalAdds = w.Count()
    65  	if currentCount != 0 {
    66  		t.Errorf("Count currentCount %d exp 0", currentCount)
    67  	}
    68  	if totalAdds != 0 {
    69  		t.Errorf("Count totalAdds %d exp 0", totalAdds)
    70  	}
    71  
    72  	// Count reflect Adds Dones
    73  	//	- Add
    74  	//	- Done
    75  	reset()
    76  	w.Add(adds0)
    77  	w.Done()
    78  	currentCount, totalAdds = w.Count()
    79  	if currentCount != currentCountExp0 {
    80  		t.Errorf("Count currentCount %d exp %d", currentCount, currentCountExp0)
    81  	}
    82  	if totalAdds != adds0 {
    83  		t.Errorf("Count totalAdds %d exp %d", totalAdds, adds0)
    84  	}
    85  
    86  	// DoneBool
    87  	reset()
    88  	w.Add(adds0)
    89  	isExit = w.DoneBool()
    90  	if isExit {
    91  		t.Error("DoneBool isExit true")
    92  	}
    93  	isExit = w.DoneBool()
    94  	if !isExit {
    95  		t.Error("DoneBool isExit false")
    96  	}
    97  
    98  	// Reset
    99  	reset()
   100  	w.Add(adds0)
   101  	if w.p.Load() == nil {
   102  		t.Error("w.p.Load nil")
   103  	}
   104  	w.Reset()
   105  	if w.p.Load() != nil {
   106  		t.Error("w.p.Load not nil")
   107  	}
   108  
   109  	// IsZero
   110  	reset()
   111  	w.Add(adds1)
   112  	isZero = w.IsZero()
   113  	if isZero {
   114  		t.Error("IsZero true")
   115  	}
   116  	w.Done()
   117  	isZero = w.IsZero()
   118  	if !isZero {
   119  		t.Error("IsZero false")
   120  	}
   121  
   122  	// String()
   123  	reset()
   124  	s = w.String()
   125  	if s != sExp {
   126  		t.Errorf("String %q exp %q", s, sExp)
   127  	}
   128  }
   129  
   130  func TestWaitGroupChWait(t *testing.T) {
   131  	var adds1 = 1
   132  	var shortTime = time.Millisecond
   133  
   134  	var isReady sync.WaitGroup
   135  	var isDone chan struct{}
   136  	var isWaitReturn atomic.Bool
   137  	var timer *time.Timer
   138  
   139  	// Add() Ch() Count() Done() DoneBool() IsZero() Reset() String()
   140  	// Wait()
   141  	var w WaitGroupCh
   142  	var reset = func() {
   143  		w = WaitGroupCh{}
   144  	}
   145  
   146  	// Wait waits until counter zero
   147  	reset()
   148  	w.Add(adds1)
   149  	isWaitReturn.Store(false)
   150  	isReady = sync.WaitGroup{}
   151  	isReady.Add(1)
   152  	isDone = make(chan struct{})
   153  	go waiter(&w, &isWaitReturn, &isReady, isDone)
   154  	isReady.Wait()
   155  	<-time.NewTimer(shortTime).C
   156  	if isWaitReturn.Load() {
   157  		t.Error("Wait returned prematurely")
   158  	}
   159  	w.Done()
   160  	// race condition between w.ch closing and
   161  	// waiter triggering isDone
   162  	timer = time.NewTimer(shortTime)
   163  	select {
   164  	case <-isDone:
   165  	case <-timer.C:
   166  	}
   167  	if !isWaitReturn.Load() {
   168  		t.Error("Wait did not return on Done")
   169  	}
   170  
   171  }
   172  
   173  // waiter tests WaitGroupCh.Wait()
   174  func waiter(
   175  	w *WaitGroupCh,
   176  	isWaitReturn *atomic.Bool,
   177  	isReady Doneable,
   178  	isDone chan struct{},
   179  ) {
   180  	defer close(isDone)
   181  	defer isWaitReturn.Store(true)
   182  
   183  	isReady.Done()
   184  	w.Wait()
   185  }