Skip to content

Commit

Permalink
从github.com/caixw/lib.go/assert导入
Browse files Browse the repository at this point in the history
  • Loading branch information
caixw committed Dec 19, 2014
1 parent cc45d11 commit 34205cf
Show file tree
Hide file tree
Showing 10 changed files with 1,282 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ _testmain.go
*.exe
*.test
*.prof

# vim
*.swp
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
language: go
go:
- tip
- 1.4
- 1.3
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2014 issue9
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
Expand Down
218 changes: 218 additions & 0 deletions assert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
// 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]
}

// 获取调用者的信息。
//
// 输出错误信息在*_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中的所有参数将依次被传递给fmt.Sprintf()函数,
// 所以msg1[0]必须可以转换成string(如:string, []byte, []rune, fmt.Stringer)
//
// msg2参数格式与msg1完全相同,在msg1为空的情况下,会使用msg2的内容,
// 否则msg2不会启作用。
func formatMessage(msg1 []interface{}, msg2 []interface{}) string {
if len(msg1) == 0 {
msg1 = msg2
}

if len(msg1) == 0 {
return "<未提供任何错误信息>"
}

format := ""
switch v := msg1[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, msg1[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})
}
153 changes: 153 additions & 0 deletions assert_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// 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 (
"errors"
"testing"
)

func TestGetCallerInfo(t *testing.T) {
str := getCallerInfo()
if len(str) == 0 {
t.Error("getCallerInfo()无法正确返回信息")
} else {
t.Logf("getCallerInfo()返回的内容为:[%v]", str)
}
}

func TestFormatMsg(t *testing.T) {
msg1 := []interface{}{}
msg2 := []interface{}{[]rune("msg:%v"), 2}
msg3 := []interface{}{"msg:%v", 3}

str := formatMessage(msg1, msg2)
if str != "msg:2" {
t.Errorf("formatMessage(msg1,msg2)返回信息错误:[%v]", str)
}

str = formatMessage(nil, msg2)
if str != "msg:2" {
t.Errorf("formatMessage(msg1,msg2)返回信息错误:[%v]", str)
}

str = formatMessage(msg2, msg3)
if str != "msg:2" {
t.Errorf("formatMessage(msg2,msg3)返回信息错误:[%v]", str)
}
}

func TestTrue(t *testing.T) {
True(t, true)
True(t, 1 == 1, "True(1==1) falid")
}

func TestFalse(t *testing.T) {
False(t, false, "False falid")
False(t, 1 == 2, "False(1==2) falid")
}

func TestNil(t *testing.T) {
Nil(t, nil, "Nil falid")

var v interface{}
Nil(t, v, "Nil(v) falid")
}

func TestNotNil(t *testing.T) {
NotNil(t, 5, "NotNil falid")

var v interface{} = 5
NotNil(t, v, "NotNil falid")
}

func TestEqual(t *testing.T) {
Equal(t, 5, 5, "Equal(5,5) falid")

var v1, v2 interface{}
v1 = 5
v2 = 5

Equal(t, 5, v1)
Equal(t, v1, v2, "Equal(v1,v2) falid")
Equal(t, int8(126), 126)
Equal(t, int64(126), int8(126))
Equal(t, uint(7), int(7))
}

func TestNotEqual(t *testing.T) {
NotEqual(t, 5, 6, "NotEqual(5,6) falid")

var v1, v2 interface{} = 5, 6

NotEqual(t, 5, v2, "NotEqual(5,v2) falid")
NotEqual(t, v1, v2, "NotEqual(v1,v2) falid")
NotEqual(t, 128, int8(127))
}

func TestEmpty(t *testing.T) {
Empty(t, 0, "Empty(0) falid")
Empty(t, "", "Empty(``) falid")
Empty(t, false, "Empty(false) falid")
Empty(t, []string{}, "Empty(slice{}) falid")
Empty(t, []int{}, "Empty(slice{}) falid")
}

func TestNotEmpty(t *testing.T) {
NotEmpty(t, 1, "NotEmpty(1) falid")
NotEmpty(t, true, "NotEmpty(true) falid")
NotEmpty(t, []string{"ab"}, "NotEmpty(slice(abc)) falid")
}

func TestError(t *testing.T) {
err := errors.New("test")
Error(t, err, "Error(err) falid")
}

func TestNotError(t *testing.T) {
NotError(t, "123", "NotError(123) falid")

var err1 error = nil
NotError(t, err1, "var err1 error falid")
}

func TestFileExists(t *testing.T) {
FileExists(t, "./assert.go", "FileExists() falid")
}

func TestFileNotExists(t *testing.T) {
FileNotExists(t, "c:/win", "FileNotExists() falid")
}

func TestPanic(t *testing.T) {
f1 := func() {
panic("panic")
}

Panic(t, f1)
}

func TestNotPanic(t *testing.T) {
f1 := func() {
}

NotPanic(t, f1)
}

func TestContains(t *testing.T) {
Contains(t, []int{1, 2, 3}, []int8{1, 2})
}

func TestNotContains(t *testing.T) {
NotContains(t, []int{1, 2, 3}, []int8{1, 3})
}

func TestStringEqual(t *testing.T) {
StringEqual(t, "abc", "aBc", StyleCase)
}

func TestStringNotEqual(t *testing.T) {
StringNotEqual(t, "abc", "aBc", StyleStrit)
}
Loading

0 comments on commit 34205cf

Please sign in to comment.