Files
gf/container/gqueue/gqueue_z_unit_test.go
Hunk Zhu 8575f01273 feat(container/gqueue): add generic queuefeature (#4497)
add TQueue

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: hailaz <739476267@qq.com>
2025-11-28 12:42:12 +08:00

346 lines
6.7 KiB
Go

// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// go test *.go -bench=".*" -benchmem
package gqueue_test
import (
"testing"
"time"
"github.com/gogf/gf/v2/container/gqueue"
"github.com/gogf/gf/v2/test/gtest"
)
func TestQueue_Len(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
maxNum = 100
maxTries = 100
)
for n := 10; n < maxTries; n++ {
q1 := gqueue.New(maxNum)
for i := range maxNum {
q1.Push(i)
}
t.Assert(q1.Len(), maxNum)
t.Assert(q1.Size(), maxNum)
}
})
gtest.C(t, func(t *gtest.T) {
var (
maxNum = 100
maxTries = 100
)
for n := 10; n < maxTries; n++ {
q1 := gqueue.New()
for i := range maxNum {
q1.Push(i)
}
t.AssertLE(q1.Len(), maxNum)
t.AssertLE(q1.Size(), maxNum)
}
})
}
func TestQueue_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
q := gqueue.New()
defer q.Close()
for i := range 100 {
q.Push(i)
}
t.Assert(q.Pop(), 0)
t.Assert(q.Pop(), 1)
})
}
func TestQueue_Pop(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
q1 := gqueue.New()
defer q1.Close()
q1.Push(1)
q1.Push(2)
q1.Push(3)
q1.Push(4)
i1 := q1.Pop()
t.Assert(i1, 1)
})
}
func TestQueue_Close(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
q1 := gqueue.New()
defer q1.Close()
q1.Push(1)
q1.Push(2)
// wait sync to channel
time.Sleep(10 * time.Millisecond)
t.Assert(q1.Len(), 2)
})
gtest.C(t, func(t *gtest.T) {
q1 := gqueue.New(2)
defer q1.Close()
q1.Push(1)
q1.Push(2)
// wait sync to channel
time.Sleep(10 * time.Millisecond)
t.Assert(q1.Len(), 2)
})
}
func Test_Issue2509(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
q := gqueue.New()
defer q.Close()
q.Push(1)
q.Push(2)
q.Push(3)
t.AssertLE(q.Len(), 3)
t.Assert(<-q.C, 1)
t.AssertLE(q.Len(), 2)
t.Assert(<-q.C, 2)
t.AssertLE(q.Len(), 1)
t.Assert(<-q.C, 3)
t.Assert(q.Len(), 0)
})
}
// Issue #4376
func TestIssue4376(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
gq := gqueue.New()
defer gq.Close()
cq := make(chan int, 100000)
defer close(cq)
for i := range 11603 {
gq.Push(i)
cq <- i
}
// May be not equal because of the async channel reading goroutine.
t.Log(gq.Len(), len(cq))
time.Sleep(50 * time.Millisecond)
t.Log(gq.Len(), len(cq))
})
}
// Test static queue (with limit) close operation
func TestQueue_StaticClose(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
q := gqueue.New(10)
defer func() {
if err := recover(); err == nil {
t.Log("Close succeeded")
}
}()
q.Push(1)
q.Push(2)
q.Close()
// After closing, Pop should return nil
v := q.Pop()
t.Assert(v, nil)
})
}
// Test Size() method (deprecated alias of Len)
func TestQueue_Size(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
q := gqueue.New(20)
for i := range 10 {
q.Push(i)
}
t.Assert(q.Size(), 10)
t.Assert(q.Len(), 10)
q.Close()
})
gtest.C(t, func(t *gtest.T) {
q := gqueue.New()
for i := range 15 {
q.Push(i)
}
time.Sleep(10 * time.Millisecond)
t.Assert(q.Size(), q.Len())
q.Close()
})
}
// Test TQueue directly with generic type
func TestTQueue_Generic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
// Test with custom type
q := gqueue.NewTQueue[string]()
defer q.Close()
q.Push("hello")
q.Push("world")
t.Assert(q.Pop(), "hello")
t.Assert(q.Pop(), "world")
})
}
// Test TQueue Size method directly
func TestTQueue_Size(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
q := gqueue.NewTQueue[int]()
defer q.Close()
for i := range 10 {
q.Push(i)
}
time.Sleep(10 * time.Millisecond)
// Size is an alias of Len for TQueue
t.Assert(q.Size(), q.Len())
})
}
// Test TQueue with static limit
func TestTQueue_StaticLimit(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
q := gqueue.NewTQueue[int](5)
defer q.Close()
for i := range 5 {
q.Push(i)
}
t.Assert(q.Len(), 5)
for i := range 5 {
t.Assert(q.Pop(), i)
}
t.Assert(q.Len(), 0)
})
}
// Test queue with large data push/pop
func TestQueue_LargeDataScale(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
q := gqueue.New()
defer q.Close()
n := 5000
for i := range n {
q.Push(i)
}
time.Sleep(50 * time.Millisecond)
// Pop should retrieve all items in order
for i := range n {
v := q.Pop()
t.Assert(v, i)
}
})
}
// Test double close (idempotent close)
func TestQueue_DoubleClose(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
q := gqueue.New()
q.Push(1)
q.Close()
// Second close should not panic
q.Close()
t.Assert(q.Pop(), nil)
})
gtest.C(t, func(t *gtest.T) {
q := gqueue.New(10)
q.Push(1)
q.Close()
// Second close should not panic for static queue
q.Close()
// Pop from closed static queue returns the buffered value
v := q.Pop()
t.Assert(v, 1)
})
}
// Test concurrent push and pop
func TestQueue_ConcurrentPushPop(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
q := gqueue.New()
defer q.Close()
// Producer goroutine
go func() {
for i := range 100 {
q.Push(i)
}
time.Sleep(50 * time.Millisecond)
q.Close()
}()
// Consumer
count := 0
for {
v := q.Pop()
if v == nil {
break
}
count++
}
t.AssertGE(count, 1)
})
}
// Test Pop on empty queue returns nil when closed
func TestQueue_PopEmptyClosed(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
q := gqueue.New()
q.Close()
v := q.Pop()
t.Assert(v, nil)
})
gtest.C(t, func(t *gtest.T) {
q := gqueue.New(10)
q.Close()
v := q.Pop()
t.Assert(v, nil)
})
}
// Test Len with dynamic queue at capacity boundary
func TestQueue_LenAtBoundary(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
q := gqueue.New()
defer q.Close()
// Push exactly defaultQueueSize items to test boundary condition
for i := range 10000 {
q.Push(i)
}
time.Sleep(50 * time.Millisecond)
len := q.Len()
t.AssertGE(len, 0)
})
}
// Test Close on dynamic queue with pending asyncLoopFromListToChannel
func TestQueue_CloseWithAsyncLoop(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
q := gqueue.New()
// Push some data to activate asyncLoopFromListToChannel
for i := range 100 {
q.Push(i)
}
// Immediately close
q.Close()
// Pop should return values until exhausted, then nil
for {
v := q.Pop()
if v == nil {
break
}
}
t.Assert(q.Pop(), nil)
})
}
// Test static queue edge case with zero limit (should create unlimited queue)
func TestQueue_ZeroLimitCreatesUnlimited(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
q := gqueue.New(0)
defer q.Close()
for i := range 100 {
q.Push(i)
}
time.Sleep(10 * time.Millisecond)
len := q.Len()
t.Assert(len, 100)
})
}