blob: 163a056a1c2d738f6b719be0ead8c03b04b01b12 [file] [log] [blame]
Brian O'Connorf3d06162014-10-02 15:54:12 -07001package org.onlab.onos.net.intent;
2
Brian O'Connorcb900f42014-10-07 21:55:33 -07003import static org.junit.Assert.assertEquals;
4import static org.junit.Assert.assertFalse;
5import static org.junit.Assert.assertNull;
6import static org.junit.Assert.fail;
7import static org.onlab.onos.net.intent.IntentEvent.Type.FAILED;
8import static org.onlab.onos.net.intent.IntentEvent.Type.INSTALLED;
9import static org.onlab.onos.net.intent.IntentEvent.Type.SUBMITTED;
10import static org.onlab.onos.net.intent.IntentEvent.Type.WITHDRAWN;
Brian O'Connorf3d06162014-10-02 15:54:12 -070011
12import java.util.ArrayList;
13import java.util.Arrays;
14import java.util.Collections;
15import java.util.Iterator;
16import java.util.List;
Brian O'Connorcb900f42014-10-07 21:55:33 -070017import java.util.concurrent.Future;
Brian O'Connorf3d06162014-10-02 15:54:12 -070018
Brian O'Connorcb900f42014-10-07 21:55:33 -070019import org.junit.After;
20import org.junit.Before;
21import org.junit.Test;
22import org.onlab.onos.net.flow.CompletedBatchOperation;
Brian O'Connorf3d06162014-10-02 15:54:12 -070023
Brian O'Connorf3d06162014-10-02 15:54:12 -070024/**
25 * Suite of tests for the intent service contract.
26 */
27public class IntentServiceTest {
28
29 public static final IntentId IID = new IntentId(123);
30 public static final IntentId INSTALLABLE_IID = new IntentId(234);
31
32 protected static final int GRACE_MS = 500; // millis
33
34 protected TestableIntentService service;
35 protected TestListener listener = new TestListener();
36
37 @Before
38 public void setUp() {
39 service = createIntentService();
40 service.addListener(listener);
41 }
42
43 @After
44 public void tearDown() {
45 service.removeListener(listener);
46 }
47
48 /**
49 * Creates a service instance appropriately instrumented for testing.
50 *
51 * @return testable intent service
52 */
53 protected TestableIntentService createIntentService() {
54 return new FakeIntentManager();
55 }
56
57 @Test
58 public void basics() {
59 // Make sure there are no intents
Brian O'Connor66630c82014-10-02 21:08:19 -070060 assertEquals("incorrect intent count", 0, service.getIntentCount());
Brian O'Connorf3d06162014-10-02 15:54:12 -070061
62 // Register a compiler and an installer both setup for success.
63 service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
64 service.registerInstaller(TestInstallableIntent.class, new TestInstaller(false));
65
66 final Intent intent = new TestIntent(IID);
67 service.submit(intent);
68
69 // Allow a small window of time until the intent is in the expected state
70 TestTools.assertAfter(GRACE_MS, new Runnable() {
71 @Override
72 public void run() {
tom85258ee2014-10-07 00:10:02 -070073 assertEquals("incorrect intent state", IntentState.INSTALLED,
74 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -070075 }
76 });
77
78 // Make sure that all expected events have been emitted
tom85258ee2014-10-07 00:10:02 -070079 validateEvents(intent, SUBMITTED, INSTALLED);
Brian O'Connorf3d06162014-10-02 15:54:12 -070080
81 // Make sure there is just one intent (and is ours)
Brian O'Connor66630c82014-10-02 21:08:19 -070082 assertEquals("incorrect intent count", 1, service.getIntentCount());
Brian O'Connorf3d06162014-10-02 15:54:12 -070083
84 // Reset the listener events
85 listener.events.clear();
86
87 // Now withdraw the intent
88 service.withdraw(intent);
89
90 // Allow a small window of time until the event is in the expected state
91 TestTools.assertAfter(GRACE_MS, new Runnable() {
92 @Override
93 public void run() {
tom85258ee2014-10-07 00:10:02 -070094 assertEquals("incorrect intent state", IntentState.WITHDRAWN,
95 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -070096 }
97 });
98
99 // Make sure that all expected events have been emitted
tom85258ee2014-10-07 00:10:02 -0700100 validateEvents(intent, WITHDRAWN);
Brian O'Connorf3d06162014-10-02 15:54:12 -0700101
102 // TODO: discuss what is the fate of intents after they have been withdrawn
103 // Make sure that the intent is no longer in the system
104// assertEquals("incorrect intent count", 0, service.getIntents().size());
tom85258ee2014-10-07 00:10:02 -0700105// assertNull("intent should not be found", service.getIntent(intent.id()));
106// assertNull("intent state should not be found", service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700107 }
108
109 @Test
110 public void failedCompilation() {
111 // Register a compiler programmed for success
112 service.registerCompiler(TestIntent.class, new TestCompiler(true));
113
114 // Submit an intent
115 final Intent intent = new TestIntent(IID);
116 service.submit(intent);
117
118 // Allow a small window of time until the intent is in the expected state
119 TestTools.assertAfter(GRACE_MS, new Runnable() {
120 @Override
121 public void run() {
tom85258ee2014-10-07 00:10:02 -0700122 assertEquals("incorrect intent state", IntentState.FAILED,
123 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700124 }
125 });
126
127 // Make sure that all expected events have been emitted
128 validateEvents(intent, SUBMITTED, FAILED);
129 }
130
131 @Test
132 public void failedInstallation() {
133 // Register a compiler programmed for success and installer for failure
134 service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
135 service.registerInstaller(TestInstallableIntent.class, new TestInstaller(true));
136
137 // Submit an intent
138 final Intent intent = new TestIntent(IID);
139 service.submit(intent);
140
141 // Allow a small window of time until the intent is in the expected state
142 TestTools.assertAfter(GRACE_MS, new Runnable() {
143 @Override
144 public void run() {
tom85258ee2014-10-07 00:10:02 -0700145 assertEquals("incorrect intent state", IntentState.FAILED,
146 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700147 }
148 });
149
150 // Make sure that all expected events have been emitted
tom85258ee2014-10-07 00:10:02 -0700151 validateEvents(intent, SUBMITTED, FAILED);
Brian O'Connorf3d06162014-10-02 15:54:12 -0700152 }
153
154 /**
155 * Validates that the test event listener has received the following events
156 * for the specified intent. Events received for other intents will not be
157 * considered.
158 *
159 * @param intent intent subject
tom85258ee2014-10-07 00:10:02 -0700160 * @param types list of event types for which events are expected
Brian O'Connorf3d06162014-10-02 15:54:12 -0700161 */
tom85258ee2014-10-07 00:10:02 -0700162 protected void validateEvents(Intent intent, IntentEvent.Type... types) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700163 Iterator<IntentEvent> events = listener.events.iterator();
tom85258ee2014-10-07 00:10:02 -0700164 for (IntentEvent.Type type : types) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700165 IntentEvent event = events.hasNext() ? events.next() : null;
166 if (event == null) {
tom85258ee2014-10-07 00:10:02 -0700167 fail("expected event not found: " + type);
168 } else if (intent.equals(event.subject())) {
169 assertEquals("incorrect state", type, event.type());
Brian O'Connorf3d06162014-10-02 15:54:12 -0700170 }
171 }
172
173 // Remainder of events should not apply to this intent; make sure.
174 while (events.hasNext()) {
175 assertFalse("unexpected event for intent",
tom85258ee2014-10-07 00:10:02 -0700176 intent.equals(events.next().subject()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700177 }
178 }
179
180 @Test
181 public void compilerBasics() {
182 // Make sure there are no compilers
183 assertEquals("incorrect compiler count", 0, service.getCompilers().size());
184
185 // Add a compiler and make sure that it appears in the map
186 IntentCompiler<TestIntent> compiler = new TestCompiler(false);
187 service.registerCompiler(TestIntent.class, compiler);
188 assertEquals("incorrect compiler", compiler,
189 service.getCompilers().get(TestIntent.class));
190
191 // Remove the same and make sure that it no longer appears in the map
192 service.unregisterCompiler(TestIntent.class);
193 assertNull("compiler should not be registered",
194 service.getCompilers().get(TestIntent.class));
195 }
196
197 @Test
198 public void installerBasics() {
199 // Make sure there are no installers
200 assertEquals("incorrect installer count", 0, service.getInstallers().size());
201
202 // Add an installer and make sure that it appears in the map
203 IntentInstaller<TestInstallableIntent> installer = new TestInstaller(false);
204 service.registerInstaller(TestInstallableIntent.class, installer);
205 assertEquals("incorrect installer", installer,
206 service.getInstallers().get(TestInstallableIntent.class));
207
208 // Remove the same and make sure that it no longer appears in the map
209 service.unregisterInstaller(TestInstallableIntent.class);
210 assertNull("installer should not be registered",
211 service.getInstallers().get(TestInstallableIntent.class));
212 }
213
214 @Test
215 public void implicitRegistration() {
216 // Add a compiler and make sure that it appears in the map
217 IntentCompiler<TestIntent> compiler = new TestCompiler(new TestSubclassInstallableIntent(INSTALLABLE_IID));
218 service.registerCompiler(TestIntent.class, compiler);
219 assertEquals("incorrect compiler", compiler,
220 service.getCompilers().get(TestIntent.class));
221
222 // Add a 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
229 // Submit an intent which is a subclass of the one we registered
230 final Intent intent = new TestSubclassIntent(IID);
231 service.submit(intent);
232
233 // Allow some time for the intent to be compiled and installed
234 TestTools.assertAfter(GRACE_MS, new Runnable() {
235 @Override
236 public void run() {
tom85258ee2014-10-07 00:10:02 -0700237 assertEquals("incorrect intent state", IntentState.INSTALLED,
238 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700239 }
240 });
241
242 // Make sure that now we have an implicit registration of the compiler
243 // under the intent subclass
244 assertEquals("incorrect compiler", compiler,
245 service.getCompilers().get(TestSubclassIntent.class));
246
247 // Make sure that now we have an implicit registration of the installer
248 // under the intent subclass
249 assertEquals("incorrect installer", installer,
250 service.getInstallers().get(TestSubclassInstallableIntent.class));
251
252 // TODO: discuss whether or if implicit registration should require implicit unregistration
253 // perhaps unregister by compiler or installer itself, rather than by class would be better
254 }
255
256
257 // Fixture to track emitted intent events
Brian O'Connor66630c82014-10-02 21:08:19 -0700258 protected class TestListener implements IntentListener {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700259 final List<IntentEvent> events = new ArrayList<>();
260
261 @Override
262 public void event(IntentEvent event) {
263 events.add(event);
264 }
265 }
266
267 // Controllable compiler
268 private class TestCompiler implements IntentCompiler<TestIntent> {
269 private final boolean fail;
270 private final List<Intent> result;
271
272 TestCompiler(boolean fail) {
273 this.fail = fail;
274 this.result = Collections.emptyList();
275 }
276
277 TestCompiler(Intent... result) {
278 this.fail = false;
279 this.result = Arrays.asList(result);
280 }
281
282 @Override
283 public List<Intent> compile(TestIntent intent) {
284 if (fail) {
285 throw new IntentException("compile failed by design");
286 }
287 List<Intent> compiled = new ArrayList<>(result);
288 return compiled;
289 }
290 }
291
292 // Controllable installer
293 private class TestInstaller implements IntentInstaller<TestInstallableIntent> {
294 private final boolean fail;
295
296 TestInstaller(boolean fail) {
297 this.fail = fail;
298 }
299
300 @Override
Brian O'Connorcb900f42014-10-07 21:55:33 -0700301 public Future<CompletedBatchOperation> install(TestInstallableIntent intent) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700302 if (fail) {
303 throw new IntentException("install failed by design");
304 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700305 return null;
Brian O'Connorf3d06162014-10-02 15:54:12 -0700306 }
307
308 @Override
Brian O'Connorcb900f42014-10-07 21:55:33 -0700309 public Future<CompletedBatchOperation> uninstall(TestInstallableIntent intent) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700310 if (fail) {
311 throw new IntentException("remove failed by design");
312 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700313 return null;
Brian O'Connorf3d06162014-10-02 15:54:12 -0700314 }
315 }
316
317}