| package net.onrc.onos.api.newintent; |
| |
| import net.onrc.onos.core.newintent.TestIntent; |
| import net.onrc.onos.core.newintent.TestSubclassIntent; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import static net.onrc.onos.api.newintent.IntentState.*; |
| import static org.junit.Assert.*; |
| |
| // TODO: consider make it categorized as integration test when it become |
| // slow test or fragile test |
| /** |
| * Suite of tests for the intent service contract. |
| */ |
| public class IntentServiceTest { |
| |
| public static final IntentId IID = new IntentId(123); |
| |
| protected static final int GRACE_MS = 500; // millis |
| |
| protected TestableIntentService service; |
| protected TestListener listener = new TestListener(); |
| |
| @Before |
| public void setUp() { |
| service = createIntentService(); |
| service.addListener(listener); |
| } |
| |
| @After |
| public void tearDown() { |
| service.removeListener(listener); |
| } |
| |
| /** |
| * Creates a service instance appropriately instrumented for testing. |
| * |
| * @return testable intent service |
| */ |
| protected TestableIntentService createIntentService() { |
| return new FakeIntentManager(); |
| } |
| |
| @Test |
| public void basics() { |
| // Make sure there are no intents |
| assertEquals("incorrect intent count", 0, service.getIntents().size()); |
| |
| // Register a compiler and an installer both setup for success. |
| service.registerCompiler(TestIntent.class, new TestCompiler(false)); |
| service.registerInstaller(TestIntent.class, new TestInstaller(false)); |
| |
| final Intent intent = new TestIntent(IID); |
| service.submit(intent); |
| |
| // Allow a small window of time until the intent is in the expected state |
| TestTools.assertAfter(GRACE_MS, new Runnable() { |
| @Override |
| public void run() { |
| assertEquals("incorrect intent state", INSTALLED, |
| service.getIntentState(intent.getId())); |
| } |
| }); |
| |
| // Make sure that all expected events have been emitted |
| validateEvents(intent, SUBMITTED, COMPILED, INSTALLED); |
| |
| // Make sure there is just one intent (and is ours) |
| assertEquals("incorrect intent count", 1, service.getIntents().size()); |
| assertEquals("incorrect intent", intent, service.getIntent(intent.getId())); |
| |
| // Reset the listener events |
| listener.events.clear(); |
| |
| // Now withdraw the intent |
| service.withdraw(intent); |
| |
| // Allow a small window of time until the event is in the expected state |
| TestTools.assertAfter(GRACE_MS, new Runnable() { |
| @Override |
| public void run() { |
| assertEquals("incorrect intent state", WITHDRAWN, |
| service.getIntentState(intent.getId())); |
| } |
| }); |
| |
| // Make sure that all expected events have been emitted |
| validateEvents(intent, WITHDRAWING, WITHDRAWN); |
| |
| // TODO: discuss what is the fate of intents after they have been withdrawn |
| // Make sure that the intent is no longer in the system |
| // assertEquals("incorrect intent count", 0, service.getIntents().size()); |
| // assertNull("intent should not be found", service.getIntent(intent.getId())); |
| // assertNull("intent state should not be found", service.getIntentState(intent.getId())); |
| } |
| |
| @Test |
| public void failedCompilation() { |
| // Register a compiler programmed for success |
| service.registerCompiler(TestIntent.class, new TestCompiler(true)); |
| |
| // Submit an intent |
| final Intent intent = new TestIntent(IID); |
| service.submit(intent); |
| |
| // Allow a small window of time until the intent is in the expected state |
| TestTools.assertAfter(GRACE_MS, new Runnable() { |
| @Override |
| public void run() { |
| assertEquals("incorrect intent state", FAILED, |
| service.getIntentState(intent.getId())); |
| } |
| }); |
| |
| // Make sure that all expected events have been emitted |
| validateEvents(intent, SUBMITTED, FAILED); |
| } |
| |
| @Test |
| public void failedInstallation() { |
| // Register a compiler programmed for success and installer for failure |
| service.registerCompiler(TestIntent.class, new TestCompiler(false)); |
| service.registerInstaller(TestIntent.class, new TestInstaller(true)); |
| |
| // Submit an intent |
| final Intent intent = new TestIntent(IID); |
| service.submit(intent); |
| |
| // Allow a small window of time until the intent is in the expected state |
| TestTools.assertAfter(GRACE_MS, new Runnable() { |
| @Override |
| public void run() { |
| assertEquals("incorrect intent state", FAILED, |
| service.getIntentState(intent.getId())); |
| } |
| }); |
| |
| // Make sure that all expected events have been emitted |
| validateEvents(intent, SUBMITTED, COMPILED, FAILED); |
| } |
| |
| /** |
| * Validates that the test event listener has received the following events |
| * for the specified intent. Events received for other intents will not be |
| * considered. |
| * |
| * @param intent intent subject |
| * @param states list of states for which events are expected |
| */ |
| protected void validateEvents(Intent intent, IntentState... states) { |
| Iterator<IntentEvent> events = listener.events.iterator(); |
| for (IntentState state : states) { |
| IntentEvent event = events.hasNext() ? events.next() : null; |
| if (event == null) { |
| fail("expected event not found: " + state); |
| } else if (intent.equals(event.getIntent())) { |
| assertEquals("incorrect state", state, event.getState()); |
| } |
| } |
| |
| // Remainder of events should not apply to this intent; make sure. |
| while (events.hasNext()) { |
| assertFalse("unexpected event for intent", |
| intent.equals(events.next().getIntent())); |
| } |
| } |
| |
| @Test |
| public void compilerBasics() { |
| // Make sure there are no compilers |
| assertEquals("incorrect compiler count", 0, service.getCompilers().size()); |
| |
| // Add a compiler and make sure that it appears in the map |
| IntentCompiler<TestIntent> compiler = new TestCompiler(false); |
| service.registerCompiler(TestIntent.class, compiler); |
| assertEquals("incorrect compiler", compiler, |
| service.getCompilers().get(TestIntent.class)); |
| |
| // Remove the same and make sure that it no longer appears in the map |
| service.unregisterCompiler(TestIntent.class); |
| assertNull("compiler should not be registered", |
| service.getCompilers().get(TestIntent.class)); |
| } |
| |
| @Test |
| public void installerBasics() { |
| // Make sure there are no installers |
| assertEquals("incorrect installer count", 0, service.getInstallers().size()); |
| |
| // Add an installer and make sure that it appears in the map |
| IntentInstaller<TestIntent> installer = new TestInstaller(false); |
| service.registerInstaller(TestIntent.class, installer); |
| assertEquals("incorrect installer", installer, |
| service.getInstallers().get(TestIntent.class)); |
| |
| // Remove the same and make sure that it no longer appears in the map |
| service.unregisterInstaller(TestIntent.class); |
| assertNull("installer should not be registered", |
| service.getInstallers().get(TestIntent.class)); |
| } |
| |
| @Test |
| public void implicitRegistration() { |
| // Add a compiler and make sure that it appears in the map |
| IntentCompiler<TestIntent> compiler = new TestCompiler(false); |
| service.registerCompiler(TestIntent.class, compiler); |
| assertEquals("incorrect compiler", compiler, |
| service.getCompilers().get(TestIntent.class)); |
| |
| // Add a installer and make sure that it appears in the map |
| IntentInstaller<TestIntent> installer = new TestInstaller(false); |
| service.registerInstaller(TestIntent.class, installer); |
| assertEquals("incorrect installer", installer, |
| service.getInstallers().get(TestIntent.class)); |
| |
| |
| // Submit an intent which is a subclass of the one we registered |
| final Intent intent = new TestSubclassIntent(IID); |
| service.submit(intent); |
| |
| // Allow some time for the intent to be compiled and installed |
| TestTools.assertAfter(GRACE_MS, new Runnable() { |
| @Override |
| public void run() { |
| assertEquals("incorrect intent state", INSTALLED, |
| service.getIntentState(intent.getId())); |
| } |
| }); |
| |
| // Make sure that now we have an implicit registration of the compiler |
| // under the intent subclass |
| assertEquals("incorrect compiler", compiler, |
| service.getCompilers().get(TestSubclassIntent.class)); |
| |
| // Make sure that now we have an implicit registration of the installer |
| // under the intent subclass |
| assertEquals("incorrect installer", installer, |
| service.getInstallers().get(TestSubclassIntent.class)); |
| |
| // TODO: discuss whether or if implicit registration should require implicit unregistration |
| // perhaps unregister by compiler or installer itself, rather than by class would be better |
| } |
| |
| |
| // Fixture to track emitted intent events |
| protected class TestListener implements IntentEventListener { |
| final List<IntentEvent> events = new ArrayList<>(); |
| |
| @Override |
| public void event(IntentEvent event) { |
| events.add(event); |
| } |
| } |
| |
| // Controllable compiler |
| private class TestCompiler implements IntentCompiler<TestIntent> { |
| private final boolean fail; |
| |
| TestCompiler(boolean fail) { |
| this.fail = fail; |
| } |
| |
| @Override |
| public List<Intent> compile(TestIntent intent) { |
| if (fail) { |
| throw new IntentException("compile failed by design"); |
| } |
| List<Intent> compiled = new ArrayList<>(1); |
| compiled.add(intent); |
| return compiled; |
| } |
| } |
| |
| // Controllable installer |
| private class TestInstaller implements IntentInstaller<TestIntent> { |
| private final boolean fail; |
| |
| TestInstaller(boolean fail) { |
| this.fail = fail; |
| } |
| |
| @Override |
| public void install(TestIntent intent) { |
| if (fail) { |
| throw new IntentException("install failed by design"); |
| } |
| } |
| |
| @Override |
| public void remove(TestIntent intent) { |
| if (fail) { |
| throw new IntentException("remove failed by design"); |
| } |
| } |
| } |
| |
| } |