1// Copyright 2017, The Go Authors. 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 5package cmp 6 7import ( 8 "io" 9 "reflect" 10 "strings" 11 "testing" 12 13 ts "github.com/google/go-cmp/cmp/internal/teststructs" 14) 15 16// Test that the creation of Option values with non-sensible inputs produces 17// a run-time panic with a decent error message 18func TestOptionPanic(t *testing.T) { 19 type myBool bool 20 tests := []struct { 21 label string // Test description 22 fnc interface{} // Option function to call 23 args []interface{} // Arguments to pass in 24 wantPanic string // Expected panic message 25 }{{ 26 label: "AllowUnexported", 27 fnc: AllowUnexported, 28 args: []interface{}{}, 29 }, { 30 label: "AllowUnexported", 31 fnc: AllowUnexported, 32 args: []interface{}{1}, 33 wantPanic: "invalid struct type", 34 }, { 35 label: "AllowUnexported", 36 fnc: AllowUnexported, 37 args: []interface{}{ts.StructA{}}, 38 }, { 39 label: "AllowUnexported", 40 fnc: AllowUnexported, 41 args: []interface{}{ts.StructA{}, ts.StructB{}, ts.StructA{}}, 42 }, { 43 label: "AllowUnexported", 44 fnc: AllowUnexported, 45 args: []interface{}{ts.StructA{}, &ts.StructB{}, ts.StructA{}}, 46 wantPanic: "invalid struct type", 47 }, { 48 label: "Comparer", 49 fnc: Comparer, 50 args: []interface{}{5}, 51 wantPanic: "invalid comparer function", 52 }, { 53 label: "Comparer", 54 fnc: Comparer, 55 args: []interface{}{func(x, y interface{}) bool { return true }}, 56 }, { 57 label: "Comparer", 58 fnc: Comparer, 59 args: []interface{}{func(x, y io.Reader) bool { return true }}, 60 }, { 61 label: "Comparer", 62 fnc: Comparer, 63 args: []interface{}{func(x, y io.Reader) myBool { return true }}, 64 wantPanic: "invalid comparer function", 65 }, { 66 label: "Comparer", 67 fnc: Comparer, 68 args: []interface{}{func(x string, y interface{}) bool { return true }}, 69 wantPanic: "invalid comparer function", 70 }, { 71 label: "Comparer", 72 fnc: Comparer, 73 args: []interface{}{(func(int, int) bool)(nil)}, 74 wantPanic: "invalid comparer function", 75 }, { 76 label: "Transformer", 77 fnc: Transformer, 78 args: []interface{}{"", 0}, 79 wantPanic: "invalid transformer function", 80 }, { 81 label: "Transformer", 82 fnc: Transformer, 83 args: []interface{}{"", func(int) int { return 0 }}, 84 }, { 85 label: "Transformer", 86 fnc: Transformer, 87 args: []interface{}{"", func(bool) bool { return true }}, 88 }, { 89 label: "Transformer", 90 fnc: Transformer, 91 args: []interface{}{"", func(int) bool { return true }}, 92 }, { 93 label: "Transformer", 94 fnc: Transformer, 95 args: []interface{}{"", func(int, int) bool { return true }}, 96 wantPanic: "invalid transformer function", 97 }, { 98 label: "Transformer", 99 fnc: Transformer, 100 args: []interface{}{"", (func(int) uint)(nil)}, 101 wantPanic: "invalid transformer function", 102 }, { 103 label: "Transformer", 104 fnc: Transformer, 105 args: []interface{}{"Func", func(Path) Path { return nil }}, 106 }, { 107 label: "Transformer", 108 fnc: Transformer, 109 args: []interface{}{"世界", func(int) bool { return true }}, 110 }, { 111 label: "Transformer", 112 fnc: Transformer, 113 args: []interface{}{"/*", func(int) bool { return true }}, 114 wantPanic: "invalid name", 115 }, { 116 label: "Transformer", 117 fnc: Transformer, 118 args: []interface{}{"_", func(int) bool { return true }}, 119 }, { 120 label: "FilterPath", 121 fnc: FilterPath, 122 args: []interface{}{(func(Path) bool)(nil), Ignore()}, 123 wantPanic: "invalid path filter function", 124 }, { 125 label: "FilterPath", 126 fnc: FilterPath, 127 args: []interface{}{func(Path) bool { return true }, Ignore()}, 128 }, { 129 label: "FilterPath", 130 fnc: FilterPath, 131 args: []interface{}{func(Path) bool { return true }, Reporter(&defaultReporter{})}, 132 wantPanic: "invalid option type", 133 }, { 134 label: "FilterPath", 135 fnc: FilterPath, 136 args: []interface{}{func(Path) bool { return true }, Options{Ignore(), Ignore()}}, 137 }, { 138 label: "FilterPath", 139 fnc: FilterPath, 140 args: []interface{}{func(Path) bool { return true }, Options{Ignore(), Reporter(&defaultReporter{})}}, 141 wantPanic: "invalid option type", 142 }, { 143 label: "FilterValues", 144 fnc: FilterValues, 145 args: []interface{}{0, Ignore()}, 146 wantPanic: "invalid values filter function", 147 }, { 148 label: "FilterValues", 149 fnc: FilterValues, 150 args: []interface{}{func(x, y int) bool { return true }, Ignore()}, 151 }, { 152 label: "FilterValues", 153 fnc: FilterValues, 154 args: []interface{}{func(x, y interface{}) bool { return true }, Ignore()}, 155 }, { 156 label: "FilterValues", 157 fnc: FilterValues, 158 args: []interface{}{func(x, y interface{}) myBool { return true }, Ignore()}, 159 wantPanic: "invalid values filter function", 160 }, { 161 label: "FilterValues", 162 fnc: FilterValues, 163 args: []interface{}{func(x io.Reader, y interface{}) bool { return true }, Ignore()}, 164 wantPanic: "invalid values filter function", 165 }, { 166 label: "FilterValues", 167 fnc: FilterValues, 168 args: []interface{}{(func(int, int) bool)(nil), Ignore()}, 169 wantPanic: "invalid values filter function", 170 }, { 171 label: "FilterValues", 172 fnc: FilterValues, 173 args: []interface{}{func(int, int) bool { return true }, Reporter(&defaultReporter{})}, 174 wantPanic: "invalid option type", 175 }, { 176 label: "FilterValues", 177 fnc: FilterValues, 178 args: []interface{}{func(int, int) bool { return true }, Options{Ignore(), Ignore()}}, 179 }, { 180 label: "FilterValues", 181 fnc: FilterValues, 182 args: []interface{}{func(int, int) bool { return true }, Options{Ignore(), Reporter(&defaultReporter{})}}, 183 wantPanic: "invalid option type", 184 }} 185 186 for _, tt := range tests { 187 t.Run(tt.label, func(t *testing.T) { 188 var gotPanic string 189 func() { 190 defer func() { 191 if ex := recover(); ex != nil { 192 if s, ok := ex.(string); ok { 193 gotPanic = s 194 } else { 195 panic(ex) 196 } 197 } 198 }() 199 var vargs []reflect.Value 200 for _, arg := range tt.args { 201 vargs = append(vargs, reflect.ValueOf(arg)) 202 } 203 reflect.ValueOf(tt.fnc).Call(vargs) 204 }() 205 if tt.wantPanic == "" { 206 if gotPanic != "" { 207 t.Fatalf("unexpected panic message: %s", gotPanic) 208 } 209 } else { 210 if !strings.Contains(gotPanic, tt.wantPanic) { 211 t.Fatalf("panic message:\ngot: %s\nwant: %s", gotPanic, tt.wantPanic) 212 } 213 } 214 }) 215 } 216} 217