1import warnings
2from io import BytesIO
3
4from pytest import mark
5
6from translate.convert import pot2po, test_convert
7from translate.storage import po
8
9
10class TestPOT2PO:
11    def setup_method(self, method):
12        warnings.resetwarnings()
13
14    def teardown_method(self, method):
15        warnings.resetwarnings()
16
17    def convertpot(self, potsource, posource=None):
18        """helper that converts pot source to po source without requiring files"""
19        potfile = BytesIO(potsource.encode())
20        if posource:
21            pofile = BytesIO(posource.encode())
22        else:
23            pofile = None
24        pooutfile = BytesIO()
25        pot2po.convertpot(potfile, pooutfile, pofile)
26        pooutfile.seek(0)
27        return po.pofile(pooutfile.read())
28
29    def singleunit(self, pofile):
30        """checks that the pofile contains a single non-header unit, and returns it"""
31        assert len(pofile.units) == 2
32        assert pofile.units[0].isheader()
33        print(pofile.units[1])
34        return pofile.units[1]
35
36    def test_convertpot_blank(self):
37        """checks that the convertpot function is working for a simple file initialisation"""
38        potsource = (
39            """#: simple.label%ssimple.accesskey\nmsgid "A &hard coded newline.\\n"\nmsgstr ""\n"""
40            % po.lsep
41        )
42        newpo = self.convertpot(potsource)
43        assert str(self.singleunit(newpo)) == potsource
44
45    def test_convertpot_blank_plurals(self):
46        """checks that the convertpot function is working for initialising plurals correctly"""
47        potsource = r"""msgid ""
48msgstr""
49
50msgid "%d manual"
51msgid_plural "%d manuals"
52msgstr[0] ""
53msgstr[1] ""
54"""
55        posource = r"""msgid ""
56msgstr""
57"Plural-Forms: nplurals=1; plural=0;\n"
58"""
59
60        poexpected = r"""msgid "%d manual"
61msgid_plural "%d manuals"
62msgstr[0] ""
63"""
64        newpo = self.convertpot(potsource, posource)
65        assert str(self.singleunit(newpo)) == poexpected
66
67    def test_merging_simple(self):
68        """checks that the convertpot function is working for a simple merge"""
69        potsource = (
70            """#: simple.label%ssimple.accesskey\nmsgid "A &hard coded newline.\\n"\nmsgstr ""\n"""
71            % po.lsep
72        )
73        posource = (
74            """#: simple.label%ssimple.accesskey\nmsgid "A &hard coded newline.\\n"\nmsgstr "&Hart gekoeerde nuwe lyne\\n"\n"""
75            % po.lsep
76        )
77        newpo = self.convertpot(potsource, posource)
78        assert str(self.singleunit(newpo)) == posource
79
80    def test_merging_messages_marked_fuzzy(self):
81        """test that when we merge PO files with a fuzzy message that it remains fuzzy"""
82        potsource = (
83            """#: simple.label%ssimple.accesskey\nmsgid "A &hard coded newline.\\n"\nmsgstr ""\n"""
84            % po.lsep
85        )
86        posource = (
87            """#: simple.label%ssimple.accesskey\n#, fuzzy\nmsgid "A &hard coded newline.\\n"\nmsgstr "&Hart gekoeerde nuwe lyne\\n"\n"""
88            % po.lsep
89        )
90        newpo = self.convertpot(potsource, posource)
91        assert str(self.singleunit(newpo)) == posource
92
93    def test_merging_plurals_with_fuzzy_matching(self):
94        """test that when we merge PO files with a fuzzy message that it remains fuzzy"""
95        potsource = r"""#: file.cpp:2
96msgid "%d manual"
97msgid_plural "%d manuals"
98msgstr[0] ""
99msgstr[1] ""
100"""
101        posource = r"""#: file.cpp:3
102#, fuzzy
103msgid "%d manual"
104msgid_plural "%d manuals"
105msgstr[0] "%d handleiding."
106msgstr[1] "%d handleidings."
107"""
108        # The #: comment and msgid's are different between the pot and the po
109        poexpected = r"""#: file.cpp:2
110#, fuzzy
111msgid "%d manual"
112msgid_plural "%d manuals"
113msgstr[0] "%d handleiding."
114msgstr[1] "%d handleidings."
115"""
116        newpo = self.convertpot(potsource, posource)
117        assert str(self.singleunit(newpo)) == poexpected
118
119    @mark.xfail(reason="Not implemented - review if this is even correct")
120    def test_merging_msgid_change(self):
121        """tests that if the msgid changes but the location stays the same that we merge"""
122        potsource = """#: simple.label\n#: simple.accesskey\nmsgid "Its &hard coding a newline.\\n"\nmsgstr ""\n"""
123        posource = """#: simple.label\n#: simple.accesskey\nmsgid "A &hard coded newline.\\n"\nmsgstr "&Hart gekoeerde nuwe lyne\\n"\n"""
124        poexpected = """#: simple.label\n#: simple.accesskey\n#, fuzzy\nmsgid "Its &hard coding a newline.\\n"\nmsgstr "&Hart gekoeerde nuwe lyne\\n"\n"""
125        newpo = self.convertpot(potsource, posource)
126        print(newpo)
127        assert str(self.singleunit(newpo)) == poexpected
128
129    def test_merging_location_change(self):
130        """tests that if the location changes but the msgid stays the same that we merge"""
131        potsource = (
132            """#: new_simple.label%snew_simple.accesskey\nmsgid "A &hard coded newline.\\n"\nmsgstr ""\n"""
133            % po.lsep
134        )
135        posource = (
136            """#: simple.label%ssimple.accesskey\nmsgid "A &hard coded newline.\\n"\nmsgstr "&Hart gekoeerde nuwe lyne\\n"\n"""
137            % po.lsep
138        )
139        poexpected = (
140            """#: new_simple.label%snew_simple.accesskey\nmsgid "A &hard coded newline.\\n"\nmsgstr "&Hart gekoeerde nuwe lyne\\n"\n"""
141            % po.lsep
142        )
143        newpo = self.convertpot(potsource, posource)
144        print(newpo)
145        assert str(self.singleunit(newpo)) == poexpected
146
147    def test_merging_location_and_whitespace_change(self):
148        """
149        test that even if the location changes that if the msgid
150        only has whitespace changes we can still merge
151        """
152
153        potsource = (
154            """#: singlespace.label%ssinglespace.accesskey\nmsgid "&We have spaces"\nmsgstr ""\n"""
155            % po.lsep
156        )
157        posource = (
158            """#: doublespace.label%sdoublespace.accesskey\nmsgid "&We  have  spaces"\nmsgstr "&One  het  spasies"\n"""
159            % po.lsep
160        )
161        poexpected = (
162            """#: singlespace.label%ssinglespace.accesskey\n#, fuzzy\nmsgid "&We have spaces"\nmsgstr "&One  het  spasies"\n"""
163            % po.lsep
164        )
165        newpo = self.convertpot(potsource, posource)
166        print(newpo)
167        assert str(self.singleunit(newpo)) == poexpected
168
169    def test_merging_location_ambiguous_with_disambiguous(self):
170        """
171        test that when we have a PO in ambiguous (Gettext form) and merge with
172        disamabiguous (KDE comment form) that we don't duplicate the
173        location #: comments
174        """
175        potsource = (
176            """#: location.c:1\nmsgid ""\n"_: location.c:1\\n"\n"Source"\nmsgstr ""\n\n"""
177            + """#: location.c:10\nmsgid ""\n"_: location.c:10\\n"\n"Source"\nmsgstr ""\n"""
178        )
179        posource = (
180            """#: location.c:1\n#: location.c:10\nmsgid "Source"\nmsgstr "Target"\n\n"""
181        )
182        poexpected1 = """#: location.c:1\n#, fuzzy\nmsgid ""\n"_: location.c:1\\n"\n"Source"\nmsgstr "Target"\n"""
183        poexpected2 = """#: location.c:10\n#, fuzzy\nmsgid ""\n"_: location.c:10\\n"\n"Source"\nmsgstr "Target"\n"""
184        newpo = self.convertpot(potsource, posource)
185        print("Expected:\n", poexpected1, "Actual:\n", newpo.units[1])
186        assert str(newpo.units[1]) == poexpected1
187        assert str(newpo.units[2]) == poexpected2
188
189    @mark.xfail(reason="Not Implemented - needs review")
190    def test_merging_accelerator_changes(self):
191        """test that a change in the accelerator localtion still allows merging"""
192        potsource = """#: someline.c\nmsgid "A&bout"\nmsgstr ""\n"""
193        posource = """#: someline.c\nmsgid "&About"\nmsgstr "&Info"\n"""
194        poexpected = """#: someline.c\nmsgid "A&bout"\nmsgstr "&Info"\n"""
195        newpo = self.convertpot(potsource, posource)
196        print(newpo)
197        assert str(self.singleunit(newpo)) == poexpected
198
199    @mark.xfail(reason="Not Implemented - review if this is even correct")
200    def test_lines_cut_differently(self):
201        """Checks that the correct formatting is preserved when pot an po lines differ."""
202        potsource = (
203            """#: simple.label\nmsgid "Line split "\n"differently"\nmsgstr ""\n"""
204        )
205        posource = """#: simple.label\nmsgid "Line"\n" split differently"\nmsgstr "Lyne verskillend gesny"\n"""
206        newpo = self.convertpot(potsource, posource)
207        newpounit = self.singleunit(newpo)
208        assert str(newpounit) == posource
209
210    def test_merging_automatic_comments_dont_duplicate(self):
211        """ensure that we can merge #. comments correctly"""
212        potsource = """#. Row 35\nmsgid "&About"\nmsgstr ""\n"""
213        posource = """#. Row 35\nmsgid "&About"\nmsgstr "&Info"\n"""
214        newpo = self.convertpot(potsource, posource)
215        newpounit = self.singleunit(newpo)
216        assert str(newpounit) == posource
217
218    def test_merging_automatic_comments_new_overides_old(self):
219        """ensure that new #. comments override the old comments"""
220        potsource = """#. new comment\n#: someline.c\nmsgid "&About"\nmsgstr ""\n"""
221        posource = """#. old comment\n#: someline.c\nmsgid "&About"\nmsgstr "&Info"\n"""
222        poexpected = (
223            """#. new comment\n#: someline.c\nmsgid "&About"\nmsgstr "&Info"\n"""
224        )
225        newpo = self.convertpot(potsource, posource)
226        newpounit = self.singleunit(newpo)
227        assert str(newpounit) == poexpected
228
229    def test_merging_comments_with_blank_comment_lines(self):
230        """test that when we merge a comment that has a blank line we keep the blank line"""
231        potsource = """#: someline.c\nmsgid "About"\nmsgstr ""\n"""
232        posource = """# comment1\n#\n# comment2\n#: someline.c\nmsgid "About"\nmsgstr "Omtrent"\n"""
233        poexpected = posource
234        newpo = self.convertpot(potsource, posource)
235        newpounit = self.singleunit(newpo)
236        assert str(newpounit) == poexpected
237
238    def test_empty_commentlines(self):
239        potsource = """#: paneSecurity.title
240msgid "Security"
241msgstr ""
242"""
243        posource = """# - Contributor(s):
244# -
245# - Alternatively, the
246# -
247#: paneSecurity.title
248msgid "Security"
249msgstr "Sekuriteit"
250"""
251        poexpected = posource
252        newpo = self.convertpot(potsource, posource)
253        newpounit = self.singleunit(newpo)
254        print("expected")
255        print(poexpected)
256        print("got:")
257        print(str(newpounit))
258        assert str(newpounit) == poexpected
259
260    def test_merging_msgidcomments(self):
261        """ensure that we can merge msgidcomments messages"""
262        potsource = r"""#: window.width
263msgid ""
264"_: Do not translate this.\n"
265"36em"
266msgstr ""
267"""
268        posource = r"""#: window.width
269msgid ""
270"_: Do not translate this.\n"
271"36em"
272msgstr "36em"
273"""
274        newpo = self.convertpot(potsource, posource)
275        newpounit = self.singleunit(newpo)
276        assert str(newpounit) == posource
277
278    def test_merging_msgid_with_msgidcomment(self):
279        """test that we can merge an otherwise identical string that has a different msgid"""
280        potsource = r"""#: pref.certs.title
281msgid ""
282"_: pref.certs.title\n"
283"Certificates"
284msgstr ""
285
286#: certs.label
287msgid ""
288"_: certs.label\n"
289"Certificates"
290msgstr ""
291"""
292        posource = r"""#: pref.certs.title
293msgid ""
294"_: pref.certs.title\n"
295"Certificates"
296msgstr ""
297
298#: certs.label
299msgid ""
300"_: certs.label\n"
301"Certificates"
302msgstr "Sertifikate"
303"""
304        expected = r"""#: pref.certs.title
305#, fuzzy
306msgid ""
307"_: pref.certs.title\n"
308"Certificates"
309msgstr "Sertifikate"
310"""
311        newpo = self.convertpot(potsource, posource)
312        newpounit = newpo.units[1]
313        assert str(newpounit) == expected
314
315    def test_merging_plurals(self):
316        """ensure that we can merge plural messages"""
317        potsource = """msgid "One"\nmsgid_plural "Two"\nmsgstr[0] ""\nmsgstr[1] ""\n"""
318        posource = """msgid "One"\nmsgid_plural "Two"\nmsgstr[0] "Een"\nmsgstr[1] "Twee"\nmsgstr[2] "Drie"\n"""
319        newpo = self.convertpot(potsource, posource)
320        print(newpo)
321        newpounit = self.singleunit(newpo)
322        assert str(newpounit) == posource
323
324    def test_merging_obsoleting_messages(self):
325        """check that we obsolete messages no longer present in the new file"""
326        # add emtpy msgid line to help factory identify format
327        potsource = 'msgid ""\nmsgstr ""\n'
328        posource = '# Some comment\n#. Extracted comment\n#: obsoleteme:10\nmsgid "One"\nmsgstr "Een"\n'
329        expected = '# Some comment\n#~ msgid "One"\n#~ msgstr "Een"\n'
330        newpo = self.convertpot(potsource, posource)
331        print(bytes(newpo))
332        newpounit = self.singleunit(newpo)
333        assert str(newpounit) == expected
334
335    def test_not_obsoleting_empty_messages(self):
336        """check that we don't obsolete (and keep) untranslated messages"""
337        # add emtpy msgid line to help factory identify format
338        potsource = 'msgid ""\nmsgstr ""\n'
339        posource = '#: obsoleteme:10\nmsgid "One"\nmsgstr ""\n'
340        newpo = self.convertpot(potsource, posource)
341        print(bytes(newpo))
342        # We should only have the header
343        assert len(newpo.units) == 1
344
345    def test_merging_new_before_obsolete(self):
346        """test to check that we place new blank message before obsolete messages"""
347        potsource = """#: newline.c\nmsgid "&About"\nmsgstr ""\n"""
348        posource = """#~ msgid "Old"\n#~ msgstr "Oud"\n"""
349        newpo = self.convertpot(potsource, posource)
350        assert len(newpo.units) == 3
351        assert newpo.units[0].isheader()
352        assert newpo.units[2].isobsolete()
353        assert str(newpo.units[1]) == potsource
354        assert str(newpo.units[2]) == posource
355
356        # Now test with real units present in posource
357        posource2 = """msgid "Old"\nmsgstr "Oud"\n"""
358        newpo = self.convertpot(potsource, posource2)
359        assert len(newpo.units) == 3
360        assert newpo.units[0].isheader()
361        assert newpo.units[2].isobsolete()
362        assert str(newpo.units[1]) == potsource
363        assert str(newpo.units[2]) == posource
364
365    def test_merging_resurect_obsolete_messages(self):
366        """check that we can reuse old obsolete messages if the message comes back"""
367        potsource = """#: resurect.c\nmsgid "&About"\nmsgstr ""\n"""
368        posource = """#~ msgid "&About"\n#~ msgstr "&Omtrent"\n"""
369        expected = """#: resurect.c\nmsgid "&About"\nmsgstr "&Omtrent"\n"""
370        newpo = self.convertpot(potsource, posource)
371        print(newpo)
372        assert len(newpo.units) == 2
373        assert newpo.units[0].isheader()
374        newpounit = self.singleunit(newpo)
375        assert str(newpounit) == expected
376
377    def test_merging_resurect_obsolete_messages_into_msgidcomment(self):
378        """check that we can reuse old obsolete messages even if the recipient has a msgidcomment"""
379        potsource = (
380            """#: resurect1.c\nmsgid "About"\nmsgstr ""\n\n"""
381            + """#: resurect2.c\nmsgid ""\n"_: resurect2.c\\n"\n"About"\nmsgstr ""\n"""
382        )
383        posource = """#~ msgid "About"\n#~ msgstr "Omtrent"\n"""
384        expected1 = """#: resurect1.c\nmsgid "About"\nmsgstr "Omtrent"\n"""
385        expected2 = """#: resurect2.c\n#, fuzzy\nmsgid ""\n"_: resurect2.c\\n"\n"About"\nmsgstr "Omtrent"\n"""
386        newpo = self.convertpot(potsource, posource)
387        print(newpo)
388        assert len(newpo.units) == 3
389        assert newpo.units[0].isheader()
390        assert str(newpo.units[1]) == expected1
391        assert str(newpo.units[2]) == expected2
392
393    def test_header_initialisation(self):
394        """test to check that we initialise the header correctly"""
395        potsource = r"""#, fuzzy
396msgid ""
397msgstr ""
398"Project-Id-Version: PACKAGE VERSION\n"
399"Report-Msgid-Bugs-To: new@example.com\n"
400"POT-Creation-Date: 2006-11-11 11:11+0000\n"
401"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
402"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
403"Language-Team: LANGUAGE <LL@li.org>\n"
404"MIME-Version: 1.0\n"
405"Content-Type: text/plain; charset=UTF-8\n"
406"Content-Transfer-Encoding: 8bit\n"
407"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
408"X-Generator: Translate Toolkit 0.10rc2\n"
409"""
410        posource = r"""msgid ""
411msgstr ""
412"Project-Id-Version: Pootle 0.10\n"
413"Report-Msgid-Bugs-To: old@example.com\n"
414"POT-Creation-Date: 2006-01-01 01:01+0100\n"
415"PO-Revision-Date: 2006-09-09 09:09+0900\n"
416"Last-Translator: Joe Translate <joe@example.com>\n"
417"Language-Team: Pig Latin <piglatin@example.com>\n"
418"MIME-Version: 1.0\n"
419"Content-Type: text/plain; charset=UTF-8\n"
420"Content-Transfer-Encoding: 8bit\n"
421"Plural-Forms: nplurals=2; plural=(n != 1);\n"
422"X-Generator: Translate Toolkit 0.9\n"
423"""
424        expected = r"""msgid ""
425msgstr ""
426"Project-Id-Version: Pootle 0.10\n"
427"Report-Msgid-Bugs-To: new@example.com\n"
428"POT-Creation-Date: 2006-11-11 11:11+0000\n"
429"PO-Revision-Date: 2006-09-09 09:09+0900\n"
430"Last-Translator: Joe Translate <joe@example.com>\n"
431"Language-Team: Pig Latin <piglatin@example.com>\n"
432"MIME-Version: 1.0\n"
433"Content-Type: text/plain; charset=UTF-8\n"
434"Content-Transfer-Encoding: 8bit\n"
435"Plural-Forms: nplurals=2; plural=(n != 1);\n"
436"X-Generator: Translate Toolkit 0.10rc2\n"
437"""
438        newpo = self.convertpot(potsource, posource)
439        print("Output Header:\n%s" % newpo)
440        print("Expected Header:\n%s" % expected)
441        assert bytes(newpo).decode("utf-8") == expected
442
443    def test_merging_comments(self):
444        """Test that we can merge comments correctly"""
445        potsource = """#. Don't do it!\n#: file.py:1\nmsgid "One"\nmsgstr ""\n"""
446        posource = """#. Don't do it!\n#: file.py:2\nmsgid "One"\nmsgstr "Een"\n"""
447        poexpected = """#. Don't do it!\n#: file.py:1\nmsgid "One"\nmsgstr "Een"\n"""
448        newpo = self.convertpot(potsource, posource)
449        print(newpo)
450        newpounit = self.singleunit(newpo)
451        assert str(newpounit) == poexpected
452
453    def test_merging_typecomments(self):
454        """Test that we can merge with typecomments"""
455        potsource = """#: file.c:1\n#, c-format\nmsgid "%d pipes"\nmsgstr ""\n"""
456        posource = """#: file.c:2\nmsgid "%d pipes"\nmsgstr "%d pype"\n"""
457        poexpected = (
458            """#: file.c:1\n#, c-format\nmsgid "%d pipes"\nmsgstr "%d pype"\n"""
459        )
460        newpo = self.convertpot(potsource, posource)
461        newpounit = self.singleunit(newpo)
462        print(newpounit)
463        assert str(newpounit) == poexpected
464
465        potsource = """#: file.c:1\n#, c-format\nmsgid "%d computers"\nmsgstr ""\n"""
466        posource = """#: file.c:2\n#, c-format\nmsgid "%s computers "\nmsgstr "%s-rekenaars"\n"""
467        poexpected = """#: file.c:1\n#, fuzzy, c-format\nmsgid "%d computers"\nmsgstr "%s-rekenaars"\n"""
468        newpo = self.convertpot(potsource, posource)
469        newpounit = self.singleunit(newpo)
470        assert newpounit.isfuzzy()
471        assert newpounit.hastypecomment("c-format")
472
473    def test_msgctxt(self):
474        """Test that msgctxt is migrated correctly"""
475        potsource = """
476#: something.h:5
477msgctxt "context1"
478msgid "text"
479msgstr ""
480
481#: something.h:6
482msgctxt "context2"
483msgid "text"
484msgstr ""
485"""
486        posource = """
487#: something.h:3
488msgctxt "context0"
489msgid "text"
490msgstr "teks"
491
492#: something.h:4
493msgctxt "context1"
494msgid "text"
495msgstr "sms"
496"""
497        poexpected = """
498#: something.h:5
499msgctxt "context1"
500msgid "text"
501msgstr "sms"
502
503#: something.h:6
504#, fuzzy
505msgctxt "context2"
506msgid "text"
507msgstr "teks"
508"""
509        newpo = self.convertpot(potsource, posource)
510        print(newpo)
511        assert poexpected in bytes(newpo).decode("utf-8")
512
513    def test_msgctxt_multiline(self):
514        """Test multiline msgctxt fields."""
515        pot_source = r"""#. |1MV
516#: ActionTe.ulf
517msgctxt ""
518"ActionTe.ulf\n"
519"OOO_ACTIONTEXT_21\n"
520"LngText.text"
521msgid "Computing space requirements"
522msgstr ""
523"""
524
525        po_source = r"""#. |1MV
526#: ActionTe.ulf
527msgctxt ""
528"ActionTe.ulf\n"
529"OOO_ACTIONTEXT_21\n"
530"LngText.text"
531msgid "Computing space requirements"
532msgstr "A szükséges lemezterület kiszámítása"
533"""
534
535        new_po = self.convertpot(pot_source, po_source)
536        assert new_po.units[0].isheader()
537        unit = new_po.units[1]
538        assert not unit.isfuzzy()
539        assert po_source == str(unit)
540
541    def test_msgid_merge_on_location(self):
542        """Tests that unit merges rely on location-based matching."""
543        pot_source = r"""
544msgid ""
545msgstr ""
546"X-Merge-On: location\n"
547
548#. $:am
549#: ActionTe.ulf
550msgctxt ""
551"ActionTe.ulf\n"
552"OOO_ACTIONTEXT_11\n"
553"LngText.text"
554msgid "Computing space requirements"
555msgstr ""
556
557#. NjJ3
558#: ActionTe.ulf
559msgctxt ""
560"ActionTe.ulf\n"
561"OOO_ACTIONTEXT_12\n"
562"LngText.text"
563msgid "Computing space requirements"
564msgstr ""
565"""
566
567        po_source = r"""
568#. $:am
569#: ActionTe.ulf
570msgctxt ""
571"ActionTe.ulf\n"
572"OOO_ACTIONTEXT_11\n"
573"LngText.text"
574msgid "Computing space requirements"
575msgstr "A szükséges lemezterület kiszámítása"
576
577#. NjJ3
578#: ActionTe.ulf
579msgctxt ""
580"ActionTe.ulf\n"
581"OOO_ACTIONTEXT_12\n"
582"LngText.text"
583msgid "Computing space requirements"
584msgstr "A szükséges lemezterület kiszámítása"
585"""
586
587        new_po = self.convertpot(pot_source, po_source)
588
589        assert len(new_po.units) == 3
590        assert new_po.units[0].isheader()
591
592        assert not new_po.units[1].isfuzzy()
593        assert new_po.units[2].isfuzzy()
594
595    def test_msgid_merge_on_id(self):
596        """Tests that unit merges rely on location-based matching."""
597        pot_source = r"""
598msgid ""
599msgstr ""
600"X-Merge-On: id\n"
601
602#. $:am
603#: ActionTe.ulf
604msgctxt ""
605"ActionTe.ulf\n"
606"OOO_ACTIONTEXT_11\n"
607"LngText.text"
608msgid "Computing space requirements"
609msgstr ""
610
611#. NjJ3
612#: ActionTe.ulf
613msgctxt ""
614"ActionTe.ulf\n"
615"OOO_ACTIONTEXT_12\n"
616"LngText.text"
617msgid "Computing space requirements"
618msgstr ""
619"""
620        pot_source_noheader = r"""
621#. $:am
622#: ActionTe.ulf
623msgctxt ""
624"ActionTe.ulf\n"
625"OOO_ACTIONTEXT_11\n"
626"LngText.text"
627msgid "Computing space requirements"
628msgstr ""
629
630#. NjJ3
631#: ActionTe.ulf
632msgctxt ""
633"ActionTe.ulf\n"
634"OOO_ACTIONTEXT_12\n"
635"LngText.text"
636msgid "Computing space requirements"
637msgstr ""
638"""
639
640        po_source = r"""
641#. $:am
642#: ActionTe.ulf
643msgctxt ""
644"ActionTe.ulf\n"
645"OOO_ACTIONTEXT_11\n"
646"LngText.text"
647msgid "Computing space requirements"
648msgstr "A szükséges lemezterület kiszámítása"
649
650#. NjJ3
651#: ActionTe.ulf
652msgctxt ""
653"ActionTe.ulf\n"
654"OOO_ACTIONTEXT_12\n"
655"LngText.text"
656msgid "Computing space requirements"
657msgstr "A szükséges lemezterület kiszámítása"
658"""
659
660        for pot in (pot_source, pot_source_noheader):
661            new_po = self.convertpot(pot, po_source)
662
663            assert len(new_po.units) == 3
664            assert new_po.units[0].isheader()
665
666            assert not new_po.units[1].isfuzzy()
667            assert not new_po.units[2].isfuzzy()
668
669    def test_empty_msgid(self):
670        """Test that we handle empty msgids correctly."""
671        # TODO: this test will fail if we don't have the gettext location
672        # comment in the pot file
673        potsource = '#: file:1\nmsgctxt "bla"\nmsgid ""\nmsgstr ""\n'
674        posource = r"""
675msgid ""
676"Project-Id-Version: Pootle 0.10\n"
677msgstr ""
678
679msgctxt "bla"
680msgid ""
681msgstr "trans"
682"""
683        newpo = self.convertpot(potsource, posource)
684        print(newpo)
685        assert len(newpo.units) == 2
686        assert newpo.units[0].isheader()
687        unit = newpo.units[1]
688        assert unit.source == ""
689        assert unit.getid() == "bla\04"
690        assert unit.target == "trans"
691        assert not unit.isfuzzy()
692
693    def test_migrate_msgidcomment_to_msgctxt(self):
694        """
695        Test that we migrate correctly from msgidcomments to msgctxt.
696
697        This is needed for our move away from using msgidcomments for mozilla.
698        """
699        potsource = r"""
700msgid ""
701msgstr ""
702"X-Accelerator-Marker: &\n"
703"X-Merge-On: location\n"
704
705
706#: bla
707msgctxt "bla"
708msgid ""
709msgstr ""
710"""
711        posource = r"""
712msgid ""
713msgstr ""
714"Project-Id-Version: Pootle 0.10\n"
715"X-Accelerator-Marker: &\n"
716
717
718#: bla
719msgid ""
720"_: bla\n"
721msgstr "trans"
722"""
723        newpo = self.convertpot(potsource, posource)
724        print(newpo)
725        assert len(newpo.units) == 2
726        assert newpo.units[0].isheader()
727        unit = newpo.units[1]
728        assert unit.source == ""
729        assert unit.getid() == "bla\04"
730        assert unit.target == "trans"
731        assert not unit.isfuzzy()
732
733    def test_obsolete_msgctxt(self):
734        """Test that obsolete units' msgctxt is preserved."""
735        potsource = 'msgctxt "newContext"\nmsgid "First unit"\nmsgstr ""'
736        posource = """
737msgctxt "newContext"
738msgid "First unit"
739msgstr "Eerste eenheid"
740
741#~ msgctxt "context"
742#~ msgid "Old unit"
743#~ msgstr "Ou eenheid1"
744
745#~ msgctxt "context2"
746#~ msgid "Old unit"
747#~ msgstr "Ou eenheid2"
748
749#~ msgid "Old unit"
750#~ msgstr "Ou eenheid3"
751"""
752        newpo = self.convertpot(potsource, posource)
753        print(newpo)
754        assert len(newpo.units) == 5
755        assert newpo.units[1].getcontext() == "newContext"
756        # Search in unit string, because obsolete units can't return a context
757        assert 'msgctxt "context"' in str(newpo.units[2])
758        assert 'msgctxt "context2"' in str(newpo.units[3])
759
760    def test_small_strings(self):
761        """
762        Test that units with small source strings are not incorrectly
763        populated by means of fuzzy matching.
764        """
765        potsource = r"""#, fuzzy
766msgid ""
767msgstr ""
768"Project-Id-Version: PACKAGE VERSION\n"
769"Report-Msgid-Bugs-To: new@example.com\n"
770"POT-Creation-Date: 2006-11-11 11:11+0000\n"
771"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
772"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
773"Language-Team: LANGUAGE <LL@li.org>\n"
774"MIME-Version: 1.0\n"
775"Content-Type: text/plain; charset=UTF-8\n"
776"Content-Transfer-Encoding: 8bit\n"
777"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
778"X-Accelerator-Marker: &\n"
779"X-Generator: Translate Toolkit 0.10rc2\n"
780"X-Merge-On: location\n"
781
782#: new_disassociated_mozilla_accesskey
783msgid "R"
784msgstr ""
785"""
786        posource = r"""msgid ""
787msgstr ""
788"Project-Id-Version: Pootle 0.10\n"
789"Report-Msgid-Bugs-To: old@example.com\n"
790"POT-Creation-Date: 2006-01-01 01:01+0100\n"
791"PO-Revision-Date: 2006-09-09 09:09+0900\n"
792"Last-Translator: Joe Translate <joe@example.com>\n"
793"Language-Team: Pig Latin <piglatin@example.com>\n"
794"MIME-Version: 1.0\n"
795"Content-Type: text/plain; charset=UTF-8\n"
796"Content-Transfer-Encoding: 8bit\n"
797"Plural-Forms: nplurals=2; plural=(n != 1);\n"
798"X-Generator: Translate Toolkit 0.9\n"
799"X-Accelerator-Marker: &\n"
800
801#: old_disassociated_mozilla_accesskey
802msgid "R"
803msgstr "S"
804"""
805        expected = r"""msgid ""
806msgstr ""
807"Project-Id-Version: Pootle 0.10\n"
808"Report-Msgid-Bugs-To: new@example.com\n"
809"POT-Creation-Date: 2006-11-11 11:11+0000\n"
810"PO-Revision-Date: 2006-09-09 09:09+0900\n"
811"Last-Translator: Joe Translate <joe@example.com>\n"
812"Language-Team: Pig Latin <piglatin@example.com>\n"
813"MIME-Version: 1.0\n"
814"Content-Type: text/plain; charset=UTF-8\n"
815"Content-Transfer-Encoding: 8bit\n"
816"Plural-Forms: nplurals=2; plural=(n != 1);\n"
817"X-Accelerator-Marker: &\n"
818"X-Generator: Translate Toolkit 0.10rc2\n"
819"X-Merge-On: location\n"
820
821#: new_disassociated_mozilla_accesskey
822msgid "R"
823msgstr ""
824"""
825        newpo = self.convertpot(potsource, posource)
826        print("Output:\n%s" % newpo)
827        print("Expected:\n%s" % expected)
828        assert bytes(newpo).decode("utf-8") == expected
829
830
831class TestPOT2POCommand(test_convert.TestConvertCommand, TestPOT2PO):
832    """Tests running actual pot2po commands on files"""
833
834    convertmodule = pot2po
835
836    def test_help(self, capsys):
837        """tests getting help"""
838        options = super().test_help(capsys)
839        options = self.help_check(options, "-t TEMPLATE, --template=TEMPLATE")
840        options = self.help_check(options, "-P, --pot")
841        options = self.help_check(options, "--tm")
842        options = self.help_check(
843            options, "-s MIN_SIMILARITY, --similarity=MIN_SIMILARITY"
844        )
845        options = self.help_check(options, "--nofuzzymatching", last=True)
846