diff --git a/.github/workflows/instrumented.yml b/.github/workflows/instrumented.yml index 765778384..1c84e88fc 100644 --- a/.github/workflows/instrumented.yml +++ b/.github/workflows/instrumented.yml @@ -9,15 +9,21 @@ on: jobs: test: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: api-level: [ 29 ] - shard: [ 0, 1, 2, 3 ] + shard: [ 0, 1, 2, 3, 4, 5 ] steps: + - name: Enable KVM group perms + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + - name: checkout uses: actions/checkout@v4 @@ -40,12 +46,6 @@ jobs: ~/.android/adb* key: avd-${{ matrix.api-level }} - - name: Enable KVM group perms - run: | - echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules - sudo udevadm control --reload-rules - sudo udevadm trigger --name-match=kvm - - name: create AVD and generate snapshot for caching if: steps.avd-cache.outputs.cache-hit != 'true' uses: reactivecircus/android-emulator-runner@v2 @@ -65,4 +65,4 @@ jobs: with: api-level: ${{ matrix.api-level }} profile: Galaxy Nexus - script: ./gradlew connectedCheck --continue -Pandroid.testInstrumentationRunnerArguments.numShards=4 -Pandroid.testInstrumentationRunnerArguments.shardIndex=${{ matrix.shard }} + script: ./gradlew connectedCheck --continue -Pandroid.testInstrumentationRunnerArguments.numShards=6 -Pandroid.testInstrumentationRunnerArguments.shardIndex=${{ matrix.shard }} diff --git a/src/androidTest/java/io/split/android/client/service/impressions/observer/ImpressionsObserverTest.java b/src/androidTest/java/io/split/android/client/service/impressions/observer/ImpressionsObserverTest.java index 250044137..2f09a1f2a 100644 --- a/src/androidTest/java/io/split/android/client/service/impressions/observer/ImpressionsObserverTest.java +++ b/src/androidTest/java/io/split/android/client/service/impressions/observer/ImpressionsObserverTest.java @@ -47,6 +47,7 @@ private List generateImpressions(long count) { System.currentTimeMillis(), (i % 2 == 0) ? "in segment all" : "whitelisted", i * i, + null, null); imps.add(imp); } @@ -61,6 +62,7 @@ public void testBasicFunctionality() { "on", System.currentTimeMillis(), "in segment all", 1234L, + null, null); // Add 5 new impressions so that the old one is evicted and re-try the test. @@ -80,13 +82,14 @@ public void testValuesArePersistedAcrossInstances() throws InterruptedException "on", System.currentTimeMillis(), "in segment all", 1234L, + null, null); Impression imp2 = new Impression("someOtherKey", null, "someOtherFeature", "on", System.currentTimeMillis(), "in segment all", 1234L, - null); + null, null); // These are not in the cache, so they should return null Long firstImp = observer.testAndSet(imp); @@ -177,6 +180,7 @@ private void caller(ImpressionsObserver o, int count, ConcurrentLinkedQueue properties, boolean isSdkReady) { diff --git a/src/main/java/io/split/android/client/impressions/Impression.java b/src/main/java/io/split/android/client/impressions/Impression.java index 42b0a7b78..89ded4504 100644 --- a/src/main/java/io/split/android/client/impressions/Impression.java +++ b/src/main/java/io/split/android/client/impressions/Impression.java @@ -1,5 +1,7 @@ package io.split.android.client.impressions; +import androidx.annotation.Nullable; + import java.util.Map; public class Impression { @@ -13,9 +15,11 @@ public class Impression { private final Long _changeNumber; private Long _previousTime; private final Map _attributes; + @Nullable + private final String _propertiesJson; - public Impression(String key, String bucketingKey, String split, String treatment, long time, String appliedRule, Long changeNumber, Map atributes) { + public Impression(String key, String bucketingKey, String split, String treatment, long time, String appliedRule, Long changeNumber, Map attributes, String propertiesJson) { _key = key; _bucketingKey = bucketingKey; _split = split; @@ -23,7 +27,8 @@ public Impression(String key, String bucketingKey, String split, String treatmen _time = time; _appliedRule = appliedRule; _changeNumber = changeNumber; - _attributes = atributes; + _attributes = attributes; + _propertiesJson = propertiesJson; } public String key() { @@ -58,6 +63,11 @@ public Map attributes() { return _attributes; } + @Nullable + public String properties() { + return _propertiesJson; + } + public Long previousTime() { return _previousTime; } diff --git a/src/main/java/io/split/android/client/localhost/LocalhostSplitClient.java b/src/main/java/io/split/android/client/localhost/LocalhostSplitClient.java index 71e766525..81e0ec11e 100644 --- a/src/main/java/io/split/android/client/localhost/LocalhostSplitClient.java +++ b/src/main/java/io/split/android/client/localhost/LocalhostSplitClient.java @@ -32,6 +32,7 @@ import io.split.android.client.utils.logger.Logger; import io.split.android.client.validators.FlagSetsValidatorImpl; import io.split.android.client.validators.KeyValidatorImpl; +import io.split.android.client.validators.PropertyValidatorImpl; import io.split.android.client.validators.SplitValidatorImpl; import io.split.android.client.validators.TreatmentManager; import io.split.android.client.validators.TreatmentManagerImpl; @@ -75,7 +76,8 @@ public LocalhostSplitClient(@NonNull LocalhostSplitFactory container, new EvaluatorImpl(splitsStorage, splitParser), new KeyValidatorImpl(), new SplitValidatorImpl(), getImpressionsListener(splitClientConfig), splitClientConfig.labelsEnabled(), eventsManager, attributesManager, attributesMerger, - telemetryStorageProducer, flagSetsFilter, splitsStorage, new ValidationMessageLoggerImpl(), new FlagSetsValidatorImpl()); + telemetryStorageProducer, flagSetsFilter, splitsStorage, new ValidationMessageLoggerImpl(), new FlagSetsValidatorImpl(), + new PropertyValidatorImpl()); } @Override diff --git a/src/main/java/io/split/android/client/service/executor/SplitBaseTaskExecutor.java b/src/main/java/io/split/android/client/service/executor/SplitBaseTaskExecutor.java index 997cfbbab..b45737a8b 100644 --- a/src/main/java/io/split/android/client/service/executor/SplitBaseTaskExecutor.java +++ b/src/main/java/io/split/android/client/service/executor/SplitBaseTaskExecutor.java @@ -125,6 +125,10 @@ public void submitOnMainThread(SplitTask splitTask) { } public void pause() { + long start = System.currentTimeMillis(); + while (!mScheduledTasks.isEmpty() && (System.currentTimeMillis() - start) < 500L) { + try { Thread.sleep(50); } catch (InterruptedException e) { break; } + } mScheduler.pause(); } diff --git a/src/main/java/io/split/android/client/service/executor/SplitTaskExecutorImpl.java b/src/main/java/io/split/android/client/service/executor/SplitTaskExecutorImpl.java index b98ecb681..d443661fd 100644 --- a/src/main/java/io/split/android/client/service/executor/SplitTaskExecutorImpl.java +++ b/src/main/java/io/split/android/client/service/executor/SplitTaskExecutorImpl.java @@ -6,7 +6,7 @@ public class SplitTaskExecutorImpl extends SplitBaseTaskExecutor { - private static final int MIN_THREAD_POOL_SIZE_WHEN_IDLE = 2; + private static final int MIN_THREAD_POOL_SIZE_WHEN_IDLE = 6; private static final String THREAD_NAME_FORMAT = "split-taskExecutor-%d"; @NonNull diff --git a/src/main/java/io/split/android/client/storage/db/SplitQueryDaoImpl.java b/src/main/java/io/split/android/client/storage/db/SplitQueryDaoImpl.java index f0dcda0e3..42458f16e 100644 --- a/src/main/java/io/split/android/client/storage/db/SplitQueryDaoImpl.java +++ b/src/main/java/io/split/android/client/storage/db/SplitQueryDaoImpl.java @@ -5,13 +5,9 @@ import androidx.annotation.NonNull; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; -import io.split.android.client.SplitClientFactoryImpl; -import io.split.android.client.SplitFactoryImpl; import io.split.android.client.utils.logger.Logger; public class SplitQueryDaoImpl implements SplitQueryDao { @@ -55,13 +51,13 @@ int getColumnIndexOrThrow(@NonNull Cursor c, @NonNull String name) { public Map getAllAsMap() { // Fast path - if the map is already initialized, return it immediately - if (mIsInitialized) { + if (mIsInitialized && !mCachedSplitsMap.isEmpty()) { return new HashMap<>(mCachedSplitsMap); } // Wait for initialization to complete if it's in progress synchronized (mLock) { - if (mIsInitialized) { + if (mIsInitialized && !mCachedSplitsMap.isEmpty()) { return new HashMap<>(mCachedSplitsMap); } diff --git a/src/main/java/io/split/android/client/validators/PropertyValidator.java b/src/main/java/io/split/android/client/validators/PropertyValidator.java new file mode 100644 index 000000000..6b86d3b9e --- /dev/null +++ b/src/main/java/io/split/android/client/validators/PropertyValidator.java @@ -0,0 +1,58 @@ +package io.split.android.client.validators; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.Map; + +public interface PropertyValidator { + + Result validate(Map properties); + + Result validate(Map properties, int initialSizeInBytes, String validationTag); + + class Result { + + private final boolean mIsValid; + @Nullable + private final Map mValidatedProperties; + private final int mSizeInBytes; + @Nullable + private final String mErrorMessage; + + private Result(boolean isValid, Map properties, int sizeInBytes, String errorMessage) { + mIsValid = isValid; + mValidatedProperties = properties; + mSizeInBytes = sizeInBytes; + mErrorMessage = errorMessage; + } + + public boolean isValid() { + return mIsValid; + } + + @Nullable + public Map getValidatedProperties() { + return mValidatedProperties; + } + + public int getSizeInBytes() { + return mSizeInBytes; + } + + @Nullable + public String getErrorMessage() { + return mErrorMessage; + } + + @NonNull + public static Result valid(Map properties, int sizeInBytes) { + return new Result(true, properties, sizeInBytes, null); + } + + @NonNull + public static Result invalid(String errorMessage, int sizeInBytes) { + return new Result(false, null, sizeInBytes, errorMessage); + } + } +} diff --git a/src/main/java/io/split/android/client/validators/PropertyValidatorImpl.java b/src/main/java/io/split/android/client/validators/PropertyValidatorImpl.java new file mode 100644 index 000000000..381637827 --- /dev/null +++ b/src/main/java/io/split/android/client/validators/PropertyValidatorImpl.java @@ -0,0 +1,16 @@ +package io.split.android.client.validators; + +import java.util.Map; + +public class PropertyValidatorImpl implements PropertyValidator { + + @Override + public Result validate(Map properties) { + return Result.valid(properties, 0); // TODO implement + } + + @Override + public Result validate(Map properties, int initialSizeInBytes, String validationTag) { + return Result.valid(properties, initialSizeInBytes); + } +} diff --git a/src/main/java/io/split/android/client/validators/TreatmentManager.java b/src/main/java/io/split/android/client/validators/TreatmentManager.java index 6929fe8e3..49890357d 100644 --- a/src/main/java/io/split/android/client/validators/TreatmentManager.java +++ b/src/main/java/io/split/android/client/validators/TreatmentManager.java @@ -11,24 +11,6 @@ public interface TreatmentManager { - String getTreatment(String split, Map attributes, boolean isClientDestroyed); - - SplitResult getTreatmentWithConfig(String split, Map attributes, boolean isClientDestroyed); - - Map getTreatments(List splits, Map attributes, boolean isClientDestroyed); - - Map getTreatmentsWithConfig(List splits, Map attributes, boolean isClientDestroyed); - - Map getTreatmentsByFlagSet(@NonNull String flagSet, @Nullable Map attributes, boolean isClientDestroyed); - - Map getTreatmentsByFlagSets(@NonNull List flagSets, @Nullable Map attributes, boolean isClientDestroyed); - - Map getTreatmentsWithConfigByFlagSet(@NonNull String flagSet, @Nullable Map attributes, boolean isClientDestroyed); - - Map getTreatmentsWithConfigByFlagSets(@NonNull List flagSets, @Nullable Map attributes, boolean isClientDestroyed); - - - // temporary methods to reduce changes in this iteration String getTreatment(String split, Map attributes, EvaluationOptions evaluationOptions, boolean isClientDestroyed); SplitResult getTreatmentWithConfig(String split, Map attributes, EvaluationOptions evaluationOptions, boolean isClientDestroyed); diff --git a/src/main/java/io/split/android/client/validators/TreatmentManagerFactoryImpl.java b/src/main/java/io/split/android/client/validators/TreatmentManagerFactoryImpl.java index 3fce796d9..3cb5335ed 100644 --- a/src/main/java/io/split/android/client/validators/TreatmentManagerFactoryImpl.java +++ b/src/main/java/io/split/android/client/validators/TreatmentManagerFactoryImpl.java @@ -30,6 +30,7 @@ public class TreatmentManagerFactoryImpl implements TreatmentManagerFactory { private final SplitsStorage mSplitsStorage; private final ValidationMessageLogger mValidationMessageLogger; private final SplitFilterValidator mFlagSetsValidator; + private final PropertyValidator mPropertyValidator; public TreatmentManagerFactoryImpl(@NonNull KeyValidator keyValidator, @NonNull SplitValidator splitValidator, @@ -51,6 +52,7 @@ public TreatmentManagerFactoryImpl(@NonNull KeyValidator keyValidator, mSplitsStorage = checkNotNull(splitsStorage); mValidationMessageLogger = new ValidationMessageLoggerImpl(); mFlagSetsValidator = new FlagSetsValidatorImpl(); + mPropertyValidator = new PropertyValidatorImpl(); } @Override @@ -70,7 +72,8 @@ public TreatmentManager getTreatmentManager(Key key, ListenableEventsManager eve mFlagSetsFilter, mSplitsStorage, mValidationMessageLogger, - mFlagSetsValidator + mFlagSetsValidator, + mPropertyValidator ); } } diff --git a/src/main/java/io/split/android/client/validators/TreatmentManagerImpl.java b/src/main/java/io/split/android/client/validators/TreatmentManagerImpl.java index 81d5ac347..3df6c6cbf 100644 --- a/src/main/java/io/split/android/client/validators/TreatmentManagerImpl.java +++ b/src/main/java/io/split/android/client/validators/TreatmentManagerImpl.java @@ -28,6 +28,7 @@ import io.split.android.client.storage.splits.SplitsStorage; import io.split.android.client.telemetry.model.Method; import io.split.android.client.telemetry.storage.TelemetryStorageProducer; +import io.split.android.client.utils.Json; import io.split.android.client.utils.logger.Logger; import io.split.android.grammar.Treatments; @@ -50,6 +51,7 @@ public class TreatmentManagerImpl implements TreatmentManager { private final FlagSetsFilter mFlagSetsFilter; private final SplitsStorage mSplitsStorage; private final SplitFilterValidator mFlagSetsValidator; + private final PropertyValidator mPropertyValidator; public TreatmentManagerImpl(String matchingKey, String bucketingKey, @@ -65,7 +67,8 @@ public TreatmentManagerImpl(String matchingKey, @Nullable FlagSetsFilter flagSetsFilter, @NonNull SplitsStorage splitsStorage, @NonNull ValidationMessageLogger validationLogger, - @NonNull SplitFilterValidator flagSetsValidator) { + @NonNull SplitFilterValidator flagSetsValidator, + @NonNull PropertyValidator propertyValidator) { mEvaluator = evaluator; mKeyValidator = keyValidator; mSplitValidator = splitValidator; @@ -81,46 +84,7 @@ public TreatmentManagerImpl(String matchingKey, mFlagSetsFilter = flagSetsFilter; mSplitsStorage = checkNotNull(splitsStorage); mFlagSetsValidator = checkNotNull(flagSetsValidator); - } - - @Override - public String getTreatment(String split, Map attributes, boolean isClientDestroyed) { - return getTreatment(split, attributes, null, isClientDestroyed); - } - - @Override - public SplitResult getTreatmentWithConfig(String split, Map attributes, boolean isClientDestroyed) { - return getTreatmentWithConfig(split, attributes, null, isClientDestroyed); - } - - @Override - public Map getTreatments(List splits, Map attributes, boolean isClientDestroyed) { - return getTreatments(splits, attributes, null, isClientDestroyed); - } - - @Override - public Map getTreatmentsWithConfig(List splits, Map attributes, boolean isClientDestroyed) { - return getTreatmentsWithConfig(splits, attributes, null, isClientDestroyed); - } - - @Override - public Map getTreatmentsByFlagSet(@NonNull String flagSet, @Nullable Map attributes, boolean isClientDestroyed) { - return getTreatmentsByFlagSet(flagSet, attributes, null, isClientDestroyed); - } - - @Override - public Map getTreatmentsByFlagSets(@NonNull List flagSets, @Nullable Map attributes, boolean isClientDestroyed) { - return getTreatmentsByFlagSets(flagSets, attributes, null, isClientDestroyed); - } - - @Override - public Map getTreatmentsWithConfigByFlagSet(@NonNull String flagSet, @Nullable Map attributes, boolean isClientDestroyed) { - return getTreatmentsWithConfigByFlagSet(flagSet, attributes, null, isClientDestroyed); - } - - @Override - public Map getTreatmentsWithConfigByFlagSets(@NonNull List flagSets, @Nullable Map attributes, boolean isClientDestroyed) { - return getTreatmentsWithConfigByFlagSets(flagSets, attributes, null, isClientDestroyed); + mPropertyValidator = checkNotNull(propertyValidator); } @Override @@ -288,7 +252,7 @@ private Map getTreatmentsWithConfigGeneric(@Nullable List // Perform evaluations for every feature flag for (String featureFlagName : names) { - TreatmentResult evaluationResult = getTreatmentWithConfigWithoutMetrics(featureFlagName, mergedAttributes, validationTag); + TreatmentResult evaluationResult = getTreatmentWithConfigWithoutMetrics(featureFlagName, mergedAttributes, validationTag, evaluationOptions); result.put(featureFlagName, resultTransformer.transform(evaluationResult.getSplitResult())); if (evaluationResult.isException()) { @@ -311,7 +275,7 @@ private Map getTreatmentsWithConfigGeneric(@Nullable List } } - private TreatmentResult getTreatmentWithConfigWithoutMetrics(String split, Map mergedAttributes, String validationTag) { + private TreatmentResult getTreatmentWithConfigWithoutMetrics(String split, Map mergedAttributes, String validationTag, EvaluationOptions evaluationOptions) { EvaluationResult evaluationResult = null; try { @@ -346,7 +310,9 @@ private TreatmentResult getTreatmentWithConfigWithoutMetrics(String split, Map attributes, boolean impressionsDisabled) { + private void logImpression(String matchingKey, String bucketingKey, String splitName, String result, String label, Long changeNumber, Map attributes, boolean impressionsDisabled, EvaluationOptions evaluationOptions, String validationTag) { try { - Impression impression = new Impression(matchingKey, bucketingKey, splitName, result, System.currentTimeMillis(), label, changeNumber, attributes); + String propertiesJson = serializeProperties(evaluationOptions, validationTag); + Impression impression = new Impression(matchingKey, bucketingKey, splitName, result, System.currentTimeMillis(), label, changeNumber, attributes, propertiesJson); DecoratedImpression decoratedImpression = new DecoratedImpression(impression, impressionsDisabled); mImpressionListener.log(decoratedImpression); mImpressionListener.log(impression); @@ -378,6 +347,32 @@ private void logImpression(String matchingKey, String bucketingKey, String split } } + @Nullable + private String serializeProperties(@Nullable EvaluationOptions evaluationOptions, String validationTag) { + if (evaluationOptions == null || evaluationOptions.getProperties() == null || evaluationOptions.getProperties().isEmpty()) { + return null; + } + + // validate using property validator + PropertyValidator.Result result = mPropertyValidator.validate(evaluationOptions.getProperties()); + + if (!result.isValid()) { + mValidationLogger.e("Properties validation failed: " + (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error"), validationTag); + return null; + } + + if (result.getValidatedProperties() == null || result.getValidatedProperties().isEmpty()) { + return null; + } + + try { + return Json.toJson(result.getValidatedProperties()); + } catch (Exception e) { + mValidationLogger.e("Failed to serialize properties to JSON: " + e.getLocalizedMessage(), validationTag); + return null; + } + } + @NonNull private Map getControlTreatmentsForSplitsWithConfig(@Nullable List names, String validationTag, ResultTransformer resultTransformer) { return TreatmentManagerHelper.controlTreatmentsForSplitsWithConfig( diff --git a/src/test/java/io/split/android/client/TreatmentManagerEvaluationOptionsTest.java b/src/test/java/io/split/android/client/TreatmentManagerEvaluationOptionsTest.java new file mode 100644 index 000000000..519db3678 --- /dev/null +++ b/src/test/java/io/split/android/client/TreatmentManagerEvaluationOptionsTest.java @@ -0,0 +1,151 @@ +package io.split.android.client; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import androidx.annotation.NonNull; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentMatcher; + +import java.util.HashMap; + +import io.split.android.client.attributes.AttributesManager; +import io.split.android.client.attributes.AttributesMerger; +import io.split.android.client.events.ListenableEventsManager; +import io.split.android.client.impressions.Impression; +import io.split.android.client.impressions.ImpressionListener; +import io.split.android.client.storage.splits.SplitsStorage; +import io.split.android.client.telemetry.storage.TelemetryStorageProducer; +import io.split.android.client.validators.FlagSetsValidatorImpl; +import io.split.android.client.validators.KeyValidator; +import io.split.android.client.validators.PropertyValidator; +import io.split.android.client.validators.SplitValidator; +import io.split.android.client.validators.TreatmentManagerImpl; +import io.split.android.client.validators.ValidationMessageLogger; + +public class TreatmentManagerEvaluationOptionsTest { + + private ImpressionListener.FederatedImpressionListener mImpressionListener; + private TreatmentManagerImpl mTreatmentManager; + private PropertyValidator mPropertyValidator; + private ValidationMessageLogger mValidationMessageLogger; + private Evaluator mEvaluator; + + @Before + public void setUp() { + mEvaluator = mock(Evaluator.class); + KeyValidator mKeyValidator = mock(KeyValidator.class); + SplitValidator mSplitValidator = mock(SplitValidator.class); + mImpressionListener = mock(ImpressionListener.FederatedImpressionListener.class); + ListenableEventsManager mEventsManager = mock(ListenableEventsManager.class); + AttributesManager mAttributesManager = mock(AttributesManager.class); + AttributesMerger mAttributesMerger = mock(AttributesMerger.class); + TelemetryStorageProducer mTelemetryStorageProducer = mock(TelemetryStorageProducer.class); + FlagSetsFilter mFlagSetsFilter = mock(FlagSetsFilter.class); + SplitsStorage mSplitsStorage = mock(SplitsStorage.class); + mPropertyValidator = mock(PropertyValidator.class); + mValidationMessageLogger = mock(ValidationMessageLogger.class); + mTreatmentManager = new TreatmentManagerImpl( + "matching_key", + "bucketing_key", + mEvaluator, + mKeyValidator, + mSplitValidator, + mImpressionListener, + SplitClientConfig.builder().build().labelsEnabled(), + mEventsManager, + mAttributesManager, + mAttributesMerger, + mTelemetryStorageProducer, + mFlagSetsFilter, + mSplitsStorage, + mValidationMessageLogger, + new FlagSetsValidatorImpl(), + mPropertyValidator); + } + + @Test + public void evaluationWithValidPropertiesAddsThemToImpressionAsJsonString() { + when(mEvaluator.getTreatment(anyString(), anyString(), anyString(), anyMap())).thenReturn(new EvaluationResult("test", "label")); + EvaluationOptions evaluationOptions = getEvaluationOptions(); + when(mPropertyValidator.validate(any())).thenReturn(PropertyValidator.Result.valid(evaluationOptions.getProperties(), 0)); + + mTreatmentManager.getTreatmentWithConfig("test", null, evaluationOptions, false); + + verify(mImpressionListener).log(argThat(new ArgumentMatcher() { + @Override + public boolean matches(Impression argument) { + return (argument.properties().equals("{\"key\":\"value\",\"key2\":2}") || + argument.properties().equals("{\"key2\":2,\"key\":\"value\"}")) && + argument.split().equals("test"); + } + })); + } + + @Test + public void evaluationWithEmptyPropertiesAddsNullPropertiesToImpression() { + when(mEvaluator.getTreatment(anyString(), anyString(), anyString(), anyMap())).thenReturn(new EvaluationResult("test", "label")); + when(mPropertyValidator.validate(any())).thenReturn(PropertyValidator.Result.valid(null, 0)); + + mTreatmentManager.getTreatmentWithConfig("test", null, new EvaluationOptions(new HashMap<>()), false); + + verify(mImpressionListener).log(argThat(new ArgumentMatcher() { + @Override + public boolean matches(Impression argument) { + return argument.properties() == null && argument.split().equals("test"); + } + })); + } + + @Test + public void invalidPropertiesAreNotAddedToImpression() { + when(mEvaluator.getTreatment(anyString(), anyString(), anyString(), anyMap())).thenReturn(new EvaluationResult("test", "label")); + EvaluationOptions evaluationOptions = getEvaluationOptions(); + when(mPropertyValidator.validate(any())).thenReturn(PropertyValidator.Result.invalid("Invalid properties", 0)); + + mTreatmentManager.getTreatmentWithConfig("test", null, evaluationOptions, false); + + verify(mImpressionListener).log(argThat(new ArgumentMatcher() { + @Override + public boolean matches(Impression argument) { + return argument.properties() == null && argument.split().equals("test"); + } + })); + } + + @Test + public void invalidPropertiesLogsMessageInValidationMessageLogger() { + when(mEvaluator.getTreatment(anyString(), anyString(), anyString(), anyMap())).thenReturn(new EvaluationResult("test", "label")); + EvaluationOptions evaluationOptions = getEvaluationOptions(); + when(mPropertyValidator.validate(any())).thenReturn(PropertyValidator.Result.invalid("Invalid properties", 0)); + + mTreatmentManager.getTreatmentWithConfig("test", null, evaluationOptions, false); + + verify(mValidationMessageLogger).e("Properties validation failed: Invalid properties", "getTreatmentWithConfig"); + } + + @Test + public void propertiesAreValidatedWithPropertyValidator() { + when(mEvaluator.getTreatment(anyString(), anyString(), anyString(), anyMap())).thenReturn(new EvaluationResult("test", "label")); + EvaluationOptions evaluationOptions = getEvaluationOptions(); + + mTreatmentManager.getTreatmentWithConfig("test", null, evaluationOptions, false); + + verify(mPropertyValidator).validate(evaluationOptions.getProperties()); + } + + @NonNull + private static EvaluationOptions getEvaluationOptions() { + HashMap properties = new HashMap<>(); + properties.put("key", "value"); + properties.put("key2", 2); + return new EvaluationOptions(properties); + } +} diff --git a/src/test/java/io/split/android/client/TreatmentManagerExceptionsTest.java b/src/test/java/io/split/android/client/TreatmentManagerExceptionsTest.java index e130f233c..382780fe5 100644 --- a/src/test/java/io/split/android/client/TreatmentManagerExceptionsTest.java +++ b/src/test/java/io/split/android/client/TreatmentManagerExceptionsTest.java @@ -31,6 +31,7 @@ import io.split.android.client.storage.splits.SplitsStorage; import io.split.android.client.telemetry.storage.TelemetryStorageProducer; import io.split.android.client.validators.KeyValidator; +import io.split.android.client.validators.PropertyValidatorImpl; import io.split.android.client.validators.SplitFilterValidator; import io.split.android.client.validators.SplitValidator; import io.split.android.client.validators.TreatmentManagerImpl; @@ -82,7 +83,8 @@ public void setUp() { mFlagSetsFilter, mSplitsStorage, new ValidationMessageLoggerImpl(), - mFlagSetsValidator); + mFlagSetsValidator, + new PropertyValidatorImpl()); when(evaluator.getTreatment(anyString(), anyString(), anyString(), anyMap())).thenReturn(new EvaluationResult("test", "label")); } @@ -101,7 +103,7 @@ public void getTreatmentLogsImpressionWithExceptionLabelWhenExceptionOccurs() { when(evaluator.getTreatment(anyString(), anyString(), anyString(), anyMap())).thenThrow(new RuntimeException("test")); when(eventsManager.eventAlreadyTriggered(SplitEvent.SDK_READY)).thenReturn(true); - treatmentManager.getTreatment("test", Collections.emptyMap(), false); + treatmentManager.getTreatment("test", Collections.emptyMap(), null, false); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Impression.class); verify(impressionListener, times(1)).log(argumentCaptor.capture()); @@ -115,7 +117,7 @@ public void getTreatmentsLogsImpressionWithExceptionLabelWhenExceptionOccurs() { when(evaluator.getTreatment(anyString(), anyString(), eq("test2"), anyMap())).thenReturn(new EvaluationResult("on", "default")); when(eventsManager.eventAlreadyTriggered(SplitEvent.SDK_READY)).thenReturn(true); - Map treatments = treatmentManager.getTreatments(Arrays.asList("test", "test2"), Collections.emptyMap(), false); + Map treatments = treatmentManager.getTreatments(Arrays.asList("test", "test2"), Collections.emptyMap(), null, false); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Impression.class); verify(impressionListener, times(2)).log(argumentCaptor.capture()); @@ -134,7 +136,7 @@ public void getTreatmentWithConfigLogsImpressionWithExceptionLabelWhenExceptionO when(evaluator.getTreatment(anyString(), anyString(), eq("test"), anyMap())).thenThrow(new RuntimeException("test")); when(eventsManager.eventAlreadyTriggered(SplitEvent.SDK_READY)).thenReturn(true); - treatmentManager.getTreatmentWithConfig("test", Collections.emptyMap(), false); + treatmentManager.getTreatmentWithConfig("test", Collections.emptyMap(), null, false); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Impression.class); verify(impressionListener, times(1)).log(argumentCaptor.capture()); @@ -148,7 +150,7 @@ public void getTreatmentsWithConfigLogsImpressionWithExceptionLabelWhenException when(evaluator.getTreatment(anyString(), anyString(), eq("test2"), anyMap())).thenReturn(new EvaluationResult("on", "default")); when(eventsManager.eventAlreadyTriggered(SplitEvent.SDK_READY)).thenReturn(true); - Map treatments = treatmentManager.getTreatmentsWithConfig(Arrays.asList("test", "test2"), Collections.emptyMap(), false); + Map treatments = treatmentManager.getTreatmentsWithConfig(Arrays.asList("test", "test2"), Collections.emptyMap(), null, false); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Impression.class); verify(impressionListener, times(2)).log(argumentCaptor.capture()); @@ -170,7 +172,7 @@ public void getTreatmentsByFlagSetLogsImpressionWithExceptionLabelWhenExceptionO when(mSplitsStorage.getNamesByFlagSets(any())).thenReturn(new HashSet<>(Arrays.asList("test", "test2"))); when(mFlagSetsValidator.items(any(), any(), any())).thenReturn(Collections.singleton("set")); - Map treatments = treatmentManager.getTreatmentsByFlagSet("set", null, false); + Map treatments = treatmentManager.getTreatmentsByFlagSet("set", null, null, false); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Impression.class); verify(impressionListener, times(2)).log(argumentCaptor.capture()); @@ -192,7 +194,7 @@ public void getTreatmentsByFlagSetsLogsImpressionWithExceptionLabelWhenException when(mSplitsStorage.getNamesByFlagSets(any())).thenReturn(new HashSet<>(Arrays.asList("test", "test2"))); when(mFlagSetsValidator.items(any(), any(), any())).thenReturn(Collections.singleton("set")); - Map treatments = treatmentManager.getTreatmentsByFlagSets(Collections.singletonList("set"), null, false); + Map treatments = treatmentManager.getTreatmentsByFlagSets(Collections.singletonList("set"), null, null, false); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Impression.class); verify(impressionListener, times(2)).log(argumentCaptor.capture()); @@ -214,7 +216,7 @@ public void getTreatmentsWithConfigByFlagSetLogsImpressionWithExceptionLabelWhen when(mSplitsStorage.getNamesByFlagSets(any())).thenReturn(new HashSet<>(Arrays.asList("test", "test2"))); when(mFlagSetsValidator.items(any(), any(), any())).thenReturn(Collections.singleton("set")); - Map treatments = treatmentManager.getTreatmentsWithConfigByFlagSet("set", null, false); + Map treatments = treatmentManager.getTreatmentsWithConfigByFlagSet("set", null, null, false); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Impression.class); verify(impressionListener, times(2)).log(argumentCaptor.capture()); @@ -236,7 +238,7 @@ public void getTreatmentsWithConfigByFlagSetsLogsImpressionWithExceptionLabelWhe when(mSplitsStorage.getNamesByFlagSets(any())).thenReturn(new HashSet<>(Arrays.asList("test", "test2"))); when(mFlagSetsValidator.items(any(), any(), any())).thenReturn(Collections.singleton("set")); - Map treatments = treatmentManager.getTreatmentsWithConfigByFlagSets(Collections.singletonList("set"), null, false); + Map treatments = treatmentManager.getTreatmentsWithConfigByFlagSets(Collections.singletonList("set"), null, null, false); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Impression.class); verify(impressionListener, times(2)).log(argumentCaptor.capture()); diff --git a/src/test/java/io/split/android/client/TreatmentManagerTelemetryTest.java b/src/test/java/io/split/android/client/TreatmentManagerTelemetryTest.java index d4b917084..d4a3c2d7e 100644 --- a/src/test/java/io/split/android/client/TreatmentManagerTelemetryTest.java +++ b/src/test/java/io/split/android/client/TreatmentManagerTelemetryTest.java @@ -28,6 +28,7 @@ import io.split.android.client.telemetry.storage.TelemetryStorageProducer; import io.split.android.client.validators.FlagSetsValidatorImpl; import io.split.android.client.validators.KeyValidator; +import io.split.android.client.validators.PropertyValidatorImpl; import io.split.android.client.validators.SplitValidator; import io.split.android.client.validators.TreatmentManagerImpl; import io.split.android.client.validators.ValidationMessageLoggerImpl; @@ -74,7 +75,9 @@ public void setUp() { attributesMerger, telemetryStorageProducer, mFlagSetsFilter, - mSplitsStorage, new ValidationMessageLoggerImpl(), new FlagSetsValidatorImpl()); + mSplitsStorage, new ValidationMessageLoggerImpl(), + new FlagSetsValidatorImpl(), + new PropertyValidatorImpl()); when(evaluator.getTreatment(anyString(), anyString(), anyString(), anyMap())).thenReturn(new EvaluationResult("test", "label")); } @@ -91,7 +94,7 @@ public void tearDown() { @Test public void getTreatmentRecordsLatencyInTelemetry() { - treatmentManager.getTreatment("split", new HashMap<>(), false); + treatmentManager.getTreatment("split", new HashMap<>(), null, false); verify(telemetryStorageProducer).recordLatency(eq(Method.TREATMENT), anyLong()); } @@ -99,21 +102,21 @@ public void getTreatmentRecordsLatencyInTelemetry() { @Test public void getTreatmentsRecordsLatencyInTelemetry() { - treatmentManager.getTreatments(Arrays.asList("split"), new HashMap<>(), false); + treatmentManager.getTreatments(Arrays.asList("split"), new HashMap<>(), null, false); verify(telemetryStorageProducer).recordLatency(eq(Method.TREATMENTS), anyLong()); } @Test public void getTreatmentWithConfigRecordsLatencyInTelemetry() { - treatmentManager.getTreatmentWithConfig("split", new HashMap<>(), false); + treatmentManager.getTreatmentWithConfig("split", new HashMap<>(), null, false); verify(telemetryStorageProducer).recordLatency(eq(Method.TREATMENT_WITH_CONFIG), anyLong()); } @Test public void getTreatmentsWithConfigRecordsLatencyInTelemetry() { - treatmentManager.getTreatmentsWithConfig(Arrays.asList("split"), new HashMap<>(), false); + treatmentManager.getTreatmentsWithConfig(Arrays.asList("split"), new HashMap<>(), null, false); verify(telemetryStorageProducer).recordLatency(eq(Method.TREATMENTS_WITH_CONFIG), anyLong()); } @@ -122,7 +125,7 @@ public void getTreatmentsWithConfigRecordsLatencyInTelemetry() { public void nonReadyUsagesAreRecordedInProducer() { when(eventsManager.eventAlreadyTriggered(SplitEvent.SDK_READY)).thenReturn(false); - treatmentManager.getTreatment("test", Collections.emptyMap(), false); + treatmentManager.getTreatment("test", Collections.emptyMap(), null, false); verify(telemetryStorageProducer).recordNonReadyUsage(); } @@ -131,7 +134,7 @@ public void nonReadyUsagesAreRecordedInProducer() { public void getTreatmentRecordsException() { when(keyValidator.validate(anyString(), anyString())).thenThrow(new RuntimeException("test")); - treatmentManager.getTreatment("test", Collections.emptyMap(), false); + treatmentManager.getTreatment("test", Collections.emptyMap(), null, false); verify(telemetryStorageProducer).recordException(Method.TREATMENT); } @@ -140,7 +143,7 @@ public void getTreatmentRecordsException() { public void getTreatmentsRecordsException() { when(keyValidator.validate(anyString(), anyString())).thenThrow(new RuntimeException("test")); - treatmentManager.getTreatments(Arrays.asList("test", "test2"), Collections.emptyMap(), false); + treatmentManager.getTreatments(Arrays.asList("test", "test2"), Collections.emptyMap(), null, false); verify(telemetryStorageProducer).recordException(Method.TREATMENTS); } @@ -149,7 +152,7 @@ public void getTreatmentsRecordsException() { public void getTreatmentWithConfigRecordsException() { when(keyValidator.validate(anyString(), anyString())).thenThrow(new RuntimeException("test")); - treatmentManager.getTreatmentWithConfig("test", Collections.emptyMap(), false); + treatmentManager.getTreatmentWithConfig("test", Collections.emptyMap(), null, false); verify(telemetryStorageProducer).recordException(Method.TREATMENT_WITH_CONFIG); } @@ -158,7 +161,7 @@ public void getTreatmentWithConfigRecordsException() { public void getTreatmentsWithConfigRecordsException() { when(keyValidator.validate(anyString(), anyString())).thenThrow(new RuntimeException("test")); - treatmentManager.getTreatmentsWithConfig(Arrays.asList("test", "test2"), Collections.emptyMap(), false); + treatmentManager.getTreatmentsWithConfig(Arrays.asList("test", "test2"), Collections.emptyMap(), null, false); verify(telemetryStorageProducer).recordException(Method.TREATMENTS_WITH_CONFIG); } diff --git a/src/test/java/io/split/android/client/TreatmentManagerTest.java b/src/test/java/io/split/android/client/TreatmentManagerTest.java index db68254ca..e81742b24 100644 --- a/src/test/java/io/split/android/client/TreatmentManagerTest.java +++ b/src/test/java/io/split/android/client/TreatmentManagerTest.java @@ -38,6 +38,7 @@ import io.split.android.client.validators.FlagSetsValidatorImpl; import io.split.android.client.validators.KeyValidator; import io.split.android.client.validators.KeyValidatorImpl; +import io.split.android.client.validators.PropertyValidatorImpl; import io.split.android.client.validators.SplitValidator; import io.split.android.client.validators.SplitValidatorImpl; import io.split.android.client.validators.TreatmentManager; @@ -100,7 +101,7 @@ public void testBasicEvaluationNoConfig() { String splitName = "FACUNDO_TEST"; TreatmentManager treatmentManager = createTreatmentManager(matchingKey, matchingKey); - SplitResult splitResult = treatmentManager.getTreatmentWithConfig(splitName, null, false); + SplitResult splitResult = treatmentManager.getTreatmentWithConfig(splitName, null, null, false); Assert.assertNotNull(splitResult); Assert.assertEquals("off", splitResult.treatment()); @@ -113,7 +114,7 @@ public void testBasicEvaluationWithConfig() { String splitName = "Test"; TreatmentManager treatmentManager = createTreatmentManager(matchingKey, matchingKey); - SplitResult splitResult = treatmentManager.getTreatmentWithConfig(splitName, null, false); + SplitResult splitResult = treatmentManager.getTreatmentWithConfig(splitName, null, null, false); Assert.assertNotNull(splitResult); Assert.assertEquals("off", splitResult.treatment()); @@ -126,7 +127,7 @@ public void testBasicEvaluations() { List splitList = Arrays.asList("FACUNDO_TEST", "testo2222", "Test"); TreatmentManager treatmentManager = createTreatmentManager(matchingKey, matchingKey); - Map splitResultList = treatmentManager.getTreatmentsWithConfig(splitList, null, false); + Map splitResultList = treatmentManager.getTreatmentsWithConfig(splitList, null, null, false); SplitResult r1 = splitResultList.get("FACUNDO_TEST"); SplitResult r2 = splitResultList.get("testo2222"); @@ -152,10 +153,10 @@ public void testClientIsDestroyed() { List splitList = Arrays.asList("FACUNDO_TEST", "a_new_split_2", "benchmark_jw_1"); TreatmentManager treatmentManager = createTreatmentManager(matchingKey, matchingKey); - String treatment = treatmentManager.getTreatment(splitName, null, true); - SplitResult splitResult = treatmentManager.getTreatmentWithConfig(splitName, null, true); - Map treatmentList = treatmentManager.getTreatments(splitList, null, true); - Map splitResultList = treatmentManager.getTreatmentsWithConfig(splitList, null, true); + String treatment = treatmentManager.getTreatment(splitName, null, null, true); + SplitResult splitResult = treatmentManager.getTreatmentWithConfig(splitName, null, null, true); + Map treatmentList = treatmentManager.getTreatments(splitList, null, null, true); + Map splitResultList = treatmentManager.getTreatmentsWithConfig(splitList, null, null, true); assertControl(splitList, treatment, treatmentList, splitResult, splitResultList); } @@ -166,10 +167,10 @@ public void testNonExistingSplits() { List splitList = Arrays.asList("NON_EXISTING_1", "NON_EXISTING_2", "NON_EXISTING_3"); TreatmentManager treatmentManager = createTreatmentManager(matchingKey, matchingKey); - String treatment = treatmentManager.getTreatment(splitName, null, false); - SplitResult splitResult = treatmentManager.getTreatmentWithConfig(splitName, null, false); - Map treatmentList = treatmentManager.getTreatments(splitList, null, false); - Map splitResultList = treatmentManager.getTreatmentsWithConfig(splitList, null, false); + String treatment = treatmentManager.getTreatment(splitName, null, null, false); + SplitResult splitResult = treatmentManager.getTreatmentWithConfig(splitName, null, null, false); + Map treatmentList = treatmentManager.getTreatments(splitList, null, null, false); + Map splitResultList = treatmentManager.getTreatmentsWithConfig(splitList, null, null, false); assertControl(splitList, treatment, treatmentList, splitResult, splitResultList); } @@ -180,10 +181,10 @@ public void testEmptySplit() { List splitList = new ArrayList<>(); TreatmentManager treatmentManager = createTreatmentManager(matchingKey, matchingKey); - String treatment = treatmentManager.getTreatment(splitName, null, false); - SplitResult splitResult = treatmentManager.getTreatmentWithConfig(splitName, null, false); - Map treatmentList = treatmentManager.getTreatments(splitList, null, false); - Map splitResultList = treatmentManager.getTreatmentsWithConfig(splitList, null, false); + String treatment = treatmentManager.getTreatment(splitName, null, null, false); + SplitResult splitResult = treatmentManager.getTreatmentWithConfig(splitName, null, null, false); + Map treatmentList = treatmentManager.getTreatments(splitList, null, null, false); + Map splitResultList = treatmentManager.getTreatmentsWithConfig(splitList, null, null, false); assertControl(splitList, treatment, treatmentList, splitResult, splitResultList); } @@ -195,10 +196,10 @@ public void testNullKey() { List splitList = Arrays.asList("FACUNDO_TEST", "a_new_split_2", "benchmark_jw_1"); TreatmentManager treatmentManager = createTreatmentManager(matchingKey, matchingKey); - String treatment = treatmentManager.getTreatment(splitName, null, false); - SplitResult splitResult = treatmentManager.getTreatmentWithConfig(splitName, null, false); - Map treatmentList = treatmentManager.getTreatments(splitList, null, false); - Map splitResultList = treatmentManager.getTreatmentsWithConfig(splitList, null, false); + String treatment = treatmentManager.getTreatment(splitName, null, null, false); + SplitResult splitResult = treatmentManager.getTreatmentWithConfig(splitName, null, null, false); + Map treatmentList = treatmentManager.getTreatments(splitList, null, null, false); + Map splitResultList = treatmentManager.getTreatmentsWithConfig(splitList, null, null, false); assertControl(splitList, treatment, treatmentList, splitResult, splitResultList); } @@ -210,10 +211,10 @@ public void testEmptyKey() { List splitList = new ArrayList<>(); TreatmentManager treatmentManager = createTreatmentManager(matchingKey, matchingKey); - String treatment = treatmentManager.getTreatment(splitName, null, false); - SplitResult splitResult = treatmentManager.getTreatmentWithConfig(splitName, null, false); - Map treatmentList = treatmentManager.getTreatments(splitList, null, false); - Map splitResultList = treatmentManager.getTreatmentsWithConfig(splitList, null, false); + String treatment = treatmentManager.getTreatment(splitName, null, null, false); + SplitResult splitResult = treatmentManager.getTreatmentWithConfig(splitName, null, null, false); + Map treatmentList = treatmentManager.getTreatments(splitList, null, null, false); + Map splitResultList = treatmentManager.getTreatmentsWithConfig(splitList, null, null, false); assertControl(splitList, treatment, treatmentList, splitResult, splitResultList); } @@ -225,10 +226,10 @@ public void testLongKey() { List splitList = new ArrayList<>(); TreatmentManager treatmentManager = createTreatmentManager(matchingKey, matchingKey); - String treatment = treatmentManager.getTreatment(splitName, null, false); - SplitResult splitResult = treatmentManager.getTreatmentWithConfig(splitName, null, false); - Map treatmentList = treatmentManager.getTreatments(splitList, null, false); - Map splitResultList = treatmentManager.getTreatmentsWithConfig(splitList, null, false); + String treatment = treatmentManager.getTreatment(splitName, null, null, false); + SplitResult splitResult = treatmentManager.getTreatmentWithConfig(splitName, null, null, false); + Map treatmentList = treatmentManager.getTreatments(splitList, null, null, false); + Map splitResultList = treatmentManager.getTreatmentsWithConfig(splitList, null, null, false); assertControl(splitList, treatment, treatmentList, splitResult, splitResultList); } @@ -240,10 +241,10 @@ public void testNullSplit() { List splitList = null; TreatmentManager treatmentManager = createTreatmentManager(matchingKey, matchingKey); - String treatment = treatmentManager.getTreatment(splitName, null, false); - SplitResult splitResult = treatmentManager.getTreatmentWithConfig(splitName, null, false); - Map treatmentList = treatmentManager.getTreatments(splitList, null, false); - Map splitResultList = treatmentManager.getTreatmentsWithConfig(splitList, null, false); + String treatment = treatmentManager.getTreatment(splitName, null, null, false); + SplitResult splitResult = treatmentManager.getTreatmentWithConfig(splitName, null, null, false); + Map treatmentList = treatmentManager.getTreatments(splitList, null, null, false); + Map splitResultList = treatmentManager.getTreatmentsWithConfig(splitList, null, null, false); Assert.assertNotNull(treatment); Assert.assertEquals(Treatments.CONTROL, treatment); @@ -257,14 +258,14 @@ public void testDefinitionNotFoundLabel() { TreatmentManagerImpl tManager = initializeTreatmentManager(evaluatorMock); - tManager.getTreatment("FACUNDO_TEST", null, false); + tManager.getTreatment("FACUNDO_TEST", null, null, false); verifyNoInteractions(impressionListener); } @Test public void getTreatmentTakesValuesFromAttributesManagerIntoAccount() { - treatmentManager.getTreatment("test_split", new HashMap<>(), false); + treatmentManager.getTreatment("test_split", new HashMap<>(), null, false); verify(attributesManager).getAllAttributes(); } @@ -272,7 +273,7 @@ public void getTreatmentTakesValuesFromAttributesManagerIntoAccount() { @Test public void getTreatmentWithConfigTakesValuesFromAttributesManagerIntoAccount() { - treatmentManager.getTreatmentWithConfig("test_split", new HashMap<>(), false); + treatmentManager.getTreatmentWithConfig("test_split", new HashMap<>(), null, false); verify(attributesManager).getAllAttributes(); } @@ -283,7 +284,7 @@ public void getTreatmentsTakesValuesFromAttributesManagerIntoAccount() { splits.add("test_split_1"); splits.add("test_split_2"); - treatmentManager.getTreatments(splits, new HashMap<>(), false); + treatmentManager.getTreatments(splits, new HashMap<>(), null, false); verify(attributesManager).getAllAttributes(); } @@ -294,7 +295,7 @@ public void getTreatmentsWithConfigTakesValuesFromAttributesManagerIntoAccount() splits.add("test_split_1"); splits.add("test_split_2"); - treatmentManager.getTreatmentsWithConfig(splits, new HashMap<>(), false); + treatmentManager.getTreatmentsWithConfig(splits, new HashMap<>(), null, false); verify(attributesManager).getAllAttributes(); } @@ -312,7 +313,7 @@ public void evaluationWhenNotReadyLogsCorrectMessage() { when(eventsManager.eventAlreadyTriggered(SplitEvent.SDK_READY)).thenReturn(false); when(eventsManager.eventAlreadyTriggered(SplitEvent.SDK_READY_FROM_CACHE)).thenReturn(false); createTreatmentManager("my_key", null, validationMessageLogger, splitValidator, evaluatorMock, eventsManager) - .getTreatment("test_split", null, false); + .getTreatment("test_split", null, null, false); verify(validationMessageLogger).w(eq("the SDK is not ready, results may be incorrect for feature flag test_split. Make sure to wait for SDK readiness before using this method"), any()); } @@ -324,7 +325,7 @@ public void trackValueFromEvaluationResultGetsPassedInToImpression() { .thenReturn(new EvaluationResult("test", "test", true)); TreatmentManagerImpl tManager = initializeTreatmentManager(evaluatorMock); - tManager.getTreatment("test_impressions_disabled", null, false); + tManager.getTreatment("test_impressions_disabled", null, null, false); verify(impressionListener).log(argThat((DecoratedImpression decoratedImpression) -> { return decoratedImpression.isImpressionsDisabled(); @@ -367,7 +368,7 @@ private TreatmentManager createTreatmentManager(String matchingKey, String bucke new KeyValidatorImpl(), splitValidator, mock(ImpressionListener.FederatedImpressionListener.class), config.labelsEnabled(), eventsManager, mock(AttributesManager.class), mock(AttributesMerger.class), - mock(TelemetryStorageProducer.class), mFlagSetsFilter, mSplitsStorage, validationLogger, new FlagSetsValidatorImpl()); + mock(TelemetryStorageProducer.class), mFlagSetsFilter, mSplitsStorage, validationLogger, new FlagSetsValidatorImpl(), new PropertyValidatorImpl()); } private TreatmentManagerImpl initializeTreatmentManager() { @@ -397,7 +398,7 @@ private TreatmentManagerImpl initializeTreatmentManager(Evaluator evaluator) { telemetryStorageProducer, mFlagSetsFilter, mSplitsStorage, - new ValidationMessageLoggerImpl(), new FlagSetsValidatorImpl()); + new ValidationMessageLoggerImpl(), new FlagSetsValidatorImpl(), new PropertyValidatorImpl()); } private Map splitsMap(List splits) { diff --git a/src/test/java/io/split/android/client/TreatmentManagerWithFlagSetsTest.java b/src/test/java/io/split/android/client/TreatmentManagerWithFlagSetsTest.java index bdcbec7db..c31db7ed1 100644 --- a/src/test/java/io/split/android/client/TreatmentManagerWithFlagSetsTest.java +++ b/src/test/java/io/split/android/client/TreatmentManagerWithFlagSetsTest.java @@ -32,6 +32,7 @@ import io.split.android.client.telemetry.storage.TelemetryStorageProducer; import io.split.android.client.validators.FlagSetsValidatorImpl; import io.split.android.client.validators.KeyValidator; +import io.split.android.client.validators.PropertyValidatorImpl; import io.split.android.client.validators.SplitValidator; import io.split.android.client.validators.TreatmentManagerImpl; import io.split.android.client.validators.ValidationMessageLoggerImpl; @@ -88,7 +89,7 @@ public void tearDown() { @Test public void getTreatmentsByFlagSetDestroyedDoesNotUseEvaluator() { - mTreatmentManager.getTreatmentsByFlagSet("set_1", null, true); + mTreatmentManager.getTreatmentsByFlagSet("set_1", null, null, true); verify(mSplitsStorage, times(0)).getNamesByFlagSets(any()); verify(mEvaluator, times(0)).getTreatment(any(), any(), any(), anyMap()); @@ -99,7 +100,7 @@ public void getTreatmentsByFlagSetWithNoConfiguredSetsQueriesStorageAndUsesEvalu when(mSplitsStorage.getNamesByFlagSets(Collections.singleton("set_1"))) .thenReturn(new HashSet<>(Collections.singletonList("test_1"))); - mTreatmentManager.getTreatmentsByFlagSet("set_1", null, false); + mTreatmentManager.getTreatmentsByFlagSet("set_1", null, null, false); verify(mSplitsStorage).getNamesByFlagSets(Collections.singleton("set_1")); verify(mEvaluator).getTreatment(eq("matching_key"), eq("bucketing_key"), eq("test_1"), anyMap()); @@ -110,7 +111,7 @@ public void getTreatmentsByFlagSetWithNoConfiguredSetsInvalidSetDoesNotQueryStor when(mSplitsStorage.getNamesByFlagSets(Collections.singleton("set_1"))) .thenReturn(new HashSet<>(Collections.singletonList("test_split"))); - mTreatmentManager.getTreatmentsByFlagSet("SET!", null, false); + mTreatmentManager.getTreatmentsByFlagSet("SET!", null, null, false); verify(mSplitsStorage, times(0)).getNamesByFlagSets(any()); verify(mEvaluator, times(0)).getTreatment(any(), any(), any(), anyMap()); @@ -122,7 +123,7 @@ public void getTreatmentsByFlagSetWithConfiguredSetsExistingSetQueriesStorageAnd when(mSplitsStorage.getNamesByFlagSets(Collections.singleton("set_1"))) .thenReturn(new HashSet<>(Collections.singletonList("test_1"))); - mTreatmentManager.getTreatmentsByFlagSet("set_1", null, false); + mTreatmentManager.getTreatmentsByFlagSet("set_1", null, null, false); verify(mSplitsStorage).getNamesByFlagSets(Collections.singleton("set_1")); verify(mEvaluator).getTreatment(eq("matching_key"), eq("bucketing_key"), eq("test_1"), anyMap()); @@ -135,7 +136,7 @@ public void getTreatmentsByFlagSetWithConfiguredSetsNonExistingSetDoesNotQuerySt when(mSplitsStorage.getNamesByFlagSets(Collections.singleton("set_1"))) .thenReturn(new HashSet<>(Collections.singletonList("test_split"))); - mTreatmentManager.getTreatmentsByFlagSet("set_2", null, false); + mTreatmentManager.getTreatmentsByFlagSet("set_2", null, null, false); verify(mSplitsStorage, times(0)).getNamesByFlagSets(any()); verify(mEvaluator, times(0)).getTreatment(any(), any(), any(), anyMap()); @@ -155,7 +156,7 @@ private void initializeTreatmentManager() { mAttributesMerger, mTelemetryStorageProducer, mFlagSetsFilter, - mSplitsStorage, new ValidationMessageLoggerImpl(), new FlagSetsValidatorImpl()); + mSplitsStorage, new ValidationMessageLoggerImpl(), new FlagSetsValidatorImpl(), new PropertyValidatorImpl()); } @Test @@ -166,7 +167,7 @@ public void getTreatmentsByFlagSetReturnsCorrectFormat() { when(mSplitsStorage.getNamesByFlagSets(Collections.singleton("set_1"))).thenReturn(mockNames); mFlagSetsFilter = new FlagSetsFilterImpl(Collections.singleton("set_1")); - Map result = mTreatmentManager.getTreatmentsByFlagSet("set_1", null, false); + Map result = mTreatmentManager.getTreatmentsByFlagSet("set_1", null, null, false); assertEquals(2, result.size()); assertEquals("result_1", result.get("test_1")); @@ -177,7 +178,7 @@ public void getTreatmentsByFlagSetReturnsCorrectFormat() { public void getTreatmentsByFlagSetRecordsTelemetry() { when(mSplitsStorage.getNamesByFlagSets(Collections.singleton("set_1"))).thenReturn(Collections.singleton("test_1")); - mTreatmentManager.getTreatmentsByFlagSet("set_1", null, false); + mTreatmentManager.getTreatmentsByFlagSet("set_1", null, null, false); verify(mTelemetryStorageProducer).recordLatency(eq(Method.TREATMENTS_BY_FLAG_SET), anyLong()); } @@ -185,7 +186,7 @@ public void getTreatmentsByFlagSetRecordsTelemetry() { /// @Test public void getTreatmentsByFlagSetsDestroyedDoesNotUseEvaluator() { - mTreatmentManager.getTreatmentsByFlagSets(Collections.singletonList("set_1"), null, true); + mTreatmentManager.getTreatmentsByFlagSets(Collections.singletonList("set_1"), null, null, true); verify(mSplitsStorage, times(0)).getNamesByFlagSets(any()); verify(mEvaluator, times(0)).getTreatment(any(), any(), any(), anyMap()); @@ -196,7 +197,7 @@ public void getTreatmentsByFlagSetsWithNoConfiguredSetsQueriesStorageAndUsesEval when(mSplitsStorage.getNamesByFlagSets(new HashSet<>(Arrays.asList("set_1", "set_2")))) .thenReturn(new HashSet<>(Arrays.asList("test_1", "test_2"))); - mTreatmentManager.getTreatmentsByFlagSets(Arrays.asList("set_1", "set_2"), null, false); + mTreatmentManager.getTreatmentsByFlagSets(Arrays.asList("set_1", "set_2"), null, null, false); verify(mSplitsStorage).getNamesByFlagSets(new HashSet<>(Arrays.asList("set_1", "set_2"))); verify(mEvaluator).getTreatment(anyString(), anyString(), eq("test_1"), anyMap()); @@ -208,7 +209,7 @@ public void getTreatmentsByFlagSetsWithNoConfiguredSetsInvalidSetDoesNotQuerySto when(mSplitsStorage.getNamesByFlagSets(Collections.singleton("set_1"))) .thenReturn(new HashSet<>(Collections.singletonList("test_1"))); - mTreatmentManager.getTreatmentsByFlagSets(Arrays.asList("set_1", "SET!"), null, false); + mTreatmentManager.getTreatmentsByFlagSets(Arrays.asList("set_1", "SET!"), null, null, false); verify(mSplitsStorage).getNamesByFlagSets(Collections.singleton("set_1")); verify(mEvaluator).getTreatment(any(), any(), eq("test_1"), anyMap()); @@ -221,7 +222,7 @@ public void getTreatmentsByFlagSetsWithConfiguredSetsExistingSetQueriesStorageFo when(mSplitsStorage.getNamesByFlagSets(Collections.singleton("set_1"))) .thenReturn(new HashSet<>(Collections.singletonList("test_1"))); - mTreatmentManager.getTreatmentsByFlagSets(Arrays.asList("set_1", "set_2"), null, false); + mTreatmentManager.getTreatmentsByFlagSets(Arrays.asList("set_1", "set_2"), null, null, false); verify(mSplitsStorage).getNamesByFlagSets(Collections.singleton("set_1")); verify(mEvaluator).getTreatment(anyString(), anyString(), eq("test_1"), anyMap()); @@ -232,7 +233,7 @@ public void getTreatmentsByFlagSetsWithConfiguredSetsNonExistingSetDoesNotQueryS mFlagSetsFilter = new FlagSetsFilterImpl(Collections.singleton("set_1")); initializeTreatmentManager(); - mTreatmentManager.getTreatmentsByFlagSets(Arrays.asList("set_2", "set_3"), null, false); + mTreatmentManager.getTreatmentsByFlagSets(Arrays.asList("set_2", "set_3"), null, null, false); verify(mSplitsStorage, times(0)).getNamesByFlagSets(any()); verify(mEvaluator, times(0)).getTreatment(any(), any(), any(), anyMap()); @@ -245,7 +246,7 @@ public void getTreatmentsByFlagSetsReturnsCorrectFormat() { mockNames.add("test_2"); when(mSplitsStorage.getNamesByFlagSets(new HashSet<>(Arrays.asList("set_1", "set_2")))).thenReturn(mockNames); - Map result = mTreatmentManager.getTreatmentsByFlagSets(Arrays.asList("set_1", "set_2"), null, false); + Map result = mTreatmentManager.getTreatmentsByFlagSets(Arrays.asList("set_1", "set_2"), null, null, false); assertEquals(2, result.size()); assertEquals("result_1", result.get("test_1")); @@ -254,14 +255,14 @@ public void getTreatmentsByFlagSetsReturnsCorrectFormat() { @Test public void getTreatmentsByFlagSetsWithDuplicatedSetDeduplicates() { - mTreatmentManager.getTreatmentsByFlagSets(Arrays.asList("set_1", "set_1"), null, false); + mTreatmentManager.getTreatmentsByFlagSets(Arrays.asList("set_1", "set_1"), null, null, false); verify(mSplitsStorage).getNamesByFlagSets(Collections.singleton("set_1")); } @Test public void getTreatmentsByFlagSetsWithNullSetListReturnsEmpty() { - Map result = mTreatmentManager.getTreatmentsByFlagSets(null, null, false); + Map result = mTreatmentManager.getTreatmentsByFlagSets(null, null, null, false); verify(mSplitsStorage, times(0)).getNamesByFlagSets(any()); verify(mEvaluator, times(0)).getTreatment(any(), any(), any(), anyMap()); @@ -272,7 +273,7 @@ public void getTreatmentsByFlagSetsWithNullSetListReturnsEmpty() { public void getTreatmentsByFlagSetsRecordsTelemetry() { when(mSplitsStorage.getNamesByFlagSets(Collections.singleton("set_1"))).thenReturn(Collections.singleton("test_1")); - mTreatmentManager.getTreatmentsByFlagSets(Arrays.asList("set_1", "set_2"), null, false); + mTreatmentManager.getTreatmentsByFlagSets(Arrays.asList("set_1", "set_2"), null, null, false); verify(mTelemetryStorageProducer).recordLatency(eq(Method.TREATMENTS_BY_FLAG_SETS), anyLong()); } @@ -280,7 +281,7 @@ public void getTreatmentsByFlagSetsRecordsTelemetry() { /// @Test public void getTreatmentsWithConfigByFlagSetDestroyedDoesNotUseEvaluator() { - mTreatmentManager.getTreatmentsWithConfigByFlagSet("set_1", null, true); + mTreatmentManager.getTreatmentsWithConfigByFlagSet("set_1", null, null, true); verify(mSplitsStorage, times(0)).getNamesByFlagSets(any()); verify(mEvaluator, times(0)).getTreatment(any(), any(), any(), anyMap()); @@ -291,7 +292,7 @@ public void getTreatmentsWithConfigByFlagSetWithNoConfiguredSetsQueriesStorageAn when(mSplitsStorage.getNamesByFlagSets(Collections.singleton("set_1"))) .thenReturn(Collections.singleton("test_1")); - mTreatmentManager.getTreatmentsWithConfigByFlagSet("set_1", null, false); + mTreatmentManager.getTreatmentsWithConfigByFlagSet("set_1", null, null, false); verify(mSplitsStorage).getNamesByFlagSets(Collections.singleton("set_1")); verify(mEvaluator).getTreatment(eq("matching_key"), eq("bucketing_key"), eq("test_1"), anyMap()); @@ -302,7 +303,7 @@ public void getTreatmentsWithConfigByFlagSetWithNoConfiguredSetsInvalidSetDoesNo when(mSplitsStorage.getNamesByFlagSets(Collections.singleton("set_1"))) .thenReturn(new HashSet<>(Collections.singletonList("test_split"))); - mTreatmentManager.getTreatmentsWithConfigByFlagSet("SET!", null, false); + mTreatmentManager.getTreatmentsWithConfigByFlagSet("SET!", null, null, false); verify(mSplitsStorage, times(0)).getNamesByFlagSets(any()); verify(mEvaluator, times(0)).getTreatment(any(), any(), any(), anyMap()); @@ -314,7 +315,7 @@ public void getTreatmentsWithConfigByFlagSetWithConfiguredSetsExistingSetQueries when(mSplitsStorage.getNamesByFlagSets(Collections.singleton("set_1"))) .thenReturn(new HashSet<>(Collections.singletonList("test_1"))); - mTreatmentManager.getTreatmentsWithConfigByFlagSet("set_1", null, false); + mTreatmentManager.getTreatmentsWithConfigByFlagSet("set_1", null, null, false); verify(mSplitsStorage).getNamesByFlagSets(Collections.singleton("set_1")); verify(mEvaluator).getTreatment(eq("matching_key"), eq("bucketing_key"), eq("test_1"), anyMap()); @@ -327,7 +328,7 @@ public void getTreatmentsWithConfigByFlagSetWithConfiguredSetsNonExistingSetDoes when(mSplitsStorage.getNamesByFlagSets(Collections.singleton("set_1"))) .thenReturn(new HashSet<>(Collections.singletonList("test_split"))); - mTreatmentManager.getTreatmentsWithConfigByFlagSet("set_2", null, false); + mTreatmentManager.getTreatmentsWithConfigByFlagSet("set_2", null, null, false); verify(mSplitsStorage, times(0)).getNamesByFlagSets(any()); verify(mEvaluator, times(0)).getTreatment(any(), any(), any(), anyMap()); @@ -341,7 +342,7 @@ public void getTreatmentsWithConfigByFlagSetReturnsCorrectFormat() { when(mSplitsStorage.getNamesByFlagSets(Collections.singleton("set_1"))).thenReturn(mockNames); mFlagSetsFilter = new FlagSetsFilterImpl(Collections.singleton("set_1")); - Map result = mTreatmentManager.getTreatmentsWithConfigByFlagSet("set_1", null, false); + Map result = mTreatmentManager.getTreatmentsWithConfigByFlagSet("set_1", null, null, false); assertEquals(2, result.size()); assertEquals("result_1", result.get("test_1").treatment()); @@ -352,7 +353,7 @@ public void getTreatmentsWithConfigByFlagSetReturnsCorrectFormat() { public void getTreatmentsWithConfigByFlagSetRecordsTelemetry() { when(mSplitsStorage.getNamesByFlagSets(Collections.singleton("set_1"))).thenReturn(Collections.singleton("test_1")); - mTreatmentManager.getTreatmentsWithConfigByFlagSet("set_1", null, false); + mTreatmentManager.getTreatmentsWithConfigByFlagSet("set_1", null, null, false); verify(mTelemetryStorageProducer).recordLatency(eq(Method.TREATMENTS_WITH_CONFIG_BY_FLAG_SET), anyLong()); } @@ -360,7 +361,7 @@ public void getTreatmentsWithConfigByFlagSetRecordsTelemetry() { /// @Test public void getTreatmentsWithConfigByFlagSetsDestroyedDoesNotUseEvaluator() { - mTreatmentManager.getTreatmentsWithConfigByFlagSets(Collections.singletonList("set_1"), null, true); + mTreatmentManager.getTreatmentsWithConfigByFlagSets(Collections.singletonList("set_1"), null, null, true); verify(mSplitsStorage, times(0)).getNamesByFlagSets(any()); verify(mEvaluator, times(0)).getTreatment(any(), any(), any(), anyMap()); @@ -371,7 +372,7 @@ public void getTreatmentsWithConfigByFlagSetsWithNoConfiguredSetsQueriesStorageA when(mSplitsStorage.getNamesByFlagSets(new HashSet<>(Arrays.asList("set_1", "set_2")))) .thenReturn(new HashSet<>(Arrays.asList("test_1", "test_2"))); - mTreatmentManager.getTreatmentsWithConfigByFlagSets(Arrays.asList("set_1", "set_2"), null, false); + mTreatmentManager.getTreatmentsWithConfigByFlagSets(Arrays.asList("set_1", "set_2"), null, null, false); verify(mSplitsStorage).getNamesByFlagSets(new HashSet<>(Arrays.asList("set_1", "set_2"))); verify(mEvaluator).getTreatment(anyString(), anyString(), eq("test_1"), anyMap()); @@ -383,7 +384,7 @@ public void getTreatmentsWithConfigByFlagSetsWithNoConfiguredSetsInvalidSetDoesN when(mSplitsStorage.getNamesByFlagSets(Collections.singleton("set_1"))) .thenReturn(new HashSet<>(Collections.singletonList("test_1"))); - mTreatmentManager.getTreatmentsWithConfigByFlagSets(Arrays.asList("set_1", "SET!"), null, false); + mTreatmentManager.getTreatmentsWithConfigByFlagSets(Arrays.asList("set_1", "SET!"), null, null, false); verify(mSplitsStorage).getNamesByFlagSets(Collections.singleton("set_1")); verify(mEvaluator).getTreatment(any(), any(), eq("test_1"), anyMap()); @@ -397,7 +398,7 @@ public void getTreatmentsWithConfigByFlagSetsWithConfiguredSetsExistingSetQuerie when(mSplitsStorage.getNamesByFlagSets(Collections.singleton("set_1"))) .thenReturn(new HashSet<>(Collections.singletonList("test_1"))); - mTreatmentManager.getTreatmentsWithConfigByFlagSets(Arrays.asList("set_1", "set_2"), null, false); + mTreatmentManager.getTreatmentsWithConfigByFlagSets(Arrays.asList("set_1", "set_2"), null, null, false); verify(mSplitsStorage).getNamesByFlagSets(Collections.singleton("set_1")); verify(mEvaluator).getTreatment(anyString(), anyString(), eq("test_1"), anyMap()); @@ -408,7 +409,7 @@ public void getTreatmentsWithConfigByFlagSetsWithConfiguredSetsNonExistingSetDoe mFlagSetsFilter = new FlagSetsFilterImpl(Collections.singleton("set_1")); initializeTreatmentManager(); - mTreatmentManager.getTreatmentsWithConfigByFlagSets(Arrays.asList("set_2", "set_3"), null, false); + mTreatmentManager.getTreatmentsWithConfigByFlagSets(Arrays.asList("set_2", "set_3"), null, null, false); verify(mSplitsStorage, times(0)).getNamesByFlagSets(any()); verify(mEvaluator, times(0)).getTreatment(any(), any(), any(), anyMap()); @@ -421,7 +422,7 @@ public void getTreatmentsWithConfigByFlagSetsReturnsCorrectFormat() { mockNames.add("test_2"); when(mSplitsStorage.getNamesByFlagSets(new HashSet<>(Arrays.asList("set_1", "set_2")))).thenReturn(mockNames); - Map result = mTreatmentManager.getTreatmentsWithConfigByFlagSets(Arrays.asList("set_1", "set_2"), null, false); + Map result = mTreatmentManager.getTreatmentsWithConfigByFlagSets(Arrays.asList("set_1", "set_2"), null, null, false); assertEquals(2, result.size()); assertEquals("result_1", result.get("test_1").treatment()); @@ -430,14 +431,14 @@ public void getTreatmentsWithConfigByFlagSetsReturnsCorrectFormat() { @Test public void getTreatmentsWithConfigByFlagSetsWithDuplicatedSetDeduplicates() { - mTreatmentManager.getTreatmentsWithConfigByFlagSets(Arrays.asList("set_1", "set_1"), null, false); + mTreatmentManager.getTreatmentsWithConfigByFlagSets(Arrays.asList("set_1", "set_1"), null, null, false); verify(mSplitsStorage).getNamesByFlagSets(Collections.singleton("set_1")); } @Test public void getTreatmentsWithConfigByFlagSetsWithNullSetListReturnsEmpty() { - Map result = mTreatmentManager.getTreatmentsWithConfigByFlagSets(null, null, false); + Map result = mTreatmentManager.getTreatmentsWithConfigByFlagSets(null, null, null, false); verify(mSplitsStorage, times(0)).getNamesByFlagSets(any()); verify(mEvaluator, times(0)).getTreatment(any(), any(), any(), anyMap()); @@ -448,7 +449,7 @@ public void getTreatmentsWithConfigByFlagSetsWithNullSetListReturnsEmpty() { public void getTreatmentsWithConfigByFlagSetsRecordsTelemetry() { when(mSplitsStorage.getNamesByFlagSets(Collections.singleton("set_1"))).thenReturn(Collections.singleton("test_1")); - mTreatmentManager.getTreatmentsWithConfigByFlagSets(Arrays.asList("set_1", "set_2"), null, false); + mTreatmentManager.getTreatmentsWithConfigByFlagSets(Arrays.asList("set_1", "set_2"), null, null, false); verify(mTelemetryStorageProducer).recordLatency(eq(Method.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS), anyLong()); } @@ -457,7 +458,7 @@ public void getTreatmentsWithConfigByFlagSetsRecordsTelemetry() { public void getTreatmentsByFlagSetExceptionIsRecordedInTelemetry() { when(mSplitsStorage.getNamesByFlagSets(any())).thenThrow(new RuntimeException("test")); - mTreatmentManager.getTreatmentsByFlagSet("set_1", null, false); + mTreatmentManager.getTreatmentsByFlagSet("set_1", null, null, false); verify(mTelemetryStorageProducer).recordException(eq(Method.TREATMENTS_BY_FLAG_SET)); } @@ -466,7 +467,7 @@ public void getTreatmentsByFlagSetExceptionIsRecordedInTelemetry() { public void getTreatmentsByFlagSetsExceptionIsRecordedInTelemetry() { when(mSplitsStorage.getNamesByFlagSets(any())).thenThrow(new RuntimeException("test")); - mTreatmentManager.getTreatmentsByFlagSets(Arrays.asList("set_1", "set_2"), null, false); + mTreatmentManager.getTreatmentsByFlagSets(Arrays.asList("set_1", "set_2"), null, null, false); verify(mTelemetryStorageProducer).recordException(eq(Method.TREATMENTS_BY_FLAG_SETS)); } @@ -475,7 +476,7 @@ public void getTreatmentsByFlagSetsExceptionIsRecordedInTelemetry() { public void getTreatmentsWithConfigByFlagSetExceptionIsRecordedInTelemetry() { when(mSplitsStorage.getNamesByFlagSets(any())).thenThrow(new RuntimeException("test")); - mTreatmentManager.getTreatmentsWithConfigByFlagSet("set_1", null, false); + mTreatmentManager.getTreatmentsWithConfigByFlagSet("set_1", null, null, false); verify(mTelemetryStorageProducer).recordException(eq(Method.TREATMENTS_WITH_CONFIG_BY_FLAG_SET)); } @@ -484,14 +485,14 @@ public void getTreatmentsWithConfigByFlagSetExceptionIsRecordedInTelemetry() { public void getTreatmentsWithConfigByFlagSetsExceptionIsRecordedInTelemetry() { when(mSplitsStorage.getNamesByFlagSets(any())).thenThrow(new RuntimeException("test")); - mTreatmentManager.getTreatmentsWithConfigByFlagSets(Arrays.asList("set_1", "set_2"), null, false); + mTreatmentManager.getTreatmentsWithConfigByFlagSets(Arrays.asList("set_1", "set_2"), null, null, false); verify(mTelemetryStorageProducer).recordException(eq(Method.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS)); } @Test public void getTreatmentsByFlagSetWithNullFlagSet() { - mTreatmentManager.getTreatmentsByFlagSet(null, null, false); + mTreatmentManager.getTreatmentsByFlagSet(null, null, null, false); verify(mSplitsStorage, times(0)).getNamesByFlagSets(any()); verify(mEvaluator, times(0)).getTreatment(any(), any(), any(), anyMap()); @@ -499,7 +500,7 @@ public void getTreatmentsByFlagSetWithNullFlagSet() { @Test public void getTreatmentsByFlagSetsWithNullFlagSets() { - mTreatmentManager.getTreatmentsByFlagSets(null, null, false); + mTreatmentManager.getTreatmentsByFlagSets(null, null, null, false); verify(mSplitsStorage, times(0)).getNamesByFlagSets(any()); verify(mEvaluator, times(0)).getTreatment(any(), any(), any(), anyMap()); @@ -507,7 +508,7 @@ public void getTreatmentsByFlagSetsWithNullFlagSets() { @Test public void getTreatmentsWithConfigByFlagSetWithNullFlagSet() { - mTreatmentManager.getTreatmentsWithConfigByFlagSet(null, null, false); + mTreatmentManager.getTreatmentsWithConfigByFlagSet(null, null, null, false); verify(mSplitsStorage, times(0)).getNamesByFlagSets(any()); verify(mEvaluator, times(0)).getTreatment(any(), any(), any(), anyMap()); @@ -515,7 +516,7 @@ public void getTreatmentsWithConfigByFlagSetWithNullFlagSet() { @Test public void getTreatmentsWithConfigByFlagSetsWithNullFlagSets() { - mTreatmentManager.getTreatmentsWithConfigByFlagSets(null, null, false); + mTreatmentManager.getTreatmentsWithConfigByFlagSets(null, null, null, false); verify(mSplitsStorage, times(0)).getNamesByFlagSets(any()); verify(mEvaluator, times(0)).getTreatment(any(), any(), any(), anyMap()); diff --git a/src/test/java/io/split/android/client/impressions/ImpressionLoggingTaskTest.java b/src/test/java/io/split/android/client/impressions/ImpressionLoggingTaskTest.java index 713f38c13..a3d7c8d0c 100644 --- a/src/test/java/io/split/android/client/impressions/ImpressionLoggingTaskTest.java +++ b/src/test/java/io/split/android/client/impressions/ImpressionLoggingTaskTest.java @@ -50,6 +50,6 @@ public void unsuccessfulExecutionDoesNotCrash() { } private static DecoratedImpression createImpression() { - return new DecoratedImpression(new Impression("key", "feature", "treatment", "on", 1402040204L, "label", 123123L, new HashMap<>()), true); + return new DecoratedImpression(new Impression("key", "feature", "treatment", "on", 1402040204L, "label", 123123L, new HashMap<>(), null), true); } } diff --git a/src/test/java/io/split/android/client/impressions/SyncImpressionListenerTest.java b/src/test/java/io/split/android/client/impressions/SyncImpressionListenerTest.java index 4131e445f..6a57fab16 100644 --- a/src/test/java/io/split/android/client/impressions/SyncImpressionListenerTest.java +++ b/src/test/java/io/split/android/client/impressions/SyncImpressionListenerTest.java @@ -44,6 +44,6 @@ public void errorWhileSubmittingTaskIsHandled() { } private static DecoratedImpression createImpression() { - return new DecoratedImpression(new Impression("key", "feature", "treatment", "on", 1402040204L, "label", 123123L, new HashMap<>()), true); + return new DecoratedImpression(new Impression("key", "feature", "treatment", "on", 1402040204L, "label", 123123L, new HashMap<>(), null), true); } } diff --git a/src/test/java/io/split/android/client/service/ImpressionHasherTest.java b/src/test/java/io/split/android/client/service/ImpressionHasherTest.java index 318a35117..291e82c1b 100644 --- a/src/test/java/io/split/android/client/service/ImpressionHasherTest.java +++ b/src/test/java/io/split/android/client/service/ImpressionHasherTest.java @@ -28,6 +28,7 @@ public void differentFeature() { System.currentTimeMillis(), "someLabel", 123L, + null, null); Long hash2 = ImpressionHasher.process(imp2); @@ -46,6 +47,7 @@ public void differentKey() { System.currentTimeMillis(), "someLabel", 123L, + null, null); Long hash2 = ImpressionHasher.process(imp2); @@ -65,6 +67,7 @@ public void differentChangeNumber() { System.currentTimeMillis(), "someLabel", 456L, + null, null); Long hash2 = ImpressionHasher.process(imp2); @@ -81,6 +84,7 @@ public void differentLabel() { System.currentTimeMillis(), "someOtherLabel", 123L, + null, null); Long hash2 = ImpressionHasher.process(imp2); @@ -97,6 +101,7 @@ public void differentTreatment() { System.currentTimeMillis(), "someLabel", 123L, + null, null); Long hash2 = ImpressionHasher.process(imp2); @@ -113,6 +118,7 @@ public void noCrashWhenSplitNull() { System.currentTimeMillis(), "someLabel", 123L, + null, null); Long hash = ImpressionHasher.process(imp1); @@ -130,6 +136,7 @@ public void noCrashWhenSplitAndKeyNull() { System.currentTimeMillis(), "someLabel", 123L, + null, null); Long hash = ImpressionHasher.process(imp1); @@ -147,6 +154,7 @@ public void noCrashWhenKeySplitChangeNumberNull() { System.currentTimeMillis(), "someLabel", null, + null, null); Long hash = ImpressionHasher.process(imp1); @@ -164,6 +172,7 @@ public void noCrashWhenKeySplitChangeNumberAppliedRuleNull() { System.currentTimeMillis(), null, null, + null, null); Long hash = ImpressionHasher.process(imp1); @@ -181,6 +190,7 @@ public void noCrashWhenOnlyAppliedRuleNotNull() { System.currentTimeMillis(), "someLabel", null, + null, null); Assert.assertNotNull(imp1); @@ -202,6 +212,7 @@ private Impression baseImpression() { System.currentTimeMillis(), "someLabel", 123L, + null, null); } } \ No newline at end of file diff --git a/src/test/java/io/split/android/client/service/SynchronizerTest.java b/src/test/java/io/split/android/client/service/SynchronizerTest.java index 79347551f..98a18c766 100644 --- a/src/test/java/io/split/android/client/service/SynchronizerTest.java +++ b/src/test/java/io/split/android/client/service/SynchronizerTest.java @@ -798,12 +798,12 @@ public void tearDown() { private DecoratedImpression createImpression() { return new DecoratedImpression(new Impression("key", "bkey", "split", "on", - 100L, "default rule", 999L, null), true); + 100L, "default rule", 999L, null, null), true); } private DecoratedImpression createUniqueImpression() { return new DecoratedImpression(new Impression("key", "bkey", UUID.randomUUID().toString(), "on", - 100L, "default rule", 999L, null), true); + 100L, "default rule", 999L, null, null), true); } private KeyImpression keyImpression(Impression impression) { diff --git a/src/test/java/io/split/android/client/service/impressions/strategy/NoneStrategyTest.kt b/src/test/java/io/split/android/client/service/impressions/strategy/NoneStrategyTest.kt index daaeb89aa..8acf6de77 100644 --- a/src/test/java/io/split/android/client/service/impressions/strategy/NoneStrategyTest.kt +++ b/src/test/java/io/split/android/client/service/impressions/strategy/NoneStrategyTest.kt @@ -93,5 +93,6 @@ fun createUniqueImpression( time, "default rule", 999L, + null, null ) diff --git a/src/test/java/io/split/android/client/service/sseclient/SplitUpdateWorkerTest.java b/src/test/java/io/split/android/client/service/sseclient/SplitUpdateWorkerTest.java index 03de536c7..0d8cbe339 100644 --- a/src/test/java/io/split/android/client/service/sseclient/SplitUpdateWorkerTest.java +++ b/src/test/java/io/split/android/client/service/sseclient/SplitUpdateWorkerTest.java @@ -131,8 +131,7 @@ public void lowerChangeNumberThanStoredDoesNothing() { @Test public void nullPreviousChangeNumberDoesNothing() { when(mSplitsStorage.getTill()).thenReturn(1000L); - SplitsChangeNotification notification = getNewNotification(); - when(notification.getPreviousChangeNumber()).thenReturn(null); + SplitsChangeNotification notification = getNewNotification(null); mNotificationsQueue.offer(notification); verify(mSynchronizer, never()) @@ -142,8 +141,7 @@ public void nullPreviousChangeNumberDoesNothing() { @Test public void zeroPreviousChangeNumberDoesNothing() { when(mSplitsStorage.getTill()).thenReturn(1000L); - SplitsChangeNotification notification = getNewNotification(); - when(notification.getPreviousChangeNumber()).thenReturn(0L); + SplitsChangeNotification notification = getNewNotification(0L); mNotificationsQueue.offer(notification); verify(mSynchronizer, never()) @@ -167,7 +165,7 @@ public void newNotificationSubmitsTaskInExecutor() throws InterruptedException { long changeNumber = 1000L; byte[] bytes = TEST_SPLIT.getBytes(); SplitInPlaceUpdateTask updateTask = mock(SplitInPlaceUpdateTask.class); - SplitsChangeNotification notification = getNewNotification(); + SplitsChangeNotification notification = getNewNotification(2000L); CompressionUtil mockCompressor = mock(CompressionUtil.class); when(mSplitTaskFactory.createSplitsUpdateTask(any(), anyLong())).thenReturn(updateTask); @@ -193,7 +191,7 @@ public void synchronizeSplitsIsCalledOnSynchronizerWhenTaskFails() throws Interr long changeNumber = 1000L; SplitInPlaceUpdateTask updateTask = mock(SplitInPlaceUpdateTask.class); - SplitsChangeNotification notification = getNewNotification(); + SplitsChangeNotification notification = getNewNotification(2000L); CompressionUtil mockCompressor = mock(CompressionUtil.class); when(updateTask.execute()).thenAnswer(invocation -> SplitTaskExecutionInfo.error(SplitTaskType.SPLITS_SYNC)); @@ -216,7 +214,7 @@ public void synchronizeSplitsIsCalledOnSynchronizerWhenParsingFails() throws Int long changeNumber = 1000L; SplitInPlaceUpdateTask updateTask = mock(SplitInPlaceUpdateTask.class); - SplitsChangeNotification notification = getNewNotification(); + SplitsChangeNotification notification = getNewNotification(2000L); CompressionUtil mockCompressor = mock(CompressionUtil.class); when(mSplitTaskFactory.createSplitsUpdateTask(any(), anyLong())).thenReturn(updateTask); @@ -236,7 +234,7 @@ public void synchronizeSplitsIsCalledOnSynchronizerWhenParsingFails() throws Int public void synchronizeSplitsIsCalledOnSynchronizerWhenDecompressingFailsDueToException() throws InterruptedException { long changeNumber = 1000L; SplitInPlaceUpdateTask updateTask = mock(SplitInPlaceUpdateTask.class); - SplitsChangeNotification notification = getNewNotification(); + SplitsChangeNotification notification = getNewNotification(2000L); CompressionUtil mockCompressor = mock(CompressionUtil.class); when(mSplitTaskFactory.createSplitsUpdateTask(any(), anyLong())).thenReturn(updateTask); @@ -256,7 +254,7 @@ public void synchronizeSplitsIsCalledOnSynchronizerWhenDecompressingFailsDueToEx public void synchronizeSplitsIsCalledOnSynchronizerWhenDecompressingFailsDueToNullDecompressor() throws InterruptedException { long changeNumber = 1000L; SplitInPlaceUpdateTask updateTask = mock(SplitInPlaceUpdateTask.class); - SplitsChangeNotification notification = getNewNotification(); + SplitsChangeNotification notification = getNewNotification(2000L); when(mSplitTaskFactory.createSplitsUpdateTask(any(), anyLong())).thenReturn(updateTask); when(mSplitsStorage.getTill()).thenReturn(changeNumber); @@ -274,7 +272,7 @@ public void synchronizeSplitsIsCalledOnSynchronizerWhenDecompressingFailsDueToNu public void synchronizeSplitsIsCalledOnSynchronizerWhenDecompressingFailsDueToNullDecompressedBytes() throws InterruptedException { long changeNumber = 1000L; SplitInPlaceUpdateTask updateTask = mock(SplitInPlaceUpdateTask.class); - SplitsChangeNotification notification = getNewNotification(); + SplitsChangeNotification notification = getNewNotification(2000L); CompressionUtil mockCompressor = mock(CompressionUtil.class); when(mSplitTaskFactory.createSplitsUpdateTask(any(), anyLong())).thenReturn(updateTask); @@ -294,7 +292,7 @@ public void synchronizeSplitsIsCalledOnSynchronizerWhenDecompressingFailsDueToNu public void synchronizeSplitsIsCalledOnSynchronizerWhenDecompressingFailsDueToFailedBase64Decoding() throws InterruptedException { long changeNumber = 1000L; SplitInPlaceUpdateTask updateTask = mock(SplitInPlaceUpdateTask.class); - SplitsChangeNotification notification = getNewNotification(); + SplitsChangeNotification notification = getNewNotification(2000L); CompressionUtil mockCompressor = mock(CompressionUtil.class); when(mSplitTaskFactory.createSplitsUpdateTask(any(), anyLong())).thenReturn(updateTask); @@ -323,18 +321,18 @@ private void initWorkerWithStubExecutor() { mWorker.start(); } - private static SplitsChangeNotification getLegacyNotification() { + private synchronized static SplitsChangeNotification getLegacyNotification() { SplitsChangeNotification mock = mock(SplitsChangeNotification.class); when(mock.getChangeNumber()).thenReturn(1000L); return mock; } - private static SplitsChangeNotification getNewNotification() { + private synchronized static SplitsChangeNotification getNewNotification(Long changeNumber) { SplitsChangeNotification mock = mock(SplitsChangeNotification.class); when(mock.getCompressionType()).thenReturn(CompressionType.ZLIB); when(mock.getData()).thenReturn(TEST_SPLIT); when(mock.getPreviousChangeNumber()).thenReturn(1000L); - when(mock.getChangeNumber()).thenReturn(2000L); + when(mock.getChangeNumber()).thenReturn(changeNumber == null ? 0 : changeNumber); return mock; } }