Skip to content

Commit 3f6bbbb

Browse files
committed
Refactor tests: split test_concat_merge.py into test_concat.py and test_merge.py
- Use pytest fixtures (tmp_path, monkeypatch) instead of setup_method/teardown_method - Use pathlib.Path for file paths - Replace FakeStdout class with monkeypatch.setattr on sys.stdout - Replace snapshot assertions with targeted behavioral checks - Fix compendium typo (compenidum -> compendium) - Fix self.compendium default to [] so _get_messages_from_compendiums is safe without guard - Add test_conflicted_po_raises_on_read
1 parent 3964846 commit 3f6bbbb

4 files changed

Lines changed: 664 additions & 807 deletions

File tree

babel/messages/frontend.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1068,7 +1068,7 @@ class MergeCatalog(CommandMixin):
10681068

10691069
def initialize_options(self):
10701070
self.input_files = None
1071-
self.compendium = None
1071+
self.compendium: list[str] = []
10721072
self.compendium_overwrite = False
10731073
self.no_compendium_comment = False
10741074
self.update = False
Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
#
2+
# Copyright (C) 2007-2011 Edgewall Software, 2013-2025 the Babel team
3+
# All rights reserved.
4+
#
5+
# This software is licensed as described in the file LICENSE, which
6+
# you should have received as part of this distribution. The terms
7+
# are also available at https://github.com/python-babel/babel/blob/master/LICENSE.
8+
#
9+
# This software consists of voluntary contributions made by many
10+
# individuals. For the exact contribution history, see the revision
11+
# history and logs, available at https://github.com/python-babel/babel/commits/master/.
12+
13+
from __future__ import annotations
14+
15+
import contextlib
16+
import io
17+
import pathlib
18+
import sys
19+
20+
import pytest
21+
from freezegun import freeze_time
22+
23+
from babel.messages import Catalog, frontend, pofile
24+
from babel.messages.frontend import OptionError
25+
from tests.messages.consts import TEST_PROJECT_DISTRIBUTION_DATA
26+
from tests.messages.utils import Distribution
27+
28+
29+
@pytest.fixture(autouse=True)
30+
def frozen_time():
31+
with freeze_time("1994-11-11"):
32+
yield
33+
34+
35+
@pytest.fixture
36+
def concat_cmd():
37+
dist = Distribution(TEST_PROJECT_DISTRIBUTION_DATA)
38+
cmd = frontend.ConcatenateCatalog(dist)
39+
cmd.initialize_options()
40+
return cmd
41+
42+
43+
@pytest.fixture
44+
def po_files(tmp_path: pathlib.Path):
45+
temp1 = tmp_path / 'msgcat_temp1.po'
46+
temp2 = tmp_path / 'msgcat_temp2.po'
47+
48+
with open(temp1, 'wb') as file:
49+
catalog = Catalog()
50+
catalog.add('other1', string='Other 1', locations=[('simple.py', 1)], flags=['flag1000'])
51+
catalog.add('other2', string='Other 2', locations=[('simple.py', 10)])
52+
catalog.add('same', string='Same', locations=[('simple.py', 100)], flags=['flag1', 'flag1.2'])
53+
catalog.add('almost_same', string='Almost same', locations=[('simple.py', 1000)], flags=['flag2'])
54+
catalog.add(('plural', 'plurals'), string=('Plural', 'Plurals'), locations=[('simple.py', 2000)])
55+
pofile.write_po(file, catalog)
56+
57+
with open(temp2, 'wb') as file:
58+
catalog = Catalog()
59+
catalog.add('other3', string='Other 3', locations=[('hard.py', 1)])
60+
catalog.add('other4', string='Other 4', locations=[('hard.py', 10)])
61+
catalog.add('almost_same', string='A bit same', locations=[('hard.py', 1000)], flags=['flag3'])
62+
catalog.add('same', string='Same', locations=[('hard.py', 100)], flags=['flag4'])
63+
catalog.add(('plural', 'plurals'), string=('Plural', 'Plurals other'), locations=[('hard.py', 2000)])
64+
pofile.write_po(file, catalog)
65+
66+
return temp1, temp2
67+
68+
69+
def test_no_input_files(concat_cmd):
70+
with pytest.raises(OptionError):
71+
concat_cmd.finalize_options()
72+
73+
74+
def test_no_output_file(concat_cmd):
75+
concat_cmd.input_files = ['project/i18n/messages.pot']
76+
concat_cmd.finalize_options() # output_file not required; defaults to stdout
77+
78+
79+
def test_unique_exclusive_with_less_than(concat_cmd, po_files):
80+
temp1, temp2 = po_files
81+
concat_cmd.input_files = [str(temp1), str(temp2)]
82+
concat_cmd.unique = True
83+
concat_cmd.less_than = 3
84+
with pytest.raises(OptionError):
85+
concat_cmd.finalize_options()
86+
87+
88+
def test_unique_exclusive_with_more_than(concat_cmd, po_files):
89+
temp1, temp2 = po_files
90+
concat_cmd.input_files = [str(temp1), str(temp2)]
91+
concat_cmd.unique = True
92+
concat_cmd.more_than = 1
93+
with pytest.raises(OptionError):
94+
concat_cmd.finalize_options()
95+
96+
97+
def test_default(concat_cmd, po_files, tmp_path):
98+
temp1, temp2 = po_files
99+
output_file = tmp_path / 'msgcat.po'
100+
concat_cmd.input_files = [str(temp1), str(temp2)]
101+
concat_cmd.output_file = str(output_file)
102+
concat_cmd.finalize_options()
103+
concat_cmd.run()
104+
105+
content = output_file.read_text()
106+
107+
assert 'msgid "other1"' in content
108+
assert 'msgstr "Other 1"' in content
109+
assert 'msgid "other3"' in content
110+
111+
assert 'msgid "same"' in content
112+
assert 'msgstr "Same"' in content
113+
assert content.count('#-#-#-#-# msgcat_temp1.po') == 0 or 'msgid "same"' not in [
114+
block for block in content.split('\n\n') if '#-#-#-#-#' in block
115+
]
116+
117+
almost_same_block = next(b for b in content.split('\n\n') if 'msgid "almost_same"' in b)
118+
assert 'fuzzy' in almost_same_block
119+
assert '#-#-#-#-#' in almost_same_block
120+
assert 'Almost same' in almost_same_block
121+
assert 'A bit same' in almost_same_block
122+
123+
plural_block = next(b for b in content.split('\n\n') if 'msgid "plural"' in b)
124+
assert 'fuzzy' in plural_block
125+
assert '#-#-#-#-#' in plural_block
126+
127+
128+
def test_use_first(concat_cmd, po_files, tmp_path):
129+
temp1, temp2 = po_files
130+
output_file = tmp_path / 'msgcat.po'
131+
concat_cmd.input_files = [str(temp1), str(temp2)]
132+
concat_cmd.output_file = str(output_file)
133+
concat_cmd.use_first = True
134+
concat_cmd.finalize_options()
135+
concat_cmd.run()
136+
137+
content = output_file.read_text()
138+
139+
assert '#-#-#-#-#' not in content
140+
141+
almost_same_block = next(b for b in content.split('\n\n') if 'msgid "almost_same"' in b)
142+
assert 'fuzzy' not in almost_same_block
143+
assert 'msgstr "Almost same"' in almost_same_block
144+
145+
plural_block = next(b for b in content.split('\n\n') if 'msgid "plural"' in b)
146+
assert 'fuzzy' not in plural_block
147+
assert 'msgstr[0] "Plural"' in plural_block
148+
assert 'msgstr[1] "Plurals"' in plural_block
149+
150+
151+
def test_unique(concat_cmd, po_files, tmp_path):
152+
temp1, temp2 = po_files
153+
output_file = tmp_path / 'msgcat.po'
154+
concat_cmd.input_files = [str(temp1), str(temp2)]
155+
concat_cmd.output_file = str(output_file)
156+
concat_cmd.unique = True
157+
concat_cmd.finalize_options()
158+
concat_cmd.run()
159+
160+
content = output_file.read_text()
161+
162+
assert 'msgid "other1"' in content
163+
assert 'msgid "other2"' in content
164+
assert 'msgid "other3"' in content
165+
assert 'msgid "other4"' in content
166+
assert 'msgid "same"' not in content
167+
assert 'msgid "almost_same"' not in content
168+
169+
170+
def test_less_than_equivalent_to_unique(concat_cmd, po_files, tmp_path):
171+
temp1, temp2 = po_files
172+
output_file = tmp_path / 'msgcat.po'
173+
concat_cmd.input_files = [str(temp1), str(temp2)]
174+
concat_cmd.output_file = str(output_file)
175+
concat_cmd.less_than = 2
176+
concat_cmd.finalize_options()
177+
concat_cmd.run()
178+
less_than_content = output_file.read_text()
179+
180+
concat_cmd.less_than = None
181+
concat_cmd.unique = True
182+
concat_cmd.finalize_options()
183+
concat_cmd.run()
184+
unique_content = output_file.read_text()
185+
186+
assert less_than_content == unique_content
187+
188+
189+
def test_more_than(concat_cmd, po_files, tmp_path):
190+
temp1, temp2 = po_files
191+
output_file = tmp_path / 'msgcat.po'
192+
concat_cmd.input_files = [str(temp1), str(temp2)]
193+
concat_cmd.output_file = str(output_file)
194+
concat_cmd.more_than = 1
195+
concat_cmd.finalize_options()
196+
concat_cmd.run()
197+
198+
content = output_file.read_text()
199+
200+
assert 'msgid "other1"' not in content
201+
assert 'msgid "other3"' not in content
202+
assert 'msgid "same"' in content
203+
assert 'msgid "almost_same"' in content
204+
assert 'msgid "plural"' in content
205+
206+
almost_same_block = next(b for b in content.split('\n\n') if 'msgid "almost_same"' in b)
207+
assert 'fuzzy' in almost_same_block
208+
209+
210+
def test_no_wrap_width_exclusive(concat_cmd, po_files):
211+
temp1, _ = po_files
212+
concat_cmd.input_files = [str(temp1)]
213+
concat_cmd.no_wrap = True
214+
concat_cmd.width = 80
215+
with pytest.raises(OptionError):
216+
concat_cmd.finalize_options()
217+
218+
219+
def test_stdout_output(concat_cmd, po_files, monkeypatch):
220+
temp1, _ = po_files
221+
concat_cmd.input_files = [str(temp1)]
222+
concat_cmd.finalize_options()
223+
224+
buf = io.BytesIO()
225+
monkeypatch.setattr(sys, 'stdout', type('FakeStdout', (), {'buffer': buf})())
226+
concat_cmd.run()
227+
228+
content = buf.getvalue().decode('utf-8')
229+
assert 'msgid "other1"' in content
230+
assert 'msgstr "Other 1"' in content
231+
assert 'msgid "same"' in content
232+
233+
234+
def test_stdout_dash(concat_cmd, po_files, monkeypatch):
235+
temp1, _ = po_files
236+
concat_cmd.input_files = [str(temp1)]
237+
concat_cmd.output_file = '-'
238+
concat_cmd.finalize_options()
239+
240+
buf = io.BytesIO()
241+
monkeypatch.setattr(sys, 'stdout', type('FakeStdout', (), {'buffer': buf})())
242+
concat_cmd.run()
243+
244+
content = buf.getvalue().decode('utf-8')
245+
assert 'msgid "other1"' in content
246+
247+
248+
def test_same_string_no_conflict(concat_cmd, po_files, tmp_path):
249+
temp1, temp2 = po_files
250+
output_file = tmp_path / 'msgcat.po'
251+
concat_cmd.input_files = [str(temp1), str(temp2)]
252+
concat_cmd.output_file = str(output_file)
253+
concat_cmd.finalize_options()
254+
concat_cmd.run()
255+
256+
content = output_file.read_text()
257+
same_block = next(b for b in content.split('\n\n') if 'msgid "same"' in b)
258+
assert 'fuzzy' not in same_block
259+
assert '#-#-#-#-#' not in same_block
260+
assert 'msgstr "Same"' in same_block
261+
262+
263+
def test_no_location(concat_cmd, po_files, tmp_path):
264+
temp1, temp2 = po_files
265+
output_file = tmp_path / 'msgcat.po'
266+
concat_cmd.input_files = [str(temp1), str(temp2)]
267+
concat_cmd.output_file = str(output_file)
268+
concat_cmd.no_location = True
269+
concat_cmd.finalize_options()
270+
concat_cmd.run()
271+
272+
content = output_file.read_text()
273+
assert '#: ' not in content
274+
assert 'msgid "other1"' in content
275+
276+
277+
def test_sort_output(concat_cmd, po_files, tmp_path):
278+
temp1, temp2 = po_files
279+
output_file = tmp_path / 'msgcat.po'
280+
concat_cmd.input_files = [str(temp1), str(temp2)]
281+
concat_cmd.output_file = str(output_file)
282+
concat_cmd.sort_output = True
283+
concat_cmd.finalize_options()
284+
concat_cmd.run()
285+
286+
content = output_file.read_text()
287+
msgid_positions = {
288+
'almost_same': content.index('msgid "almost_same"'),
289+
'other1': content.index('msgid "other1"'),
290+
'other2': content.index('msgid "other2"'),
291+
'other3': content.index('msgid "other3"'),
292+
'other4': content.index('msgid "other4"'),
293+
'same': content.index('msgid "same"'),
294+
}
295+
ordered = sorted(msgid_positions, key=msgid_positions.get)
296+
assert ordered == ['almost_same', 'other1', 'other2', 'other3', 'other4', 'same']
297+
298+
299+
def test_single_input_file(concat_cmd, po_files, tmp_path):
300+
temp1, _ = po_files
301+
output_file = tmp_path / 'msgcat.po'
302+
concat_cmd.input_files = [str(temp1)]
303+
concat_cmd.output_file = str(output_file)
304+
concat_cmd.finalize_options()
305+
concat_cmd.run()
306+
307+
content = output_file.read_text()
308+
assert 'msgid "other1"' in content
309+
assert 'msgid "other2"' in content
310+
assert 'msgid "same"' in content
311+
assert '#-#-#-#-#' not in content
312+
assert 'fuzzy' not in content
313+
314+
315+
def test_unique_exclusive_with_more_than_nonzero(concat_cmd, po_files):
316+
temp1, temp2 = po_files
317+
concat_cmd.input_files = [str(temp1), str(temp2)]
318+
concat_cmd.unique = True
319+
concat_cmd.more_than = 0
320+
concat_cmd.finalize_options()
321+
322+
323+
def test_conflicted_po_raises_on_read(tmp_path):
324+
from babel.messages.pofile import PoFileError, read_po
325+
326+
conflicted = tmp_path / 'conflicted.po'
327+
conflicted.write_text(
328+
'msgid "hello"\n'
329+
'#-#-#-#-# file1.po (PROJECT 1.0) #-#-#-#-#\n'
330+
'msgstr "Hello"\n'
331+
)
332+
with pytest.raises(PoFileError):
333+
with open(conflicted) as f:
334+
read_po(f, abort_invalid=True)

0 commit comments

Comments
 (0)