blob: 047037979c1e4ca7266ba57d79b4531ce42c52e0 [file] [log] [blame]
Colin Cross0bc7e072015-10-27 18:15:15 -07001// 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 "errors"
19 "fmt"
20 "reflect"
21 "strings"
22 "testing"
23)
24
Colin Cross75c47012016-05-05 15:58:02 -070025type appendPropertyTestCase struct {
Jiyong Park10f27f82019-11-15 18:31:56 +090026 in1 interface{}
27 in2 interface{}
28 out interface{}
29 order Order // default is Append
30 filter ExtendPropertyFilterFunc
31 err error
Colin Cross75c47012016-05-05 15:58:02 -070032}
Colin Cross0bc7e072015-10-27 18:15:15 -070033
Colin Cross75c47012016-05-05 15:58:02 -070034func appendPropertiesTestCases() []appendPropertyTestCase {
35 return []appendPropertyTestCase{
36 // Valid inputs
37
38 {
39 // Append bool
40 in1: &struct{ B1, B2, B3, B4 bool }{
41 B1: true,
42 B2: false,
43 B3: true,
44 B4: false,
45 },
46 in2: &struct{ B1, B2, B3, B4 bool }{
47 B1: true,
48 B2: true,
49 B3: false,
50 B4: false,
51 },
52 out: &struct{ B1, B2, B3, B4 bool }{
53 B1: true,
54 B2: true,
55 B3: true,
56 B4: false,
57 },
Colin Cross0bc7e072015-10-27 18:15:15 -070058 },
Colin Cross75c47012016-05-05 15:58:02 -070059 {
60 // Prepend bool
61 in1: &struct{ B1, B2, B3, B4 bool }{
62 B1: true,
63 B2: false,
64 B3: true,
65 B4: false,
66 },
67 in2: &struct{ B1, B2, B3, B4 bool }{
68 B1: true,
69 B2: true,
70 B3: false,
71 B4: false,
72 },
73 out: &struct{ B1, B2, B3, B4 bool }{
74 B1: true,
75 B2: true,
76 B3: true,
77 B4: false,
78 },
Jiyong Park10f27f82019-11-15 18:31:56 +090079 order: Prepend,
Colin Cross0bc7e072015-10-27 18:15:15 -070080 },
Colin Cross75c47012016-05-05 15:58:02 -070081 {
82 // Append strings
83 in1: &struct{ S string }{
Colin Cross0bc7e072015-10-27 18:15:15 -070084 S: "string1",
85 },
Colin Cross75c47012016-05-05 15:58:02 -070086 in2: &struct{ S string }{
Colin Cross0bc7e072015-10-27 18:15:15 -070087 S: "string2",
88 },
Colin Cross75c47012016-05-05 15:58:02 -070089 out: &struct{ S string }{
Colin Cross0bc7e072015-10-27 18:15:15 -070090 S: "string1string2",
91 },
92 },
Colin Cross75c47012016-05-05 15:58:02 -070093 {
94 // Prepend strings
95 in1: &struct{ S string }{
Colin Cross0bc7e072015-10-27 18:15:15 -070096 S: "string1",
97 },
Colin Cross75c47012016-05-05 15:58:02 -070098 in2: &struct{ S string }{
Colin Cross0bc7e072015-10-27 18:15:15 -070099 S: "string2",
100 },
Colin Cross75c47012016-05-05 15:58:02 -0700101 out: &struct{ S string }{
Colin Cross0bc7e072015-10-27 18:15:15 -0700102 S: "string2string1",
103 },
Jiyong Park10f27f82019-11-15 18:31:56 +0900104 order: Prepend,
Colin Cross0bc7e072015-10-27 18:15:15 -0700105 },
Colin Cross75c47012016-05-05 15:58:02 -0700106 {
107 // Append pointer to bool
108 in1: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
109 B1: BoolPtr(true),
110 B2: BoolPtr(false),
111 B3: nil,
112 B4: BoolPtr(true),
113 B5: BoolPtr(false),
114 B6: nil,
115 B7: BoolPtr(true),
116 B8: BoolPtr(false),
117 B9: nil,
118 },
119 in2: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
120 B1: nil,
121 B2: nil,
122 B3: nil,
123 B4: BoolPtr(true),
124 B5: BoolPtr(true),
125 B6: BoolPtr(true),
126 B7: BoolPtr(false),
127 B8: BoolPtr(false),
128 B9: BoolPtr(false),
129 },
130 out: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
131 B1: BoolPtr(true),
132 B2: BoolPtr(false),
133 B3: nil,
134 B4: BoolPtr(true),
135 B5: BoolPtr(true),
136 B6: BoolPtr(true),
137 B7: BoolPtr(false),
138 B8: BoolPtr(false),
139 B9: BoolPtr(false),
Colin Cross0bc7e072015-10-27 18:15:15 -0700140 },
141 },
Colin Cross75c47012016-05-05 15:58:02 -0700142 {
143 // Prepend pointer to bool
144 in1: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
145 B1: BoolPtr(true),
146 B2: BoolPtr(false),
147 B3: nil,
148 B4: BoolPtr(true),
149 B5: BoolPtr(false),
150 B6: nil,
151 B7: BoolPtr(true),
152 B8: BoolPtr(false),
153 B9: nil,
154 },
155 in2: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
156 B1: nil,
157 B2: nil,
158 B3: nil,
159 B4: BoolPtr(true),
160 B5: BoolPtr(true),
161 B6: BoolPtr(true),
162 B7: BoolPtr(false),
163 B8: BoolPtr(false),
164 B9: BoolPtr(false),
165 },
166 out: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
167 B1: BoolPtr(true),
168 B2: BoolPtr(false),
169 B3: nil,
170 B4: BoolPtr(true),
171 B5: BoolPtr(false),
172 B6: BoolPtr(true),
173 B7: BoolPtr(true),
174 B8: BoolPtr(false),
175 B9: BoolPtr(false),
176 },
Jiyong Park10f27f82019-11-15 18:31:56 +0900177 order: Prepend,
Colin Cross75c47012016-05-05 15:58:02 -0700178 },
179 {
Nan Zhangf5865442017-11-01 14:03:28 -0700180 // Append pointer to integer
181 in1: &struct{ I1, I2, I3, I4, I5, I6, I7, I8, I9 *int64 }{
182 I1: Int64Ptr(55),
183 I2: Int64Ptr(-3),
184 I3: nil,
185 I4: Int64Ptr(100),
186 I5: Int64Ptr(33),
187 I6: nil,
188 I7: Int64Ptr(77),
189 I8: Int64Ptr(0),
190 I9: nil,
191 },
192 in2: &struct{ I1, I2, I3, I4, I5, I6, I7, I8, I9 *int64 }{
193 I1: nil,
194 I2: nil,
195 I3: nil,
196 I4: Int64Ptr(1),
197 I5: Int64Ptr(-2),
198 I6: Int64Ptr(8),
199 I7: Int64Ptr(9),
200 I8: Int64Ptr(10),
201 I9: Int64Ptr(11),
202 },
203 out: &struct{ I1, I2, I3, I4, I5, I6, I7, I8, I9 *int64 }{
204 I1: Int64Ptr(55),
205 I2: Int64Ptr(-3),
206 I3: nil,
207 I4: Int64Ptr(1),
208 I5: Int64Ptr(-2),
209 I6: Int64Ptr(8),
210 I7: Int64Ptr(9),
211 I8: Int64Ptr(10),
212 I9: Int64Ptr(11),
213 },
214 },
215 {
216 // Prepend pointer to integer
217 in1: &struct{ I1, I2, I3 *int64 }{
218 I1: Int64Ptr(55),
219 I3: nil,
220 },
221 in2: &struct{ I1, I2, I3 *int64 }{
222 I2: Int64Ptr(33),
223 },
224 out: &struct{ I1, I2, I3 *int64 }{
225 I1: Int64Ptr(55),
226 I2: Int64Ptr(33),
227 I3: nil,
228 },
Jiyong Park10f27f82019-11-15 18:31:56 +0900229 order: Prepend,
Nan Zhangf5865442017-11-01 14:03:28 -0700230 },
231 {
Colin Cross75c47012016-05-05 15:58:02 -0700232 // Append pointer to strings
233 in1: &struct{ S1, S2, S3, S4 *string }{
234 S1: StringPtr("string1"),
235 S2: StringPtr("string2"),
236 },
237 in2: &struct{ S1, S2, S3, S4 *string }{
238 S1: StringPtr("string3"),
239 S3: StringPtr("string4"),
240 },
241 out: &struct{ S1, S2, S3, S4 *string }{
242 S1: StringPtr("string3"),
243 S2: StringPtr("string2"),
244 S3: StringPtr("string4"),
245 S4: nil,
Colin Cross0bc7e072015-10-27 18:15:15 -0700246 },
247 },
Colin Cross75c47012016-05-05 15:58:02 -0700248 {
249 // Prepend pointer to strings
250 in1: &struct{ S1, S2, S3, S4 *string }{
251 S1: StringPtr("string1"),
252 S2: StringPtr("string2"),
253 },
254 in2: &struct{ S1, S2, S3, S4 *string }{
255 S1: StringPtr("string3"),
256 S3: StringPtr("string4"),
257 },
258 out: &struct{ S1, S2, S3, S4 *string }{
259 S1: StringPtr("string1"),
260 S2: StringPtr("string2"),
261 S3: StringPtr("string4"),
262 S4: nil,
263 },
Jiyong Park10f27f82019-11-15 18:31:56 +0900264 order: Prepend,
Colin Cross75c47012016-05-05 15:58:02 -0700265 },
266 {
267 // Append slice
268 in1: &struct{ S []string }{
269 S: []string{"string1"},
270 },
271 in2: &struct{ S []string }{
272 S: []string{"string2"},
273 },
274 out: &struct{ S []string }{
275 S: []string{"string1", "string2"},
Colin Cross0bc7e072015-10-27 18:15:15 -0700276 },
277 },
Colin Cross75c47012016-05-05 15:58:02 -0700278 {
279 // Prepend slice
280 in1: &struct{ S []string }{
281 S: []string{"string1"},
282 },
283 in2: &struct{ S []string }{
284 S: []string{"string2"},
285 },
286 out: &struct{ S []string }{
287 S: []string{"string2", "string1"},
288 },
Jiyong Park10f27f82019-11-15 18:31:56 +0900289 order: Prepend,
290 },
291 {
292 // Replace slice
293 in1: &struct{ S []string }{
294 S: []string{"string1"},
295 },
296 in2: &struct{ S []string }{
297 S: []string{"string2"},
298 },
299 out: &struct{ S []string }{
300 S: []string{"string2"},
301 },
302 order: Replace,
Colin Cross75c47012016-05-05 15:58:02 -0700303 },
304 {
305 // Append empty slice
306 in1: &struct{ S1, S2 []string }{
307 S1: []string{"string1"},
308 S2: []string{},
309 },
310 in2: &struct{ S1, S2 []string }{
311 S1: []string{},
312 S2: []string{"string2"},
313 },
314 out: &struct{ S1, S2 []string }{
315 S1: []string{"string1"},
316 S2: []string{"string2"},
Colin Cross0bc7e072015-10-27 18:15:15 -0700317 },
318 },
Colin Cross75c47012016-05-05 15:58:02 -0700319 {
320 // Prepend empty slice
321 in1: &struct{ S1, S2 []string }{
322 S1: []string{"string1"},
323 S2: []string{},
324 },
325 in2: &struct{ S1, S2 []string }{
326 S1: []string{},
327 S2: []string{"string2"},
328 },
329 out: &struct{ S1, S2 []string }{
330 S1: []string{"string1"},
331 S2: []string{"string2"},
332 },
Jiyong Park10f27f82019-11-15 18:31:56 +0900333 order: Prepend,
334 },
335 {
336 // Replace empty slice
337 in1: &struct{ S1, S2 []string }{
338 S1: []string{"string1"},
339 S2: []string{},
340 },
341 in2: &struct{ S1, S2 []string }{
342 S1: []string{},
343 S2: []string{"string2"},
344 },
345 out: &struct{ S1, S2 []string }{
346 S1: []string{},
347 S2: []string{"string2"},
348 },
349 order: Replace,
Colin Cross75c47012016-05-05 15:58:02 -0700350 },
351 {
352 // Append nil slice
353 in1: &struct{ S1, S2, S3 []string }{
354 S1: []string{"string1"},
355 },
356 in2: &struct{ S1, S2, S3 []string }{
357 S2: []string{"string2"},
358 },
359 out: &struct{ S1, S2, S3 []string }{
360 S1: []string{"string1"},
361 S2: []string{"string2"},
362 S3: nil,
Colin Cross0bc7e072015-10-27 18:15:15 -0700363 },
364 },
Colin Cross75c47012016-05-05 15:58:02 -0700365 {
366 // Prepend nil slice
367 in1: &struct{ S1, S2, S3 []string }{
368 S1: []string{"string1"},
Colin Cross0bc7e072015-10-27 18:15:15 -0700369 },
Colin Cross75c47012016-05-05 15:58:02 -0700370 in2: &struct{ S1, S2, S3 []string }{
371 S2: []string{"string2"},
Colin Cross9d1469d2015-11-20 17:03:25 -0800372 },
Colin Cross75c47012016-05-05 15:58:02 -0700373 out: &struct{ S1, S2, S3 []string }{
374 S1: []string{"string1"},
375 S2: []string{"string2"},
376 S3: nil,
377 },
Jiyong Park10f27f82019-11-15 18:31:56 +0900378 order: Prepend,
379 },
380 {
381 // Replace nil slice
382 in1: &struct{ S1, S2, S3 []string }{
383 S1: []string{"string1"},
384 },
385 in2: &struct{ S1, S2, S3 []string }{
386 S2: []string{"string2"},
387 },
388 out: &struct{ S1, S2, S3 []string }{
389 S1: []string{"string1"},
390 S2: []string{"string2"},
391 S3: nil,
392 },
393 order: Replace,
394 },
395 {
396 // Replace embedded slice
397 in1: &struct{ S *struct{ S1 []string } }{
398 S: &struct{ S1 []string }{
399 S1: []string{"string1"},
400 },
401 },
402 in2: &struct{ S *struct{ S1 []string } }{
403 S: &struct{ S1 []string }{
404 S1: []string{"string2"},
405 },
406 },
407 out: &struct{ S *struct{ S1 []string } }{
408 S: &struct{ S1 []string }{
409 S1: []string{"string2"},
410 },
411 },
412 order: Replace,
Colin Cross75c47012016-05-05 15:58:02 -0700413 },
414 {
Sasha Smundak29fdcad2020-02-11 22:39:47 -0800415 // Append slice of structs
416 in1: &struct{ S []struct{ F string } }{
417 S: []struct{ F string }{
418 {F: "foo"}, {F: "bar"},
419 },
420 },
421 in2: &struct{ S []struct{ F string } }{
422 S: []struct{ F string }{
423 {F: "baz"},
424 },
425 },
426 out: &struct{ S []struct{ F string } }{
427 S: []struct{ F string }{
428 {F: "foo"}, {F: "bar"}, {F: "baz"},
429 },
430 },
431 order: Append,
432 },
433 {
434 // Prepend slice of structs
435 in1: &struct{ S []struct{ F string } }{
436 S: []struct{ F string }{
437 {F: "foo"}, {F: "bar"},
438 },
439 },
440 in2: &struct{ S []struct{ F string } }{
441 S: []struct{ F string }{
442 {F: "baz"},
443 },
444 },
445 out: &struct{ S []struct{ F string } }{
446 S: []struct{ F string }{
447 {F: "baz"}, {F: "foo"}, {F: "bar"},
448 },
449 },
450 order: Prepend,
451 },
452 {
453 // Replace slice of structs
454 in1: &struct{ S []struct{ F string } }{
455 S: []struct{ F string }{
456 {F: "foo"}, {F: "bar"},
457 },
458 },
459 in2: &struct{ S []struct{ F string } }{
460 S: []struct{ F string }{
461 {F: "baz"},
462 },
463 },
464 out: &struct{ S []struct{ F string } }{
465 S: []struct{ F string }{
466 {F: "baz"},
467 },
468 },
469 order: Replace,
470 },
471 {
Colin Cross75c47012016-05-05 15:58:02 -0700472 // Append pointer
473 in1: &struct{ S *struct{ S string } }{
474 S: &struct{ S string }{
475 S: "string1",
476 },
477 },
478 in2: &struct{ S *struct{ S string } }{
479 S: &struct{ S string }{
Colin Cross9d1469d2015-11-20 17:03:25 -0800480 S: "string2",
481 },
482 },
Colin Cross75c47012016-05-05 15:58:02 -0700483 out: &struct{ S *struct{ S string } }{
484 S: &struct{ S string }{
485 S: "string1string2",
Colin Cross9d1469d2015-11-20 17:03:25 -0800486 },
487 },
488 },
Colin Cross75c47012016-05-05 15:58:02 -0700489 {
490 // Prepend pointer
491 in1: &struct{ S *struct{ S string } }{
492 S: &struct{ S string }{
493 S: "string1",
Colin Cross9d1469d2015-11-20 17:03:25 -0800494 },
495 },
Colin Cross75c47012016-05-05 15:58:02 -0700496 in2: &struct{ S *struct{ S string } }{
497 S: &struct{ S string }{
Colin Cross9d1469d2015-11-20 17:03:25 -0800498 S: "string2",
499 },
500 },
Colin Cross75c47012016-05-05 15:58:02 -0700501 out: &struct{ S *struct{ S string } }{
502 S: &struct{ S string }{
503 S: "string2string1",
504 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800505 },
Jiyong Park10f27f82019-11-15 18:31:56 +0900506 order: Prepend,
Colin Cross75c47012016-05-05 15:58:02 -0700507 },
508 {
509 // Append interface
510 in1: &struct{ S interface{} }{
511 S: &struct{ S string }{
512 S: "string1",
513 },
514 },
515 in2: &struct{ S interface{} }{
516 S: &struct{ S string }{
517 S: "string2",
518 },
519 },
520 out: &struct{ S interface{} }{
521 S: &struct{ S string }{
522 S: "string1string2",
Colin Cross9d1469d2015-11-20 17:03:25 -0800523 },
524 },
525 },
Colin Cross75c47012016-05-05 15:58:02 -0700526 {
527 // Prepend interface
528 in1: &struct{ S interface{} }{
529 S: &struct{ S string }{
530 S: "string1",
531 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800532 },
Colin Cross75c47012016-05-05 15:58:02 -0700533 in2: &struct{ S interface{} }{
534 S: &struct{ S string }{
535 S: "string2",
536 },
537 },
538 out: &struct{ S interface{} }{
539 S: &struct{ S string }{
540 S: "string2string1",
541 },
542 },
Jiyong Park10f27f82019-11-15 18:31:56 +0900543 order: Prepend,
Colin Cross75c47012016-05-05 15:58:02 -0700544 },
545 {
546 // Unexported field
547 in1: &struct{ s string }{
548 s: "string1",
549 },
550 in2: &struct{ s string }{
551 s: "string2",
552 },
553 out: &struct{ s string }{
554 s: "string1",
555 },
556 },
557 {
Nan Zhangf5865442017-11-01 14:03:28 -0700558 // Unexported field
559 in1: &struct{ i *int64 }{
560 i: Int64Ptr(33),
561 },
562 in2: &struct{ i *int64 }{
563 i: Int64Ptr(5),
564 },
565 out: &struct{ i *int64 }{
566 i: Int64Ptr(33),
567 },
568 },
569 {
Colin Cross75c47012016-05-05 15:58:02 -0700570 // Empty struct
571 in1: &struct{}{},
572 in2: &struct{}{},
573 out: &struct{}{},
574 },
575 {
576 // Interface nil
577 in1: &struct{ S interface{} }{
578 S: nil,
579 },
580 in2: &struct{ S interface{} }{
581 S: nil,
582 },
583 out: &struct{ S interface{} }{
584 S: nil,
585 },
586 },
587 {
588 // Pointer nil
589 in1: &struct{ S *struct{} }{
590 S: nil,
591 },
592 in2: &struct{ S *struct{} }{
593 S: nil,
594 },
595 out: &struct{ S *struct{} }{
596 S: nil,
597 },
598 },
599 {
600 // Anonymous struct
601 in1: &struct {
602 EmbeddedStruct
603 Nested struct{ EmbeddedStruct }
604 }{
605 EmbeddedStruct: EmbeddedStruct{
606 S: "string1",
Nan Zhangf5865442017-11-01 14:03:28 -0700607 I: Int64Ptr(55),
Colin Cross75c47012016-05-05 15:58:02 -0700608 },
609 Nested: struct{ EmbeddedStruct }{
610 EmbeddedStruct: EmbeddedStruct{
611 S: "string2",
Nan Zhangf5865442017-11-01 14:03:28 -0700612 I: Int64Ptr(-4),
Colin Cross75c47012016-05-05 15:58:02 -0700613 },
614 },
615 },
616 in2: &struct {
617 EmbeddedStruct
618 Nested struct{ EmbeddedStruct }
619 }{
620 EmbeddedStruct: EmbeddedStruct{
621 S: "string3",
Nan Zhangf5865442017-11-01 14:03:28 -0700622 I: Int64Ptr(66),
Colin Cross75c47012016-05-05 15:58:02 -0700623 },
624 Nested: struct{ EmbeddedStruct }{
625 EmbeddedStruct: EmbeddedStruct{
626 S: "string4",
Nan Zhangf5865442017-11-01 14:03:28 -0700627 I: Int64Ptr(-8),
Colin Cross75c47012016-05-05 15:58:02 -0700628 },
629 },
630 },
631 out: &struct {
632 EmbeddedStruct
633 Nested struct{ EmbeddedStruct }
634 }{
635 EmbeddedStruct: EmbeddedStruct{
636 S: "string1string3",
Nan Zhangf5865442017-11-01 14:03:28 -0700637 I: Int64Ptr(66),
Colin Cross75c47012016-05-05 15:58:02 -0700638 },
639 Nested: struct{ EmbeddedStruct }{
640 EmbeddedStruct: EmbeddedStruct{
641 S: "string2string4",
Nan Zhangf5865442017-11-01 14:03:28 -0700642 I: Int64Ptr(-8),
Colin Cross75c47012016-05-05 15:58:02 -0700643 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800644 },
645 },
646 },
Colin Cross75c47012016-05-05 15:58:02 -0700647 {
648 // Anonymous interface
649 in1: &struct {
650 EmbeddedInterface
651 Nested struct{ EmbeddedInterface }
652 }{
Nan Zhangf5865442017-11-01 14:03:28 -0700653 EmbeddedInterface: &struct {
654 S string
655 I *int64
656 }{
Colin Cross75c47012016-05-05 15:58:02 -0700657 S: "string1",
Nan Zhangf5865442017-11-01 14:03:28 -0700658 I: Int64Ptr(-8),
Colin Cross75c47012016-05-05 15:58:02 -0700659 },
660 Nested: struct{ EmbeddedInterface }{
Nan Zhangf5865442017-11-01 14:03:28 -0700661 EmbeddedInterface: &struct {
662 S string
663 I *int64
664 }{
Colin Cross75c47012016-05-05 15:58:02 -0700665 S: "string2",
Nan Zhangf5865442017-11-01 14:03:28 -0700666 I: Int64Ptr(55),
Colin Cross75c47012016-05-05 15:58:02 -0700667 },
668 },
669 },
670 in2: &struct {
671 EmbeddedInterface
672 Nested struct{ EmbeddedInterface }
673 }{
Nan Zhangf5865442017-11-01 14:03:28 -0700674 EmbeddedInterface: &struct {
675 S string
676 I *int64
677 }{
Colin Cross75c47012016-05-05 15:58:02 -0700678 S: "string3",
Nan Zhangf5865442017-11-01 14:03:28 -0700679 I: Int64Ptr(6),
Colin Cross75c47012016-05-05 15:58:02 -0700680 },
681 Nested: struct{ EmbeddedInterface }{
Nan Zhangf5865442017-11-01 14:03:28 -0700682 EmbeddedInterface: &struct {
683 S string
684 I *int64
685 }{
Colin Cross75c47012016-05-05 15:58:02 -0700686 S: "string4",
Nan Zhangf5865442017-11-01 14:03:28 -0700687 I: Int64Ptr(6),
Colin Cross75c47012016-05-05 15:58:02 -0700688 },
689 },
690 },
691 out: &struct {
692 EmbeddedInterface
693 Nested struct{ EmbeddedInterface }
694 }{
Nan Zhangf5865442017-11-01 14:03:28 -0700695 EmbeddedInterface: &struct {
696 S string
697 I *int64
698 }{
Colin Cross75c47012016-05-05 15:58:02 -0700699 S: "string1string3",
Nan Zhangf5865442017-11-01 14:03:28 -0700700 I: Int64Ptr(6),
Colin Cross75c47012016-05-05 15:58:02 -0700701 },
702 Nested: struct{ EmbeddedInterface }{
Nan Zhangf5865442017-11-01 14:03:28 -0700703 EmbeddedInterface: &struct {
704 S string
705 I *int64
706 }{
Colin Cross75c47012016-05-05 15:58:02 -0700707 S: "string2string4",
Nan Zhangf5865442017-11-01 14:03:28 -0700708 I: Int64Ptr(6),
Colin Cross75c47012016-05-05 15:58:02 -0700709 },
710 },
Colin Cross0bc7e072015-10-27 18:15:15 -0700711 },
712 },
Colin Crossc3d73122016-08-05 17:19:36 -0700713 {
714 // Nil pointer to a struct
715 in1: &struct {
716 Nested *struct {
717 S string
718 }
719 }{},
720 in2: &struct {
721 Nested *struct {
722 S string
723 }
724 }{
725 Nested: &struct {
726 S string
727 }{
728 S: "string",
729 },
730 },
731 out: &struct {
732 Nested *struct {
733 S string
734 }
735 }{
736 Nested: &struct {
737 S string
738 }{
739 S: "string",
740 },
741 },
742 },
743 {
744 // Nil pointer to a struct in an interface
745 in1: &struct {
746 Nested interface{}
747 }{
748 Nested: (*struct{ S string })(nil),
749 },
750 in2: &struct {
751 Nested interface{}
752 }{
753 Nested: &struct {
754 S string
755 }{
756 S: "string",
757 },
758 },
759 out: &struct {
760 Nested interface{}
761 }{
762 Nested: &struct {
763 S string
764 }{
765 S: "string",
766 },
767 },
768 },
Colin Crossbf2adbf2016-08-19 18:16:33 -0700769 {
770 // Interface src nil
771 in1: &struct{ S interface{} }{
772 S: &struct{ S string }{
773 S: "string1",
774 },
775 },
776 in2: &struct{ S interface{} }{
777 S: nil,
778 },
779 out: &struct{ S interface{} }{
780 S: &struct{ S string }{
781 S: "string1",
782 },
783 },
784 },
Colin Cross75c47012016-05-05 15:58:02 -0700785
786 // Errors
787
788 {
789 // Non-pointer in1
790 in1: struct{}{},
Colin Crossc3d73122016-08-05 17:19:36 -0700791 in2: &struct{}{},
Colin Cross75c47012016-05-05 15:58:02 -0700792 err: errors.New("expected pointer to struct, got struct {}"),
793 out: struct{}{},
Colin Cross0bc7e072015-10-27 18:15:15 -0700794 },
Colin Cross75c47012016-05-05 15:58:02 -0700795 {
796 // Non-pointer in2
797 in1: &struct{}{},
798 in2: struct{}{},
799 err: errors.New("expected pointer to struct, got struct {}"),
800 out: &struct{}{},
Colin Cross0bc7e072015-10-27 18:15:15 -0700801 },
Colin Cross75c47012016-05-05 15:58:02 -0700802 {
803 // Non-struct in1
804 in1: &[]string{"bad"},
Colin Crossc3d73122016-08-05 17:19:36 -0700805 in2: &struct{}{},
Colin Cross75c47012016-05-05 15:58:02 -0700806 err: errors.New("expected pointer to struct, got *[]string"),
807 out: &[]string{"bad"},
808 },
809 {
810 // Non-struct in2
811 in1: &struct{}{},
812 in2: &[]string{"bad"},
813 err: errors.New("expected pointer to struct, got *[]string"),
814 out: &struct{}{},
815 },
816 {
817 // Mismatched types
818 in1: &struct{ A string }{
Colin Cross0bc7e072015-10-27 18:15:15 -0700819 A: "string1",
820 },
Colin Cross75c47012016-05-05 15:58:02 -0700821 in2: &struct{ B string }{
Colin Cross0bc7e072015-10-27 18:15:15 -0700822 B: "string2",
823 },
Colin Cross75c47012016-05-05 15:58:02 -0700824 out: &struct{ A string }{
Colin Cross0bc7e072015-10-27 18:15:15 -0700825 A: "string1",
826 },
Colin Cross75c47012016-05-05 15:58:02 -0700827 err: errors.New("expected matching types for dst and src, got *struct { A string } and *struct { B string }"),
Colin Cross0bc7e072015-10-27 18:15:15 -0700828 },
Colin Cross75c47012016-05-05 15:58:02 -0700829 {
830 // Unsupported kind
831 in1: &struct{ I int }{
Colin Cross0bc7e072015-10-27 18:15:15 -0700832 I: 1,
833 },
Colin Cross75c47012016-05-05 15:58:02 -0700834 in2: &struct{ I int }{
Colin Cross0bc7e072015-10-27 18:15:15 -0700835 I: 2,
836 },
Colin Cross75c47012016-05-05 15:58:02 -0700837 out: &struct{ I int }{
Colin Cross0bc7e072015-10-27 18:15:15 -0700838 I: 1,
839 },
Colin Cross75c47012016-05-05 15:58:02 -0700840 err: extendPropertyErrorf("i", "unsupported kind int"),
Colin Cross0bc7e072015-10-27 18:15:15 -0700841 },
Colin Cross75c47012016-05-05 15:58:02 -0700842 {
Nan Zhangf5865442017-11-01 14:03:28 -0700843 // Unsupported kind
844 in1: &struct{ I int64 }{
845 I: 1,
846 },
847 in2: &struct{ I int64 }{
848 I: 2,
849 },
850 out: &struct{ I int64 }{
851 I: 1,
852 },
853 err: extendPropertyErrorf("i", "unsupported kind int64"),
854 },
855 {
Colin Cross75c47012016-05-05 15:58:02 -0700856 // Interface nilitude mismatch
857 in1: &struct{ S interface{} }{
Colin Cross75c47012016-05-05 15:58:02 -0700858 S: nil,
859 },
Colin Crossbf2adbf2016-08-19 18:16:33 -0700860 in2: &struct{ S interface{} }{
Colin Cross75c47012016-05-05 15:58:02 -0700861 S: &struct{ S string }{
862 S: "string1",
863 },
864 },
Colin Crossbf2adbf2016-08-19 18:16:33 -0700865 out: &struct{ S interface{} }{
866 S: nil,
867 },
Colin Cross75c47012016-05-05 15:58:02 -0700868 err: extendPropertyErrorf("s", "nilitude mismatch"),
869 },
870 {
871 // Interface type mismatch
872 in1: &struct{ S interface{} }{
873 S: &struct{ A string }{
874 A: "string1",
875 },
876 },
877 in2: &struct{ S interface{} }{
878 S: &struct{ B string }{
879 B: "string2",
880 },
881 },
882 out: &struct{ S interface{} }{
883 S: &struct{ A string }{
884 A: "string1",
885 },
886 },
887 err: extendPropertyErrorf("s", "mismatched types struct { A string } and struct { B string }"),
888 },
889 {
890 // Interface not a pointer
891 in1: &struct{ S interface{} }{
892 S: struct{ S string }{
893 S: "string1",
894 },
895 },
896 in2: &struct{ S interface{} }{
897 S: struct{ S string }{
898 S: "string2",
899 },
900 },
901 out: &struct{ S interface{} }{
902 S: struct{ S string }{
903 S: "string1",
904 },
905 },
906 err: extendPropertyErrorf("s", "interface not a pointer"),
907 },
908 {
Colin Cross75c47012016-05-05 15:58:02 -0700909 // Pointer not a struct
910 in1: &struct{ S *[]string }{
911 S: &[]string{"string1"},
912 },
913 in2: &struct{ S *[]string }{
914 S: &[]string{"string2"},
915 },
916 out: &struct{ S *[]string }{
917 S: &[]string{"string1"},
918 },
919 err: extendPropertyErrorf("s", "pointer is a slice"),
920 },
921 {
922 // Error in nested struct
923 in1: &struct{ S interface{} }{
924 S: &struct{ I int }{
925 I: 1,
926 },
927 },
928 in2: &struct{ S interface{} }{
929 S: &struct{ I int }{
930 I: 2,
931 },
932 },
933 out: &struct{ S interface{} }{
934 S: &struct{ I int }{
935 I: 1,
936 },
937 },
938 err: extendPropertyErrorf("s.i", "unsupported kind int"),
939 },
Colin Cross0bc7e072015-10-27 18:15:15 -0700940
Colin Cross75c47012016-05-05 15:58:02 -0700941 // Filters
Colin Cross0bc7e072015-10-27 18:15:15 -0700942
Colin Cross75c47012016-05-05 15:58:02 -0700943 {
944 // Filter true
945 in1: &struct{ S string }{
946 S: "string1",
947 },
948 in2: &struct{ S string }{
949 S: "string2",
950 },
951 out: &struct{ S string }{
952 S: "string1string2",
953 },
954 filter: func(property string,
955 dstField, srcField reflect.StructField,
956 dstValue, srcValue interface{}) (bool, error) {
957 return true, nil
958 },
Colin Cross0bc7e072015-10-27 18:15:15 -0700959 },
Colin Cross75c47012016-05-05 15:58:02 -0700960 {
961 // Filter false
962 in1: &struct{ S string }{
963 S: "string1",
964 },
965 in2: &struct{ S string }{
966 S: "string2",
967 },
968 out: &struct{ S string }{
969 S: "string1",
970 },
971 filter: func(property string,
972 dstField, srcField reflect.StructField,
973 dstValue, srcValue interface{}) (bool, error) {
974 return false, nil
975 },
Colin Cross0bc7e072015-10-27 18:15:15 -0700976 },
Colin Cross75c47012016-05-05 15:58:02 -0700977 {
978 // Filter check args
979 in1: &struct{ S string }{
980 S: "string1",
981 },
982 in2: &struct{ S string }{
983 S: "string2",
984 },
985 out: &struct{ S string }{
986 S: "string1string2",
987 },
988 filter: func(property string,
989 dstField, srcField reflect.StructField,
990 dstValue, srcValue interface{}) (bool, error) {
991 return property == "s" &&
992 dstField.Name == "S" && srcField.Name == "S" &&
993 dstValue.(string) == "string1" && srcValue.(string) == "string2", nil
994 },
Colin Cross0bc7e072015-10-27 18:15:15 -0700995 },
Colin Cross75c47012016-05-05 15:58:02 -0700996 {
997 // Filter mutated
998 in1: &struct {
999 S string `blueprint:"mutated"`
1000 }{
1001 S: "string1",
1002 },
1003 in2: &struct {
1004 S string `blueprint:"mutated"`
1005 }{
1006 S: "string2",
1007 },
1008 out: &struct {
1009 S string `blueprint:"mutated"`
1010 }{
1011 S: "string1",
1012 },
Colin Cross0bc7e072015-10-27 18:15:15 -07001013 },
Colin Cross75c47012016-05-05 15:58:02 -07001014 {
Nan Zhangf5865442017-11-01 14:03:28 -07001015 // Filter mutated
1016 in1: &struct {
1017 S *int64 `blueprint:"mutated"`
1018 }{
1019 S: Int64Ptr(4),
1020 },
1021 in2: &struct {
1022 S *int64 `blueprint:"mutated"`
1023 }{
1024 S: Int64Ptr(5),
1025 },
1026 out: &struct {
1027 S *int64 `blueprint:"mutated"`
1028 }{
1029 S: Int64Ptr(4),
1030 },
1031 },
1032 {
Colin Cross75c47012016-05-05 15:58:02 -07001033 // Filter error
1034 in1: &struct{ S string }{
1035 S: "string1",
1036 },
1037 in2: &struct{ S string }{
1038 S: "string2",
1039 },
1040 out: &struct{ S string }{
1041 S: "string1",
1042 },
1043 filter: func(property string,
1044 dstField, srcField reflect.StructField,
1045 dstValue, srcValue interface{}) (bool, error) {
1046 return true, fmt.Errorf("filter error")
1047 },
1048 err: extendPropertyErrorf("s", "filter error"),
Colin Cross0bc7e072015-10-27 18:15:15 -07001049 },
Colin Cross75c47012016-05-05 15:58:02 -07001050 }
Colin Cross0bc7e072015-10-27 18:15:15 -07001051}
1052
1053func TestAppendProperties(t *testing.T) {
Colin Cross75c47012016-05-05 15:58:02 -07001054 for _, testCase := range appendPropertiesTestCases() {
Colin Cross0bc7e072015-10-27 18:15:15 -07001055 testString := fmt.Sprintf("%v, %v -> %v", testCase.in1, testCase.in2, testCase.out)
1056
1057 got := testCase.in1
1058 var err error
1059 var testType string
1060
Jiyong Park10f27f82019-11-15 18:31:56 +09001061 switch testCase.order {
1062 case Append:
Colin Cross0bc7e072015-10-27 18:15:15 -07001063 testType = "append"
1064 err = AppendProperties(got, testCase.in2, testCase.filter)
Jiyong Park10f27f82019-11-15 18:31:56 +09001065 case Prepend:
1066 testType = "prepend"
1067 err = PrependProperties(got, testCase.in2, testCase.filter)
1068 case Replace:
1069 testType = "replace"
1070 err = ExtendProperties(got, testCase.in2, testCase.filter, OrderReplace)
Colin Cross0bc7e072015-10-27 18:15:15 -07001071 }
1072
1073 check(t, testType, testString, got, err, testCase.out, testCase.err)
1074 }
1075}
1076
Colin Cross75c47012016-05-05 15:58:02 -07001077func TestExtendProperties(t *testing.T) {
1078 for _, testCase := range appendPropertiesTestCases() {
1079 testString := fmt.Sprintf("%v, %v -> %v", testCase.in1, testCase.in2, testCase.out)
1080
1081 got := testCase.in1
1082 var err error
1083 var testType string
1084
1085 order := func(property string,
1086 dstField, srcField reflect.StructField,
1087 dstValue, srcValue interface{}) (Order, error) {
Jiyong Park10f27f82019-11-15 18:31:56 +09001088 switch testCase.order {
1089 case Append:
Colin Cross75c47012016-05-05 15:58:02 -07001090 return Append, nil
Jiyong Park10f27f82019-11-15 18:31:56 +09001091 case Prepend:
1092 return Prepend, nil
1093 case Replace:
1094 return Replace, nil
Colin Cross75c47012016-05-05 15:58:02 -07001095 }
Jiyong Park10f27f82019-11-15 18:31:56 +09001096 return Append, errors.New("unknown order")
Colin Cross75c47012016-05-05 15:58:02 -07001097 }
1098
Jiyong Park10f27f82019-11-15 18:31:56 +09001099 switch testCase.order {
1100 case Append:
Colin Cross75c47012016-05-05 15:58:02 -07001101 testType = "prepend"
Jiyong Park10f27f82019-11-15 18:31:56 +09001102 case Prepend:
Colin Cross75c47012016-05-05 15:58:02 -07001103 testType = "append"
Jiyong Park10f27f82019-11-15 18:31:56 +09001104 case Replace:
1105 testType = "replace"
Colin Cross75c47012016-05-05 15:58:02 -07001106 }
1107
1108 err = ExtendProperties(got, testCase.in2, testCase.filter, order)
1109
1110 check(t, testType, testString, got, err, testCase.out, testCase.err)
1111 }
1112}
1113
1114type appendMatchingPropertiesTestCase struct {
Jiyong Park10f27f82019-11-15 18:31:56 +09001115 in1 []interface{}
1116 in2 interface{}
1117 out []interface{}
1118 order Order // default is Append
1119 filter ExtendPropertyFilterFunc
1120 err error
Colin Cross75c47012016-05-05 15:58:02 -07001121}
1122
1123func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase {
1124 return []appendMatchingPropertiesTestCase{
1125 {
1126 // Append strings
1127 in1: []interface{}{&struct{ S string }{
Colin Cross0bc7e072015-10-27 18:15:15 -07001128 S: "string1",
Colin Cross75c47012016-05-05 15:58:02 -07001129 }},
1130 in2: &struct{ S string }{
Colin Cross0bc7e072015-10-27 18:15:15 -07001131 S: "string2",
1132 },
Colin Cross75c47012016-05-05 15:58:02 -07001133 out: []interface{}{&struct{ S string }{
Colin Cross0bc7e072015-10-27 18:15:15 -07001134 S: "string1string2",
Colin Cross75c47012016-05-05 15:58:02 -07001135 }},
Colin Cross0bc7e072015-10-27 18:15:15 -07001136 },
Colin Cross75c47012016-05-05 15:58:02 -07001137 {
1138 // Prepend strings
1139 in1: []interface{}{&struct{ S string }{
Colin Cross0bc7e072015-10-27 18:15:15 -07001140 S: "string1",
Colin Cross75c47012016-05-05 15:58:02 -07001141 }},
1142 in2: &struct{ S string }{
Colin Cross0bc7e072015-10-27 18:15:15 -07001143 S: "string2",
1144 },
Colin Cross75c47012016-05-05 15:58:02 -07001145 out: []interface{}{&struct{ S string }{
1146 S: "string2string1",
1147 }},
Jiyong Park10f27f82019-11-15 18:31:56 +09001148 order: Prepend,
Colin Cross0bc7e072015-10-27 18:15:15 -07001149 },
Colin Cross75c47012016-05-05 15:58:02 -07001150 {
1151 // Append all
1152 in1: []interface{}{
1153 &struct{ S, A string }{
1154 S: "string1",
1155 },
1156 &struct{ S, B string }{
1157 S: "string2",
1158 },
1159 },
1160 in2: &struct{ S string }{
1161 S: "string3",
1162 },
1163 out: []interface{}{
1164 &struct{ S, A string }{
1165 S: "string1string3",
1166 },
1167 &struct{ S, B string }{
1168 S: "string2string3",
1169 },
1170 },
1171 },
1172 {
1173 // Append some
1174 in1: []interface{}{
1175 &struct{ S, A string }{
1176 S: "string1",
1177 },
1178 &struct{ B string }{},
1179 },
1180 in2: &struct{ S string }{
1181 S: "string2",
1182 },
1183 out: []interface{}{
1184 &struct{ S, A string }{
1185 S: "string1string2",
1186 },
1187 &struct{ B string }{},
1188 },
1189 },
1190 {
1191 // Append mismatched structs
1192 in1: []interface{}{&struct{ S, A string }{
1193 S: "string1",
1194 }},
1195 in2: &struct{ S string }{
1196 S: "string2",
1197 },
1198 out: []interface{}{&struct{ S, A string }{
Colin Cross0bc7e072015-10-27 18:15:15 -07001199 S: "string1string2",
Colin Cross75c47012016-05-05 15:58:02 -07001200 }},
1201 },
1202 {
1203 // Append mismatched pointer structs
1204 in1: []interface{}{&struct{ S *struct{ S, A string } }{
1205 S: &struct{ S, A string }{
1206 S: "string1",
1207 },
1208 }},
1209 in2: &struct{ S *struct{ S string } }{
1210 S: &struct{ S string }{
1211 S: "string2",
1212 },
Colin Cross0bc7e072015-10-27 18:15:15 -07001213 },
Colin Cross75c47012016-05-05 15:58:02 -07001214 out: []interface{}{&struct{ S *struct{ S, A string } }{
1215 S: &struct{ S, A string }{
1216 S: "string1string2",
1217 },
1218 }},
1219 },
Colin Crossbf2adbf2016-08-19 18:16:33 -07001220 {
1221 // Append through mismatched types
1222 in1: []interface{}{
1223 &struct{ B string }{},
1224 &struct{ S interface{} }{
1225 S: &struct{ S, A string }{
1226 S: "string1",
1227 },
1228 },
1229 },
1230 in2: &struct{ S struct{ S string } }{
1231 S: struct{ S string }{
1232 S: "string2",
1233 },
1234 },
1235 out: []interface{}{
1236 &struct{ B string }{},
1237 &struct{ S interface{} }{
1238 S: &struct{ S, A string }{
1239 S: "string1string2",
1240 },
1241 },
1242 },
1243 },
1244 {
1245 // Append through mismatched types and nil
1246 in1: []interface{}{
1247 &struct{ B string }{},
1248 &struct{ S interface{} }{
1249 S: (*struct{ S, A string })(nil),
1250 },
1251 },
1252 in2: &struct{ S struct{ S string } }{
1253 S: struct{ S string }{
1254 S: "string2",
1255 },
1256 },
1257 out: []interface{}{
1258 &struct{ B string }{},
1259 &struct{ S interface{} }{
1260 S: &struct{ S, A string }{
1261 S: "string2",
1262 },
1263 },
1264 },
1265 },
1266 {
1267 // Append through multiple matches
1268 in1: []interface{}{
1269 &struct {
1270 S struct{ S, A string }
1271 }{
1272 S: struct{ S, A string }{
1273 S: "string1",
1274 },
1275 },
1276 &struct {
1277 S struct{ S, B string }
1278 }{
1279 S: struct{ S, B string }{
1280 S: "string2",
1281 },
1282 },
1283 },
1284 in2: &struct{ S struct{ B string } }{
1285 S: struct{ B string }{
1286 B: "string3",
1287 },
1288 },
1289 out: []interface{}{
1290 &struct {
1291 S struct{ S, A string }
1292 }{
1293 S: struct{ S, A string }{
1294 S: "string1",
1295 },
1296 },
1297 &struct {
1298 S struct{ S, B string }
1299 }{
1300 S: struct{ S, B string }{
1301 S: "string2",
1302 B: "string3",
1303 },
1304 },
1305 },
1306 },
Colin Cross0bc7e072015-10-27 18:15:15 -07001307
Colin Cross75c47012016-05-05 15:58:02 -07001308 // Errors
Colin Cross0bc7e072015-10-27 18:15:15 -07001309
Colin Cross75c47012016-05-05 15:58:02 -07001310 {
1311 // Non-pointer in1
1312 in1: []interface{}{struct{}{}},
Colin Crossc3d73122016-08-05 17:19:36 -07001313 in2: &struct{}{},
Colin Cross75c47012016-05-05 15:58:02 -07001314 err: errors.New("expected pointer to struct, got struct {}"),
1315 out: []interface{}{struct{}{}},
Colin Cross0bc7e072015-10-27 18:15:15 -07001316 },
Colin Cross75c47012016-05-05 15:58:02 -07001317 {
1318 // Non-pointer in2
1319 in1: []interface{}{&struct{}{}},
1320 in2: struct{}{},
1321 err: errors.New("expected pointer to struct, got struct {}"),
1322 out: []interface{}{&struct{}{}},
Colin Cross0bc7e072015-10-27 18:15:15 -07001323 },
Colin Cross75c47012016-05-05 15:58:02 -07001324 {
1325 // Non-struct in1
1326 in1: []interface{}{&[]string{"bad"}},
Colin Crossc3d73122016-08-05 17:19:36 -07001327 in2: &struct{}{},
Colin Cross75c47012016-05-05 15:58:02 -07001328 err: errors.New("expected pointer to struct, got *[]string"),
1329 out: []interface{}{&[]string{"bad"}},
Colin Cross0bc7e072015-10-27 18:15:15 -07001330 },
Colin Cross75c47012016-05-05 15:58:02 -07001331 {
1332 // Non-struct in2
1333 in1: []interface{}{&struct{}{}},
1334 in2: &[]string{"bad"},
1335 err: errors.New("expected pointer to struct, got *[]string"),
1336 out: []interface{}{&struct{}{}},
1337 },
1338 {
1339 // Append none
1340 in1: []interface{}{
1341 &struct{ A string }{},
1342 &struct{ B string }{},
1343 },
1344 in2: &struct{ S string }{
Colin Cross0bc7e072015-10-27 18:15:15 -07001345 S: "string1",
1346 },
Colin Cross75c47012016-05-05 15:58:02 -07001347 out: []interface{}{
1348 &struct{ A string }{},
1349 &struct{ B string }{},
Colin Cross0bc7e072015-10-27 18:15:15 -07001350 },
Colin Cross75c47012016-05-05 15:58:02 -07001351 err: extendPropertyErrorf("s", "failed to find property to extend"),
Colin Cross0bc7e072015-10-27 18:15:15 -07001352 },
Colin Cross75c47012016-05-05 15:58:02 -07001353 {
1354 // Append mismatched kinds
1355 in1: []interface{}{
1356 &struct{ S string }{
1357 S: "string1",
1358 },
Colin Cross0bc7e072015-10-27 18:15:15 -07001359 },
Colin Cross75c47012016-05-05 15:58:02 -07001360 in2: &struct{ S []string }{
1361 S: []string{"string2"},
Colin Cross0bc7e072015-10-27 18:15:15 -07001362 },
Colin Cross75c47012016-05-05 15:58:02 -07001363 out: []interface{}{
1364 &struct{ S string }{
1365 S: "string1",
1366 },
1367 },
1368 err: extendPropertyErrorf("s", "mismatched types string and []string"),
Colin Cross0bc7e072015-10-27 18:15:15 -07001369 },
Colin Cross75c47012016-05-05 15:58:02 -07001370 {
1371 // Append mismatched types
1372 in1: []interface{}{
1373 &struct{ S []int }{
1374 S: []int{1},
1375 },
1376 },
1377 in2: &struct{ S []string }{
1378 S: []string{"string2"},
1379 },
1380 out: []interface{}{
1381 &struct{ S []int }{
1382 S: []int{1},
1383 },
1384 },
1385 err: extendPropertyErrorf("s", "mismatched types []int and []string"),
1386 },
1387 }
Colin Cross0bc7e072015-10-27 18:15:15 -07001388}
1389
1390func TestAppendMatchingProperties(t *testing.T) {
Colin Cross75c47012016-05-05 15:58:02 -07001391 for _, testCase := range appendMatchingPropertiesTestCases() {
Colin Cross0bc7e072015-10-27 18:15:15 -07001392 testString := fmt.Sprintf("%s, %s -> %s", p(testCase.in1), p(testCase.in2), p(testCase.out))
1393
1394 got := testCase.in1
1395 var err error
1396 var testType string
1397
Jiyong Park10f27f82019-11-15 18:31:56 +09001398 switch testCase.order {
1399 case Append:
1400 testType = "append"
Colin Cross0bc7e072015-10-27 18:15:15 -07001401 err = AppendMatchingProperties(got, testCase.in2, testCase.filter)
Jiyong Park10f27f82019-11-15 18:31:56 +09001402 case Prepend:
1403 testType = "prepend"
1404 err = PrependMatchingProperties(got, testCase.in2, testCase.filter)
1405 case Replace:
1406 testType = "replace"
1407 err = ExtendMatchingProperties(got, testCase.in2, testCase.filter, OrderReplace)
Colin Cross0bc7e072015-10-27 18:15:15 -07001408 }
1409
1410 check(t, testType, testString, got, err, testCase.out, testCase.err)
1411 }
1412}
1413
Colin Cross75c47012016-05-05 15:58:02 -07001414func TestExtendMatchingProperties(t *testing.T) {
1415 for _, testCase := range appendMatchingPropertiesTestCases() {
1416 testString := fmt.Sprintf("%s, %s -> %s", p(testCase.in1), p(testCase.in2), p(testCase.out))
1417
1418 got := testCase.in1
1419 var err error
1420 var testType string
1421
1422 order := func(property string,
1423 dstField, srcField reflect.StructField,
1424 dstValue, srcValue interface{}) (Order, error) {
Jiyong Park10f27f82019-11-15 18:31:56 +09001425 switch testCase.order {
1426 case Append:
Colin Cross75c47012016-05-05 15:58:02 -07001427 return Append, nil
Jiyong Park10f27f82019-11-15 18:31:56 +09001428 case Prepend:
1429 return Prepend, nil
1430 case Replace:
1431 return Replace, nil
Colin Cross75c47012016-05-05 15:58:02 -07001432 }
Jiyong Park10f27f82019-11-15 18:31:56 +09001433 return Append, errors.New("unknown order")
Colin Cross75c47012016-05-05 15:58:02 -07001434 }
1435
Jiyong Park10f27f82019-11-15 18:31:56 +09001436 switch testCase.order {
1437 case Append:
Colin Cross75c47012016-05-05 15:58:02 -07001438 testType = "prepend matching"
Jiyong Park10f27f82019-11-15 18:31:56 +09001439 case Prepend:
Colin Cross75c47012016-05-05 15:58:02 -07001440 testType = "append matching"
Jiyong Park10f27f82019-11-15 18:31:56 +09001441 case Replace:
1442 testType = "replace matching"
Colin Cross75c47012016-05-05 15:58:02 -07001443 }
1444
1445 err = ExtendMatchingProperties(got, testCase.in2, testCase.filter, order)
1446
1447 check(t, testType, testString, got, err, testCase.out, testCase.err)
1448 }
1449}
1450
Colin Cross0bc7e072015-10-27 18:15:15 -07001451func check(t *testing.T, testType, testString string,
1452 got interface{}, err error,
1453 expected interface{}, expectedErr error) {
1454
1455 printedTestCase := false
1456 e := func(s string, expected, got interface{}) {
1457 if !printedTestCase {
1458 t.Errorf("test case %s: %s", testType, testString)
1459 printedTestCase = true
1460 }
1461 t.Errorf("incorrect %s", s)
1462 t.Errorf(" expected: %s", p(expected))
1463 t.Errorf(" got: %s", p(got))
1464 }
1465
1466 if err != nil {
1467 if expectedErr != nil {
1468 if err.Error() != expectedErr.Error() {
1469 e("unexpected error", expectedErr.Error(), err.Error())
1470 }
1471 } else {
1472 e("unexpected error", nil, err.Error())
1473 }
1474 } else {
1475 if expectedErr != nil {
1476 e("missing error", expectedErr, nil)
1477 }
1478 }
1479
1480 if !reflect.DeepEqual(expected, got) {
1481 e("output:", expected, got)
1482 }
1483}
1484
1485func p(in interface{}) string {
1486 if v, ok := in.([]interface{}); ok {
1487 s := make([]string, len(v))
1488 for i := range v {
1489 s[i] = fmt.Sprintf("%#v", v[i])
1490 }
1491 return "[" + strings.Join(s, ", ") + "]"
1492 } else {
1493 return fmt.Sprintf("%#v", in)
1494 }
1495}