From e4e58791a6645fd6aa7a572c16d96de3b0020b1a Mon Sep 17 00:00:00 2001 From: "zcool321@sina.com" <1234qwer> Date: Thu, 13 Jun 2019 23:50:12 +0800 Subject: [PATCH 01/23] add empty test --- g/internal/empty/empty_test.go | 90 ++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 g/internal/empty/empty_test.go diff --git a/g/internal/empty/empty_test.go b/g/internal/empty/empty_test.go new file mode 100644 index 000000000..0241a8b2c --- /dev/null +++ b/g/internal/empty/empty_test.go @@ -0,0 +1,90 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). 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. + +package empty_test + +import ( + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/internal/empty" + "github.com/gogf/gf/g/test/gtest" + "github.com/gogf/gf/g/util/gconv" + "testing" +) + +type TestPerson interface { + Say() string +} +type TestWoman struct { +} + +func (woman TestWoman) Say() string { + return "nice" +} + +func TestIsEmpty(t *testing.T) { + gtest.Case(t, func() { + tmpT1 := "0" + tmpT2 := func() {} + tmpT2 = nil + tmpT3 := make(chan int, 0) + var tmpT4 TestPerson = nil + var tmpT5 *TestPerson = nil + tmpF1 := "1" + tmpF2 := func(a string) string { return "1" } + tmpF3 := make(chan int, 1) + tmpF3 <- 1 + var tmpF4 TestPerson = TestWoman{} + tmpF5 := &tmpF4 + // true + gtest.Assert(empty.IsEmpty(nil), true) + gtest.Assert(empty.IsEmpty(gconv.Int(tmpT1)), true) + gtest.Assert(empty.IsEmpty(gconv.Int8(tmpT1)), true) + gtest.Assert(empty.IsEmpty(gconv.Int16(tmpT1)), true) + gtest.Assert(empty.IsEmpty(gconv.Int32(tmpT1)), true) + gtest.Assert(empty.IsEmpty(gconv.Int64(tmpT1)), true) + gtest.Assert(empty.IsEmpty(gconv.Uint64(tmpT1)), true) + gtest.Assert(empty.IsEmpty(gconv.Uint(tmpT1)), true) + gtest.Assert(empty.IsEmpty(gconv.Uint16(tmpT1)), true) + gtest.Assert(empty.IsEmpty(gconv.Uint32(tmpT1)), true) + gtest.Assert(empty.IsEmpty(gconv.Uint64(tmpT1)), true) + gtest.Assert(empty.IsEmpty(gconv.Float32(tmpT1)), true) + gtest.Assert(empty.IsEmpty(gconv.Float64(tmpT1)), true) + gtest.Assert(empty.IsEmpty(false), true) + gtest.Assert(empty.IsEmpty([]byte("")), true) + gtest.Assert(empty.IsEmpty(""), true) + gtest.Assert(empty.IsEmpty(g.Map{}), true) + gtest.Assert(empty.IsEmpty(g.Slice{}), true) + gtest.Assert(empty.IsEmpty(g.Array{}), true) + gtest.Assert(empty.IsEmpty(tmpT2), true) + gtest.Assert(empty.IsEmpty(tmpT3), true) + gtest.Assert(empty.IsEmpty(tmpT3), true) + gtest.Assert(empty.IsEmpty(tmpT4), true) + gtest.Assert(empty.IsEmpty(tmpT5), true) + // false + gtest.Assert(empty.IsEmpty(gconv.Int(tmpF1)), false) + gtest.Assert(empty.IsEmpty(gconv.Int8(tmpF1)), false) + gtest.Assert(empty.IsEmpty(gconv.Int16(tmpF1)), false) + gtest.Assert(empty.IsEmpty(gconv.Int32(tmpF1)), false) + gtest.Assert(empty.IsEmpty(gconv.Int64(tmpF1)), false) + gtest.Assert(empty.IsEmpty(gconv.Uint(tmpF1)), false) + gtest.Assert(empty.IsEmpty(gconv.Uint8(tmpF1)), false) + gtest.Assert(empty.IsEmpty(gconv.Uint16(tmpF1)), false) + gtest.Assert(empty.IsEmpty(gconv.Uint32(tmpF1)), false) + gtest.Assert(empty.IsEmpty(gconv.Uint64(tmpF1)), false) + gtest.Assert(empty.IsEmpty(gconv.Float32(tmpF1)), false) + gtest.Assert(empty.IsEmpty(gconv.Float64(tmpF1)), false) + gtest.Assert(empty.IsEmpty(true), false) + gtest.Assert(empty.IsEmpty(tmpT1), false) + gtest.Assert(empty.IsEmpty([]byte("1")), false) + gtest.Assert(empty.IsEmpty(g.Map{"a": 1}), false) + gtest.Assert(empty.IsEmpty(g.Slice{"1"}), false) + gtest.Assert(empty.IsEmpty(g.Array{"1"}), false) + gtest.Assert(empty.IsEmpty(tmpF2), false) + gtest.Assert(empty.IsEmpty(tmpF3), false) + gtest.Assert(empty.IsEmpty(tmpF4), false) + gtest.Assert(empty.IsEmpty(tmpF5), false) + }) +} From 48f610a216ce31182e8f7c7db13c37c20d94f406 Mon Sep 17 00:00:00 2001 From: "zcool321@sina.com" <1234qwer> Date: Fri, 14 Jun 2019 00:03:13 +0800 Subject: [PATCH 02/23] add mutex and rwmutex test --- g/internal/mutex/mutex_z_unit_test.go | 32 +++++++++++++++++++ g/internal/rwmutex/rwmutex_z_unit_test.go | 38 +++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 g/internal/mutex/mutex_z_unit_test.go create mode 100644 g/internal/rwmutex/rwmutex_z_unit_test.go diff --git a/g/internal/mutex/mutex_z_unit_test.go b/g/internal/mutex/mutex_z_unit_test.go new file mode 100644 index 000000000..905d6c0d2 --- /dev/null +++ b/g/internal/mutex/mutex_z_unit_test.go @@ -0,0 +1,32 @@ +// Copyright 2018 gf Author(https://github.com/gogf/gf). 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. + +package mutex_test + +import ( + "github.com/gogf/gf/g/internal/mutex" + "github.com/gogf/gf/g/test/gtest" + "testing" +) + +func TestMutex(t *testing.T) { + gtest.Case(t, func() { + lock := mutex.New() + lock.Lock() + lock.Unlock() + gtest.Assert(lock.IsSafe(), true) + + safeLock1 := mutex.New(false) + safeLock1.Lock() + safeLock1.Unlock() + gtest.Assert(safeLock1.IsSafe(), true) + + unsafeLock1 := mutex.New(true) + unsafeLock1.Lock() + unsafeLock1.Unlock() + gtest.Assert(unsafeLock1.IsSafe(), false) + }) +} diff --git a/g/internal/rwmutex/rwmutex_z_unit_test.go b/g/internal/rwmutex/rwmutex_z_unit_test.go new file mode 100644 index 000000000..d6ada7ada --- /dev/null +++ b/g/internal/rwmutex/rwmutex_z_unit_test.go @@ -0,0 +1,38 @@ +// Copyright 2018 gf Author(https://github.com/gogf/gf). 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. + +package rwmutex_test + +import ( + "github.com/gogf/gf/g/internal/rwmutex" + "github.com/gogf/gf/g/test/gtest" + "testing" +) + +func TestRwmutex(t *testing.T) { + gtest.Case(t, func() { + lock := rwmutex.New() + lock.Lock() + lock.Unlock() + lock.RLock() + lock.RUnlock() + gtest.Assert(lock.IsSafe(), true) + + safeLock1 := rwmutex.New(false) + safeLock1.Lock() + safeLock1.Unlock() + safeLock1.RLock() + safeLock1.RUnlock() + gtest.Assert(safeLock1.IsSafe(), true) + + unsafeLock1 := rwmutex.New(true) + unsafeLock1.Lock() + unsafeLock1.Unlock() + unsafeLock1.RLock() + unsafeLock1.RUnlock() + gtest.Assert(unsafeLock1.IsSafe(), false) + }) +} From 5baa82da8faa59cd796d8e987f324800ec4aed8d Mon Sep 17 00:00:00 2001 From: "zcool321@sina.com" <1234qwer> Date: Fri, 14 Jun 2019 00:35:18 +0800 Subject: [PATCH 03/23] add gcmd test --- g/os/gcmd/gcmd_z_unit_test.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/g/os/gcmd/gcmd_z_unit_test.go b/g/os/gcmd/gcmd_z_unit_test.go index bd9e05a84..dda04293e 100644 --- a/g/os/gcmd/gcmd_z_unit_test.go +++ b/g/os/gcmd/gcmd_z_unit_test.go @@ -14,7 +14,6 @@ import ( "testing" ) - func Test_ValueAndOption(t *testing.T) { os.Args = []string{"v1", "v2", "--o1=111", "-o2=222"} doInit() @@ -23,6 +22,28 @@ func Test_ValueAndOption(t *testing.T) { gtest.Assert(Value.Get(0), "v1") gtest.Assert(Value.Get(1), "v2") gtest.Assert(Value.Get(2), "") + gtest.Assert(Value.Get(2, "1"), "1") + gtest.Assert(Value.GetVar(1, "1").String(), "v2") + gtest.Assert(Value.GetVar(2, "1").String(), "1") + + gtest.Assert(Option.GetAll(), map[string]string{"o1": "111", "o2": "222"}) + gtest.Assert(Option.Get("o1"), "111") + gtest.Assert(Option.Get("o2"), "222") + gtest.Assert(Option.Get("o3", "1"), "1") + gtest.Assert(Option.GetVar("o2", "1").String(), "222") + gtest.Assert(Option.GetVar("o3", "1").String(), "1") + }) } +func Test_Handle(t *testing.T) { + os.Args = []string{"gf", "gf"} + doInit() + gtest.Case(t, func() { + BindHandle("gf", func() { + print("gf test") + }) + RunHandle("gf") + AutoRun() + }) +} From 1f19ed71aae328e4d70e128bd39a3abb811ac000 Mon Sep 17 00:00:00 2001 From: zhangbiao Date: Fri, 14 Jun 2019 10:20:15 +0800 Subject: [PATCH 04/23] update mutex test --- g/internal/mutex/mutex_z_unit_test.go | 87 ++++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 10 deletions(-) diff --git a/g/internal/mutex/mutex_z_unit_test.go b/g/internal/mutex/mutex_z_unit_test.go index 905d6c0d2..cb2567fab 100644 --- a/g/internal/mutex/mutex_z_unit_test.go +++ b/g/internal/mutex/mutex_z_unit_test.go @@ -7,26 +7,93 @@ package mutex_test import ( + "github.com/gogf/gf/g/container/garray" "github.com/gogf/gf/g/internal/mutex" "github.com/gogf/gf/g/test/gtest" "testing" + "time" ) func TestMutex(t *testing.T) { gtest.Case(t, func() { lock := mutex.New() - lock.Lock() - lock.Unlock() gtest.Assert(lock.IsSafe(), true) - safeLock1 := mutex.New(false) - safeLock1.Lock() - safeLock1.Unlock() - gtest.Assert(safeLock1.IsSafe(), true) + lock = mutex.New(false) + gtest.Assert(lock.IsSafe(), true) - unsafeLock1 := mutex.New(true) - unsafeLock1.Lock() - unsafeLock1.Unlock() - gtest.Assert(unsafeLock1.IsSafe(), false) + lock = mutex.New(false, false) + gtest.Assert(lock.IsSafe(), true) + + lock = mutex.New(true, false) + gtest.Assert(lock.IsSafe(), false) + + lock = mutex.New(true, true) + gtest.Assert(lock.IsSafe(), false) + + lock = mutex.New(true) + gtest.Assert(lock.IsSafe(), false) + }) +} + +func TestSafeMutex(t *testing.T) { + gtest.Case(t, func() { + safeLock := mutex.New(false) + array := garray.New() + + go func() { + safeLock.Lock() + array.Append(1) + time.Sleep(100 * time.Millisecond) + array.Append(1) + safeLock.Unlock() + }() + go func() { + time.Sleep(10 * time.Millisecond) + safeLock.Lock() + array.Append(1) + time.Sleep(200 * time.Millisecond) + array.Append(1) + safeLock.Unlock() + }() + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(80 * time.Millisecond) + gtest.Assert(array.Len(), 3) + time.Sleep(100 * time.Millisecond) + gtest.Assert(array.Len(), 3) + time.Sleep(100 * time.Millisecond) + gtest.Assert(array.Len(), 4) + }) +} + +func TestUnsafeMutex(t *testing.T) { + gtest.Case(t, func() { + unsafeLock := mutex.New(true) + array := garray.New() + + go func() { + unsafeLock.Lock() + array.Append(1) + time.Sleep(100 * time.Millisecond) + array.Append(1) + unsafeLock.Unlock() + }() + go func() { + time.Sleep(10 * time.Millisecond) + unsafeLock.Lock() + array.Append(1) + time.Sleep(200 * time.Millisecond) + array.Append(1) + unsafeLock.Unlock() + }() + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 2) + time.Sleep(100 * time.Millisecond) + gtest.Assert(array.Len(), 3) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 3) + time.Sleep(100 * time.Millisecond) + gtest.Assert(array.Len(), 4) }) } From 3e2d5e0bdbc7f051aa88ed6037b15012841d9540 Mon Sep 17 00:00:00 2001 From: zhangbiao Date: Fri, 14 Jun 2019 10:28:24 +0800 Subject: [PATCH 05/23] update mutex test2 --- g/internal/mutex/mutex_z_unit_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/g/internal/mutex/mutex_z_unit_test.go b/g/internal/mutex/mutex_z_unit_test.go index cb2567fab..ca7f0c368 100644 --- a/g/internal/mutex/mutex_z_unit_test.go +++ b/g/internal/mutex/mutex_z_unit_test.go @@ -14,7 +14,7 @@ import ( "time" ) -func TestMutex(t *testing.T) { +func TestMutexIsSafe(t *testing.T) { gtest.Case(t, func() { lock := mutex.New() gtest.Assert(lock.IsSafe(), true) From 5c592afebe53888d9687462af96851fba2be739e Mon Sep 17 00:00:00 2001 From: hailaz <739476267@qq.com> Date: Sat, 15 Jun 2019 17:51:48 +0800 Subject: [PATCH 06/23] Improve gtree unit testing. --- g/container/gtree/gtree_z_avl_tree_test.go | 193 +++++++++++++++--- g/container/gtree/gtree_z_b_tree_test.go | 147 +++++++++++-- .../gtree/gtree_z_redblack_tree_test.go | 193 ++++++++++++++++-- 3 files changed, 466 insertions(+), 67 deletions(-) diff --git a/g/container/gtree/gtree_z_avl_tree_test.go b/g/container/gtree/gtree_z_avl_tree_test.go index f20ed2601..8cf58032f 100644 --- a/g/container/gtree/gtree_z_avl_tree_test.go +++ b/g/container/gtree/gtree_z_avl_tree_test.go @@ -7,13 +7,15 @@ package gtree_test import ( + "fmt" + "testing" + "github.com/gogf/gf/g/container/gtree" + "github.com/gogf/gf/g/container/gvar" "github.com/gogf/gf/g/test/gtest" "github.com/gogf/gf/g/util/gutil" - "testing" ) - func Test_AVLTree_Basic(t *testing.T) { gtest.Case(t, func() { m := gtree.NewAVLTree(gutil.ComparatorString) @@ -24,6 +26,7 @@ func Test_AVLTree_Basic(t *testing.T) { gtest.Assert(m.Size(), 1) gtest.Assert(m.IsEmpty(), false) + gtest.Assert(m.GetOrSet("key2", "val2"), "val2") gtest.Assert(m.GetOrSet("key2", "val2"), "val2") gtest.Assert(m.SetIfNotExist("key2", "val2"), false) @@ -37,9 +40,14 @@ func Test_AVLTree_Basic(t *testing.T) { gtest.AssertIN("val3", m.Values()) gtest.AssertIN("val1", m.Values()) + m.Sets(map[interface{}]interface{}{"key3": "val3", "key1": "val1"}) + m.Flip() gtest.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"}) + m.Flip(gutil.ComparatorString) + gtest.Assert(m.Map(), map[interface{}]interface{}{"key3": "val3", "key1": "val1"}) + m.Clear() gtest.Assert(m.Size(), 0) gtest.Assert(m.IsEmpty(), true) @@ -49,14 +57,45 @@ func Test_AVLTree_Basic(t *testing.T) { }) } func Test_AVLTree_Set_Fun(t *testing.T) { - m := gtree.NewAVLTree(gutil.ComparatorString) - m.GetOrSetFunc("fun", getValue) - m.GetOrSetFuncLock("funlock", getValue) - gtest.Assert(m.Get("funlock"), 3) - gtest.Assert(m.Get("fun"), 3) - m.GetOrSetFunc("fun", getValue) - gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false) - gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false) + //GetOrSetFunc lock or unlock + gtest.Case(t, func() { + m := gtree.NewAVLTree(gutil.ComparatorString) + gtest.Assert(m.GetOrSetFunc("fun", getValue), 3) + gtest.Assert(m.GetOrSetFunc("fun", getValue), 3) + gtest.Assert(m.GetOrSetFuncLock("funlock", getValue), 3) + gtest.Assert(m.GetOrSetFuncLock("funlock", getValue), 3) + gtest.Assert(m.Get("funlock"), 3) + gtest.Assert(m.Get("fun"), 3) + }) + //SetIfNotExistFunc lock or unlock + gtest.Case(t, func() { + m := gtree.NewAVLTree(gutil.ComparatorString) + gtest.Assert(m.SetIfNotExistFunc("fun", getValue), true) + gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false) + gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), true) + gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false) + gtest.Assert(m.Get("funlock"), 3) + gtest.Assert(m.Get("fun"), 3) + }) + +} + +func Test_AVLTree_Get_Set_Var(t *testing.T) { + gtest.Case(t, func() { + m := gtree.NewAVLTree(gutil.ComparatorString) + gtest.AssertEQ(m.SetIfNotExist("key1", "val1"), true) + gtest.AssertEQ(m.SetIfNotExist("key1", "val1"), false) + gtest.AssertEQ(m.GetVarOrSet("key1", "val1"), gvar.New("val1", true)) + gtest.AssertEQ(m.GetVar("key1"), gvar.New("val1", true)) + }) + + gtest.Case(t, func() { + m := gtree.NewAVLTree(gutil.ComparatorString) + gtest.AssertEQ(m.GetVarOrSetFunc("fun", getValue), gvar.New(3, true)) + gtest.AssertEQ(m.GetVarOrSetFunc("fun", getValue), gvar.New(3, true)) + gtest.AssertEQ(m.GetVarOrSetFuncLock("funlock", getValue), gvar.New(3, true)) + gtest.AssertEQ(m.GetVarOrSetFuncLock("funlock", getValue), gvar.New(3, true)) + }) } func Test_AVLTree_Batch(t *testing.T) { @@ -66,27 +105,61 @@ func Test_AVLTree_Batch(t *testing.T) { m.Removes([]interface{}{"key1", 1}) gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"}) } -func Test_AVLTree_Iterator(t *testing.T){ - expect := map[interface{}]interface{}{1: 1, "key1": "val1"} + +func Test_AVLTree_Iterator(t *testing.T) { + keys := []string{"1", "key1", "key2", "key3", "key4"} + keyLen := len(keys) + index := 0 + + expect := map[interface{}]interface{}{"key4": "val4", 1: 1, "key1": "val1", "key2": "val2", "key3": "val3"} m := gtree.NewAVLTreeFrom(gutil.ComparatorString, expect) m.Iterator(func(k interface{}, v interface{}) bool { + gtest.Assert(k, keys[index]) + index++ gtest.Assert(expect[k], v) return true }) - // 断言返回值对遍历控制 - i := 0 - j := 0 - m.Iterator(func(k interface{}, v interface{}) bool { - i++ + + m.IteratorDesc(func(k interface{}, v interface{}) bool { + index-- + gtest.Assert(k, keys[index]) + gtest.Assert(expect[k], v) return true }) - m.Iterator(func(k interface{}, v interface{}) bool { - j++ - return false + + m.Print() + // 断言返回值对遍历控制 + gtest.Case(t, func() { + i := 0 + j := 0 + m.Iterator(func(k interface{}, v interface{}) bool { + i++ + return true + }) + m.Iterator(func(k interface{}, v interface{}) bool { + j++ + return false + }) + gtest.Assert(i, keyLen) + gtest.Assert(j, 1) }) - gtest.Assert(i, 2) - gtest.Assert(j, 1) + + gtest.Case(t, func() { + i := 0 + j := 0 + m.IteratorDesc(func(k interface{}, v interface{}) bool { + i++ + return true + }) + m.IteratorDesc(func(k interface{}, v interface{}) bool { + j++ + return false + }) + gtest.Assert(i, keyLen) + gtest.Assert(j, 1) + }) + } func Test_AVLTree_Clone(t *testing.T) { @@ -100,4 +173,78 @@ func Test_AVLTree_Clone(t *testing.T) { m_clone.Remove("key1") //修改clone map,原 map 不影响 gtest.AssertIN("key1", m.Keys()) -} \ No newline at end of file +} + +func Test_AVLTree_LRNode(t *testing.T) { + expect := map[interface{}]interface{}{"key4": "val4", "key1": "val1", "key2": "val2", "key3": "val3"} + //safe + gtest.Case(t, func() { + m := gtree.NewAVLTreeFrom(gutil.ComparatorString, expect) + gtest.Assert(m.Left().Key, "key1") + gtest.Assert(m.Right().Key, "key4") + }) + //unsafe + gtest.Case(t, func() { + m := gtree.NewAVLTreeFrom(gutil.ComparatorString, expect, true) + gtest.Assert(m.Left().Key, "key1") + gtest.Assert(m.Right().Key, "key4") + }) +} + +func Test_AVLTree_CeilingFloor(t *testing.T) { + expect := map[interface{}]interface{}{ + 20: "val20", + 6: "val6", + 10: "val10", + 12: "val12", + 1: "val1", + 15: "val15", + 19: "val19", + 8: "val8", + 4: "val4"} + //found and eq + gtest.Case(t, func() { + m := gtree.NewAVLTreeFrom(gutil.ComparatorInt, expect) + c, cf := m.Ceiling(8) + gtest.Assert(cf, true) + gtest.Assert(c.Value, "val8") + f, ff := m.Floor(20) + gtest.Assert(ff, true) + gtest.Assert(f.Value, "val20") + }) + //found and neq + gtest.Case(t, func() { + m := gtree.NewAVLTreeFrom(gutil.ComparatorInt, expect) + c, cf := m.Ceiling(9) + gtest.Assert(cf, true) + gtest.Assert(c.Value, "val10") + f, ff := m.Floor(5) + gtest.Assert(ff, true) + gtest.Assert(f.Value, "val4") + }) + //nofound + gtest.Case(t, func() { + m := gtree.NewAVLTreeFrom(gutil.ComparatorInt, expect) + c, cf := m.Ceiling(21) + gtest.Assert(cf, false) + gtest.Assert(c, nil) + f, ff := m.Floor(-1) + gtest.Assert(ff, false) + gtest.Assert(f, nil) + }) +} + +func Test_AVLTree_Remove(t *testing.T) { + m := gtree.NewAVLTree(gutil.ComparatorInt) + for i := 1; i <= 50; i++ { + m.Set(i, fmt.Sprintf("val%d", i)) + } + expect := m.Map() + gtest.Case(t, func() { + for k, v := range expect { + m1 := m.Clone() + gtest.Assert(m1.Remove(k), v) + gtest.Assert(m1.Remove(k), nil) + } + }) +} diff --git a/g/container/gtree/gtree_z_b_tree_test.go b/g/container/gtree/gtree_z_b_tree_test.go index 1596bd175..8659b5366 100644 --- a/g/container/gtree/gtree_z_b_tree_test.go +++ b/g/container/gtree/gtree_z_b_tree_test.go @@ -7,16 +7,22 @@ package gtree_test import ( + "fmt" + "testing" + "github.com/gogf/gf/g/container/gtree" + "github.com/gogf/gf/g/container/gvar" "github.com/gogf/gf/g/test/gtest" "github.com/gogf/gf/g/util/gutil" - "testing" ) func Test_BTree_Basic(t *testing.T) { gtest.Case(t, func() { m := gtree.NewBTree(3, gutil.ComparatorString) m.Set("key1", "val1") + + gtest.Assert(m.Height(), 1) + gtest.Assert(m.Keys(), []interface{}{"key1"}) gtest.Assert(m.Get("key1"), "val1") @@ -44,15 +50,47 @@ func Test_BTree_Basic(t *testing.T) { gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"}) }) } + func Test_BTree_Set_Fun(t *testing.T) { - m := gtree.NewBTree(3, gutil.ComparatorString) - m.GetOrSetFunc("fun", getValue) - m.GetOrSetFuncLock("funlock", getValue) - gtest.Assert(m.Get("funlock"), 3) - gtest.Assert(m.Get("fun"), 3) - m.GetOrSetFunc("fun", getValue) - gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false) - gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false) + //GetOrSetFunc lock or unlock + gtest.Case(t, func() { + m := gtree.NewBTree(3, gutil.ComparatorString) + gtest.Assert(m.GetOrSetFunc("fun", getValue), 3) + gtest.Assert(m.GetOrSetFunc("fun", getValue), 3) + gtest.Assert(m.GetOrSetFuncLock("funlock", getValue), 3) + gtest.Assert(m.GetOrSetFuncLock("funlock", getValue), 3) + gtest.Assert(m.Get("funlock"), 3) + gtest.Assert(m.Get("fun"), 3) + }) + //SetIfNotExistFunc lock or unlock + gtest.Case(t, func() { + m := gtree.NewBTree(3, gutil.ComparatorString) + gtest.Assert(m.SetIfNotExistFunc("fun", getValue), true) + gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false) + gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), true) + gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false) + gtest.Assert(m.Get("funlock"), 3) + gtest.Assert(m.Get("fun"), 3) + }) + +} + +func Test_BTree_Get_Set_Var(t *testing.T) { + gtest.Case(t, func() { + m := gtree.NewBTree(3, gutil.ComparatorString) + gtest.AssertEQ(m.SetIfNotExist("key1", "val1"), true) + gtest.AssertEQ(m.SetIfNotExist("key1", "val1"), false) + gtest.AssertEQ(m.GetVarOrSet("key1", "val1"), gvar.New("val1", true)) + gtest.AssertEQ(m.GetVar("key1"), gvar.New("val1", true)) + }) + + gtest.Case(t, func() { + m := gtree.NewBTree(3, gutil.ComparatorString) + gtest.AssertEQ(m.GetVarOrSetFunc("fun", getValue), gvar.New(3, true)) + gtest.AssertEQ(m.GetVarOrSetFunc("fun", getValue), gvar.New(3, true)) + gtest.AssertEQ(m.GetVarOrSetFuncLock("funlock", getValue), gvar.New(3, true)) + gtest.AssertEQ(m.GetVarOrSetFuncLock("funlock", getValue), gvar.New(3, true)) + }) } func Test_BTree_Batch(t *testing.T) { @@ -62,27 +100,61 @@ func Test_BTree_Batch(t *testing.T) { m.Removes([]interface{}{"key1", 1}) gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"}) } -func Test_BTree_Iterator(t *testing.T){ - expect := map[interface{}]interface{}{1: 1, "key1": "val1"} + +func Test_BTree_Iterator(t *testing.T) { + keys := []string{"1", "key1", "key2", "key3", "key4"} + keyLen := len(keys) + index := 0 + + expect := map[interface{}]interface{}{"key4": "val4", 1: 1, "key1": "val1", "key2": "val2", "key3": "val3"} m := gtree.NewBTreeFrom(3, gutil.ComparatorString, expect) m.Iterator(func(k interface{}, v interface{}) bool { + gtest.Assert(k, keys[index]) + index++ gtest.Assert(expect[k], v) return true }) - // 断言返回值对遍历控制 - i := 0 - j := 0 - m.Iterator(func(k interface{}, v interface{}) bool { - i++ + + m.IteratorDesc(func(k interface{}, v interface{}) bool { + index-- + gtest.Assert(k, keys[index]) + gtest.Assert(expect[k], v) return true }) - m.Iterator(func(k interface{}, v interface{}) bool { - j++ - return false + + m.Print() + // 断言返回值对遍历控制 + gtest.Case(t, func() { + i := 0 + j := 0 + m.Iterator(func(k interface{}, v interface{}) bool { + i++ + return true + }) + m.Iterator(func(k interface{}, v interface{}) bool { + j++ + return false + }) + gtest.Assert(i, keyLen) + gtest.Assert(j, 1) }) - gtest.Assert(i, 2) - gtest.Assert(j, 1) + + gtest.Case(t, func() { + i := 0 + j := 0 + m.IteratorDesc(func(k interface{}, v interface{}) bool { + i++ + return true + }) + m.IteratorDesc(func(k interface{}, v interface{}) bool { + j++ + return false + }) + gtest.Assert(i, keyLen) + gtest.Assert(j, 1) + }) + } func Test_BTree_Clone(t *testing.T) { @@ -96,4 +168,35 @@ func Test_BTree_Clone(t *testing.T) { m_clone.Remove("key1") //修改clone map,原 map 不影响 gtest.AssertIN("key1", m.Keys()) -} \ No newline at end of file +} + +func Test_BTree_LRNode(t *testing.T) { + expect := map[interface{}]interface{}{"key4": "val4", "key1": "val1", "key2": "val2", "key3": "val3"} + //safe + gtest.Case(t, func() { + m := gtree.NewBTreeFrom(3, gutil.ComparatorString, expect) + gtest.Assert(m.Left().Key, "key1") + gtest.Assert(m.Right().Key, "key4") + }) + //unsafe + gtest.Case(t, func() { + m := gtree.NewBTreeFrom(3, gutil.ComparatorString, expect, true) + gtest.Assert(m.Left().Key, "key1") + gtest.Assert(m.Right().Key, "key4") + }) +} + +func Test_BTree_Remove(t *testing.T) { + m := gtree.NewBTree(3, gutil.ComparatorInt) + for i := 1; i <= 100; i++ { + m.Set(i, fmt.Sprintf("val%d", i)) + } + expect := m.Map() + gtest.Case(t, func() { + for k, v := range expect { + m1 := m.Clone() + gtest.Assert(m1.Remove(k), v) + gtest.Assert(m1.Remove(k), nil) + } + }) +} diff --git a/g/container/gtree/gtree_z_redblack_tree_test.go b/g/container/gtree/gtree_z_redblack_tree_test.go index 6dbe85c6a..29406bad8 100644 --- a/g/container/gtree/gtree_z_redblack_tree_test.go +++ b/g/container/gtree/gtree_z_redblack_tree_test.go @@ -7,10 +7,13 @@ package gtree_test import ( + "fmt" + "testing" + "github.com/gogf/gf/g/container/gtree" + "github.com/gogf/gf/g/container/gvar" "github.com/gogf/gf/g/test/gtest" "github.com/gogf/gf/g/util/gutil" - "testing" ) func getValue() interface{} { @@ -27,6 +30,7 @@ func Test_RedBlackTree_Basic(t *testing.T) { gtest.Assert(m.Size(), 1) gtest.Assert(m.IsEmpty(), false) + gtest.Assert(m.GetOrSet("key2", "val2"), "val2") gtest.Assert(m.GetOrSet("key2", "val2"), "val2") gtest.Assert(m.SetIfNotExist("key2", "val2"), false) @@ -40,9 +44,14 @@ func Test_RedBlackTree_Basic(t *testing.T) { gtest.AssertIN("val3", m.Values()) gtest.AssertIN("val1", m.Values()) + m.Sets(map[interface{}]interface{}{"key3": "val3", "key1": "val1"}) + m.Flip() gtest.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"}) + m.Flip(gutil.ComparatorString) + gtest.Assert(m.Map(), map[interface{}]interface{}{"key3": "val3", "key1": "val1"}) + m.Clear() gtest.Assert(m.Size(), 0) gtest.Assert(m.IsEmpty(), true) @@ -51,15 +60,47 @@ func Test_RedBlackTree_Basic(t *testing.T) { gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"}) }) } + func Test_RedBlackTree_Set_Fun(t *testing.T) { - m := gtree.NewRedBlackTree(gutil.ComparatorString) - m.GetOrSetFunc("fun", getValue) - m.GetOrSetFuncLock("funlock", getValue) - gtest.Assert(m.Get("funlock"), 3) - gtest.Assert(m.Get("fun"), 3) - m.GetOrSetFunc("fun", getValue) - gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false) - gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false) + //GetOrSetFunc lock or unlock + gtest.Case(t, func() { + m := gtree.NewRedBlackTree(gutil.ComparatorString) + gtest.Assert(m.GetOrSetFunc("fun", getValue), 3) + gtest.Assert(m.GetOrSetFunc("fun", getValue), 3) + gtest.Assert(m.GetOrSetFuncLock("funlock", getValue), 3) + gtest.Assert(m.GetOrSetFuncLock("funlock", getValue), 3) + gtest.Assert(m.Get("funlock"), 3) + gtest.Assert(m.Get("fun"), 3) + }) + //SetIfNotExistFunc lock or unlock + gtest.Case(t, func() { + m := gtree.NewRedBlackTree(gutil.ComparatorString) + gtest.Assert(m.SetIfNotExistFunc("fun", getValue), true) + gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false) + gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), true) + gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false) + gtest.Assert(m.Get("funlock"), 3) + gtest.Assert(m.Get("fun"), 3) + }) + +} + +func Test_RedBlackTree_Get_Set_Var(t *testing.T) { + gtest.Case(t, func() { + m := gtree.NewRedBlackTree(gutil.ComparatorString) + gtest.AssertEQ(m.SetIfNotExist("key1", "val1"), true) + gtest.AssertEQ(m.SetIfNotExist("key1", "val1"), false) + gtest.AssertEQ(m.GetVarOrSet("key1", "val1"), gvar.New("val1", true)) + gtest.AssertEQ(m.GetVar("key1"), gvar.New("val1", true)) + }) + + gtest.Case(t, func() { + m := gtree.NewRedBlackTree(gutil.ComparatorString) + gtest.AssertEQ(m.GetVarOrSetFunc("fun", getValue), gvar.New(3, true)) + gtest.AssertEQ(m.GetVarOrSetFunc("fun", getValue), gvar.New(3, true)) + gtest.AssertEQ(m.GetVarOrSetFuncLock("funlock", getValue), gvar.New(3, true)) + gtest.AssertEQ(m.GetVarOrSetFuncLock("funlock", getValue), gvar.New(3, true)) + }) } func Test_RedBlackTree_Batch(t *testing.T) { @@ -69,27 +110,61 @@ func Test_RedBlackTree_Batch(t *testing.T) { m.Removes([]interface{}{"key1", 1}) gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"}) } -func Test_RedBlackTree_Iterator(t *testing.T){ - expect := map[interface{}]interface{}{1: 1, "key1": "val1"} + +func Test_RedBlackTree_Iterator(t *testing.T) { + keys := []string{"1", "key1", "key2", "key3", "key4"} + keyLen := len(keys) + index := 0 + + expect := map[interface{}]interface{}{"key4": "val4", 1: 1, "key1": "val1", "key2": "val2", "key3": "val3"} m := gtree.NewRedBlackTreeFrom(gutil.ComparatorString, expect) m.Iterator(func(k interface{}, v interface{}) bool { + gtest.Assert(k, keys[index]) + index++ gtest.Assert(expect[k], v) return true }) - // 断言返回值对遍历控制 - i := 0 - j := 0 - m.Iterator(func(k interface{}, v interface{}) bool { - i++ + + m.IteratorDesc(func(k interface{}, v interface{}) bool { + index-- + gtest.Assert(k, keys[index]) + gtest.Assert(expect[k], v) return true }) - m.Iterator(func(k interface{}, v interface{}) bool { - j++ - return false + + m.Print() + // 断言返回值对遍历控制 + gtest.Case(t, func() { + i := 0 + j := 0 + m.Iterator(func(k interface{}, v interface{}) bool { + i++ + return true + }) + m.Iterator(func(k interface{}, v interface{}) bool { + j++ + return false + }) + gtest.Assert(i, keyLen) + gtest.Assert(j, 1) }) - gtest.Assert(i, 2) - gtest.Assert(j, 1) + + gtest.Case(t, func() { + i := 0 + j := 0 + m.IteratorDesc(func(k interface{}, v interface{}) bool { + i++ + return true + }) + m.IteratorDesc(func(k interface{}, v interface{}) bool { + j++ + return false + }) + gtest.Assert(i, keyLen) + gtest.Assert(j, 1) + }) + } func Test_RedBlackTree_Clone(t *testing.T) { @@ -103,4 +178,78 @@ func Test_RedBlackTree_Clone(t *testing.T) { m_clone.Remove("key1") //修改clone map,原 map 不影响 gtest.AssertIN("key1", m.Keys()) -} \ No newline at end of file +} + +func Test_RedBlackTree_LRNode(t *testing.T) { + expect := map[interface{}]interface{}{"key4": "val4", "key1": "val1", "key2": "val2", "key3": "val3"} + //safe + gtest.Case(t, func() { + m := gtree.NewRedBlackTreeFrom(gutil.ComparatorString, expect) + gtest.Assert(m.Left().Key, "key1") + gtest.Assert(m.Right().Key, "key4") + }) + //unsafe + gtest.Case(t, func() { + m := gtree.NewRedBlackTreeFrom(gutil.ComparatorString, expect, true) + gtest.Assert(m.Left().Key, "key1") + gtest.Assert(m.Right().Key, "key4") + }) +} + +func Test_RedBlackTree_CeilingFloor(t *testing.T) { + expect := map[interface{}]interface{}{ + 20: "val20", + 6: "val6", + 10: "val10", + 12: "val12", + 1: "val1", + 15: "val15", + 19: "val19", + 8: "val8", + 4: "val4"} + //found and eq + gtest.Case(t, func() { + m := gtree.NewRedBlackTreeFrom(gutil.ComparatorInt, expect) + c, cf := m.Ceiling(8) + gtest.Assert(cf, true) + gtest.Assert(c.Value, "val8") + f, ff := m.Floor(20) + gtest.Assert(ff, true) + gtest.Assert(f.Value, "val20") + }) + //found and neq + gtest.Case(t, func() { + m := gtree.NewRedBlackTreeFrom(gutil.ComparatorInt, expect) + c, cf := m.Ceiling(9) + gtest.Assert(cf, true) + gtest.Assert(c.Value, "val10") + f, ff := m.Floor(5) + gtest.Assert(ff, true) + gtest.Assert(f.Value, "val4") + }) + //nofound + gtest.Case(t, func() { + m := gtree.NewRedBlackTreeFrom(gutil.ComparatorInt, expect) + c, cf := m.Ceiling(21) + gtest.Assert(cf, false) + gtest.Assert(c, nil) + f, ff := m.Floor(-1) + gtest.Assert(ff, false) + gtest.Assert(f, nil) + }) +} + +func Test_RedBlackTree_Remove(t *testing.T) { + m := gtree.NewRedBlackTree(gutil.ComparatorInt) + for i := 1; i <= 100; i++ { + m.Set(i, fmt.Sprintf("val%d", i)) + } + expect := m.Map() + gtest.Case(t, func() { + for k, v := range expect { + m1 := m.Clone() + gtest.Assert(m1.Remove(k), v) + gtest.Assert(m1.Remove(k), nil) + } + }) +} From cd30efaaa1fa7338cc51f78bd38a687e4d8d397b Mon Sep 17 00:00:00 2001 From: "zcool321@sina.com" <1234qwer> Date: Sat, 15 Jun 2019 22:11:08 +0800 Subject: [PATCH 07/23] update gcmd test --- g/os/gcmd/gcmd_z_unit_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/g/os/gcmd/gcmd_z_unit_test.go b/g/os/gcmd/gcmd_z_unit_test.go index dda04293e..be548c46a 100644 --- a/g/os/gcmd/gcmd_z_unit_test.go +++ b/g/os/gcmd/gcmd_z_unit_test.go @@ -40,10 +40,13 @@ func Test_Handle(t *testing.T) { os.Args = []string{"gf", "gf"} doInit() gtest.Case(t, func() { + num := 1 BindHandle("gf", func() { - print("gf test") + num += 1 }) RunHandle("gf") + gtest.AssertEQ(num, 2) AutoRun() + gtest.AssertEQ(num, 3) }) } From cb4e36f59190ae4e09da9ec2b07f51c626e53d75 Mon Sep 17 00:00:00 2001 From: "zcool321@sina.com" <1234qwer> Date: Sat, 15 Jun 2019 22:19:37 +0800 Subject: [PATCH 08/23] update rwmutex test2 --- g/internal/rwmutex/rwmutex_z_unit_test.go | 134 +++++++++++++++++++--- 1 file changed, 117 insertions(+), 17 deletions(-) diff --git a/g/internal/rwmutex/rwmutex_z_unit_test.go b/g/internal/rwmutex/rwmutex_z_unit_test.go index d6ada7ada..2c5cccbc1 100644 --- a/g/internal/rwmutex/rwmutex_z_unit_test.go +++ b/g/internal/rwmutex/rwmutex_z_unit_test.go @@ -7,32 +7,132 @@ package rwmutex_test import ( + "github.com/gogf/gf/g/container/garray" "github.com/gogf/gf/g/internal/rwmutex" "github.com/gogf/gf/g/test/gtest" "testing" + "time" ) -func TestRwmutex(t *testing.T) { +func TestRwmutexIsSafe(t *testing.T) { gtest.Case(t, func() { lock := rwmutex.New() - lock.Lock() - lock.Unlock() - lock.RLock() - lock.RUnlock() gtest.Assert(lock.IsSafe(), true) - safeLock1 := rwmutex.New(false) - safeLock1.Lock() - safeLock1.Unlock() - safeLock1.RLock() - safeLock1.RUnlock() - gtest.Assert(safeLock1.IsSafe(), true) + lock = rwmutex.New(false) + gtest.Assert(lock.IsSafe(), true) - unsafeLock1 := rwmutex.New(true) - unsafeLock1.Lock() - unsafeLock1.Unlock() - unsafeLock1.RLock() - unsafeLock1.RUnlock() - gtest.Assert(unsafeLock1.IsSafe(), false) + lock = rwmutex.New(false, false) + gtest.Assert(lock.IsSafe(), true) + + lock = rwmutex.New(true, false) + gtest.Assert(lock.IsSafe(), false) + + lock = rwmutex.New(true, true) + gtest.Assert(lock.IsSafe(), false) + + lock = rwmutex.New(true) + gtest.Assert(lock.IsSafe(), false) + }) +} + +func TestSafeRwmutex(t *testing.T) { + gtest.Case(t, func() { + safeLock := rwmutex.New() + array := garray.New() + + go func() { + safeLock.Lock() + array.Append(1) + time.Sleep(100 * time.Millisecond) + array.Append(1) + safeLock.Unlock() + }() + go func() { + time.Sleep(10 * time.Millisecond) + safeLock.Lock() + array.Append(1) + time.Sleep(200 * time.Millisecond) + array.Append(1) + safeLock.Unlock() + }() + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(80 * time.Millisecond) + gtest.Assert(array.Len(), 3) + time.Sleep(100 * time.Millisecond) + gtest.Assert(array.Len(), 3) + time.Sleep(100 * time.Millisecond) + gtest.Assert(array.Len(), 4) + }) +} + +func TestSafeReaderRwmutex(t *testing.T) { + gtest.Case(t, func() { + safeLock := rwmutex.New() + array := garray.New() + + go func() { + safeLock.RLock() + array.Append(1) + time.Sleep(100 * time.Millisecond) + array.Append(1) + safeLock.RUnlock() + }() + go func() { + time.Sleep(10 * time.Millisecond) + safeLock.RLock() + array.Append(1) + time.Sleep(200 * time.Millisecond) + array.Append(1) + time.Sleep(100 * time.Millisecond) + array.Append(1) + safeLock.RUnlock() + }() + go func() { + time.Sleep(50 * time.Millisecond) + safeLock.Lock() + array.Append(1) + safeLock.Unlock() + }() + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 2) + time.Sleep(100 * time.Millisecond) + gtest.Assert(array.Len(), 3) + time.Sleep(100 * time.Millisecond) + gtest.Assert(array.Len(), 4) + time.Sleep(100 * time.Millisecond) + gtest.Assert(array.Len(), 6) + }) +} + +func TestUnsafeRwmutex(t *testing.T) { + gtest.Case(t, func() { + unsafeLock := rwmutex.New(true) + array := garray.New() + + go func() { + unsafeLock.Lock() + array.Append(1) + time.Sleep(100 * time.Millisecond) + array.Append(1) + unsafeLock.Unlock() + }() + go func() { + time.Sleep(10 * time.Millisecond) + unsafeLock.Lock() + array.Append(1) + time.Sleep(200 * time.Millisecond) + array.Append(1) + unsafeLock.Unlock() + }() + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 2) + time.Sleep(100 * time.Millisecond) + gtest.Assert(array.Len(), 3) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 3) + time.Sleep(100 * time.Millisecond) + gtest.Assert(array.Len(), 4) }) } From 54392941f333f6444c5aeb9840b46229294aedc3 Mon Sep 17 00:00:00 2001 From: "zcool321@sina.com" <1234qwer> Date: Sat, 15 Jun 2019 23:45:58 +0800 Subject: [PATCH 09/23] add gfpool normal test --- g/os/gfpool/gfpool_z_unit_test.go | 80 +++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 g/os/gfpool/gfpool_z_unit_test.go diff --git a/g/os/gfpool/gfpool_z_unit_test.go b/g/os/gfpool/gfpool_z_unit_test.go new file mode 100644 index 000000000..ee667b699 --- /dev/null +++ b/g/os/gfpool/gfpool_z_unit_test.go @@ -0,0 +1,80 @@ +package gfpool_test + +import ( + "fmt" + "github.com/gogf/gf/g/os/gfile" + "github.com/gogf/gf/g/os/gfpool" + "github.com/gogf/gf/g/test/gtest" + "os" + "testing" + "time" +) + +func TestOpen(t *testing.T) { + testFile := start() + + gtest.Case(t, func() { + f, _ := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666) + f.Close() + + f2, _ := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666) + gtest.AssertEQ(f, f2) + f2.Close() + + // Deprecated test + f3, _ := gfpool.OpenFile(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666) + gtest.AssertEQ(f, f3) + f3.Close() + + }) + + stop() +} + +func TestOpenErr(t *testing.T) { + testFile := "errorPath" + + gtest.Case(t, func() { + _, err := gfpool.Open(testFile, os.O_RDWR, 0666) + gtest.AssertNE(err, nil) + }) +} + +func TestOpenExipre(t *testing.T) { + testFile := start() + + gtest.Case(t, func() { + f, _ := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666, 100) + f.Close() + + time.Sleep(150 * time.Millisecond) + f2, _ := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666, 100) + gtest.AssertNE(f, f2) + f2.Close() + + // Deprecated test + f3, _ := gfpool.OpenFile(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666, 100) + gtest.AssertEQ(f2, f3) + f3.Close() + }) + + stop() +} + +func start() string { + testFile := os.TempDir() + string(os.PathSeparator) + "testGfpool.txt" + fmt.Println(testFile) + if gfile.Exists(testFile) { + gfile.Remove(testFile) + } + content := "123" + gfile.PutContents(testFile, content) + return testFile +} + +func stop() { + testFile := os.TempDir() + string(os.PathSeparator) + "testGfpool.txt" + if gfile.Exists(testFile) { + gfile.Remove(testFile) + } +} From 4f007fdd442007b8d4d9e70113f8569fd64542eb Mon Sep 17 00:00:00 2001 From: "zcool321@sina.com" <1234qwer> Date: Sun, 16 Jun 2019 00:04:46 +0800 Subject: [PATCH 10/23] update gfpool test --- g/os/gfpool/gfpool_z_unit_test.go | 43 ++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/g/os/gfpool/gfpool_z_unit_test.go b/g/os/gfpool/gfpool_z_unit_test.go index ee667b699..ab0506110 100644 --- a/g/os/gfpool/gfpool_z_unit_test.go +++ b/g/os/gfpool/gfpool_z_unit_test.go @@ -1,7 +1,6 @@ package gfpool_test import ( - "fmt" "github.com/gogf/gf/g/os/gfile" "github.com/gogf/gf/g/os/gfpool" "github.com/gogf/gf/g/test/gtest" @@ -10,8 +9,12 @@ import ( "time" ) +var ( + testErrFile = "errorPath" +) + func TestOpen(t *testing.T) { - testFile := start() + testFile := start("TestOpen.txt") gtest.Case(t, func() { f, _ := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666) @@ -28,20 +31,19 @@ func TestOpen(t *testing.T) { }) - stop() + stop(testFile) } func TestOpenErr(t *testing.T) { - testFile := "errorPath" gtest.Case(t, func() { - _, err := gfpool.Open(testFile, os.O_RDWR, 0666) + _, err := gfpool.Open(testErrFile, os.O_RDWR, 0666) gtest.AssertNE(err, nil) }) } func TestOpenExipre(t *testing.T) { - testFile := start() + testFile := start("TestOpenExipre.txt") gtest.Case(t, func() { f, _ := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666, 100) @@ -58,12 +60,30 @@ func TestOpenExipre(t *testing.T) { f3.Close() }) - stop() + stop(testFile) } -func start() string { - testFile := os.TempDir() + string(os.PathSeparator) + "testGfpool.txt" - fmt.Println(testFile) +func TestNewPool(t *testing.T) { + testFile := start("TestNewPool.txt") + + gtest.Case(t, func() { + f, _ := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666) + f.Close() + + pool := gfpool.New(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666) + f2, _ := pool.File() + // pool not equal + gtest.AssertNE(f, f2) + f2.Close() + + pool.Close() + }) + + stop(testFile) +} + +func start(name string) string { + testFile := os.TempDir() + string(os.PathSeparator) + name if gfile.Exists(testFile) { gfile.Remove(testFile) } @@ -72,8 +92,7 @@ func start() string { return testFile } -func stop() { - testFile := os.TempDir() + string(os.PathSeparator) + "testGfpool.txt" +func stop(testFile string) { if gfile.Exists(testFile) { gfile.Remove(testFile) } From 68d8e25bc43b678e2e48023b88a42bdc59862880 Mon Sep 17 00:00:00 2001 From: "zcool321@sina.com" <1234qwer> Date: Sun, 16 Jun 2019 00:07:06 +0800 Subject: [PATCH 11/23] update gfpool test2 --- g/os/gfpool/gfpool_z_unit_test.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/g/os/gfpool/gfpool_z_unit_test.go b/g/os/gfpool/gfpool_z_unit_test.go index ab0506110..79a200321 100644 --- a/g/os/gfpool/gfpool_z_unit_test.go +++ b/g/os/gfpool/gfpool_z_unit_test.go @@ -9,10 +9,6 @@ import ( "time" ) -var ( - testErrFile = "errorPath" -) - func TestOpen(t *testing.T) { testFile := start("TestOpen.txt") @@ -35,8 +31,8 @@ func TestOpen(t *testing.T) { } func TestOpenErr(t *testing.T) { - gtest.Case(t, func() { + testErrFile := "errorPath" _, err := gfpool.Open(testErrFile, os.O_RDWR, 0666) gtest.AssertNE(err, nil) }) From 4ffe4f22623a6819bfd866dc5ab4955b5f70c8f8 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 16 Jun 2019 18:54:48 +0800 Subject: [PATCH 12/23] add returned error for ghttp.ClientResponse.Close --- g/net/ghttp/ghttp_client_response.go | 5 ++--- geg/other/test.go | 13 ++++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/g/net/ghttp/ghttp_client_response.go b/g/net/ghttp/ghttp_client_response.go index c9dd587d4..e2bf13e74 100644 --- a/g/net/ghttp/ghttp_client_response.go +++ b/g/net/ghttp/ghttp_client_response.go @@ -3,7 +3,6 @@ // 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. -// HTTP客户端请求返回. package ghttp @@ -48,7 +47,7 @@ func (r *ClientResponse) ReadAllString() string { } // 关闭返回的HTTP链接 -func (r *ClientResponse) Close() { +func (r *ClientResponse) Close() error { r.Response.Close = true - r.Body.Close() + return r.Body.Close() } \ No newline at end of file diff --git a/geg/other/test.go b/geg/other/test.go index 49c122b37..dae805c09 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -6,9 +6,12 @@ import ( ) func main() { - q1 := gqueue.New(2) - q1.Push(1) - q1.Push(2) - gtest.Assert(q1.Size(),2) + max := 100 + for i := 0; i < 10; i++ { + q := gqueue.New(max) + for i := 1; i < max; i++ { + q.Push(i) + } + gtest.Assert(q.Pop(), 1) + } } - From 0eb028f0b5fe335e0f9c3fcb0e0d768a5646e2d4 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 16 Jun 2019 20:25:08 +0800 Subject: [PATCH 13/23] README updates --- README.MD | 2 +- README_ZH.MD | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.MD b/README.MD index f98010c13..f363f0e54 100644 --- a/README.MD +++ b/README.MD @@ -2,7 +2,7 @@
-[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf) +[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf/g#pkg-subdirectories) [![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf) [![Go Report](https://goreportcard.com/badge/github.com/gogf/gf)](https://goreportcard.com/report/github.com/gogf/gf) [![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master) diff --git a/README_ZH.MD b/README_ZH.MD index dffab1c07..3a6a8ea45 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -2,7 +2,7 @@
-[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf) +[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf/g#pkg-subdirectories) [![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf) [![Go Report](https://goreportcard.com/badge/github.com/gogf/gf)](https://goreportcard.com/report/github.com/gogf/gf) [![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master) From c342310389fd70ca928787c4619e683bcd3de2c2 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 16 Jun 2019 20:38:05 +0800 Subject: [PATCH 14/23] add example for garray/gchan/glist --- g/container/gchan/gchan_example_test.go | 29 ++++++++++++++++++++ g/container/glist/glist_example_test.go | 36 +++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 g/container/gchan/gchan_example_test.go create mode 100644 g/container/glist/glist_example_test.go diff --git a/g/container/gchan/gchan_example_test.go b/g/container/gchan/gchan_example_test.go new file mode 100644 index 000000000..acb1cbff9 --- /dev/null +++ b/g/container/gchan/gchan_example_test.go @@ -0,0 +1,29 @@ +// Copyright 2018 gf Author(https://github.com/gogf/gf). 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. + +package gchan_test + +import ( + "fmt" + "github.com/gogf/gf/g/container/gchan" +) + +func Example_basic() { + n := 10 + c := gchan.New(n) + for i := 0; i < n; i++ { + c.Push(i) + } + fmt.Println(c.Len(), c.Cap()) + for i := 0; i < n; i++ { + fmt.Print(c.Pop()) + } + c.Close() + + // Output: + //10 10 + //0123456789 +} diff --git a/g/container/glist/glist_example_test.go b/g/container/glist/glist_example_test.go new file mode 100644 index 000000000..959b8f5da --- /dev/null +++ b/g/container/glist/glist_example_test.go @@ -0,0 +1,36 @@ +// Copyright 2018 gf Author(https://github.com/gogf/gf). 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. + +package glist_test + +import ( + "fmt" + "github.com/gogf/gf/g/container/glist" +) + +func Example_basic() { + n := 10 + l := glist.New() + for i := 0; i < n; i++ { + l.PushBack(i) + } + fmt.Println(l.Len()) + fmt.Println(l.FrontAll()) + fmt.Println(l.BackAll()) + for i := 0; i < n; i++ { + fmt.Print(l.PopFront()) + } + l.Clear() + fmt.Println() + fmt.Println(l.Len()) + + // Output: + //10 + //[0 1 2 3 4 5 6 7 8 9] + //[9 8 7 6 5 4 3 2 1 0] + //0123456789 + //0 +} From 866482a8e8512857f705c9550e1e09eb57d63d86 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 16 Jun 2019 21:52:41 +0800 Subject: [PATCH 15/23] fix issue in gqueue.Close --- g/container/gqueue/gqueue.go | 15 ++++++++++++--- geg/other/test.go | 12 ++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/g/container/gqueue/gqueue.go b/g/container/gqueue/gqueue.go index eb4b2a340..d8ffd2420 100644 --- a/g/container/gqueue/gqueue.go +++ b/g/container/gqueue/gqueue.go @@ -105,9 +105,18 @@ func (q *Queue) Pop() interface{} { // Notice: It would notify all goroutines return immediately, // which are being blocked reading using Pop method. func (q *Queue) Close() { - close(q.C) - close(q.events) - close(q.closed) + if q.C != nil { + close(q.C) + q.C = nil + } + if q.events != nil { + close(q.events) + q.events = nil + } + if q.closed != nil { + close(q.closed) + q.closed = nil + } } // Len returns the length of the queue. diff --git a/geg/other/test.go b/geg/other/test.go index dae805c09..40c961e28 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -7,11 +7,11 @@ import ( func main() { max := 100 - for i := 0; i < 10; i++ { - q := gqueue.New(max) - for i := 1; i < max; i++ { - q.Push(i) - } - gtest.Assert(q.Pop(), 1) + q := gqueue.New(max) + for i := 1; i < max; i++ { + q.Push(i) } + q.Close() + gtest.Assert(q.Len(), 1) + } From 11972ef96c55c5fb0774b78c64f1794c4497c36b Mon Sep 17 00:00:00 2001 From: John Date: Sun, 16 Jun 2019 22:19:59 +0800 Subject: [PATCH 16/23] fix issue in gqueue.Close --- g/container/gqueue/gqueue.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/g/container/gqueue/gqueue.go b/g/container/gqueue/gqueue.go index d8ffd2420..1b3814a59 100644 --- a/g/container/gqueue/gqueue.go +++ b/g/container/gqueue/gqueue.go @@ -107,15 +107,12 @@ func (q *Queue) Pop() interface{} { func (q *Queue) Close() { if q.C != nil { close(q.C) - q.C = nil } if q.events != nil { close(q.events) - q.events = nil } if q.closed != nil { close(q.closed) - q.closed = nil } } From 563a1408ea1768e009238d492a300f979c6c7ae1 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 16 Jun 2019 23:00:00 +0800 Subject: [PATCH 17/23] fix issue in gqueue.Close --- g/container/gqueue/gqueue.go | 22 ++++++-- g/container/gqueue/gqueue_bench_test.go | 71 ++++++++++++------------- 2 files changed, 53 insertions(+), 40 deletions(-) diff --git a/g/container/gqueue/gqueue.go b/g/container/gqueue/gqueue.go index 1b3814a59..8d61b108e 100644 --- a/g/container/gqueue/gqueue.go +++ b/g/container/gqueue/gqueue.go @@ -21,6 +21,7 @@ package gqueue import ( "github.com/gogf/gf/g/container/glist" "math" + "strings" ) type Queue struct { @@ -58,6 +59,17 @@ func New(limit...int) *Queue { // startAsyncLoop starts an asynchronous goroutine, // which handles the data synchronization from list to channel . func (q *Queue) startAsyncLoop() { + defer func() { + if err := recover(); err != nil { + if e, ok := err.(error); ok { + // If any error occurs by writing to closed q.C, ignore it. + if strings.Contains(e.Error(), "closed channel") { + return + } + } + panic(err) + } + }() for { select { case <- q.closed: @@ -74,6 +86,8 @@ func (q *Queue) startAsyncLoop() { } } for _, v := range array { + // When q.C closes, it will panic here, especially q.C is being blocked for writing. + // It will be caught by recover and be ignored, if any error occurs here. q.C <- v } } else { @@ -105,14 +119,14 @@ func (q *Queue) Pop() interface{} { // Notice: It would notify all goroutines return immediately, // which are being blocked reading using Pop method. func (q *Queue) Close() { - if q.C != nil { - close(q.C) + if q.closed != nil { + close(q.closed) } if q.events != nil { close(q.events) } - if q.closed != nil { - close(q.closed) + if q.C != nil { + close(q.C) } } diff --git a/g/container/gqueue/gqueue_bench_test.go b/g/container/gqueue/gqueue_bench_test.go index 403ff22af..789933f15 100644 --- a/g/container/gqueue/gqueue_bench_test.go +++ b/g/container/gqueue/gqueue_bench_test.go @@ -1,50 +1,49 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). 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" "github.com/gogf/gf/g/container/gqueue" + "github.com/gogf/gf/g/test/gtest" + "testing" ) -var bn = 20000000 -var length = 1000000 -var qstatic = gqueue.New(length) -var qdynamic = gqueue.New() -var cany = make(chan interface{}, length) - -func Benchmark_Gqueue_StaticPushAndPop(b *testing.B) { - b.N = bn - for i := 0; i < b.N; i++ { - qstatic.Push(i) - qstatic.Pop() +func TestQueue_Len(t *testing.T) { + q1 := gqueue.New(300) + for i := 0; i < 200; i++ { + q1.Push(i) } + gtest.Assert(q1.Len(), 200) } -func Benchmark_Gqueue_DynamicPush(b *testing.B) { - b.N = bn - for i := 0; i < b.N; i++ { - qdynamic.Push(i) +func TestQueue_Pop(t *testing.T) { + q1 := gqueue.New() + + q1.Push(1) + q1.Push(2) + i1 := q1.Pop() + gtest.Assert(i1, 1) + q1.Close() + i1 = q1.Pop() + gtest.Assert(i1, 2) + + maxs := 12 + q2 := gqueue.New(maxs) + for i := 0; i < maxs; i++ { + q2.Push(i) } + + i3 := q2.Pop() + gtest.Assert(i3, 0) + } -func Benchmark_Gqueue_DynamicPop(b *testing.B) { - b.N = bn - for i := 0; i < b.N; i++ { - qdynamic.Pop() - } -} -func Benchmark_Channel_PushAndPop(b *testing.B) { - b.N = bn - for i := 0; i < b.N; i++ { - cany <- i - <- cany - } +func TestQueue_Close(t *testing.T) { + q1 := gqueue.New() + q1.Push(1) + q1.Push(2) + gtest.Assert(q1.Len(), 2) + + q1.Close() + gtest.Assert(q1.Len(), 2) + } From 474d1669d9eba768050fcea96a02c6295ab3044b Mon Sep 17 00:00:00 2001 From: John Date: Sun, 16 Jun 2019 23:00:47 +0800 Subject: [PATCH 18/23] fix issue in gqueue.Close --- g/container/gqueue/gqueue_bench_test.go | 73 +++++++++++++------------ 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/g/container/gqueue/gqueue_bench_test.go b/g/container/gqueue/gqueue_bench_test.go index 789933f15..403ff22af 100644 --- a/g/container/gqueue/gqueue_bench_test.go +++ b/g/container/gqueue/gqueue_bench_test.go @@ -1,49 +1,50 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 ( - "github.com/gogf/gf/g/container/gqueue" - "github.com/gogf/gf/g/test/gtest" "testing" + "github.com/gogf/gf/g/container/gqueue" ) -func TestQueue_Len(t *testing.T) { - q1 := gqueue.New(300) - for i := 0; i < 200; i++ { - q1.Push(i) +var bn = 20000000 +var length = 1000000 +var qstatic = gqueue.New(length) +var qdynamic = gqueue.New() +var cany = make(chan interface{}, length) + +func Benchmark_Gqueue_StaticPushAndPop(b *testing.B) { + b.N = bn + for i := 0; i < b.N; i++ { + qstatic.Push(i) + qstatic.Pop() } - gtest.Assert(q1.Len(), 200) } -func TestQueue_Pop(t *testing.T) { - q1 := gqueue.New() - - q1.Push(1) - q1.Push(2) - i1 := q1.Pop() - gtest.Assert(i1, 1) - q1.Close() - i1 = q1.Pop() - gtest.Assert(i1, 2) - - maxs := 12 - q2 := gqueue.New(maxs) - for i := 0; i < maxs; i++ { - q2.Push(i) +func Benchmark_Gqueue_DynamicPush(b *testing.B) { + b.N = bn + for i := 0; i < b.N; i++ { + qdynamic.Push(i) } - - i3 := q2.Pop() - gtest.Assert(i3, 0) - } - -func TestQueue_Close(t *testing.T) { - q1 := gqueue.New() - q1.Push(1) - q1.Push(2) - gtest.Assert(q1.Len(), 2) - - q1.Close() - gtest.Assert(q1.Len(), 2) - +func Benchmark_Gqueue_DynamicPop(b *testing.B) { + b.N = bn + for i := 0; i < b.N; i++ { + qdynamic.Pop() + } +} + +func Benchmark_Channel_PushAndPop(b *testing.B) { + b.N = bn + for i := 0; i < b.N; i++ { + cany <- i + <- cany + } } From 98cae011a5a103d103f9ed2d7fbc03b14b9a5cf9 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 16 Jun 2019 23:10:50 +0800 Subject: [PATCH 19/23] fix issue in gqueue.Close --- g/container/gqueue/gqueue.go | 58 +++++++++++++----------------------- 1 file changed, 21 insertions(+), 37 deletions(-) diff --git a/g/container/gqueue/gqueue.go b/g/container/gqueue/gqueue.go index 8d61b108e..2aafec72c 100644 --- a/g/container/gqueue/gqueue.go +++ b/g/container/gqueue/gqueue.go @@ -20,15 +20,15 @@ package gqueue import ( "github.com/gogf/gf/g/container/glist" + "github.com/gogf/gf/g/container/gtype" "math" - "strings" ) type Queue struct { limit int // Limit for queue size. list *glist.List // Underlying list structure for data maintaining. + closed *gtype.Bool // Whether queue is closed. events chan struct{} // Events for data writing. - closed chan struct{} // Events for queue closing. C chan interface{} // Underlying channel for data reading. } @@ -42,7 +42,7 @@ const ( // When is given, the queue will be static and high performance which is comparable with stdlib channel. func New(limit...int) *Queue { q := &Queue { - closed : make(chan struct{}, 0), + closed : gtype.NewBool(), } if len(limit) > 0 { q.limit = limit[0] @@ -60,40 +60,26 @@ func New(limit...int) *Queue { // which handles the data synchronization from list to channel . func (q *Queue) startAsyncLoop() { defer func() { - if err := recover(); err != nil { - if e, ok := err.(error); ok { - // If any error occurs by writing to closed q.C, ignore it. - if strings.Contains(e.Error(), "closed channel") { - return - } - } - panic(err) + if q.closed.Val() { + _ = recover() } }() - for { - select { - case <- q.closed: - return - case <- q.events: - for { - if length := q.list.Len(); length > 0 { - array := make([]interface{}, length) - for i := 0; i < length; i++ { - if e := q.list.Front(); e != nil { - array[i] = q.list.Remove(e) - } else { - break - } - } - for _, v := range array { - // When q.C closes, it will panic here, especially q.C is being blocked for writing. - // It will be caught by recover and be ignored, if any error occurs here. - q.C <- v - } - } else { - break - } + for !q.closed.Val() { + <- q.events + if length := q.list.Len(); length > 0 { + array := make([]interface{}, length) + for i := 0; i < length; i++ { + if e := q.list.Front(); e != nil { + array[i] = q.list.Remove(e) + } else { + break } + } + for _, v := range array { + // When q.C closes, it will panic here, especially q.C is being blocked for writing. + // It will be caught by recover and be ignored, if any error occurs here. + q.C <- v + } } } } @@ -119,9 +105,7 @@ func (q *Queue) Pop() interface{} { // Notice: It would notify all goroutines return immediately, // which are being blocked reading using Pop method. func (q *Queue) Close() { - if q.closed != nil { - close(q.closed) - } + q.closed.Set(true) if q.events != nil { close(q.events) } From 4713739d1aed674bf1ce25a76354e8f5e2ab3dd3 Mon Sep 17 00:00:00 2001 From: zhangbiao Date: Mon, 17 Jun 2019 17:52:10 +0800 Subject: [PATCH 20/23] update gfpool test:delete err --- g/os/gfpool/gfpool_z_unit_test.go | 48 +++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/g/os/gfpool/gfpool_z_unit_test.go b/g/os/gfpool/gfpool_z_unit_test.go index 79a200321..e916175f3 100644 --- a/g/os/gfpool/gfpool_z_unit_test.go +++ b/g/os/gfpool/gfpool_z_unit_test.go @@ -3,12 +3,14 @@ package gfpool_test import ( "github.com/gogf/gf/g/os/gfile" "github.com/gogf/gf/g/os/gfpool" + "github.com/gogf/gf/g/os/glog" "github.com/gogf/gf/g/test/gtest" "os" "testing" "time" ) +// TestOpen test open file cache func TestOpen(t *testing.T) { testFile := start("TestOpen.txt") @@ -30,16 +32,50 @@ func TestOpen(t *testing.T) { stop(testFile) } +// TestOpenErr test open file error func TestOpenErr(t *testing.T) { gtest.Case(t, func() { testErrFile := "errorPath" _, err := gfpool.Open(testErrFile, os.O_RDWR, 0666) gtest.AssertNE(err, nil) + + // delete file error + testFile := start("TestOpenDeleteErr.txt") + f, _ := gfpool.Open(testFile, os.O_RDWR, 0666) + f.Close() + stop(testFile) + _, err = gfpool.Open(testFile, os.O_RDWR, 0666) + gtest.AssertNE(err, nil) + + // append mode delete file error + testFile = start("TestOpenCreateErr.txt") + f, _ = gfpool.Open(testFile, os.O_CREATE, 0666) + f.Close() + stop(testFile) + _, err = gfpool.Open(testFile, os.O_CREATE, 0666) + gtest.AssertNE(err, nil) + + // append mode delete file error + testFile = start("TestOpenAppendErr.txt") + f, _ = gfpool.Open(testFile, os.O_APPEND, 0666) + f.Close() + stop(testFile) + _, err = gfpool.Open(testFile, os.O_APPEND, 0666) + gtest.AssertNE(err, nil) + + // trunc mode delete file error + testFile = start("TestOpenTruncErr.txt") + f, _ = gfpool.Open(testFile, os.O_TRUNC, 0666) + f.Close() + stop(testFile) + _, err = gfpool.Open(testFile, os.O_TRUNC, 0666) + gtest.AssertNE(err, nil) }) } -func TestOpenExipre(t *testing.T) { - testFile := start("TestOpenExipre.txt") +// TestOpenExpire test open file cache expire +func TestOpenExpire(t *testing.T) { + testFile := start("TestOpenExpire.txt") gtest.Case(t, func() { f, _ := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666, 100) @@ -59,6 +95,7 @@ func TestOpenExipre(t *testing.T) { stop(testFile) } +// TestNewPool test gfpool new function func TestNewPool(t *testing.T) { testFile := start("TestNewPool.txt") @@ -78,6 +115,7 @@ func TestNewPool(t *testing.T) { stop(testFile) } +// test before func start(name string) string { testFile := os.TempDir() + string(os.PathSeparator) + name if gfile.Exists(testFile) { @@ -88,8 +126,12 @@ func start(name string) string { return testFile } +// test after func stop(testFile string) { if gfile.Exists(testFile) { - gfile.Remove(testFile) + err := gfile.Remove(testFile) + if err != nil { + glog.Error(err) + } } } From b3b0ba775c7e11a1c4454f5d8cf6fbf62c8591b4 Mon Sep 17 00:00:00 2001 From: zhangbiao Date: Mon, 17 Jun 2019 18:09:56 +0800 Subject: [PATCH 21/23] update gfpool test:error --- g/os/gfpool/gfpool_z_unit_test.go | 61 +++++++++++++++++++------------ 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/g/os/gfpool/gfpool_z_unit_test.go b/g/os/gfpool/gfpool_z_unit_test.go index e916175f3..0cc827de1 100644 --- a/g/os/gfpool/gfpool_z_unit_test.go +++ b/g/os/gfpool/gfpool_z_unit_test.go @@ -15,15 +15,19 @@ func TestOpen(t *testing.T) { testFile := start("TestOpen.txt") gtest.Case(t, func() { - f, _ := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666) + f, err := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666) + gtest.AssertEQ(gfile.GetContents(f.Name()), "123") + gtest.AssertEQ(err, nil) f.Close() - f2, _ := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666) + f2, err1 := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666) + gtest.AssertEQ(err1, nil) gtest.AssertEQ(f, f2) f2.Close() // Deprecated test - f3, _ := gfpool.OpenFile(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666) + f3, err2 := gfpool.OpenFile(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666) + gtest.AssertEQ(err2, nil) gtest.AssertEQ(f, f3) f3.Close() @@ -41,35 +45,39 @@ func TestOpenErr(t *testing.T) { // delete file error testFile := start("TestOpenDeleteErr.txt") - f, _ := gfpool.Open(testFile, os.O_RDWR, 0666) - f.Close() + pool := gfpool.New(testFile, os.O_RDWR, 0666) + _, err1 := pool.File() + gtest.AssertEQ(err1, nil) stop(testFile) - _, err = gfpool.Open(testFile, os.O_RDWR, 0666) - gtest.AssertNE(err, nil) + _, err1 = pool.File() + gtest.AssertNE(err1, nil) // append mode delete file error testFile = start("TestOpenCreateErr.txt") - f, _ = gfpool.Open(testFile, os.O_CREATE, 0666) - f.Close() + pool = gfpool.New(testFile, os.O_CREATE, 0666) + _, err1 = pool.File() + gtest.AssertEQ(err1, nil) stop(testFile) - _, err = gfpool.Open(testFile, os.O_CREATE, 0666) - gtest.AssertNE(err, nil) + _, err1 = pool.File() + gtest.AssertNE(err1, nil) // append mode delete file error testFile = start("TestOpenAppendErr.txt") - f, _ = gfpool.Open(testFile, os.O_APPEND, 0666) - f.Close() + pool = gfpool.New(testFile, os.O_APPEND, 0666) + _, err1 = pool.File() + gtest.AssertEQ(err1, nil) stop(testFile) - _, err = gfpool.Open(testFile, os.O_APPEND, 0666) - gtest.AssertNE(err, nil) + _, err1 = pool.File() + gtest.AssertNE(err1, nil) // trunc mode delete file error testFile = start("TestOpenTruncErr.txt") - f, _ = gfpool.Open(testFile, os.O_TRUNC, 0666) - f.Close() + pool = gfpool.New(testFile, os.O_TRUNC, 0666) + _, err1 = pool.File() + gtest.AssertEQ(err1, nil) stop(testFile) - _, err = gfpool.Open(testFile, os.O_TRUNC, 0666) - gtest.AssertNE(err, nil) + _, err1 = pool.File() + gtest.AssertNE(err1, nil) }) } @@ -78,16 +86,19 @@ func TestOpenExpire(t *testing.T) { testFile := start("TestOpenExpire.txt") gtest.Case(t, func() { - f, _ := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666, 100) + f, err := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666, 100) + gtest.AssertEQ(err, nil) f.Close() time.Sleep(150 * time.Millisecond) - f2, _ := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666, 100) + f2, err1 := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666, 100) + gtest.AssertEQ(err1, nil) gtest.AssertNE(f, f2) f2.Close() // Deprecated test - f3, _ := gfpool.OpenFile(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666, 100) + f3, err2 := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666, 100) + gtest.AssertEQ(err2, nil) gtest.AssertEQ(f2, f3) f3.Close() }) @@ -100,12 +111,14 @@ func TestNewPool(t *testing.T) { testFile := start("TestNewPool.txt") gtest.Case(t, func() { - f, _ := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666) + f, err := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666) + gtest.AssertEQ(err, nil) f.Close() pool := gfpool.New(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666) - f2, _ := pool.File() + f2, err1 := pool.File() // pool not equal + gtest.AssertEQ(err1, nil) gtest.AssertNE(f, f2) f2.Close() From 80655bce50a6ffbe4f3cb39aab5b38ce4ebb5962 Mon Sep 17 00:00:00 2001 From: zhangbiao Date: Mon, 17 Jun 2019 18:39:27 +0800 Subject: [PATCH 22/23] add gfpool test:delete error --- g/os/gfpool/gfpool_z_unit_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/g/os/gfpool/gfpool_z_unit_test.go b/g/os/gfpool/gfpool_z_unit_test.go index 0cc827de1..71587fadd 100644 --- a/g/os/gfpool/gfpool_z_unit_test.go +++ b/g/os/gfpool/gfpool_z_unit_test.go @@ -16,7 +16,6 @@ func TestOpen(t *testing.T) { gtest.Case(t, func() { f, err := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666) - gtest.AssertEQ(gfile.GetContents(f.Name()), "123") gtest.AssertEQ(err, nil) f.Close() @@ -52,14 +51,14 @@ func TestOpenErr(t *testing.T) { _, err1 = pool.File() gtest.AssertNE(err1, nil) - // append mode delete file error + // append mode delete file error and create again testFile = start("TestOpenCreateErr.txt") pool = gfpool.New(testFile, os.O_CREATE, 0666) _, err1 = pool.File() gtest.AssertEQ(err1, nil) stop(testFile) _, err1 = pool.File() - gtest.AssertNE(err1, nil) + gtest.AssertEQ(err1, nil) // append mode delete file error testFile = start("TestOpenAppendErr.txt") @@ -77,6 +76,7 @@ func TestOpenErr(t *testing.T) { gtest.AssertEQ(err1, nil) stop(testFile) _, err1 = pool.File() + glog.Error(err1) gtest.AssertNE(err1, nil) }) } From f7d1613d62ea67e25eefbce302c9794df96a3423 Mon Sep 17 00:00:00 2001 From: John Date: Mon, 17 Jun 2019 19:54:00 +0800 Subject: [PATCH 23/23] improve gqueue --- g/container/glist/glist.go | 12 ++---- g/container/gqueue/gqueue.go | 56 +++++++++++++++----------- g/container/gqueue/gqueue_unit_test.go | 56 ++++++++++++++++++++++++++ g/test/gtest/gtest_test.go | 18 +++++++++ geg/other/test.go | 18 ++++----- 5 files changed, 119 insertions(+), 41 deletions(-) create mode 100644 g/container/gqueue/gqueue_unit_test.go create mode 100644 g/test/gtest/gtest_test.go diff --git a/g/container/glist/glist.go b/g/container/glist/glist.go index fa0361702..a3ddd5651 100644 --- a/g/container/glist/glist.go +++ b/g/container/glist/glist.go @@ -93,11 +93,9 @@ func (l *List) PopBacks(max int) (values []interface{}) { if max > 0 && max < length { length = max } - tempe := (*Element)(nil) values = make([]interface{}, length) for i := 0; i < length; i++ { - tempe = l.list.Back() - values[i] = l.list.Remove(tempe) + values[i] = l.list.Remove(l.list.Back()) } } l.mu.Unlock() @@ -107,20 +105,18 @@ func (l *List) PopBacks(max int) (values []interface{}) { // PopFronts removes elements from front of // and returns values of the removed elements as slice. func (l *List) PopFronts(max int) (values []interface{}) { - l.mu.RLock() + l.mu.Lock() length := l.list.Len() if length > 0 { if max > 0 && max < length { length = max } - tempe := (*Element)(nil) values = make([]interface{}, length) for i := 0; i < length; i++ { - tempe = l.list.Front() - values[i] = l.list.Remove(tempe) + values[i] = l.list.Remove(l.list.Front()) } } - l.mu.RUnlock() + l.mu.Unlock() return } diff --git a/g/container/gqueue/gqueue.go b/g/container/gqueue/gqueue.go index 2aafec72c..75c1b33c7 100644 --- a/g/container/gqueue/gqueue.go +++ b/g/container/gqueue/gqueue.go @@ -19,9 +19,9 @@ package gqueue import ( - "github.com/gogf/gf/g/container/glist" - "github.com/gogf/gf/g/container/gtype" - "math" + "github.com/gogf/gf/g/container/glist" + "github.com/gogf/gf/g/container/gtype" + "math" ) type Queue struct { @@ -35,6 +35,8 @@ type Queue struct { const ( // Size for queue buffer. gDEFAULT_QUEUE_SIZE = 10000 + // Max batch size per-fetching from list. + gDEFAULT_MAX_BATCH_SIZE = 10 ) // New returns an empty queue object. @@ -66,22 +68,28 @@ func (q *Queue) startAsyncLoop() { }() for !q.closed.Val() { <- q.events - if length := q.list.Len(); length > 0 { - array := make([]interface{}, length) - for i := 0; i < length; i++ { - if e := q.list.Front(); e != nil { - array[i] = q.list.Remove(e) - } else { - break - } - } - for _, v := range array { - // When q.C closes, it will panic here, especially q.C is being blocked for writing. - // It will be caught by recover and be ignored, if any error occurs here. - q.C <- v - } + for !q.closed.Val() { + if length := q.list.Len(); length > 0 { + if length > gDEFAULT_MAX_BATCH_SIZE { + length = gDEFAULT_MAX_BATCH_SIZE + } + for _, v := range q.list.PopFronts(length) { + // When q.C is closed, it will panic here, especially q.C is being blocked for writing. + // If any error occurs here, it will be caught by recover and be ignored. + q.C <- v + } + } else { + break + } + } + // Clear q.events to remain just one event to do the next synchronization check. + for i := 0; i < len(q.events) - 1; i++ { + <- q.events } } + // It should be here to close q.C. + // It's the sender's responsibility to close channel when it should be closed. + close(q.C) } // Push pushes the data into the queue. @@ -91,7 +99,9 @@ func (q *Queue) Push(v interface{}) { q.C <- v } else { q.list.PushBack(v) - q.events <- struct{}{} + if len(q.events) < gDEFAULT_QUEUE_SIZE { + q.events <- struct{}{} + } } } @@ -106,11 +116,11 @@ func (q *Queue) Pop() interface{} { // which are being blocked reading using Pop method. func (q *Queue) Close() { q.closed.Set(true) - if q.events != nil { - close(q.events) - } - if q.C != nil { - close(q.C) + if q.events != nil { + close(q.events) + } + for i := 0; i < gDEFAULT_MAX_BATCH_SIZE; i++ { + q.Pop() } } diff --git a/g/container/gqueue/gqueue_unit_test.go b/g/container/gqueue/gqueue_unit_test.go new file mode 100644 index 000000000..5a06b1c98 --- /dev/null +++ b/g/container/gqueue/gqueue_unit_test.go @@ -0,0 +1,56 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 ( + "github.com/gogf/gf/g/container/gqueue" + "github.com/gogf/gf/g/test/gtest" + "testing" +) + +func TestQueue_Len(t *testing.T) { + max := 100 + for n := 10; n < max; n++ { + q1 := gqueue.New(max) + for i := 0; i < max; i++ { + q1.Push(i) + } + gtest.Assert(q1.Len(), max) + gtest.Assert(q1.Size(), max) + } +} + +func TestQueue_Basic(t *testing.T) { + q := gqueue.New() + for i := 0; i < 100; i++ { + q.Push(i) + } + gtest.Assert(q.Pop(), 0) + gtest.Assert(q.Pop(), 1) +} + + +func TestQueue_Pop(t *testing.T) { + q1 := gqueue.New() + q1.Push(1) + q1.Push(2) + q1.Push(3) + q1.Push(4) + i1 := q1.Pop() + gtest.Assert(i1, 1) + +} + +func TestQueue_Close(t *testing.T) { + q1 := gqueue.New() + q1.Push(1) + q1.Push(2) + gtest.Assert(q1.Len(), 2) + q1.Close() +} \ No newline at end of file diff --git a/g/test/gtest/gtest_test.go b/g/test/gtest/gtest_test.go new file mode 100644 index 000000000..3ba96669f --- /dev/null +++ b/g/test/gtest/gtest_test.go @@ -0,0 +1,18 @@ +// Copyright 2018 gf Author(https://github.com/gogf/gf). 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. + +package gtest_test + +import ( + "github.com/gogf/gf/g/test/gtest" + "testing" +) + +func TestCase(t *testing.T) { + gtest.Case(t, func() { + gtest.Assert(1, 1) + }) +} \ No newline at end of file diff --git a/geg/other/test.go b/geg/other/test.go index 40c961e28..7e31feb29 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,17 +1,15 @@ package main import ( - "github.com/gogf/gf/g/container/gqueue" - "github.com/gogf/gf/g/test/gtest" + "fmt" + "sync" ) func main() { - max := 100 - q := gqueue.New(max) - for i := 1; i < max; i++ { - q.Push(i) - } - q.Close() - gtest.Assert(q.Len(), 1) - + wg := sync.WaitGroup{} + wg.Add(1) + wg.Add(-100) + wg.Add() + wg.Wait() + fmt.Println(1) }