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

svn_grass at osgeo.org svn_grass at osgeo.org
Wed Jun 25 21:17:20 PDT 2014


Author: wenzeslaus
Date: 2014-06-25 21:17:20 -0700 (Wed, 25 Jun 2014)
New Revision: 60979

Added:
   sandbox/wenzeslaus/gunittest/gmodules.py
   sandbox/wenzeslaus/gunittest/utils.py
Modified:
   sandbox/wenzeslaus/gunittest/__init__.py
   sandbox/wenzeslaus/gunittest/testsuite/test_doctests.py
Log:
gunittest: adding custom fucntions for running modules (commands) based on start_command() function

Modified: sandbox/wenzeslaus/gunittest/__init__.py
===================================================================
--- sandbox/wenzeslaus/gunittest/__init__.py	2014-06-25 21:58:42 UTC (rev 60978)
+++ sandbox/wenzeslaus/gunittest/__init__.py	2014-06-26 04:17:20 UTC (rev 60979)
@@ -0,0 +1 @@
+import case, checkers, gmodules, utils

Added: sandbox/wenzeslaus/gunittest/gmodules.py
===================================================================
--- sandbox/wenzeslaus/gunittest/gmodules.py	                        (rev 0)
+++ sandbox/wenzeslaus/gunittest/gmodules.py	2014-06-26 04:17:20 UTC (rev 60979)
@@ -0,0 +1,133 @@
+# -*- coding: utf-8 -*-
+
+import subprocess
+from grass.script.core import start_command
+
+from .utils import do_doctest_gettext_workaround
+
+
+class CalledModuleError(subprocess.CalledProcessError):
+    def __init__(self, returncode, module, kwargs, errors=None):
+        # expecting module name to be args[0]
+        super(CalledModuleError, self).__init__(returncode, module)
+        # TODO: this must be somewhere when doctest is called, not here
+        # TODO: ultimate solution is not to use _ as a buildin in lib/python
+        # for now it is the only place where it works
+        do_doctest_gettext_workaround()
+        # TODO: format parameters
+        # TODO: ignore std* params
+        msg = _("Module run %s %s ended with error") % (module, kwargs)
+        msg += _("\nProcess ended with non-zero return code %s") % returncode
+        if errors:
+            msg += _(". See the following errors:\n%s") % errors
+        else:
+            msg += _(". See errors in the (error) output.")
+        self.msg = msg
+        # TODO: handle other parameters
+
+    def __str__(self):
+        return self.msg
+
+
+def run_module(module, **kwargs):
+    """Run module with parameters given in `kwargs`.
+
+    :param str module: module name
+    :param kwargs: module parameters
+
+    :raises CalledModuleError: if module return code is non-zero
+    """
+    process = start_command(module, **kwargs)
+    returncode = process.wait()
+    if returncode:
+        raise CalledModuleError(returncode, module, kwargs)
+
+
+def read_module(module, **kwargs):
+    """Run module with parameters given in `kwargs` and return its output.
+
+    :param str module: module name
+    :param kwargs: module parameters
+
+    :raises CalledModuleError: if module return code is non-zero
+    """
+    if 'stdout' in kwargs:
+        raise ValueError('stdout argument not allowed, it would be overridden.')
+    kwargs['stdout'] = subprocess.PIPE
+    process = start_command(module, **kwargs)
+    output = process.communicate()[0]
+    returncode = process.poll()
+    if returncode:
+        raise CalledModuleError(returncode, module, kwargs)
+    return output
+
+
+def write_module(module, stdin, **kwargs):
+    """Run module with given parameters and feed module standard input.
+
+    :param str module: module name
+    :param stdin: string to be used as module standard input (stdin)
+    :param kwargs: module parameters
+
+    :raises CalledModuleError: if module return code is non-zero
+    """
+    if not stdin:
+        raise ValueError('stdin argument is required.')
+    kwargs['stdin'] = subprocess.PIPE  # to be able to send data to stdin
+    process = start_command(module, **kwargs)
+    process.communicate(stdin)
+    returncode = process.poll()
+    if returncode:
+        raise CalledModuleError(returncode, module, kwargs)
+
+
+def call_module(module, stdin=None, merge_stderr=False, **kwargs):
+    r"""Run module with parameters given in `kwargs` and return its output.
+
+    >>> print call_module('g.region', flags='pg')  # doctest: +ELLIPSIS
+    n=...
+    s=...
+    w=...
+    >>> call_module('g.region', aabbbccc='notexist')  # doctest: +IGNORE_EXCEPTION_DETAIL
+    Traceback (most recent call last):
+        ...
+    CalledModuleError: Module run g.region ... ended with error
+    >>> call_module('m.proj', flags='i', input='-', stdin="50.0 41.5")
+    '8642890.65|6965155.61|0.00\n'
+
+    If `stdin` is not set and `kwargs` contains ``input`` with value set
+    to ``-`` (dash), the function raises an error.
+
+    :param str module: module name
+    :param stdin: string to be used as module standard input (stdin) or `None`
+    :param merge_stderr: if the standard error output should be merged with stdout
+    :param kwargs: module parameters
+
+    :returns: module standard output (stdout)
+
+    :raises CalledModuleError: if module return code is non-zero
+    :raises ValueError: if the parameters are not correct
+    """
+    # implemenation inspired by subprocess.check_output() function
+    if stdin:
+        kwargs['stdin'] = subprocess.PIPE  # to be able to send data to stdin
+    elif 'input' in kwargs and kwargs['input'] != '-':
+        raise ValueError(_("stdin must be set when input='-'"))
+    if 'stdout' in kwargs:
+        raise ValueError(_("stdout argument not allowed, it would be overridden"))
+    if 'stderr' in kwargs:
+        raise ValueError(_("stderr argument not allowed, it would be overridden"))
+
+    kwargs['stdout'] = subprocess.PIPE
+    if merge_stderr:
+        kwargs['stderr'] = subprocess.STDOUT
+    else:
+        kwargs['stderr'] = subprocess.PIPE
+    process = start_command(module, **kwargs)
+    # input=None means no stdin (our default)
+    # for stderr=STDOUT errors in None which is fine for CalledModuleError
+    output, errors = process.communicate(input=stdin)
+    returncode = process.poll()
+    if returncode:
+        raise CalledModuleError(returncode, module, kwargs, errors)
+    return output

