diff options
Diffstat (limited to 'framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/IntentJsonMatcher.java')
-rw-r--r-- | framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/IntentJsonMatcher.java | 512 |
1 files changed, 512 insertions, 0 deletions
diff --git a/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/IntentJsonMatcher.java b/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/IntentJsonMatcher.java new file mode 100644 index 00000000..e485a5fa --- /dev/null +++ b/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/IntentJsonMatcher.java @@ -0,0 +1,512 @@ +/* + * Copyright 2015 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.codec.impl; + +import java.util.List; +import java.util.Set; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Link; +import org.onosproject.net.NetworkResource; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.criteria.Criterion; +import org.onosproject.net.flow.instructions.Instruction; +import org.onosproject.net.intent.ConnectivityIntent; +import org.onosproject.net.intent.Constraint; +import org.onosproject.net.intent.HostToHostIntent; +import org.onosproject.net.intent.Intent; +import org.onosproject.net.intent.PointToPointIntent; +import org.onosproject.net.intent.constraint.AnnotationConstraint; +import org.onosproject.net.intent.constraint.BandwidthConstraint; +import org.onosproject.net.intent.constraint.LambdaConstraint; +import org.onosproject.net.intent.constraint.LatencyConstraint; +import org.onosproject.net.intent.constraint.LinkTypeConstraint; +import org.onosproject.net.intent.constraint.ObstacleConstraint; +import org.onosproject.net.intent.constraint.WaypointConstraint; + +import com.fasterxml.jackson.databind.JsonNode; + +/** + * Hamcrest matcher to check that an intent representation in JSON matches + * the actual intent. + */ +public final class IntentJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> { + + private final Intent intent; + + /** + * Constructor is private, use factory method. + * + * @param intentValue the intent object to compare against + */ + private IntentJsonMatcher(Intent intentValue) { + intent = intentValue; + } + + /** + * Matches the JSON representation of a host to host intent. + * + * @param jsonIntent JSON representation of the intent + * @param description Description object used for recording errors + * @return true if the JSON matches the intent, false otherwise + */ + private boolean matchHostToHostIntent(JsonNode jsonIntent, Description description) { + final HostToHostIntent hostToHostIntent = (HostToHostIntent) intent; + + // check host one + final String host1 = hostToHostIntent.one().toString(); + final String jsonHost1 = jsonIntent.get("one").asText(); + if (!host1.equals(jsonHost1)) { + description.appendText("host one was " + jsonHost1); + return false; + } + + // check host 2 + final String host2 = hostToHostIntent.two().toString(); + final String jsonHost2 = jsonIntent.get("two").asText(); + if (!host2.equals(jsonHost2)) { + description.appendText("host two was " + jsonHost2); + return false; + } + return true; + } + + /** + * Matches the JSON representation of a point to point intent. + * + * @param jsonIntent JSON representation of the intent + * @param description Description object used for recording errors + * @return true if the JSON matches the intent, false otherwise + */ + private boolean matchPointToPointIntent(JsonNode jsonIntent, Description description) { + final PointToPointIntent pointToPointIntent = (PointToPointIntent) intent; + + // check ingress connection + final ConnectPoint ingress = pointToPointIntent.ingressPoint(); + final ConnectPointJsonMatcher ingressMatcher = + ConnectPointJsonMatcher.matchesConnectPoint(ingress); + final JsonNode jsonIngress = jsonIntent.get("ingressPoint"); + final boolean ingressMatches = + ingressMatcher.matchesSafely(jsonIngress, description); + + if (!ingressMatches) { + description.appendText("ingress was " + jsonIngress); + return false; + } + + // check egress connection + final ConnectPoint egress = pointToPointIntent.egressPoint(); + final ConnectPointJsonMatcher egressMatcher = + ConnectPointJsonMatcher.matchesConnectPoint(egress); + final JsonNode jsonEgress = jsonIntent.get("egressPoint"); + final boolean egressMatches = + egressMatcher.matchesSafely(jsonEgress, description); + + if (!egressMatches) { + description.appendText("egress was " + jsonEgress); + return false; + } + + return true; + } + + + /** + * Matches a bandwidth constraint against a JSON representation of the + * constraint. + * + * @param bandwidthConstraint constraint object to match + * @param constraintJson JSON representation of the constraint + * @return true if the constraint and JSON match, false otherwise. + */ + private boolean matchBandwidthConstraint(BandwidthConstraint bandwidthConstraint, + JsonNode constraintJson) { + final JsonNode bandwidthJson = constraintJson.get("bandwidth"); + return bandwidthJson != null + && constraintJson.get("bandwidth").asDouble() + == bandwidthConstraint.bandwidth().toDouble(); + } + + /** + * Matches a lamdba constraint against a JSON representation of the + * constraint. + * + * @param lambdaConstraint constraint object to match + * @param constraintJson JSON representation of the constraint + * @return true if the constraint and JSON match, false otherwise. + */ + private boolean matchLambdaConstraint(LambdaConstraint lambdaConstraint, + JsonNode constraintJson) { + final JsonNode lambdaJson = constraintJson.get("lambda"); + return lambdaJson != null + && constraintJson.get("lambda").asInt() + == lambdaConstraint.lambda().toInt(); + } + + /** + * Matches a link type constraint against a JSON representation of the + * constraint. + * + * @param linkTypeConstraint constraint object to match + * @param constraintJson JSON representation of the constraint + * @return true if the constraint and JSON match, false otherwise. + */ + private boolean matchLinkTypeConstraint(LinkTypeConstraint linkTypeConstraint, + JsonNode constraintJson) { + final JsonNode inclusiveJson = constraintJson.get("inclusive"); + final JsonNode typesJson = constraintJson.get("types"); + + if (typesJson.size() != linkTypeConstraint.types().size()) { + return false; + } + + int foundType = 0; + for (Link.Type type : linkTypeConstraint.types()) { + for (int jsonIndex = 0; jsonIndex < typesJson.size(); jsonIndex++) { + if (type.name().equals(typesJson.get(jsonIndex).asText())) { + foundType++; + break; + } + } + } + return (inclusiveJson != null && + inclusiveJson.asBoolean() == linkTypeConstraint.isInclusive()) && + foundType == typesJson.size(); + } + + /** + * Matches an annotation constraint against a JSON representation of the + * constraint. + * + * @param annotationConstraint constraint object to match + * @param constraintJson JSON representation of the constraint + * @return true if the constraint and JSON match, false otherwise. + */ + private boolean matchAnnotationConstraint(AnnotationConstraint annotationConstraint, + JsonNode constraintJson) { + final JsonNode keyJson = constraintJson.get("key"); + final JsonNode thresholdJson = constraintJson.get("threshold"); + return (keyJson != null + && keyJson.asText().equals(annotationConstraint.key())) && + (thresholdJson != null + && thresholdJson.asDouble() == annotationConstraint.threshold()); + } + + /** + * Matches a latency constraint against a JSON representation of the + * constraint. + * + * @param latencyConstraint constraint object to match + * @param constraintJson JSON representation of the constraint + * @return true if the constraint and JSON match, false otherwise. + */ + private boolean matchLatencyConstraint(LatencyConstraint latencyConstraint, + JsonNode constraintJson) { + final JsonNode latencyJson = constraintJson.get("latencyMillis"); + return (latencyJson != null + && latencyJson.asInt() == latencyConstraint.latency().toMillis()); + } + + /** + * Matches an obstacle constraint against a JSON representation of the + * constraint. + * + * @param obstacleConstraint constraint object to match + * @param constraintJson JSON representation of the constraint + * @return true if the constraint and JSON match, false otherwise. + */ + private boolean matchObstacleConstraint(ObstacleConstraint obstacleConstraint, + JsonNode constraintJson) { + final JsonNode obstaclesJson = constraintJson.get("obstacles"); + + if (obstaclesJson.size() != obstacleConstraint.obstacles().size()) { + return false; + } + + for (int obstaclesIndex = 0; obstaclesIndex < obstaclesJson.size(); + obstaclesIndex++) { + boolean obstacleFound = false; + final String obstacleJson = obstaclesJson.get(obstaclesIndex) + .asText(); + for (DeviceId obstacle : obstacleConstraint.obstacles()) { + if (obstacle.toString().equals(obstacleJson)) { + obstacleFound = true; + } + } + if (!obstacleFound) { + return false; + } + } + return true; + } + + /** + * Matches a waypoint constraint against a JSON representation of the + * constraint. + * + * @param waypointConstraint constraint object to match + * @param constraintJson JSON representation of the constraint + * @return true if the constraint and JSON match, false otherwise. + */ + private boolean matchWaypointConstraint(WaypointConstraint waypointConstraint, + JsonNode constraintJson) { + final JsonNode waypointsJson = constraintJson.get("waypoints"); + + if (waypointsJson.size() != waypointConstraint.waypoints().size()) { + return false; + } + + for (int waypointsIndex = 0; waypointsIndex < waypointsJson.size(); + waypointsIndex++) { + boolean waypointFound = false; + final String waypointJson = waypointsJson.get(waypointsIndex) + .asText(); + for (DeviceId waypoint : waypointConstraint.waypoints()) { + if (waypoint.toString().equals(waypointJson)) { + waypointFound = true; + } + } + if (!waypointFound) { + return false; + } + } + return true; + } + + + /** + * Matches a constraint against a JSON representation of the + * constraint. + * + * @param constraint constraint object to match + * @param constraintJson JSON representation of the constraint + * @return true if the constraint and JSON match, false otherwise. + */ + private boolean matchConstraint(Constraint constraint, JsonNode constraintJson) { + final JsonNode typeJson = constraintJson.get("type"); + if (!typeJson.asText().equals(constraint.getClass().getSimpleName())) { + return false; + } + if (constraint instanceof BandwidthConstraint) { + return matchBandwidthConstraint((BandwidthConstraint) constraint, + constraintJson); + } else if (constraint instanceof LambdaConstraint) { + return matchLambdaConstraint((LambdaConstraint) constraint, + constraintJson); + } else if (constraint instanceof LinkTypeConstraint) { + return matchLinkTypeConstraint((LinkTypeConstraint) constraint, + constraintJson); + } else if (constraint instanceof AnnotationConstraint) { + return matchAnnotationConstraint((AnnotationConstraint) constraint, + constraintJson); + } else if (constraint instanceof LatencyConstraint) { + return matchLatencyConstraint((LatencyConstraint) constraint, + constraintJson); + } else if (constraint instanceof ObstacleConstraint) { + return matchObstacleConstraint((ObstacleConstraint) constraint, + constraintJson); + } else if (constraint instanceof WaypointConstraint) { + return matchWaypointConstraint((WaypointConstraint) constraint, + constraintJson); + } + return true; + } + + /** + * Matches the JSON representation of a connectivity intent. Calls the + * matcher for the connectivity intent subtype. + * + * @param jsonIntent JSON representation of the intent + * @param description Description object used for recording errors + * @return true if the JSON matches the intent, false otherwise + */ + private boolean matchConnectivityIntent(JsonNode jsonIntent, Description description) { + final ConnectivityIntent connectivityIntent = (ConnectivityIntent) intent; + + // check selector + final JsonNode jsonSelector = jsonIntent.get("selector"); + final TrafficSelector selector = connectivityIntent.selector(); + final Set<Criterion> criteria = selector.criteria(); + final JsonNode jsonCriteria = jsonSelector.get("criteria"); + if (jsonCriteria.size() != criteria.size()) { + description.appendText("size of criteria array is " + + Integer.toString(jsonCriteria.size())); + return false; + } + + for (Criterion criterion : criteria) { + boolean criterionFound = false; + for (int criterionIndex = 0; criterionIndex < jsonCriteria.size(); criterionIndex++) { + final CriterionJsonMatcher criterionMatcher = + CriterionJsonMatcher.matchesCriterion(criterion); + if (criterionMatcher.matches(jsonCriteria.get(criterionIndex))) { + criterionFound = true; + break; + } + } + if (!criterionFound) { + description.appendText("criterion not found " + criterion.toString()); + return false; + } + } + + // check treatment + final JsonNode jsonTreatment = jsonIntent.get("treatment"); + final TrafficTreatment treatment = connectivityIntent.treatment(); + final List<Instruction> instructions = treatment.immediate(); + final JsonNode jsonInstructions = jsonTreatment.get("instructions"); + if (jsonInstructions.size() != instructions.size()) { + description.appendText("size of instructions array is " + + Integer.toString(jsonInstructions.size())); + return false; + } + + for (Instruction instruction : instructions) { + boolean instructionFound = false; + for (int instructionIndex = 0; instructionIndex < jsonInstructions.size(); instructionIndex++) { + final InstructionJsonMatcher instructionMatcher = + InstructionJsonMatcher.matchesInstruction(instruction); + if (instructionMatcher.matches(jsonInstructions.get(instructionIndex))) { + instructionFound = true; + break; + } + } + if (!instructionFound) { + description.appendText("instruction not found " + instruction.toString()); + return false; + } + } + + // Check constraints + final JsonNode jsonConstraints = jsonIntent.get("constraints"); + if (connectivityIntent.constraints() != null) { + if (connectivityIntent.constraints().size() != jsonConstraints.size()) { + description.appendText("constraints array size was " + + Integer.toString(jsonConstraints.size())); + return false; + } + for (final Constraint constraint : connectivityIntent.constraints()) { + boolean constraintFound = false; + for (int constraintIndex = 0; constraintIndex < jsonConstraints.size(); + constraintIndex++) { + final JsonNode value = jsonConstraints.get(constraintIndex); + if (matchConstraint(constraint, value)) { + constraintFound = true; + } + } + if (!constraintFound) { + final String constraintString = constraint.toString(); + description.appendText("constraint missing " + constraintString); + return false; + } + } + } else if (jsonConstraints.size() != 0) { + description.appendText("constraint array not empty"); + return false; + } + + if (connectivityIntent instanceof HostToHostIntent) { + return matchHostToHostIntent(jsonIntent, description); + } else if (connectivityIntent instanceof PointToPointIntent) { + return matchPointToPointIntent(jsonIntent, description); + } else { + description.appendText("class of connectivity intent is unknown"); + return false; + } + } + + @Override + public boolean matchesSafely(JsonNode jsonIntent, Description description) { + // check id + final String jsonId = jsonIntent.get("id").asText(); + final String id = intent.id().toString(); + if (!jsonId.equals(id)) { + description.appendText("id was " + jsonId); + return false; + } + + // check application id + final JsonNode jsonAppIdNode = jsonIntent.get("appId"); + + final String jsonAppId = jsonAppIdNode.asText(); + final String appId = intent.appId().name(); + if (!jsonAppId.equals(appId)) { + description.appendText("appId was " + jsonAppId); + return false; + } + + // check intent type + final String jsonType = jsonIntent.get("type").asText(); + final String type = intent.getClass().getSimpleName(); + if (!jsonType.equals(type)) { + description.appendText("type was " + jsonType); + return false; + } + + // check resources array + final JsonNode jsonResources = jsonIntent.get("resources"); + if (intent.resources() != null) { + if (intent.resources().size() != jsonResources.size()) { + description.appendText("resources array size was " + + Integer.toString(jsonResources.size())); + return false; + } + for (final NetworkResource resource : intent.resources()) { + boolean resourceFound = false; + final String resourceString = resource.toString(); + for (int resourceIndex = 0; resourceIndex < jsonResources.size(); resourceIndex++) { + final JsonNode value = jsonResources.get(resourceIndex); + if (value.asText().equals(resourceString)) { + resourceFound = true; + } + } + if (!resourceFound) { + description.appendText("resource missing " + resourceString); + return false; + } + } + } else if (jsonResources.size() != 0) { + description.appendText("resources array empty"); + return false; + } + + if (intent instanceof ConnectivityIntent) { + return matchConnectivityIntent(jsonIntent, description); + } else { + description.appendText("class of intent is unknown"); + return false; + } + } + + @Override + public void describeTo(Description description) { + description.appendText(intent.toString()); + } + + /** + * Factory to allocate an intent matcher. + * + * @param intent intent object we are looking for + * @return matcher + */ + public static IntentJsonMatcher matchesIntent(Intent intent) { + return new IntentJsonMatcher(intent); + } +} |