1/*
2Copyright 2020 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 topologymanager
18
19import (
20	"reflect"
21	"testing"
22
23	v1 "k8s.io/api/core/v1"
24)
25
26func TestPodCalculateAffinity(t *testing.T) {
27	tcases := []struct {
28		name     string
29		hp       []HintProvider
30		expected []map[string][]TopologyHint
31	}{
32		{
33			name:     "No hint providers",
34			hp:       []HintProvider{},
35			expected: ([]map[string][]TopologyHint)(nil),
36		},
37		{
38			name: "HintProvider returns empty non-nil map[string][]TopologyHint",
39			hp: []HintProvider{
40				&mockHintProvider{
41					map[string][]TopologyHint{},
42				},
43			},
44			expected: []map[string][]TopologyHint{
45				{},
46			},
47		},
48		{
49			name: "HintProvider returns -nil map[string][]TopologyHint from provider",
50			hp: []HintProvider{
51				&mockHintProvider{
52					map[string][]TopologyHint{
53						"resource": nil,
54					},
55				},
56			},
57			expected: []map[string][]TopologyHint{
58				{
59					"resource": nil,
60				},
61			},
62		},
63		{
64			name: "Assorted HintProviders",
65			hp: []HintProvider{
66				&mockHintProvider{
67					map[string][]TopologyHint{
68						"resource-1/A": {
69							{NUMANodeAffinity: NewTestBitMask(0), Preferred: true},
70							{NUMANodeAffinity: NewTestBitMask(0, 1), Preferred: false},
71						},
72						"resource-1/B": {
73							{NUMANodeAffinity: NewTestBitMask(1), Preferred: true},
74							{NUMANodeAffinity: NewTestBitMask(1, 2), Preferred: false},
75						},
76					},
77				},
78				&mockHintProvider{
79					map[string][]TopologyHint{
80						"resource-2/A": {
81							{NUMANodeAffinity: NewTestBitMask(2), Preferred: true},
82							{NUMANodeAffinity: NewTestBitMask(3, 4), Preferred: false},
83						},
84						"resource-2/B": {
85							{NUMANodeAffinity: NewTestBitMask(2), Preferred: true},
86							{NUMANodeAffinity: NewTestBitMask(3, 4), Preferred: false},
87						},
88					},
89				},
90				&mockHintProvider{
91					map[string][]TopologyHint{
92						"resource-3": nil,
93					},
94				},
95			},
96			expected: []map[string][]TopologyHint{
97				{
98					"resource-1/A": {
99						{NUMANodeAffinity: NewTestBitMask(0), Preferred: true},
100						{NUMANodeAffinity: NewTestBitMask(0, 1), Preferred: false},
101					},
102					"resource-1/B": {
103						{NUMANodeAffinity: NewTestBitMask(1), Preferred: true},
104						{NUMANodeAffinity: NewTestBitMask(1, 2), Preferred: false},
105					},
106				},
107				{
108					"resource-2/A": {
109						{NUMANodeAffinity: NewTestBitMask(2), Preferred: true},
110						{NUMANodeAffinity: NewTestBitMask(3, 4), Preferred: false},
111					},
112					"resource-2/B": {
113						{NUMANodeAffinity: NewTestBitMask(2), Preferred: true},
114						{NUMANodeAffinity: NewTestBitMask(3, 4), Preferred: false},
115					},
116				},
117				{
118					"resource-3": nil,
119				},
120			},
121		},
122	}
123
124	for _, tc := range tcases {
125		podScope := &podScope{
126			scope{
127				hintProviders: tc.hp,
128				policy:        &mockPolicy{},
129				name:          podTopologyScope,
130			},
131		}
132
133		podScope.calculateAffinity(&v1.Pod{})
134		actual := podScope.policy.(*mockPolicy).ph
135		if !reflect.DeepEqual(tc.expected, actual) {
136			t.Errorf("Test Case: %s", tc.name)
137			t.Errorf("Expected result to be %v, got %v", tc.expected, actual)
138		}
139	}
140}
141
142func TestPodAccumulateProvidersHints(t *testing.T) {
143	tcases := []struct {
144		name     string
145		hp       []HintProvider
146		expected []map[string][]TopologyHint
147	}{
148		{
149			name:     "TopologyHint not set",
150			hp:       []HintProvider{},
151			expected: nil,
152		},
153		{
154			name: "HintProvider returns empty non-nil map[string][]TopologyHint",
155			hp: []HintProvider{
156				&mockHintProvider{
157					map[string][]TopologyHint{},
158				},
159			},
160			expected: []map[string][]TopologyHint{
161				{},
162			},
163		},
164		{
165			name: "HintProvider returns - nil map[string][]TopologyHint from provider",
166			hp: []HintProvider{
167				&mockHintProvider{
168					map[string][]TopologyHint{
169						"resource": nil,
170					},
171				},
172			},
173			expected: []map[string][]TopologyHint{
174				{
175					"resource": nil,
176				},
177			},
178		},
179		{
180			name: "2 HintProviders with 1 resource returns hints",
181			hp: []HintProvider{
182				&mockHintProvider{
183					map[string][]TopologyHint{
184						"resource1": {TopologyHint{}},
185					},
186				},
187				&mockHintProvider{
188					map[string][]TopologyHint{
189						"resource2": {TopologyHint{}},
190					},
191				},
192			},
193			expected: []map[string][]TopologyHint{
194				{
195					"resource1": {TopologyHint{}},
196				},
197				{
198					"resource2": {TopologyHint{}},
199				},
200			},
201		},
202		{
203			name: "2 HintProviders 1 with 1 resource 1 with nil hints",
204			hp: []HintProvider{
205				&mockHintProvider{
206					map[string][]TopologyHint{
207						"resource1": {TopologyHint{}},
208					},
209				},
210				&mockHintProvider{nil},
211			},
212			expected: []map[string][]TopologyHint{
213				{
214					"resource1": {TopologyHint{}},
215				},
216				nil,
217			},
218		},
219		{
220			name: "2 HintProviders 1 with 1 resource 1 empty hints",
221			hp: []HintProvider{
222				&mockHintProvider{
223					map[string][]TopologyHint{
224						"resource1": {TopologyHint{}},
225					},
226				},
227				&mockHintProvider{
228					map[string][]TopologyHint{},
229				},
230			},
231			expected: []map[string][]TopologyHint{
232				{
233					"resource1": {TopologyHint{}},
234				},
235				{},
236			},
237		},
238		{
239			name: "HintProvider with 2 resources returns hints",
240			hp: []HintProvider{
241				&mockHintProvider{
242					map[string][]TopologyHint{
243						"resource1": {TopologyHint{}},
244						"resource2": {TopologyHint{}},
245					},
246				},
247			},
248			expected: []map[string][]TopologyHint{
249				{
250					"resource1": {TopologyHint{}},
251					"resource2": {TopologyHint{}},
252				},
253			},
254		},
255	}
256
257	for _, tc := range tcases {
258		pScope := podScope{
259			scope{
260				hintProviders: tc.hp,
261			},
262		}
263		actual := pScope.accumulateProvidersHints(&v1.Pod{})
264		if !reflect.DeepEqual(actual, tc.expected) {
265			t.Errorf("Test Case %s: Expected NUMANodeAffinity in result to be %v, got %v", tc.name, tc.expected, actual)
266		}
267	}
268}
269