blob: a7004a275c5b8eaf559a5d30d312b713eb387853 [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'Connorf2dbde52014-10-10 16:20:24 -070037import org.onlab.onos.net.flow.FlowRuleBatchOperation;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070038import org.onlab.onos.net.resource.LinkResourceAllocations;
Brian O'Connorf3d06162014-10-02 15:54:12 -070039
Brian O'Connorf3d06162014-10-02 15:54:12 -070040/**
41 * Suite of tests for the intent service contract.
42 */
43public class IntentServiceTest {
44
45 public static final IntentId IID = new IntentId(123);
46 public static final IntentId INSTALLABLE_IID = new IntentId(234);
47
48 protected static final int GRACE_MS = 500; // millis
49
50 protected TestableIntentService service;
51 protected TestListener listener = new TestListener();
52
53 @Before
54 public void setUp() {
55 service = createIntentService();
56 service.addListener(listener);
57 }
58
59 @After
60 public void tearDown() {
61 service.removeListener(listener);
62 }
63
64 /**
65 * Creates a service instance appropriately instrumented for testing.
66 *
67 * @return testable intent service
68 */
69 protected TestableIntentService createIntentService() {
70 return new FakeIntentManager();
71 }
72
73 @Test
74 public void basics() {
75 // Make sure there are no intents
Brian O'Connor66630c82014-10-02 21:08:19 -070076 assertEquals("incorrect intent count", 0, service.getIntentCount());
Brian O'Connorf3d06162014-10-02 15:54:12 -070077
78 // Register a compiler and an installer both setup for success.
79 service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
80 service.registerInstaller(TestInstallableIntent.class, new TestInstaller(false));
81
82 final Intent intent = new TestIntent(IID);
83 service.submit(intent);
84
85 // Allow a small window of time until the intent is in the expected state
86 TestTools.assertAfter(GRACE_MS, new Runnable() {
87 @Override
88 public void run() {
tom85258ee2014-10-07 00:10:02 -070089 assertEquals("incorrect intent state", IntentState.INSTALLED,
90 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -070091 }
92 });
93
94 // Make sure that all expected events have been emitted
tom85258ee2014-10-07 00:10:02 -070095 validateEvents(intent, SUBMITTED, INSTALLED);
Brian O'Connorf3d06162014-10-02 15:54:12 -070096
97 // Make sure there is just one intent (and is ours)
Brian O'Connor66630c82014-10-02 21:08:19 -070098 assertEquals("incorrect intent count", 1, service.getIntentCount());
Brian O'Connorf3d06162014-10-02 15:54:12 -070099
100 // Reset the listener events
101 listener.events.clear();
102
103 // Now withdraw the intent
104 service.withdraw(intent);
105
106 // Allow a small window of time until the event is in the expected state
107 TestTools.assertAfter(GRACE_MS, new Runnable() {
108 @Override
109 public void run() {
tom85258ee2014-10-07 00:10:02 -0700110 assertEquals("incorrect intent state", IntentState.WITHDRAWN,
111 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700112 }
113 });
114
115 // Make sure that all expected events have been emitted
tom85258ee2014-10-07 00:10:02 -0700116 validateEvents(intent, WITHDRAWN);
Brian O'Connorf3d06162014-10-02 15:54:12 -0700117
118 // TODO: discuss what is the fate of intents after they have been withdrawn
119 // Make sure that the intent is no longer in the system
120// assertEquals("incorrect intent count", 0, service.getIntents().size());
tom85258ee2014-10-07 00:10:02 -0700121// assertNull("intent should not be found", service.getIntent(intent.id()));
122// assertNull("intent state should not be found", service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700123 }
124
125 @Test
126 public void failedCompilation() {
127 // Register a compiler programmed for success
128 service.registerCompiler(TestIntent.class, new TestCompiler(true));
129
130 // Submit an intent
131 final Intent intent = new TestIntent(IID);
132 service.submit(intent);
133
134 // Allow a small window of time until the intent is in the expected state
135 TestTools.assertAfter(GRACE_MS, new Runnable() {
136 @Override
137 public void run() {
tom85258ee2014-10-07 00:10:02 -0700138 assertEquals("incorrect intent state", IntentState.FAILED,
139 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700140 }
141 });
142
143 // Make sure that all expected events have been emitted
144 validateEvents(intent, SUBMITTED, FAILED);
145 }
146
147 @Test
148 public void failedInstallation() {
149 // Register a compiler programmed for success and installer for failure
150 service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
151 service.registerInstaller(TestInstallableIntent.class, new TestInstaller(true));
152
153 // Submit an intent
154 final Intent intent = new TestIntent(IID);
155 service.submit(intent);
156
157 // Allow a small window of time until the intent is in the expected state
158 TestTools.assertAfter(GRACE_MS, new Runnable() {
159 @Override
160 public void run() {
tom85258ee2014-10-07 00:10:02 -0700161 assertEquals("incorrect intent state", IntentState.FAILED,
162 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700163 }
164 });
165
166 // Make sure that all expected events have been emitted
tom85258ee2014-10-07 00:10:02 -0700167 validateEvents(intent, SUBMITTED, FAILED);
Brian O'Connorf3d06162014-10-02 15:54:12 -0700168 }
169
170 /**
171 * Validates that the test event listener has received the following events
172 * for the specified intent. Events received for other intents will not be
173 * considered.
174 *
175 * @param intent intent subject
tom85258ee2014-10-07 00:10:02 -0700176 * @param types list of event types for which events are expected
Brian O'Connorf3d06162014-10-02 15:54:12 -0700177 */
tom85258ee2014-10-07 00:10:02 -0700178 protected void validateEvents(Intent intent, IntentEvent.Type... types) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700179 Iterator<IntentEvent> events = listener.events.iterator();
tom85258ee2014-10-07 00:10:02 -0700180 for (IntentEvent.Type type : types) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700181 IntentEvent event = events.hasNext() ? events.next() : null;
182 if (event == null) {
tom85258ee2014-10-07 00:10:02 -0700183 fail("expected event not found: " + type);
184 } else if (intent.equals(event.subject())) {
185 assertEquals("incorrect state", type, event.type());
Brian O'Connorf3d06162014-10-02 15:54:12 -0700186 }
187 }
188
189 // Remainder of events should not apply to this intent; make sure.
190 while (events.hasNext()) {
191 assertFalse("unexpected event for intent",
tom85258ee2014-10-07 00:10:02 -0700192 intent.equals(events.next().subject()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700193 }
194 }
195
196 @Test
197 public void compilerBasics() {
198 // Make sure there are no compilers
199 assertEquals("incorrect compiler count", 0, service.getCompilers().size());
200
201 // Add a compiler and make sure that it appears in the map
202 IntentCompiler<TestIntent> compiler = new TestCompiler(false);
203 service.registerCompiler(TestIntent.class, compiler);
204 assertEquals("incorrect compiler", compiler,
205 service.getCompilers().get(TestIntent.class));
206
207 // Remove the same and make sure that it no longer appears in the map
208 service.unregisterCompiler(TestIntent.class);
209 assertNull("compiler should not be registered",
210 service.getCompilers().get(TestIntent.class));
211 }
212
213 @Test
214 public void installerBasics() {
215 // Make sure there are no installers
216 assertEquals("incorrect installer count", 0, service.getInstallers().size());
217
218 // Add an installer and make sure that it appears in the map
219 IntentInstaller<TestInstallableIntent> installer = new TestInstaller(false);
220 service.registerInstaller(TestInstallableIntent.class, installer);
221 assertEquals("incorrect installer", installer,
222 service.getInstallers().get(TestInstallableIntent.class));
223
224 // Remove the same and make sure that it no longer appears in the map
225 service.unregisterInstaller(TestInstallableIntent.class);
226 assertNull("installer should not be registered",
227 service.getInstallers().get(TestInstallableIntent.class));
228 }
229
230 @Test
231 public void implicitRegistration() {
232 // Add a compiler and make sure that it appears in the map
233 IntentCompiler<TestIntent> compiler = new TestCompiler(new TestSubclassInstallableIntent(INSTALLABLE_IID));
234 service.registerCompiler(TestIntent.class, compiler);
235 assertEquals("incorrect compiler", compiler,
236 service.getCompilers().get(TestIntent.class));
237
238 // Add a installer and make sure that it appears in the map
239 IntentInstaller<TestInstallableIntent> installer = new TestInstaller(false);
240 service.registerInstaller(TestInstallableIntent.class, installer);
241 assertEquals("incorrect installer", installer,
242 service.getInstallers().get(TestInstallableIntent.class));
243
244
245 // Submit an intent which is a subclass of the one we registered
246 final Intent intent = new TestSubclassIntent(IID);
247 service.submit(intent);
248
249 // Allow some time for the intent to be compiled and installed
250 TestTools.assertAfter(GRACE_MS, new Runnable() {
251 @Override
252 public void run() {
tom85258ee2014-10-07 00:10:02 -0700253 assertEquals("incorrect intent state", IntentState.INSTALLED,
254 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700255 }
256 });
257
258 // Make sure that now we have an implicit registration of the compiler
259 // under the intent subclass
260 assertEquals("incorrect compiler", compiler,
261 service.getCompilers().get(TestSubclassIntent.class));
262
263 // Make sure that now we have an implicit registration of the installer
264 // under the intent subclass
265 assertEquals("incorrect installer", installer,
266 service.getInstallers().get(TestSubclassInstallableIntent.class));
267
268 // TODO: discuss whether or if implicit registration should require implicit unregistration
269 // perhaps unregister by compiler or installer itself, rather than by class would be better
270 }
271
272
273 // Fixture to track emitted intent events
Brian O'Connor66630c82014-10-02 21:08:19 -0700274 protected class TestListener implements IntentListener {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700275 final List<IntentEvent> events = new ArrayList<>();
276
277 @Override
278 public void event(IntentEvent event) {
279 events.add(event);
280 }
281 }
282
283 // Controllable compiler
284 private class TestCompiler implements IntentCompiler<TestIntent> {
285 private final boolean fail;
286 private final List<Intent> result;
287
288 TestCompiler(boolean fail) {
289 this.fail = fail;
290 this.result = Collections.emptyList();
291 }
292
293 TestCompiler(Intent... result) {
294 this.fail = false;
295 this.result = Arrays.asList(result);
296 }
297
298 @Override
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700299 public List<Intent> compile(TestIntent intent, List<Intent> installable,
300 Set<LinkResourceAllocations> resources) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700301 if (fail) {
302 throw new IntentException("compile failed by design");
303 }
304 List<Intent> compiled = new ArrayList<>(result);
305 return compiled;
306 }
307 }
308
309 // Controllable installer
310 private class TestInstaller implements IntentInstaller<TestInstallableIntent> {
311 private final boolean fail;
312
313 TestInstaller(boolean fail) {
314 this.fail = fail;
315 }
316
317 @Override
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700318 public List<FlowRuleBatchOperation> install(TestInstallableIntent intent) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700319 if (fail) {
320 throw new IntentException("install failed by design");
321 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700322 return null;
Brian O'Connorf3d06162014-10-02 15:54:12 -0700323 }
324
325 @Override
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700326 public List<FlowRuleBatchOperation> uninstall(TestInstallableIntent intent) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700327 if (fail) {
328 throw new IntentException("remove failed by design");
329 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700330 return null;
Brian O'Connorf3d06162014-10-02 15:54:12 -0700331 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700332
333 @Override
334 public List<FlowRuleBatchOperation> replace(TestInstallableIntent intent,
335 TestInstallableIntent newIntent) {
336 return null;
337 }
Brian O'Connorf3d06162014-10-02 15:54:12 -0700338 }
339
340}