├── .travis.yml ├── .gitignore ├── LICENSE ├── utils.go ├── errors_test.go ├── hello.go ├── errors_helper.go ├── README.md ├── example_test.go ├── errors.go └── testing_test.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.5 5 | - 1.6 6 | - 1.7 7 | - tip 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, chai2010 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of errors nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 . All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package errors 6 | 7 | import ( 8 | "bytes" 9 | "encoding/json" 10 | "regexp" 11 | "runtime" 12 | "strings" 13 | ) 14 | 15 | var ( 16 | reEmpty = regexp.MustCompile(`^\s*$`) 17 | reInit = regexp.MustCompile(`init·?\d+$`) // main.init·1 18 | reClosure = regexp.MustCompile(`func·?\d+$`) // main.func·001 19 | ) 20 | 21 | // name format: 22 | // runtime.goexit 23 | // runtime.main 24 | // main.init 25 | // main.main 26 | // main.init·1 -> main.init 27 | // main.func·001 -> main.func 28 | // github.com/chai2010/errors.New 29 | // ... 30 | func callerInfo(skip int) (name, file string, line int, ok bool) { 31 | pc, file, line, ok := runtime.Caller(skip) 32 | if !ok { 33 | name = "???" 34 | file = "???" 35 | line = 1 36 | return 37 | } 38 | 39 | name = runtime.FuncForPC(pc).Name() 40 | if reInit.MatchString(name) { 41 | name = reInit.ReplaceAllString(name, "init") 42 | } 43 | if reClosure.MatchString(name) { 44 | name = reClosure.ReplaceAllString(name, "func") 45 | } 46 | 47 | // Truncate file name at last file name separator. 48 | if idx := strings.LastIndex(file, "/"); idx >= 0 { 49 | file = file[idx+1:] 50 | } else if idx = strings.LastIndex(file, "\\"); idx >= 0 { 51 | file = file[idx+1:] 52 | } 53 | return 54 | } 55 | 56 | func jsonEncode(m interface{}) []byte { 57 | data, err := json.Marshal(m) 58 | if err != nil { 59 | return nil 60 | } 61 | data = bytes.Replace(data, []byte("\\u003c"), []byte("<"), -1) // < 62 | data = bytes.Replace(data, []byte("\\u003e"), []byte(">"), -1) // > 63 | data = bytes.Replace(data, []byte("\\u0026"), []byte("&"), -1) // & 64 | return data 65 | } 66 | 67 | func jsonEncodeIndent(m interface{}) []byte { 68 | data, err := json.MarshalIndent(m, "", "\t") 69 | if err != nil { 70 | return nil 71 | } 72 | data = bytes.Replace(data, []byte("\\u003c"), []byte("<"), -1) // < 73 | data = bytes.Replace(data, []byte("\\u003e"), []byte(">"), -1) // > 74 | data = bytes.Replace(data, []byte("\\u0026"), []byte("&"), -1) // & 75 | return data 76 | } 77 | 78 | func jsonEncodeString(m interface{}) string { 79 | return string(jsonEncodeIndent(m)) 80 | } 81 | 82 | func jsonDecode(data []byte, m interface{}) error { 83 | return json.Unmarshal(data, m) 84 | } 85 | -------------------------------------------------------------------------------- /errors_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 . All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package errors 6 | 7 | import ( 8 | "reflect" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | func TestError(t *testing.T) { 14 | err0 := New("err0 message") 15 | tAssert(t, "err0 message" == err0.Error()) 16 | tAssert(t, len(err0.(Error).Wraped()) == 0) 17 | 18 | err1 := Wrap(err0, "err1 message") 19 | tAssert(t, strings.HasPrefix(err1.Error(), "err1 message")) 20 | tAssert(t, strings.HasSuffix(err1.Error(), "{"+err0.Error()+"}")) 21 | tAssert(t, strings.Contains(err1.Error(), err0.Error())) 22 | tAssert(t, len(err1.(Error).Wraped()) == 1) 23 | tAssert(t, err1.(Error).Wraped()[0] == err0) 24 | } 25 | 26 | func TestError_callar(t *testing.T) { 27 | err := New("error message") 28 | callers := err.(Error).Caller() 29 | tAssert(t, len(callers) == 2) 30 | tAssert(t, callers[0].FuncName == "github.com/chai2010/errors.TestError_callar") 31 | tAssert(t, callers[len(callers)-2].FuncName == "github.com/chai2010/errors.TestError_callar") 32 | tAssert(t, callers[len(callers)-1].FuncName == "testing.tRunner") 33 | } 34 | 35 | func TestError_wraped(t *testing.T) { 36 | err0 := New("err0") 37 | err1 := Wrap(err0, "err1") 38 | err2 := func() error { return Wrap(err1, "err2") }() 39 | err3 := func() error { return Wrap(err2, "err3") }() 40 | 41 | tAssert(t, len(err0.(Error).Wraped()) == 0) 42 | tAssert(t, len(err1.(Error).Wraped()) == 1) 43 | tAssert(t, len(err2.(Error).Wraped()) == 2) 44 | tAssert(t, len(err3.(Error).Wraped()) == 3) 45 | 46 | tAssert(t, err1.(Error).Wraped()[0] == err0) 47 | 48 | tAssert(t, err2.(Error).Wraped()[0] == err1) 49 | tAssert(t, err2.(Error).Wraped()[1] == err0) 50 | 51 | tAssert(t, err3.(Error).Wraped()[0] == err2) 52 | tAssert(t, err3.(Error).Wraped()[1] == err1) 53 | tAssert(t, err3.(Error).Wraped()[2] == err0) 54 | } 55 | 56 | func TestError_json(t *testing.T) { 57 | err0 := New("err0") 58 | err1 := Wrap(err0, "err1") 59 | err2 := func() error { return Wrap(err1, "err2") }() 60 | err3 := func() error { return Wrap(err2, "err3") }() 61 | 62 | errx := MustFromJson(string(jsonEncode(err3))) 63 | if !reflect.DeepEqual(errx, err3) { 64 | t.Logf("errx: %s\n", jsonEncodeString(errx)) 65 | t.Logf("err3: %s\n", jsonEncodeString(err3)) 66 | t.Fatal(errx, "!=", err3) 67 | } 68 | } 69 | 70 | func TestCaller(t *testing.T) { 71 | skip0Caller := Caller(0) 72 | tAssert(t, len(skip0Caller) >= 2) 73 | tAssert(t, skip0Caller[0].FuncName == "github.com/chai2010/errors.Caller") 74 | tAssert(t, skip0Caller[1].FuncName == "github.com/chai2010/errors.TestCaller") 75 | 76 | skip1Caller := Caller(1) 77 | tAssert(t, len(skip1Caller) >= 2) 78 | tAssert(t, skip1Caller[0].FuncName == "github.com/chai2010/errors.TestCaller") 79 | tAssert(t, skip1Caller[1].FuncName == "testing.tRunner") 80 | } 81 | -------------------------------------------------------------------------------- /hello.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 . All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build ingore 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | 12 | "github.com/chai2010/errors" 13 | ) 14 | 15 | var ( 16 | e0 = errors.New("err:init0") 17 | e1 error 18 | e2 error 19 | ) 20 | 21 | func init() { 22 | e1 = errors.New("err:init1") 23 | } 24 | 25 | func init() { 26 | e2 = errors.New("err:init2") 27 | } 28 | 29 | func main() { 30 | var e3 = errors.New("err:main3") 31 | var e4 = func() error { 32 | return errors.New("err:main4") 33 | }() 34 | var e5 = errors.Wrap(e1, "err:main5") 35 | var e6 = errors.Wrap(e5, "err:main6") 36 | var e7 = errors.Wrap(e6, "err:main7") 37 | 38 | fmt.Println(e0, e0.(errors.Error).Caller()) 39 | fmt.Println(e1, e1.(errors.Error).Caller()) 40 | fmt.Println(e2, e2.(errors.Error).Caller()) 41 | fmt.Println(e3, e3.(errors.Error).Caller()) 42 | fmt.Println(e4, e4.(errors.Error).Caller()) 43 | fmt.Println(e5, e5.(errors.Error).Caller()) 44 | fmt.Println(e6, e6.(errors.Error).Caller()) 45 | 46 | for i, e := range e7.(errors.Error).Wraped() { 47 | fmt.Printf("err7: wraped(%d): %v\n", i, e) 48 | } 49 | 50 | fmt.Println("err7:", e7.(fmt.Stringer).String()) 51 | } 52 | 53 | /* 54 | Output: 55 | err:init0 [{main.init hello.go 16}] 56 | err:init1 [{main.init.1 hello.go 22} {main.init hello.go 51}] 57 | err:init2 [{main.init.2 hello.go 26} {main.init hello.go 51}] 58 | err:main3 [{main.main hello.go 30}] 59 | err:main4 [{main.main.func1 hello.go 32} {main.main hello.go 33}] 60 | err:main5 -> {err:init1} [{main.main hello.go 34}] 61 | err:main6 -> {err:main5 -> {err:init1}} [{main.main hello.go 35}] 62 | err7: wraped(0): err:main6 -> {err:main5 -> {err:init1}} 63 | err7: wraped(1): err:main5 -> {err:init1} 64 | err7: wraped(2): err:init1 65 | err7: { 66 | "Code": 0, 67 | "Error": "err:main7 -> {err:main6 -> {err:main5 -> {err:init1}}}", 68 | "Caller": [ 69 | { 70 | "FuncName": "main.main", 71 | "FileName": "hello.go", 72 | "FileLine": 36 73 | } 74 | ], 75 | "Wraped": [ 76 | { 77 | "Code": 0, 78 | "Error": "err:main6 -> {err:main5 -> {err:init1}}", 79 | "Caller": [ 80 | { 81 | "FuncName": "main.main", 82 | "FileName": "hello.go", 83 | "FileLine": 35 84 | } 85 | ] 86 | }, 87 | { 88 | "Code": 0, 89 | "Error": "err:main5 -> {err:init1}", 90 | "Caller": [ 91 | { 92 | "FuncName": "main.main", 93 | "FileName": "hello.go", 94 | "FileLine": 34 95 | } 96 | ] 97 | }, 98 | { 99 | "Code": 0, 100 | "Error": "err:init1", 101 | "Caller": [ 102 | { 103 | "FuncName": "main.init.1", 104 | "FileName": "hello.go", 105 | "FileLine": 22 106 | }, 107 | { 108 | "FuncName": "main.init", 109 | "FileName": "hello.go", 110 | "FileLine": 51 111 | } 112 | ] 113 | } 114 | ] 115 | } 116 | */ 117 | -------------------------------------------------------------------------------- /errors_helper.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 . All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package errors 6 | 7 | import ( 8 | "errors" 9 | ) 10 | 11 | var ( 12 | _ error = _ErrorString("") 13 | ) 14 | 15 | type _ErrorString string 16 | 17 | func (e _ErrorString) Error() string { return string(e) } 18 | 19 | type _ErrorStruct struct { 20 | XCode int `json:"Code"` 21 | XError _ErrorString `json:"Error"` 22 | XCaller []CallerInfo `json:"Caller,omitempty"` 23 | XWraped []*_ErrorStruct `json:"Wraped,omitempty"` 24 | } 25 | 26 | func newErrorStruct(err error) *_ErrorStruct { 27 | if err == nil { 28 | return nil 29 | } 30 | 31 | x, ok := err.(Error) 32 | if !ok { 33 | return &_ErrorStruct{ 34 | XError: _ErrorString(x.Error()), 35 | } 36 | } 37 | p := &_ErrorStruct{ 38 | XCode: x.Code(), 39 | XError: _ErrorString(x.Error()), 40 | XCaller: x.Caller(), 41 | } 42 | for _, it := range x.Wraped() { 43 | if it, ok := it.(Error); ok { 44 | p.XWraped = append(p.XWraped, &_ErrorStruct{ 45 | XCode: it.Code(), 46 | XError: _ErrorString(it.Error()), 47 | XCaller: it.Caller(), 48 | }) 49 | } else { 50 | p.XWraped = append(p.XWraped, &_ErrorStruct{ 51 | XError: _ErrorString(it.Error()), 52 | }) 53 | } 54 | } 55 | return p 56 | } 57 | 58 | func newErrorStructFromJson(json string) (p *_ErrorStruct, err error) { 59 | if json == "" || reEmpty.MatchString(json) { 60 | return nil, nil 61 | } 62 | p = new(_ErrorStruct) 63 | if err = jsonDecode([]byte(json), p); err != nil { 64 | return nil, err 65 | } 66 | if p.XError == "" && p.XCode == 0 { 67 | return nil, nil 68 | } 69 | return 70 | } 71 | 72 | func (p *_ErrorStruct) ToError() *_Error { 73 | if p == nil { 74 | return nil 75 | } 76 | 77 | if p.XError == "" && p.XCode == 0 { 78 | return &_Error{} 79 | } 80 | 81 | errx := &_Error{ 82 | XCode: p.XCode, 83 | XError: errors.New(string(p.XError)), 84 | XCaller: p.XCaller, 85 | } 86 | for i := len(p.XWraped) - 1; i >= 0; i-- { 87 | if p.XWraped[i].XError == "" && p.XWraped[i].XCode == 0 { 88 | continue 89 | } 90 | if len(errx.XWraped) == 0 { 91 | if p.XWraped[i].XCode == 0 && len(p.XWraped[i].XCaller) == 0 { 92 | errx.XWraped = []error{errors.New(string(p.XWraped[i].XError))} 93 | continue 94 | } 95 | } 96 | errx.XWraped = append( 97 | []error{&_Error{ 98 | XCode: p.XWraped[i].XCode, 99 | XError: errors.New(string(p.XWraped[i].XError)), 100 | XCaller: p.XWraped[i].XCaller, 101 | XWraped: errx.XWraped, 102 | }}, 103 | errx.XWraped..., 104 | ) 105 | } 106 | return errx 107 | } 108 | 109 | func (p *_ErrorStruct) ToErrorInterface() Error { 110 | if x := p.ToError(); x != nil { 111 | return x 112 | } 113 | return nil 114 | } 115 | 116 | func (p *_ErrorStruct) ToStdError() error { 117 | if p.XError == "" && p.XCode == 0 { 118 | return nil 119 | } 120 | return p.ToErrorInterface() 121 | } 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | - *Go语言QQ群: 102319854, 1055927514* 2 | - *凹语言(凹读音“Wa”)(The Wa Programming Language): https://github.com/wa-lang/wa* 3 | 4 | ---- 5 | 6 | # Go errors process 7 | 8 | [![Build Status](https://travis-ci.org/chai2010/errors.svg)](https://travis-ci.org/chai2010/errors) 9 | [![GoDoc](https://godoc.org/github.com/chai2010/errors?status.svg)](https://godoc.org/github.com/chai2010/errors) 10 | 11 | # Install 12 | 13 | 1. `go get github.com/chai2010/errors` 14 | 2. `go run hello.go` 15 | 16 | 17 | # Example 18 | 19 | ```Go 20 | package main 21 | 22 | import ( 23 | "fmt" 24 | 25 | "github.com/chai2010/errors" 26 | ) 27 | 28 | var ( 29 | e0 = errors.New("err:init0") 30 | e1 error 31 | e2 error 32 | ) 33 | 34 | func init() { 35 | e1 = errors.New("err:init1") 36 | } 37 | 38 | func init() { 39 | e2 = errors.New("err:init2") 40 | } 41 | 42 | func main() { 43 | var e3 = errors.New("err:main3") 44 | var e4 = func() error { 45 | return errors.New("err:main4") 46 | }() 47 | var e5 = errors.Wrap(e1, "err:main5") 48 | var e6 = errors.Wrap(e5, "err:main6") 49 | var e7 = errors.Wrap(e6, "err:main7") 50 | 51 | fmt.Println(e0, e0.(errors.Error).Caller()) 52 | fmt.Println(e1, e1.(errors.Error).Caller()) 53 | fmt.Println(e2, e2.(errors.Error).Caller()) 54 | fmt.Println(e3, e3.(errors.Error).Caller()) 55 | fmt.Println(e4, e4.(errors.Error).Caller()) 56 | fmt.Println(e5, e5.(errors.Error).Caller()) 57 | fmt.Println(e6, e6.(errors.Error).Caller()) 58 | 59 | for i, e := range e7.(errors.Error).Wraped() { 60 | fmt.Printf("err7: wraped(%d): %v\n", i, e) 61 | } 62 | 63 | fmt.Println("err7:", e7.(fmt.Stringer).String()) 64 | } 65 | ``` 66 | 67 | Output: 68 | 69 | ``` 70 | err:init0 [{main.init hello.go 16}] 71 | err:init1 [{main.init.1 hello.go 22} {main.init hello.go 51}] 72 | err:init2 [{main.init.2 hello.go 26} {main.init hello.go 51}] 73 | err:main3 [{main.main hello.go 30}] 74 | err:main4 [{main.main.func1 hello.go 32} {main.main hello.go 33}] 75 | err:main5 -> {err:init1} [{main.main hello.go 34}] 76 | err:main6 -> {err:main5 -> {err:init1}} [{main.main hello.go 35}] 77 | err7: wraped(0): err:main6 -> {err:main5 -> {err:init1}} 78 | err7: wraped(1): err:main5 -> {err:init1} 79 | err7: wraped(2): err:init1 80 | err7: { 81 | "Code": 0, 82 | "Error": "err:main7 -> {err:main6 -> {err:main5 -> {err:init1}}}", 83 | "Caller": [ 84 | { 85 | "FuncName": "main.main", 86 | "FileName": "hello.go", 87 | "FileLine": 36 88 | } 89 | ], 90 | "Wraped": [ 91 | { 92 | "Code": 0, 93 | "Error": "err:main6 -> {err:main5 -> {err:init1}}", 94 | "Caller": [ 95 | { 96 | "FuncName": "main.main", 97 | "FileName": "hello.go", 98 | "FileLine": 35 99 | } 100 | ] 101 | }, 102 | { 103 | "Code": 0, 104 | "Error": "err:main5 -> {err:init1}", 105 | "Caller": [ 106 | { 107 | "FuncName": "main.main", 108 | "FileName": "hello.go", 109 | "FileLine": 34 110 | } 111 | ] 112 | }, 113 | { 114 | "Code": 0, 115 | "Error": "err:init1", 116 | "Caller": [ 117 | { 118 | "FuncName": "main.init.1", 119 | "FileName": "hello.go", 120 | "FileLine": 22 121 | }, 122 | { 123 | "FuncName": "main.init", 124 | "FileName": "hello.go", 125 | "FileLine": 51 126 | } 127 | ] 128 | } 129 | ] 130 | } 131 | ``` 132 | 133 | # BUGS 134 | 135 | Report bugs to . 136 | 137 | Thanks! 138 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 . All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package errors_test 6 | 7 | import ( 8 | "encoding/json" 9 | "fmt" 10 | "log" 11 | "reflect" 12 | "runtime" 13 | "strings" 14 | 15 | "github.com/chai2010/errors" 16 | ) 17 | 18 | func Example() { 19 | err0 := errors.New("err0") 20 | err1 := errors.Wrap(err0, "err1") 21 | err2 := func() error { return errors.Wrap(err1, "err2") }() 22 | err3 := func() error { return errors.Wrap(err2, "err3") }() 23 | 24 | fmt.Println(err0) 25 | fmt.Println(err1) 26 | fmt.Println(err2) 27 | fmt.Println(err3) 28 | // Output: 29 | // err0 30 | // err1 -> {err0} 31 | // err2 -> {err1 -> {err0}} 32 | // err3 -> {err2 -> {err1 -> {err0}}} 33 | } 34 | 35 | func Example_code() { 36 | err := errors.NewWithCode(404, "http error code") 37 | 38 | fmt.Println(err) 39 | fmt.Println(err.(errors.Error).Code()) 40 | // Output: 41 | // http error code 42 | // 404 43 | } 44 | 45 | func Example_caller() { 46 | err := errors.New("error message") 47 | 48 | fmt.Println(err) 49 | for i, x := range err.(errors.Error).Caller() { 50 | fmt.Printf("caller:%d: %s\n", i, x.FuncName) 51 | } 52 | // Output: 53 | // error message 54 | // caller:0: github.com/chai2010/errors_test.Example_caller 55 | // caller:1: testing.runExample 56 | // caller:2: testing.RunExamples 57 | // caller:3: testing.(*M).Run 58 | // caller:4: main.main 59 | } 60 | 61 | func Example_wraped() { 62 | err0 := errors.New("err0") 63 | err1 := errors.Wrap(err0, "err1") 64 | err2 := func() error { return errors.Wrap(err1, "err2") }() 65 | err3 := func() error { return errors.Wrap(err2, "err3") }() 66 | 67 | fmt.Println(err3) 68 | for j, x := range err3.(errors.Error).Caller() { 69 | if j == 0 { 70 | // Closure path is different between Go1.4 and Go1.5 71 | ver := runtime.Version() 72 | if strings.HasPrefix(ver, "go1.3") || strings.HasPrefix(ver, "go1.4") { 73 | if x.FuncName == "github.com/chai2010/errors_test.func" { 74 | x.FuncName = "github.com/chai2010/errors_test.Example_wraped.func" 75 | } 76 | } 77 | } 78 | fmt.Printf("caller:%d: %s\n", j, x.FuncName) 79 | } 80 | for i, err := range err3.(errors.Error).Wraped() { 81 | fmt.Printf("wraped:%d: %v\n", i, err) 82 | for j, x := range err.(errors.Error).Caller() { 83 | fmt.Printf(" caller:%d: %s\n", j, x.FuncName) 84 | } 85 | } 86 | // Output: 87 | // err3 -> {err2 -> {err1 -> {err0}}} 88 | // caller:0: github.com/chai2010/errors_test.Example_wraped.func 89 | // caller:1: github.com/chai2010/errors_test.Example_wraped 90 | // caller:2: testing.runExample 91 | // caller:3: testing.RunExamples 92 | // caller:4: testing.(*M).Run 93 | // caller:5: main.main 94 | // wraped:0: err2 -> {err1 -> {err0}} 95 | // caller:0: github.com/chai2010/errors_test.Example_wraped.func 96 | // caller:1: github.com/chai2010/errors_test.Example_wraped 97 | // caller:2: testing.runExample 98 | // caller:3: testing.RunExamples 99 | // caller:4: testing.(*M).Run 100 | // caller:5: main.main 101 | // wraped:1: err1 -> {err0} 102 | // caller:0: github.com/chai2010/errors_test.Example_wraped 103 | // caller:1: testing.runExample 104 | // caller:2: testing.RunExamples 105 | // caller:3: testing.(*M).Run 106 | // caller:4: main.main 107 | // wraped:2: err0 108 | // caller:0: github.com/chai2010/errors_test.Example_wraped 109 | // caller:1: testing.runExample 110 | // caller:2: testing.RunExamples 111 | // caller:3: testing.(*M).Run 112 | // caller:4: main.main 113 | } 114 | 115 | func Example_json() { 116 | err0 := errors.New("err0") 117 | err1 := errors.Wrap(err0, "err1") 118 | err2 := func() error { return errors.Wrap(err1, "err2") }() 119 | err3 := func() error { return errors.Wrap(err2, "err3") }() 120 | 121 | err3JsonData, err := json.Marshal(err3) 122 | if err != nil { 123 | log.Fatal(err) 124 | } 125 | 126 | errx := errors.MustFromJson(string(err3JsonData)) 127 | if !reflect.DeepEqual(errx, err3) { 128 | log.Fatal("errors_test.Example_json:", errx, "!=", err3) 129 | } 130 | 131 | fmt.Println("done") 132 | // Output: 133 | // done 134 | } 135 | 136 | func Example_stringer() { 137 | err0 := errors.New("err0") 138 | err1 := errors.Wrap(err0, "err1") 139 | err2 := func() error { return errors.Wrap(err1, "err2") }() 140 | err3 := func() error { return errors.Wrap(err2, "err3") }() 141 | 142 | fmt.Println(err3) // fmt.Println(err3.Error()) 143 | fmt.Println(err3.(fmt.Stringer).String()) // print json string 144 | } 145 | 146 | func ExampleCaller_skip0() { 147 | skip0Caller := errors.Caller(0) 148 | 149 | fmt.Println(skip0Caller[0].FuncName) 150 | fmt.Println(skip0Caller[1].FuncName) 151 | 152 | // Output: 153 | // github.com/chai2010/errors.Caller 154 | // github.com/chai2010/errors_test.ExampleCaller_skip0 155 | } 156 | 157 | func ExampleCaller_skip1() { 158 | skip1Caller := errors.Caller(1) 159 | 160 | fmt.Println(skip1Caller[0].FuncName) 161 | fmt.Println(skip1Caller[1].FuncName) 162 | 163 | // Output: 164 | // github.com/chai2010/errors_test.ExampleCaller_skip1 165 | // testing.runExample 166 | } 167 | -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 . All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package errors implements functions to manipulate errors. 6 | package errors // import "github.com/chai2010/errors" 7 | 8 | import ( 9 | "errors" 10 | "fmt" 11 | "strings" 12 | ) 13 | 14 | var ( 15 | _ Error = (*_Error)(nil) 16 | _ fmt.Stringer = (*_Error)(nil) 17 | ) 18 | 19 | type Error interface { 20 | Caller() []CallerInfo 21 | Wraped() []error 22 | Code() int 23 | error 24 | 25 | private() 26 | } 27 | 28 | type _Error struct { 29 | XCode int `json:"Code"` 30 | XError error `json:"Error"` 31 | XCaller []CallerInfo `json:"Caller,omitempty"` 32 | XWraped []error `json:"Wraped,omitempty"` 33 | } 34 | 35 | type CallerInfo struct { 36 | FuncName string 37 | FileName string 38 | FileLine int 39 | } 40 | 41 | func New(msg string) error { 42 | return &_Error{ 43 | XCaller: Caller(2), 44 | XError: errors.New(msg), 45 | } 46 | } 47 | 48 | func NewFrom(err error) error { 49 | if err == nil { 50 | return nil 51 | } 52 | if e, ok := err.(Error); ok { 53 | return e 54 | } 55 | return &_Error{ 56 | XCaller: Caller(2), 57 | XError: err, 58 | } 59 | } 60 | 61 | func Newf(format string, args ...interface{}) error { 62 | return &_Error{ 63 | XCaller: Caller(2), 64 | XError: fmt.Errorf(format, args...), 65 | } 66 | } 67 | 68 | func NewWithCode(code int, msg string) error { 69 | return &_Error{ 70 | XCaller: Caller(2), 71 | XError: errors.New(msg), 72 | XCode: code, 73 | } 74 | } 75 | 76 | func NewWithCodef(code int, format string, args ...interface{}) error { 77 | return &_Error{ 78 | XCaller: Caller(2), 79 | XError: fmt.Errorf(format, args...), 80 | XCode: code, 81 | } 82 | } 83 | 84 | func MustFromJson(json string) error { 85 | p, err := newErrorStructFromJson(json) 86 | if err != nil { 87 | panic(err) 88 | } 89 | return p.ToStdError() 90 | } 91 | 92 | func FromJson(json string) (Error, error) { 93 | p, err := newErrorStructFromJson(json) 94 | if err != nil { 95 | return nil, &_Error{ 96 | XCaller: Caller(1), // skip == 1 97 | XWraped: []error{err}, 98 | XError: errors.New(fmt.Sprintf("errors.FromJson: jsonDecode failed: %v!", err)), 99 | } 100 | } 101 | 102 | return p.ToErrorInterface(), nil 103 | } 104 | 105 | func ToJson(err error) string { 106 | if p, ok := (err).(*_Error); ok { 107 | return p.String() 108 | } 109 | p := &_Error{XError: err} 110 | return p.String() 111 | } 112 | 113 | func Wrap(err error, msg string) error { 114 | p := &_Error{ 115 | XCaller: Caller(2), 116 | XWraped: []error{err}, 117 | XError: errors.New(fmt.Sprintf("%s -> {%v}", msg, err)), 118 | } 119 | if e, ok := err.(Error); ok { 120 | p.XWraped = append(p.XWraped, e.Wraped()...) 121 | } 122 | return p 123 | } 124 | 125 | func Wrapf(err error, format string, args ...interface{}) error { 126 | p := &_Error{ 127 | XCaller: Caller(2), 128 | XWraped: []error{err}, 129 | XError: errors.New(fmt.Sprintf("%s -> {%v}", fmt.Sprintf(format, args...), err)), 130 | } 131 | if e, ok := err.(Error); ok { 132 | p.XWraped = append(p.XWraped, e.Wraped()...) 133 | } 134 | return p 135 | } 136 | 137 | func WrapWithCode(code int, err error, msg string) error { 138 | p := &_Error{ 139 | XCaller: Caller(2), 140 | XWraped: []error{err}, 141 | XError: errors.New(fmt.Sprintf("%s -> {%v}", msg, err)), 142 | XCode: code, 143 | } 144 | if e, ok := err.(Error); ok { 145 | p.XWraped = append(p.XWraped, e.Wraped()...) 146 | } 147 | return p 148 | } 149 | 150 | func WrapWithCodef(code int, err error, format string, args ...interface{}) error { 151 | p := &_Error{ 152 | XCaller: Caller(2), 153 | XWraped: []error{err}, 154 | XError: errors.New(fmt.Sprintf("%s -> {%v}", fmt.Sprintf(format, args...), err)), 155 | XCode: code, 156 | } 157 | if e, ok := err.(Error); ok { 158 | p.XWraped = append(p.XWraped, e.Wraped()...) 159 | } 160 | return p 161 | } 162 | 163 | func Caller(skip int) []CallerInfo { 164 | var infos []CallerInfo 165 | for ; ; skip++ { 166 | name, file, line, ok := callerInfo(skip + 1) 167 | if !ok { 168 | return infos 169 | } 170 | if strings.HasPrefix(name, "runtime.") { 171 | return infos 172 | } 173 | infos = append(infos, CallerInfo{ 174 | FuncName: name, 175 | FileName: file, 176 | FileLine: line, 177 | }) 178 | } 179 | panic("unreached!") 180 | } 181 | 182 | func (p *_Error) Caller() []CallerInfo { 183 | return p.XCaller 184 | } 185 | 186 | func (p *_Error) Wraped() []error { 187 | return p.XWraped 188 | } 189 | 190 | func (p *_Error) Error() string { 191 | return p.XError.Error() 192 | } 193 | 194 | func (p *_Error) Code() int { 195 | return p.XCode 196 | } 197 | 198 | func (p *_Error) String() string { 199 | return jsonEncodeString(p) 200 | } 201 | 202 | func (p *_Error) MarshalJSON() ([]byte, error) { 203 | return jsonEncode(newErrorStruct(p)), nil 204 | } 205 | 206 | func (p *_Error) UnmarshalJSON(data []byte) error { 207 | px, err := newErrorStructFromJson(string(data)) 208 | if err != nil { 209 | return err 210 | } 211 | if px != nil { 212 | *p = *px.ToError() 213 | } else { 214 | *p = _Error{} 215 | } 216 | return nil 217 | } 218 | 219 | func (p *_Error) private() { 220 | panic("unreached!") 221 | } 222 | -------------------------------------------------------------------------------- /testing_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 . All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // testing helper, please fork this file, and fix the package name. 6 | 7 | package errors 8 | 9 | import ( 10 | "fmt" 11 | "math" 12 | "os" 13 | "reflect" 14 | "regexp" 15 | "runtime" 16 | "strings" 17 | "testing" 18 | ) 19 | 20 | func tIsIntType(v interface{}) bool { 21 | switch reflect.ValueOf(v).Kind() { 22 | case reflect.Int, 23 | reflect.Int8, 24 | reflect.Int16, 25 | reflect.Int32, 26 | reflect.Int64: 27 | return true 28 | } 29 | return false 30 | } 31 | 32 | func tIsUintType(v interface{}) bool { 33 | switch reflect.ValueOf(v).Kind() { 34 | case reflect.Uint, 35 | reflect.Uint8, 36 | reflect.Uint16, 37 | reflect.Uint32, 38 | reflect.Uint64, 39 | reflect.Uintptr: 40 | return true 41 | } 42 | return false 43 | } 44 | 45 | func tIsFloatType(v interface{}) bool { 46 | switch reflect.ValueOf(v).Kind() { 47 | case reflect.Float32, 48 | reflect.Float64: 49 | return true 50 | } 51 | return false 52 | } 53 | 54 | func tIsNumberType(v interface{}) bool { 55 | switch reflect.ValueOf(v).Kind() { 56 | case reflect.Int, 57 | reflect.Int8, 58 | reflect.Int16, 59 | reflect.Int32, 60 | reflect.Int64, 61 | reflect.Uint, 62 | reflect.Uint8, 63 | reflect.Uint16, 64 | reflect.Uint32, 65 | reflect.Uint64, 66 | reflect.Uintptr, 67 | reflect.Float32, 68 | reflect.Float64, 69 | reflect.Complex64, 70 | reflect.Complex128: 71 | return true 72 | } 73 | return false 74 | } 75 | 76 | func tIsNumberEqual(a, b interface{}) bool { 77 | if tIsNumberType(a) && tIsNumberType(b) { 78 | return fmt.Sprintf("%v", a) == fmt.Sprintf("%v", b) 79 | } 80 | return false 81 | } 82 | 83 | func tCallerFileLine(skip int) (file string, line int) { 84 | _, file, line, ok := runtime.Caller(skip + 1) 85 | if ok { 86 | // Truncate file name at last file name separator. 87 | if index := strings.LastIndex(file, "/"); index >= 0 { 88 | file = file[index+1:] 89 | } else if index = strings.LastIndex(file, "\\"); index >= 0 { 90 | file = file[index+1:] 91 | } 92 | } else { 93 | file = "???" 94 | line = 1 95 | } 96 | return 97 | } 98 | 99 | func tAssert(t testing.TB, condition bool, args ...interface{}) { 100 | if !condition { 101 | file, line := tCallerFileLine(1) 102 | if msg := fmt.Sprint(args...); msg != "" { 103 | t.Fatalf("%s:%d: tAssert failed, %s", file, line, msg) 104 | } else { 105 | t.Fatalf("%s:%d: tAssert failed", file, line) 106 | } 107 | } 108 | } 109 | 110 | func tAssertf(t *testing.T, condition bool, format string, a ...interface{}) { 111 | if !condition { 112 | file, line := tCallerFileLine(1) 113 | if msg := fmt.Sprintf(format, a...); msg != "" { 114 | t.Fatalf("%s:%d: tAssert failed, %s", file, line, msg) 115 | } else { 116 | t.Fatalf("%s:%d: tAssert failed", file, line) 117 | } 118 | } 119 | } 120 | 121 | func tAssertNil(t testing.TB, p interface{}, args ...interface{}) { 122 | if p != nil { 123 | file, line := tCallerFileLine(1) 124 | if msg := fmt.Sprint(args...); msg != "" { 125 | if err, ok := p.(error); ok && err != nil { 126 | t.Fatalf("%s:%d: tAssertNil failed, err = %v, %s", file, line, err, msg) 127 | } else { 128 | t.Fatalf("%s:%d: tAssertNil failed, %s", file, line, msg) 129 | } 130 | } else { 131 | if err, ok := p.(error); ok && err != nil { 132 | t.Fatalf("%s:%d: tAssertNil failed, err = %v", file, line, err) 133 | } else { 134 | t.Fatalf("%s:%d: tAssertNil failed", file, line) 135 | } 136 | } 137 | } 138 | } 139 | 140 | func tAssertNotNil(t testing.TB, p interface{}, args ...interface{}) { 141 | if p == nil { 142 | file, line := tCallerFileLine(1) 143 | if msg := fmt.Sprint(args...); msg != "" { 144 | t.Fatalf("%s:%d: tAssertNotNil failed, %s", file, line, msg) 145 | } else { 146 | t.Fatalf("%s:%d: tAssertNotNil failed", file, line) 147 | } 148 | } 149 | } 150 | 151 | func tAssertTrue(t testing.TB, condition bool, args ...interface{}) { 152 | if condition != true { 153 | file, line := tCallerFileLine(1) 154 | if msg := fmt.Sprint(args...); msg != "" { 155 | t.Fatalf("%s:%d: tAssertTrue failed, %s", file, line, msg) 156 | } else { 157 | t.Fatalf("%s:%d: tAssertTrue failed", file, line) 158 | } 159 | } 160 | } 161 | 162 | func tAssertFalse(t testing.TB, condition bool, args ...interface{}) { 163 | if condition != false { 164 | file, line := tCallerFileLine(1) 165 | if msg := fmt.Sprint(args...); msg != "" { 166 | t.Fatalf("%s:%d: tAssertFalse failed, %s", file, line, msg) 167 | } else { 168 | t.Fatalf("%s:%d: tAssertFalse failed", file, line) 169 | } 170 | } 171 | } 172 | 173 | func tAssertEqual(t testing.TB, expected, got interface{}, args ...interface{}) { 174 | if !reflect.DeepEqual(expected, got) { 175 | file, line := tCallerFileLine(1) 176 | if msg := fmt.Sprint(args...); msg != "" { 177 | t.Fatalf("%s:%d: tAssertEqual failed, expected = %v, got = %v, %s", file, line, expected, got, msg) 178 | } else { 179 | t.Fatalf("%s:%d: tAssertEqual failed, expected = %v, got = %v", file, line, expected, got) 180 | } 181 | } 182 | } 183 | 184 | func tAssertNotEqual(t testing.TB, expected, got interface{}, args ...interface{}) { 185 | if reflect.DeepEqual(expected, got) { 186 | file, line := tCallerFileLine(1) 187 | if msg := fmt.Sprint(args...); msg != "" { 188 | t.Fatalf("%s:%d: tAssertNotEqual failed, expected = %v, got = %v, %s", file, line, expected, got, msg) 189 | } else { 190 | t.Fatalf("%s:%d: tAssertNotEqual failed, expected = %v, got = %v", file, line, expected, got) 191 | } 192 | } 193 | } 194 | 195 | func tAssertNear(t testing.TB, expected, got, abs float64, args ...interface{}) { 196 | if math.Abs(expected-got) > abs { 197 | file, line := tCallerFileLine(1) 198 | if msg := fmt.Sprint(args...); msg != "" { 199 | t.Fatalf("%s:%d: tAssertNear failed, expected = %v, got = %v, abs = %v, %s", file, line, expected, got, abs, msg) 200 | } else { 201 | t.Fatalf("%s:%d: tAssertNear failed, expected = %v, got = %v, abs = %v", file, line, expected, got, abs) 202 | } 203 | } 204 | } 205 | 206 | func tAssertBetween(t testing.TB, min, max, val float64, args ...interface{}) { 207 | if val < min || max < val { 208 | file, line := tCallerFileLine(1) 209 | if msg := fmt.Sprint(args...); msg != "" { 210 | t.Fatalf("%s:%d: tAssertBetween failed, min = %v, max = %v, val = %v, %s", file, line, min, max, val, msg) 211 | } else { 212 | t.Fatalf("%s:%d: tAssertBetween failed, min = %v, max = %v, val = %v", file, line, min, max, val) 213 | } 214 | } 215 | } 216 | 217 | func tAssertNotBetween(t testing.TB, min, max, val float64, args ...interface{}) { 218 | if min <= val && val <= max { 219 | file, line := tCallerFileLine(1) 220 | if msg := fmt.Sprint(args...); msg != "" { 221 | t.Fatalf("%s:%d: tAssertNotBetween failed, min = %v, max = %v, val = %v, %s", file, line, min, max, val, msg) 222 | } else { 223 | t.Fatalf("%s:%d: tAssertNotBetween failed, min = %v, max = %v, val = %v", file, line, min, max, val) 224 | } 225 | } 226 | } 227 | 228 | func tAssertMatch(t testing.TB, expectedPattern string, got []byte, args ...interface{}) { 229 | if matched, err := regexp.Match(expectedPattern, got); err != nil || !matched { 230 | file, line := tCallerFileLine(1) 231 | if err != nil { 232 | if msg := fmt.Sprint(args...); msg != "" { 233 | t.Fatalf("%s:%d: tAssertMatch failed, expected = %q, got = %v, err = %v, %s", file, line, expectedPattern, got, err, msg) 234 | } else { 235 | t.Fatalf("%s:%d: tAssertMatch failed, expected = %q, got = %v, err = %v", file, line, expectedPattern, got, err) 236 | } 237 | } else { 238 | if msg := fmt.Sprint(args...); msg != "" { 239 | t.Fatalf("%s:%d: tAssertMatch failed, expected = %q, got = %v, %s", file, line, expectedPattern, got, msg) 240 | } else { 241 | t.Fatalf("%s:%d: tAssertMatch failed, expected = %q, got = %v", file, line, expectedPattern, got) 242 | } 243 | } 244 | } 245 | } 246 | 247 | func tAssertMatchString(t testing.TB, expectedPattern, got string, args ...interface{}) { 248 | if matched, err := regexp.MatchString(expectedPattern, got); err != nil || !matched { 249 | file, line := tCallerFileLine(1) 250 | if err != nil { 251 | if msg := fmt.Sprint(args...); msg != "" { 252 | t.Fatalf("%s:%d: tAssertMatchString failed, expected = %q, got = %v, err = %v, %s", file, line, expectedPattern, got, err, msg) 253 | } else { 254 | t.Fatalf("%s:%d: tAssertMatchString failed, expected = %q, got = %v, err = %v", file, line, expectedPattern, got, err) 255 | } 256 | } else { 257 | if msg := fmt.Sprint(args...); msg != "" { 258 | t.Fatalf("%s:%d: tAssertMatchString failed, expected = %q, got = %v, %s", file, line, expectedPattern, got, msg) 259 | } else { 260 | t.Fatalf("%s:%d: tAssertMatchString failed, expected = %q, got = %v", file, line, expectedPattern, got) 261 | } 262 | } 263 | } 264 | } 265 | 266 | func tAssertSliceContain(t testing.TB, slice, val interface{}, args ...interface{}) { 267 | sliceVal := reflect.ValueOf(slice) 268 | if sliceVal.Kind() != reflect.Slice { 269 | panic(fmt.Sprintf("tAssertSliceContain called with non-slice value of type %T", slice)) 270 | } 271 | var contained bool 272 | for i := 0; i < sliceVal.Len(); i++ { 273 | if reflect.DeepEqual(sliceVal.Index(i).Interface(), val) { 274 | contained = true 275 | break 276 | } 277 | } 278 | if !contained { 279 | file, line := tCallerFileLine(1) 280 | if msg := fmt.Sprint(args...); msg != "" { 281 | t.Fatalf("%s:%d: tAssertSliceContain failed, slice = %v, val = %v, %s", file, line, slice, val, msg) 282 | } else { 283 | t.Fatalf("%s:%d: tAssertSliceContain failed, slice = %v, val = %v", file, line, slice, val) 284 | } 285 | } 286 | } 287 | 288 | func tAssertSliceNotContain(t testing.TB, slice, val interface{}, args ...interface{}) { 289 | sliceVal := reflect.ValueOf(slice) 290 | if sliceVal.Kind() != reflect.Slice { 291 | panic(fmt.Sprintf("tAssertSliceNotContain called with non-slice value of type %T", slice)) 292 | } 293 | var contained bool 294 | for i := 0; i < sliceVal.Len(); i++ { 295 | if reflect.DeepEqual(sliceVal.Index(i).Interface(), val) { 296 | contained = true 297 | break 298 | } 299 | } 300 | if contained { 301 | file, line := tCallerFileLine(1) 302 | if msg := fmt.Sprint(args...); msg != "" { 303 | t.Fatalf("%s:%d: tAssertSliceNotContain failed, slice = %v, val = %v, %s", file, line, slice, val, msg) 304 | } else { 305 | t.Fatalf("%s:%d: tAssertSliceNotContain failed, slice = %v, val = %v", file, line, slice, val) 306 | } 307 | } 308 | } 309 | 310 | func tAssertMapContain(t testing.TB, m, key, val interface{}, args ...interface{}) { 311 | mapVal := reflect.ValueOf(m) 312 | if mapVal.Kind() != reflect.Map { 313 | panic(fmt.Sprintf("tAssertMapContain called with non-map value of type %T", m)) 314 | } 315 | elemVal := mapVal.MapIndex(reflect.ValueOf(key)) 316 | if !elemVal.IsValid() || !reflect.DeepEqual(elemVal.Interface(), val) { 317 | file, line := tCallerFileLine(1) 318 | if msg := fmt.Sprint(args...); msg != "" { 319 | t.Fatalf("%s:%d: tAssertMapContain failed, map = %v, key = %v, val = %v, %s", file, line, m, key, val, msg) 320 | } else { 321 | t.Fatalf("%s:%d: tAssertMapContain failed, map = %v, key = %v, val = %v", file, line, m, key, val) 322 | } 323 | } 324 | } 325 | 326 | func tAssertMapContainKey(t testing.TB, m, key interface{}, args ...interface{}) { 327 | mapVal := reflect.ValueOf(m) 328 | if mapVal.Kind() != reflect.Map { 329 | panic(fmt.Sprintf("tAssertMapContainKey called with non-map value of type %T", m)) 330 | } 331 | elemVal := mapVal.MapIndex(reflect.ValueOf(key)) 332 | if !elemVal.IsValid() { 333 | file, line := tCallerFileLine(1) 334 | if msg := fmt.Sprint(args...); msg != "" { 335 | t.Fatalf("%s:%d: tAssertMapContainKey failed, map = %v, key = %v, %s", file, line, m, key, msg) 336 | } else { 337 | t.Fatalf("%s:%d: tAssertMapContainKey failed, map = %v, key = %v", file, line, m, key) 338 | } 339 | } 340 | } 341 | 342 | func tAssertMapContainVal(t testing.TB, m, val interface{}, args ...interface{}) { 343 | mapVal := reflect.ValueOf(m) 344 | if mapVal.Kind() != reflect.Map { 345 | panic(fmt.Sprintf("tAssertMapContainVal called with non-map value of type %T", m)) 346 | } 347 | var contained bool 348 | for _, key := range mapVal.MapKeys() { 349 | elemVal := mapVal.MapIndex(key) 350 | if elemVal.IsValid() && reflect.DeepEqual(elemVal.Interface(), val) { 351 | contained = true 352 | break 353 | } 354 | } 355 | if !contained { 356 | file, line := tCallerFileLine(1) 357 | if msg := fmt.Sprint(args...); msg != "" { 358 | t.Fatalf("%s:%d: tAssertMapContainVal failed, map = %v, val = %v, %s", file, line, m, val, msg) 359 | } else { 360 | t.Fatalf("%s:%d: tAssertMapContainVal failed, map = %v, val = %v", file, line, m, val) 361 | } 362 | } 363 | } 364 | 365 | func tAssertMapNotContain(t testing.TB, m, key, val interface{}, args ...interface{}) { 366 | mapVal := reflect.ValueOf(m) 367 | if mapVal.Kind() != reflect.Map { 368 | panic(fmt.Sprintf("tAssertMapNotContain called with non-map value of type %T", m)) 369 | } 370 | elemVal := mapVal.MapIndex(reflect.ValueOf(key)) 371 | if elemVal.IsValid() && reflect.DeepEqual(elemVal.Interface(), val) { 372 | file, line := tCallerFileLine(1) 373 | if msg := fmt.Sprint(args...); msg != "" { 374 | t.Fatalf("%s:%d: tAssertMapNotContain failed, map = %v, key = %v, val = %v, %s", file, line, m, key, val, msg) 375 | } else { 376 | t.Fatalf("%s:%d: tAssertMapNotContain failed, map = %v, key = %v, val = %v", file, line, m, key, val) 377 | } 378 | } 379 | } 380 | 381 | func tAssertMapNotContainKey(t testing.TB, m, key interface{}, args ...interface{}) { 382 | mapVal := reflect.ValueOf(m) 383 | if mapVal.Kind() != reflect.Map { 384 | panic(fmt.Sprintf("tAssertMapNotContainKey called with non-map value of type %T", m)) 385 | } 386 | elemVal := mapVal.MapIndex(reflect.ValueOf(key)) 387 | if elemVal.IsValid() { 388 | file, line := tCallerFileLine(1) 389 | if msg := fmt.Sprint(args...); msg != "" { 390 | t.Fatalf("%s:%d: tAssertMapNotContainKey failed, map = %v, key = %v, %s", file, line, m, key, msg) 391 | } else { 392 | t.Fatalf("%s:%d: tAssertMapNotContainKey failed, map = %v, key = %v", file, line, m, key) 393 | } 394 | } 395 | } 396 | 397 | func tAssertMapNotContainVal(t testing.TB, m, val interface{}, args ...interface{}) { 398 | mapVal := reflect.ValueOf(m) 399 | if mapVal.Kind() != reflect.Map { 400 | panic(fmt.Sprintf("tAssertMapNotContainVal called with non-map value of type %T", m)) 401 | } 402 | var contained bool 403 | for _, key := range mapVal.MapKeys() { 404 | elemVal := mapVal.MapIndex(key) 405 | if elemVal.IsValid() && reflect.DeepEqual(elemVal.Interface(), val) { 406 | contained = true 407 | break 408 | } 409 | } 410 | if contained { 411 | file, line := tCallerFileLine(1) 412 | if msg := fmt.Sprint(args...); msg != "" { 413 | t.Fatalf("%s:%d: tAssertMapNotContainVal failed, map = %v, val = %v, %s", file, line, m, val, msg) 414 | } else { 415 | t.Fatalf("%s:%d: tAssertMapNotContainVal failed, map = %v, val = %v", file, line, m, val) 416 | } 417 | } 418 | } 419 | 420 | func tAssertZero(t testing.TB, val interface{}, args ...interface{}) { 421 | if !reflect.DeepEqual(reflect.Zero(reflect.TypeOf(val)).Interface(), val) { 422 | file, line := tCallerFileLine(1) 423 | if msg := fmt.Sprint(args...); msg != "" { 424 | t.Fatalf("%s:%d: tAssertZero failed, val = %v, %s", file, line, val, msg) 425 | } else { 426 | t.Fatalf("%s:%d: tAssertZero failed, val = %v", file, line, val) 427 | } 428 | } 429 | } 430 | 431 | func tAssertNotZero(t testing.TB, val interface{}, args ...interface{}) { 432 | if reflect.DeepEqual(reflect.Zero(reflect.TypeOf(val)).Interface(), val) { 433 | file, line := tCallerFileLine(1) 434 | if msg := fmt.Sprint(args...); msg != "" { 435 | t.Fatalf("%s:%d: tAssertNotZero failed, val = %v, %s", file, line, val, msg) 436 | } else { 437 | t.Fatalf("%s:%d: tAssertNotZero failed, val = %v", file, line, val) 438 | } 439 | } 440 | } 441 | 442 | func tAssertFileExists(t testing.TB, path string, args ...interface{}) { 443 | if _, err := os.Stat(path); err != nil { 444 | file, line := tCallerFileLine(1) 445 | if msg := fmt.Sprint(args...); msg != "" { 446 | if err != nil { 447 | t.Fatalf("%s:%d: tAssertFileExists failed, path = %v, err = %v, %s", file, line, path, err, msg) 448 | } else { 449 | t.Fatalf("%s:%d: tAssertFileExists failed, path = %v, %s", file, line, path, msg) 450 | } 451 | } else { 452 | if err != nil { 453 | t.Fatalf("%s:%d: tAssertFileExists failed, path = %v, err = %v", file, line, path, err) 454 | } else { 455 | t.Fatalf("%s:%d: tAssertFileExists failed, path = %v", file, line, path) 456 | } 457 | } 458 | } 459 | } 460 | 461 | func tAssertFileNotExists(t testing.TB, path string, args ...interface{}) { 462 | if _, err := os.Stat(path); !os.IsNotExist(err) { 463 | file, line := tCallerFileLine(1) 464 | if msg := fmt.Sprint(args...); msg != "" { 465 | if err != nil { 466 | t.Fatalf("%s:%d: tAssertFileNotExists failed, path = %v, err = %v, %s", file, line, path, err, msg) 467 | } else { 468 | t.Fatalf("%s:%d: tAssertFileNotExists failed, path = %v, %s", file, line, path, msg) 469 | } 470 | } else { 471 | if err != nil { 472 | t.Fatalf("%s:%d: tAssertFileNotExists failed, path = %v, err = %v", file, line, path, err) 473 | } else { 474 | t.Fatalf("%s:%d: tAssertFileNotExists failed, path = %v", file, line, path) 475 | } 476 | } 477 | } 478 | } 479 | 480 | func tAssertImplements(t testing.TB, interfaceObj, obj interface{}, args ...interface{}) { 481 | if !reflect.TypeOf(obj).Implements(reflect.TypeOf(interfaceObj).Elem()) { 482 | file, line := tCallerFileLine(1) 483 | if msg := fmt.Sprint(args...); msg != "" { 484 | t.Fatalf("%s:%d: tAssertImplements failed, interface = %T, obj = %T, %s", file, line, interfaceObj, obj, msg) 485 | } else { 486 | t.Fatalf("%s:%d: tAssertImplements failed, interface = %T, obj = %T", file, line, interfaceObj, obj) 487 | } 488 | } 489 | } 490 | 491 | func tAssertSameType(t testing.TB, expectedType interface{}, obj interface{}, args ...interface{}) { 492 | if !reflect.DeepEqual(reflect.TypeOf(obj), reflect.TypeOf(expectedType)) { 493 | file, line := tCallerFileLine(1) 494 | if msg := fmt.Sprint(args...); msg != "" { 495 | t.Fatalf("%s:%d: tAssertSameType failed, expected = %T, obj = %T, %s", file, line, expectedType, obj, msg) 496 | } else { 497 | t.Fatalf("%s:%d: tAssertSameType failed, expected = %T, obj = %T", file, line, expectedType, obj) 498 | } 499 | } 500 | } 501 | 502 | func tAssertPanic(t testing.TB, f func(), args ...interface{}) { 503 | panicVal := func() (panicVal interface{}) { 504 | defer func() { 505 | panicVal = recover() 506 | }() 507 | f() 508 | return 509 | }() 510 | 511 | if panicVal == nil { 512 | file, line := tCallerFileLine(1) 513 | if msg := fmt.Sprint(args...); msg != "" { 514 | t.Fatalf("%s:%d: tAssertPanic failed, %s", file, line, msg) 515 | } else { 516 | t.Fatalf("%s:%d: tAssertPanic failed", file, line) 517 | } 518 | } 519 | } 520 | 521 | func tAssertNotPanic(t testing.TB, f func(), args ...interface{}) { 522 | panicVal := func() (panicVal interface{}) { 523 | defer func() { 524 | panicVal = recover() 525 | }() 526 | f() 527 | return 528 | }() 529 | 530 | if panicVal != nil { 531 | file, line := tCallerFileLine(1) 532 | if msg := fmt.Sprint(args...); msg != "" { 533 | t.Fatalf("%s:%d: tAssertNotPanic failed, panic = %v, %s", file, line, panicVal, msg) 534 | } else { 535 | t.Fatalf("%s:%d: tAssertNotPanic failed, panic = %v", file, line, panicVal) 536 | } 537 | } 538 | } 539 | 540 | func tAssertEQ(t testing.TB, expected, got interface{}, args ...interface{}) { 541 | if !reflect.DeepEqual(expected, got) && !tIsNumberEqual(expected, got) { 542 | file, line := tCallerFileLine(1) 543 | if msg := fmt.Sprint(args...); msg != "" { 544 | t.Fatalf("%s:%d: tAssertEQ failed, expected = %v, got = %v, %s", file, line, expected, got, msg) 545 | } else { 546 | t.Fatalf("%s:%d: tAssertEQ failed, expected = %v, got = %v", file, line, expected, got) 547 | } 548 | } 549 | } 550 | 551 | func tAssertNE(t testing.TB, expected, got interface{}, args ...interface{}) { 552 | if reflect.DeepEqual(expected, got) || tIsNumberEqual(expected, got) { 553 | file, line := tCallerFileLine(1) 554 | if msg := fmt.Sprint(args...); msg != "" { 555 | t.Fatalf("%s:%d: tAssertNE failed, expected = %v, got = %v, %s", file, line, expected, got, msg) 556 | } else { 557 | t.Fatalf("%s:%d: tAssertNE failed, expected = %v, got = %v", file, line, expected, got) 558 | } 559 | } 560 | } 561 | 562 | func tAssertLE(t testing.TB, a, b int, args ...interface{}) { 563 | if !(a <= b) { 564 | file, line := tCallerFileLine(1) 565 | if msg := fmt.Sprint(args...); msg != "" { 566 | t.Fatalf("%s:%d: tAssertLE failed, expected %v <= %v, %s", file, line, a, b, msg) 567 | } else { 568 | t.Fatalf("%s:%d: tAssertLE failed, expected %v <= %v", file, line, a, b) 569 | } 570 | } 571 | } 572 | --------------------------------------------------------------------------------