blob: 86ebc5936d6b3ae2549f336464b3b28796752bf4 [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;
32
Brian O'Connorcb900f42014-10-07 21:55:33 -070033import org.junit.After;
34import org.junit.Before;
35import org.junit.Test;
Brian O'Connorf2dbde52014-10-10 16:20:24 -070036import org.onlab.onos.net.flow.FlowRuleBatchOperation;
Brian O'Connorf3d06162014-10-02 15:54:12 -070037
Brian O'Connorf3d06162014-10-02 15:54:12 -070038/**
39 * Suite of tests for the intent service contract.
40 */
41public class IntentServiceTest {
42
43 public static final IntentId IID = new IntentId(123);
44 public static final IntentId INSTALLABLE_IID = new IntentId(234);
45
46 protected static final int GRACE_MS = 500; // millis
47
48 protected TestableIntentService service;
49 protected TestListener listener = new TestListener();
50
51 @Before
52 public void setUp() {
53 service = createIntentService();
54 service.addListener(listener);
55 }
56
57 @After
58 public void tearDown() {
59 service.removeListener(listener);
60 }
61
62 /**
63 * Creates a service instance appropriately instrumented for testing.
64 *
65 * @return testable intent service
66 */
67 protected TestableIntentService createIntentService() {
68 return new FakeIntentManager();
69 }
70
71 @Test
72 public void basics() {
73 // Make sure there are no intents
Brian O'Connor66630c82014-10-02 21:08:19 -070074 assertEquals("incorrect intent count", 0, service.getIntentCount());
Brian O'Connorf3d06162014-10-02 15:54:12 -070075
76 // Register a compiler and an installer both setup for success.
77 service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
78 service.registerInstaller(TestInstallableIntent.class, new TestInstaller(false));
79
80 final Intent intent = new TestIntent(IID);
81 service.submit(intent);
82
83 // Allow a small window of time until the intent is in the expected state
84 TestTools.assertAfter(GRACE_MS, new Runnable() {
85 @Override
86 public void run() {
tom85258ee2014-10-07 00:10:02 -070087 assertEquals("incorrect intent state", IntentState.INSTALLED,
88 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -070089 }
90 });
91
92 // Make sure that all expected events have been emitted
tom85258ee2014-10-07 00:10:02 -070093 validateEvents(intent, SUBMITTED, INSTALLED);
Brian O'Connorf3d06162014-10-02 15:54:12 -070094
95 // Make sure there is just one intent (and is ours)
Brian O'Connor66630c82014-10-02 21:08:19 -070096 assertEquals("incorrect intent count", 1, service.getIntentCount());
Brian O'Connorf3d06162014-10-02 15:54:12 -070097
98 // Reset the listener events
99 listener.events.clear();
100
101 // Now withdraw the intent
102 service.withdraw(intent);
103
104 // Allow a small window of time until the event is in the expected state
105 TestTools.assertAfter(GRACE_MS, new Runnable() {
106 @Override
107 public void run() {
tom85258ee2014-10-07 00:10:02 -0700108 assertEquals("incorrect intent state", IntentState.WITHDRAWN,
109 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700110 }
111 });
112
113 // Make sure that all expected events have been emitted
tom85258ee2014-10-07 00:10:02 -0700114 validateEvents(intent, WITHDRAWN);
Brian O'Connorf3d06162014-10-02 15:54:12 -0700115
116 // TODO: discuss what is the fate of intents after they have been withdrawn
117 // Make sure that the intent is no longer in the system
118// assertEquals("incorrect intent count", 0, service.getIntents().size());
tom85258ee2014-10-07 00:10:02 -0700119// assertNull("intent should not be found", service.getIntent(intent.id()));
120// assertNull("intent state should not be found", service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700121 }
122
123 @Test
124 public void failedCompilation() {
125 // Register a compiler programmed for success
126 service.registerCompiler(TestIntent.class, new TestCompiler(true));
127
128 // Submit an intent
129 final Intent intent = new TestIntent(IID);
130 service.submit(intent);
131
132 // Allow a small window of time until the intent is in the expected state
133 TestTools.assertAfter(GRACE_MS, new Runnable() {
134 @Override
135 public void run() {
tom85258ee2014-10-07 00:10:02 -0700136 assertEquals("incorrect intent state", IntentState.FAILED,
137 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700138 }
139 });
140
141 // Make sure that all expected events have been emitted
142 validateEvents(intent, SUBMITTED, FAILED);
143 }
144
145 @Test
146 public void failedInstallation() {
147 // Register a compiler programmed for success and installer for failure
148 service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
149 service.registerInstaller(TestInstallableIntent.class, new TestInstaller(true));
150
151 // Submit an intent
152 final Intent intent = new TestIntent(IID);
153 service.submit(intent);
154
155 // Allow a small window of time until the intent is in the expected state
156 TestTools.assertAfter(GRACE_MS, new Runnable() {
157 @Override
158 public void run() {
tom85258ee2014-10-07 00:10:02 -0700159 assertEquals("incorrect intent state", IntentState.FAILED,
160 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700161 }
162 });
163
164 // Make sure that all expected events have been emitted
tom85258ee2014-10-07 00:10:02 -0700165 validateEvents(intent, SUBMITTED, FAILED);
Brian O'Connorf3d06162014-10-02 15:54:12 -0700166 }
167
168 /**
169 * Validates that the test event listener has received the following events
170 * for the specified intent. Events received for other intents will not be
171 * considered.
172 *
173 * @param intent intent subject
tom85258ee2014-10-07 00:10:02 -0700174 * @param types list of event types for which events are expected
Brian O'Connorf3d06162014-10-02 15:54:12 -0700175 */
tom85258ee2014-10-07 00:10:02 -0700176 protected void validateEvents(Intent intent, IntentEvent.Type... types) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700177 Iterator<IntentEvent> events = listener.events.iterator();
tom85258ee2014-10-07 00:10:02 -0700178 for (IntentEvent.Type type : types) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700179 IntentEvent event = events.hasNext() ? events.next() : null;
180 if (event == null) {
tom85258ee2014-10-07 00:10:02 -0700181 fail("expected event not found: " + type);
182 } else if (intent.equals(event.subject())) {
183 assertEquals("incorrect state", type, event.type());
Brian O'Connorf3d06162014-10-02 15:54:12 -0700184 }
185 }
186
187 // Remainder of events should not apply to this intent; make sure.
188 while (events.hasNext()) {
189 assertFalse("unexpected event for intent",
tom85258ee2014-10-07 00:10:02 -0700190 intent.equals(events.next().subject()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700191 }
192 }
193
194 @Test
195 public void compilerBasics() {
196 // Make sure there are no compilers
197 assertEquals("incorrect compiler count", 0, service.getCompilers().size());
198
199 // Add a compiler and make sure that it appears in the map
200 IntentCompiler<TestIntent> compiler = new TestCompiler(false);
201 service.registerCompiler(TestIntent.class, compiler);
202 assertEquals("incorrect compiler", compiler,
203 service.getCompilers().get(TestIntent.class));
204
205 // Remove the same and make sure that it no longer appears in the map
206 service.unregisterCompiler(TestIntent.class);
207 assertNull("compiler should not be registered",
208 service.getCompilers().get(TestIntent.class));
209 }
210
211 @Test
212 public void installerBasics() {
213 // Make sure there are no installers
214 assertEquals("incorrect installer count", 0, service.getInstallers().size());
215
216 // Add an installer and make sure that it appears in the map
217 IntentInstaller<TestInstallableIntent> installer = new TestInstaller(false);
218 service.registerInstaller(TestInstallableIntent.class, installer);
219 assertEquals("incorrect installer", installer,
220 service.getInstallers().get(TestInstallableIntent.class));
221
222 // Remove the same and make sure that it no longer appears in the map
223 service.unregisterInstaller(TestInstallableIntent.class);
224 assertNull("installer should not be registered",
225 service.getInstallers().get(TestInstallableIntent.class));
226 }
227
228 @Test
229 public void implicitRegistration() {
230 // Add a compiler and make sure that it appears in the map
231 IntentCompiler<TestIntent> compiler = new TestCompiler(new TestSubclassInstallableIntent(INSTALLABLE_IID));
232 service.registerCompiler(TestIntent.class, compiler);
233 assertEquals("incorrect compiler", compiler,
234 service.getCompilers().get(TestIntent.class));
235
236 // Add a installer and make sure that it appears in the map
237 IntentInstaller<TestInstallableIntent> installer = new TestInstaller(false);
238 service.registerInstaller(TestInstallableIntent.class, installer);
239 assertEquals("incorrect installer", installer,
240 service.getInstallers().get(TestInstallableIntent.class));
241
242
243 // Submit an intent which is a subclass of the one we registered
244 final Intent intent = new TestSubclassIntent(IID);
245 service.submit(intent);
246
247 // Allow some time for the intent to be compiled and installed
248 TestTools.assertAfter(GRACE_MS, new Runnable() {
249 @Override
250 public void run() {
tom85258ee2014-10-07 00:10:02 -0700251 assertEquals("incorrect intent state", IntentState.INSTALLED,
252 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700253 }
254 });
255
256 // Make sure that now we have an implicit registration of the compiler
257 // under the intent subclass
258 assertEquals("incorrect compiler", compiler,
259 service.getCompilers().get(TestSubclassIntent.class));
260
261 // Make sure that now we have an implicit registration of the installer
262 // under the intent subclass
263 assertEquals("incorrect installer", installer,
264 service.getInstallers().get(TestSubclassInstallableIntent.class));
265
266 // TODO: discuss whether or if implicit registration should require implicit unregistration
267 // perhaps unregister by compiler or installer itself, rather than by class would be better
268 }
269
270
271 // Fixture to track emitted intent events
Brian O'Connor66630c82014-10-02 21:08:19 -0700272 protected class TestListener implements IntentListener {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700273 final List<IntentEvent> events = new ArrayList<>();
274
275 @Override
276 public void event(IntentEvent event) {
277 events.add(event);
278 }
279 }
280
281 // Controllable compiler
282 private class TestCompiler implements IntentCompiler<TestIntent> {
283 private final boolean fail;
284 private final List<Intent> result;
285
286 TestCompiler(boolean fail) {
287 this.fail = fail;
288 this.result = Collections.emptyList();
289 }
290
291 TestCompiler(Intent... result) {
292 this.fail = false;
293 this.result = Arrays.asList(result);
294 }
295
296 @Override
297 public List<Intent> compile(TestIntent intent) {
298 if (fail) {
299 throw new IntentException("compile failed by design");
300 }
301 List<Intent> compiled = new ArrayList<>(result);
302 return compiled;
303 }
304 }
305
306 // Controllable installer
307 private class TestInstaller implements IntentInstaller<TestInstallableIntent> {
308 private final boolean fail;
309
310 TestInstaller(boolean fail) {
311 this.fail = fail;
312 }
313
314 @Override
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700315 public List<FlowRuleBatchOperation> install(TestInstallableIntent intent) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700316 if (fail) {
317 throw new IntentException("install failed by design");
318 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700319 return null;
Brian O'Connorf3d06162014-10-02 15:54:12 -0700320 }
321
322 @Override
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700323 public List<FlowRuleBatchOperation> uninstall(TestInstallableIntent intent) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700324 if (fail) {
325 throw new IntentException("remove failed by design");
326 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700327 return null;
Brian O'Connorf3d06162014-10-02 15:54:12 -0700328 }
329 }
330
331}