Skip to content

Commit

Permalink
🖊️ Revise number
Browse files Browse the repository at this point in the history
  • Loading branch information
oakbani committed Dec 20, 2018
1 parent 5b96ff2 commit 66159a8
Show file tree
Hide file tree
Showing 4 changed files with 246 additions and 26 deletions.
13 changes: 10 additions & 3 deletions optimizely/helpers/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,20 +187,24 @@ def is_attribute_valid(attribute_key, attribute_value):
if not isinstance(attribute_key, string_types):
return False

if isinstance(attribute_value, string_types) or type(attribute_value) in (int, float, bool):
if isinstance(attribute_value, (string_types, bool)):
return True

if isinstance(attribute_value, (numbers.Integral, float)):
return is_finite_number(attribute_value)

return False


def is_finite_number(value):
""" Method to validate if the given value is a number and not one of NAN, INF, -INF.
""" Validates if the given value is a number, enforces
absolute limit of 2^53 and restricts NAN, INF, -INF.
Args:
value: Value to be validated.
Returns:
Boolean: True if value is a number and not NAN, INF or -INF else False.
Boolean: True if value is a number and not NAN, INF, -INF or greater than 2^53 else False.
"""
if not isinstance(value, (numbers.Integral, float)):
# numbers.Integral instead of int to accomodate long integer in python 2
Expand All @@ -214,6 +218,9 @@ def is_finite_number(value):
if math.isnan(value) or math.isinf(value):
return False

if abs(value) > (2**53):
return False

return True


Expand Down
5 changes: 5 additions & 0 deletions tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,14 @@

import json
import unittest
from six import PY3

from optimizely import optimizely

if PY3:
def long(a):
raise NotImplementedError('Tests should only call `long` if running in PY2')


class BaseTest(unittest.TestCase):

Expand Down
212 changes: 207 additions & 5 deletions tests/helpers_tests/test_condition.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,12 @@
# limitations under the License.

import mock
from six import PY2, PY3
from six import PY2

from optimizely.helpers import condition as condition_helper

from tests import base

if PY3:
def long(a):
raise NotImplementedError('Tests should only call `long` if running in PY2')

browserConditionSafari = ['browser_type', 'safari', 'custom_attribute', 'exact']
booleanCondition = ['is_firefox', True, 'custom_attribute', 'exact']
integerCondition = ['num_users', 10, 'custom_attribute', 'exact']
Expand Down Expand Up @@ -205,6 +201,37 @@ def test_exact_int__returns_true__when_user_provided_value_is_equal_to_condition

self.assertStrictTrue(evaluator.evaluate(0))

def test_exact_int__calls_is_finite_number(self):
""" Returns True if is_finite_number returns True. Returns None if is_finite_number returns False. """

if PY2:
evaluator = condition_helper.CustomAttributeConditionEvaluator(
exact_int_condition_list, {'lasers_count': long(9000)}
)

with mock.patch('optimizely.helpers.validator.is_finite_number',
return_value=True) as is_finite:
self.assertStrictTrue(evaluator.evaluate(0))
is_finite.assert_called_with(long(9000))

evaluator = condition_helper.CustomAttributeConditionEvaluator(
exact_int_condition_list, {'lasers_count': 9000}
)

with mock.patch('optimizely.helpers.validator.is_finite_number',
return_value=False) as is_finite:
self.assertIsNone(evaluator.evaluate(0))
is_finite.assert_called_with(9000)

evaluator = condition_helper.CustomAttributeConditionEvaluator(
exact_int_condition_list, {'lasers_count': 9000.0}
)

with mock.patch('optimizely.helpers.validator.is_finite_number',
return_value=False) as is_finite:
self.assertIsNone(evaluator.evaluate(0))
is_finite.assert_called_with(9000.0)

def test_exact_float__returns_true__when_user_provided_value_is_equal_to_condition_value(self):

if PY2:
Expand All @@ -226,6 +253,37 @@ def test_exact_float__returns_true__when_user_provided_value_is_equal_to_conditi

self.assertStrictTrue(evaluator.evaluate(0))

def test_exact_float__calls_is_finite_number(self):
""" Returns True if is_finite_number returns True. Returns None if is_finite_number returns False. """

if PY2:
evaluator = condition_helper.CustomAttributeConditionEvaluator(
exact_float_condition_list, {'lasers_count': long(9000)}
)

with mock.patch('optimizely.helpers.validator.is_finite_number',
return_value=True) as is_finite:
self.assertStrictTrue(evaluator.evaluate(0))
is_finite.assert_called_with(long(9000))

evaluator = condition_helper.CustomAttributeConditionEvaluator(
exact_float_condition_list, {'lasers_count': 9000}
)

with mock.patch('optimizely.helpers.validator.is_finite_number',
return_value=False) as is_finite:
self.assertIsNone(evaluator.evaluate(0))
is_finite.assert_called_with(9000)

evaluator = condition_helper.CustomAttributeConditionEvaluator(
exact_float_condition_list, {'lasers_count': 9000.0}
)

with mock.patch('optimizely.helpers.validator.is_finite_number',
return_value=False) as is_finite:
self.assertIsNone(evaluator.evaluate(0))
is_finite.assert_called_with(9000.0)

def test_exact_int__returns_false__when_user_provided_value_is_not_equal_to_condition_value(self):

evaluator = condition_helper.CustomAttributeConditionEvaluator(
Expand Down Expand Up @@ -371,6 +429,42 @@ def test_greater_than_int__returns_true__when_user_value_greater_than_condition_

self.assertStrictTrue(evaluator.evaluate(0))

def test_greater_than_int__calls_is_finite_number(self):
""" Returns True if is_finite_number returns True. Returns None if is_finite_number returns False. """

evaluator = condition_helper.CustomAttributeConditionEvaluator(
gt_int_condition_list, {'meters_travelled': 48.1}
)

with mock.patch('optimizely.helpers.validator.is_finite_number',
return_value=True) as is_finite:
self.assertStrictTrue(evaluator.evaluate(0))
is_finite.assert_called_with(48.1)

evaluator = condition_helper.CustomAttributeConditionEvaluator(
gt_int_condition_list, {'meters_travelled': 49}
)

def side_effect(*args):
if args[0] == 49:
return False
return True

with mock.patch('optimizely.helpers.validator.is_finite_number',
side_effect=side_effect) as is_finite:
self.assertIsNone(evaluator.evaluate(0))
is_finite.assert_any_call(49)

if PY2:
evaluator = condition_helper.CustomAttributeConditionEvaluator(
gt_int_condition_list, {'meters_travelled': long(49)}
)

with mock.patch('optimizely.helpers.validator.is_finite_number',
side_effect=side_effect) as is_finite:
self.assertIsNone(evaluator.evaluate(0))
is_finite.assert_called_with(long(49))

def test_greater_than_float__returns_true__when_user_value_greater_than_condition_value(self):

evaluator = condition_helper.CustomAttributeConditionEvaluator(
Expand All @@ -392,6 +486,42 @@ def test_greater_than_float__returns_true__when_user_value_greater_than_conditio

self.assertStrictTrue(evaluator.evaluate(0))

def test_greater_than_float__calls_is_finite_number(self):
""" Returns True if is_finite_number returns True. Returns None if is_finite_number returns False. """

evaluator = condition_helper.CustomAttributeConditionEvaluator(
gt_float_condition_list, {'meters_travelled': 48.3}
)

with mock.patch('optimizely.helpers.validator.is_finite_number',
return_value=True) as is_finite:
self.assertStrictTrue(evaluator.evaluate(0))
is_finite.assert_called_with(48.3)

def side_effect(*args):
if args[0] == 49:
return False
return True

evaluator = condition_helper.CustomAttributeConditionEvaluator(
gt_float_condition_list, {'meters_travelled': 49}
)

with mock.patch('optimizely.helpers.validator.is_finite_number',
side_effect=side_effect) as is_finite:
self.assertIsNone(evaluator.evaluate(0))
is_finite.assert_called_with(49)

if PY2:
evaluator = condition_helper.CustomAttributeConditionEvaluator(
gt_float_condition_list, {'meters_travelled': long(49)}
)

with mock.patch('optimizely.helpers.validator.is_finite_number',
side_effect=side_effect) as is_finite:
self.assertIsNone(evaluator.evaluate(0))
is_finite.assert_called_with(long(49))

def test_greater_than_int__returns_false__when_user_value_not_greater_than_condition_value(self):

evaluator = condition_helper.CustomAttributeConditionEvaluator(
Expand Down Expand Up @@ -499,6 +629,42 @@ def test_less_than_int__returns_true__when_user_value_less_than_condition_value(

self.assertStrictTrue(evaluator.evaluate(0))

def test_less_than_int__calls_is_finite_number(self):
""" Returns True if is_finite_number returns True. Returns None if is_finite_number returns False. """

evaluator = condition_helper.CustomAttributeConditionEvaluator(
lt_int_condition_list, {'meters_travelled': 47.9}
)

with mock.patch('optimizely.helpers.validator.is_finite_number',
return_value=True) as is_finite:
self.assertStrictTrue(evaluator.evaluate(0))
is_finite.assert_called_with(47.9)

evaluator = condition_helper.CustomAttributeConditionEvaluator(
lt_int_condition_list, {'meters_travelled': 47}
)

def side_effect(*args):
if args[0] == 47:
return False
return True

with mock.patch('optimizely.helpers.validator.is_finite_number',
side_effect=side_effect) as is_finite:
self.assertIsNone(evaluator.evaluate(0))
is_finite.assert_any_call(47)

if PY2:
evaluator = condition_helper.CustomAttributeConditionEvaluator(
lt_int_condition_list, {'meters_travelled': long(47)}
)

with mock.patch('optimizely.helpers.validator.is_finite_number',
side_effect=side_effect) as is_finite:
self.assertIsNone(evaluator.evaluate(0))
is_finite.assert_called_with(long(47))

def test_less_than_float__returns_true__when_user_value_less_than_condition_value(self):

evaluator = condition_helper.CustomAttributeConditionEvaluator(
Expand All @@ -520,6 +686,42 @@ def test_less_than_float__returns_true__when_user_value_less_than_condition_valu

self.assertStrictTrue(evaluator.evaluate(0))

def test_less_than_float__calls_is_finite_number(self):
""" Returns True if is_finite_number returns True. Returns None if is_finite_number returns False. """

evaluator = condition_helper.CustomAttributeConditionEvaluator(
lt_float_condition_list, {'meters_travelled': 48.1}
)

with mock.patch('optimizely.helpers.validator.is_finite_number',
return_value=True) as is_finite:
self.assertStrictTrue(evaluator.evaluate(0))
is_finite.assert_called_with(48.1)

evaluator = condition_helper.CustomAttributeConditionEvaluator(
lt_float_condition_list, {'meters_travelled': 48}
)

def side_effect(*args):
if args[0] == 48:
return False
return True

with mock.patch('optimizely.helpers.validator.is_finite_number',
side_effect=side_effect) as is_finite:
self.assertIsNone(evaluator.evaluate(0))
is_finite.assert_any_call(48)

if PY2:
evaluator = condition_helper.CustomAttributeConditionEvaluator(
lt_float_condition_list, {'meters_travelled': long(48)}
)

with mock.patch('optimizely.helpers.validator.is_finite_number',
side_effect=side_effect) as is_finite:
self.assertIsNone(evaluator.evaluate(0))
is_finite.assert_called_with(long(48))

def test_less_than_int__returns_false__when_user_value_not_less_than_condition_value(self):

evaluator = condition_helper.CustomAttributeConditionEvaluator(
Expand Down
42 changes: 24 additions & 18 deletions tests/helpers_tests/test_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,16 +184,17 @@ def test_is_attribute_valid(self):

is_finite.assert_called_once_with(5.5)

with mock.patch('optimizely.helpers.validator.is_finite_number',
return_value='abc') as is_finite:
self.assertEqual('abc', validator.is_attribute_valid('test_attribute', 0))
if PY2:
with mock.patch('optimizely.helpers.validator.is_finite_number',
return_value=None) as is_finite:
self.assertIsNone(validator.is_attribute_valid('test_attribute', long(5)))

is_finite.assert_called_once_with(0)
is_finite.assert_called_once_with(long(5))

def test_is_finite_number(self):
""" Test that it returns true if value is a number and is not more than 1e53 if an Integer,
and not one of NAN, INF or -INF if it's a double. """

