github.com/dubbogo/gost@v1.14.0/container/queue/queue_test.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package gxqueue
    19  
    20  import (
    21  	"sync"
    22  	"sync/atomic"
    23  	"testing"
    24  	"time"
    25  )
    26  
    27  import (
    28  	"github.com/stretchr/testify/assert"
    29  )
    30  
    31  func TestPut(t *testing.T) {
    32  	q := New(10)
    33  
    34  	q.Put(`test`)
    35  	assert.Equal(t, int64(1), q.Len())
    36  
    37  	results, err := q.Get(1)
    38  	assert.Nil(t, err)
    39  
    40  	result := results[0]
    41  	assert.Equal(t, `test`, result)
    42  	assert.True(t, q.Empty())
    43  
    44  	q.Put(`test2`)
    45  	assert.Equal(t, int64(1), q.Len())
    46  
    47  	results, err = q.Get(1)
    48  	assert.Nil(t, err)
    49  
    50  	result = results[0]
    51  	assert.Equal(t, `test2`, result)
    52  	assert.True(t, q.Empty())
    53  }
    54  
    55  func TestGet(t *testing.T) {
    56  	q := New(10)
    57  
    58  	q.Put(`test`)
    59  	result, err := q.Get(2)
    60  	if !assert.Nil(t, err) {
    61  		return
    62  	}
    63  
    64  	assert.Len(t, result, 1)
    65  	assert.Equal(t, `test`, result[0])
    66  	assert.Equal(t, int64(0), q.Len())
    67  
    68  	q.Put(`1`)
    69  	q.Put(`2`)
    70  
    71  	result, err = q.Get(1)
    72  	if !assert.Nil(t, err) {
    73  		return
    74  	}
    75  
    76  	assert.Len(t, result, 1)
    77  	assert.Equal(t, `1`, result[0])
    78  	assert.Equal(t, int64(1), q.Len())
    79  
    80  	result, err = q.Get(2)
    81  	if !assert.Nil(t, err) {
    82  		return
    83  	}
    84  
    85  	assert.Equal(t, `2`, result[0])
    86  }
    87  
    88  func TestPoll(t *testing.T) {
    89  	q := New(10)
    90  
    91  	// should be able to Poll() before anything is present, without breaking future Puts
    92  	q.Poll(1, time.Millisecond)
    93  
    94  	q.Put(`test`)
    95  	result, err := q.Poll(2, 0)
    96  	if !assert.Nil(t, err) {
    97  		return
    98  	}
    99  
   100  	assert.Len(t, result, 1)
   101  	assert.Equal(t, `test`, result[0])
   102  	assert.Equal(t, int64(0), q.Len())
   103  
   104  	q.Put(`1`)
   105  	q.Put(`2`)
   106  
   107  	result, err = q.Poll(1, time.Millisecond)
   108  	if !assert.Nil(t, err) {
   109  		return
   110  	}
   111  
   112  	assert.Len(t, result, 1)
   113  	assert.Equal(t, `1`, result[0])
   114  	assert.Equal(t, int64(1), q.Len())
   115  
   116  	result, err = q.Poll(2, time.Millisecond)
   117  	if !assert.Nil(t, err) {
   118  		return
   119  	}
   120  
   121  	assert.Equal(t, `2`, result[0])
   122  
   123  	before := time.Now()
   124  	_, err = q.Poll(1, 5*time.Millisecond)
   125  	// This delta is normally 1-3 ms but running tests in CI with -race causes
   126  	// this to run much slower. For now, just bump up the threshold.
   127  	assert.InDelta(t, 5, time.Since(before).Seconds()*1000, 10)
   128  	assert.Equal(t, ErrTimeout, err)
   129  }
   130  
   131  func TestPollNoMemoryLeak(t *testing.T) {
   132  	q := New(0)
   133  
   134  	assert.Len(t, q.waiters, 0)
   135  
   136  	for i := 0; i < 10; i++ {
   137  		// Poll() should cleanup waiters after timeout
   138  		q.Poll(1, time.Nanosecond)
   139  		assert.Len(t, q.waiters, 0)
   140  	}
   141  }
   142  
   143  func TestAddEmptyPut(t *testing.T) {
   144  	q := New(10)
   145  
   146  	q.Put()
   147  
   148  	if q.Len() != 0 {
   149  		t.Errorf(`Expected len: %d, received: %d`, 0, q.Len())
   150  	}
   151  }
   152  
   153  func TestGetNonPositiveNumber(t *testing.T) {
   154  	q := New(10)
   155  
   156  	q.Put(`test`)
   157  	result, err := q.Get(0)
   158  	if !assert.Nil(t, err) {
   159  		return
   160  	}
   161  
   162  	if len(result) != 0 {
   163  		t.Errorf(`Expected len: %d, received: %d`, 0, len(result))
   164  	}
   165  }
   166  
   167  func TestEmpty(t *testing.T) {
   168  	q := New(10)
   169  
   170  	if !q.Empty() {
   171  		t.Errorf(`Expected empty queue.`)
   172  	}
   173  
   174  	q.Put(`test`)
   175  	if q.Empty() {
   176  		t.Errorf(`Expected non-empty queue.`)
   177  	}
   178  }
   179  
   180  func TestGetEmpty(t *testing.T) {
   181  	q := New(10)
   182  
   183  	go func() {
   184  		time.Sleep(time.Second)
   185  		q.Put(`a`)
   186  	}()
   187  
   188  	result, err := q.Get(2)
   189  	if !assert.Nil(t, err) {
   190  		return
   191  	}
   192  
   193  	assert.Len(t, result, 1)
   194  	assert.Equal(t, `a`, result[0])
   195  }
   196  
   197  func TestMultipleGetEmpty(t *testing.T) {
   198  	q := New(10)
   199  	var wg sync.WaitGroup
   200  	wg.Add(2)
   201  	results := make([][]interface{}, 2)
   202  
   203  	go func() {
   204  		wg.Done()
   205  		local, err := q.Get(1)
   206  		assert.Nil(t, err)
   207  		results[0] = local
   208  		wg.Done()
   209  	}()
   210  
   211  	go func() {
   212  		wg.Done()
   213  		local, err := q.Get(1)
   214  		assert.Nil(t, err)
   215  		results[1] = local
   216  		wg.Done()
   217  	}()
   218  
   219  	wg.Wait()
   220  	wg.Add(2)
   221  
   222  	q.Put(`a`, `b`, `c`)
   223  	wg.Wait()
   224  
   225  	if assert.Len(t, results[0], 1) && assert.Len(t, results[1], 1) {
   226  		assert.True(t, (results[0][0] == `a` && results[1][0] == `b`) ||
   227  			(results[0][0] == `b` && results[1][0] == `a`),
   228  			`The array should be a, b or b, a`)
   229  	}
   230  }
   231  
   232  func TestDispose(t *testing.T) {
   233  	// when the queue is empty
   234  	q := New(10)
   235  	itemsDisposed := q.Dispose()
   236  
   237  	assert.Empty(t, itemsDisposed)
   238  
   239  	// when the queue is not empty
   240  	q = New(10)
   241  	q.Put(`1`)
   242  	itemsDisposed = q.Dispose()
   243  
   244  	expected := []interface{}{`1`}
   245  	assert.Equal(t, expected, itemsDisposed)
   246  
   247  	// when the queue has been disposed
   248  	itemsDisposed = q.Dispose()
   249  	assert.Nil(t, itemsDisposed)
   250  }
   251  
   252  func TestEmptyGetWithDispose(t *testing.T) {
   253  	q := New(10)
   254  	var wg sync.WaitGroup
   255  	wg.Add(1)
   256  
   257  	var err error
   258  
   259  	go func() {
   260  		wg.Done()
   261  		_, err = q.Get(1)
   262  		wg.Done()
   263  	}()
   264  
   265  	wg.Wait()
   266  	wg.Add(1)
   267  
   268  	q.Dispose()
   269  
   270  	wg.Wait()
   271  
   272  	assert.IsType(t, ErrDisposed, err)
   273  }
   274  
   275  func TestDisposeAfterEmptyPoll(t *testing.T) {
   276  	q := New(10)
   277  
   278  	_, err := q.Poll(1, time.Millisecond)
   279  	assert.IsType(t, ErrTimeout, err)
   280  
   281  	// it should not hang
   282  	q.Dispose()
   283  
   284  	_, err = q.Poll(1, time.Millisecond)
   285  	assert.IsType(t, ErrDisposed, err)
   286  }
   287  
   288  func TestGetPutDisposed(t *testing.T) {
   289  	q := New(10)
   290  
   291  	q.Dispose()
   292  
   293  	_, err := q.Get(1)
   294  	assert.IsType(t, ErrDisposed, err)
   295  
   296  	err = q.Put(`a`)
   297  	assert.IsType(t, ErrDisposed, err)
   298  }
   299  
   300  func BenchmarkQueue(b *testing.B) {
   301  	q := New(int64(b.N))
   302  	var wg sync.WaitGroup
   303  	wg.Add(1)
   304  	i := 0
   305  
   306  	go func() {
   307  		for {
   308  			q.Get(1)
   309  			i++
   310  			if i == b.N {
   311  				wg.Done()
   312  				break
   313  			}
   314  		}
   315  	}()
   316  
   317  	for i := 0; i < b.N; i++ {
   318  		q.Put(`a`)
   319  	}
   320  
   321  	wg.Wait()
   322  }
   323  
   324  func BenchmarkChannel(b *testing.B) {
   325  	ch := make(chan interface{}, 1)
   326  	var wg sync.WaitGroup
   327  	wg.Add(1)
   328  	i := 0
   329  
   330  	go func() {
   331  		for {
   332  			<-ch
   333  			i++
   334  			if i == b.N {
   335  				wg.Done()
   336  				break
   337  			}
   338  		}
   339  	}()
   340  
   341  	for i := 0; i < b.N; i++ {
   342  		ch <- `a`
   343  	}
   344  
   345  	wg.Wait()
   346  }
   347  
   348  func TestPeek(t *testing.T) {
   349  	q := New(10)
   350  	q.Put(`a`)
   351  	q.Put(`b`)
   352  	q.Put(`c`)
   353  	peekResult, err := q.Peek()
   354  	peekExpected := `a`
   355  	assert.Nil(t, err)
   356  	assert.Equal(t, q.Len(), int64(3))
   357  	assert.Equal(t, peekExpected, peekResult)
   358  
   359  	popResult, err := q.Get(1)
   360  	assert.Nil(t, err)
   361  	assert.Equal(t, peekResult, popResult[0])
   362  	assert.Equal(t, q.Len(), int64(2))
   363  }
   364  
   365  func TestPeekOnDisposedQueue(t *testing.T) {
   366  	q := New(10)
   367  	q.Dispose()
   368  	result, err := q.Peek()
   369  
   370  	assert.Nil(t, result)
   371  	assert.IsType(t, ErrDisposed, err)
   372  }
   373  
   374  func TestGetUntil(t *testing.T) {
   375  	q := New(10)
   376  	q.Put(`a`, `b`, `c`)
   377  	result, err := q.GetUntil(func(item interface{}) bool {
   378  		return item != `c`
   379  	})
   380  
   381  	if !assert.Nil(t, err) {
   382  		return
   383  	}
   384  
   385  	expected := []interface{}{`a`, `b`}
   386  	assert.Equal(t, expected, result)
   387  }
   388  
   389  func TestGetUntilEmptyQueue(t *testing.T) {
   390  	q := New(10)
   391  	result, err := q.GetUntil(func(item interface{}) bool {
   392  		return item != `c`
   393  	})
   394  
   395  	if !assert.Nil(t, err) {
   396  		return
   397  	}
   398  
   399  	expected := []interface{}{}
   400  	assert.Equal(t, expected, result)
   401  }
   402  
   403  func TestGetUntilThenGet(t *testing.T) {
   404  	q := New(10)
   405  	q.Put(`a`, `b`, `c`)
   406  	takeItems, _ := q.GetUntil(func(item interface{}) bool {
   407  		return item != `c`
   408  	})
   409  
   410  	restItems, _ := q.Get(3)
   411  	assert.Equal(t, []interface{}{`a`, `b`}, takeItems)
   412  	assert.Equal(t, []interface{}{`c`}, restItems)
   413  }
   414  
   415  func TestGetUntilNoMatches(t *testing.T) {
   416  	q := New(10)
   417  	q.Put(`a`, `b`, `c`)
   418  	takeItems, _ := q.GetUntil(func(item interface{}) bool {
   419  		return item != `a`
   420  	})
   421  
   422  	restItems, _ := q.Get(3)
   423  	assert.Equal(t, []interface{}{}, takeItems)
   424  	assert.Equal(t, []interface{}{`a`, `b`, `c`}, restItems)
   425  }
   426  
   427  func TestGetUntilOnDisposedQueue(t *testing.T) {
   428  	q := New(10)
   429  	q.Dispose()
   430  	result, err := q.GetUntil(func(item interface{}) bool {
   431  		return true
   432  	})
   433  
   434  	assert.Nil(t, result)
   435  	assert.IsType(t, ErrDisposed, err)
   436  }
   437  
   438  func TestWaiters(t *testing.T) {
   439  	s1, s2, s3, s4 := newSema(), newSema(), newSema(), newSema()
   440  
   441  	w := waiters{}
   442  	assert.Len(t, w, 0)
   443  
   444  	//
   445  	// test put()
   446  	w.put(s1)
   447  	assert.Equal(t, waiters{s1}, w)
   448  
   449  	w.put(s2)
   450  	w.put(s3)
   451  	w.put(s4)
   452  	assert.Equal(t, waiters{s1, s2, s3, s4}, w)
   453  
   454  	//
   455  	// test remove()
   456  	//
   457  	// remove from middle
   458  	w.remove(s2)
   459  	assert.Equal(t, waiters{s1, s3, s4}, w)
   460  
   461  	// remove non-existing element
   462  	w.remove(s2)
   463  	assert.Equal(t, waiters{s1, s3, s4}, w)
   464  
   465  	// remove from beginning
   466  	w.remove(s1)
   467  	assert.Equal(t, waiters{s3, s4}, w)
   468  
   469  	// remove from end
   470  	w.remove(s4)
   471  	assert.Equal(t, waiters{s3}, w)
   472  
   473  	// remove last element
   474  	w.remove(s3)
   475  	assert.Empty(t, w)
   476  
   477  	// remove non-existing element
   478  	w.remove(s3)
   479  	assert.Empty(t, w)
   480  
   481  	//
   482  	// test get()
   483  	//
   484  	// start with 3 elements in list
   485  	w.put(s1)
   486  	w.put(s2)
   487  	w.put(s3)
   488  	assert.Equal(t, waiters{s1, s2, s3}, w)
   489  
   490  	// get() returns each item in insertion order
   491  	assert.Equal(t, s1, w.get())
   492  	assert.Equal(t, s2, w.get())
   493  	w.put(s4) // interleave a put(), item should go to the end
   494  	assert.Equal(t, s3, w.get())
   495  	assert.Equal(t, s4, w.get())
   496  	assert.Empty(t, w)
   497  	assert.Nil(t, w.get())
   498  }
   499  
   500  func TestExecuteInParallel(t *testing.T) {
   501  	q := New(10)
   502  	for i := 0; i < 10; i++ {
   503  		q.Put(i)
   504  	}
   505  
   506  	numCalls := uint64(0)
   507  
   508  	ExecuteInParallel(q, func(item interface{}) {
   509  		t.Logf("ExecuteInParallel called us with %+v", item)
   510  		atomic.AddUint64(&numCalls, 1)
   511  	})
   512  
   513  	assert.Equal(t, uint64(10), numCalls)
   514  	assert.True(t, q.Disposed())
   515  }
   516  
   517  func TestExecuteInParallelEmptyQueue(t *testing.T) {
   518  	q := New(1)
   519  
   520  	// basically just ensuring we don't deadlock here
   521  	ExecuteInParallel(q, func(interface{}) {
   522  		t.Fail()
   523  	})
   524  }
   525  
   526  func BenchmarkQueuePut(b *testing.B) {
   527  	numItems := int64(1000)
   528  
   529  	qs := make([]*Queue, 0, b.N)
   530  
   531  	for i := 0; i < b.N; i++ {
   532  		q := New(10)
   533  		qs = append(qs, q)
   534  	}
   535  
   536  	b.ResetTimer()
   537  	for i := 0; i < b.N; i++ {
   538  		q := qs[i]
   539  		for j := int64(0); j < numItems; j++ {
   540  			q.Put(j)
   541  		}
   542  	}
   543  }
   544  
   545  func BenchmarkQueueGet(b *testing.B) {
   546  	numItems := int64(1000)
   547  
   548  	qs := make([]*Queue, 0, b.N)
   549  
   550  	for i := 0; i < b.N; i++ {
   551  		q := New(numItems)
   552  		for j := int64(0); j < numItems; j++ {
   553  			q.Put(j)
   554  		}
   555  		qs = append(qs, q)
   556  	}
   557  
   558  	b.ResetTimer()
   559  
   560  	for i := 0; i < b.N; i++ {
   561  		q := qs[i]
   562  		for j := int64(0); j < numItems; j++ {
   563  			q.Get(1)
   564  		}
   565  	}
   566  }
   567  
   568  func BenchmarkQueuePoll(b *testing.B) {
   569  	numItems := int64(1000)
   570  
   571  	qs := make([]*Queue, 0, b.N)
   572  
   573  	for i := 0; i < b.N; i++ {
   574  		q := New(numItems)
   575  		for j := int64(0); j < numItems; j++ {
   576  			q.Put(j)
   577  		}
   578  		qs = append(qs, q)
   579  	}
   580  
   581  	b.ResetTimer()
   582  
   583  	for _, q := range qs {
   584  		for j := int64(0); j < numItems; j++ {
   585  			q.Poll(1, time.Millisecond)
   586  		}
   587  	}
   588  }
   589  
   590  func BenchmarkExecuteInParallel(b *testing.B) {
   591  	numItems := int64(1000)
   592  
   593  	qs := make([]*Queue, 0, b.N)
   594  
   595  	for i := 0; i < b.N; i++ {
   596  		q := New(numItems)
   597  		for j := int64(0); j < numItems; j++ {
   598  			q.Put(j)
   599  		}
   600  		qs = append(qs, q)
   601  	}
   602  
   603  	var counter int64
   604  	fn := func(ifc interface{}) {
   605  		c := ifc.(int64)
   606  		atomic.AddInt64(&counter, c)
   607  	}
   608  
   609  	b.ResetTimer()
   610  
   611  	for i := 0; i < b.N; i++ {
   612  		q := qs[i]
   613  		ExecuteInParallel(q, fn)
   614  	}
   615  }