~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_export_pot.py

  • Committer: Vincent Ladeuil
  • Date: 2017-01-17 13:48:10 UTC
  • mfrom: (6615.3.6 merges)
  • mto: This revision was merged to the branch mainline in revision 6620.
  • Revision ID: v.ladeuil+lp@free.fr-20170117134810-j9p3lidfy6pfyfsc
Merge 2.7, resolving conflicts

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2011, 2016 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
from cStringIO import StringIO
 
18
import textwrap
 
19
 
 
20
from bzrlib import (
 
21
    commands,
 
22
    export_pot,
 
23
    option,
 
24
    registry,
 
25
    tests,
 
26
    )
 
27
 
 
28
import re
 
29
 
 
30
 
 
31
class TestEscape(tests.TestCase):
 
32
 
 
33
    def test_simple_escape(self):
 
34
        self.assertEqual(
 
35
                export_pot._escape('foobar'),
 
36
                'foobar')
 
37
 
 
38
        s = '''foo\nbar\r\tbaz\\"spam"'''
 
39
        e = '''foo\\nbar\\r\\tbaz\\\\\\"spam\\"'''
 
40
        self.assertEqual(export_pot._escape(s), e)
 
41
 
 
42
    def test_complex_escape(self):
 
43
        s = '''\\r \\\n'''
 
44
        e = '''\\\\r \\\\\\n'''
 
45
        self.assertEqual(export_pot._escape(s), e)
 
46
 
 
47
 
 
48
class TestNormalize(tests.TestCase):
 
49
 
 
50
    def test_single_line(self):
 
51
        s = 'foobar'
 
52
        e = '"foobar"'
 
53
        self.assertEqual(export_pot._normalize(s), e)
 
54
 
 
55
        s = 'foo"bar'
 
56
        e = '"foo\\"bar"'
 
57
        self.assertEqual(export_pot._normalize(s), e)
 
58
 
 
59
    def test_multi_lines(self):
 
60
        s = 'foo\nbar\n'
 
61
        e = '""\n"foo\\n"\n"bar\\n"'
 
62
        self.assertEqual(export_pot._normalize(s), e)
 
63
 
 
64
        s = '\nfoo\nbar\n'
 
65
        e = ('""\n'
 
66
             '"\\n"\n'
 
67
             '"foo\\n"\n'
 
68
             '"bar\\n"')
 
69
        self.assertEqual(export_pot._normalize(s), e)
 
70
 
 
71
 
 
72
class TestParseSource(tests.TestCase):
 
73
    """Check mappings to line numbers generated from python source"""
 
74
 
 
75
    def test_classes(self):
 
76
        src = '''
 
77
class Ancient:
 
78
    """Old style class"""
 
79
 
 
80
class Modern(object):
 
81
    """New style class"""
 
82
'''
 
83
        cls_lines, _ = export_pot._parse_source(src)
 
84
        self.assertEqual(cls_lines,
 
85
            {"Ancient": 2, "Modern": 5})
 
86
 
 
87
    def test_classes_nested(self):
 
88
        src = '''
 
89
class Matroska(object):
 
90
    class Smaller(object):
 
91
        class Smallest(object):
 
92
            pass
 
93
'''
 
94
        cls_lines, _ = export_pot._parse_source(src)
 
95
        self.assertEqual(cls_lines,
 
96
            {"Matroska": 2, "Smaller": 3, "Smallest":4})
 
97
 
 
98
    def test_strings_docstrings(self):
 
99
        src = '''\
 
100
"""Module"""
 
101
 
 
102
def function():
 
103
    """Function"""
 
104
 
 
105
class Class(object):
 
106
    """Class"""
 
107
 
 
108
    def method(self):
 
109
        """Method"""
 
110
'''
 
111
        _, str_lines = export_pot._parse_source(src)
 
112
        self.assertEqual(str_lines,
 
113
            {"Module": 1, "Function": 4, "Class": 7, "Method": 10})
 
