knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/kmp/reporters_test.go (about) 1 /* 2 Copyright 2019 The Knative Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package kmp 18 19 import ( 20 "testing" 21 22 "github.com/google/go-cmp/cmp" 23 "github.com/google/go-cmp/cmp/cmpopts" 24 ) 25 26 type testStruct struct { 27 A string `json:"a"` 28 StringField string `json:"foo"` 29 IntField int 30 StructField childStruct `json:"child"` 31 Omit string `json:"omit,omitempty"` 32 Ignore string `json:"-"` 33 Dash string `json:"-,"` 34 //nolint:staticcheck // We actually want to test this broken json options work 35 MultiComma string `json:"multi,omitempty,somethingelse"` 36 } 37 38 type childStruct struct { 39 ChildString string 40 ChildInt int 41 } 42 43 type privateStruct struct { 44 privateField string 45 } 46 47 func TestFieldListReporter(t *testing.T) { 48 tests := []struct { 49 name string 50 x interface{} 51 y interface{} 52 want []string 53 opts []cmp.Option 54 }{{ 55 name: "No diff", 56 x: testStruct{ 57 StringField: "foo", 58 }, 59 y: testStruct{ 60 StringField: "foo", 61 }, 62 want: nil, 63 }, { 64 name: "Both nil objects", 65 want: nil, 66 }, { 67 name: "Nil second object", 68 x: testStruct{ 69 StringField: "foo", 70 }, 71 want: []string{"{any}"}, 72 }, { 73 name: "Single character field name", 74 x: testStruct{ 75 A: "foo", 76 }, 77 y: testStruct{}, 78 want: []string{"a"}, 79 }, { 80 name: "Single field", 81 x: testStruct{ 82 StringField: "foo", 83 }, 84 y: testStruct{ 85 StringField: "bar", 86 }, 87 want: []string{"foo"}, 88 }, { 89 name: "Multi field", 90 x: testStruct{ 91 StringField: "foo", 92 IntField: 5, 93 }, 94 y: testStruct{ 95 StringField: "bar", 96 IntField: 6, 97 }, 98 want: []string{"IntField", "foo"}, 99 }, { 100 name: "Missing field", 101 x: testStruct{ 102 StringField: "test", 103 }, 104 y: testStruct{}, 105 want: []string{"foo"}, 106 }, { 107 name: "Nested field", 108 x: testStruct{ 109 StructField: childStruct{ 110 ChildString: "baz", 111 }, 112 }, 113 y: testStruct{ 114 StructField: childStruct{ 115 ChildString: "zap", 116 ChildInt: 1, 117 }, 118 }, 119 want: []string{"child"}, 120 }, { 121 name: "Non Struct", 122 x: "foo", 123 y: "bar", 124 want: []string{"{string}"}, 125 }, { 126 name: "Private field allowed", 127 x: privateStruct{ 128 privateField: "Foo", 129 }, 130 y: privateStruct{ 131 privateField: "Bar", 132 }, 133 want: []string{"privateField"}, 134 opts: []cmp.Option{cmp.AllowUnexported(privateStruct{})}, 135 }, { 136 name: "Private field ignored", 137 x: privateStruct{ 138 privateField: "Foo", 139 }, 140 y: privateStruct{ 141 privateField: "Bar", 142 }, 143 want: nil, 144 opts: []cmp.Option{cmpopts.IgnoreUnexported(privateStruct{})}, 145 }, { 146 name: "Omit empty", 147 x: testStruct{ 148 Omit: "Foo", 149 }, 150 y: testStruct{ 151 Omit: "Bar", 152 }, 153 want: []string{"omit"}, 154 }, { 155 name: "Ignore JSON", 156 x: testStruct{ 157 Ignore: "Foo", 158 }, 159 y: testStruct{ 160 Ignore: "Bar", 161 }, 162 want: []string{"Ignore"}, 163 }, { 164 name: "Dash json name", 165 x: testStruct{ 166 Dash: "Foo", 167 }, 168 y: testStruct{ 169 Dash: "Bar", 170 }, 171 want: []string{"-"}, 172 }, { 173 name: "Multi comma in json tag", 174 x: testStruct{ 175 MultiComma: "Foo", 176 }, 177 y: testStruct{ 178 MultiComma: "Bar", 179 }, 180 want: []string{"multi"}, 181 }} 182 183 for _, test := range tests { 184 t.Run(test.name, func(t *testing.T) { 185 reporter := new(fieldListReporter) 186 cmp.Equal(test.x, test.y, append(test.opts, cmp.Reporter(reporter))...) 187 got := reporter.Fields() 188 if diff := cmp.Diff(got, test.want); diff != "" { 189 t.Errorf("%s: Fields() = %v, Want %v", test.name, got, test.want) 190 } 191 }) 192 } 193 } 194 195 func TestImmutableReporter(t *testing.T) { 196 tests := []struct { 197 name string 198 x interface{} 199 y interface{} 200 want string 201 expectErr bool 202 opts []cmp.Option 203 }{{ 204 name: "No diff", 205 x: testStruct{ 206 StringField: "foo", 207 }, 208 y: testStruct{ 209 StringField: "foo", 210 }, 211 want: "", 212 }, { 213 name: "Both nil objects", 214 want: "", 215 }, { 216 name: "Nil second object", 217 x: testStruct{ 218 StringField: "foo", 219 }, 220 want: `{any}: 221 -: "{A: StringField:foo IntField:0 StructField:{ChildString: ChildInt:0} Omit: Ignore: Dash: MultiComma:}" 222 `, 223 }, { 224 name: "Single character field name", 225 x: testStruct{ 226 A: "foo", 227 }, 228 y: testStruct{}, 229 want: `{kmp.testStruct}.A: 230 -: "foo" 231 +: "" 232 `, 233 }, { 234 name: "Single field", 235 x: testStruct{ 236 StringField: "foo", 237 }, 238 y: testStruct{ 239 StringField: "bar", 240 }, 241 want: `{kmp.testStruct}.StringField: 242 -: "foo" 243 +: "bar" 244 `, 245 }, { 246 name: "Multi field", 247 x: testStruct{ 248 StringField: "foo", 249 IntField: 5, 250 }, 251 y: testStruct{ 252 StringField: "bar", 253 IntField: 6, 254 }, 255 want: `{kmp.testStruct}.StringField: 256 -: "foo" 257 +: "bar" 258 {kmp.testStruct}.IntField: 259 -: "5" 260 +: "6" 261 `, 262 }, { 263 name: "Missing field", 264 x: testStruct{ 265 StringField: "foo", 266 }, 267 y: testStruct{}, 268 want: `{kmp.testStruct}.StringField: 269 -: "foo" 270 +: "" 271 `, 272 }, { 273 name: "Nested field", 274 x: testStruct{ 275 StructField: childStruct{ 276 ChildString: "baz", 277 }, 278 }, 279 y: testStruct{ 280 StructField: childStruct{ 281 ChildString: "zap", 282 ChildInt: 1, 283 }, 284 }, 285 want: `{kmp.testStruct}.StructField.ChildString: 286 -: "baz" 287 +: "zap" 288 {kmp.testStruct}.StructField.ChildInt: 289 -: "0" 290 +: "1" 291 `, 292 }, { 293 name: "Non Struct", 294 x: "foo", 295 y: "bar", 296 want: `{string}: 297 -: "foo" 298 +: "bar" 299 `, 300 }, { 301 name: "Private field allowed", 302 x: privateStruct{ 303 privateField: "Foo", 304 }, 305 y: privateStruct{ 306 privateField: "Bar", 307 }, 308 want: `{kmp.privateStruct}.privateField: 309 -: "Foo" 310 +: "Bar" 311 `, 312 opts: []cmp.Option{cmp.AllowUnexported(privateStruct{})}, 313 }, { 314 name: "Private field ignored", 315 x: privateStruct{ 316 privateField: "Foo", 317 }, 318 y: privateStruct{ 319 privateField: "Bar", 320 }, 321 want: "", 322 opts: []cmp.Option{cmpopts.IgnoreUnexported(privateStruct{})}, 323 }, { 324 name: "Map with a missing key", 325 x: map[string]string{ 326 "Foo": "Bar", 327 }, 328 y: map[string]string{}, 329 want: `{map[string]string}["Foo"]: 330 -: "Bar" 331 `, 332 }, { 333 name: "Map add a key", 334 x: map[string]string{}, 335 y: map[string]string{ 336 "Foo": "Bar", 337 }, 338 want: `{map[string]string}["Foo"]: 339 +: "Bar" 340 `, 341 }} 342 343 for _, test := range tests { 344 t.Run(test.name, func(t *testing.T) { 345 reporter := new(shortDiffReporter) 346 cmp.Equal(test.x, test.y, append(test.opts, cmp.Reporter(reporter))...) 347 got, err := reporter.Diff() 348 if test.expectErr { 349 if err == nil { 350 t.Fatalf("%s: Diff(), expected err, got nil", test.name) 351 } 352 } else { 353 if err != nil { 354 t.Fatalf("%s: Diff(), unexpected err: %v", test.name, err) 355 } 356 if diff := cmp.Diff(test.want, got); diff != "" { 357 t.Errorf("%s: Diff() (-want, +got):\n %s", test.name, diff) 358 } 359 } 360 }) 361 } 362 }