Merge lp:~jtv/launchpad/recife-check-conflicts into lp:~launchpad/launchpad/recife

Proposed by Jeroen T. Vermeulen
Status: Merged
Approved by: Graham Binns
Approved revision: no longer in the source branch.
Merged at revision: 9163
Proposed branch: lp:~jtv/launchpad/recife-check-conflicts
Merge into: lp:~launchpad/launchpad/recife
Prerequisite: lp:~jtv/launchpad/recife-retire-traits-helpers
Diff against target: 469 lines (+219/-27)
7 files modified
lib/lp/translations/interfaces/potmsgset.py (+8/-2)
lib/lp/translations/interfaces/translationmessage.py (+2/-1)
lib/lp/translations/model/potmsgset.py (+98/-15)
lib/lp/translations/model/translationmessage.py (+4/-2)
lib/lp/translations/tests/test_clearcurrenttranslation.py (+14/-1)
lib/lp/translations/tests/test_potmsgset.py (+77/-5)
lib/lp/translations/tests/test_translationmessage.py (+16/-1)
To merge this branch: bzr merge lp:~jtv/launchpad/recife-check-conflicts
Reviewer Review Type Date Requested Status
Graham Binns (community) code Approve
Review via email: mp+33494@code.launchpad.net

Commit message

Consistent support for conflict checks.

Description of the change

= Check for Translation Conflicts =

For the Recife feature branch. This implements more consistent support for translation conflict checks.

It's something we're already doing in the data model we have in production: when a translation is changed (whether through the web UI or through an import), a timestamp can be given that basically says, "this is when I looked at the state of this translation and decided to change it." The methods that change the translation can then check whether someone else might have set a new translation in the meantime; if they have, the later change is effectively obsolete.

Here I change the higher-level methods that set translations to accept a lock_timestamp, and check.

You'll notice that _checkForConflict builds on the existing _maybeRaiseTranslationConflict in a slightly awkward way. I hope we'll want to merge the two methods in the future to make this easier, but didn't want to pull in more changes for now. The third alternative would have been to check for identical translations _before_ calling _maybeRaiseTranslationConflict, but since this check is the most expensive step (and this is pretty "hot" code) I also wanted it to be the rarest.

To test:
{{{
./bin/test -vvc -m lp.translations.tests -t TestApprove -t CurrentTranslation
}}}

Jeroen

