Madan Jampani

Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next

Showing 24 changed files with 1053 additions and 10 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 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>
......@@ -98,16 +111,16 @@
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<!-- Web related -->
......@@ -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>
......
......@@ -6,5 +6,10 @@
<groupId>org.onlab.tools</groupId>
<artifactId>onos-build-conf</artifactId>
<version>1.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
......