blob: 009b524afb2a2000f02646a1e127204ecce9c1e6 [file] [log] [blame]
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.
package cmp
import (
"io"
"reflect"
"strings"
"testing"
ts "github.com/google/go-cmp/cmp/internal/teststructs"
)
// Test that the creation of Option values with non-sensible inputs produces
// a run-time panic with a decent error message
func TestOptionPanic(t *testing.T) {
type myBool bool
tests := []struct {
label string // Test description
fnc interface{} // Option function to call
args []interface{} // Arguments to pass in
wantPanic string // Expected panic message
}{{
label: "AllowUnexported",
fnc: AllowUnexported,
args: []interface{}{},
}, {
label: "AllowUnexported",
fnc: AllowUnexported,
args: []interface{}{1},
wantPanic: "invalid struct type",
}, {
label: "AllowUnexported",
fnc: AllowUnexported,
args: []interface{}{ts.StructA{}},
}, {
label: "AllowUnexported",
fnc: AllowUnexported,
args: []interface{}{ts.StructA{}, ts.StructB{}, ts.StructA{}},
}, {
label: "AllowUnexported",
fnc: AllowUnexported,
args: []interface{}{ts.StructA{}, &ts.StructB{}, ts.StructA{}},
wantPanic: "invalid struct type",
}, {
label: "Comparer",
fnc: Comparer,
args: []interface{}{5},
wantPanic: "invalid comparer function",
}, {
label: "Comparer",
fnc: Comparer,
args: []interface{}{func(x, y interface{}) bool { return true }},
}, {
label: "Comparer",
fnc: Comparer,
args: []interface{}{func(x, y io.Reader) bool { return true }},
}, {
label: "Comparer",
fnc: Comparer,
args: []interface{}{func(x, y io.Reader) myBool { return true }},
wantPanic: "invalid comparer function",
}, {
label: "Comparer",
fnc: Comparer,
args: []interface{}{func(x string, y interface{}) bool { return true }},
wantPanic: "invalid comparer function",
}, {
label: "Comparer",
fnc: Comparer,
args: []interface{}{(func(int, int) bool)(nil)},
wantPanic: "invalid comparer function",
}, {
label: "Transformer",
fnc: Transformer,
args: []interface{}{"", 0},
wantPanic: "invalid transformer function",
}, {
label: "Transformer",
fnc: Transformer,
args: []interface{}{"", func(int) int { return 0 }},
}, {
label: "Transformer",
fnc: Transformer,
args: []interface{}{"", func(bool) bool { return true }},
}, {
label: "Transformer",
fnc: Transformer,
args: []interface{}{"", func(int) bool { return true }},
}, {
label: "Transformer",
fnc: Transformer,
args: []interface{}{"", func(int, int) bool { return true }},
wantPanic: "invalid transformer function",
}, {
label: "Transformer",
fnc: Transformer,
args: []interface{}{"", (func(int) uint)(nil)},
wantPanic: "invalid transformer function",
}, {
label: "Transformer",
fnc: Transformer,
args: []interface{}{"Func", func(Path) Path { return nil }},
}, {
label: "Transformer",
fnc: Transformer,
args: []interface{}{"世界", func(int) bool { return true }},
}, {
label: "Transformer",
fnc: Transformer,
args: []interface{}{"/*", func(int) bool { return true }},
wantPanic: "invalid name",
}, {
label: "Transformer",
fnc: Transformer,
args: []interface{}{"_", func(int) bool { return true }},
wantPanic: "invalid name",
}, {
label: "FilterPath",
fnc: FilterPath,
args: []interface{}{(func(Path) bool)(nil), Ignore()},
wantPanic: "invalid path filter function",
}, {
label: "FilterPath",
fnc: FilterPath,
args: []interface{}{func(Path) bool { return true }, Ignore()},
}, {
label: "FilterPath",
fnc: FilterPath,
args: []interface{}{func(Path) bool { return true }, &defaultReporter{}},
wantPanic: "invalid option type",
}, {
label: "FilterPath",
fnc: FilterPath,
args: []interface{}{func(Path) bool { return true }, Options{Ignore(), Ignore()}},
}, {
label: "FilterPath",
fnc: FilterPath,
args: []interface{}{func(Path) bool { return true }, Options{Ignore(), &defaultReporter{}}},
wantPanic: "invalid option type",
}, {
label: "FilterValues",
fnc: FilterValues,
args: []interface{}{0, Ignore()},
wantPanic: "invalid values filter function",
}, {
label: "FilterValues",
fnc: FilterValues,
args: []interface{}{func(x, y int) bool { return true }, Ignore()},
}, {
label: "FilterValues",
fnc: FilterValues,
args: []interface{}{func(x, y interface{}) bool { return true }, Ignore()},
}, {
label: "FilterValues",
fnc: FilterValues,
args: []interface{}{func(x, y interface{}) myBool { return true }, Ignore()},
wantPanic: "invalid values filter function",
}, {
label: "FilterValues",
fnc: FilterValues,
args: []interface{}{func(x io.Reader, y interface{}) bool { return true }, Ignore()},
wantPanic: "invalid values filter function",
}, {
label: "FilterValues",
fnc: FilterValues,
args: []interface{}{(func(int, int) bool)(nil), Ignore()},
wantPanic: "invalid values filter function",
}, {
label: "FilterValues",
fnc: FilterValues,
args: []interface{}{func(int, int) bool { return true }, &defaultReporter{}},
wantPanic: "invalid option type",
}, {
label: "FilterValues",
fnc: FilterValues,
args: []interface{}{func(int, int) bool { return true }, Options{Ignore(), Ignore()}},
}, {
label: "FilterValues",
fnc: FilterValues,
args: []interface{}{func(int, int) bool { return true }, Options{Ignore(), &defaultReporter{}}},
wantPanic: "invalid option type",
}}
for _, tt := range tests {
tRun(t, tt.label, func(t *testing.T) {
var gotPanic string
func() {
defer func() {
if ex := recover(); ex != nil {
if s, ok := ex.(string); ok {
gotPanic = s
} else {
panic(ex)
}
}
}()
var vargs []reflect.Value
for _, arg := range tt.args {
vargs = append(vargs, reflect.ValueOf(arg))
}
reflect.ValueOf(tt.fnc).Call(vargs)
}()
if tt.wantPanic == "" {
if gotPanic != "" {
t.Fatalf("unexpected panic message: %s", gotPanic)
}
} else {
if !strings.Contains(gotPanic, tt.wantPanic) {
t.Fatalf("panic message:\ngot: %s\nwant: %s", gotPanic, tt.wantPanic)
}
}
})
}
}
// TODO: Delete this hack when we drop Go1.6 support.
func tRun(t *testing.T, name string, f func(t *testing.T)) {
type runner interface {
Run(string, func(t *testing.T)) bool
}
var ti interface{} = t
if r, ok := ti.(runner); ok {
r.Run(name, f)
} else {
t.Logf("Test: %s", name)
f(t)
}
}