blob: a51746dd227a3abc6f7469f9fc40665518578840 [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;
17
Brian O'Connorcb900f42014-10-07 21:55:33 -070018import org.junit.After;
19import org.junit.Before;
20import org.junit.Test;
Brian O'Connorf2dbde52014-10-10 16:20:24 -070021import org.onlab.onos.net.flow.FlowRuleBatchOperation;
Brian O'Connorf3d06162014-10-02 15:54:12 -070022
Brian O'Connorf3d06162014-10-02 15:54:12 -070023/**
24 * Suite of tests for the intent service contract.
25 */
26public class IntentServiceTest {
27
28 public static final IntentId IID = new IntentId(123);
29 public static final IntentId INSTALLABLE_IID = new IntentId(234);
30
31 protected static final int GRACE_MS = 500; // millis
32
33 protected TestableIntentService service;
34 protected TestListener listener = new TestListener();
35
36 @Before
37 public void setUp() {
38 service = createIntentService();
39 service.addListener(listener);
40 }
41
42 @After
43 public void tearDown() {
44 service.removeListener(listener);
45 }
46
47 /**
48 * Creates a service instance appropriately instrumented for testing.
49 *
50 * @return testable intent service
51 */
52 protected TestableIntentService createIntentService() {
53 return new FakeIntentManager();
54 }
55
56 @Test
57 public void basics() {
58 // Make sure there are no intents
Brian O'Connor66630c82014-10-02 21:08:19 -070059 assertEquals("incorrect intent count", 0, service.getIntentCount());
Brian O'Connorf3d06162014-10-02 15:54:12 -070060
61 // Register a compiler and an installer both setup for success.
62 service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
63 service.registerInstaller(TestInstallableIntent.class, new TestInstaller(false));
64
65 final Intent intent = new TestIntent(IID);
66 service.submit(intent);
67
68 // Allow a small window of time until the intent is in the expected state
69 TestTools.assertAfter(GRACE_MS, new Runnable() {
70 @Override
71 public void run() {
tom85258ee2014-10-07 00:10:02 -070072 assertEquals("incorrect intent state", IntentState.INSTALLED,
73 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -070074 }
75 });
76
77 // Make sure that all expected events have been emitted
tom85258ee2014-10-07 00:10:02 -070078 validateEvents(intent, SUBMITTED, INSTALLED);
Brian O'Connorf3d06162014-10-02 15:54:12 -070079
80 // Make sure there is just one intent (and is ours)
Brian O'Connor66630c82014-10-02 21:08:19 -070081 assertEquals("incorrect intent count", 1, service.getIntentCount());
Brian O'Connorf3d06162014-10-02 15:54:12 -070082
83 // Reset the listener events
84 listener.events.clear();
85
86 // Now withdraw the intent
87 service.withdraw(intent);
88
89 // Allow a small window of time until the event 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.WITHDRAWN,
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, WITHDRAWN);
Brian O'Connorf3d06162014-10-02 15:54:12 -0700100
101 // TODO: discuss what is the fate of intents after they have been withdrawn
102 // Make sure that the intent is no longer in the system
103// assertEquals("incorrect intent count", 0, service.getIntents().size());
tom85258ee2014-10-07 00:10:02 -0700104// assertNull("intent should not be found", service.getIntent(intent.id()));
105// assertNull("intent state should not be found", service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700106 }
107
108 @Test
109 public void failedCompilation() {
110 // Register a compiler programmed for success
111 service.registerCompiler(TestIntent.class, new TestCompiler(true));
112
113 // Submit an intent
114 final Intent intent = new TestIntent(IID);
115 service.submit(intent);
116
117 // Allow a small window of time until the intent is in the expected state
118 TestTools.assertAfter(GRACE_MS, new Runnable() {
119 @Override
120 public void run() {
tom85258ee2014-10-07 00:10:02 -0700121 assertEquals("incorrect intent state", IntentState.FAILED,
122 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700123 }
124 });
125
126 // Make sure that all expected events have been emitted
127 validateEvents(intent, SUBMITTED, FAILED);
128 }
129
130 @Test
131 public void failedInstallation() {
132 // Register a compiler programmed for success and installer for failure
133 service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
134 service.registerInstaller(TestInstallableIntent.class, new TestInstaller(true));
135
136 // Submit an intent
137 final Intent intent = new TestIntent(IID);
138 service.submit(intent);
139
140 // Allow a small window of time until the intent is in the expected state
141 TestTools.assertAfter(GRACE_MS, new Runnable() {
142 @Override
143 public void run() {
tom85258ee2014-10-07 00:10:02 -0700144 assertEquals("incorrect intent state", IntentState.FAILED,
145 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700146 }
147 });
148
149 // Make sure that all expected events have been emitted
tom85258ee2014-10-07 00:10:02 -0700150 validateEvents(intent, SUBMITTED, FAILED);
Brian O'Connorf3d06162014-10-02 15:54:12 -0700151 }
152
153 /**
154 * Validates that the test event listener has received the following events
155 * for the specified intent. Events received for other intents will not be
156 * considered.
157 *
158 * @param intent intent subject
tom85258ee2014-10-07 00:10:02 -0700159 * @param types list of event types for which events are expected
Brian O'Connorf3d06162014-10-02 15:54:12 -0700160 */
tom85258ee2014-10-07 00:10:02 -0700161 protected void validateEvents(Intent intent, IntentEvent.Type... types) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700162 Iterator<IntentEvent> events = listener.events.iterator();
tom85258ee2014-10-07 00:10:02 -0700163 for (IntentEvent.Type type : types) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700164 IntentEvent event = events.hasNext() ? events.next() : null;
165 if (event == null) {
tom85258ee2014-10-07 00:10:02 -0700166 fail("expected event not found: " + type);
167 } else if (intent.equals(event.subject())) {
168 assertEquals("incorrect state", type, event.type());
Brian O'Connorf3d06162014-10-02 15:54:12 -0700169 }
170 }
171
172 // Remainder of events should not apply to this intent; make sure.
173 while (events.hasNext()) {
174 assertFalse("unexpected event for intent",
tom85258ee2014-10-07 00:10:02 -0700175 intent.equals(events.next().subject()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700176 }
177 }
178
179 @Test
180 public void compilerBasics() {
181 // Make sure there are no compilers
182 assertEquals("incorrect compiler count", 0, service.getCompilers().size());
183
184 // Add a compiler and make sure that it appears in the map
185 IntentCompiler<TestIntent> compiler = new TestCompiler(false);
186 service.registerCompiler(TestIntent.class, compiler);
187 assertEquals("incorrect compiler", compiler,
188 service.getCompilers().get(TestIntent.class));
189
190 // Remove the same and make sure that it no longer appears in the map
191 service.unregisterCompiler(TestIntent.class);
192 assertNull("compiler should not be registered",
193 service.getCompilers().get(TestIntent.class));
194 }
195
196 @Test
197 public void installerBasics() {
198 // Make sure there are no installers
199 assertEquals("incorrect installer count", 0, service.getInstallers().size());
200
201 // Add an installer and make sure that it appears in the map
202 IntentInstaller<TestInstallableIntent> installer = new TestInstaller(false);
203 service.registerInstaller(TestInstallableIntent.class, installer);
204 assertEquals("incorrect installer", installer,
205 service.getInstallers().get(TestInstallableIntent.class));
206
207 // Remove the same and make sure that it no longer appears in the map
208 service.unregisterInstaller(TestInstallableIntent.class);
209 assertNull("installer should not be registered",
210 service.getInstallers().get(TestInstallableIntent.class));
211 }
212
213 @Test
214 public void implicitRegistration() {
215 // Add a compiler and make sure that it appears in the map
216 IntentCompiler<TestIntent> compiler = new TestCompiler(new TestSubclassInstallableIntent(INSTALLABLE_IID));
217 service.registerCompiler(TestIntent.class, compiler);
218 assertEquals("incorrect compiler", compiler,
219 service.getCompilers().get(TestIntent.class));
220
221 // Add a installer and make sure that it appears in the map
222 IntentInstaller<TestInstallableIntent> installer = new TestInstaller(false);
223 service.registerInstaller(TestInstallableIntent.class, installer);
224 assertEquals("incorrect installer", installer,
225 service.getInstallers().get(TestInstallableIntent.class));
226
227
228 // Submit an intent which is a subclass of the one we registered
229 final Intent intent = new TestSubclassIntent(IID);
230 service.submit(intent);
231
232 // Allow some time for the intent to be compiled and installed
233 TestTools.assertAfter(GRACE_MS, new Runnable() {
234 @Override
235 public void run() {
tom85258ee2014-10-07 00:10:02 -0700236 assertEquals("incorrect intent state", IntentState.INSTALLED,
237 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700238 }
239 });
240
241 // Make sure that now we have an implicit registration of the compiler
242 // under the intent subclass
243 assertEquals("incorrect compiler", compiler,
244 service.getCompilers().get(TestSubclassIntent.class));
245
246 // Make sure that now we have an implicit registration of the installer
247 // under the intent subclass
248 assertEquals("incorrect installer", installer,
249 service.getInstallers().get(TestSubclassInstallableIntent.class));
250
251 // TODO: discuss whether or if implicit registration should require implicit unregistration
252 // perhaps unregister by compiler or installer itself, rather than by class would be better
253 }
254
255
256 // Fixture to track emitted intent events
Brian O'Connor66630c82014-10-02 21:08:19 -0700257 protected class TestListener implements IntentListener {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700258 final List<IntentEvent> events = new ArrayList<>();
259
260 @Override
261 public void event(IntentEvent event) {
262 events.add(event);
263 }
264 }
265
266 // Controllable compiler
267 private class TestCompiler implements IntentCompiler<TestIntent> {
268 private final boolean fail;
269 private final List<Intent> result;
270
271 TestCompiler(boolean fail) {
272 this.fail = fail;
273 this.result = Collections.emptyList();
274 }
275
276 TestCompiler(Intent... result) {
277 this.fail = false;
278 this.result = Arrays.asList(result);
279 }
280
281 @Override
282 public List<Intent> compile(TestIntent intent) {
283 if (fail) {
284 throw new IntentException("compile failed by design");
285 }
286 List<Intent> compiled = new ArrayList<>(result);
287 return compiled;
288 }
289 }
290
291 // Controllable installer
292 private class TestInstaller implements IntentInstaller<TestInstallableIntent> {
293 private final boolean fail;
294
295 TestInstaller(boolean fail) {
296 this.fail = fail;
297 }
298
299 @Override
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700300 public List<FlowRuleBatchOperation> install(TestInstallableIntent intent) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700301 if (fail) {
302 throw new IntentException("install failed by design");
303 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700304 return null;
Brian O'Connorf3d06162014-10-02 15:54:12 -0700305 }
306
307 @Override
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700308 public List<FlowRuleBatchOperation> uninstall(TestInstallableIntent intent) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700309 if (fail) {
310 throw new IntentException("remove failed by design");
311 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700312 return null;
Brian O'Connorf3d06162014-10-02 15:54:12 -0700313 }
314 }
315
316}