diff --git a/optimizely/helpers/condition.py b/optimizely/helpers/condition.py
index 0676aecb..2cd80dde 100644
--- a/optimizely/helpers/condition.py
+++ b/optimizely/helpers/condition.py
@@ -18,6 +18,8 @@
 
 from . import validator
 from .enums import CommonAudienceEvaluationLogs as audience_logs
+from .enums import Errors
+from .enums import VersionType
 
 
 class ConditionOperatorTypes(object):
@@ -30,7 +32,14 @@ class ConditionMatchTypes(object):
     EXACT = 'exact'
     EXISTS = 'exists'
     GREATER_THAN = 'gt'
+    GREATER_THAN_OR_EQUAL = 'ge'
     LESS_THAN = 'lt'
+    LESS_THAN_OR_EQUAL = 'le'
+    SEMVER_EQ = 'semver_eq'
+    SEMVER_GE = 'semver_ge'
+    SEMVER_GT = 'semver_gt'
+    SEMVER_LE = 'semver_le'
+    SEMVER_LT = 'semver_lt'
     SUBSTRING = 'substring'
 
 
@@ -84,6 +93,112 @@ def is_value_a_number(self, value):
 
         return False
 
+    def is_pre_release_version(self, version):
+        """ Method to check if given version is pre-release.
+            Criteria for pre-release includes:
+                - Version includes "-"
+
+        Args:
+          version: Given version in string.
+
+        Returns:
+          Boolean:
+            - True if the given version is pre-release
+            - False if it doesn't
+        """
+        if VersionType.IS_PRE_RELEASE in version:
+            user_version_release_index = version.find(VersionType.IS_PRE_RELEASE)
+            user_version_build_index = version.find(VersionType.IS_BUILD)
+            if (user_version_release_index < user_version_build_index) or (user_version_build_index < 0):
+                return True
+        return False
+
+    def is_build_version(self, version):
+        """ Method to check given version is a build version.
+            Criteria for build version includes:
+                - Version includes "+"
+
+        Args:
+          version: Given version in string.
+
+        Returns:
+          Boolean:
+            - True if the given version is a build version
+            - False if it doesn't
+        """
+        if VersionType.IS_BUILD in version:
+            user_version_release_index = version.find(VersionType.IS_PRE_RELEASE)
+            user_version_build_index = version.find(VersionType.IS_BUILD)
+            if (user_version_build_index < user_version_release_index) or (user_version_release_index < 0):
+                return True
+        return False
+
+    def has_white_space(self, version):
+        """ Method to check if the given version contains " " (white space)
+
+        Args:
+          version: Given version in string.
+
+        Returns:
+          Boolean:
+            - True if the given version does contain whitespace
+            - False if it doesn't
+        """
+        return ' ' in version
+
+    def compare_user_version_with_target_version(self, target_version, user_version):
+        """ Method to compare user version with target version.
+
+        Args:
+          target_version: String representing condition value
+          user_version: String representing user value
+
+        Returns:
+          Int:
+            -  0 if user version is equal to target version.
+            -  1 if user version is greater than target version.
+            - -1 if user version is less than target version or, in case of exact string match, doesn't match the target
+            version.
+          None:
+            - if the user version value format is not a valid semantic version.
+        """
+        is_pre_release_in_target_version = self.is_pre_release_version(target_version)
+        is_pre_release_in_user_version = self.is_pre_release_version(user_version)
+        is_build_in_target_version = self.is_build_version(target_version)
+
+        target_version_parts = self.split_version(target_version)
+        if target_version_parts is None:
+            return None
+
+        user_version_parts = self.split_version(user_version)
+        if user_version_parts is None:
+            return None
+
+        user_version_parts_len = len(user_version_parts)
+
+        for (idx, _) in enumerate(target_version_parts):
+            if user_version_parts_len <= idx:
+                return 1 if is_pre_release_in_target_version or is_build_in_target_version else -1
+            elif not user_version_parts[idx].isdigit():
+                if user_version_parts[idx] < target_version_parts[idx]:
+                    return 1 if is_pre_release_in_target_version and not \
+                        is_pre_release_in_user_version else -1
+                elif user_version_parts[idx] > target_version_parts[idx]:
+                    return -1 if not is_pre_release_in_target_version and \
+                        is_pre_release_in_user_version else 1
+            else:
+                user_version_part = int(user_version_parts[idx])
+                target_version_part = int(target_version_parts[idx])
+                if user_version_part > target_version_part:
+                    return 1
+                elif user_version_part < target_version_part:
+                    return -1
+
+        # check if user version contains pre-release and target version doesn't
+        if is_pre_release_in_user_version and not is_pre_release_in_target_version:
+            return -1
+        return 0
+
     def exact_evaluator(self, index):
         """ Evaluate the given exact match condition for the user attributes.
 
@@ -171,6 +286,40 @@ def greater_than_evaluator(self, index):
 
         return user_value > condition_value
 
+    def greater_than_or_equal_evaluator(self, index):
+        """ Evaluate the given greater than or equal to match condition for the user attributes.
+
+        Args:
+          index: Index of the condition to be evaluated.
+
+        Returns:
+          Boolean:
+            - True if the user attribute value is greater than or equal to the condition value.
+            - False if the user attribute value is less than the condition value.
+            None: if the condition value isn't finite or the user attribute value isn't finite.
+        """
+        condition_name = self.condition_data[index][0]
+        condition_value = self.condition_data[index][1]
+        user_value = self.attributes.get(condition_name)
+
+        if not validator.is_finite_number(condition_value):
+            self.logger.warning(audience_logs.UNKNOWN_CONDITION_VALUE.format(self._get_condition_json(index)))
+            return None
+
+        if not self.is_value_a_number(user_value):
+            self.logger.warning(
+                audience_logs.UNEXPECTED_TYPE.format(self._get_condition_json(index), type(user_value), condition_name)
+            )
+            return None
+
+        if not validator.is_finite_number(user_value):
+            self.logger.warning(
+                audience_logs.INFINITE_ATTRIBUTE_VALUE.format(self._get_condition_json(index), condition_name)
+            )
+            return None
+
+        return user_value >= condition_value
+
     def less_than_evaluator(self, index):
         """ Evaluate the given less than match condition for the user attributes.
 
@@ -205,6 +354,40 @@ def less_than_evaluator(self, index):
 
         return user_value < condition_value
 
