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

svn_grass at osgeo.org svn_grass at osgeo.org
Thu Jul 3 07:42:23 PDT 2014


Author: wenzeslaus
Date: 2014-07-03 07:42:23 -0700 (Thu, 03 Jul 2014)
New Revision: 61137

Added:
   sandbox/wenzeslaus/gunittest/invoker.py
   sandbox/wenzeslaus/gunittest/main.py
Removed:
   sandbox/wenzeslaus/gunittest/main.py
Modified:
   sandbox/wenzeslaus/gunittest/utils.py
Log:
gunittest: split main into invoker, main and utils

Copied: sandbox/wenzeslaus/gunittest/invoker.py (from rev 61133, sandbox/wenzeslaus/gunittest/main.py)
===================================================================
--- sandbox/wenzeslaus/gunittest/invoker.py	                        (rev 0)
+++ sandbox/wenzeslaus/gunittest/invoker.py	2014-07-03 14:42:23 UTC (rev 61137)
@@ -0,0 +1,141 @@
+# -*- coding: utf-8 -*-
+
+import os
+import sys
+import shutil
+import string
+import subprocess
+import datetime
+
+from unittest.main import TestProgram, USAGE_AS_MAIN
+TestProgram.USAGE = USAGE_AS_MAIN
+
+from loader import GrassTestLoader, discover_modules
+import utils
+
+import grass.script.setup as gsetup
+
+
+class GrassTestFilesInvoker(object):
+
+    # 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 _create_mapset(self, gisdbase, location, module):
+        """Create mapset according to informations in module.
+
+        :param loader.GrassTestPythonModule module:
+        """
+        # 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
+        # note that backslash cannot be at the end of raw string
+        dir_as_name = module.tested_dir.translate(string.maketrans(r'/\.', '___'))
+        mapset = dir_as_name + '_' + module.name
+        # TODO: use grass module to do this? but we are not in the right gisdbase
+        mapset_dir = os.path.join(gisdbase, location, mapset)
+        if self.clean_before:
+            utils.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 also 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))
+        return mapset, mapset_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")
+        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
+        utils.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)
+            utils.ensure_dir(os.path.abspath(cwd))
+            # TODO: put this to constructor and copy here again
+            env = os.environ.copy()
+            mapset, mapset_dir = self._create_mapset(gisdbase, location, module)
+            gisrc = gsetup.write_gisrc(gisdbase, location, mapset)
+            env['GISRC'] = gisrc
+
+            # 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
+            # TODO: use some grass function to run?
+            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>')
+        main_index.close()

