Repository: kytos/python-openflow

Issue:
Check  1.0.1 and 1.0.2 compliance

Discussion:

Patch:
diff --git a/pyof/v0x01/controller2switch/packet_out.py b/pyof/v0x01/controller2switch/packet_out.py
index 78cb669..e7c9965 100644
--- a/pyof/v0x01/controller2switch/packet_out.py
+++ b/pyof/v0x01/controller2switch/packet_out.py
@@ -7,12 +7,16 @@ uses the OFPT_PACKET_OUT message"""
 
 # Local source tree imports
 from pyof.v0x01.common import header as of_header
+from pyof.v0x01.common.phy_port import Port
 from pyof.v0x01.controller2switch import common
 from pyof.v0x01.foundation import base
 from pyof.v0x01.foundation import basic_types
+from pyof.v0x01.foundation.exceptions import ValidationError
 
 # Classes
 
+#: in_port valid virtual port values, for validation
+_VIRT_IN_PORTS = (Port.OFPP_LOCAL, Port.OFPP_CONTROLLER, Port.OFPP_NONE)
 
 class PacketOut(base.GenericMessage):
     """
@@ -47,3 +51,25 @@ class PacketOut(base.GenericMessage):
         self.actions_len = actions_len
         self.actions = [] if actions is None else actions
         self.data = data
+
+    def validate(self):
+        if not super().is_valid():
+            raise ValidationError()
+        self._validate_in_port()
+
+    def is_valid(self):
+        try:
+            self.validate()
+            return True
+        except ValidationError:
+            return False
+
+    def _validate_in_port(self):
+        port = self.in_port
+        valid = True
+        if isinstance(port, int) and (port < 1 or port >= Port.OFPP_MAX.value):
+            valid = False
+        elif isinstance(port, Port) and port not in _VIRT_IN_PORTS:
+            valid = False
+        if not valid:
+            raise ValidationError('{} is not a valid input port.'.format(port))
diff --git a/pyof/v0x01/foundation/exceptions.py b/pyof/v0x01/foundation/exceptions.py
index 0dab2cd..13e9296 100644
--- a/pyof/v0x01/foundation/exceptions.py
+++ b/pyof/v0x01/foundation/exceptions.py
@@ -1,6 +1,11 @@
 """Exceptions defined on this Library"""
 
 
+class ValidationError(Exception):
+    """Can be used directly or inherited by specific validation errors."""
+    pass
+
+
 class MethodNotImplemented(Exception):
     """Exception to be raised when a method is not implemented"""
     def __init__(self, message=None):
@@ -33,44 +38,11 @@ class WrongListItemType(Exception):
         return message
 
 
-class PADHasNoValue(Exception):
-    """Exception raised when user tries to set a value on a PAD attribute"""
-    def __str__(self):
-        return "You can't set a value on a PAD attribute"
-
-
-class AttributeTypeError(Exception):
-    """Error raise when the attribute is not of the expected type
-    defined on the class definition"""
-
-    def __init__(self, item, item_class, expected_class):
-        super().__init__()
-        self.item = item
-        self.item_class = item_class
-        self.expected_class = expected_class
-
-    def __str__(self):
-        msg = "Unexpected value '{}' ".format(str(self.item))
-        msg += "with class '{}' ".format(str(self.item_class))
-        msg += "and expected class '{}'".format(str(self.expected_class))
-        return msg
-
-
 class NotBinaryData(Exception):
     """Error raised when the content of a BinaryData attribute is not binary"""
     def __str__(self):
         return "The content of this variable needs to be binary data"
 
 
-class ValidationError(Exception):
-    """Error on validate message or struct"""
-    def __init__(self, msg="Error on validate message"):
-        super().__init__()
-        self.msg = msg
-
-    def __str__(self):
-        return self.msg
-
-
 class UnpackException(Exception):
     pass


Test patch:
diff --git a/tests/v0x01/test_controller2switch/test_packet_out.py b/tests/v0x01/test_controller2switch/test_packet_out.py
index 38dfd5a..4130c77 100644
--- a/tests/v0x01/test_controller2switch/test_packet_out.py
+++ b/tests/v0x01/test_controller2switch/test_packet_out.py
@@ -1,11 +1,12 @@
 import unittest
 
 from pyof.v0x01.common import phy_port
+from pyof.v0x01.common.phy_port import Port
 from pyof.v0x01.controller2switch import packet_out
+from pyof.v0x01.foundation.exceptions import ValidationError
 
 
 class TestPacketOut(unittest.TestCase):
-
     def setUp(self):
         self.message = packet_out.PacketOut()
         self.message.header.xid = 80
@@ -28,3 +29,35 @@ class TestPacketOut(unittest.TestCase):
         """[Controller2Switch/PacketOut] - unpacking"""
         # TODO
         pass
+
+    def test_valid_virtual_in_ports(self):
+        """Valid virtual ports as defined in 1.0.1 spec."""
+        valid = (Port.OFPP_LOCAL, Port.OFPP_CONTROLLER, Port.OFPP_NONE)
+        msg = packet_out.PacketOut()
+        for in_port in valid:
+            msg.in_port = in_port
+            self.assertTrue(msg.is_valid())
+
+    def test_invalid_virtual_in_ports(self):
+        """Invalid virtual ports as defined in 1.0.1 spec."""
+        invalid = (Port.OFPP_IN_PORT, Port.OFPP_TABLE, Port.OFPP_NORMAL,
+                   Port.OFPP_FLOOD, Port.OFPP_ALL)
+        for in_port in invalid:
+            self.message.in_port = in_port
+            self.assertFalse(self.message.is_valid())
+            self.assertRaises(ValidationError, self.message.validate)
+
+    def test_valid_physical_in_ports(self):
+        """Physical port limits from 1.0.0 spec."""
+        max_valid = int(Port.OFPP_MAX.value) - 1
+        for in_port in (1, max_valid):
+            self.message.in_port = in_port
+            self.assertTrue(self.message.is_valid())
+
+    def test_invalid_physical_in_port(self):
+        """Physical port limits from 1.0.0 spec."""
+        max_valid = int(Port.OFPP_MAX.value) - 1
+        for in_port in (-1, 0, max_valid + 1, max_valid + 2):
+            self.message.in_port = in_port
+            self.assertFalse(self.message.is_valid())
+            self.assertRaises(ValidationError, self.message.validate)