[GRASS-SVN] r64734 - in grass/trunk/lib/python/gunittest: . testsuite

svn_grass at osgeo.org svn_grass at osgeo.org
Tue Feb 24 20:49:29 PST 2015


Author: wenzeslaus
Date: 2015-02-24 20:49:29 -0800 (Tue, 24 Feb 2015)
New Revision: 64734

Modified:
   grass/trunk/lib/python/gunittest/__init__.py
   grass/trunk/lib/python/gunittest/case.py
   grass/trunk/lib/python/gunittest/checkers.py
   grass/trunk/lib/python/gunittest/testsuite/test_checkers.py
Log:
gunittest: support text files for MD5 sum comparisons in multiplatform way

Modified: grass/trunk/lib/python/gunittest/__init__.py
===================================================================
--- grass/trunk/lib/python/gunittest/__init__.py	2015-02-25 00:56:29 UTC (rev 64733)
+++ grass/trunk/lib/python/gunittest/__init__.py	2015-02-25 04:49:29 UTC (rev 64734)
@@ -12,6 +12,8 @@
 by Vaclav Petras as a student and Soeren Gebbert as a mentor.
 """
 
+# TODO: consider removing all from here before the backport or release
+
 from __future__ import print_function
 
 try:

Modified: grass/trunk/lib/python/gunittest/case.py
===================================================================
--- grass/trunk/lib/python/gunittest/case.py	2015-02-25 00:56:29 UTC (rev 64733)
+++ grass/trunk/lib/python/gunittest/case.py	2015-02-25 04:49:29 UTC (rev 64734)
@@ -22,7 +22,7 @@
 from .gmodules import call_module, SimpleModule
 from .checkers import (check_text_ellipsis,
                        text_to_keyvalue, keyvalue_equals, diff_keyvalue,
-                       file_md5, files_equal_md5)
+                       file_md5, text_file_md5, files_equal_md5)
 from .utils import safe_repr
 from .gutils import is_map_in_mapset
 
@@ -35,6 +35,8 @@
 
     Always use keyword arguments for all parameters other than first two. For
     the first two, it is recommended to use keyword arguments but not required.
+    Be especially careful and always use keyword argument syntax for *msg*
+    parameter.
     """
     longMessage = True  # to get both standard and custom message
     maxDiff = None  # we can afford long diffs
@@ -543,35 +545,52 @@
             stdmsg = 'File %s is not accessible 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.
+    def assertFileMd5(self, filename, md5, text=False, msg=None):
+        r"""Test that file MD5 sum is equal to the provided sum.
 
+        Usually, this function is used to test binary files or large text files
+        which cannot be tested in some other way. Text files can be usually
+        tested by some finer method.
+
+        To test text files with this function, you should always use parameter
+        *text* set to ``True``. Note that function ``checkers.text_file_md5()``
+        offers additional parameters which might be advantageous when testing
+        text files.
+
         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...')
+            self.assertFileMd5('result.png', md5='807bba4ffa...')
 
         Use `file_md5()` function from this package::
 
-            file_md5('original_result.txt')
+            file_md5('original_result.png')
 
         Or in command line, use ``md5sum`` command if available:
 
         .. code-block:: sh
 
-            md5sum some_file.txt
+            md5sum some_file.png
 
         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.update(open('original_result.png', 'rb').read())
             hasher.hexdigest()
+
+        .. note:
+            For text files, always create MD5 sum using ``\n`` (LF)
+            as newline characters for consistency. Also use newline
+            at the end of file (as for example, Git or PEP8 requires).
         """
         self.assertFileExists(filename, msg=msg)
