This commit is contained in:
arraykeys
2019-08-08 17:13:34 +08:00
parent e8e5966a8c
commit f0bf2d5fec
2885 changed files with 1195993 additions and 12 deletions
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 caixw
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+219
View File
@@ -0,0 +1,219 @@
// Copyright 2014 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.
package assert
import (
"fmt"
"os"
"path"
"runtime"
"strconv"
"strings"
"testing"
)
// 获取某个pc寄存器中的函数名,并去掉函数名之前的路径信息。
func funcName(pc uintptr) string {
if pc == 0 {
return "<无法获取函数信息>"
}
name := runtime.FuncForPC(pc).Name()
arr := strings.Split(name, "/")
return arr[len(arr)-1]
}
// 获取调用者的信息。
//
// go test输出的错误信息中,并不包含_test.go文件中的定
// 位信息,有时候很难找到在_test.go中的具体位置,此函
// 数的作用就是定位到_test.go文件中的具体位置,并返回。
// 若测试包中的函数是嵌套调用的,则有可能不正确。
func getCallerInfo() string {
for i := 0; ; i++ {
pc, file, line, ok := runtime.Caller(i)
if !ok {
return "<无法获取调用者信息>"
}
basename := path.Base(file)
// 定位以_test.go结尾的文件,认定为起始调用的测试包。
// 8 == len("_test.go")
l := len(basename)
if l < 8 || (basename[l-8:l] != "_test.go") {
continue
}
return " @ " + funcName(pc) + "(" + basename + ":" + strconv.Itoa(line) + ")"
}
return "<无法获取调用者信息>"
}
// 格式化错误提示信息。
// 优先使用msg1中的信息,若msg1为空,则使用msg2中的内容,两者格式相同。
//
// msg*中的所有参数将依次传递给fmt.Sprintf()函数,所以第一个元素的值必
// 须为string或是可转换成string的值(如[]byte,[]rune,fmt.Stringer等)
func formatMessage(msg1 []interface{}, msg2 []interface{}) string {
msg := msg1
if len(msg) == 0 {
msg = msg2
}
if len(msg) == 0 {
return "<未提供任何错误信息>"
}
format := ""
switch v := msg[0].(type) {
case []byte:
format = string(v)
case []rune:
format = string(v)
case string:
format = v
case fmt.Stringer:
format = v.String()
default:
return "<无法正确转换错误提示信息>"
}
return fmt.Sprintf(format, msg[1:]...)
}
// 当expr条件不成立时,输出错误信息。
//
// expr 返回结果值为bool类型的表达式;
// msg1,msg2输出的错误信息,之所以提供两组信息,是方便在用户没有提供的情况下,
// 可以使用系统内部提供的信息,优先使用msg1中的信息,若不存在,则使用msg2的内容。
func assert(t *testing.T, expr bool, msg1 []interface{}, msg2 []interface{}) {
if !expr {
t.Error(formatMessage(msg1, msg2) + getCallerInfo())
}
}
// 断言表达式expr为true,否则输出错误信息。
//
// args对应fmt.Printf()函数中的参数,其中args[0]对应第一个参数format,依次类推,
// 具体可参数getCallerInfo()函数的介绍。
// 其它断言函数的args参数,功能与此相同。
func True(t *testing.T, expr bool, args ...interface{}) {
assert(t, expr, args, []interface{}{"True失败,实际值为[%T:%v]", expr, expr})
}
// 断言表达式expr为false,否则输出错误信息
func False(t *testing.T, expr bool, args ...interface{}) {
assert(t, !expr, args, []interface{}{"False失败,实际值为[%T:%v]", expr, expr})
}
// 断言表达式expr为nil,否则输出错误信息
func Nil(t *testing.T, expr interface{}, args ...interface{}) {
assert(t, IsNil(expr), args, []interface{}{"Nil失败,实际值为[%T:%v]", expr, expr})
}
// 断言表达式expr为非nil值,否则输出错误信息
func NotNil(t *testing.T, expr interface{}, args ...interface{}) {
assert(t, !IsNil(expr), args, []interface{}{"NotNil失败,实际值为[%T:%v]", expr, expr})
}
// 断言v1与v2两个值相等,否则输出错误信息
func Equal(t *testing.T, v1, v2 interface{}, args ...interface{}) {
assert(t, IsEqual(v1, v2), args, []interface{}{"Equal失败,实际值为v1=[%T:%v];v2=[%T:%v]", v1, v1, v2, v2})
}
// 断言v1与v2两个值不相等,否则输出错误信息
func NotEqual(t *testing.T, v1, v2 interface{}, args ...interface{}) {
assert(t, !IsEqual(v1, v2), args, []interface{}{"NotEqual失败,实际值为v1=[%T:%v];v2=[%T:%v]", v1, v1, v2, v2})
}
// 断言expr的值为空(nil,"",0,false),否则输出错误信息
func Empty(t *testing.T, expr interface{}, args ...interface{}) {
assert(t, IsEmpty(expr), args, []interface{}{"Empty失败,实际值为[%T:%v]", expr, expr})
}
// 断言expr的值为非空(除nil,"",0,false之外),否则输出错误信息
func NotEmpty(t *testing.T, expr interface{}, args ...interface{}) {
assert(t, !IsEmpty(expr), args, []interface{}{"NotEmpty失败,实际值为[%T:%v]", expr, expr})
}
// 断言有错误发生,否则输出错误信息
// 传递未初始化的error值(var err error = nil),将断言失败
func Error(t *testing.T, expr interface{}, args ...interface{}) {
if IsNil(expr) { // 空值,必定没有错误
assert(t, false, args, []interface{}{"Error失败,实际类型为[%T]", expr})
} else {
_, ok := expr.(error)
assert(t, ok, args, []interface{}{"Error失败,实际类型为[%T]", expr})
}
}
// 断言没有错误发生,否则输出错误信息
func NotError(t *testing.T, expr interface{}, args ...interface{}) {
if IsNil(expr) { // 空值必定没有错误
assert(t, true, args, []interface{}{"NotError失败,实际类型为[%T]", expr})
} else {
err, ok := expr.(error)
assert(t, !ok, args, []interface{}{"NotError失败,错误信息为[%v]", err})
}
}
// 断言文件存在,否则输出错误信息
func FileExists(t *testing.T, path string, args ...interface{}) {
_, err := os.Stat(path)
if err != nil && !os.IsExist(err) {
assert(t, false, args, []interface{}{"FileExists发生以下错误:%v", err.Error()})
}
}
// 断言文件不存在,否则输出错误信息
func FileNotExists(t *testing.T, path string, args ...interface{}) {
_, err := os.Stat(path)
assert(t, os.IsNotExist(err), args, []interface{}{"FileExists发生以下错误:%v", err.Error()})
}
// 断言函数会发生panic,否则输出错误信息。
func Panic(t *testing.T, fn func(), args ...interface{}) {
has, _ := HasPanic(fn)
assert(t, has, args, []interface{}{"并未发生panic"})
}
// 断言函数会发生panic,否则输出错误信息。
func NotPanic(t *testing.T, fn func(), args ...interface{}) {
has, msg := HasPanic(fn)
assert(t, !has, args, []interface{}{"发生了panic,其信息为[%]", msg})
}
// 断言container包含item的或是包含item中的所有项
// 具体函数说明可参考IsContains()
func Contains(t *testing.T, container, item interface{}, args ...interface{}) {
assert(t, IsContains(container, item), args,
[]interface{}{"container:[%v]并未包含item[%v]", container, item})
}
// 断言container不包含item的或是不包含item中的所有项
func NotContains(t *testing.T, container, item interface{}, args ...interface{}) {
assert(t, !IsContains(container, item), args,
[]interface{}{"container:[%v]包含item[%v]", container, item})
}
// 判断两个字符串相等。
//
// StringEqual()与Equal()的不同之处在于:
// StringEqual()可以以相对宽松的条件来比较字符串是否相等,
// 比如忽略大小写;忽略多余的空格等,比较方式由style参数指定。
// 若style值指定为StyleStrit,则和Equal()完全相等。
func StringEqual(t *testing.T, s1, s2 string, style int, args ...interface{}) {
assert(t, StringIsEqual(s1, s2, style), args,
[]interface{}{"在[%v]比较方式中s1[%v] != s2[%v]", styleString(style), s1, s2})
}
// 判断两个字符串不相等。
func StringNotEqual(t *testing.T, s1, s2 string, style int, args ...interface{}) {
assert(t, !StringIsEqual(s1, s2, style), args,
[]interface{}{"在[%v]比较方式中s1[%v] == s2[%v]", styleString(style), s1, s2})
}
+114
View File
@@ -0,0 +1,114 @@
// Copyright 2014 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.
package assert
import (
"testing"
)
// Assertion是对testing.T进行了简单的封装。
// 可以以对象的方式调用包中的各个断言函数,
// 减少了参数t的传递。
type Assertion struct {
t *testing.T
}
// 返回Assertion对象。
func New(t *testing.T) *Assertion {
return &Assertion{t: t}
}
// 返回testing.T对象
func (a *Assertion) T() *testing.T {
return a.t
}
func (a *Assertion) True(expr bool, msg ...interface{}) *Assertion {
True(a.t, expr, msg...)
return a
}
func (a *Assertion) False(expr bool, msg ...interface{}) *Assertion {
False(a.t, expr, msg...)
return a
}
func (a *Assertion) Nil(expr interface{}, msg ...interface{}) *Assertion {
Nil(a.t, expr, msg...)
return a
}
func (a *Assertion) NotNil(expr interface{}, msg ...interface{}) *Assertion {
NotNil(a.t, expr, msg...)
return a
}
func (a *Assertion) Equal(v1, v2 interface{}, msg ...interface{}) *Assertion {
Equal(a.t, v1, v2, msg...)
return a
}
func (a *Assertion) NotEqual(v1, v2 interface{}, msg ...interface{}) *Assertion {
NotEqual(a.t, v1, v2, msg...)
return a
}
func (a *Assertion) Empty(expr interface{}, msg ...interface{}) *Assertion {
Empty(a.t, expr, msg...)
return a
}
func (a *Assertion) NotEmpty(expr interface{}, msg ...interface{}) *Assertion {
NotEmpty(a.t, expr, msg...)
return a
}
func (a *Assertion) Error(expr interface{}, msg ...interface{}) *Assertion {
Error(a.t, expr, msg...)
return a
}
func (a *Assertion) NotError(expr interface{}, msg ...interface{}) *Assertion {
NotError(a.t, expr, msg...)
return a
}
func (a *Assertion) FileExists(path string, msg ...interface{}) *Assertion {
FileExists(a.t, path, msg...)
return a
}
func (a *Assertion) FileNotExists(path string, msg ...interface{}) *Assertion {
FileNotExists(a.t, path, msg...)
return a
}
func (a *Assertion) Panic(fn func(), msg ...interface{}) *Assertion {
Panic(a.t, fn, msg...)
return a
}
func (a *Assertion) NotPanic(fn func(), msg ...interface{}) *Assertion {
NotPanic(a.t, fn, msg...)
return a
}
func (a *Assertion) Contains(container, item interface{}, msg ...interface{}) *Assertion {
Contains(a.t, container, item, msg...)
return a
}
func (a *Assertion) NotContains(container, item interface{}, msg ...interface{}) *Assertion {
NotContains(a.t, container, item, msg...)
return a
}
func (a *Assertion) StringEqual(s1, s2 string, style int, msg ...interface{}) *Assertion {
StringEqual(a.t, s1, s2, style, msg...)
return a
}
func (a *Assertion) StringNotEqual(s1, s2 string, style int, msg ...interface{}) *Assertion {
StringNotEqual(a.t, s1, s2, style, msg...)
return a
}
+30
View File
@@ -0,0 +1,30 @@
// Copyright 2014 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.
// assert是对testing包的一些简单包装。方便在测试包里少写一点代码。
//
// 提供了两种操作方式:直接调用包函数;或是使用Assertion对象。
// 两种方式完全等价,可以根据自己需要,选择一种。
// func TestAssert(t *testing.T) {
// var v interface{} = 5
//
// // 直接调用包函数
// assert.True(t, v == 5, "v的值[%v]不等于5", v)
// assert.Equal(t, 5, v, "v的值[%v]不等于5", v)
// assert.Nil(t, v)
//
// // 以Assertion对象方式使用
// a := assert.New(t)
// a.True(v==5, "v的值[%v]不等于5", v)
// a.Equal(5, v, "v的值[%v]不等于5", v)
// a.Nil(v)
// a.T().Log("success")
//
// // 以函数链的形式调用Assertion对象的方法
// a.True(false).Equal(5,6)
// }
package assert
// 当前库的版本号
const Version = "0.5.11.141118"
+420
View File
@@ -0,0 +1,420 @@
// Copyright 2014 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.
package assert
import (
"bytes"
"reflect"
"regexp"
"strconv"
"strings"
"time"
)
// 判断一个值是否为空(0, "", false, 空数组等)。
// []string{""}空数组里套一个空字符串,不会被判断为空。
func IsEmpty(expr interface{}) bool {
if expr == nil {
return true
}
switch v := expr.(type) {
case bool:
return false == v
case int:
return 0 == v
case int8:
return 0 == v
case int16:
return 0 == v
case int32:
return 0 == v
case int64:
return 0 == v
case uint:
return 0 == v
case uint8:
return 0 == v
case uint16:
return 0 == v
case uint32:
return 0 == v
case uint64:
return 0 == v
case string:
return "" == v
case time.Time:
return v.IsZero()
case *time.Time:
return v.IsZero()
}
// 符合IsNil条件的,都为Empty
ret := IsNil(expr)
if ret {
return true
}
v := reflect.ValueOf(expr)
switch v.Kind() {
case reflect.Slice, reflect.Map, reflect.Chan:
return 0 == v.Len()
case reflect.Ptr:
return false
}
return false
}
// 判断一个值是否为nil。
// 当特定类型的变量,已经声明,但还未赋值时,也将返回true
func IsNil(expr interface{}) bool {
if nil == expr {
return true
}
v := reflect.ValueOf(expr)
k := v.Kind()
if (k == reflect.Chan ||
k == reflect.Func ||
k == reflect.Interface ||
k == reflect.Map ||
k == reflect.Ptr ||
k == reflect.Slice) &&
v.IsNil() {
return true
}
return false
}
// 判断两个值是否相等。
//
// 除了通过reflect.DeepEqual()判断值是否相等之外,一些类似
// 可转换的数值也能正确判断,比如以下值也将会被判断为相等:
// int8(5) == int(5)
// []int{1,2} == []int8{1,2}
// []int{1,2} == [2]int8{1,2}
// []int{1,2} == []float32{1,2}
// map[string]int{"1":"2":2} == map[string]int8{"1":1,"2":2}
//
// // map的键值不同,即使可相互转换也判断不相等。
// map[int]int{1:1,2:2} <> map[int8]int{1:1,2:2}
func IsEqual(v1, v2 interface{}) bool {
if reflect.DeepEqual(v1, v2) {
return true
}
vv1 := reflect.ValueOf(v1)
vv2 := reflect.ValueOf(v2)
// NOTE: 这里返回false,而不是true
if !vv1.IsValid() || !vv2.IsValid() {
return false
}
if vv1 == vv2 {
return true
}
vv1Type := vv1.Type()
vv2Type := vv2.Type()
// 过滤掉已经在reflect.DeepEqual()进行处理的类型
switch vv1Type.Kind() {
case reflect.Struct, reflect.Ptr, reflect.Func, reflect.Interface:
return false
case reflect.Slice, reflect.Array:
// vv2.Kind()与vv1的不相同
if vv2.Kind() != reflect.Slice && vv2.Kind() != reflect.Array {
// 虽然类型不同,但可以相互转换成vv1的,如:vv2是stringvv2是[]byte
if vv2Type.ConvertibleTo(vv1Type) {
return IsEqual(vv1.Interface(), vv2.Convert(vv1Type).Interface())
}
return false
}
// reflect.DeepEqual()未考虑类型不同但是类型可转换的情况,比如:
// []int{8,9} == []int8{8,9},此处重新对slice和array做比较处理。
if vv1.Len() != vv2.Len() {
return false
}
for i := 0; i < vv1.Len(); i++ {
if !IsEqual(vv1.Index(i).Interface(), vv2.Index(i).Interface()) {
return false
}
}
return true // for中所有的值比较都相等,返回true
case reflect.Map:
if vv2.Kind() != reflect.Map {
return false
}
if vv1.IsNil() != vv2.IsNil() {
return false
}
if vv1.Len() != vv2.Len() {
return false
}
if vv1.Pointer() == vv2.Pointer() {
return true
}
// 两个map的键名类型不同
if vv2Type.Key().Kind() != vv1Type.Key().Kind() {
return false
}
for _, index := range vv1.MapKeys() {
if !IsEqual(vv1.MapIndex(index).Interface(), vv2.MapIndex(index).Interface()) {
return false
}
}
return true // for中所有的值比较都相等,返回true
case reflect.String:
if vv2.Kind() == reflect.String {
return vv1.String() == vv2.String()
}
if vv2Type.ConvertibleTo(vv1Type) { // 考虑v1是stringv2是[]byte的情况
return IsEqual(vv1.Interface(), vv2.Convert(vv1Type).Interface())
}
return false
}
if vv1Type.ConvertibleTo(vv2Type) {
return vv2.Interface() == vv1.Convert(vv2Type).Interface()
} else if vv2Type.ConvertibleTo(vv1Type) {
return vv1.Interface() == vv2.Convert(vv1Type).Interface()
}
return false
}
// 判断fn函数是否会发生panic
// 若发生了panic,将把msg一起返回。
func HasPanic(fn func()) (has bool, msg interface{}) {
defer func() {
if msg = recover(); msg != nil {
has = true
}
}()
fn()
return
}
// 判断container是否包含了item的内容。若是指针,会判断指针指向的内容,
// 但是不支持多重指针。
//
// 若container是字符串(string、[]byte和[]rune,不包含fmt.Stringer接口)
// 都将会以字符串的形式判断其是否包含item。
// 若container是个列表(array、slice、map)则判断其元素中是否包含item中的
// 的所有项,或是item本身就是container中的一个元素。
func IsContains(container, item interface{}) bool {
if container == nil { // nil不包含任何东西
return false
}
cv := reflect.ValueOf(container)
iv := reflect.ValueOf(item)
if cv.Kind() == reflect.Ptr {
cv = cv.Elem()
}
if iv.Kind() == reflect.Ptr {
iv = iv.Elem()
}
if IsEqual(container, item) {
return true
}
// 判断是字符串的情况
switch c := cv.Interface().(type) {
case string:
switch i := iv.Interface().(type) {
case string:
return strings.Contains(c, i)
case []byte:
return strings.Contains(c, string(i))
case []rune:
return strings.Contains(c, string(i))
case byte:
return bytes.IndexByte([]byte(c), i) != -1
case rune:
return bytes.IndexRune([]byte(c), i) != -1
}
case []byte:
switch i := iv.Interface().(type) {
case string:
return bytes.Contains(c, []byte(i))
case []byte:
return bytes.Contains(c, i)
case []rune:
return strings.Contains(string(c), string(i))
case byte:
return bytes.IndexByte(c, i) != -1
case rune:
return bytes.IndexRune(c, i) != -1
}
case []rune:
switch i := iv.Interface().(type) {
case string:
return strings.Contains(string(c), string(i))
case []byte:
return strings.Contains(string(c), string(i))
case []rune:
return strings.Contains(string(c), string(i))
case byte:
return strings.IndexByte(string(c), i) != -1
case rune:
return strings.IndexRune(string(c), i) != -1
}
}
if (cv.Kind() == reflect.Slice) || (cv.Kind() == reflect.Array) {
if !cv.IsValid() || cv.Len() == 0 { // 空的,就不算包含另一个,即使另一个也是空值。
return false
}
if !iv.IsValid() {
return false
}
// item是container的一个元素
for i := 0; i < cv.Len(); i++ {
if IsEqual(cv.Index(i).Interface(), iv.Interface()) {
return true
}
}
// 开始判断item的元素是否与container中的元素相等。
// 若item的长度为0,表示不包含
if (iv.Kind() != reflect.Slice) || (iv.Len() == 0) {
return false
}
// item的元素比container的元素多,必须在判断完item不是container中的一个元素之
if iv.Len() > cv.Len() {
return false
}
// 依次比较item的各个子元素是否都存在于container,且下标都相同
ivIndex := 0
for i := 0; i < cv.Len(); i++ {
if IsEqual(cv.Index(i).Interface(), iv.Index(ivIndex).Interface()) {
if (ivIndex == 0) && (i+iv.Len() > cv.Len()) {
return false
}
ivIndex++
if ivIndex == iv.Len() { // 已经遍历完iv
return true
}
} else if ivIndex > 0 {
return false
}
}
return false
} // end cv.Kind == reflect.Slice and reflect.Array
if cv.Kind() == reflect.Map {
if cv.Len() == 0 {
return false
}
if (iv.Kind() != reflect.Map) || (iv.Len() == 0) {
return false
}
if iv.Len() > cv.Len() {
return false
}
// 判断所有item的项都存在于container中
for _, key := range iv.MapKeys() {
cvItem := iv.MapIndex(key)
if !cvItem.IsValid() { // container中不包含该值。
return false
}
if !IsEqual(cvItem.Interface(), iv.MapIndex(key).Interface()) {
return false
}
}
// for中的所有判断都成立,返回true
return true
}
return false
}
const (
StyleStrit = 1 << iota // 严格的字符串比较,会忽略其它方式
StyleTrim // 去掉首尾空格
StyleSpace // 缩减所有的空格为一个
StyleCase // 不区分大小写
styleAll = StyleTrim | StyleSpace | StyleCase
)
// 将StringIsEqual()中的Style参数转换为字符串
func styleString(style int) (ret string) {
if style > styleAll {
return "<invalid style:" + strconv.Itoa(style) + ">"
}
if (style & StyleStrit) == StyleStrit {
return "StyleStrit"
}
if (style & StyleTrim) == StyleTrim {
ret += " | StyleTrim"
}
if (style & StyleSpace) == StyleSpace {
ret += " | StyleSpace"
}
if (style & StyleCase) == StyleCase {
ret += " | StyleCase"
}
return ret[3:] // 去掉第一个|
}
var spaceReplaceRegexp = regexp.MustCompile("\\s+")
// 比较两个字符串是否相等。
// 根据第三个参数style指定比较方式,style值可以是:
// - StyleStrit
// - StyleTrim
// - StyleSpace
// - StyleCase
func StringIsEqual(s1, s2 string, style int) (ret bool) {
// 若存在StyleStrit,则忽略其它比较属性。
if (style & StyleStrit) == StyleStrit {
return s1 == s2
}
if (style & StyleTrim) == StyleTrim {
s1 = strings.TrimSpace(s1)
s2 = strings.TrimSpace(s2)
}
if (style & StyleSpace) == StyleSpace {
s1 = spaceReplaceRegexp.ReplaceAllString(s1, " ")
s2 = spaceReplaceRegexp.ReplaceAllString(s2, " ")
}
if (style & StyleCase) == StyleCase {
s1 = strings.ToLower(s1)
s2 = strings.ToLower(s2)
}
return s1 == s2
}