Commit 0422043e authored by Tatuya Kamada's avatar Tatuya Kamada

simulation: Test the divergence on Float Equivalence Tester with the epsilon-span by default

Before:
 Float Equivalence Tester does not test the divergence even if Divergence Provider is ON

After:
 Float Equivalence Tester test the divergence with the epsilon-span if Divergence Provider is ON

Before, we needed the following condition to test the divergence on
Float Equivalence Tester:

"Divergence Provider is ON"  AND
("Absolute Tolerance has some value"  OR
 "Relative Tolerance has some value" )

However this default behavior was not consistent with other testers, and
difficult to notice from the user interface.
Thus test the divergence by default with the epsilon span which is defined in
FloatEquivalenceTester class.

The epsilon-span behavior is the same that we put 0.0 into the ranges that
are the configuration of erp5_configurator_standard_trade_template.
parent 2aca4f96
......@@ -117,14 +117,23 @@ class FloatEquivalenceTester(Predicate, EquivalenceTesterMixin):
# 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')
tolerance_base = self.getProperty('tolerance_base')
# If tolerance_base is None, check the divergece with epsilon-span by default
# If tolerance_base is not None, we can use tolerance_base (absolute has priority)
if tolerance_base is None:
absolute_tolerance_min = self.getProperty('quantity_range_min') or -epsilon
else:
absolute_tolerance_min = self.getProperty('quantity_range_min')
if absolute_tolerance_min is not None and \
delta < (absolute_tolerance_min or - epsilon):
return (
prevision_value, decision_value,
explanation_start + 'is less than ${value}.',
getMappingDict(value=absolute_tolerance_min))
absolute_tolerance_max = self.getProperty('quantity_range_max')
if tolerance_base is None:
absolute_tolerance_max = self.getProperty('quantity_range_max') or epsilon
else:
absolute_tolerance_max = self.getProperty('quantity_range_max')
if absolute_tolerance_max is not None and \
delta > (absolute_tolerance_max or epsilon):
return (
......@@ -132,7 +141,6 @@ class FloatEquivalenceTester(Predicate, EquivalenceTesterMixin):
explanation_start + 'is larger than ${value}.',
getMappingDict(value=absolute_tolerance_max))
tolerance_base = self.getProperty('tolerance_base')
base = None
if tolerance_base == 'resource_quantity_precision':
# Precision of this movement's resource base unit quantity
......
......@@ -31,6 +31,7 @@ from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Products.ERP5Type.tests.Sequence import Sequence
from Products.ERP5.tests.testPackingList import TestPackingListMixin
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
from Products.ERP5.Document.FloatEquivalenceTester import DEFAULT_PRECISION
class TestDivergenceTester(TestPackingListMixin, ERP5TypeTestCase):
"""
......@@ -294,6 +295,18 @@ class TestDivergenceTester(TestPackingListMixin, ERP5TypeTestCase):
sequence = Sequence(self)
sequence(sequence_string, quiet=self.quiet)
def stepAddDefaultRangePriceDivergenceTester(self, sequence):
"""
Test the default behavior of Float Divergence Tester.
"""
self._addDivergenceTester(
sequence,
tester_name='price',
tester_type='Float Divergence Tester',
)
def test_QuantityDivergenceTesterCompareMethod(self):
# XXX-Leo this test is actually just testing
# FloatEquivalenceTester, and is incomplete. It should test also
......@@ -339,6 +352,171 @@ class TestDivergenceTester(TestPackingListMixin, ERP5TypeTestCase):
divergence_tester.setDecimalExponent('0.01')
self.assertTrue(divergence_tester_compare(3.0, 3.001))
def stepCheckFloatEquivalenceTesterWithTheDefaultRange(self, sequence):
"""
Check Float equivalence tester behavior, especially around the
default range = epsilon = abs(prevision_value * DEFAULT_PRECISION)
"""
divergence_tester = sequence['price_divergence_tester']
decision = sequence['order_line']
prevision = decision.getDeliveryRelatedValue(
portal_type=self.simulation_movement_portal_type)
def divergence_tester_compare(prevision_value, decision_value):
prevision.setPrice(prevision_value)
decision.setPrice(decision_value)
return divergence_tester.compare(prevision, decision)
prevision_value = 3.0
# Def. epsilon
# The is the epsilon defined by
# FloatEquivalenceTester._comparePrevisionDecisionValue()
epsilon = abs(prevision_value * DEFAULT_PRECISION)
self.assertFalse(divergence_tester.isDecimalAlignmentEnabled())
a_value_slightly_bigger_than_the_range = 3.0 + epsilon + DEFAULT_PRECISION
# Since
# delta = abs(decision - prevision)
# delta = (3.0 + epsilon + DEFAULT_PRECISION) - 3.0
# = epsilon + DEFAULT_PRECISION
# epsilon + DEFAULT_PRECISION < -epsilon is False
# epsilon + DEFAULT_PRECISION > epsilon is True
# --> Diverged <=> compare() returns False
self.assertFalse(
divergence_tester_compare(3.0, a_value_slightly_bigger_than_the_range))
a_value_slightly_smaller_than_the_range = 3.0 + epsilon - DEFAULT_PRECISION
# Since,
# delta = abs(decision - prevision)
# delta = (3.0 + epsilon - DEFAULT_PRECISION) - 3.0
# = epsilon - DEFAULT_PRECISION
# epsilon - DEFAULT_PRECISION < -epsilon is False
# epsilon - DEFAULT_PRECISION > epsilon is False
# --> Not diverged <=> compare() returns True
self.assertTrue(
divergence_tester_compare(3.0, a_value_slightly_smaller_than_the_range))
the_value_equal_to_the_range = 3.0 + epsilon
# Since,
# delta = abs(decision - prevision)
# delta = (3.0 + epsilon) - 3.0
# = epsilon
# epsilon < -epsilon is False
# epsilon > epsilon is False
# -- Not diverged <=> compare() returns True
self.assertTrue(
divergence_tester_compare(3.0, the_value_equal_to_the_range))
# The behavior for absolute_range is the same when we set
# 0.0 for the range.
# That is why the default bihavior is safe since most of sample
# test data has 0.0 for its range.
self.assertFalse(divergence_tester_compare(3.0, 3.001))
self.assertTrue(divergence_tester_compare(3.0, 3.0))
def testFloatEquivalenceTesterAroundTheDefaultRange(
self, quiet=quiet, run=run_all_test):
"""
All the ranges of Float Equivalence Tester:
- quantity_range_max,
- quantity_rang_min,
- tolerance_range_max,
- tolerance_range_min
are None by default. 0.0 is NOT the default value.
This is partly because FloatEquivalenceTester use None range as a flag
whether we use absolute_range or tolerance_range.
The logic uses the fact that 0.0 is not None, but bool(0.0) is False.
So here checking how it work around the range when they are default value
"""
if not run: return
sequence_string = self.confirmed_order_without_packing_list + """
ResetDeliveringRule
AddDefaultRangePriceDivergenceTester
Tic
CheckFloatEquivalenceTesterWithTheDefaultRange
"""
sequence = Sequence(self)
sequence(sequence_string, quiet=self.quiet)
def stepSetNewPrice(self, sequence=None,
sequence_list=None, **kw):
"""
Modify the quantity of the delivery movement
"""
packing_list = sequence.get('packing_list')
movement = sequence.get('movement')
movement.setPrice(movement.getPrice()+1234)
def stepSetPreviousPrice(self, sequence=None,
sequence_list=None, **kw):
"""
Reset the quantity of the delivery movement
"""
simulation_movement = sequence.get('sim_mvt')
movement = sequence.get('movement')
movement.setPrice(simulation_movement.getPrice())
def stepSetPreviousPriceWithEpsilon(self, sequence=None,
sequence_list=None, **kw):
simulation_movement = sequence.get('sim_mvt')
movement = sequence.get('movement')
prevision = simulation_movement.getPrice()
decision = prevision * (1 + 1e-15)
self.assertNotEqual(prevision, decision)
movement.setPrice(decision)
def stepSetSmallRangePriceDivergenceTesterWithToleranceBase(self, sequence):
"""
Test the default behavior of Float Divergence Tester.
"""
tester = sequence['price_divergence_tester']
tester.setProperty('tolerance_base', 'tested_property')
# 1 digit smaller than the default range meaning more strict than default
tester.setProperty('tolerance_range_max', 1e-16)
tester.setProperty('tolerance_range_min', 1e-16)
def testFloatDivergenceTesterWithTheDefaultRangeWithToleranceBase(
self, quiet=quiet, run=run_all_test):
"""
Test float divergence tester with the default range, and a
tolerance base configurataion.
The defined behavior:
1. If nothing is configured, the default epsilon range is used
2. If having absolute value, the absolute value is used
3. If having both absolete and relative value, the absolute value is used
4. If not having absolete and having relative, the relative value is used
We only check case 1 and 4 in this test.
"""
if not run: return
sequence_string = TestPackingListMixin.default_sequence + """
SetPackingListMovementAndSimulationMovement
ResetDeliveringRule
SetNewPrice
Tic
CheckPackingListIsNotDivergent
AddDefaultRangePriceDivergenceTester
CheckPackingListIsDivergent
SetPreviousPrice
Tic
CheckPackingListIsNotDivergent
SetPreviousPriceWithEpsilon
Tic
CheckPackingListIsNotDivergent
SetSmallRangePriceDivergenceTesterWithToleranceBase
Tic
CheckPackingListIsDivergent
"""
sequence = Sequence(self)
sequence(sequence_string, quiet=self.quiet)
def test_suite():
suite = unittest.TestSuite()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment