~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_export_pot.py

  • Committer: Tarmac
  • Author(s): Vincent Ladeuil
  • Date: 2017-01-30 14:42:05 UTC
  • mfrom: (6620.1.1 trunk)
  • Revision ID: tarmac-20170130144205-r8fh2xpmiuxyozpv
Merge  2.7 into trunk including fix for bug #1657238 [r=vila]

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2011 Canonical Ltd
 
1
# Copyright (C) 2011, 2016 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
20
20
from bzrlib import (
21
21
    commands,
22
22
    export_pot,
 
23
    option,
 
24
    registry,
23
25
    tests,
24
26
    )
25
27
 
67
69
        self.assertEqual(export_pot._normalize(s), e)
68
70
 
69
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
 
70
346
class PoEntryTestCase(tests.TestCase):
71
347
 
72
348
    def setUp(self):
73
 
        self.overrideAttr(export_pot, '_FOUND_MSGID', set())
74
 
        self._outf = StringIO()
75
349
        super(PoEntryTestCase, self).setUp()
 
350
        self.exporter = export_pot._PotExporter(StringIO())
76
351
 
77
352
    def check_output(self, expected):
78
353
        self.assertEqual(
79
 
                self._outf.getvalue(),
 
354
                self.exporter.outf.getvalue(),
80
355
                textwrap.dedent(expected)
81
356
                )
82
357
 
 
358
 
83
359
class TestPoEntry(PoEntryTestCase):
84
360
 
85
361
    def test_simple(self):
86
 
        export_pot._poentry(self._outf, 'dummy', 1, "spam")
87
 
        export_pot._poentry(self._outf, 'dummy', 2, "ham", 'EGG')
 
362
        self.exporter.poentry('dummy', 1, "spam")
 
363
        self.exporter.poentry('dummy', 2, "ham", 'EGG')
88
364
        self.check_output('''\
89
365
                #: dummy:1
90
366
                msgid "spam"
98
374
                ''')
99
375
 
100
376
    def test_duplicate(self):
101
 
        export_pot._poentry(self._outf, 'dummy', 1, "spam")
 
377
        self.exporter.poentry('dummy', 1, "spam")
102
378
        # This should be ignored.
103
 
        export_pot._poentry(self._outf, 'dummy', 2, "spam", 'EGG')
 
379
        self.exporter.poentry('dummy', 2, "spam", 'EGG')
104
380
 
105
381
        self.check_output('''\
106
382
                #: dummy:1
112
388
class TestPoentryPerPergraph(PoEntryTestCase):
113
389
 
114
390
    def test_single(self):
115
 
        export_pot._poentry_per_paragraph(
116
 
                self._outf,
 
391
        self.exporter.poentry_per_paragraph(
117
392
                'dummy',
118
393
                10,
119
394
                '''foo\nbar\nbaz\n'''
128
403
                ''')
129
404
 
130
405
    def test_multi(self):
131
 
        export_pot._poentry_per_paragraph(
132
 
                self._outf,
 
406
        self.exporter.poentry_per_paragraph(
133
407
                'dummy',
134
408
                10,
135
409
                '''spam\nham\negg\n\nSPAM\nHAM\nEGG\n'''
169
443
            Blah Blah Blah
170
444
            """
171
445
 
172
 
        export_pot._write_command_help(self._outf, cmd_Demo())
173
 
        result = self._outf.getvalue()
 
446
        export_pot._write_command_help(self.exporter, cmd_Demo())
 
447
        result = self.exporter.outf.getvalue()
174
448
        # We don't care about filename and lineno here.
175
449
        result = re.sub(r'(?m)^#: [^\n]+\n', '', result)
176
450