Deleted: sandbox/wenzeslaus/gunittest/main.py
===================================================================
--- sandbox/wenzeslaus/gunittest/main.py	2014-07-03 14:42:15 UTC (rev 61136)
+++ sandbox/wenzeslaus/gunittest/main.py	2014-07-03 14:42:23 UTC (rev 61137)
@@ -1,262 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import os
-import sys
-import shutil
-import errno
-import string
-import subprocess
-import datetime
-
-from unittest.main import TestProgram, USAGE_AS_MAIN
-TestProgram.USAGE = USAGE_AS_MAIN
-
-from loader import GrassTestLoader, discover_modules
-from runner import GrassTestRunner
-
-import grass.script.setup as gsetup
-import grass.script.core as gcore
-
-
-class GrassTestProgram(TestProgram):
-
-    def __init__(self, exit_at_end, grass_location, clean_outputs=True,
-                 unittest_argv=None,  module=None,
-                 verbosity=1,
-                 failfast=None, catchbreak=None):
-        """Prepares the tests in GRASS way and then runs the tests.
-
-        :param bool clean_outputs: if outputs in mapset and in ?
-        """
-        self.test = None
-        self.grass_location = grass_location
-        # it is unclear what the exact behavior is in unittest
-        # buffer stdout and stderr during tests
-        buffer_stdout_stderr = False
-
-        grass_loader = GrassTestLoader(grass_location=self.grass_location)
-        grass_runner = GrassTestRunner(verbosity=verbosity,
-                                       failfast=failfast,
-                                       buffer=buffer_stdout_stderr)
-
-        super(GrassTestProgram, self).__init__(module=module,
-                                               argv=unittest_argv,
-                                               testLoader=grass_loader,
-                                               testRunner=grass_runner,
-                                               exit=exit_at_end,
-                                               verbosity=verbosity,
-                                               failfast=failfast,
-                                               catchbreak=catchbreak,
-                                               buffer=buffer_stdout_stderr)
-
-
-def test():
-    """Run a test of a module.
-    """
-    import coverage
-    cov = coverage.coverage(omit="*testsuite*")
-    cov.start()
-
-    # TODO: enable passing omit to exclude also gunittest or nothing
-    program = GrassTestProgram(module='__main__', exit_at_end=False, grass_location='all')
-
-    cov.stop()
-    cov.html_report(directory='testcodecoverage')
-
-    sys.exit(not program.result.wasSuccessful())
-
-
-# TODO: test or main? test looks more general
-main = test
-
-
-def discovery():
-    """Recursively find all tests in testsuite directories and run them
-
-    Everything is imported and runs in this process.
-
-    Runs using::
-        python main.py discovery [start_directory]
-    """
-    import coverage
-    cov = coverage.coverage(omit="*testsuite*")
-    cov.start()
-
-    program = GrassTestProgram(grass_location='nc', exit_at_end=False)
-
-    cov.stop()
-    cov.html_report(directory='testcodecoverage')
-
-    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 silent_rmtree(filename):
-    """Remove the file but do nothing if file does not exist."""
-    try:
-        shutil.rmtree(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
-
-
-class GrassTestFilesInvoker(object):
-
-    # 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 _create_mapset(self, module):
-        """Create mapset according to informations in module.
-        
-        :param loader.GrassTestPythonModule module:
-        """
-        # 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
-        # TODO: use grass module to do this? but we are not in the right gisdbase
-        mapset_dir = os.path.join(gisdbase, location, mapset)
-        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))
-        return mapset, mapset_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")
-        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()
-            mapset, mapset_dir = self._create_mapset(module)
-            gisrc = gsetup.write_gisrc(gisdbase, location, mapset)
-            env['GISRC'] = gisrc
-
-            # 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
-            # TODO: use some grass function to run?
-            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>')
-        main_index.close()
-
-
-# TODO: create a full interface (using grass parser or argparse)
-if __name__ == '__main__':
-    if len(sys.argv) == 4:
-        gisdbase = sys.argv[1]
-        location = sys.argv[2]
-        location_shortcut = sys.argv[3]
-    elif len(sys.argv) == 3:
-        location = sys.argv[1]
-        location_shortcut = sys.argv[2]
-        gisdbase = gcore.gisenv()['GISDBASE']
-    else:
-        sys.stderr.write("Usage: %s [gisdbase] location location_shortcut\n" % sys.argv[0])
-        sys.exit(1)
-    assert gisdbase
-    if not os.path.exists(gisdbase):
-        sys.stderr.write("GISDBASE <%s> does not exist\n" % gisdbase)
-        sys.exit(1)
-    results_dir = 'testresults'
-    silent_rmtree(results_dir)  # TODO: too brute force?
-
-    invoker = GrassTestFilesInvoker(start_dir='.')
-    invoker.run_in_location(gisdbase=gisdbase,
-                            location=location,
-                            location_shortcut=location_shortcut,
-                            results_dir=results_dir)

