diff --git a/core-api/src/main/java/com/optimizely/ab/OptimizelyUserContext.java b/core-api/src/main/java/com/optimizely/ab/OptimizelyUserContext.java index d05df3bbb..e4a85d3b6 100644 --- a/core-api/src/main/java/com/optimizely/ab/OptimizelyUserContext.java +++ b/core-api/src/main/java/com/optimizely/ab/OptimizelyUserContext.java @@ -16,7 +16,6 @@ */ package com.optimizely.ab; -import com.optimizely.ab.config.Variation; import com.optimizely.ab.optimizelydecision.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,6 +35,9 @@ public class OptimizelyUserContext { @Nonnull private final Map attributes; + @Nonnull + private final List qualifiedSegments; + @Nonnull private final Optimizely optimizely; @@ -44,13 +46,7 @@ public class OptimizelyUserContext { public OptimizelyUserContext(@Nonnull Optimizely optimizely, @Nonnull String userId, @Nonnull Map attributes) { - this.optimizely = optimizely; - this.userId = userId; - if (attributes != null) { - this.attributes = Collections.synchronizedMap(new HashMap<>(attributes)); - } else { - this.attributes = Collections.synchronizedMap(new HashMap<>()); - } + this(optimizely, userId, attributes, Collections.EMPTY_MAP); } public OptimizelyUserContext(@Nonnull Optimizely optimizely, @@ -67,6 +63,7 @@ public OptimizelyUserContext(@Nonnull Optimizely optimizely, if (forcedDecisionsMap != null) { this.forcedDecisionsMap = new ConcurrentHashMap<>(forcedDecisionsMap); } + this.qualifiedSegments = Collections.synchronizedList(new ArrayList<>()); } public OptimizelyUserContext(@Nonnull Optimizely optimizely, @Nonnull String userId) { @@ -85,6 +82,10 @@ public Optimizely getOptimizely() { return optimizely; } + public List getQualifiedSegments() { + return qualifiedSegments; + } + public OptimizelyUserContext copy() { return new OptimizelyUserContext(optimizely, userId, attributes, forcedDecisionsMap); } diff --git a/core-api/src/main/java/com/optimizely/ab/bucketing/DecisionService.java b/core-api/src/main/java/com/optimizely/ab/bucketing/DecisionService.java index c7ee0b3f3..db097e245 100644 --- a/core-api/src/main/java/com/optimizely/ab/bucketing/DecisionService.java +++ b/core-api/src/main/java/com/optimizely/ab/bucketing/DecisionService.java @@ -152,7 +152,12 @@ public DecisionResponse getVariation(@Nonnull Experiment experiment, } } - DecisionResponse decisionMeetAudience = ExperimentUtils.doesUserMeetAudienceConditions(projectConfig, experiment, user.getAttributes(), EXPERIMENT, experiment.getKey()); + DecisionResponse decisionMeetAudience = ExperimentUtils.doesUserMeetAudienceConditions(projectConfig, + experiment, + user.getAttributes(), + EXPERIMENT, + experiment.getKey(), + user.getQualifiedSegments()); reasons.merge(decisionMeetAudience.getReasons()); if (decisionMeetAudience.getResult()) { String bucketingId = getBucketingId(user.getUserId(), user.getAttributes()); @@ -695,7 +700,8 @@ DecisionResponse getVariationFromDeliveryRule(@Nonnull rule, user.getAttributes(), RULE, - String.valueOf(ruleIndex + 1) + String.valueOf(ruleIndex + 1), + user.getQualifiedSegments() ); reasons.merge(audienceDecisionResponse.getReasons()); diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java index f6561a65c..a8f5e1257 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java @@ -42,7 +42,7 @@ public List getConditions() { } @Nullable - public Boolean evaluate(ProjectConfig config, Map attributes) { + public Boolean evaluate(ProjectConfig config, Map attributes, List qualifiedSegments) { if (conditions == null) return null; boolean foundNull = false; // According to the matrix where: @@ -53,7 +53,7 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { // true and true is true // null and null is null for (Condition condition : conditions) { - Boolean conditionEval = condition.evaluate(config, attributes); + Boolean conditionEval = condition.evaluate(config, attributes, qualifiedSegments); if (conditionEval == null) { foundNull = true; } else if (!conditionEval) { // false with nulls or trues is false. diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/AttributeType.java b/core-api/src/main/java/com/optimizely/ab/config/audience/AttributeType.java new file mode 100644 index 000000000..926e5cca4 --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/AttributeType.java @@ -0,0 +1,33 @@ +/** + * + * Copyright 2022, Optimizely and contributors + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.optimizely.ab.config.audience; + +public enum AttributeType { + CUSTOM_ATTRIBUTE("custom_attribute"), + THIRD_PARTY_DIMENSION("third_party_dimension"); + + private final String key; + + AttributeType(String key) { + this.key = key; + } + + @Override + public String toString() { + return key; + } +} diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/AudienceIdCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/AudienceIdCondition.java index e07757016..57e665ae7 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/AudienceIdCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/AudienceIdCondition.java @@ -1,6 +1,6 @@ /** * - * Copyright 2018-2021, Optimizely and contributors + * Copyright 2018-2022, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -71,7 +72,7 @@ public String getOperandOrId() { @Nullable @Override - public Boolean evaluate(ProjectConfig config, Map attributes) { + public Boolean evaluate(ProjectConfig config, Map attributes, List qualifiedSegments) { if (config != null) { audience = config.getAudienceIdMapping().get(audienceId); } @@ -80,7 +81,7 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { return null; } logger.debug("Starting to evaluate audience \"{}\" with conditions: {}.", audience.getId(), audience.getConditions()); - Boolean result = audience.getConditions().evaluate(config, attributes); + Boolean result = audience.getConditions().evaluate(config, attributes, qualifiedSegments); logger.debug("Audience \"{}\" evaluated to {}.", audience.getId(), result); return result; } diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java index 11b7165b9..d9e6d1cbf 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016-2018, Optimizely and contributors + * Copyright 2016-2018, 2022, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ import com.optimizely.ab.config.ProjectConfig; import javax.annotation.Nullable; +import java.util.List; import java.util.Map; /** @@ -27,7 +28,7 @@ public interface Condition { @Nullable - Boolean evaluate(ProjectConfig config, Map attributes); + Boolean evaluate(ProjectConfig config, Map attributes, List qualifiedSegments); String toJson(); diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/EmptyCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/EmptyCondition.java index 9bb355a13..3f4a70026 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/EmptyCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/EmptyCondition.java @@ -1,5 +1,5 @@ /** - * Copyright 2019, Optimizely Inc. and contributors + * Copyright 2019, 2022, Optimizely Inc. and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,12 +18,13 @@ import com.optimizely.ab.config.ProjectConfig; import javax.annotation.Nullable; +import java.util.List; import java.util.Map; public class EmptyCondition implements Condition { @Nullable @Override - public Boolean evaluate(ProjectConfig config, Map attributes) { + public Boolean evaluate(ProjectConfig config, Map attributes, List qualifiedSegments) { return true; } diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java index cabc07812..a4b5addf3 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016-2019, Optimizely and contributors + * Copyright 2016-2019, 2022, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import javax.annotation.concurrent.Immutable; import javax.annotation.Nonnull; +import java.util.List; import java.util.Map; import java.util.StringJoiner; @@ -43,9 +44,9 @@ public Condition getCondition() { } @Nullable - public Boolean evaluate(ProjectConfig config, Map attributes) { + public Boolean evaluate(ProjectConfig config, Map attributes, List qualifiedSegments) { - Boolean conditionEval = condition == null ? null : condition.evaluate(config, attributes); + Boolean conditionEval = condition == null ? null : condition.evaluate(config, attributes, qualifiedSegments); return (conditionEval == null ? null : !conditionEval); } diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/NullCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/NullCondition.java index 10633aed9..06d6481e3 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/NullCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/NullCondition.java @@ -1,5 +1,5 @@ /** - * Copyright 2019, Optimizely Inc. and contributors + * Copyright 2019, 2022, Optimizely Inc. and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,12 +18,13 @@ import com.optimizely.ab.config.ProjectConfig; import javax.annotation.Nullable; +import java.util.List; import java.util.Map; public class NullCondition implements Condition { @Nullable @Override - public Boolean evaluate(ProjectConfig config, Map attributes) { + public Boolean evaluate(ProjectConfig config, Map attributes, List qualifiedSegments) { return null; } diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java index 293687f66..ccd991fad 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016-2019, Optimizely and contributors + * Copyright 2016-2019, 2022, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,12 +47,12 @@ public List getConditions() { // false or false is false // null or null is null @Nullable - public Boolean evaluate(ProjectConfig config, Map attributes) { + public Boolean evaluate(ProjectConfig config, Map attributes, List qualifiedSegments) { if (conditions == null) return null; boolean foundNull = false; for (Condition condition : conditions) { - Boolean conditionEval = condition.evaluate(config, attributes); - if (conditionEval == null) { // true with falses and nulls is still true + Boolean conditionEval = condition.evaluate(config, attributes, qualifiedSegments); + if (conditionEval == null) { // true with false and nulls is still true foundNull = true; } else if (conditionEval) { return true; diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java index ed029f89c..527ecd5eb 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016-2020, Optimizely and contributors + * Copyright 2016-2020, 2022, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,8 +28,12 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import java.util.Collections; +import java.util.List; import java.util.Map; +import static com.optimizely.ab.config.audience.AttributeType.CUSTOM_ATTRIBUTE; +import static com.optimizely.ab.config.audience.AttributeType.THIRD_PARTY_DIMENSION; + /** * Represents a user attribute instance within an audience's conditions. */ @@ -71,21 +75,27 @@ public Object getValue() { } @Nullable - public Boolean evaluate(ProjectConfig config, Map attributes) { + public Boolean evaluate(ProjectConfig config, Map attributes, List qualifiedSegments) { if (attributes == null) { attributes = Collections.emptyMap(); } - // Valid for primitive types, but needs to change when a value is an object or an array - Object userAttributeValue = attributes.get(name); - if (!"custom_attribute".equals(type)) { + if (!isValidType(type)) { logger.warn("Audience condition \"{}\" uses an unknown condition type. You may need to upgrade to a newer release of the Optimizely SDK.", this); return null; // unknown type } + // Valid for primitive types, but needs to change when a value is an object or an array + Object userAttributeValue = attributes.get(name); + // check user attribute value is equal try { Match matcher = MatchRegistry.getMatch(match); - Boolean result = matcher.eval(value, userAttributeValue); + Boolean result; + if ("qualified".equals(match)) { + result = matcher.eval(value, qualifiedSegments); + } else { + result = matcher.eval(value, userAttributeValue); + } if (result == null) { throw new UnknownValueTypeException(); } @@ -118,6 +128,13 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { return null; } + private boolean isValidType(String type) { + if (type.equals(CUSTOM_ATTRIBUTE.toString()) || type.equals(THIRD_PARTY_DIMENSION.toString())) { + return true; + } + return false; + } + @Override public String getOperandOrId() { return null; diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/match/MatchRegistry.java b/core-api/src/main/java/com/optimizely/ab/config/audience/match/MatchRegistry.java index f78c35c8d..5f8525a9e 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/match/MatchRegistry.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/match/MatchRegistry.java @@ -1,6 +1,6 @@ /** * - * Copyright 2020-2021, Optimizely and contributors + * Copyright 2020-2022, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,6 +41,7 @@ public class MatchRegistry { public static final String SEMVER_LE = "semver_le"; public static final String SEMVER_LT = "semver_lt"; public static final String SUBSTRING = "substring"; + public static final String QUALIFIED = "qualified"; static { register(EXACT, new ExactMatch()); @@ -56,6 +57,7 @@ public class MatchRegistry { register(SEMVER_LE, new SemanticVersionLEMatch()); register(SEMVER_LT, new SemanticVersionLTMatch()); register(SUBSTRING, new SubstringMatch()); + register(QUALIFIED, new QualifiedMatch()); } // TODO rename Match to Matcher diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/match/QualifiedMatch.java b/core-api/src/main/java/com/optimizely/ab/config/audience/match/QualifiedMatch.java new file mode 100644 index 000000000..13b7ef62b --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/match/QualifiedMatch.java @@ -0,0 +1,35 @@ +/** + * + * Copyright 2022, Optimizely and contributors + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.optimizely.ab.config.audience.match; + +import javax.annotation.Nullable; +import java.util.List; + +public class QualifiedMatch implements Match { + @Nullable + public Boolean eval(Object conditionValue, Object attributeValue) throws UnexpectedValueTypeException { + if (!(conditionValue instanceof String)) { + throw new UnexpectedValueTypeException(); + } + + if (!(attributeValue instanceof List)) { + return null; + } + + return ((List)attributeValue).contains(conditionValue.toString()); + } +} diff --git a/core-api/src/main/java/com/optimizely/ab/internal/ExperimentUtils.java b/core-api/src/main/java/com/optimizely/ab/internal/ExperimentUtils.java index c1494bbda..2bc7179f9 100644 --- a/core-api/src/main/java/com/optimizely/ab/internal/ExperimentUtils.java +++ b/core-api/src/main/java/com/optimizely/ab/internal/ExperimentUtils.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017-2021, Optimizely and contributors + * Copyright 2017-2022, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,9 +28,7 @@ import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.*; public final class ExperimentUtils { @@ -48,7 +46,6 @@ private ExperimentUtils() { public static boolean isExperimentActive(@Nonnull Experiment experiment) { return experiment.isActive(); } - /** * Determines whether a user satisfies audience conditions for the experiment. * @@ -65,14 +62,34 @@ public static DecisionResponse doesUserMeetAudienceConditions(@Nonnull @Nonnull Map attributes, @Nonnull String loggingEntityType, @Nonnull String loggingKey) { + return doesUserMeetAudienceConditions(projectConfig, experiment, attributes, loggingEntityType, loggingKey, Collections.synchronizedList(new ArrayList<>())); + } + + /** + * Determines whether a user satisfies audience conditions for the experiment. + * + * @param projectConfig the current projectConfig + * @param experiment the experiment we are evaluating audiences for + * @param attributes the attributes of the user + * @param loggingEntityType It can be either experiment or rule. + * @param loggingKey In case of loggingEntityType is experiment it will be experiment key or else it will be rule number. + * @return whether the user meets the criteria for the experiment + */ + @Nonnull + public static DecisionResponse doesUserMeetAudienceConditions(@Nonnull ProjectConfig projectConfig, + @Nonnull Experiment experiment, + @Nonnull Map attributes, + @Nonnull String loggingEntityType, + @Nonnull String loggingKey, + @Nonnull List qualifiedSegments) { DecisionReasons reasons = DefaultDecisionReasons.newInstance(); DecisionResponse decisionResponse; if (experiment.getAudienceConditions() != null) { logger.debug("Evaluating audiences for {} \"{}\": {}.", loggingEntityType, loggingKey, experiment.getAudienceConditions()); - decisionResponse = evaluateAudienceConditions(projectConfig, experiment, attributes, loggingEntityType, loggingKey); + decisionResponse = evaluateAudienceConditions(projectConfig, experiment, attributes, loggingEntityType, loggingKey, qualifiedSegments); } else { - decisionResponse = evaluateAudience(projectConfig, experiment, attributes, loggingEntityType, loggingKey); + decisionResponse = evaluateAudience(projectConfig, experiment, attributes, loggingEntityType, loggingKey, qualifiedSegments); } Boolean resolveReturn = decisionResponse.getResult(); @@ -88,7 +105,8 @@ public static DecisionResponse evaluateAudience(@Nonnull ProjectConfig @Nonnull Experiment experiment, @Nonnull Map attributes, @Nonnull String loggingEntityType, - @Nonnull String loggingKey) { + @Nonnull String loggingKey, + @Nonnull List qualifiedSegments) { DecisionReasons reasons = DefaultDecisionReasons.newInstance(); List experimentAudienceIds = experiment.getAudienceIds(); @@ -108,7 +126,7 @@ public static DecisionResponse evaluateAudience(@Nonnull ProjectConfig logger.debug("Evaluating audiences for {} \"{}\": {}.", loggingEntityType, loggingKey, conditions); - Boolean result = implicitOr.evaluate(projectConfig, attributes); + Boolean result = implicitOr.evaluate(projectConfig, attributes, qualifiedSegments); String message = reasons.addInfo("Audiences for %s \"%s\" collectively evaluated to %s.", loggingEntityType, loggingKey, result); logger.info(message); @@ -120,7 +138,8 @@ public static DecisionResponse evaluateAudienceConditions(@Nonnull Proj @Nonnull Experiment experiment, @Nonnull Map attributes, @Nonnull String loggingEntityType, - @Nonnull String loggingKey) { + @Nonnull String loggingKey, + @Nonnull List qualifiedSegments) { DecisionReasons reasons = DefaultDecisionReasons.newInstance(); Condition conditions = experiment.getAudienceConditions(); @@ -128,7 +147,7 @@ public static DecisionResponse evaluateAudienceConditions(@Nonnull Proj Boolean result = null; try { - result = conditions.evaluate(projectConfig, attributes); + result = conditions.evaluate(projectConfig, attributes, qualifiedSegments); String message = reasons.addInfo("Audiences for %s \"%s\" collectively evaluated to %s.", loggingEntityType, loggingKey, result); logger.info(message); } catch (Exception e) { diff --git a/core-api/src/test/java/com/optimizely/ab/config/DatafileProjectConfigTestUtils.java b/core-api/src/test/java/com/optimizely/ab/config/DatafileProjectConfigTestUtils.java index b96815a39..39470dc41 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/DatafileProjectConfigTestUtils.java +++ b/core-api/src/test/java/com/optimizely/ab/config/DatafileProjectConfigTestUtils.java @@ -24,6 +24,7 @@ import com.optimizely.ab.config.audience.NotCondition; import com.optimizely.ab.config.audience.OrCondition; import com.optimizely.ab.config.audience.UserAttribute; +import org.junit.Assert; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; @@ -491,7 +492,7 @@ private static void verifyExperiments(List actual, List assertThat(actualExperiment.getGroupId(), is(expectedExperiment.getGroupId())); assertThat(actualExperiment.getStatus(), is(expectedExperiment.getStatus())); assertThat(actualExperiment.getAudienceIds(), is(expectedExperiment.getAudienceIds())); - assertThat(actualExperiment.getAudienceConditions(), is(expectedExperiment.getAudienceConditions())); + Assert.assertEquals(actualExperiment.getAudienceConditions(), expectedExperiment.getAudienceConditions()); assertThat(actualExperiment.getUserIdToVariationKeyMap(), is(expectedExperiment.getUserIdToVariationKeyMap())); diff --git a/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java b/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java index 07a0c1ad1..3cc94ef91 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java @@ -70,7 +70,7 @@ public void emptyConditionTest() throws Exception { EmptyCondition emptyCondition = new EmptyCondition(); assertEquals(null, emptyCondition.toJson()); assertEquals(null, emptyCondition.getOperandOrId()); - assertEquals(true, emptyCondition.evaluate(null, null)); + assertEquals(true, emptyCondition.evaluate(null, null, null)); } /** @@ -129,7 +129,7 @@ public void userAttributeEvaluateTrue() throws Exception { assertNull(testInstance.getMatch()); assertEquals(testInstance.getName(), "browser_type"); assertEquals(testInstance.getType(), "custom_attribute"); - assertTrue(testInstance.evaluate(null, testUserAttributes)); + assertTrue(testInstance.evaluate(null, testUserAttributes, null)); } /** @@ -138,7 +138,7 @@ public void userAttributeEvaluateTrue() throws Exception { @Test public void userAttributeEvaluateFalse() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "custom_attribute", null, "firefox"); - assertFalse(testInstance.evaluate(null, testUserAttributes)); + assertFalse(testInstance.evaluate(null, testUserAttributes, null)); } /** @@ -147,7 +147,7 @@ public void userAttributeEvaluateFalse() throws Exception { @Test public void userAttributeUnknownAttribute() throws Exception { UserAttribute testInstance = new UserAttribute("unknown_dim", "custom_attribute", null, "unknown"); - assertFalse(testInstance.evaluate(null, testUserAttributes)); + assertFalse(testInstance.evaluate(null, testUserAttributes, null)); } /** @@ -156,7 +156,7 @@ public void userAttributeUnknownAttribute() throws Exception { @Test public void invalidMatchCondition() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "unknown_dimension", null, "chrome"); - assertNull(testInstance.evaluate(null, testUserAttributes)); + assertNull(testInstance.evaluate(null, testUserAttributes, null)); } /** @@ -165,7 +165,7 @@ public void invalidMatchCondition() throws Exception { @Test public void invalidMatch() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "custom_attribute", "blah", "chrome"); - assertNull(testInstance.evaluate(null, testUserAttributes)); + assertNull(testInstance.evaluate(null, testUserAttributes, null)); logbackVerifier.expectMessage(Level.WARN, "Audience condition \"{name='browser_type', type='custom_attribute', match='blah', value='chrome'}\" uses an unknown match type. You may need to upgrade to a newer release of the Optimizely SDK"); } @@ -176,7 +176,7 @@ public void invalidMatch() throws Exception { @Test public void unexpectedAttributeType() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "custom_attribute", "gt", 20); - assertNull(testInstance.evaluate(null, testUserAttributes)); + assertNull(testInstance.evaluate(null, testUserAttributes, null)); logbackVerifier.expectMessage(Level.WARN, "Audience condition \"{name='browser_type', type='custom_attribute', match='gt', value=20}\" evaluated to UNKNOWN because a value of type \"java.lang.String\" was passed for user attribute \"browser_type\""); } @@ -187,7 +187,7 @@ public void unexpectedAttributeType() throws Exception { @Test public void unexpectedAttributeTypeNull() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "custom_attribute", "gt", 20); - assertNull(testInstance.evaluate(null, Collections.singletonMap("browser_type", null))); + assertNull(testInstance.evaluate(null, Collections.singletonMap("browser_type", null), null)); logbackVerifier.expectMessage(Level.DEBUG, "Audience condition \"{name='browser_type', type='custom_attribute', match='gt', value=20}\" evaluated to UNKNOWN because a null value was passed for user attribute \"browser_type\""); } @@ -215,7 +215,7 @@ public void missingAttribute_returnsNullAndLogDebugMessage() throws Exception { for (Map.Entry entry : items.entrySet()) { for (Object value : entry.getValue()) { UserAttribute testInstance = new UserAttribute("n", "custom_attribute", entry.getKey(), value); - assertNull(testInstance.evaluate(null, Collections.EMPTY_MAP)); + assertNull(testInstance.evaluate(null, Collections.EMPTY_MAP, null)); String valueStr = (value instanceof String) ? ("'" + value + "'") : value.toString(); logbackVerifier.expectMessage(Level.DEBUG, "Audience condition \"{name='n', type='custom_attribute', match='" + entry.getKey() + "', value=" + valueStr + "}\" evaluated to UNKNOWN because no value was passed for user attribute \"n\""); @@ -229,7 +229,7 @@ public void missingAttribute_returnsNullAndLogDebugMessage() throws Exception { @Test public void nullAttribute() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "custom_attribute", "gt", 20); - assertNull(testInstance.evaluate(null, null)); + assertNull(testInstance.evaluate(null, null, null)); logbackVerifier.expectMessage(Level.DEBUG, "Audience condition \"{name='browser_type', type='custom_attribute', match='gt', value=20}\" evaluated to UNKNOWN because no value was passed for user attribute \"browser_type\""); } @@ -240,7 +240,7 @@ public void nullAttribute() throws Exception { @Test public void unknownConditionType() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "blah", "exists", "firefox"); - assertNull(testInstance.evaluate(null, testUserAttributes)); + assertNull(testInstance.evaluate(null, testUserAttributes, null)); logbackVerifier.expectMessage(Level.WARN, "Audience condition \"{name='browser_type', type='blah', match='exists', value='firefox'}\" uses an unknown condition type. You may need to upgrade to a newer release of the Optimizely SDK."); } @@ -254,9 +254,9 @@ public void existsMatchConditionEmptyStringEvaluatesTrue() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "custom_attribute", "exists", "firefox"); Map attributes = new HashMap<>(); attributes.put("browser_type", ""); - assertTrue(testInstance.evaluate(null, attributes)); + assertTrue(testInstance.evaluate(null, attributes, null)); attributes.put("browser_type", null); - assertFalse(testInstance.evaluate(null, attributes)); + assertFalse(testInstance.evaluate(null, attributes, null)); } /** @@ -266,16 +266,16 @@ public void existsMatchConditionEmptyStringEvaluatesTrue() throws Exception { @Test public void existsMatchConditionEvaluatesTrue() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "custom_attribute", "exists", "firefox"); - assertTrue(testInstance.evaluate(null, testUserAttributes)); + assertTrue(testInstance.evaluate(null, testUserAttributes, null)); UserAttribute testInstanceBoolean = new UserAttribute("is_firefox", "custom_attribute", "exists", false); UserAttribute testInstanceInteger = new UserAttribute("num_size", "custom_attribute", "exists", 5); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "exists", 4.55); UserAttribute testInstanceObject = new UserAttribute("meta_data", "custom_attribute", "exists", testUserAttributes); - assertTrue(testInstanceBoolean.evaluate(null, testTypedUserAttributes)); - assertTrue(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertTrue(testInstanceDouble.evaluate(null, testTypedUserAttributes)); - assertTrue(testInstanceObject.evaluate(null, testTypedUserAttributes)); + assertTrue(testInstanceBoolean.evaluate(null, testTypedUserAttributes, null)); + assertTrue(testInstanceInteger.evaluate(null, testTypedUserAttributes, null)); + assertTrue(testInstanceDouble.evaluate(null, testTypedUserAttributes, null)); + assertTrue(testInstanceObject.evaluate(null, testTypedUserAttributes, null)); } /** @@ -286,8 +286,8 @@ public void existsMatchConditionEvaluatesTrue() throws Exception { public void existsMatchConditionEvaluatesFalse() throws Exception { UserAttribute testInstance = new UserAttribute("bad_var", "custom_attribute", "exists", "chrome"); UserAttribute testInstanceNull = new UserAttribute("null_val", "custom_attribute", "exists", "chrome"); - assertFalse(testInstance.evaluate(null, testTypedUserAttributes)); - assertFalse(testInstanceNull.evaluate(null, testTypedUserAttributes)); + assertFalse(testInstance.evaluate(null, testTypedUserAttributes, null)); + assertFalse(testInstanceNull.evaluate(null, testTypedUserAttributes, null)); } /** @@ -302,11 +302,11 @@ public void exactMatchConditionEvaluatesTrue() { UserAttribute testInstanceFloat = new UserAttribute("num_size", "custom_attribute", "exact", (float) 3); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "exact", 3.55); - assertTrue(testInstanceString.evaluate(null, testUserAttributes)); - assertTrue(testInstanceBoolean.evaluate(null, testTypedUserAttributes)); - assertTrue(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertTrue(testInstanceFloat.evaluate(null, Collections.singletonMap("num_size", (float) 3))); - assertTrue(testInstanceDouble.evaluate(null, testTypedUserAttributes)); + assertTrue(testInstanceString.evaluate(null, testUserAttributes, null)); + assertTrue(testInstanceBoolean.evaluate(null, testTypedUserAttributes, null)); + assertTrue(testInstanceInteger.evaluate(null, testTypedUserAttributes, null)); + assertTrue(testInstanceFloat.evaluate(null, Collections.singletonMap("num_size", (float) 3), null)); + assertTrue(testInstanceDouble.evaluate(null, testTypedUserAttributes, null)); } /** @@ -339,22 +339,22 @@ public void exactMatchConditionEvaluatesNullWithInvalidUserAttr() { assertNull(testInstanceInteger.evaluate( null, - Collections.singletonMap("num_size", bigInteger))); + Collections.singletonMap("num_size", bigInteger), null)); assertNull(testInstanceFloat.evaluate( null, - Collections.singletonMap("num_size", invalidFloatValue))); + Collections.singletonMap("num_size", invalidFloatValue), null)); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble))); + Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble), null)); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble))); + Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble), null)); assertNull(testInstanceDouble.evaluate( null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", infiniteNANDouble)))); + Collections.singletonMap("num_counts", infiniteNANDouble)), null)); assertNull(testInstanceDouble.evaluate( null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", largeDouble)))); + Collections.singletonMap("num_counts", largeDouble)), null)); } /** @@ -372,10 +372,10 @@ public void invalidExactMatchConditionEvaluatesNull() { UserAttribute testInstanceNegativeInfiniteDouble = new UserAttribute("num_counts", "custom_attribute", "exact", infiniteNegativeInfiniteDouble); UserAttribute testInstanceNANDouble = new UserAttribute("num_counts", "custom_attribute", "exact", infiniteNANDouble); - assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertNull(testInstancePositiveInfinite.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNANDouble.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstancePositiveInfinite.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceNANDouble.evaluate(null, testTypedUserAttributes, null)); } /** @@ -389,10 +389,10 @@ public void exactMatchConditionEvaluatesFalse() { UserAttribute testInstanceInteger = new UserAttribute("num_size", "custom_attribute", "exact", 5); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "exact", 5.55); - assertFalse(testInstanceString.evaluate(null, testUserAttributes)); - assertFalse(testInstanceBoolean.evaluate(null, testTypedUserAttributes)); - assertFalse(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertFalse(testInstanceDouble.evaluate(null, testTypedUserAttributes)); + assertFalse(testInstanceString.evaluate(null, testUserAttributes, null)); + assertFalse(testInstanceBoolean.evaluate(null, testTypedUserAttributes, null)); + assertFalse(testInstanceInteger.evaluate(null, testTypedUserAttributes, null)); + assertFalse(testInstanceDouble.evaluate(null, testTypedUserAttributes, null)); } /** @@ -408,15 +408,15 @@ public void exactMatchConditionEvaluatesNull() { UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "exact", "3.55"); UserAttribute testInstanceNull = new UserAttribute("null_val", "custom_attribute", "exact", "null_val"); - assertNull(testInstanceObject.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceString.evaluate(null, testUserAttributes)); - assertNull(testInstanceBoolean.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceDouble.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNull.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceObject.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceString.evaluate(null, testUserAttributes, null)); + assertNull(testInstanceBoolean.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceDouble.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceNull.evaluate(null, testTypedUserAttributes, null)); Map attr = new HashMap<>(); attr.put("browser_type", "true"); - assertNull(testInstanceString.evaluate(null, attr)); + assertNull(testInstanceString.evaluate(null, attr, null)); } /** @@ -430,13 +430,13 @@ public void gtMatchConditionEvaluatesTrue() { UserAttribute testInstanceFloat = new UserAttribute("num_size", "custom_attribute", "gt", (float) 2); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "gt", 2.55); - assertTrue(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertTrue(testInstanceFloat.evaluate(null, Collections.singletonMap("num_size", (float) 3))); - assertTrue(testInstanceDouble.evaluate(null, testTypedUserAttributes)); + assertTrue(testInstanceInteger.evaluate(null, testTypedUserAttributes, null)); + assertTrue(testInstanceFloat.evaluate(null, Collections.singletonMap("num_size", (float) 3), null)); + assertTrue(testInstanceDouble.evaluate(null, testTypedUserAttributes, null)); Map badAttributes = new HashMap<>(); badAttributes.put("num_size", "bobs burgers"); - assertNull(testInstanceInteger.evaluate(null, badAttributes)); + assertNull(testInstanceInteger.evaluate(null, badAttributes, null)); } /** @@ -470,22 +470,22 @@ public void gtMatchConditionEvaluatesNullWithInvalidUserAttr() { assertNull(testInstanceInteger.evaluate( null, - Collections.singletonMap("num_size", bigInteger))); + Collections.singletonMap("num_size", bigInteger), null)); assertNull(testInstanceFloat.evaluate( null, - Collections.singletonMap("num_size", invalidFloatValue))); + Collections.singletonMap("num_size", invalidFloatValue), null)); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble))); + Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble), null)); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble))); + Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble), null)); assertNull(testInstanceDouble.evaluate( null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", infiniteNANDouble)))); + Collections.singletonMap("num_counts", infiniteNANDouble)), null)); assertNull(testInstanceDouble.evaluate( null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", largeDouble)))); + Collections.singletonMap("num_counts", largeDouble)), null)); } /** @@ -503,10 +503,10 @@ public void gtMatchConditionEvaluatesNullWithInvalidAttr() { UserAttribute testInstanceNegativeInfiniteDouble = new UserAttribute("num_counts", "custom_attribute", "gt", infiniteNegativeInfiniteDouble); UserAttribute testInstanceNANDouble = new UserAttribute("num_counts", "custom_attribute", "gt", infiniteNANDouble); - assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertNull(testInstancePositiveInfinite.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNANDouble.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstancePositiveInfinite.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceNANDouble.evaluate(null, testTypedUserAttributes, null)); } /** @@ -519,8 +519,8 @@ public void gtMatchConditionEvaluatesFalse() { UserAttribute testInstanceInteger = new UserAttribute("num_size", "custom_attribute", "gt", 5); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "gt", 5.55); - assertFalse(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertFalse(testInstanceDouble.evaluate(null, testTypedUserAttributes)); + assertFalse(testInstanceInteger.evaluate(null, testTypedUserAttributes, null)); + assertFalse(testInstanceDouble.evaluate(null, testTypedUserAttributes, null)); } /** @@ -534,10 +534,10 @@ public void gtMatchConditionEvaluatesNull() { UserAttribute testInstanceObject = new UserAttribute("meta_data", "custom_attribute", "gt", 3.5); UserAttribute testInstanceNull = new UserAttribute("null_val", "custom_attribute", "gt", 3.5); - assertNull(testInstanceString.evaluate(null, testUserAttributes)); - assertNull(testInstanceBoolean.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceObject.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNull.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceString.evaluate(null, testUserAttributes, null)); + assertNull(testInstanceBoolean.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceObject.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceNull.evaluate(null, testTypedUserAttributes, null)); } @@ -552,13 +552,13 @@ public void geMatchConditionEvaluatesTrue() { UserAttribute testInstanceFloat = new UserAttribute("num_size", "custom_attribute", "ge", (float) 2); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "ge", 2.55); - assertTrue(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertTrue(testInstanceFloat.evaluate(null, Collections.singletonMap("num_size", (float) 2))); - assertTrue(testInstanceDouble.evaluate(null, testTypedUserAttributes)); + assertTrue(testInstanceInteger.evaluate(null, testTypedUserAttributes, null)); + assertTrue(testInstanceFloat.evaluate(null, Collections.singletonMap("num_size", (float) 2), null)); + assertTrue(testInstanceDouble.evaluate(null, testTypedUserAttributes, null)); Map badAttributes = new HashMap<>(); badAttributes.put("num_size", "bobs burgers"); - assertNull(testInstanceInteger.evaluate(null, badAttributes)); + assertNull(testInstanceInteger.evaluate(null, badAttributes, null)); } /** @@ -592,22 +592,22 @@ public void geMatchConditionEvaluatesNullWithInvalidUserAttr() { assertNull(testInstanceInteger.evaluate( null, - Collections.singletonMap("num_size", bigInteger))); + Collections.singletonMap("num_size", bigInteger), null)); assertNull(testInstanceFloat.evaluate( null, - Collections.singletonMap("num_size", invalidFloatValue))); + Collections.singletonMap("num_size", invalidFloatValue), null)); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble))); + Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble), null)); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble))); + Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble), null)); assertNull(testInstanceDouble.evaluate( null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", infiniteNANDouble)))); + Collections.singletonMap("num_counts", infiniteNANDouble)), null)); assertNull(testInstanceDouble.evaluate( null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", largeDouble)))); + Collections.singletonMap("num_counts", largeDouble)), null)); } /** @@ -625,10 +625,10 @@ public void geMatchConditionEvaluatesNullWithInvalidAttr() { UserAttribute testInstanceNegativeInfiniteDouble = new UserAttribute("num_counts", "custom_attribute", "ge", infiniteNegativeInfiniteDouble); UserAttribute testInstanceNANDouble = new UserAttribute("num_counts", "custom_attribute", "ge", infiniteNANDouble); - assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertNull(testInstancePositiveInfinite.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNANDouble.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstancePositiveInfinite.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceNANDouble.evaluate(null, testTypedUserAttributes, null)); } /** @@ -641,8 +641,8 @@ public void geMatchConditionEvaluatesFalse() { UserAttribute testInstanceInteger = new UserAttribute("num_size", "custom_attribute", "ge", 5); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "ge", 5.55); - assertFalse(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertFalse(testInstanceDouble.evaluate(null, testTypedUserAttributes)); + assertFalse(testInstanceInteger.evaluate(null, testTypedUserAttributes, null)); + assertFalse(testInstanceDouble.evaluate(null, testTypedUserAttributes, null)); } /** @@ -656,10 +656,10 @@ public void geMatchConditionEvaluatesNull() { UserAttribute testInstanceObject = new UserAttribute("meta_data", "custom_attribute", "ge", 3.5); UserAttribute testInstanceNull = new UserAttribute("null_val", "custom_attribute", "ge", 3.5); - assertNull(testInstanceString.evaluate(null, testUserAttributes)); - assertNull(testInstanceBoolean.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceObject.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNull.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceString.evaluate(null, testUserAttributes, null)); + assertNull(testInstanceBoolean.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceObject.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceNull.evaluate(null, testTypedUserAttributes, null)); } @@ -673,8 +673,8 @@ public void ltMatchConditionEvaluatesTrue() { UserAttribute testInstanceInteger = new UserAttribute("num_size", "custom_attribute", "lt", 5); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "lt", 5.55); - assertTrue(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertTrue(testInstanceDouble.evaluate(null, testTypedUserAttributes)); + assertTrue(testInstanceInteger.evaluate(null, testTypedUserAttributes, null)); + assertTrue(testInstanceDouble.evaluate(null, testTypedUserAttributes, null)); } /** @@ -687,8 +687,8 @@ public void ltMatchConditionEvaluatesFalse() { UserAttribute testInstanceInteger = new UserAttribute("num_size", "custom_attribute", "lt", 2); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "lt", 2.55); - assertFalse(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertFalse(testInstanceDouble.evaluate(null, testTypedUserAttributes)); + assertFalse(testInstanceInteger.evaluate(null, testTypedUserAttributes, null)); + assertFalse(testInstanceDouble.evaluate(null, testTypedUserAttributes, null)); } /** @@ -702,10 +702,10 @@ public void ltMatchConditionEvaluatesNull() { UserAttribute testInstanceObject = new UserAttribute("meta_data", "custom_attribute", "lt", 3.5); UserAttribute testInstanceNull = new UserAttribute("null_val", "custom_attribute", "lt", 3.5); - assertNull(testInstanceString.evaluate(null, testUserAttributes)); - assertNull(testInstanceBoolean.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceObject.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNull.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceString.evaluate(null, testUserAttributes, null)); + assertNull(testInstanceBoolean.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceObject.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceNull.evaluate(null, testTypedUserAttributes, null)); } /** @@ -739,22 +739,22 @@ public void ltMatchConditionEvaluatesNullWithInvalidUserAttr() { assertNull(testInstanceInteger.evaluate( null, - Collections.singletonMap("num_size", bigInteger))); + Collections.singletonMap("num_size", bigInteger), null)); assertNull(testInstanceFloat.evaluate( null, - Collections.singletonMap("num_size", invalidFloatValue))); + Collections.singletonMap("num_size", invalidFloatValue), null)); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble))); + Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble), null)); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble))); + Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble), null)); assertNull(testInstanceDouble.evaluate( null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", infiniteNANDouble)))); + Collections.singletonMap("num_counts", infiniteNANDouble)), null)); assertNull(testInstanceDouble.evaluate( null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", largeDouble)))); + Collections.singletonMap("num_counts", largeDouble)), null)); } /** @@ -772,10 +772,10 @@ public void ltMatchConditionEvaluatesNullWithInvalidAttributes() { UserAttribute testInstanceNegativeInfiniteDouble = new UserAttribute("num_counts", "custom_attribute", "lt", infiniteNegativeInfiniteDouble); UserAttribute testInstanceNANDouble = new UserAttribute("num_counts", "custom_attribute", "lt", infiniteNANDouble); - assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertNull(testInstancePositiveInfinite.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNANDouble.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstancePositiveInfinite.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceNANDouble.evaluate(null, testTypedUserAttributes, null)); } @@ -789,8 +789,8 @@ public void leMatchConditionEvaluatesTrue() { UserAttribute testInstanceInteger = new UserAttribute("num_size", "custom_attribute", "le", 5); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "le", 5.55); - assertTrue(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertTrue(testInstanceDouble.evaluate(null, Collections.singletonMap("num_counts", 5.55))); + assertTrue(testInstanceInteger.evaluate(null, testTypedUserAttributes, null)); + assertTrue(testInstanceDouble.evaluate(null, Collections.singletonMap("num_counts", 5.55), null)); } /** @@ -803,8 +803,8 @@ public void leMatchConditionEvaluatesFalse() { UserAttribute testInstanceInteger = new UserAttribute("num_size", "custom_attribute", "le", 2); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "le", 2.55); - assertFalse(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertFalse(testInstanceDouble.evaluate(null, testTypedUserAttributes)); + assertFalse(testInstanceInteger.evaluate(null, testTypedUserAttributes, null)); + assertFalse(testInstanceDouble.evaluate(null, testTypedUserAttributes, null)); } /** @@ -818,10 +818,10 @@ public void leMatchConditionEvaluatesNull() { UserAttribute testInstanceObject = new UserAttribute("meta_data", "custom_attribute", "le", 3.5); UserAttribute testInstanceNull = new UserAttribute("null_val", "custom_attribute", "le", 3.5); - assertNull(testInstanceString.evaluate(null, testUserAttributes)); - assertNull(testInstanceBoolean.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceObject.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNull.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceString.evaluate(null, testUserAttributes, null)); + assertNull(testInstanceBoolean.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceObject.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceNull.evaluate(null, testTypedUserAttributes, null)); } /** @@ -855,22 +855,22 @@ public void leMatchConditionEvaluatesNullWithInvalidUserAttr() { assertNull(testInstanceInteger.evaluate( null, - Collections.singletonMap("num_size", bigInteger))); + Collections.singletonMap("num_size", bigInteger), null)); assertNull(testInstanceFloat.evaluate( null, - Collections.singletonMap("num_size", invalidFloatValue))); + Collections.singletonMap("num_size", invalidFloatValue), null)); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble))); + Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble), null)); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble))); + Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble), null)); assertNull(testInstanceDouble.evaluate( null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", infiniteNANDouble)))); + Collections.singletonMap("num_counts", infiniteNANDouble)), null)); assertNull(testInstanceDouble.evaluate( null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", largeDouble)))); + Collections.singletonMap("num_counts", largeDouble)), null)); } /** @@ -888,10 +888,10 @@ public void leMatchConditionEvaluatesNullWithInvalidAttributes() { UserAttribute testInstanceNegativeInfiniteDouble = new UserAttribute("num_counts", "custom_attribute", "le", infiniteNegativeInfiniteDouble); UserAttribute testInstanceNANDouble = new UserAttribute("num_counts", "custom_attribute", "le", infiniteNANDouble); - assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertNull(testInstancePositiveInfinite.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNANDouble.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstancePositiveInfinite.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceNANDouble.evaluate(null, testTypedUserAttributes, null)); } /** @@ -901,7 +901,7 @@ public void leMatchConditionEvaluatesNullWithInvalidAttributes() { @Test public void substringMatchConditionEvaluatesTrue() { UserAttribute testInstanceString = new UserAttribute("browser_type", "custom_attribute", "substring", "chrome"); - assertTrue(testInstanceString.evaluate(null, testUserAttributes)); + assertTrue(testInstanceString.evaluate(null, testUserAttributes, null)); } /** @@ -911,7 +911,7 @@ public void substringMatchConditionEvaluatesTrue() { @Test public void substringMatchConditionPartialMatchEvaluatesTrue() { UserAttribute testInstanceString = new UserAttribute("browser_type", "custom_attribute", "substring", "chro"); - assertTrue(testInstanceString.evaluate(null, testUserAttributes)); + assertTrue(testInstanceString.evaluate(null, testUserAttributes, null)); } /** @@ -921,7 +921,7 @@ public void substringMatchConditionPartialMatchEvaluatesTrue() { @Test public void substringMatchConditionEvaluatesFalse() { UserAttribute testInstanceString = new UserAttribute("browser_type", "custom_attribute", "substring", "chr0me"); - assertFalse(testInstanceString.evaluate(null, testUserAttributes)); + assertFalse(testInstanceString.evaluate(null, testUserAttributes, null)); } /** @@ -936,11 +936,11 @@ public void substringMatchConditionEvaluatesNull() { UserAttribute testInstanceObject = new UserAttribute("meta_data", "custom_attribute", "substring", "chrome1"); UserAttribute testInstanceNull = new UserAttribute("null_val", "custom_attribute", "substring", "chrome1"); - assertNull(testInstanceBoolean.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceDouble.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceObject.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNull.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceBoolean.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceDouble.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceObject.evaluate(null, testTypedUserAttributes, null)); + assertNull(testInstanceNull.evaluate(null, testTypedUserAttributes, null)); } //======== Semantic version evaluation tests ========// @@ -951,7 +951,7 @@ public void testSemanticVersionEqualsMatchInvalidInput() { Map testAttributes = new HashMap(); testAttributes.put("version", 2.0); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "2.0.0"); - assertNull(testInstanceString.evaluate(null, testAttributes)); + assertNull(testInstanceString.evaluate(null, testAttributes, null)); } @Test @@ -959,7 +959,7 @@ public void semanticVersionInvalidMajorShouldBeNumberOnly() { Map testAttributes = new HashMap(); testAttributes.put("version", "a.1.2"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "2.0.0"); - assertNull(testInstanceString.evaluate(null, testAttributes)); + assertNull(testInstanceString.evaluate(null, testAttributes, null)); } @Test @@ -967,7 +967,7 @@ public void semanticVersionInvalidMinorShouldBeNumberOnly() { Map testAttributes = new HashMap(); testAttributes.put("version", "1.b.2"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "2.0.0"); - assertNull(testInstanceString.evaluate(null, testAttributes)); + assertNull(testInstanceString.evaluate(null, testAttributes, null)); } @Test @@ -975,7 +975,7 @@ public void semanticVersionInvalidPatchShouldBeNumberOnly() { Map testAttributes = new HashMap(); testAttributes.put("version", "1.2.c"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "2.0.0"); - assertNull(testInstanceString.evaluate(null, testAttributes)); + assertNull(testInstanceString.evaluate(null, testAttributes, null)); } // Test SemanticVersionEqualsMatch returns null if given invalid UserCondition Variable type @@ -984,7 +984,7 @@ public void testSemanticVersionEqualsMatchInvalidUserConditionVariable() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.0"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", 2.0); - assertNull(testInstanceString.evaluate(null, testAttributes)); + assertNull(testInstanceString.evaluate(null, testAttributes, null)); } // Test SemanticVersionGTMatch returns null if given invalid value type @@ -993,7 +993,7 @@ public void testSemanticVersionGTMatchInvalidInput() { Map testAttributes = new HashMap(); testAttributes.put("version", false); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "2.0.0"); - assertNull(testInstanceString.evaluate(null, testAttributes)); + assertNull(testInstanceString.evaluate(null, testAttributes, null)); } // Test SemanticVersionGEMatch returns null if given invalid value type @@ -1002,7 +1002,7 @@ public void testSemanticVersionGEMatchInvalidInput() { Map testAttributes = new HashMap(); testAttributes.put("version", 2); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_ge", "2.0.0"); - assertNull(testInstanceString.evaluate(null, testAttributes)); + assertNull(testInstanceString.evaluate(null, testAttributes, null)); } // Test SemanticVersionLTMatch returns null if given invalid value type @@ -1011,7 +1011,7 @@ public void testSemanticVersionLTMatchInvalidInput() { Map testAttributes = new HashMap(); testAttributes.put("version", 2); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_lt", "2.0.0"); - assertNull(testInstanceString.evaluate(null, testAttributes)); + assertNull(testInstanceString.evaluate(null, testAttributes, null)); } // Test SemanticVersionLEMatch returns null if given invalid value type @@ -1020,7 +1020,7 @@ public void testSemanticVersionLEMatchInvalidInput() { Map testAttributes = new HashMap(); testAttributes.put("version", 2); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_le", "2.0.0"); - assertNull(testInstanceString.evaluate(null, testAttributes)); + assertNull(testInstanceString.evaluate(null, testAttributes, null)); } // Test if not same when targetVersion is only major.minor.patch and version is major.minor @@ -1029,7 +1029,7 @@ public void testIsSemanticNotSameConditionValueMajorMinorPatch() { Map testAttributes = new HashMap(); testAttributes.put("version", "1.2"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "1.2.0"); - assertFalse(testInstanceString.evaluate(null, testAttributes)); + assertFalse(testInstanceString.evaluate(null, testAttributes, null)); } // Test if same when target is only major but user condition checks only major.minor,patch @@ -1038,7 +1038,7 @@ public void testIsSemanticSameSingleDigit() { Map testAttributes = new HashMap(); testAttributes.put("version", "3.0.0"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "3"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, testAttributes, null)); } // Test if greater when User value patch is greater even when its beta @@ -1047,7 +1047,7 @@ public void testIsSemanticGreaterWhenUserConditionComparesMajorMinorAndPatchVers Map testAttributes = new HashMap(); testAttributes.put("version", "3.1.1-beta"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "3.1.0"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, testAttributes, null)); } // Test if greater when preRelease is greater alphabetically @@ -1056,7 +1056,7 @@ public void testIsSemanticGreaterWhenMajorMinorPatchReleaseVersionCharacter() { Map testAttributes = new HashMap(); testAttributes.put("version", "3.1.1-beta.y.1+1.1"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "3.1.1-beta.x.1+1.1"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, testAttributes, null)); } // Test if greater when preRelease version number is greater @@ -1065,7 +1065,7 @@ public void testIsSemanticGreaterWhenMajorMinorPatchPreReleaseVersionNum() { Map testAttributes = new HashMap(); testAttributes.put("version", "3.1.1-beta.x.2+1.1"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "3.1.1-beta.x.1+1.1"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, testAttributes, null)); } // Test if equals semantic version even when only same preRelease is passed in user attribute and no build meta @@ -1074,7 +1074,7 @@ public void testIsSemanticEqualWhenMajorMinorPatchPreReleaseVersionNum() { Map testAttributes = new HashMap(); testAttributes.put("version", "3.1.1-beta.x.1"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "3.1.1-beta.x.1"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, testAttributes, null)); } // Test if not same @@ -1083,7 +1083,7 @@ public void testIsSemanticNotSameReturnsFalse() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.2"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "2.1.1"); - assertFalse(testInstanceString.evaluate(null, testAttributes)); + assertFalse(testInstanceString.evaluate(null, testAttributes, null)); } // Test when target is full semantic version major.minor.patch @@ -1092,7 +1092,7 @@ public void testIsSemanticSameFull() { Map testAttributes = new HashMap(); testAttributes.put("version", "3.0.1"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "3.0.1"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, testAttributes, null)); } // Test compare less when user condition checks only major.minor @@ -1101,7 +1101,7 @@ public void testIsSemanticLess() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.6"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_lt", "2.2"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, testAttributes, null)); } // When user condition checks major.minor but target is major.minor.patch then its equals @@ -1110,7 +1110,7 @@ public void testIsSemanticLessFalse() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.0"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_lt", "2.1"); - assertFalse(testInstanceString.evaluate(null, testAttributes)); + assertFalse(testInstanceString.evaluate(null, testAttributes, null)); } // Test compare less when target is full major.minor.patch @@ -1119,7 +1119,7 @@ public void testIsSemanticFullLess() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.6"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_lt", "2.1.9"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, testAttributes, null)); } // Test compare greater when user condition checks only major.minor @@ -1128,7 +1128,7 @@ public void testIsSemanticMore() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.3.6"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "2.2"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, testAttributes, null)); } // Test compare greater when both are major.minor.patch-beta but target is greater than user condition @@ -1137,7 +1137,7 @@ public void testIsSemanticMoreWhenBeta() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.3.6-beta"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "2.3.5-beta"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, testAttributes, null)); } // Test compare greater when target is major.minor.patch @@ -1146,7 +1146,7 @@ public void testIsSemanticFullMore() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.7"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "2.1.6"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, testAttributes, null)); } // Test compare greater when target is major.minor.patch is smaller then it returns false @@ -1155,7 +1155,7 @@ public void testSemanticVersionGTFullMoreReturnsFalse() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.9"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "2.1.10"); - assertFalse(testInstanceString.evaluate(null, testAttributes)); + assertFalse(testInstanceString.evaluate(null, testAttributes, null)); } // Test compare equal when both are exactly same - major.minor.patch-beta @@ -1164,7 +1164,7 @@ public void testIsSemanticFullEqual() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.9-beta"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "2.1.9-beta"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, testAttributes, null)); } // Test compare equal when both major.minor.patch is same, but due to beta user condition is smaller @@ -1173,7 +1173,7 @@ public void testIsSemanticLessWhenBeta() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.9"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "2.1.9-beta"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, testAttributes, null)); } // Test compare greater when target is major.minor.patch-beta and user condition only compares major.minor.patch @@ -1182,7 +1182,7 @@ public void testIsSemanticGreaterBeta() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.9"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "2.1.9-beta"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, testAttributes, null)); } // Test compare equal when target is major.minor.patch @@ -1191,7 +1191,7 @@ public void testIsSemanticLessEqualsWhenEqualsReturnsTrue() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.9"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_le", "2.1.9"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, testAttributes, null)); } // Test compare less when target is major.minor.patch @@ -1200,7 +1200,7 @@ public void testIsSemanticLessEqualsWhenLessReturnsTrue() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.132.9"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_le", "2.233.91"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, testAttributes, null)); } // Test compare less when target is major.minor.patch @@ -1209,7 +1209,7 @@ public void testIsSemanticLessEqualsWhenGreaterReturnsFalse() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.233.91"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_le", "2.132.009"); - assertFalse(testInstanceString.evaluate(null, testAttributes)); + assertFalse(testInstanceString.evaluate(null, testAttributes, null)); } // Test compare equal when target is major.minor.patch @@ -1218,7 +1218,7 @@ public void testIsSemanticGreaterEqualsWhenEqualsReturnsTrue() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.9"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_ge", "2.1.9"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, testAttributes, null)); } // Test compare less when target is major.minor.patch @@ -1227,7 +1227,7 @@ public void testIsSemanticGreaterEqualsWhenLessReturnsTrue() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.233.91"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_ge", "2.132.9"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, testAttributes, null)); } // Test compare less when target is major.minor.patch @@ -1236,7 +1236,7 @@ public void testIsSemanticGreaterEqualsWhenLessReturnsFalse() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.132.009"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_ge", "2.233.91"); - assertFalse(testInstanceString.evaluate(null, testAttributes)); + assertFalse(testInstanceString.evaluate(null, testAttributes, null)); } /** @@ -1245,7 +1245,7 @@ public void testIsSemanticGreaterEqualsWhenLessReturnsFalse() { @Test public void notConditionEvaluateNull() { NotCondition notCondition = new NotCondition(new NullCondition()); - assertNull(notCondition.evaluate(null, testUserAttributes)); + assertNull(notCondition.evaluate(null, testUserAttributes, null)); } /** @@ -1254,11 +1254,11 @@ public void notConditionEvaluateNull() { @Test public void notConditionEvaluateTrue() { UserAttribute userAttribute = mock(UserAttribute.class); - when(userAttribute.evaluate(null, testUserAttributes)).thenReturn(false); + when(userAttribute.evaluate(null, testUserAttributes, null)).thenReturn(false); NotCondition notCondition = new NotCondition(userAttribute); - assertTrue(notCondition.evaluate(null, testUserAttributes)); - verify(userAttribute, times(1)).evaluate(null, testUserAttributes); + assertTrue(notCondition.evaluate(null, testUserAttributes, null)); + verify(userAttribute, times(1)).evaluate(null, testUserAttributes, null); } /** @@ -1267,11 +1267,11 @@ public void notConditionEvaluateTrue() { @Test public void notConditionEvaluateFalse() { UserAttribute userAttribute = mock(UserAttribute.class); - when(userAttribute.evaluate(null, testUserAttributes)).thenReturn(true); + when(userAttribute.evaluate(null, testUserAttributes, null)).thenReturn(true); NotCondition notCondition = new NotCondition(userAttribute); - assertFalse(notCondition.evaluate(null, testUserAttributes)); - verify(userAttribute, times(1)).evaluate(null, testUserAttributes); + assertFalse(notCondition.evaluate(null, testUserAttributes, null)); + verify(userAttribute, times(1)).evaluate(null, testUserAttributes, null); } /** @@ -1280,20 +1280,20 @@ public void notConditionEvaluateFalse() { @Test public void orConditionEvaluateTrue() { UserAttribute userAttribute1 = mock(UserAttribute.class); - when(userAttribute1.evaluate(null, testUserAttributes)).thenReturn(true); + when(userAttribute1.evaluate(null, testUserAttributes, null)).thenReturn(true); UserAttribute userAttribute2 = mock(UserAttribute.class); - when(userAttribute2.evaluate(null, testUserAttributes)).thenReturn(false); + when(userAttribute2.evaluate(null, testUserAttributes, null)).thenReturn(false); List conditions = new ArrayList(); conditions.add(userAttribute1); conditions.add(userAttribute2); OrCondition orCondition = new OrCondition(conditions); - assertTrue(orCondition.evaluate(null, testUserAttributes)); - verify(userAttribute1, times(1)).evaluate(null, testUserAttributes); + assertTrue(orCondition.evaluate(null, testUserAttributes, null)); + verify(userAttribute1, times(1)).evaluate(null, testUserAttributes, null); // shouldn't be called due to short-circuiting in 'Or' evaluation - verify(userAttribute2, times(0)).evaluate(null, testUserAttributes); + verify(userAttribute2, times(0)).evaluate(null, testUserAttributes, null); } /** @@ -1302,20 +1302,20 @@ public void orConditionEvaluateTrue() { @Test public void orConditionEvaluateTrueWithNullAndTrue() { UserAttribute userAttribute1 = mock(UserAttribute.class); - when(userAttribute1.evaluate(null, testUserAttributes)).thenReturn(null); + when(userAttribute1.evaluate(null, testUserAttributes, null)).thenReturn(null); UserAttribute userAttribute2 = mock(UserAttribute.class); - when(userAttribute2.evaluate(null, testUserAttributes)).thenReturn(true); + when(userAttribute2.evaluate(null, testUserAttributes, null)).thenReturn(true); List conditions = new ArrayList(); conditions.add(userAttribute1); conditions.add(userAttribute2); OrCondition orCondition = new OrCondition(conditions); - assertTrue(orCondition.evaluate(null, testUserAttributes)); - verify(userAttribute1, times(1)).evaluate(null, testUserAttributes); + assertTrue(orCondition.evaluate(null, testUserAttributes, null)); + verify(userAttribute1, times(1)).evaluate(null, testUserAttributes, null); // shouldn't be called due to short-circuiting in 'Or' evaluation - verify(userAttribute2, times(1)).evaluate(null, testUserAttributes); + verify(userAttribute2, times(1)).evaluate(null, testUserAttributes, null); } /** @@ -1324,20 +1324,20 @@ public void orConditionEvaluateTrueWithNullAndTrue() { @Test public void orConditionEvaluateNullWithNullAndFalse() { UserAttribute userAttribute1 = mock(UserAttribute.class); - when(userAttribute1.evaluate(null, testUserAttributes)).thenReturn(null); + when(userAttribute1.evaluate(null, testUserAttributes, null)).thenReturn(null); UserAttribute userAttribute2 = mock(UserAttribute.class); - when(userAttribute2.evaluate(null, testUserAttributes)).thenReturn(false); + when(userAttribute2.evaluate(null, testUserAttributes, null)).thenReturn(false); List conditions = new ArrayList(); conditions.add(userAttribute1); conditions.add(userAttribute2); OrCondition orCondition = new OrCondition(conditions); - assertNull(orCondition.evaluate(null, testUserAttributes)); - verify(userAttribute1, times(1)).evaluate(null, testUserAttributes); + assertNull(orCondition.evaluate(null, testUserAttributes, null)); + verify(userAttribute1, times(1)).evaluate(null, testUserAttributes, null); // shouldn't be called due to short-circuiting in 'Or' evaluation - verify(userAttribute2, times(1)).evaluate(null, testUserAttributes); + verify(userAttribute2, times(1)).evaluate(null, testUserAttributes, null); } /** @@ -1346,20 +1346,20 @@ public void orConditionEvaluateNullWithNullAndFalse() { @Test public void orConditionEvaluateFalseWithFalseAndFalse() { UserAttribute userAttribute1 = mock(UserAttribute.class); - when(userAttribute1.evaluate(null, testUserAttributes)).thenReturn(false); + when(userAttribute1.evaluate(null, testUserAttributes, null)).thenReturn(false); UserAttribute userAttribute2 = mock(UserAttribute.class); - when(userAttribute2.evaluate(null, testUserAttributes)).thenReturn(false); + when(userAttribute2.evaluate(null, testUserAttributes, null)).thenReturn(false); List conditions = new ArrayList(); conditions.add(userAttribute1); conditions.add(userAttribute2); OrCondition orCondition = new OrCondition(conditions); - assertFalse(orCondition.evaluate(null, testUserAttributes)); - verify(userAttribute1, times(1)).evaluate(null, testUserAttributes); + assertFalse(orCondition.evaluate(null, testUserAttributes, null)); + verify(userAttribute1, times(1)).evaluate(null, testUserAttributes, null); // shouldn't be called due to short-circuiting in 'Or' evaluation - verify(userAttribute2, times(1)).evaluate(null, testUserAttributes); + verify(userAttribute2, times(1)).evaluate(null, testUserAttributes, null); } /** @@ -1368,19 +1368,19 @@ public void orConditionEvaluateFalseWithFalseAndFalse() { @Test public void orConditionEvaluateFalse() { UserAttribute userAttribute1 = mock(UserAttribute.class); - when(userAttribute1.evaluate(null, testUserAttributes)).thenReturn(false); + when(userAttribute1.evaluate(null, testUserAttributes, null)).thenReturn(false); UserAttribute userAttribute2 = mock(UserAttribute.class); - when(userAttribute2.evaluate(null, testUserAttributes)).thenReturn(false); + when(userAttribute2.evaluate(null, testUserAttributes, null)).thenReturn(false); List conditions = new ArrayList(); conditions.add(userAttribute1); conditions.add(userAttribute2); OrCondition orCondition = new OrCondition(conditions); - assertFalse(orCondition.evaluate(null, testUserAttributes)); - verify(userAttribute1, times(1)).evaluate(null, testUserAttributes); - verify(userAttribute2, times(1)).evaluate(null, testUserAttributes); + assertFalse(orCondition.evaluate(null, testUserAttributes, null)); + verify(userAttribute1, times(1)).evaluate(null, testUserAttributes, null); + verify(userAttribute2, times(1)).evaluate(null, testUserAttributes, null); } /** @@ -1389,19 +1389,19 @@ public void orConditionEvaluateFalse() { @Test public void andConditionEvaluateTrue() { OrCondition orCondition1 = mock(OrCondition.class); - when(orCondition1.evaluate(null, testUserAttributes)).thenReturn(true); + when(orCondition1.evaluate(null, testUserAttributes, null)).thenReturn(true); OrCondition orCondition2 = mock(OrCondition.class); - when(orCondition2.evaluate(null, testUserAttributes)).thenReturn(true); + when(orCondition2.evaluate(null, testUserAttributes, null)).thenReturn(true); List conditions = new ArrayList(); conditions.add(orCondition1); conditions.add(orCondition2); AndCondition andCondition = new AndCondition(conditions); - assertTrue(andCondition.evaluate(null, testUserAttributes)); - verify(orCondition1, times(1)).evaluate(null, testUserAttributes); - verify(orCondition2, times(1)).evaluate(null, testUserAttributes); + assertTrue(andCondition.evaluate(null, testUserAttributes, null)); + verify(orCondition1, times(1)).evaluate(null, testUserAttributes, null); + verify(orCondition2, times(1)).evaluate(null, testUserAttributes, null); } /** @@ -1410,19 +1410,19 @@ public void andConditionEvaluateTrue() { @Test public void andConditionEvaluateFalseWithNullAndFalse() { OrCondition orCondition1 = mock(OrCondition.class); - when(orCondition1.evaluate(null, testUserAttributes)).thenReturn(null); + when(orCondition1.evaluate(null, testUserAttributes, null)).thenReturn(null); OrCondition orCondition2 = mock(OrCondition.class); - when(orCondition2.evaluate(null, testUserAttributes)).thenReturn(false); + when(orCondition2.evaluate(null, testUserAttributes, null)).thenReturn(false); List conditions = new ArrayList(); conditions.add(orCondition1); conditions.add(orCondition2); AndCondition andCondition = new AndCondition(conditions); - assertFalse(andCondition.evaluate(null, testUserAttributes)); - verify(orCondition1, times(1)).evaluate(null, testUserAttributes); - verify(orCondition2, times(1)).evaluate(null, testUserAttributes); + assertFalse(andCondition.evaluate(null, testUserAttributes, null)); + verify(orCondition1, times(1)).evaluate(null, testUserAttributes, null); + verify(orCondition2, times(1)).evaluate(null, testUserAttributes, null); } /** @@ -1431,19 +1431,19 @@ public void andConditionEvaluateFalseWithNullAndFalse() { @Test public void andConditionEvaluateNullWithNullAndTrue() { OrCondition orCondition1 = mock(OrCondition.class); - when(orCondition1.evaluate(null, testUserAttributes)).thenReturn(null); + when(orCondition1.evaluate(null, testUserAttributes, null)).thenReturn(null); OrCondition orCondition2 = mock(OrCondition.class); - when(orCondition2.evaluate(null, testUserAttributes)).thenReturn(true); + when(orCondition2.evaluate(null, testUserAttributes, null)).thenReturn(true); List conditions = new ArrayList(); conditions.add(orCondition1); conditions.add(orCondition2); AndCondition andCondition = new AndCondition(conditions); - assertNull(andCondition.evaluate(null, testUserAttributes)); - verify(orCondition1, times(1)).evaluate(null, testUserAttributes); - verify(orCondition2, times(1)).evaluate(null, testUserAttributes); + assertNull(andCondition.evaluate(null, testUserAttributes, null)); + verify(orCondition1, times(1)).evaluate(null, testUserAttributes, null); + verify(orCondition2, times(1)).evaluate(null, testUserAttributes, null); } /** @@ -1452,10 +1452,10 @@ public void andConditionEvaluateNullWithNullAndTrue() { @Test public void andConditionEvaluateFalse() { OrCondition orCondition1 = mock(OrCondition.class); - when(orCondition1.evaluate(null, testUserAttributes)).thenReturn(false); + when(orCondition1.evaluate(null, testUserAttributes, null)).thenReturn(false); OrCondition orCondition2 = mock(OrCondition.class); - when(orCondition2.evaluate(null, testUserAttributes)).thenReturn(true); + when(orCondition2.evaluate(null, testUserAttributes, null)).thenReturn(true); // and[false, true] List conditions = new ArrayList(); @@ -1463,13 +1463,13 @@ public void andConditionEvaluateFalse() { conditions.add(orCondition2); AndCondition andCondition = new AndCondition(conditions); - assertFalse(andCondition.evaluate(null, testUserAttributes)); - verify(orCondition1, times(1)).evaluate(null, testUserAttributes); + assertFalse(andCondition.evaluate(null, testUserAttributes, null)); + verify(orCondition1, times(1)).evaluate(null, testUserAttributes, null); // shouldn't be called due to short-circuiting in 'And' evaluation - verify(orCondition2, times(0)).evaluate(null, testUserAttributes); + verify(orCondition2, times(0)).evaluate(null, testUserAttributes, null); OrCondition orCondition3 = mock(OrCondition.class); - when(orCondition3.evaluate(null, testUserAttributes)).thenReturn(null); + when(orCondition3.evaluate(null, testUserAttributes, null)).thenReturn(null); // and[null, false] List conditions2 = new ArrayList(); @@ -1477,7 +1477,7 @@ public void andConditionEvaluateFalse() { conditions2.add(orCondition1); AndCondition andCondition2 = new AndCondition(conditions2); - assertFalse(andCondition2.evaluate(null, testUserAttributes)); + assertFalse(andCondition2.evaluate(null, testUserAttributes, null)); // and[true, false, null] List conditions3 = new ArrayList(); @@ -1486,7 +1486,7 @@ public void andConditionEvaluateFalse() { conditions3.add(orCondition1); AndCondition andCondition3 = new AndCondition(conditions3); - assertFalse(andCondition3.evaluate(null, testUserAttributes)); + assertFalse(andCondition3.evaluate(null, testUserAttributes, null)); } /** @@ -1518,8 +1518,271 @@ public void nullValueEvaluate() { attributeValue ); - assertNull(nullValueAttribute.evaluate(null, Collections.emptyMap())); - assertNull(nullValueAttribute.evaluate(null, Collections.singletonMap(attributeName, attributeValue))); - assertNull(nullValueAttribute.evaluate(null, (Collections.singletonMap(attributeName, "")))); + assertNull(nullValueAttribute.evaluate(null, Collections.emptyMap(), null)); + assertNull(nullValueAttribute.evaluate(null, Collections.singletonMap(attributeName, attributeValue), null)); + assertNull(nullValueAttribute.evaluate(null, (Collections.singletonMap(attributeName, "")), null)); + } + + /** + * Verify that with odp segment evaluator single ODP audience evaluates true + */ + @Test + public void singleODPAudienceEvaluateTrueIfSegmentExist() throws Exception { + + UserAttribute testInstanceSingleAudience = new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-1"); + List userConditions = new ArrayList<>(); + userConditions.add(testInstanceSingleAudience); + AndCondition andCondition = new AndCondition(userConditions); + + // Should evaluate true if qualified segment exist + List qualifiedSegments = Collections.singletonList("odp-segment-1"); + assertTrue(andCondition.evaluate(null, null, qualifiedSegments)); + } + + /** + * Verify that with odp segment evaluator single ODP audience evaluates false + */ + @Test + public void singleODPAudienceEvaluateFalseIfSegmentNotExist() throws Exception { + + UserAttribute testInstanceSingleAudience = new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-1"); + List userConditions = new ArrayList<>(); + userConditions.add(testInstanceSingleAudience); + AndCondition andCondition = new AndCondition(userConditions); + + // Should evaluate false if qualified segment does not exist + List qualifiedSegments = Collections.singletonList("odp-segment-2"); + assertFalse(andCondition.evaluate(null, null, qualifiedSegments)); + } + + /** + * Verify that with odp segment evaluator single ODP audience evaluates false when segments not provided + */ + @Test + public void singleODPAudienceEvaluateFalseIfSegmentNotProvided() throws Exception { + + UserAttribute testInstanceSingleAudience = new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-1"); + List userConditions = new ArrayList<>(); + userConditions.add(testInstanceSingleAudience); + AndCondition andCondition = new AndCondition(userConditions); + + // Should evaluate false if qualified segment does not exist + List qualifiedSegments = Collections.emptyList(); + assertFalse(andCondition.evaluate(null, null, qualifiedSegments)); + } + + /** + * Verify that with odp segment evaluator evaluates multiple ODP audience true when segment provided exist + */ + @Test + public void singleODPAudienceEvaluateMultipleOdpConditions() { + Condition andCondition = createMultipleConditionAudienceAndOrODP(); + // Should evaluate correctly based on the given segments + List qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + qualifiedSegments.add("odp-segment-3"); + + assertTrue(andCondition.evaluate(null, null, qualifiedSegments)); + + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + qualifiedSegments.add("odp-segment-4"); + + assertTrue(andCondition.evaluate(null, null, qualifiedSegments)); + + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + qualifiedSegments.add("odp-segment-3"); + qualifiedSegments.add("odp-segment-4"); + + assertTrue(andCondition.evaluate(null, null, qualifiedSegments)); + } + + /** + * Verify that with odp segment evaluator evaluates multiple ODP audience true when segment provided exist + */ + @Test + public void singleODPAudienceEvaluateMultipleOdpConditionsEvaluateFalse() { + Condition andCondition = createMultipleConditionAudienceAndOrODP(); + // Should evaluate correctly based on the given segments + List qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-3"); + qualifiedSegments.add("odp-segment-4"); + + assertFalse(andCondition.evaluate(null, null, qualifiedSegments)); + + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-2"); + qualifiedSegments.add("odp-segment-3"); + qualifiedSegments.add("odp-segment-4"); + + assertFalse(andCondition.evaluate(null, null, qualifiedSegments)); + } + + /** + * Verify that with odp segment evaluator evaluates multiple ODP audience with multiple conditions true or false when segment conditions meet + */ + @Test + public void multipleAudienceEvaluateMultipleOdpConditionsEvaluate() { + // ["and", "1", "2"] + List audience1And2 = new ArrayList<>(); + audience1And2.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-1")); + audience1And2.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-2")); + AndCondition audienceCondition1 = new AndCondition(audience1And2); + + // ["and", "3", "4"] + List audience3And4 = new ArrayList<>(); + audience3And4.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-3")); + audience3And4.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-4")); + AndCondition audienceCondition2 = new AndCondition(audience3And4); + + // ["or", "5", "6"] + List audience5And6 = new ArrayList<>(); + audience5And6.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-5")); + audience5And6.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-6")); + OrCondition audienceCondition3 = new OrCondition(audience5And6); + + + //Scenario 1- ['or', '1', '2', '3'] + List conditions = new ArrayList<>(); + conditions.add(audienceCondition1); + conditions.add(audienceCondition2); + conditions.add(audienceCondition3); + + OrCondition implicitOr = new OrCondition(conditions); + // Should evaluate correctly based on the given segments + List qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + assertTrue(implicitOr.evaluate(null, null, qualifiedSegments)); + + + //Scenario 2- ['and', '1', '2', '3'] + AndCondition implicitAnd = new AndCondition(conditions); + // Should evaluate correctly based on the given segments + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + assertFalse(implicitAnd.evaluate(null, null, qualifiedSegments)); + + // Should evaluate correctly based on the given segments + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + qualifiedSegments.add("odp-segment-3"); + qualifiedSegments.add("odp-segment-4"); + qualifiedSegments.add("odp-segment-6"); + assertTrue(implicitAnd.evaluate(null, null, qualifiedSegments)); + + + ////Scenario 3- ['and', '1', '2',['not', '3']] + conditions = new ArrayList<>(); + conditions.add(audienceCondition1); + conditions.add(audienceCondition2); + conditions.add(new NotCondition(audienceCondition3)); + implicitAnd = new AndCondition(conditions); + + // Should evaluate correctly based on the given segments + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + qualifiedSegments.add("odp-segment-3"); + qualifiedSegments.add("odp-segment-4"); + assertTrue(implicitAnd.evaluate(null, null, qualifiedSegments)); + + // Should evaluate correctly based on the given segments + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + qualifiedSegments.add("odp-segment-3"); + qualifiedSegments.add("odp-segment-4"); + qualifiedSegments.add("odp-segment-5"); + assertFalse(implicitAnd.evaluate(null, null, qualifiedSegments)); + } + + /** + * Verify that with odp segment evaluator evaluates multiple ODP audience with multiple type of evaluators + */ + @Test + public void multipleAudienceEvaluateMultipleOdpConditionsEvaluateWithMultipleTypeOfEvaluator() { + // ["and", "1", "2"] + List audience1And2 = new ArrayList<>(); + audience1And2.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-1")); + audience1And2.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-2")); + AndCondition audienceCondition1 = new AndCondition(audience1And2); + + // ["and", "3", "4"] + List audience3And4 = new ArrayList<>(); + audience3And4.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-3")); + audience3And4.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-4")); + AndCondition audienceCondition2 = new AndCondition(audience3And4); + + // ["or", "chrome", "safari"] + List chromeUserAudience = new ArrayList<>(); + chromeUserAudience.add(new UserAttribute("browser_type", "custom_attribute", "exact", "chrome")); + chromeUserAudience.add(new UserAttribute("browser_type", "custom_attribute", "exact", "safari")); + OrCondition audienceCondition3 = new OrCondition(chromeUserAudience); + + + //Scenario 1- ['or', '1', '2', '3'] + List conditions = new ArrayList<>(); + conditions.add(audienceCondition1); + conditions.add(audienceCondition2); + conditions.add(audienceCondition3); + + OrCondition implicitOr = new OrCondition(conditions); + // Should evaluate correctly based on the given segments + List qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + assertTrue(implicitOr.evaluate(null, null, qualifiedSegments)); + + + //Scenario 2- ['and', '1', '2', '3'] + AndCondition implicitAnd = new AndCondition(conditions); + // Should evaluate correctly based on the given segments + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + assertFalse(implicitAnd.evaluate(null, Collections.singletonMap("browser_type", "chrome"), qualifiedSegments)); + + // Should evaluate correctly based on the given segments + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + qualifiedSegments.add("odp-segment-3"); + qualifiedSegments.add("odp-segment-4"); + assertTrue(implicitAnd.evaluate(null, Collections.singletonMap("browser_type", "chrome"), qualifiedSegments)); + + // Should evaluate correctly based on the given segments + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + qualifiedSegments.add("odp-segment-3"); + qualifiedSegments.add("odp-segment-4"); + assertFalse(implicitAnd.evaluate(null, Collections.singletonMap("browser_type", "not_chrome"), qualifiedSegments)); + } + + public Condition createMultipleConditionAudienceAndOrODP() { + UserAttribute testInstanceSingleAudience1 = new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-1"); + UserAttribute testInstanceSingleAudience2 = new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-2"); + UserAttribute testInstanceSingleAudience3 = new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-3"); + UserAttribute testInstanceSingleAudience4 = new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-4"); + + List userConditionsOR = new ArrayList<>(); + userConditionsOR.add(testInstanceSingleAudience3); + userConditionsOR.add(testInstanceSingleAudience4); + OrCondition orCondition = new OrCondition(userConditionsOR); + List userConditionsAnd = new ArrayList<>(); + userConditionsAnd.add(testInstanceSingleAudience1); + userConditionsAnd.add(testInstanceSingleAudience2); + userConditionsAnd.add(orCondition); + AndCondition andCondition = new AndCondition(userConditionsAnd); + + return andCondition; } } diff --git a/core-api/src/test/java/com/optimizely/ab/config/audience/match/QualifiedMatchTest.java b/core-api/src/test/java/com/optimizely/ab/config/audience/match/QualifiedMatchTest.java new file mode 100644 index 000000000..6f72345aa --- /dev/null +++ b/core-api/src/test/java/com/optimizely/ab/config/audience/match/QualifiedMatchTest.java @@ -0,0 +1,68 @@ +/** + * + * Copyright 2022, Optimizely and contributors + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.optimizely.ab.config.audience.match; + +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.*; + +public class QualifiedMatchTest { + + private QualifiedMatch match; + private static final List INVALIDS = Collections.unmodifiableList(Arrays.asList(new byte[0], new Object(), null)); + + @Before + public void setUp() { + match = new QualifiedMatch(); + } + + @Test + public void testInvalidConditionValues() { + for (Object invalid : INVALIDS) { + try { + match.eval(invalid, "valid"); + fail("should have raised exception"); + } catch (UnexpectedValueTypeException e) { + //pass + } + } + } + + @Test + public void testMismatchClasses() throws Exception { + assertNull(match.eval("odp-segment-1", "false")); + assertNull(match.eval("odp-segment-1", null)); + } + + @Test + public void testValueContains() throws Exception { + assertTrue(match.eval("odp-segment-1", Collections.singletonList("odp-segment-1"))); + assertFalse(match.eval("odp-segment-1", Collections.singletonList("odp-segment-2"))); + } + + @Test(expected = UnexpectedValueTypeException.class) + public void testUnknownValueType() throws UnexpectedValueTypeException { + match.eval(1, Collections.singletonList("odp-segment-1")); + } + +}