--- /dev/null
+"""Tests for the Tools/i18n/msgfmt.py tool."""
+
+import sys
+import unittest
+from gettext import GNUTranslations
+from pathlib import Path
+
+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 skip_if_missing, toolsdir
+
+
+skip_if_missing('i18n')
+
+data_dir = (Path(__file__).parent / 'msgfmt_data').resolve()
+script_dir = Path(toolsdir) / 'i18n'
+msgfmt = script_dir / 'msgfmt.py'
+
+
+def compile_messages(po_file, mo_file):
+ assert_python_ok(msgfmt, '-o', mo_file, po_file)
+
+
+class CompilationTest(unittest.TestCase):
+
+ def test_compilation(self):
+ self.maxDiff = None
+ with temp_cwd():
+ for po_file in data_dir.glob('*.po'):
+ with self.subTest(po_file=po_file):
+ mo_file = po_file.with_suffix('.mo')
+ with open(mo_file, 'rb') as f:
+ expected = GNUTranslations(f)
+
+ tmp_mo_file = mo_file.name
+ compile_messages(po_file, tmp_mo_file)
+ with open(tmp_mo_file, 'rb') as f:
+ actual = GNUTranslations(f)
+
+ self.assertDictEqual(actual._catalog, expected._catalog)
+
+ def test_invalid_msgid_plural(self):
+ with temp_cwd():
+ Path('invalid.po').write_text('''\
+msgid_plural "plural"
+msgstr[0] "singular"
+''')
+
+ res = assert_python_failure(msgfmt, 'invalid.po')
+ err = res.err.decode('utf-8')
+ self.assertIn('msgid_plural not preceded by msgid', err)
+
+ def test_plural_without_msgid_plural(self):
+ with temp_cwd():
+ Path('invalid.po').write_text('''\
+msgid "foo"
+msgstr[0] "bar"
+''')
+
+ res = assert_python_failure(msgfmt, 'invalid.po')
+ err = res.err.decode('utf-8')
+ self.assertIn('plural without msgid_plural', err)
+
+ def test_indexed_msgstr_without_msgid_plural(self):
+ with temp_cwd():
+ Path('invalid.po').write_text('''\
+msgid "foo"
+msgid_plural "foos"
+msgstr "bar"
+''')
+
+ res = assert_python_failure(msgfmt, 'invalid.po')
+ err = res.err.decode('utf-8')
+ self.assertIn('indexed msgstr required for plural', err)
+
+ def test_generic_syntax_error(self):
+ with temp_cwd():
+ Path('invalid.po').write_text('''\
+"foo"
+''')
+
+ res = assert_python_failure(msgfmt, 'invalid.po')
+ err = res.err.decode('utf-8')
+ self.assertIn('Syntax error', err)
+
+class CLITest(unittest.TestCase):
+
+ def test_help(self):
+ for option in ('--help', '-h'):
+ res = assert_python_ok(msgfmt, option)
+ err = res.err.decode('utf-8')
+ self.assertIn('Generate binary message catalog from textual translation description.', err)
+
+ def test_version(self):
+ for option in ('--version', '-V'):
+ res = assert_python_ok(msgfmt, option)
+ out = res.out.decode('utf-8').strip()
+ self.assertEqual('msgfmt.py 1.2', out)
+
+ def test_invalid_option(self):
+ res = assert_python_failure(msgfmt, '--invalid-option')
+ err = res.err.decode('utf-8')
+ self.assertIn('Generate binary message catalog from textual translation description.', err)
+ self.assertIn('option --invalid-option not recognized', err)
+
+ def test_no_input_file(self):
+ res = assert_python_ok(msgfmt)
+ err = res.err.decode('utf-8').replace('\r\n', '\n')
+ self.assertIn('No input file given\n'
+ "Try `msgfmt --help' for more information.", err)
+
+ def test_nonexistent_file(self):
+ assert_python_failure(msgfmt, 'nonexistent.po')
+
+
+def update_catalog_snapshots():
+ for po_file in data_dir.glob('*.po'):
+ mo_file = po_file.with_suffix('.mo')
+ compile_messages(po_file, mo_file)
+
+
+if __name__ == '__main__':
+ if len(sys.argv) > 1 and sys.argv[1] == '--snapshot-update':
+ update_catalog_snapshots()
+ sys.exit(0)
+ unittest.main()