blob: e4d906b2f2715b88fc171547d9e1df7a35310cab [file] [log] [blame]
/*
* Copyright 2014-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.intent;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onosproject.core.IdGenerator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import static org.junit.Assert.*;
import static org.onosproject.net.intent.IntentEvent.Type.*;
/**
* Suite of tests for the intent service contract.
*/
public class IntentServiceTest {
public static final int IID = 123;
public static final int INSTALLABLE_IID = 234;
protected static final int GRACE_MS = 500; // millis
protected TestableIntentService service;
protected TestListener listener = new TestListener();
protected IdGenerator idGenerator = new MockIdGenerator();
@Before
public void setUp() {
service = createIntentService();
service.addListener(listener);
Intent.bindIdGenerator(idGenerator);
}
@After
public void tearDown() {
service.removeListener(listener);
Intent.unbindIdGenerator(idGenerator);
}
/**
* 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.getIntentCount());
// Register a compiler and an installer both setup for success.
service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
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, () ->
assertEquals("incorrect intent state", IntentState.INSTALLED, service.getIntentState(intent.key())));
// Make sure that all expected events have been emitted
validateEvents(intent, INSTALL_REQ, INSTALLED);
// Make sure there is just one intent (and is ours)
assertEquals("incorrect intent count", 1, service.getIntentCount());
// 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, () ->
assertEquals("incorrect intent state", IntentState.WITHDRAWN, service.getIntentState(intent.key())));
// Make sure that all expected events have been emitted
validateEvents(intent, 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.id()));
// assertNull("intent state should not be found", service.getIntentState(intent.id()));
}
@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, () ->
assertEquals("incorrect intent state", IntentState.FAILED, service.getIntentState(intent.key())));
// Make sure that all expected events have been emitted
validateEvents(intent, INSTALL_REQ, 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 types list of event types for which events are expected
*/
protected void validateEvents(Intent intent, IntentEvent.Type... types) {
Iterator<IntentEvent> events = listener.events.iterator();
for (IntentEvent.Type type : types) {
IntentEvent event = events.hasNext() ? events.next() : null;
if (event == null) {
fail("expected event not found: " + type);
} else if (intent.equals(event.subject())) {
assertEquals("incorrect state", type, event.type());
}
}
// Remainder of events should not apply to this intent; make sure.
while (events.hasNext()) {
assertFalse("unexpected event for intent",
intent.equals(events.next().subject()));
}
}
@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 implicitRegistration() {
// Add a compiler and make sure that it appears in the map
IntentCompiler<TestIntent> compiler = new TestCompiler(new TestSubclassInstallableIntent(INSTALLABLE_IID));
service.registerCompiler(TestIntent.class, compiler);
assertEquals("incorrect compiler", compiler,
service.getCompilers().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, () ->
assertEquals("incorrect intent state", IntentState.INSTALLED, service.getIntentState(intent.key())));
// 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));
// 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 IntentListener {
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;
private final List<Intent> result;
TestCompiler(boolean fail) {
this.fail = fail;
this.result = Collections.emptyList();
}
TestCompiler(Intent... result) {
this.fail = false;
this.result = Arrays.asList(result);
}
@Override
public List<Intent> compile(TestIntent intent, List<Intent> installable) {
if (fail) {
throw new IntentException("compile failed by design");
}
List<Intent> compiled = new ArrayList<>(result);
return compiled;
}
}
}