From 2e5f3b75939bffbe8fa067079fe9a1fb2ce60a1a Mon Sep 17 00:00:00 2001 From: RAJVEER42 Date: Wed, 10 Jun 2026 00:47:23 +0530 Subject: [PATCH] r.soillossbare: restore the user's region with a temporary region r.soillossbare set the computational region to its own working resolution via "g.region -a res=..." but never restored it, so after the module ran the user was left with their region resolution silently changed. Use gs.use_temp_region() so the original region is restored automatically on exit, including on error. Add a testsuite that fails without the fix (the region is left at the module's resolution) and passes with it. --- src/raster/r.soillossbare/r.soillossbare.py | 3 + .../testsuite/test_r_soillossbare.py | 65 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 src/raster/r.soillossbare/testsuite/test_r_soillossbare.py diff --git a/src/raster/r.soillossbare/r.soillossbare.py b/src/raster/r.soillossbare/r.soillossbare.py index 4aad947ab1..e2b3643628 100755 --- a/src/raster/r.soillossbare/r.soillossbare.py +++ b/src/raster/r.soillossbare/r.soillossbare.py @@ -518,6 +518,9 @@ def main(): if gs.verbosity() > 2: quiet = False + # Use a temporary region so the user's computational region is restored + # on exit instead of being left at the module's working resolution + gs.use_temp_region() gs.run_command("g.region", flags="a", res=resolution) if flowacc: diff --git a/src/raster/r.soillossbare/testsuite/test_r_soillossbare.py b/src/raster/r.soillossbare/testsuite/test_r_soillossbare.py new file mode 100644 index 0000000000..41eed42417 --- /dev/null +++ b/src/raster/r.soillossbare/testsuite/test_r_soillossbare.py @@ -0,0 +1,65 @@ +import grass.script as gs + +from grass.gunittest.case import TestCase +from grass.gunittest.main import test + + +class TestRSoillossbare(TestCase): + elevation = "test_elevation" + kfactor = "test_kfactor" + rfactor = "test_rfactor" + output = "test_soillossbare" + + @classmethod + def setUpClass(cls): + cls.use_temp_region() + cls.runModule("g.region", n=10, s=0, e=10, w=0, res=1) + cls.runModule( + "r.mapcalc", expression=f"{cls.elevation} = row() + col()", overwrite=True + ) + cls.runModule("r.mapcalc", expression=f"{cls.kfactor} = 0.3", overwrite=True) + cls.runModule("r.mapcalc", expression=f"{cls.rfactor} = 100", overwrite=True) + + @classmethod + def tearDownClass(cls): + cls.runModule( + "g.remove", + flags="f", + type="raster", + name=[cls.elevation, cls.kfactor, cls.rfactor], + ) + cls.del_temp_region() + + def tearDown(self): + self.runModule("g.remove", flags="f", type="raster", name=self.output) + + def test_region_preserved(self): + """The module must not leave the user's computational region changed. + + The module sets the region to its own working resolution. Here a + resolution (0.5) different from the current region (res 1) is used, so + a module that does not restore the region would leave it at res 0.5. + """ + self.runModule("g.region", n=10, s=0, e=10, w=0, res=1) + before = gs.region() + self.assertModule( + "r.soillossbare", + elevation=self.elevation, + kfactor=self.kfactor, + rfactor=self.rfactor, + resolution=0.5, + soillossbare=self.output, + flowaccmethod="r.watershed", + flags="r", + overwrite=True, + ) + after = gs.region() + self.assertEqual( + before, + after, + msg="r.soillossbare must restore the user's computational region.", + ) + + +if __name__ == "__main__": + test()