1// Copyright 2015 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package proptools 16 17import ( 18 "fmt" 19 "reflect" 20 "testing" 21) 22 23var clonePropertiesTestCases = []struct { 24 in interface{} 25 out interface{} 26 err error 27}{ 28 // Valid inputs 29 30 { 31 // Clone bool 32 in: &struct{ B1, B2 bool }{ 33 B1: true, 34 B2: false, 35 }, 36 out: &struct{ B1, B2 bool }{ 37 B1: true, 38 B2: false, 39 }, 40 }, 41 { 42 // Clone strings 43 in: &struct{ S string }{ 44 S: "string1", 45 }, 46 out: &struct{ S string }{ 47 S: "string1", 48 }, 49 }, 50 { 51 // Clone slice 52 in: &struct{ S []string }{ 53 S: []string{"string1"}, 54 }, 55 out: &struct{ S []string }{ 56 S: []string{"string1"}, 57 }, 58 }, 59 { 60 // Clone empty slice 61 in: &struct{ S []string }{ 62 S: []string{}, 63 }, 64 out: &struct{ S []string }{ 65 S: []string{}, 66 }, 67 }, 68 { 69 // Clone nil slice 70 in: &struct{ S []string }{}, 71 out: &struct{ S []string }{}, 72 }, 73 { 74 // Clone slice of structs 75 in: &struct{ S []struct{ T string } }{ 76 S: []struct{ T string }{ 77 {"string1"}, {"string2"}, 78 }, 79 }, 80 out: &struct{ S []struct{ T string } }{ 81 S: []struct{ T string }{ 82 {"string1"}, {"string2"}, 83 }, 84 }, 85 }, 86 { 87 // Clone map 88 in: &struct{ S map[string]string }{ 89 S: map[string]string{"key": "string1"}, 90 }, 91 out: &struct{ S map[string]string }{ 92 S: map[string]string{"key": "string1"}, 93 }, 94 }, 95 { 96 // Clone empty map 97 in: &struct{ S map[string]string }{ 98 S: map[string]string{}, 99 }, 100 out: &struct{ S map[string]string }{ 101 S: map[string]string{}, 102 }, 103 }, 104 { 105 // Clone nil map 106 in: &struct{ S map[string]string }{}, 107 out: &struct{ S map[string]string }{}, 108 }, 109 { 110 // Clone pointer to bool 111 in: &struct{ B1, B2 *bool }{ 112 B1: BoolPtr(true), 113 B2: BoolPtr(false), 114 }, 115 out: &struct{ B1, B2 *bool }{ 116 B1: BoolPtr(true), 117 B2: BoolPtr(false), 118 }, 119 }, 120 { 121 // Clone pointer to string 122 in: &struct{ S *string }{ 123 S: StringPtr("string1"), 124 }, 125 out: &struct{ S *string }{ 126 S: StringPtr("string1"), 127 }, 128 }, 129 { 130 // Clone pointer to int64 131 in: &struct{ S *int64 }{ 132 S: Int64Ptr(5), 133 }, 134 out: &struct{ S *int64 }{ 135 S: Int64Ptr(5), 136 }, 137 }, 138 { 139 // Clone struct 140 in: &struct{ S struct{ S string } }{ 141 S: struct{ S string }{ 142 S: "string1", 143 }, 144 }, 145 out: &struct{ S struct{ S string } }{ 146 S: struct{ S string }{ 147 S: "string1", 148 }, 149 }, 150 }, 151 { 152 // Clone struct pointer 153 in: &struct{ S *struct{ S string } }{ 154 S: &struct{ S string }{ 155 S: "string1", 156 }, 157 }, 158 out: &struct{ S *struct{ S string } }{ 159 S: &struct{ S string }{ 160 S: "string1", 161 }, 162 }, 163 }, 164 { 165 // Clone interface 166 in: &struct{ S interface{} }{ 167 S: &struct{ S string }{ 168 S: "string1", 169 }, 170 }, 171 out: &struct{ S interface{} }{ 172 S: &struct{ S string }{ 173 S: "string1", 174 }, 175 }, 176 }, 177 { 178 // Clone nested interface 179 in: &struct { 180 Nested struct{ S interface{} } 181 }{ 182 Nested: struct{ S interface{} }{ 183 S: &struct{ S string }{ 184 S: "string1", 185 }, 186 }, 187 }, 188 out: &struct { 189 Nested struct{ S interface{} } 190 }{ 191 Nested: struct{ S interface{} }{ 192 S: &struct{ S string }{ 193 S: "string1", 194 }, 195 }, 196 }, 197 }, { 198 // Empty struct 199 in: &struct{}{}, 200 out: &struct{}{}, 201 }, 202 { 203 // Interface nil 204 in: &struct{ S interface{} }{ 205 S: nil, 206 }, 207 out: &struct{ S interface{} }{ 208 S: nil, 209 }, 210 }, 211 { 212 // Interface pointer to nil 213 in: &struct{ S interface{} }{ 214 S: (*struct{ S string })(nil), 215 }, 216 out: &struct{ S interface{} }{ 217 S: (*struct{ S string })(nil), 218 }, 219 }, 220 { 221 // Pointer nil 222 in: &struct{ S *struct{} }{ 223 S: nil, 224 }, 225 out: &struct{ S *struct{} }{ 226 S: nil, 227 }, 228 }, 229 { 230 // Anonymous struct 231 in: &struct { 232 EmbeddedStruct 233 Nested struct{ EmbeddedStruct } 234 }{ 235 EmbeddedStruct: EmbeddedStruct{ 236 S: "string1", 237 I: Int64Ptr(55), 238 }, 239 Nested: struct{ EmbeddedStruct }{ 240 EmbeddedStruct: EmbeddedStruct{ 241 S: "string2", 242 I: Int64Ptr(5), 243 }, 244 }, 245 }, 246 out: &struct { 247 EmbeddedStruct 248 Nested struct{ EmbeddedStruct } 249 }{ 250 EmbeddedStruct: EmbeddedStruct{ 251 S: "string1", 252 I: Int64Ptr(55), 253 }, 254 Nested: struct{ EmbeddedStruct }{ 255 EmbeddedStruct: EmbeddedStruct{ 256 S: "string2", 257 I: Int64Ptr(5), 258 }, 259 }, 260 }, 261 }, 262 { 263 // Anonymous interface 264 in: &struct { 265 EmbeddedInterface 266 Nested struct{ EmbeddedInterface } 267 }{ 268 EmbeddedInterface: &struct{ S string }{ 269 S: "string1", 270 }, 271 Nested: struct{ EmbeddedInterface }{ 272 EmbeddedInterface: &struct{ S string }{ 273 S: "string2", 274 }, 275 }, 276 }, 277 out: &struct { 278 EmbeddedInterface 279 Nested struct{ EmbeddedInterface } 280 }{ 281 EmbeddedInterface: &struct{ S string }{ 282 S: "string1", 283 }, 284 Nested: struct{ EmbeddedInterface }{ 285 EmbeddedInterface: &struct{ S string }{ 286 S: "string2", 287 }, 288 }, 289 }, 290 }, 291} 292 293type EmbeddedStruct struct { 294 S string 295 I *int64 296} 297type EmbeddedInterface interface{} 298 299func isPointerToEmptyStruct(v any) bool { 300 t := reflect.TypeOf(v) 301 if t.Kind() != reflect.Ptr { 302 return false 303 } 304 t = t.Elem() 305 if t.Kind() != reflect.Struct { 306 return false 307 } 308 if t.NumField() > 0 { 309 return false 310 } 311 return true 312} 313 314func TestCloneProperties(t *testing.T) { 315 for _, testCase := range clonePropertiesTestCases { 316 testString := fmt.Sprintf("%s", testCase.in) 317 318 got := CloneProperties(reflect.ValueOf(testCase.in)).Interface() 319 320 if !reflect.DeepEqual(testCase.out, got) { 321 t.Errorf("test case %s", testString) 322 t.Errorf("incorrect output") 323 t.Errorf(" expected: %#v", testCase.out) 324 t.Errorf(" got: %#v", got) 325 } 326 if testCase.out == got && !isPointerToEmptyStruct(testCase.out) { 327 t.Errorf("test case %s", testString) 328 t.Errorf("items should be cloned, not the original") 329 t.Errorf(" expected: %s", testCase.out) 330 t.Errorf(" got: %s", got) 331 } 332 } 333} 334 335var cloneEmptyPropertiesTestCases = []struct { 336 in interface{} 337 out interface{} 338 err error 339}{ 340 // Valid inputs 341 342 { 343 // Clone bool 344 in: &struct{ B1, B2 bool }{ 345 B1: true, 346 B2: false, 347 }, 348 out: &struct{ B1, B2 bool }{}, 349 }, 350 { 351 // Clone strings 352 in: &struct{ S string }{ 353 S: "string1", 354 }, 355 out: &struct{ S string }{}, 356 }, 357 { 358 // Clone slice 359 in: &struct{ S []string }{ 360 S: []string{"string1"}, 361 }, 362 out: &struct{ S []string }{}, 363 }, 364 { 365 // Clone empty slice 366 in: &struct{ S []string }{ 367 S: []string{}, 368 }, 369 out: &struct{ S []string }{}, 370 }, 371 { 372 // Clone nil slice 373 in: &struct{ S []string }{}, 374 out: &struct{ S []string }{}, 375 }, 376 { 377 // Clone slice of structs 378 in: &struct{ S []struct{ T string } }{ 379 S: []struct{ T string }{ 380 {"string1"}, {"string2"}, 381 }, 382 }, 383 out: &struct{ S []struct{ T string } }{ 384 S: []struct{ T string }(nil), 385 }, 386 }, 387 { 388 // Clone pointer to bool 389 in: &struct{ B1, B2 *bool }{ 390 B1: BoolPtr(true), 391 B2: BoolPtr(false), 392 }, 393 out: &struct{ B1, B2 *bool }{}, 394 }, 395 { 396 // Clone pointer to int64 397 in: &struct{ B1, B2 *int64 }{ 398 B1: Int64Ptr(5), 399 B2: Int64Ptr(4), 400 }, 401 out: &struct{ B1, B2 *int64 }{}, 402 }, 403 { 404 // Clone pointer to string 405 in: &struct{ S *string }{ 406 S: StringPtr("string1"), 407 }, 408 out: &struct{ S *string }{}, 409 }, 410 { 411 // Clone struct 412 in: &struct{ S struct{ S string } }{ 413 S: struct{ S string }{ 414 S: "string1", 415 }, 416 }, 417 out: &struct{ S struct{ S string } }{ 418 S: struct{ S string }{}, 419 }, 420 }, 421 { 422 // Clone struct pointer 423 in: &struct{ S *struct{ S string } }{ 424 S: &struct{ S string }{ 425 S: "string1", 426 }, 427 }, 428 out: &struct{ S *struct{ S string } }{ 429 S: &struct{ S string }{}, 430 }, 431 }, 432 { 433 // Clone interface 434 in: &struct{ S interface{} }{ 435 S: &struct{ S string }{ 436 S: "string1", 437 }, 438 }, 439 out: &struct{ S interface{} }{ 440 S: &struct{ S string }{}, 441 }, 442 }, 443 { 444 // Clone nested interface 445 in: &struct { 446 Nested struct{ S interface{} } 447 }{ 448 Nested: struct{ S interface{} }{ 449 S: &struct{ S string }{ 450 S: "string1", 451 }, 452 }, 453 }, 454 out: &struct { 455 Nested struct{ S interface{} } 456 }{ 457 Nested: struct{ S interface{} }{ 458 S: &struct{ S string }{}, 459 }, 460 }, 461 }, 462 { 463 // Empty struct 464 in: &struct{}{}, 465 out: &struct{}{}, 466 }, 467 { 468 // Interface nil 469 in: &struct{ S interface{} }{ 470 S: nil, 471 }, 472 out: &struct{ S interface{} }{}, 473 }, 474 { 475 // Interface pointer to nil 476 in: &struct{ S interface{} }{ 477 S: (*struct{ S string })(nil), 478 }, 479 out: &struct{ S interface{} }{ 480 S: (*struct{ S string })(nil), 481 }, 482 }, 483 { 484 // Pointer nil 485 in: &struct{ S *struct{} }{ 486 S: nil, 487 }, 488 out: &struct{ S *struct{} }{}, 489 }, 490 { 491 // Anonymous struct 492 in: &struct { 493 EmbeddedStruct 494 Nested struct{ EmbeddedStruct } 495 }{ 496 EmbeddedStruct: EmbeddedStruct{ 497 S: "string1", 498 }, 499 Nested: struct{ EmbeddedStruct }{ 500 EmbeddedStruct: EmbeddedStruct{ 501 S: "string2", 502 }, 503 }, 504 }, 505 out: &struct { 506 EmbeddedStruct 507 Nested struct{ EmbeddedStruct } 508 }{ 509 EmbeddedStruct: EmbeddedStruct{}, 510 Nested: struct{ EmbeddedStruct }{ 511 EmbeddedStruct: EmbeddedStruct{}, 512 }, 513 }, 514 }, 515 { 516 // Anonymous interface 517 in: &struct { 518 EmbeddedInterface 519 Nested struct{ EmbeddedInterface } 520 }{ 521 EmbeddedInterface: &struct{ S string }{ 522 S: "string1", 523 }, 524 Nested: struct{ EmbeddedInterface }{ 525 EmbeddedInterface: &struct{ S string }{ 526 S: "string2", 527 }, 528 }, 529 }, 530 out: &struct { 531 EmbeddedInterface 532 Nested struct{ EmbeddedInterface } 533 }{ 534 EmbeddedInterface: &struct{ S string }{}, 535 Nested: struct{ EmbeddedInterface }{ 536 EmbeddedInterface: &struct{ S string }{}, 537 }, 538 }, 539 }, 540} 541 542func TestCloneEmptyProperties(t *testing.T) { 543 for _, testCase := range cloneEmptyPropertiesTestCases { 544 testString := fmt.Sprintf("%#v", testCase.in) 545 546 got := CloneEmptyProperties(reflect.ValueOf(testCase.in)).Interface() 547 548 if !reflect.DeepEqual(testCase.out, got) { 549 t.Errorf("test case %s", testString) 550 t.Errorf("incorrect output") 551 t.Errorf(" expected: %#v", testCase.out) 552 t.Errorf(" got: %#v", got) 553 } 554 } 555} 556 557func TestZeroProperties(t *testing.T) { 558 for _, testCase := range cloneEmptyPropertiesTestCases { 559 testString := fmt.Sprintf("%#v", testCase.in) 560 561 got := CloneProperties(reflect.ValueOf(testCase.in)).Interface() 562 ZeroProperties(reflect.ValueOf(got)) 563 564 if !reflect.DeepEqual(testCase.out, got) { 565 t.Errorf("test case %s", testString) 566 t.Errorf("incorrect output") 567 t.Errorf(" expected: %#v", testCase.out) 568 t.Errorf(" got: %#v", got) 569 } 570 } 571} 572