Repository: azavea/raster-vision

Issue:
Unit tests for `core`
![screenshot from 2018-10-11 09-50-53](https://user-images.githubusercontent.com/11281373/46813861-7a1da200-cd45-11e8-8a99-295cf8a810c2.png)

Pursuant to #305 


Discussion:

Resolving patch:
diff --git a/rastervision/core/box.py b/rastervision/core/box.py
--- a/rastervision/core/box.py
+++ b/rastervision/core/box.py
@@ -84,10 +84,10 @@ def to_npboxes(boxes):
             npboxes[boxind, :] = box.npbox_format()
         return npboxes
 
-    def __str__(self):
+    def __str__(self):  # pragma: no cover
         return str(self.npbox_format())
 
-    def __repr__(self):
+    def __repr__(self):  # pragma: no cover
         return str(self)
 
     def geojson_coordinates(self):
@@ -109,7 +109,7 @@ def make_random_square_container(self, size):
         if size < self.get_width():
             raise BoxSizeError('size of random container cannot be < width')
 
-        if size < self.get_height():
+        if size < self.get_height():  # pragma: no cover
             raise BoxSizeError('size of random container cannot be < height')
 
         lb = self.ymin - (size - self.get_height())
@@ -132,7 +132,7 @@ def make_random_square(self, size):
         if size >= self.get_width():
             raise BoxSizeError('size of random square cannot be >= width')
 
-        if size >= self.get_height():
+        if size >= self.get_height():  # pragma: no cover
             raise BoxSizeError('size of random square cannot be >= height')
 
         lb = self.ymin
diff --git a/rastervision/core/class_map.py b/rastervision/core/class_map.py
--- a/rastervision/core/class_map.py
+++ b/rastervision/core/class_map.py
@@ -23,9 +23,9 @@ def __eq__(self, other):
         if isinstance(other, ClassItem):
             return (self.id == other.id and self.name == other.name
                     and self.color == other.color)
-        return False
+        return False  # pragma: no cover
 
-    def __repr__(self):
+    def __repr__(self):  # pragma: no cover
         s = 'CLASS ITEM: [{}] {}'.format(self.id, self.name)
         if self.color:
             s += ' ({})'.format(self.color)
diff --git a/rastervision/core/config.py b/rastervision/core/config.py
--- a/rastervision/core/config.py
+++ b/rastervision/core/config.py
@@ -13,13 +13,13 @@ class Config(ABC):
     def to_builder(self):
         """Return a builder based on this config.
         """
-        pass
+        pass  # pragma: no cover
 
     @abstractmethod
     def to_proto(self):
         """Returns the protobuf configuration for this config.
         """
-        pass
+        pass  # pragma: no cover
 
     @abstractmethod
     def update_for_command(self, command_type, experiment_config,
@@ -43,7 +43,7 @@ def update_for_command(self, command_type, experiment_config,
 
            Returns: (config, io_def)
         """
-        pass
+        pass  # pragma: no cover
 
     @staticmethod
     @abstractmethod
@@ -51,7 +51,7 @@ def builder():
         """Returns a new builder that takes this configuration
            as its starting point.
         """
-        pass
+        pass  # pragma: no cover
 
     @staticmethod
     @abstractmethod
@@ -59,7 +59,7 @@ def from_proto(msg):
         """Creates a Config from the specificed protobuf message
         TODO: Allow loading from file uri or dict
         """
-        pass
+        pass  # pragma: no cover
 
 
 class ConfigBuilder(ABC):
@@ -72,7 +72,7 @@ def __init__(self, config_class, config=None):
                      into the __init__ method of config_class to build the configuration.
                      This config is modified with the fluent builder methods.
         """
-        if config is None:
+        if config is None:  # pragma: no cover
             config = {}
 
         self.config_class = config_class
@@ -88,14 +88,14 @@ def validate(self):
         """Validate this config, if there is validation on the builder that
            is not captured by the required arguments of the config.
         """
-        pass
+        pass  # pragma: no cover
 
     @abstractmethod
     def from_proto(self, msg):
         """Return a builder that takes the configuration from the proto message
            as its starting point.
         """
-        pass
+        pass  # pragma: no cover
 
 
 class BundledConfigMixin(ABC):
@@ -111,7 +111,7 @@ def save_bundle_files(self, bundle_dir):
                  with the basenames of URIs in place of the original URIs,
                  and a list of URIs that are to be bundled.
         """
-        pass
+        pass  # pragma: no cover
 
     def bundle_file(self, uri, bundle_dir):
         local_path = download_or_copy(uri, bundle_dir)
@@ -121,4 +121,4 @@ def bundle_file(self, uri, bundle_dir):
     @abstractmethod
     def load_bundle_files(self, bundle_dir):
         """Load files from a prediction package bundle directory."""
-        pass
+        pass  # pragma: no cover


Test patch:
diff --git a/tests/core/__init__.py b/tests/core/__init__.py
new file mode 100644
diff --git a/tests/core/test_box.py b/tests/core/test_box.py
--- a/tests/core/test_box.py
+++ b/tests/core/test_box.py
@@ -16,6 +16,40 @@ def setUp(self):
         self.xmax = 3
         self.box = Box(self.ymin, self.xmin, self.ymax, self.xmax)
 
+    def test_reproject(self):
+        def transform(point):
+            (y, x) = point
+            return ((y + 1) // 2, x // 2)
+
+        reproj = self.box.reproject(transform)
+        self.assertTrue(reproj.xmin == 0)
+        self.assertTrue(reproj.ymin == 0)
+        self.assertTrue(reproj.ymax == 1)
+        self.assertTrue(reproj.xmax == 2)
+
+    def test_dict(self):
+        dictionary = self.box.to_dict()
+        other = Box.from_dict(dictionary)
+        self.assertTrue(self.box == other)
+
+    def test_bad_square(self):
+        self.assertRaises(BoxSizeError,
+                          lambda: self.box.make_random_square(10))
+
+    def test_bad_conatiner(self):
+        self.assertRaises(BoxSizeError,
+                          lambda: self.box.make_random_square_container(1))
+
+    def test_neq(self):
+        other = Box(self.ymin + 1, self.xmin, self.ymax, self.xmax)
+        self.assertTrue(self.box != other)
+
+    def test_int(self):
+        other = Box(
+            float(self.ymin) + 0.01, float(self.xmin), float(self.ymax),
+            float(self.xmax))
+        self.assertTrue(other.to_int() == self.box)
+
     def test_get_height(self):
         height = self.ymax - self.ymin
         self.assertEqual(self.box.get_height(), height)
diff --git a/tests/core/test_class_map.py b/tests/core/test_class_map.py
--- a/tests/core/test_class_map.py
+++ b/tests/core/test_class_map.py
@@ -14,6 +14,47 @@ def test_construct_from_list_str(self):
             actual = cm.get_by_id(i + 1)
             self.assertEqual(actual, expected)
 
+    def test_construct_from(self):
+        self.assertRaises(Exception,
+                          lambda: ClassMap.construct_from('some string'))
+
+    def test_all_color_true(self):
+        source = [
+            ClassItemMsg(id=1, name='one', color='red'),
+            ClassItemMsg(id=2, name='two', color='green'),
+            ClassItemMsg(id=3, name='three', color='blue')
+        ]
+        cm = ClassMap.construct_from(source)
+        self.assertTrue(cm.has_all_colors())
+
+    def test_all_color_false(self):
+        source = [
+            ClassItemMsg(id=1, name='one', color='red'),
+            ClassItemMsg(id=2, name='two', color='green'),
+            ClassItemMsg(id=3, name='three')
+        ]
+        cm = ClassMap.construct_from(source)
+        self.assertFalse(cm.has_all_colors())
+
+    def test_category_index(self):
+        source = [
+            ClassItemMsg(id=1, name='one', color='red'),
+            ClassItemMsg(id=2, name='two', color='green'),
+            ClassItemMsg(id=3, name='three')
+        ]
+        cm = ClassMap.construct_from(source)
+        index = cm.get_category_index()
+        self.assertEqual(index[1], {'id': 1, 'name': 'one'})
+
+    def test_get_by_name_negative(self):
+        source = [
+            ClassItemMsg(id=1, name='one', color='red'),
+            ClassItemMsg(id=2, name='two', color='green'),
+            ClassItemMsg(id=3, name='three', color='blue')
+        ]
+        cm = ClassMap.construct_from(source)
+        self.assertRaises(ValueError, lambda: cm.get_by_name('four'))
+
     def test_construct_from_protos(self):
         source = [
             ClassItemMsg(id=1, name='one', color='red'),
diff --git a/tests/core/test_command_io_definition.py b/tests/core/test_command_io_definition.py
new file mode 100644
--- /dev/null
+++ b/tests/core/test_command_io_definition.py
@@ -0,0 +1,10 @@
+import unittest
+
+from rastervision.core import CommandIODefinition
+
+
+class TestCommandIoDefinition(unittest.TestCase):
+    def test_add_missing(self):
+        cid = CommandIODefinition()
+        cid.add_missing('message')
+        self.assertEqual(cid.missing_input_messages[0], 'message')