114
 
 
115
    def test_strings_literals(self):
 
116
        src = '''\
 
117
s = "One"
 
118
t = (2, "Two")
 
119
f = dict(key="Three")
 
120
'''
 
121
        _, str_lines = export_pot._parse_source(src)
 
122
        self.assertEqual(str_lines,
 
123
            {"One": 1, "Two": 2, "Three": 3})
 
124
 
 
125
    def test_strings_multiline(self):
 
126
        src = '''\
 
127
"""Start
 
128
 
 
129
End
 
130
"""
 
131
t = (
 
132
    "A"
 
133
    "B"
 
134
    "C"
 
135
    )
 
136
'''
 
137
        _, str_lines = export_pot._parse_source(src)
 
138
        self.assertEqual(str_lines,
 
139
            {"Start\n\nEnd\n": 1, "ABC": 6})
 
140
 
 
141
    def test_strings_multiline_escapes(self):
 
142
        src = '''\
 
143
s = "Escaped\\n"
 
144
r = r"Raw\\n"
 
145
t = (
 
146
    "A\\n\\n"
 
147
    "B\\n\\n"
 
148
    "C\\n\\n"
 
149
    )
 
150
'''
 
151
        _, str_lines = export_pot._parse_source(src)
 
152
        self.expectFailure("Escaped newlines confuses the multiline handling",
 
153
            self.assertNotEqual, str_lines,
 
154
            {"Escaped\n": 0, "Raw\\n": 2, "A\n\nB\n\nC\n\n": -2})
 
155
        self.assertEqual(str_lines,
 
156
            {"Escaped\n": 1, "Raw\\n": 2, "A\n\nB\n\nC\n\n": 4})
 
157
 
 
158
 
 
159
class TestModuleContext(tests.TestCase):
 
160
    """Checks for source context tracking objects"""
 
161
 
 
162
    def check_context(self, context, path, lineno):
 
163
        self.assertEqual((context.path, context.lineno), (path, lineno))
 
164
 
 
165
    def test___init__(self):
 
166
        context = export_pot._ModuleContext("one.py")
 
167
        self.check_context(context, "one.py", 1)
 
168
        context = export_pot._ModuleContext("two.py", 5)
 
169
        self.check_context(context, "two.py", 5)
 
170
 
 
171
    def test_from_class(self):
 
172
        """New context returned with lineno updated from class"""
 
173
        path = "cls.py"
 
174
        class A(object): pass
 
175
        class B(object): pass
 
176
        cls_lines = {"A": 5, "B": 7}
 
177
        context = export_pot._ModuleContext(path, _source_info=(cls_lines, {}))
 
178
        contextA = context.from_class(A)
 
179
        self.check_context(contextA, path, 5)
 
180
        contextB1 = context.from_class(B)
 
181
        self.check_context(contextB1, path, 7)
 
182
        contextB2 = contextA.from_class(B)
 
183
        self.check_context(contextB2, path, 7)
 
184
        self.check_context(context, path, 1)
 
185
        self.assertEqual("", self.get_log())
 
186
 
 
187
    def test_from_class_missing(self):
 
188
        """When class has no lineno the old context details are returned"""
 
189
        path = "cls_missing.py"
 
190
        class A(object): pass
 
191
        class M(object): pass
 
192
        context = export_pot._ModuleContext(path, 3, ({"A": 15}, {}))
 
193
        contextA = context.from_class(A)
 
194
        contextM1 = context.from_class(M)
 
195
        self.check_context(contextM1, path, 3)
 
196
        contextM2 = contextA.from_class(M)
 
197
        self.check_context(contextM2, path, 15)
 
198
        self.assertContainsRe(self.get_log(), "Definition of <.*M'> not found")
 
199
 
 
200
    def test_from_string(self):
 
201
        """New context returned with lineno updated from string"""
 
