Home | History | Annotate | Download | only in test
      1 import os
      2 import base64
      3 import shutil
      4 import gettext
      5 import unittest
      6 
      7 from test import test_support
      8 
      9 
     10 # TODO:
     11 #  - Add new tests, for example for "dgettext"
     12 #  - Remove dummy tests, for example testing for single and double quotes
     13 #    has no sense, it would have if we were testing a parser (i.e. pygettext)
     14 #  - Tests should have only one assert.
     15 
     16 GNU_MO_DATA = '''\
     17 3hIElQAAAAAGAAAAHAAAAEwAAAALAAAAfAAAAAAAAACoAAAAFQAAAKkAAAAjAAAAvwAAAKEAAADj
     18 AAAABwAAAIUBAAALAAAAjQEAAEUBAACZAQAAFgAAAN8CAAAeAAAA9gIAAKEAAAAVAwAABQAAALcD
     19 AAAJAAAAvQMAAAEAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABQAAAAYAAAACAAAAAFJh
     20 eW1vbmQgTHV4dXJ5IFlhY2gtdABUaGVyZSBpcyAlcyBmaWxlAFRoZXJlIGFyZSAlcyBmaWxlcwBU
     21 aGlzIG1vZHVsZSBwcm92aWRlcyBpbnRlcm5hdGlvbmFsaXphdGlvbiBhbmQgbG9jYWxpemF0aW9u
     22 CnN1cHBvcnQgZm9yIHlvdXIgUHl0aG9uIHByb2dyYW1zIGJ5IHByb3ZpZGluZyBhbiBpbnRlcmZh
     23 Y2UgdG8gdGhlIEdOVQpnZXR0ZXh0IG1lc3NhZ2UgY2F0YWxvZyBsaWJyYXJ5LgBtdWxsdXNrAG51
     24 ZGdlIG51ZGdlAFByb2plY3QtSWQtVmVyc2lvbjogMi4wClBPLVJldmlzaW9uLURhdGU6IDIwMDAt
     25 MDgtMjkgMTI6MTktMDQ6MDAKTGFzdC1UcmFuc2xhdG9yOiBKLiBEYXZpZCBJYsOhw7FleiA8ai1k
     26 YXZpZEBub29zLmZyPgpMYW5ndWFnZS1UZWFtOiBYWCA8cHl0aG9uLWRldkBweXRob24ub3JnPgpN
     27 SU1FLVZlcnNpb246IDEuMApDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9aXNvLTg4
     28 NTktMQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBub25lCkdlbmVyYXRlZC1CeTogcHlnZXR0
     29 ZXh0LnB5IDEuMQpQbHVyYWwtRm9ybXM6IG5wbHVyYWxzPTI7IHBsdXJhbD1uIT0xOwoAVGhyb2F0
     30 d29iYmxlciBNYW5ncm92ZQBIYXkgJXMgZmljaGVybwBIYXkgJXMgZmljaGVyb3MAR3V2ZiB6YnFo
     31 eXIgY2ViaXZxcmYgdmFncmVhbmd2YmFueXZtbmd2YmEgbmFxIHlicG55dm1uZ3ZiYQpmaGNjYmVn
     32 IHNiZSBsYmhlIENsZ3ViYSBjZWJ0ZW56ZiBvbCBjZWJpdnF2YXQgbmEgdmFncmVzbnByIGdiIGd1
     33 ciBUQUgKdHJnZ3JrZyB6cmZmbnRyIHBuZ255YnQgeXZvZW5lbC4AYmFjb24Ad2luayB3aW5rAA==
     34 '''
     35 
     36 UMO_DATA = '''\
     37 3hIElQAAAAACAAAAHAAAACwAAAAFAAAAPAAAAAAAAABQAAAABAAAAFEAAAAPAQAAVgAAAAQAAABm
     38 AQAAAQAAAAIAAAAAAAAAAAAAAAAAAAAAYWLDngBQcm9qZWN0LUlkLVZlcnNpb246IDIuMApQTy1S
     39 ZXZpc2lvbi1EYXRlOiAyMDAzLTA0LTExIDEyOjQyLTA0MDAKTGFzdC1UcmFuc2xhdG9yOiBCYXJy
     40 eSBBLiBXQXJzYXcgPGJhcnJ5QHB5dGhvbi5vcmc+Ckxhbmd1YWdlLVRlYW06IFhYIDxweXRob24t
     41 ZGV2QHB5dGhvbi5vcmc+Ck1JTUUtVmVyc2lvbjogMS4wCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFp
     42 bjsgY2hhcnNldD11dGYtOApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiA3Yml0CkdlbmVyYXRl
     43 ZC1CeTogbWFudWFsbHkKAMKkeXoA
     44 '''
     45 
     46 MMO_DATA = '''\
     47 3hIElQAAAAABAAAAHAAAACQAAAADAAAALAAAAAAAAAA4AAAAeAEAADkAAAABAAAAAAAAAAAAAAAA
     48 UHJvamVjdC1JZC1WZXJzaW9uOiBObyBQcm9qZWN0IDAuMApQT1QtQ3JlYXRpb24tRGF0ZTogV2Vk
     49 IERlYyAxMSAwNzo0NDoxNSAyMDAyClBPLVJldmlzaW9uLURhdGU6IDIwMDItMDgtMTQgMDE6MTg6
     50 NTgrMDA6MDAKTGFzdC1UcmFuc2xhdG9yOiBKb2huIERvZSA8amRvZUBleGFtcGxlLmNvbT4KSmFu
     51 ZSBGb29iYXIgPGpmb29iYXJAZXhhbXBsZS5jb20+Ckxhbmd1YWdlLVRlYW06IHh4IDx4eEBleGFt
     52 cGxlLmNvbT4KTUlNRS1WZXJzaW9uOiAxLjAKQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFy
     53 c2V0PWlzby04ODU5LTE1CkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IHF1b3RlZC1wcmludGFi
     54 bGUKR2VuZXJhdGVkLUJ5OiBweWdldHRleHQucHkgMS4zCgA=
     55 '''
     56 
     57 LOCALEDIR = os.path.join('xx', 'LC_MESSAGES')
     58 MOFILE = os.path.join(LOCALEDIR, 'gettext.mo')
     59 UMOFILE = os.path.join(LOCALEDIR, 'ugettext.mo')
     60 MMOFILE = os.path.join(LOCALEDIR, 'metadata.mo')
     61 
     62 
     63 class GettextBaseTest(unittest.TestCase):
     64     def setUp(self):
     65         if not os.path.isdir(LOCALEDIR):
     66             os.makedirs(LOCALEDIR)
     67         with open(MOFILE, 'wb') as fp:
     68             fp.write(base64.decodestring(GNU_MO_DATA))
     69         with open(UMOFILE, 'wb') as fp:
     70             fp.write(base64.decodestring(UMO_DATA))
     71         with open(MMOFILE, 'wb') as fp:
     72             fp.write(base64.decodestring(MMO_DATA))
     73 
     74         self.env = test_support.EnvironmentVarGuard()
     75         self.env['LANGUAGE'] = 'xx'
     76         gettext._translations.clear()
     77 
     78     def tearDown(self):
     79         self.env.__exit__()
     80         del self.env
     81         shutil.rmtree(os.path.split(LOCALEDIR)[0])
     82 
     83 GNU_MO_DATA_ISSUE_17898 = b'''\
     84 3hIElQAAAAABAAAAHAAAACQAAAAAAAAAAAAAAAAAAAAsAAAAggAAAC0AAAAAUGx1cmFsLUZvcm1z
     85 OiBucGx1cmFscz0yOyBwbHVyYWw9KG4gIT0gMSk7CiMtIy0jLSMtIyAgbWVzc2FnZXMucG8gKEVk
     86 WCBTdHVkaW8pICAjLSMtIy0jLSMKQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PVVU
     87 Ri04CgA=
     88 '''
     89 
     90 class GettextTestCase1(GettextBaseTest):
     91     def setUp(self):
     92         GettextBaseTest.setUp(self)
     93         self.localedir = os.curdir
     94         self.mofile = MOFILE
     95         gettext.install('gettext', self.localedir)
     96 
     97     def test_some_translations(self):
     98         eq = self.assertEqual
     99         # test some translations
    100         eq(_('albatross'), 'albatross')
    101         eq(_(u'mullusk'), 'bacon')
    102         eq(_(r'Raymond Luxury Yach-t'), 'Throatwobbler Mangrove')
    103         eq(_(ur'nudge nudge'), 'wink wink')
    104 
    105     def test_double_quotes(self):
    106         eq = self.assertEqual
    107         # double quotes
    108         eq(_("albatross"), 'albatross')
    109         eq(_(u"mullusk"), 'bacon')
    110         eq(_(r"Raymond Luxury Yach-t"), 'Throatwobbler Mangrove')
    111         eq(_(ur"nudge nudge"), 'wink wink')
    112 
    113     def test_triple_single_quotes(self):
    114         eq = self.assertEqual
    115         # triple single quotes
    116         eq(_('''albatross'''), 'albatross')
    117         eq(_(u'''mullusk'''), 'bacon')
    118         eq(_(r'''Raymond Luxury Yach-t'''), 'Throatwobbler Mangrove')
    119         eq(_(ur'''nudge nudge'''), 'wink wink')
    120 
    121     def test_triple_double_quotes(self):
    122         eq = self.assertEqual
    123         # triple double quotes
    124         eq(_("""albatross"""), 'albatross')
    125         eq(_(u"""mullusk"""), 'bacon')
    126         eq(_(r"""Raymond Luxury Yach-t"""), 'Throatwobbler Mangrove')
    127         eq(_(ur"""nudge nudge"""), 'wink wink')
    128 
    129     def test_multiline_strings(self):
    130         eq = self.assertEqual
    131         # multiline strings
    132         eq(_('''This module provides internationalization and localization
    133 support for your Python programs by providing an interface to the GNU
    134 gettext message catalog library.'''),
    135            '''Guvf zbqhyr cebivqrf vagreangvbanyvmngvba naq ybpnyvmngvba
    136 fhccbeg sbe lbhe Clguba cebtenzf ol cebivqvat na vagresnpr gb gur TAH
    137 trggrkg zrffntr pngnybt yvoenel.''')
    138 
    139     def test_the_alternative_interface(self):
    140         eq = self.assertEqual
    141         # test the alternative interface
    142         with open(self.mofile, 'rb') as fp:
    143             t = gettext.GNUTranslations(fp)
    144         # Install the translation object
    145         t.install()
    146         eq(_('nudge nudge'), 'wink wink')
    147         # Try unicode return type
    148         t.install(unicode=True)
    149         eq(_('mullusk'), 'bacon')
    150         # Test installation of other methods
    151         import __builtin__
    152         t.install(unicode=True, names=["gettext", "lgettext"])
    153         eq(_, t.ugettext)
    154         eq(__builtin__.gettext, t.ugettext)
    155         eq(lgettext, t.lgettext)
    156         del __builtin__.gettext
    157         del __builtin__.lgettext
    158 
    159 
    160 class GettextTestCase2(GettextBaseTest):
    161     def setUp(self):
    162         GettextBaseTest.setUp(self)
    163         self.localedir = os.curdir
    164         # Set up the bindings
    165         gettext.bindtextdomain('gettext', self.localedir)
    166         gettext.textdomain('gettext')
    167         # For convenience
    168         self._ = gettext.gettext
    169 
    170     def test_bindtextdomain(self):
    171         self.assertEqual(gettext.bindtextdomain('gettext'), self.localedir)
    172 
    173     def test_textdomain(self):
    174         self.assertEqual(gettext.textdomain(), 'gettext')
    175 
    176     def test_some_translations(self):
    177         eq = self.assertEqual
    178         # test some translations
    179         eq(self._('albatross'), 'albatross')
    180         eq(self._(u'mullusk'), 'bacon')
    181         eq(self._(r'Raymond Luxury Yach-t'), 'Throatwobbler Mangrove')
    182         eq(self._(ur'nudge nudge'), 'wink wink')
    183 
    184     def test_double_quotes(self):
    185         eq = self.assertEqual
    186         # double quotes
    187         eq(self._("albatross"), 'albatross')
    188         eq(self._(u"mullusk"), 'bacon')
    189         eq(self._(r"Raymond Luxury Yach-t"), 'Throatwobbler Mangrove')
    190         eq(self._(ur"nudge nudge"), 'wink wink')
    191 
    192     def test_triple_single_quotes(self):
    193         eq = self.assertEqual
    194         # triple single quotes
    195         eq(self._('''albatross'''), 'albatross')
    196         eq(self._(u'''mullusk'''), 'bacon')
    197         eq(self._(r'''Raymond Luxury Yach-t'''), 'Throatwobbler Mangrove')
    198         eq(self._(ur'''nudge nudge'''), 'wink wink')
    199 
    200     def test_triple_double_quotes(self):
    201         eq = self.assertEqual
    202         # triple double quotes
    203         eq(self._("""albatross"""), 'albatross')
    204         eq(self._(u"""mullusk"""), 'bacon')
    205         eq(self._(r"""Raymond Luxury Yach-t"""), 'Throatwobbler Mangrove')
    206         eq(self._(ur"""nudge nudge"""), 'wink wink')
    207 
    208     def test_multiline_strings(self):
    209         eq = self.assertEqual
    210         # multiline strings
    211         eq(self._('''This module provides internationalization and localization
    212 support for your Python programs by providing an interface to the GNU
    213 gettext message catalog library.'''),
    214            '''Guvf zbqhyr cebivqrf vagreangvbanyvmngvba naq ybpnyvmngvba
    215 fhccbeg sbe lbhe Clguba cebtenzf ol cebivqvat na vagresnpr gb gur TAH
    216 trggrkg zrffntr pngnybt yvoenel.''')
    217 
    218 
    219 class PluralFormsTestCase(GettextBaseTest):
    220     def setUp(self):
    221         GettextBaseTest.setUp(self)
    222         self.mofile = MOFILE
    223 
    224     def test_plural_forms1(self):
    225         eq = self.assertEqual
    226         x = gettext.ngettext('There is %s file', 'There are %s files', 1)
    227         eq(x, 'Hay %s fichero')
    228         x = gettext.ngettext('There is %s file', 'There are %s files', 2)
    229         eq(x, 'Hay %s ficheros')
    230 
    231     def test_plural_forms2(self):
    232         eq = self.assertEqual
    233         with open(self.mofile, 'rb') as fp:
    234             t = gettext.GNUTranslations(fp)
    235         x = t.ngettext('There is %s file', 'There are %s files', 1)
    236         eq(x, 'Hay %s fichero')
    237         x = t.ngettext('There is %s file', 'There are %s files', 2)
    238         eq(x, 'Hay %s ficheros')
    239 
    240     # Examples from http://www.gnu.org/software/gettext/manual/gettext.html
    241 
    242     def test_ja(self):
    243         eq = self.assertEqual
    244         f = gettext.c2py('0')
    245         s = ''.join([ str(f(x)) for x in range(200) ])
    246         eq(s, "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
    247 
    248     def test_de(self):
    249         eq = self.assertEqual
    250         f = gettext.c2py('n != 1')
    251         s = ''.join([ str(f(x)) for x in range(200) ])
    252         eq(s, "10111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")
    253 
    254     def test_fr(self):
    255         eq = self.assertEqual
    256         f = gettext.c2py('n>1')
    257         s = ''.join([ str(f(x)) for x in range(200) ])
    258         eq(s, "00111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")
    259 
    260     def test_lv(self):
    261         eq = self.assertEqual
    262         f = gettext.c2py('n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2')
    263         s = ''.join([ str(f(x)) for x in range(200) ])
    264         eq(s, "20111111111111111111101111111110111111111011111111101111111110111111111011111111101111111110111111111011111111111111111110111111111011111111101111111110111111111011111111101111111110111111111011111111")
    265 
    266     def test_gd(self):
    267         eq = self.assertEqual
    268         f = gettext.c2py('n==1 ? 0 : n==2 ? 1 : 2')
    269         s = ''.join([ str(f(x)) for x in range(200) ])
    270         eq(s, "20122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222")
    271 
    272     def test_gd2(self):
    273         eq = self.assertEqual
    274         # Tests the combination of parentheses and "?:"
    275         f = gettext.c2py('n==1 ? 0 : (n==2 ? 1 : 2)')
    276         s = ''.join([ str(f(x)) for x in range(200) ])
    277         eq(s, "20122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222")
    278 
    279     def test_ro(self):
    280         eq = self.assertEqual
    281         f = gettext.c2py('n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2')
    282         s = ''.join([ str(f(x)) for x in range(200) ])
    283         eq(s, "10111111111111111111222222222222222222222222222222222222222222222222222222222222222222222222222222222111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222")
    284 
    285     def test_lt(self):
    286         eq = self.assertEqual
    287         f = gettext.c2py('n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2')
    288         s = ''.join([ str(f(x)) for x in range(200) ])
    289         eq(s, "20111111112222222222201111111120111111112011111111201111111120111111112011111111201111111120111111112011111111222222222220111111112011111111201111111120111111112011111111201111111120111111112011111111")
    290 
    291     def test_ru(self):
    292         eq = self.assertEqual
    293         f = gettext.c2py('n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2')
    294         s = ''.join([ str(f(x)) for x in range(200) ])
    295         eq(s, "20111222222222222222201112222220111222222011122222201112222220111222222011122222201112222220111222222011122222222222222220111222222011122222201112222220111222222011122222201112222220111222222011122222")
    296 
    297     def test_cs(self):
    298         eq = self.assertEqual
    299         f = gettext.c2py('(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2')
    300         s = ''.join([ str(f(x)) for x in range(200) ])
    301         eq(s, "20111222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222")
    302 
    303     def test_pl(self):
    304         eq = self.assertEqual
    305         f = gettext.c2py('n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2')
    306         s = ''.join([ str(f(x)) for x in range(200) ])
    307         eq(s, "20111222222222222222221112222222111222222211122222221112222222111222222211122222221112222222111222222211122222222222222222111222222211122222221112222222111222222211122222221112222222111222222211122222")
    308 
    309     def test_sl(self):
    310         eq = self.assertEqual
    311         f = gettext.c2py('n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3')
    312         s = ''.join([ str(f(x)) for x in range(200) ])
    313         eq(s, "30122333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333012233333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333")
    314 
    315     def test_ar(self):
    316         eq = self.assertEqual
    317         f = gettext.c2py('n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5')
    318         s = ''.join([ str(f(x)) for x in range(200) ])
    319         eq(s, "01233333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444445553333333344444444444444444444444444444444444444444444444444444444444444444444444444444444444444444")
    320 
    321     def test_security(self):
    322         raises = self.assertRaises
    323         # Test for a dangerous expression
    324         raises(ValueError, gettext.c2py, "os.chmod('/etc/passwd',0777)")
    325         # issue28563
    326         raises(ValueError, gettext.c2py, '"(eval(foo) && ""')
    327         raises(ValueError, gettext.c2py, 'f"{os.system(\'sh\')}"')
    328         # Maximum recursion depth exceeded during compilation
    329         raises(ValueError, gettext.c2py, 'n+'*10000 + 'n')
    330         self.assertEqual(gettext.c2py('n+'*100 + 'n')(1), 101)
    331         # MemoryError during compilation
    332         raises(ValueError, gettext.c2py, '('*100 + 'n' + ')'*100)
    333         # Maximum recursion depth exceeded in C to Python translator
    334         raises(ValueError, gettext.c2py, '('*10000 + 'n' + ')'*10000)
    335         self.assertEqual(gettext.c2py('('*20 + 'n' + ')'*20)(1), 1)
    336 
    337     def test_chained_comparison(self):
    338         # C doesn't chain comparison as Python so 2 == 2 == 2 gets different results
    339         f = gettext.c2py('n == n == n')
    340         self.assertEqual(''.join(str(f(x)) for x in range(3)), '010')
    341         f = gettext.c2py('1 < n == n')
    342         self.assertEqual(''.join(str(f(x)) for x in range(3)), '100')
    343         f = gettext.c2py('n == n < 2')
    344         self.assertEqual(''.join(str(f(x)) for x in range(3)), '010')
    345         f = gettext.c2py('0 < n < 2')
    346         self.assertEqual(''.join(str(f(x)) for x in range(3)), '111')
    347 
    348     def test_decimal_number(self):
    349         self.assertEqual(gettext.c2py('0123')(1), 123)
    350 
    351     def test_invalid_syntax(self):
    352         invalid_expressions = [
    353             'x>1', '(n>1', 'n>1)', '42**42**42', '0xa', '1.0', '1e2',
    354             'n>0x1', '+n', '-n', 'n()', 'n(1)', '1+', 'nn', 'n n',
    355         ]
    356         for expr in invalid_expressions:
    357             with self.assertRaises(ValueError):
    358                 gettext.c2py(expr)
    359 
    360     def test_nested_condition_operator(self):
    361         self.assertEqual(gettext.c2py('n?1?2:3:4')(0), 4)
    362         self.assertEqual(gettext.c2py('n?1?2:3:4')(1), 2)
    363         self.assertEqual(gettext.c2py('n?1:3?4:5')(0), 4)
    364         self.assertEqual(gettext.c2py('n?1:3?4:5')(1), 1)
    365 
    366     def test_division(self):
    367         f = gettext.c2py('2/n*3')
    368         self.assertEqual(f(1), 6)
    369         self.assertEqual(f(2), 3)
    370         self.assertEqual(f(3), 0)
    371         self.assertEqual(f(-1), -6)
    372         self.assertRaises(ZeroDivisionError, f, 0)
    373 
    374     def test_plural_number(self):
    375         f = gettext.c2py('n != 1')
    376         self.assertEqual(f(1), 0)
    377         self.assertEqual(f(2), 1)
    378         self.assertEqual(f(1.0), 0)
    379         self.assertEqual(f(2.0), 1)
    380         self.assertEqual(f(1.1), 1)
    381         self.assertRaises(TypeError, f, '2')
    382         self.assertRaises(TypeError, f, b'2')
    383         self.assertRaises(TypeError, f, [])
    384         self.assertRaises(TypeError, f, object())
    385 
    386 
    387 class GNUTranslationParsingTest(GettextBaseTest):
    388     def test_plural_form_error_issue17898(self):
    389         with open(MOFILE, 'wb') as fp:
    390             fp.write(base64.decodestring(GNU_MO_DATA_ISSUE_17898))
    391         with open(MOFILE, 'rb') as fp:
    392             # If this runs cleanly, the bug is fixed.
    393             t = gettext.GNUTranslations(fp)
    394 
    395 
    396 class UnicodeTranslationsTest(GettextBaseTest):
    397     def setUp(self):
    398         GettextBaseTest.setUp(self)
    399         with open(UMOFILE, 'rb') as fp:
    400             self.t = gettext.GNUTranslations(fp)
    401         self._ = self.t.ugettext
    402 
    403     def test_unicode_msgid(self):
    404         unless = self.assertTrue
    405         unless(isinstance(self._(''), unicode))
    406         unless(isinstance(self._(u''), unicode))
    407 
    408     def test_unicode_msgstr(self):
    409         eq = self.assertEqual
    410         eq(self._(u'ab\xde'), u'\xa4yz')
    411 
    412 
    413 class WeirdMetadataTest(GettextBaseTest):
    414     def setUp(self):
    415         GettextBaseTest.setUp(self)
    416         with open(MMOFILE, 'rb') as fp:
    417             try:
    418                 self.t = gettext.GNUTranslations(fp)
    419             except:
    420                 self.tearDown()
    421                 raise
    422 
    423     def test_weird_metadata(self):
    424         info = self.t.info()
    425         self.assertEqual(info['last-translator'],
    426            'John Doe <jdoe (at] example.com>\nJane Foobar <jfoobar (at] example.com>')
    427 
    428 
    429 class DummyGNUTranslations(gettext.GNUTranslations):
    430     def foo(self):
    431         return 'foo'
    432 
    433 
    434 class GettextCacheTestCase(GettextBaseTest):
    435     def test_cache(self):
    436         self.localedir = os.curdir
    437         self.mofile = MOFILE
    438 
    439         self.assertEqual(len(gettext._translations), 0)
    440 
    441         t = gettext.translation('gettext', self.localedir)
    442 
    443         self.assertEqual(len(gettext._translations), 1)
    444 
    445         t = gettext.translation('gettext', self.localedir,
    446                                 class_=DummyGNUTranslations)
    447 
    448         self.assertEqual(len(gettext._translations), 2)
    449         self.assertEqual(t.__class__, DummyGNUTranslations)
    450 
    451         # Calling it again doesn't add to the cache
    452 
    453         t = gettext.translation('gettext', self.localedir,
    454                                 class_=DummyGNUTranslations)
    455 
    456         self.assertEqual(len(gettext._translations), 2)
    457         self.assertEqual(t.__class__, DummyGNUTranslations)
    458 
    459 
    460 def test_main():
    461     test_support.run_unittest(__name__)
    462 
    463 if __name__ == '__main__':
    464     test_main()
    465 
    466 
    467 # For reference, here's the .po file used to created the GNU_MO_DATA above.
    468 #
    469 # The original version was automatically generated from the sources with
    470 # pygettext. Later it was manually modified to add plural forms support.
    471 
    472 '''
    473 # Dummy translation for the Python test_gettext.py module.
    474 # Copyright (C) 2001 Python Software Foundation
    475 # Barry Warsaw <barry (at] python.org>, 2000.
    476 #
    477 msgid ""
    478 msgstr ""
    479 "Project-Id-Version: 2.0\n"
    480 "PO-Revision-Date: 2003-04-11 14:32-0400\n"
    481 "Last-Translator: J. David Ibanez <j-david (at] noos.fr>\n"
    482 "Language-Team: XX <python-dev (at] python.org>\n"
    483 "MIME-Version: 1.0\n"
    484 "Content-Type: text/plain; charset=iso-8859-1\n"
    485 "Content-Transfer-Encoding: 8bit\n"
    486 "Generated-By: pygettext.py 1.1\n"
    487 "Plural-Forms: nplurals=2; plural=n!=1;\n"
    488 
    489 #: test_gettext.py:19 test_gettext.py:25 test_gettext.py:31 test_gettext.py:37
    490 #: test_gettext.py:51 test_gettext.py:80 test_gettext.py:86 test_gettext.py:92
    491 #: test_gettext.py:98
    492 msgid "nudge nudge"
    493 msgstr "wink wink"
    494 
    495 #: test_gettext.py:16 test_gettext.py:22 test_gettext.py:28 test_gettext.py:34
    496 #: test_gettext.py:77 test_gettext.py:83 test_gettext.py:89 test_gettext.py:95
    497 msgid "albatross"
    498 msgstr ""
    499 
    500 #: test_gettext.py:18 test_gettext.py:24 test_gettext.py:30 test_gettext.py:36
    501 #: test_gettext.py:79 test_gettext.py:85 test_gettext.py:91 test_gettext.py:97
    502 msgid "Raymond Luxury Yach-t"
    503 msgstr "Throatwobbler Mangrove"
    504 
    505 #: test_gettext.py:17 test_gettext.py:23 test_gettext.py:29 test_gettext.py:35
    506 #: test_gettext.py:56 test_gettext.py:78 test_gettext.py:84 test_gettext.py:90
    507 #: test_gettext.py:96
    508 msgid "mullusk"
    509 msgstr "bacon"
    510 
    511 #: test_gettext.py:40 test_gettext.py:101
    512 msgid ""
    513 "This module provides internationalization and localization\n"
    514 "support for your Python programs by providing an interface to the GNU\n"
    515 "gettext message catalog library."
    516 msgstr ""
    517 "Guvf zbqhyr cebivqrf vagreangvbanyvmngvba naq ybpnyvmngvba\n"
    518 "fhccbeg sbe lbhe Clguba cebtenzf ol cebivqvat na vagresnpr gb gur TAH\n"
    519 "trggrkg zrffntr pngnybt yvoenel."
    520 
    521 # Manually added, as neither pygettext nor xgettext support plural forms
    522 # in Python.
    523 msgid "There is %s file"
    524 msgid_plural "There are %s files"
    525 msgstr[0] "Hay %s fichero"
    526 msgstr[1] "Hay %s ficheros"
    527 '''
    528 
    529 # Here's the second example po file example, used to generate the UMO_DATA
    530 # containing utf-8 encoded Unicode strings
    531 
    532 '''
    533 # Dummy translation for the Python test_gettext.py module.
    534 # Copyright (C) 2001 Python Software Foundation
    535 # Barry Warsaw <barry (at] python.org>, 2000.
    536 #
    537 msgid ""
    538 msgstr ""
    539 "Project-Id-Version: 2.0\n"
    540 "PO-Revision-Date: 2003-04-11 12:42-0400\n"
    541 "Last-Translator: Barry A. WArsaw <barry (at] python.org>\n"
    542 "Language-Team: XX <python-dev (at] python.org>\n"
    543 "MIME-Version: 1.0\n"
    544 "Content-Type: text/plain; charset=utf-8\n"
    545 "Content-Transfer-Encoding: 7bit\n"
    546 "Generated-By: manually\n"
    547 
    548 #: nofile:0
    549 msgid "ab\xc3\x9e"
    550 msgstr "\xc2\xa4yz"
    551 '''
    552 
    553 # Here's the third example po file, used to generate MMO_DATA
    554 
    555 '''
    556 msgid ""
    557 msgstr ""
    558 "Project-Id-Version: No Project 0.0\n"
    559 "POT-Creation-Date: Wed Dec 11 07:44:15 2002\n"
    560 "PO-Revision-Date: 2002-08-14 01:18:58+00:00\n"
    561 "Last-Translator: John Doe <jdoe (at] example.com>\n"
    562 "Jane Foobar <jfoobar (at] example.com>\n"
    563 "Language-Team: xx <xx (at] example.com>\n"
    564 "MIME-Version: 1.0\n"
    565 "Content-Type: text/plain; charset=iso-8859-15\n"
    566 "Content-Transfer-Encoding: quoted-printable\n"
    567 "Generated-By: pygettext.py 1.3\n"
    568 '''
    569 
    570 #
    571 # messages.po, used for bug 17898
    572 #
    573 
    574 '''
    575 # test file for http://bugs.python.org/issue17898
    576 msgid ""
    577 msgstr ""
    578 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
    579 "#-#-#-#-#  messages.po (EdX Studio)  #-#-#-#-#\n"
    580 "Content-Type: text/plain; charset=UTF-8\n"
    581 '''
    582