Modified: sandbox/wenzeslaus/gunittest/testsuite/test_doctests.py
===================================================================
--- sandbox/wenzeslaus/gunittest/testsuite/test_doctests.py	2014-06-25 21:58:42 UTC (rev 60978)
+++ sandbox/wenzeslaus/gunittest/testsuite/test_doctests.py	2014-06-26 04:17:20 UTC (rev 60979)
@@ -26,5 +26,7 @@
 
 
 def load_tests(loader, tests, ignore):
+    # this should be called at some top level
+    tests.addTests(doctest.DocTestSuite(gunittest.gmodules))
     tests.addTests(doctest.DocTestSuite(gunittest.checkers))
     return tests

Added: sandbox/wenzeslaus/gunittest/utils.py
===================================================================
--- sandbox/wenzeslaus/gunittest/utils.py	                        (rev 0)
+++ sandbox/wenzeslaus/gunittest/utils.py	2014-06-26 04:17:20 UTC (rev 60979)
@@ -0,0 +1,27 @@
+import sys
+
+
+def do_doctest_gettext_workaround():
+    """Setups environment for doing a doctest with gettext usage.
+
+    When using gettext with dynamically defined underscore function
+    (``_("For translation")``), doctest does not work properly. One option is
+    to use `import as` instead of dynamically defined underscore function but
+    this would require change all modules which are used by tested module.
+    This should be considered for the future. The second option is to define
+    dummy underscore function and one other function which creates the right
+    environment to satisfy all. This is done by this function.
+    """
+    def new_displayhook(string):
+        """A replacement for default `sys.displayhook`"""
+        if string is not None:
+            sys.stdout.write("%r\n" % (string,))
+
+    def new_translator(string):
+        """A fake gettext underscore function."""
+        return string
+
+    sys.displayhook = new_displayhook
+
+    import __builtin__
+    __builtin__._ = new_translator



More information about the grass-commit mailing list