github.com/fholzer/ordered-concurrently/v3@v3.0.0-20221001131746-406a6eece748/main_test.go (about)

     1  package orderedconcurrently
     2  
     3  import (
     4  	"context"
     5  	"math/rand"
     6  	"sort"
     7  	"sync"
     8  	"testing"
     9  	"time"
    10  )
    11  
    12  type zeroLoadWorker int
    13  
    14  func zeroLoadWorkerRun(w interface{}) interface{} {
    15  	return w.(zeroLoadWorker) * 2
    16  }
    17  
    18  type loadWorker int
    19  
    20  func loadWorkerRun(w interface{}) interface{} {
    21  	time.Sleep(time.Millisecond * time.Duration(rand.Intn(100)))
    22  	return w.(loadWorker) * 2
    23  }
    24  
    25  func processGeneratorGenerator(wf ProcessFunc) processFuncGenerator {
    26  	return func(int) (ProcessFunc, error) {
    27  		return wf, nil
    28  	}
    29  }
    30  
    31  func Test1(t *testing.T) {
    32  	t.Run("Test with Preset Pool Size", func(t *testing.T) {
    33  		ctx := context.Background()
    34  		max := 10
    35  		inputChan := make(chan interface{})
    36  		wg := &sync.WaitGroup{}
    37  
    38  		outChan, err := Process(ctx, inputChan, processGeneratorGenerator(loadWorkerRun), &Options{PoolSize: 10})
    39  		if err != nil {
    40  			panic(err)
    41  		}
    42  		counter := 0
    43  		go func(t *testing.T) {
    44  			for out := range outChan {
    45  				if _, ok := out.Value.(loadWorker); !ok {
    46  					t.Error("Invalid output")
    47  				} else {
    48  					counter++
    49  				}
    50  				wg.Done()
    51  			}
    52  		}(t)
    53  
    54  		// Create work and the associated order
    55  		for work := 0; work < max; work++ {
    56  			wg.Add(1)
    57  			inputChan <- loadWorker(work)
    58  		}
    59  		close(inputChan)
    60  		wg.Wait()
    61  		if counter != max {
    62  			t.Error("Input count does not match output count")
    63  		}
    64  		t.Log("Test with Preset Pool Size Completed")
    65  	})
    66  }
    67  
    68  func Test2(t *testing.T) {
    69  	t.Run("Test with default Pool Size", func(t *testing.T) {
    70  		ctx := context.Background()
    71  
    72  		max := 10
    73  		inputChan := make(chan interface{})
    74  		wg := &sync.WaitGroup{}
    75  
    76  		outChan, err := Process(ctx, inputChan, processGeneratorGenerator(loadWorkerRun), &Options{OutChannelBuffer: 2})
    77  		if err != nil {
    78  			panic(err)
    79  		}
    80  		counter := 0
    81  		go func(t *testing.T) {
    82  			for out := range outChan {
    83  				if _, ok := out.Value.(loadWorker); !ok {
    84  					t.Error("Invalid output")
    85  				} else {
    86  					counter++
    87  				}
    88  				wg.Done()
    89  			}
    90  		}(t)
    91  
    92  		// Create work and the associated order
    93  		for work := 0; work < max; work++ {
    94  			wg.Add(1)
    95  			inputChan <- loadWorker(work)
    96  		}
    97  		close(inputChan)
    98  		wg.Wait()
    99  		if counter != max {
   100  			t.Error("Input count does not match output count")
   101  		}
   102  		t.Log("Test with Default Pool Size Completed")
   103  	})
   104  }
   105  
   106  func Test3(t *testing.T) {
   107  	t.Run("Test Zero Load", func(t *testing.T) {
   108  		ctx := context.Background()
   109  
   110  		max := 10
   111  		inputChan := make(chan interface{})
   112  		wg := &sync.WaitGroup{}
   113  
   114  		outChan, err := Process(ctx, inputChan, processGeneratorGenerator(zeroLoadWorkerRun), &Options{OutChannelBuffer: 2})
   115  		if err != nil {
   116  			panic(err)
   117  		}
   118  		counter := 0
   119  		go func(t *testing.T) {
   120  			for out := range outChan {
   121  				if _, ok := out.Value.(zeroLoadWorker); !ok {
   122  					t.Error("Invalid output")
   123  				} else {
   124  					counter++
   125  				}
   126  				wg.Done()
   127  			}
   128  		}(t)
   129  
   130  		// Create work and the associated order
   131  		for work := 0; work < max; work++ {
   132  			wg.Add(1)
   133  			inputChan <- zeroLoadWorker(work)
   134  		}
   135  		close(inputChan)
   136  		wg.Wait()
   137  		if counter != max {
   138  			t.Error("Input count does not match output count")
   139  		}
   140  		t.Log("Test with Default Pool Size and Zero Load Completed")
   141  	})
   142  
   143  }
   144  
   145  func Test4(t *testing.T) {
   146  	t.Run("Test without workgroup", func(t *testing.T) {
   147  		ctx := context.Background()
   148  
   149  		max := 10
   150  		inputChan := make(chan interface{})
   151  		output, err := Process(ctx, inputChan, processGeneratorGenerator(zeroLoadWorkerRun), &Options{PoolSize: 10, OutChannelBuffer: 10})
   152  		if err != nil {
   153  			panic(err)
   154  		}
   155  		go func() {
   156  			for work := 0; work < max; work++ {
   157  				inputChan <- zeroLoadWorker(work)
   158  			}
   159  			close(inputChan)
   160  		}()
   161  		counter := 0
   162  		for out := range output {
   163  			if _, ok := out.Value.(zeroLoadWorker); !ok {
   164  				t.Error("Invalid output")
   165  			} else {
   166  				counter++
   167  			}
   168  		}
   169  		if counter != max {
   170  			t.Error("Input count does not match output count")
   171  		}
   172  		t.Log("Test without workgroup Completed")
   173  	})
   174  }
   175  
   176  func TestSortedData(t *testing.T) {
   177  	t.Run("Test if response is sorted", func(t *testing.T) {
   178  		ctx := context.Background()
   179  
   180  		max := 10
   181  		inputChan := make(chan interface{})
   182  		output, err := Process(ctx, inputChan, processGeneratorGenerator(loadWorkerRun), &Options{PoolSize: 10, OutChannelBuffer: 10})
   183  		if err != nil {
   184  			panic(err)
   185  		}
   186  		go func() {
   187  			for work := 0; work < max; work++ {
   188  				inputChan <- loadWorker(work)
   189  			}
   190  			close(inputChan)
   191  		}()
   192  		var res []loadWorker
   193  		for out := range output {
   194  			res = append(res, out.Value.(loadWorker))
   195  		}
   196  		isSorted := sort.SliceIsSorted(res, func(i, j int) bool {
   197  			return res[i] < res[j]
   198  		})
   199  		if !isSorted {
   200  			t.Error("output is not sorted")
   201  		}
   202  		t.Log("Test if response is sorted")
   203  	})
   204  }
   205  
   206  func TestSortedDataMultiple(t *testing.T) {
   207  	for i := 0; i < 50; i++ {
   208  		t.Run("Test if response is sorted", func(t *testing.T) {
   209  			ctx := context.Background()
   210  
   211  			max := 10
   212  			inputChan := make(chan interface{})
   213  			output, err := Process(ctx, inputChan, processGeneratorGenerator(loadWorkerRun), &Options{PoolSize: 10, OutChannelBuffer: 10})
   214  			if err != nil {
   215  				panic(err)
   216  			}
   217  			go func() {
   218  				for work := 0; work < max; work++ {
   219  					inputChan <- loadWorker(work)
   220  				}
   221  				close(inputChan)
   222  			}()
   223  			var res []loadWorker
   224  			for out := range output {
   225  				res = append(res, out.Value.(loadWorker))
   226  			}
   227  			isSorted := sort.SliceIsSorted(res, func(i, j int) bool {
   228  				return res[i] < res[j]
   229  			})
   230  			if !isSorted {
   231  				t.Error("output is not sorted")
   232  			}
   233  			t.Log("Test if response is sorted")
   234  		})
   235  	}
   236  }
   237  
   238  func TestStreamingInput(t *testing.T) {
   239  	t.Run("Test streaming input", func(t *testing.T) {
   240  		ctx := context.Background()
   241  		inputChan := make(chan interface{}, 10)
   242  		output, err := Process(ctx, inputChan, processGeneratorGenerator(zeroLoadWorkerRun), &Options{PoolSize: 10, OutChannelBuffer: 10})
   243  		if err != nil {
   244  			panic(err)
   245  		}
   246  
   247  		ticker := time.NewTicker(100 * time.Millisecond)
   248  		done := make(chan bool)
   249  		wg := &sync.WaitGroup{}
   250  		go func() {
   251  			input := 0
   252  			for {
   253  				select {
   254  				case <-done:
   255  					return
   256  				case <-ticker.C:
   257  					inputChan <- zeroLoadWorker(input)
   258  					wg.Add(1)
   259  					input++
   260  				default:
   261  				}
   262  			}
   263  		}()
   264  
   265  		var res []zeroLoadWorker
   266  
   267  		go func() {
   268  			for out := range output {
   269  				res = append(res, out.Value.(zeroLoadWorker))
   270  				wg.Done()
   271  			}
   272  		}()
   273  
   274  		time.Sleep(1600 * time.Millisecond)
   275  		ticker.Stop()
   276  		done <- true
   277  		close(inputChan)
   278  		wg.Wait()
   279  		isSorted := sort.SliceIsSorted(res, func(i, j int) bool {
   280  			return res[i] < res[j]
   281  		})
   282  		if !isSorted {
   283  			t.Error("output is not sorted")
   284  		}
   285  		t.Log("Test streaming input")
   286  	})
   287  }
   288  
   289  func BenchmarkOC(b *testing.B) {
   290  	max := 100000
   291  	inputChan := make(chan interface{})
   292  	output, err := Process(context.Background(), inputChan, processGeneratorGenerator(zeroLoadWorkerRun), &Options{PoolSize: 10, OutChannelBuffer: 10})
   293  	if err != nil {
   294  		panic(err)
   295  	}
   296  	go func() {
   297  		for work := 0; work < max; work++ {
   298  			inputChan <- zeroLoadWorker(work)
   299  		}
   300  		close(inputChan)
   301  	}()
   302  	for out := range output {
   303  		_ = out
   304  	}
   305  }
   306  
   307  func BenchmarkOCLoad(b *testing.B) {
   308  	max := 10
   309  	inputChan := make(chan interface{})
   310  	output, err := Process(context.Background(), inputChan, processGeneratorGenerator(loadWorkerRun), &Options{PoolSize: 10, OutChannelBuffer: 10})
   311  	if err != nil {
   312  		panic(err)
   313  	}
   314  	go func() {
   315  		for work := 0; work < max; work++ {
   316  			inputChan <- loadWorker(work)
   317  		}
   318  		close(inputChan)
   319  	}()
   320  	for out := range output {
   321  		_ = out
   322  	}
   323  }
   324  
   325  func BenchmarkOC2(b *testing.B) {
   326  	for i := 0; i < 100; i++ {
   327  		max := 1000
   328  		inputChan := make(chan interface{})
   329  		output, err := Process(context.Background(), inputChan, processGeneratorGenerator(zeroLoadWorkerRun), &Options{PoolSize: 10, OutChannelBuffer: 10})
   330  		if err != nil {
   331  			panic(err)
   332  		}
   333  		go func() {
   334  			for work := 0; work < max; work++ {
   335  				inputChan <- zeroLoadWorker(work)
   336  			}
   337  			close(inputChan)
   338  		}()
   339  		for out := range output {
   340  			_ = out
   341  		}
   342  	}
   343  }