Browse Source

gunittest: 3D raster assert methods (with tests), vector assert methods (v.info-based with tests)

git-svn-id: https://svn.osgeo.org/grass/grass/trunk@61265 15284696-431f-4ddb-bdfa-cd5b030d7da7
Vaclav Petras 10 years ago
parent
commit
424d10f949

+ 296 - 12
lib/python/gunittest/case.py

@@ -14,7 +14,6 @@ for details.
 
 
 import os
 import os
 import subprocess
 import subprocess
-import time
 import unittest
 import unittest
 from unittest.util import safe_repr
 from unittest.util import safe_repr
 
 
@@ -224,9 +223,9 @@ class TestCase(unittest.TestCase):
 
 
     def assertRasterFitsInfo(self, raster, reference,
     def assertRasterFitsInfo(self, raster, reference,
                              precision=None, msg=None):
                              precision=None, msg=None):
-        r"""Test that raster map has the values obtained by v.univar module.
+        r"""Test that raster map has the values obtained by r.univar module.
 
 
-        The function does not require all values from v.univar.
+        The function does not require all values from r.univar.
         Only the provided values are tested.
         Only the provided values are tested.
         Typical example is checking minimum, maximum and type of the map::
         Typical example is checking minimum, maximum and type of the map::
 
 
@@ -243,6 +242,103 @@ class TestCase(unittest.TestCase):
                                   reference=reference, msg=msg, sep='=',
                                   reference=reference, msg=msg, sep='=',
                                   precision=precision)
                                   precision=precision)
 
 
+    def assertRaster3dFitsUnivar(self, raster, reference,
+                                 precision=None, msg=None):
+        r"""Test that 3D raster map has the values obtained by r3.univar module.
+
+        The function does not require all values from r3.univar.
+        Only the provided values are tested.
+
+        Use keyword arguments syntax for all function parameters.
+
+        Does not -e (extended statistics) flag, use `assertModuleKeyValue()`
+        for the full interface of arbitrary module.
+        """
+        self.assertModuleKeyValue(module='r3.univar',
+                                  map=raster,
+                                  separator='=',
+                                  flags='g',
+                                  reference=reference, msg=msg, sep='=',
+                                  precision=precision)
+
+    def assertRaster3dFitsInfo(self, raster, reference,
+                               precision=None, msg=None):
+        r"""Test that raster map has the values obtained by r3.info module.
+
+        The function does not require all values from r3.info.
+        Only the provided values are tested.
+
+        Use keyword arguments syntax for all function parameters.
+
+        This function supports values obtained by -g (info) and -r (range).
+        """
+        self.assertModuleKeyValue(module='r3.info',
+                                  map=raster, flags='gr',
+                                  reference=reference, msg=msg, sep='=',
+                                  precision=precision)
+
+    def assertVectorFitsTopoInfo(self, vector, reference, msg=None):
+        r"""Test that raster map has the values obtained by ``v.info`` module.
+
+        This function uses ``-t`` flag of ``v.info`` module to get topology
+        info, so the reference dictionary should contain appropriate set or
+        subset of values (only the provided values are tested).
+
+        A example of checking number of points::
+
+            topology = dict(points=10938, primitives=10938)
+            self.assertVectorFitsTopoInfo(map='bridges', reference=topology)
+
+        Note that here we are checking also the number of primitives to prove
+        that there are no other features besides points.
+
+        No precision is applied (no difference is required). So, this function
+        is not suitable for testing items which are floating point number
+        (no such items are currently in topological information).
+
+        Use keyword arguments syntax for all function parameters.
+        """
+        self.assertModuleKeyValue(module='v.info',
+                                  map=vector, flags='t',
+                                  reference=reference, msg=msg, sep='=',
+                                  precision=0)
+
+    def assertVectorFitsRegionInfo(self, vector, reference,
+                                   precision, msg=None):
+        r"""Test that raster map has the values obtained by ``v.info`` module.
+
+        This function uses ``-g`` flag of ``v.info`` module to get topology
+        info, so the reference dictionary should contain appropriate set or
+        subset of values (only the provided values are tested).
+
+        Use keyword arguments syntax for all function parameters.
+        """
+        self.assertModuleKeyValue(module='v.info',
+                                  map=vector, flags='g',
+                                  reference=reference, msg=msg, sep='=',
+                                  precision=precision)
+
+    def assertVectorFitsExtendedInfo(self, vector, reference, msg=None):
+        r"""Test that raster map has the values obtained by ``v.info`` module.
+
+        This function uses ``-e`` flag of ``v.info`` module to get topology
+        info, so the reference dictionary should contain appropriate set or
+        subset of values (only the provided values are tested).
+
+        The most useful items for testing (considering circumstances of test
+        invocation) are name, title, level and num_dblinks. (When testing
+        storing of ``v.info -e`` metadata, the selection might be different.)
+
+        No precision is applied (no difference is required). So, this function
+        is not suitable for testing items which are floating point number.
+
+        Use keyword arguments syntax for all function parameters.
+        """
+        self.assertModuleKeyValue(module='v.info',
+                                  map=vector, flags='e',
+                                  reference=reference, msg=msg, sep='=',
+                                  precision=0)
+
     def assertVectorFitsUnivar(self, map, column, reference, msg=None,
     def assertVectorFitsUnivar(self, map, column, reference, msg=None,
                                layer=None, type=None, where=None,
                                layer=None, type=None, where=None,
                                precision=None):
                                precision=None):
