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

svn_grass at osgeo.org svn_grass at osgeo.org
Fri Jun 27 12:51:37 PDT 2014


Author: wenzeslaus
Date: 2014-06-27 12:51:37 -0700 (Fri, 27 Jun 2014)
New Revision: 61018

Modified:
   sandbox/wenzeslaus/gunittest/case.py
   sandbox/wenzeslaus/gunittest/testsuite/test_assertions.py
Log:
gunittest: documentation of assert methods; more tests especially for MD5 functions

Modified: sandbox/wenzeslaus/gunittest/case.py
===================================================================
--- sandbox/wenzeslaus/gunittest/case.py	2014-06-27 15:14:01 UTC (rev 61017)
+++ sandbox/wenzeslaus/gunittest/case.py	2014-06-27 19:51:37 UTC (rev 61018)
@@ -38,6 +38,10 @@
     """
 
     def assertLooksLike(self, actual, reference, msg=None):
+        """Test that ``actual`` text is the same as ``referece`` with ellipses.
+
+        See :func:`check_text_ellipsis` for details of behavior.
+        """
         self.assert_(isinstance(actual, basestring), (
                      'actual argument is not a string'))
         self.assert_(isinstance(reference, basestring), (
@@ -48,6 +52,8 @@
                                                                   reference)
             self.fail(self._formatMessage(msg, standardMsg))
 
+    # TODO: support precision for all functions
+    # TODO: auto-determine precision based on the map type
     # TODO: we can have also more general function without the subset reference
     def assertCommandKeyValue(self, module, parameters, reference, sep,
                               msg=None):
@@ -71,6 +77,24 @@
             self.fail(self._formatMessage(msg, standardMsg))
 
     def assertRasterFitsUnivar(self, raster, reference, msg=None):
+        r"""Test that raster map has the values obtained by r.univar module.
+
+        The function does not require all values from r.univar.
+        Only the provided values are tested.
+        Typical example is checking minimum, maximum and number of NULL cells
+        in the map::
+
+            values = 'null_cells=0\nmin=55.5787925720215\nmax=156.329864501953'
+            self.assertRasterFitsUnivar(map='elevation', reference=values)
+
+        Use keyword arguments syntax for all function parameters.
+
+        .. todo::
+            support precision
+
+        Does not -e (extended statistics) flag, use `assertCommandKeyValue`
+        for the full interface of arbitrary module.
+        """
         self.assertCommandKeyValue(module='r.univar',
                                    parameters=dict(map=raster,
                                                    separator='=',
@@ -78,18 +102,47 @@
                                    reference=reference, msg=msg, sep='=')
 
     def assertRasterFitsInfo(self, raster, reference, msg=None):
+        r"""Test that raster map has the values obtained by v.univar module.
+
+        The function does not require all values from v.univar.
+        Only the provided values are tested.
+        Typical example is checking minimum, maximum and type of the map::
+
+            minmax = 'min=0\nmax=1451\ndatatype=FCELL'
+            self.assertRasterFitsInfo(map='elevation', reference=values)
+
+        Use keyword arguments syntax for all function parameters.
+
+        .. todo::
+            support precision
+
+        This function supports values obtainded -r (range) and
+        -e (extended metadata) flags.
+        """
         self.assertCommandKeyValue(module='r.info',
-                                   parameters=dict(map=raster, flags='g'),
+                                   parameters=dict(map=raster, flags='gre'),
                                    reference=reference, msg=msg, sep='=')
 
     def assertVectorFitsUnivar(self, map, column, reference, msg=None,
                                layer=None, type=None, where=None):
-        """
+        r"""Test that vector map has the values obtained by v.univar module.
 
+        The function does not require all values from v.univar.
+        Only the provided values are tested.
+        Typical example is checking minimum and maximum of a column::
+
+            minmax = 'min=0\nmax=1451'
+            self.assertVectorFitsUnivar(map='bridges', column='WIDTH',
+                                        reference=minmax)
+
         Use keyword arguments syntax for all function parameters.
 
+        .. todo::
+            support precision
+
         Does not support -d (geometry distances) flag, -e (extended statistics)
-        flag and few other, use `assertCommandKeyValue` for the full interface.
+        flag and few other, use `assertCommandKeyValue` for the full interface
+        of arbitrary module.
         """
         parameters = dict(map=map, column=column, flags='g')
         if layer:
@@ -102,32 +155,68 @@
                                    parameters=parameters,
                                    reference=reference, msg=msg, sep='=')
 
-    def assertFileExists(self, filename, msg=None, skip_size_check=False):
-        """
+    def assertFileExists(self, filename, msg=None,
+                         skip_size_check=False, skip_access_check=False):
+        """Test the existence of a file.
 
         .. note:
             By default this also checks if the file size is greater than 0
