blob: 5fce2b6f7e50dc50feb7b4420c1a09920ad3903d [file] [log] [blame]
Sho SHIMIZU15ed4fd2014-08-05 14:40:42 -07001package net.onrc.onos.api.newintent;
2
3import org.junit.After;
4import org.junit.Before;
5import org.junit.Test;
6
7import java.util.ArrayList;
8import java.util.Iterator;
9import java.util.List;
10
11import static net.onrc.onos.api.newintent.IntentState.*;
12import static org.junit.Assert.*;
13
14// TODO: consider make it categorized as integration test when it become
15// slow test or fragile test
16/**
17 * Suite of tests for the intent service contract.
18 */
19public class IntentServiceTest {
20
21 public static final IntentId IID = new IntentId(123);
22
23 protected static final int GRACE_MS = 500; // millis
24
25 protected TestableIntentService service;
26 protected TestListener listener = new TestListener();
27
28 @Before
29 public void setUp() {
30 service = createIntentService();
31 service.addListener(listener);
32 }
33
34 @After
35 public void tearDown() {
36 service.removeListener(listener);
37 }
38
39 /**
40 * Creates a service instance appropriately instrumented for testing.
41 *
42 * @return testable intent service
43 */
44 protected TestableIntentService createIntentService() {
45 return new FakeIntentManager();
46 }
47
48 @Test
49 public void basics() {
50 // Make sure there are no intents
51 assertEquals("incorrect intent count", 0, service.getIntents().size());
52
53 // Register a compiler and an installer both setup for success.
54 service.registerCompiler(TestIntent.class, new TestCompiler(false));
55 service.registerInstaller(TestIntent.class, new TestInstaller(false));
56
57 final Intent intent = new TestIntent(IID);
58 service.submit(intent);
59
60 // Allow a small window of time until the intent is in the expected state
61 TestTools.assertAfter(GRACE_MS, new Runnable() {
62 @Override
63 public void run() {
64 assertEquals("incorrect intent state", INSTALLED,
65 service.getIntentState(intent.getId()));
66 }
67 });
68
69 // Make sure that all expected events have been emitted
70 validateEvents(intent, SUBMITTED, COMPILED, INSTALLED);
71
72 // Make sure there is just one intent (and is ours)
73 assertEquals("incorrect intent count", 1, service.getIntents().size());
74 assertEquals("incorrect intent", intent, service.getIntent(intent.getId()));
75
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() {
86 assertEquals("incorrect intent state", WITHDRAWN,
87 service.getIntentState(intent.getId()));
88 }
89 });
90
91 // Make sure that all expected events have been emitted
92 validateEvents(intent, WITHDRAWING, WITHDRAWN);
93
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());
97// assertNull("intent should not be found", service.getIntent(intent.getId()));
98// assertNull("intent state should not be found", service.getIntentState(intent.getId()));
99 }
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() {
114 assertEquals("incorrect intent state", FAILED,
115 service.getIntentState(intent.getId()));
116 }
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(false));
127 service.registerInstaller(TestIntent.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() {
137 assertEquals("incorrect intent state", FAILED,
138 service.getIntentState(intent.getId()));
139 }
140 });
141
142 // Make sure that all expected events have been emitted
143 validateEvents(intent, SUBMITTED, COMPILED, FAILED);
144 }
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
152 * @param states list of states for which events are expected
153 */
154 protected void validateEvents(Intent intent, IntentState... states) {
155 Iterator<IntentEvent> events = listener.events.iterator();
156 for (IntentState state : states) {
157 IntentEvent event = events.hasNext() ? events.next() : null;
158 if (event == null) {
159 fail("expected event not found: " + state);
160 } else if (intent.equals(event.getIntent())) {
161 assertEquals("incorrect state", state, event.getState());
162 }
163 }
164
165 // Remainder of events should not apply to this intent; make sure.
166 while (events.hasNext()) {
167 assertFalse("unexpected event for intent",
168 intent.equals(events.next().getIntent()));
169 }
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<TestIntent> installer = new TestInstaller(false);
196 service.registerInstaller(TestIntent.class, installer);
197 assertEquals("incorrect installer", installer,
198 service.getInstallers().get(TestIntent.class));
199
200 // Remove the same and make sure that it no longer appears in the map
201 service.unregisterInstaller(TestIntent.class);
202 assertNull("installer should not be registered",
203 service.getInstallers().get(TestIntent.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(false);
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<TestIntent> installer = new TestInstaller(false);
216 service.registerInstaller(TestIntent.class, installer);
217 assertEquals("incorrect installer", installer,
218 service.getInstallers().get(TestIntent.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() {
229 assertEquals("incorrect intent state", INSTALLED,
230 service.getIntentState(intent.getId()));
231 }
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(TestSubclassIntent.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
250 protected class TestListener implements IntentEventListener {
251 final List<IntentEvent> events = new ArrayList<>();
252
253 @Override
254 public void event(IntentEvent event) {
255 events.add(event);
256 }
257 }
258
259 private class TestIntent extends AbstractIntent implements InstallableIntent {
260 TestIntent(IntentId id) {
261 super(id);
262 }
263 }
264
265 private class TestSubclassIntent extends TestIntent {
266 TestSubclassIntent(IntentId id) {
267 super(id);
268 }
269 }
270
271 // Controllable compiler
272 private class TestCompiler implements IntentCompiler<TestIntent> {
273 private final boolean fail;
274
275 TestCompiler(boolean fail) {
276 this.fail = fail;
277 }
278
279 @Override
280 public List<Intent> compile(TestIntent intent) {
281 if (fail) {
282 throw new IntentException("compile failed by design");
283 }
284 List<Intent> compiled = new ArrayList<>(1);
285 compiled.add(intent);
286 return compiled;
287 }
288 }
289
290 // Controllable installer
291 private class TestInstaller implements IntentInstaller<TestIntent> {
292 private final boolean fail;
293
294 TestInstaller(boolean fail) {
295 this.fail = fail;
296 }
297
298 @Override
299 public void install(TestIntent intent) {
300 if (fail) {
301 throw new IntentException("install failed by design");
302 }
303 }
304
305 @Override
306 public void remove(TestIntent intent) {
307 if (fail) {
308 throw new IntentException("remove failed by design");
309 }
310 }
311 }
312
313}