init
This commit is contained in:
+21
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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是string,vv2是[]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是string,v2是[]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
|
||||
}
|
||||
Reference in New Issue
Block a user