/**
   * Run tests for {@link VTNEtherMatch#match(FlowMatchContext)}.
   *
   * @param vid A VLAN ID.
   * @param pcp A VLAN priority.
   * @throws Exception An error occurred.
   */
  private void matchTest(Integer vid, Short pcp) throws Exception {
    EtherAddress src = new EtherAddress(0x001122334455L);
    EtherAddress dst = new EtherAddress(0xaabbccddeeffL);
    int etype = 0x806;
    EtherMatchParams header = new EtherMatchParams();
    header.setSourceAddress(src);
    header.setDestinationAddress(dst);
    header.setEtherType(etype);
    header.setVlanId(vid);
    header.setVlanPriority(pcp);
    TestMatchContext ctx = new TestMatchContext().setEtherHeader(header);

    // Empty match should match every packet.
    VTNEtherMatch ematch = new VTNEtherMatch();
    assertEquals(true, ematch.match(ctx));
    ctx.checkMatchFields();

    // Specify single field in Ethernet frame.
    ctx.reset();
    EtherMatchParams params = new EtherMatchParams();
    params.setSourceAddress(src);
    ematch = params.toVTNEtherMatch();
    assertEquals(true, ematch.match(ctx));
    ctx.checkMatchFields(FlowMatchType.DL_SRC);

    ctx.reset();
    params.reset().setDestinationAddress(dst);
    ematch = params.toVTNEtherMatch();
    assertEquals(true, ematch.match(ctx));
    ctx.checkMatchFields(FlowMatchType.DL_DST);

    ctx.reset();
    params.reset().setEtherType(etype);
    ematch = params.toVTNEtherMatch();
    assertEquals(true, ematch.match(ctx));
    ctx.checkMatchFields(FlowMatchType.DL_TYPE);

    // FlowMatchType.DL_VLAN should never be set into FlowMatchContext.
    ctx.reset();
    params.reset().setVlanId(vid);
    ematch = params.toVTNEtherMatch();
    assertEquals(true, ematch.match(ctx));
    ctx.checkMatchFields();

    Set<FlowMatchType> expected =
        EnumSet.of(FlowMatchType.DL_SRC, FlowMatchType.DL_DST, FlowMatchType.DL_TYPE);
    if (vid.intValue() != EtherHeader.VLAN_NONE) {
      params.setVlanPriority(pcp);
      ematch = params.toVTNEtherMatch();
      assertEquals(true, ematch.match(ctx));
      ctx.checkMatchFields(FlowMatchType.DL_VLAN_PCP);
      expected.add(FlowMatchType.DL_VLAN_PCP);
    }

    // Specfify all fields.
    ctx.reset();
    params.setSourceAddress(src);
    params.setDestinationAddress(dst);
    params.setEtherType(etype);
    ematch = params.toVTNEtherMatch();
    assertEquals(true, ematch.match(ctx));
    ctx.checkMatchFields(expected);

    // Ensure that match() returns false if one field does not match.
    EtherAddress anotherMac = new EtherAddress(0x102030405060L);
    ctx.reset();
    params.setSourceAddress(anotherMac);
    ematch = params.toVTNEtherMatch();
    assertEquals(false, ematch.match(ctx));
    ctx.checkMatchFields(FlowMatchType.DL_SRC);

    ctx.reset();
    params.setSourceAddress(src);
    params.setDestinationAddress(anotherMac);
    ematch = params.toVTNEtherMatch();
    assertEquals(false, ematch.match(ctx));
    ctx.checkMatchFields(FlowMatchType.DL_SRC, FlowMatchType.DL_DST);

    int anotherType = 0x800;
    ctx.reset();
    params.setDestinationAddress(dst);
    params.setEtherType(anotherType);
    ematch = params.toVTNEtherMatch();
    assertEquals(false, ematch.match(ctx));
    ctx.checkMatchFields(FlowMatchType.DL_SRC, FlowMatchType.DL_DST, FlowMatchType.DL_TYPE);

    params.setEtherType(etype);
    if (vid.intValue() == EtherHeader.VLAN_NONE) {
      Integer[] vids = {1, 4095};
      for (Integer v : vids) {
        ctx.reset();
        params.setVlanId(v);
        ematch = params.toVTNEtherMatch();
        assertEquals(false, ematch.match(ctx));
        ctx.checkMatchFields(FlowMatchType.DL_SRC, FlowMatchType.DL_DST, FlowMatchType.DL_TYPE);

        ctx.reset();
        params.setVlanPriority((short) 0);
        ematch = params.toVTNEtherMatch();
        assertEquals(false, ematch.match(ctx));
        ctx.checkMatchFields(FlowMatchType.DL_SRC, FlowMatchType.DL_DST, FlowMatchType.DL_TYPE);
      }
    } else {
      int anotherVid = vid.intValue() + 1;
      if (anotherVid > 4095) {
        anotherVid = 1;
      }
      Integer[] vids = {EtherHeader.VLAN_NONE, anotherVid};
      for (Integer v : vids) {
        ctx.reset();
        params.setVlanId(v);
        if (v.intValue() == EtherHeader.VLAN_NONE) {
          params.setVlanPriority((Short) null);
        } else {
          params.setVlanPriority(pcp);
        }
        ematch = params.toVTNEtherMatch();
        assertEquals(false, ematch.match(ctx));
        ctx.checkMatchFields(FlowMatchType.DL_SRC, FlowMatchType.DL_DST, FlowMatchType.DL_TYPE);
      }

      short anotherPcp = (short) ((pcp.shortValue() + 1) & 0x7);
      ctx.reset();
      params.setVlanId(vid);
      params.setVlanPriority(anotherPcp);
      ematch = params.toVTNEtherMatch();
      assertEquals(false, ematch.match(ctx));
      ctx.checkMatchFields(
          FlowMatchType.DL_SRC,
          FlowMatchType.DL_DST,
          FlowMatchType.DL_TYPE,
          FlowMatchType.DL_VLAN_PCP);
    }
  }
  /**
   * Ensure that {@link VTNEtherMatch} is mapped to XML root element.
   *
   * @throws Exception An error occurred.
   */
  @Test
  public void testJAXB() throws Exception {
    Unmarshaller um = createUnmarshaller(VTNEtherMatch.class);
    EtherMatchParams params = new EtherMatchParams();

    // Empty match.
    String xml = new XmlNode(XML_ROOT).toString();
    VTNEtherMatch ematch = unmarshal(um, xml, VTNEtherMatch.class);
    ematch.verify();
    assertEquals(params.toVTNEtherMatch(), ematch);

    // Specifying all fields.
    EtherAddress src = new EtherAddress(0x001122334455L);
    EtherAddress dst = new EtherAddress(0x0abcdef12345L);
    Integer etype = Integer.valueOf(0x806);
    Integer vid = Integer.valueOf(4095);
    Short pcp = Short.valueOf((short) 7);
    params.setSourceAddress(src);
    params.setDestinationAddress(dst);
    params.setEtherType(etype);
    params.setVlanId(vid);
    params.setVlanPriority(pcp);

    final String tagSrc = "source-address";
    final String tagDst = "destination-address";
    final String tagType = "ether-type";
    final String tagVid = "vlan-id";
    final String tagPcp = "vlan-pcp";
    xml =
        new XmlNode(XML_ROOT)
            .add(new XmlNode(tagSrc, src.getText()))
            .add(new XmlNode(tagDst, dst.getText()))
            .add(new XmlNode(tagType, etype))
            .add(new XmlNode(tagVid, vid))
            .add(new XmlNode(tagPcp, pcp))
            .toString();
    ematch = unmarshal(um, xml, VTNEtherMatch.class);
    ematch.verify();
    assertEquals(params.toVTNEtherMatch(), ematch);

    // Specifying single field.
    params.reset().setSourceAddress(src);
    xml = new XmlNode(XML_ROOT).add(new XmlNode(tagSrc, src.getText())).toString();
    ematch = unmarshal(um, xml, VTNEtherMatch.class);
    ematch.verify();
    assertEquals(params.toVTNEtherMatch(), ematch);

    params.reset().setDestinationAddress(dst);
    xml = new XmlNode(XML_ROOT).add(new XmlNode(tagDst, dst.getText())).toString();
    ematch = unmarshal(um, xml, VTNEtherMatch.class);
    ematch.verify();
    assertEquals(params.toVTNEtherMatch(), ematch);

    params.reset().setEtherType(etype);
    xml = new XmlNode(XML_ROOT).add(new XmlNode(tagType, etype)).toString();
    ematch = unmarshal(um, xml, VTNEtherMatch.class);
    ematch.verify();
    assertEquals(params.toVTNEtherMatch(), ematch);

    params.reset().setVlanId(vid);
    xml = new XmlNode(XML_ROOT).add(new XmlNode(tagVid, vid)).toString();
    ematch = unmarshal(um, xml, VTNEtherMatch.class);
    ematch.verify();
    assertEquals(params.toVTNEtherMatch(), ematch);

    params.setVlanPriority(pcp);
    xml =
        new XmlNode(XML_ROOT)
            .add(new XmlNode(tagVid, vid))
            .add(new XmlNode(tagPcp, pcp))
            .toString();
    ematch = unmarshal(um, xml, VTNEtherMatch.class);
    ematch.verify();
    assertEquals(params.toVTNEtherMatch(), ematch);

    // Ensure that broken values in XML can be detected.
    jaxbErrorTest(VTNEtherMatch.class, getXmlDataTypes(XML_ROOT));
  }