blob: 6a3197d5a715cadd2f5845da3de28cbda4ec2b6c [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Brian O'Connorf3d06162014-10-02 15:54:12 -070016package org.onlab.onos.net.intent;
17
Brian O'Connorcb900f42014-10-07 21:55:33 -070018import static org.junit.Assert.assertEquals;
19import static org.junit.Assert.assertFalse;
20import static org.junit.Assert.assertNull;
21import static org.junit.Assert.fail;
22import static org.onlab.onos.net.intent.IntentEvent.Type.FAILED;
23import static org.onlab.onos.net.intent.IntentEvent.Type.INSTALLED;
24import static org.onlab.onos.net.intent.IntentEvent.Type.SUBMITTED;
25import static org.onlab.onos.net.intent.IntentEvent.Type.WITHDRAWN;
Brian O'Connorf3d06162014-10-02 15:54:12 -070026
27import java.util.ArrayList;
28import java.util.Arrays;
29import java.util.Collections;
30import java.util.Iterator;
31import java.util.List;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070032import java.util.Set;
Brian O'Connorf3d06162014-10-02 15:54:12 -070033
Brian O'Connorcb900f42014-10-07 21:55:33 -070034import org.junit.After;
35import org.junit.Before;
36import org.junit.Test;
Brian O'Connor520c0522014-11-23 23:50:47 -080037import org.onlab.onos.core.IdGenerator;
Brian O'Connorf2dbde52014-10-10 16:20:24 -070038import org.onlab.onos.net.flow.FlowRuleBatchOperation;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070039import org.onlab.onos.net.resource.LinkResourceAllocations;
Brian O'Connorf3d06162014-10-02 15:54:12 -070040
Brian O'Connorf3d06162014-10-02 15:54:12 -070041/**
42 * Suite of tests for the intent service contract.
43 */
44public class IntentServiceTest {
45
Brian O'Connor520c0522014-11-23 23:50:47 -080046 public static final int IID = 123;
47 public static final int INSTALLABLE_IID = 234;
Brian O'Connorf3d06162014-10-02 15:54:12 -070048
49 protected static final int GRACE_MS = 500; // millis
50
51 protected TestableIntentService service;
52 protected TestListener listener = new TestListener();
Brian O'Connor520c0522014-11-23 23:50:47 -080053 protected IdGenerator idGenerator = new MockIdGenerator();
Brian O'Connorf3d06162014-10-02 15:54:12 -070054
55 @Before
56 public void setUp() {
57 service = createIntentService();
58 service.addListener(listener);
Brian O'Connor520c0522014-11-23 23:50:47 -080059 Intent.bindIdGenerator(idGenerator);
Brian O'Connorf3d06162014-10-02 15:54:12 -070060 }
61
62 @After
63 public void tearDown() {
64 service.removeListener(listener);
Brian O'Connor520c0522014-11-23 23:50:47 -080065 Intent.unbindIdGenerator(idGenerator);
Brian O'Connorf3d06162014-10-02 15:54:12 -070066 }
67
68 /**
69 * Creates a service instance appropriately instrumented for testing.
70 *
71 * @return testable intent service
72 */
73 protected TestableIntentService createIntentService() {
74 return new FakeIntentManager();
75 }
76
77 @Test
78 public void basics() {
79 // Make sure there are no intents
Brian O'Connor66630c82014-10-02 21:08:19 -070080 assertEquals("incorrect intent count", 0, service.getIntentCount());
Brian O'Connorf3d06162014-10-02 15:54:12 -070081
82 // Register a compiler and an installer both setup for success.
83 service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
84 service.registerInstaller(TestInstallableIntent.class, new TestInstaller(false));
85
86 final Intent intent = new TestIntent(IID);
87 service.submit(intent);
88
89 // Allow a small window of time until the intent is in the expected state
90 TestTools.assertAfter(GRACE_MS, new Runnable() {
91 @Override
92 public void run() {
tom85258ee2014-10-07 00:10:02 -070093 assertEquals("incorrect intent state", IntentState.INSTALLED,
94 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -070095 }
96 });
97
98 // Make sure that all expected events have been emitted
tom85258ee2014-10-07 00:10:02 -070099 validateEvents(intent, SUBMITTED, INSTALLED);
Brian O'Connorf3d06162014-10-02 15:54:12 -0700100
101 // Make sure there is just one intent (and is ours)
Brian O'Connor66630c82014-10-02 21:08:19 -0700102 assertEquals("incorrect intent count", 1, service.getIntentCount());
Brian O'Connorf3d06162014-10-02 15:54:12 -0700103
104 // Reset the listener events
105 listener.events.clear();
106
107 // Now withdraw the intent
108 service.withdraw(intent);
109
110 // Allow a small window of time until the event is in the expected state
111 TestTools.assertAfter(GRACE_MS, new Runnable() {
112 @Override
113 public void run() {
tom85258ee2014-10-07 00:10:02 -0700114 assertEquals("incorrect intent state", IntentState.WITHDRAWN,
115 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700116 }
117 });
118
119 // Make sure that all expected events have been emitted
tom85258ee2014-10-07 00:10:02 -0700120 validateEvents(intent, WITHDRAWN);
Brian O'Connorf3d06162014-10-02 15:54:12 -0700121
122 // TODO: discuss what is the fate of intents after they have been withdrawn
123 // Make sure that the intent is no longer in the system
124// assertEquals("incorrect intent count", 0, service.getIntents().size());
tom85258ee2014-10-07 00:10:02 -0700125// assertNull("intent should not be found", service.getIntent(intent.id()));
126// assertNull("intent state should not be found", service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700127 }
128
129 @Test
130 public void failedCompilation() {
131 // Register a compiler programmed for success
132 service.registerCompiler(TestIntent.class, new TestCompiler(true));
133
134 // Submit an intent
135 final Intent intent = new TestIntent(IID);
136 service.submit(intent);
137
138 // Allow a small window of time until the intent is in the expected state
139 TestTools.assertAfter(GRACE_MS, new Runnable() {
140 @Override
141 public void run() {
tom85258ee2014-10-07 00:10:02 -0700142 assertEquals("incorrect intent state", IntentState.FAILED,
143 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700144 }
145 });
146
147 // Make sure that all expected events have been emitted
148 validateEvents(intent, SUBMITTED, FAILED);
149 }
150
151 @Test
152 public void failedInstallation() {
153 // Register a compiler programmed for success and installer for failure
154 service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
155 service.registerInstaller(TestInstallableIntent.class, new TestInstaller(true));
156
157 // Submit an intent
158 final Intent intent = new TestIntent(IID);
159 service.submit(intent);
160
161 // Allow a small window of time until the intent is in the expected state
162 TestTools.assertAfter(GRACE_MS, new Runnable() {
163 @Override
164 public void run() {
tom85258ee2014-10-07 00:10:02 -0700165 assertEquals("incorrect intent state", IntentState.FAILED,
166 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700167 }
168 });
169
170 // Make sure that all expected events have been emitted
tom85258ee2014-10-07 00:10:02 -0700171 validateEvents(intent, SUBMITTED, FAILED);
Brian O'Connorf3d06162014-10-02 15:54:12 -0700172 }
173
174 /**
175 * Validates that the test event listener has received the following events
176 * for the specified intent. Events received for other intents will not be
177 * considered.
178 *
179 * @param intent intent subject
tom85258ee2014-10-07 00:10:02 -0700180 * @param types list of event types for which events are expected
Brian O'Connorf3d06162014-10-02 15:54:12 -0700181 */
tom85258ee2014-10-07 00:10:02 -0700182 protected void validateEvents(Intent intent, IntentEvent.Type... types) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700183 Iterator<IntentEvent> events = listener.events.iterator();
tom85258ee2014-10-07 00:10:02 -0700184 for (IntentEvent.Type type : types) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700185 IntentEvent event = events.hasNext() ? events.next() : null;
186 if (event == null) {
tom85258ee2014-10-07 00:10:02 -0700187 fail("expected event not found: " + type);
188 } else if (intent.equals(event.subject())) {
189 assertEquals("incorrect state", type, event.type());
Brian O'Connorf3d06162014-10-02 15:54:12 -0700190 }
191 }
192
193 // Remainder of events should not apply to this intent; make sure.
194 while (events.hasNext()) {
195 assertFalse("unexpected event for intent",
tom85258ee2014-10-07 00:10:02 -0700196 intent.equals(events.next().subject()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700197 }
198 }
199
200 @Test
201 public void compilerBasics() {
202 // Make sure there are no compilers
203 assertEquals("incorrect compiler count", 0, service.getCompilers().size());
204
205 // Add a compiler and make sure that it appears in the map
206 IntentCompiler<TestIntent> compiler = new TestCompiler(false);
207 service.registerCompiler(TestIntent.class, compiler);
208 assertEquals("incorrect compiler", compiler,
209 service.getCompilers().get(TestIntent.class));
210
211 // Remove the same and make sure that it no longer appears in the map
212 service.unregisterCompiler(TestIntent.class);
213 assertNull("compiler should not be registered",
214 service.getCompilers().get(TestIntent.class));
215 }
216
217 @Test
218 public void installerBasics() {
219 // Make sure there are no installers
220 assertEquals("incorrect installer count", 0, service.getInstallers().size());
221
222 // Add an installer and make sure that it appears in the map
223 IntentInstaller<TestInstallableIntent> installer = new TestInstaller(false);
224 service.registerInstaller(TestInstallableIntent.class, installer);
225 assertEquals("incorrect installer", installer,
226 service.getInstallers().get(TestInstallableIntent.class));
227
228 // Remove the same and make sure that it no longer appears in the map
229 service.unregisterInstaller(TestInstallableIntent.class);
230 assertNull("installer should not be registered",
231 service.getInstallers().get(TestInstallableIntent.class));
232 }
233
234 @Test
235 public void implicitRegistration() {
236 // Add a compiler and make sure that it appears in the map
237 IntentCompiler<TestIntent> compiler = new TestCompiler(new TestSubclassInstallableIntent(INSTALLABLE_IID));
238 service.registerCompiler(TestIntent.class, compiler);
239 assertEquals("incorrect compiler", compiler,
240 service.getCompilers().get(TestIntent.class));
241
242 // Add a installer and make sure that it appears in the map
243 IntentInstaller<TestInstallableIntent> installer = new TestInstaller(false);
244 service.registerInstaller(TestInstallableIntent.class, installer);
245 assertEquals("incorrect installer", installer,
246 service.getInstallers().get(TestInstallableIntent.class));
247
248
249 // Submit an intent which is a subclass of the one we registered
250 final Intent intent = new TestSubclassIntent(IID);
251 service.submit(intent);
252
253 // Allow some time for the intent to be compiled and installed
254 TestTools.assertAfter(GRACE_MS, new Runnable() {
255 @Override
256 public void run() {
tom85258ee2014-10-07 00:10:02 -0700257 assertEquals("incorrect intent state", IntentState.INSTALLED,
258 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700259 }
260 });
261
262 // Make sure that now we have an implicit registration of the compiler
263 // under the intent subclass
264 assertEquals("incorrect compiler", compiler,
265 service.getCompilers().get(TestSubclassIntent.class));
266
267 // Make sure that now we have an implicit registration of the installer
268 // under the intent subclass
269 assertEquals("incorrect installer", installer,
270 service.getInstallers().get(TestSubclassInstallableIntent.class));
271
272 // TODO: discuss whether or if implicit registration should require implicit unregistration
273 // perhaps unregister by compiler or installer itself, rather than by class would be better
274 }
275
276
277 // Fixture to track emitted intent events
Brian O'Connor66630c82014-10-02 21:08:19 -0700278 protected class TestListener implements IntentListener {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700279 final List<IntentEvent> events = new ArrayList<>();
280
281 @Override
282 public void event(IntentEvent event) {
283 events.add(event);
284 }
285 }
286
287 // Controllable compiler
288 private class TestCompiler implements IntentCompiler<TestIntent> {
289 private final boolean fail;
290 private final List<Intent> result;
291
292 TestCompiler(boolean fail) {
293 this.fail = fail;
294 this.result = Collections.emptyList();
295 }
296
297 TestCompiler(Intent... result) {
298 this.fail = false;
299 this.result = Arrays.asList(result);
300 }
301
302 @Override
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700303 public List<Intent> compile(TestIntent intent, List<Intent> installable,
304 Set<LinkResourceAllocations> resources) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700305 if (fail) {
306 throw new IntentException("compile failed by design");
307 }
308 List<Intent> compiled = new ArrayList<>(result);
309 return compiled;
310 }
311 }
312
313 // Controllable installer
314 private class TestInstaller implements IntentInstaller<TestInstallableIntent> {
315 private final boolean fail;
316
317 TestInstaller(boolean fail) {
318 this.fail = fail;
319 }
320
321 @Override
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700322 public List<FlowRuleBatchOperation> install(TestInstallableIntent intent) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700323 if (fail) {
324 throw new IntentException("install failed by design");
325 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700326 return null;
Brian O'Connorf3d06162014-10-02 15:54:12 -0700327 }
328
329 @Override
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700330 public List<FlowRuleBatchOperation> uninstall(TestInstallableIntent intent) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700331 if (fail) {
332 throw new IntentException("remove failed by design");
333 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700334 return null;
Brian O'Connorf3d06162014-10-02 15:54:12 -0700335 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700336
337 @Override
338 public List<FlowRuleBatchOperation> replace(TestInstallableIntent intent,
339 TestInstallableIntent newIntent) {
340 return null;
341 }
Brian O'Connorf3d06162014-10-02 15:54:12 -0700342 }
343
344}