Added: sandbox/wenzeslaus/gunittest/main.py
===================================================================
--- sandbox/wenzeslaus/gunittest/main.py	                        (rev 0)
+++ sandbox/wenzeslaus/gunittest/main.py	2014-07-03 14:42:23 UTC (rev 61137)
@@ -0,0 +1,114 @@
+# -*- coding: utf-8 -*-
+
+import os
+import sys
+
+from unittest.main import TestProgram, USAGE_AS_MAIN
+TestProgram.USAGE = USAGE_AS_MAIN
+
+from loader import GrassTestLoader
+from runner import GrassTestRunner
+from invoker import GrassTestFilesInvoker
+import utils
+
+import grass.script.core as gcore
+
+
+class GrassTestProgram(TestProgram):
+
+    def __init__(self, exit_at_end, grass_location, clean_outputs=True,
+                 unittest_argv=None,  module=None,
+                 verbosity=1,
+                 failfast=None, catchbreak=None):
+        """Prepares the tests in GRASS way and then runs the tests.
+
+        :param bool clean_outputs: if outputs in mapset and in ?
+        """
+        self.test = None
+        self.grass_location = grass_location
+        # it is unclear what the exact behavior is in unittest
+        # buffer stdout and stderr during tests
+        buffer_stdout_stderr = False
+
+        grass_loader = GrassTestLoader(grass_location=self.grass_location)
+        grass_runner = GrassTestRunner(verbosity=verbosity,
+                                       failfast=failfast,
+                                       buffer=buffer_stdout_stderr)
+
+        super(GrassTestProgram, self).__init__(module=module,
+                                               argv=unittest_argv,
+                                               testLoader=grass_loader,
+                                               testRunner=grass_runner,
+                                               exit=exit_at_end,
+                                               verbosity=verbosity,
+                                               failfast=failfast,
+                                               catchbreak=catchbreak,
+                                               buffer=buffer_stdout_stderr)
+
+
+def test():
+    """Run a test of a module.
+    """
+    import coverage
+    cov = coverage.coverage(omit="*testsuite*")
+    cov.start()
+
+    # TODO: enable passing omit to exclude also gunittest or nothing
+    program = GrassTestProgram(module='__main__', exit_at_end=False, grass_location='all')
+
+    cov.stop()
+    cov.html_report(directory='testcodecoverage')
+
+    sys.exit(not program.result.wasSuccessful())
+
+
+# TODO: test or main? test looks more general
+# unittest has main() but doctest has testmod()
+main = test
+
+
+def discovery():
+    """Recursively find all tests in testsuite directories and run them
+
+    Everything is imported and runs in this process.
+
+    Runs using::
+        python main.py discovery [start_directory]
+    """
+    import coverage
+    cov = coverage.coverage(omit="*testsuite*")
+    cov.start()
+
+    program = GrassTestProgram(grass_location='nc', exit_at_end=False)
+
+    cov.stop()
+    cov.html_report(directory='testcodecoverage')
+
+    sys.exit(not program.result.wasSuccessful())
+
+
+# TODO: create a full interface (using grass parser or argparse)
+if __name__ == '__main__':
+    if len(sys.argv) == 4:
+        gisdbase = sys.argv[1]
+        location = sys.argv[2]
+        location_shortcut = sys.argv[3]
+    elif len(sys.argv) == 3:
+        location = sys.argv[1]
+        location_shortcut = sys.argv[2]
+        gisdbase = gcore.gisenv()['GISDBASE']
+    else:
+        sys.stderr.write("Usage: %s [gisdbase] location location_shortcut\n" % sys.argv[0])
+        sys.exit(1)
+    assert gisdbase
+    if not os.path.exists(gisdbase):
+        sys.stderr.write("GISDBASE <%s> does not exist\n" % gisdbase)
+        sys.exit(1)
+    results_dir = 'testresults'
+    utils.silent_rmtree(results_dir)  # TODO: too brute force?
+
+    invoker = GrassTestFilesInvoker(start_dir='.')
+    invoker.run_in_location(gisdbase=gisdbase,
+                            location=location,
+                            location_shortcut=location_shortcut,
+                            results_dir=results_dir)

Modified: sandbox/wenzeslaus/gunittest/utils.py
===================================================================
--- sandbox/wenzeslaus/gunittest/utils.py	2014-07-03 14:42:15 UTC (rev 61136)
+++ sandbox/wenzeslaus/gunittest/utils.py	2014-07-03 14:42:23 UTC (rev 61137)
@@ -1,6 +1,27 @@
+import os
 import sys
+import shutil
+import errno
 
 
+# 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 silent_rmtree(filename):
+    """Remove the file but do nothing if file does not exist."""
+    try:
+        shutil.rmtree(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
+
+
 def do_doctest_gettext_workaround():
     """Setups environment for doing a doctest with gettext usage.
 



More information about the grass-commit mailing list