import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.Futures;
import java.math.BigInteger;
-import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Objects;
import org.junit.ComparisonFailure;
import org.mockito.Mockito;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.genius.infra.Datastore.Configuration;
+import org.opendaylight.genius.infra.TypedReadWriteTransaction;
+import org.opendaylight.genius.infra.TypedWriteTransaction;
import org.opendaylight.genius.mdsalutil.FlowEntity;
+import org.opendaylight.genius.mdsalutil.GroupEntity;
import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
* of it using it's static {@link #newInstance()} method.
*
* @author Michael Vorburger
- * @autor Faseela K
+ * @author Faseela K
*/
public abstract class TestIMdsalApiManager implements IMdsalApiManager {
private static final Logger LOG = LoggerFactory.getLogger(TestIMdsalApiManager.class);
- private List<FlowEntity> flows;
- private List<Group> groups;
+ private Map<InternalFlowKey, FlowEntity> flows;
+ private Map<InternalGroupKey, Group> groups;
+ private Map<InternalBucketKey, Bucket> buckets;
public static TestIMdsalApiManager newInstance() {
- return Mockito.mock(TestIMdsalApiManager.class, realOrException());
+ TestIMdsalApiManager instance = Mockito.mock(TestIMdsalApiManager.class, realOrException());
+ instance.init();
+ return instance;
+ }
+
+ private void init() {
+ this.flows = new HashMap<>();
+ this.groups = new HashMap<>();
+ this.buckets = new HashMap<>();
}
/**
* @return immutable copy of list of flows
*/
public synchronized List<FlowEntity> getFlows() {
- return ImmutableList.copyOf(getOrNewFlows());
- }
-
- private synchronized List<FlowEntity> getOrNewFlows() {
- if (flows == null) {
- flows = new ArrayList<>();
- }
- return flows;
- }
-
- private synchronized List<Group> getOrNewGroups() {
- if (groups == null) {
- groups = new ArrayList<>();
- }
- return groups;
+ return ImmutableList.copyOf(retrieveFlows());
}
public synchronized void assertFlows(Iterable<FlowEntity> expectedFlows) {
checkNonEmptyFlows(expectedFlows);
- List<FlowEntity> nonNullFlows = getOrNewFlows();
+ Collection<FlowEntity> nonNullFlows = retrieveFlows();
if (!Iterables.isEmpty(expectedFlows)) {
assertTrue("No Flows created (bean wiring may be broken?)", !nonNullFlows.isEmpty());
}
private synchronized void checkNonEmptyFlows(Iterable<FlowEntity> expectedFlows) {
if (!Iterables.isEmpty(expectedFlows)) {
- assertTrue("No Flows created (bean wiring may be broken?)", !getOrNewFlows().isEmpty());
+ assertTrue("No Flows created (bean wiring may be broken?)", !retrieveFlows().isEmpty());
}
}
public synchronized void assertFlowsInAnyOrder(Iterable<FlowEntity> expectedFlows) {
checkNonEmptyFlows(expectedFlows);
// TODO Support Iterable <-> List directly within XtendBeanGenerator
- List<FlowEntity> expectedFlowsAsNewArrayList = Lists.newArrayList(expectedFlows);
- List<FlowEntity> sortedFlows = sortFlows(flows);
- List<FlowEntity> sortedExpectedFlows = sortFlows(expectedFlowsAsNewArrayList);
+ List<FlowEntity> sortedFlows = sortFlows(retrieveFlows());
+ Map<InternalFlowKey, FlowEntity> keyedExpectedFlows = new HashMap<>();
+ for (FlowEntity expectedFlow : expectedFlows) {
+ keyedExpectedFlows.put(
+ new InternalFlowKey(expectedFlow.getDpnId(), expectedFlow.getFlowId(), expectedFlow.getTableId()),
+ expectedFlow);
+ }
+ List<FlowEntity> sortedExpectedFlows = sortFlows(keyedExpectedFlows.values());
// FYI: This containsExactlyElementsIn() assumes that FlowEntity, and everything in it,
// has correctly working equals() implementations. assertEqualBeans() does not assume
// is, much, more readable), there are cases when looking more closely
// at the full toString() output of the flows is still useful, so:
// TIP: Use e.g. 'wdiff -n expected.txt actual.txt | colordiff' to compare these!
- LOG.warn("assert failed [order ignored!]; expected flows: {}", sortedExpectedFlows);
- LOG.warn("assert failed [order ignored!]; actual flows : {}", sortedFlows);
+ LOG.warn("assert failed [order ignored!]; expected flows ({}): {}", sortedExpectedFlows.size(),
+ sortedExpectedFlows);
+ LOG.warn("assert failed [order ignored!]; actual flows ({}): {}", sortedFlows.size(), sortedFlows);
+ for (int i = 0; i < sortedExpectedFlows.size() && i < sortedFlows.size(); i++) {
+ if (!sortedExpectedFlows.get(i).equals(sortedFlows.get(i))) {
+ LOG.warn("First mismatch at index {};", i);
+ LOG.warn(" expected {}", sortedExpectedFlows.get(i));
+ LOG.warn(" got {}", sortedFlows.get(i));
+ break;
+ }
+ }
// The point of now also doing assertEqualBeans() is just that its output,
// in case of a comparison failure, is *A LOT* more clearly readable
// than what G Truth (or Hamcrest) can do based on toString.
return sortedFlows;
}
+ private void storeFlow(FlowEntity flowEntity) {
+ flows.put(new InternalFlowKey(flowEntity.getDpnId(), flowEntity.getFlowId(), flowEntity.getTableId()),
+ flowEntity);
+ }
+
+ private Collection<FlowEntity> retrieveFlows() {
+ return flows.values();
+ }
+
+ private void deleteFlow(BigInteger dpId, String flowId, short tableId) {
+ flows.remove(new InternalFlowKey(dpId, flowId, tableId));
+ }
+
+ private void storeGroup(BigInteger dpnId, Group group) {
+ groups.put(new InternalGroupKey(dpnId, group.key().getGroupId().getValue()), group);
+ }
+
+ private Collection<Group> retrieveGroups() {
+ return groups.values();
+ }
+
+ private void deleteGroup(BigInteger dpnId, long groupId) {
+ groups.remove(new InternalGroupKey(dpnId, groupId));
+ }
+
+ private void storeBucket(BigInteger dpnId, long groupId, Bucket bucket) {
+ buckets.put(new InternalBucketKey(dpnId, groupId, bucket.getBucketId().getValue()), bucket);
+ }
+
+ private Collection<Bucket> retrieveBuckets() {
+ return buckets.values();
+ }
+
+ private void deleteBucket(BigInteger dpnId, long groupId, long bucketId) {
+ buckets.remove(new InternalBucketKey(dpnId, groupId, bucketId));
+ }
+
@Override
- public synchronized CheckedFuture<Void, TransactionCommitFailedException> installFlow(FlowEntity flowEntity) {
- getOrNewFlows().add(flowEntity);
- return Futures.immediateCheckedFuture(null);
+ public void addFlow(TypedWriteTransaction<Configuration> tx, FlowEntity flowEntity) {
+ storeFlow(flowEntity);
}
@Override
- public synchronized CheckedFuture<Void, TransactionCommitFailedException> installFlow(BigInteger dpId,
- FlowEntity flowEntity) {
- // TODO should dpId be considered here? how? Copy clone FlowEntity and change its dpId?
- return installFlow(flowEntity);
+ public void addFlow(TypedWriteTransaction<Configuration> tx, BigInteger dpId, Flow flow) {
+ throw new UnsupportedOperationException("addFlow(..., BigInteger, Flow) isn't supported yet");
+ }
+
+ @Override
+ public void removeFlow(TypedReadWriteTransaction<Configuration> tx, BigInteger dpId, Flow flow) {
+ removeFlow(tx, dpId, flow.key(), flow.getTableId());
+ }
+
+ @Override
+ public void removeFlow(TypedReadWriteTransaction<Configuration> tx, FlowEntity flowEntity) {
+ deleteFlow(flowEntity.getDpnId(), flowEntity.getFlowId(), flowEntity.getTableId());
+ }
+
+ @Override
+ public void removeFlow(TypedReadWriteTransaction<Configuration> tx, BigInteger dpId, FlowKey flowKey,
+ short tableId) {
+ deleteFlow(dpId, flowKey.getId().getValue(), tableId);
+ }
+
+ @Override
+ public void removeFlow(TypedReadWriteTransaction<Configuration> tx, BigInteger dpId, String flowId,
+ short tableId) {
+ deleteFlow(dpId, flowId, tableId);
}
@Override
public synchronized CheckedFuture<Void, TransactionCommitFailedException> removeFlow(BigInteger dpnId,
FlowEntity flowEntity) {
- // TODO should dpId be considered here? how? Copy clone FlowEntity and change its dpId?
- getOrNewFlows().remove(flowEntity);
+ deleteFlow(dpnId, flowEntity.getFlowId(), flowEntity.getTableId());
+ return Futures.immediateCheckedFuture(null);
+ }
+
+ @Override
+ public void addGroup(TypedWriteTransaction<Configuration> tx, GroupEntity groupEntity) {
+ storeGroup(groupEntity.getDpnId(), groupEntity.getGroupBuilder().build());
+ }
+
+ @Override
+ public void addGroup(TypedWriteTransaction<Configuration> tx, BigInteger dpId, Group group) {
+ storeGroup(dpId, group);
+ }
+
+ @Override
+ public void removeGroup(TypedReadWriteTransaction<Configuration> tx, BigInteger dpId, Group group) {
+ deleteGroup(dpId, group.getGroupId().getValue());
+ }
+
+ @Override
+ public void removeGroup(TypedReadWriteTransaction<Configuration> tx, BigInteger dpId, long groupId) {
+ deleteGroup(dpId, groupId);
+ }
+
+ @Override
+ public void addBucket(TypedReadWriteTransaction<Configuration> tx, BigInteger dpId, long groupId,
+ Bucket bucket) {
+ storeBucket(dpId, groupId, bucket);
+ }
+
+ @Override
+ public void removeBucket(TypedReadWriteTransaction<Configuration> tx, BigInteger dpId, long groupId,
+ long bucketId) {
+ deleteBucket(dpId, groupId, bucketId);
+ }
+
+ @Override
+ public synchronized CheckedFuture<Void, TransactionCommitFailedException> installFlow(FlowEntity flowEntity) {
+ storeFlow(flowEntity);
return Futures.immediateCheckedFuture(null);
}
+ @Override
+ public synchronized CheckedFuture<Void, TransactionCommitFailedException> installFlow(BigInteger dpId,
+ FlowEntity flowEntity) {
+ // TODO should dpId be considered here? how? Copy clone FlowEntity and change its dpId?
+ return installFlow(flowEntity);
+ }
+
@Override
public synchronized void batchedAddFlow(BigInteger dpId, FlowEntity flowEntity) {
- getOrNewFlows().add(flowEntity);
+ storeFlow(flowEntity);
}
@Override
public synchronized void batchedRemoveFlow(BigInteger dpId, FlowEntity flowEntity) {
- getOrNewFlows().remove(flowEntity);
+ deleteFlow(dpId, flowEntity.getFlowId(), flowEntity.getTableId());
}
@Override
public void syncInstallGroup(BigInteger dpId, Group group, long delayTime) {
- getOrNewGroups().add(group);
+ storeGroup(dpId, group);
}
@Override
public void syncInstallGroup(BigInteger dpId, Group group) {
- getOrNewGroups().add(group);
+ storeGroup(dpId, group);
}
@Override
public void syncRemoveGroup(BigInteger dpId, Group groupEntity) {
- getOrNewGroups().remove(groupEntity);
+ deleteGroup(dpId, groupEntity.getGroupId().getValue());
+ }
+
+ private final class InternalFlowKey {
+ private final BigInteger dpnId;
+ private final String flowId;
+ private final short tableId;
+
+ private InternalFlowKey(BigInteger dpnId, String flowId, short tableId) {
+ this.dpnId = dpnId;
+ this.flowId = flowId;
+ this.tableId = tableId;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ InternalFlowKey that = (InternalFlowKey) obj;
+ return tableId == that.tableId && Objects.equals(dpnId, that.dpnId) && Objects.equals(flowId, that.flowId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(dpnId, flowId, tableId);
+ }
+ }
+
+ private final class InternalGroupKey {
+ private final BigInteger dpnId;
+ private final long groupId;
+
+ private InternalGroupKey(BigInteger dpnId, long groupId) {
+ this.dpnId = dpnId;
+ this.groupId = groupId;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ InternalGroupKey that = (InternalGroupKey) obj;
+ return groupId == that.groupId && Objects.equals(dpnId, that.dpnId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(dpnId, groupId);
+ }
+ }
+
+ private final class InternalBucketKey {
+ private final BigInteger dpnId;
+ private final long groupId;
+ private final long bucketId;
+
+ private InternalBucketKey(BigInteger dpnId, long groupId, long bucketId) {
+ this.dpnId = dpnId;
+ this.groupId = groupId;
+ this.bucketId = bucketId;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ InternalBucketKey that = (InternalBucketKey) obj;
+ return groupId == that.groupId && bucketId == that.bucketId && Objects.equals(dpnId, that.dpnId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(dpnId, groupId, bucketId);
+ }
}
}