From 9ec2cce20c8d5932c15250f29e8cbe22863c941e Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Sun, 27 Apr 2025 22:52:10 +0200 Subject: [PATCH 1/7] Improve msgfmt.py test coverage --- Lib/test/test_tools/test_msgfmt.py | 356 +++++++++++++++++++++++++++++ 1 file changed, 356 insertions(+) diff --git a/Lib/test/test_tools/test_msgfmt.py b/Lib/test/test_tools/test_msgfmt.py index 7be606bbff606a..64af0544bd1314 100644 --- a/Lib/test/test_tools/test_msgfmt.py +++ b/Lib/test/test_tools/test_msgfmt.py @@ -240,6 +240,362 @@ def test_strings(self): with self.assertRaises(Exception): msgfmt.make('messages.po', 'messages.mo') + def test_general_syntax_errors(self): + invalid_po_files = ( + '', + '"', + '""', + '"foo"', + # 'msgid', # invalid but currently accepted + 'msgstr', + 'msgid_plural', + # 'msgctxt', # invalid but currently accepted + 'msgstr', + 'msgstr[0]', + '[0]', + + # unclosed string + ''' +msgid " +msgstr "bar" +''', + + # unclosed string + ''' +msgid "foo +msgstr "bar" +''', + + # unclosed string + ''' +msgid "foo" " +msgstr "bar" +''', + + # unclosed string + ''' +msgid "foo" +" +msgstr "bar" +''', + + # illegal backslash + ''' +msgid "foo\\" +" +msgstr "bar" +''', + + # msgid with an index + ''' +msgid[0] "foo" +msgstr "bar" +''', + + # invalid plural index + # invalid but currently accepted +# ''' +# msgid "foo" +# msgid_plural "foos" +# msgstr[foo] "baz" +# ''', + + # invalid plural index + ''' +msgid "foo" +msgid_plural "foos" +msgstr[0 "baz" +''', + + # invalid plural index + # invalid but currently accepted +# ''' +# msgid "foo" +# msgid_plural "foos" +# msgstr[] "baz" +# ''', + + # invalid plural index + ''' +msgid "foo" +msgid_plural "foos" +msgstr1] "baz" +''', + + # invalid plural index + ''' +msgid "foo" +msgid_plural "foos" +msgstr[[0]] "baz" +''', + ) + with temp_cwd(): + for invalid_po in invalid_po_files: + with self.subTest(invalid_po=invalid_po): + Path('messages.po').write_text(invalid_po) + # Reset the global MESSAGES dictionary + msgfmt.MESSAGES.clear() + with self.assertRaises((SystemExit, UnboundLocalError, + IndexError, SyntaxError)): + msgfmt.make('messages.po', 'messages.mo') + + def test_semantic_errors(self): + invalid_po_files = ( + # missing msgid after msgctxt + # invalid but currently accepted + # 'msgctxt "foo"', + + # missing msgstr after msgid + # invalid but currently accepted + # 'msgid "foo"', + + # comment line not allowed after msgctxt + # invalid but currently accepted +# ''' +# msgctxt "foo" +# # comment +# msgid "bar" +# msgstr "bar" +# ''', + + # comment line not allowed after msgid + # invalid but currently accepted +# ''' +# msgid "foo" +# # comment +# msgstr "bar" +# ''', + + # comment line not allowed after msgid_plural + # invalid but currently accepted +# ''' +# msgid "foo" +# msgid_plural "foos" +# # comment +# msgstr[0] "bar" +# ''', + + # msgctxt not allowed after msgctxt + # invalid but currently accepted +# ''' +# msgctxt "foo" + +# msgctxt "bar" +# msgid "foo" +# msgstr "bar" +# ''', + + # msgctxt not allowed after msgid + # invalid but currently accepted +# ''' +# msgid "foo" +# msgctxt "bar" + +# msgid "bar" +# msgstr "baz" +# ''', + + # msgctxt not allowed after msgid_plural + # invalid but currently accepted +# ''' +# msgid "foo" +# msgid_plural "foos" +# msgctxt "bar" + +# msgid "bar" +# msgstr "baz" +# ''', + + # msgid not allowed after msgid + # invalid but currently accepted +# ''' +# msgid "foo" + +# msgid "bar" +# msgstr "baz" +# ''', + + # msgid not allowed after msgid_plural + # invalid but currently accepted +# ''' +# msgid "foo" +# msgid_plural "foos" + +# msgid "bar" +# msgstr "baz" +# ''', + + # msgid_plural must be preceded by msgid + ''' +msgid_plural "foos" + +msgid "bar" +msgstr "baz" +''', + + # msgid_plural not allowed after comment + ''' +# comment +msgid_plural "foos" + +msgid "bar" +msgstr "baz" +''', + + # msgid_plural not allowed after msgid_plural + # invalid but currently accepted +# ''' +# msgid "foo" +# msgid_plural "foos" +# msgid_plural "bars" + +# msgid "bar" +# msgstr "baz" +# ''', + + # msgid_plural not allowed after msgctxt + ''' +msgctxt "foo" +msgid_plural "foos" + +msgid "bar" +msgstr "baz" +''', + + # msgid_plural not allowed after msgstr + ''' +msgid "foo" +msgstr "bar" +msgid_plural "foos" + +msgid "bar" +msgstr "baz" +''', + + # msgstr must be preceded by msgid + ''' +msgstr "foo" + +msgid "bar" +msgstr "baz" +''', + + # msgstr not allowed after comment + # invalid but currently accepted +# ''' +# # comment +# # msgstr "foo" + +# msgid "bar" +# msgstr "baz" +# ''', + + # msgstr not allowed after msgctxt + ''' +msgctxt "foo" +msgstr "bar" + +msgid "foo" +msgstr "bar" +''', + + # msgstr not allowed after msgstr + # invalid but currently accepted +# ''' +# msgid "foo" +# msgstr "bar" +# msgstr "baz" + +# msgid "bar" +# msgstr "baz" +# ''', + + # missing msgid_plural section + ''' +msgid "foo" +msgstr[0] "bar" + +msgid "bar" +msgstr "baz" +''' + ) + with temp_cwd(): + for invalid_po in invalid_po_files: + with self.subTest(invalid_po=invalid_po): + Path('messages.po').write_text(invalid_po) + # Reset the global MESSAGES dictionary + msgfmt.MESSAGES.clear() + with self.assertRaises((SystemExit, UnboundLocalError)): + msgfmt.make('messages.po', 'messages.mo') + + def test_msgstr_invalid_indices(self): + invalid_po_files = ( + # wrong plural form index + # invalid but currently accepted +# ''' +# msgid "foo" +# msgid_plural "foos" +# msgstr[42] "bar" +# ''', + + # wrong plural form index + # invalid but currently accepted +# ''' +# msgid "foo" +# msgid_plural "foos" +# msgstr[0] "bar" +# msgstr[42] "bars" +# ''', + + # msgstr not pluralized + ''' +msgid "foo" +msgid_plural "foos" +msgstr "bar" +''', + ) + with temp_cwd(): + for invalid_po in invalid_po_files: + with self.subTest(invalid_po=invalid_po): + Path('messages.po').write_text(invalid_po) + # Reset the global MESSAGES dictionary + msgfmt.MESSAGES.clear() + with self.assertRaises(SystemExit): + msgfmt.make('messages.po', 'messages.mo') + + def test_duplicate_entries(self): + invalid_po_files = ( + # duplicate msgid + # invalid but currently accepted +# ''' +# msgid "foo" +# msgstr "bar" + +# msgid "foo" +# msgstr "baz" +# ''', + + # duplicate msgctxt+msgid + # invalid but currently accepted +# ''' +# msgctxt "context" +# msgid "foo" +# msgstr "bar" + +# msgctxt "context" +# msgid "foo" +# msgstr "baz" +# ''' + ) + with temp_cwd(): + for invalid_po in invalid_po_files: + with self.subTest(invalid_po=invalid_po): + Path('messages.po').write_text(invalid_po) + # Reset the global MESSAGES dictionary + msgfmt.MESSAGES.clear() + with self.assertRaises(SystemExit): + msgfmt.make('messages.po', 'messages.mo') + class CLITest(unittest.TestCase): From 6ba1488e67469ced05d0db9c5bd1f1bc9a3515d3 Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Sun, 4 May 2025 20:32:09 +0200 Subject: [PATCH 2/7] Remove commented-out tests --- Lib/test/test_tools/test_msgfmt.py | 182 ----------------------------- 1 file changed, 182 deletions(-) diff --git a/Lib/test/test_tools/test_msgfmt.py b/Lib/test/test_tools/test_msgfmt.py index 64af0544bd1314..dd47ad5674c65f 100644 --- a/Lib/test/test_tools/test_msgfmt.py +++ b/Lib/test/test_tools/test_msgfmt.py @@ -292,14 +292,6 @@ def test_general_syntax_errors(self): msgstr "bar" ''', - # invalid plural index - # invalid but currently accepted -# ''' -# msgid "foo" -# msgid_plural "foos" -# msgstr[foo] "baz" -# ''', - # invalid plural index ''' msgid "foo" @@ -307,14 +299,6 @@ def test_general_syntax_errors(self): msgstr[0 "baz" ''', - # invalid plural index - # invalid but currently accepted -# ''' -# msgid "foo" -# msgid_plural "foos" -# msgstr[] "baz" -# ''', - # invalid plural index ''' msgid "foo" @@ -341,90 +325,6 @@ def test_general_syntax_errors(self): def test_semantic_errors(self): invalid_po_files = ( - # missing msgid after msgctxt - # invalid but currently accepted - # 'msgctxt "foo"', - - # missing msgstr after msgid - # invalid but currently accepted - # 'msgid "foo"', - - # comment line not allowed after msgctxt - # invalid but currently accepted -# ''' -# msgctxt "foo" -# # comment -# msgid "bar" -# msgstr "bar" -# ''', - - # comment line not allowed after msgid - # invalid but currently accepted -# ''' -# msgid "foo" -# # comment -# msgstr "bar" -# ''', - - # comment line not allowed after msgid_plural - # invalid but currently accepted -# ''' -# msgid "foo" -# msgid_plural "foos" -# # comment -# msgstr[0] "bar" -# ''', - - # msgctxt not allowed after msgctxt - # invalid but currently accepted -# ''' -# msgctxt "foo" - -# msgctxt "bar" -# msgid "foo" -# msgstr "bar" -# ''', - - # msgctxt not allowed after msgid - # invalid but currently accepted -# ''' -# msgid "foo" -# msgctxt "bar" - -# msgid "bar" -# msgstr "baz" -# ''', - - # msgctxt not allowed after msgid_plural - # invalid but currently accepted -# ''' -# msgid "foo" -# msgid_plural "foos" -# msgctxt "bar" - -# msgid "bar" -# msgstr "baz" -# ''', - - # msgid not allowed after msgid - # invalid but currently accepted -# ''' -# msgid "foo" - -# msgid "bar" -# msgstr "baz" -# ''', - - # msgid not allowed after msgid_plural - # invalid but currently accepted -# ''' -# msgid "foo" -# msgid_plural "foos" - -# msgid "bar" -# msgstr "baz" -# ''', - # msgid_plural must be preceded by msgid ''' msgid_plural "foos" @@ -442,17 +342,6 @@ def test_semantic_errors(self): msgstr "baz" ''', - # msgid_plural not allowed after msgid_plural - # invalid but currently accepted -# ''' -# msgid "foo" -# msgid_plural "foos" -# msgid_plural "bars" - -# msgid "bar" -# msgstr "baz" -# ''', - # msgid_plural not allowed after msgctxt ''' msgctxt "foo" @@ -480,16 +369,6 @@ def test_semantic_errors(self): msgstr "baz" ''', - # msgstr not allowed after comment - # invalid but currently accepted -# ''' -# # comment -# # msgstr "foo" - -# msgid "bar" -# msgstr "baz" -# ''', - # msgstr not allowed after msgctxt ''' msgctxt "foo" @@ -499,17 +378,6 @@ def test_semantic_errors(self): msgstr "bar" ''', - # msgstr not allowed after msgstr - # invalid but currently accepted -# ''' -# msgid "foo" -# msgstr "bar" -# msgstr "baz" - -# msgid "bar" -# msgstr "baz" -# ''', - # missing msgid_plural section ''' msgid "foo" @@ -530,23 +398,6 @@ def test_semantic_errors(self): def test_msgstr_invalid_indices(self): invalid_po_files = ( - # wrong plural form index - # invalid but currently accepted -# ''' -# msgid "foo" -# msgid_plural "foos" -# msgstr[42] "bar" -# ''', - - # wrong plural form index - # invalid but currently accepted -# ''' -# msgid "foo" -# msgid_plural "foos" -# msgstr[0] "bar" -# msgstr[42] "bars" -# ''', - # msgstr not pluralized ''' msgid "foo" @@ -563,39 +414,6 @@ def test_msgstr_invalid_indices(self): with self.assertRaises(SystemExit): msgfmt.make('messages.po', 'messages.mo') - def test_duplicate_entries(self): - invalid_po_files = ( - # duplicate msgid - # invalid but currently accepted -# ''' -# msgid "foo" -# msgstr "bar" - -# msgid "foo" -# msgstr "baz" -# ''', - - # duplicate msgctxt+msgid - # invalid but currently accepted -# ''' -# msgctxt "context" -# msgid "foo" -# msgstr "bar" - -# msgctxt "context" -# msgid "foo" -# msgstr "baz" -# ''' - ) - with temp_cwd(): - for invalid_po in invalid_po_files: - with self.subTest(invalid_po=invalid_po): - Path('messages.po').write_text(invalid_po) - # Reset the global MESSAGES dictionary - msgfmt.MESSAGES.clear() - with self.assertRaises(SystemExit): - msgfmt.make('messages.po', 'messages.mo') - class CLITest(unittest.TestCase): From 05a205f4d43f79822ff39cac1e26de3c39be5492 Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Sun, 4 May 2025 20:42:47 +0200 Subject: [PATCH 3/7] Use textwrap.dedent --- Lib/test/test_tools/test_msgfmt.py | 175 +++++++++++++++-------------- 1 file changed, 88 insertions(+), 87 deletions(-) diff --git a/Lib/test/test_tools/test_msgfmt.py b/Lib/test/test_tools/test_msgfmt.py index dd47ad5674c65f..e229f2f368720e 100644 --- a/Lib/test/test_tools/test_msgfmt.py +++ b/Lib/test/test_tools/test_msgfmt.py @@ -10,6 +10,7 @@ from test.support.os_helper import temp_cwd from test.support.script_helper import assert_python_failure, assert_python_ok from test.test_tools import imports_under_tool, skip_if_missing, toolsdir +from textwrap import dedent skip_if_missing('i18n') @@ -255,63 +256,63 @@ def test_general_syntax_errors(self): '[0]', # unclosed string - ''' -msgid " -msgstr "bar" -''', + dedent('''\ + msgid " + msgstr "bar" + '''), # unclosed string - ''' -msgid "foo -msgstr "bar" -''', + dedent('''\ + msgid "foo + msgstr "bar" + '''), # unclosed string - ''' -msgid "foo" " -msgstr "bar" -''', + dedent('''\ + msgid "foo" " + msgstr "bar" + '''), # unclosed string - ''' -msgid "foo" -" -msgstr "bar" -''', + dedent('''\ + msgid "foo" + " + msgstr "bar" + '''), # illegal backslash - ''' -msgid "foo\\" -" -msgstr "bar" -''', + dedent('''\ + msgid "foo\\" + " + msgstr "bar" + '''), # msgid with an index - ''' -msgid[0] "foo" -msgstr "bar" -''', + dedent('''\ + msgid[0] "foo" + msgstr "bar" + '''), # invalid plural index - ''' -msgid "foo" -msgid_plural "foos" -msgstr[0 "baz" -''', + dedent('''\ + msgid "foo" + msgid_plural "foos" + msgstr[0 "baz" + '''), # invalid plural index - ''' -msgid "foo" -msgid_plural "foos" -msgstr1] "baz" -''', + dedent('''\ + msgid "foo" + msgid_plural "foos" + msgstr1] "baz" + '''), # invalid plural index - ''' -msgid "foo" -msgid_plural "foos" -msgstr[[0]] "baz" -''', + dedent('''\ + msgid "foo" + msgid_plural "foos" + msgstr[[0]] "baz" + '''), ) with temp_cwd(): for invalid_po in invalid_po_files: @@ -326,66 +327,66 @@ def test_general_syntax_errors(self): def test_semantic_errors(self): invalid_po_files = ( # msgid_plural must be preceded by msgid - ''' -msgid_plural "foos" + dedent('''\ + msgid_plural "foos" -msgid "bar" -msgstr "baz" -''', + msgid "bar" + msgstr "baz" + '''), # msgid_plural not allowed after comment - ''' -# comment -msgid_plural "foos" + dedent('''\ + # comment + msgid_plural "foos" -msgid "bar" -msgstr "baz" -''', + msgid "bar" + msgstr "baz" + '''), # msgid_plural not allowed after msgctxt - ''' -msgctxt "foo" -msgid_plural "foos" + dedent('''\ + msgctxt "foo" + msgid_plural "foos" -msgid "bar" -msgstr "baz" -''', + msgid "bar" + msgstr "baz" + '''), # msgid_plural not allowed after msgstr - ''' -msgid "foo" -msgstr "bar" -msgid_plural "foos" + dedent('''\ + msgid "foo" + msgstr "bar" + msgid_plural "foos" -msgid "bar" -msgstr "baz" -''', + msgid "bar" + msgstr "baz" + '''), # msgstr must be preceded by msgid - ''' -msgstr "foo" + dedent('''\ + msgstr "foo" -msgid "bar" -msgstr "baz" -''', + msgid "bar" + msgstr "baz" + '''), # msgstr not allowed after msgctxt - ''' -msgctxt "foo" -msgstr "bar" + dedent('''\ + msgctxt "foo" + msgstr "bar" -msgid "foo" -msgstr "bar" -''', + msgid "foo" + msgstr "bar" + '''), # missing msgid_plural section - ''' -msgid "foo" -msgstr[0] "bar" + dedent('''\ + msgid "foo" + msgstr[0] "bar" -msgid "bar" -msgstr "baz" -''' + msgid "bar" + msgstr "baz" + '''), ) with temp_cwd(): for invalid_po in invalid_po_files: @@ -399,11 +400,11 @@ def test_semantic_errors(self): def test_msgstr_invalid_indices(self): invalid_po_files = ( # msgstr not pluralized - ''' -msgid "foo" -msgid_plural "foos" -msgstr "bar" -''', + dedent('''\ + msgid "foo" + msgid_plural "foos" + msgstr "bar" + '''), ) with temp_cwd(): for invalid_po in invalid_po_files: From 98917e53419c550ab4b9f35b8dc3c7435a12bdf2 Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Sun, 4 May 2025 20:48:08 +0200 Subject: [PATCH 4/7] Silence error output --- Lib/test/test_tools/test_msgfmt.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_tools/test_msgfmt.py b/Lib/test/test_tools/test_msgfmt.py index e229f2f368720e..5865fdcc8b1670 100644 --- a/Lib/test/test_tools/test_msgfmt.py +++ b/Lib/test/test_tools/test_msgfmt.py @@ -4,13 +4,15 @@ import struct import sys import unittest +from contextlib import redirect_stderr from gettext import GNUTranslations +from io import StringIO from pathlib import Path +from textwrap import dedent from test.support.os_helper import temp_cwd from test.support.script_helper import assert_python_failure, assert_python_ok from test.test_tools import imports_under_tool, skip_if_missing, toolsdir -from textwrap import dedent skip_if_missing('i18n') @@ -320,9 +322,10 @@ def test_general_syntax_errors(self): Path('messages.po').write_text(invalid_po) # Reset the global MESSAGES dictionary msgfmt.MESSAGES.clear() - with self.assertRaises((SystemExit, UnboundLocalError, - IndexError, SyntaxError)): - msgfmt.make('messages.po', 'messages.mo') + with redirect_stderr(StringIO()) as output: + with self.assertRaises((SystemExit, UnboundLocalError, + IndexError, SyntaxError)): + msgfmt.make('messages.po', 'messages.mo') def test_semantic_errors(self): invalid_po_files = ( @@ -394,8 +397,9 @@ def test_semantic_errors(self): Path('messages.po').write_text(invalid_po) # Reset the global MESSAGES dictionary msgfmt.MESSAGES.clear() - with self.assertRaises((SystemExit, UnboundLocalError)): - msgfmt.make('messages.po', 'messages.mo') + with redirect_stderr(StringIO()) as output: + with self.assertRaises((SystemExit, UnboundLocalError)): + msgfmt.make('messages.po', 'messages.mo') def test_msgstr_invalid_indices(self): invalid_po_files = ( @@ -412,8 +416,9 @@ def test_msgstr_invalid_indices(self): Path('messages.po').write_text(invalid_po) # Reset the global MESSAGES dictionary msgfmt.MESSAGES.clear() - with self.assertRaises(SystemExit): - msgfmt.make('messages.po', 'messages.mo') + with redirect_stderr(StringIO()) as output: + with self.assertRaises(SystemExit): + msgfmt.make('messages.po', 'messages.mo') class CLITest(unittest.TestCase): From 2955e906503654be640b669ac8908b41104222fe Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Sun, 4 May 2025 21:11:56 +0200 Subject: [PATCH 5/7] Check error messages --- Lib/test/test_tools/test_msgfmt.py | 262 ++++++++++++++++++----------- 1 file changed, 161 insertions(+), 101 deletions(-) diff --git a/Lib/test/test_tools/test_msgfmt.py b/Lib/test/test_tools/test_msgfmt.py index 5865fdcc8b1670..8840901b25afe9 100644 --- a/Lib/test/test_tools/test_msgfmt.py +++ b/Lib/test/test_tools/test_msgfmt.py @@ -1,6 +1,7 @@ """Tests for the Tools/i18n/msgfmt.py tool.""" import json +import os import struct import sys import unittest @@ -244,80 +245,108 @@ def test_strings(self): msgfmt.make('messages.po', 'messages.mo') def test_general_syntax_errors(self): + # 2-tuples of input PO files and expected error messages invalid_po_files = ( - '', - '"', - '""', - '"foo"', + ('', None), + ('"', None), + ('""', 'Syntax error on messages.po:1 before:'), + ('"foo"', f'Syntax error on messages.po:1 before:{os.linesep}foo'), # 'msgid', # invalid but currently accepted - 'msgstr', - 'msgid_plural', + ('msgstr', None), + ('msgid_plural', 'msgid_plural not preceded by msgid on messages.po:1'), # 'msgctxt', # invalid but currently accepted - 'msgstr', - 'msgstr[0]', - '[0]', + ('msgstr', None), + ('msgstr[0]', None), + ('[0]', f'Syntax error on messages.po:1 before:{os.linesep}[0]'), # unclosed string - dedent('''\ - msgid " - msgstr "bar" - '''), + ( + dedent('''\ + msgid " + msgstr "bar" + '''), + None + ), # unclosed string - dedent('''\ - msgid "foo - msgstr "bar" - '''), + ( + dedent('''\ + msgid "foo + msgstr "bar" + '''), + None + ), # unclosed string - dedent('''\ - msgid "foo" " - msgstr "bar" - '''), + ( + dedent('''\ + msgid "foo" " + msgstr "bar" + '''), + None + ), # unclosed string - dedent('''\ - msgid "foo" - " - msgstr "bar" - '''), + ( + dedent('''\ + msgid "foo" + " + msgstr "bar" + '''), + None + ), # illegal backslash - dedent('''\ - msgid "foo\\" - " - msgstr "bar" - '''), + ( + dedent('''\ + msgid "foo\\" + " + msgstr "bar" + '''), + None + ), # msgid with an index - dedent('''\ - msgid[0] "foo" - msgstr "bar" - '''), + ( + dedent('''\ + msgid[0] "foo" + msgstr "bar" + '''), + None + ), # invalid plural index - dedent('''\ - msgid "foo" - msgid_plural "foos" - msgstr[0 "baz" - '''), + ( + dedent('''\ + msgid "foo" + msgid_plural "foos" + msgstr[0 "baz" + '''), + None + ), # invalid plural index - dedent('''\ - msgid "foo" - msgid_plural "foos" - msgstr1] "baz" - '''), + ( + dedent('''\ + msgid "foo" + msgid_plural "foos" + msgstr1] "baz" + '''), + 'indexed msgstr required for plural on messages.po:3' + ), # invalid plural index - dedent('''\ - msgid "foo" - msgid_plural "foos" - msgstr[[0]] "baz" - '''), + ( + dedent('''\ + msgid "foo" + msgid_plural "foos" + msgstr[[0]] "baz" + '''), + None + ) ) with temp_cwd(): - for invalid_po in invalid_po_files: + for invalid_po, err_msg in invalid_po_files: with self.subTest(invalid_po=invalid_po): Path('messages.po').write_text(invalid_po) # Reset the global MESSAGES dictionary @@ -326,73 +355,97 @@ def test_general_syntax_errors(self): with self.assertRaises((SystemExit, UnboundLocalError, IndexError, SyntaxError)): msgfmt.make('messages.po', 'messages.mo') + if err_msg: + self.assertEqual(output.getvalue().strip(), err_msg) def test_semantic_errors(self): + # 2-tuples of input PO files and expected error messages invalid_po_files = ( # msgid_plural must be preceded by msgid - dedent('''\ - msgid_plural "foos" + ( + dedent('''\ + msgid_plural "foos" - msgid "bar" - msgstr "baz" - '''), + msgid "bar" + msgstr "baz" + '''), + 'msgid_plural not preceded by msgid on messages.po:1' + ), # msgid_plural not allowed after comment - dedent('''\ - # comment - msgid_plural "foos" + ( + dedent('''\ + # comment + msgid_plural "foos" - msgid "bar" - msgstr "baz" - '''), + msgid "bar" + msgstr "baz" + '''), + 'msgid_plural not preceded by msgid on messages.po:2' + ), # msgid_plural not allowed after msgctxt - dedent('''\ - msgctxt "foo" - msgid_plural "foos" + ( + dedent('''\ + msgctxt "foo" + msgid_plural "foos" - msgid "bar" - msgstr "baz" - '''), + msgid "bar" + msgstr "baz" + '''), + 'msgid_plural not preceded by msgid on messages.po:2' + ), # msgid_plural not allowed after msgstr - dedent('''\ - msgid "foo" - msgstr "bar" - msgid_plural "foos" - - msgid "bar" - msgstr "baz" - '''), + ( + dedent('''\ + msgid "foo" + msgstr "bar" + msgid_plural "foos" + + msgid "bar" + msgstr "baz" + '''), + 'msgid_plural not preceded by msgid on messages.po:3' + ), # msgstr must be preceded by msgid - dedent('''\ - msgstr "foo" + ( + dedent('''\ + msgstr "foo" - msgid "bar" - msgstr "baz" - '''), + msgid "bar" + msgstr "baz" + '''), + None + ), # msgstr not allowed after msgctxt - dedent('''\ - msgctxt "foo" - msgstr "bar" + ( + dedent('''\ + msgctxt "foo" + msgstr "bar" - msgid "foo" - msgstr "bar" - '''), + msgid "foo" + msgstr "bar" + '''), + None + ), # missing msgid_plural section - dedent('''\ - msgid "foo" - msgstr[0] "bar" - - msgid "bar" - msgstr "baz" - '''), + ( + dedent('''\ + msgid "foo" + msgstr[0] "bar" + + msgid "bar" + msgstr "baz" + '''), + 'plural without msgid_plural on messages.po:2' + ), ) with temp_cwd(): - for invalid_po in invalid_po_files: + for invalid_po, err_msg in invalid_po_files: with self.subTest(invalid_po=invalid_po): Path('messages.po').write_text(invalid_po) # Reset the global MESSAGES dictionary @@ -400,18 +453,24 @@ def test_semantic_errors(self): with redirect_stderr(StringIO()) as output: with self.assertRaises((SystemExit, UnboundLocalError)): msgfmt.make('messages.po', 'messages.mo') + if err_msg: + self.assertEqual(output.getvalue().strip(), err_msg) def test_msgstr_invalid_indices(self): + # 2-tuples of input PO files and expected error messages invalid_po_files = ( # msgstr not pluralized - dedent('''\ - msgid "foo" - msgid_plural "foos" - msgstr "bar" - '''), + ( + dedent('''\ + msgid "foo" + msgid_plural "foos" + msgstr "bar" + '''), + 'indexed msgstr required for plural on messages.po:3' + ), ) with temp_cwd(): - for invalid_po in invalid_po_files: + for invalid_po, err_msg in invalid_po_files: with self.subTest(invalid_po=invalid_po): Path('messages.po').write_text(invalid_po) # Reset the global MESSAGES dictionary @@ -419,6 +478,7 @@ def test_msgstr_invalid_indices(self): with redirect_stderr(StringIO()) as output: with self.assertRaises(SystemExit): msgfmt.make('messages.po', 'messages.mo') + self.assertEqual(output.getvalue().strip(), err_msg) class CLITest(unittest.TestCase): From 389827a6f10c86f461e3c229955315d9550e4ea8 Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Sun, 4 May 2025 21:24:46 +0200 Subject: [PATCH 6/7] Get rid of extra newlines --- Lib/test/test_tools/test_msgfmt.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/Lib/test/test_tools/test_msgfmt.py b/Lib/test/test_tools/test_msgfmt.py index 8840901b25afe9..fd1f8b0b8ee6af 100644 --- a/Lib/test/test_tools/test_msgfmt.py +++ b/Lib/test/test_tools/test_msgfmt.py @@ -258,7 +258,6 @@ def test_general_syntax_errors(self): ('msgstr', None), ('msgstr[0]', None), ('[0]', f'Syntax error on messages.po:1 before:{os.linesep}[0]'), - # unclosed string ( dedent('''\ @@ -267,7 +266,6 @@ def test_general_syntax_errors(self): '''), None ), - # unclosed string ( dedent('''\ @@ -276,7 +274,6 @@ def test_general_syntax_errors(self): '''), None ), - # unclosed string ( dedent('''\ @@ -285,7 +282,6 @@ def test_general_syntax_errors(self): '''), None ), - # unclosed string ( dedent('''\ @@ -295,7 +291,6 @@ def test_general_syntax_errors(self): '''), None ), - # illegal backslash ( dedent('''\ @@ -305,7 +300,6 @@ def test_general_syntax_errors(self): '''), None ), - # msgid with an index ( dedent('''\ @@ -314,7 +308,6 @@ def test_general_syntax_errors(self): '''), None ), - # invalid plural index ( dedent('''\ @@ -324,7 +317,6 @@ def test_general_syntax_errors(self): '''), None ), - # invalid plural index ( dedent('''\ @@ -334,7 +326,6 @@ def test_general_syntax_errors(self): '''), 'indexed msgstr required for plural on messages.po:3' ), - # invalid plural index ( dedent('''\ @@ -371,7 +362,6 @@ def test_semantic_errors(self): '''), 'msgid_plural not preceded by msgid on messages.po:1' ), - # msgid_plural not allowed after comment ( dedent('''\ @@ -383,7 +373,6 @@ def test_semantic_errors(self): '''), 'msgid_plural not preceded by msgid on messages.po:2' ), - # msgid_plural not allowed after msgctxt ( dedent('''\ @@ -395,7 +384,6 @@ def test_semantic_errors(self): '''), 'msgid_plural not preceded by msgid on messages.po:2' ), - # msgid_plural not allowed after msgstr ( dedent('''\ @@ -408,7 +396,6 @@ def test_semantic_errors(self): '''), 'msgid_plural not preceded by msgid on messages.po:3' ), - # msgstr must be preceded by msgid ( dedent('''\ @@ -419,7 +406,6 @@ def test_semantic_errors(self): '''), None ), - # msgstr not allowed after msgctxt ( dedent('''\ @@ -431,7 +417,6 @@ def test_semantic_errors(self): '''), None ), - # missing msgid_plural section ( dedent('''\ From d6bdd7c0897b98f0988cc1fdf215504e1c668859 Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Sun, 4 May 2025 21:35:37 +0200 Subject: [PATCH 7/7] Windows fix --- Lib/test/test_tools/test_msgfmt.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_tools/test_msgfmt.py b/Lib/test/test_tools/test_msgfmt.py index fd1f8b0b8ee6af..4dbcced4fdf691 100644 --- a/Lib/test/test_tools/test_msgfmt.py +++ b/Lib/test/test_tools/test_msgfmt.py @@ -1,7 +1,6 @@ """Tests for the Tools/i18n/msgfmt.py tool.""" import json -import os import struct import sys import unittest @@ -250,14 +249,14 @@ def test_general_syntax_errors(self): ('', None), ('"', None), ('""', 'Syntax error on messages.po:1 before:'), - ('"foo"', f'Syntax error on messages.po:1 before:{os.linesep}foo'), + ('"foo"', f'Syntax error on messages.po:1 before:\nfoo'), # 'msgid', # invalid but currently accepted ('msgstr', None), ('msgid_plural', 'msgid_plural not preceded by msgid on messages.po:1'), # 'msgctxt', # invalid but currently accepted ('msgstr', None), ('msgstr[0]', None), - ('[0]', f'Syntax error on messages.po:1 before:{os.linesep}[0]'), + ('[0]', f'Syntax error on messages.po:1 before:\n[0]'), # unclosed string ( dedent('''\ pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy