[GRASS-SVN] r60948 - in sandbox/wenzeslaus/gunittest: . testsuite

svn_grass at osgeo.org svn_grass at osgeo.org
Tue Jun 24 12:16:28 PDT 2014


Author: wenzeslaus
Date: 2014-06-24 12:16:28 -0700 (Tue, 24 Jun 2014)
New Revision: 60948

Modified:
   sandbox/wenzeslaus/gunittest/case.py
   sandbox/wenzeslaus/gunittest/checkers.py
   sandbox/wenzeslaus/gunittest/grass_py_static_check.py
   sandbox/wenzeslaus/gunittest/testsuite/test_assertions.py
   sandbox/wenzeslaus/gunittest/testsuite/test_checkers.py
Log:
gunittest: sunset option for keyvalue comparison, keyvalue difference, and functions for comparing raster maps using r.univar output

Modified: sandbox/wenzeslaus/gunittest/case.py
===================================================================
--- sandbox/wenzeslaus/gunittest/case.py	2014-06-24 18:24:05 UTC (rev 60947)
+++ sandbox/wenzeslaus/gunittest/case.py	2014-06-24 19:16:28 UTC (rev 60948)
@@ -16,10 +16,22 @@
 import unittest
 from unittest.util import safe_repr
 
-from .checkers import check_text_ellipsis
+import grass.script.core as gcore
 
+from .checkers import (check_text_ellipsis,
+                       text_to_keyvalue, compare_keyvalue, diff_keyvalue)
 
+
+# the following lines are for code coverage
+import coverage
+cov = coverage.coverage(omit="*testsuite*")
+cov.start()
+
+
 class GrassTestCase(unittest.TestCase):