+    def less_than_or_equal_evaluator(self, index):
+        """ Evaluate the given less than or equal to match condition for the user attributes.
+
+        Args:
+          index: Index of the condition to be evaluated.
+
+        Returns:
+          Boolean:
+            - True if the user attribute value is less than or equal to the condition value.
+            - False if the user attribute value is greater than the condition value.
+          None: if the condition value isn't finite or the user attribute value isn't finite.
+        """
+        condition_name = self.condition_data[index][0]
+        condition_value = self.condition_data[index][1]
+        user_value = self.attributes.get(condition_name)
+
+        if not validator.is_finite_number(condition_value):
+            self.logger.warning(audience_logs.UNKNOWN_CONDITION_VALUE.format(self._get_condition_json(index)))
+            return None
+
+        if not self.is_value_a_number(user_value):
+            self.logger.warning(
+                audience_logs.UNEXPECTED_TYPE.format(self._get_condition_json(index), type(user_value), condition_name)
+            )
+            return None
+
+        if not validator.is_finite_number(user_value):
+            self.logger.warning(
+                audience_logs.INFINITE_ATTRIBUTE_VALUE.format(self._get_condition_json(index), condition_name)
+            )
+            return None
+
+        return user_value <= condition_value
+
     def substring_evaluator(self, index):
         """ Evaluate the given substring match condition for the given user attributes.
 
@@ -233,14 +416,251 @@ def substring_evaluator(self, index):
 
         return condition_value in user_value
 
+    def semver_equal_evaluator(self, index):
+        """ Evaluate the given semantic version equal match target version for the user version.
+
+        Args:
+          index: Index of the condition to be evaluated.
+
+        Returns:
+          Boolean:
+            - True if the user version is equal (==) to the target version.
+            - False if the user version is not equal (!=) to the target version.
+          None:
+            - if the user version value is not string type or is null.
+        """
+
+        condition_name = self.condition_data[index][0]
+        target_version = self.condition_data[index][1]
+        user_version = self.attributes.get(condition_name)
+
+        if not isinstance(target_version, string_types):
+            self.logger.warning(audience_logs.UNKNOWN_CONDITION_VALUE.format(self._get_condition_json(index), ))
+            return None
+
+        if not isinstance(user_version, string_types):
+            self.logger.warning(
+                audience_logs.UNEXPECTED_TYPE.format(
+                    self._get_condition_json(index), type(user_version), condition_name
+                )
+            )
+            return None
+
+        result = self.compare_user_version_with_target_version(target_version, user_version)
+        if result is None:
+            return None
+
+        return result == 0
+
+    def semver_greater_than_evaluator(self, index):
+        """ Evaluate the given semantic version greater than match target version for the user version.
+
+        Args:
+          index: Index of the condition to be evaluated.
+
+        Returns:
+          Boolean:
+            - True if the user version is greater than the target version.
+            - False if the user version is less than or equal to the target version.
+          None:
+            - if the user version value is not string type or is null.
+        """
+        condition_name = self.condition_data[index][0]
+        target_version = self.condition_data[index][1]
+        user_version = self.attributes.get(condition_name)
+
+        if not isinstance(target_version, string_types):
+            self.logger.warning(audience_logs.UNKNOWN_CONDITION_VALUE.format(self._get_condition_json(index), ))
+            return None
+
+        if not isinstance(user_version, string_types):
+            self.logger.warning(
+                audience_logs.UNEXPECTED_TYPE.format(
+                    self._get_condition_json(index), type(user_version), condition_name
+                )
+            )
+            return None
+
+        result = self.compare_user_version_with_target_version(target_version, user_version)
+        if result is None:
+            return None
+
+        return result > 0
+
+    def semver_less_than_evaluator(self, index):
+        """ Evaluate the given semantic version less than match target version for the user version.
+
+        Args:
+          index: Index of the condition to be evaluated.
+
+        Returns:
+          Boolean:
+            - True if the user version is less than the target version.
+            - False if the user version is greater than or equal to the target version.
+          None:
+            - if the user version value is not string type or is null.
+        """
+        condition_name = self.condition_data[index][0]
+        target_version = self.condition_data[index][1]
+        user_version = self.attributes.get(condition_name)
+
+        if not isinstance(target_version, string_types):
+            self.logger.warning(audience_logs.UNKNOWN_CONDITION_VALUE.format(self._get_condition_json(index), ))
+            return None
+
+        if not isinstance(user_version, string_types):
+            self.logger.warning(
+                audience_logs.UNEXPECTED_TYPE.format(
+                    self._get_condition_json(index), type(user_version), condition_name
+                )
+            )
+            return None
+
+        result = self.compare_user_version_with_target_version(target_version, user_version)
+        if result is None:
+            return None
+
+        return result < 0
+
+    def semver_less_than_or_equal_evaluator(self, index):
+        """ Evaluate the given semantic version less than or equal to match target version for the user version.
+
+        Args:
+          index: Index of the condition to be evaluated.
+
+        Returns:
+          Boolean:
+            - True if the user version is less than or equal to the target version.
+            - False if the user version is greater than the target version.
+          None:
+            - if the user version value is not string type or is null.
+        """
+        condition_name = self.condition_data[index][0]
+        target_version = self.condition_data[index][1]
+        user_version = self.attributes.get(condition_name)
+
+        if not isinstance(target_version, string_types):
+            self.logger.warning(audience_logs.UNKNOWN_CONDITION_VALUE.format(self._get_condition_json(index), ))
+            return None
+
+        if not isinstance(user_version, string_types):
+            self.logger.warning(
+                audience_logs.UNEXPECTED_TYPE.format(
+                    self._get_condition_json(index), type(user_version), condition_name
+                )
+            )
+            return None
+
+        result = self.compare_user_version_with_target_version(target_version, user_version)
+        if result is None:
+            return None
+
+        return result <= 0
+
+    def semver_greater_than_or_equal_evaluator(self, index):
+        """ Evaluate the given semantic version greater than or equal to match target version for the user version.
+
+        Args:
+          index: Index of the condition to be evaluated.
+
+        Returns:
+          Boolean:
+            - True if the user version is greater than or equal to the target version.
+            - False if the user version is less than the target version.
+          None:
+            - if the user version value is not string type or is null.
+        """
+        condition_name = self.condition_data[index][0]
+        target_version = self.condition_data[index][1]
+        user_version = self.attributes.get(condition_name)
+
+        if not isinstance(target_version, string_types):
+            self.logger.warning(audience_logs.UNKNOWN_CONDITION_VALUE.format(self._get_condition_json(index), ))
+            return None
+
+        if not isinstance(user_version, string_types):
+            self.logger.warning(
+                audience_logs.UNEXPECTED_TYPE.format(
+                    self._get_condition_json(index), type(user_version), condition_name
+                )
+            )
+            return None
+
+        result = self.compare_user_version_with_target_version(target_version, user_version)
+        if result is None:
+            return None
+
+        return result >= 0
+
     EVALUATORS_BY_MATCH_TYPE = {
         ConditionMatchTypes.EXACT: exact_evaluator,
         ConditionMatchTypes.EXISTS: exists_evaluator,
         ConditionMatchTypes.GREATER_THAN: greater_than_evaluator,
+        ConditionMatchTypes.GREATER_THAN_OR_EQUAL: greater_than_or_equal_evaluator,
         ConditionMatchTypes.LESS_THAN: less_than_evaluator,
-        ConditionMatchTypes.SUBSTRING: substring_evaluator,
+        ConditionMatchTypes.LESS_THAN_OR_EQUAL: less_than_or_equal_evaluator,
+        ConditionMatchTypes.SEMVER_EQ: semver_equal_evaluator,
+        ConditionMatchTypes.SEMVER_GE: semver_greater_than_or_equal_evaluator,
+        ConditionMatchTypes.SEMVER_GT: semver_greater_than_evaluator,
+        ConditionMatchTypes.SEMVER_LE: semver_less_than_or_equal_evaluator,
+        ConditionMatchTypes.SEMVER_LT: semver_less_than_evaluator,
+        ConditionMatchTypes.SUBSTRING: substring_evaluator
     }
 
+    def split_version(self, version):
+        """ Method to split the given version.
+
+        Args:
+          version: Given version.
+
+        Returns:
+          List:
+            - The array of version split into smaller parts i.e major, minor, patch etc
+          None:
+            - if the given version is invalid in format
+        """
+        target_prefix = version
+        target_suffix = ""
+        target_parts = []
+
+        # check that version shouldn't have white space
+        if self.has_white_space(version):
+            self.logger.warning(Errors.INVALID_ATTRIBUTE_FORMAT)
+            return None
+
+        # check for pre release e.g. 1.0.0-alpha where 'alpha' is a pre release
+        # otherwise check for build e.g. 1.0.0+001 where 001 is a build metadata
+        if self.is_pre_release_version(version) or self.is_build_version(version):
+            target_parts = version.split(VersionType.IS_PRE_RELEASE, 1) if self.is_pre_release_version(version) else \
+                version.split(VersionType.IS_BUILD, 1)
+
+        # split version into prefix and suffix
+        if target_parts:
+            if len(target_parts) < 1:
+                self.logger.warning(Errors.INVALID_ATTRIBUTE_FORMAT)
+                return None
+            target_prefix = str(target_parts[0])
+            target_suffix = target_parts[1:]
+
+        # check dot counts in target_prefix
+        dot_count = target_prefix.count(".")
+        if dot_count > 2:
+            self.logger.warning(Errors.INVALID_ATTRIBUTE_FORMAT)
+            return None
+
+        target_version_parts = target_prefix.split(".")
+        if len(target_version_parts) != dot_count + 1:
+            self.logger.warning(Errors.INVALID_ATTRIBUTE_FORMAT)
+            return None
+        for part in target_version_parts:
+            if not part.isdigit():
+                self.logger.warning(Errors.INVALID_ATTRIBUTE_FORMAT)
+                return None
+
+        if target_suffix:
+            target_version_parts.extend(target_suffix)
+        return target_version_parts
+
     def evaluate(self, index):
         """ Given a custom attribute audience condition and user attributes, evaluate the
         condition against the attributes.