@@ -303,6 +399,36 @@ class TestCase(unittest.TestCase):
                           a=actual['max'], r=refmax, m=map, o=actual['min']))
                           a=actual['max'], r=refmax, m=map, o=actual['min']))
             self.fail(self._formatMessage(msg, stdmsg))
             self.fail(self._formatMessage(msg, stdmsg))
 
 
+    # TODO: use precision?
+    # TODO: write a test for this method with r.in.ascii
+    # TODO: almost the same as 2D version
+    def assertRaster3dMinMax(self, map, refmin, refmax, msg=None):
+        """Test that 3D raster map minimum and maximum are within limits.
+
+        Map minimum and maximum is tested against expression::
+
+            refmin <= actualmin and refmax >= actualmax
+
+        Use keyword arguments syntax for all function parameters.
+
+        To check that more statistics have certain values use
+        `assertRaster3DFitsUnivar()` or `assertRaster3DFitsInfo()`
+        """
+        stdout = call_module('r3.info', map=map, flags='r')
+        actual = text_to_keyvalue(stdout, sep='=')
+        if refmin > actual['min']:
+            stdmsg = ('The actual minimum ({a}) is smaller than the reference'
+                      ' one ({r}) for 3D raster map {m}'
+                      ' (with maximum {o})'.format(
+                          a=actual['min'], r=refmin, m=map, o=actual['max']))
+            self.fail(self._formatMessage(msg, stdmsg))
+        if refmax < actual['max']:
+            stdmsg = ('The actual maximum ({a}) is greater than the reference'
+                      ' one ({r}) for 3D raster map {m}'
+                      ' (with minimum {o})'.format(
+                          a=actual['max'], r=refmax, m=map, o=actual['min']))
+            self.fail(self._formatMessage(msg, stdmsg))
+
     def assertFileExists(self, filename, msg=None,
     def assertFileExists(self, filename, msg=None,
                          skip_size_check=False, skip_access_check=False):
                          skip_size_check=False, skip_access_check=False):
         """Test the existence of a file.
         """Test the existence of a file.
@@ -387,8 +513,46 @@ class TestCase(unittest.TestCase):
                                                          s=second))
                                                          s=second))
         return diff
         return diff
 
 
