summaryrefslogtreecommitdiff
path: root/bzrlib/tests/test_export_pot.py
diff options
context:
space:
mode:
Diffstat (limited to 'bzrlib/tests/test_export_pot.py')
-rw-r--r--bzrlib/tests/test_export_pot.py467
1 files changed, 467 insertions, 0 deletions
diff --git a/bzrlib/tests/test_export_pot.py b/bzrlib/tests/test_export_pot.py
new file mode 100644
index 0000000..58e34b7
--- /dev/null
+++ b/bzrlib/tests/test_export_pot.py
@@ -0,0 +1,467 @@
+# Copyright (C) 2011 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+from cStringIO import StringIO
+import textwrap
+
+from bzrlib import (
+ commands,
+ export_pot,
+ option,
+ registry,
+ tests,
+ )
+
+import re
+
+
+class TestEscape(tests.TestCase):
+
+ def test_simple_escape(self):
+ self.assertEqual(
+ export_pot._escape('foobar'),
+ 'foobar')
+
+ s = '''foo\nbar\r\tbaz\\"spam"'''
+ e = '''foo\\nbar\\r\\tbaz\\\\\\"spam\\"'''
+ self.assertEqual(export_pot._escape(s), e)
+
+ def test_complex_escape(self):
+ s = '''\\r \\\n'''
+ e = '''\\\\r \\\\\\n'''
+ self.assertEqual(export_pot._escape(s), e)
+
+
+class TestNormalize(tests.TestCase):
+
+ def test_single_line(self):
+ s = 'foobar'
+ e = '"foobar"'
+ self.assertEqual(export_pot._normalize(s), e)
+
+ s = 'foo"bar'
+ e = '"foo\\"bar"'
+ self.assertEqual(export_pot._normalize(s), e)
+
+ def test_multi_lines(self):
+ s = 'foo\nbar\n'
+ e = '""\n"foo\\n"\n"bar\\n"'
+ self.assertEqual(export_pot._normalize(s), e)
+
+ s = '\nfoo\nbar\n'
+ e = ('""\n'
+ '"\\n"\n'
+ '"foo\\n"\n'
+ '"bar\\n"')
+ self.assertEqual(export_pot._normalize(s), e)
+
+
+class TestParseSource(tests.TestCase):
+ """Check mappings to line numbers generated from python source"""
+
+ def test_classes(self):
+ src = '''
+class Ancient:
+ """Old style class"""
+
+class Modern(object):
+ """New style class"""
+'''
+ cls_lines, _ = export_pot._parse_source(src)
+ self.assertEqual(cls_lines,
+ {"Ancient": 2, "Modern": 5})
+
+ def test_classes_nested(self):
+ src = '''
+class Matroska(object):
+ class Smaller(object):
+ class Smallest(object):
+ pass
+'''
+ cls_lines, _ = export_pot._parse_source(src)
+ self.assertEqual(cls_lines,
+ {"Matroska": 2, "Smaller": 3, "Smallest":4})
+
+ def test_strings_docstrings(self):
+ src = '''\
+"""Module"""
+
+def function():
+ """Function"""
+
+class Class(object):
+ """Class"""
+
+ def method(self):
+ """Method"""
+'''
+ _, str_lines = export_pot._parse_source(src)
+ self.assertEqual(str_lines,
+ {"Module": 1, "Function": 4, "Class": 7, "Method": 10})
+
+ def test_strings_literals(self):
+ src = '''\
+s = "One"
+t = (2, "Two")
+f = dict(key="Three")
+'''
+ _, str_lines = export_pot._parse_source(src)
+ self.assertEqual(str_lines,
+ {"One": 1, "Two": 2, "Three": 3})
+
+ def test_strings_multiline(self):
+ src = '''\
+"""Start
+
+End
+"""
+t = (
+ "A"
+ "B"
+ "C"
+ )
+'''
+ _, str_lines = export_pot._parse_source(src)
+ self.assertEqual(str_lines,
+ {"Start\n\nEnd\n": 1, "ABC": 6})
+
+ def test_strings_multiline_escapes(self):
+ src = '''\
+s = "Escaped\\n"
+r = r"Raw\\n"
+t = (
+ "A\\n\\n"
+ "B\\n\\n"
+ "C\\n\\n"
+ )
+'''
+ _, str_lines = export_pot._parse_source(src)
+ self.expectFailure("Escaped newlines confuses the multiline handling",
+ self.assertNotEqual, str_lines,
+ {"Escaped\n": 0, "Raw\\n": 2, "A\n\nB\n\nC\n\n": -2})
+ self.assertEqual(str_lines,
+ {"Escaped\n": 1, "Raw\\n": 2, "A\n\nB\n\nC\n\n": 4})
+
+
+class TestModuleContext(tests.TestCase):
+ """Checks for source context tracking objects"""
+
+ def check_context(self, context, path, lineno):
+ self.assertEquals((context.path, context.lineno), (path, lineno))
+
+ def test___init__(self):
+ context = export_pot._ModuleContext("one.py")
+ self.check_context(context, "one.py", 1)
+ context = export_pot._ModuleContext("two.py", 5)
+ self.check_context(context, "two.py", 5)
+
+ def test_from_class(self):
+ """New context returned with lineno updated from class"""
+ path = "cls.py"
+ class A(object): pass
+ class B(object): pass
+ cls_lines = {"A": 5, "B": 7}
+ context = export_pot._ModuleContext(path, _source_info=(cls_lines, {}))
+ contextA = context.from_class(A)
+ self.check_context(contextA, path, 5)
+ contextB1 = context.from_class(B)
+ self.check_context(contextB1, path, 7)
+ contextB2 = contextA.from_class(B)
+ self.check_context(contextB2, path, 7)
+ self.check_context(context, path, 1)
+ self.assertEquals("", self.get_log())
+
+ def test_from_class_missing(self):
+ """When class has no lineno the old context details are returned"""
+ path = "cls_missing.py"
+ class A(object): pass
+ class M(object): pass
+ context = export_pot._ModuleContext(path, 3, ({"A": 15}, {}))
+ contextA = context.from_class(A)
+ contextM1 = context.from_class(M)
+ self.check_context(contextM1, path, 3)
+ contextM2 = contextA.from_class(M)
+ self.check_context(contextM2, path, 15)
+ self.assertContainsRe(self.get_log(), "Definition of <.*M'> not found")
+
+ def test_from_string(self):
+ """New context returned with lineno updated from string"""
+ path = "str.py"
+ str_lines = {"one": 14, "two": 42}
+ context = export_pot._ModuleContext(path, _source_info=({}, str_lines))
+ context1 = context.from_string("one")
+ self.check_context(context1, path, 14)
+ context2A = context.from_string("two")
+ self.check_context(context2A, path, 42)
+ context2B = context1.from_string("two")
+ self.check_context(context2B, path, 42)
+ self.check_context(context, path, 1)
+ self.assertEquals("", self.get_log())
+
+ def test_from_string_missing(self):
+ """When string has no lineno the old context details are returned"""
+ path = "str_missing.py"
+ context = export_pot._ModuleContext(path, 4, ({}, {"line\n": 21}))
+ context1 = context.from_string("line\n")
+ context2A = context.from_string("not there")
+ self.check_context(context2A, path, 4)
+ context2B = context1.from_string("not there")
+ self.check_context(context2B, path, 21)
+ self.assertContainsRe(self.get_log(), "String 'not there' not found")
+
+
+class TestWriteOption(tests.TestCase):
+ """Tests for writing texts extracted from options in pot format"""
+
+ def pot_from_option(self, opt, context=None, note="test"):
+ sio = StringIO()
+ exporter = export_pot._PotExporter(sio)
+ if context is None:
+ context = export_pot._ModuleContext("nowhere", 0)
+ export_pot._write_option(exporter, context, opt, note)
+ return sio.getvalue()
+
+ def test_option_without_help(self):
+ opt = option.Option("helpless")
+ self.assertEqual("", self.pot_from_option(opt))
+
+ def test_option_with_help(self):
+ opt = option.Option("helpful", help="Info.")
+ self.assertContainsString(self.pot_from_option(opt), "\n"
+ "# help of 'helpful' test\n"
+ "msgid \"Info.\"\n")
+
+ def test_option_hidden(self):
+ opt = option.Option("hidden", help="Unseen.", hidden=True)
+ self.assertEqual("", self.pot_from_option(opt))
+
+ def test_option_context_missing(self):
+ context = export_pot._ModuleContext("remote.py", 3)
+ opt = option.Option("metaphor", help="Not a literal in the source.")
+ self.assertContainsString(self.pot_from_option(opt, context),
+ "#: remote.py:3\n"
+ "# help of 'metaphor' test\n")
+
+ def test_option_context_string(self):
+ s = "Literally."
+ context = export_pot._ModuleContext("local.py", 3, ({}, {s: 17}))
+ opt = option.Option("example", help=s)
+ self.assertContainsString(self.pot_from_option(opt, context),
+ "#: local.py:17\n"
+ "# help of 'example' test\n")
+
+ def test_registry_option_title(self):
+ opt = option.RegistryOption.from_kwargs("group", help="Pick one.",
+ title="Choose!")
+ pot = self.pot_from_option(opt)
+ self.assertContainsString(pot, "\n"
+ "# title of 'group' test\n"
+ "msgid \"Choose!\"\n")
+ self.assertContainsString(pot, "\n"
+ "# help of 'group' test\n"
+ "msgid \"Pick one.\"\n")
+
+ def test_registry_option_title_context_missing(self):
+ context = export_pot._ModuleContext("theory.py", 3)
+ opt = option.RegistryOption.from_kwargs("abstract", title="Unfounded!")
+ self.assertContainsString(self.pot_from_option(opt, context),
+ "#: theory.py:3\n"
+ "# title of 'abstract' test\n")
+
+ def test_registry_option_title_context_string(self):
+ s = "Grounded!"
+ context = export_pot._ModuleContext("practice.py", 3, ({}, {s: 144}))
+ opt = option.RegistryOption.from_kwargs("concrete", title=s)
+ self.assertContainsString(self.pot_from_option(opt, context),
+ "#: practice.py:144\n"
+ "# title of 'concrete' test\n")
+
+ def test_registry_option_value_switches(self):
+ opt = option.RegistryOption.from_kwargs("switch", help="Flip one.",
+ value_switches=True, enum_switch=False,
+ red="Big.", green="Small.")
+ pot = self.pot_from_option(opt)
+ self.assertContainsString(pot, "\n"
+ "# help of 'switch' test\n"
+ "msgid \"Flip one.\"\n")
+ self.assertContainsString(pot, "\n"
+ "# help of 'switch=red' test\n"
+ "msgid \"Big.\"\n")
+ self.assertContainsString(pot, "\n"
+ "# help of 'switch=green' test\n"
+ "msgid \"Small.\"\n")
+
+ def test_registry_option_value_switches_hidden(self):
+ reg = registry.Registry()
+ class Hider(object):
+ hidden = True
+ reg.register("new", 1, "Current.")
+ reg.register("old", 0, "Legacy.", info=Hider())
+ opt = option.RegistryOption("protocol", "Talking.", reg,
+ value_switches=True, enum_switch=False)
+ pot = self.pot_from_option(opt)
+ self.assertContainsString(pot, "\n"
+ "# help of 'protocol' test\n"
+ "msgid \"Talking.\"\n")
+ self.assertContainsString(pot, "\n"
+ "# help of 'protocol=new' test\n"
+ "msgid \"Current.\"\n")
+ self.assertNotContainsString(pot, "'protocol=old'")
+
+
+class TestPotExporter(tests.TestCase):
+ """Test for logic specific to the _PotExporter class"""
+
+ # This test duplicates test_duplicates below
+ def test_duplicates(self):
+ exporter = export_pot._PotExporter(StringIO())
+ context = export_pot._ModuleContext("mod.py", 1)
+ exporter.poentry_in_context(context, "Common line.")
+ context.lineno = 3
+ exporter.poentry_in_context(context, "Common line.")
+ self.assertEqual(1, exporter.outf.getvalue().count("Common line."))
+
+ def test_duplicates_included(self):
+ exporter = export_pot._PotExporter(StringIO(), True)
+ context = export_pot._ModuleContext("mod.py", 1)
+ exporter.poentry_in_context(context, "Common line.")
+ context.lineno = 3
+ exporter.poentry_in_context(context, "Common line.")
+ self.assertEqual(2, exporter.outf.getvalue().count("Common line."))
+
+
+class PoEntryTestCase(tests.TestCase):
+
+ def setUp(self):
+ super(PoEntryTestCase, self).setUp()
+ self.exporter = export_pot._PotExporter(StringIO())
+
+ def check_output(self, expected):
+ self.assertEqual(
+ self.exporter.outf.getvalue(),
+ textwrap.dedent(expected)
+ )
+
+
+class TestPoEntry(PoEntryTestCase):
+
+ def test_simple(self):
+ self.exporter.poentry('dummy', 1, "spam")
+ self.exporter.poentry('dummy', 2, "ham", 'EGG')
+ self.check_output('''\
+ #: dummy:1
+ msgid "spam"
+ msgstr ""
+
+ #: dummy:2
+ # EGG
+ msgid "ham"
+ msgstr ""
+
+ ''')
+
+ def test_duplicate(self):
+ self.exporter.poentry('dummy', 1, "spam")
+ # This should be ignored.
+ self.exporter.poentry('dummy', 2, "spam", 'EGG')
+
+ self.check_output('''\
+ #: dummy:1
+ msgid "spam"
+ msgstr ""\n
+ ''')
+
+
+class TestPoentryPerPergraph(PoEntryTestCase):
+
+ def test_single(self):
+ self.exporter.poentry_per_paragraph(
+ 'dummy',
+ 10,
+ '''foo\nbar\nbaz\n'''
+ )
+ self.check_output('''\
+ #: dummy:10
+ msgid ""
+ "foo\\n"
+ "bar\\n"
+ "baz\\n"
+ msgstr ""\n
+ ''')
+
+ def test_multi(self):
+ self.exporter.poentry_per_paragraph(
+ 'dummy',
+ 10,
+ '''spam\nham\negg\n\nSPAM\nHAM\nEGG\n'''
+ )
+ self.check_output('''\
+ #: dummy:10
+ msgid ""
+ "spam\\n"
+ "ham\\n"
+ "egg"
+ msgstr ""
+
+ #: dummy:14
+ msgid ""
+ "SPAM\\n"
+ "HAM\\n"
+ "EGG\\n"
+ msgstr ""\n
+ ''')
+
+
+class TestExportCommandHelp(PoEntryTestCase):
+
+ def test_command_help(self):
+
+ class cmd_Demo(commands.Command):
+ __doc__ = """A sample command.
+
+ :Usage:
+ bzr demo
+
+ :Examples:
+ Example 1::
+
+ cmd arg1
+
+ Blah Blah Blah
+ """
+
+ export_pot._write_command_help(self.exporter, cmd_Demo())
+ result = self.exporter.outf.getvalue()
+ # We don't care about filename and lineno here.
+ result = re.sub(r'(?m)^#: [^\n]+\n', '', result)
+
+ self.assertEqualDiff(
+ 'msgid "A sample command."\n'
+ 'msgstr ""\n'
+ '\n' # :Usage: should not be translated.
+ 'msgid ""\n'
+ '":Examples:\\n"\n'
+ '" Example 1::"\n'
+ 'msgstr ""\n'
+ '\n'
+ 'msgid " cmd arg1"\n'
+ 'msgstr ""\n'
+ '\n'
+ 'msgid "Blah Blah Blah"\n'
+ 'msgstr ""\n'
+ '\n',
+ result
+ )