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 }