[GRASS-SVN] r61238 - in grass/trunk/lib/python: docs/src gunittest gunittest/testsuite
svn_grass at osgeo.org
svn_grass at osgeo.org
Fri Jul 11 10:24:37 PDT 2014
Author: wenzeslaus
Date: 2014-07-11 10:24:37 -0700 (Fri, 11 Jul 2014)
New Revision: 61238
Modified:
grass/trunk/lib/python/docs/src/gunittest_testing.rst
grass/trunk/lib/python/gunittest/case.py
grass/trunk/lib/python/gunittest/gmodules.py
grass/trunk/lib/python/gunittest/testsuite/test_assertions.py
grass/trunk/lib/python/gunittest/testsuite/test_module_assertions.py
Log:
gunittest: use PyGRASS for the whole module run (use finish_ and try-except), few updates in doc
Modified: grass/trunk/lib/python/docs/src/gunittest_testing.rst
===================================================================
--- grass/trunk/lib/python/docs/src/gunittest_testing.rst 2014-07-10 23:00:30 UTC (rev 61237)
+++ grass/trunk/lib/python/docs/src/gunittest_testing.rst 2014-07-11 17:24:37 UTC (rev 61238)
@@ -48,7 +48,7 @@
To run (invoke) all tests in the source tree run::
- python python -m grass.gunittest.main [gisdbase] location test_data_category
+ python -m grass.gunittest.main [gisdbase] location test_data_category
All test files in all ``testsuite`` directories will be executed and
a report will be created in a newly created ``testreport`` directory.
@@ -81,6 +81,10 @@
Tests of GRASS modules
----------------------
+This is applicable for both GRASS modules written in C or C++ and
+GRASS modules written in Python since we are testing the whole module
+(which is invoked as a subprocess).
+
::
def test_elevation(self):
@@ -104,38 +108,63 @@
.. todo::
Add example of assertions of key-value results.
-.. todo::
- Add example with module producing a map.
+Especially if a module module has a lot of different parameters allowed
+in different combinations, you should test the if the wrong ones are really
+disallowed and proper error messages are provided (in addition, you can
+test things such as creation and removal of maps in error states).
::
from grass.gunittest import SimpleModule
- class TestRInfoInputHandling(TestCase):
+ class TestRInfoParameterHandling(TestCase):
+ """Test r.info handling of wrong input of parameters."""
def test_rinfo_wrong_map(self):
+ """Test input of map which does not exist."""
map_name = 'does_not_exist'
- rinfo = SimpleModule('r.info', map=, flags='g')
+ # create a module instance suitable for testing
+ rinfo = SimpleModule('r.info', map=map_name, flags='g')
+ # test that module fails (ends with non-zero return code)
self.assertModuleFail(rinfo)
+ # test that error output is not empty
self.assertTrue(rinfo.outputs.stderr)
+ # test that the right map is mentioned in the error message
self.assertIn(map_name, stderr)
+In some cases it might be advantageous to create a module instance
+in `setUp()` method and then modify it in test methods.
+.. note:
+ Test should be (natural) language, i.e. locale, independent
+ to allow testing the functionality under different locale settings.
+ So, if you are testing content of messages (which should be usually
+ translated), use `assertIn()` method (regular expression might be
+ applicable in some cases but in most cases `in` is exactly the
+ operation needed).
+
Tests of C and C++ code
-----------------------
Tests of Python code
--------------------
+Use `gunittest` for this purpose in the same way as `unittest`_ would be used.
+
Testing Python code with doctest
--------------------------------
-In Python, the easiest thing to test are functions which performs some computations
-or string manipulations, i.e. they have sum numbers or strings on the input and
-some others on the output.
+.. note::
+ The primary use of ``doctest`` is to ensure that the documentation
+ for functions and classes is valid. Additionally, it can increase
+ the number of tests when executed together with other tests.
+In Python, the easiest thing to test are functions which performs some
+computations or string manipulations, i.e. they have some numbers or strings
+on the input and some other numbers or strings on the output.
+
At the beginning you can use doctest for this purpose. The syntax is as follows::
def sum_list(list_to_sum):
@@ -155,7 +184,6 @@
import doctest
doctest.testmod()
else:
- grass.parser()
main()
No output means that everything was successful. Note that you cannot use all
@@ -163,7 +191,7 @@
to the dot or dots in the file name. Moreover, it is sometimes required that
the file is accessible through sys.path which is not true for case of GRASS modules.
-Do not use use doctest for tests of edge cases, for tests which require
+However, do not use use doctest for tests of edge cases, for tests which require
generate complex data first, etc. In these cases use `gunittest`.
Modified: grass/trunk/lib/python/gunittest/case.py
===================================================================
--- grass/trunk/lib/python/gunittest/case.py 2014-07-10 23:00:30 UTC (rev 61237)
+++ grass/trunk/lib/python/gunittest/case.py 2014-07-11 17:24:37 UTC (rev 61238)
@@ -408,6 +408,10 @@
finally:
call_module('g.remove', rast=diff)
# general case
+ # TODO: we are using r.info min max and r.univar min max interchangably
+ # but they might be different if region is different from map
+ # not considered as an huge issue since we expect the tested maps
+ # to match with region, however a documentation should containe a notice
self.assertRastersDifference(actual=actual, reference=reference,
statistics=statistics,
precision=precision, msg=msg)
@@ -446,19 +450,13 @@
"""
module = _module_from_parameters(module, **kwargs)
_check_module_run_parameters(module)
-
- # do what module.run() with finish_=True would do
- start = time.time()
- module.run()
- stdout, stderr = module.popen.communicate(input=module.stdin)
- module.outputs['stdout'].value = stdout if stdout else ''
- module.outputs['stderr'].value = stderr if stderr else ''
- module.time = time.time() - start
- if module.popen.poll():
+ try:
+ module.run()
+ except CalledModuleError:
# here exception raised by run() with finish_=True would be
# almost enough but we want some additional info to be included
# in the test report
- errors = module.outputs['stderr'].value
+ errors = module.outputs.stderr
# provide diagnostic at least in English locale
# TODO: standardized error code would be handy here
import re
@@ -503,18 +501,11 @@
"""
module = _module_from_parameters(module, **kwargs)
_check_module_run_parameters(module)
-
- # do what module.run() with finish_=True would do
- start = time.time()
- module.run()
- stdout, stderr = module.popen.communicate(input=module.stdin)
- module.outputs['stdout'].value = stdout if stdout else ''
- module.outputs['stderr'].value = stderr if stderr else ''
- module.time = time.time() - start
- # TODO: these two lines should go to report in some better way
- print module.outputs['stdout'].value
- print module.outputs['stderr'].value
- if module.popen.poll():
+ try:
+ module.run()
+ except CalledModuleError:
+ print module.outputs.stdout
+ print module.outputs.stderr
# TODO: message format
# TODO: stderr?
stdmsg = ('Running <{m.name}> module ended'
@@ -523,10 +514,11 @@
'See the folowing errors:\n'
'{errors}'.format(
m=module, code=module.get_python(),
- errors=module.outputs["stderr"].value
+ errors=module.outputs.stderr
))
self.fail(self._formatMessage(msg, stdmsg))
-
+ print module.outputs.stdout
+ print module.outputs.stderr
# log these to final report
# TODO: always or only if the calling test method failed?
# in any case, this must be done before self.fail()
@@ -541,18 +533,16 @@
"""
module = _module_from_parameters(module, **kwargs)
_check_module_run_parameters(module)
-
- # do what module.run() with finish_=True would do
- start = time.time()
- module.run()
- stdout, stderr = module.popen.communicate(input=module.stdin)
- module.outputs['stdout'].value = stdout if stdout else ''
- module.outputs['stderr'].value = stderr if stderr else ''
- module.time = time.time() - start
- # TODO: these two lines should go to report in some better way
- print module.outputs['stdout'].value
- print module.outputs['stderr'].value
- if not module.popen.poll():
+ # note that we cannot use finally because we do not leave except
+ try:
+ module.run()
+ except CalledModuleError:
+ print module.outputs.stdout
+ print module.outputs.stderr
+ pass
+ else:
+ print module.outputs.stdout
+ print module.outputs.stderr
stdmsg = ('Running <%s> ended with zero (successful) return code'
' when expecting module to fail' % module.get_python())
self.fail(self._formatMessage(msg, stdmsg))
@@ -578,9 +568,9 @@
# in this case module already run and we would start it again
if module.run_:
raise ValueError('Do not run the module manually, set run_=False')
- if module.finish_:
+ if not module.finish_:
raise ValueError('This function will always finish module run,'
- ' set finish_=None or finish_=False.')
+ ' set finish_=None or finish_=True.')
# we expect most of the usages with stdout=PIPE
# TODO: in any case capture PIPE always?
if module.stdout_ is None:
Modified: grass/trunk/lib/python/gunittest/gmodules.py
===================================================================
--- grass/trunk/lib/python/gunittest/gmodules.py 2014-07-10 23:00:30 UTC (rev 61237)
+++ grass/trunk/lib/python/gunittest/gmodules.py 2014-07-11 17:24:37 UTC (rev 61238)
@@ -50,7 +50,7 @@
', it would be overriden' % banned)
kargs['stdout_'] = subprocess.PIPE
kargs['stderr_'] = subprocess.PIPE
- kargs['finish_'] = False
+ kargs['finish_'] = True
kargs['run_'] = False
Module.__init__(self, cmd, *args, **kargs)
Modified: grass/trunk/lib/python/gunittest/testsuite/test_assertions.py
===================================================================
--- grass/trunk/lib/python/gunittest/testsuite/test_assertions.py 2014-07-10 23:00:30 UTC (rev 61237)
+++ grass/trunk/lib/python/gunittest/testsuite/test_assertions.py 2014-07-11 17:24:37 UTC (rev 61238)
@@ -85,7 +85,7 @@
class TestAssertCommandKeyValue(grass.gunittest.TestCase):
- """Test usage of `.assertModuleKeyValue` method."""
+ """Test usage of `assertModuleKeyValue` method."""
# pylint: disable=R0904
@classmethod
@@ -98,9 +98,9 @@
cls.del_temp_region()
def test_pygrass_module(self):
- """Test syntax with Module as module"""
+ """Test syntax with Module and required parameters as module"""
module = Module('r.info', map='elevation', flags='gr',
- run_=False, finish_=False)
+ run_=False, finish_=True)
self.assertModuleKeyValue(module,
reference=dict(min=55.58, max=156.33),
precision=0.01, sep='=')
Modified: grass/trunk/lib/python/gunittest/testsuite/test_module_assertions.py
===================================================================
--- grass/trunk/lib/python/gunittest/testsuite/test_module_assertions.py 2014-07-10 23:00:30 UTC (rev 61237)
+++ grass/trunk/lib/python/gunittest/testsuite/test_module_assertions.py 2014-07-11 17:24:37 UTC (rev 61238)
@@ -17,25 +17,25 @@
def setUp(self):
"""Create two Module instances one correct and one with wrong map"""
self.rinfo = Module('r.info', map='elevation', flags='g',
- stdout_=subprocess.PIPE, run_=False, finish_=False)
+ stdout_=subprocess.PIPE, run_=False, finish_=True)
self.rinfo_wrong = copy.deepcopy(self.rinfo)
self.wrong_map = 'does_not_exists'
self.rinfo_wrong.inputs['map'].value = self.wrong_map
def test_runModule(self):
- """Module used in runModule()"""
+ """Correct and incorrect Module used in runModule()"""
self.runModule(self.rinfo)
self.assertTrue(self.rinfo.outputs['stdout'].value)
self.assertRaises(CalledModuleError, self.runModule, self.rinfo_wrong)
def test_assertModule(self):
- """Module used in assertModule()"""
+ """Correct and incorrect Module used in assertModule()"""
self.assertModule(self.rinfo)
self.assertTrue(self.rinfo.outputs['stdout'].value)
self.assertRaises(self.failureException, self.assertModule, self.rinfo_wrong)
def test_assertModuleFail(self):
- """Module used in assertModuleFail()"""
+ """Correct and incorrect Module used in assertModuleFail()"""
self.assertModuleFail(self.rinfo_wrong)
stderr = self.rinfo_wrong.outputs['stderr'].value
self.assertTrue(stderr)
@@ -56,19 +56,19 @@
self.rinfo_wrong.inputs['map'].value = self.wrong_map
def test_runModule(self):
- """SimpleModule used in runModule()"""
+ """Correct and incorrect SimpleModule used in runModule()"""
self.runModule(self.rinfo)
self.assertTrue(self.rinfo.outputs['stdout'].value)
self.assertRaises(CalledModuleError, self.runModule, self.rinfo_wrong)
def test_assertModule(self):
- """SimpleModule used in assertModule()"""
+ """Correct and incorrect SimpleModule used in assertModule()"""
self.assertModule(self.rinfo)
self.assertTrue(self.rinfo.outputs['stdout'].value)
self.assertRaises(self.failureException, self.assertModule, self.rinfo_wrong)
def test_assertModuleFail(self):
- """SimpleModule used in assertModuleFail()"""
+ """Correct and incorrect SimpleModule used in assertModuleFail()"""
self.assertModuleFail(self.rinfo_wrong)
stderr = self.rinfo_wrong.outputs['stderr'].value
self.assertTrue(stderr)
@@ -76,6 +76,5 @@
self.assertRaises(self.failureException, self.assertModuleFail, self.rinfo)
-
if __name__ == '__main__':
grass.gunittest.test()
More information about the grass-commit
mailing list