1 # -*- coding: utf-8 -*-
3 from __future__
import with_statement
10 import simplejson
as json
11 from mock
import Mock
, patch
13 from txclib
.project
import Project
14 from txclib
.config
import Flipdict
17 class TestProject(unittest
.TestCase
):
19 def test_extract_fields(self
):
20 """Test the functions that extract a field from a stats object."""
23 'last_update': '00:00',
27 stats
['completed'], '%s%%' % Project
._extract_completed(stats
)
29 self
.assertEqual(stats
['last_update'], Project
._extract_updated(stats
))
31 def test_specifying_resources(self
):
32 """Test the various ways to specify resources in a project."""
33 p
= Project(init
=False)
40 with patch
.object(p
, 'get_resource_list') as mock
:
41 mock
.return_value
= resources
43 'proj1.res1', '*1*', 'transifex*', '*r*',
44 '*o', 'transifex.tx?', 'transifex.txn',
49 ['transifex.txn', 'transifex.txo', ],
50 ['proj1.res1', 'proj2.res2', 'transifex.txn', 'transifex.txo', ],
52 ['transifex.txn', 'transifex.txo', ],
57 for i
, arg
in enumerate(cmd_args
):
59 self
.assertEqual(p
.get_chosen_resources(resources
), results
[i
])
62 resources
= ['*trasnifex*', ]
63 self
.assertRaises(Exception, p
.get_chosen_resources
, resources
)
66 class TestProjectMinimumPercent(unittest
.TestCase
):
67 """Test the minimum-perc option."""
70 super(TestProjectMinimumPercent
, self
).setUp()
71 self
.p
= Project(init
=False)
72 self
.p
.minimum_perc
= None
73 self
.p
.resource
= "resource"
75 def test_cmd_option(self
):
76 """Test command-line option."""
77 self
.p
.minimum_perc
= 20
78 results
= itertools
.cycle([80, 90 ])
79 def side_effect(*args
):
82 with patch
.object(self
.p
, "get_resource_option") as mock
:
83 mock
.side_effect
= side_effect
84 self
.assertFalse(self
.p
._satisfies_min_translated({'completed': '12%'}))
85 self
.assertTrue(self
.p
._satisfies_min_translated({'completed': '20%'}))
86 self
.assertTrue(self
.p
._satisfies_min_translated({'completed': '30%'}))
88 def test_global_only(self
):
89 """Test only global option."""
90 results
= itertools
.cycle([80, None ])
91 def side_effect(*args
):
94 with patch
.object(self
.p
, "get_resource_option") as mock
:
95 mock
.side_effect
= side_effect
96 self
.assertFalse(self
.p
._satisfies_min_translated({'completed': '70%'}))
97 self
.assertTrue(self
.p
._satisfies_min_translated({'completed': '80%'}))
98 self
.assertTrue(self
.p
._satisfies_min_translated({'completed': '90%'}))
100 def test_local_lower_than_global(self
):
101 """Test the case where the local option is lower than the global."""
102 results
= itertools
.cycle([80, 70 ])
103 def side_effect(*args
):
104 return results
.next()
106 with patch
.object(self
.p
, "get_resource_option") as mock
:
107 mock
.side_effect
= side_effect
108 self
.assertFalse(self
.p
._satisfies_min_translated({'completed': '60%'}))
109 self
.assertTrue(self
.p
._satisfies_min_translated({'completed': '70%'}))
110 self
.assertTrue(self
.p
._satisfies_min_translated({'completed': '80%'}))
111 self
.assertTrue(self
.p
._satisfies_min_translated({'completed': '90%'}))
113 def test_local_higher_than_global(self
):
114 """Test the case where the local option is lower than the global."""
115 results
= itertools
.cycle([60, 70 ])
116 def side_effect(*args
):
117 return results
.next()
119 with patch
.object(self
.p
, "get_resource_option") as mock
:
120 mock
.side_effect
= side_effect
121 self
.assertFalse(self
.p
._satisfies_min_translated({'completed': '60%'}))
122 self
.assertTrue(self
.p
._satisfies_min_translated({'completed': '70%'}))
123 self
.assertTrue(self
.p
._satisfies_min_translated({'completed': '80%'}))
124 self
.assertTrue(self
.p
._satisfies_min_translated({'completed': '90%'}))
126 def test_local_only(self
):
127 """Test the case where the local option is lower than the global."""
128 results
= itertools
.cycle([None, 70 ])
129 def side_effect(*args
):
130 return results
.next()
132 with patch
.object(self
.p
, "get_resource_option") as mock
:
133 mock
.side_effect
= side_effect
134 self
.assertFalse(self
.p
._satisfies_min_translated({'completed': '60%'}))
135 self
.assertTrue(self
.p
._satisfies_min_translated({'completed': '70%'}))
136 self
.assertTrue(self
.p
._satisfies_min_translated({'completed': '80%'}))
137 self
.assertTrue(self
.p
._satisfies_min_translated({'completed': '90%'}))
139 def test_no_option(self
):
140 """"Test the case there is nothing defined."""
141 results
= itertools
.cycle([None, None ])
142 def side_effect(*args
):
143 return results
.next()
145 with patch
.object(self
.p
, "get_resource_option") as mock
:
146 mock
.side_effect
= side_effect
147 self
.assertTrue(self
.p
._satisfies_min_translated({'completed': '0%'}))
148 self
.assertTrue(self
.p
._satisfies_min_translated({'completed': '10%'}))
149 self
.assertTrue(self
.p
._satisfies_min_translated({'completed': '90%'}))
152 class TestProjectFilters(unittest
.TestCase
):
153 """Test filters used to decide whether to push/pull a translation or not."""
156 super(TestProjectFilters
, self
).setUp()
157 self
.p
= Project(init
=False)
158 self
.p
.minimum_perc
= None
159 self
.p
.resource
= "resource"
162 'completed': '100%', 'last_update': '2011-11-01 15:00:00',
164 'completed': '60%', 'last_update': '2011-11-01 15:00:00',
166 'completed': '70%', 'last_update': '2011-11-01 15:00:00',
169 self
.langs
= self
.stats
.keys()
171 def test_add_translation(self
):
172 """Test filters for adding translations.
174 We do not test here for minimum percentages.
176 with patch
.object(self
.p
, "get_resource_option") as mock
:
177 mock
.return_value
= None
178 should_add
= self
.p
._should_add_translation
179 for force
in [True, False]:
180 for lang
in self
.langs
:
181 self
.assertTrue(should_add(lang
, self
.stats
, force
))
184 self
.assertFalse(should_add('es', self
.stats
))
186 def test_update_translation(self
):
187 """Test filters for updating a translation.
189 We do not test here for minimum percentages.
191 with patch
.object(self
.p
, "get_resource_option") as mock
:
192 mock
.return_value
= None
194 should_update
= self
.p
._should_update_translation
196 for lang
in self
.langs
:
197 self
.assertTrue(should_update(lang
, self
.stats
, 'foo', force
))
199 force
= False # reminder
203 self
.assertFalse(should_update('es', self
.stats
, local_file
))
206 with patch
.object(self
.p
, "_get_time_of_local_file") as time_mock
:
207 time_mock
.return_value
= None
208 with patch
.object(self
.p
, "get_full_path") as path_mock
:
209 path_mock
.return_value
= "foo"
210 for lang
in self
.langs
:
212 should_update(lang
, self
.stats
, local_file
)
216 local_times
= [self
.p
._generate_timestamp('2011-11-01 14:00:59')]
217 results
= itertools
.cycle(local_times
)
218 def side_effect(*args
):
219 return results
.next()
221 with patch
.object(self
.p
, "_get_time_of_local_file") as time_mock
:
222 time_mock
.side_effect
= side_effect
223 with patch
.object(self
.p
, "get_full_path") as path_mock
:
224 path_mock
.return_value
= "foo"
225 for lang
in self
.langs
:
227 should_update(lang
, self
.stats
, local_file
)
231 local_times
= [self
.p
._generate_timestamp('2011-11-01 15:01:59')]
232 results
= itertools
.cycle(local_times
)
233 def side_effect(*args
):
234 return results
.next()
236 with patch
.object(self
.p
, "_get_time_of_local_file") as time_mock
:
237 time_mock
.side_effect
= side_effect
238 with patch
.object(self
.p
, "get_full_path") as path_mock
:
239 path_mock
.return_value
= "foo"
240 for lang
in self
.langs
:
242 should_update(lang
, self
.stats
, local_file
)
245 def test_push_translation(self
):
246 """Test filters for pushing a translation file."""
247 with patch
.object(self
.p
, "get_resource_option") as mock
:
248 mock
.return_value
= None
251 should_push
= self
.p
._should_push_translation
253 for lang
in self
.langs
:
254 self
.assertTrue(should_push(lang
, self
.stats
, local_file
, force
))
256 force
= False # reminder
259 self
.assertTrue(should_push('es', self
.stats
, local_file
))
262 local_times
= [self
.p
._generate_timestamp('2011-11-01 14:00:59')]
263 results
= itertools
.cycle(local_times
)
264 def side_effect(*args
):
265 return results
.next()
267 with patch
.object(self
.p
, "_get_time_of_local_file") as time_mock
:
268 time_mock
.side_effect
= side_effect
269 with patch
.object(self
.p
, "get_full_path") as path_mock
:
270 path_mock
.return_value
= "foo"
271 for lang
in self
.langs
:
273 should_push(lang
, self
.stats
, local_file
)
277 local_times
= [self
.p
._generate_timestamp('2011-11-01 15:01:59')]
278 results
= itertools
.cycle(local_times
)
279 def side_effect(*args
):
280 return results
.next()
282 with patch
.object(self
.p
, "_get_time_of_local_file") as time_mock
:
283 time_mock
.side_effect
= side_effect
284 with patch
.object(self
.p
, "get_full_path") as path_mock
:
285 path_mock
.return_value
= "foo"
286 for lang
in self
.langs
:
288 should_push(lang
, self
.stats
, local_file
)
292 class TestProjectPull(unittest
.TestCase
):
293 """Test bits & pieces of the pull method."""
296 super(TestProjectPull
, self
).setUp()
297 self
.p
= Project(init
=False)
298 self
.p
.minimum_perc
= None
299 self
.p
.resource
= "resource"
301 self
.p
.project_slug
= 'foo'
302 self
.p
.resource_slug
= 'foo'
305 'completed': '100%', 'last_update': '2011-11-01 15:00:00',
307 'completed': '60%', 'last_update': '2011-11-01 15:00:00',
309 'completed': '70%', 'last_update': '2011-11-01 15:00:00',
312 self
.langs
= self
.stats
.keys()
313 self
.files
= dict(zip(self
.langs
, itertools
.repeat(None)))
314 self
.details
= {'available_languages': []}
315 for lang
in self
.langs
:
316 self
.details
['available_languages'].append({'code': lang
})
318 self
.lang_map
= Flipdict()
320 def test_new_translations(self
):
321 """Test finding new transaltions to add."""
322 with patch
.object(self
.p
, 'do_url_request') as resource_mock
:
323 resource_mock
.return_value
= json
.dumps(self
.details
)
324 files_keys
= self
.langs
325 new_trans
= self
.p
._new_translations_to_add
326 for force
in [True, False]:
328 self
.files
, self
.slang
, self
.lang_map
, self
.stats
, force
330 self
.assertEquals(res
, set([]))
332 with patch
.object(self
.p
, '_should_add_translation') as filter_mock
:
333 filter_mock
.return_value
= True
334 for force
in [True, False]:
336 {'el': None}, self
.slang
, self
.lang_map
, self
.stats
, force
338 self
.assertEquals(res
, set(['pt']))
339 for force
in [True, False]:
341 {}, self
.slang
, self
.lang_map
, self
.stats
, force
343 self
.assertEquals(res
, set(['el', 'pt']))
346 files
['pt_PT'] = None
347 lang_map
= {'pt': 'pt_PT'}
348 for force
in [True, False]:
350 files
, self
.slang
, lang_map
, self
.stats
, force
352 self
.assertEquals(res
, set(['el']))
354 def test_languages_to_pull_empty_initial_list(self
):
355 """Test determining the languages to pull, when the initial
361 res
= self
.p
._languages_to_pull(
362 languages
, self
.files
, self
.lang_map
, self
.stats
, force
366 self
.assertEquals(existing
, set(['el', 'en', 'pt']))
367 self
.assertFalse(new
)
370 self
.files
['el-gr'] = None
371 self
.lang_map
['el'] = 'el-gr'
372 res
= self
.p
._languages_to_pull(
373 languages
, self
.files
, self
.lang_map
, self
.stats
, force
377 self
.assertEquals(existing
, set(['el', 'en', 'pt']))
378 self
.assertFalse(new
)
380 def test_languages_to_pull_with_initial_list(self
):
381 """Test determining the languages to pull, then there is a
382 language selection from the user.
384 languages
= ['el', 'en']
385 self
.lang_map
['el'] = 'el-gr'
387 self
.files
['el-gr'] = None
390 with patch
.object(self
.p
, '_should_add_translation') as mock
:
391 mock
.return_value
= True
392 res
= self
.p
._languages_to_pull(
393 languages
, self
.files
, self
.lang_map
, self
.stats
, force
397 self
.assertEquals(existing
, set(['en', 'el-gr', ]))
398 self
.assertFalse(new
)
400 mock
.return_value
= False
401 res
= self
.p
._languages_to_pull(
402 languages
, self
.files
, self
.lang_map
, self
.stats
, force
406 self
.assertEquals(existing
, set(['en', 'el-gr', ]))
407 self
.assertFalse(new
)
409 del self
.files
['el-gr']
410 mock
.return_value
= True
411 res
= self
.p
._languages_to_pull(
412 languages
, self
.files
, self
.lang_map
, self
.stats
, force
416 self
.assertEquals(existing
, set(['en', ]))
417 self
.assertEquals(new
, set(['el', ]))
419 mock
.return_value
= False
420 res
= self
.p
._languages_to_pull(
421 languages
, self
.files
, self
.lang_map
, self
.stats
, force
425 self
.assertEquals(existing
, set(['en', ]))
426 self
.assertEquals(new
, set([]))
428 def test_in_combination_with_force_option(self
):
429 """Test the minumum-perc option along with -f."""
430 with patch
.object(self
.p
, 'get_resource_option') as mock
:
431 mock
.return_value
= 70
433 res
= self
.p
._should_download('de', self
.stats
, None, False)
434 self
.assertEquals(res
, False)
435 res
= self
.p
._should_download('el', self
.stats
, None, False)
436 self
.assertEquals(res
, False)
437 res
= self
.p
._should_download('el', self
.stats
, None, True)
438 self
.assertEquals(res
, False)
439 res
= self
.p
._should_download('en', self
.stats
, None, False)
440 self
.assertEquals(res
, True)
441 res
= self
.p
._should_download('en', self
.stats
, None, True)
442 self
.assertEquals(res
, True)
444 with patch
.object(self
.p
, '_remote_is_newer') as local_file_mock
:
445 local_file_mock
= False
446 res
= self
.p
._should_download('pt', self
.stats
, None, False)
447 self
.assertEquals(res
, True)
448 res
= self
.p
._should_download('pt', self
.stats
, None, True)
449 self
.assertEquals(res
, True)
452 class TestFormats(unittest
.TestCase
):
453 """Tests for the supported formats."""
456 self
.p
= Project(init
=False)
458 def test_extensions(self
):
459 """Test returning the correct extension for a format."""
461 'PO': {'file-extensions': '.po, .pot'},
462 'QT': {'file-extensions': '.ts'},
464 extensions
= ['.po', '.ts', '', ]
465 with patch
.object(self
.p
, "do_url_request") as mock
:
466 mock
.return_value
= json
.dumps(sample_formats
)
467 for (type_
, ext
) in zip(['PO', 'QT', 'NONE', ], extensions
):
468 extension
= self
.p
._extension_for(type_
)
469 self
.assertEquals(extension
, ext
)
472 class TestOptions(unittest
.TestCase
):
473 """Test the methods related to parsing the configuration file."""
476 self
.p
= Project(init
=False)
478 def test_get_option(self
):
479 """Test _get_option method."""
480 with contextlib
.nested(
481 patch
.object(self
.p
, 'get_resource_option'),
482 patch
.object(self
.p
, 'config', create
=True)
484 rmock
.return_value
= 'resource'
485 cmock
.has_option
.return_value
= 'main'
486 cmock
.get
.return_value
= 'main'
487 self
.assertEqual(self
.p
._get_option(None, None), 'resource')
488 rmock
.return_value
= None
489 cmock
.has_option
.return_value
= 'main'
490 cmock
.get
.return_value
= 'main'
491 self
.assertEqual(self
.p
._get_option(None, None), 'main')
492 cmock
.has_option
.return_value
= None
493 self
.assertIs(self
.p
._get_option(None, None), None)
496 class TestConfigurationOptions(unittest
.TestCase
):
497 """Test the various configuration options."""
499 def test_i18n_type(self
):
500 p
= Project(init
=False)
503 with patch
.object(p
, 'config', create
=True) as config_mock
:
504 p
.set_i18n_type([], i18n_type
)
505 calls
= config_mock
.method_calls
506 self
.assertEquals('set', calls
[0][0])
507 self
.assertEquals('main', calls
[0][1][0])
508 p
.set_i18n_type(['transifex.txo'], 'PO')
509 calls
= config_mock
.method_calls
510 self
.assertEquals('set', calls
[0][0])
511 p
.set_i18n_type(['transifex.txo', 'transifex.txn'], 'PO')
512 calls
= config_mock
.method_calls
513 self
.assertEquals('set', calls
[0][0])
514 self
.assertEquals('set', calls
[1][0])
517 class TestStats(unittest
.TestCase
):
518 """Test the access to the stats objects."""
522 self
.stats
.__getitem__
= Mock()
523 self
.stats
.__getitem__
.return_value
= '12%'
525 def test_field_used_per_mode(self
):
526 """Test the fields used for each mode."""
527 Project
._extract_completed(self
.stats
, 'translate')
528 self
.stats
.__getitem__
.assert_called_with('completed')
529 Project
._extract_completed(self
.stats
, 'reviewed')
530 self
.stats
.__getitem__
.assert_called_with('reviewed_percentage')