[GRASS-SVN] r61125 - sandbox/wenzeslaus/gunittest

svn_grass at osgeo.org svn_grass at osgeo.org
Wed Jul 2 13:21:06 PDT 2014


Author: wenzeslaus
Date: 2014-07-02 13:21:05 -0700 (Wed, 02 Jul 2014)
New Revision: 61125

Modified:
   sandbox/wenzeslaus/gunittest/case.py
   sandbox/wenzeslaus/gunittest/main.py
Log:
gunittest: invoking function converted to class

Modified: sandbox/wenzeslaus/gunittest/case.py
===================================================================
--- sandbox/wenzeslaus/gunittest/case.py	2014-07-02 19:02:12 UTC (rev 61124)
+++ sandbox/wenzeslaus/gunittest/case.py	2014-07-02 20:21:05 UTC (rev 61125)
@@ -40,7 +40,8 @@
         """Use temporary region instead of the standard one for this process.
 
         If you use this method, you have to call it in `setUp()` and call
-        `del_temp_region()` in `tearDown()`.
+        `del_temp_region()` in `tearDown()`. By this you ensure that each test
+        method will have its own region and will not influece others.
 
         Copies the current region to a temporary region with
         "g.region save=", then sets WIND_OVERRIDE to refer to that region.
@@ -93,6 +94,7 @@
     # (note that we don't need precision for strings and usually for integers)
     # TODO: auto-determine precision based on the map type
     # TODO: we can have also more general function without the subset reference
+    # TODO: implement this also for PyGRASS Module
     def assertCommandKeyValue(self, module, parameters, reference, sep,
                               precision=None, msg=None):
         if isinstance(reference, basestring):

Modified: sandbox/wenzeslaus/gunittest/main.py
===================================================================
--- sandbox/wenzeslaus/gunittest/main.py	2014-07-02 19:02:12 UTC (rev 61124)
+++ sandbox/wenzeslaus/gunittest/main.py	2014-07-02 20:21:05 UTC (rev 61125)
@@ -3,6 +3,9 @@
 import os
 import sys
 import shutil
+import errno
+import string
+import subprocess
 
 from unittest.main import TestProgram, USAGE_AS_MAIN
 TestProgram.USAGE = USAGE_AS_MAIN
@@ -46,7 +49,7 @@
                                                buffer=buffer_stdout_stderr)
 
 
-def main():
+def test():
     """Run a test of a module.
     """
     import coverage
@@ -61,7 +64,8 @@
     sys.exit(not program.result.wasSuccessful())
 
 
-test = main
+# TODO: test or main? test looks more general
+main = test
 
 
 def discovery():
@@ -84,97 +88,151 @@
     sys.exit(not program.result.wasSuccessful())
 
 
+# TODO: move these to utils or even somewhere more general
 def ensure_dir(directory):
+    """Create all directories in the given path if needed."""
     if not os.path.exists(directory):
         os.makedirs(directory)
 
 
-def recursive_runs():
-    grass_location = 'nc'
-    # here should never be the same as start_dir, it leads to confusing (and dangerous) dir tree
-    results_root = 'testresults'
-    import subprocess
-    modules = discover_modules(start_dir='.',
-                               file_pattern=GrassTestLoader.files_in_testsuite,
-                               skip_dirs=GrassTestLoader.skip_dirs,
-                               testsuite_dir=GrassTestLoader.testsuite_dir,
-                               grass_location=grass_location,
-                               all_locations_value=GrassTestLoader.all_tests_value,
-                               universal_location_value=GrassTestLoader.universal_tests_value,
-                               import_modules=False)
-    ensure_dir(os.path.abspath(results_root))
-    main_index = open(os.path.join(results_root, 'index.html'), 'w')
-    main_index.write('<html><body>'
-                     '<ul>')
-    for module in modules:
-        cwd = os.path.join(results_root, module.tested_dir, module.name)
-        ensure_dir(os.path.abspath(cwd))
+def silent_rmtree(filename):
+    """Remove the file but do nothing if file does not exist."""
+    try:
+        os.remove(filename)
+    except OSError as e:
+        # errno.ENOENT is "No such file or directory"
+        # re-raise if a different error occured
+        if e.errno != errno.ENOENT:
+            raise
 
-        env = os.environ.copy()
-        # using path.sep but also / and \ for cases when it is confused
-        # (namely the case of Unix path on MS Windows)
-        # replace . to get rid of unclean path
-        # TODO: clean paths
-        dir_as_name = module.tested_dir.replace('.', '_').replace('/', '_').replace('\\', '_').replace(os.path.sep, '_')
-        mapset = dir_as_name + '_' + module.name
-        location = 'nc_spm_08_grass7_tests'
-        gisdbase = gcore.gisenv()['GISDBASE']
-        assert gisdbase
-        gisrc = gsetup.write_gisrc(gisdbase, location, mapset)
-        env['GISRC'] = gisrc
-        # TODO: use grass module to do this?
-        mapset_dir = os.path.join(gisdbase, location, mapset)
-        # TODO: perhaps remove dir if exists
-        os.mkdir(mapset_dir)
-        # TODO: default region in mapset will be what?
-        # copy WIND file from PERMANENT
-        # TODO: this should be a function in grass.script (used also in gis_set.py, PyGRASS alos has its way with Mapset)
-        # TODO: are premisions an issue here?
-        shutil.copy(os.path.join(gisdbase, location, 'PERMANENT', 'WIND'),
-                    os.path.join(mapset_dir))
 
-        # TODO: we don't do any HTML escaping, use txt file
-        stdout = open(os.path.join(cwd, 'stdout.html'), 'w')
-        stderr = open(os.path.join(cwd, 'stderr.html'), 'w')
-        stdout.write('<html><body><h1>%s</h1><pre>' % (module.name + ' stdout'))
-        stderr.write('<html><body><h1>%s</h1><pre>' % (module.name + ' stderr'))
-        stdout.flush()  # we need to flush to write our changes before stdout
-        stderr.flush()
+class GrassTestFilesInvoker(object):
 
-        # TODO: we might clean the directory here before test if non-empty
-        p = subprocess.Popen([sys.executable, module.abs_file_path],
-                             cwd=cwd, env=env,
-                             stdout=stdout, stderr=stderr)
-        returncode = p.wait()
-        stdout.flush()
-        stderr.flush()
-        stdout.write('</pre></body></html>')
-        stderr.write('</pre></body></html>')
-        file_index_path = os.path.join(cwd, 'index.html')        
-        if returncode:
-            sys.stderr.write('{d}/{m} failed (see {f})\n'.format(d=module.tested_dir,
-                                                                 m=module.name,
-                                                                 f=file_index_path))
-        main_index.write('<li><a href="{d}/{m}/index.html">{d}/{m}</a>'.format(d=module.tested_dir, m=module.name))
-        file_index = open(file_index_path, 'w')
-        file_index.write('<html><body>'
-                         '<ul>'
-                         '<li><a href="stdout.html">standard output (stdout)</a>'
-                         '<li><a href="stderr.html">standard error output (stderr)</a>'
-                         '<li><a href="testcodecoverage/index.html">code coverage</a>'
-                         '</ul>'
+    # TODO: it is not clear what clean_outputs mean, if should be split
+    # std stream, random outputs, saved results, profiling
+    # not stdout and stderr if they contain test results
+    # we can also save only failed tests, or generate only if assert fails
+    def __init__(self, start_dir,
+                 clean_mapsets=True, clean_outputs=True, clean_before=True,
+                 testsuite_dir='testsuite'):
+        """
+
+        :param bool clean_mapsets: if the mapsets should be removed
+        :param bool clean_outputs: unclear: random tests outputs, saved images from maps, profiling
+        :param bool clean_before: if mapsets, outputs, and results
+            should be removed before the tests start
+            (advantageous when the previous run left everything behind)
+        """
+        self.start_dir = start_dir
+        self.clean_mapsets = clean_mapsets
+        self.clean_outputs = clean_outputs
+        self.clean_before = clean_before
+        self.testsuite_dir = testsuite_dir
+
+    def run_in_location(self, gisdbase, location, location_shortcut,
+                        results_dir):
+        if os.path.abspath(results_dir) == os.path.abspath(self.start_dir):
+            raise RuntimeError("Results root directory should not be the same"
+                               " as discovery start directory")
+
+        import datetime
+        main_start_time = datetime.datetime.now()
+
+        # TODO: move constants out of loader class or even module
+        modules = discover_modules(start_dir=self.start_dir,
+                                   grass_location=location_shortcut,
+                                   file_pattern=GrassTestLoader.files_in_testsuite,
+                                   skip_dirs=GrassTestLoader.skip_dirs,
+                                   testsuite_dir=GrassTestLoader.testsuite_dir,
+                                   all_locations_value=GrassTestLoader.all_tests_value,
+                                   universal_location_value=GrassTestLoader.universal_tests_value,
+                                   import_modules=False)
+        # TODO: no directory cleaning (self.clean_before)? now cleaned by caller
+        ensure_dir(os.path.abspath(results_dir))
+        main_index = open(os.path.join(results_dir, 'index.html'), 'w')
+        main_index.write('<html><body>'
+                         '<h1>Test results</h1>'
+                         '{time:%Y-%m-%d %H:%M:%S}'
+                         '<ul>'.format(time=main_start_time))
+        for module in modules:
+            cwd = os.path.join(results_dir, module.tested_dir, module.name)
+            ensure_dir(os.path.abspath(cwd))
+            # TODO: put this to constructor and copy here again
+            env = os.environ.copy()
+            # using path.sep but also / and \ for cases when it is confused
+            # (namely the case of Unix path on MS Windows)
+            # replace . to get rid of unclean path
+            # TODO: clean paths
+            dir_as_name = module.tested_dir.translate(string.maketrans('./\\', '___'))
+            #dir_as_name = module.tested_dir.replace('.', '_').replace('/', '_').replace('\\', '_').replace(os.path.sep, '_')
+            mapset = dir_as_name + '_' + module.name
+            location = location
+            gisrc = gsetup.write_gisrc(gisdbase, location, mapset)
+            env['GISRC'] = gisrc
+            # TODO: use grass module to do this?
+            mapset_dir = os.path.join(gisdbase, location, mapset)
+            # TODO: perhaps remove dir if exists
+            if self.clean_before:
+                silent_rmtree(mapset_dir)
+            os.mkdir(mapset_dir)
+            # TODO: default region in mapset will be what?
+            # copy WIND file from PERMANENT
+            # TODO: this should be a function in grass.script (used also in gis_set.py, PyGRASS alos has its way with Mapset)
+            # TODO: are premisions an issue here?
+            shutil.copy(os.path.join(gisdbase, location, 'PERMANENT', 'WIND'),
+                        os.path.join(mapset_dir))
+    
+            # TODO: we don't do any HTML escaping, use txt file
+            stdout = open(os.path.join(cwd, 'stdout.html'), 'w')
+            stderr = open(os.path.join(cwd, 'stderr.html'), 'w')
+            stdout.write('<html><body><h1>%s</h1><pre>' % (module.name + ' stdout'))
+            stderr.write('<html><body><h1>%s</h1><pre>' % (module.name + ' stderr'))
+            stdout.flush()  # we need to flush to write our changes before stdout
+            stderr.flush()
+    
+            # TODO: we might clean the directory here before test if non-empty
+            p = subprocess.Popen([sys.executable, module.abs_file_path],
+                                 cwd=cwd, env=env,
+                                 stdout=stdout, stderr=stderr)
+            returncode = p.wait()
+            stdout.flush()
+            stderr.flush()
+            stdout.write('</pre></body></html>')
+            stderr.write('</pre></body></html>')
+            file_index_path = os.path.join(cwd, 'index.html')        
+            if returncode:
+                sys.stderr.write('{d}/{m} failed (see {f})\n'.format(d=module.tested_dir,
+                                                                     m=module.name,
+                                                                     f=file_index_path))
+            main_index.write('<li><a href="{d}/{m}/index.html">{d}/{m}</a>'.format(d=module.tested_dir, m=module.name))
+            file_index = open(file_index_path, 'w')
+            file_index.write('<html><body>'
+                             '<ul>'
+                             '<li><a href="stdout.html">standard output (stdout)</a>'
+                             '<li><a href="stderr.html">standard error output (stderr)</a>'
+                             '<li><a href="testcodecoverage/index.html">code coverage</a>'
+                             '</ul>'
+                             '</body></html>')
+            stdout.close()
+            stderr.close()
+            file_index.close()
+            # TODO: add some try-except or with for better error handling
+            os.remove(gisrc)
+            # TODO: only if clean up
+            if self.clean_mapsets:
+                shutil.rmtree(mapset_dir)
+    
+        main_index.write('</ul>'
                          '</body></html>')
-        stdout.close()
-        stderr.close()
-        file_index.close()
-        os.remove(gisrc)
-        # TODO: only if clean up
-        shutil.rmtree(mapset_dir)
+        main_index.close()
 
-    main_index.write('</ul>'
-                     '</body></html>')
-    main_index.close()
 
+if __name__ == '__main__':
+    gisdbase = gcore.gisenv()['GISDBASE']
+    assert gisdbase
+    results_dir = 'testresults'
+    shutil.rmtree(results_dir)  # TODO: too brute force?
 
-if __name__ == '__main__':
-    recursive_runs()
+    invoker = GrassTestFilesInvoker(start_dir='.')
+    invoker.run_in_location(gisdbase=gisdbase, location='nc_spm_08_grass7_tests', location_shortcut='nc',
+                            results_dir=results_dir)



More information about the grass-commit mailing list