Merge lp:~jtv/launchpad/recife-diverge into lp:~launchpad/launchpad/recife

Proposed by Jeroen T. Vermeulen on 2010-09-02
Status: Merged
Merged at revision: 9168
Proposed branch: lp:~jtv/launchpad/recife-diverge
Merge into: lp:~launchpad/launchpad/recife
Diff against target: 380 lines (+288/-12)
6 files modified
lib/lp/testing/factory.py (+5/-11)
lib/lp/testing/tests/test_factory.py (+0/-1)
lib/lp/translations/interfaces/translationmessage.py (+3/-0)
lib/lp/translations/model/potmsgset.py (+64/-0)
lib/lp/translations/model/translationmessage.py (+9/-0)
lib/lp/translations/tests/test_translationmessage.py (+207/-0)
To merge this branch: bzr merge lp:~jtv/launchpad/recife-diverge
Reviewer Review Type Date Requested Status
Michael Nelson (community) code 2010-09-02 Approve on 2010-09-09
Review via email: mp+34407@code.launchpad.net

Commit Message

TranslationMessage.approveAsDiverged

Description of the Change

= approveAsDiverged =

For the Recife feature branch. This implements the option to approve a translation suggestion, not as the new shared translation as you'd normally want, but as a diverged translation specific to one particular POTemplate.

Interface-wise, the new method is on TranslationMessage: "approve this one in this POFile." The actual implementation is in POTMsgSet, where all the supporting infrastructure is, and the published one forwards to it.

To test:
{{{
./bin/test -vvc lp.translations.tests.test_translationmessage -t ApproveAsDiverged
}}}

