blob: c7682b1ab07dd33b161c7d91de932dc77ebded68 [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
13import static org.onlab.onos.net.intent.IntentState.*;
14import static org.junit.Assert.*;
15
16// TODO: consider make it categorized as integration test when it become
17// slow test or fragile test
18/**
19 * Suite of tests for the intent service contract.
20 */
21public class IntentServiceTest {
22
23 public static final IntentId IID = new IntentId(123);
24 public static final IntentId INSTALLABLE_IID = new IntentId(234);
25
26 protected static final int GRACE_MS = 500; // millis
27
28 protected TestableIntentService service;
29 protected TestListener listener = new TestListener();
30
31 @Before
32 public void setUp() {
33 service = createIntentService();
34 service.addListener(listener);
35 }
36
37 @After
38 public void tearDown() {
39 service.removeListener(listener);
40 }
41
42 /**
43 * Creates a service instance appropriately instrumented for testing.
44 *
45 * @return testable intent service
46 */
47 protected TestableIntentService createIntentService() {
48 return new FakeIntentManager();
49 }
50
51 @Test
52 public void basics() {
53 // Make sure there are no intents
54 assertEquals("incorrect intent count", 0, service.getIntents().size());
55
56 // Register a compiler and an installer both setup for success.
57 service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
58 service.registerInstaller(TestInstallableIntent.class, new TestInstaller(false));
59
60 final Intent intent = new TestIntent(IID);
61 service.submit(intent);
62
63 // Allow a small window of time until the intent is in the expected state
64 TestTools.assertAfter(GRACE_MS, new Runnable() {
65 @Override
66 public void run() {
67 assertEquals("incorrect intent state", INSTALLED,
68 service.getIntentState(intent.getId()));
69 }
70 });
71
72 // Make sure that all expected events have been emitted
73 validateEvents(intent, SUBMITTED, COMPILED, INSTALLED);
74
75 // Make sure there is just one intent (and is ours)
76 assertEquals("incorrect intent count", 1, service.getIntents().size());
77 assertEquals("incorrect intent", intent, service.getIntent(intent.getId()));
78
79 // Reset the listener events
80 listener.events.clear();
81
82 // Now withdraw the intent
83 service.withdraw(intent);
84
85 // Allow a small window of time until the event is in the expected state
86 TestTools.assertAfter(GRACE_MS, new Runnable() {
87 @Override
88 public void run() {
89 assertEquals("incorrect intent state", WITHDRAWN,
90 service.getIntentState(intent.getId()));
91 }
92 });
93
94 // Make sure that all expected events have been emitted
95 validateEvents(intent, WITHDRAWING, WITHDRAWN);
96
97 // TODO: discuss what is the fate of intents after they have been withdrawn
98 // Make sure that the intent is no longer in the system
99// assertEquals("incorrect intent count", 0, service.getIntents().size());
100// assertNull("intent should not be found", service.getIntent(intent.getId()));
101// assertNull("intent state should not be found", service.getIntentState(intent.getId()));
102 }
103
104 @Test
105 public void failedCompilation() {
106 // Register a compiler programmed for success
107 service.registerCompiler(TestIntent.class, new TestCompiler(true));
108
109 // Submit an intent
110 final Intent intent = new TestIntent(IID);
111 service.submit(intent);
112
113 // Allow a small window of time until the intent is in the expected state
114 TestTools.assertAfter(GRACE_MS, new Runnable() {
115 @Override
116 public void run() {
117 assertEquals("incorrect intent state", FAILED,
118 service.getIntentState(intent.getId()));
119 }
120 });
121
122 // Make sure that all expected events have been emitted
123 validateEvents(intent, SUBMITTED, FAILED);
124 }
125
126 @Test
127 public void failedInstallation() {
128 // Register a compiler programmed for success and installer for failure
129 service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
130 service.registerInstaller(TestInstallableIntent.class, new TestInstaller(true));
131
132 // Submit an intent
133 final Intent intent = new TestIntent(IID);
134 service.submit(intent);
135
136 // Allow a small window of time until the intent is in the expected state
137 TestTools.assertAfter(GRACE_MS, new Runnable() {
138 @Override
139 public void run() {
140 assertEquals("incorrect intent state", FAILED,
141 service.getIntentState(intent.getId()));
142 }
143 });
144
145 // Make sure that all expected events have been emitted
146 validateEvents(intent, SUBMITTED, COMPILED, FAILED);
147 }
148
149 /**
150 * Validates that the test event listener has received the following events
151 * for the specified intent. Events received for other intents will not be
152 * considered.
153 *
154 * @param intent intent subject
155 * @param states list of states for which events are expected
156 */
157 protected void validateEvents(Intent intent, IntentState... states) {
158 Iterator<IntentEvent> events = listener.events.iterator();
159 for (IntentState state : states) {
160 IntentEvent event = events.hasNext() ? events.next() : null;
161 if (event == null) {
162 fail("expected event not found: " + state);
163 } else if (intent.equals(event.getIntent())) {
164 assertEquals("incorrect state", state, event.getState());
165 }
166 }
167
168 // Remainder of events should not apply to this intent; make sure.
169 while (events.hasNext()) {
170 assertFalse("unexpected event for intent",
171 intent.equals(events.next().getIntent()));
172 }
173 }
174
175 @Test
176 public void compilerBasics() {
177 // Make sure there are no compilers
178 assertEquals("incorrect compiler count", 0, service.getCompilers().size());
179
180 // Add a compiler and make sure that it appears in the map
181 IntentCompiler<TestIntent> compiler = new TestCompiler(false);
182 service.registerCompiler(TestIntent.class, compiler);
183 assertEquals("incorrect compiler", compiler,
184 service.getCompilers().get(TestIntent.class));
185
186 // Remove the same and make sure that it no longer appears in the map
187 service.unregisterCompiler(TestIntent.class);
188 assertNull("compiler should not be registered",
189 service.getCompilers().get(TestIntent.class));
190 }
191
192 @Test
193 public void installerBasics() {
194 // Make sure there are no installers
195 assertEquals("incorrect installer count", 0, service.getInstallers().size());
196
197 // Add an installer and make sure that it appears in the map
198 IntentInstaller<TestInstallableIntent> installer = new TestInstaller(false);
199 service.registerInstaller(TestInstallableIntent.class, installer);
200 assertEquals("incorrect installer", installer,
201 service.getInstallers().get(TestInstallableIntent.class));
202
203 // Remove the same and make sure that it no longer appears in the map
204 service.unregisterInstaller(TestInstallableIntent.class);
205 assertNull("installer should not be registered",
206 service.getInstallers().get(TestInstallableIntent.class));
207 }
208
209 @Test
210 public void implicitRegistration() {
211 // Add a compiler and make sure that it appears in the map
212 IntentCompiler<TestIntent> compiler = new TestCompiler(new TestSubclassInstallableIntent(INSTALLABLE_IID));
213 service.registerCompiler(TestIntent.class, compiler);
214 assertEquals("incorrect compiler", compiler,
215 service.getCompilers().get(TestIntent.class));
216
217 // Add a installer and make sure that it appears in the map
218 IntentInstaller<TestInstallableIntent> installer = new TestInstaller(false);
219 service.registerInstaller(TestInstallableIntent.class, installer);
220 assertEquals("incorrect installer", installer,
221 service.getInstallers().get(TestInstallableIntent.class));
222
223
224 // Submit an intent which is a subclass of the one we registered
225 final Intent intent = new TestSubclassIntent(IID);
226 service.submit(intent);
227
228 // Allow some time for the intent to be compiled and installed
229 TestTools.assertAfter(GRACE_MS, new Runnable() {
230 @Override
231 public void run() {
232 assertEquals("incorrect intent state", INSTALLED,
233 service.getIntentState(intent.getId()));
234 }
235 });
236
237 // Make sure that now we have an implicit registration of the compiler
238 // under the intent subclass
239 assertEquals("incorrect compiler", compiler,
240 service.getCompilers().get(TestSubclassIntent.class));
241
242 // Make sure that now we have an implicit registration of the installer
243 // under the intent subclass
244 assertEquals("incorrect installer", installer,
245 service.getInstallers().get(TestSubclassInstallableIntent.class));
246
247 // TODO: discuss whether or if implicit registration should require implicit unregistration
248 // perhaps unregister by compiler or installer itself, rather than by class would be better
249 }
250
251
252 // Fixture to track emitted intent events
253 protected class TestListener implements IntentEventListener {
254 final List<IntentEvent> events = new ArrayList<>();
255
256 @Override
257 public void event(IntentEvent event) {
258 events.add(event);
259 }
260 }
261
262 // Controllable compiler
263 private class TestCompiler implements IntentCompiler<TestIntent> {
264 private final boolean fail;
265 private final List<Intent> result;
266
267 TestCompiler(boolean fail) {
268 this.fail = fail;
269 this.result = Collections.emptyList();
270 }
271
272 TestCompiler(Intent... result) {
273 this.fail = false;
274 this.result = Arrays.asList(result);
275 }
276
277 @Override
278 public List<Intent> compile(TestIntent intent) {
279 if (fail) {
280 throw new IntentException("compile failed by design");
281 }
282 List<Intent> compiled = new ArrayList<>(result);
283 return compiled;
284 }
285 }
286
287 // Controllable installer
288 private class TestInstaller implements IntentInstaller<TestInstallableIntent> {
289 private final boolean fail;
290
291 TestInstaller(boolean fail) {
292 this.fail = fail;
293 }
294
295 @Override
296 public void install(TestInstallableIntent intent) {
297 if (fail) {
298 throw new IntentException("install failed by design");
299 }
300 }
301
302 @Override
303 public void uninstall(TestInstallableIntent intent) {
304 if (fail) {
305 throw new IntentException("remove failed by design");
306 }
307 }
308 }
309
310}