From fc9934b7fca6ba017efb0a0a44dd0188f81cb9f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Fri, 9 Oct 2026 19:28:32 +0000 Subject: [PATCH] grass.gunittest: Preserve exact bytes in MD5 test fixtures Python's default text-mode writing translates "\n" to os.linesep, which on Windows turns the MD5 test fixtures into "\r\n" files. Two tests hash them in binary mode against MD5 sums computed from "\n" content: test_text_file_binary in test_checkers.py and test_assertFileMd5 in test_assertions.py. Write these fixtures with newline="" so the bytes match the hardcoded sums on every platform. Do the same for the other fixtures written next to file_with_md5, since test_assertFilesEqualMd5 compares their hashes against it. Remove the now obsolete @xfail_windows markers from the two tests, because an unexpected success makes the gunittest run fail. Also fix the "platfrom" typos in the test_text_file_platform test name and the platform_newline attribute. Based on the newline handling in OSGeo/grass#2616 by Stefan Blumentrath, reworked against current main with AI assistance. Co-authored-by: Stefan Blumentrath <4246620+ninsbl@users.noreply.github.com> --- .../gunittest/testsuite/test_assertions.py | 21 ++++++++++++------- .../gunittest/testsuite/test_checkers.py | 8 +++---- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/python/grass/gunittest/testsuite/test_assertions.py b/python/grass/gunittest/testsuite/test_assertions.py index 64bc790b3dd..10fbf2f8c6f 100644 --- a/python/grass/gunittest/testsuite/test_assertions.py +++ b/python/grass/gunittest/testsuite/test_assertions.py @@ -18,7 +18,7 @@ class TestTextAssertions(TestCase): # pylint: disable=R0904 std_newline = "aaa\nbbb\n" - platfrom_newline = "aaa{nl}bbb{nl}".format(nl=os.linesep) + platform_newline = "aaa{nl}bbb{nl}".format(nl=os.linesep) def test_assertLooksLike(self): self.assertLooksLike("Generated map is ", "Generated map is <...>") @@ -63,7 +63,7 @@ def do_all_combidnations(self, first, second, function): def test_assertMultiLineEqual(self): r"""Test different combinations of ``\n`` and os.linesep""" self.do_all_combidnations( - self.std_newline, self.platfrom_newline, function=self.assertMultiLineEqual + self.std_newline, self.platform_newline, function=self.assertMultiLineEqual ) def test_assertMultiLineEqual_raises(self): @@ -78,7 +78,7 @@ def test_assertMultiLineEqual_raises(self): def test_assertEqual(self): """Test for of newlines for strings (uses overwritten assertMultiLineEqual())""" self.do_all_combidnations( - self.std_newline, self.platfrom_newline, function=self.assertEqual + self.std_newline, self.platform_newline, function=self.assertEqual ) def test_assertEqual_raises(self): @@ -351,18 +351,24 @@ def setUpClass(cls): open(cls.emtpy_file, "w").close() cls.file_with_md5 = cls.__name__ + "_this_is_a_file_with_known_md5" file_content = "Content of the file with known MD5.\n" - Path(cls.file_with_md5).write_text(file_content) + + def write_exact(filename, content): + # newline="" keeps \n as-is instead of translating it to \r\n on + # Windows, so the binary MD5 matches the value below. + Path(filename).write_text(content, newline="") + + write_exact(cls.file_with_md5, file_content) # MD5 sum created using: # echo 'Content of the file with known MD5.' > some_file.txt # md5sum some_file.txt cls.file_md5 = "807bba4ffac4bb351bc3f27853009949" cls.file_with_same_content = cls.__name__ + "_file_with_same_content" - Path(cls.file_with_same_content).write_text(file_content) + write_exact(cls.file_with_same_content, file_content) cls.file_with_different_content = cls.__name__ + "_file_with_different_content" - Path(cls.file_with_different_content).write_text( - file_content + " Something else here." + write_exact( + cls.file_with_different_content, file_content + " Something else here." ) @classmethod @@ -386,7 +392,6 @@ def test_assertFileExists_empty_file(self): self.failureException, self.assertFileExists, filename=self.emtpy_file ) - @xfail_windows def test_assertFileMd5(self): self.assertFileMd5(filename=self.file_with_md5, md5=self.file_md5) self.assertRaises( diff --git a/python/grass/gunittest/testsuite/test_checkers.py b/python/grass/gunittest/testsuite/test_checkers.py index c1a692fbc6d..a657756cef0 100644 --- a/python/grass/gunittest/testsuite/test_checkers.py +++ b/python/grass/gunittest/testsuite/test_checkers.py @@ -22,7 +22,6 @@ file_md5, text_file_md5, ) -from grass.gunittest.utils import xfail_windows class TestValuesEqual(TestCase): @@ -369,8 +368,8 @@ def setUpClass(cls): with open(cls.correct_file_name_platform_nl, "w") as f: # \n should be converted to platform newline f.writelines(line + "\n" for line in CORRECT_LINES) - with open(cls.correct_file_name_unix_nl, "w") as f: - # binary mode will write pure \n + with open(cls.correct_file_name_unix_nl, "w", newline="") as f: + # newline="" writes pure \n on every platform f.writelines(line + "\n" for line in CORRECT_LINES) with open(cls.wrong_file_name, "w") as f: # \n should be converted to platform newline @@ -382,7 +381,6 @@ def tearDownClass(cls): try_remove(cls.correct_file_name_unix_nl) try_remove(cls.wrong_file_name) - @xfail_windows def test_text_file_binary(self): r"""File with ``\n`` (LF) newlines as binary (MD5 has ``\n``).""" self.assertEqual( @@ -391,7 +389,7 @@ def test_text_file_binary(self): msg="MD5 sums different", ) - def test_text_file_platfrom(self): + def test_text_file_platform(self): r"""Text file with platform dependent newlines""" self.assertEqual( text_file_md5(self.correct_file_name_platform_nl),