Skip to content

Commit 94c589d

Browse files
committed
Merge branch 'master' of github.com:matryer/is
2 parents f94466d + d065469 commit 94c589d

File tree

9 files changed

+236
-58
lines changed

9 files changed

+236
-58
lines changed

.travis.yml

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,40 @@
1+
os:
2+
- linux
3+
- osx
4+
- windows
5+
16
language: go
27

38
sudo: required
49

510
go:
11+
# "1.x" always refers to the latest Go version, inc. the patch release.
12+
# e.g. "1.x" is 1.13 until 1.13.1 is available.
13+
- 1.x
614
- 1.6.x
715
- 1.7.x
16+
- 1.8.x
17+
- 1.9.x
18+
- 1.10.x
19+
- 1.11.x
20+
- 1.12.x
21+
- 1.13.x
22+
- 1.14.x
823
- tip
924

10-
env:
11-
- GIMME_OS=linux GIMME_ARCH=amd64
12-
- GIMME_OS=darwin GIMME_ARCH=amd64
13-
- GIMME_OS=windows GIMME_ARCH=amd64
25+
matrix:
26+
allow_failures:
27+
- os: windows
28+
go: tip
29+
exclude:
30+
# OSX 1.6.4 is not present in travis.
31+
# https://github.com/travis-ci/travis-ci/issues/10309
32+
- go: 1.6.x
33+
os: osx
1434

1535
install:
16-
- go get -d -v ./...
36+
- go get -d -v ./...
1737

