Brian O'Connor

Adding Intent API tests

Showing 23 changed files with 1348 additions and 0 deletions
package org.onlab.onos.net.intent;
//TODO is this the right package?
import static com.google.common.base.Preconditions.checkNotNull;
......
package org.onlab.onos.net.intent;
//TODO is this the right package?
import java.util.Objects;
......
package org.onlab.onos.net.intent;
//TODO is this the right package?
/**
* An interface of the class which is assigned to BatchOperation.
......
package org.onlab.onos.net.intent;
//TODO is this the right package?
/**
* A generalized interface for ID generation
*
* {@link #getNewId()} generates a globally unique ID instance on
* each invocation.
*
* @param <T> the type of ID
*/
// TODO: do we need to define a base marker interface for ID,
// then changed the type parameter to <T extends BaseId> something
// like that?
public interface IdGenerator<T> {
/**
* Returns a globally unique ID instance.
*
* @return globally unique ID instance
*/
T getNewId();
}
package org.onlab.onos.net.intent;
import java.util.Set;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.flow.DefaultTrafficSelector;
import org.onlab.onos.net.flow.DefaultTrafficTreatment;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
/**
* Base facilities to test various connectivity tests.
*/
public abstract class ConnectivityIntentTest extends IntentTest {
public static final IntentId IID = new IntentId(123);
public static final TrafficSelector MATCH = (new DefaultTrafficSelector.Builder()).build();
public static final TrafficTreatment NOP = (new DefaultTrafficTreatment.Builder()).build();
public static final ConnectPoint P1 = new ConnectPoint(DeviceId.deviceId("111"), PortNumber.portNumber(0x1));
public static final ConnectPoint P2 = new ConnectPoint(DeviceId.deviceId("222"), PortNumber.portNumber(0x2));
public static final ConnectPoint P3 = new ConnectPoint(DeviceId.deviceId("333"), PortNumber.portNumber(0x3));
public static final Set<ConnectPoint> PS1 = itemSet(new ConnectPoint[]{P1, P3});
public static final Set<ConnectPoint> PS2 = itemSet(new ConnectPoint[]{P2, P3});
}
package org.onlab.onos.net.intent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Fake implementation of the intent service to assist in developing tests
* of the interface contract.
*/
public class FakeIntentManager implements TestableIntentService {
private final Map<IntentId, Intent> intents = new HashMap<>();
private final Map<IntentId, IntentState> intentStates = new HashMap<>();
private final Map<IntentId, List<InstallableIntent>> installables = new HashMap<>();
private final Set<IntentEventListener> listeners = new HashSet<>();
private final Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> compilers = new HashMap<>();
private final Map<Class<? extends InstallableIntent>,
IntentInstaller<? extends InstallableIntent>> installers = new HashMap<>();
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final List<IntentException> exceptions = new ArrayList<>();
@Override
public List<IntentException> getExceptions() {
return exceptions;
}
// Provides an out-of-thread simulation of intent submit life-cycle
private void executeSubmit(final Intent intent) {
registerSubclassCompilerIfNeeded(intent);
executor.execute(new Runnable() {
@Override
public void run() {
try {
List<InstallableIntent> installable = compileIntent(intent);
installIntents(intent, installable);
} catch (IntentException e) {
exceptions.add(e);
}
}
});
}
// Provides an out-of-thread simulation of intent withdraw life-cycle
private void executeWithdraw(final Intent intent) {
executor.execute(new Runnable() {
@Override
public void run() {
try {
List<InstallableIntent> installable = getInstallable(intent.getId());
uninstallIntents(intent, installable);
} catch (IntentException e) {
exceptions.add(e);
}
}
});
}
private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
@SuppressWarnings("unchecked")
IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
if (compiler == null) {
throw new IntentException("no compiler for class " + intent.getClass());
}
return compiler;
}
private <T extends InstallableIntent> IntentInstaller<T> getInstaller(T intent) {
@SuppressWarnings("unchecked")
IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
if (installer == null) {
throw new IntentException("no installer for class " + intent.getClass());
}
return installer;
}
private <T extends Intent> List<InstallableIntent> compileIntent(T intent) {
try {
// For the fake, we compile using a single level pass
List<InstallableIntent> installable = new ArrayList<>();
for (Intent compiled : getCompiler(intent).compile(intent)) {
installable.add((InstallableIntent) compiled);
}
setState(intent, IntentState.COMPILED);
return installable;
} catch (IntentException e) {
setState(intent, IntentState.FAILED);
throw e;
}
}
private void installIntents(Intent intent, List<InstallableIntent> installable) {
try {
for (InstallableIntent ii : installable) {
registerSubclassInstallerIfNeeded(ii);
getInstaller(ii).install(ii);
}
setState(intent, IntentState.INSTALLED);
putInstallable(intent.getId(), installable);
} catch (IntentException e) {
setState(intent, IntentState.FAILED);
throw e;
}
}
private void uninstallIntents(Intent intent, List<InstallableIntent> installable) {
try {
for (InstallableIntent ii : installable) {
getInstaller(ii).uninstall(ii);
}
setState(intent, IntentState.WITHDRAWN);
removeInstallable(intent.getId());
} catch (IntentException e) {
setState(intent, IntentState.FAILED);
throw e;
}
}
// Sets the internal state for the given intent and dispatches an event
private void setState(Intent intent, IntentState state) {
IntentState previous = intentStates.get(intent.getId());
intentStates.put(intent.getId(), state);
dispatch(new IntentEvent(intent, state, previous, System.currentTimeMillis()));
}
private void putInstallable(IntentId id, List<InstallableIntent> installable) {
installables.put(id, installable);
}
private void removeInstallable(IntentId id) {
installables.remove(id);
}
private List<InstallableIntent> getInstallable(IntentId id) {
List<InstallableIntent> installable = installables.get(id);
if (installable != null) {
return installable;
} else {
return Collections.emptyList();
}
}
@Override
public void submit(Intent intent) {
intents.put(intent.getId(), intent);
setState(intent, IntentState.SUBMITTED);
executeSubmit(intent);
}
@Override
public void withdraw(Intent intent) {
intents.remove(intent.getId());
setState(intent, IntentState.WITHDRAWING);
executeWithdraw(intent);
}
@Override
public void execute(IntentOperations operations) {
// TODO: implement later
}
@Override
public Set<Intent> getIntents() {
return Collections.unmodifiableSet(new HashSet<>(intents.values()));
}
@Override
public Intent getIntent(IntentId id) {
return intents.get(id);
}
@Override
public IntentState getIntentState(IntentId id) {
return intentStates.get(id);
}
@Override
public void addListener(IntentEventListener listener) {
listeners.add(listener);
}
@Override
public void removeListener(IntentEventListener listener) {
listeners.remove(listener);
}
private void dispatch(IntentEvent event) {
for (IntentEventListener listener : listeners) {
listener.event(event);
}
}
@Override
public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
compilers.put(cls, compiler);
}
@Override
public <T extends Intent> void unregisterCompiler(Class<T> cls) {
compilers.remove(cls);
}
@Override
public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
return Collections.unmodifiableMap(compilers);
}
@Override
public <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
installers.put(cls, installer);
}
@Override
public <T extends InstallableIntent> void unregisterInstaller(Class<T> cls) {
installers.remove(cls);
}
@Override
public Map<Class<? extends InstallableIntent>,
IntentInstaller<? extends InstallableIntent>> getInstallers() {
return Collections.unmodifiableMap(installers);
}
private void registerSubclassCompilerIfNeeded(Intent intent) {
if (!compilers.containsKey(intent.getClass())) {
Class<?> cls = intent.getClass();
while (cls != Object.class) {
// As long as we're within the Intent class descendants
if (Intent.class.isAssignableFrom(cls)) {
IntentCompiler<?> compiler = compilers.get(cls);
if (compiler != null) {
compilers.put(intent.getClass(), compiler);
return;
}
}
cls = cls.getSuperclass();
}
}
}
private void registerSubclassInstallerIfNeeded(InstallableIntent intent) {
if (!installers.containsKey(intent.getClass())) {
Class<?> cls = intent.getClass();
while (cls != Object.class) {
// As long as we're within the InstallableIntent class descendants
if (InstallableIntent.class.isAssignableFrom(cls)) {
IntentInstaller<?> installer = installers.get(cls);
if (installer != null) {
installers.put(intent.getClass(), installer);
return;
}
}
cls = cls.getSuperclass();
}
}
}
}
package org.onlab.onos.net.intent;
//TODO is this the right package?
import org.hamcrest.Description;
import org.hamcrest.StringDescription;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* Hamcrest style class for verifying that a class follows the
* accepted rules for immutable classes.
*
* The rules that are enforced for immutable classes:
* - the class must be declared final
* - all data members of the class must be declared private and final
* - the class must not define any setter methods
*/
public class ImmutableClassChecker {
private String failureReason = "";
/**
* Method to determine if a given class is a properly specified
* immutable class.
*
* @param clazz the class to check
* @return true if the given class is a properly specified immutable class.
*/
private boolean isImmutableClass(Class<?> clazz) {
// class must be declared final
if (!Modifier.isFinal(clazz.getModifiers())) {
failureReason = "a class that is not final";
return false;
}
// class must have only final and private data members
for (final Field field : clazz.getDeclaredFields()) {
if (field.getName().startsWith("__cobertura")) {
// cobertura sticks these fields into classes - ignore them
continue;
}
if (!Modifier.isFinal(field.getModifiers())) {
failureReason = "a field named '" + field.getName() +
"' that is not final";
return false;
}
if (!Modifier.isPrivate(field.getModifiers())) {
//
// NOTE: We relax the recommended rules for defining immutable
// objects and allow "static final" fields that are not
// private. The "final" check was already done above so we
// don't repeat it here.
//
if (!Modifier.isStatic(field.getModifiers())) {
failureReason = "a field named '" + field.getName() +
"' that is not private and is not static";
return false;
}
}
}
// class must not define any setters
for (final Method method : clazz.getMethods()) {
if (method.getDeclaringClass().equals(clazz)) {
if (method.getName().startsWith("set")) {
failureReason = "a class with a setter named '" + method.getName() + "'";
return false;
}
}
}
return true;
}
/**
* Describe why an error was reported. Uses Hamcrest style Description
* interfaces.
*
* @param description the Description object to use for reporting the
* mismatch
*/
public void describeMismatch(Description description) {
description.appendText(failureReason);
}
/**
* Describe the source object that caused an error, using a Hamcrest
* Matcher style interface. In this case, it always returns
* that we are looking for a properly defined utility class.
*
* @param description the Description object to use to report the "to"
* object
*/
public void describeTo(Description description) {
description.appendText("a properly defined immutable class");
}
/**
* Assert that the given class adheres to the utility class rules.
*
* @param clazz the class to check
*
* @throws java.lang.AssertionError if the class is not a valid
* utility class
*/
public static void assertThatClassIsImmutable(Class<?> clazz) {
final ImmutableClassChecker checker = new ImmutableClassChecker();
if (!checker.isImmutableClass(clazz)) {
final Description toDescription = new StringDescription();
final Description mismatchDescription = new StringDescription();
checker.describeTo(toDescription);
checker.describeMismatch(mismatchDescription);
final String reason =
"\n" +
"Expected: is \"" + toDescription.toString() + "\"\n" +
" but : was \"" + mismatchDescription.toString() + "\"";
throw new AssertionError(reason);
}
}
}
package org.onlab.onos.net.intent;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Test of the intent exception.
*/
public class IntentExceptionTest {
@Test
public void basics() {
validate(new IntentException(), null, null);
validate(new IntentException("foo"), "foo", null);
Throwable cause = new NullPointerException("bar");
validate(new IntentException("foo", cause), "foo", cause);
}
/**
* Validates that the specified exception has the correct message and cause.
*
* @param e exception to test
* @param message expected message
* @param cause expected cause
*/
protected void validate(RuntimeException e, String message, Throwable cause) {
assertEquals("incorrect message", message, e.getMessage());
assertEquals("incorrect cause", cause, e.getCause());
}
}
package org.onlab.onos.net.intent;
/**
* This interface is for generator of IntentId. It is defined only for
* testing purpose to keep type safety on mock creation.
*
* <p>
* {@link #getNewId()} generates a globally unique {@link IntentId} instance
* on each invocation. Application developers should not generate IntentId
* by themselves. Instead use an implementation of this interface.
* </p>
*/
public interface IntentIdGenerator extends IdGenerator<IntentId> {
}
package org.onlab.onos.net.intent;
import org.junit.Test;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
/**
* This class tests the immutability, equality, and non-equality of
* {@link IntentId}.
*/
public class IntentIdTest {
/**
* Tests the immutability of {@link IntentId}.
*/
@Test
public void intentIdFollowsGuidelineForImmutableObject() {
ImmutableClassChecker.assertThatClassIsImmutable(IntentId.class);
}
/**
* Tests equality of {@link IntentId}.
*/
@Test
public void testEquality() {
IntentId id1 = new IntentId(1L);
IntentId id2 = new IntentId(1L);
assertThat(id1, is(id2));
}
/**
* Tests non-equality of {@link IntentId}.
*/
@Test
public void testNonEquality() {
IntentId id1 = new IntentId(1L);
IntentId id2 = new IntentId(2L);
assertThat(id1, is(not(id2)));
}
@Test
public void valueOf() {
IntentId id = new IntentId(12345);
assertEquals("incorrect valueOf", id, IntentId.valueOf("12345"));
}
@Test
public void valueOfHex() {
IntentId id = new IntentId(0xdeadbeefL);
assertEquals("incorrect valueOf", id, IntentId.valueOf(id.toString()));
}
}
package org.onlab.onos.net.intent;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import static org.onlab.onos.net.intent.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);
public static final IntentId INSTALLABLE_IID = new IntentId(234);
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(new TestInstallableIntent(INSTALLABLE_IID)));
service.registerInstaller(TestInstallableIntent.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(new TestInstallableIntent(INSTALLABLE_IID)));
service.registerInstaller(TestInstallableIntent.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<TestInstallableIntent> installer = new TestInstaller(false);
service.registerInstaller(TestInstallableIntent.class, installer);
assertEquals("incorrect installer", installer,
service.getInstallers().get(TestInstallableIntent.class));
// Remove the same and make sure that it no longer appears in the map
service.unregisterInstaller(TestInstallableIntent.class);
assertNull("installer should not be registered",
service.getInstallers().get(TestInstallableIntent.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));
// Add a installer and make sure that it appears in the map
IntentInstaller<TestInstallableIntent> installer = new TestInstaller(false);
service.registerInstaller(TestInstallableIntent.class, installer);
assertEquals("incorrect installer", installer,
service.getInstallers().get(TestInstallableIntent.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(TestSubclassInstallableIntent.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;
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) {
if (fail) {
throw new IntentException("compile failed by design");
}
List<Intent> compiled = new ArrayList<>(result);
return compiled;
}
}
// Controllable installer
private class TestInstaller implements IntentInstaller<TestInstallableIntent> {
private final boolean fail;
TestInstaller(boolean fail) {
this.fail = fail;
}
@Override
public void install(TestInstallableIntent intent) {
if (fail) {
throw new IntentException("install failed by design");
}
}
@Override
public void uninstall(TestInstallableIntent intent) {
if (fail) {
throw new IntentException("remove failed by design");
}
}
}
}
package org.onlab.onos.net.intent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.junit.Test;
/**
* Base facilities to test various intent tests.
*/
public abstract class IntentTest {
/**
* Produces a set of items from the supplied items.
*
* @param items items to be placed in set
* @param <T> item type
* @return set of items
*/
protected static <T> Set<T> itemSet(T[] items) {
return new HashSet<>(Arrays.asList(items));
}
@Test
public void equalsAndHashCode() {
Intent one = createOne();
Intent like = createOne();
Intent another = createAnother();
assertTrue("should be equal", one.equals(like));
assertEquals("incorrect hashCode", one.hashCode(), like.hashCode());
assertFalse("should not be equal", one.equals(another));
assertFalse("should not be equal", one.equals(null));
assertFalse("should not be equal", one.equals("foo"));
}
@Test
public void testToString() {
Intent one = createOne();
Intent like = createOne();
assertEquals("incorrect toString", one.toString(), like.toString());
}
/**
* Creates a new intent, but always a like intent, i.e. all instances will
* be equal, but should not be the same.
*
* @return intent
*/
protected abstract Intent createOne();
/**
* Creates another intent, not equals to the one created by
* {@link #createOne()} and with a different hash code.
*
* @return another intent
*/
protected abstract Intent createAnother();
}
package org.onlab.onos.net.intent;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Suite of tests of the multi-to-single point intent descriptor.
*/
public class MultiPointToSinglePointIntentTest extends ConnectivityIntentTest {
@Test
public void basics() {
MultiPointToSinglePointIntent intent = createOne();
assertEquals("incorrect id", IID, intent.getId());
assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
assertEquals("incorrect ingress", PS1, intent.getIngressPorts());
assertEquals("incorrect egress", P2, intent.getEgressPort());
}
@Override
protected MultiPointToSinglePointIntent createOne() {
return new MultiPointToSinglePointIntent(IID, MATCH, NOP, PS1, P2);
}
@Override
protected MultiPointToSinglePointIntent createAnother() {
return new MultiPointToSinglePointIntent(IID, MATCH, NOP, PS2, P1);
}
}
package org.onlab.onos.net.intent;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.onlab.onos.net.NetTestTools;
import org.onlab.onos.net.Path;
public class PathIntentTest extends ConnectivityIntentTest {
// 111:11 --> 222:22
private static final Path PATH1 = NetTestTools.createPath("111", "222");
// 111:11 --> 333:33
private static final Path PATH2 = NetTestTools.createPath("222", "333");
@Test
public void basics() {
PathIntent intent = createOne();
assertEquals("incorrect id", IID, intent.getId());
assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
assertEquals("incorrect action", NOP, intent.getTrafficTreatment());
assertEquals("incorrect ingress", P1, intent.getIngressPort());
assertEquals("incorrect egress", P2, intent.getEgressPort());
assertEquals("incorrect path", PATH1, intent.getPath());
}
@Override
protected PathIntent createOne() {
return new PathIntent(IID, MATCH, NOP, P1, P2, PATH1);
}
@Override
protected PathIntent createAnother() {
return new PathIntent(IID, MATCH, NOP, P1, P3, PATH2);
}
}
package org.onlab.onos.net.intent;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Suite of tests of the point-to-point intent descriptor.
*/
public class PointToPointIntentTest extends ConnectivityIntentTest {
@Test
public void basics() {
PointToPointIntent intent = createOne();
assertEquals("incorrect id", IID, intent.getId());
assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
assertEquals("incorrect ingress", P1, intent.getIngressPort());
assertEquals("incorrect egress", P2, intent.getEgressPort());
}
@Override
protected PointToPointIntent createOne() {
return new PointToPointIntent(IID, MATCH, NOP, P1, P2);
}
@Override
protected PointToPointIntent createAnother() {
return new PointToPointIntent(IID, MATCH, NOP, P2, P1);
}
}
package org.onlab.onos.net.intent;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Suite of tests of the single-to-multi point intent descriptor.
*/
public class SinglePointToMultiPointIntentTest extends ConnectivityIntentTest {
@Test
public void basics() {
SinglePointToMultiPointIntent intent = createOne();
assertEquals("incorrect id", IID, intent.getId());
assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
assertEquals("incorrect ingress", P1, intent.getIngressPort());
assertEquals("incorrect egress", PS2, intent.getEgressPorts());
}
@Override
protected SinglePointToMultiPointIntent createOne() {
return new SinglePointToMultiPointIntent(IID, MATCH, NOP, P1, PS2);
}
@Override
protected SinglePointToMultiPointIntent createAnother() {
return new SinglePointToMultiPointIntent(IID, MATCH, NOP, P2, PS1);
}
}
package org.onlab.onos.net.intent;
//TODO is this the right package?
/**
* An installable intent used in the unit test.
*
* FIXME: we don't want to expose this class publicly, but the current Kryo
* serialization mechanism does not allow this class to be private and placed
* on testing directory.
*/
public class TestInstallableIntent extends AbstractIntent implements InstallableIntent {
/**
* Constructs an instance with the specified intent ID.
*
* @param id intent ID
*/
public TestInstallableIntent(IntentId id) {
super(id);
}
/**
* Constructor for serializer.
*/
protected TestInstallableIntent() {
super();
}
}
package org.onlab.onos.net.intent;
//TODO is this the right package?
/**
* An intent used in the unit test.
*
* FIXME: we don't want to expose this class publicly, but the current Kryo
* serialization mechanism does not allow this class to be private and placed
* on testing directory.
*/
public class TestIntent extends AbstractIntent {
/**
* Constructs an instance with the specified intent ID.
*
* @param id intent ID
*/
public TestIntent(IntentId id) {
super(id);
}
/**
* Constructor for serializer.
*/
protected TestIntent() {
super();
}
}
package org.onlab.onos.net.intent;
//TODO is this the right package?
/**
* An intent used in the unit test.
*
* FIXME: we don't want to expose this class publicly, but the current Kryo
* serialization mechanism does not allow this class to be private and placed
* on testing directory.
*/
public class TestSubclassInstallableIntent extends TestInstallableIntent implements InstallableIntent {
/**
* Constructs an instance with the specified intent ID.
*
* @param id intent ID
*/
public TestSubclassInstallableIntent(IntentId id) {
super(id);
}
/**
* Constructor for serializer.
*/
protected TestSubclassInstallableIntent() {
super();
}
}
package org.onlab.onos.net.intent;
//TODO is this the right package?
/**
* An intent used in the unit test.
*
* FIXME: we don't want to expose this class publicly, but the current Kryo
* serialization mechanism does not allow this class to be private and placed
* on testing directory.
*/
public class TestSubclassIntent extends TestIntent {
/**
* Constructs an instance with the specified intent ID.
*
* @param id intent ID
*/
public TestSubclassIntent(IntentId id) {
super(id);
}
/**
* Constructor for serializer.
*/
protected TestSubclassIntent() {
super();
}
}
package org.onlab.onos.net.intent;
import static org.junit.Assert.fail;
/**
* Set of test tools.
*/
public final class TestTools {
// Disallow construction
private TestTools() {
}
/**
* Utility method to pause the current thread for the specified number of
* milliseconds.
*
* @param ms number of milliseconds to pause
*/
public static void delay(int ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
fail("unexpected interrupt");
}
}
/**
* Periodically runs the given runnable, which should contain a series of
* test assertions until all the assertions succeed, in which case it will
* return, or until the the time expires, in which case it will throw the
* first failed assertion error.
*
* @param start start time, in millis since start of epoch from which the
* duration will be measured
* @param delay initial delay (in milliseconds) before the first assertion
* attempt
* @param step delay (in milliseconds) between successive assertion
* attempts
* @param duration number of milliseconds beyond the given start time,
* after which the failed assertions will be propagated and allowed
* to fail the test
* @param assertions runnable housing the test assertions
*/
public static void assertAfter(long start, int delay, int step,
int duration, Runnable assertions) {
delay(delay);
while (true) {
try {
assertions.run();
break;
} catch (AssertionError e) {
if (System.currentTimeMillis() - start > duration) {
throw e;
}
}
delay(step);
}
}
/**
* Periodically runs the given runnable, which should contain a series of
* test assertions until all the assertions succeed, in which case it will
* return, or until the the time expires, in which case it will throw the
* first failed assertion error.
* <p>
* The start of the period is the current time.
*
* @param delay initial delay (in milliseconds) before the first assertion
* attempt
* @param step delay (in milliseconds) between successive assertion
* attempts
* @param duration number of milliseconds beyond the current time time,
* after which the failed assertions will be propagated and allowed
* to fail the test
* @param assertions runnable housing the test assertions
*/
public static void assertAfter(int delay, int step, int duration,
Runnable assertions) {
assertAfter(System.currentTimeMillis(), delay, step, duration,
assertions);
}
/**
* Periodically runs the given runnable, which should contain a series of
* test assertions until all the assertions succeed, in which case it will
* return, or until the the time expires, in which case it will throw the
* first failed assertion error.
* <p>
* The start of the period is the current time and the first assertion
* attempt is delayed by the value of {@code step} parameter.
*
* @param step delay (in milliseconds) between successive assertion
* attempts
* @param duration number of milliseconds beyond the current time time,
* after which the failed assertions will be propagated and allowed
* to fail the test
* @param assertions runnable housing the test assertions
*/
public static void assertAfter(int step, int duration,
Runnable assertions) {
assertAfter(step, step, duration, assertions);
}
/**
* Periodically runs the given runnable, which should contain a series of
* test assertions until all the assertions succeed, in which case it will
* return, or until the the time expires, in which case it will throw the
* first failed assertion error.
* <p>
* The start of the period is the current time and each successive
* assertion attempt is delayed by at least 10 milliseconds unless the
* {@code duration} is less than that, in which case the one and only
* assertion is made after that delay.
*
* @param duration number of milliseconds beyond the current time,
* after which the failed assertions will be propagated and allowed
* to fail the test
* @param assertions runnable housing the test assertions
*/
public static void assertAfter(int duration, Runnable assertions) {
int step = Math.min(duration, Math.max(10, duration / 10));
assertAfter(step, duration, assertions);
}
}
package org.onlab.onos.net.intent;
import java.util.List;
/**
* Abstraction of an extensible intent service enabled for unit tests.
*/
public interface TestableIntentService extends IntentService, IntentExtensionService {
List<IntentException> getExceptions();
}
......@@ -48,6 +48,19 @@
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.6</version>
......@@ -244,6 +257,14 @@
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
</dependency>
......@@ -320,6 +341,35 @@
</plugin>
<!-- TODO: add findbugs plugin for static code analysis; for explicit invocation only -->
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.jacoco</groupId>
<artifactId>
jacoco-maven-plugin
</artifactId>
<versionRange>
[0.7.1.201405082137,)
</versionRange>
<goals>
<goal>prepare-agent</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore></ignore>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
......