202
        path = "str.py"
 
203
        str_lines = {"one": 14, "two": 42}
 
204
        context = export_pot._ModuleContext(path, _source_info=({}, str_lines))
 
205
        context1 = context.from_string("one")
 
206
        self.check_context(context1, path, 14)
 
207
        context2A = context.from_string("two")
 
208
        self.check_context(context2A, path, 42)
 
209
        context2B = context1.from_string("two")
 
210
        self.check_context(context2B, path, 42)
 
211
        self.check_context(context, path, 1)
 
212
        self.assertEqual("", self.get_log())
 
213
 
 
214
    def test_from_string_missing(self):
 
215
        """When string has no lineno the old context details are returned"""
 
216
        path = "str_missing.py"
 
217
        context = export_pot._ModuleContext(path, 4, ({}, {"line\n": 21}))
 
218
        context1 = context.from_string("line\n")
 
219
        context2A = context.from_string("not there")
 
220
        self.check_context(context2A, path, 4)
 
221
        context2B = context1.from_string("not there")
 
222
        self.check_context(context2B, path, 21)
 
223
        self.assertContainsRe(self.get_log(), "String 'not there' not found")
 
224
 
 
225
 
 
226
class TestWriteOption(tests.TestCase):
 
227
    """Tests for writing texts extracted from options in pot format"""
 
228
 
 
229
    def pot_from_option(self, opt, context=None, note="test"):
 
230
        sio = StringIO()
 
231
        exporter = export_pot._PotExporter(sio)
 
232
        if context is None:
 
233
            context = export_pot._ModuleContext("nowhere", 0)
 
234
        export_pot._write_option(exporter, context, opt, note)
 
235
        return sio.getvalue()
 
236
 
 
237
    def test_option_without_help(self):
 
238
        opt = option.Option("helpless")
 
239
        self.assertEqual("", self.pot_from_option(opt))
 
240
 
 
241
    def test_option_with_help(self):
 
242
        opt = option.Option("helpful", help="Info.")
 
243
        self.assertContainsString(self.pot_from_option(opt), "\n"
 
244
            "# help of 'helpful' test\n"
 
245
            "msgid \"Info.\"\n")
 
246
 
 
247
    def test_option_hidden(self):
 
248
        opt = option.Option("hidden", help="Unseen.", hidden=True)
 
249
        self.assertEqual("", self.pot_from_option(opt))
 
250
 
 
251
    def test_option_context_missing(self):
 
252
        context = export_pot._ModuleContext("remote.py", 3)
 
253
        opt = option.Option("metaphor", help="Not a literal in the source.")
 
254
        self.assertContainsString(self.pot_from_option(opt, context),
 
255
            "#: remote.py:3\n"
 
256
            "# help of 'metaphor' test\n")
 
257
 
 
258
    def test_option_context_string(self):
 
259
        s = "Literally."
 
260
        context = export_pot._ModuleContext("local.py", 3, ({}, {s: 17}))
 
261
        opt = option.Option("example", help=s)
 
262
        self.assertContainsString(self.pot_from_option(opt, context),
 
263
            "#: local.py:17\n"
 
264
            "# help of 'example' test\n")
 
265
 
 
266
    def test_registry_option_title(self):
 
267
        opt = option.RegistryOption.from_kwargs("group", help="Pick one.",
 
268
            title="Choose!")
 
269
        pot = self.pot_from_option(opt)
 
270
        self.assertContainsString(pot, "\n"
 
271
            "# title of 'group' test\n"
 
272
            "msgid \"Choose!\"\n")
 
273
        self.assertContainsString(pot, "\n"
 
274
            "# help of 'group' test\n"
 
275
            "msgid \"Pick one.\"\n")
 
276
 
 
277
    def test_registry_option_title_context_missing(self):
 
278
        context = export_pot._ModuleContext("theory.py", 3)
 