+    # we dissable R0904 for all TestCase classes because their purpose is to
+    # provide a lot of assert methods
+    # pylint: disable=R0904
 
     def assertLooksLike(self, actual, reference, msg=None):
         self.assert_(isinstance(actual, basestring), (
@@ -27,18 +39,34 @@
         self.assert_(isinstance(reference, basestring), (
                      'reference argument is not a string'))
         if not check_text_ellipsis(actual=actual, reference=reference):
-            standardMsg = self._formatMessage(msg, '"%s" does not correspond with "%s"'
-                                                   % (safe_repr(actual),
-                                                      safe_repr(reference)))
+            standardMsg = '"%s" does not correspond with "%s"' % (safe_repr(actual),
+                                                                  safe_repr(reference))
             self.fail(self._formatMessage(msg, standardMsg))
 
+    def assertRasterFitsUnivar(self, raster, reference_univar, msg=None):
+        if isinstance(reference_univar, basestring):
+            reference_univar = text_to_keyvalue(reference_univar, sep='=')
+        stdout = gcore.read_command('r.univar',
+                                    map=raster, separator='=', flags='ge')
+        raster_univar = text_to_keyvalue(stdout, sep='=')
+        if not compare_keyvalue(dict_a=reference_univar, dict_b=raster_univar,
+                                a_is_subset=True):
+            unused, missing, mismatch = diff_keyvalue(dict_a=reference_univar,
+                                                      dict_b=raster_univar,
+                                                     a_is_subset=True)
+            standardMsg = "r.univar difference:\n"
+            if mismatch:
+                standardMsg += "mismatch values: %s\n" % mismatch
+            if missing:
+                raise ValueError("r.univar output does not contain"
+                                 " the following keys"
+                                 " provided in reference_univar"
+                                 ": %s\n" % missing)
+            self.fail(self._formatMessage(msg, standardMsg))
 
-# the following lines are for code coverage
-import coverage
-cov = coverage.coverage(omit="*testsuite*")
-cov.start()
 
 
+# the following lines are for code coverage
 def endcov(cov):
     cov.stop()
     cov.html_report(directory='testcodecoverage')

Modified: sandbox/wenzeslaus/gunittest/checkers.py
===================================================================
--- sandbox/wenzeslaus/gunittest/checkers.py	2014-06-24 18:24:05 UTC (rev 60947)
+++ sandbox/wenzeslaus/gunittest/checkers.py	2014-06-24 19:16:28 UTC (rev 60948)
@@ -243,8 +243,10 @@
     return True
 
 
+# TODO: rename to equals
 def compare_keyvalue(dict_a, dict_b, precision=0.000001,
-                     def_equal=values_equal, key_equal=None):
+                     def_equal=values_equal, key_equal=None,
+                     a_is_subset=False):
     """Compare two dictionaries
 
     This method will print a warning in case keys that are present in the first
@@ -252,6 +254,9 @@
     The comparison method tries to convert the values into their native format
     (float, int or string) to allow correct comparison.
 
+    Always use keyword arguments for all parameters with defaults. It is a good
+    idea to use keyword arguments also for the first two parameters.
+
     An example key-value text file may have this content:
 
     >>> compare_keyvalue(text_to_keyvalue('''a: Hello
@@ -271,16 +276,23 @@
     @param precision precision with which the floating point values are compared
     @param proj True if it has to check some information about projection system
     @param units True if it has to check some information about units
+    :param callable def_equal: function used for comparison by default
+    :param dict key_equal: dictionary of functions used for comparison of specific keys
 
     @return True if full or almost identical, False if different
     """
     key_equal = {} if key_equal is None else key_equal
 
-    if sorted(dict_a.keys()) != sorted(dict_b.keys()):
+    if not a_is_subset and sorted(dict_a.keys()) != sorted(dict_b.keys()):
         return False
+    b_keys = dict_b.keys() if a_is_subset else None
 
-    # We compare matching keys
+    # iterate over subset or just any if not a_is_subset
+    # check for missing keys in superset
+    # compare matching keys
     for key in dict_a.keys():
+        if a_is_subset and key not in b_keys:
+            return False
         equal_fun = key_equal.get(key, def_equal)
         if not equal_fun(dict_a[key], dict_b[key], precision):
             return False
@@ -296,6 +308,47 @@
     return True
 
 
+# TODO: should the retrun depend on the a_is_subset parameter?
+# must have the same interface and behavior as compare_keyvalue
+def diff_keyvalue(dict_a, dict_b, precision=0.000001,
+                  def_equal=values_equal, key_equal=None,
+                  a_is_subset=False):
+    """
+
+    >>> a = {'c': 2, 'b': 3, 'a': 4}
+    >>> b = {'c': 1, 'b': 3, 'd': 5}
+    >>> diff_keyvalue(a, b)
+    (['d'], ['a'], [('c', 2, 1)])
+    >>> diff_keyvalue(a, b, a_is_subset=True)
+    ([], ['a'], [('c', 2, 1)])
+    """
+    key_equal = {} if key_equal is None else key_equal
+
+    a_keys = dict_a.keys()
+    b_keys = dict_b.keys()
+
+    missing_in_a = []
+    missing_in_b = []
+    mismatched = []
+
+    if not a_is_subset:
+        for key in b_keys:
+            if key not in a_keys:
+                missing_in_a.append(key)
+
+    # iterate over a, so we know that it is in a
+    for key in a_keys:
+        # check if it is in b
+        if key not in b_keys:
+            missing_in_b.append(key)
+        else:
+            equal_fun = key_equal.get(key, def_equal)
+            if not equal_fun(dict_a[key], dict_b[key], precision):
+                mismatched.append((key, dict_a[key], dict_b[key]))
+
+    return sorted(missing_in_a), sorted(missing_in_b), sorted(mismatched)
+
+
 def proj_info_equals(text_a, text_b):
     def compare_sums(list_a, list_b, precision):
         # We compare the sum of the entries

Modified: sandbox/wenzeslaus/gunittest/grass_py_static_check.py
===================================================================
--- sandbox/wenzeslaus/gunittest/grass_py_static_check.py	2014-06-24 18:24:05 UTC (rev 60947)
+++ sandbox/wenzeslaus/gunittest/grass_py_static_check.py	2014-06-24 19:16:28 UTC (rev 60948)
@@ -32,6 +32,12 @@
     cmd = ['pylint', '--rcfile=tools/pylintrc.txt', '--output-format=html',
            '--ignore=.svn']
     cmd = cmd + [target]
+    # graph options:
+    # --import-graph=<file.dot>
+    # --ext-import-graph=<file.dot>
+    # --int-import-graph=<file.dot>
+    # also we can call pylint directly as a package and create our own HTML
+    # report with message ids and perhaps even symbolic message names
     # result
     ensure_dir(output_dir)
     output_file = open(output_dir + '/pylint_report.html', 'w')

Modified: sandbox/wenzeslaus/gunittest/testsuite/test_assertions.py
===================================================================
--- sandbox/wenzeslaus/gunittest/testsuite/test_assertions.py	2014-06-24 18:24:05 UTC (rev 60947)
+++ sandbox/wenzeslaus/gunittest/testsuite/test_assertions.py	2014-06-24 19:16:28 UTC (rev 60948)
@@ -14,9 +14,34 @@
 
 
 class TestTextAssertions(GrassTestCase):
+    # pylint: disable=R0904
     def test_assertLooksLike(self):
         self.assertLooksLike("Generated map is <elevation>",
                              "Generated map is <...>")
         self.assertRaises(self.failureException,
                           self.assertLooksLike, "Generated map is elevation.",
                                                 "Generated map is <...>")
+
+
+R_UNIVAR_ELEVATION_SUBSET = """n=2025000
+null_cells=0
+min=55.5787925720215
+max=156.329864501953
+"""
+
+RANDOM_KEYVALUES = """abc=2025000
+aaa=55.5787925720215
+bbb=156.329864501953
+"""
+
+
+class TestRasterMapAssertations(GrassTestCase):
+    # pylint: disable=R0904
+    def test_assertRasterFitsUnivar(self):
+        self.assertRasterFitsUnivar('elevation', R_UNIVAR_ELEVATION_SUBSET)
+        self.assertRaises(self.failureException,
+                          self.assertRasterFitsUnivar,
+                          'aspect', R_UNIVAR_ELEVATION_SUBSET)
+        self.assertRaises(ValueError,
+                          self.assertRasterFitsUnivar,
+                          'elevation', RANDOM_KEYVALUES)

Modified: sandbox/wenzeslaus/gunittest/testsuite/test_checkers.py
===================================================================
--- sandbox/wenzeslaus/gunittest/testsuite/test_checkers.py	2014-06-24 18:24:05 UTC (rev 60947)
+++ sandbox/wenzeslaus/gunittest/testsuite/test_checkers.py	2014-06-24 19:16:28 UTC (rev 60948)
@@ -23,6 +23,7 @@
 sys.path.insert(0, os.path.split(os.path.split((os.path.dirname(__file__)))[0])[0])
 from gunittest.case import GrassTestCase
 from gunittest.checkers import (values_equal, text_to_keyvalue,
+                                compare_keyvalue,
                                 proj_info_equals, proj_units_equals)
 
 
@@ -235,4 +236,78 @@
         self.assertDictEqual(parse_key_val(R_UNIVAR_KEYVAL_INT, val_type=int),
                                            R_UNIVAR_KEYVAL_INT_DICT)
 
-    
\ No newline at end of file
+
+R_UNIVAR_ELEVATION = """n=2025000
+null_cells=57995100
+cells=60020100
+min=55.5787925720215
+max=156.329864501953
+range=100.751071929932
+mean=110.375440275606
+mean_of_abs=110.375440275606
+stddev=20.3153233205981
+variance=412.712361620436
+coeff_var=18.4056555243368
+sum=223510266.558102
+first_quartile=94.79
+median=108.88
+third_quartile=126.792
+percentile_90=138.66
+"""
+
+R_UNIVAR_ELEVATION_ROUNDED = """n=2025000
+null_cells=57995100
+cells=60020100
+min=55.5788
+max=156.33
+range=100.751
+mean=110.375
+mean_of_abs=110.375
+stddev=20.3153
+variance=412.712
+coeff_var=18.4057
+sum=223510266.558
+first_quartile=94.79
+median=108.88
+third_quartile=126.792
+percentile_90=138.66
+"""
+
+R_UNIVAR_ELEVATION_SUBSET = """n=2025000
+null_cells=57995100
+cells=60020100
+min=55.5787925720215
+max=156.329864501953
+"""
+
+
+class TestRasterMapComparisons(GrassTestCase):
+
+    def test_compare_univars(self):
+        self.assertTrue(compare_keyvalue(text_to_keyvalue(R_UNIVAR_ELEVATION,
+                                                          sep='='),
+                                         text_to_keyvalue(R_UNIVAR_ELEVATION,
+                                                          sep='=')))
+        self.assertFalse(compare_keyvalue(text_to_keyvalue(R_UNIVAR_ELEVATION,
+                                                           sep='='),
+                                          text_to_keyvalue(R_UNIVAR_ELEVATION_SUBSET,
+                                                           sep='=')))
+
+    def test_compare_univars_subset(self):
+        self.assertTrue(compare_keyvalue(text_to_keyvalue(R_UNIVAR_ELEVATION_SUBSET,
+                                                          sep='='),
+                                         text_to_keyvalue(R_UNIVAR_ELEVATION,
+                                                          sep='='),
+                                         a_is_subset=True))
+        self.assertFalse(compare_keyvalue(text_to_keyvalue(R_UNIVAR_ELEVATION,
+                                                          sep='='),
+                                          text_to_keyvalue(R_UNIVAR_ELEVATION_SUBSET,
+                                                           sep='='),
+                                          a_is_subset=True))
+
+    def test_compare_univars_rounded(self):
+        self.assertTrue(compare_keyvalue(text_to_keyvalue(R_UNIVAR_ELEVATION,
+                                                          sep='='),
+                                         text_to_keyvalue(R_UNIVAR_ELEVATION_ROUNDED,
+                                                          sep='='),
+                                         precision=0.001))



More information about the grass-commit mailing list