P4Runtime unit tests for api, ctl, model modules
Change-Id: Iac1b1ef6e274c355ce3a26cffbd8adcb39694f69
diff --git a/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4InfoParserTest.java b/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4InfoParserTest.java
new file mode 100644
index 0000000..452a7a7
--- /dev/null
+++ b/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4InfoParserTest.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.p4runtime.model;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.testing.EqualsTester;
+import com.google.protobuf.ExtensionRegistry;
+import com.google.protobuf.TextFormat;
+import org.hamcrest.collection.IsIterableContainingInAnyOrder;
+import org.hamcrest.collection.IsIterableContainingInOrder;
+import org.junit.Test;
+import org.onosproject.net.pi.model.PiActionId;
+import org.onosproject.net.pi.model.PiActionModel;
+import org.onosproject.net.pi.model.PiActionParamId;
+import org.onosproject.net.pi.model.PiActionParamModel;
+import org.onosproject.net.pi.model.PiActionProfileId;
+import org.onosproject.net.pi.model.PiActionProfileModel;
+import org.onosproject.net.pi.model.PiCounterId;
+import org.onosproject.net.pi.model.PiCounterModel;
+import org.onosproject.net.pi.model.PiMatchFieldId;
+import org.onosproject.net.pi.model.PiMatchFieldModel;
+import org.onosproject.net.pi.model.PiMatchType;
+import org.onosproject.net.pi.model.PiMeterModel;
+import org.onosproject.net.pi.model.PiPacketOperationModel;
+import org.onosproject.net.pi.model.PiPacketOperationType;
+import org.onosproject.net.pi.model.PiPipelineModel;
+import org.onosproject.net.pi.model.PiTableId;
+import org.onosproject.net.pi.model.PiTableModel;
+import p4.config.P4InfoOuterClass.Table;
+import p4.config.P4InfoOuterClass.P4Info;
+import p4.config.P4InfoOuterClass.MatchField;
+import p4.config.P4InfoOuterClass.ActionRef;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.*;
+import static org.hamcrest.core.IsNull.notNullValue;
+
+/**
+ * Test for P4Info Parser.
+ */
+public class P4InfoParserTest {
+ private static final String PATH = "basic.p4info";
+
+ private final URL p4InfoUrl = P4InfoParserTest.class.getResource(PATH);
+
+ private static final Long DEFAULT_MAX_TABLE_SIZE = 1024L;
+ private static final Long DEFAULT_MAX_ACTION_PROFILE_SIZE = 64L;
+
+ public P4InfoParserTest() throws MalformedURLException { }
+
+ /**
+ * Tests parse method.
+ * @throws Exception if equality group objects dose not match as expected
+ */
+ @Test
+ public void testParse() throws Exception {
+ // Generate two PiPipelineModels from p4Info file
+ PiPipelineModel model = P4InfoParser.parse(p4InfoUrl);
+ PiPipelineModel model2 = P4InfoParser.parse(p4InfoUrl);
+
+ // Check equality
+ new EqualsTester().addEqualityGroup(model, model2).testEquals();
+
+ // Generate a P4Info object from the file
+ final P4Info p4info;
+ try {
+ p4info = getP4InfoMessage(p4InfoUrl);
+ } catch (IOException e) {
+ throw new P4InfoParserException("Unable to parse protobuf " + p4InfoUrl.toString(), e);
+ }
+
+ List<Table> tableMsgs = p4info.getTablesList();
+ PiTableId table0Id = PiTableId.of(tableMsgs.get(0).getPreamble().getName());
+ PiTableId wcmpTableId = PiTableId.of(tableMsgs.get(1).getPreamble().getName());
+
+ //parse tables
+ PiTableModel table0Model = model.table(table0Id).orElse(null);
+ PiTableModel wcmpTableModel = model.table(wcmpTableId).orElse(null);
+ PiTableModel table0Model2 = model2.table(table0Id).orElse(null);
+ PiTableModel wcmpTableModel2 = model2.table(wcmpTableId).orElse(null);
+
+ new EqualsTester().addEqualityGroup(table0Model, table0Model2)
+ .addEqualityGroup(wcmpTableModel, wcmpTableModel2).testEquals();
+
+ // Check existence
+ assertThat("model parsed value is null", table0Model, notNullValue());
+ assertThat("model parsed value is null", wcmpTableModel, notNullValue());
+ assertThat("Incorrect size for table0 size", table0Model.maxSize(), is(equalTo(DEFAULT_MAX_TABLE_SIZE)));
+ assertThat("Incorrect size for wcmp_table size", wcmpTableModel.maxSize(), is(equalTo(DEFAULT_MAX_TABLE_SIZE)));
+
+ // Check matchFields
+ List<MatchField> matchFieldList = tableMsgs.get(0).getMatchFieldsList();
+ List<PiMatchFieldModel> piMatchFieldList = new ArrayList<>();
+
+ for (MatchField matchFieldIter : matchFieldList) {
+ int matchTypeNumber = matchFieldIter.getMatchType().getNumber();
+ PiMatchType piMatchType = PiMatchType.VALID;
+ switch (matchTypeNumber) {
+ case 1: piMatchType = PiMatchType.VALID; break;
+ case 2: piMatchType = PiMatchType.EXACT; break;
+ case 3: piMatchType = PiMatchType.LPM; break;
+ case 4: piMatchType = piMatchType.TERNARY; break;
+ case 5: piMatchType = piMatchType.RANGE; break;
+ default: piMatchType = PiMatchType.VALID; break;
+ }
+ piMatchFieldList.add(new P4MatchFieldModel(PiMatchFieldId.of(matchFieldIter.getName()),
+ matchFieldIter.getBitwidth(), piMatchType));
+ }
+ // Check MatchFields size
+ assertThat("Incorrect size for matchFields", table0Model.matchFields().size(), is(equalTo(9)));
+ // Check if matchFields are in order
+ assertThat("Incorrect order for matchFields", table0Model.matchFields(), IsIterableContainingInOrder.contains(
+ piMatchFieldList.get(0), piMatchFieldList.get(1),
+ piMatchFieldList.get(2), piMatchFieldList.get(3),
+ piMatchFieldList.get(4), piMatchFieldList.get(5),
+ piMatchFieldList.get(6), piMatchFieldList.get(7),
+ piMatchFieldList.get(8)));
+
+ assertThat("Incorrect size for matchFields", wcmpTableModel.matchFields().size(), is(equalTo(1)));
+
+ // check if matchFields are in order
+ matchFieldList = tableMsgs.get(1).getMatchFieldsList();
+ assertThat("Incorrect order for matchFields",
+ wcmpTableModel.matchFields(), IsIterableContainingInOrder.contains(
+ new P4MatchFieldModel(PiMatchFieldId.of(matchFieldList.get(0).getName()),
+ matchFieldList.get(0).getBitwidth(), PiMatchType.EXACT)));
+
+ //check table0 actionsRefs
+ List<ActionRef> actionRefList = tableMsgs.get(0).getActionRefsList();
+ assertThat("Incorrect size for actionRefs", actionRefList.size(), is(equalTo(4)));
+
+ //create action instances
+ PiActionId actionId = PiActionId.of("set_egress_port");
+ PiActionParamId piActionParamId = PiActionParamId.of("port");
+ int bitWitdth = 9;
+ PiActionParamModel actionParamModel = new P4ActionParamModel(piActionParamId, bitWitdth);
+ ImmutableMap<PiActionParamId, PiActionParamModel> params = new
+ ImmutableMap.Builder<PiActionParamId, PiActionParamModel>()
+ .put(piActionParamId, actionParamModel).build();
+
+ PiActionModel setEgressPortAction = new P4ActionModel(actionId, params);
+
+ actionId = PiActionId.of("send_to_cpu");
+ PiActionModel sendToCpuAction =
+ new P4ActionModel(actionId, new ImmutableMap.Builder<PiActionParamId, PiActionParamModel>().build());
+
+ actionId = PiActionId.of("_drop");
+ PiActionModel dropAction =
+ new P4ActionModel(actionId, new ImmutableMap.Builder<PiActionParamId, PiActionParamModel>().build());
+
+ actionId = PiActionId.of("NoAction");
+ PiActionModel noAction =
+ new P4ActionModel(actionId, new ImmutableMap.Builder<PiActionParamId, PiActionParamModel>().build());
+
+ actionId = PiActionId.of("table0_control.set_next_hop_id");
+ piActionParamId = PiActionParamId.of("next_hop_id");
+ bitWitdth = 16;
+ actionParamModel = new P4ActionParamModel(piActionParamId, bitWitdth);
+ params = new ImmutableMap.Builder<PiActionParamId, PiActionParamModel>()
+ .put(piActionParamId, actionParamModel).build();
+
+ PiActionModel setNextHopIdAction = new P4ActionModel(actionId, params);
+
+ //check table0 actions
+ assertThat("action dose not match",
+ table0Model.actions(), IsIterableContainingInAnyOrder.containsInAnyOrder(
+ setEgressPortAction, sendToCpuAction, setNextHopIdAction, dropAction));
+
+ //check wcmp_table actions
+ assertThat("actions dose not match",
+ wcmpTableModel.actions(), IsIterableContainingInAnyOrder.containsInAnyOrder(
+ setEgressPortAction, noAction));
+
+ PiActionModel table0DefaultAction = table0Model.defaultAction().orElse(null);
+
+ new EqualsTester().addEqualityGroup(table0DefaultAction, dropAction).testEquals();
+
+ // Check existence
+ assertThat("model parsed value is null", table0DefaultAction, notNullValue());
+
+ //parse action profiles
+ PiTableId tableId = PiTableId.of("wcmp_control.wcmp_table");
+ ImmutableSet<PiTableId> tableIds = new ImmutableSet.Builder<PiTableId>().add(tableId).build();
+ PiActionProfileId actionProfileId = PiActionProfileId.of("wcmp_control.wcmp_selector");
+ PiActionProfileModel wcmpSelector3 = new P4ActionProfileModel(actionProfileId, tableIds,
+ true, DEFAULT_MAX_ACTION_PROFILE_SIZE);
+ PiActionProfileModel wcmpSelector = model.actionProfiles(actionProfileId).orElse(null);
+ PiActionProfileModel wcmpSelector2 = model2.actionProfiles(actionProfileId).orElse(null);
+
+ new EqualsTester().addEqualityGroup(wcmpSelector, wcmpSelector2, wcmpSelector3).testEquals();
+
+ // Check existence
+ assertThat("model parsed value is null", wcmpSelector, notNullValue());
+ assertThat("Incorrect value for actions profiles", model.actionProfiles(), containsInAnyOrder(wcmpSelector));
+ // ActionProfiles size
+ assertThat("Incorrect size for action profiles", model.actionProfiles().size(), is(equalTo(1)));
+
+ //parse counters
+ PiCounterModel ingressPortCounterModel =
+ model.counter(PiCounterId.of("port_counters_ingress.ingress_port_counter")).orElse(null);
+ PiCounterModel egressPortCounterModel =
+ model.counter(PiCounterId.of("port_counters_egress.egress_port_counter")).orElse(null);
+ PiCounterModel table0CounterModel =
+ model.counter(PiCounterId.of("table0_control.table0_counter")).orElse(null);
+ PiCounterModel wcmpTableCounterModel =
+ model.counter(PiCounterId.of("wcmp_control.wcmp_table_counter")).orElse(null);
+
+ PiCounterModel ingressPortCounterModel2 =
+ model2.counter(PiCounterId.of("port_counters_ingress.ingress_port_counter")).orElse(null);
+ PiCounterModel egressPortCounterModel2 =
+ model2.counter(PiCounterId.of("port_counters_egress.egress_port_counter")).orElse(null);
+ PiCounterModel table0CounterModel2 =
+ model2.counter(PiCounterId.of("table0_control.table0_counter")).orElse(null);
+ PiCounterModel wcmpTableCounterModel2 =
+ model2.counter(PiCounterId.of("wcmp_control.wcmp_table_counter")).orElse(null);
+
+ new EqualsTester()
+ .addEqualityGroup(ingressPortCounterModel, ingressPortCounterModel2)
+ .addEqualityGroup(egressPortCounterModel, egressPortCounterModel2)
+ .addEqualityGroup(table0CounterModel, table0CounterModel2)
+ .addEqualityGroup(wcmpTableCounterModel, wcmpTableCounterModel2)
+ .testEquals();
+
+ assertThat("model parsed value is null", ingressPortCounterModel, notNullValue());
+ assertThat("model parsed value is null", egressPortCounterModel, notNullValue());
+ assertThat("model parsed value is null", table0CounterModel, notNullValue());
+ assertThat("model parsed value is null", wcmpTableCounterModel, notNullValue());
+
+ //Parse meters
+ Collection<PiMeterModel> meterModel = model.meters();
+ Collection<PiMeterModel> meterModel2 = model2.meters();
+
+ assertThat("model pased meter collaction should be empty", meterModel.isEmpty(), is(true));
+ assertThat("model pased meter collaction should be empty", meterModel2.isEmpty(), is(true));
+
+ //parse packet operations
+ PiPacketOperationModel packetInOperationalModel =
+ model.packetOperationModel(PiPacketOperationType.PACKET_IN).orElse(null);
+ PiPacketOperationModel packetOutOperationalModel =
+ model.packetOperationModel(PiPacketOperationType.PACKET_OUT).orElse(null);
+
+ PiPacketOperationModel packetInOperationalModel2 =
+ model2.packetOperationModel(PiPacketOperationType.PACKET_IN).orElse(null);
+ PiPacketOperationModel packetOutOperationalModel2 =
+ model2.packetOperationModel(PiPacketOperationType.PACKET_OUT).orElse(null);
+
+ new EqualsTester()
+ .addEqualityGroup(packetInOperationalModel, packetInOperationalModel2)
+ .addEqualityGroup(packetOutOperationalModel, packetOutOperationalModel2)
+ .testEquals();
+
+ // Check existence
+ assertThat("model parsed value is null", packetInOperationalModel, notNullValue());
+ assertThat("model parsed value is null", packetOutOperationalModel, notNullValue());
+ }
+
+ /**
+ * Gets P4Info message from the URL.
+ * @param p4InfoUrl link to the p4Info file
+ * @return a P4Info object
+ * @throws IOException if any problem occurs while reading from the URL connection.
+ */
+ private P4Info getP4InfoMessage(URL p4InfoUrl) throws IOException {
+ InputStream p4InfoStream = p4InfoUrl.openStream();
+ P4Info.Builder p4InfoBuilder = P4Info.newBuilder();
+ TextFormat.getParser().merge(new InputStreamReader(p4InfoStream),
+ ExtensionRegistry.getEmptyRegistry(),
+ p4InfoBuilder);
+ return p4InfoBuilder.build();
+ }
+}