1/*
2Copyright 2018 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package merge_test
18
19import (
20	"testing"
21
22	"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
23	. "sigs.k8s.io/structured-merge-diff/v4/internal/fixture"
24	"sigs.k8s.io/structured-merge-diff/v4/merge"
25	"sigs.k8s.io/structured-merge-diff/v4/typed"
26)
27
28var leafFieldsParser = func() Parser {
29	parser, err := typed.NewParser(`types:
30- name: leafFields
31  map:
32    fields:
33    - name: numeric
34      type:
35        scalar: numeric
36    - name: string
37      type:
38        scalar: string
39    - name: bool
40      type:
41        scalar: boolean`)
42	if err != nil {
43		panic(err)
44	}
45	return SameVersionParser{T: parser.Type("leafFields")}
46}()
47
48func TestUpdateLeaf(t *testing.T) {
49	tests := map[string]TestCase{
50		"apply_twice": {
51			Ops: []Operation{
52				Apply{
53					Manager: "default",
54					Object: `
55						numeric: 1
56						string: "string"
57					`,
58					APIVersion: "v1",
59				},
60				Apply{
61					Manager: "default",
62					Object: `
63						numeric: 2
64						string: "string"
65						bool: false
66					`,
67					APIVersion: "v1",
68				},
69			},
70			Object: `
71				numeric: 2
72				string: "string"
73				bool: false
74			`,
75			APIVersion: "v1",
76			Managed: fieldpath.ManagedFields{
77				"default": fieldpath.NewVersionedSet(
78					_NS(
79						_P("numeric"), _P("string"), _P("bool"),
80					),
81					"v1",
82					false,
83				),
84			},
85		},
86		"apply_twice_different_versions": {
87			Ops: []Operation{
88				Apply{
89					Manager: "default",
90					Object: `
91						numeric: 1
92						string: "string"
93					`,
94					APIVersion: "v1",
95				},
96				Apply{
97					Manager: "default",
98					Object: `
99						numeric: 2
100						string: "string"
101						bool: false
102					`,
103					APIVersion: "v2",
104				},
105			},
106			Object: `
107				numeric: 2
108				string: "string"
109				bool: false
110			`,
111			APIVersion: "v1",
112			Managed: fieldpath.ManagedFields{
113				"default": fieldpath.NewVersionedSet(
114					_NS(
115						_P("numeric"), _P("string"), _P("bool"),
116					),
117					"v2",
118					false,
119				),
120			},
121		},
122		"apply_update_apply_no_conflict": {
123			Ops: []Operation{
124				Apply{
125					Manager:    "default",
126					APIVersion: "v1",
127					Object: `
128						numeric: 1
129						string: "string"
130					`,
131				},
132				Update{
133					Manager:    "controller",
134					APIVersion: "v1",
135					Object: `
136						numeric: 1
137						string: "string"
138						bool: true
139					`,
140				},
141				Apply{
142					Manager:    "default",
143					APIVersion: "v1",
144					Object: `
145						numeric: 2
146						string: "string"
147					`,
148				},
149			},
150			Object: `
151				numeric: 2
152				string: "string"
153				bool: true
154			`,
155			APIVersion: "v1",
156			Managed: fieldpath.ManagedFields{
157				"default": fieldpath.NewVersionedSet(
158					_NS(
159						_P("numeric"), _P("string"),
160					),
161					"v1",
162					false,
163				),
164				"controller": fieldpath.NewVersionedSet(
165					_NS(
166						_P("bool"),
167					),
168					"v1",
169					false,
170				),
171			},
172		},
173		"apply_update_apply_no_conflict_different_version": {
174			Ops: []Operation{
175				Apply{
176					Manager:    "default",
177					APIVersion: "v1",
178					Object: `
179						numeric: 1
180						string: "string"
181					`,
182				},
183				Update{
184					Manager:    "controller",
185					APIVersion: "v2",
186					Object: `
187						numeric: 1
188						string: "string"
189						bool: true
190					`,
191				},
192				Apply{
193					Manager:    "default",
194					APIVersion: "v1",
195					Object: `
196						numeric: 2
197						string: "string"
198					`,
199				},
200			},
201			Object: `
202				numeric: 2
203				string: "string"
204				bool: true
205			`,
206			APIVersion: "v1",
207			Managed: fieldpath.ManagedFields{
208				"default": fieldpath.NewVersionedSet(
209					_NS(
210						_P("numeric"), _P("string"),
211					),
212					"v1",
213					false,
214				),
215				"controller": fieldpath.NewVersionedSet(
216					_NS(
217						_P("bool"),
218					),
219					"v2",
220					false,
221				),
222			},
223		},
224		"apply_update_apply_with_conflict": {
225			Ops: []Operation{
226				Apply{
227					Manager:    "default",
228					APIVersion: "v1",
229					Object: `
230						numeric: 1
231						string: "string"
232					`,
233				},
234				Update{
235					Manager:    "controller",
236					APIVersion: "v1",
237					Object: `
238						numeric: 1
239						string: "controller string"
240						bool: true
241					`,
242				},
243				Apply{
244					Manager:    "default",
245					APIVersion: "v1",
246					Object: `
247						numeric: 2
248						string: "user string"
249					`,
250					Conflicts: merge.Conflicts{
251						merge.Conflict{Manager: "controller", Path: _P("string")},
252					},
253				},
254				ForceApply{
255					Manager:    "default",
256					APIVersion: "v1",
257					Object: `
258						numeric: 2
259						string: "user string"
260					`,
261				},
262			},
263			Object: `
264				numeric: 2
265				string: "user string"
266				bool: true
267			`,
268			APIVersion: "v1",
269			Managed: fieldpath.ManagedFields{
270				"default": fieldpath.NewVersionedSet(
271					_NS(
272						_P("numeric"), _P("string"),
273					),
274					"v1",
275					false,
276				),
277				"controller": fieldpath.NewVersionedSet(
278					_NS(
279						_P("bool"),
280					),
281					"v1",
282					false,
283				),
284			},
285		},
286		"apply_update_apply_with_conflict_across_version": {
287			Ops: []Operation{
288				Apply{
289					Manager:    "default",
290					APIVersion: "v1",
291					Object: `
292						numeric: 1
293						string: "string"
294					`,
295				},
296				Update{
297					Manager:    "controller",
298					APIVersion: "v2",
299					Object: `
300						numeric: 1
301						string: "controller string"
302						bool: true
303					`,
304				},
305				Apply{
306					Manager:    "default",
307					APIVersion: "v1",
308					Object: `
309						numeric: 2
310						string: "user string"
311					`,
312					Conflicts: merge.Conflicts{
313						merge.Conflict{Manager: "controller", Path: _P("string")},
314					},
315				},
316				ForceApply{
317					Manager:    "default",
318					APIVersion: "v1",
319					Object: `
320						numeric: 2
321						string: "user string"
322					`,
323				},
324			},
325			Object: `
326				numeric: 2
327				string: "user string"
328				bool: true
329			`,
330			APIVersion: "v1",
331			Managed: fieldpath.ManagedFields{
332				"default": fieldpath.NewVersionedSet(
333					_NS(
334						_P("numeric"), _P("string"),
335					),
336					"v1",
337					false,
338				),
339				"controller": fieldpath.NewVersionedSet(
340					_NS(
341						_P("bool"),
342					),
343					"v2",
344					false,
345				),
346			},
347		},
348		"apply_twice_remove": {
349			Ops: []Operation{
350				Apply{
351					Manager:    "default",
352					APIVersion: "v1",
353					Object: `
354						numeric: 1
355						string: "string"
356						bool: false
357					`,
358				},
359				Apply{
360					Manager:    "default",
361					APIVersion: "v1",
362					Object: `
363						string: "new string"
364					`,
365				},
366			},
367			Object: `
368				string: "new string"
369			`,
370			APIVersion: "v1",
371			Managed: fieldpath.ManagedFields{
372				"default": fieldpath.NewVersionedSet(
373					_NS(
374						_P("string"),
375					),
376					"v1",
377					false,
378				),
379			},
380		},
381		"update_apply_omits": {
382			Ops: []Operation{
383				Apply{
384					Manager:    "default",
385					APIVersion: "v1",
386					Object: `
387						numeric: 2
388					`,
389				},
390				Update{
391					Manager:    "controller",
392					APIVersion: "v1",
393					Object: `
394						numeric: 1
395					`,
396				},
397				Apply{
398					Manager:    "default",
399					APIVersion: "v1",
400					Object:     ``,
401				},
402			},
403			Object: `
404						numeric: 1
405			`,
406			APIVersion: "v1",
407			Managed: fieldpath.ManagedFields{
408				"controller": fieldpath.NewVersionedSet(
409					_NS(
410						_P("numeric"),
411					),
412					"v1",
413					false,
414				),
415			},
416		},
417		"apply_twice_remove_different_version": {
418			Ops: []Operation{
419				Apply{
420					Manager:    "default",
421					APIVersion: "v1",
422					Object: `
423						numeric: 1
424						string: "string"
425						bool: false
426					`,
427				},
428				Apply{
429					Manager:    "default",
430					APIVersion: "v2",
431					Object: `
432						string: "new string"
433					`,
434				},
435			},
436			Object: `
437				string: "new string"
438			`,
439			APIVersion: "v1",
440			Managed: fieldpath.ManagedFields{
441				"default": fieldpath.NewVersionedSet(
442					_NS(
443						_P("string"),
444					),
445					"v2",
446					false,
447				),
448			},
449		},
450		"update_remove_empty_set": {
451			Ops: []Operation{
452				Apply{
453					Manager:    "default",
454					APIVersion: "v1",
455					Object: `
456						string: "string"
457					`,
458				},
459				Update{
460					Manager:    "controller",
461					APIVersion: "v1",
462					Object: `
463						string: "new string"
464					`,
465				},
466			},
467			Object: `
468				string: "new string"
469			`,
470			APIVersion: "v1",
471			Managed: fieldpath.ManagedFields{
472				"controller": fieldpath.NewVersionedSet(
473					_NS(
474						_P("string"),
475					),
476					"v1",
477					false,
478				),
479			},
480		},
481		"apply_remove_empty_set": {
482			Ops: []Operation{
483				Apply{
484					Manager:    "default",
485					APIVersion: "v1",
486					Object: `
487						string: "string"
488					`,
489				},
490				Apply{
491					Manager:    "default",
492					APIVersion: "v1",
493					Object:     "",
494				},
495			},
496			Object: `
497			`,
498			APIVersion: "v1",
499			Managed:    fieldpath.ManagedFields{},
500		},
501	}
502
503	for name, test := range tests {
504		t.Run(name, func(t *testing.T) {
505			if err := test.Test(leafFieldsParser); err != nil {
506				t.Fatal(err)
507			}
508		})
509	}
510}
511
512func BenchmarkLeafConflictAcrossVersion(b *testing.B) {
513	test := TestCase{
514		Ops: []Operation{
515			Apply{
516				Manager:    "default",
517				APIVersion: "v1",
518				Object: `
519					numeric: 1
520					string: "string"
521				`,
522			},
523			Update{
524				Manager:    "controller",
525				APIVersion: "v2",
526				Object: `
527					numeric: 1
528					string: "controller string"
529					bool: true
530				`,
531			},
532			Apply{
533				Manager:    "default",
534				APIVersion: "v1",
535				Object: `
536					numeric: 2
537					string: "user string"
538				`,
539				Conflicts: merge.Conflicts{
540					merge.Conflict{Manager: "controller", Path: _P("string")},
541				},
542			},
543			ForceApply{
544				Manager:    "default",
545				APIVersion: "v1",
546				Object: `
547					numeric: 2
548					string: "user string"
549				`,
550			},
551		},
552		Object: `
553			numeric: 2
554			string: "user string"
555			bool: true
556		`,
557		APIVersion: "v1",
558		Managed: fieldpath.ManagedFields{
559			"default": fieldpath.NewVersionedSet(
560				_NS(
561					_P("numeric"), _P("string"),
562				),
563				"v1",
564				false,
565			),
566			"controller": fieldpath.NewVersionedSet(
567				_NS(
568					_P("bool"),
569				),
570				"v2",
571				false,
572			),
573		},
574	}
575
576	// Make sure this passes...
577	if err := test.Test(leafFieldsParser); err != nil {
578		b.Fatal(err)
579	}
580
581	test.PreprocessOperations(leafFieldsParser)
582
583	b.ReportAllocs()
584	b.ResetTimer()
585	for n := 0; n < b.N; n++ {
586		if err := test.Bench(leafFieldsParser); err != nil {
587			b.Fatal(err)
588		}
589	}
590}
591