[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