1/*
2Copyright 2016 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 generators
18
19import (
20	"reflect"
21	"testing"
22
23	"k8s.io/gengo/types"
24)
25
26func Test_isRootedUnder(t *testing.T) {
27	testCases := []struct {
28		path   string
29		roots  []string
30		expect bool
31	}{
32		{
33			path:   "/foo/bar",
34			roots:  nil,
35			expect: false,
36		},
37		{
38			path:   "/foo/bar",
39			roots:  []string{},
40			expect: false,
41		},
42		{
43			path: "/foo/bar",
44			roots: []string{
45				"/bad",
46			},
47			expect: false,
48		},
49		{
50			path: "/foo/bar",
51			roots: []string{
52				"/foo",
53			},
54			expect: true,
55		},
56		{
57			path: "/foo/bar",
58			roots: []string{
59				"/bad",
60				"/foo",
61			},
62			expect: true,
63		},
64		{
65			path: "/foo/bar/qux/zorb",
66			roots: []string{
67				"/foo/bar/qux",
68			},
69			expect: true,
70		},
71		{
72			path: "/foo/bar",
73			roots: []string{
74				"/foo/bar",
75			},
76			expect: true,
77		},
78		{
79			path: "/foo/barn",
80			roots: []string{
81				"/foo/bar",
82			},
83			expect: false,
84		},
85		{
86			path: "/foo/bar",
87			roots: []string{
88				"/foo/barn",
89			},
90			expect: false,
91		},
92		{
93			path: "/foo/bar",
94			roots: []string{
95				"",
96			},
97			expect: true,
98		},
99	}
100
101	for i, tc := range testCases {
102		r := isRootedUnder(tc.path, tc.roots)
103		if r != tc.expect {
104			t.Errorf("case[%d]: expected %t, got %t for %q in %q", i, tc.expect, r, tc.path, tc.roots)
105		}
106	}
107}
108
109func Test_deepCopyMethod(t *testing.T) {
110	testCases := []struct {
111		typ    types.Type
112		expect bool
113		error  bool
114	}{
115		{
116			typ: types.Type{
117				Name: types.Name{Package: "pkgname", Name: "typename"},
118				Kind: types.Builtin,
119				// No DeepCopy method.
120				Methods: map[string]*types.Type{},
121			},
122			expect: false,
123		},
124		{
125			typ: types.Type{
126				Name: types.Name{Package: "pkgname", Name: "typename"},
127				Kind: types.Builtin,
128				Methods: map[string]*types.Type{
129					// No DeepCopy method.
130					"method": {
131						Name: types.Name{Package: "pkgname", Name: "func()"},
132						Kind: types.Func,
133						Signature: &types.Signature{
134							Receiver: &types.Type{
135								Kind: types.Pointer,
136								Elem: &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
137							},
138							Parameters: []*types.Type{},
139							Results:    []*types.Type{},
140						},
141					},
142				},
143			},
144			expect: false,
145		},
146		{
147			typ: types.Type{
148				Name: types.Name{Package: "pkgname", Name: "typename"},
149				Kind: types.Builtin,
150				Methods: map[string]*types.Type{
151					// Wrong signature (no result).
152					"DeepCopy": {
153						Name: types.Name{Package: "pkgname", Name: "func()"},
154						Kind: types.Func,
155						Signature: &types.Signature{
156							Receiver: &types.Type{
157								Kind: types.Pointer,
158								Elem: &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
159							},
160							Parameters: []*types.Type{},
161							Results:    []*types.Type{},
162						},
163					},
164				},
165			},
166			expect: false,
167			error:  true,
168		},
169		{
170			typ: types.Type{
171				Name: types.Name{Package: "pkgname", Name: "typename"},
172				Kind: types.Builtin,
173				Methods: map[string]*types.Type{
174					// Wrong signature (wrong result).
175					"DeepCopy": {
176						Name: types.Name{Package: "pkgname", Name: "func() int"},
177						Kind: types.Func,
178						Signature: &types.Signature{
179							Receiver: &types.Type{
180								Kind: types.Pointer,
181								Elem: &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
182							},
183							Parameters: []*types.Type{},
184							Results: []*types.Type{
185								{
186									Name: types.Name{Name: "int"},
187									Kind: types.Builtin,
188								},
189							},
190						},
191					},
192				},
193			},
194			expect: false,
195			error:  true,
196		},
197		{
198			typ: types.Type{
199				Name: types.Name{Package: "pkgname", Name: "typename"},
200				Kind: types.Builtin,
201				Methods: map[string]*types.Type{
202					// Wrong signature with pointer receiver, but non-pointer result.
203					"DeepCopy": {
204						Name: types.Name{Package: "pkgname", Name: "func() pkgname.typename"},
205						Kind: types.Func,
206						Signature: &types.Signature{
207							Receiver: &types.Type{
208								Kind: types.Pointer,
209								Elem: &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
210							},
211							Parameters: []*types.Type{},
212							Results: []*types.Type{
213								{
214									Name: types.Name{Package: "pkgname", Name: "typename"},
215									Kind: types.Builtin,
216								},
217							},
218						},
219					},
220				},
221			},
222			expect: false,
223			error:  true,
224		},
225		{
226			typ: types.Type{
227				Name: types.Name{Package: "pkgname", Name: "typename"},
228				Kind: types.Builtin,
229				Methods: map[string]*types.Type{
230					// Wrong signature with non-pointer receiver, but pointer result.
231					"DeepCopy": {
232						Name: types.Name{Package: "pkgname", Name: "func() pkgname.typename"},
233						Kind: types.Func,
234						Signature: &types.Signature{
235							Receiver:   &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
236							Parameters: []*types.Type{},
237							Results: []*types.Type{
238								{
239									Kind: types.Pointer,
240									Elem: &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
241								},
242							},
243						},
244					},
245				},
246			},
247			expect: false,
248			error:  true,
249		},
250		{
251			typ: types.Type{
252				Name: types.Name{Package: "pkgname", Name: "typename"},
253				Kind: types.Builtin,
254				Methods: map[string]*types.Type{
255					// Correct signature with non-pointer receiver.
256					"DeepCopy": {
257						Name: types.Name{Package: "pkgname", Name: "func() pkgname.typename"},
258						Kind: types.Func,
259						Signature: &types.Signature{
260							Receiver:   &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
261							Parameters: []*types.Type{},
262							Results: []*types.Type{
263								{
264									Name: types.Name{Package: "pkgname", Name: "typename"},
265									Kind: types.Builtin,
266								},
267							},
268						},
269					},
270				},
271			},
272			expect: true,
273		},
274		{
275			typ: types.Type{
276				Name: types.Name{Package: "pkgname", Name: "typename"},
277				Kind: types.Builtin,
278				Methods: map[string]*types.Type{
279					// Correct signature with pointer receiver.
280					"DeepCopy": {
281						Name: types.Name{Package: "pkgname", Name: "func() pkgname.typename"},
282						Kind: types.Func,
283						Signature: &types.Signature{
284							Receiver: &types.Type{
285								Kind: types.Pointer,
286								Elem: &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
287							},
288							Parameters: []*types.Type{},
289							Results: []*types.Type{
290								{
291									Kind: types.Pointer,
292									Elem: &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
293								},
294							},
295						},
296					},
297				},
298			},
299			expect: true,
300		},
301		{
302			typ: types.Type{
303				Name: types.Name{Package: "pkgname", Name: "typename"},
304				Kind: types.Builtin,
305				Methods: map[string]*types.Type{
306					// Wrong signature (has params).
307					"DeepCopy": {
308						Name: types.Name{Package: "pkgname", Name: "func(int) pkgname.typename"},
309						Kind: types.Func,
310						Signature: &types.Signature{
311							Receiver: &types.Type{
312								Kind: types.Pointer,
313								Elem: &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
314							},
315							Parameters: []*types.Type{
316								{
317									Name: types.Name{Name: "int"},
318									Kind: types.Builtin,
319								},
320							},
321							Results: []*types.Type{
322								{
323									Name: types.Name{Package: "pkgname", Name: "typename"},
324									Kind: types.Builtin,
325								},
326							},
327						},
328					},
329				},
330			},
331			expect: false,
332			error:  true,
333		},
334		{
335			typ: types.Type{
336				Name: types.Name{Package: "pkgname", Name: "typename"},
337				Kind: types.Builtin,
338				Methods: map[string]*types.Type{
339					// Wrong signature (extra results).
340					"DeepCopy": {
341						Name: types.Name{Package: "pkgname", Name: "func() (pkgname.typename, int)"},
342						Kind: types.Func,
343						Signature: &types.Signature{
344							Receiver: &types.Type{
345								Kind: types.Pointer,
346								Elem: &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
347							},
348							Parameters: []*types.Type{},
349							Results: []*types.Type{
350								{
351									Name: types.Name{Package: "pkgname", Name: "typename"},
352									Kind: types.Builtin,
353								},
354								{
355									Name: types.Name{Name: "int"},
356									Kind: types.Builtin,
357								},
358							},
359						},
360					},
361				},
362			},
363			expect: false,
364			error:  true,
365		},
366	}
367
368	for i, tc := range testCases {
369		r, err := deepCopyMethod(&tc.typ)
370		if tc.error && err == nil {
371			t.Errorf("case[%d]: expected an error, got none", i)
372		} else if !tc.error && err != nil {
373			t.Errorf("case[%d]: expected no error, got: %v", i, err)
374		} else if !tc.error && (r != nil) != tc.expect {
375			t.Errorf("case[%d]: expected result %v, got: %v", i, tc.expect, r)
376		}
377	}
378}
379
380func Test_deepCopyIntoMethod(t *testing.T) {
381	testCases := []struct {
382		typ    types.Type
383		expect bool
384		error  bool
385	}{
386		{
387			typ: types.Type{
388				Name: types.Name{Package: "pkgname", Name: "typename"},
389				Kind: types.Builtin,
390				// No DeepCopyInto method.
391				Methods: map[string]*types.Type{},
392			},
393			expect: false,
394		},
395		{
396			typ: types.Type{
397				Name: types.Name{Package: "pkgname", Name: "typename"},
398				Kind: types.Builtin,
399				Methods: map[string]*types.Type{
400					// No DeepCopyInto method.
401					"method": {
402						Name: types.Name{Package: "pkgname", Name: "func()"},
403						Kind: types.Func,
404						Signature: &types.Signature{
405							Receiver: &types.Type{
406								Kind: types.Pointer,
407								Elem: &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
408							},
409							Parameters: []*types.Type{},
410							Results:    []*types.Type{},
411						},
412					},
413				},
414			},
415			expect: false,
416		},
417		{
418			typ: types.Type{
419				Name: types.Name{Package: "pkgname", Name: "typename"},
420				Kind: types.Builtin,
421				Methods: map[string]*types.Type{
422					// Wrong signature (no parameter).
423					"DeepCopyInto": {
424						Name: types.Name{Package: "pkgname", Name: "func()"},
425						Kind: types.Func,
426						Signature: &types.Signature{
427							Receiver: &types.Type{
428								Kind: types.Pointer,
429								Elem: &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
430							},
431							Parameters: []*types.Type{},
432							Results:    []*types.Type{},
433						},
434					},
435				},
436			},
437			expect: false,
438			error:  true,
439		},
440		{
441			typ: types.Type{
442				Name: types.Name{Package: "pkgname", Name: "typename"},
443				Kind: types.Builtin,
444				Methods: map[string]*types.Type{
445					// Wrong signature (unexpected result).
446					"DeepCopyInto": {
447						Name: types.Name{Package: "pkgname", Name: "func(*pkgname.typename) int"},
448						Kind: types.Func,
449						Signature: &types.Signature{
450							Receiver: &types.Type{
451								Kind: types.Pointer,
452								Elem: &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
453							},
454							Parameters: []*types.Type{
455								{
456									Kind: types.Pointer,
457									Elem: &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
458								},
459							},
460							Results: []*types.Type{
461								{
462									Name: types.Name{Name: "int"},
463									Kind: types.Builtin,
464								},
465							},
466						},
467					},
468				},
469			},
470			expect: false,
471			error:  true,
472		},
473		{
474			typ: types.Type{
475				Name: types.Name{Package: "pkgname", Name: "typename"},
476				Kind: types.Builtin,
477				Methods: map[string]*types.Type{
478					// Wrong signature (non-pointer parameter, pointer receiver).
479					"DeepCopyInto": {
480						Name: types.Name{Package: "pkgname", Name: "func(pkgname.typename)"},
481						Kind: types.Func,
482						Signature: &types.Signature{
483							Receiver: &types.Type{
484								Kind: types.Pointer,
485								Elem: &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
486							},
487							Parameters: []*types.Type{
488								{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
489							},
490							Results: []*types.Type{},
491						},
492					},
493				},
494			},
495			expect: false,
496			error:  true,
497		},
498		{
499			typ: types.Type{
500				Name: types.Name{Package: "pkgname", Name: "typename"},
501				Kind: types.Builtin,
502				Methods: map[string]*types.Type{
503					// Wrong signature (non-pointer parameter, non-pointer receiver).
504					"DeepCopyInto": {
505						Name: types.Name{Package: "pkgname", Name: "func(pkgname.typename)"},
506						Kind: types.Func,
507						Signature: &types.Signature{
508							Receiver: &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
509							Parameters: []*types.Type{
510								{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
511							},
512							Results: []*types.Type{},
513						},
514					},
515				},
516			},
517			expect: false,
518			error:  true,
519		},
520		{
521			typ: types.Type{
522				Name: types.Name{Package: "pkgname", Name: "typename"},
523				Kind: types.Builtin,
524				Methods: map[string]*types.Type{
525					// Correct signature with non-pointer receiver.
526					"DeepCopyInto": {
527						Name: types.Name{Package: "pkgname", Name: "func(*pkgname.typename)"},
528						Kind: types.Func,
529						Signature: &types.Signature{
530							Receiver: &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
531							Parameters: []*types.Type{
532								{
533									Kind: types.Pointer,
534									Elem: &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
535								},
536							},
537							Results: []*types.Type{},
538						},
539					},
540				},
541			},
542			expect: true,
543		},
544		{
545			typ: types.Type{
546				Name: types.Name{Package: "pkgname", Name: "typename"},
547				Kind: types.Builtin,
548				Methods: map[string]*types.Type{
549					// Correct signature with pointer receiver.
550					"DeepCopyInto": {
551						Name: types.Name{Package: "pkgname", Name: "func(*pkgname.typename)"},
552						Kind: types.Func,
553						Signature: &types.Signature{
554							Receiver: &types.Type{
555								Kind: types.Pointer,
556								Elem: &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
557							},
558							Parameters: []*types.Type{
559								{
560									Kind: types.Pointer,
561									Elem: &types.Type{Kind: types.Struct, Name: types.Name{Package: "pkgname", Name: "typename"}},
562								},
563							},
564							Results: []*types.Type{},
565						},
566					},
567				},
568			},
569			expect: true,
570		},
571	}
572
573	for i, tc := range testCases {
574		r, err := deepCopyIntoMethod(&tc.typ)
575		if tc.error && err == nil {
576			t.Errorf("case[%d]: expected an error, got none", i)
577		} else if !tc.error && err != nil {
578			t.Errorf("case[%d]: expected no error, got: %v", i, err)
579		} else if !tc.error && (r != nil) != tc.expect {
580			t.Errorf("case[%d]: expected result %v, got: %v", i, tc.expect, r)
581		}
582	}
583}
584
585func Test_extractTagParams(t *testing.T) {
586	testCases := []struct {
587		comments []string
588		expect   *enabledTagValue
589	}{
590		{
591			comments: []string{
592				"Human comment",
593			},
594			expect: nil,
595		},
596		{
597			comments: []string{
598				"Human comment",
599				"+k8s:deepcopy-gen",
600			},
601			expect: &enabledTagValue{
602				value:    "",
603				register: false,
604			},
605		},
606		{
607			comments: []string{
608				"Human comment",
609				"+k8s:deepcopy-gen=package",
610			},
611			expect: &enabledTagValue{
612				value:    "package",
613				register: false,
614			},
615		},
616		{
617			comments: []string{
618				"Human comment",
619				"+k8s:deepcopy-gen=package,register",
620			},
621			expect: &enabledTagValue{
622				value:    "package",
623				register: true,
624			},
625		},
626		{
627			comments: []string{
628				"Human comment",
629				"+k8s:deepcopy-gen=package,register=true",
630			},
631			expect: &enabledTagValue{
632				value:    "package",
633				register: true,
634			},
635		},
636		{
637			comments: []string{
638				"Human comment",
639				"+k8s:deepcopy-gen=package,register=false",
640			},
641			expect: &enabledTagValue{
642				value:    "package",
643				register: false,
644			},
645		},
646	}
647
648	for i, tc := range testCases {
649		r := extractEnabledTag(tc.comments)
650		if r == nil && tc.expect != nil {
651			t.Errorf("case[%d]: expected non-nil", i)
652		}
653		if r != nil && tc.expect == nil {
654			t.Errorf("case[%d]: expected nil, got %v", i, *r)
655		}
656		if r != nil && *r != *tc.expect {
657			t.Errorf("case[%d]: expected %v, got %v", i, *tc.expect, *r)
658		}
659	}
660}
661
662func Test_extractInterfacesTag(t *testing.T) {
663	testCases := []struct {
664		comments, secondComments []string
665		expect                   []string
666	}{
667		{
668			comments: []string{},
669			expect:   nil,
670		},
671		{
672			comments: []string{
673				"+k8s:deepcopy-gen:interfaces=k8s.io/kubernetes/runtime.Object",
674			},
675			expect: []string{
676				"k8s.io/kubernetes/runtime.Object",
677			},
678		},
679		{
680			comments: []string{
681				"+k8s:deepcopy-gen:interfaces=k8s.io/kubernetes/runtime.Object",
682				"+k8s:deepcopy-gen:interfaces=k8s.io/kubernetes/runtime.List",
683			},
684			expect: []string{
685				"k8s.io/kubernetes/runtime.Object",
686				"k8s.io/kubernetes/runtime.List",
687			},
688		},
689		{
690			comments: []string{
691				"+k8s:deepcopy-gen:interfaces=k8s.io/kubernetes/runtime.Object",
692				"+k8s:deepcopy-gen:interfaces=k8s.io/kubernetes/runtime.Object",
693			},
694			expect: []string{
695				"k8s.io/kubernetes/runtime.Object",
696				"k8s.io/kubernetes/runtime.Object",
697			},
698		},
699		{
700			secondComments: []string{
701				"+k8s:deepcopy-gen:interfaces=k8s.io/kubernetes/runtime.Object",
702			},
703			expect: []string{
704				"k8s.io/kubernetes/runtime.Object",
705			},
706		},
707		{
708			comments: []string{
709				"+k8s:deepcopy-gen:interfaces=k8s.io/kubernetes/runtime.Object",
710			},
711			secondComments: []string{
712				"+k8s:deepcopy-gen:interfaces=k8s.io/kubernetes/runtime.List",
713			},
714			expect: []string{
715				"k8s.io/kubernetes/runtime.List",
716				"k8s.io/kubernetes/runtime.Object",
717			},
718		},
719		{
720			comments: []string{
721				"+k8s:deepcopy-gen:interfaces=k8s.io/kubernetes/runtime.Object",
722			},
723			secondComments: []string{
724				"+k8s:deepcopy-gen:interfaces=k8s.io/kubernetes/runtime.Object",
725			},
726			expect: []string{
727				"k8s.io/kubernetes/runtime.Object",
728				"k8s.io/kubernetes/runtime.Object",
729			},
730		},
731	}
732
733	for i, tc := range testCases {
734		typ := &types.Type{
735			CommentLines:              tc.comments,
736			SecondClosestCommentLines: tc.secondComments,
737		}
738		r := extractInterfacesTag(typ)
739		if r == nil && tc.expect != nil {
740			t.Errorf("case[%d]: expected non-nil", i)
741		}
742		if r != nil && tc.expect == nil {
743			t.Errorf("case[%d]: expected nil, got %v", i, r)
744		}
745		if r != nil && !reflect.DeepEqual(r, tc.expect) {
746			t.Errorf("case[%d]: expected %v, got %v", i, tc.expect, r)
747		}
748	}
749}
750