1838
script:
19-
- go build -v ./...
39+
- go build -v ./...
40+
- go test ./...

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ func Test(t *testing.T) {
3131

3232
## Color
3333

34-
To turn off the colors, run `go test` with the `-nocolor` flag.
34+
To turn off the colors, run `go test` with the `-nocolor` flag, or with the env var `IS_NO_COLOR=true`.
3535

3636
```
3737
go test -nocolor
3838
```
39+
40+
```
41+
IS_NO_COLOR=true go test
42+
```

is-1.7.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// +build go1.7
2+
3+
package is
4+
5+
import (
6+
"regexp"
7+
"runtime"
8+
)
9+
10+
// Helper marks the calling function as a test helper function.
11+
// When printing file and line information, that function will be skipped.
12+
//
13+
// Available with Go 1.7 and later.
14+
func (is *I) Helper() {
15+
is.helpers[callerName(1)] = struct{}{}
16+
}
17+
18+
// callerName gives the function name (qualified with a package path)
19+
// for the caller after skip frames (where 0 means the current function).
20+
func callerName(skip int) string {
21+
// Make room for the skip PC.
22+
var pc [1]uintptr
23+
n := runtime.Callers(skip+2, pc[:]) // skip + runtime.Callers + callerName
24+
if n == 0 {
25+
panic("is: zero callers found")
26+
}
27+
frames := runtime.CallersFrames(pc[:n])
28+
frame, _ := frames.Next()
29+
return frame.Function
30+
}
31+
32+
// The maximum number of stack frames to go through when skipping helper functions for
33+
// the purpose of decorating log messages.
34+
const maxStackLen = 50
35+
36+
var reIsSourceFile = regexp.MustCompile("is(-1.7)?\\.go$")
37+
38+
func (is *I) callerinfo() (path string, line int, ok bool) {
39+
var pc [maxStackLen]uintptr
40+
// Skip two extra frames to account for this function
41+
// and runtime.Callers itself.
42+
n := runtime.Callers(2, pc[:])
43+
if n == 0 {
44+
panic("is: zero callers found")
45+
}
46+
frames := runtime.CallersFrames(pc[:n])
47+
var firstFrame, frame runtime.Frame
48+
for more := true; more; {
49+
frame, more = frames.Next()
50+
if reIsSourceFile.MatchString(frame.File) {
51+
continue
52+
}
53+
if firstFrame.PC == 0 {
54+
firstFrame = frame
55+
}
56+
if _, ok := is.helpers[frame.Function]; ok {
57+
// Frame is inside a helper function.
58+
continue
59+
}
60+
return frame.File, frame.Line, true
61+
}
62+
// If no "non-helper" frame is found, the first non is frame is returned.
63+
return firstFrame.File, firstFrame.Line, true
64+
}

is-1.7_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// +build go1.7
2+
3+
package is
4+
5+
import (
6+
"bytes"
7+
"strings"
8+
"testing"
9+
)
10+
11+
// TestSubtests ensures subtests work as expected.
12+
// https://github.com/matryer/is/issues/1
13+
func TestSubtests(t *testing.T) {
14+
t.Run("sub1", func(t *testing.T) {
15+
is := New(t)
16+
is.Equal(1+1, 2)
17+
})
18+
}
19+
20+
func TestHelper(t *testing.T) {
21+
tests := []struct {
22+
name string
23+
helper bool
24+
expectedFilename string
25+
}{
26+
{
27+
name: "without helper",
28+
helper: false,
29+
expectedFilename: "is_helper_test.go",
30+
},
31+
{
32+
name: "with helper",
33+
helper: true,
34+
expectedFilename: "is-1.7_test.go",
35+
},
36+
}
37+
38+
for _, tc := range tests {
39+
t.Run(tc.name, func(t *testing.T) {
40+
tt := &mockT{}
41+
is := NewRelaxed(tt)
42+
43+
var buf bytes.Buffer
44+
is.out = &buf
45+
helper(is, tc.helper)
46+
actual := buf.String()
47+
t.Log(actual)
48+
if !strings.Contains(actual, tc.expectedFilename) {
49+
t.Errorf("string does not contain correct filename: %s", actual)
50+
}
51+
})
52+
}
53+
}

is-before-1.7.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// +build !go1.7
2+
3+
package is
4+
5+
import (
6+
"regexp"
7+
"runtime"
8+
)
9+
10+
var reIsSourceFile = regexp.MustCompile("is(-before-1.7)?\\.go$")
11+
12+
func (is *I) callerinfo() (path string, line int, ok bool) {
13+
for i := 0; ; i++ {
14+
_, path, line, ok = runtime.Caller(i)
15+
if !ok {
16+
return
17+
}
18+
if reIsSourceFile.MatchString(path) {
19+
continue
20+
}
21+
return path, line, true
22+
}
23+
}

is.go

Lines changed: 33 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ import (
4545
"os"
4646
"path/filepath"
4747
"reflect"
48-
"runtime"
4948
"strings"
5049
"testing"
5150
)
@@ -69,28 +68,31 @@ type I struct {
6968
fail func()
7069
out io.Writer
7170
colorful bool
71+
72+
helpers map[string]struct{} // functions to be skipped when writing file/line info
7273
}
7374

7475
var noColorFlag bool
7576

7677
func init() {
77-
flag.BoolVar(&noColorFlag, "nocolor", false, "turns off colors")
78+
envNoColor := os.Getenv("IS_NO_COLOR") == "true"
79+
flag.BoolVar(&noColorFlag, "nocolor", envNoColor, "turns off colors")
7880
}
7981

8082
// New makes a new testing helper using the specified
8183
// T through which failures will be reported.
8284
// In strict mode, failures call T.FailNow causing the test
8385
// to be aborted. See NewRelaxed for alternative behavior.
8486
func New(t T) *I {
85-
return &I{t, t.FailNow, os.Stdout, !noColorFlag}
87+
return &I{t, t.FailNow, os.Stdout, !noColorFlag, map[string]struct{}{}}
8688
}
8789

8890
// NewRelaxed makes a new testing helper using the specified
8991
// T through which failures will be reported.
9092
// In relaxed mode, failures call T.Fail allowing
9193
// multiple failures per test.
9294
func NewRelaxed(t T) *I {
93-
return &I{t, t.Fail, os.Stdout, !noColorFlag}
95+
return &I{t, t.Fail, os.Stdout, !noColorFlag, map[string]struct{}{}}
9496
}
9597

9698
func (is *I) log(args ...interface{}) {
@@ -146,23 +148,14 @@ func (is *I) True(expression bool) {
146148
//
147149
// your_test.go:123: Hey Mat != Hi Mat // greeting
148150
func (is *I) Equal(a, b interface{}) {
149-
if !areEqual(a, b) {
150-
if isNil(a) || isNil(b) {
151-
aLabel := is.valWithType(a)
152-
bLabel := is.valWithType(b)
153-
if isNil(a) {
154-
aLabel = "<nil>"
155-
}
156-
if isNil(b) {
157-
bLabel = "<nil>"
158-
}
159-
is.logf("%s != %s", aLabel, bLabel)
160-
return
161-
}
162-
if reflect.ValueOf(a).Type() == reflect.ValueOf(b).Type() {
163-
is.logf("%v != %v", a, b)
164-
return
165-
}
151+
if areEqual(a, b) {
152+
return
153+
}
154+
if isNil(a) || isNil(b) {
155+
is.logf("%s != %s", is.valWithType(a), is.valWithType(b))
156+
} else if reflect.ValueOf(a).Type() == reflect.ValueOf(b).Type() {
157+
is.logf("%v != %v", a, b)
158+
} else {
166159
is.logf("%s != %s", is.valWithType(a), is.valWithType(b))
167160
}
168161
}
@@ -198,6 +191,9 @@ func (is *I) NewRelaxed(t *testing.T) *I {
198191
}
199192

200193
func (is *I) valWithType(v interface{}) string {
194+
if isNil(v) {
195+
return "<nil>"
196+
}
201197
if is.colorful {
202198
return fmt.Sprintf("%[1]s%[3]T(%[2]s%[3]v%[1]s)%[2]s", colorType, colorNormal, v)
203199
}
@@ -237,15 +233,12 @@ func isNil(object interface{}) bool {
237233

238234
// areEqual gets whether a equals b or not.
239235
func areEqual(a, b interface{}) bool {
240-
if isNil(a) || isNil(b) {
241-
if isNil(a) && !isNil(b) {
242-
return false
243-
}
244-
if !isNil(a) && isNil(b) {
245-
return false
246-
}
236+
if isNil(a) && isNil(b) {
247237
return true
248238
}
239+
if isNil(a) || isNil(b) {
240+
return false
241+
}
249242
if reflect.DeepEqual(a, b) {
250243
return true
251244
}
@@ -254,19 +247,6 @@ func areEqual(a, b interface{}) bool {
254247
return aValue == bValue
255248
}
256249

257-
func callerinfo() (path string, line int, ok bool) {
258-
for i := 0; ; i++ {
259-
_, path, line, ok = runtime.Caller(i)
260-
if !ok {
261-
return
262-
}
263-
if strings.HasSuffix(path, "is.go") {
264-
continue
265-
}
266-
return path, line, true
267-
}
268-
}
269-
270250
// loadComment gets the Go comment from the specified line
271251
// in the specified file.
272252
func loadComment(path string, line int) (string, bool) {
@@ -280,7 +260,7 @@ func loadComment(path string, line int) (string, bool) {
280260
for s.Scan() {
281261
if i == line {
282262
text := s.Text()
283-
commentI := strings.Index(text, "//")
263+
commentI := strings.Index(text, "// ")
284264
if commentI == -1 {
285265
return "", false // no comment
286266
}
@@ -339,7 +319,7 @@ func loadArguments(path string, line int) (string, bool) {
339319
// and inserts the final newline if needed and indentation tabs for formatting.
340320
// this function was copied from the testing framework and modified.
341321
func (is *I) decorate(s string) string {
342-
path, lineNumber, ok := callerinfo() // decorate + log + public function.
322+
path, lineNumber, ok := is.callerinfo() // decorate + log + public function.
343323
file := filepath.Base(path)
344324
if ok {
345325
// Truncate file name at last file name separator.
@@ -362,6 +342,9 @@ func (is *I) decorate(s string) string {
362342
if is.colorful {
363343
buf.WriteString(colorNormal)
364344
}
345+
346+
s = escapeFormatString(s)
347+
365348
lines := strings.Split(s, "\n")
366349
if l := len(lines); l > 1 && lines[l-1] == "" {
367350
lines = lines[:l-1]
@@ -384,6 +367,7 @@ func (is *I) decorate(s string) string {
384367
buf.WriteString(colorComment)
385368
}
386369
buf.WriteString(" // ")
370+
comment = escapeFormatString(comment)
387371
buf.WriteString(comment)
388372
if is.colorful {
389373
buf.WriteString(colorNormal)
@@ -393,9 +377,14 @@ func (is *I) decorate(s string) string {
393377
return buf.String()
394378
}
395379

380+
// escapeFormatString escapes strings for use in formatted functions like Sprintf.
381+
func escapeFormatString(fmt string) string {
382+
return strings.Replace(fmt, "%", "%%", -1)
383+
}
384+
396385
const (
397386
colorNormal = "\u001b[39m"
398-
colorComment = "\u001b[32m"
387+
colorComment = "\u001b[31m"
399388
colorFile = "\u001b[90m"
400389
colorType = "\u001b[90m"
401390
)

is_helper_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// +build go1.7
2+
3+
package is
4+
5+
func helper(is *I, helper bool) {
6+
if helper {
7+
is.Helper()
8+
}
9+
is.True(false)
10+
}

0 commit comments

Comments
 (0)