(Although of course I ran the rest of 'em as well).

I also seized this opportunity to replace a factory method, previously a hack to work around the lack of this new method, to do things more properly.

No lint,

Jeroen

To post a comment you must log in.
Michael Nelson (michael.nelson) wrote :
Download full text (17.1 KiB)

Hi Jeroen,

As a person not-so-familiar with the translations infrastructure, I find a lot about this branch hard to understand... but I'm not sure if they are things that are just due to the complexity of the problem, or could be improved by the vocabulary. See what you think. To me it seems to be going the direction that soyuz went a while ago (methods that are hard for anyone without insider knowledge to understand) and we've been trying to make it easier to maintain ever since.

Also, if possible, it would be great if approvedAsDiverged() could be simplified (by creating smaller methods that encapsulate sections of it... some thoughts below).

That said, if you look at what I've written and think it doesn't apply, I'll recant and approve - but I would love to be able to understand more easily what's going on in this branch.

> === modified file 'lib/lp/testing/factory.py'
> --- lib/lp/testing/factory.py 2010-09-01 13:47:24 +0000
> +++ lib/lp/testing/factory.py 2010-09-02 12:37:46 +0000
> @@ -2300,18 +2298,16 @@
> """Create a diverged, current `TranslationMessage`."""
> if pofile is None:
> pofile = self.makePOFile('lt')
> + if reviewer is None:
> + reviewer = self.makePerson()
>
> # This creates a suggestion, then diverges it, then activates it.
> # Once we have a method for diverging messages, do this in a more
> # proper way.

With your changes below, is it time to update the above comment?

> - message = self.makeSharedTranslationMessage(
> + message = self.makeSuggestion(
> pofile=pofile, potmsgset=potmsgset, translator=translator,
> - reviewer=reviewer, translations=translations, suggestion=True)
> - traits = getUtility(ITranslationSideTraitsSet).getTraits(
> - pofile.potemplate.translation_side)
> - removeSecurityProxy(message).potemplate = pofile.potemplate
> - traits.setFlag(message, True)
> - return message
> + translations=translations)
> + return message.approveAsDiverged(pofile, reviewer)
>
> def makeTranslation(self, pofile, sequence,
> english=None, translated=None,
>
> === modified file 'lib/lp/translations/model/potmsgset.py'
> --- lib/lp/translations/model/potmsgset.py 2010-09-01 15:11:18 +0000
> +++ lib/lp/translations/model/potmsgset.py 2010-09-02 12:37:46 +0000
> @@ -1145,6 +1145,61 @@
> template.awardKarma(translator, 'translationsuggestionapproved')
> template.awardKarma(reviewer, 'translationreview')
>
> + def approveAsDiverged(self, pofile, suggestion, reviewer,
> + lock_timestamp=None):
> + """Approve a suggestion to become a diverged translation."""
> + template = pofile.potemplate
> + traits = getUtility(ITranslationSideTraitsSet).getTraits(
> + template.translation_side)
> + if suggestion.potemplate == template and traits.getFlag(suggestion):
> + # The suggestion is already current and diverged for the
> + # right template.
> + return suggestion

Could this be extracted (so that this method...

review: Needs Information (code)
Данило Шеган (danilo) wrote :

У чет, 02. 09 2010. у 13:55 +0000, Michael Nelson пише:
>
> Should it be obvious to me what traits.setFlag(message, True) actually
> means, without going and looking it up?

It should at least be "more obvious". We should probably make the
method be named "traits.setCurrent(message, value)", but that would be a
different branch ("make the traits class understandable"), because
setFlags was not introduced with this branch.

Anyway, I agree we should make code more understandable and I am sure
Jeroen and you can make a good job out of it :)

> + def test_makeCounterpartPOFile(self):
> > + # self.factory.makePOFile makes POFiles on the upstream
side by
> > + # default. self._makeCounterpartPOFile makes POFiles on
the
> > + # Ubuntu side.
> > + pofile = self.factory.makePOFile('es')
> > + other_pofile = self._makeCounterpartPOFile(pofile)
> > +
> > + self.assertEqual(pofile.language, other_pofile.language)
> > + self.assertNotEqual(
> > + pofile.potemplate.translation_side,
> > + other_pofile.potemplate.translation_side)
>
> Wow... a test for your factory method.

That should be a norm these days (perhaps worth raising in the reviewers
meeting if not everybody is aware of it :). Though, that's for "real"
factory methods with tests in lib/lp/testing/tests/test_factory.py

Jeroen T. Vermeulen (jtv) wrote :
Download full text (12.3 KiB)

Hi Michael,

As I mentioned on IRC when you started on this, it's a good thing to get an outside view from time to time. Like Soyuz, Translations suffers from a lot of "inside knowledge" that one needs before certain things become clear. We'll try to come up with a clearer naming pattern for the TranslationSideTraits methods that wrap the TranslationMessage "is_current" flags.

I greatly simplified approveAsDiverged, leaving a sequence of top-level "if"s and two early return statements as the only signs of structural complexity.

With that, on to your individual notes:

> > === modified file 'lib/lp/testing/factory.py'
> > --- lib/lp/testing/factory.py 2010-09-01 13:47:24 +0000
> > +++ lib/lp/testing/factory.py 2010-09-02 12:37:46 +0000
> > @@ -2300,18 +2298,16 @@
> > """Create a diverged, current `TranslationMessage`."""
> > if pofile is None:
> > pofile = self.makePOFile('lt')
> > + if reviewer is None:
> > + reviewer = self.makePerson()
> >
> > # This creates a suggestion, then diverges it, then activates it.
> > # Once we have a method for diverging messages, do this in a more
> > # proper way.
>
> With your changes below, is it time to update the above comment?

Yes, thank you for spotting that. This is exactly the change that that comment foreshadowed.

> > === modified file 'lib/lp/translations/model/potmsgset.py'
> > --- lib/lp/translations/model/potmsgset.py 2010-09-01 15:11:18 +0000
> > +++ lib/lp/translations/model/potmsgset.py 2010-09-02 12:37:46 +0000
> > @@ -1145,6 +1145,61 @@
> > template.awardKarma(translator,
> 'translationsuggestionapproved')
> > template.awardKarma(reviewer, 'translationreview')
> >
> > + def approveAsDiverged(self, pofile, suggestion, reviewer,
> > + lock_timestamp=None):
> > + """Approve a suggestion to become a diverged translation."""
> > + template = pofile.potemplate
> > + traits = getUtility(ITranslationSideTraitsSet).getTraits(
> > + template.translation_side)
> > + if suggestion.potemplate == template and
> traits.getFlag(suggestion):
> > + # The suggestion is already current and diverged for the
> > + # right template.
> > + return suggestion
>
> Could this be extracted (so that this method doesn't deal with traits and
> flags - which I have no idea about), into
> suggestion.isCurrentAndDiverged(pofile.potemplate) or similar? Eg.

I was unable to extract the traits and the flags, because they are at the very core of what this message does. But you could say I managed to simplify everything else so that the intent of the traits-related code stands out more.

Also, I introduced some explanatory variables. This is a say-it-in-code pattern: give an expression or statement a name, not because it's long or complex or highly reusable, but because it allows the code that uses it to be phrased in terms of intent rather than implementationn.

As a relevant example, we used to say "foo.potemplate is None" instead of "foo.is_diverged" and that worked relatively well, but the introduction of the i...

Michael Nelson (michael.nelson) wrote :
Download full text (13.0 KiB)

On Wed, Sep 8, 2010 at 5:16 AM, Jeroen T. Vermeulen <email address hidden> wrote:
> Hi Michael,
>
> As I mentioned on IRC when you started on this, it's a good thing to get an outside view from time to time.  Like Soyuz, Translations suffers from a lot of "inside knowledge" that one needs before certain things become clear.  We'll try to come up with a clearer naming pattern for the TranslationSideTraits methods that wrap the TranslationMessage "is_current" flags.
>
> I greatly simplified approveAsDiverged, leaving a sequence of top-level "if"s and two early return statements as the only signs of structural complexity.

Great! Thanks Jeroen. In summary, it's much easier for me to
understand now. Approved, with a few comments that you can
take-or-leave below.

Cheers,
Michael

>
> With that, on to your individual notes:
>
[snip]
>
>> > === modified file 'lib/lp/translations/model/potmsgset.py'
>> > --- lib/lp/translations/model/potmsgset.py    2010-09-01 15:11:18 +0000
>> > +++ lib/lp/translations/model/potmsgset.py    2010-09-02 12:37:46 +0000
>> > @@ -1145,6 +1145,61 @@
>> >              template.awardKarma(translator,
>> 'translationsuggestionapproved')
>> >              template.awardKarma(reviewer, 'translationreview')
>> >
>> > +    def approveAsDiverged(self, pofile, suggestion, reviewer,
>> > +                          lock_timestamp=None):
>> > +        """Approve a suggestion to become a diverged translation."""
>> > +        template = pofile.potemplate
>> > +        traits = getUtility(ITranslationSideTraitsSet).getTraits(
>> > +            template.translation_side)
>> > +        if suggestion.potemplate == template and
>> traits.getFlag(suggestion):
>> > +            # The suggestion is already current and diverged for the
>> > +            # right template.
>> > +            return suggestion
>>
>> Could this be extracted (so that this method doesn't deal with traits and
>> flags - which I have no idea about), into
>> suggestion.isCurrentAndDiverged(pofile.potemplate) or similar? Eg.
>
> I was unable to extract the traits and the flags, because they are at the very core of what this message does.  But you could say I managed to simplify everything else so that the intent of the traits-related code stands out more.
>
> Also, I introduced some explanatory variables.  This is a say-it-in-code pattern: give an expression or statement a name, not because it's long or complex or highly reusable, but because it allows the code that uses it to be phrased in terms of intent rather than implementationn.

Yes, that makes a huge difference! Small point: you could move the
definition of used_on_other_side down to where it is first checked.

>
> As a relevant example, we used to say "foo.potemplate is None" instead of "foo.is_diverged" and that worked relatively well, but the introduction of the is_diverged attribute made a lot of code much clearer despite hiding the relationship between divergence and potemplate.
>
>
>> > +
>> > +        incumbent = traits.getCurrentMessage(self, template,
>> pofile.language)
>> > +        if incumbent is not None:
>> > +            self._checkForConflict(incumbent, lock_timestamp)
>> > +            if incumben...

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/testing/factory.py'
2--- lib/lp/testing/factory.py 2010-09-06 10:40:54 +0000
3+++ lib/lp/testing/factory.py 2010-09-08 02:43:41 +0000
4@@ -250,7 +250,6 @@
5 time_counter,
6 )
7 from lp.translations.interfaces.potemplate import IPOTemplateSet
8-from lp.translations.interfaces.side import ITranslationSideTraitsSet
9 from lp.translations.interfaces.translationfileformat import (
10 TranslationFileFormat,
11 )
12@@ -2393,18 +2392,13 @@
13 """Create a diverged, current `TranslationMessage`."""
14 if pofile is None:
15 pofile = self.makePOFile('lt')
16+ if reviewer is None:
17+ reviewer = self.makePerson()
18
19- # This creates a suggestion, then diverges it, then activates it.
20- # Once we have a method for diverging messages, do this in a more
21- # proper way.
22- message = self.makeSharedTranslationMessage(
23+ message = self.makeSuggestion(
24 pofile=pofile, potmsgset=potmsgset, translator=translator,
25- reviewer=reviewer, translations=translations, suggestion=True)
26- traits = getUtility(ITranslationSideTraitsSet).getTraits(
27- pofile.potemplate.translation_side)
28- removeSecurityProxy(message).potemplate = pofile.potemplate
29- traits.setFlag(message, True)
30- return message
31+ translations=translations)
32+ return message.approveAsDiverged(pofile, reviewer)
33
34 def makeTranslation(self, pofile, sequence,
35 english=None, translated=None,
36
37=== modified file 'lib/lp/testing/tests/test_factory.py'
38--- lib/lp/testing/tests/test_factory.py 2010-09-06 10:40:54 +0000
39+++ lib/lp/testing/tests/test_factory.py 2010-09-08 02:43:41 +0000
40@@ -6,7 +6,6 @@
41 __metaclass__ = type
42
43 from datetime import datetime
44-import unittest
45
46 import pytz
47 from zope.component import getUtility
48
49=== modified file 'lib/lp/translations/interfaces/translationmessage.py'
50--- lib/lp/translations/interfaces/translationmessage.py 2010-09-06 10:40:54 +0000
51+++ lib/lp/translations/interfaces/translationmessage.py 2010-09-08 02:43:41 +0000
52@@ -265,6 +265,9 @@
53 lock_timestamp=None):
54 """Approve this suggestion, making it a current translation."""
55
56+ def approveAsDiverged(pofile, reviewer, lock_timestamp=None):
57+ """Approve this suggestion, as a diverged translation."""
58+
59 # XXX CarlosPerelloMarin 20071022: We should move this into browser code.
60 def makeHTMLID(description):
61 """Unique identifier for self, suitable for use in HTML element ids.
62
63=== modified file 'lib/lp/translations/model/potmsgset.py'
64--- lib/lp/translations/model/potmsgset.py 2010-09-06 10:40:54 +0000
65+++ lib/lp/translations/model/potmsgset.py 2010-09-08 02:43:41 +0000
66@@ -1144,6 +1144,70 @@
67 template.awardKarma(translator, 'translationsuggestionapproved')
68 template.awardKarma(reviewer, 'translationreview')
69
70+ def _cloneAndDiverge(self, original_message, pofile):
71+ """Create a diverged clone of a `TranslationMessage`.
72+
73+ The message is not made current; the caller must do so in order
74+ to keep the message in a consistent state.
75+ """
76+ potranslations = self._findPOTranslations(
77+ dict(enumerate(original_message.translations)))
78+ message = self._makeTranslationMessage(
79+ pofile, original_message.submitter, potranslations,
80+ original_message.origin, diverged=True)
81+ return message
82+
83+ def approveAsDiverged(self, pofile, suggestion, reviewer,
84+ lock_timestamp=None):
85+ """Approve a suggestion to become a diverged translation."""
86+ template = pofile.potemplate
87+ traits = getUtility(ITranslationSideTraitsSet).getTraits(
88+ template.translation_side)
89+
90+ diverged = suggestion.is_diverged
91+ used_here = traits.getFlag(suggestion)
92+ used_on_other_side = traits.other_side_traits.getFlag(suggestion)
93+
94+ if used_here and suggestion.potemplate == template:
95+ # The suggestion is already current and diverged for the
96+ # right template.
97+ return suggestion
98+
99+ incumbent = traits.getCurrentMessage(self, template, pofile.language)
100+ if incumbent is not None:
101+ # Ensure that the current message hasn't changed from the
102+ # state the reviewer inspected before making this change.
103+ self._checkForConflict(incumbent, lock_timestamp)
104+
105+ if incumbent is not None and incumbent.is_diverged:
106+ # The incumbent is also diverged, so it's in the way of the
107+ # suggestion we're trying to diverge. Disable it.
108+ traits.setFlag(incumbent, False)
109+ incumbent.markReviewed(reviewer)
110+ incumbent.shareIfPossible()
111+ pofile.markChanged()
112+
113+ if used_here and not diverged and not used_on_other_side:
114+ # This message is already the shared current message. If it
115+ # was previously masked by a diverged message, it no longer
116+ # is. This is probably the behaviour the user would expect.
117+ return suggestion
118+
119+ if used_here or used_on_other_side:
120+ # The suggestion is already current somewhere else. Can't
121+ # reuse it as a diverged message in this template, so clone
122+ # it.
123+ message = self._cloneAndDiverge(suggestion, pofile)
124+ else:
125+ # No obstacles. Diverge.
126+ message = suggestion
127+ message.potemplate = template
128+
129+ traits.setFlag(message, True)
130+ message.markReviewed(reviewer)
131+ pofile.markChanged()
132+ return message
133+
134 def setCurrentTranslation(self, pofile, submitter, translations, origin,
135 share_with_other_side=False,
136 lock_timestamp=None):
137
138=== modified file 'lib/lp/translations/model/translationmessage.py'
139--- lib/lp/translations/model/translationmessage.py 2010-09-06 10:40:54 +0000
140+++ lib/lp/translations/model/translationmessage.py 2010-09-08 02:43:41 +0000
141@@ -175,6 +175,10 @@
142 """See `ITranslationMessage`."""
143 raise NotImplementedError()
144
145+ def approveAsDiverged(self, *args, **kwargs):
146+ """See `ITranslationMessage`."""
147+ raise NotImplementedError()
148+
149 def getOnePOFile(self):
150 """See `ITranslationMessage`."""
151 return None
152@@ -352,6 +356,11 @@
153 share_with_other_side=share_with_other_side,
154 lock_timestamp=lock_timestamp)
155
156+ def approveAsDiverged(self, pofile, reviewer, lock_timestamp=None):
157+ """See `ITranslationMessage`."""
158+ return self.potmsgset.approveAsDiverged(
159+ pofile, self, reviewer, lock_timestamp=lock_timestamp)
160+
161 def getOnePOFile(self):
162 """See `ITranslationMessage`."""
163 from lp.translations.model.pofile import POFile
164
165=== modified file 'lib/lp/translations/tests/test_translationmessage.py'
166--- lib/lp/translations/tests/test_translationmessage.py 2010-08-24 11:39:06 +0000
167+++ lib/lp/translations/tests/test_translationmessage.py 2010-09-08 02:43:41 +0000
168@@ -269,6 +269,213 @@
169 pofile, self.factory.makePerson(), lock_timestamp=old)
170
171
172+class TestApproveAsDiverged(TestCaseWithFactory):
173+ """Tests for `TranslationMessage.approveAsDiverged`."""
174+
175+ layer = ZopelessDatabaseLayer
176+
177+ def _makeCounterpartPOFile(self, pofile):
178+ """Make a `POFile` on the opposite translation side.
179+
180+ :param pofile: A `POFile` to match. Assumed to be on the
181+ "upstream" translation side.
182+ """
183+ assert pofile.potemplate.productseries is not None, (
184+ "This test needs a product template; got a package template.""")
185+ other_template = self.factory.makePOTemplate(
186+ distroseries=self.factory.makeDistroSeries(),
187+ sourcepackagename=self.factory.makeSourcePackageName())
188+
189+ return self.factory.makePOFile(
190+ pofile.language.code, potemplate=other_template)
191+
192+ def test_makeCounterpartPOFile(self):
193+ # self.factory.makePOFile makes POFiles on the upstream side by
194+ # default. self._makeCounterpartPOFile makes POFiles on the
195+ # Ubuntu side.
196+ pofile = self.factory.makePOFile('es')
197+ other_pofile = self._makeCounterpartPOFile(pofile)
198+
199+ self.assertEqual(pofile.language, other_pofile.language)
200+ self.assertNotEqual(
201+ pofile.potemplate.translation_side,
202+ other_pofile.potemplate.translation_side)
203+
204+ def test_no_change(self):
205+ # Approving and diverging a message that's already active and
206+ # diverged for this POFile does nothing. Even the reviewer
207+ # stays unchanged.
208+ translator = self.factory.makePerson()
209+ original_reviewer = self.factory.makePerson()
210+ later_reviewer = self.factory.makePerson()
211+ pofile = self.factory.makePOFile('es_AR')
212+ message = self.factory.makeDivergedTranslationMessage(
213+ pofile=pofile, translator=translator, reviewer=original_reviewer)
214+
215+ resulting_message = message.approveAsDiverged(pofile, later_reviewer)
216+
217+ self.assertEqual(message, resulting_message)
218+ self.assertEqual(translator, message.submitter)
219+ self.assertEqual(original_reviewer, message.reviewer)
220+ self.assertEqual(pofile.potemplate, message.potemplate)
221+
222+ def test_activates_and_diverges(self):
223+ # Approving a simple suggestion activates and diverges it.
224+ pofile = self.factory.makePOFile('es_BO')
225+ suggestion = self.factory.makeSuggestion(pofile=pofile)
226+
227+ resulting_message = suggestion.approveAsDiverged(
228+ pofile, self.factory.makePerson())
229+
230+ self.assertEqual(suggestion, resulting_message)
231+ self.assertTrue(suggestion.is_current_upstream)
232+ self.assertTrue(suggestion.is_diverged)
233+ self.assertEqual(pofile.potemplate, suggestion.potemplate)
234+
235+ def test_activating_reviews(self):
236+ # Activating a message marks it as reviewed.
237+ pofile = self.factory.makePOFile('es_BO')
238+ reviewer = self.factory.makePerson()
239+ suggestion = self.factory.makeSuggestion(pofile=pofile)
240+
241+ resulting_message = suggestion.approveAsDiverged(pofile, reviewer)
242+
243+ self.assertEqual(reviewer, resulting_message.reviewer)
244+
245+ def test_diverge_current_shared_leaves_message_intact(self):
246+ # Calling approveAsDiverged on the current shared translation
247+ # leaves it untouched.
248+ original_reviewer = self.factory.makePerson()
249+ later_reviewer = self.factory.makePerson()
250+ pofile = self.factory.makePOFile('es_CL')
251+ message = self.factory.makeCurrentTranslationMessage(
252+ pofile=pofile, reviewer=original_reviewer)
253+
254+ resulting_message = message.approveAsDiverged(pofile, later_reviewer)
255+
256+ self.assertEqual(message, resulting_message)
257+ self.assertEqual(original_reviewer, message.reviewer)
258+ self.assertFalse(message.is_diverged)
259+
260+ def test_diverge_current_shared_message_unmasks_it(self):
261+ # Calling approveAsDiverged on the current shared translation
262+ # deactivates any diverged message that may be masking it.
263+ pofile = self.factory.makePOFile('es_CO')
264+ reviewer = self.factory.makePerson()
265+ shared = self.factory.makeCurrentTranslationMessage(pofile=pofile)
266+ diverged = self.factory.makeDivergedTranslationMessage(
267+ pofile=pofile, potmsgset=shared.potmsgset)
268+
269+ self.assertTrue(shared.is_current_upstream)
270+ self.assertTrue(diverged.is_current_upstream)
271+ self.assertFalse(shared.is_diverged)
272+ self.assertTrue(diverged.is_diverged)
273+
274+ shared.approveAsDiverged(pofile, reviewer)
275+
276+ self.assertTrue(shared.is_current_upstream)
277+ self.assertFalse(diverged.is_current_upstream)
278+ self.assertFalse(shared.is_diverged)
279+
280+ def test_does_not_affect_other_side(self):
281+ # Approving a message that's current on the other side clones
282+ # it, so that the other side remains unaffected by this local
283+ # change.
284+ reviewer = self.factory.makePerson()
285+ pofile = self.factory.makePOFile('es_CR')
286+ other_pofile = self._makeCounterpartPOFile(pofile)
287+ suggestion = self.factory.makeCurrentTranslationMessage(
288+ pofile=other_pofile)
289+ self.assertEqual(
290+ (False, True),
291+ (suggestion.is_current_upstream, suggestion.is_current_ubuntu))
292+
293+ resulting_message = suggestion.approveAsDiverged(pofile, reviewer)
294+
295+ self.assertNotEqual(suggestion, resulting_message)
296+ self.assertEqual(pofile.potemplate, resulting_message.potemplate)
297+ self.assertFalse(suggestion.is_diverged)
298+ self.assertTrue(resulting_message.is_current_upstream)
299+ self.assertEqual(
300+ (False, True),
301+ (suggestion.is_current_upstream, suggestion.is_current_ubuntu))
302+
303+ def test_does_not_affect_diverged_elsewhere(self):
304+ # Approving a message that's current and diverged to another
305+ # template clones it, so that the other template remains
306+ # unaffected by this local change.
307+ reviewer = self.factory.makePerson()
308+ pofile = self.factory.makePOFile('es_DO')
309+ elsewhere = self.factory.makePOFile('es_DO')
310+
311+ suggestion = self.factory.makeDivergedTranslationMessage(
312+ pofile=elsewhere)
313+
314+ resulting_message = suggestion.approveAsDiverged(pofile, reviewer)
315+
316+ self.assertNotEqual(suggestion, resulting_message)
317+ self.assertEqual(pofile.potemplate, resulting_message.potemplate)
318+ self.assertEqual(elsewhere.potemplate, suggestion.potemplate)
319+ self.assertTrue(resulting_message.is_current_upstream)
320+ self.assertTrue(suggestion.is_current_upstream)
321+
322+ def test_detects_conflict(self):
323+ # Trying to approve and diverge a message based on outdated
324+ # information raises TranslationConflict.
325+ reviewer = self.factory.makePerson()
326+ pofile = self.factory.makePOFile('es_EC')
327+ suggestion = self.factory.makeSuggestion(pofile=pofile)
328+ current = self.factory.makeDivergedTranslationMessage(
329+ pofile=pofile, potmsgset=suggestion.potmsgset)
330+ earlier = current.date_reviewed - timedelta(1)
331+
332+ self.assertRaises(
333+ TranslationConflict,
334+ suggestion.approveAsDiverged,
335+ pofile, reviewer, lock_timestamp=earlier)
336+
337+ def test_passes_conflict_check_if_no_conflict(self):
338+ # Trying to approve and diverge a message based on up-to-date
339+ # works without raising a conflict.
340+ reviewer = self.factory.makePerson()
341+ pofile = self.factory.makePOFile('es_GT')
342+ suggestion = self.factory.makeSuggestion(pofile=pofile)
343+ current = self.factory.makeDivergedTranslationMessage(
344+ pofile=pofile, potmsgset=suggestion.potmsgset)
345+ later = current.date_reviewed + timedelta(1)
346+
347+ suggestion.approveAsDiverged(pofile, reviewer, lock_timestamp=later)
348+
349+ self.assertTrue(suggestion.is_current_upstream)
350+
351+ def test_passes_conflict_check_if_same_translations(self):
352+ # Trying to approve and diverge a message based on outdated
353+ # information works just fine if in the meantime the suggestion
354+ # has become the diverged current translation.
355+ reviewer = self.factory.makePerson()
356+ pofile = self.factory.makePOFile('es_HN')
357+ current = self.factory.makeDivergedTranslationMessage(pofile=pofile)
358+ earlier = current.date_reviewed - timedelta(1)
359+
360+ current.approveAsDiverged(pofile, reviewer, lock_timestamp=earlier)
361+
362+ self.assertTrue(current.is_current_upstream)
363+
364+ def test_updates_pofile(self):
365+ # Approving a message as a diverged translation marks the POFile
366+ # as updated.
367+ reviewer = self.factory.makePerson()
368+ pofile = self.factory.makePOFile('es_MX')
369+ suggestion = self.factory.makeSuggestion(pofile=pofile)
370+ earlier = pofile.date_changed - timedelta(1)
371+ pofile.markChanged(timestamp=earlier)
372+
373+ self.assertEqual(earlier, pofile.date_changed)
374+ suggestion.approveAsDiverged(pofile, reviewer)
375+ self.assertNotEqual(earlier, pofile.date_changed)
376+ self.assertEqual(suggestion.date_reviewed, pofile.date_changed)
377+
378+
379 class TestTranslationMessageFindIdenticalMessage(TestCaseWithFactory):
380 """Tests for `TranslationMessage.findIdenticalMessage`."""
381

Subscribers

People subscribed via source and target branches