diff --git a/optimizely/helpers/enums.py b/optimizely/helpers/enums.py
index beaba157..3eed4a30 100644
--- a/optimizely/helpers/enums.py
+++ b/optimizely/helpers/enums.py
@@ -157,3 +157,8 @@ class NotificationTypes(object):
     OPTIMIZELY_CONFIG_UPDATE = 'OPTIMIZELY_CONFIG_UPDATE'
     TRACK = 'TRACK:event_key, user_id, attributes, event_tags, event'
     LOG_EVENT = 'LOG_EVENT:log_event'
+
+
+class VersionType(object):
+    IS_PRE_RELEASE = '-'
+    IS_BUILD = '+'
diff --git a/tests/base.py b/tests/base.py
index 432d5287..9dceec2d 100644
--- a/tests/base.py
+++ b/tests/base.py
@@ -518,6 +518,7 @@ def setUp(self, config_dict='config_dict'):
                                 '3468206647',
                                 '3468206644',
                                 '3468206643',
+                                '18278344267'
                             ],
                             'variations': [
                                 {'variables': [], 'id': '11557362669', 'key': '11557362669', 'featureEnabled': True}
@@ -556,7 +557,8 @@ def setUp(self, config_dict='config_dict'):
                             'audienceConditions': [
                                 'and',
                                 ['or', '3468206642', '3988293898'],
-                                ['or', '3988293899', '3468206646', '3468206647', '3468206644', '3468206643'],
+                                ['or', '3988293899', '3468206646', '3468206647', '3468206644', '3468206643',
+                                 '18278344267'],
                             ],
                             'variations': [
                                 {'variables': [], 'id': '11557362670', 'key': '11557362670', 'featureEnabled': True}
@@ -626,6 +628,7 @@ def setUp(self, config_dict='config_dict'):
                         '3468206647',
                         '3468206644',
                         '3468206643',
+                        '18278344267'
                     ],
                     'variations': [
                         {
@@ -653,6 +656,7 @@ def setUp(self, config_dict='config_dict'):
                         '3468206647',
                         '3468206644',
                         '3468206643',
+                        '18278344267'
                     ],
                     'forcedVariations': {},
                 },
@@ -667,7 +671,7 @@ def setUp(self, config_dict='config_dict'):
                     'audienceConditions': [
                         'and',
                         ['or', '3468206642', '3988293898'],
-                        ['or', '3988293899', '3468206646', '3468206647', '3468206644', '3468206643'],
+                        ['or', '3988293899', '3468206646', '3468206647', '3468206644', '3468206643', '18278344267'],
                     ],
                     'forcedVariations': {},
                 },
@@ -689,7 +693,7 @@ def setUp(self, config_dict='config_dict'):
                     'audienceConditions': [
                         'and',
                         ['or', '3468206642', '3988293898'],
-                        ['or', '3988293899', '3468206646', '3468206647', '3468206644', '3468206643'],
+                        ['or', '3988293899', '3468206646', '3468206647', '3468206644', '3468206643', '18278344267'],
                     ],
                     'forcedVariations': {},
                 },
@@ -837,6 +841,37 @@ def setUp(self, config_dict='config_dict'):
                         ],
                     ],
                 },
+                {
+                    "id": "18278344267",
+                    "name": "semverReleaseLt1.2.3Gt1.0.0",
+                    "conditions": [
+                        "and",
+                        [
+                            "or",
+                            [
+                                "or",
+                                {
+                                    "value": "1.2.3",
+                                    "type": "custom_attribute",
+                                    "name": "android-release",
+                                    "match": "semver_lt"
+                                }
+                            ]
+                        ],
+                        [
+                            "or",
+                            [
+                                "or",
+                                {
+                                    "value": "1.0.0",
+                                    "type": "custom_attribute",
+                                    "name": "android-release",
+                                    "match": "semver_gt"
+                                }
+                            ]
+                        ]
+                    ]
+                }
             ],
             'groups': [],
             'attributes': [
@@ -844,6 +879,7 @@ def setUp(self, config_dict='config_dict'):
                 {'key': 'lasers', 'id': '594016'},
                 {'key': 'should_do_it', 'id': '594017'},
                 {'key': 'favorite_ice_cream', 'id': '594018'},
+                {'key': 'android-release', 'id': '594019'},
             ],
             'botFiltering': False,
             'accountId': '4879520872',
diff --git a/tests/helpers_tests/test_condition.py b/tests/helpers_tests/test_condition.py
index b4dee368..1a20e9ae 100644
--- a/tests/helpers_tests/test_condition.py
+++ b/tests/helpers_tests/test_condition.py
@@ -1,4 +1,4 @@
-# Copyright 2016-2019, Optimizely
+# Copyright 2016-2020, Optimizely
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # You may obtain a copy of the License at
@@ -32,11 +32,15 @@
 substring_condition_list = [['headline_text', 'buy now', 'custom_attribute', 'substring']]
 gt_int_condition_list = [['meters_travelled', 48, 'custom_attribute', 'gt']]
 gt_float_condition_list = [['meters_travelled', 48.2, 'custom_attribute', 'gt']]
+ge_int_condition_list = [['meters_travelled', 48, 'custom_attribute', 'ge']]
+ge_float_condition_list = [['meters_travelled', 48.2, 'custom_attribute', 'ge']]
 lt_int_condition_list = [['meters_travelled', 48, 'custom_attribute', 'lt']]
 lt_float_condition_list = [['meters_travelled', 48.2, 'custom_attribute', 'lt']]
