├── readme.md ├── .travis.yml ├── ensure_no_race_test.go ├── common_test.go ├── license ├── ensure_test.go └── ensure.go /readme.md: -------------------------------------------------------------------------------- 1 | ensure [![Build Status](https://secure.travis-ci.org/facebookgo/ensure.png)](http://travis-ci.org/facebookgo/ensure) 2 | ====== 3 | 4 | Documentation: https://godoc.org/github.com/facebookgo/ensure 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.5 5 | 6 | before_install: 7 | - go get -v golang.org/x/tools/cmd/vet 8 | - go get -v golang.org/x/tools/cmd/cover 9 | - go get -v github.com/golang/lint/golint 10 | 11 | install: 12 | - go install -race -v std 13 | - go get -race -t -v ./... 14 | - go install -race -v ./... 15 | 16 | script: 17 | - go vet ./... 18 | - $HOME/gopath/bin/golint . 19 | - go test -cpu=2 -race -v ./... 20 | - go test -cpu=2 -covermode=atomic ./... 21 | -------------------------------------------------------------------------------- /ensure_no_race_test.go: -------------------------------------------------------------------------------- 1 | // +build !race 2 | 3 | package ensure_test 4 | 5 | import ( 6 | "testing" 7 | 8 | "github.com/facebookgo/ensure" 9 | ) 10 | 11 | func indirect(f ensure.Fataler) { 12 | ensure.StringContains(f, "foo", "bar") 13 | } 14 | 15 | func TestIndirectStackTrace(t *testing.T) { 16 | var c capture 17 | indirect(&c) 18 | c.Contains(t, "github.com/facebookgo/ensure/ensure_no_race_test.go:12") 19 | c.Contains(t, "indirect") 20 | c.Contains(t, "github.com/facebookgo/ensure/ensure_no_race_test.go:17") 21 | c.Contains(t, "TestIndirectStackTrace") 22 | c.Contains(t, `expected substring "bar" was not found in "foo"`) 23 | } 24 | -------------------------------------------------------------------------------- /common_test.go: -------------------------------------------------------------------------------- 1 | package ensure_test 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | "regexp" 8 | "strings" 9 | "testing" 10 | 11 | "github.com/facebookgo/ensure" 12 | ) 13 | 14 | var log = os.Getenv("ENSURE_LOG") == "1" 15 | 16 | type capture struct { 17 | bytes.Buffer 18 | } 19 | 20 | func (c *capture) Fatal(a ...interface{}) { 21 | fmt.Fprint(&c.Buffer, a...) 22 | } 23 | 24 | var equalPrefix = strings.Repeat("\b", 20) 25 | 26 | func (c *capture) Equal(t testing.TB, expected string) { 27 | // trim the deleteSelf '\b' prefix 28 | actual := strings.TrimLeft(c.String(), "\b") 29 | ensure.DeepEqual(t, actual, expected) 30 | if log && expected != "" { 31 | t.Log(equalPrefix, expected) 32 | } 33 | } 34 | 35 | func (c *capture) Contains(t testing.TB, suffix string) { 36 | ensure.StringContains(t, c.String(), suffix) 37 | if log && suffix != "" { 38 | t.Log(equalPrefix, suffix) 39 | } 40 | } 41 | 42 | func (c *capture) Matches(t testing.TB, pattern string) { 43 | re := regexp.MustCompile(pattern) 44 | s := c.String() 45 | ensure.True(t, re.MatchString(s), s, "does not match pattern", pattern) 46 | } 47 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | BSD License 2 | 3 | For ensure software 4 | 5 | Copyright (c) 2015, Facebook, Inc. All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | * Neither the name Facebook nor the names of its contributors may be used to 18 | endorse or promote products derived from this software without specific 19 | prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 28 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /ensure_test.go: -------------------------------------------------------------------------------- 1 | package ensure_test 2 | 3 | import ( 4 | "errors" 5 | "regexp" 6 | "testing" 7 | 8 | "github.com/facebookgo/ensure" 9 | ) 10 | 11 | func TestNilErr(t *testing.T) { 12 | var c capture 13 | e := errors.New("foo") 14 | ensure.Err(&c, e, nil) 15 | c.Equal(t, "ensure_test.go:14: unexpected error: foo") 16 | } 17 | 18 | func TestMatchingError(t *testing.T) { 19 | var c capture 20 | e := errors.New("foo") 21 | ensure.Err(&c, e, regexp.MustCompile("bar")) 22 | c.Equal(t, "ensure_test.go:21: expected error: \"bar\" but got \"foo\"") 23 | } 24 | 25 | type typ struct { 26 | Answer int 27 | } 28 | 29 | func TestExtras(t *testing.T) { 30 | var c capture 31 | e := errors.New("foo") 32 | ensure.Err( 33 | &c, 34 | e, 35 | nil, 36 | map[string]int{"answer": 42}, 37 | "baz", 38 | 43, 39 | 44.45, 40 | typ{Answer: 46}, 41 | ) 42 | c.Equal(t, `ensure_test.go:41: unexpected error: foo 43 | (map[string]int) (len=1) { 44 | (string) (len=6) "answer": (int) 42 45 | } 46 | (string) (len=3) "baz" 47 | (int) 43 48 | (float64) 44.45 49 | (ensure_test.typ) { 50 | Answer: (int) 46 51 | }`) 52 | } 53 | 54 | func TestDeepEqualStruct(t *testing.T) { 55 | var c capture 56 | actual := typ{Answer: 41} 57 | expected := typ{Answer: 42} 58 | ensure.DeepEqual(&c, actual, expected) 59 | c.Equal(t, `ensure_test.go:58: expected these to be equal: 60 | ACTUAL: 61 | (ensure_test.typ) { 62 | Answer: (int) 41 63 | } 64 | 65 | EXPECTED: 66 | (ensure_test.typ) { 67 | Answer: (int) 42 68 | }`) 69 | } 70 | 71 | func TestDeepEqualString(t *testing.T) { 72 | var c capture 73 | ensure.DeepEqual(&c, "foo", "bar") 74 | c.Equal(t, `ensure_test.go:73: expected these to be equal: 75 | ACTUAL: 76 | (string) (len=3) "foo" 77 | 78 | EXPECTED: 79 | (string) (len=3) "bar"`) 80 | } 81 | 82 | func TestNotDeepEqualStruct(t *testing.T) { 83 | var c capture 84 | v := typ{Answer: 42} 85 | ensure.NotDeepEqual(&c, v, v) 86 | c.Equal(t, `ensure_test.go:85: expected two different values, but got the same: 87 | (ensure_test.typ) { 88 | Answer: (int) 42 89 | }`) 90 | } 91 | 92 | func TestSubsetStruct(t *testing.T) { 93 | var c capture 94 | ensure.Subset(&c, typ{}, typ{Answer: 42}) 95 | c.Equal(t, `ensure_test.go:94: expected subset not found: 96 | ACTUAL: 97 | (ensure_test.typ) { 98 | Answer: (int) 0 99 | } 100 | 101 | EXPECTED SUBSET 102 | (ensure_test.typ) { 103 | Answer: (int) 42 104 | }`) 105 | } 106 | 107 | func TestUnexpectedNilErr(t *testing.T) { 108 | var c capture 109 | ensure.Err(&c, nil, regexp.MustCompile("bar")) 110 | c.Equal(t, "ensure_test.go:109: expected error: \"bar\" but got a nil error") 111 | } 112 | 113 | func TestNilString(t *testing.T) { 114 | var c capture 115 | ensure.Nil(&c, "foo") 116 | c.Equal(t, "ensure_test.go:115: expected nil value but got: (string) (len=3) \"foo\"") 117 | } 118 | 119 | func TestNilInt(t *testing.T) { 120 | var c capture 121 | ensure.Nil(&c, 1) 122 | c.Equal(t, "ensure_test.go:121: expected nil value but got: (int) 1") 123 | } 124 | 125 | func TestNilStruct(t *testing.T) { 126 | var c capture 127 | ensure.Nil(&c, typ{}) 128 | c.Equal(t, `ensure_test.go:127: expected nil value but got: 129 | (ensure_test.typ) { 130 | Answer: (int) 0 131 | }`) 132 | } 133 | 134 | func TestNonNil(t *testing.T) { 135 | var c capture 136 | ensure.NotNil(&c, nil) 137 | c.Equal(t, `ensure_test.go:136: expected a value but got nil`) 138 | } 139 | 140 | func TestStringContains(t *testing.T) { 141 | var c capture 142 | ensure.StringContains(&c, "foo", "bar") 143 | c.Equal(t, "ensure_test.go:142: expected substring \"bar\" was not found in \"foo\"") 144 | } 145 | 146 | func TestStringDoesNotContain(t *testing.T) { 147 | var c capture 148 | ensure.StringDoesNotContain(&c, "foo", "o") 149 | c.Equal(t, "ensure_test.go:148: substring \"o\" was not supposed to be found in \"foo\"") 150 | if log { 151 | t.Log("foo") 152 | } 153 | } 154 | 155 | func TestExpectedNilErr(t *testing.T) { 156 | var c capture 157 | ensure.Err(&c, nil, nil) 158 | c.Equal(t, "") 159 | } 160 | 161 | func TestNilErrUsingNil(t *testing.T) { 162 | var c capture 163 | e := errors.New("foo") 164 | ensure.Nil(&c, e) 165 | c.Equal(t, "ensure_test.go:164: unexpected error: foo") 166 | } 167 | 168 | func TestTrue(t *testing.T) { 169 | var c capture 170 | ensure.True(&c, false) 171 | c.Equal(t, `ensure_test.go:170: expected true but got false`) 172 | } 173 | 174 | func TestSameElementsIntAndInterface(t *testing.T) { 175 | ensure.SameElements(t, []int{1, 2}, []interface{}{2, 1}) 176 | } 177 | 178 | func TestSameElementsLengthDifference(t *testing.T) { 179 | var c capture 180 | ensure.SameElements(&c, []int{1, 2}, []interface{}{1}) 181 | c.Equal(t, `ensure_test.go:180: expected same elements but found slices of different lengths: 182 | ACTUAL: 183 | ([]int) (len=2 cap=2) { 184 | (int) 1, 185 | (int) 2 186 | } 187 | EXPECTED 188 | ([]interface {}) (len=1 cap=1) { 189 | (int) 1 190 | }`) 191 | } 192 | 193 | func TestSameElementsRepeated(t *testing.T) { 194 | var c capture 195 | ensure.SameElements(&c, []int{1, 2}, []interface{}{1, 1}) 196 | c.Equal(t, `ensure_test.go:195: missing expected element: 197 | ACTUAL: 198 | ([]int) (len=2 cap=2) { 199 | (int) 1, 200 | (int) 2 201 | } 202 | EXPECTED: 203 | ([]interface {}) (len=2 cap=2) { 204 | (int) 1, 205 | (int) 1 206 | } 207 | MISSING ELEMENT 208 | (int) 1`) 209 | } 210 | 211 | func TestFalse(t *testing.T) { 212 | var c capture 213 | ensure.False(t, false) 214 | ensure.False(&c, true) 215 | c.Equal(t, `ensure_test.go:214: expected false but got true`) 216 | } 217 | 218 | func TestPanicDeepEqualNil(t *testing.T) { 219 | defer ensure.PanicDeepEqual(t, "can't pass nil to ensure.PanicDeepEqual") 220 | ensure.PanicDeepEqual(t, nil) 221 | } 222 | 223 | func TestPanicDeepEqualSuccess(t *testing.T) { 224 | defer ensure.PanicDeepEqual(t, 1) 225 | panic(1) 226 | } 227 | 228 | func TestPanicDeepEqualFailure(t *testing.T) { 229 | var c capture 230 | func() { 231 | defer ensure.PanicDeepEqual(&c, 1) 232 | panic(2) 233 | }() 234 | c.Matches(t, `TestPanicDeepEqualFailure((.func1)?) 235 | expected these to be equal: 236 | ACTUAL: 237 | \(int\) 2 238 | 239 | EXPECTED: 240 | \(int\) 1`) 241 | } 242 | 243 | func TestMultiLineStringContains(t *testing.T) { 244 | var c capture 245 | ensure.StringContains(&c, "foo\nbaz", "bar") 246 | c.Equal(t, `ensure_test.go:245: expected substring was not found: 247 | EXPECTED SUBSTRING: 248 | bar 249 | ACTUAL: 250 | foo 251 | baz`) 252 | } 253 | -------------------------------------------------------------------------------- /ensure.go: -------------------------------------------------------------------------------- 1 | // Package ensure provides utilities for testing to ensure the 2 | // given conditions are met and Fatal if they aren't satisified. 3 | // 4 | // The various functions here show a useful error message automatically 5 | // including identifying source location. They additionally support arbitary 6 | // arguments which will be printed using the spew library. 7 | package ensure 8 | 9 | import ( 10 | "bytes" 11 | "fmt" 12 | "path/filepath" 13 | "reflect" 14 | "regexp" 15 | "strings" 16 | 17 | "github.com/davecgh/go-spew/spew" 18 | "github.com/facebookgo/stack" 19 | subsetp "github.com/facebookgo/subset" 20 | ) 21 | 22 | // Fataler defines the minimal interface necessary to trigger a Fatal when a 23 | // condition is hit. testing.T & testing.B satisfy this for example. 24 | type Fataler interface { 25 | Fatal(a ...interface{}) 26 | } 27 | 28 | // cond represents a condition that wasn't satisfied, and is useful to generate 29 | // log messages. 30 | type cond struct { 31 | Fataler Fataler 32 | Skip int 33 | Format string 34 | FormatArgs []interface{} 35 | Extra []interface{} 36 | DisableDeleteSelf bool 37 | } 38 | 39 | // This deletes "ensure.go:xx" removing a confusing piece of information since 40 | // it will be an internal reference. 41 | var deleteSelf = strings.Repeat("\b", 15) 42 | 43 | func (c cond) String() string { 44 | var b bytes.Buffer 45 | if c.DisableDeleteSelf { 46 | fmt.Fprint(&b, "\n") 47 | } else { 48 | fmt.Fprint(&b, deleteSelf) 49 | } 50 | fmt.Fprint(&b, pstack(stack.Callers(c.Skip+1), c.DisableDeleteSelf)) 51 | if c.Format != "" { 52 | fmt.Fprintf(&b, c.Format, c.FormatArgs...) 53 | } 54 | if len(c.Extra) != 0 { 55 | fmt.Fprint(&b, "\n") 56 | fmt.Fprint(&b, tsdump(c.Extra...)) 57 | } 58 | return b.String() 59 | } 60 | 61 | // fatal triggers the fatal and logs the cond's message. It adds 2 to Skip, to 62 | // skip itself as well as the caller. 63 | func fatal(c cond) { 64 | c.Skip = c.Skip + 2 65 | c.Fataler.Fatal(c.String()) 66 | } 67 | 68 | // Err ensures the error satisfies the given regular expression. 69 | func Err(t Fataler, err error, re *regexp.Regexp, a ...interface{}) { 70 | if err == nil && re == nil { 71 | return 72 | } 73 | 74 | if err == nil && re != nil { 75 | fatal(cond{ 76 | Fataler: t, 77 | Format: `expected error: "%s" but got a nil error`, 78 | FormatArgs: []interface{}{re}, 79 | Extra: a, 80 | }) 81 | return 82 | } 83 | 84 | if err != nil && re == nil { 85 | fatal(cond{ 86 | Fataler: t, 87 | Format: `unexpected error: %s`, 88 | FormatArgs: []interface{}{err}, 89 | Extra: a, 90 | }) 91 | return 92 | } 93 | 94 | if !re.MatchString(err.Error()) { 95 | fatal(cond{ 96 | Fataler: t, 97 | Format: `expected error: "%s" but got "%s"`, 98 | FormatArgs: []interface{}{re, err}, 99 | Extra: a, 100 | }) 101 | } 102 | } 103 | 104 | // DeepEqual ensures actual and expected are equal. It does so using 105 | // reflect.DeepEqual. 106 | func DeepEqual(t Fataler, actual, expected interface{}, a ...interface{}) { 107 | if !reflect.DeepEqual(actual, expected) { 108 | fatal(cond{ 109 | Fataler: t, 110 | Format: "expected these to be equal:\nACTUAL:\n%s\nEXPECTED:\n%s", 111 | FormatArgs: []interface{}{spew.Sdump(actual), tsdump(expected)}, 112 | Extra: a, 113 | }) 114 | } 115 | } 116 | 117 | // NotDeepEqual ensures actual and expected are not equal. It does so using 118 | // reflect.DeepEqual. 119 | func NotDeepEqual(t Fataler, actual, expected interface{}, a ...interface{}) { 120 | if reflect.DeepEqual(actual, expected) { 121 | fatal(cond{ 122 | Fataler: t, 123 | Format: "expected two different values, but got the same:\n%s", 124 | FormatArgs: []interface{}{tsdump(actual)}, 125 | Extra: a, 126 | }) 127 | } 128 | } 129 | 130 | // Subset ensures actual matches subset. 131 | func Subset(t Fataler, actual, subset interface{}, a ...interface{}) { 132 | if !subsetp.Check(subset, actual) { 133 | fatal(cond{ 134 | Fataler: t, 135 | Format: "expected subset not found:\nACTUAL:\n%s\nEXPECTED SUBSET\n%s", 136 | FormatArgs: []interface{}{spew.Sdump(actual), tsdump(subset)}, 137 | Extra: a, 138 | }) 139 | } 140 | } 141 | 142 | // DisorderedSubset attempts to find all the given subsets in the list of actuals. 143 | // Does not allow one actual to match more than one subset, be warray of the 144 | // possibility of insufficiently specific subsets. 145 | func DisorderedSubset(t Fataler, a, s interface{}, extra ...interface{}) { 146 | actuals := toInterfaceSlice(a) 147 | subsets := toInterfaceSlice(s) 148 | 149 | used := make([]bool, len(actuals)) 150 | matches := 0 151 | for _, subset := range subsets { 152 | for i, actual := range actuals { 153 | if used[i] { 154 | continue 155 | } 156 | if subsetp.Check(subset, actual) { 157 | matches++ 158 | used[i] = true 159 | break 160 | } 161 | } 162 | } 163 | if matches != len(subsets) { 164 | fatal(cond{ 165 | Fataler: t, 166 | Format: "expected subsets not found:\nACTUAL:\n%s\nEXPECTED SUBSET\n%s", 167 | FormatArgs: []interface{}{spew.Sdump(actuals), tsdump(subsets)}, 168 | Extra: extra, 169 | }) 170 | } 171 | } 172 | 173 | // Nil ensures v is nil. 174 | func Nil(t Fataler, v interface{}, a ...interface{}) { 175 | vs := tsdump(v) 176 | sp := " " 177 | if strings.Contains(vs[:len(vs)-1], "\n") { 178 | sp = "\n" 179 | } 180 | 181 | if v != nil { 182 | // Special case errors for prettier output. 183 | if _, ok := v.(error); ok { 184 | fatal(cond{ 185 | Fataler: t, 186 | Format: `unexpected error: %s`, 187 | FormatArgs: []interface{}{v}, 188 | Extra: a, 189 | }) 190 | } else { 191 | fatal(cond{ 192 | Fataler: t, 193 | Format: "expected nil value but got:%s%s", 194 | FormatArgs: []interface{}{sp, vs}, 195 | Extra: a, 196 | }) 197 | } 198 | } 199 | } 200 | 201 | // NotNil ensures v is not nil. 202 | func NotNil(t Fataler, v interface{}, a ...interface{}) { 203 | if v == nil { 204 | fatal(cond{ 205 | Fataler: t, 206 | Format: "expected a value but got nil", 207 | Extra: a, 208 | }) 209 | } 210 | } 211 | 212 | // True ensures v is true. 213 | func True(t Fataler, v bool, a ...interface{}) { 214 | if !v { 215 | fatal(cond{ 216 | Fataler: t, 217 | Format: "expected true but got false", 218 | Extra: a, 219 | }) 220 | } 221 | } 222 | 223 | // False ensures v is false. 224 | func False(t Fataler, v bool, a ...interface{}) { 225 | if v { 226 | fatal(cond{ 227 | Fataler: t, 228 | Format: "expected false but got true", 229 | Extra: a, 230 | }) 231 | } 232 | } 233 | 234 | // StringContains ensures string s contains the string substr. 235 | func StringContains(t Fataler, s, substr string, a ...interface{}) { 236 | if !strings.Contains(s, substr) { 237 | format := `expected substring "%s" was not found in "%s"` 238 | 239 | // use multi line output if either string contains newlines 240 | if strings.Contains(s, "\n") || strings.Contains(substr, "\n") { 241 | format = "expected substring was not found:\nEXPECTED SUBSTRING:\n%s\nACTUAL:\n%s" 242 | } 243 | 244 | fatal(cond{ 245 | Fataler: t, 246 | Format: format, 247 | FormatArgs: []interface{}{substr, s}, 248 | Extra: a, 249 | }) 250 | } 251 | } 252 | 253 | // StringDoesNotContain ensures string s does not contain the string substr. 254 | func StringDoesNotContain(t Fataler, s, substr string, a ...interface{}) { 255 | if strings.Contains(s, substr) { 256 | fatal(cond{ 257 | Fataler: t, 258 | Format: `substring "%s" was not supposed to be found in "%s"`, 259 | FormatArgs: []interface{}{substr, s}, 260 | Extra: a, 261 | }) 262 | } 263 | } 264 | 265 | // SameElements ensures the two given slices contain the same elements, 266 | // ignoring the order. It uses DeepEqual for element comparison. 267 | func SameElements(t Fataler, actual, expected interface{}, extra ...interface{}) { 268 | actualSlice := toInterfaceSlice(actual) 269 | expectedSlice := toInterfaceSlice(expected) 270 | if len(actualSlice) != len(expectedSlice) { 271 | fatal(cond{ 272 | Fataler: t, 273 | Format: "expected same elements but found slices of different lengths:\nACTUAL:\n%s\nEXPECTED\n%s", 274 | FormatArgs: []interface{}{tsdump(actual), tsdump(expected)}, 275 | Extra: extra, 276 | }) 277 | } 278 | 279 | used := map[int]bool{} 280 | outer: 281 | for _, a := range expectedSlice { 282 | for i, b := range actualSlice { 283 | if !used[i] && reflect.DeepEqual(a, b) { 284 | used[i] = true 285 | continue outer 286 | } 287 | } 288 | fatal(cond{ 289 | Fataler: t, 290 | Format: "missing expected element:\nACTUAL:\n%s\nEXPECTED:\n%s\nMISSING ELEMENT\n%s", 291 | FormatArgs: []interface{}{tsdump(actual), tsdump(expected), tsdump(a)}, 292 | Extra: extra, 293 | }) 294 | } 295 | } 296 | 297 | // PanicDeepEqual ensures a panic occurs and the recovered value is DeepEqual 298 | // to the expected value. 299 | func PanicDeepEqual(t Fataler, expected interface{}, a ...interface{}) { 300 | if expected == nil { 301 | panic("can't pass nil to ensure.PanicDeepEqual") 302 | } 303 | actual := recover() 304 | if !reflect.DeepEqual(actual, expected) { 305 | fatal(cond{ 306 | Fataler: t, 307 | Format: "expected these to be equal:\nACTUAL:\n%s\nEXPECTED:\n%s", 308 | FormatArgs: []interface{}{spew.Sdump(actual), tsdump(expected)}, 309 | Extra: a, 310 | DisableDeleteSelf: true, 311 | }) 312 | } 313 | } 314 | 315 | // makes any slice into an []interface{} 316 | func toInterfaceSlice(v interface{}) []interface{} { 317 | rv := reflect.ValueOf(v) 318 | l := rv.Len() 319 | ret := make([]interface{}, l) 320 | for i := 0; i < l; i++ { 321 | ret[i] = rv.Index(i).Interface() 322 | } 323 | return ret 324 | } 325 | 326 | // tsdump is Sdump without the trailing newline. 327 | func tsdump(a ...interface{}) string { 328 | return strings.TrimSpace(spew.Sdump(a...)) 329 | } 330 | 331 | // pstack is the stack upto the Test function frame. 332 | func pstack(s stack.Stack, skipPrefix bool) string { 333 | first := s[0] 334 | if isTestFrame(first) { 335 | return fmt.Sprintf("%s:%d: ", filepath.Base(first.File), first.Line) 336 | } 337 | prefix := " " 338 | if skipPrefix { 339 | prefix = "" 340 | } 341 | var snew stack.Stack 342 | for _, f := range s { 343 | snew = append(snew, f) 344 | if isTestFrame(f) { 345 | return prefix + snew.String() + "\n" 346 | } 347 | } 348 | return prefix + s.String() + "\n" 349 | } 350 | 351 | func isTestFrame(f stack.Frame) bool { 352 | return strings.HasPrefix(f.Name, "Test") 353 | } 354 | --------------------------------------------------------------------------------