1"""
2Name:      r_watershed_test
3Purpose:   This script is to demonstrate a unit test for r.watershed
4           module (originally developed for GIS582 course at NCSU).
5
6Author:    Stephanie Wendel
7Copyright: (C) 205 by Stephanie Wendel and the GRASS Development Team
8Licence:   This program is free software under the GNU General Public
9           License (>=v2). Read the file COPYING that comes with GRASS
10           for details.
11"""
12
13from grass.gunittest.case import TestCase
14from grass.gunittest.main import test
15
16
17class TestWatershed(TestCase):
18    """Test case for watershed module"""
19
20    # Setup variables to be used for outputs
21    accumulation = 'test_accumulation'
22    drainage = 'test_drainage'
23    basin = 'test_basin'
24    stream = 'test_stream'
25    halfbasin = 'test_halfbasin'
26    slopelength = 'test_slopelength'
27    slopesteepness = 'test_slopesteepness'
28    elevation = 'elevation'
29    lengthslope_2 = 'test_lengthslope_2'
30    stream_2 = 'test_stream_2'
31
32    @classmethod
33    def setUpClass(cls):
34        """Ensures expected computational region and setup"""
35        # Always use the computational region of the raster elevation
36        cls.use_temp_region()
37        cls.runModule('g.region', raster=cls.elevation)
38
39    @classmethod
40    def tearDownClass(cls):
41        """Remove the temporary region"""
42        cls.del_temp_region()
43
44    def tearDown(self):
45        """Remove the outputs created from the watershed module
46
47        This is executed after each test run.
48        """
49        self.runModule('g.remove', flags='f', type='raster',
50                       name=[self.accumulation, self.drainage,
51                             self.basin, self.stream,
52                             self.halfbasin, self.slopelength,
53                             self.slopesteepness, self.lengthslope_2,
54                             self.stream_2])
55
56    def test_OutputCreated(self):
57        """Test to see if the outputs are created"""
58        # run the watershed module
59        self.assertModule('r.watershed', elevation=self.elevation,
60                          threshold='10000', accumulation=self.accumulation,
61                          drainage=self.drainage, basin=self.basin, stream=self.stream,
62                          half_basin=self.halfbasin, length_slope=self.slopelength,
63                          slope_steepness=self.slopesteepness)
64        # check to see if accumulation output is in mapset
65        self.assertRasterExists(self.accumulation,
66                                msg='accumulation output was not created')
67        # check to see if drainage output is in mapset
68        self.assertRasterExists(self.drainage,
69                                msg='drainage output was not created')
70        # check to see if basin output is in mapset
71        self.assertRasterExists(self.basin,
72                                msg='basin output was not created')
73        # check to see if stream output is in mapset
74        self.assertRasterExists(self.stream,
75                                msg='stream output was not created')
76        # check to see if half.basin output is in mapset
77        self.assertRasterExists(self.halfbasin,
78                                msg='half.basin output was not created')
79        # check to see if length.slope output is in mapset
80        self.assertRasterExists(self.slopelength,
81                                msg='length.slope output was not created')
82        # check to see if slope.steepness output is in mapset
83        self.assertRasterExists(self.slopesteepness,
84                                msg='slope.steepness output was not created')
85
86    def test_fourFlag(self):
87        """Test the -4 flag and the stream and slope lengths
88
89        Tests the -4 flag to see if the stream and slope lengths are
90        approximately the same as the outputs from the default
91        module run.
92        """
93        # Run module with default settings
94        self.assertModule('r.watershed', elevation=self.elevation,
95                          threshold='10000', stream=self.stream,
96                          length_slope=self.slopelength, overwrite=True)
97        # Run module with flag 4
98        self.assertModule('r.watershed', flags='4', elevation='elevation',
99                          threshold='10000', stream=self.stream_2,
100                          length_slope=self.lengthslope_2)
101        # Use the assertRastersNoDifference with precsion 100 to see if close
102        # Compare stream output
103        self.assertRastersNoDifference(self.stream_2, self.stream, 100)
104        # Compare length_slope output
105        self.assertRastersNoDifference(self.lengthslope_2,
106                                       self.slopelength, 10)
107
108    def test_watershedThreadholdfail(self):
109        """Test if threshold of 0 or a negative is accepted
110
111        The module should fail in this test, if it fails, test succeeds.
112        """
113        self.assertModuleFail('r.watershed', elevation=self.elevation,
114                              threshold='0', stream=self.stream, overwrite=True,
115                              msg='Threshold value of 0 considered valid.')
116        self.assertModuleFail('r.watershed', elevation=self.elevation,
117                              threshold='-1', stream=self.stream, overwrite=True,
118                              msg='Threshold value of 0 considered valid.')
119
120    def test_thresholdsize(self):
121        """Test the expected range of basin output values"""
122        self.assertModule('r.watershed', elevation=self.elevation,
123                          threshold='100000', basin=self.basin, overwrite=True)
124        # it is expected that 100k Threshold has a min=2 and max=12 for this
125        # data
126        self.assertRasterMinMax(self.basin, 2, 12)
127        # it is expected that 100k Threshold has a min=2 and max=256 for this
128        # data
129        self.assertModule('r.watershed', elevation=self.elevation,
130                          threshold='10000', basin=self.basin, overwrite=True)
131        self.assertRasterMinMax(self.basin, 2, 256)
132
133    def test_drainageDirection(self):
134        """Test if the drainage direction is between -8 and 8."""
135        self.assertModule('r.watershed', elevation=self.elevation,
136                          threshold='100000', drainage=self.drainage)
137        # Make sure the min/max is between -8 and 8
138        self.assertRasterMinMax(self.drainage, -8, 8,
139                                msg='Direction must be between -8 and 8')
140
141    def test_basinValue(self):
142        """Check to see if the basin value is 0 or greater"""
143        self.assertModule('r.watershed', elevation=self.elevation,
144                          threshold='10000', basin=self.basin)
145        # Make sure the minimum value is 0 for basin value representing unique
146        # positive integer.
147        # TODO: test just min, max is theoretically unlimited
148        # or set a lower value according to what is expected with this data
149        # TODO: add test which tests that 'max basin id' == 'num of basins'
150        self.assertRasterMinMax(self.basin, 0, 1000000,
151                                msg='A basin value is less than 0 or greater than 1000000')
152
153if __name__ == '__main__':
154    test()
155