+le_int_condition_list = [['meters_travelled', 48, 'custom_attribute', 'le']]
+le_float_condition_list = [['meters_travelled', 48.2, 'custom_attribute', 'le']]
 
 
-class CustomAttributeConditionEvaluator(base.BaseTest):
+class CustomAttributeConditionEvaluatorTest(base.BaseTest):
     def setUp(self):
         base.BaseTest.setUp(self)
         self.condition_list = [
@@ -108,6 +112,208 @@ def test_evaluate__returns_null__when_condition_has_an_invalid_type_property(sel
 
         self.assertIsNone(evaluator.evaluate(0))
 
+    def test_semver_eq__returns_true(self):
+        semver_equal_2_0_condition_list = [['Android', "2.0", 'custom_attribute', 'semver_eq']]
+        user_versions = ['2.0.0', '2.0']
+        for user_version in user_versions:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                semver_equal_2_0_condition_list, {'Android': user_version}, self.mock_client_logger)
+            result = evaluator.evaluate(0)
+            custom_err_msg = "Got {} in result. Failed for user version: {}".format(result, user_version)
+            self.assertTrue(result, custom_err_msg)
+
+    def test_semver_eq__returns_false(self):
+        semver_equal_2_0_condition_list = [['Android', "2.0", 'custom_attribute', 'semver_eq']]
+        user_versions = ['2.9', '1.9']
+        for user_version in user_versions:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                semver_equal_2_0_condition_list, {'Android': user_version}, self.mock_client_logger)
+            result = evaluator.evaluate(0)
+            custom_err_msg = "Got {} in result. Failed for user version: {}".format(result, user_version)
+            self.assertFalse(result, custom_err_msg)
+
+    def test_semver_le__returns_true(self):
+        semver_less_than_or_equal_2_0_condition_list = [['Android', "2.0", 'custom_attribute', 'semver_le']]
+        user_versions = ['2.0.0', '1.9']
+        for user_version in user_versions:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                semver_less_than_or_equal_2_0_condition_list, {'Android': user_version}, self.mock_client_logger)
+            result = evaluator.evaluate(0)
+            custom_err_msg = "Got {} in result. Failed for user version: {}".format(result, user_version)
+            self.assertTrue(result, custom_err_msg)
+
+    def test_semver_le__returns_false(self):
+        semver_less_than_or_equal_2_0_condition_list = [['Android', "2.0", 'custom_attribute', 'semver_le']]
+        user_versions = ['2.5.1']
+        for user_version in user_versions:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                semver_less_than_or_equal_2_0_condition_list, {'Android': user_version}, self.mock_client_logger)
+            result = evaluator.evaluate(0)
+            custom_err_msg = "Got {} in result. Failed for user version: {}".format(result, user_version)
+            self.assertFalse(result, custom_err_msg)
+
+    def test_semver_ge__returns_true(self):
+        semver_greater_than_or_equal_2_0_condition_list = [['Android', "2.0", 'custom_attribute', 'semver_ge']]
+        user_versions = ['2.0.0', '2.9']
+        for user_version in user_versions:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                semver_greater_than_or_equal_2_0_condition_list, {'Android': user_version}, self.mock_client_logger)
+            result = evaluator.evaluate(0)
+            custom_err_msg = "Got {} in result. Failed for user version: {}".format(result, user_version)
+            self.assertTrue(result, custom_err_msg)
+
+    def test_semver_ge__returns_false(self):
+        semver_greater_than_or_equal_2_0_condition_list = [['Android', "2.0", 'custom_attribute', 'semver_ge']]
+        user_versions = ['1.9']
+        for user_version in user_versions:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                semver_greater_than_or_equal_2_0_condition_list, {'Android': user_version}, self.mock_client_logger)
+            result = evaluator.evaluate(0)
+            custom_err_msg = "Got {} in result. Failed for user version: {}".format(result, user_version)
+            self.assertFalse(result, custom_err_msg)
+
+    def test_semver_lt__returns_true(self):
+        semver_less_than_2_0_condition_list = [['Android', "2.0", 'custom_attribute', 'semver_lt']]
+        user_versions = ['1.9']
+        for user_version in user_versions:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                semver_less_than_2_0_condition_list, {'Android': user_version}, self.mock_client_logger)
+            result = evaluator.evaluate(0)
+            custom_err_msg = "Got {} in result. Failed for user version: {}".format(result, user_version)
+            self.assertTrue(result, custom_err_msg)
+
+    def test_semver_lt__returns_false(self):
+        semver_less_than_2_0_condition_list = [['Android', "2.0", 'custom_attribute', 'semver_lt']]
+        user_versions = ['2.0.0', '2.5.1']
+        for user_version in user_versions:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                semver_less_than_2_0_condition_list, {'Android': user_version}, self.mock_client_logger)
+            result = evaluator.evaluate(0)
+            custom_err_msg = "Got {} in result. Failed for user version: {}".format(result, user_version)
+            self.assertFalse(result, custom_err_msg)
+
+    def test_semver_gt__returns_true(self):
+        semver_greater_than_2_0_condition_list = [['Android', "2.0", 'custom_attribute', 'semver_gt']]
+        user_versions = ['2.9']
+        for user_version in user_versions:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                semver_greater_than_2_0_condition_list, {'Android': user_version}, self.mock_client_logger)
+            result = evaluator.evaluate(0)
+            custom_err_msg = "Got {} in result. Failed for user version: {}".format(result, user_version)
+            self.assertTrue(result, custom_err_msg)
+
+    def test_semver_gt__returns_false(self):
+        semver_greater_than_2_0_condition_list = [['Android', "2.0", 'custom_attribute', 'semver_gt']]
+        user_versions = ['2.0.0', '1.9']
+        for user_version in user_versions:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                semver_greater_than_2_0_condition_list, {'Android': user_version}, self.mock_client_logger)
+            result = evaluator.evaluate(0)
+            custom_err_msg = "Got {} in result. Failed for user version: {}".format(result, user_version)
+            self.assertFalse(result, custom_err_msg)
+
+    def test_evaluate__returns_None__when_user_version_is_not_string(self):
+        semver_greater_than_2_0_condition_list = [['Android', "2.0", 'custom_attribute', 'semver_gt']]
+        user_versions = [True, 37]
+        for user_version in user_versions:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                semver_greater_than_2_0_condition_list, {'Android': user_version}, self.mock_client_logger)
+            result = evaluator.evaluate(0)
+            custom_err_msg = "Got {} in result. Failed for user version: {}".format(result, user_version)
+            self.assertIsNone(result, custom_err_msg)
+
+    def test_evaluate__returns_None__when_user_version_with_invalid_semantic(self):
+        semver_greater_than_2_0_condition_list = [['Android', "2.0", 'custom_attribute', 'semver_gt']]
+        user_versions = ['3.7.2.2', '+']
+        for user_version in user_versions:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                semver_greater_than_2_0_condition_list, {'Android': user_version}, self.mock_client_logger)
+            result = evaluator.evaluate(0)
+            custom_err_msg = "Got {} in result. Failed for user version: {}".format(result, user_version)
+            self.assertIsNone(result, custom_err_msg)
+
+    def test_compare_user_version_with_target_version_equal_to_0(self):
+        semver_greater_than_2_0_condition_list = [['Android', "2.0", 'custom_attribute', 'semver_gt']]
+        versions = [
+            ('2.0.1', '2.0.1'),
+            ('2.9.9-beta', '2.9.9-beta'),
+            ('2.1', '2.1.0'),
+            ('2', '2.12'),
+            ('2.9', '2.9.1'),
+            ('2.9.1', '2.9.1+beta')
+        ]
+        for target_version, user_version in versions:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                semver_greater_than_2_0_condition_list, {'Android': user_version}, self.mock_client_logger)
+            result = evaluator.compare_user_version_with_target_version(target_version, user_version)
+            custom_err_msg = "Got {} in result. Failed for user version:" \
+                             " {} and target version: {}".format(result,
+                                                                 user_version,
+                                                                 target_version
+                                                                 )
+            self.assertEqual(result, 0, custom_err_msg)
+
+    def test_compare_user_version_with_target_version_greater_than_0(self):
+        semver_greater_than_2_0_condition_list = [['Android', "2.0", 'custom_attribute', 'semver_gt']]
+        versions = [
+            ('2.0.0', '2.0.1'),
+            ('2.0', '3.0.1'),
+            ('2.1.2-beta', '2.1.2-release'),
+            ('2.1.3-beta1', '2.1.3-beta2'),
+            ('2.9.9-beta', '2.9.9'),
+            ('2.9.9+beta', '2.9.9'),
+            ('3.7.0-prerelease+build', '3.7.0-prerelease+rc'),
+            ('2.2.3-beta-beta1', '2.2.3-beta-beta2'),
+            ('2.2.3-beta+beta1', '2.2.3-beta+beta2'),
+            ('2.2.3+beta2-beta1', '2.2.3+beta3-beta2')
+        ]
+        for target_version, user_version in versions:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                semver_greater_than_2_0_condition_list, {'Android': user_version}, self.mock_client_logger)
+            result = evaluator.compare_user_version_with_target_version(target_version, user_version)
+            custom_err_msg = "Got {} in result. Failed for user version:" \
+                             " {} and target version: {}".format(result,
+                                                                 user_version,
+                                                                 target_version)
+            self.assertEqual(result, 1, custom_err_msg)
+
+    def test_compare_user_version_with_target_version_less_than_0(self):
+        semver_greater_than_2_0_condition_list = [['Android', "2.0", 'custom_attribute', 'semver_gt']]
+        versions = [
+            ('2.0.1', '2.0.0'),
+            ('3.0', '2.0.1'),
+            ('2.3', '2.0.1'),
+            ('2.3.5', '2.3.1'),
+            ('2.9.8', '2.9'),
+            ('2.1.2-release', '2.1.2-beta'),
+            ('2.9.9+beta', '2.9.9-beta'),
+            ('3.7.0+build3.7.0-prerelease+build', '3.7.0-prerelease'),
+            ('2.1.3-beta-beta2', '2.1.3-beta'),
+            ('2.1.3-beta1+beta3', '2.1.3-beta1+beta2')
+        ]
+        for target_version, user_version in versions:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                semver_greater_than_2_0_condition_list, {'Android': user_version}, self.mock_client_logger)
+            result = evaluator.compare_user_version_with_target_version(target_version, user_version)
+            custom_err_msg = "Got {} in result. Failed for user version: {} " \
+                             "and target version: {}".format(result,
+                                                             user_version,
+                                                             target_version)
+            self.assertEqual(result, -1, custom_err_msg)
+
+    def test_compare_invalid_user_version_with(self):
+        semver_greater_than_2_0_condition_list = [['Android', "2.0", 'custom_attribute', 'semver_gt']]
+        versions = ['-', '.', '..', '+', '+test', ' ', '2 .3. 0', '2.', '.2.2', '3.7.2.2', '3.x', ',',
+                    '+build-prerelease', '2..2']
+        target_version = '2.1.0'
+
+        for user_version in versions:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                semver_greater_than_2_0_condition_list, {'Android': user_version}, self.mock_client_logger)
+            result = evaluator.compare_user_version_with_target_version(user_version, target_version)
+            custom_err_msg = "Got {} in result. Failed for user version: {}".format(result, user_version)
+            self.assertIsNone(result, custom_err_msg)
+
     def test_exists__returns_false__when_no_user_provided_value(self):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
