/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5830.2.18 by INADA Naoki
Add some tests for export_pot module.
1
# Copyright (C) 2011 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
5830.2.20 by INADA Naoki
Add test for _poentry_per_peragraph()
18
import textwrap
5830.2.18 by INADA Naoki
Add some tests for export_pot module.
19
20
from bzrlib import (
5875.3.20 by INADA Naoki
Add test for exporting command help.
21
    commands,
5830.2.18 by INADA Naoki
Add some tests for export_pot module.
22
    export_pot,
6282.2.3 by Martin Packman
Export registry help to pot for unhidden option value switches
23
    option,
24
    registry,
5830.2.18 by INADA Naoki
Add some tests for export_pot module.
25
    tests,
26
    )
27
5875.3.20 by INADA Naoki
Add test for exporting command help.
28
import re
29
30
5830.2.18 by INADA Naoki
Add some tests for export_pot module.
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
6282.2.2 by Martin Packman
Add export_pot._ModuleContext class for more structured source location tracking, and share option writing logic
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.assertEquals((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.assertEquals("", 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.assertEquals("", 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
6282.2.3 by Martin Packman
Export registry help to pot for unhidden option value switches
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
5830.2.20 by INADA Naoki
Add test for _poentry_per_peragraph()
325
class PoEntryTestCase(tests.TestCase):
5830.2.18 by INADA Naoki
Add some tests for export_pot module.
326
327
    def setUp(self):
5830.2.20 by INADA Naoki
Add test for _poentry_per_peragraph()
328
        super(PoEntryTestCase, self).setUp()
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
329
        self.exporter = export_pot._PotExporter(StringIO())
5830.2.20 by INADA Naoki
Add test for _poentry_per_peragraph()
330
331
    def check_output(self, expected):
332
        self.assertEqual(
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
333
                self.exporter.outf.getvalue(),
5830.2.20 by INADA Naoki
Add test for _poentry_per_peragraph()
334
                textwrap.dedent(expected)
335
                )
336
6282.2.2 by Martin Packman
Add export_pot._ModuleContext class for more structured source location tracking, and share option writing logic
337
5830.2.20 by INADA Naoki
Add test for _poentry_per_peragraph()
338
class TestPoEntry(PoEntryTestCase):
5830.2.18 by INADA Naoki
Add some tests for export_pot module.
339
340
    def test_simple(self):
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
341
        self.exporter.poentry('dummy', 1, "spam")
342
        self.exporter.poentry('dummy', 2, "ham", 'EGG')
5830.2.20 by INADA Naoki
Add test for _poentry_per_peragraph()
343
        self.check_output('''\
344
                #: dummy:1
345
                msgid "spam"
346
                msgstr ""
347
348
                #: dummy:2
349
                # EGG
350
                msgid "ham"
351
                msgstr ""
352
353
                ''')
5830.2.18 by INADA Naoki
Add some tests for export_pot module.
354
355
    def test_duplicate(self):
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
356
        self.exporter.poentry('dummy', 1, "spam")
5830.2.18 by INADA Naoki
Add some tests for export_pot module.
357
        # This should be ignored.
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
358
        self.exporter.poentry('dummy', 2, "spam", 'EGG')
5830.2.18 by INADA Naoki
Add some tests for export_pot module.
359
5830.2.20 by INADA Naoki
Add test for _poentry_per_peragraph()
360
        self.check_output('''\
361
                #: dummy:1
362
                msgid "spam"
363
                msgstr ""\n
364
                ''')
365
366
367
class TestPoentryPerPergraph(PoEntryTestCase):
368
369
    def test_single(self):
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
370
        self.exporter.poentry_per_paragraph(
5830.2.20 by INADA Naoki
Add test for _poentry_per_peragraph()
371
                'dummy',
372
                10,
373
                '''foo\nbar\nbaz\n'''
374
                )
375
        self.check_output('''\
376
                #: dummy:10
377
                msgid ""
378
                "foo\\n"
379
                "bar\\n"
380
                "baz\\n"
381
                msgstr ""\n
382
                ''')
383
384
    def test_multi(self):
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
385
        self.exporter.poentry_per_paragraph(
5830.2.20 by INADA Naoki
Add test for _poentry_per_peragraph()
386
                'dummy',
387
                10,
388
                '''spam\nham\negg\n\nSPAM\nHAM\nEGG\n'''
389
                )
390
        self.check_output('''\
391
                #: dummy:10
392
                msgid ""
393
                "spam\\n"
394
                "ham\\n"
395
                "egg"
396
                msgstr ""
397
398
                #: dummy:14
399
                msgid ""
400
                "SPAM\\n"
401
                "HAM\\n"
402
                "EGG\\n"
403
                msgstr ""\n
404
                ''')
5875.3.20 by INADA Naoki
Add test for exporting command help.
405
406
407
class TestExportCommandHelp(PoEntryTestCase):
408
409
    def test_command_help(self):
410
411
        class cmd_Demo(commands.Command):
412
            __doc__ = """A sample command.
413
414
            :Usage:
415
                bzr demo
416
417
            :Examples:
418
                Example 1::
419
420
                    cmd arg1
421
422
            Blah Blah Blah
423
            """
424
6282.2.1 by Martin Packman
Add export_pot._PotExporter class to avoid module global, and other minor cleanups
425
        export_pot._write_command_help(self.exporter, cmd_Demo())
426
        result = self.exporter.outf.getvalue()
5993.1.2 by Vincent Ladeuil
Fix python2.6 compatibility.
427
        # We don't care about filename and lineno here.
428
        result = re.sub(r'(?m)^#: [^\n]+\n', '', result)
5875.3.20 by INADA Naoki
Add test for exporting command help.
429
430
        self.assertEqualDiff(
431
                'msgid "A sample command."\n'
432
                'msgstr ""\n'
433
                '\n'                # :Usage: should not be translated.
434
                'msgid ""\n'
435
                '":Examples:\\n"\n'
436
                '"    Example 1::"\n'
437
                'msgstr ""\n'
438
                '\n'
439
                'msgid "        cmd arg1"\n'
440
                'msgstr ""\n'
441
                '\n'
442
                'msgid "Blah Blah Blah"\n'
443
                'msgstr ""\n'
444
                '\n',
445
                result
446
                )