-            since we rarely want a file to be empty.
+            since we rarely want a file to be empty. And it also checks
+            if the file is access for reading.
         """
         if not os.path.isfile(filename):
-            standardMsg = 'File %s does not exist' % filename
-            self.fail(self._formatMessage(msg, standardMsg))
-        if not os.path.getsize(filename):
-            standardMsg = 'File %s is empty' % filename
-            self.fail(self._formatMessage(msg, standardMsg))
+            stdmsg = 'File %s does not exist' % filename
+            self.fail(self._formatMessage(msg, stdmsg))
+        if not skip_size_check and not os.path.getsize(filename):
+            stdmsg = 'File %s is empty' % filename
+            self.fail(self._formatMessage(msg, stdmsg))
+        if not skip_access_check and not os.access(filename, os.R_OK):
+            stdmsg = 'File %s is not accesible for reading' % filename
+            self.fail(self._formatMessage(msg, stdmsg))
 
     def assertFileMd5(self, filename, md5, msg=None):
+        """Test that file MD5 sum is equal to the provided sum.
+
+        The typical workflow is that you create a file in a way you
+        trust (that you obtain the right file). Then you compute MD5
+        sum of the file. And provide the sum in a test as a string::
+
+            self.assertFileMd5('result.txt', md5='807bba4ffa...')
+
+        Use `file_md5()` function from this package::
+
+            file_md5('original_result.txt')
+
+        Or in command line, use ``md5sum`` command if available:
+
+        .. code-block:: sh
+            md5sum some_file.txt
+
+        Finaly, you can use Python ``hashlib`` to obtain MD5::
+
+            import hashlib
+            hasher = hashlib.md5()
+            # expecting the file to fit into memory
+            hasher.update(open('original_result.txt', 'rb').read())
+            hasher.hexdigest()
+        """
         self.assertFileExists(filename, msg=msg)
         if not file_md5(filename) == md5:
             standardMsg = 'File %s does not have the right MD5 sum' % filename
             self.fail(self._formatMessage(msg, standardMsg))
 
-    def assertFilesEqualMd5(self, filename, reference_filename, msg=None):
+    def assertFilesEqualMd5(self, filename, reference, msg=None):
+        """Test that files are the same using MD5 sum.
+
+        This functions requires you to provide a file to test and
+        a reference file. For both, MD5 sum will be computed and compared with
+        each other.
+        """
         self.assertFileExists(filename, msg=msg)
         # nothing for ref, missing ref_filename is an error not a test failure
-        if not files_equal_md5(filename, reference_filename):
+        if not files_equal_md5(filename, reference):
             stdmsg = 'Files %s and %s don\'t have the same MD5 sums' % (filename,
-                                                                        reference_filename)
+                                                                        reference)
             self.fail(self._formatMessage(msg, stdmsg))
 
     # TODO: this function might be useful but it is questionable if it is really needed
@@ -136,6 +225,7 @@
     # TODO: this should be the function used for valgrind or profiling or debug
     # TODO: how these functions will behave when doing some benchmark with stdin/stdout?
     # TODO: should call fail instead of raising? probably not, it is not assert but...
+    # TODO: we can also comapre time to some expected but that's tricky
     # maybe we should measure time but the real benchmarks with stdin/stdout
     # should be done by some other function
     # can accept stdin
@@ -144,6 +234,10 @@
     def run_module(self, module, stdin=None, **kwargs):
         """Run module and save stdout and stderr.
 
+        Use this function to call the module you are currently testing.
+        Do not use this function to call modules when preparing data,
+        or testing the results.
+
         This function should be used when you expect the module to succeed
         and you are testing outputs of the module other than stdout.
         For example, if you are interested in raster map it produced.

Modified: sandbox/wenzeslaus/gunittest/testsuite/test_assertions.py
===================================================================
--- sandbox/wenzeslaus/gunittest/testsuite/test_assertions.py	2014-06-27 15:14:01 UTC (rev 61017)
+++ sandbox/wenzeslaus/gunittest/testsuite/test_assertions.py	2014-06-27 19:51:37 UTC (rev 61018)
@@ -8,6 +8,8 @@
 import sys
 import os
 
+import grass.script.core as gcore
+
 # import gunittest as a package so that relative imports there works
 sys.path.insert(0, os.path.split(os.path.split((os.path.dirname(__file__)))[0])[0])
 from gunittest.case import GrassTestCase
@@ -24,10 +26,26 @@
                           "Generated map is <...>")
         self.assertLooksLike("Projection string: '+proj=longlat +datum=WGS84'",
                              "Projection string: ...")
+
+    def test_assertLooksLike_multiline(self):
         self.assertLooksLike("a=123\nb=456\nc=789",
                              "a=...\nb=...\nc=...")
 