""" Test that it returns true if value is a number and not NAN, INF, -INF or greater than 2^53.
Otherwise False.
"""
# test non number values
self.assertFalse(validator.is_finite_number('HelloWorld'))
self.assertFalse(validator.is_finite_number(True))
Expand All @@ -204,21 +205,26 @@ def test_is_finite_number(self):
self.assertFalse(validator.is_finite_number(()))

# test invalid numbers
self.assertFalse(validator.is_finite_number((float('inf'))))
self.assertFalse(validator.is_finite_number((float('-inf'))))
self.assertFalse(validator.is_finite_number((float('nan'))))
self.assertFalse(validator.is_finite_number(int(1e53) + 1))
self.assertFalse(validator.is_finite_number(float('inf')))
self.assertFalse(validator.is_finite_number(float('-inf')))
self.assertFalse(validator.is_finite_number(float('nan')))
self.assertFalse(validator.is_finite_number(int(2**53) + 1))
self.assertFalse(validator.is_finite_number(-int(2**53) - 1))
self.assertFalse(validator.is_finite_number(float(2**53) + 2.0))
self.assertFalse(validator.is_finite_number(-float(2**53) - 2.0))
if PY2:
self.assertFalse(validator.is_finite_number(long(1e53) + 1))
self.assertFalse(validator.is_finite_number(long(2**53) + 1))
self.assertFalse(validator.is_finite_number(-long(2**53) - 1))

# test valid numbers
self.assertTrue(validator.is_finite_number((0)))
self.assertTrue(validator.is_finite_number((5)))
self.assertTrue(validator.is_finite_number((5.5)))
self.assertTrue(validator.is_finite_number((float(1e53) + 1)))
self.assertTrue(validator.is_finite_number((int(1e53))))
self.assertTrue(validator.is_finite_number(0))
self.assertTrue(validator.is_finite_number(5))
self.assertTrue(validator.is_finite_number(5.5))
self.assertTrue(validator.is_finite_number(float(2**53) + 1.0))
self.assertTrue(validator.is_finite_number(-float(2**53) - 1.0))
self.assertTrue(validator.is_finite_number(int(2**53)))
if PY2:
self.assertTrue(validator.is_finite_number((long(1e53))))
self.assertTrue(validator.is_finite_number(long(2**53)))


class DatafileValidationTests(base.BaseTest):
Expand Down

0 comments on commit 66159a8

Please sign in to comment.