diff --git a/.travis.yml b/.travis.yml index 037ff1e7d..f522ba548 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: go go: - - "1.10.x" - "1.11.x" - "1.12.x" @@ -11,29 +10,29 @@ branches: - develop env: - - GO111MODULE=on +- GO111MODULE=on services: - - mysql - - redis-server +- mysql +- redis-server addons: hosts: - - local + - local before_install: - - pwd +- pwd install: - - cat /etc/hosts +- cat /etc/hosts script: - - cd g - - GOARCH=386 go test -v ./... - - GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic +- cd g +- GOARCH=386 go test -v ./... +- GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic after_success: - - bash <(curl -s https://codecov.io/bash) +- bash <(curl -s https://codecov.io/bash) diff --git a/README.MD b/README.MD index 7085c7660..b9ace2114 100644 --- a/README.MD +++ b/README.MD @@ -64,13 +64,15 @@ func main() { `GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever. + # Contributors - [aloncn](https://github.com/aloncn) - [chenyang351](https://github.com/chenyang351) - [garfieldkwong](https://gitee.com/garfieldkwong) - [hailaz](https://gitee.com/hailaz) -- [johng](https://gitee.com/johng) +- [johng](https://johng.cn) +- [jroam](https://github.com/jroam) - [pibigstar](https://github.com/pibigstar) - [qq1054000800](https://gitee.com/qq1054000800) - [qq976739120](https://github.com/qq976739120) @@ -86,6 +88,7 @@ func main() { - [flyke-xu](https://gitee.com/flyke-xu) - [hailaz](https://gitee.com/hailaz) +- [ireadx](https://github.com/ireadx) - [mg91](https://gitee.com/mg91) - [pibigstar](https://github.com/pibigstar) - [tiangenglan](https://gitee.com/tiangenglan) @@ -98,4 +101,3 @@ func main() { - diff --git a/README_ZH.MD b/README_ZH.MD index 5473bfce5..36f1878f2 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -30,7 +30,6 @@ go get -u github.com/gogf/gf ``` require github.com/gogf/gf latest ``` -> 如果您是从旧版本`1.x`升级到`1.5.0`那么请参考:[1.x升级到1.5.0](https://goframe.org/upgradeto150) # 限制 ```shell @@ -90,7 +89,8 @@ func main() { - [chenyang351](https://github.com/chenyang351) - [garfieldkwong](https://gitee.com/garfieldkwong) - [hailaz](https://gitee.com/hailaz) -- [johng](https://gitee.com/johng) +- [johng](https://johng.cn) +- [jroam](https://github.com/jroam) - [pibigstar](https://github.com/pibigstar) - [qq1054000800](https://gitee.com/qq1054000800) - [qq976739120](https://github.com/qq976739120) @@ -106,6 +106,7 @@ func main() { - [flyke-xu](https://gitee.com/flyke-xu) - [hailaz](https://gitee.com/hailaz) +- [ireadx](https://github.com/ireadx) - [mg91](https://gitee.com/mg91) - [pibigstar](https://github.com/pibigstar) - [tiangenglan](https://gitee.com/tiangenglan) diff --git a/TODO.MD b/TODO.MD index e456a8c6c..6ac97bca3 100644 --- a/TODO.MD +++ b/TODO.MD @@ -44,6 +44,7 @@ 1. 添加sqlite数据库的单元测试用例; 1. gredis增加cluster支持; 1. gset.Add/Remove/Contains方法增加批量操作支持; +1. gmlock增加手动清理机制:当内存锁不再使用时,由调用端决定是否清理内存锁; # DONE 1. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换; diff --git a/g/container/gtype/bool.go b/g/container/gtype/bool.go index 9ae7db583..e919ce9de 100644 --- a/g/container/gtype/bool.go +++ b/g/container/gtype/bool.go @@ -11,35 +11,39 @@ import ( ) type Bool struct { - val int32 + value int32 } +// NewBool returns a concurrent-safe object for bool type, +// with given initial value . func NewBool(value...bool) *Bool { t := &Bool{} if len(value) > 0 { if value[0] { - t.val = 1 + t.value = 1 } else { - t.val = 0 + t.value = 0 } } return t } +// Clone clones and returns a new concurrent-safe object for bool type. func (t *Bool) Clone() *Bool { return NewBool(t.Val()) } -// 并发安全设置变量值,返回之前的旧值 +// Set atomically stores value into t.valueue and returns the previous t.value value. func (t *Bool) Set(value bool) (old bool) { if value { - old = atomic.SwapInt32(&t.val, 1) == 1 + old = atomic.SwapInt32(&t.value, 1) == 1 } else { - old = atomic.SwapInt32(&t.val, 0) == 1 + old = atomic.SwapInt32(&t.value, 0) == 1 } return } +// Val atomically loads t.valueue. func (t *Bool) Val() bool { - return atomic.LoadInt32(&t.val) > 0 + return atomic.LoadInt32(&t.value) > 0 } diff --git a/g/container/gtype/byte.go b/g/container/gtype/byte.go index 104df712c..e6acc2e2a 100644 --- a/g/container/gtype/byte.go +++ b/g/container/gtype/byte.go @@ -11,29 +11,36 @@ import ( ) type Byte struct { - val int32 + value int32 } +// NewByte returns a concurrent-safe object for byte type, +// with given initial value . func NewByte(value...byte) *Byte { if len(value) > 0 { - return &Byte{val : int32(value[0])} + return &Byte{ + value : int32(value[0]), + } } return &Byte{} } +// Clone clones and returns a new concurrent-safe object for byte type. func (t *Byte) Clone() *Byte { return NewByte(t.Val()) } -// 并发安全设置变量值,返回之前的旧值 +// Set atomically stores value into t.value and returns the previous t.value value. func (t *Byte) Set(value byte) (old byte) { - return byte(atomic.SwapInt32(&t.val, int32(value))) + return byte(atomic.SwapInt32(&t.value, int32(value))) } +// Val atomically loads t.value. func (t *Byte) Val() byte { - return byte(atomic.LoadInt32(&t.val)) + return byte(atomic.LoadInt32(&t.value)) } -func (t *Byte) Add(delta int) byte { - return byte(atomic.AddInt32(&t.val, int32(delta))) +// Add atomically adds delta to t.value and returns the new value. +func (t *Byte) Add(delta int) (new byte) { + return byte(atomic.AddInt32(&t.value, int32(delta))) } diff --git a/g/container/gtype/bytes.go b/g/container/gtype/bytes.go index 125c92632..909b8f41d 100644 --- a/g/container/gtype/bytes.go +++ b/g/container/gtype/bytes.go @@ -9,29 +9,35 @@ package gtype import "sync/atomic" type Bytes struct { - val atomic.Value + value atomic.Value } +// NewBytes returns a concurrent-safe object for []byte type, +// with given initial value . func NewBytes(value...[]byte) *Bytes { t := &Bytes{} if len(value) > 0 { - t.val.Store(value[0]) + t.value.Store(value[0]) } return t } +// Clone clones and returns a new concurrent-safe object for []byte type. func (t *Bytes) Clone() *Bytes { return NewBytes(t.Val()) } +// Set atomically stores value into t.value and returns the previous t.value value. +// Note: The parameter cannot be nil. func (t *Bytes) Set(value []byte) (old []byte) { old = t.Val() - t.val.Store(value) + t.value.Store(value) return } +// Val atomically loads t.value. func (t *Bytes) Val() []byte { - if s := t.val.Load(); s != nil { + if s := t.value.Load(); s != nil { return s.([]byte) } return nil diff --git a/g/container/gtype/float32.go b/g/container/gtype/float32.go index 26a5d50b2..680910770 100644 --- a/g/container/gtype/float32.go +++ b/g/container/gtype/float32.go @@ -7,47 +7,53 @@ package gtype import ( - "sync/atomic" - "github.com/gogf/gf/g/encoding/gbinary" + "math" + "sync/atomic" + "unsafe" ) type Float32 struct { - val uint32 + value uint32 } +// NewFloat32 returns a concurrent-safe object for float32 type, +// with given initial value . func NewFloat32(value...float32) *Float32 { if len(value) > 0 { - return &Float32{ val : float32ToUint32InBits(value[0]) } + return &Float32{ + value : math.Float32bits(value[0]), + } } return &Float32{} } +// Clone clones and returns a new concurrent-safe object for float32 type. func (t *Float32) Clone() *Float32 { return NewFloat32(t.Val()) } +// Set atomically stores value into t.value and returns the previous t.value value. func (t *Float32) Set(value float32) (old float32) { - return uint32ToFloat32InBits(atomic.SwapUint32(&t.val, float32ToUint32InBits(value))) + return math.Float32frombits(atomic.SwapUint32(&t.value, math.Float32bits(value))) } +// Val atomically loads t.value. func (t *Float32) Val() float32 { - return uint32ToFloat32InBits(atomic.LoadUint32(&t.val)) + return math.Float32frombits(atomic.LoadUint32(&t.value)) } -func (t *Float32) Add(delta float32) float32 { - return uint32ToFloat32InBits(atomic.AddUint32(&t.val, float32ToUint32InBits(delta))) -} - -// 通过二进制的方式将float32转换为uint32(都是32bits) -func float32ToUint32InBits(value float32) uint32 { - b := gbinary.Encode(value) - i := gbinary.DecodeToUint32(b) - return i -} - -// 通过二进制的方式将uint32转换为float32(都是32bits) -func uint32ToFloat32InBits(value uint32) float32 { - b := gbinary.Encode(value) - f := gbinary.DecodeToFloat32(b) - return f +// Add atomically adds delta to t.value and returns the new value. +func (t *Float32) Add(delta float32) (new float32) { + for { + old := math.Float32frombits(t.value) + new = old + delta + if atomic.CompareAndSwapUint32( + (*uint32)(unsafe.Pointer(&t.value)), + math.Float32bits(old), + math.Float32bits(new), + ) { + break + } + } + return } \ No newline at end of file diff --git a/g/container/gtype/float64.go b/g/container/gtype/float64.go index 59896e7fe..1ffb6a03f 100644 --- a/g/container/gtype/float64.go +++ b/g/container/gtype/float64.go @@ -7,47 +7,53 @@ package gtype import ( - "sync/atomic" - "github.com/gogf/gf/g/encoding/gbinary" + "math" + "sync/atomic" + "unsafe" ) type Float64 struct { - val uint64 + value uint64 } +// NewFloat64 returns a concurrent-safe object for float64 type, +// with given initial value . func NewFloat64(value...float64) *Float64 { if len(value) > 0 { - return &Float64{ val : float64ToUint64InBits(value[0]) } + return &Float64{ + value : math.Float64bits(value[0]), + } } return &Float64{} } +// Clone clones and returns a new concurrent-safe object for float64 type. func (t *Float64) Clone() *Float64 { return NewFloat64(t.Val()) } +// Set atomically stores value into t.value and returns the previous t.value value. func (t *Float64) Set(value float64) (old float64) { - return uint64ToFloat64InBits(atomic.SwapUint64(&t.val, float64ToUint64InBits(value))) + return math.Float64frombits(atomic.SwapUint64(&t.value, math.Float64bits(value))) } +// Val atomically loads t.value. func (t *Float64) Val() float64 { - return uint64ToFloat64InBits(atomic.LoadUint64(&t.val)) + return math.Float64frombits(atomic.LoadUint64(&t.value)) } -func (t *Float64) Add(delta float64) float64 { - return uint64ToFloat64InBits(atomic.AddUint64(&t.val, float64ToUint64InBits(delta))) +// Add atomically adds delta to t.value and returns the new value. +func (t *Float64) Add(delta float64) (new float64) { + for { + old := math.Float64frombits(t.value) + new = old + delta + if atomic.CompareAndSwapUint64( + (*uint64)(unsafe.Pointer(&t.value)), + math.Float64bits(old), + math.Float64bits(new), + ) { + break + } + } + return } - -// 通过二进制的方式将float64转换为uint64(都是64bits) -func float64ToUint64InBits(value float64) uint64 { - b := gbinary.Encode(value) - i := gbinary.DecodeToUint64(b) - return i -} - -// 通过二进制的方式将uint64转换为float64(都是64bits) -func uint64ToFloat64InBits(value uint64) float64 { - b := gbinary.Encode(value) - f := gbinary.DecodeToFloat64(b) - return f -} \ No newline at end of file diff --git a/g/container/gtype/gtype.go b/g/container/gtype/gtype.go index d23d509bb..a1bcf92f8 100644 --- a/g/container/gtype/gtype.go +++ b/g/container/gtype/gtype.go @@ -4,11 +4,12 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package gtype provides kinds of high performance, concurrent-safe/unsafe basic variable types. +// Package gtype provides kinds of high performance and concurrent-safe basic variable types. package gtype type Type = Interface +// See NewInterface. func New(value ... interface{}) *Type { return NewInterface(value...) } \ No newline at end of file diff --git a/g/container/gtype/int.go b/g/container/gtype/int.go index d8b2f6a4a..6e240a873 100644 --- a/g/container/gtype/int.go +++ b/g/container/gtype/int.go @@ -11,30 +11,36 @@ import ( ) type Int struct { - val int64 + value int64 } +// NewInt returns a concurrent-safe object for int type, +// with given initial value . func NewInt(value...int) *Int { if len(value) > 0 { - return &Int{val:int64(value[0])} + return &Int{ + value : int64(value[0]), + } } return &Int{} } +// Clone clones and returns a new concurrent-safe object for int type. func (t *Int) Clone() *Int { return NewInt(t.Val()) } -// 并发安全设置变量值,返回之前的旧值 +// Set atomically stores value into t.value and returns the previous t.value value. func (t *Int) Set(value int) (old int) { - return int(atomic.SwapInt64(&t.val, int64(value))) + return int(atomic.SwapInt64(&t.value, int64(value))) } +// Val atomically loads t.value. func (t *Int) Val() int { - return int(atomic.LoadInt64(&t.val)) + return int(atomic.LoadInt64(&t.value)) } -// 数值增加delta,并返回**新**的数值 -func (t *Int) Add(delta int) int { - return int(atomic.AddInt64(&t.val, int64(delta))) +// Add atomically adds delta to t.value and returns the new value. +func (t *Int) Add(delta int) (new int) { + return int(atomic.AddInt64(&t.value, int64(delta))) } \ No newline at end of file diff --git a/g/container/gtype/int32.go b/g/container/gtype/int32.go index c98caa817..7802ebb0d 100644 --- a/g/container/gtype/int32.go +++ b/g/container/gtype/int32.go @@ -11,28 +11,36 @@ import ( ) type Int32 struct { - val int32 + value int32 } +// NewInt32 returns a concurrent-safe object for int32 type, +// with given initial value . func NewInt32(value...int32) *Int32 { if len(value) > 0 { - return &Int32{val: value[0]} + return &Int32{ + value : value[0], + } } return &Int32{} } +// Clone clones and returns a new concurrent-safe object for int32 type. func (t *Int32) Clone() *Int32 { return NewInt32(t.Val()) } +// Set atomically stores value into t.value and returns the previous t.value value. func (t *Int32) Set(value int32) (old int32) { - return atomic.SwapInt32(&t.val, value) + return atomic.SwapInt32(&t.value, value) } +// Val atomically loads t.value. func (t *Int32) Val() int32 { - return atomic.LoadInt32(&t.val) + return atomic.LoadInt32(&t.value) } -func (t *Int32) Add(delta int32) int32 { - return atomic.AddInt32(&t.val, delta) +// Add atomically adds delta to t.value and returns the new value. +func (t *Int32) Add(delta int32) (new int32) { + return atomic.AddInt32(&t.value, delta) } \ No newline at end of file diff --git a/g/container/gtype/int64.go b/g/container/gtype/int64.go index 77f4a37d9..9758be4a8 100644 --- a/g/container/gtype/int64.go +++ b/g/container/gtype/int64.go @@ -11,28 +11,36 @@ import ( ) type Int64 struct { - val int64 + value int64 } +// NewInt64 returns a concurrent-safe object for int64 type, +// with given initial value . func NewInt64(value...int64) *Int64 { if len(value) > 0 { - return &Int64{val:value[0]} + return &Int64{ + value : value[0], + } } return &Int64{} } +// Clone clones and returns a new concurrent-safe object for int64 type. func (t *Int64) Clone() *Int64 { return NewInt64(t.Val()) } +// Set atomically stores value into t.value and returns the previous t.value value. func (t *Int64) Set(value int64) (old int64) { - return atomic.SwapInt64(&t.val, value) + return atomic.SwapInt64(&t.value, value) } +// Val atomically loads t.value. func (t *Int64) Val() int64 { - return atomic.LoadInt64(&t.val) + return atomic.LoadInt64(&t.value) } +// Add atomically adds delta to t.value and returns the new value. func (t *Int64) Add(delta int64) int64 { - return atomic.AddInt64(&t.val, delta) + return atomic.AddInt64(&t.value, delta) } \ No newline at end of file diff --git a/g/container/gtype/interface.go b/g/container/gtype/interface.go index fd606656d..3dd1b1507 100644 --- a/g/container/gtype/interface.go +++ b/g/container/gtype/interface.go @@ -10,32 +10,34 @@ import ( "sync/atomic" ) -// 比较通用的并发安全数据类型 type Interface struct { - val atomic.Value + value atomic.Value } +// NewInterface returns a concurrent-safe object for interface{} type, +// with given initial value . func NewInterface(value...interface{}) *Interface { t := &Interface{} if len(value) > 0 && value[0] != nil { - t.val.Store(value[0]) + t.value.Store(value[0]) } return t } +// Clone clones and returns a new concurrent-safe object for interface{} type. func (t *Interface) Clone() *Interface { return NewInterface(t.Val()) } +// Set atomically stores value into t.value and returns the previous t.value value. +// Note: The parameter cannot be nil. func (t *Interface) Set(value interface{}) (old interface{}) { - if value == nil { - return - } old = t.Val() - t.val.Store(value) + t.value.Store(value) return } +// Val atomically loads t.value. func (t *Interface) Val() interface{} { - return t.val.Load() + return t.value.Load() } \ No newline at end of file diff --git a/g/container/gtype/string.go b/g/container/gtype/string.go index 70b8c8959..f524d20bb 100644 --- a/g/container/gtype/string.go +++ b/g/container/gtype/string.go @@ -11,29 +11,34 @@ import ( ) type String struct { - val atomic.Value + value atomic.Value } +// NewString returns a concurrent-safe object for string type, +// with given initial value . func NewString(value...string) *String { t := &String{} if len(value) > 0 { - t.val.Store(value[0]) + t.value.Store(value[0]) } return t } +// Clone clones and returns a new concurrent-safe object for string type. func (t *String) Clone() *String { return NewString(t.Val()) } +// Set atomically stores value into t.value and returns the previous t.value value. func (t *String) Set(value string) (old string) { old = t.Val() - t.val.Store(value) + t.value.Store(value) return } +// Val atomically loads t.value. func (t *String) Val() string { - s := t.val.Load() + s := t.value.Load() if s != nil { return s.(string) } diff --git a/g/container/gtype/uint.go b/g/container/gtype/uint.go index 4c8286df5..c9b5ad3d8 100644 --- a/g/container/gtype/uint.go +++ b/g/container/gtype/uint.go @@ -11,28 +11,36 @@ import ( ) type Uint struct { - val uint64 + value uint64 } +// NewUint returns a concurrent-safe object for uint type, +// with given initial value . func NewUint(value...uint) *Uint { if len(value) > 0 { - return &Uint{val:uint64(value[0])} + return &Uint{ + value : uint64(value[0]), + } } return &Uint{} } +// Clone clones and returns a new concurrent-safe object for uint type. func (t *Uint) Clone() *Uint { return NewUint(t.Val()) } +// Set atomically stores value into t.value and returns the previous t.value value. func (t *Uint) Set(value uint) (old uint) { - return uint(atomic.SwapUint64(&t.val, uint64(value))) + return uint(atomic.SwapUint64(&t.value, uint64(value))) } +// Val atomically loads t.value. func (t *Uint) Val() uint { - return uint(atomic.LoadUint64(&t.val)) + return uint(atomic.LoadUint64(&t.value)) } -func (t *Uint) Add(delta uint) int { - return int(atomic.AddUint64(&t.val, uint64(delta))) +// Add atomically adds delta to t.value and returns the new value. +func (t *Uint) Add(delta uint) (new uint) { + return uint(atomic.AddUint64(&t.value, uint64(delta))) } \ No newline at end of file diff --git a/g/container/gtype/uint32.go b/g/container/gtype/uint32.go index 6596e3467..c3a18c904 100644 --- a/g/container/gtype/uint32.go +++ b/g/container/gtype/uint32.go @@ -11,28 +11,36 @@ import ( ) type Uint32 struct { - val uint32 + value uint32 } +// NewUint32 returns a concurrent-safe object for uint32 type, +// with given initial value . func NewUint32(value...uint32) *Uint32 { if len(value) > 0 { - return &Uint32{val:value[0]} + return &Uint32{ + value : value[0], + } } return &Uint32{} } +// Clone clones and returns a new concurrent-safe object for uint32 type. func (t *Uint32) Clone() *Uint32 { return NewUint32(t.Val()) } +// Set atomically stores value into t.value and returns the previous t.value value. func (t *Uint32) Set(value uint32) (old uint32) { - return atomic.SwapUint32(&t.val, value) + return atomic.SwapUint32(&t.value, value) } +// Val atomically loads t.value. func (t *Uint32) Val() uint32 { - return atomic.LoadUint32(&t.val) + return atomic.LoadUint32(&t.value) } -func (t *Uint32) Add(delta uint32) uint32 { - return atomic.AddUint32(&t.val, delta) +// Add atomically adds delta to t.value and returns the new value. +func (t *Uint32) Add(delta uint32) (new uint32) { + return atomic.AddUint32(&t.value, delta) } \ No newline at end of file diff --git a/g/container/gtype/uint64.go b/g/container/gtype/uint64.go index 79d185312..9e63234e0 100644 --- a/g/container/gtype/uint64.go +++ b/g/container/gtype/uint64.go @@ -11,28 +11,36 @@ import ( ) type Uint64 struct { - val uint64 + value uint64 } +// NewUint64 returns a concurrent-safe object for uint64 type, +// with given initial value . func NewUint64(value...uint64) *Uint64 { if len(value) > 0 { - return &Uint64{val:value[0]} + return &Uint64{ + value : value[0], + } } return &Uint64{} } +// Clone clones and returns a new concurrent-safe object for uint64 type. func (t *Uint64) Clone() *Uint64 { return NewUint64(t.Val()) } +// Set atomically stores value into t.value and returns the previous t.value value. func (t *Uint64) Set(value uint64) (old uint64) { - return atomic.SwapUint64(&t.val, value) + return atomic.SwapUint64(&t.value, value) } +// Val atomically loads t.value. func (t *Uint64) Val() uint64 { - return atomic.LoadUint64(&t.val) + return atomic.LoadUint64(&t.value) } -func (t *Uint64) Add(delta uint64) uint64 { - return atomic.AddUint64(&t.val, delta) +// Add atomically adds delta to t.value and returns the new value. +func (t *Uint64) Add(delta uint64) (new uint64) { + return atomic.AddUint64(&t.value, delta) } \ No newline at end of file diff --git a/g/crypto/gaes/gaes_test.go b/g/crypto/gaes/gaes_test.go new file mode 100644 index 000000000..43f2b04c1 --- /dev/null +++ b/g/crypto/gaes/gaes_test.go @@ -0,0 +1,62 @@ +// 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. + +// go test *.go -bench=".*" + +package gaes_test + +import ( + "testing" + + "github.com/gogf/gf/g/crypto/gaes" + "github.com/gogf/gf/g/test/gtest" +) + +var ( + content = []byte("pibigstar") + // iv 长度必须等于blockSize,只能为16 + iv = []byte("Hello My GoFrame") + key_16 = []byte("1234567891234567") + key_24 = []byte("123456789123456789123456") + key_32 = []byte("12345678912345678912345678912345") +) + +func TestEncrypt(t *testing.T) { + gtest.Case(t, func() { + _, err := gaes.Encrypt(content, key_16) + gtest.Assert(err, nil) + _, err = gaes.Encrypt(content, key_24) + gtest.Assert(err, nil) + _, err = gaes.Encrypt(content, key_32) + gtest.Assert(err, nil) + _, err = gaes.Encrypt(content, key_16, iv) + gtest.Assert(err, nil) + }) +} + +func TestDecrypt(t *testing.T) { + gtest.Case(t, func() { + encrypt, err := gaes.Encrypt(content, key_16) + decrypt, err := gaes.Decrypt(encrypt, key_16) + gtest.Assert(err, nil) + gtest.Assert(string(decrypt), string(content)) + + encrypt, err = gaes.Encrypt(content, key_24) + decrypt, err = gaes.Decrypt(encrypt, key_24) + gtest.Assert(err, nil) + gtest.Assert(string(decrypt), string(content)) + + encrypt, err = gaes.Encrypt(content, key_32) + decrypt, err = gaes.Decrypt(encrypt, key_32) + gtest.Assert(err, nil) + gtest.Assert(string(decrypt), string(content)) + + encrypt, err = gaes.Encrypt(content, key_32, iv) + decrypt, err = gaes.Decrypt(encrypt, key_32, iv) + gtest.Assert(err, nil) + gtest.Assert(string(decrypt), string(content)) + }) +} diff --git a/g/crypto/gcrc32/gcrc32_test.go b/g/crypto/gcrc32/gcrc32_test.go new file mode 100644 index 000000000..32f9a3094 --- /dev/null +++ b/g/crypto/gcrc32/gcrc32_test.go @@ -0,0 +1,27 @@ +// 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. + +// go test *.go -bench=".*" + +package gcrc32_test + +import ( + "testing" + + "github.com/gogf/gf/g/crypto/gcrc32" + "github.com/gogf/gf/g/test/gtest" +) + +func TestEncrypt(t *testing.T) { + gtest.Case(t, func() { + s := "pibigstar" + result := 693191136 + encrypt1 := gcrc32.EncryptString(s) + encrypt2 := gcrc32.EncryptBytes([]byte(s)) + gtest.AssertEQ(int(encrypt1), result) + gtest.AssertEQ(int(encrypt2), result) + }) +} diff --git a/g/crypto/gdes/gdes_test.go b/g/crypto/gdes/gdes_test.go index 075e7d07b..bba03375f 100644 --- a/g/crypto/gdes/gdes_test.go +++ b/g/crypto/gdes/gdes_test.go @@ -1,185 +1,239 @@ package gdes_test import ( - "testing" - "bytes" "encoding/hex" - "fmt" + "testing" + "github.com/gogf/gf/g/crypto/gdes" + "github.com/gogf/gf/g/test/gtest" ) -func TestDesECB(t *testing.T){ - { +var ( + errKey = []byte("1111111111111234123456789") + errIv = []byte("123456789") + errPadding = 5 +) + +func TestDesECB(t *testing.T) { + gtest.Case(t, func() { key := []byte("11111111") text := []byte("12345678") padding := gdes.NOPADDING + result := "858b176da8b12503" + // encrypt test cipherText, err := gdes.DesECBEncrypt(key, text, padding) - if err != nil { - t.Errorf("%v", err) - } - + gtest.AssertEQ(err, nil) + gtest.AssertEQ(hex.EncodeToString(cipherText),result) + // decrypt test clearText, err := gdes.DesECBDecrypt(key, cipherText, padding) - if err != nil { - t.Errorf("%v", err) - } + gtest.AssertEQ(err, nil) + gtest.AssertEQ(string(clearText), "12345678") - if bytes.Equal(clearText, text) == false { - t.Errorf("text:%v, clearText:%v", hex.EncodeToString(text), hex.EncodeToString(clearText)) - } - fmt.Println("clearText:", hex.EncodeToString(clearText), "cipherText:", hex.EncodeToString(cipherText)) + // encrypt err test. when throw exception,the err is not equal nil and the string is nil + errEncrypt, err := gdes.DesECBEncrypt(key, text, errPadding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errEncrypt, nil) + errEncrypt, err = gdes.DesECBEncrypt(errKey, text, padding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errEncrypt, nil) + // err decrypt test. + errDecrypt, err := gdes.DesECBDecrypt(errKey, cipherText, padding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errDecrypt, nil) + errDecrypt, err = gdes.DesECBDecrypt(key, cipherText, errPadding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errDecrypt, nil) + }) - } - - { + gtest.Case(t, func() { key := []byte("11111111") text := []byte("12345678") padding := gdes.PKCS5PADDING + errPadding := 5 + result := "858b176da8b12503ad6a88b4fa37833d" cipherText, err := gdes.DesECBEncrypt(key, text, padding) - if err != nil { - t.Errorf("%v", err) - } - + gtest.AssertEQ(err,nil) + gtest.AssertEQ(hex.EncodeToString(cipherText),result) + // decrypt test clearText, err := gdes.DesECBDecrypt(key, cipherText, padding) - if err != nil { - t.Errorf("%v", err) - } + gtest.AssertEQ(err,nil) + gtest.AssertEQ(string(clearText),"12345678") - if bytes.Equal(clearText, text) == false { - t.Errorf("text:%v, clearText:%v", hex.EncodeToString(text), hex.EncodeToString(clearText)) - } - fmt.Println("clearText:", hex.EncodeToString(clearText), "cipherText:", hex.EncodeToString(cipherText)) - } + // err test + errEncrypt, err := gdes.DesECBEncrypt(key, text, errPadding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errEncrypt, nil) + errDecrypt, err := gdes.DesECBDecrypt(errKey, cipherText, padding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errDecrypt, nil) + }) } -func Test3DesECB(t *testing.T){ - { +func Test3DesECB(t *testing.T) { + gtest.Case(t, func() { key := []byte("1111111111111234") text := []byte("1234567812345678") padding := gdes.NOPADDING + result := "a23ee24b98c26263a23ee24b98c26263" + // encrypt test cipherText, err := gdes.TripleDesECBEncrypt(key, text, padding) - if err != nil { - t.Errorf("%v", err) - } - + gtest.AssertEQ(err,nil) + gtest.AssertEQ(hex.EncodeToString(cipherText),result) + // decrypt test clearText, err := gdes.TripleDesECBDecrypt(key, cipherText, padding) - if err != nil { - t.Errorf("%v", err) - } + gtest.AssertEQ(err,nil) + gtest.AssertEQ(string(clearText),"1234567812345678") + // err test + errEncrypt, err := gdes.DesECBEncrypt(key, text, errPadding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errEncrypt, nil) + }) - if bytes.Equal(clearText, text) == false { - t.Errorf("text:%v, clearText:%v", hex.EncodeToString(text), hex.EncodeToString(clearText)) - } - fmt.Println("key:", hex.EncodeToString(key),"clearText:", hex.EncodeToString(clearText), "cipherText:", hex.EncodeToString(cipherText)) - - } - - { + gtest.Case(t, func() { key := []byte("111111111111123412345678") text := []byte("123456789") padding := gdes.PKCS5PADDING + errPadding := 5 + result := "37989b1effc07a6d00ff89a7d052e79f" + // encrypt test cipherText, err := gdes.TripleDesECBEncrypt(key, text, padding) - if err != nil { - t.Errorf("%v", err) - } - + gtest.AssertEQ(err,nil) + gtest.AssertEQ(hex.EncodeToString(cipherText),result) + // decrypt test clearText, err := gdes.TripleDesECBDecrypt(key, cipherText, padding) - if err != nil { - t.Errorf("%v", err) - } - - if bytes.Equal(clearText, text) == false { - t.Errorf("text:%v, clearText:%v", hex.EncodeToString(text), hex.EncodeToString(clearText)) - } - fmt.Println("key:", hex.EncodeToString(key),"clearText:", hex.EncodeToString(clearText), "cipherText:", hex.EncodeToString(cipherText)) - } + gtest.AssertEQ(err,nil) + gtest.AssertEQ(string(clearText),"123456789") + // err test, when key is err, but text and padding is right + errEncrypt, err := gdes.TripleDesECBEncrypt(errKey, text, padding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errEncrypt, nil) + // when padding is err,but key and text is right + errEncrypt, err = gdes.TripleDesECBEncrypt(key, text, errPadding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errEncrypt, nil) + // decrypt err test,when key is err + errEncrypt, err = gdes.TripleDesECBDecrypt(errKey, text, padding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errEncrypt, nil) + }) } -func TestDesCBC(t *testing.T){ - { +func TestDesCBC(t *testing.T) { + gtest.Case(t, func() { key := []byte("11111111") text := []byte("1234567812345678") padding := gdes.NOPADDING iv := []byte("12345678") - cipherText, err := gdes.DesCBCEncrypt(key, text, iv,padding) - if err != nil { - t.Errorf("%v", err) - } - + result := "40826a5800608c87585ca7c9efabee47" + // encrypt test + cipherText, err := gdes.DesCBCEncrypt(key, text, iv, padding) + gtest.AssertEQ(err,nil) + gtest.AssertEQ(hex.EncodeToString(cipherText),result) + // decrypt test clearText, err := gdes.DesCBCDecrypt(key, cipherText, iv, padding) - if err != nil { - t.Errorf("%v", err) - } + gtest.AssertEQ(err,nil) + gtest.AssertEQ(string(clearText),"1234567812345678") + // encrypt err test. + errEncrypt, err := gdes.DesCBCEncrypt(errKey, text, iv, padding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errEncrypt, nil) + // the iv is err + errEncrypt, err = gdes.DesCBCEncrypt(key, text, errIv, padding) + //gtest.AssertNE(err,nil) + gtest.AssertEQ(errEncrypt, nil) + // the padding is err + errEncrypt, err = gdes.DesCBCEncrypt(key, text, iv, errPadding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errEncrypt, nil) + // decrypt err test. the key is err + errDecrypt, err := gdes.DesCBCDecrypt(errKey, cipherText, iv, padding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errDecrypt, nil) + // the iv is err + errDecrypt, err = gdes.DesCBCDecrypt(key, cipherText, errIv, padding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errDecrypt, nil) + // the padding is err + errDecrypt, err = gdes.DesCBCDecrypt(key, cipherText, iv, errPadding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errDecrypt, nil) + }) - if bytes.Equal(clearText, text) == false { - t.Errorf("text:%v, clearText:%v", hex.EncodeToString(text), hex.EncodeToString(clearText)) - } - fmt.Println("key:", hex.EncodeToString(key),"clearText:", hex.EncodeToString(clearText), "cipherText:", hex.EncodeToString(cipherText)) - - } - - { + gtest.Case(t, func() { key := []byte("11111111") text := []byte("12345678") padding := gdes.PKCS5PADDING iv := []byte("12345678") + result := "40826a5800608c87100a25d86ac7c52c" + // encrypt test cipherText, err := gdes.DesCBCEncrypt(key, text, iv, padding) - if err != nil { - t.Errorf("%v", err) - } - + gtest.AssertEQ(err,nil) + gtest.AssertEQ(hex.EncodeToString(cipherText),result) + // decrypt test clearText, err := gdes.DesCBCDecrypt(key, cipherText, iv, padding) - if err != nil { - t.Errorf("%v", err) - } - - if bytes.Equal(clearText, text) == false { - t.Errorf("text:%v, clearText:%v", hex.EncodeToString(text), hex.EncodeToString(clearText)) - } - fmt.Println("key:", hex.EncodeToString(key),"clearText:", hex.EncodeToString(clearText), "cipherText:", hex.EncodeToString(cipherText)) - } + gtest.AssertEQ(err,nil) + gtest.AssertEQ(string(clearText),"12345678") + // err test + errEncrypt, err := gdes.DesCBCEncrypt(key, text, errIv, padding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errEncrypt, nil) + }) } -func Test3DesCBC(t *testing.T){ - { +func Test3DesCBC(t *testing.T) { + gtest.Case(t, func() { key := []byte("1111111112345678") text := []byte("1234567812345678") padding := gdes.NOPADDING iv := []byte("12345678") - cipherText, err := gdes.TripleDesCBCEncrypt(key, text, iv,padding) - if err != nil { - t.Errorf("%v", err) - } - + result := "bfde1394e265d5f738d5cab170c77c88" + // encrypt test + cipherText, err := gdes.TripleDesCBCEncrypt(key, text, iv, padding) + gtest.AssertEQ(err,nil) + gtest.AssertEQ(hex.EncodeToString(cipherText),result) + // decrypt test clearText, err := gdes.TripleDesCBCDecrypt(key, cipherText, iv, padding) - if err != nil { - t.Errorf("%v", err) - } - - if bytes.Equal(clearText, text) == false { - t.Errorf("text:%v, clearText:%v", hex.EncodeToString(text), hex.EncodeToString(clearText)) - } - fmt.Println("key:", hex.EncodeToString(key),"clearText:", hex.EncodeToString(clearText), "cipherText:", hex.EncodeToString(cipherText)) - - } - - { + gtest.AssertEQ(err,nil) + gtest.AssertEQ(string(clearText),"1234567812345678") + // encrypt err test + errEncrypt, err := gdes.TripleDesCBCEncrypt(errKey, text, iv, padding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errEncrypt, nil) + // the iv is err + errEncrypt, err = gdes.TripleDesCBCEncrypt(key, text, errIv, padding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errEncrypt, nil) + // the padding is err + errEncrypt, err = gdes.TripleDesCBCEncrypt(key, text, iv, errPadding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errEncrypt, nil) + // decrypt err test + errDecrypt, err := gdes.TripleDesCBCDecrypt(errKey, cipherText, iv, padding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errDecrypt, nil) + // the iv is err + errDecrypt, err = gdes.TripleDesCBCDecrypt(key, cipherText, errIv, padding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errDecrypt, nil) + // the padding is err + errDecrypt, err = gdes.TripleDesCBCDecrypt(key, cipherText, iv, errPadding) + gtest.AssertNE(err, nil) + gtest.AssertEQ(errDecrypt, nil) + }) + gtest.Case(t, func() { key := []byte("111111111234567812345678") text := []byte("12345678") padding := gdes.PKCS5PADDING iv := []byte("12345678") + result := "40826a5800608c87100a25d86ac7c52c" + // encrypt test cipherText, err := gdes.TripleDesCBCEncrypt(key, text, iv, padding) - if err != nil { - t.Errorf("%v", err) - } - + gtest.AssertEQ(err,nil) + gtest.AssertEQ(hex.EncodeToString(cipherText),result) + // decrypt test clearText, err := gdes.TripleDesCBCDecrypt(key, cipherText, iv, padding) - if err != nil { - t.Errorf("%v", err) - } + gtest.AssertEQ(err,nil) + gtest.AssertEQ(string(clearText),"12345678") + }) - if bytes.Equal(clearText, text) == false { - t.Errorf("text:%v, clearText:%v", hex.EncodeToString(text), hex.EncodeToString(clearText)) - } - fmt.Println("key:", hex.EncodeToString(key),"clearText:", hex.EncodeToString(clearText), "cipherText:", hex.EncodeToString(cipherText)) - } -} \ No newline at end of file +} diff --git a/g/crypto/gmd5/gmd5_test.go b/g/crypto/gmd5/gmd5_test.go new file mode 100644 index 000000000..12c3f066e --- /dev/null +++ b/g/crypto/gmd5/gmd5_test.go @@ -0,0 +1,78 @@ +// 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. + +// go test *.go -bench=".*" + +package gmd5_test + +import ( + "os" + "testing" + + "github.com/gogf/gf/g/crypto/gmd5" + "github.com/gogf/gf/g/test/gtest" +) + +var ( + s = "pibigstar" + // online generated MD5 value + result = "d175a1ff66aedde64344785f7f7a3df8" +) + +type user struct { + name string + password string + age int +} + +func TestEncrypt(t *testing.T) { + gtest.Case(t, func() { + encryptString := gmd5.Encrypt(s) + gtest.Assert(encryptString, result) + + result := "1427562bb29f88a1161590b76398ab72" + encrypt := gmd5.Encrypt(123456) + gtest.AssertEQ(encrypt,result) + }) + + gtest.Case(t, func() { + user := &user{ + name: "派大星", + password: "123456", + age: 23, + } + result := "70917ebce8bd2f78c736cda63870fb39" + encrypt := gmd5.Encrypt(user) + gtest.AssertEQ(encrypt,result) + }) +} + +func TestEncryptString(t *testing.T) { + gtest.Case(t, func() { + encryptString := gmd5.EncryptString(s) + gtest.Assert(encryptString, result) + }) +} + +func TestEncryptFile(t *testing.T) { + path := "test.text" + errorPath := "err.txt" + result := "e6e6e1cd41895beebff16d5452dfce12" + gtest.Case(t, func() { + file, err := os.Create(path) + defer os.Remove(path) + defer file.Close() + gtest.Assert(err, nil) + file.Write([]byte("Hello Go Frame")) + encryptFile := gmd5.EncryptFile(path) + gtest.AssertEQ(encryptFile, result) + // when the file is not exist,encrypt will return empty string + errEncrypt := gmd5.EncryptFile(errorPath) + gtest.AssertEQ(errEncrypt, "") + }) + + +} diff --git a/g/crypto/gsha1/gsha1_test.go b/g/crypto/gsha1/gsha1_test.go new file mode 100644 index 000000000..59f21489c --- /dev/null +++ b/g/crypto/gsha1/gsha1_test.go @@ -0,0 +1,67 @@ +// 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. + +// go test *.go -bench=".*" + +package gsha1_test + +import ( + "os" + "testing" + + "github.com/gogf/gf/g/crypto/gsha1" + "github.com/gogf/gf/g/test/gtest" +) + +type user struct { + name string + password string + age int +} + +func TestEncrypt(t *testing.T) { + gtest.Case(t, func() { + user := &user{ + name: "派大星", + password: "123456", + age: 23, + } + result := "97386736e3ee4adee5ca595c78c12129f6032cad" + encrypt := gsha1.Encrypt(user) + gtest.AssertEQ(encrypt, result) + }) + gtest.Case(t, func() { + result := "5b4c1c2a08ca85ddd031ef8627414f4cb2620b41" + s := gsha1.Encrypt("pibigstar") + gtest.AssertEQ(s, result) + }) +} + +func TestEncryptString(t *testing.T) { + gtest.Case(t, func() { + result := "5b4c1c2a08ca85ddd031ef8627414f4cb2620b41" + s := gsha1.EncryptString("pibigstar") + gtest.AssertEQ(s, result) + }) +} + +func TestEncryptFile(t *testing.T) { + path := "test.text" + errPath := "err.text" + gtest.Case(t, func() { + result := "8b05d3ba24b8d2374b8f5149d9f3fbada14ea984" + file, err := os.Create(path) + defer os.Remove(path) + defer file.Close() + gtest.Assert(err, nil) + file.Write([]byte("Hello Go Frame")) + encryptFile := gsha1.EncryptFile(path) + gtest.AssertEQ(encryptFile, result) + // when the file is not exist,encrypt will return empty string + errEncrypt := gsha1.EncryptFile(errPath) + gtest.AssertEQ(errEncrypt,"") + }) +} diff --git a/g/os/gfile/gfile_contents_test.go b/g/os/gfile/gfile_contents_test.go new file mode 100644 index 000000000..c066b7d1d --- /dev/null +++ b/g/os/gfile/gfile_contents_test.go @@ -0,0 +1,334 @@ +package gfile_test + +import ( + "github.com/gogf/gf/g/os/gfile" + "github.com/gogf/gf/g/test/gtest" + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" +) + +// 创建测试文件 +func createTestFile(filename, content string) error { + TempDir := testpath() + err := ioutil.WriteFile(TempDir+filename, []byte(content), 0666) + return err +} + +// 测试完删除文件或目录 +func delTestFiles(filenames string) { + os.RemoveAll(testpath() + filenames) +} + +// 创建目录 +func createDir(paths string) { + TempDir := testpath() + os.Mkdir(TempDir+paths, 0777) +} + +// 统一格式化文件目录为"/" +func formatpaths(paths []string) []string { + for k, v := range paths { + paths[k] = filepath.ToSlash(v) + paths[k] = strings.Replace(paths[k], "./", "/", 1) + } + + return paths +} + +// 统一格式化文件目录为"/" +func formatpath(paths string) string { + paths = filepath.ToSlash(paths) + paths = strings.Replace(paths, "./", "/", 1) + return paths +} + +// 指定返回要测试的目录 +func testpath() string { + return os.TempDir() +} + +func TestGetContents(t *testing.T) { + gtest.Case(t, func() { + + var ( + filepaths string = "/testfile_t1.txt" + ) + createTestFile(filepaths, "my name is jroam") + defer delTestFiles(filepaths) + + gtest.Assert(gfile.GetContents(testpath()+filepaths), "my name is jroam") + gtest.Assert(gfile.GetContents(""), "") + + }) +} + +func TestGetBinContents(t *testing.T) { + gtest.Case(t, func() { + var ( + filepaths1 string = "/testfile_t1.txt" // 文件存在时 + filepaths2 string = testpath() + "/testfile_t1_no.txt" // 文件不存在时 + readcontent []byte + str1 string = "my name is jroam" + ) + createTestFile(filepaths1, str1) + defer delTestFiles(filepaths1) + readcontent = gfile.GetBinContents(testpath() + filepaths1) + gtest.Assert(readcontent, []byte(str1)) + + readcontent = gfile.GetBinContents(filepaths2) + gtest.Assert(string(readcontent), "") + + gtest.Assert(string(gfile.GetBinContents(filepaths2)), "") + + }) +} + +// 截断文件为指定的大小 +func TestTruncate(t *testing.T) { + gtest.Case(t, func() { + var ( + filepaths1 string = "/testfile_GetContentsyyui.txt" //文件存在时 + err error + files *os.File + ) + createTestFile(filepaths1, "abcdefghijkmln") + defer delTestFiles(filepaths1) + err = gfile.Truncate(testpath()+filepaths1, 10) + gtest.Assert(err, nil) + + //=========================检查修改文后的大小,是否与期望一致 + files, err = os.Open(testpath() + filepaths1) + defer files.Close() + gtest.Assert(err, nil) + fileinfo, err2 := files.Stat() + gtest.Assert(err2, nil) + gtest.Assert(fileinfo.Size(), 10) + + //====测试当为空时,是否报错 + err = gfile.Truncate("", 10) + gtest.AssertNE(err, nil) + + }) +} + +func TestPutContents(t *testing.T) { + gtest.Case(t, func() { + var ( + filepaths string = "/testfile_PutContents.txt" + err error + readcontent []byte + ) + createTestFile(filepaths, "a") + defer delTestFiles(filepaths) + + err = gfile.PutContents(testpath()+filepaths, "test!") + gtest.Assert(err, nil) + + //==================判断是否真正写入 + readcontent, err = ioutil.ReadFile(testpath() + filepaths) + gtest.Assert(err, nil) + gtest.Assert(string(readcontent), "test!") + + err = gfile.PutContents("", "test!") + gtest.AssertNE(err, nil) + + }) +} + +func TestPutContentsAppend(t *testing.T) { + gtest.Case(t, func() { + var ( + filepaths string = "/testfile_PutContents.txt" + err error + readcontent []byte + ) + + createTestFile(filepaths, "a") + defer delTestFiles(filepaths) + err = gfile.PutContentsAppend(testpath()+filepaths, "hello") + gtest.Assert(err, nil) + + //==================判断是否真正写入 + readcontent, err = ioutil.ReadFile(testpath() + filepaths) + gtest.Assert(err, nil) + gtest.Assert(string(readcontent), "ahello") + + err = gfile.PutContentsAppend("", "hello") + gtest.AssertNE(err, nil) + + }) + +} + +func TestPutBinContents(t *testing.T) { + gtest.Case(t, func() { + var ( + filepaths string = "/testfile_PutContents.txt" + err error + readcontent []byte + ) + createTestFile(filepaths, "a") + defer delTestFiles(filepaths) + + err = gfile.PutBinContents(testpath()+filepaths, []byte("test!!")) + gtest.Assert(err, nil) + + // 判断是否真正写入 + readcontent, err = ioutil.ReadFile(testpath() + filepaths) + gtest.Assert(err, nil) + gtest.Assert(string(readcontent), "test!!") + + err = gfile.PutBinContents("", []byte("test!!")) + gtest.AssertNE(err, nil) + + }) +} + +func TestPutBinContentsAppend(t *testing.T) { + gtest.Case(t, func() { + var ( + filepaths string = "/testfile_PutContents.txt" //原文件内容: yy + err error + readcontent []byte + ) + createTestFile(filepaths, "test!!") + defer delTestFiles(filepaths) + err = gfile.PutBinContentsAppend(testpath()+filepaths, []byte("word")) + gtest.Assert(err, nil) + + // 判断是否真正写入 + readcontent, err = ioutil.ReadFile(testpath() + filepaths) + gtest.Assert(err, nil) + gtest.Assert(string(readcontent), "test!!word") + + err = gfile.PutBinContentsAppend("", []byte("word")) + gtest.AssertNE(err, nil) + + }) +} + +func TestGetBinContentsByTwoOffsetsByPath(t *testing.T) { + gtest.Case(t, func() { + var ( + filepaths string = "/testfile_GetContents.txt" // 文件内容: abcdefghijk + readcontent []byte + ) + + createTestFile(filepaths, "abcdefghijk") + defer delTestFiles(filepaths) + readcontent = gfile.GetBinContentsByTwoOffsetsByPath(testpath()+filepaths, 2, 5) + + gtest.Assert(string(readcontent), "cde") + + readcontent = gfile.GetBinContentsByTwoOffsetsByPath("", 2, 5) + gtest.Assert(len(readcontent), 0) + + }) + +} + +func TestGetNextCharOffsetByPath(t *testing.T) { + gtest.Case(t, func() { + var ( + filepaths string = "/testfile_GetContents.txt" // 文件内容: abcdefghijk + localindex int64 + ) + createTestFile(filepaths, "abcdefghijk") + defer delTestFiles(filepaths) + localindex = gfile.GetNextCharOffsetByPath(testpath()+filepaths, 'd', 1) + gtest.Assert(localindex, 3) + + localindex = gfile.GetNextCharOffsetByPath("", 'd', 1) + gtest.Assert(localindex, -1) + + }) +} + +func TestGetNextCharOffset(t *testing.T) { + gtest.Case(t, func() { + var ( + localindex int64 + ) + reader := strings.NewReader("helloword") + + localindex = gfile.GetNextCharOffset(reader, 'w', 1) + gtest.Assert(localindex, 5) + + localindex = gfile.GetNextCharOffset(reader, 'j', 1) + gtest.Assert(localindex, -1) + + }) +} + +func TestGetBinContentsByTwoOffsets(t *testing.T) { + gtest.Case(t, func() { + var ( + reads []byte + ) + reader := strings.NewReader("helloword") + + reads = gfile.GetBinContentsByTwoOffsets(reader, 1, 3) + gtest.Assert(string(reads), "el") + + reads = gfile.GetBinContentsByTwoOffsets(reader, 10, 30) + gtest.Assert(string(reads), "") + + }) +} + +func TestGetBinContentsTilChar(t *testing.T) { + gtest.Case(t, func() { + var ( + reads []byte + indexs int64 + ) + reader := strings.NewReader("helloword") + + reads, _ = gfile.GetBinContentsTilChar(reader, 'w', 2) + gtest.Assert(string(reads), "llow") + + _, indexs = gfile.GetBinContentsTilChar(reader, 'w', 20) + gtest.Assert(indexs, -1) + + }) +} + +func TestGetBinContentsTilCharByPath(t *testing.T) { + gtest.Case(t, func() { + var ( + reads []byte + indexs int64 + filepaths string = "/testfile_GetContents.txt" + ) + + createTestFile(filepaths, "abcdefghijklmn") + defer delTestFiles(filepaths) + + reads, _ = gfile.GetBinContentsTilCharByPath(testpath()+filepaths, 'c', 2) + gtest.Assert(string(reads), "c") + + reads, _ = gfile.GetBinContentsTilCharByPath(testpath()+filepaths, 'y', 1) + gtest.Assert(string(reads), "") + + _, indexs = gfile.GetBinContentsTilCharByPath(testpath()+filepaths, 'x', 1) + gtest.Assert(indexs, -1) + + }) +} + +func TestHome(t *testing.T) { + gtest.Case(t, func() { + var ( + reads string + err error + ) + + reads, err = gfile.Home() + gtest.Assert(err, nil) + gtest.AssertNE(reads, "") + + }) +} diff --git a/g/os/gfile/gfile_search_test.go b/g/os/gfile/gfile_search_test.go new file mode 100644 index 000000000..ca6018a24 --- /dev/null +++ b/g/os/gfile/gfile_search_test.go @@ -0,0 +1,63 @@ +package gfile_test + +import ( + "github.com/gogf/gf/g/os/gfile" + "github.com/gogf/gf/g/test/gtest" + "path/filepath" + "testing" +) + +func TestSearch(t *testing.T) { + gtest.Case(t, func() { + var ( + paths1 string = "/testfiless" + paths2 string = "./testfile/dirfiles_no" + tpath string + tpath2 string + tempstr string + ypaths1 string + err error + ) + + createDir(paths1) + defer delTestFiles(paths1) + ypaths1 = paths1 + + tpath, err = gfile.Search(testpath() + paths1) + gtest.Assert(err, nil) + + tpath = filepath.ToSlash(tpath) + + // 自定义优先路径 + tpath2, err = gfile.Search(testpath() + paths1) + gtest.Assert(err, nil) + tpath2 = filepath.ToSlash(tpath2) + + tempstr = testpath() + paths1 = tempstr + paths1 + paths1 = filepath.ToSlash(paths1) + + gtest.Assert(tpath, paths1) + + gtest.Assert(tpath2, tpath) + + // 测试给定目录 + tpath2, err = gfile.Search(paths1, "testfiless") + tpath2 = filepath.ToSlash(tpath2) + tempss := filepath.ToSlash(paths1) + gtest.Assert(tpath2, tempss) + + // 测试当前目录 + tempstr, _ = filepath.Abs("./") + tempstr = testpath() + paths1 = tempstr + ypaths1 + paths1 = filepath.ToSlash(paths1) + + gtest.Assert(tpath2, paths1) + + // 测试目录不存在时 + _, err = gfile.Search(paths2) + gtest.AssertNE(err, nil) + + }) +} diff --git a/g/os/gfile/gfile_size_test.go b/g/os/gfile/gfile_size_test.go new file mode 100644 index 000000000..9f738366d --- /dev/null +++ b/g/os/gfile/gfile_size_test.go @@ -0,0 +1,59 @@ +package gfile_test + +import ( + "github.com/gogf/gf/g/os/gfile" + "github.com/gogf/gf/g/test/gtest" + "testing" +) + +func TestSize(t *testing.T) { + gtest.Case(t, func() { + var ( + paths1 string = "/testfile_t1.txt" + sizes int64 + ) + + createTestFile(paths1, "abcdefghijklmn") + defer delTestFiles(paths1) + + sizes = gfile.Size(testpath() + paths1) + gtest.Assert(sizes, 14) + + sizes = gfile.Size("") + gtest.Assert(sizes, 0) + + }) +} + +func TestFormatSize(t *testing.T) { + gtest.Case(t, func() { + gtest.Assert(gfile.FormatSize(0), "0.00B") + gtest.Assert(gfile.FormatSize(16), "16.00B") + + gtest.Assert(gfile.FormatSize(1024), "1.00K") + + gtest.Assert(gfile.FormatSize(16000000), "15.26M") + + gtest.Assert(gfile.FormatSize(1600000000), "1.49G") + + gtest.Assert(gfile.FormatSize(9600000000000), "8.73T") + gtest.Assert(gfile.FormatSize(9600000000000000), "8.53P") + + gtest.Assert(gfile.FormatSize(9600000000000000000), "TooLarge") + + }) +} + +func TestReadableSize(t *testing.T) { + gtest.Case(t, func() { + + var ( + paths1 string = "/testfile_t1.txt" + ) + createTestFile(paths1, "abcdefghijklmn") + defer delTestFiles(paths1) + gtest.Assert(gfile.ReadableSize(testpath()+paths1), "14.00B") + gtest.Assert(gfile.ReadableSize(""), "0.00B") + + }) +} diff --git a/g/os/gfile/gfile_test.go b/g/os/gfile/gfile_test.go new file mode 100644 index 000000000..b7b9317b8 --- /dev/null +++ b/g/os/gfile/gfile_test.go @@ -0,0 +1,686 @@ +package gfile_test + +import ( + "github.com/gogf/gf/g/os/gfile" + "github.com/gogf/gf/g/test/gtest" + "os" + "path/filepath" + "strings" + "testing" +) + +func TestIsDir(t *testing.T) { + + gtest.Case(t, func() { + paths := "/testfile" + createDir(paths) + defer delTestFiles(paths) + + gtest.Assert(gfile.IsDir(testpath()+paths), true) + gtest.Assert(gfile.IsDir("./testfile2"), false) + gtest.Assert(gfile.IsDir("./testfile/tt.txt"), false) + gtest.Assert(gfile.IsDir(""), false) + + }) + +} + +func TestCreate(t *testing.T) { + gtest.Case(t, func() { + var ( + err error + filepaths []string + fileobj *os.File + ) + + filepaths = append(filepaths, "/testfile_cc1.txt") + filepaths = append(filepaths, "/testfile_cc2.txt") + + for _, v := range filepaths { + fileobj, err = gfile.Create(testpath() + v) + defer delTestFiles(v) + fileobj.Close() + gtest.Assert(err, nil) + + } + + }) + +} + +func TestOpen(t *testing.T) { + gtest.Case(t, func() { + var ( + err error + files []string + flags []bool + fileobj *os.File + ) + + file1 := "/testfile_nc1.txt" + createTestFile(file1, "") + defer delTestFiles(file1) + + files = append(files, file1) + flags = append(flags, true) + + files = append(files, "./testfile/file1/c1.txt") + flags = append(flags, false) + + for k, v := range files { + fileobj, err = gfile.Open(testpath() + v) + fileobj.Close() + if flags[k] { + gtest.Assert(err, nil) + } else { + gtest.AssertNE(err, nil) + } + + } + + }) +} + +func TestOpenFile(t *testing.T) { + gtest.Case(t, func() { + var ( + err error + files []string + flags []bool + fileobj *os.File + ) + + files = append(files, "./testfile/file1/nc1.txt") + flags = append(flags, false) + + f1 := "/testfile_tt.txt" + createTestFile(f1, "") + defer delTestFiles(f1) + + files = append(files, f1) + flags = append(flags, true) + + for k, v := range files { + fileobj, err = gfile.OpenFile(testpath()+v, os.O_RDWR, 0666) + fileobj.Close() + if flags[k] { + gtest.Assert(err, nil) + } else { + gtest.AssertNE(err, nil) + } + + } + + }) +} + +func TestOpenWithFlag(t *testing.T) { + gtest.Case(t, func() { + var ( + err error + files []string + flags []bool + fileobj *os.File + ) + + file1 := "/testfile_t1.txt" + createTestFile(file1, "") + defer delTestFiles(file1) + files = append(files, file1) + flags = append(flags, true) + + files = append(files, "/testfiless/dirfiles/t1_no.txt") + flags = append(flags, false) + + for k, v := range files { + fileobj, err = gfile.OpenWithFlag(testpath()+v, os.O_RDWR) + fileobj.Close() + if flags[k] { + gtest.Assert(err, nil) + } else { + gtest.AssertNE(err, nil) + } + + } + + }) +} + +func TestOpenWithFlagPerm(t *testing.T) { + gtest.Case(t, func() { + var ( + err error + files []string + flags []bool + fileobj *os.File + ) + file1 := "/testfile_nc1.txt" + createTestFile(file1, "") + defer delTestFiles(file1) + files = append(files, file1) + flags = append(flags, true) + + files = append(files, "/testfileyy/tt.txt") + flags = append(flags, false) + + for k, v := range files { + fileobj, err = gfile.OpenWithFlagPerm(testpath()+v, os.O_RDWR, 666) + fileobj.Close() + if flags[k] { + gtest.Assert(err, nil) + } else { + gtest.AssertNE(err, nil) + } + + } + + }) +} + +func TestExists(t *testing.T) { + + gtest.Case(t, func() { + var ( + flag bool + files []string + flags []bool + ) + + file1 := "/testfile_GetContents.txt" + createTestFile(file1, "") + defer delTestFiles(file1) + + files = append(files, file1) + flags = append(flags, true) + + files = append(files, "./testfile/havefile1/tt_no.txt") + flags = append(flags, false) + + for k, v := range files { + flag = gfile.Exists(testpath() + v) + if flags[k] { + gtest.Assert(flag, true) + } else { + gtest.Assert(flag, false) + } + + } + + }) +} + +func TestPwd(t *testing.T) { + gtest.Case(t, func() { + paths, err := os.Getwd() + gtest.Assert(err, nil) + gtest.Assert(gfile.Pwd(), paths) + + }) +} + +func TestIsFile(t *testing.T) { + gtest.Case(t, func() { + var ( + flag bool + files []string + flags []bool + ) + + file1 := "/testfile_tt.txt" + createTestFile(file1, "") + defer delTestFiles(file1) + files = append(files, file1) + flags = append(flags, true) + + dir1 := "/testfiless" + createDir(dir1) + defer delTestFiles(dir1) + files = append(files, dir1) + flags = append(flags, false) + + files = append(files, "./testfiledd/tt1.txt") + flags = append(flags, false) + + for k, v := range files { + flag = gfile.IsFile(testpath() + v) + if flags[k] { + gtest.Assert(flag, true) + } else { + gtest.Assert(flag, false) + } + + } + + }) +} + +func TestInfo(t *testing.T) { + gtest.Case(t, func() { + var ( + err error + paths string = "/testfile_t1.txt" + files os.FileInfo + files2 os.FileInfo + ) + + createTestFile(paths, "") + defer delTestFiles(paths) + files, err = gfile.Info(testpath() + paths) + gtest.Assert(err, nil) + + files2, err = os.Stat(testpath() + paths) + gtest.Assert(err, nil) + + gtest.Assert(files, files2) + + }) +} + +func TestMove(t *testing.T) { + gtest.Case(t, func() { + var ( + paths string = "/ovetest" + filepaths string = "/testfile_ttn1.txt" + topath string = "/testfile_ttn2.txt" + ) + createDir("/ovetest") + createTestFile(paths+filepaths, "a") + + defer delTestFiles(paths) + + yfile := testpath() + paths + filepaths + tofile := testpath() + paths + topath + + gtest.Assert(gfile.Move(yfile, tofile), nil) + + // 检查移动后的文件是否真实存在 + _, err := os.Stat(tofile) + gtest.Assert(os.IsNotExist(err), false) + + }) +} + +func TestRename(t *testing.T) { + gtest.Case(t, func() { + var ( + paths string = "/testfiles" + ypath string = "/testfilettm1.txt" + topath string = "/testfilettm2.txt" + ) + createDir(paths) + createTestFile(paths+ypath, "a") + defer delTestFiles(paths) + + ypath = testpath() + paths + ypath + topath = testpath() + paths + topath + + gtest.Assert(gfile.Rename(ypath, topath), nil) + gtest.Assert(gfile.IsFile(topath), true) + + gtest.AssertNE(gfile.Rename("", ""), nil) + + }) + +} + +func TestCopy(t *testing.T) { + gtest.Case(t, func() { + var ( + paths string = "/testfile_copyfile1.txt" + topath string = "/testfile_copyfile2.txt" + ) + + createTestFile(paths, "") + defer delTestFiles(paths) + + gtest.Assert(gfile.Copy(testpath()+paths, testpath()+topath), nil) + defer delTestFiles(topath) + + gtest.Assert(gfile.IsFile(testpath()+topath), true) + + gtest.AssertNE(gfile.Copy("", ""), nil) + + }) +} + +func TestDirNames(t *testing.T) { + gtest.Case(t, func() { + var ( + paths string = "/testdirs" + err error + readlist []string + ) + havelist := []string{ + "t1.txt", + "t2.txt", + } + + // 创建测试文件 + createDir(paths) + for _, v := range havelist { + createTestFile(paths+"/"+v, "") + } + defer delTestFiles(paths) + + readlist, err = gfile.DirNames(testpath() + paths) + + gtest.Assert(err, nil) + gtest.Assert(havelist, readlist) + + _, err = gfile.DirNames("") + gtest.AssertNE(err, nil) + + }) +} + +func TestGlob(t *testing.T) { + gtest.Case(t, func() { + var ( + paths string = "/testfiles/*.txt" + dirpath string = "/testfiles" + err error + resultlist []string + ) + + havelist1 := []string{ + "t1.txt", + "t2.txt", + } + + havelist2 := []string{ + testpath() + "/testfiles/t1.txt", + testpath() + "/testfiles/t2.txt", + } + + //===============================构建测试文件 + createDir(dirpath) + for _, v := range havelist1 { + createTestFile(dirpath+"/"+v, "") + } + defer delTestFiles(dirpath) + + resultlist, err = gfile.Glob(testpath()+paths, true) + gtest.Assert(err, nil) + gtest.Assert(resultlist, havelist1) + + resultlist, err = gfile.Glob(testpath()+paths, false) + + gtest.Assert(err, nil) + gtest.Assert(formatpaths(resultlist), formatpaths(havelist2)) + + _, err = gfile.Glob("", true) + gtest.Assert(err, nil) + + }) +} + +func TestRemove(t *testing.T) { + gtest.Case(t, func() { + var ( + paths string = "/testfile_t1.txt" + ) + createTestFile(paths, "") + gtest.Assert(gfile.Remove(testpath()+paths), nil) + + gtest.Assert(gfile.Remove(""), nil) + + defer delTestFiles(paths) + + }) +} + +func TestIsReadable(t *testing.T) { + gtest.Case(t, func() { + var ( + paths1 string = "/testfile_GetContents.txt" + paths2 string = "./testfile_GetContents_no.txt" + ) + + createTestFile(paths1, "") + defer delTestFiles(paths1) + + gtest.Assert(gfile.IsReadable(testpath()+paths1), true) + gtest.Assert(gfile.IsReadable(paths2), false) + + }) +} + +func TestIsWritable(t *testing.T) { + gtest.Case(t, func() { + var ( + paths1 string = "/testfile_GetContents.txt" + paths2 string = "./testfile_GetContents_no.txt" + ) + + createTestFile(paths1, "") + defer delTestFiles(paths1) + gtest.Assert(gfile.IsWritable(testpath()+paths1), true) + gtest.Assert(gfile.IsWritable(paths2), false) + + }) +} + +func TestChmod(t *testing.T) { + gtest.Case(t, func() { + var ( + paths1 string = "/testfile_GetContents.txt" + paths2 string = "./testfile_GetContents_no.txt" + ) + createTestFile(paths1, "") + defer delTestFiles(paths1) + + gtest.Assert(gfile.Chmod(testpath()+paths1, 0777), nil) + gtest.AssertNE(gfile.Chmod(paths2, 0777), nil) + + }) +} + +func TestScanDir(t *testing.T) { + gtest.Case(t, func() { + var ( + paths1 string = "/testfiledirs" + files []string + err error + ) + + createDir(paths1) + createTestFile(paths1+"/t1.txt", "") + createTestFile(paths1+"/t2.txt", "") + defer delTestFiles(paths1) + + files, err = gfile.ScanDir(testpath()+paths1, "t*") + + result := []string{ + testpath() + paths1 + "/t1.txt", + testpath() + paths1 + "/t2.txt", + } + + gtest.Assert(err, nil) + + gtest.Assert(formatpaths(files), formatpaths(result)) + + _, err = gfile.ScanDir("", "t*") + gtest.AssertNE(err, nil) + + }) +} + +// 获取绝对目录地址 +func TestRealPath(t *testing.T) { + gtest.Case(t, func() { + var ( + paths1 string = "/testfile_files" + readlPath string + + tempstr string + ) + + createDir(paths1) + defer delTestFiles(paths1) + + readlPath = gfile.RealPath("./") + + tempstr, _ = filepath.Abs("./") + + gtest.Assert(readlPath, tempstr) + + gtest.Assert(gfile.RealPath("./nodirs"), "") + + }) +} + +// 获取当前执行文件的目录 +func TestSelfPath(t *testing.T) { + gtest.Case(t, func() { + var ( + paths1 string + readlPath string + tempstr string + ) + readlPath = gfile.SelfPath() + readlPath = filepath.ToSlash(readlPath) + + tempstr, _ = filepath.Abs(os.Args[0]) + paths1 = filepath.ToSlash(tempstr) + paths1 = strings.Replace(paths1, "./", "/", 1) + + gtest.Assert(readlPath, paths1) + + }) +} + +func TestSelfDir(t *testing.T) { + gtest.Case(t, func() { + var ( + paths1 string + readlPath string + tempstr string + ) + readlPath = gfile.SelfDir() + + tempstr, _ = filepath.Abs(os.Args[0]) + paths1 = filepath.Dir(tempstr) + + gtest.Assert(readlPath, paths1) + + }) +} + +func TestBasename(t *testing.T) { + gtest.Case(t, func() { + var ( + paths1 string = "/testfilerr_GetContents.txt" + readlPath string + ) + + createTestFile(paths1, "") + defer delTestFiles(paths1) + + readlPath = gfile.Basename(testpath() + paths1) + gtest.Assert(readlPath, "testfilerr_GetContents.txt") + + }) +} + +func TestDir(t *testing.T) { + gtest.Case(t, func() { + var ( + paths1 string = "/testfiless" + readlPath string + ) + createDir(paths1) + defer delTestFiles(paths1) + + readlPath = gfile.Dir(testpath() + paths1) + + gtest.Assert(readlPath, testpath()) + + }) +} + +// 获取文件名 +func TestExt(t *testing.T) { + gtest.Case(t, func() { + var ( + paths1 string = "/testfile_GetContents.txt" + dirpath1 = "/testdirs" + ) + createTestFile(paths1, "") + defer delTestFiles(paths1) + + createDir(dirpath1) + defer delTestFiles(dirpath1) + + gtest.Assert(gfile.Ext(testpath()+paths1), ".txt") + gtest.Assert(gfile.Ext(testpath()+dirpath1), "") + + }) +} + +func TestTempDir(t *testing.T) { + gtest.Case(t, func() { + var ( + tpath string + ) + + tpath = gfile.TempDir() + gtest.Assert(tpath, os.TempDir()) + + }) +} + +func TestMkdir(t *testing.T) { + gtest.Case(t, func() { + var ( + tpath string = "/testfile/createdir" + err error + ) + + defer delTestFiles("/testfile") + + err = gfile.Mkdir(testpath() + tpath) + gtest.Assert(err, nil) + + err = gfile.Mkdir("") + gtest.AssertNE(err, nil) + + err = gfile.Mkdir(testpath() + tpath + "2/t1") + gtest.Assert(err, nil) + + }) +} + +func TestStat(t *testing.T) { + gtest.Case(t, func() { + var ( + tpath1 string = "/testfile_t1.txt" + tpath2 string = "./testfile_t1_no.txt" + err error + fileiofo os.FileInfo + ) + + createTestFile(tpath1, "a") + defer delTestFiles(tpath1) + + fileiofo, err = gfile.Stat(testpath() + tpath1) + gtest.Assert(err, nil) + + gtest.Assert(fileiofo.Size(), 1) + + _, err = gfile.Stat(tpath2) + gtest.AssertNE(err, nil) + + }) +} + +func TestMainPkgPath(t *testing.T) { + gtest.Case(t, func() { + var ( + reads string + ) + + reads = gfile.MainPkgPath() + gtest.Assert(reads, "") + + }) +} diff --git a/g/os/gfile/gfile_time_test.go b/g/os/gfile/gfile_time_test.go new file mode 100644 index 000000000..b2e8c5f31 --- /dev/null +++ b/g/os/gfile/gfile_time_test.go @@ -0,0 +1,45 @@ +package gfile_test + +import ( + "github.com/gogf/gf/g/os/gfile" + "github.com/gogf/gf/g/test/gtest" + "os" + "testing" +) + +func TestMTime(t *testing.T) { + gtest.Case(t, func() { + + var ( + file1 string = "/testfile_t1.txt" + err error + fileobj os.FileInfo + ) + + createTestFile(file1, "") + defer delTestFiles(file1) + fileobj, err = os.Stat(testpath() + file1) + gtest.Assert(err, nil) + + gtest.Assert(gfile.MTime(testpath()+file1), fileobj.ModTime().Unix()) + gtest.Assert(gfile.MTime(""), 0) + }) +} + +func TestMTimeMillisecond(t *testing.T) { + gtest.Case(t, func() { + var ( + file1 string = "/testfile_t1.txt" + err error + fileobj os.FileInfo + ) + + createTestFile(file1, "") + defer delTestFiles(file1) + fileobj, err = os.Stat(testpath() + file1) + gtest.Assert(err, nil) + + gtest.AssertGTE(gfile.MTimeMillisecond(testpath()+file1), fileobj.ModTime().Nanosecond()/1000000) + gtest.Assert(gfile.MTimeMillisecond(""), 0) + }) +} diff --git a/g/os/gview/gview.go b/g/os/gview/gview.go index 4a3c42212..6da57c117 100644 --- a/g/os/gview/gview.go +++ b/g/os/gview/gview.go @@ -29,7 +29,6 @@ import ( "sync" ) -// 视图对象 type View struct { mu sync.RWMutex paths *garray.StringArray // 模板查找目录(绝对路径) @@ -38,29 +37,32 @@ type View struct { delimiters []string // 模板变量分隔符号 } -// 模板变量 +// Template params type. type Params = map[string]interface{} -// 函数映射表 +// Customized template function map type. type FuncMap = map[string]interface{} -// 默认的视图对象 +// Default view object. var defaultViewObj *View -// 初始化默认的视图对象, 默认加载包不会初始化,使用包方法才会初始化模板引擎对象。 +// checkAndInitDefaultView checks and initializes the default view object. +// The default view object will be initialized just once. func checkAndInitDefaultView() { if defaultViewObj == nil { defaultViewObj = New(gfile.Pwd()) } } -// 直接解析模板内容,返回解析后的内容 +// ParseContent parses the template content directly using the default view object +// and returns the parsed content. func ParseContent(content string, params Params) ([]byte, error) { checkAndInitDefaultView() return defaultViewObj.ParseContent(content, params) } -// 生成一个视图对象 +// New returns a new view object. +// The parameter specifies the template directory path to load template files. func New(path...string) *View { view := &View { paths : garray.NewStringArray(), @@ -92,11 +94,11 @@ func New(path...string) *View { } } view.SetDelimiters("{{", "}}") - // 内置变量 + // default build-in variables. view.data["GF"] = map[string]interface{} { "version" : gf.VERSION, } - // 内置方法 + // default build-in functions. view.BindFunc("text", view.funcText) view.BindFunc("html", view.funcHtmlEncode) view.BindFunc("htmlencode", view.funcHtmlEncode) @@ -117,12 +119,13 @@ func New(path...string) *View { return view } -// 设置模板目录绝对路径 +// SetPath sets the template directory path for template file search. +// The param can be absolute or relative path, but absolute path is suggested. func (view *View) SetPath(path string) error { - // 判断绝对路径(或者工作目录下目录) + // Absolute path. realPath := gfile.RealPath(path) if realPath == "" { - // 判断相对路径 + // Relative path. view.paths.RLockFunc(func(array []string) { for _, v := range array { if path, _ := gspath.Search(v, path); path != "" { @@ -132,7 +135,7 @@ func (view *View) SetPath(path string) error { } }) } - // 目录不存在错误处理 + // Path not exist. if realPath == "" { buffer := bytes.NewBuffer(nil) if view.paths.Len() > 0 { @@ -149,13 +152,13 @@ func (view *View) SetPath(path string) error { glog.Error(err) return err } - // 路径必须为目录类型 + // Should be a directory. if !gfile.IsDir(realPath) { err := errors.New(fmt.Sprintf(`[gview] SetPath failed: path "%s" should be directory type`, path)) glog.Error(err) return err } - // 重复判断 + // Repeated path check. if view.paths.Search(realPath) != -1 { return nil } @@ -165,12 +168,12 @@ func (view *View) SetPath(path string) error { return nil } -// 添加模板目录搜索路径 +// AddPath adds a absolute or relative path to the search paths. func (view *View) AddPath(path string) error { - // 判断绝对路径(或者工作目录下目录) + // Absolute path. realPath := gfile.RealPath(path) if realPath == "" { - // 判断相对路径 + // Relative path. view.paths.RLockFunc(func(array []string) { for _, v := range array { if path, _ := gspath.Search(v, path); path != "" { @@ -180,7 +183,7 @@ func (view *View) AddPath(path string) error { } }) } - // 目录不存在错误处理 + // Path not exist. if realPath == "" { buffer := bytes.NewBuffer(nil) if view.paths.Len() > 0 { @@ -197,13 +200,13 @@ func (view *View) AddPath(path string) error { glog.Error(err) return err } - // 路径必须为目录类型 + // realPath should be type of folder. if !gfile.IsDir(realPath) { err := errors.New(fmt.Sprintf(`[gview] AddPath failed: path "%s" should be directory type`, path)) glog.Error(err) return err } - // 重复判断 + // Repeated path check. if view.paths.Search(realPath) != -1 { return nil } @@ -212,7 +215,8 @@ func (view *View) AddPath(path string) error { return nil } -// 批量绑定模板变量,即调用之后每个线程都会生效,因此有并发安全控制 +// Assign binds multiple template variables to current view object. +// Each goroutine will take effect after the call, so it is concurrent-safe. func (view *View) Assigns(data Params) { view.mu.Lock() for k, v := range data { @@ -221,15 +225,18 @@ func (view *View) Assigns(data Params) { view.mu.Unlock() } -// 绑定模板变量,即调用之后每个线程都会生效,因此有并发安全控制 +// Assign binds a template variable to current view object. +// Each goroutine will take effect after the call, so it is concurrent-safe. func (view *View) Assign(key string, value interface{}) { view.mu.Lock() view.data[key] = value view.mu.Unlock() } -// 解析模板,返回解析后的内容 -func (view *View) Parse(file string, params Params, funcmap...map[string]interface{}) ([]byte, error) { +// ParseContent parses given template file +// with given template parameters and function map +// and returns the parsed content in []byte. +func (view *View) Parse(file string, params Params, funcMap...map[string]interface{}) ([]byte, error) { path := "" view.paths.RLockFunc(func(array []string) { for _, v := range array { @@ -254,19 +261,19 @@ func (view *View) Parse(file string, params Params, funcmap...map[string]interfa return nil, errors.New(fmt.Sprintf(`tpl "%s" not found`, file)) } content := gfcache.GetContents(path) - // 执行模板解析,互斥锁主要是用于funcmap view.mu.RLock() defer view.mu.RUnlock() buffer := bytes.NewBuffer(nil) - tplobj := template.New(path).Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcmap) - if len(funcmap) > 0 { - tplobj = tplobj.Funcs(funcmap[0]) + tplObj := template.New(path).Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcmap) + if len(funcMap) > 0 { + tplObj = tplObj.Funcs(funcMap[0]) } - if tpl, err := tplobj.Parse(content); err != nil { + if tpl, err := tplObj.Parse(content); err != nil { return nil, err } else { - // 注意模板变量赋值不能改变已有的params或者view.data的值,因为这两个变量都是指针 - // 因此在必要条件下,需要合并两个map的值到一个新的map + // Note that the template variable assignment cannot change the value + // of the existing or view.data because both variables are pointers. + // It's need to merge the values of the two maps into a new map. vars := (map[string]interface{})(nil) if len(view.data) > 0 { if len(params) > 0 { @@ -290,21 +297,24 @@ func (view *View) Parse(file string, params Params, funcmap...map[string]interfa return buffer.Bytes(), nil } -// 直接解析模板内容,返回解析后的内容 -func (view *View) ParseContent(content string, params Params, funcmap...map[string]interface{}) ([]byte, error) { +// ParseContent parses given template content +// with given template parameters and function map +// and returns the parsed content in []byte. +func (view *View) ParseContent(content string, params Params, funcMap...map[string]interface{}) ([]byte, error) { view.mu.RLock() defer view.mu.RUnlock() name := gconv.String(ghash.BKDRHash64([]byte(content))) buffer := bytes.NewBuffer(nil) - tplobj := template.New(name).Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcmap) - if len(funcmap) > 0 { - tplobj = tplobj.Funcs(funcmap[0]) + tplObj := template.New(name).Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcmap) + if len(funcMap) > 0 { + tplObj = tplObj.Funcs(funcMap[0]) } - if tpl, err := tplobj.Parse(content); err != nil { + if tpl, err := tplObj.Parse(content); err != nil { return nil, err } else { - // 注意模板变量赋值不能改变已有的params或者view.data的值,因为这两个变量都是指针 - // 因此在必要条件下,需要合并两个map的值到一个新的map + // Note that the template variable assignment cannot change the value + // of the existing or view.data because both variables are pointers. + // It's need to merge the values of the two maps into a new map. vars := (map[string]interface{})(nil) if len(view.data) > 0 { if len(params) > 0 { @@ -328,20 +338,33 @@ func (view *View) ParseContent(content string, params Params, funcmap...map[stri return buffer.Bytes(), nil } -// 设置模板变量解析分隔符号 +// SetDelimiters sets customized delimiters for template parsing. func (view *View) SetDelimiters(left, right string) { view.delimiters[0] = left view.delimiters[1] = right } -// 绑定自定义函数,该函数是全局有效,即调用之后每个线程都会生效,因此有并发安全控制 +// BindFunc registers customized template function named +// with given function to current view object. +// The is the function name which can be called in template content. func (view *View) BindFunc(name string, function interface{}) { view.mu.Lock() view.funcmap[name] = function view.mu.Unlock() } -// 模板内置方法:include +// BindFuncMap registers customized template functions by map to current view object. +// The key of map is the template function name +// and the value of map is the address of customized function. +func (view *View) BindFuncMap(funcMap FuncMap) { + view.mu.Lock() + for k, v := range funcMap { + view.funcmap[k] = v + } + view.mu.Unlock() +} + +// Build-in template function: include func (view *View) funcInclude(file string, data...map[string]interface{}) string { var m map[string]interface{} = nil if len(data) > 0 { @@ -354,27 +377,27 @@ func (view *View) funcInclude(file string, data...map[string]interface{}) string return string(content) } -// 模板内置方法:text +// Build-in template function: text func (view *View) funcText(html interface{}) string { return ghtml.StripTags(gconv.String(html)) } -// 模板内置方法:html +// Build-in template function: html func (view *View) funcHtmlEncode(html interface{}) string { return ghtml.Entities(gconv.String(html)) } -// 模板内置方法:htmldecode +// Build-in template function: htmldecode func (view *View) funcHtmlDecode(html interface{}) string { return ghtml.EntitiesDecode(gconv.String(html)) } -// 模板内置方法:url +// Build-in template function: url func (view *View) funcUrlEncode(url interface{}) string { return gurl.Encode(gconv.String(url)) } -// 模板内置方法:urldecode +// Build-in template function: urldecode func (view *View) funcUrlDecode(url interface{}) string { if content, err := gurl.Decode(gconv.String(url)); err == nil { return content @@ -383,7 +406,7 @@ func (view *View) funcUrlDecode(url interface{}) string { } } -// 模板内置方法:date +// Build-in template function: date func (view *View) funcDate(format string, timestamp...interface{}) string { t := int64(0) if len(timestamp) > 0 { @@ -395,42 +418,42 @@ func (view *View) funcDate(format string, timestamp...interface{}) string { return gtime.NewFromTimeStamp(t).Format(format) } -// 模板内置方法:compare +// Build-in template function: compare func (view *View) funcCompare(value1, value2 interface{}) int { return strings.Compare(gconv.String(value1), gconv.String(value2)) } -// 模板内置方法:substr +// Build-in template function: substr func (view *View) funcSubStr(start, end int, str interface{}) string { return gstr.SubStr(gconv.String(str), start, end) } -// 模板内置方法:strlimit +// Build-in template function: strlimit func (view *View) funcStrLimit(length int, suffix string, str interface{}) string { return gstr.StrLimit(gconv.String(str), length, suffix) } -// 模板内置方法:highlight +// Build-in template function: highlight func (view *View) funcHighlight(key string, color string, str interface{}) string { return gstr.Replace(gconv.String(str), key, fmt.Sprintf(`%s`, color, key)) } -// 模板内置方法:hidestr +// Build-in template function: hidestr func (view *View) funcHideStr(percent int, hide string, str interface{}) string { return gstr.HideStr(gconv.String(str), percent, hide) } -// 模板内置方法:toupper +// Build-in template function: toupper func (view *View) funcToUpper(str interface{}) string { return gstr.ToUpper(gconv.String(str)) } -// 模板内置方法:toupper +// Build-in template function: toupper func (view *View) funcToLower(str interface{}) string { return gstr.ToLower(gconv.String(str)) } -// 模板内置方法:nl2br +// Build-in template function: nl2br func (view *View) funcNl2Br(str interface{}) string { return gstr.Nl2Br(gconv.String(str)) } diff --git a/geg/os/gview/bind_func/gview_func.go b/geg/os/gview/bind_func/gview_func.go deleted file mode 100644 index 4e0fd5a4c..000000000 --- a/geg/os/gview/bind_func/gview_func.go +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - "fmt" - "github.com/gogf/gf/g" - "github.com/gogf/gf/g/os/gview" -) - -// 用于测试的内置函数 -func funcTest() string { - return "test" -} - -func main() { - view := g.View() - b, err := view.Parse("index.html", nil, gview.FuncMap{ - "test": funcTest, - }) - fmt.Println(err) - fmt.Println(string(b)) -} diff --git a/geg/os/gview/bind_func/gview_func1.go b/geg/os/gview/bind_func/gview_func1.go new file mode 100644 index 000000000..2182b7342 --- /dev/null +++ b/geg/os/gview/bind_func/gview_func1.go @@ -0,0 +1,23 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/os/gview" +) + +// 用于测试的内置函数 +func funcTest() string { + return "test content" +} + +func main() { + // 解析模板的时候传递模板函数映射Map,仅会在当前模板解析生效 + parsed, err := g.View().ParseContent(`call build-in function test: {{test}}`, nil, gview.FuncMap { + "test": funcTest, + }) + if err != nil { + panic(err) + } + fmt.Println(string(parsed)) +} diff --git a/geg/os/gview/bind_func/gview_func2.go b/geg/os/gview/bind_func/gview_func2.go new file mode 100644 index 000000000..5bd5f29ca --- /dev/null +++ b/geg/os/gview/bind_func/gview_func2.go @@ -0,0 +1,31 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/g" +) + +// 用于测试的带参数的内置函数 +func funcHello(name string) string { + return fmt.Sprintf(`Hello %s`, name) +} + +func main() { + // 绑定全局的模板函数 + g.View().BindFunc("hello", funcHello) + + // 普通方式传参 + parsed1, err := g.View().ParseContent(`{{hello "GoFrame"}}`, nil) + if err != nil { + panic(err) + } + fmt.Println(string(parsed1)) + + // 通过管道传参 + parsed2, err := g.View().ParseContent(`{{"GoFrame" | hello}}`, nil) + if err != nil { + panic(err) + } + fmt.Println(string(parsed2)) +} + diff --git a/geg/other/test.go b/geg/other/test.go index 63996a987..8de0ca94a 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -2,14 +2,21 @@ package main import ( "fmt" - "github.com/gogf/gf/g/util/grand" + "github.com/gogf/gf/g/container/gtype" + "github.com/gogf/gf/g/test/gtest" ) - - func main() { - for { - fmt.Println(grand.Intn(100)) - } + + var add float32 + add = 3.1415926 + + myF32 := gtype.NewFloat32(add) + myAdd := myF32.Add(float32(6.2951413)) + + add += float32(6.2951413) + + fmt.Println(myF32.Val()) + gtest.AssertEQ(myAdd, add) } \ No newline at end of file diff --git a/go.mod b/go.mod index ef37cb8d6..081d99cd0 100644 --- a/go.mod +++ b/go.mod @@ -1 +1,3 @@ module github.com/gogf/gf + +go 1.12