From aa1df0e4f9b772f00e8545b3cbb35c86347933d0 Mon Sep 17 00:00:00 2001 From: Julien Muchembled <jm@nexedi.com> Date: Thu, 30 Dec 2010 19:31:06 +0000 Subject: [PATCH] Float divergence testers: workaround issues due to limited precision of floats This fixes test_01c_PackingListSplitBuildInvoiceBuild (testLegacyTradeModelLine). git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@41890 20353a03-c40f-0410-a6d1-a30d3c3de9de --- .../ERP5/Document/FloatEquivalenceTester.py | 15 ++- product/ERP5/tests/testDivergenceTester.py | 111 ++++++++++-------- .../Document/PropertyDivergenceTester.py | 8 ++ .../Document/QuantityDivergenceTester.py | 4 +- .../tests/testLegacyTradeModelLine.py | 2 +- 5 files changed, 85 insertions(+), 55 deletions(-) diff --git a/product/ERP5/Document/FloatEquivalenceTester.py b/product/ERP5/Document/FloatEquivalenceTester.py index a36aa9bd36..e30c4ed746 100644 --- a/product/ERP5/Document/FloatEquivalenceTester.py +++ b/product/ERP5/Document/FloatEquivalenceTester.py @@ -33,6 +33,9 @@ from Products.ERP5Type.Core.Predicate import Predicate from Products.ERP5Type import Permissions, PropertySheet from Products.ERP5.mixin.equivalence_tester import EquivalenceTesterMixin +# On Python >= 2.6, we could compute a value based on sys.float_info.epsilon +DEFAULT_PRECISION = 1e-12 + class FloatEquivalenceTester(Predicate, EquivalenceTesterMixin): """ Compare float values, with support for rounding. """ @@ -79,12 +82,20 @@ class FloatEquivalenceTester(Predicate, EquivalenceTesterMixin): prevision_value = self._round(prevision_value) delta = decision_value - prevision_value + if type(delta) is float: + # XXX: What if prevision or decision is 0 ? + # How to know if the other value is negligible or not ? + epsilon = abs(prevision_value * DEFAULT_PRECISION) + __lt__ = lambda a, b: a < b - epsilon + else: + from operator import __lt__ + # XXX we should use appropriate property sheets and getter methods # for these properties. # Maybe, but beware of default values of quantity when doing so absolute_tolerance_min = self.getProperty('quantity_range_min') if absolute_tolerance_min is not None and \ - delta < absolute_tolerance_min: + __lt__(delta, absolute_tolerance_min): return ( prevision_value, decision_value, 'The difference of ${property_name} between decision and prevision is less than ${value}.', @@ -92,7 +103,7 @@ class FloatEquivalenceTester(Predicate, EquivalenceTesterMixin): value=absolute_tolerance_min)) absolute_tolerance_max = self.getProperty('quantity_range_max') if absolute_tolerance_max is not None and \ - delta > absolute_tolerance_max: + __lt__(absolute_tolerance_max, delta): return ( prevision_value, decision_value, 'The difference of ${property_name} between decision and prevision is larger than ${value}.', diff --git a/product/ERP5/tests/testDivergenceTester.py b/product/ERP5/tests/testDivergenceTester.py index da284a53ef..204d040a9d 100644 --- a/product/ERP5/tests/testDivergenceTester.py +++ b/product/ERP5/tests/testDivergenceTester.py @@ -139,6 +139,15 @@ class TestDivergenceTester(TestPackingListMixin, ERP5TypeTestCase): movement = sequence.get('movement') movement.setQuantity(sim_mvt.getQuantity()) + def stepSetPreviousQuantityWithEpsilon(self, sequence=None, + sequence_list=None, **kw): + sim_mvt = sequence.get('sim_mvt') + movement = sequence.get('movement') + prevision = sim_mvt.getQuantity() + decision = prevision * (1 + 1e-15) + self.assertNotEqual(prevision, decision) + movement.setQuantity(decision) + def stepAddQuantityDivergenceTester(self, sequence=None, sequence_list=None, **kw): """ @@ -155,17 +164,19 @@ class TestDivergenceTester(TestPackingListMixin, ERP5TypeTestCase): if not run: return sequence_list = SequenceList() # Create a clean packing list - sequence_string = ' \ - stepGetPackingList \ - stepCheckPackingListIsNotDivergent \ - stepSetNewQuantity \ - stepCheckPackingListIsNotDivergent \ - stepAddQuantityDivergenceTester \ - stepCheckPackingListIsDivergent \ - stepSetPreviousQuantity \ - stepCheckPackingListIsNotDivergent \ - Tic \ - ' + sequence_string = """ + GetPackingList + CheckPackingListIsNotDivergent + SetNewQuantity + CheckPackingListIsNotDivergent + AddQuantityDivergenceTester + CheckPackingListIsDivergent + SetPreviousQuantity + CheckPackingListIsNotDivergent + SetPreviousQuantityWithEpsilon + CheckPackingListIsNotDivergent + Tic + """ sequence_list.addSequenceString(sequence_string) sequence_list.play(self, quiet=self.quiet) @@ -211,19 +222,19 @@ class TestDivergenceTester(TestPackingListMixin, ERP5TypeTestCase): if not run: return sequence_list = SequenceList() # Create a clean packing list - sequence_string = ' \ - stepGetPackingList \ - stepCheckPackingListIsNotDivergent \ - stepSetNewSource \ - stepCheckPackingListIsNotDivergent \ - stepAddCategoryDivergenceTester \ - stepCheckPackingListIsNotDivergent \ - stepConfigureCategoryDivergenceTesterForSource \ - stepCheckPackingListIsDivergent \ - stepSetPreviousSource \ - stepCheckPackingListIsNotDivergent \ - Tic \ - ' + sequence_string = """ + GetPackingList + CheckPackingListIsNotDivergent + SetNewSource + CheckPackingListIsNotDivergent + AddCategoryDivergenceTester + CheckPackingListIsNotDivergent + ConfigureCategoryDivergenceTesterForSource + CheckPackingListIsDivergent + SetPreviousSource + CheckPackingListIsNotDivergent + Tic + """ sequence_list.addSequenceString(sequence_string) sequence_list.play(self, quiet=self.quiet) @@ -269,19 +280,19 @@ class TestDivergenceTester(TestPackingListMixin, ERP5TypeTestCase): if not run: return sequence_list = SequenceList() # Create a clean packing list - sequence_string = ' \ - stepGetPackingList \ - stepCheckPackingListIsNotDivergent \ - stepSetNewStartDate \ - stepCheckPackingListIsNotDivergent \ - stepAddPropertyDivergenceTester \ - stepCheckPackingListIsNotDivergent \ - stepConfigurePropertyDivergenceTesterForStartDate \ - stepCheckPackingListIsDivergent \ - stepSetPreviousStartDate \ - stepCheckPackingListIsNotDivergent \ - Tic \ - ' + sequence_string = """ + GetPackingList + CheckPackingListIsNotDivergent + SetNewStartDate + CheckPackingListIsNotDivergent + AddPropertyDivergenceTester + CheckPackingListIsNotDivergent + ConfigurePropertyDivergenceTesterForStartDate + CheckPackingListIsDivergent + SetPreviousStartDate + CheckPackingListIsNotDivergent + Tic + """ sequence_list.addSequenceString(sequence_string) sequence_list.play(self, quiet=self.quiet) @@ -319,19 +330,19 @@ class TestDivergenceTester(TestPackingListMixin, ERP5TypeTestCase): if not run: return sequence_list = SequenceList() # Create a clean packing list - sequence_string = ' \ - stepGetPackingList \ - stepCheckPackingListIsNotDivergent \ - stepSetNewAggregate \ - stepCheckPackingListIsNotDivergent \ - stepAddCategoryDivergenceTester \ - stepCheckPackingListIsNotDivergent \ - stepConfigureCategoryDivergenceTesterForAggregate \ - stepCheckPackingListIsDivergent \ - stepSetPreviousAggregate \ - stepCheckPackingListIsNotDivergent \ - Tic \ - ' + sequence_string = """ + GetPackingList + CheckPackingListIsNotDivergent + SetNewAggregate + CheckPackingListIsNotDivergent + AddCategoryDivergenceTester + CheckPackingListIsNotDivergent + ConfigureCategoryDivergenceTesterForAggregate + CheckPackingListIsDivergent + SetPreviousAggregate + CheckPackingListIsNotDivergent + Tic + """ sequence_list.addSequenceString(sequence_string) sequence_list.play(self, quiet=self.quiet) diff --git a/product/ERP5Legacy/Document/PropertyDivergenceTester.py b/product/ERP5Legacy/Document/PropertyDivergenceTester.py index fa22cff9d1..b8bbf10724 100644 --- a/product/ERP5Legacy/Document/PropertyDivergenceTester.py +++ b/product/ERP5Legacy/Document/PropertyDivergenceTester.py @@ -33,6 +33,7 @@ from AccessControl import ClassSecurityInfo from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Type.DivergenceMessage import DivergenceMessage from Products.ERP5Type import Permissions, PropertySheet, interfaces +from Products.ERP5.Document.FloatEquivalenceTester import DEFAULT_PRECISION class PropertyDivergenceTester(XMLObject): """ @@ -88,6 +89,13 @@ class PropertyDivergenceTester(XMLObject): delivery_mvt_property = delivery_mvt_getProperty(tested_property_id) simulation_mvt_property = simulation_movement_getProperty(tested_property_id) if delivery_mvt_property != simulation_mvt_property: + try: + # XXX: What if prevision or decision is 0 ? + if abs(delivery_mvt_property - simulation_mvt_property) <= \ + abs(simulation_mvt_property * DEFAULT_PRECISION): + continue + except TypeError: + pass message = DivergenceMessage( divergence_scope='property', object_relative_url=delivery_mvt.getRelativeUrl(), diff --git a/product/ERP5Legacy/Document/QuantityDivergenceTester.py b/product/ERP5Legacy/Document/QuantityDivergenceTester.py index df85d63790..032b89c89f 100644 --- a/product/ERP5Legacy/Document/QuantityDivergenceTester.py +++ b/product/ERP5Legacy/Document/QuantityDivergenceTester.py @@ -32,6 +32,7 @@ from AccessControl import ClassSecurityInfo from Products.ERP5Type.DivergenceMessage import DivergenceMessage from Products.ERP5Type import Permissions, PropertySheet, Constraint, interfaces +from Products.ERP5.Document.FloatEquivalenceTester import DEFAULT_PRECISION from Products.ERP5Legacy.Document.PropertyDivergenceTester import \ PropertyDivergenceTester @@ -150,8 +151,7 @@ class QuantityDivergenceTester(PropertyDivergenceTester): == Decimal(str(y)).quantize(Decimal(self.getDecimalExponent()), rounding=rounding_option)) - else: - return x==y + return abs(x - y) <= abs(y * DEFAULT_PRECISION) # XXX: What if x or y is 0 ? def getTestedProperty(self, default=None): """ diff --git a/product/ERP5Legacy/tests/testLegacyTradeModelLine.py b/product/ERP5Legacy/tests/testLegacyTradeModelLine.py index b1da83bfe9..4d61c844be 100644 --- a/product/ERP5Legacy/tests/testLegacyTradeModelLine.py +++ b/product/ERP5Legacy/tests/testLegacyTradeModelLine.py @@ -133,7 +133,7 @@ def checkTradeModelRuleSimulationExpand(self, delivery): total_price = expected_result_dict[use].get(line.getId()) if total_price: sm = result_dict.pop(use) - self.assertEqual(sm.getTotalPrice(), total_price) + self.assertEqual(str(sm.getTotalPrice()), str(total_price)) self.assertEqual(1, len(sm.getCausalityValueList())) self.assertEqual(1, len(sm.getCausalityValueList( portal_type='Business Path'))) -- 2.30.9