blob: 825be86ca34305a59cd6f469826710c654478275 [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
Brian O'Connor66630c82014-10-02 21:08:19 -070054 assertEquals("incorrect intent count", 0, service.getIntentCount());
Brian O'Connorf3d06162014-10-02 15:54:12 -070055
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)
Brian O'Connor66630c82014-10-02 21:08:19 -070076 assertEquals("incorrect intent count", 1, service.getIntentCount());
Brian O'Connorf3d06162014-10-02 15:54:12 -070077
78 // Reset the listener events
79 listener.events.clear();
80
81 // Now withdraw the intent
82 service.withdraw(intent);
83
84 // Allow a small window of time until the event is in the expected state
85 TestTools.assertAfter(GRACE_MS, new Runnable() {
86 @Override
87 public void run() {
88 assertEquals("incorrect intent state", WITHDRAWN,
89 service.getIntentState(intent.getId()));
90 }
91 });
92
93 // Make sure that all expected events have been emitted
94 validateEvents(intent, WITHDRAWING, WITHDRAWN);
95
96 // TODO: discuss what is the fate of intents after they have been withdrawn
97 // Make sure that the intent is no longer in the system
98// assertEquals("incorrect intent count", 0, service.getIntents().size());
99// assertNull("intent should not be found", service.getIntent(intent.getId()));
100// assertNull("intent state should not be found", service.getIntentState(intent.getId()));
101 }
102
103 @Test
104 public void failedCompilation() {
105 // Register a compiler programmed for success
106 service.registerCompiler(TestIntent.class, new TestCompiler(true));
107
108 // Submit an intent
109 final Intent intent = new TestIntent(IID);
110 service.submit(intent);
111
112 // Allow a small window of time until the intent is in the expected state
113 TestTools.assertAfter(GRACE_MS, new Runnable() {
114 @Override
115 public void run() {
116 assertEquals("incorrect intent state", FAILED,
117 service.getIntentState(intent.getId()));
118 }
119 });
120
121 // Make sure that all expected events have been emitted
122 validateEvents(intent, SUBMITTED, FAILED);
123 }
124
125 @Test
126 public void failedInstallation() {
127 // Register a compiler programmed for success and installer for failure
128 service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
129 service.registerInstaller(TestInstallableIntent.class, new TestInstaller(true));
130
131 // Submit an intent
132 final Intent intent = new TestIntent(IID);
133 service.submit(intent);
134
135 // Allow a small window of time until the intent is in the expected state
136 TestTools.assertAfter(GRACE_MS, new Runnable() {
137 @Override
138 public void run() {
139 assertEquals("incorrect intent state", FAILED,
140 service.getIntentState(intent.getId()));
141 }
142 });
143
144 // Make sure that all expected events have been emitted
145 validateEvents(intent, SUBMITTED, COMPILED, FAILED);
146 }
147
148 /**
149 * Validates that the test event listener has received the following events
150 * for the specified intent. Events received for other intents will not be
151 * considered.
152 *
153 * @param intent intent subject
154 * @param states list of states for which events are expected
155 */
156 protected void validateEvents(Intent intent, IntentState... states) {
157 Iterator<IntentEvent> events = listener.events.iterator();
158 for (IntentState state : states) {
159 IntentEvent event = events.hasNext() ? events.next() : null;
160 if (event == null) {
161 fail("expected event not found: " + state);
162 } else if (intent.equals(event.getIntent())) {
163 assertEquals("incorrect state", state, event.getState());
164 }
165 }
166
167 // Remainder of events should not apply to this intent; make sure.
168 while (events.hasNext()) {
169 assertFalse("unexpected event for intent",
170 intent.equals(events.next().getIntent()));
171 }
172 }
173
174 @Test
175 public void compilerBasics() {
176 // Make sure there are no compilers
177 assertEquals("incorrect compiler count", 0, service.getCompilers().size());
178
179 // Add a compiler and make sure that it appears in the map
180 IntentCompiler<TestIntent> compiler = new TestCompiler(false);
181 service.registerCompiler(TestIntent.class, compiler);
182 assertEquals("incorrect compiler", compiler,
183 service.getCompilers().get(TestIntent.class));
184
185 // Remove the same and make sure that it no longer appears in the map
186 service.unregisterCompiler(TestIntent.class);
187 assertNull("compiler should not be registered",
188 service.getCompilers().get(TestIntent.class));
189 }
190
191 @Test
192 public void installerBasics() {
193 // Make sure there are no installers
194 assertEquals("incorrect installer count", 0, service.getInstallers().size());
195
196 // Add an installer and make sure that it appears in the map
197 IntentInstaller<TestInstallableIntent> installer = new TestInstaller(false);
198 service.registerInstaller(TestInstallableIntent.class, installer);
199 assertEquals("incorrect installer", installer,
200 service.getInstallers().get(TestInstallableIntent.class));
201
202 // Remove the same and make sure that it no longer appears in the map
203 service.unregisterInstaller(TestInstallableIntent.class);
204 assertNull("installer should not be registered",
205 service.getInstallers().get(TestInstallableIntent.class));
206 }
207
208 @Test
209 public void implicitRegistration() {
210 // Add a compiler and make sure that it appears in the map
211 IntentCompiler<TestIntent> compiler = new TestCompiler(new TestSubclassInstallableIntent(INSTALLABLE_IID));
212 service.registerCompiler(TestIntent.class, compiler);
213 assertEquals("incorrect compiler", compiler,
214 service.getCompilers().get(TestIntent.class));
215
216 // Add a 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
223 // Submit an intent which is a subclass of the one we registered
224 final Intent intent = new TestSubclassIntent(IID);
225 service.submit(intent);
226
227 // Allow some time for the intent to be compiled and installed
228 TestTools.assertAfter(GRACE_MS, new Runnable() {
229 @Override
230 public void run() {
231 assertEquals("incorrect intent state", INSTALLED,
232 service.getIntentState(intent.getId()));
233 }
234 });
235
236 // Make sure that now we have an implicit registration of the compiler
237 // under the intent subclass
238 assertEquals("incorrect compiler", compiler,
239 service.getCompilers().get(TestSubclassIntent.class));
240
241 // Make sure that now we have an implicit registration of the installer
242 // under the intent subclass
243 assertEquals("incorrect installer", installer,
244 service.getInstallers().get(TestSubclassInstallableIntent.class));
245
246 // TODO: discuss whether or if implicit registration should require implicit unregistration
247 // perhaps unregister by compiler or installer itself, rather than by class would be better
248 }
249
250
251 // Fixture to track emitted intent events
Brian O'Connor66630c82014-10-02 21:08:19 -0700252 protected class TestListener implements IntentListener {
Brian O'Connorf3d06162014-10-02 15:54:12 -0700253 final List<IntentEvent> events = new ArrayList<>();
254
255 @Override
256 public void event(IntentEvent event) {
257 events.add(event);
258 }
259 }
260
261 // Controllable compiler
262 private class TestCompiler implements IntentCompiler<TestIntent> {
263 private final boolean fail;
264 private final List<Intent> result;
265
266 TestCompiler(boolean fail) {
267 this.fail = fail;
268 this.result = Collections.emptyList();
269 }
270
271 TestCompiler(Intent... result) {
272 this.fail = false;
273 this.result = Arrays.asList(result);
274 }
275
276 @Override
277 public List<Intent> compile(TestIntent intent) {
278 if (fail) {
279 throw new IntentException("compile failed by design");
280 }
281 List<Intent> compiled = new ArrayList<>(result);
282 return compiled;
283 }
284 }
285
286 // Controllable installer
287 private class TestInstaller implements IntentInstaller<TestInstallableIntent> {
288 private final boolean fail;
289
290 TestInstaller(boolean fail) {
291 this.fail = fail;
292 }
293
294 @Override
295 public void install(TestInstallableIntent intent) {
296 if (fail) {
297 throw new IntentException("install failed by design");
298 }
299 }
300
301 @Override
302 public void uninstall(TestInstallableIntent intent) {
303 if (fail) {
304 throw new IntentException("remove failed by design");
305 }
306 }
307 }
308
309}