-        actual = file_md5(filename)
+        if text:
+            actual = text_file_md5(filename)
+        else:
+            actual = file_md5(filename)
         if not actual == md5:
             standardMsg = ('File <{name}> does not have the right MD5 sum.\n'
                            'Expected is <{expected}>,'

Modified: grass/trunk/lib/python/gunittest/checkers.py
===================================================================
--- grass/trunk/lib/python/gunittest/checkers.py	2015-02-25 00:56:29 UTC (rev 64733)
+++ grass/trunk/lib/python/gunittest/checkers.py	2015-02-25 04:49:29 UTC (rev 64734)
@@ -9,6 +9,7 @@
 :authors: Vaclav Petras, Soeren Gebbert
 """
 
+import os
 import sys
 import re
 import doctest
@@ -570,17 +571,43 @@
     return hasher.hexdigest()
 
 
-def text_file_md5(filename, exclude_lines=None,
+def text_file_md5(filename, exclude_lines=None, exclude_re=None,
                   prepend_lines=None, append_lines=None):
     """Get a MD5 (check) sum of a text file.
 
-    Works in the same way as `file_md5()` function but allows to
-    exclude lines from the file as well as prepend or append them.
+    Works in the same way as `file_md5()` function but ignores newlines
+    characters and excludes lines from the file as well as prepend or
+    append them if requested.
 
-    .. todo::
-        Implement this function.
+    :param exclude_lines: list of strings to be excluded
+        (newline characters should not be part of the strings)
+    :param exclude_re: regular expression string;
+        lines matching this regular expression will not be considered
+    :param prepend_lines: list of lines to be prepended to the file
+        before computing the sum
+    :param append_lines: list of lines  to be appended to the file
+        before computing the sum
     """
-    raise NotImplementedError("Implement, or use file_md5() function instead")
+    hasher = hashlib.md5()
+    if exclude_re:
+        regexp = re.compile(exclude_re)
+    if prepend_lines:
+        for line in prepend_lines:
+            hasher.update(line)
+    with open(filename, 'r') as f:
+        for line in f:
+            # replace platform newlines by standard newline
+            if os.linesep != '\n':
+                line = line.rstrip(os.linesep) + '\n'
+            if exclude_lines and line in exclude_lines:
+                continue
+            if exclude_re and regexp.match(line):
+                continue
+            hasher.update(line)
+    if append_lines:
+        for line in append_lines:
+            hasher.update(line)
+    return hasher.hexdigest()
 
 
 def files_equal_md5(filename_a, filename_b):

Modified: grass/trunk/lib/python/gunittest/testsuite/test_checkers.py
===================================================================
--- grass/trunk/lib/python/gunittest/testsuite/test_checkers.py	2015-02-25 00:56:29 UTC (rev 64733)
+++ grass/trunk/lib/python/gunittest/testsuite/test_checkers.py	2015-02-25 04:49:29 UTC (rev 64734)
@@ -14,13 +14,16 @@
 """
 
 
-from grass.script.utils import parse_key_val
+from grass.script.utils import parse_key_val, try_remove
 
 import grass.gunittest
-from grass.gunittest.checkers import (values_equal, text_to_keyvalue,
-    keyvalue_equals, proj_info_equals, proj_units_equals)
+from grass.gunittest.checkers import (
+    values_equal, text_to_keyvalue,
+    keyvalue_equals, proj_info_equals, proj_units_equals,
+    file_md5, text_file_md5)
 
 
+
 class TestValuesEqual(grass.gunittest.TestCase):
 
     def test_floats(self):
@@ -308,6 +311,87 @@
                                                           sep='='),
                                          precision=0.001))
 
+CORRECT_LINES = [
+    "null_cells=57995100",
+    "cells=60020100",
+    "min=55.5787925720215",
+    "max=156.329864501953"
+]
 
+INCORRECT_LINES = [
+    "null_cells=579951",
+    "cells=60020100",
+    "min=5.5787925720215",
+    "max=156.329864501953"
+]
+
+
+class TestMd5Sums(grass.gunittest.TestCase):
+    r"""
+
+    To create MD5 which is used for testing use:
+
+    .. code: sh
+    $ cat > test.txt << EOF
+    null_cells=57995100
+    cells=60020100
+    min=55.5787925720215
+    max=156.329864501953
+    EOF
+    $ md5sum test.txt
+    9dd6c4bb9d2cf6051b12f4b5f9d70523  test.txt
+    """
+
+    correct_md5sum = '9dd6c4bb9d2cf6051b12f4b5f9d70523'
+    correct_file_name_platform_nl = 'md5_sum_correct_file_platform_nl'
+    correct_file_name_unix_nl = 'md5_sum_correct_file_unix_nl'
+    wrong_file_name = 'md5_sum_wrong_file'
+
+    @classmethod
+    def setUpClass(cls):
+        with open(cls.correct_file_name_platform_nl, 'w') as f:
+            for line in CORRECT_LINES:
+                # \n should be converted to platform newline
+                f.write(line + '\n')
+        with open(cls.correct_file_name_unix_nl, 'wb') as f:
+            for line in CORRECT_LINES:
+                # binary mode will write pure \n
+                f.write(line + '\n')
+        with open(cls.wrong_file_name, 'w') as f:
+            for line in INCORRECT_LINES:
+                # \n should be converted to platform newline
+                f.write(line + '\n')
+
+    @classmethod
+    def tearDownClass(cls):
+        try_remove(cls.correct_file_name_platform_nl)
+        try_remove(cls.correct_file_name_unix_nl)
+        try_remove(cls.wrong_file_name)
+
+    def test_text_file_binary(self):
+        r"""File with ``\n`` (LF) newlines as binary (MD5 has ``\n``)."""
+        self.assertEquals(file_md5(self.correct_file_name_unix_nl),
+                          self.correct_md5sum,
+                          msg="MD5 sums different")
+
+    def test_text_file_platfrom(self):
+        r"""Text file with platform dependent newlines"""
+        self.assertEquals(text_file_md5(self.correct_file_name_platform_nl),
+                          self.correct_md5sum,
+                          msg="MD5 sums different")
+
+    def test_text_file_unix(self):
+        r"""Text file with ``\n`` (LF) newlines"""
+        self.assertEquals(text_file_md5(self.correct_file_name_unix_nl),
+                          self.correct_md5sum,
+                          msg="MD5 sums different")
+
+    def test_text_file_different(self):
+        r"""Text file with ``\n`` (LF) newlines"""
+        self.assertNotEquals(text_file_md5(self.wrong_file_name),
+                             self.correct_md5sum,
+                             msg="MD5 sums must be different")
+
+
 if __name__ == '__main__':
     grass.gunittest.test()



More information about the grass-commit mailing list