279
        opt = option.RegistryOption.from_kwargs("abstract", title="Unfounded!")
 
280
        self.assertContainsString(self.pot_from_option(opt, context),
 
281
            "#: theory.py:3\n"
 
282
            "# title of 'abstract' test\n")
 
283
 
 
284
    def test_registry_option_title_context_string(self):
 
285
        s = "Grounded!"
 
286
        context = export_pot._ModuleContext("practice.py", 3, ({}, {s: 144}))
 
287
        opt = option.RegistryOption.from_kwargs("concrete", title=s)
 
288
        self.assertContainsString(self.pot_from_option(opt, context),
 
289
            "#: practice.py:144\n"
 
290
            "# title of 'concrete' test\n")
 
291
 
 
292
    def test_registry_option_value_switches(self):
 
293
        opt = option.RegistryOption.from_kwargs("switch", help="Flip one.",
 
294
            value_switches=True, enum_switch=False,
 
295
            red="Big.", green="Small.")
 
296
        pot = self.pot_from_option(opt)
 
297
        self.assertContainsString(pot, "\n"
 
298
            "# help of 'switch' test\n"
 
299
            "msgid \"Flip one.\"\n")
 
300
        self.assertContainsString(pot, "\n"
 
301
            "# help of 'switch=red' test\n"
 
302
            "msgid \"Big.\"\n")
 
303
        self.assertContainsString(pot, "\n"
 
304
            "# help of 'switch=green' test\n"
 
305
            "msgid \"Small.\"\n")
 
306
 
 
307
    def test_registry_option_value_switches_hidden(self):
 
308
        reg = registry.Registry()
 
309
        class Hider(object):
 
310
            hidden = True
 
311
        reg.register("new", 1, "Current.")
 
312
        reg.register("old", 0, "Legacy.", info=Hider())
 
313
        opt = option.RegistryOption("protocol", "Talking.", reg,
 
314
            value_switches=True, enum_switch=False)
 
315
        pot = self.pot_from_option(opt)
 
316
        self.assertContainsString(pot, "\n"
 
317
            "# help of 'protocol' test\n"
 
318
            "msgid \"Talking.\"\n")
 
319
        self.assertContainsString(pot, "\n"
 
320
            "# help of 'protocol=new' test\n"
 
321
            "msgid \"Current.\"\n")
 
322
        self.assertNotContainsString(pot, "'protocol=old'")
 
323
 
 
324
 
 
325
class TestPotExporter(tests.TestCase):
 
326
    """Test for logic specific to the _PotExporter class"""
 
327
 
 
328
    # This test duplicates test_duplicates below
 
329
    def test_duplicates(self):
 
330
        exporter = export_pot._PotExporter(StringIO())
 
331
        context = export_pot._ModuleContext("mod.py", 1)
 
332
        exporter.poentry_in_context(context, "Common line.")
 
333
        context.lineno = 3
 
334
        exporter.poentry_in_context(context, "Common line.")
 
335
        self.assertEqual(1, exporter.outf.getvalue().count("Common line."))
 
336
    
 
337
    def test_duplicates_included(self):
 
338
        exporter = export_pot._PotExporter(StringIO(), True)
 
339
        context = export_pot._ModuleContext("mod.py", 1)
 
340
        exporter.poentry_in_context(context, "Common line.")
 
341
        context.lineno = 3
 
342
        exporter.poentry_in_context(context, "Common line.")
 
343
        self.assertEqual(2, exporter.outf.getvalue().count("Common line."))
 
344
 
 
345
 
 
346
class PoEntryTestCase(tests.TestCase):
 
347
 
 
348
    def setUp(self):
 
349
        super(PoEntryTestCase, self).setUp()
 
350
        self.exporter = export_pot._PotExporter(StringIO())
 
351
 
 
352
    def check_output(self, expected):
 
353
        self.assertEqual(
 
354
                self.exporter.outf.getvalue(),
 
355
                textwrap.dedent(expected)
 
356
                )
 