To post a comment you must log in.
Revision history for this message
Graham Binns (gmb) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/translations/interfaces/potmsgset.py'
--- lib/lp/translations/interfaces/potmsgset.py 2010-08-10 17:28:08 +0000
+++ lib/lp/translations/interfaces/potmsgset.py 2010-08-24 07:55:55 +0000
@@ -267,7 +267,8 @@
267 """267 """
268268
269 def setCurrentTranslation(pofile, submitter, translations, origin,269 def setCurrentTranslation(pofile, submitter, translations, origin,
270 share_with_other_side=False):270 share_with_other_side=False,
271 lock_timestamp=None):
271 """Set the message's translation in Ubuntu, or upstream, or both.272 """Set the message's translation in Ubuntu, or upstream, or both.
272273
273 :param pofile: `POFile` you're setting translations in. Other274 :param pofile: `POFile` you're setting translations in. Other
@@ -279,6 +280,8 @@
279 :param origin: A `RosettaTranslationOrigin`.280 :param origin: A `RosettaTranslationOrigin`.
280 :param share_with_other_side: When sharing this translation,281 :param share_with_other_side: When sharing this translation,
281 share it with the other `TranslationSide` as well.282 share it with the other `TranslationSide` as well.
283 :param lock_timestamp: Timestamp of the original translation state
284 that this change is based on.
282 """285 """
283286
284 def resetCurrentTranslation(pofile, lock_timestamp):287 def resetCurrentTranslation(pofile, lock_timestamp):
@@ -294,7 +297,8 @@
294 """297 """
295298
296 def clearCurrentTranslation(pofile, submitter, origin,299 def clearCurrentTranslation(pofile, submitter, origin,
297 share_with_other_side=False):300 share_with_other_side=False,
301 lock_timestamp=None):
298 """Set the current message in `pofile` to be untranslated.302 """Set the current message in `pofile` to be untranslated.
299303
300 If the current message is shared, this will also clear it in304 If the current message is shared, this will also clear it in
@@ -310,6 +314,8 @@
310 current on the other side (i.e. the Ubuntu side if working314 current on the other side (i.e. the Ubuntu side if working
311 upstream, or vice versa) then should it be cleared there as315 upstream, or vice versa) then should it be cleared there as
312 well?316 well?
317 :param lock_timestamp: Timestamp of the original translation state
318 that this change is based on.
313 """319 """
314320
315 def applySanityFixes(unicode_text):321 def applySanityFixes(unicode_text):
316322
=== modified file 'lib/lp/translations/interfaces/translationmessage.py'
--- lib/lp/translations/interfaces/translationmessage.py 2010-08-20 05:56:43 +0000
+++ lib/lp/translations/interfaces/translationmessage.py 2010-08-24 07:55:55 +0000
@@ -240,7 +240,8 @@
240 It must not be referenced by any other object.240 It must not be referenced by any other object.
241 """241 """
242242
243 def approve(pofile, reviewer, share_with_other_side=False):243 def approve(pofile, reviewer, share_with_other_side=False,
244 lock_timestamp=None):
244 """Approve this suggestion, making it a current translation."""245 """Approve this suggestion, making it a current translation."""
245246
246 # XXX CarlosPerelloMarin 20071022: We should move this into browser code.247 # XXX CarlosPerelloMarin 20071022: We should move this into browser code.
247248
=== modified file 'lib/lp/translations/model/potmsgset.py'
--- lib/lp/translations/model/potmsgset.py 2010-08-24 07:55:54 +0000
+++ lib/lp/translations/model/potmsgset.py 2010-08-24 07:55:55 +0000
@@ -37,8 +37,7 @@
37 IPOTMsgSet,37 IPOTMsgSet,
38 POTMsgSetInIncompatibleTemplatesError,38 POTMsgSetInIncompatibleTemplatesError,
39 TranslationCreditsType)39 TranslationCreditsType)
40from lp.translations.interfaces.side import (40from lp.translations.interfaces.side import ITranslationSideTraitsSet
41 ITranslationSideTraitsSet, TranslationSide)
42from lp.translations.interfaces.translationfileformat import (41from lp.translations.interfaces.translationfileformat import (
43 TranslationFileFormat)42 TranslationFileFormat)
44from lp.translations.interfaces.translationimporter import (43from lp.translations.interfaces.translationimporter import (
@@ -89,6 +88,22 @@
89incumbent_unknown = object()88incumbent_unknown = object()
9089
9190
91def dictify_msgstrs(potranslations):
92 """Represent `potranslations` as a normalized dict.
93
94 :param potranslations: a dict or sequence of `POTranslation`s per
95 plural form.
96 :return: a dict mapping each translated plural form to a
97 `POTranslation`. Untranslated forms are omitted.
98 """
99 if not isinstance(potranslations, dict):
100 potranslations = dict(enumerate(potranslations))
101 return dict(
102 (form, msgstr)
103 for form, msgstr in potranslations.iteritems()
104 if msgstr is not None)
105
106
92class POTMsgSet(SQLBase):107class POTMsgSet(SQLBase):
93 implements(IPOTMsgSet)108 implements(IPOTMsgSet)
94109
@@ -932,6 +947,60 @@
932 origin=RosettaTranslationOrigin.ROSETTAWEB, submitter=submitter,947 origin=RosettaTranslationOrigin.ROSETTAWEB, submitter=submitter,
933 **forms)948 **forms)
934949
950 def _checkForConflict(self, current_message, lock_timestamp,
951 potranslations=None):
952 """Check `message` for conflicting changes since `lock_timestamp`.
953
954 Call this before changing this message's translations, to ensure
955 that a read-modify-write operation on a message does not
956 accidentally overwrite newer changes based on older information.
957
958 One example of a read-modify-write operation is: user downloads
959 translation file, translates a message, then re-uploads.
960 Another is: user looks at a message in the web UI, decides that
961 neither the current translation nor any of the suggestions are
962 right, and clears the message.
963
964 In these scenarios, it's possible for someone else to come along
965 and change the message's translation between the time we provide
966 the user with a view of the current state and the time we
967 receive a change from the user. We call this a conflict.
968
969 Raises `TranslationConflict` if a conflict exists.
970
971 :param currentmessage: The `TranslationMessage` that is current
972 now. This is where we'll see any conflicting changes
973 reflected in the date_reviewed timestamp.
974 :param lock_timestamp: The timestamp of the translation state
975 that the change is based on.
976 :param potranslations: `POTranslation`s dict for the new
977 translation. If these are given, and identical to those of
978 `current_message`, there is no conflict.
979 """
980 if lock_timestamp is None:
981 # We're not really being asked to check for conflicts.
982 return
983 if current_message is None:
984 # There is no current message to conflict with.
985 return
986 try:
987 self._maybeRaiseTranslationConflict(
988 current_message, lock_timestamp)
989 except TranslationConflict:
990 if potranslations is None:
991 # We don't know what translations are going to be set;
992 # based on the timestamps this is a conflict.
993 raise
994 old_msgstrs = dictify_msgstrs(current_message.all_msgstrs)
995 new_msgstrs = dictify_msgstrs(potranslations)
996 if new_msgstrs != old_msgstrs:
997 # Yup, there really is a difference. This is a proper
998 # conflict.
999 raise
1000 else:
1001 # Two identical translations crossed. Not a conflict.
1002 pass
1003
935 def _maybeRaiseTranslationConflict(self, message, lock_timestamp):1004 def _maybeRaiseTranslationConflict(self, message, lock_timestamp):
936 """Checks if there is a translation conflict for the message.1005 """Checks if there is a translation conflict for the message.
9371006
@@ -1008,7 +1077,7 @@
1008 **translation_args)1077 **translation_args)
10091078
1010 def approveSuggestion(self, pofile, suggestion, reviewer,1079 def approveSuggestion(self, pofile, suggestion, reviewer,
1011 share_with_other_side=False):1080 share_with_other_side=False, lock_timestamp=None):
1012 """Approve a suggestion.1081 """Approve a suggestion.
10131082
1014 :param pofile: The `POFile` that the suggestion is being approved for.1083 :param pofile: The `POFile` that the suggestion is being approved for.
@@ -1017,6 +1086,8 @@
1017 suggestion.1086 suggestion.
1018 :param share_with_other_side: Policy selector: share this change with1087 :param share_with_other_side: Policy selector: share this change with
1019 the other translation side if possible?1088 the other translation side if possible?
1089 :param lock_timestamp: Timestamp of the original translation state
1090 that this change is based on.
1020 """1091 """
1021 template = pofile.potemplate1092 template = pofile.potemplate
1022 traits = getUtility(ITranslationSideTraitsSet).getTraits(1093 traits = getUtility(ITranslationSideTraitsSet).getTraits(
@@ -1030,7 +1101,7 @@
1030 activated_message = self._setTranslation(1101 activated_message = self._setTranslation(
1031 pofile, translator, suggestion.origin, potranslations,1102 pofile, translator, suggestion.origin, potranslations,
1032 share_with_other_side=share_with_other_side,1103 share_with_other_side=share_with_other_side,
1033 identical_message=suggestion)1104 identical_message=suggestion, lock_timestamp=lock_timestamp)
10341105
1035 activated_message.markReviewed(reviewer)1106 activated_message.markReviewed(reviewer)
1036 if reviewer != translator:1107 if reviewer != translator:
@@ -1038,7 +1109,8 @@
1038 template.awardKarma(reviewer, 'translationreview')1109 template.awardKarma(reviewer, 'translationreview')
10391110
1040 def setCurrentTranslation(self, pofile, submitter, translations, origin,1111 def setCurrentTranslation(self, pofile, submitter, translations, origin,
1041 share_with_other_side=False):1112 share_with_other_side=False,
1113 lock_timestamp=None):
1042 """See `IPOTMsgSet`."""1114 """See `IPOTMsgSet`."""
1043 potranslations = self._findPOTranslations(translations)1115 potranslations = self._findPOTranslations(translations)
1044 identical_message = self._findTranslationMessage(1116 identical_message = self._findTranslationMessage(
@@ -1046,10 +1118,12 @@
1046 return self._setTranslation(1118 return self._setTranslation(
1047 pofile, submitter, origin, potranslations,1119 pofile, submitter, origin, potranslations,
1048 share_with_other_side=share_with_other_side,1120 share_with_other_side=share_with_other_side,
1049 identical_message=identical_message)1121 identical_message=identical_message,
1122 lock_timestamp=lock_timestamp)
10501123
1051 def _setTranslation(self, pofile, submitter, origin, potranslations,1124 def _setTranslation(self, pofile, submitter, origin, potranslations,
1052 identical_message=None, share_with_other_side=False):1125 identical_message=None, share_with_other_side=False,
1126 lock_timestamp=None):
1053 """Set the current translation.1127 """Set the current translation.
10541128
1055 :param pofile: The `POFile` to set the translation in.1129 :param pofile: The `POFile` to set the translation in.
@@ -1060,11 +1134,13 @@
1060 :param identical_message: The already existing message, if any,1134 :param identical_message: The already existing message, if any,
1061 that's either shared or diverged for `pofile.potemplate`,1135 that's either shared or diverged for `pofile.potemplate`,
1062 whose translations are identical to the ones we're setting.1136 whose translations are identical to the ones we're setting.
1063 :param share_with_other_side:1137 :param share_with_other_side: Propagate this change to the other
1064 :return:1138 translation side if appropriate.
1139 :param lock_timestamp: The timestamp of the translation state
1140 that the change is based on.
1141 :return: The `TranslationMessage` that is current after
1142 completion.
1065 """1143 """
1066 # XXX JeroenVermeulen 2010-08-16: Update pofile.date_changed.
1067
1068 twin = identical_message1144 twin = identical_message
10691145
1070 traits = getUtility(ITranslationSideTraitsSet).getTraits(1146 traits = getUtility(ITranslationSideTraitsSet).getTraits(
@@ -1074,6 +1150,9 @@
1074 incumbent_message = traits.getCurrentMessage(1150 incumbent_message = traits.getCurrentMessage(
1075 self, pofile.potemplate, pofile.language)1151 self, pofile.potemplate, pofile.language)
10761152
1153 self._checkForConflict(
1154 incumbent_message, lock_timestamp, potranslations=potranslations)
1155
1077 # Summary of the matrix:1156 # Summary of the matrix:
1078 # * If the incumbent message is diverged and we're setting a1157 # * If the incumbent message is diverged and we're setting a
1079 # translation that's already shared: converge.1158 # translation that's already shared: converge.
@@ -1178,7 +1257,7 @@
1178 traits.other_side_traits.getCurrentMessage(1257 traits.other_side_traits.getCurrentMessage(
1179 self, pofile.potemplate, pofile.language))1258 self, pofile.potemplate, pofile.language))
1180 if other_incumbent is None:1259 if other_incumbent is None:
1181 traits.other_side_side.setFlag(message, True)1260 traits.other_side_traits.setFlag(message, True)
1182 elif character == '+':1261 elif character == '+':
1183 if share_with_other_side:1262 if share_with_other_side:
1184 traits.other_side_traits.setFlag(message, True)1263 traits.other_side_traits.setFlag(message, True)
@@ -1189,7 +1268,9 @@
1189 if decisions == '':1268 if decisions == '':
1190 message = twin1269 message = twin
11911270
1192 traits.setFlag(message, True)1271 if not traits.getFlag(message):
1272 traits.setFlag(message, True)
1273 pofile.markChanged(translator=submitter)
11931274
1194 return message1275 return message
11951276
@@ -1214,11 +1295,13 @@
1214 pofile.date_changed = UTC_NOW1295 pofile.date_changed = UTC_NOW
12151296
1216 def clearCurrentTranslation(self, pofile, submitter, origin,1297 def clearCurrentTranslation(self, pofile, submitter, origin,
1217 share_with_other_side=False):1298 share_with_other_side=False,
1299 lock_timestamp=None):
1218 """See `IPOTMsgSet`."""1300 """See `IPOTMsgSet`."""
1219 message = self.setCurrentTranslation(1301 message = self.setCurrentTranslation(
1220 pofile, submitter, [], origin,1302 pofile, submitter, [], origin,
1221 share_with_other_side=share_with_other_side)1303 share_with_other_side=share_with_other_side,
1304 lock_timestamp=lock_timestamp)
1222 message.markReviewed(submitter)1305 message.markReviewed(submitter)
12231306
1224 def applySanityFixes(self, text):1307 def applySanityFixes(self, text):
12251308
=== modified file 'lib/lp/translations/model/translationmessage.py'
--- lib/lp/translations/model/translationmessage.py 2010-08-20 05:56:43 +0000
+++ lib/lp/translations/model/translationmessage.py 2010-08-24 07:55:55 +0000
@@ -326,11 +326,13 @@
326 date_reviewed = current.date_created326 date_reviewed = current.date_created
327 return date_reviewed > self.date_created327 return date_reviewed > self.date_created
328328
329 def approve(self, pofile, reviewer, share_with_other_side=False):329 def approve(self, pofile, reviewer, share_with_other_side=False,
330 lock_timestamp=None):
330 """See `ITranslationMessage`."""331 """See `ITranslationMessage`."""
331 self.potmsgset.approveSuggestion(332 self.potmsgset.approveSuggestion(
332 pofile, self, reviewer,333 pofile, self, reviewer,
333 share_with_other_side=share_with_other_side)334 share_with_other_side=share_with_other_side,
335 lock_timestamp=lock_timestamp)
334336
335 def getOnePOFile(self):337 def getOnePOFile(self):
336 """See `ITranslationMessage`."""338 """See `ITranslationMessage`."""
337339
=== modified file 'lib/lp/translations/tests/test_clearcurrenttranslation.py'
--- lib/lp/translations/tests/test_clearcurrenttranslation.py 2010-08-20 05:43:19 +0000
+++ lib/lp/translations/tests/test_clearcurrenttranslation.py 2010-08-24 07:55:55 +0000
@@ -21,7 +21,9 @@
21from lp.testing import TestCaseWithFactory21from lp.testing import TestCaseWithFactory
22from lp.translations.interfaces.side import ITranslationSideTraitsSet22from lp.translations.interfaces.side import ITranslationSideTraitsSet
23from lp.translations.interfaces.translationmessage import (23from lp.translations.interfaces.translationmessage import (
24 RosettaTranslationOrigin)24 RosettaTranslationOrigin,
25 TranslationConflict,
26 )
2527
26ORIGIN = RosettaTranslationOrigin.SCM28ORIGIN = RosettaTranslationOrigin.SCM
2729
@@ -249,6 +251,17 @@
249 self.assertEqual(new_reviewer, current.reviewer)251 self.assertEqual(new_reviewer, current.reviewer)
250 self.assertSqlAttributeEqualsDate(current, 'date_reviewed', UTC_NOW)252 self.assertSqlAttributeEqualsDate(current, 'date_reviewed', UTC_NOW)
251253
254 def test_detects_conflict(self):
255 pofile = self._makePOFile()
256 current_message = self.factory.makeCurrentTranslationMessage(
257 pofile=pofile)
258 old = datetime.now(UTC) - timedelta(days=7)
259
260 self.assertRaises(
261 TranslationConflict,
262 current_message.potmsgset.clearCurrentTranslation,
263 pofile, self.factory.makePerson(), ORIGIN, lock_timestamp=old)
264
252265
253class TestClearCurrentTranslationUpstream(TestCaseWithFactory,266class TestClearCurrentTranslationUpstream(TestCaseWithFactory,
254 ScenarioMixin):267 ScenarioMixin):
255268
=== modified file 'lib/lp/translations/tests/test_potmsgset.py'
--- lib/lp/translations/tests/test_potmsgset.py 2010-08-24 07:55:54 +0000
+++ lib/lp/translations/tests/test_potmsgset.py 2010-08-24 07:55:55 +0000
@@ -14,6 +14,8 @@
14from zope.security.proxy import isinstance as zope_isinstance14from zope.security.proxy import isinstance as zope_isinstance
15from zope.security.proxy import removeSecurityProxy15from zope.security.proxy import removeSecurityProxy
1616
17from storm.locals import Store
18
17from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities19from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
18from lp.registry.interfaces.person import IPersonSet20from lp.registry.interfaces.person import IPersonSet
19from lp.registry.interfaces.product import IProductSet21from lp.registry.interfaces.product import IProductSet
@@ -24,7 +26,6 @@
24 TranslationFileFormat)26 TranslationFileFormat)
25from lp.translations.interfaces.translationmessage import (27from lp.translations.interfaces.translationmessage import (
26 RosettaTranslationOrigin, TranslationConflict)28 RosettaTranslationOrigin, TranslationConflict)
27from lp.translations.interfaces.side import TranslationSide
28from lp.translations.model.translationmessage import (29from lp.translations.model.translationmessage import (
29 DummyTranslationMessage)30 DummyTranslationMessage)
3031
@@ -945,11 +946,10 @@
945 name='main', product=self.foo)946 name='main', product=self.foo)
946 removeSecurityProxy(self.foo).official_rosetta = True947 removeSecurityProxy(self.foo).official_rosetta = True
947948
948 self.potemplate = self.factory.makePOTemplate(949 template = self.potemplate = self.factory.makePOTemplate(
949 productseries=self.foo_main, name="messages")950 productseries=self.foo_main, name="messages")
950 self.potmsgset = self.factory.makePOTMsgSet(self.potemplate,951 self.potmsgset = self.factory.makePOTMsgSet(template, sequence=1)
951 sequence=1)952 self.pofile = self.factory.makePOFile('eo', template)
952 self.pofile = self.factory.makePOFile('eo', self.potemplate)
953953
954 def test_resetCurrentTranslation_shared(self):954 def test_resetCurrentTranslation_shared(self):
955 # Resetting a shared current translation will change iscurrent=False955 # Resetting a shared current translation will change iscurrent=False
@@ -1625,3 +1625,75 @@
16251625
1626 self.assertEqual(message, potmsgset.getImportedTranslationMessage(1626 self.assertEqual(message, potmsgset.getImportedTranslationMessage(
1627 pofile.potemplate, pofile.language))1627 pofile.potemplate, pofile.language))
1628
1629 def test_detects_conflict(self):
1630 pofile, potmsgset = self._makePOFileAndPOTMsgSet()
1631 translations = self._makeTranslations(potmsgset)
1632 origin = RosettaTranslationOrigin.ROSETTAWEB
1633
1634 # A translator bases a change on a page view from 5 minutes ago.
1635 lock_timestamp = datetime.now(pytz.UTC) - timedelta(minutes=5)
1636
1637 # Meanwhile someone else changes the same message's translation.
1638 newer_translation = self.factory.makeCurrentTranslationMessage(
1639 pofile=pofile, potmsgset=potmsgset)
1640
1641 # This raises a translation conflict.
1642 self.assertRaises(
1643 TranslationConflict,
1644 potmsgset.setCurrentTranslation,
1645 pofile, pofile.potemplate.owner, translations, origin,
1646 lock_timestamp=lock_timestamp)
1647
1648
1649class TestCheckForConflict(TestCaseWithFactory):
1650 """Test POTMsgSet._checkForConflict."""
1651
1652 layer = ZopelessDatabaseLayer
1653
1654 def test_passes_nonconflict(self):
1655 # If there is no conflict, _checkForConflict completes normally.
1656 current_tm = self.factory.makeCurrentTranslationMessage()
1657 potmsgset = removeSecurityProxy(current_tm.potmsgset)
1658 newer = current_tm.date_reviewed + timedelta(days=1)
1659
1660 potmsgset._checkForConflict(current_tm, newer)
1661
1662 def test_detects_conflict(self):
1663 # If there's been another translation since lock_timestamp,
1664 # _checkForConflict raises TranslationConflict.
1665 current_tm = self.factory.makeCurrentTranslationMessage()
1666 potmsgset = removeSecurityProxy(current_tm.potmsgset)
1667 older = current_tm.date_reviewed - timedelta(days=1)
1668
1669 self.assertRaises(
1670 TranslationConflict,
1671 potmsgset._checkForConflict,
1672 current_tm, older)
1673
1674 def test_passes_identical_change(self):
1675 # When concurrent translations are identical, there is no
1676 # conflict.
1677 current_tm = self.factory.makeCurrentTranslationMessage()
1678 potmsgset = removeSecurityProxy(current_tm.potmsgset)
1679 older = current_tm.date_reviewed - timedelta(days=1)
1680
1681 potmsgset._checkForConflict(
1682 current_tm, older, potranslations=current_tm.all_msgstrs)
1683
1684 def test_quiet_if_no_current_message(self):
1685 # If there is no current translation, _checkForConflict accepts
1686 # that as conflict-free.
1687 potemplate = self.factory.makePOTemplate()
1688 potmsgset = self.factory.makePOTMsgSet(potemplate, sequence=1)
1689 old = datetime.now(pytz.UTC) - timedelta(days=366)
1690
1691 removeSecurityProxy(potmsgset)._checkForConflict(None, old)
1692
1693 def test_quiet_if_no_timestamp(self):
1694 # If there is no lock_timestamp, _checkForConflict does not
1695 # check for conflicts.
1696 current_tm = self.factory.makeCurrentTranslationMessage()
1697 potmsgset = removeSecurityProxy(current_tm.potmsgset)
1698
1699 removeSecurityProxy(potmsgset)._checkForConflict(current_tm, None)
16281700
=== modified file 'lib/lp/translations/tests/test_translationmessage.py'
--- lib/lp/translations/tests/test_translationmessage.py 2010-08-17 10:13:25 +0000
+++ lib/lp/translations/tests/test_translationmessage.py 2010-08-24 07:55:55 +0000
@@ -18,7 +18,9 @@
18from lp.translations.model.translationmessage import DummyTranslationMessage18from lp.translations.model.translationmessage import DummyTranslationMessage
19from lp.translations.interfaces.side import ITranslationSideTraitsSet19from lp.translations.interfaces.side import ITranslationSideTraitsSet
20from lp.translations.interfaces.translationmessage import (20from lp.translations.interfaces.translationmessage import (
21 ITranslationMessage)21 ITranslationMessage,
22 TranslationConflict,
23 )
22from lp.translations.interfaces.translations import TranslationConstants24from lp.translations.interfaces.translations import TranslationConstants
23from canonical.testing import ZopelessDatabaseLayer25from canonical.testing import ZopelessDatabaseLayer
2426
@@ -250,6 +252,19 @@
250252
251 self.assertEqual([], karmarecorder.karma_events)253 self.assertEqual([], karmarecorder.karma_events)
252254
255 def test_approve_detects_conflict(self):
256 pofile = self.factory.makePOFile('bo')
257 current = self.factory.makeCurrentTranslationMessage(pofile=pofile)
258 potmsgset = current.potmsgset
259 suggestion = self.factory.makeSuggestion(
260 pofile=pofile, potmsgset=potmsgset)
261 old = datetime.now(UTC) - timedelta(days=1)
262
263 self.assertRaises(
264 TranslationConflict,
265 suggestion.approve,
266 pofile, self.factory.makePerson(), lock_timestamp=old)
267
253268
254class TestTranslationMessageFindIdenticalMessage(TestCaseWithFactory):269class TestTranslationMessageFindIdenticalMessage(TestCaseWithFactory):
255 """Tests for `TranslationMessage.findIdenticalMessage`."""270 """Tests for `TranslationMessage.findIdenticalMessage`."""

Subscribers

People subscribed via source and target branches