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) 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.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/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 +} diff --git a/g/container/gqueue/gqueue.go b/g/container/gqueue/gqueue.go index eb4b2a340..75c1b33c7 100644 --- a/g/container/gqueue/gqueue.go +++ b/g/container/gqueue/gqueue.go @@ -19,21 +19,24 @@ package gqueue import ( - "github.com/gogf/gf/g/container/glist" - "math" + "github.com/gogf/gf/g/container/glist" + "github.com/gogf/gf/g/container/gtype" + "math" ) 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. } 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. @@ -41,7 +44,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] @@ -58,30 +61,35 @@ func New(limit...int) *Queue { // startAsyncLoop starts an asynchronous goroutine, // which handles the data synchronization from list to channel . func (q *Queue) startAsyncLoop() { - 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 { - q.C <- v - } - } else { - break - } - } + defer func() { + if q.closed.Val() { + _ = recover() + } + }() + for !q.closed.Val() { + <- q.events + 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{}{} + } } } @@ -105,9 +115,13 @@ 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) + q.closed.Set(true) + if q.events != nil { + close(q.events) + } + for i := 0; i < gDEFAULT_MAX_BATCH_SIZE; i++ { + q.Pop() + } } // Len returns the length of the queue. 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/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) + } + }) +} 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) + }) +} 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..ca7f0c368 --- /dev/null +++ b/g/internal/mutex/mutex_z_unit_test.go @@ -0,0 +1,99 @@ +// 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/container/garray" + "github.com/gogf/gf/g/internal/mutex" + "github.com/gogf/gf/g/test/gtest" + "testing" + "time" +) + +func TestMutexIsSafe(t *testing.T) { + gtest.Case(t, func() { + lock := mutex.New() + gtest.Assert(lock.IsSafe(), true) + + lock = mutex.New(false) + gtest.Assert(lock.IsSafe(), true) + + 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) + }) +} 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..2c5cccbc1 --- /dev/null +++ b/g/internal/rwmutex/rwmutex_z_unit_test.go @@ -0,0 +1,138 @@ +// 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/container/garray" + "github.com/gogf/gf/g/internal/rwmutex" + "github.com/gogf/gf/g/test/gtest" + "testing" + "time" +) + +func TestRwmutexIsSafe(t *testing.T) { + gtest.Case(t, func() { + lock := rwmutex.New() + gtest.Assert(lock.IsSafe(), true) + + lock = rwmutex.New(false) + gtest.Assert(lock.IsSafe(), true) + + 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) + }) +} 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/g/os/gcmd/gcmd_z_unit_test.go b/g/os/gcmd/gcmd_z_unit_test.go index bd9e05a84..be548c46a 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,31 @@ 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() { + num := 1 + BindHandle("gf", func() { + num += 1 + }) + RunHandle("gf") + gtest.AssertEQ(num, 2) + AutoRun() + gtest.AssertEQ(num, 3) + }) +} 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..71587fadd --- /dev/null +++ b/g/os/gfpool/gfpool_z_unit_test.go @@ -0,0 +1,150 @@ +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") + + gtest.Case(t, func() { + f, err := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666) + gtest.AssertEQ(err, nil) + f.Close() + + 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, 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() + + }) + + 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") + pool := gfpool.New(testFile, os.O_RDWR, 0666) + _, err1 := pool.File() + gtest.AssertEQ(err1, nil) + stop(testFile) + _, err1 = pool.File() + gtest.AssertNE(err1, nil) + + // 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.AssertEQ(err1, nil) + + // append mode delete file error + testFile = start("TestOpenAppendErr.txt") + pool = gfpool.New(testFile, os.O_APPEND, 0666) + _, err1 = pool.File() + gtest.AssertEQ(err1, nil) + stop(testFile) + _, err1 = pool.File() + gtest.AssertNE(err1, nil) + + // trunc mode delete file error + testFile = start("TestOpenTruncErr.txt") + pool = gfpool.New(testFile, os.O_TRUNC, 0666) + _, err1 = pool.File() + gtest.AssertEQ(err1, nil) + stop(testFile) + _, err1 = pool.File() + glog.Error(err1) + gtest.AssertNE(err1, nil) + }) +} + +// TestOpenExpire test open file cache expire +func TestOpenExpire(t *testing.T) { + testFile := start("TestOpenExpire.txt") + + gtest.Case(t, func() { + 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, 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, 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() + }) + + stop(testFile) +} + +// TestNewPool test gfpool new function +func TestNewPool(t *testing.T) { + testFile := start("TestNewPool.txt") + + gtest.Case(t, func() { + 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, err1 := pool.File() + // pool not equal + gtest.AssertEQ(err1, nil) + gtest.AssertNE(f, f2) + f2.Close() + + pool.Close() + }) + + stop(testFile) +} + +// test before +func start(name string) string { + testFile := os.TempDir() + string(os.PathSeparator) + name + if gfile.Exists(testFile) { + gfile.Remove(testFile) + } + content := "123" + gfile.PutContents(testFile, content) + return testFile +} + +// test after +func stop(testFile string) { + if gfile.Exists(testFile) { + err := gfile.Remove(testFile) + if err != nil { + glog.Error(err) + } + } +} 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 49c122b37..7e31feb29 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,14 +1,15 @@ package main import ( - "github.com/gogf/gf/g/container/gqueue" - "github.com/gogf/gf/g/test/gtest" + "fmt" + "sync" ) func main() { - q1 := gqueue.New(2) - q1.Push(1) - q1.Push(2) - gtest.Assert(q1.Size(),2) + wg := sync.WaitGroup{} + wg.Add(1) + wg.Add(-100) + wg.Add() + wg.Wait() + fmt.Println(1) } -