[GRASS-SVN] r61221 - in sandbox/wenzeslaus/gunittest: . testsuite
svn_grass at osgeo.org
svn_grass at osgeo.org
Wed Jul 9 13:54:01 PDT 2014
Author: wenzeslaus
Date: 2014-07-09 13:54:01 -0700 (Wed, 09 Jul 2014)
New Revision: 61221
Modified:
sandbox/wenzeslaus/gunittest/__init__.py
sandbox/wenzeslaus/gunittest/case.py
sandbox/wenzeslaus/gunittest/checkers.py
sandbox/wenzeslaus/gunittest/gmodules.py
sandbox/wenzeslaus/gunittest/gutils.py
sandbox/wenzeslaus/gunittest/invoker.py
sandbox/wenzeslaus/gunittest/loader.py
sandbox/wenzeslaus/gunittest/main.py
sandbox/wenzeslaus/gunittest/reporters.py
sandbox/wenzeslaus/gunittest/runner.py
sandbox/wenzeslaus/gunittest/testsuite/test_assertions.py
sandbox/wenzeslaus/gunittest/testsuite/test_checkers.py
sandbox/wenzeslaus/gunittest/utils.py
Log:
gunittest: add missing test for assert raster difference and run only r.info -r if it is enough, rename compare to equals, add module docstrings with licences
Modified: sandbox/wenzeslaus/gunittest/__init__.py
===================================================================
--- sandbox/wenzeslaus/gunittest/__init__.py 2014-07-09 15:16:18 UTC (rev 61220)
+++ sandbox/wenzeslaus/gunittest/__init__.py 2014-07-09 20:54:01 UTC (rev 61221)
@@ -1,2 +1,19 @@
+# -*- coding: utf-8 -*-
+"""!@package grass.gunittest
+
+ at brief GRASS Python testing framework module for running from command line
+
+Copyright (C) 2014 by the GRASS Development Team
+This program is free software under the GNU General Public
+License (>=v2). Read the file COPYING that comes with GRASS GIS
+for details.
+
+ at author Vaclav Petras
+ at author Soeren Gebbert
+
+Initial version of `gunittest` was created during Google Summer of Code 2014
+by Vaclav Petras as a student and Soeren Gebbert as a mentor.
+"""
+
from .case import TestCase
from .main import test
Modified: sandbox/wenzeslaus/gunittest/case.py
===================================================================
--- sandbox/wenzeslaus/gunittest/case.py 2014-07-09 15:16:18 UTC (rev 61220)
+++ sandbox/wenzeslaus/gunittest/case.py 2014-07-09 20:54:01 UTC (rev 61221)
@@ -22,7 +22,7 @@
from .gmodules import call_module
from .checkers import (check_text_ellipsis,
- text_to_keyvalue, compare_keyvalue, diff_keyvalue,
+ text_to_keyvalue, keyvalue_equals, diff_keyvalue,
file_md5, files_equal_md5)
@@ -106,7 +106,7 @@
def del_temp_region(cls):
"""Remove the temporary region.
- Unsets WIND_OVERRIDE and removes any region named by it.
+ Unsets ``WIND_OVERRIDE`` and removes any region named by it.
"""
assert cls._temp_region
name = os.environ.pop('WIND_OVERRIDE')
@@ -145,16 +145,34 @@
# (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
+ # TODO: change name to Module
def assertCommandKeyValue(self, module, reference, sep,
- precision=None, msg=None, **parameters):
+ precision, msg=None, **parameters):
+ """Test that output of a module is the same as provided subset.
+
+ ::
+
+ self.assertCommandKeyValue('r.info', map='elevation', flags='gr',
+ reference=dict(min=55.58, max=156.33),
+ precision=0.01, sep='=')
+
+ ::
+
+ module = SimpleModule('r.info', map='elevation', flags='gr')
+ self.assertCommandKeyValue(module,
+ reference=dict(min=55.58, max=156.33),
+ precision=0.01, sep='=')
+
+ The output of the module should be key-value pairs (shell script style)
+ which is typically obtained using ``-g`` flag.
+ """
if isinstance(reference, basestring):
reference = text_to_keyvalue(reference, sep=sep, skip_empty=True)
module = _module_from_parameters(module, **parameters)
self.runModule(module)
raster_univar = text_to_keyvalue(module.outputs.stdout,
sep=sep, skip_empty=True)
- if not compare_keyvalue(dict_a=reference, dict_b=raster_univar,
+ if not keyvalue_equals(dict_a=reference, dict_b=raster_univar,
a_is_subset=True, precision=precision):
unused, missing, mismatch = diff_keyvalue(dict_a=reference,
dict_b=raster_univar,
@@ -166,17 +184,18 @@
" provided in reference"
": %s\n" % (module, ", ".join(missing)))
if mismatch:
- standardMsg = "%s difference:\n" % module
- standardMsg += "mismatch values (key, reference, actual): %s\n" % mismatch
- standardMsg += 'command: %s %s' % (module, parameters)
+ stdMsg = "%s difference:\n" % module
+ stdMsg += "mismatch values"
+ stdMsg += "(key, reference, actual): %s\n" % mismatch
+ stdMsg += 'command: %s %s' % (module, parameters)
else:
# we can probably remove this once we have more tests
- # of compare_keyvalue and diff_keyvalue against each other
- raise RuntimeError("compare_keyvalue() showed difference but"
+ # of keyvalue_equals and diff_keyvalue against each other
+ raise RuntimeError("keyvalue_equals() showed difference but"
" diff_keyvalue() did not. This can be"
" a bug in one of them or in the caller"
" (assertCommandKeyValue())")
- self.fail(self._formatMessage(msg, standardMsg))
+ self.fail(self._formatMessage(msg, stdMsg))
def assertRasterFitsUnivar(self, raster, reference,
precision=None, msg=None):
@@ -192,7 +211,7 @@
Use keyword arguments syntax for all function parameters.
- Does not -e (extended statistics) flag, use `assertCommandKeyValue`
+ Does not -e (extended statistics) flag, use `assertCommandKeyValue()`
for the full interface of arbitrary module.
"""
self.assertCommandKeyValue(module='r.univar',
@@ -266,7 +285,7 @@
Use keyword arguments syntax for all function parameters.
To check that more statistics have certain values use
- `assertRasterFitsUnivar` or `assertRasterFitsInfo`
+ `assertRasterFitsUnivar()` or `assertRasterFitsInfo()`
"""
stdout = call_module('r.info', map=map, flags='r')
actual = text_to_keyvalue(stdout, sep='=')
@@ -347,21 +366,50 @@
reference)
self.fail(self._formatMessage(msg, stdmsg))
- # TODO: add tests for this method
+ def _compute_difference_raster(self, first, second, name_part):
+ """Compute difference of two rasters (first - second)
+
+ The name of the new raster is a long name designed to be as unique as
+ possible and contains names of two input rasters.
+
+ :param first: raster to subtract from
+ :param second: raster used as decrement
+ :param name_part: a unique string to be used in the difference name
+
+ :returns: name of a new raster
+ """
+ diff = ('tmp_' + self.id() + '_compute_difference_raster_'
+ + name_part + '_' + first + '_minus_' + second)
+ call_module('r.mapcalc',
+ stdin='"{d}" = "{f}" - "{s}"'.format(d=diff,
+ f=first,
+ s=second))
+ return diff
+
def assertRastersNoDifference(self, actual, reference,
precision, statistics=None, msg=None):
"""Test that `actual` raster is not different from `reference` raster
-
+
Method behaves in the same way as `assertRasterFitsUnivar()`
but works on difference ``referece - actual``.
If statistics is not given ``dict(min=-precision, max=precision)``
is used.
"""
- if statistics is None:
- statistics = dict(min=-precision, max=precision)
+ if statistics is None or sorted(statistics.keys()) == ['max', 'min']:
+ if statistics is None:
+ statistics = dict(min=-precision, max=precision)
+ diff = self._compute_difference_raster(reference, actual,
+ 'assertRastersNoDifference')
+ try:
+ self.assertCommandKeyValue('r.info', map=diff, flags='r',
+ sep='=', precision=precision,
+ reference=statistics, msg=msg)
+ finally:
+ call_module('g.remove', rast=diff)
+ # general case
self.assertRastersDifference(actual=actual, reference=reference,
- statistics=statistics, precision=precision,
- msg=msg)
+ statistics=statistics,
+ precision=precision, msg=msg)
def assertRastersDifference(self, actual, reference,
statistics, precision, msg=None):
@@ -369,12 +417,11 @@
For cases when you are interested in no or minimal difference,
use `assertRastersNoDifference()` instead.
+
+ This method should not be used to test r.mapcalc or r.univar.
"""
- diff = ('tmp__' + self.id() + '__assertRastersDifference_diff'
- + actual + '__diff__' + reference)
- call_module('r.mapcalc', stdin='"{d}" = "{r}" - "{a}"'.format(d=diff,
- a=actual,
- r=reference))
+ diff = self._compute_difference_raster(reference, actual,
+ 'assertRastersDifference')
try:
self.assertRasterFitsUnivar(raster=diff, reference=statistics,
precision=precision, msg=msg)
@@ -437,10 +484,11 @@
# TODO: this should be the function used for valgrind or profiling or debug
# TODO: it asserts the rc but it does much more, so testModule?
# TODO: do we need special function for testing module failures or just add parameter returncode=0?
- # TODO: consider allwing to call this method more than once
+ # TODO: consider not allowing to call this method more than once
# the original idea was to run this method just once for test method
# but for "integration" tests (script-like tests with more than one module)
# it would be better to be able to use this multiple times
+ # TODO: enable merging streams?
def assertModule(self, module, msg=None, **kwargs):
"""Run PyGRASS module in controled way and assert non-zero return code.
@@ -449,13 +497,11 @@
the execution, error handling and storing of output.
It will not print module stdout and stderr, instead it will always
- store them for furthere examination.
- ? If you don't specify stdout_=PIPE, it will merge stderr into
- stdout. ?
+ store them for further examination. Streams are stored separately.
- This function is not suitable for testing error states of the module.
- If you want to test behavior which involves non-zero return code
- and examine stderr in test, use PyGRASS directly.
+ This method is not suitable for testing error states of the module.
+ If you want to test behavior which involves non-zero return codes
+ and examine stderr in test, use `assertModuleFail()` method.
Runs the module and causes test failure if module ends with
non-zero return code.
@@ -503,6 +549,10 @@
# TODO: should we merge stderr to stdout in this case?
def assertModuleFail(self, module, msg=None, **kwargs):
+ """Test that module fails with a non-zero return code.
+
+ Works like `assertModule()` but expects module to fail.
+ """
module = _module_from_parameters(module, **kwargs)
if module.run_:
@@ -531,6 +581,7 @@
# TODO: add tests and documentation to methods which are using this function
+# some test and documentation add to assertCommandKeyValue
def _module_from_parameters(module, **kwargs):
if kwargs:
if not isinstance(module, basestring):
Modified: sandbox/wenzeslaus/gunittest/checkers.py
===================================================================
--- sandbox/wenzeslaus/gunittest/checkers.py 2014-07-09 15:16:18 UTC (rev 61220)
+++ sandbox/wenzeslaus/gunittest/checkers.py 2014-07-09 20:54:01 UTC (rev 61221)
@@ -21,7 +21,6 @@
# alternative term to check(er(s)) would be compare
-# TODO: these functions returns dictionary but they also modify it
def unify_projection(dic):
"""Unifies names of projections.
@@ -29,21 +28,24 @@
'Universal Transverse Mercator' and 'Universe Transverse Mercator'.
This function replaces synonyms by a unified name.
- >>> unify_projection({'name': ['Universe Transverse Mercator']})
- {'name': ['Universal Transverse Mercator']}
+ Example of common typo in UTM replaced by correct spelling::
- @param dic The dictionary containing information about projection
+ >>> unify_projection({'name': ['Universe Transverse Mercator']})
+ {'name': ['Universal Transverse Mercator']}
- @return The dictionary with the new values if needed
+ :param dic: The dictionary containing information about projection
+
+ :return: The dictionary with the new values if needed or a copy of old one
"""
# the lookup variable is a list of list, each list contains all the
# possible name for a projection system
- lookup = [['Universal Transverse Mercator', 'Universe Transverse Mercator']]
+ lookup = [['Universal Transverse Mercator',
+ 'Universe Transverse Mercator']]
dic = dict(dic)
- for lo in lookup:
+ for l in lookup:
for n in range(len(dic['name'])):
- if dic['name'][n] in lo:
- dic['name'][n] = lo[0]
+ if dic['name'][n] in l:
+ dic['name'][n] = l[0]
return dic
@@ -53,12 +55,14 @@
Some units have different spelling although they are the same units.
This functions replaces different spelling options by unified one.
- >>> unify_units({'units': ['metres'], 'unit': ['metre']})
- {'units': ['meters'], 'unit': ['meter']}
+ Example of British English spelling replaced by US English spelling::
- @param dic The dictionary containing information about units
+ >>> unify_units({'units': ['metres'], 'unit': ['metre']})
+ {'units': ['meters'], 'unit': ['meter']}
- @return The dictionary with the new values if needed
+ :param dic: The dictionary containing information about units
+
+ :return: The dictionary with the new values if needed or a copy of old one
"""
# the lookup variable is a list of list, each list contains all the
# possible name for a units
@@ -79,76 +83,93 @@
dic['unit'] = l[0]
if not isinstance(dic['units'], types.StringTypes):
for n in range(len(dic['units'])):
- if dic['units'][n] in l:
- dic['units'][n] = l[0]
+ if dic['units'][n] in l:
+ dic['units'][n] = l[0]
else:
if dic['units'] in l:
dic['units'] = l[0]
return dic
+def value_from_string(value):
+ """Create value of a most fitting type from a string.
+
+ Type conversions are applied in order ``int``, ``float``, ``string``
+ where string is no conversion.
+
+ >>> value_from_string('1')
+ 1
+ >>> value_from_string('5.6')
+ 5.6
+ >>> value_from_string(' 5.6\t ')
+ 5.6
+ >>> value_from_string('hello')
+ 'hello'
+ """
+ not_float = False
+ not_int = False
+ # Convert values into correct types
+ # We first try integer then float because
+ # int('1.0') is ValueError (although int(1.0) is not)
+ # while float('1') is not
+ try:
+ value_converted = int(value)
+ except ValueError:
+ not_int = True
+ if not_int:
+ try:
+ value_converted = float(value)
+ except ValueError:
+ not_float = True
+ # strip strings from whitespace (expecting spaces and tabs)
+ if not_int and not_float:
+ value_converted = value.strip()
+ return value_converted
+
+
+# TODO: what is the default separator?
def text_to_keyvalue(text, sep=":", val_sep=",", functions=None,
- skip_invalid=False, skip_empty=False):
- """
+ skip_invalid=False, skip_empty=False,
+ from_string=value_from_string):
+ """Convert test to key-value pairs (dictionary-like KeyValue object).
+
Converts a key-value text file, where entries are separated
by newlines and the key and value are separated by `sep`,
- into a key-value dictionary and discover/use the correct
+ into a key-value dictionary and discovers/uses the correct
data types (float, int or string) for values.
- And empty string is a valid input because empty dictionary is a valid
- dictionary.
+ Besides key-value pairs it also parses values itself. Value is created
+ with the best fitting type using `value_from_string()` function by default.
+ When val_sep is present in value part, the resulting value is
+ a list of values.
- @param text string to convert
- @param sep character that separates the keys and values, default is ":"
- @param val_sep character that separates the values of a single key, default is ","
- @param functions list of functions to apply on the resulting dictionary
- @param skip_invalid skip all lines which does not contain separator
- @param skip_empty skip empty lines
+ :param text: string to convert
+ :param sep: character that separates the keys and values
+ :param val_sep: character that separates the values of a single key
+ :param functions: list of functions to apply on the resulting dictionary
+ :param skip_invalid: skip all lines which does not contain separator
+ :param skip_empty: skip empty lines
+ :param from_string: a function used to convert strings to values,
+ use ``lambda x: x`` for no conversion
- @return The dictionary
+ :return: a dictionary representation of text
+ :return type: grass.script.core.KeyValue
- A text file with this content:
- Will be represented as this dictionary:
+ And example of converting text with text, floats, integeres and list
+ to a dictionary::
- >>> sorted(text_to_keyvalue('''a: Hello
- ... b: 1.0
- ... c: 1,2,3,4,5
- ... d : hello,8,0.1''').items()) # sorted items from the dictionary
- [('a', 'Hello'), ('b', 1.0), ('c', [1, 2, 3, 4, 5]), ('d', ['hello', 8, 0.1])]
+ >>> sorted(text_to_keyvalue('''a: Hello
+ ... b: 1.0
+ ... c: 1,2,3,4,5
+ ... d : hello,8,0.1''').items()) # sorted items from the dictionary
+ [('a', 'Hello'), ('b', 1.0), ('c', [1, 2, 3, 4, 5]), ('d', ['hello', 8, 0.1])]
+
+ .. warning::
+ And empty string is a valid input because empty dictionary is a valid
+ dictionary. You need to test this separately accorting
+ to the circumstances.
"""
- def value_from_string(value):
- """
- >>> convert_from_string('1')
- 1
- >>> convert_from_string('5.6')
- 5.6
- >>> convert_from_string(' 5.6\t ')
- 5.6
- >>> convert_from_string('hello')
- 'hello'
- """
- # note that above code is not invoked by doctest
- not_float = False
- not_int = False
- # Convert values into correct types
- # We first try integer then float because
- # int('1.0') is ValueError (although int(1.0) is not)
- # while float('1') is not
- try:
- value_converted = int(value)
- except ValueError:
- not_int = True
- if not_int:
- try:
- value_converted = float(value)
- except ValueError:
- not_float = True
- # strip strings from whitespace (expecting spaces and tabs)
- if not_int and not_float:
- value_converted = value.strip()
- return value_converted
-
- # split according to universal newlines approach
+ # splitting according to universal newlines approach
# TODO: add also general split with vsep
text = text.splitlines()
kvdict = gcore.KeyValue()
@@ -189,19 +210,21 @@
values = value.split(val_sep)
value_list = []
for value in values:
- value_converted = value_from_string(value)
+ value_converted = from_string(value)
value_list.append(value_converted)
kvdict[key] = value_list
else:
# single values
- kvdict[key] = value_from_string(value)
+ kvdict[key] = from_string(value)
for function in functions:
kvdict = function(kvdict)
return kvdict
# TODO: decide if there should be some default for precision
+# TODO: define standard precisions for DCELL, FCELL, CELL, mm, ft, cm, ...
# TODO: decide if None is valid, and use some default or no compare
+# TODO: is None a valid value for precision?
def values_equal(value_a, value_b, precision=0.000001):
"""
>>> values_equal(1.022, 1.02, precision=0.01)
@@ -233,7 +256,7 @@
precision = float(precision)
# we will apply precision to int-float comparison
# rather than converting both to integer
- # (as in the original function from core)
+ # (as in the original function from grass.script.core)
if abs(value_a - value_b) > precision:
return False
@@ -256,43 +279,46 @@
return True
-# TODO: rename to equals
-def compare_keyvalue(dict_a, dict_b, precision=0.000001,
- def_equal=values_equal, key_equal=None,
- a_is_subset=False):
- """Compare two dictionaries
+def keyvalue_equals(dict_a, dict_b, precision,
+ def_equal=values_equal, key_equal=None,
+ a_is_subset=False):
+ """Compare two dictionaries.
- This method will print a warning in case keys that are present in the first
- file are not present in the second one.
- The comparison method tries to convert the values into their native format
- (float, int or string) to allow correct comparison.
+ .. note::
+ Always use keyword arguments for all parameters with defaults.
+ It is a good idea to use keyword arguments also for the first
+ two parameters.
- Always use keyword arguments for all parameters with defaults. It is a good
- idea to use keyword arguments also for the first two parameters.
+ An example of key-value texts comparison::
- An example key-value text file may have this content:
+ >>> keyvalue_equals(text_to_keyvalue('''a: Hello
+ ... b: 1.0
+ ... c: 1,2,3,4,5
+ ... d: hello,8,0.1'''),
+ ... text_to_keyvalue('''a: Hello
+ ... b: 1.1
+ ... c: 1,22,3,4,5
+ ... d: hello,8,0.1'''), precision=0.1)
+ False
- >>> compare_keyvalue(text_to_keyvalue('''a: Hello
- ... b: 1.0
- ... c: 1,2,3,4,5
- ... d: hello,8,0.1'''),
- ... text_to_keyvalue('''a: Hello
- ... b: 1.1
- ... c: 1,22,3,4,5
- ... d: hello,8,0.1'''), precision=0.1)
- False
-
- @param filename_a name of the first key-value text file
- @param filenmae_b name of the second key-value text file
- @param sep character that separates the keys and values, default is ":"
- @param val_sep character that separates the values of a single key, default is ","
- @param precision precision with which the floating point values are compared
- @param proj True if it has to check some information about projection system
- @param units True if it has to check some information about units
+ :param dict_a: first dictionary
+ :param dict_b: second dictionary
+ :param precision: precision with which the floating point values
+ are compared (passed to equality functions)
:param callable def_equal: function used for comparison by default
- :param dict key_equal: dictionary of functions used for comparison of specific keys
+ :param dict key_equal: dictionary of functions used for comparison
+ of specific keys, `def_equal` is used for the rest,
+ keys in dictionary are keys in `dict_a` and `dict_b` dictionaries,
+ values are the fuctions used to comapare the given key
+ :param a_is_subset: `True` if `dict_a` is a subset of `dict_b`,
+ `False` otherwise
- @return True if full or almost identical, False if different
+ :return: `True` if identical, `False` if different
+
+ Use `diff_keyvalue()` to get information about differeces.
+ You can use this function to find out if there is a difference and then
+ use `diff_keyvalue()` to determine all the differences between
+ dictionaries.
"""
key_equal = {} if key_equal is None else key_equal
@@ -309,31 +335,41 @@
equal_fun = key_equal.get(key, def_equal)
if not equal_fun(dict_a[key], dict_b[key], precision):
return False
- # bahavior change comparing to the original proj fun
- # elif isinstance(dict_a[key], float) or isinstance(dict_b[key], float):
- #warning(_("Mixing value types. Will try to compare after "
- # "integer conversion"))
- #return int(dict_a[key]) == int(dict_b[key])
- #elif key == "+towgs84":
- # # We compare the sum of the entries
- # if abs(sum(dict_a[key]) - sum(dict_b[key])) > precision:
- # return False
return True
-# TODO: should the retrun depend on the a_is_subset parameter?
-# must have the same interface and behavior as compare_keyvalue
-def diff_keyvalue(dict_a, dict_b, precision=0.000001,
+# TODO: should the return depend on the a_is_subset parameter?
+# this function must have the same interface and behavior as keyvalue_equals
+def diff_keyvalue(dict_a, dict_b, precision,
def_equal=values_equal, key_equal=None,
a_is_subset=False):
- """
+ """Determine the difference of two dictionaries.
- >>> a = {'c': 2, 'b': 3, 'a': 4}
- >>> b = {'c': 1, 'b': 3, 'd': 5}
- >>> diff_keyvalue(a, b)
- (['d'], ['a'], [('c', 2, 1)])
- >>> diff_keyvalue(a, b, a_is_subset=True)
- ([], ['a'], [('c', 2, 1)])
+ The function returns missing keys and differnt values for common keys::
+
+ >>> a = {'c': 2, 'b': 3, 'a': 4}
+ >>> b = {'c': 1, 'b': 3, 'd': 5}
+ >>> diff_keyvalue(a, b, precision=0)
+ (['d'], ['a'], [('c', 2, 1)])
+
+ You can provide only a subset of values in dict_a, in this case
+ first item in tuple is an emptu list::
+
+ >>> diff_keyvalue(a, b, a_is_subset=True, precision=0)
+ ([], ['a'], [('c', 2, 1)])
+
+ This function behaves the same as `keyvalue_equals()`.
+
+ :returns: A tuple of lists, fist is list of missing keys in dict_a,
+ second missing keys in dict_b and third is a list of mismatched
+ values as tuples (key, value_from_a, value_from_b)
+ :rtype: (list, list, list)
+
+ Comparing to the Python ``difflib`` package this function does not create
+ any difference output. It just returns the dictionaries.
+ Comapring to the Python ``unittest`` ``assertDictEqual()``,
+ this function does not issues error or exception, it just determines
+ what it the difference.
"""
key_equal = {} if key_equal is None else key_equal
@@ -363,11 +399,12 @@
def proj_info_equals(text_a, text_b):
+ """Test if two PROJ_INFO texts are equal."""
def compare_sums(list_a, list_b, precision):
- # We compare the sum of the entries
+ """Compare difference of sums of two list using precision"""
+ # derived from the code in grass.script.core
if abs(sum(list_a) - sum(list_b)) > precision:
return False
-
sep = ':'
val_sep = ','
key_equal = {'+towgs84': compare_sums}
@@ -375,16 +412,22 @@
functions=[unify_projection])
dict_b = text_to_keyvalue(text_b, sep=sep, val_sep=val_sep,
functions=[unify_projection])
- return compare_keyvalue(dict_a, dict_b,
+ return keyvalue_equals(dict_a, dict_b,
precision=0.000001,
def_equal=values_equal,
key_equal=key_equal)
def proj_units_equals(text_a, text_b):
+ """Test if two PROJ_UNITS texts are equal."""
def lowercase_equals(string_a, string_b, precision=None):
+ # we don't need a waring for unused precision
+ # pylint: disable=W0613
+ """Test equality of two strings ignoring their case using ``lower()``.
+
+ Precision is accepted as require by `keyvalue_equals()` but ignored.
+ """
return string_a.lower() == string_b.lower()
-
sep = ':'
val_sep = ','
key_equal = {'unit': lowercase_equals, 'units': lowercase_equals}
@@ -392,7 +435,7 @@
functions=[unify_units])
dict_b = text_to_keyvalue(text_b, sep, val_sep,
functions=[unify_units])
- return compare_keyvalue(dict_a, dict_b,
+ return keyvalue_equals(dict_a, dict_b,
precision=0.000001,
def_equal=values_equal,
key_equal=key_equal)
@@ -510,6 +553,7 @@
# TODO: accept also open file object
def file_md5(filename):
+ """Get MD5 (check) sum of a file."""
hasher = hashlib.md5()
with open(filename, 'rb') as f:
buf = f.read(_BUFFER_SIZE)
@@ -521,14 +565,24 @@
def text_file_md5(filename, exclude_lines=None,
prepend_lines=None, append_lines=None):
+ """Get a MD5 (check) sum of a text file.
+
+ Works in the same way as `file_md5()` function but allows to
+ exclude lines from the file as well as prepend or append them.
+
+ .. todo::
+ Implement this function.
+ """
raise NotImplementedError("Implement, or use file_md5() function instead")
def files_equal_md5(filename_a, filename_b):
+ """Check equality of two files according to their MD5 sums"""
return file_md5(filename_a) == file_md5(filename_b)
def main(): # pragma: no cover
+ """Run the doctest"""
ret = doctest.testmod()
return ret.failed
Modified: sandbox/wenzeslaus/gunittest/gmodules.py
===================================================================
--- sandbox/wenzeslaus/gunittest/gmodules.py 2014-07-09 15:16:18 UTC (rev 61220)
+++ sandbox/wenzeslaus/gunittest/gmodules.py 2014-07-09 20:54:01 UTC (rev 61221)
@@ -1,17 +1,16 @@
# -*- coding: utf-8 -*-
-"""!@package grass.gunittest.case
+"""!@package grass.gunittest.gmodules
- at brief GRASS Python testing framework specialized GRASS module interfaces
+ at brief Specialized interfaces for invoking modules for testing framework
-(C) 2014 by the GRASS Development Team
+Copyright (C) 2014 by the GRASS Development Team
This program is free software under the GNU General Public
-License (>=v2). Read the file COPYING that comes with GRASS
+License (>=v2). Read the file COPYING that comes with GRASS GIS
for details.
@author Vaclav Petras
@author Soeren Gebbert
"""
-
import subprocess
from grass.script.core import start_command
from grass.exceptions import CalledModuleError
Modified: sandbox/wenzeslaus/gunittest/gutils.py
===================================================================
--- sandbox/wenzeslaus/gunittest/gutils.py 2014-07-09 15:16:18 UTC (rev 61220)
+++ sandbox/wenzeslaus/gunittest/gutils.py 2014-07-09 20:54:01 UTC (rev 61221)
@@ -1,8 +1,15 @@
# -*- coding: utf-8 -*-
+"""!@package grass.gunittest.gutils
+
+ at brief Utilities related to GRASS GIS for GRASS Python testing framework
+
+Copyright (C) 2014 by the GRASS Development Team
+This program is free software under the GNU General Public
+License (>=v2). Read the file COPYING that comes with GRASS GIS
+for details.
+
+ at author Vaclav Petras
"""
-Utilities related to GRASS GIS.
-"""
-
from .gmodules import call_module
Modified: sandbox/wenzeslaus/gunittest/invoker.py
===================================================================
--- sandbox/wenzeslaus/gunittest/invoker.py 2014-07-09 15:16:18 UTC (rev 61220)
+++ sandbox/wenzeslaus/gunittest/invoker.py 2014-07-09 20:54:01 UTC (rev 61221)
@@ -1,5 +1,16 @@
# -*- coding: utf-8 -*-
+"""!@package grass.gunittest.invoker
+ at brief GRASS Python testing framework test files invoker (runner)
+
+Copyright (C) 2014 by the GRASS Development Team
+This program is free software under the GNU General Public
+License (>=v2). Read the file COPYING that comes with GRASS GIS
+for details.
+
+ at author Vaclav Petras
+"""
+
import os
import sys
import shutil
Modified: sandbox/wenzeslaus/gunittest/loader.py
===================================================================
--- sandbox/wenzeslaus/gunittest/loader.py 2014-07-09 15:16:18 UTC (rev 61220)
+++ sandbox/wenzeslaus/gunittest/loader.py 2014-07-09 20:54:01 UTC (rev 61221)
@@ -1,7 +1,16 @@
# -*- coding: utf-8 -*-
+"""!@package grass.gunittest.loader
-"""Loading unittests."""
+ at brief GRASS Python testing framework test loading functionality
+Copyright (C) 2014 by the GRASS Development Team
+This program is free software under the GNU General Public
+License (>=v2). Read the file COPYING that comes with GRASS GIS
+for details.
+
+ at author Vaclav Petras
+"""
+
import os
import sys
import fnmatch
Modified: sandbox/wenzeslaus/gunittest/main.py
===================================================================
--- sandbox/wenzeslaus/gunittest/main.py 2014-07-09 15:16:18 UTC (rev 61220)
+++ sandbox/wenzeslaus/gunittest/main.py 2014-07-09 20:54:01 UTC (rev 61221)
@@ -1,5 +1,16 @@
# -*- coding: utf-8 -*-
+"""!@package grass.gunittest.main
+ at brief GRASS Python testing framework module for running from command line
+
+Copyright (C) 2014 by the GRASS Development Team
+This program is free software under the GNU General Public
+License (>=v2). Read the file COPYING that comes with GRASS GIS
+for details.
+
+ at author Vaclav Petras
+"""
+
import os
import sys
Modified: sandbox/wenzeslaus/gunittest/reporters.py
===================================================================
--- sandbox/wenzeslaus/gunittest/reporters.py 2014-07-09 15:16:18 UTC (rev 61220)
+++ sandbox/wenzeslaus/gunittest/reporters.py 2014-07-09 20:54:01 UTC (rev 61221)
@@ -1,5 +1,17 @@
# -*- coding: utf-8 -*-
+"""!@package grass.gunittest.reporters
+ at brief GRASS Python testing framework module for report generation
+
+Copyright (C) 2014 by the GRASS Development Team
+This program is free software under the GNU General Public
+License (>=v2). Read the file COPYING that comes with GRASS GIS
+for details.
+
+ at author Vaclav Petras
+"""
+
+
import os
import sys
import datetime
Modified: sandbox/wenzeslaus/gunittest/runner.py
===================================================================
--- sandbox/wenzeslaus/gunittest/runner.py 2014-07-09 15:16:18 UTC (rev 61220)
+++ sandbox/wenzeslaus/gunittest/runner.py 2014-07-09 20:54:01 UTC (rev 61221)
@@ -1,11 +1,20 @@
# -*- coding: utf-8 -*-
+"""!@package grass.gunittest.runner
-"""Running tests
+ at brief Testing framework module for running tests in Python unittest fashion
+Copyright (C) 2014 by the GRASS Development Team
+This program is free software under the GNU General Public
+License (>=v2). Read the file COPYING that comes with GRASS GIS
+for details.
+
+ at author Vaclav Petras
+
File content taken from Python's ``unittest.runner``, it will be used as
a template. It is not expected that something will left.
"""
+
import sys
import time
Modified: sandbox/wenzeslaus/gunittest/testsuite/test_assertions.py
===================================================================
--- sandbox/wenzeslaus/gunittest/testsuite/test_assertions.py 2014-07-09 15:16:18 UTC (rev 61220)
+++ sandbox/wenzeslaus/gunittest/testsuite/test_assertions.py 2014-07-09 20:54:01 UTC (rev 61221)
@@ -11,6 +11,7 @@
from grass.pygrass.modules import Module
import gunittest
+from gunittest.gmodules import SimpleModule
class TestTextAssertions(gunittest.TestCase):
@@ -83,6 +84,47 @@
"""
+class TestAssertCommandKeyValue(gunittest.TestCase):
+ """Test usage of `.assertCommandKeyValue` method."""
+ # pylint: disable=R0904
+
+ @classmethod
+ def setUpClass(cls):
+ cls.use_temp_region()
+ cls.runModule(Module('g.region', rast='elevation', run_=False))
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.del_temp_region()
+
+ def test_pygrass_module(self):
+ """Test syntax with Module as module"""
+ module = Module('r.info', map='elevation', flags='gr', run_=False)
+ self.assertCommandKeyValue(module,
+ reference=dict(min=55.58, max=156.33),
+ precision=0.01, sep='=')
+
+ def test_pygrass_simple_module(self):
+ """Test syntax with SimpleModule as module"""
+ module = SimpleModule('r.info', map='elevation', flags='gr')
+ self.assertCommandKeyValue(module,
+ reference=dict(min=55.58, max=156.33),
+ precision=0.01, sep='=')
+
+ def test_direct_parameters(self):
+ """Test syntax with module and its parameters as fnction parameters"""
+ self.assertCommandKeyValue('r.info', map='elevation', flags='gr',
+ reference=dict(min=55.58, max=156.33),
+ precision=0.01, sep='=')
+
+ def test_parameters_parameter(self):
+ """Test syntax with module parameters in one parameters dictionary"""
+ self.assertCommandKeyValue(module='r.info',
+ parameters=dict(map='elevation', flags='gr'),
+ reference=dict(min=55.58, max=156.33),
+ precision=0.01, sep='=')
+
+
class TestRasterMapAssertations(gunittest.TestCase):
# pylint: disable=R0904
@@ -125,7 +167,35 @@
# this also tests if we are using r.info -e flag
self.assertRasterFitsInfo('elevation', ELEVATION_MAPSET_DICT)
+ def test_assertRastersNoDifference(self):
+ """Test basic usage of assertRastersNoDifference"""
+ self.assertRastersNoDifference(actual='elevation',
+ reference='elevation',
+ precision=0, # this might need to be increased
+ msg="The same maps should have no difference")
+ self.assertRaises(self.failureException,
+ self.assertRastersNoDifference,
+ actual='elevation',
+ reference='aspect',
+ precision=1,
+ msg="Different maps should have difference")
+ def test_assertRastersNoDifference_mean(self):
+ """Test usage of assertRastersNoDifference with mean"""
+ self.assertRastersNoDifference(actual='elevation',
+ reference='elevation',
+ precision=0, # this might need to be increased
+ statistics=dict(mean=0),
+ msg="The difference of same maps should have small mean")
+ self.assertRaises(self.failureException,
+ self.assertRastersNoDifference,
+ actual='elevation',
+ reference='aspect',
+ precision=1,
+ statistics=dict(mean=0),
+ msg="The difference of different maps should have huge mean")
+
+
class TestVectorMapAssertations(gunittest.TestCase):
# pylint: disable=R0904
def test_assertVectorFitsUnivar(self):
Modified: sandbox/wenzeslaus/gunittest/testsuite/test_checkers.py
===================================================================
--- sandbox/wenzeslaus/gunittest/testsuite/test_checkers.py 2014-07-09 15:16:18 UTC (rev 61220)
+++ sandbox/wenzeslaus/gunittest/testsuite/test_checkers.py 2014-07-09 20:54:01 UTC (rev 61221)
@@ -18,7 +18,7 @@
import gunittest
from gunittest.checkers import (values_equal, text_to_keyvalue,
- compare_keyvalue,
+ keyvalue_equals,
proj_info_equals, proj_units_equals)
@@ -279,29 +279,31 @@
class TestRasterMapComparisons(gunittest.TestCase):
def test_compare_univars(self):
- self.assertTrue(compare_keyvalue(text_to_keyvalue(R_UNIVAR_ELEVATION,
+ self.assertTrue(keyvalue_equals(text_to_keyvalue(R_UNIVAR_ELEVATION,
sep='='),
text_to_keyvalue(R_UNIVAR_ELEVATION,
- sep='=')))
- self.assertFalse(compare_keyvalue(text_to_keyvalue(R_UNIVAR_ELEVATION,
+ sep='='),
+ precision=0))
+ self.assertFalse(keyvalue_equals(text_to_keyvalue(R_UNIVAR_ELEVATION,
sep='='),
text_to_keyvalue(R_UNIVAR_ELEVATION_SUBSET,
- sep='=')))
+ sep='='),
+ precision=0))
def test_compare_univars_subset(self):
- self.assertTrue(compare_keyvalue(text_to_keyvalue(R_UNIVAR_ELEVATION_SUBSET,
+ self.assertTrue(keyvalue_equals(text_to_keyvalue(R_UNIVAR_ELEVATION_SUBSET,
sep='='),
text_to_keyvalue(R_UNIVAR_ELEVATION,
sep='='),
- a_is_subset=True))
- self.assertFalse(compare_keyvalue(text_to_keyvalue(R_UNIVAR_ELEVATION,
+ a_is_subset=True, precision=0))
+ self.assertFalse(keyvalue_equals(text_to_keyvalue(R_UNIVAR_ELEVATION,
sep='='),
text_to_keyvalue(R_UNIVAR_ELEVATION_SUBSET,
sep='='),
- a_is_subset=True))
+ a_is_subset=True, precision=0))
def test_compare_univars_rounded(self):
- self.assertTrue(compare_keyvalue(text_to_keyvalue(R_UNIVAR_ELEVATION,
+ self.assertTrue(keyvalue_equals(text_to_keyvalue(R_UNIVAR_ELEVATION,
sep='='),
text_to_keyvalue(R_UNIVAR_ELEVATION_ROUNDED,
sep='='),
Modified: sandbox/wenzeslaus/gunittest/utils.py
===================================================================
--- sandbox/wenzeslaus/gunittest/utils.py 2014-07-09 15:16:18 UTC (rev 61220)
+++ sandbox/wenzeslaus/gunittest/utils.py 2014-07-09 20:54:01 UTC (rev 61221)
@@ -1,3 +1,16 @@
+# -*- coding: utf-8 -*-
+"""!@package grass.gunittest.utils
+
+ at brief GRASS Python testing framework utilities (general and test-specific)
+
+Copyright (C) 2014 by the GRASS Development Team
+This program is free software under the GNU General Public
+License (>=v2). Read the file COPYING that comes with GRASS GIS
+for details.
+
+ at author Vaclav Petras
+"""
+
import os
import sys
import shutil
@@ -4,7 +17,6 @@
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):
More information about the grass-commit
mailing list