+    # TODO: name of map generation is repeted three times
+    # TODO: this method is almost the same as the one for 2D
+    def _compute_difference_raster3d(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('r3.mapcalc',
+                    stdin='"{d}" = "{f}" - "{s}"'.format(d=diff,
+                                                         f=first,
+                                                         s=second))
+        return diff
+
+    def _compute_vector_xor(self, ainput, alayer, binput, blayer, name_part):
+        """Compute symmetric difference (xor) of two vectors
+
+        :returns: name of a new vector
+        """
+        diff = ('tmp_' + self.id() + '_compute_difference_vector_'
+                + name_part + '_' + ainput + '_' + alayer
+                + '_minus_' + binput + '_' + blayer)
+        call_module('v.overlay', operator='xor', ainput=ainput, binput=binput,
+                    alayer=alayer, blayer=blayer,
+                    output=diff, atype='area', btype='area', olayer='')
+        # trying to avoid long reports full of categories by olayer=''
+        # olayer   Output layer for new category, ainput and binput
+        #     If 0 or not given, the category is not written
+        return diff
+
     def assertRastersNoDifference(self, actual, reference,
     def assertRastersNoDifference(self, actual, reference,
-                                   precision, statistics=None, msg=None):
+                                  precision, statistics=None, msg=None):
         """Test that `actual` raster is not different from `reference` raster
         """Test that `actual` raster is not different from `reference` raster
 
 
         Method behaves in the same way as `assertRasterFitsUnivar()`
         Method behaves in the same way as `assertRasterFitsUnivar()`
@@ -407,14 +571,15 @@ class TestCase(unittest.TestCase):
                                           reference=statistics, msg=msg)
                                           reference=statistics, msg=msg)
             finally:
             finally:
                 call_module('g.remove', rast=diff)
                 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)
+        else:
+            # 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)
 
 
     def assertRastersDifference(self, actual, reference,
     def assertRastersDifference(self, actual, reference,
                                 statistics, precision, msg=None):
                                 statistics, precision, msg=None):
@@ -433,6 +598,125 @@ class TestCase(unittest.TestCase):
         finally:
         finally:
             call_module('g.remove', rast=diff)
             call_module('g.remove', rast=diff)
 
 
