blob: 7eb0e19b57c517f8f1e2868596f57af8e77f803d [file] [log] [blame]
Brian O'Connorf3d06162014-10-02 15:54:12 -07001package org.onlab.onos.net.intent;
2
3import org.junit.After;
4import org.junit.Before;
5import org.junit.Test;
6
7import java.util.ArrayList;
8import java.util.Arrays;
9import java.util.Collections;
10import java.util.Iterator;
11import java.util.List;
12
Brian O'Connorf3d06162014-10-02 15:54:12 -070013import static org.junit.Assert.*;
tom85258ee2014-10-07 00:10:02 -070014import static org.onlab.onos.net.intent.IntentEvent.Type.*;
Brian O'Connorf3d06162014-10-02 15:54:12 -070015
Brian O'Connorf3d06162014-10-02 15:54:12 -070016/**
17 * Suite of tests for the intent service contract.
18 */
19public class IntentServiceTest {
20
21 public static final IntentId IID = new IntentId(123);
22 public static final IntentId INSTALLABLE_IID = new IntentId(234);
23
24 protected static final int GRACE_MS = 500; // millis
25
26 protected TestableIntentService service;
27 protected TestListener listener = new TestListener();
28
29 @Before
30 public void setUp() {
31 service = createIntentService();
32 service.addListener(listener);
33 }
34
35 @After
36 public void tearDown() {
37 service.removeListener(listener);
38 }
39
40 /**
41 * Creates a service instance appropriately instrumented for testing.
42 *
43 * @return testable intent service
44 */
45 protected TestableIntentService createIntentService() {
46 return new FakeIntentManager();
47 }
48
49 @Test
50 public void basics() {
51 // Make sure there are no intents
Brian O'Connor66630c82014-10-02 21:08:19 -070052 assertEquals("incorrect intent count", 0, service.getIntentCount());
Brian O'Connorf3d06162014-10-02 15:54:12 -070053
54 // Register a compiler and an installer both setup for success.
55 service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
56 service.registerInstaller(TestInstallableIntent.class, new TestInstaller(false));
57
58 final Intent intent = new TestIntent(IID);
59 service.submit(intent);
60
61 // Allow a small window of time until the intent is in the expected state
62 TestTools.assertAfter(GRACE_MS, new Runnable() {
63 @Override
64 public void run() {
tom85258ee2014-10-07 00:10:02 -070065 assertEquals("incorrect intent state", IntentState.INSTALLED,
66 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -070067 }
68 });
69
70 // Make sure that all expected events have been emitted
tom85258ee2014-10-07 00:10:02 -070071 validateEvents(intent, SUBMITTED, INSTALLED);
Brian O'Connorf3d06162014-10-02 15:54:12 -070072
73 // Make sure there is just one intent (and is ours)
Brian O'Connor66630c82014-10-02 21:08:19 -070074 assertEquals("incorrect intent count", 1, service.getIntentCount());
Brian O'Connorf3d06162014-10-02 15:54:12 -070075
76 // Reset the listener events
77 listener.events.clear();
78
79 // Now withdraw the intent
80 service.withdraw(intent);
81
82 // Allow a small window of time until the event is in the expected state
83 TestTools.assertAfter(GRACE_MS, new Runnable() {
84 @Override
85 public void run() {
tom85258ee2014-10-07 00:10:02 -070086 assertEquals("incorrect intent state", IntentState.WITHDRAWN,
87 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -070088 }
89 });
90
91 // Make sure that all expected events have been emitted
tom85258ee2014-10-07 00:10:02 -070092 validateEvents(intent, WITHDRAWN);
Brian O'Connorf3d06162014-10-02 15:54:12 -070093
94 // TODO: discuss what is the fate of intents after they have been withdrawn
95 // Make sure that the intent is no longer in the system
96// assertEquals("incorrect intent count", 0, service.getIntents().size());
tom85258ee2014-10-07 00:10:02 -070097// assertNull("intent should not be found", service.getIntent(intent.id()));
98// assertNull("intent state should not be found", service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -070099 }
100
101 @Test
102 public void failedCompilation() {
103 // Register a compiler programmed for success
104 service.registerCompiler(TestIntent.class, new TestCompiler(true));
105
106 // Submit an intent
107 final Intent intent = new TestIntent(IID);
108 service.submit(intent);
109
110 // Allow a small window of time until the intent 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.FAILED,
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
120 validateEvents(intent, SUBMITTED, FAILED);
121 }
122
123 @Test
124 public void failedInstallation() {
125 // Register a compiler programmed for success and installer for failure
126 service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
127 service.registerInstaller(TestInstallableIntent.class, new TestInstaller(true));
128
129 // Submit an intent
130 final Intent intent = new TestIntent(IID);
131 service.submit(intent);
132
133 // Allow a small window of time until the intent is in the expected state
134 TestTools.assertAfter(GRACE_MS, new Runnable() {
135 @Override
136 public void run() {
tom85258ee2014-10-07 00:10:02 -0700137 assertEquals("incorrect intent state", IntentState.FAILED,
138 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700139 }
140 });
141
142 // Make sure that all expected events have been emitted
tom85258ee2014-10-07 00:10:02 -0700143 validateEvents(intent, SUBMITTED, FAILED);
Brian O'Connorf3d06162014-10-02 15:54:12 -0700144 }
145
146 /**
147 * Validates that the test event listener has received the following events
148 * for the specified intent. Events received for other intents will not be
149 * considered.
150 *
151 * @param intent intent subject
tom85258ee2014-10-07 00:10:02 -0700152 * @param types list of event types for which events are expected
Brian O'Connorf3d06162014-10-02 15:54:12 -0700153 */
tom85258ee2014-10-07 00:10:02 -0700154 protected void validateEvents(Intent intent, IntentEvent.Type... types) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700155 Iterator<IntentEvent> events = listener.events.iterator();
tom85258ee2014-10-07 00:10:02 -0700156 for (IntentEvent.Type type : types) {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700157 IntentEvent event = events.hasNext() ? events.next() : null;
158 if (event == null) {
tom85258ee2014-10-07 00:10:02 -0700159 fail("expected event not found: " + type);
160 } else if (intent.equals(event.subject())) {
161 assertEquals("incorrect state", type, event.type());
Brian O'Connorf3d06162014-10-02 15:54:12 -0700162 }
163 }
164
165 // Remainder of events should not apply to this intent; make sure.
166 while (events.hasNext()) {
167 assertFalse("unexpected event for intent",
tom85258ee2014-10-07 00:10:02 -0700168 intent.equals(events.next().subject()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700169 }
170 }
171
172 @Test
173 public void compilerBasics() {
174 // Make sure there are no compilers
175 assertEquals("incorrect compiler count", 0, service.getCompilers().size());
176
177 // Add a compiler and make sure that it appears in the map
178 IntentCompiler<TestIntent> compiler = new TestCompiler(false);
179 service.registerCompiler(TestIntent.class, compiler);
180 assertEquals("incorrect compiler", compiler,
181 service.getCompilers().get(TestIntent.class));
182
183 // Remove the same and make sure that it no longer appears in the map
184 service.unregisterCompiler(TestIntent.class);
185 assertNull("compiler should not be registered",
186 service.getCompilers().get(TestIntent.class));
187 }
188
189 @Test
190 public void installerBasics() {
191 // Make sure there are no installers
192 assertEquals("incorrect installer count", 0, service.getInstallers().size());
193
194 // Add an installer and make sure that it appears in the map
195 IntentInstaller<TestInstallableIntent> installer = new TestInstaller(false);
196 service.registerInstaller(TestInstallableIntent.class, installer);
197 assertEquals("incorrect installer", installer,
198 service.getInstallers().get(TestInstallableIntent.class));
199
200 // Remove the same and make sure that it no longer appears in the map
201 service.unregisterInstaller(TestInstallableIntent.class);
202 assertNull("installer should not be registered",
203 service.getInstallers().get(TestInstallableIntent.class));
204 }
205
206 @Test
207 public void implicitRegistration() {
208 // Add a compiler and make sure that it appears in the map
209 IntentCompiler<TestIntent> compiler = new TestCompiler(new TestSubclassInstallableIntent(INSTALLABLE_IID));
210 service.registerCompiler(TestIntent.class, compiler);
211 assertEquals("incorrect compiler", compiler,
212 service.getCompilers().get(TestIntent.class));
213
214 // Add a installer and make sure that it appears in the map
215 IntentInstaller<TestInstallableIntent> installer = new TestInstaller(false);
216 service.registerInstaller(TestInstallableIntent.class, installer);
217 assertEquals("incorrect installer", installer,
218 service.getInstallers().get(TestInstallableIntent.class));
219
220
221 // Submit an intent which is a subclass of the one we registered
222 final Intent intent = new TestSubclassIntent(IID);
223 service.submit(intent);
224
225 // Allow some time for the intent to be compiled and installed
226 TestTools.assertAfter(GRACE_MS, new Runnable() {
227 @Override
228 public void run() {
tom85258ee2014-10-07 00:10:02 -0700229 assertEquals("incorrect intent state", IntentState.INSTALLED,
230 service.getIntentState(intent.id()));
Brian O'Connorf3d06162014-10-02 15:54:12 -0700231 }
232 });
233
234 // Make sure that now we have an implicit registration of the compiler
235 // under the intent subclass
236 assertEquals("incorrect compiler", compiler,
237 service.getCompilers().get(TestSubclassIntent.class));
238
239 // Make sure that now we have an implicit registration of the installer
240 // under the intent subclass
241 assertEquals("incorrect installer", installer,
242 service.getInstallers().get(TestSubclassInstallableIntent.class));
243
244 // TODO: discuss whether or if implicit registration should require implicit unregistration
245 // perhaps unregister by compiler or installer itself, rather than by class would be better
246 }
247
248
249 // Fixture to track emitted intent events
Brian O'Connor66630c82014-10-02 21:08:19 -0700250 protected class TestListener implements IntentListener {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700251 final List<IntentEvent> events = new ArrayList<>();
252
253 @Override
254 public void event(IntentEvent event) {
255 events.add(event);
256 }
257 }
258
259 // Controllable compiler
260 private class TestCompiler implements IntentCompiler<TestIntent> {
261 private final boolean fail;
262 private final List<Intent> result;
263
264 TestCompiler(boolean fail) {
265 this.fail = fail;
266 this.result = Collections.emptyList();
267 }
268
269 TestCompiler(Intent... result) {
270 this.fail = false;
271 this.result = Arrays.asList(result);
272 }
273
274 @Override
275 public List<Intent> compile(TestIntent intent) {
276 if (fail) {
277 throw new IntentException("compile failed by design");
278 }
279 List<Intent> compiled = new ArrayList<>(result);
280 return compiled;
281 }
282 }
283
284 // Controllable installer
285 private class TestInstaller implements IntentInstaller<TestInstallableIntent> {
286 private final boolean fail;
287
288 TestInstaller(boolean fail) {
289 this.fail = fail;
290 }
291
292 @Override
293 public void install(TestInstallableIntent intent) {
294 if (fail) {
295 throw new IntentException("install failed by design");
296 }
297 }
298
299 @Override
300 public void uninstall(TestInstallableIntent intent) {
301 if (fail) {
302 throw new IntentException("remove failed by design");
303 }
304 }
305 }
306
307}