357
 
 
358
 
 
359
class TestPoEntry(PoEntryTestCase):
 
360
 
 
361
    def test_simple(self):
 
362
        self.exporter.poentry('dummy', 1, "spam")
 
363
        self.exporter.poentry('dummy', 2, "ham", 'EGG')
 
364
        self.check_output('''\
 
365
                #: dummy:1
 
366
                msgid "spam"
 
367
                msgstr ""
 
368
 
 
369
                #: dummy:2
 
370
                # EGG
 
371
                msgid "ham"
 
372
                msgstr ""
 
373
 
 
374
                ''')
 
375
 
 
376
    def test_duplicate(self):
 
377
        self.exporter.poentry('dummy', 1, "spam")
 
378
        # This should be ignored.
 
379
        self.exporter.poentry('dummy', 2, "spam", 'EGG')
 
380
 
 
381
        self.check_output('''\
 
382
                #: dummy:1
 
383
                msgid "spam"
 
384
                msgstr ""\n
 
385
                ''')
 
386
 
 
387
 
 
388
class TestPoentryPerPergraph(PoEntryTestCase):
 
389
 
 
390
    def test_single(self):
 
391
        self.exporter.poentry_per_paragraph(
 
392
                'dummy',
 
393
                10,
 
394
                '''foo\nbar\nbaz\n'''
 
395
                )
 
396
        self.check_output('''\
 
397
                #: dummy:10
 
398
                msgid ""
 
399
                "foo\\n"
 
400
                "bar\\n"
 
401
                "baz\\n"
 
402
                msgstr ""\n
 
403
                ''')
 
404
 
 
405
    def test_multi(self):
 
406
        self.exporter.poentry_per_paragraph(
 
407
                'dummy',
 
408
                10,
 
409
                '''spam\nham\negg\n\nSPAM\nHAM\nEGG\n'''
 
410
                )
 
411
        self.check_output('''\
 
412
                #: dummy:10
 
413
                msgid ""
 
414
                "spam\\n"
 
415
                "ham\\n"
 
416
                "egg"
 
417
                msgstr ""
 
418
 
 
419
                #: dummy:14
 
420
                msgid ""
 
421
                "SPAM\\n"
 
422
                "HAM\\n"
 
423
                "EGG\\n"
 
424
                msgstr ""\n
 
425
                ''')
 
426
 
 
427
 
 
428
class TestExportCommandHelp(PoEntryTestCase):
 
429
 
 
430
    def test_command_help(self):
 
431
 
 
432
        class cmd_Demo(commands.Command):
 
433
            __doc__ = """A sample command.
 
434
 
 
435
            :Usage:
 
436
                bzr demo
 
437
 
 
438
            :Examples:
 
439
                Example 1::
 
440
 
 
441
                    cmd arg1
 
442
 
 
443
            Blah Blah Blah
 
444
            """
 
445
 
 
446
        export_pot._write_command_help(self.exporter, cmd_Demo())
 
447
        result = self.exporter.outf.getvalue()
 
448
        # We don't care about filename and lineno here.
 
449
        result = re.sub(r'(?m)^#: [^\n]+\n', '', result)
 
450
 
 
451
        self.assertEqualDiff(
 
452
                'msgid "A sample command."\n'
 
453
                'msgstr ""\n'
 
454
                '\n'                # :Usage: should not be translated.
 
455
                'msgid ""\n'
 
456
                '":Examples:\\n"\n'
 
457
                '"    Example 1::"\n'
 
458
                'msgstr ""\n'
 
459
                '\n'
 
460
                'msgid "        cmd arg1"\n'
 
461
                'msgstr ""\n'
 
462
                '\n'
 
463
                'msgid "Blah Blah Blah"\n'
 
464
                'msgstr ""\n'
 
465
                '\n',
 
466
                result
 
467
                )