@@ -154,7 +360,7 @@ def test_exists__returns_true__when_user_provided_value_is_boolean(self):
 
         self.assertStrictTrue(evaluator.evaluate(0))
 
-    def test_exact_string__returns_true__when_user_provided_value_is_equal_to_condition_value(self,):
+    def test_exact_string__returns_true__when_user_provided_value_is_equal_to_condition_value(self, ):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             exact_string_condition_list, {'favorite_constellation': 'Lacerta'}, self.mock_client_logger,
@@ -162,7 +368,7 @@ def test_exact_string__returns_true__when_user_provided_value_is_equal_to_condit
 
         self.assertStrictTrue(evaluator.evaluate(0))
 
-    def test_exact_string__returns_false__when_user_provided_value_is_not_equal_to_condition_value(self,):
+    def test_exact_string__returns_false__when_user_provided_value_is_not_equal_to_condition_value(self, ):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             exact_string_condition_list, {'favorite_constellation': 'The Big Dipper'}, self.mock_client_logger,
@@ -170,7 +376,7 @@ def test_exact_string__returns_false__when_user_provided_value_is_not_equal_to_c
 
         self.assertStrictFalse(evaluator.evaluate(0))
 
-    def test_exact_string__returns_null__when_user_provided_value_is_different_type_from_condition_value(self,):
+    def test_exact_string__returns_null__when_user_provided_value_is_different_type_from_condition_value(self, ):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             exact_string_condition_list, {'favorite_constellation': False}, self.mock_client_logger,
@@ -186,7 +392,7 @@ def test_exact_string__returns_null__when_no_user_provided_value(self):
 
         self.assertIsNone(evaluator.evaluate(0))
 
