1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Copyright (c) 2013 the BabelFish authors. All rights reserved.
5# Use of this source code is governed by the 3-clause BSD license
6# that can be found in the LICENSE file.
7#
8from __future__ import unicode_literals
9import re
10import sys
11import pickle
12from unittest import TestCase, TestSuite, TestLoader, TextTestRunner
13from pkg_resources import resource_stream  # @UnresolvedImport
14from babelfish import (LANGUAGES, Language, Country, Script, language_converters, country_converters,
15    LanguageReverseConverter, LanguageConvertError, LanguageReverseError, CountryReverseError)
16
17
18if sys.version_info[:2] <= (2, 6):
19    _MAX_LENGTH = 80
20
21    def safe_repr(obj, short=False):
22        try:
23            result = repr(obj)
24        except Exception:
25            result = object.__repr__(obj)
26        if not short or len(result) < _MAX_LENGTH:
27            return result
28        return result[:_MAX_LENGTH] + ' [truncated]...'
29
30    class _AssertRaisesContext(object):
31        """A context manager used to implement TestCase.assertRaises* methods."""
32
33        def __init__(self, expected, test_case, expected_regexp=None):
34            self.expected = expected
35            self.failureException = test_case.failureException
36            self.expected_regexp = expected_regexp
37
38        def __enter__(self):
39            return self
40
41        def __exit__(self, exc_type, exc_value, tb):
42            if exc_type is None:
43                try:
44                    exc_name = self.expected.__name__
45                except AttributeError:
46                    exc_name = str(self.expected)
47                raise self.failureException(
48                    "{0} not raised".format(exc_name))
49            if not issubclass(exc_type, self.expected):
50                # let unexpected exceptions pass through
51                return False
52            self.exception = exc_value  # store for later retrieval
53            if self.expected_regexp is None:
54                return True
55
56            expected_regexp = self.expected_regexp
57            if isinstance(expected_regexp, basestring):
58                expected_regexp = re.compile(expected_regexp)
59            if not expected_regexp.search(str(exc_value)):
60                raise self.failureException('"%s" does not match "%s"' %
61                         (expected_regexp.pattern, str(exc_value)))
62            return True
63
64    class _Py26FixTestCase(object):
65        def assertIsNone(self, obj, msg=None):
66            """Same as self.assertTrue(obj is None), with a nicer default message."""
67            if obj is not None:
68                standardMsg = '%s is not None' % (safe_repr(obj),)
69                self.fail(self._formatMessage(msg, standardMsg))
70
71        def assertIsNotNone(self, obj, msg=None):
72            """Included for symmetry with assertIsNone."""
73            if obj is None:
74                standardMsg = 'unexpectedly None'
75                self.fail(self._formatMessage(msg, standardMsg))
76
77        def assertIn(self, member, container, msg=None):
78            """Just like self.assertTrue(a in b), but with a nicer default message."""
79            if member not in container:
80                standardMsg = '%s not found in %s' % (safe_repr(member),
81                                                      safe_repr(container))
82                self.fail(self._formatMessage(msg, standardMsg))
83
84        def assertNotIn(self, member, container, msg=None):
85            """Just like self.assertTrue(a not in b), but with a nicer default message."""
86            if member in container:
87                standardMsg = '%s unexpectedly found in %s' % (safe_repr(member),
88                                                            safe_repr(container))
89                self.fail(self._formatMessage(msg, standardMsg))
90
91        def assertIs(self, expr1, expr2, msg=None):
92            """Just like self.assertTrue(a is b), but with a nicer default message."""
93            if expr1 is not expr2:
94                standardMsg = '%s is not %s' % (safe_repr(expr1),
95                                                 safe_repr(expr2))
96                self.fail(self._formatMessage(msg, standardMsg))
97
98        def assertIsNot(self, expr1, expr2, msg=None):
99            """Just like self.assertTrue(a is not b), but with a nicer default message."""
100            if expr1 is expr2:
101                standardMsg = 'unexpectedly identical: %s' % (safe_repr(expr1),)
102                self.fail(self._formatMessage(msg, standardMsg))
103
104else:
105    class _Py26FixTestCase(object):
106        pass
107
108
109class TestScript(TestCase, _Py26FixTestCase):
110    def test_wrong_script(self):
111        self.assertRaises(ValueError, lambda: Script('Azer'))
112
113    def test_eq(self):
114        self.assertEqual(Script('Latn'), Script('Latn'))
115
116    def test_ne(self):
117        self.assertNotEqual(Script('Cyrl'), Script('Latn'))
118
119    def test_hash(self):
120        self.assertEqual(hash(Script('Hira')), hash('Hira'))
121
122    def test_pickle(self):
123        self.assertEqual(pickle.loads(pickle.dumps(Script('Latn'))), Script('Latn'))
124
125
126class TestCountry(TestCase, _Py26FixTestCase):
127    def test_wrong_country(self):
128        self.assertRaises(ValueError, lambda: Country('ZZ'))
129
130    def test_eq(self):
131        self.assertEqual(Country('US'), Country('US'))
132
133    def test_ne(self):
134        self.assertNotEqual(Country('GB'), Country('US'))
135        self.assertIsNotNone(Country('US'))
136
137    def test_hash(self):
138        self.assertEqual(hash(Country('US')), hash('US'))
139
140    def test_pickle(self):
141        for country in [Country('GB'), Country('US')]:
142            self.assertEqual(pickle.loads(pickle.dumps(country)), country)
143
144    def test_converter_name(self):
145        self.assertEqual(Country('US').name, 'UNITED STATES')
146        self.assertEqual(Country.fromname('UNITED STATES'), Country('US'))
147        self.assertEqual(Country.fromcode('UNITED STATES', 'name'), Country('US'))
148        self.assertRaises(CountryReverseError, lambda: Country.fromname('ZZZZZ'))
149        self.assertEqual(len(country_converters['name'].codes), 249)
150
151
152class TestLanguage(TestCase, _Py26FixTestCase):
153    def test_languages(self):
154        self.assertEqual(len(LANGUAGES), 7874)
155
156    def test_wrong_language(self):
157        self.assertRaises(ValueError, lambda: Language('zzz'))
158
159    def test_unknown_language(self):
160        self.assertEqual(Language('zzzz', unknown='und'), Language('und'))
161
162    def test_converter_alpha2(self):
163        self.assertEqual(Language('eng').alpha2, 'en')
164        self.assertEqual(Language.fromalpha2('en'), Language('eng'))
165        self.assertEqual(Language.fromcode('en', 'alpha2'), Language('eng'))
166        self.assertRaises(LanguageReverseError, lambda: Language.fromalpha2('zz'))
167        self.assertRaises(LanguageConvertError, lambda: Language('aaa').alpha2)
168        self.assertEqual(len(language_converters['alpha2'].codes), 184)
169
170    def test_converter_alpha3b(self):
171        self.assertEqual(Language('fra').alpha3b, 'fre')
172        self.assertEqual(Language.fromalpha3b('fre'), Language('fra'))
173        self.assertEqual(Language.fromcode('fre', 'alpha3b'), Language('fra'))
174        self.assertRaises(LanguageReverseError, lambda: Language.fromalpha3b('zzz'))
175        self.assertRaises(LanguageConvertError, lambda: Language('aaa').alpha3b)
176        self.assertEqual(len(language_converters['alpha3b'].codes), 418)
177
178    def test_converter_alpha3t(self):
179        self.assertEqual(Language('fra').alpha3t, 'fra')
180        self.assertEqual(Language.fromalpha3t('fra'), Language('fra'))
181        self.assertEqual(Language.fromcode('fra', 'alpha3t'), Language('fra'))
182        self.assertRaises(LanguageReverseError, lambda: Language.fromalpha3t('zzz'))
183        self.assertRaises(LanguageConvertError, lambda: Language('aaa').alpha3t)
184        self.assertEqual(len(language_converters['alpha3t'].codes), 418)
185
186    def test_converter_name(self):
187        self.assertEqual(Language('eng').name, 'English')
188        self.assertEqual(Language.fromname('English'), Language('eng'))
189        self.assertEqual(Language.fromcode('English', 'name'), Language('eng'))
190        self.assertRaises(LanguageReverseError, lambda: Language.fromname('Zzzzzzzzz'))
191        self.assertEqual(len(language_converters['name'].codes), 7874)
192
193    def test_converter_scope(self):
194        self.assertEqual(language_converters['scope'].codes, set(['I', 'S', 'M']))
195        self.assertEqual(Language('eng').scope, 'individual')
196        self.assertEqual(Language('und').scope, 'special')
197
198    def test_converter_type(self):
199        self.assertEqual(language_converters['type'].codes, set(['A', 'C', 'E', 'H', 'L', 'S']))
200        self.assertEqual(Language('eng').type, 'living')
201        self.assertEqual(Language('und').type, 'special')
202
203    def test_converter_opensubtitles(self):
204        self.assertEqual(Language('fra').opensubtitles, Language('fra').alpha3b)
205        self.assertEqual(Language('por', 'BR').opensubtitles, 'pob')
206        self.assertEqual(Language.fromopensubtitles('fre'), Language('fra'))
207        self.assertEqual(Language.fromopensubtitles('pob'), Language('por', 'BR'))
208        self.assertEqual(Language.fromopensubtitles('pb'), Language('por', 'BR'))
209        # Montenegrin is not recognized as an ISO language (yet?) but for now it is
210        # unofficially accepted as Serbian from Montenegro
211        self.assertEqual(Language.fromopensubtitles('mne'), Language('srp', 'ME'))
212        self.assertEqual(Language.fromcode('pob', 'opensubtitles'), Language('por', 'BR'))
213        self.assertRaises(LanguageReverseError, lambda: Language.fromopensubtitles('zzz'))
214        self.assertRaises(LanguageConvertError, lambda: Language('aaa').opensubtitles)
215        self.assertEqual(len(language_converters['opensubtitles'].codes), 606)
216
217        # test with all the LANGUAGES from the opensubtitles api
218        # downloaded from: http://www.opensubtitles.org/addons/export_languages.php
219        f = resource_stream('babelfish', 'data/opensubtitles_languages.txt')
220        f.readline()
221        for l in f:
222            idlang, alpha2, _, upload_enabled, web_enabled = l.decode('utf-8').strip().split('\t')
223            if not int(upload_enabled) and not int(web_enabled):
224                # do not test LANGUAGES that are too esoteric / not widely available
225                continue
226            self.assertEqual(Language.fromopensubtitles(idlang).opensubtitles, idlang)
227            if alpha2:
228                self.assertEqual(Language.fromopensubtitles(idlang), Language.fromopensubtitles(alpha2))
229        f.close()
230
231    def test_fromietf_country_script(self):
232        language = Language.fromietf('fra-FR-Latn')
233        self.assertEqual(language.alpha3, 'fra')
234        self.assertEqual(language.country, Country('FR'))
235        self.assertEqual(language.script, Script('Latn'))
236
237    def test_fromietf_country_no_script(self):
238        language = Language.fromietf('fra-FR')
239        self.assertEqual(language.alpha3, 'fra')
240        self.assertEqual(language.country, Country('FR'))
241        self.assertIsNone(language.script)
242
243    def test_fromietf_no_country_no_script(self):
244        language = Language.fromietf('fra-FR')
245        self.assertEqual(language.alpha3, 'fra')
246        self.assertEqual(language.country, Country('FR'))
247        self.assertIsNone(language.script)
248
249    def test_fromietf_no_country_script(self):
250        language = Language.fromietf('fra-Latn')
251        self.assertEqual(language.alpha3, 'fra')
252        self.assertIsNone(language.country)
253        self.assertEqual(language.script, Script('Latn'))
254
255    def test_fromietf_alpha2_language(self):
256        language = Language.fromietf('fr-Latn')
257        self.assertEqual(language.alpha3, 'fra')
258        self.assertIsNone(language.country)
259        self.assertEqual(language.script, Script('Latn'))
260
261    def test_fromietf_wrong_language(self):
262        self.assertRaises(ValueError, lambda: Language.fromietf('xyz-FR'))
263
264    def test_fromietf_wrong_country(self):
265        self.assertRaises(ValueError, lambda: Language.fromietf('fra-YZ'))
266
267    def test_fromietf_wrong_script(self):
268        self.assertRaises(ValueError, lambda: Language.fromietf('fra-FR-Wxyz'))
269
270    def test_eq(self):
271        self.assertEqual(Language('eng'), Language('eng'))
272
273    def test_ne(self):
274        self.assertNotEqual(Language('fra'), Language('eng'))
275        self.assertIsNotNone(Language('fra'))
276
277    def test_nonzero(self):
278        self.assertFalse(bool(Language('und')))
279        self.assertTrue(bool(Language('eng')))
280
281    def test_language_hasattr(self):
282        self.assertTrue(hasattr(Language('fra'), 'alpha3'))
283        self.assertTrue(hasattr(Language('fra'), 'alpha2'))
284        self.assertFalse(hasattr(Language('bej'), 'alpha2'))
285
286    def test_country_hasattr(self):
287        self.assertTrue(hasattr(Country('US'), 'name'))
288        self.assertTrue(hasattr(Country('FR'), 'alpha2'))
289        self.assertFalse(hasattr(Country('BE'), 'none'))
290
291    def test_country(self):
292        self.assertEqual(Language('por', 'BR').country, Country('BR'))
293        self.assertEqual(Language('eng', Country('US')).country, Country('US'))
294
295    def test_eq_with_country(self):
296        self.assertEqual(Language('eng', 'US'), Language('eng', Country('US')))
297
298    def test_ne_with_country(self):
299        self.assertNotEqual(Language('eng', 'US'), Language('eng', Country('GB')))
300
301    def test_script(self):
302        self.assertEqual(Language('srp', script='Latn').script, Script('Latn'))
303        self.assertEqual(Language('srp', script=Script('Cyrl')).script, Script('Cyrl'))
304
305    def test_eq_with_script(self):
306        self.assertEqual(Language('srp', script='Latn'), Language('srp', script=Script('Latn')))
307
308    def test_ne_with_script(self):
309        self.assertNotEqual(Language('srp', script='Latn'), Language('srp', script=Script('Cyrl')))
310
311    def test_eq_with_country_and_script(self):
312        self.assertEqual(Language('srp', 'SR', 'Latn'), Language('srp', Country('SR'), Script('Latn')))
313
314    def test_ne_with_country_and_script(self):
315        self.assertNotEqual(Language('srp', 'SR', 'Latn'), Language('srp', Country('SR'), Script('Cyrl')))
316
317    def test_hash(self):
318        self.assertEqual(hash(Language('fra')), hash('fr'))
319        self.assertEqual(hash(Language('ace')), hash('ace'))
320        self.assertEqual(hash(Language('por', 'BR')), hash('pt-BR'))
321        self.assertEqual(hash(Language('srp', script='Cyrl')), hash('sr-Cyrl'))
322        self.assertEqual(hash(Language('eng', 'US', 'Latn')), hash('en-US-Latn'))
323
324    def test_pickle(self):
325        for lang in [Language('fra'),
326                     Language('eng', 'US'),
327                     Language('srp', script='Latn'),
328                     Language('eng', 'US', 'Latn')]:
329            self.assertEqual(pickle.loads(pickle.dumps(lang)), lang)
330
331    def test_str(self):
332        self.assertEqual(Language.fromietf(str(Language('eng', 'US', 'Latn'))), Language('eng', 'US', 'Latn'))
333        self.assertEqual(Language.fromietf(str(Language('fra', 'FR'))), Language('fra', 'FR'))
334        self.assertEqual(Language.fromietf(str(Language('bel'))), Language('bel'))
335
336    def test_register_converter(self):
337        class TestConverter(LanguageReverseConverter):
338            def __init__(self):
339                self.to_test = {'fra': 'test1', 'eng': 'test2'}
340                self.from_test = {'test1': 'fra', 'test2': 'eng'}
341
342            def convert(self, alpha3, country=None, script=None):
343                if alpha3 not in self.to_test:
344                    raise LanguageConvertError(alpha3, country, script)
345                return self.to_test[alpha3]
346
347            def reverse(self, test):
348                if test not in self.from_test:
349                    raise LanguageReverseError(test)
350                return (self.from_test[test], None)
351        language = Language('fra')
352        self.assertFalse(hasattr(language, 'test'))
353        language_converters['test'] = TestConverter()
354        self.assertTrue(hasattr(language, 'test'))
355        self.assertIn('test', language_converters)
356        self.assertEqual(Language('fra').test, 'test1')
357        self.assertEqual(Language.fromtest('test2').alpha3, 'eng')
358        del language_converters['test']
359        self.assertNotIn('test', language_converters)
360        self.assertRaises(KeyError, lambda: Language.fromtest('test1'))
361        self.assertRaises(AttributeError, lambda: Language('fra').test)
362
363
364def suite():
365    suite = TestSuite()
366    suite.addTest(TestLoader().loadTestsFromTestCase(TestScript))
367    suite.addTest(TestLoader().loadTestsFromTestCase(TestCountry))
368    suite.addTest(TestLoader().loadTestsFromTestCase(TestLanguage))
369    return suite
370
371
372if __name__ == '__main__':
373    TextTestRunner().run(suite())
374