+    def test_assertLooksLike_numbers(self):
+        self.assertLooksLike("abc = 125521",
+                             "abc = 125...")
+        self.assertLooksLike("abc = 689.156",
+                             "abc = 689...")
+        self.assertLooksLike("abc = 689.159589",
+                             "abc = 689.15...")
+        # this should fail accoring to the implementation
+        # first three dots are considered as ellipses
+        self.assertRaises(self.failureException,
+                          self.assertLooksLike,
+                          "abc = 689.159589",
+                          "abc = 689....")
 
+
 R_UNIVAR_ELEVATION_SUBSET = """n=2025000
 null_cells=0
 min=55.5787925720215
@@ -45,6 +63,17 @@
 datatype=FCELL
 """
 
+# r.info -gre map=elevation
+ELEVATION_MAPSET_DICT = {'mapset': 'PERMANENT'}
+
+# r.univar map=elevation
+ELEVATION_MINMAX = """min=55.5787925720215
+max=156.329864501953
+"""
+
+# values rounded manually to maximal expected perecision
+ELEVATION_MINMAX_DICT = {'min': 55.58, 'max': 156.33}
+
 V_UNIVAR_BRIDGES_WIDTH_SUBSET = """n=10938
 nmissing=0
 nnull=0
@@ -75,7 +104,15 @@
                           self.assertRasterFitsInfo,
                           'elevation', RANDOM_KEYVALUES)
 
+    def test_common_values_info_univar(self):
+        self.assertRasterFitsUnivar('elevation', ELEVATION_MINMAX)
+        self.assertRasterFitsInfo('elevation', ELEVATION_MINMAX)
 
+    def test_dict_as_parameter(self):
+        # this also tests if we are using r.info -e flag
+        self.assertRasterFitsInfo('elevation', ELEVATION_MAPSET_DICT)
+
+
 class TestVectorMapAssertations(GrassTestCase):
     # pylint: disable=R0904
     def test_assertVectorFitsUnivar(self):
@@ -89,3 +126,66 @@
                           self.assertVectorFitsUnivar,
                           map='bridges', column='WIDTH',
                           reference=RANDOM_KEYVALUES)
+
+
+class TestFileAssertations(GrassTestCase):
+    # pylint: disable=R0904
+
+    @classmethod
+    def setUpClass(cls):
+        # we expect WIND to be always present
+        gisenv = gcore.gisenv()
+        cls.existing_file = os.path.join(gisenv['GISDBASE'],
+                                         gisenv['LOCATION_NAME'],
+                                         'PERMANENT', 'WIND')
+        cls.emtpy_file = cls.__name__ + '_this_is_an_empty_file'
+        open(cls.emtpy_file, 'w').close()
+        cls.file_with_md5 = cls.__name__ + '_this_is_a_file_with_known_md5'
+        file_content = 'Content of the file with known MD5.\n'
+        with open(cls.file_with_md5, 'w') as f:
+            f.write(file_content)
+        # MD5 sum created using:
+        # echo 'Content of the file with known MD5.' > some_file.txt
+        # md5sum some_file.txt
+        cls.file_md5 = '807bba4ffac4bb351bc3f27853009949'
+
+        cls.file_with_same_content = cls.__name__ + '_file_with_same_content'
+        with open(cls.file_with_same_content, 'w') as f:
+            f.write(file_content)
+
+        cls.file_with_different_content = cls.__name__ + '_file_with_different_content'
+        with open(cls.file_with_different_content, 'w') as f:
+            f.write(file_content + ' Something else here.')
+
+    @classmethod
+    def tearDownClass(cls):
+        os.remove(cls.emtpy_file)
+        os.remove(cls.file_with_md5)
+        os.remove(cls.file_with_same_content)
+        os.remove(cls.file_with_different_content)
+
+    def test_assertFileExists(self):
+        self.assertFileExists(filename=self.existing_file)
+        self.assertRaises(self.failureException,
+                          self.assertFileExists,
+                          filename='this_one_does_not_exists')
+
+    def test_assertFileExists_empty_file(self):
+        self.assertFileExists(filename=self.emtpy_file, skip_size_check=True)
+        self.assertRaises(self.failureException,
+                          self.assertFileExists,
+                          filename=self.emtpy_file)
+
+    def test_assertFileMd5(self):
+        self.assertFileMd5(filename=self.file_with_md5, md5=self.file_md5)
+        self.assertRaises(self.failureException,
+                          self.assertFileMd5,
+                          filename=self.file_with_md5, md5='wrongmd5')
+
+    def test_assertFilesEqualMd5(self):
+        self.assertFilesEqualMd5(filename=self.file_with_md5,
+                                 reference=self.file_with_same_content)
+        self.assertRaises(self.failureException,
+                          self.assertFilesEqualMd5,
+                          filename=self.file_with_md5,
+                          reference=self.file_with_different_content)



More information about the grass-commit mailing list