-    def test_exact_int__returns_true__when_user_provided_value_is_equal_to_condition_value(self,):
+    def test_exact_int__returns_true__when_user_provided_value_is_equal_to_condition_value(self, ):
 
         if PY2:
             evaluator = condition_helper.CustomAttributeConditionEvaluator(
@@ -207,7 +413,7 @@ def test_exact_int__returns_true__when_user_provided_value_is_equal_to_condition
 
         self.assertStrictTrue(evaluator.evaluate(0))
 
-    def test_exact_float__returns_true__when_user_provided_value_is_equal_to_condition_value(self,):
+    def test_exact_float__returns_true__when_user_provided_value_is_equal_to_condition_value(self, ):
 
         if PY2:
             evaluator = condition_helper.CustomAttributeConditionEvaluator(
@@ -228,7 +434,7 @@ def test_exact_float__returns_true__when_user_provided_value_is_equal_to_conditi
 
         self.assertStrictTrue(evaluator.evaluate(0))
 
-    def test_exact_int__returns_false__when_user_provided_value_is_not_equal_to_condition_value(self,):
+    def test_exact_int__returns_false__when_user_provided_value_is_not_equal_to_condition_value(self, ):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             exact_int_condition_list, {'lasers_count': 8000}, self.mock_client_logger
@@ -236,7 +442,7 @@ def test_exact_int__returns_false__when_user_provided_value_is_not_equal_to_cond
 
         self.assertStrictFalse(evaluator.evaluate(0))
 
-    def test_exact_float__returns_false__when_user_provided_value_is_not_equal_to_condition_value(self,):
+    def test_exact_float__returns_false__when_user_provided_value_is_not_equal_to_condition_value(self, ):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             exact_float_condition_list, {'lasers_count': 8000.0}, self.mock_client_logger,
@@ -244,7 +450,7 @@ def test_exact_float__returns_false__when_user_provided_value_is_not_equal_to_co
 
         self.assertStrictFalse(evaluator.evaluate(0))
 
-    def test_exact_int__returns_null__when_user_provided_value_is_different_type_from_condition_value(self,):
+    def test_exact_int__returns_null__when_user_provided_value_is_different_type_from_condition_value(self, ):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             exact_int_condition_list, {'lasers_count': 'hi'}, self.mock_client_logger
@@ -258,7 +464,7 @@ def test_exact_int__returns_null__when_user_provided_value_is_different_type_fro
 
         self.assertIsNone(evaluator.evaluate(0))
 
-    def test_exact_float__returns_null__when_user_provided_value_is_different_type_from_condition_value(self,):
+    def test_exact_float__returns_null__when_user_provided_value_is_different_type_from_condition_value(self, ):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             exact_float_condition_list, {'lasers_count': 'hi'}, self.mock_client_logger
@@ -315,7 +521,7 @@ def test_exact__given_number_values__calls_is_finite_number(self):
 
         mock_is_finite.assert_has_calls([mock.call(9000), mock.call(9000)])
 
-    def test_exact_bool__returns_true__when_user_provided_value_is_equal_to_condition_value(self,):
+    def test_exact_bool__returns_true__when_user_provided_value_is_equal_to_condition_value(self, ):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             exact_bool_condition_list, {'did_register_user': False}, self.mock_client_logger,
@@ -323,7 +529,7 @@ def test_exact_bool__returns_true__when_user_provided_value_is_equal_to_conditio
 
         self.assertStrictTrue(evaluator.evaluate(0))
 
-    def test_exact_bool__returns_false__when_user_provided_value_is_not_equal_to_condition_value(self,):
+    def test_exact_bool__returns_false__when_user_provided_value_is_not_equal_to_condition_value(self, ):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             exact_bool_condition_list, {'did_register_user': True}, self.mock_client_logger,
@@ -331,7 +537,7 @@ def test_exact_bool__returns_false__when_user_provided_value_is_not_equal_to_con
 
         self.assertStrictFalse(evaluator.evaluate(0))
 
-    def test_exact_bool__returns_null__when_user_provided_value_is_different_type_from_condition_value(self,):
+    def test_exact_bool__returns_null__when_user_provided_value_is_different_type_from_condition_value(self, ):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             exact_bool_condition_list, {'did_register_user': 0}, self.mock_client_logger
@@ -347,7 +553,7 @@ def test_exact_bool__returns_null__when_no_user_provided_value(self):
 
         self.assertIsNone(evaluator.evaluate(0))
 
-    def test_substring__returns_true__when_condition_value_is_substring_of_user_value(self,):
+    def test_substring__returns_true__when_condition_value_is_substring_of_user_value(self, ):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             substring_condition_list, {'headline_text': 'Limited time, buy now!'}, self.mock_client_logger,
@@ -355,7 +561,7 @@ def test_substring__returns_true__when_condition_value_is_substring_of_user_valu
 
         self.assertStrictTrue(evaluator.evaluate(0))
 
-    def test_substring__returns_false__when_condition_value_is_not_a_substring_of_user_value(self,):
+    def test_substring__returns_false__when_condition_value_is_not_a_substring_of_user_value(self, ):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             substring_condition_list, {'headline_text': 'Breaking news!'}, self.mock_client_logger,
@@ -379,7 +585,7 @@ def test_substring__returns_null__when_no_user_provided_value(self):
 
         self.assertIsNone(evaluator.evaluate(0))
 
-    def test_greater_than_int__returns_true__when_user_value_greater_than_condition_value(self,):
+    def test_greater_than_int__returns_true__when_user_value_greater_than_condition_value(self, ):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             gt_int_condition_list, {'meters_travelled': 48.1}, self.mock_client_logger
@@ -400,7 +606,7 @@ def test_greater_than_int__returns_true__when_user_value_greater_than_condition_
 
             self.assertStrictTrue(evaluator.evaluate(0))
 
-    def test_greater_than_float__returns_true__when_user_value_greater_than_condition_value(self,):
+    def test_greater_than_float__returns_true__when_user_value_greater_than_condition_value(self, ):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             gt_float_condition_list, {'meters_travelled': 48.3}, self.mock_client_logger
@@ -421,7 +627,7 @@ def test_greater_than_float__returns_true__when_user_value_greater_than_conditio
 
             self.assertStrictTrue(evaluator.evaluate(0))
 
-    def test_greater_than_int__returns_false__when_user_value_not_greater_than_condition_value(self,):
+    def test_greater_than_int__returns_false__when_user_value_not_greater_than_condition_value(self, ):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             gt_int_condition_list, {'meters_travelled': 47.9}, self.mock_client_logger
@@ -442,7 +648,7 @@ def test_greater_than_int__returns_false__when_user_value_not_greater_than_condi
 
             self.assertStrictFalse(evaluator.evaluate(0))
 
-    def test_greater_than_float__returns_false__when_user_value_not_greater_than_condition_value(self,):
+    def test_greater_than_float__returns_false__when_user_value_not_greater_than_condition_value(self, ):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             gt_float_condition_list, {'meters_travelled': 48.2}, self.mock_client_logger
@@ -507,7 +713,149 @@ def test_greater_than_float__returns_null__when_no_user_provided_value(self):
 
         self.assertIsNone(evaluator.evaluate(0))
 
-    def test_less_than_int__returns_true__when_user_value_less_than_condition_value(self,):
+    def test_greater_than_or_equal_int__returns_true__when_user_value_greater_than_or_equal_condition_value(self):
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            ge_int_condition_list, {'meters_travelled': 48.1}, self.mock_client_logger
+        )
+
+        self.assertStrictTrue(evaluator.evaluate(0))
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            ge_int_condition_list, {'meters_travelled': 48}, self.mock_client_logger
+        )
+
+        self.assertStrictTrue(evaluator.evaluate(0))
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            ge_int_condition_list, {'meters_travelled': 49}, self.mock_client_logger
+        )
+
+        self.assertStrictTrue(evaluator.evaluate(0))
+
+        if PY2:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                gt_int_condition_list, {'meters_travelled': long(49)}, self.mock_client_logger,
+            )
+
+            self.assertStrictTrue(evaluator.evaluate(0))
+
+    def test_greater_than_or_equal_float__returns_true__when_user_value_greater_than_or_equal_condition_value(self):
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            ge_float_condition_list, {'meters_travelled': 48.3}, self.mock_client_logger
+        )
+
+        self.assertStrictTrue(evaluator.evaluate(0))
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            ge_float_condition_list, {'meters_travelled': 48.2}, self.mock_client_logger
+        )
+
+        self.assertStrictTrue(evaluator.evaluate(0))
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            ge_float_condition_list, {'meters_travelled': 49}, self.mock_client_logger
+        )
+
+        self.assertStrictTrue(evaluator.evaluate(0))
+
+        if PY2:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                ge_float_condition_list, {'meters_travelled': long(49)}, self.mock_client_logger,
+            )
+
+            self.assertStrictTrue(evaluator.evaluate(0))
+
+    def test_greater_than_or_equal_int__returns_false__when_user_value_not_greater_than_or_equal_condition_value(
+            self):
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            ge_int_condition_list, {'meters_travelled': 47.9}, self.mock_client_logger
+        )
+
+        self.assertStrictFalse(evaluator.evaluate(0))
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            ge_int_condition_list, {'meters_travelled': 47}, self.mock_client_logger
+        )
+
+        self.assertStrictFalse(evaluator.evaluate(0))
+
+        if PY2:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                ge_int_condition_list, {'meters_travelled': long(47)}, self.mock_client_logger,
+            )
+
+            self.assertStrictFalse(evaluator.evaluate(0))
+
+    def test_greater_than_or_equal_float__returns_false__when_user_value_not_greater_than_or_equal_condition_value(
+            self):
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            ge_float_condition_list, {'meters_travelled': 48.1}, self.mock_client_logger
+        )
+
+        self.assertStrictFalse(evaluator.evaluate(0))
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            ge_float_condition_list, {'meters_travelled': 48}, self.mock_client_logger
+        )
+
+        self.assertStrictFalse(evaluator.evaluate(0))
+
+        if PY2:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                ge_float_condition_list, {'meters_travelled': long(48)}, self.mock_client_logger,
+            )
+
+            self.assertStrictFalse(evaluator.evaluate(0))
+
+    def test_greater_than_or_equal_int__returns_null__when_user_value_is_not_a_number(self):
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            ge_int_condition_list, {'meters_travelled': 'a long way'}, self.mock_client_logger,
+        )
+
+        self.assertIsNone(evaluator.evaluate(0))
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            ge_int_condition_list, {'meters_travelled': False}, self.mock_client_logger
+        )
+
+        self.assertIsNone(evaluator.evaluate(0))
+
+    def test_greater_than_or_equal_float__returns_null__when_user_value_is_not_a_number(self):
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            ge_float_condition_list, {'meters_travelled': 'a long way'}, self.mock_client_logger,
+        )
+
+        self.assertIsNone(evaluator.evaluate(0))
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            ge_float_condition_list, {'meters_travelled': False}, self.mock_client_logger,
+        )
+
+        self.assertIsNone(evaluator.evaluate(0))
+
+    def test_greater_than_or_equal_int__returns_null__when_no_user_provided_value(self):
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            ge_int_condition_list, {}, self.mock_client_logger
+        )
+
+        self.assertIsNone(evaluator.evaluate(0))
+
+    def test_greater_than_or_equal_float__returns_null__when_no_user_provided_value(self):
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            ge_float_condition_list, {}, self.mock_client_logger
+        )
+
+        self.assertIsNone(evaluator.evaluate(0))
+
+    def test_less_than_int__returns_true__when_user_value_less_than_condition_value(self):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             lt_int_condition_list, {'meters_travelled': 47.9}, self.mock_client_logger
@@ -528,7 +876,7 @@ def test_less_than_int__returns_true__when_user_value_less_than_condition_value(
 
             self.assertStrictTrue(evaluator.evaluate(0))
 
-    def test_less_than_float__returns_true__when_user_value_less_than_condition_value(self,):
+    def test_less_than_float__returns_true__when_user_value_less_than_condition_value(self, ):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             lt_float_condition_list, {'meters_travelled': 48.1}, self.mock_client_logger
@@ -549,7 +897,7 @@ def test_less_than_float__returns_true__when_user_value_less_than_condition_valu
 
             self.assertStrictTrue(evaluator.evaluate(0))
 
-    def test_less_than_int__returns_false__when_user_value_not_less_than_condition_value(self,):
+    def test_less_than_int__returns_false__when_user_value_not_less_than_condition_value(self, ):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             lt_int_condition_list, {'meters_travelled': 48.1}, self.mock_client_logger
@@ -570,7 +918,7 @@ def test_less_than_int__returns_false__when_user_value_not_less_than_condition_v
 
             self.assertStrictFalse(evaluator.evaluate(0))
 
-    def test_less_than_float__returns_false__when_user_value_not_less_than_condition_value(self,):
+    def test_less_than_float__returns_false__when_user_value_not_less_than_condition_value(self, ):
 
         evaluator = condition_helper.CustomAttributeConditionEvaluator(
             lt_float_condition_list, {'meters_travelled': 48.2}, self.mock_client_logger
@@ -623,6 +971,140 @@ def test_less_than_float__returns_null__when_no_user_provided_value(self):
 
         self.assertIsNone(evaluator.evaluate(0))
 
+    def test_less_than_or_equal_int__returns_true__when_user_value_less_than_or_equal_condition_value(self):
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            le_int_condition_list, {'meters_travelled': 47.9}, self.mock_client_logger
+        )
+
+        self.assertStrictTrue(evaluator.evaluate(0))
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            le_int_condition_list, {'meters_travelled': 47}, self.mock_client_logger
+        )
+
+        self.assertStrictTrue(evaluator.evaluate(0))
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            le_int_condition_list, {'meters_travelled': 48}, self.mock_client_logger
+        )
+
+        self.assertStrictTrue(evaluator.evaluate(0))
+
+        if PY2:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                le_int_condition_list, {'meters_travelled': long(47)}, self.mock_client_logger,
+            )
+
+            self.assertStrictTrue(evaluator.evaluate(0))
+
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                le_int_condition_list, {'meters_travelled': long(48)}, self.mock_client_logger,
+            )
+
+            self.assertStrictTrue(evaluator.evaluate(0))
+
+    def test_less_than_or_equal_float__returns_true__when_user_value_less_than_or_equal_condition_value(self):
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            le_float_condition_list, {'meters_travelled': 48.1}, self.mock_client_logger
+        )
+
+        self.assertStrictTrue(evaluator.evaluate(0))
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            le_float_condition_list, {'meters_travelled': 48.2}, self.mock_client_logger
+        )
+
+        self.assertStrictTrue(evaluator.evaluate(0))
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            le_float_condition_list, {'meters_travelled': 48}, self.mock_client_logger
+        )
+
+        self.assertStrictTrue(evaluator.evaluate(0))
+
+        if PY2:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                le_float_condition_list, {'meters_travelled': long(48)}, self.mock_client_logger,
+            )
+
+            self.assertStrictTrue(evaluator.evaluate(0))
+
+    def test_less_than_or_equal_int__returns_false__when_user_value_not_less_than_or_equal_condition_value(self):
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            le_int_condition_list, {'meters_travelled': 48.1}, self.mock_client_logger
+        )
+
+        self.assertStrictFalse(evaluator.evaluate(0))
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            le_int_condition_list, {'meters_travelled': 49}, self.mock_client_logger
+        )
+
+        self.assertStrictFalse(evaluator.evaluate(0))
+
+        if PY2:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                le_int_condition_list, {'meters_travelled': long(49)}, self.mock_client_logger,
+            )
+
+            self.assertStrictFalse(evaluator.evaluate(0))
+
+    def test_less_than_or_equal_float__returns_false__when_user_value_not_less_than_or_equal_condition_value(self):
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            le_float_condition_list, {'meters_travelled': 48.3}, self.mock_client_logger
+        )
+
+        self.assertStrictFalse(evaluator.evaluate(0))
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            le_float_condition_list, {'meters_travelled': 49}, self.mock_client_logger
+        )
+
+        self.assertStrictFalse(evaluator.evaluate(0))
+
+        if PY2:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                le_float_condition_list, {'meters_travelled': long(49)}, self.mock_client_logger,
+            )
+
+            self.assertStrictFalse(evaluator.evaluate(0))
+
+    def test_less_than_or_equal_int__returns_null__when_user_value_is_not_a_number(self):
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            le_int_condition_list, {'meters_travelled': False}, self.mock_client_logger
+        )
+
+        self.assertIsNone(evaluator.evaluate(0))
+
+    def test_less_than_or_equal_float__returns_null__when_user_value_is_not_a_number(self):
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            le_float_condition_list, {'meters_travelled': False}, self.mock_client_logger,
+        )
+
+        self.assertIsNone(evaluator.evaluate(0))
+
+    def test_less_than_or_equal_int__returns_null__when_no_user_provided_value(self):
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            le_int_condition_list, {}, self.mock_client_logger
+        )
+
+        self.assertIsNone(evaluator.evaluate(0))
+
+    def test_less_than_or_equal_float__returns_null__when_no_user_provided_value(self):
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            le_float_condition_list, {}, self.mock_client_logger
+        )
+
+        self.assertIsNone(evaluator.evaluate(0))
+
     def test_greater_than__calls_is_finite_number(self):
         """ Test that CustomAttributeConditionEvaluator.evaluate returns True
         if is_finite_number returns True. Returns None if is_finite_number returns False. """