+    def assertRasters3dNoDifference(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 ``reference - actual``.
+        If statistics is not given ``dict(min=-precision, max=precision)``
+        is used.
+        """
+        if statistics is None or sorted(statistics.keys()) == ['max', 'min']:
+            if statistics is None:
+                statistics = dict(min=-precision, max=precision)
+            diff = self._compute_difference_raster3d(reference, actual,
+                                                     'assertRasters3dNoDifference')
+            try:
+                self.assertModuleKeyValue('r3.info', map=diff, flags='r',
+                                          sep='=', precision=precision,
+                                          reference=statistics, msg=msg)
+            finally:
+                call_module('g.remove', rast3d=diff)
+        else:
+            # 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.assertRasters3dDifference(actual=actual, reference=reference,
+                                           statistics=statistics,
+                                           precision=precision, msg=msg)
+
+    def assertRasters3dDifference(self, actual, reference,
+                                statistics, precision, msg=None):
+        """Test statistical values of difference of reference and actual rasters
+
+        For cases when you are interested in no or minimal difference,
+        use `assertRastersNoDifference()` instead.
+
+        This method should not be used to test r3.mapcalc or r3.univar.
+        """
+        diff = self._compute_difference_raster3d(reference, actual,
+                                                 'assertRasters3dDifference')
+        try:
+            self.assertRaster3dFitsUnivar(raster=diff, reference=statistics,
+                                          precision=precision, msg=msg)
+        finally:
+            call_module('g.remove', rast3d=diff)
+
+    # TODO: this works only in 2D
+    # TODO: write tests
+    def assertVectorIsVectorBuffered(self, actual, reference, precision, msg=None):
+        """
+
+        This method should not be used to test v.buffer, v.overlay or v.select.
+        """
+        # TODO: if msg is None: add info specific to this function
+        layer = '-1'
+        module = SimpleModule('v.info', flags='t', map=reference)
+        self.runModule(module)
+        ref_topo = text_to_keyvalue(module.outputs.stdout, sep='=')
+        module = SimpleModule('v.info', flags='g', map=reference)
+        self.runModule(module)
+        ref_info = text_to_keyvalue(module.outputs.stdout, sep='=')
+        self.assertVectorFitsTopoInfo(vector=actual, reference=ref_topo,
+                                      msg=msg)
+        self.assertVectorFitsRegionInfo(vector=actual, reference=ref_info,
+                                        msg=msg, precision=precision)
+        remove = []
+        buffered = reference + '_buffered'  # TODO: more unique name
+        intersection = reference + '_intersection'  # TODO: more unique name
+        self.runModule('v.buffer', input=reference, layer=layer,
+                       output=buffered, distance=precision)
+        remove.append(buffered)
+        try:
+            self.runModule('v.overlay', operator='and', ainput=actual,
+                           binput=reference,
+                           alayer=layer, blayer=layer,
+                           output=intersection, atype='area', btype='area',
+                           olayer='')
+            remove.append(intersection)
+            self.assertVectorFitsTopoInfo(vector=intersection, reference=ref_topo,
+                                          msg=msg)
+            self.assertVectorFitsRegionInfo(vector=intersection, reference=ref_info,
+                                            msg=msg, precision=precision)
+        finally:
+            call_module('g.remove', vect=remove)
+
+    # TODO: write tests
+    def assertVectorsNoAreaDifference(self, actual, reference, precision,
+                                      layer=1, msg=None):
+        """Test statistical values of difference of reference and actual rasters
+
+        Works only for areas.
+
+        Use keyword arguments syntax for all function parameters.
+
+        This method should not be used to test v.overlay or v.select.
+        """
+        diff = self._compute_xor_vectors(ainput=reference, binput=actual,
+                                         alayer=layer, blayer=layer,
+                                         name_part='assertVectorsNoDifference')
+        try:
+            module = SimpleModule('v.to.db', map=diff,
+                                  flags='pc', separator='=')
+            self.runModule(module)
+            # the output of v.to.db -pc sep== should look like:
+            # ...
+            # 43=98606087.5818323
+            # 44=727592.902311112
+            # total area=2219442027.22035
+            total_area = module.outputs.stdout.splitlines()[-1].split('=')[-1]
+            if total_area > precision:
+                stdmsg = ("Area of difference of vectors <{va}> and <{vr}>"
+                          " should be 0"
+                          " in the given precision ({p}) not {a}").format(
+                    va=actual, vr=reference, p=precision, a=total_area)
+                self.fail(self._formatMessage(msg, stdmsg))
+        finally:
+            call_module('g.remove', vect=diff)
+
     @classmethod
     @classmethod
     def runModule(cls, module, **kwargs):
     def runModule(cls, module, **kwargs):
         """Run PyGRASS module.
         """Run PyGRASS module.

+ 1 - 0
lib/python/gunittest/main.py

@@ -118,6 +118,7 @@ def discovery():
     sys.exit(not program.result.wasSuccessful())
     sys.exit(not program.result.wasSuccessful())
 
 
 
 
+# TODO: makefile rule should depend on the whole build
 # TODO: create a full interface (using grass parser or argparse)
 # TODO: create a full interface (using grass parser or argparse)
 if __name__ == '__main__':
 if __name__ == '__main__':
     if len(sys.argv) == 4:
     if len(sys.argv) == 4:

+ 7 - 32
lib/python/gunittest/testsuite/test_assertions.py

@@ -73,18 +73,8 @@ max=156.329864501953
 # values rounded manually to maximal expected perecision
 # values rounded manually to maximal expected perecision
 ELEVATION_MINMAX_DICT = {'min': 55.58, 'max': 156.33}
 ELEVATION_MINMAX_DICT = {'min': 55.58, 'max': 156.33}
 
 
-V_UNIVAR_BRIDGES_WIDTH_SUBSET = """n=10938
-nmissing=0
-nnull=0
-min=0
-max=1451
-range=1451
-sum=2.6299e+06
-mean=240.437
-"""
-
 
 
-class TestAssertCommandKeyValue(grass.gunittest.TestCase):
+class TestAssertModuleKeyValue(grass.gunittest.TestCase):
     """Test usage of `assertModuleKeyValue` method."""
     """Test usage of `assertModuleKeyValue` method."""
     # pylint: disable=R0904
     # pylint: disable=R0904
 
 
@@ -113,7 +103,7 @@ class TestAssertCommandKeyValue(grass.gunittest.TestCase):
                                   precision=0.01, sep='=')
                                   precision=0.01, sep='=')
 
 
     def test_direct_parameters(self):
     def test_direct_parameters(self):
-        """Test syntax with module and its parameters as fnction parameters"""
+        """Test syntax with module and its parameters as function parameters"""
         self.assertModuleKeyValue('r.info', map='elevation', flags='gr',
         self.assertModuleKeyValue('r.info', map='elevation', flags='gr',
                                   reference=dict(min=55.58, max=156.33),
                                   reference=dict(min=55.58, max=156.33),
                                   precision=0.01, sep='=')
                                   precision=0.01, sep='=')
@@ -126,7 +116,7 @@ class TestAssertCommandKeyValue(grass.gunittest.TestCase):
                                   precision=0.01, sep='=')
                                   precision=0.01, sep='=')
 
 
 
 
-class TestRasterMapAssertations(grass.gunittest.TestCase):
+class TestRasterMapAssertions(grass.gunittest.TestCase):
     # pylint: disable=R0904
     # pylint: disable=R0904
 
 
     @classmethod
     @classmethod
@@ -165,7 +155,9 @@ class TestRasterMapAssertations(grass.gunittest.TestCase):
                                   ELEVATION_MINMAX, precision=0.01)
                                   ELEVATION_MINMAX, precision=0.01)
 
 
     def test_dict_as_parameter(self):
     def test_dict_as_parameter(self):
-        # this also tests if we are using r.info -e flag
+        """This also tests if we are using r.info -e flag and that precision is
+        not required for strings.
+        """
         self.assertRasterFitsInfo('elevation', ELEVATION_MAPSET_DICT)
         self.assertRasterFitsInfo('elevation', ELEVATION_MAPSET_DICT)
 
 
     def test_assertRastersNoDifference(self):
     def test_assertRastersNoDifference(self):
@@ -197,24 +189,7 @@ class TestRasterMapAssertations(grass.gunittest.TestCase):
                           msg="The difference of different maps should have huge mean")
                           msg="The difference of different maps should have huge mean")
 
 
 
 
-class TestVectorMapAssertations(grass.gunittest.TestCase):
-    # pylint: disable=R0904
-    def test_assertVectorFitsUnivar(self):
-        self.assertVectorFitsUnivar(map='bridges', column='WIDTH',
-                                    reference=V_UNIVAR_BRIDGES_WIDTH_SUBSET,
-                                    precision=0.01)
-        self.assertRaises(self.failureException,
-                          self.assertVectorFitsUnivar,
-                          map='bridges', column='YEAR_BUILT',
-                          reference=V_UNIVAR_BRIDGES_WIDTH_SUBSET,
-                          precision=0.01)
-        self.assertRaises(ValueError,
-                          self.assertVectorFitsUnivar,
-                          map='bridges', column='WIDTH',
-                          reference=RANDOM_KEYVALUES)
-
-
-class TestFileAssertations(grass.gunittest.TestCase):
+class TestFileAssertions(grass.gunittest.TestCase):
     # pylint: disable=R0904
     # pylint: disable=R0904
 
 
     @classmethod
     @classmethod

+ 128 - 0
lib/python/gunittest/testsuite/test_assertions_rast3d.py

@@ -0,0 +1,128 @@
+# -*- coding: utf-8 -*-
+
+"""
+Tests assertion methods for 3D rasters.
+"""
+
+
+from grass.exceptions import CalledModuleError
+
+import grass.gunittest as gunittest
+
+
+class TestRaster3dMapAssertions(gunittest.TestCase):
+    # pylint: disable=R0904
+    constant_map = 'raster3d_assertions_constant'
+    rcd_increasing_map = 'raster3d_assertions_rcd_increasing'
+
+    @classmethod
+    def setUpClass(cls):
+        cls.use_temp_region()
+        # TODO: here we should actually not call self.runModule but call_module
+        cls.runModule('g.region', n=200, s=100, e=400, w=200,
+                      t=500, b=450, res3=1)
+        cls.runModule('r3.mapcalc', expression='%s = 155' % cls.constant_map)
+        cls.runModule('r3.mapcalc',
+                      expression='%s = row() + col() + depth()' % cls.rcd_increasing_map)
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.del_temp_region()
+        # TODO: input as list does not work, why?
+        cls.runModule('g.remove',
+                      rast3d=','.join([cls.constant_map, cls.rcd_increasing_map]))
+
+    def test_assertRaster3dFitsUnivar(self):
+        reference = dict(n=1000000,
+                         null_cells=0,
+                         cells=1000000,
+                         min=155,
+                         max=155,
+                         range=0,
+                         mean=155,
+                         mean_of_abs=155,
+                         stddev=0,
+                         variance=0,
+                         coeff_var=0,
+                         sum=155000000)
+        self.assertRaster3dFitsUnivar(self.constant_map, reference=reference,
+                                      precision=0.000001)
+        self.assertRaises(self.failureException,
+                          self.assertRaster3dFitsUnivar,
+                          self.rcd_increasing_map,
+                          reference=reference, precision=1)
+        self.assertRaises(ValueError,
+                          self.assertRaster3dFitsUnivar,
+                          self.constant_map, reference=dict(a=4, b=5, c=6))
+        self.assertRaises(CalledModuleError,
+                          self.assertRaster3dFitsUnivar,
+                          'does_not_exists', reference=dict(a=4, b=5, c=6))
+
+    def test_assertRaster3dFitsInfo(self):
+        reference = dict(north=200,
+                         south=100,
+                         east=400,
+                         west=200,
+                         bottom=450,
+                         top=500,
+                         nsres=1,
+                         ewres=1,
+                         tbres=1,
+                         rows=100,
+                         cols=200,
+                         depths=50)
+        self.assertRaster3dFitsInfo(self.constant_map, reference=reference)
+
+        reference['north'] = 500
+        self.assertRaises(self.failureException,
+                          self.assertRaster3dFitsInfo,
+                          self.constant_map, reference=reference)
+        self.assertRaises(ValueError,
+                          self.assertRaster3dFitsInfo,
+                          self.constant_map, reference=dict(a=5))
+
+    def test_common_values_info_univar(self):
+        minmax = dict(min=3, max=350)
+        self.assertRaster3dFitsUnivar(self.rcd_increasing_map,
+                                      minmax, precision=0.01)
+        self.assertRaster3dFitsInfo(self.rcd_increasing_map,
+                                    minmax, precision=0.01)
+
+    def test_string_as_parameter(self):
+        self.assertRaster3dFitsInfo(self.constant_map,
+                                    reference="max=155", precision=1)
+        self.assertRaster3dFitsUnivar(self.rcd_increasing_map,
+                                      reference="n=1000000", precision=0)
+
+    def test_assertRasters3dNoDifference(self):
+        """Test basic usage of assertRastersNoDifference"""
+        # precision might need to be increased
+        self.assertRasters3dNoDifference(actual=self.rcd_increasing_map,
+                                       reference=self.rcd_increasing_map,
+                                       precision=0,
+                                       msg="The same maps should have no difference")
+        self.assertRaises(self.failureException,
+                          self.assertRasters3dNoDifference,
+                          actual=self.constant_map,
+                          reference=self.rcd_increasing_map,
+                          precision=1,
+                          msg="Different maps should have difference")
+
+    def test_assertRasters3dNoDifference_mean(self):
+        """Test usage of assertRastersNoDifference with mean"""
+        self.assertRasters3dNoDifference(actual=self.rcd_increasing_map,
+                                       reference=self.rcd_increasing_map,
+                                       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.assertRasters3dNoDifference,
+                          actual=self.constant_map,
+                          reference=self.rcd_increasing_map,
+                          precision=1,
+                          statistics=dict(mean=0),
+                          msg="The difference of different maps should have huge mean")
+
+
+if __name__ == '__main__':
+    gunittest.test()

+ 114 - 0
lib/python/gunittest/testsuite/test_assertions_vect.py

@@ -0,0 +1,114 @@
+# -*- coding: utf-8 -*-
+
+"""
+Tests assertion methods for vectors.
+"""
+
+import grass.gunittest as gunittest
+
+
+V_UNIVAR_BRIDGES_WIDTH_SUBSET = """n=10938
+nmissing=0
+nnull=0
+min=0
+max=1451
+range=1451
+sum=2.6299e+06
+mean=240.437
+"""
+
+RANDOM_KEYVALUES = """abc=2025000
+aaa=55.5787925720215
+bbb=156.329864501953
+"""
+
+# v.info bridges -t
+V_UNIVAR_BRIDGES_TOPO = dict(
+    nodes=0,
+    points=10938,
+    lines=0,
+    boundaries=0,
+    centroids=0,
+    areas=0,
+    islands=0,
+    primitives=10938,
+    map3d=0,
+)
+
+# v.info bridges -g and rounded
+V_UNIVAR_BRIDGES_REGION = dict(
+    north=317757,
+    south=14691,
+    east=915045,
+    west=125794,
+    top=0,
+    bottom=0,
+)
+
+# v.info bridges -g and reduced to minumum
+V_UNIVAR_BRIDGES_EXTENDED = dict(
+    name='bridges',
+    level=2,
+    num_dblinks=1,
+)
+
+
+class TestVectorMapAssertions(gunittest.TestCase):
+    """Test assertions of map meta and statistics"""
+    # pylint: disable=R0904
+    def test_assertVectorFitsUnivar(self):
+        self.assertVectorFitsUnivar(map='bridges', column='WIDTH',
+                                    reference=V_UNIVAR_BRIDGES_WIDTH_SUBSET,
+                                    precision=0.01)
+        self.assertRaises(self.failureException,
+                          self.assertVectorFitsUnivar,
+                          map='bridges', column='YEAR_BUILT',
+                          reference=V_UNIVAR_BRIDGES_WIDTH_SUBSET,
+                          precision=0.01)
+        self.assertRaises(ValueError,
+                          self.assertVectorFitsUnivar,
+                          map='bridges', column='WIDTH',
+                          reference=RANDOM_KEYVALUES)
+
+    def test_assertVectorFitsTopoInfo(self):
+        self.assertVectorFitsTopoInfo('bridges', V_UNIVAR_BRIDGES_TOPO)
+        self.assertRaises(self.failureException,
+                          self.assertVectorFitsTopoInfo,
+                          'lakes',
+                          V_UNIVAR_BRIDGES_TOPO)
+        self.assertRaises(ValueError,
+                          self.assertVectorFitsTopoInfo,
+                          'bridges', RANDOM_KEYVALUES)
+        self.assertRaises(ValueError,
+                          self.assertVectorFitsTopoInfo,
+                          'bridges', V_UNIVAR_BRIDGES_REGION)
+
+    def test_assertVectorFitsRegionInfo(self):
+        self.assertVectorFitsRegionInfo('bridges', V_UNIVAR_BRIDGES_REGION, precision=1.0)
+        self.assertRaises(self.failureException,
+                          self.assertVectorFitsRegionInfo,
+                          'lakes', V_UNIVAR_BRIDGES_REGION, precision=1.0)
+        self.assertRaises(ValueError,
+                          self.assertVectorFitsRegionInfo,
+                          'bridges', RANDOM_KEYVALUES, precision=1.0)
+        self.assertRaises(ValueError,
+                          self.assertVectorFitsRegionInfo,
+                          'bridges', V_UNIVAR_BRIDGES_TOPO, precision=1.0)
+
+    def test_assertVectorFitsExtendedInfo(self):
+        self.assertVectorFitsExtendedInfo('bridges', V_UNIVAR_BRIDGES_EXTENDED)
+        self.assertRaises(self.failureException,
+                          self.assertVectorFitsExtendedInfo,
+                          'lakes',
+                          V_UNIVAR_BRIDGES_EXTENDED)
+        self.assertRaises(ValueError,
+                          self.assertVectorFitsExtendedInfo,
+                          'bridges',
+                          RANDOM_KEYVALUES)
+        self.assertRaises(ValueError,
+                          self.assertVectorFitsExtendedInfo,
+                          'bridges',
+                          V_UNIVAR_BRIDGES_TOPO)
+
+if __name__ == '__main__':
+    gunittest.test()