Home | History | Annotate | Download | only in test
      1 import os
      2 import base64
      3 import gettext
      4 import locale
      5 import unittest
      6 
      7 from test import 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 = b'''\
     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 # This data contains an invalid major version number (5)
     37 # An unexpected major version number should be treated as an error when
     38 # parsing a .mo file
     39 
     40 GNU_MO_DATA_BAD_MAJOR_VERSION = b'''\
     41 3hIElQAABQAGAAAAHAAAAEwAAAALAAAAfAAAAAAAAACoAAAAFQAAAKkAAAAjAAAAvwAAAKEAAADj
     42 AAAABwAAAIUBAAALAAAAjQEAAEUBAACZAQAAFgAAAN8CAAAeAAAA9gIAAKEAAAAVAwAABQAAALcD
     43 AAAJAAAAvQMAAAEAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABQAAAAYAAAACAAAAAFJh
     44 eW1vbmQgTHV4dXJ5IFlhY2gtdABUaGVyZSBpcyAlcyBmaWxlAFRoZXJlIGFyZSAlcyBmaWxlcwBU
     45 aGlzIG1vZHVsZSBwcm92aWRlcyBpbnRlcm5hdGlvbmFsaXphdGlvbiBhbmQgbG9jYWxpemF0aW9u
     46 CnN1cHBvcnQgZm9yIHlvdXIgUHl0aG9uIHByb2dyYW1zIGJ5IHByb3ZpZGluZyBhbiBpbnRlcmZh
     47 Y2UgdG8gdGhlIEdOVQpnZXR0ZXh0IG1lc3NhZ2UgY2F0YWxvZyBsaWJyYXJ5LgBtdWxsdXNrAG51
     48 ZGdlIG51ZGdlAFByb2plY3QtSWQtVmVyc2lvbjogMi4wClBPLVJldmlzaW9uLURhdGU6IDIwMDAt
     49 MDgtMjkgMTI6MTktMDQ6MDAKTGFzdC1UcmFuc2xhdG9yOiBKLiBEYXZpZCBJYsOhw7FleiA8ai1k
     50 YXZpZEBub29zLmZyPgpMYW5ndWFnZS1UZWFtOiBYWCA8cHl0aG9uLWRldkBweXRob24ub3JnPgpN
     51 SU1FLVZlcnNpb246IDEuMApDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9aXNvLTg4
     52 NTktMQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBub25lCkdlbmVyYXRlZC1CeTogcHlnZXR0
     53 ZXh0LnB5IDEuMQpQbHVyYWwtRm9ybXM6IG5wbHVyYWxzPTI7IHBsdXJhbD1uIT0xOwoAVGhyb2F0
     54 d29iYmxlciBNYW5ncm92ZQBIYXkgJXMgZmljaGVybwBIYXkgJXMgZmljaGVyb3MAR3V2ZiB6YnFo
     55 eXIgY2ViaXZxcmYgdmFncmVhbmd2YmFueXZtbmd2YmEgbmFxIHlicG55dm1uZ3ZiYQpmaGNjYmVn
     56 IHNiZSBsYmhlIENsZ3ViYSBjZWJ0ZW56ZiBvbCBjZWJpdnF2YXQgbmEgdmFncmVzbnByIGdiIGd1
     57 ciBUQUgKdHJnZ3JrZyB6cmZmbnRyIHBuZ255YnQgeXZvZW5lbC4AYmFjb24Ad2luayB3aW5rAA==
     58 '''
     59 
     60 # This data contains an invalid minor version number (7)
     61 # An unexpected minor version number only indicates that some of the file's
     62 # contents may not be able to be read. It does not indicate an error.
     63 
     64 GNU_MO_DATA_BAD_MINOR_VERSION = b'''\
     65 3hIElQcAAAAGAAAAHAAAAEwAAAALAAAAfAAAAAAAAACoAAAAFQAAAKkAAAAjAAAAvwAAAKEAAADj
     66 AAAABwAAAIUBAAALAAAAjQEAAEUBAACZAQAAFgAAAN8CAAAeAAAA9gIAAKEAAAAVAwAABQAAALcD
     67 AAAJAAAAvQMAAAEAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABQAAAAYAAAACAAAAAFJh
     68 eW1vbmQgTHV4dXJ5IFlhY2gtdABUaGVyZSBpcyAlcyBmaWxlAFRoZXJlIGFyZSAlcyBmaWxlcwBU
     69 aGlzIG1vZHVsZSBwcm92aWRlcyBpbnRlcm5hdGlvbmFsaXphdGlvbiBhbmQgbG9jYWxpemF0aW9u
     70 CnN1cHBvcnQgZm9yIHlvdXIgUHl0aG9uIHByb2dyYW1zIGJ5IHByb3ZpZGluZyBhbiBpbnRlcmZh
     71 Y2UgdG8gdGhlIEdOVQpnZXR0ZXh0IG1lc3NhZ2UgY2F0YWxvZyBsaWJyYXJ5LgBtdWxsdXNrAG51
     72 ZGdlIG51ZGdlAFByb2plY3QtSWQtVmVyc2lvbjogMi4wClBPLVJldmlzaW9uLURhdGU6IDIwMDAt
     73 MDgtMjkgMTI6MTktMDQ6MDAKTGFzdC1UcmFuc2xhdG9yOiBKLiBEYXZpZCBJYsOhw7FleiA8ai1k
     74 YXZpZEBub29zLmZyPgpMYW5ndWFnZS1UZWFtOiBYWCA8cHl0aG9uLWRldkBweXRob24ub3JnPgpN
     75 SU1FLVZlcnNpb246IDEuMApDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9aXNvLTg4
     76 NTktMQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBub25lCkdlbmVyYXRlZC1CeTogcHlnZXR0
     77 ZXh0LnB5IDEuMQpQbHVyYWwtRm9ybXM6IG5wbHVyYWxzPTI7IHBsdXJhbD1uIT0xOwoAVGhyb2F0
     78 d29iYmxlciBNYW5ncm92ZQBIYXkgJXMgZmljaGVybwBIYXkgJXMgZmljaGVyb3MAR3V2ZiB6YnFo
     79 eXIgY2ViaXZxcmYgdmFncmVhbmd2YmFueXZtbmd2YmEgbmFxIHlicG55dm1uZ3ZiYQpmaGNjYmVn
     80 IHNiZSBsYmhlIENsZ3ViYSBjZWJ0ZW56ZiBvbCBjZWJpdnF2YXQgbmEgdmFncmVzbnByIGdiIGd1
     81 ciBUQUgKdHJnZ3JrZyB6cmZmbnRyIHBuZ255YnQgeXZvZW5lbC4AYmFjb24Ad2luayB3aW5rAA==
     82 '''
     83 
     84 
     85 UMO_DATA = b'''\
     86 3hIElQAAAAACAAAAHAAAACwAAAAFAAAAPAAAAAAAAABQAAAABAAAAFEAAAAPAQAAVgAAAAQAAABm
     87 AQAAAQAAAAIAAAAAAAAAAAAAAAAAAAAAYWLDngBQcm9qZWN0LUlkLVZlcnNpb246IDIuMApQTy1S
     88 ZXZpc2lvbi1EYXRlOiAyMDAzLTA0LTExIDEyOjQyLTA0MDAKTGFzdC1UcmFuc2xhdG9yOiBCYXJy
     89 eSBBLiBXQXJzYXcgPGJhcnJ5QHB5dGhvbi5vcmc+Ckxhbmd1YWdlLVRlYW06IFhYIDxweXRob24t
     90 ZGV2QHB5dGhvbi5vcmc+Ck1JTUUtVmVyc2lvbjogMS4wCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFp
     91 bjsgY2hhcnNldD11dGYtOApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiA3Yml0CkdlbmVyYXRl
     92 ZC1CeTogbWFudWFsbHkKAMKkeXoA
     93 '''
     94 
     95 MMO_DATA = b'''\
     96 3hIElQAAAAABAAAAHAAAACQAAAADAAAALAAAAAAAAAA4AAAAeAEAADkAAAABAAAAAAAAAAAAAAAA
     97 UHJvamVjdC1JZC1WZXJzaW9uOiBObyBQcm9qZWN0IDAuMApQT1QtQ3JlYXRpb24tRGF0ZTogV2Vk
     98 IERlYyAxMSAwNzo0NDoxNSAyMDAyClBPLVJldmlzaW9uLURhdGU6IDIwMDItMDgtMTQgMDE6MTg6
     99 NTgrMDA6MDAKTGFzdC1UcmFuc2xhdG9yOiBKb2huIERvZSA8amRvZUBleGFtcGxlLmNvbT4KSmFu
    100 ZSBGb29iYXIgPGpmb29iYXJAZXhhbXBsZS5jb20+Ckxhbmd1YWdlLVRlYW06IHh4IDx4eEBleGFt
    101 cGxlLmNvbT4KTUlNRS1WZXJzaW9uOiAxLjAKQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFy
    102 c2V0PWlzby04ODU5LTE1CkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IHF1b3RlZC1wcmludGFi
    103 bGUKR2VuZXJhdGVkLUJ5OiBweWdldHRleHQucHkgMS4zCgA=
    104 '''
    105 
    106 LOCALEDIR = os.path.join('xx', 'LC_MESSAGES')
    107 MOFILE = os.path.join(LOCALEDIR, 'gettext.mo')
    108 MOFILE_BAD_MAJOR_VERSION = os.path.join(LOCALEDIR, 'gettext_bad_major_version.mo')
    109 MOFILE_BAD_MINOR_VERSION = os.path.join(LOCALEDIR, 'gettext_bad_minor_version.mo')
    110 UMOFILE = os.path.join(LOCALEDIR, 'ugettext.mo')
    111 MMOFILE = os.path.join(LOCALEDIR, 'metadata.mo')
    112 
    113 
    114 class GettextBaseTest(unittest.TestCase):
    115     def setUp(self):
    116         if not os.path.isdir(LOCALEDIR):
    117             os.makedirs(LOCALEDIR)
    118         with open(MOFILE, 'wb') as fp:
    119             fp.write(base64.decodebytes(GNU_MO_DATA))
    120         with open(MOFILE_BAD_MAJOR_VERSION, 'wb') as fp:
    121             fp.write(base64.decodebytes(GNU_MO_DATA_BAD_MAJOR_VERSION))
    122         with open(MOFILE_BAD_MINOR_VERSION, 'wb') as fp:
    123             fp.write(base64.decodebytes(GNU_MO_DATA_BAD_MINOR_VERSION))
    124         with open(UMOFILE, 'wb') as fp:
    125             fp.write(base64.decodebytes(UMO_DATA))
    126         with open(MMOFILE, 'wb') as fp:
    127             fp.write(base64.decodebytes(MMO_DATA))
    128         self.env = support.EnvironmentVarGuard()
    129         self.env['LANGUAGE'] = 'xx'
    130         gettext._translations.clear()
    131 
    132     def tearDown(self):
    133         self.env.__exit__()
    134         del self.env
    135         support.rmtree(os.path.split(LOCALEDIR)[0])
    136 
    137 GNU_MO_DATA_ISSUE_17898 = b'''\
    138 3hIElQAAAAABAAAAHAAAACQAAAAAAAAAAAAAAAAAAAAsAAAAggAAAC0AAAAAUGx1cmFsLUZvcm1z
    139 OiBucGx1cmFscz0yOyBwbHVyYWw9KG4gIT0gMSk7CiMtIy0jLSMtIyAgbWVzc2FnZXMucG8gKEVk
    140 WCBTdHVkaW8pICAjLSMtIy0jLSMKQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PVVU
    141 Ri04CgA=
    142 '''
    143 
    144 class GettextTestCase1(GettextBaseTest):
    145     def setUp(self):
    146         GettextBaseTest.setUp(self)
    147         self.localedir = os.curdir
    148         self.mofile = MOFILE
    149         gettext.install('gettext', self.localedir)
    150 
    151     def test_some_translations(self):
    152         eq = self.assertEqual
    153         # test some translations
    154         eq(_('albatross'), 'albatross')
    155         eq(_('mullusk'), 'bacon')
    156         eq(_(r'Raymond Luxury Yach-t'), 'Throatwobbler Mangrove')
    157         eq(_(r'nudge nudge'), 'wink wink')
    158 
    159     def test_double_quotes(self):
    160         eq = self.assertEqual
    161         # double quotes
    162         eq(_("albatross"), 'albatross')
    163         eq(_("mullusk"), 'bacon')
    164         eq(_(r"Raymond Luxury Yach-t"), 'Throatwobbler Mangrove')
    165         eq(_(r"nudge nudge"), 'wink wink')
    166 
    167     def test_triple_single_quotes(self):
    168         eq = self.assertEqual
    169         # triple single quotes
    170         eq(_('''albatross'''), 'albatross')
    171         eq(_('''mullusk'''), 'bacon')
    172         eq(_(r'''Raymond Luxury Yach-t'''), 'Throatwobbler Mangrove')
    173         eq(_(r'''nudge nudge'''), 'wink wink')
    174 
    175     def test_triple_double_quotes(self):
    176         eq = self.assertEqual
    177         # triple double quotes
    178         eq(_("""albatross"""), 'albatross')
    179         eq(_("""mullusk"""), 'bacon')
    180         eq(_(r"""Raymond Luxury Yach-t"""), 'Throatwobbler Mangrove')
    181         eq(_(r"""nudge nudge"""), 'wink wink')
    182 
    183     def test_multiline_strings(self):
    184         eq = self.assertEqual
    185         # multiline strings
    186         eq(_('''This module provides internationalization and localization
    187 support for your Python programs by providing an interface to the GNU
    188 gettext message catalog library.'''),
    189            '''Guvf zbqhyr cebivqrf vagreangvbanyvmngvba naq ybpnyvmngvba
    190 fhccbeg sbe lbhe Clguba cebtenzf ol cebivqvat na vagresnpr gb gur TAH
    191 trggrkg zrffntr pngnybt yvoenel.''')
    192 
    193     def test_the_alternative_interface(self):
    194         eq = self.assertEqual
    195         # test the alternative interface
    196         with open(self.mofile, 'rb') as fp:
    197             t = gettext.GNUTranslations(fp)
    198         # Install the translation object
    199         t.install()
    200         eq(_('nudge nudge'), 'wink wink')
    201         # Try unicode return type
    202         t.install()
    203         eq(_('mullusk'), 'bacon')
    204         # Test installation of other methods
    205         import builtins
    206         t.install(names=["gettext", "lgettext"])
    207         eq(_, t.gettext)
    208         eq(builtins.gettext, t.gettext)
    209         eq(lgettext, t.lgettext)
    210         del builtins.gettext
    211         del builtins.lgettext
    212 
    213 
    214 class GettextTestCase2(GettextBaseTest):
    215     def setUp(self):
    216         GettextBaseTest.setUp(self)
    217         self.localedir = os.curdir
    218         # Set up the bindings
    219         gettext.bindtextdomain('gettext', self.localedir)
    220         gettext.textdomain('gettext')
    221         # For convenience
    222         self._ = gettext.gettext
    223 
    224     def test_bindtextdomain(self):
    225         self.assertEqual(gettext.bindtextdomain('gettext'), self.localedir)
    226 
    227     def test_textdomain(self):
    228         self.assertEqual(gettext.textdomain(), 'gettext')
    229 
    230     def test_bad_major_version(self):
    231         with open(MOFILE_BAD_MAJOR_VERSION, 'rb') as fp:
    232             with self.assertRaises(OSError) as cm:
    233                 gettext.GNUTranslations(fp)
    234 
    235             exception = cm.exception
    236             self.assertEqual(exception.errno, 0)
    237             self.assertEqual(exception.strerror, "Bad version number 5")
    238             self.assertEqual(exception.filename, MOFILE_BAD_MAJOR_VERSION)
    239 
    240     def test_bad_minor_version(self):
    241         with open(MOFILE_BAD_MINOR_VERSION, 'rb') as fp:
    242             # Check that no error is thrown with a bad minor version number
    243             gettext.GNUTranslations(fp)
    244 
    245     def test_some_translations(self):
    246         eq = self.assertEqual
    247         # test some translations
    248         eq(self._('albatross'), 'albatross')
    249         eq(self._('mullusk'), 'bacon')
    250         eq(self._(r'Raymond Luxury Yach-t'), 'Throatwobbler Mangrove')
    251         eq(self._(r'nudge nudge'), 'wink wink')
    252 
    253     def test_double_quotes(self):
    254         eq = self.assertEqual
    255         # double quotes
    256         eq(self._("albatross"), 'albatross')
    257         eq(self._("mullusk"), 'bacon')
    258         eq(self._(r"Raymond Luxury Yach-t"), 'Throatwobbler Mangrove')
    259         eq(self._(r"nudge nudge"), 'wink wink')
    260 
    261     def test_triple_single_quotes(self):
    262         eq = self.assertEqual
    263         # triple single quotes
    264         eq(self._('''albatross'''), 'albatross')
    265         eq(self._('''mullusk'''), 'bacon')
    266         eq(self._(r'''Raymond Luxury Yach-t'''), 'Throatwobbler Mangrove')
    267         eq(self._(r'''nudge nudge'''), 'wink wink')
    268 
    269     def test_triple_double_quotes(self):
    270         eq = self.assertEqual
    271         # triple double quotes
    272         eq(self._("""albatross"""), 'albatross')
    273         eq(self._("""mullusk"""), 'bacon')
    274         eq(self._(r"""Raymond Luxury Yach-t"""), 'Throatwobbler Mangrove')
    275         eq(self._(r"""nudge nudge"""), 'wink wink')
    276 
    277     def test_multiline_strings(self):
    278         eq = self.assertEqual
    279         # multiline strings
    280         eq(self._('''This module provides internationalization and localization
    281 support for your Python programs by providing an interface to the GNU
    282 gettext message catalog library.'''),
    283            '''Guvf zbqhyr cebivqrf vagreangvbanyvmngvba naq ybpnyvmngvba
    284 fhccbeg sbe lbhe Clguba cebtenzf ol cebivqvat na vagresnpr gb gur TAH
    285 trggrkg zrffntr pngnybt yvoenel.''')
    286 
    287 
    288 class PluralFormsTestCase(GettextBaseTest):
    289     def setUp(self):
    290         GettextBaseTest.setUp(self)
    291         self.mofile = MOFILE
    292 
    293     def test_plural_forms1(self):
    294         eq = self.assertEqual
    295         x = gettext.ngettext('There is %s file', 'There are %s files', 1)
    296         eq(x, 'Hay %s fichero')
    297         x = gettext.ngettext('There is %s file', 'There are %s files', 2)
    298         eq(x, 'Hay %s ficheros')
    299 
    300     def test_plural_forms2(self):
    301         eq = self.assertEqual
    302         with open(self.mofile, 'rb') as fp:
    303             t = gettext.GNUTranslations(fp)
    304         x = t.ngettext('There is %s file', 'There are %s files', 1)
    305         eq(x, 'Hay %s fichero')
    306         x = t.ngettext('There is %s file', 'There are %s files', 2)
    307         eq(x, 'Hay %s ficheros')
    308 
    309     # Examples from http://www.gnu.org/software/gettext/manual/gettext.html
    310 
    311     def test_ja(self):
    312         eq = self.assertEqual
    313         f = gettext.c2py('0')
    314         s = ''.join([ str(f(x)) for x in range(200) ])
    315         eq(s, "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
    316 
    317     def test_de(self):
    318         eq = self.assertEqual
    319         f = gettext.c2py('n != 1')
    320         s = ''.join([ str(f(x)) for x in range(200) ])
    321         eq(s, "10111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")
    322 
    323     def test_fr(self):
    324         eq = self.assertEqual
    325         f = gettext.c2py('n>1')
    326         s = ''.join([ str(f(x)) for x in range(200) ])
    327         eq(s, "00111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")
    328 
    329     def test_lv(self):
    330         eq = self.assertEqual
    331         f = gettext.c2py('n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2')
    332         s = ''.join([ str(f(x)) for x in range(200) ])
    333         eq(s, "20111111111111111111101111111110111111111011111111101111111110111111111011111111101111111110111111111011111111111111111110111111111011111111101111111110111111111011111111101111111110111111111011111111")
    334 
    335     def test_gd(self):
    336         eq = self.assertEqual
    337         f = gettext.c2py('n==1 ? 0 : n==2 ? 1 : 2')
    338         s = ''.join([ str(f(x)) for x in range(200) ])
    339         eq(s, "20122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222")
    340 
    341     def test_gd2(self):
    342         eq = self.assertEqual
    343         # Tests the combination of parentheses and "?:"
    344         f = gettext.c2py('n==1 ? 0 : (n==2 ? 1 : 2)')
    345         s = ''.join([ str(f(x)) for x in range(200) ])
    346         eq(s, "20122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222")
    347 
    348     def test_ro(self):
    349         eq = self.assertEqual
    350         f = gettext.c2py('n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2')
    351         s = ''.join([ str(f(x)) for x in range(200) ])
    352         eq(s, "10111111111111111111222222222222222222222222222222222222222222222222222222222222222222222222222222222111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222")
    353 
    354     def test_lt(self):
    355         eq = self.assertEqual
    356         f = gettext.c2py('n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2')
    357         s = ''.join([ str(f(x)) for x in range(200) ])
    358         eq(s, "20111111112222222222201111111120111111112011111111201111111120111111112011111111201111111120111111112011111111222222222220111111112011111111201111111120111111112011111111201111111120111111112011111111")
    359 
    360     def test_ru(self):
    361         eq = self.assertEqual
    362         f = gettext.c2py('n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2')
    363         s = ''.join([ str(f(x)) for x in range(200) ])
    364         eq(s, "20111222222222222222201112222220111222222011122222201112222220111222222011122222201112222220111222222011122222222222222220111222222011122222201112222220111222222011122222201112222220111222222011122222")
    365 
    366     def test_cs(self):
    367         eq = self.assertEqual
    368         f = gettext.c2py('(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2')
    369         s = ''.join([ str(f(x)) for x in range(200) ])
    370         eq(s, "20111222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222")
    371 
    372     def test_pl(self):
    373         eq = self.assertEqual
    374         f = gettext.c2py('n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2')
    375         s = ''.join([ str(f(x)) for x in range(200) ])
    376         eq(s, "20111222222222222222221112222222111222222211122222221112222222111222222211122222221112222222111222222211122222222222222222111222222211122222221112222222111222222211122222221112222222111222222211122222")
    377 
    378     def test_sl(self):
    379         eq = self.assertEqual
    380         f = gettext.c2py('n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3')
    381         s = ''.join([ str(f(x)) for x in range(200) ])
    382         eq(s, "30122333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333012233333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333")
    383 
    384     def test_ar(self):
    385         eq = self.assertEqual
    386         f = gettext.c2py('n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5')
    387         s = ''.join([ str(f(x)) for x in range(200) ])
    388         eq(s, "01233333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444445553333333344444444444444444444444444444444444444444444444444444444444444444444444444444444444444444")
    389 
    390     def test_security(self):
    391         raises = self.assertRaises
    392         # Test for a dangerous expression
    393         raises(ValueError, gettext.c2py, "os.chmod('/etc/passwd',0777)")
    394         # issue28563
    395         raises(ValueError, gettext.c2py, '"(eval(foo) && ""')
    396         raises(ValueError, gettext.c2py, 'f"{os.system(\'sh\')}"')
    397         # Maximum recursion depth exceeded during compilation
    398         raises(ValueError, gettext.c2py, 'n+'*10000 + 'n')
    399         self.assertEqual(gettext.c2py('n+'*100 + 'n')(1), 101)
    400         # MemoryError during compilation
    401         raises(ValueError, gettext.c2py, '('*100 + 'n' + ')'*100)
    402         # Maximum recursion depth exceeded in C to Python translator
    403         raises(ValueError, gettext.c2py, '('*10000 + 'n' + ')'*10000)
    404         self.assertEqual(gettext.c2py('('*20 + 'n' + ')'*20)(1), 1)
    405 
    406     def test_chained_comparison(self):
    407         # C doesn't chain comparison as Python so 2 == 2 == 2 gets different results
    408         f = gettext.c2py('n == n == n')
    409         self.assertEqual(''.join(str(f(x)) for x in range(3)), '010')
    410         f = gettext.c2py('1 < n == n')
    411         self.assertEqual(''.join(str(f(x)) for x in range(3)), '100')
    412         f = gettext.c2py('n == n < 2')
    413         self.assertEqual(''.join(str(f(x)) for x in range(3)), '010')
    414         f = gettext.c2py('0 < n < 2')
    415         self.assertEqual(''.join(str(f(x)) for x in range(3)), '111')
    416 
    417     def test_decimal_number(self):
    418         self.assertEqual(gettext.c2py('0123')(1), 123)
    419 
    420     def test_invalid_syntax(self):
    421         invalid_expressions = [
    422             'x>1', '(n>1', 'n>1)', '42**42**42', '0xa', '1.0', '1e2',
    423             'n>0x1', '+n', '-n', 'n()', 'n(1)', '1+', 'nn', 'n n',
    424         ]
    425         for expr in invalid_expressions:
    426             with self.assertRaises(ValueError):
    427                 gettext.c2py(expr)
    428 
    429     def test_nested_condition_operator(self):
    430         self.assertEqual(gettext.c2py('n?1?2:3:4')(0), 4)
    431         self.assertEqual(gettext.c2py('n?1?2:3:4')(1), 2)
    432         self.assertEqual(gettext.c2py('n?1:3?4:5')(0), 4)
    433         self.assertEqual(gettext.c2py('n?1:3?4:5')(1), 1)
    434 
    435     def test_division(self):
    436         f = gettext.c2py('2/n*3')
    437         self.assertEqual(f(1), 6)
    438         self.assertEqual(f(2), 3)
    439         self.assertEqual(f(3), 0)
    440         self.assertEqual(f(-1), -6)
    441         self.assertRaises(ZeroDivisionError, f, 0)
    442 
    443     def test_plural_number(self):
    444         f = gettext.c2py('n != 1')
    445         self.assertEqual(f(1), 0)
    446         self.assertEqual(f(2), 1)
    447         with self.assertWarns(DeprecationWarning):
    448             self.assertEqual(f(1.0), 0)
    449         with self.assertWarns(DeprecationWarning):
    450             self.assertEqual(f(2.0), 1)
    451         with self.assertWarns(DeprecationWarning):
    452             self.assertEqual(f(1.1), 1)
    453         self.assertRaises(TypeError, f, '2')
    454         self.assertRaises(TypeError, f, b'2')
    455         self.assertRaises(TypeError, f, [])
    456         self.assertRaises(TypeError, f, object())
    457 
    458 
    459 class LGettextTestCase(GettextBaseTest):
    460     def setUp(self):
    461         GettextBaseTest.setUp(self)
    462         self.mofile = MOFILE
    463 
    464     def test_lgettext(self):
    465         lgettext = gettext.lgettext
    466         ldgettext = gettext.ldgettext
    467         self.assertEqual(lgettext('mullusk'), b'bacon')
    468         self.assertEqual(lgettext('spam'), b'spam')
    469         self.assertEqual(ldgettext('gettext', 'mullusk'), b'bacon')
    470         self.assertEqual(ldgettext('gettext', 'spam'), b'spam')
    471 
    472     def test_lgettext_2(self):
    473         with open(self.mofile, 'rb') as fp:
    474             t = gettext.GNUTranslations(fp)
    475         lgettext = t.lgettext
    476         self.assertEqual(lgettext('mullusk'), b'bacon')
    477         self.assertEqual(lgettext('spam'), b'spam')
    478 
    479     def test_lgettext_bind_textdomain_codeset(self):
    480         lgettext = gettext.lgettext
    481         ldgettext = gettext.ldgettext
    482         saved_codeset = gettext.bind_textdomain_codeset('gettext')
    483         try:
    484             gettext.bind_textdomain_codeset('gettext', 'utf-16')
    485             self.assertEqual(lgettext('mullusk'), 'bacon'.encode('utf-16'))
    486             self.assertEqual(lgettext('spam'), 'spam'.encode('utf-16'))
    487             self.assertEqual(ldgettext('gettext', 'mullusk'), 'bacon'.encode('utf-16'))
    488             self.assertEqual(ldgettext('gettext', 'spam'), 'spam'.encode('utf-16'))
    489         finally:
    490             del gettext._localecodesets['gettext']
    491             gettext.bind_textdomain_codeset('gettext', saved_codeset)
    492 
    493     def test_lgettext_output_encoding(self):
    494         with open(self.mofile, 'rb') as fp:
    495             t = gettext.GNUTranslations(fp)
    496         lgettext = t.lgettext
    497         t.set_output_charset('utf-16')
    498         self.assertEqual(lgettext('mullusk'), 'bacon'.encode('utf-16'))
    499         self.assertEqual(lgettext('spam'), 'spam'.encode('utf-16'))
    500 
    501     def test_lngettext(self):
    502         lngettext = gettext.lngettext
    503         ldngettext = gettext.ldngettext
    504         x = lngettext('There is %s file', 'There are %s files', 1)
    505         self.assertEqual(x, b'Hay %s fichero')
    506         x = lngettext('There is %s file', 'There are %s files', 2)
    507         self.assertEqual(x, b'Hay %s ficheros')
    508         x = lngettext('There is %s directory', 'There are %s directories', 1)
    509         self.assertEqual(x, b'There is %s directory')
    510         x = lngettext('There is %s directory', 'There are %s directories', 2)
    511         self.assertEqual(x, b'There are %s directories')
    512         x = ldngettext('gettext', 'There is %s file', 'There are %s files', 1)
    513         self.assertEqual(x, b'Hay %s fichero')
    514         x = ldngettext('gettext', 'There is %s file', 'There are %s files', 2)
    515         self.assertEqual(x, b'Hay %s ficheros')
    516         x = ldngettext('gettext', 'There is %s directory', 'There are %s directories', 1)
    517         self.assertEqual(x, b'There is %s directory')
    518         x = ldngettext('gettext', 'There is %s directory', 'There are %s directories', 2)
    519         self.assertEqual(x, b'There are %s directories')
    520 
    521     def test_lngettext_2(self):
    522         with open(self.mofile, 'rb') as fp:
    523             t = gettext.GNUTranslations(fp)
    524         lngettext = t.lngettext
    525         x = lngettext('There is %s file', 'There are %s files', 1)
    526         self.assertEqual(x, b'Hay %s fichero')
    527         x = lngettext('There is %s file', 'There are %s files', 2)
    528         self.assertEqual(x, b'Hay %s ficheros')
    529         x = lngettext('There is %s directory', 'There are %s directories', 1)
    530         self.assertEqual(x, b'There is %s directory')
    531         x = lngettext('There is %s directory', 'There are %s directories', 2)
    532         self.assertEqual(x, b'There are %s directories')
    533 
    534     def test_lngettext_bind_textdomain_codeset(self):
    535         lngettext = gettext.lngettext
    536         ldngettext = gettext.ldngettext
    537         saved_codeset = gettext.bind_textdomain_codeset('gettext')
    538         try:
    539             gettext.bind_textdomain_codeset('gettext', 'utf-16')
    540             x = lngettext('There is %s file', 'There are %s files', 1)
    541             self.assertEqual(x, 'Hay %s fichero'.encode('utf-16'))
    542             x = lngettext('There is %s file', 'There are %s files', 2)
    543             self.assertEqual(x, 'Hay %s ficheros'.encode('utf-16'))
    544             x = lngettext('There is %s directory', 'There are %s directories', 1)
    545             self.assertEqual(x, 'There is %s directory'.encode('utf-16'))
    546             x = lngettext('There is %s directory', 'There are %s directories', 2)
    547             self.assertEqual(x, 'There are %s directories'.encode('utf-16'))
    548             x = ldngettext('gettext', 'There is %s file', 'There are %s files', 1)
    549             self.assertEqual(x, 'Hay %s fichero'.encode('utf-16'))
    550             x = ldngettext('gettext', 'There is %s file', 'There are %s files', 2)
    551             self.assertEqual(x, 'Hay %s ficheros'.encode('utf-16'))
    552             x = ldngettext('gettext', 'There is %s directory', 'There are %s directories', 1)
    553             self.assertEqual(x, 'There is %s directory'.encode('utf-16'))
    554             x = ldngettext('gettext', 'There is %s directory', 'There are %s directories', 2)
    555             self.assertEqual(x, 'There are %s directories'.encode('utf-16'))
    556         finally:
    557             del gettext._localecodesets['gettext']
    558             gettext.bind_textdomain_codeset('gettext', saved_codeset)
    559 
    560     def test_lngettext_output_encoding(self):
    561         with open(self.mofile, 'rb') as fp:
    562             t = gettext.GNUTranslations(fp)
    563         lngettext = t.lngettext
    564         t.set_output_charset('utf-16')
    565         x = lngettext('There is %s file', 'There are %s files', 1)
    566         self.assertEqual(x, 'Hay %s fichero'.encode('utf-16'))
    567         x = lngettext('There is %s file', 'There are %s files', 2)
    568         self.assertEqual(x, 'Hay %s ficheros'.encode('utf-16'))
    569         x = lngettext('There is %s directory', 'There are %s directories', 1)
    570         self.assertEqual(x, 'There is %s directory'.encode('utf-16'))
    571         x = lngettext('There is %s directory', 'There are %s directories', 2)
    572         self.assertEqual(x, 'There are %s directories'.encode('utf-16'))
    573 
    574 
    575 class GNUTranslationParsingTest(GettextBaseTest):
    576     def test_plural_form_error_issue17898(self):
    577         with open(MOFILE, 'wb') as fp:
    578             fp.write(base64.decodebytes(GNU_MO_DATA_ISSUE_17898))
    579         with open(MOFILE, 'rb') as fp:
    580             # If this runs cleanly, the bug is fixed.
    581             t = gettext.GNUTranslations(fp)
    582 
    583 
    584 class UnicodeTranslationsTest(GettextBaseTest):
    585     def setUp(self):
    586         GettextBaseTest.setUp(self)
    587         with open(UMOFILE, 'rb') as fp:
    588             self.t = gettext.GNUTranslations(fp)
    589         self._ = self.t.gettext
    590 
    591     def test_unicode_msgid(self):
    592         self.assertIsInstance(self._(''), str)
    593 
    594     def test_unicode_msgstr(self):
    595         self.assertEqual(self._('ab\xde'), '\xa4yz')
    596 
    597 
    598 class WeirdMetadataTest(GettextBaseTest):
    599     def setUp(self):
    600         GettextBaseTest.setUp(self)
    601         with open(MMOFILE, 'rb') as fp:
    602             try:
    603                 self.t = gettext.GNUTranslations(fp)
    604             except:
    605                 self.tearDown()
    606                 raise
    607 
    608     def test_weird_metadata(self):
    609         info = self.t.info()
    610         self.assertEqual(len(info), 9)
    611         self.assertEqual(info['last-translator'],
    612            'John Doe <jdoe (at] example.com>\nJane Foobar <jfoobar (at] example.com>')
    613 
    614 
    615 class DummyGNUTranslations(gettext.GNUTranslations):
    616     def foo(self):
    617         return 'foo'
    618 
    619 
    620 class GettextCacheTestCase(GettextBaseTest):
    621     def test_cache(self):
    622         self.localedir = os.curdir
    623         self.mofile = MOFILE
    624 
    625         self.assertEqual(len(gettext._translations), 0)
    626 
    627         t = gettext.translation('gettext', self.localedir)
    628 
    629         self.assertEqual(len(gettext._translations), 1)
    630 
    631         t = gettext.translation('gettext', self.localedir,
    632                                 class_=DummyGNUTranslations)
    633 
    634         self.assertEqual(len(gettext._translations), 2)
    635         self.assertEqual(t.__class__, DummyGNUTranslations)
    636 
    637         # Calling it again doesn't add to the cache
    638 
    639         t = gettext.translation('gettext', self.localedir,
    640                                 class_=DummyGNUTranslations)
    641 
    642         self.assertEqual(len(gettext._translations), 2)
    643         self.assertEqual(t.__class__, DummyGNUTranslations)
    644 
    645 
    646 class MiscTestCase(unittest.TestCase):
    647     def test__all__(self):
    648         blacklist = {'c2py', 'ENOENT'}
    649         support.check__all__(self, gettext, blacklist=blacklist)
    650 
    651 
    652 def test_main():
    653     support.run_unittest(__name__)
    654 
    655 if __name__ == '__main__':
    656     test_main()
    657 
    658 
    659 # For reference, here's the .po file used to created the GNU_MO_DATA above.
    660 #
    661 # The original version was automatically generated from the sources with
    662 # pygettext. Later it was manually modified to add plural forms support.
    663 
    664 b'''
    665 # Dummy translation for the Python test_gettext.py module.
    666 # Copyright (C) 2001 Python Software Foundation
    667 # Barry Warsaw <barry (at] python.org>, 2000.
    668 #
    669 msgid ""
    670 msgstr ""
    671 "Project-Id-Version: 2.0\n"
    672 "PO-Revision-Date: 2003-04-11 14:32-0400\n"
    673 "Last-Translator: J. David Ibanez <j-david (at] noos.fr>\n"
    674 "Language-Team: XX <python-dev (at] python.org>\n"
    675 "MIME-Version: 1.0\n"
    676 "Content-Type: text/plain; charset=iso-8859-1\n"
    677 "Content-Transfer-Encoding: 8bit\n"
    678 "Generated-By: pygettext.py 1.1\n"
    679 "Plural-Forms: nplurals=2; plural=n!=1;\n"
    680 
    681 #: test_gettext.py:19 test_gettext.py:25 test_gettext.py:31 test_gettext.py:37
    682 #: test_gettext.py:51 test_gettext.py:80 test_gettext.py:86 test_gettext.py:92
    683 #: test_gettext.py:98
    684 msgid "nudge nudge"
    685 msgstr "wink wink"
    686 
    687 #: test_gettext.py:16 test_gettext.py:22 test_gettext.py:28 test_gettext.py:34
    688 #: test_gettext.py:77 test_gettext.py:83 test_gettext.py:89 test_gettext.py:95
    689 msgid "albatross"
    690 msgstr ""
    691 
    692 #: test_gettext.py:18 test_gettext.py:24 test_gettext.py:30 test_gettext.py:36
    693 #: test_gettext.py:79 test_gettext.py:85 test_gettext.py:91 test_gettext.py:97
    694 msgid "Raymond Luxury Yach-t"
    695 msgstr "Throatwobbler Mangrove"
    696 
    697 #: test_gettext.py:17 test_gettext.py:23 test_gettext.py:29 test_gettext.py:35
    698 #: test_gettext.py:56 test_gettext.py:78 test_gettext.py:84 test_gettext.py:90
    699 #: test_gettext.py:96
    700 msgid "mullusk"
    701 msgstr "bacon"
    702 
    703 #: test_gettext.py:40 test_gettext.py:101
    704 msgid ""
    705 "This module provides internationalization and localization\n"
    706 "support for your Python programs by providing an interface to the GNU\n"
    707 "gettext message catalog library."
    708 msgstr ""
    709 "Guvf zbqhyr cebivqrf vagreangvbanyvmngvba naq ybpnyvmngvba\n"
    710 "fhccbeg sbe lbhe Clguba cebtenzf ol cebivqvat na vagresnpr gb gur TAH\n"
    711 "trggrkg zrffntr pngnybt yvoenel."
    712 
    713 # Manually added, as neither pygettext nor xgettext support plural forms
    714 # in Python.
    715 msgid "There is %s file"
    716 msgid_plural "There are %s files"
    717 msgstr[0] "Hay %s fichero"
    718 msgstr[1] "Hay %s ficheros"
    719 '''
    720 
    721 # Here's the second example po file example, used to generate the UMO_DATA
    722 # containing utf-8 encoded Unicode strings
    723 
    724 b'''
    725 # Dummy translation for the Python test_gettext.py module.
    726 # Copyright (C) 2001 Python Software Foundation
    727 # Barry Warsaw <barry (at] python.org>, 2000.
    728 #
    729 msgid ""
    730 msgstr ""
    731 "Project-Id-Version: 2.0\n"
    732 "PO-Revision-Date: 2003-04-11 12:42-0400\n"
    733 "Last-Translator: Barry A. WArsaw <barry (at] python.org>\n"
    734 "Language-Team: XX <python-dev (at] python.org>\n"
    735 "MIME-Version: 1.0\n"
    736 "Content-Type: text/plain; charset=utf-8\n"
    737 "Content-Transfer-Encoding: 7bit\n"
    738 "Generated-By: manually\n"
    739 
    740 #: nofile:0
    741 msgid "ab\xc3\x9e"
    742 msgstr "\xc2\xa4yz"
    743 '''
    744 
    745 # Here's the third example po file, used to generate MMO_DATA
    746 
    747 b'''
    748 msgid ""
    749 msgstr ""
    750 "Project-Id-Version: No Project 0.0\n"
    751 "POT-Creation-Date: Wed Dec 11 07:44:15 2002\n"
    752 "PO-Revision-Date: 2002-08-14 01:18:58+00:00\n"
    753 "Last-Translator: John Doe <jdoe (at] example.com>\n"
    754 "Jane Foobar <jfoobar (at] example.com>\n"
    755 "Language-Team: xx <xx (at] example.com>\n"
    756 "MIME-Version: 1.0\n"
    757 "Content-Type: text/plain; charset=iso-8859-15\n"
    758 "Content-Transfer-Encoding: quoted-printable\n"
    759 "Generated-By: pygettext.py 1.3\n"
    760 '''
    761 
    762 #
    763 # messages.po, used for bug 17898
    764 #
    765 
    766 b'''
    767 # test file for http://bugs.python.org/issue17898
    768 msgid ""
    769 msgstr ""
    770 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
    771 "#-#-#-#-#  messages.po (EdX Studio)  #-#-#-#-#\n"
    772 "Content-Type: text/plain; charset=UTF-8\n"
    773 '''
    774