@@ -637,7 +1119,8 @@ def is_finite_number__rejecting_condition_value(value):
             return True
 
         with mock.patch(
-            'optimizely.helpers.validator.is_finite_number', side_effect=is_finite_number__rejecting_condition_value,
+                'optimizely.helpers.validator.is_finite_number',
+                side_effect=is_finite_number__rejecting_condition_value,
         ) as mock_is_finite:
             self.assertIsNone(evaluator.evaluate(0))
 
@@ -650,8 +1133,8 @@ def is_finite_number__rejecting_user_attribute_value(value):
             return True
 
         with mock.patch(
-            'optimizely.helpers.validator.is_finite_number',
-            side_effect=is_finite_number__rejecting_user_attribute_value,
+                'optimizely.helpers.validator.is_finite_number',
+                side_effect=is_finite_number__rejecting_user_attribute_value,
         ) as mock_is_finite:
             self.assertIsNone(evaluator.evaluate(0))
 
@@ -662,7 +1145,7 @@ def is_finite_number__accepting_both_values(value):
             return True
 
         with mock.patch(
-            'optimizely.helpers.validator.is_finite_number', side_effect=is_finite_number__accepting_both_values,
+                'optimizely.helpers.validator.is_finite_number', side_effect=is_finite_number__accepting_both_values,
         ):
             self.assertTrue(evaluator.evaluate(0))
 
@@ -680,7 +1163,8 @@ def is_finite_number__rejecting_condition_value(value):
             return True
 
         with mock.patch(
-            'optimizely.helpers.validator.is_finite_number', side_effect=is_finite_number__rejecting_condition_value,
+                'optimizely.helpers.validator.is_finite_number',
+                side_effect=is_finite_number__rejecting_condition_value,
         ) as mock_is_finite:
             self.assertIsNone(evaluator.evaluate(0))
 
@@ -693,8 +1177,8 @@ def is_finite_number__rejecting_user_attribute_value(value):
             return True
 
         with mock.patch(
-            'optimizely.helpers.validator.is_finite_number',
-            side_effect=is_finite_number__rejecting_user_attribute_value,
+                'optimizely.helpers.validator.is_finite_number',
+                side_effect=is_finite_number__rejecting_user_attribute_value,
         ) as mock_is_finite:
             self.assertIsNone(evaluator.evaluate(0))
 
@@ -705,10 +1189,112 @@ def is_finite_number__accepting_both_values(value):
             return True
 
         with mock.patch(
-            'optimizely.helpers.validator.is_finite_number', side_effect=is_finite_number__accepting_both_values,
+                'optimizely.helpers.validator.is_finite_number', side_effect=is_finite_number__accepting_both_values,
+        ):
+            self.assertTrue(evaluator.evaluate(0))
+
+    def test_greater_than_or_equal__calls_is_finite_number(self):
+        """ Test that CustomAttributeConditionEvaluator.evaluate returns True
+        if is_finite_number returns True. Returns None if is_finite_number returns False. """
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            ge_int_condition_list, {'meters_travelled': 48.1}, self.mock_client_logger
+        )
+
+        def is_finite_number__rejecting_condition_value(value):
+            if value == 48:
+                return False
+            return True
+
+        with mock.patch(
+                'optimizely.helpers.validator.is_finite_number',
+                side_effect=is_finite_number__rejecting_condition_value,
+        ) as mock_is_finite:
+            self.assertIsNone(evaluator.evaluate(0))
+
+        # assert that isFiniteNumber only needs to reject condition value to stop evaluation.
+        mock_is_finite.assert_called_once_with(48)
+
+        def is_finite_number__rejecting_user_attribute_value(value):
+            if value == 48.1:
+                return False
+            return True
+
+        with mock.patch(
+                'optimizely.helpers.validator.is_finite_number',
+                side_effect=is_finite_number__rejecting_user_attribute_value,
+        ) as mock_is_finite:
+            self.assertIsNone(evaluator.evaluate(0))
+
+        # assert that isFiniteNumber evaluates user value only if it has accepted condition value.
+        mock_is_finite.assert_has_calls([mock.call(48), mock.call(48.1)])
+
+        def is_finite_number__accepting_both_values(value):
+            return True
+
+        with mock.patch(
+                'optimizely.helpers.validator.is_finite_number', side_effect=is_finite_number__accepting_both_values,
         ):
             self.assertTrue(evaluator.evaluate(0))
 
+    def test_less_than_or_equal__calls_is_finite_number(self):
+        """ Test that CustomAttributeConditionEvaluator.evaluate returns True
+        if is_finite_number returns True. Returns None if is_finite_number returns False. """
+
+        evaluator = condition_helper.CustomAttributeConditionEvaluator(
+            le_int_condition_list, {'meters_travelled': 47}, self.mock_client_logger
+        )
+
+        def is_finite_number__rejecting_condition_value(value):
+            if value == 48:
+                return False
+            return True
+
+        with mock.patch(
+                'optimizely.helpers.validator.is_finite_number',
+                side_effect=is_finite_number__rejecting_condition_value,
+        ) as mock_is_finite:
+            self.assertIsNone(evaluator.evaluate(0))
+
+        # assert that isFiniteNumber only needs to reject condition value to stop evaluation.
+        mock_is_finite.assert_called_once_with(48)
+
+        def is_finite_number__rejecting_user_attribute_value(value):
+            if value == 47:
+                return False
+            return True
+
+        with mock.patch(
+                'optimizely.helpers.validator.is_finite_number',
+                side_effect=is_finite_number__rejecting_user_attribute_value,
+        ) as mock_is_finite:
+            self.assertIsNone(evaluator.evaluate(0))
+
+        # assert that isFiniteNumber evaluates user value only if it has accepted condition value.
+        mock_is_finite.assert_has_calls([mock.call(48), mock.call(47)])
+
+        def is_finite_number__accepting_both_values(value):
+            return True
+
+        with mock.patch(
+                'optimizely.helpers.validator.is_finite_number', side_effect=is_finite_number__accepting_both_values,
+        ):
+            self.assertTrue(evaluator.evaluate(0))
+
+    def test_invalid_semver__returns_None__when_semver_is_invalid(self):
+        semver_less_than_or_equal_2_0_1_condition_list = [['Android', "2.0.1", 'custom_attribute', 'semver_le']]
+        invalid_test_cases = ["-", ".", "..", "+", "+test", " ", "2 .0. 0",
+                              "2.", ".0.0", "1.2.2.2", "2.x", ",",
+                              "+build-prerelease", "2..0"]
+
+        for user_version in invalid_test_cases:
+            evaluator = condition_helper.CustomAttributeConditionEvaluator(
+                semver_less_than_or_equal_2_0_1_condition_list, {'Android': user_version}, self.mock_client_logger)
+
+            result = evaluator.evaluate(0)
+            custom_err_msg = "Got {} in result. Failed for user version: {}".format(result, user_version)
+            self.assertIsNone(result, custom_err_msg)
+
 
 class ConditionDecoderTests(base.BaseTest):
     def test_loads(self):
diff --git a/tests/test_optimizely.py b/tests/test_optimizely.py
index 94783a7a..f586c44c 100644
--- a/tests/test_optimizely.py
+++ b/tests/test_optimizely.py
@@ -844,6 +844,48 @@ def test_activate__with_attributes__typed_audience_match(self):
 
         self.assertTrue(expected_attr in [x.__dict__ for x in mock_process.call_args[0][0].visitor_attributes])
 
+    def test_activate__with_attributes__typed_audience_with_semver_match(self):
+        """ Test that activate calls process with right params and returns expected
+    variation when attributes are provided and typed audience conditions are met. """
+        opt_obj = optimizely.Optimizely(json.dumps(self.config_dict_with_typed_audiences))
+
+        with mock.patch('optimizely.event.event_processor.ForwardingEventProcessor.process') as mock_process:
+            # Should be included via exact match string audience with id '18278344267'
+            self.assertEqual(
+                'A', opt_obj.activate('typed_audience_experiment', 'test_user', {'android-release': '1.0.1'}),
+            )
+        expected_attr = {
+            'type': 'custom',
+            'value': '1.0.1',
+            'entity_id': '594019',
+            'key': 'android-release',
+        }
+
+        self.assertTrue(expected_attr in [x.__dict__ for x in mock_process.call_args[0][0].visitor_attributes])
+
+        mock_process.reset()
+
+        with mock.patch('optimizely.event.event_processor.ForwardingEventProcessor.process') as mock_process:
+            self.assertEqual(
+                'A', opt_obj.activate('typed_audience_experiment', 'test_user', {'android-release': "1.2.2"}),
+            )
+        expected_attr = {
+            'type': 'custom',
+            'value': "1.2.2",
+            'entity_id': '594019',
+            'key': 'android-release',
+        }
+
+        self.assertTrue(expected_attr in [x.__dict__ for x in mock_process.call_args[0][0].visitor_attributes])
+
+    def test_activate__with_attributes__typed_audience_with_semver_mismatch(self):
+        """ Test that activate returns None when typed audience conditions do not match. """
+        opt_obj = optimizely.Optimizely(json.dumps(self.config_dict_with_typed_audiences))
+
+        with mock.patch('optimizely.event.event_processor.ForwardingEventProcessor.process') as mock_process:
+            self.assertIsNone(opt_obj.activate('typed_audience_experiment', 'test_user', {'android-release': '1.2.9'}))
+        self.assertEqual(0, mock_process.call_count)
+
     def test_activate__with_attributes__typed_audience_mismatch(self):
         """ Test that activate returns None when typed audience conditions do not match. """
         opt_obj = optimizely.Optimizely(json.dumps(self.config_dict_with_typed_audiences))