diff --git a/src/androidTest/java/fake/SyncManagerStub.java b/src/androidTest/java/fake/SyncManagerStub.java index de24ad9cb..179a8461f 100644 --- a/src/androidTest/java/fake/SyncManagerStub.java +++ b/src/androidTest/java/fake/SyncManagerStub.java @@ -1,7 +1,7 @@ package fake; import io.split.android.client.dtos.Event; -import io.split.android.client.impressions.Impression; +import io.split.android.client.impressions.DecoratedImpression; import io.split.android.client.service.synchronizer.SyncManager; import io.split.android.client.shared.UserConsent; @@ -42,6 +42,6 @@ public void pushEvent(Event event) { } @Override - public void pushImpression(Impression impression) { + public void pushImpression(DecoratedImpression impression) { } } diff --git a/src/androidTest/java/fake/SynchronizerSpyImpl.java b/src/androidTest/java/fake/SynchronizerSpyImpl.java index 2203f3913..81a1c8fdd 100644 --- a/src/androidTest/java/fake/SynchronizerSpyImpl.java +++ b/src/androidTest/java/fake/SynchronizerSpyImpl.java @@ -5,7 +5,7 @@ import io.split.android.client.api.Key; import io.split.android.client.dtos.Event; -import io.split.android.client.impressions.Impression; +import io.split.android.client.impressions.DecoratedImpression; import io.split.android.client.service.synchronizer.Synchronizer; import io.split.android.client.service.synchronizer.SynchronizerSpy; import io.split.android.client.service.synchronizer.attributes.AttributesSynchronizer; @@ -88,7 +88,7 @@ public void pushEvent(Event event) { } @Override - public void pushImpression(Impression impression) { + public void pushImpression(DecoratedImpression impression) { mSynchronizer.pushImpression(impression); } diff --git a/src/main/java/io/split/android/client/EvaluationResult.java b/src/main/java/io/split/android/client/EvaluationResult.java index f5ecebc80..d7f16ec4c 100644 --- a/src/main/java/io/split/android/client/EvaluationResult.java +++ b/src/main/java/io/split/android/client/EvaluationResult.java @@ -1,24 +1,33 @@ package io.split.android.client; +import androidx.annotation.VisibleForTesting; + public final class EvaluationResult { private final String mTreatment; private final String mLabel; private final Long mChangeNumber; private final String mConfigurations; + private final boolean mTrackImpression; + @VisibleForTesting public EvaluationResult(String treatment, String label) { - this(treatment, label, null); + this(treatment, label, null, null, true); + } + + public EvaluationResult(String treatment, String label, boolean trackImpression) { + this(treatment, label, null, null, trackImpression); } - EvaluationResult(String treatment, String label, Long changeNumber) { - this(treatment, label, changeNumber, null); + EvaluationResult(String treatment, String label, Long changeNumber, boolean trackImpression) { + this(treatment, label, changeNumber, null, trackImpression); } - public EvaluationResult(String treatment, String label, Long changeNumber, String configurations) { + public EvaluationResult(String treatment, String label, Long changeNumber, String configurations, boolean trackImpression) { mTreatment = treatment; mLabel = label; mChangeNumber = changeNumber; mConfigurations = configurations; + mTrackImpression = trackImpression; } public String getTreatment() { @@ -36,4 +45,8 @@ public Long getChangeNumber() { public String getConfigurations() { return mConfigurations; } + + public boolean getTrackImpression() { + return mTrackImpression; + } } diff --git a/src/main/java/io/split/android/client/EvaluatorImpl.java b/src/main/java/io/split/android/client/EvaluatorImpl.java index ea15ab796..33c0eac9b 100644 --- a/src/main/java/io/split/android/client/EvaluatorImpl.java +++ b/src/main/java/io/split/android/client/EvaluatorImpl.java @@ -28,16 +28,16 @@ public EvaluationResult getTreatment(String matchingKey, String bucketingKey, St try { ParsedSplit parsedSplit = mSplitParser.parse(mSplitsStorage.get(splitName), matchingKey); if (parsedSplit == null) { - return new EvaluationResult(Treatments.CONTROL, TreatmentLabels.DEFINITION_NOT_FOUND); + return new EvaluationResult(Treatments.CONTROL, TreatmentLabels.DEFINITION_NOT_FOUND, true); } return getTreatment(matchingKey, bucketingKey, parsedSplit, attributes); } catch (ChangeNumberExceptionWrapper ex) { Logger.e(ex, "Catch Change Number Exception"); - return new EvaluationResult(Treatments.CONTROL, TreatmentLabels.EXCEPTION, ex.changeNumber()); + return new EvaluationResult(Treatments.CONTROL, TreatmentLabels.EXCEPTION, ex.changeNumber(), true); } catch (Exception e) { Logger.e(e, "Catch All Exception"); - return new EvaluationResult(Treatments.CONTROL, TreatmentLabels.EXCEPTION); + return new EvaluationResult(Treatments.CONTROL, TreatmentLabels.EXCEPTION, true); } } @@ -52,7 +52,7 @@ public EvaluationResult getTreatment(String matchingKey, String bucketingKey, St private EvaluationResult getTreatment(String matchingKey, String bucketingKey, ParsedSplit parsedSplit, Map attributes) throws ChangeNumberExceptionWrapper { try { if (parsedSplit.killed()) { - return new EvaluationResult(parsedSplit.defaultTreatment(), TreatmentLabels.KILLED, parsedSplit.changeNumber(), configForTreatment(parsedSplit, parsedSplit.defaultTreatment())); + return new EvaluationResult(parsedSplit.defaultTreatment(), TreatmentLabels.KILLED, parsedSplit.changeNumber(), configForTreatment(parsedSplit, parsedSplit.defaultTreatment()), parsedSplit.trackImpressions()); } /* @@ -75,7 +75,7 @@ private EvaluationResult getTreatment(String matchingKey, String bucketingKey, P if (bucket > parsedSplit.trafficAllocation()) { // out of split - return new EvaluationResult(parsedSplit.defaultTreatment(), TreatmentLabels.NOT_IN_SPLIT, parsedSplit.changeNumber(), configForTreatment(parsedSplit, parsedSplit.defaultTreatment())); + return new EvaluationResult(parsedSplit.defaultTreatment(), TreatmentLabels.NOT_IN_SPLIT, parsedSplit.changeNumber(), configForTreatment(parsedSplit, parsedSplit.defaultTreatment()), parsedSplit.trackImpressions()); } } @@ -84,11 +84,11 @@ private EvaluationResult getTreatment(String matchingKey, String bucketingKey, P if (parsedCondition.matcher().match(matchingKey, bucketingKey, attributes, this)) { String treatment = Splitter.getTreatment(bk, parsedSplit.seed(), parsedCondition.partitions(), parsedSplit.algo()); - return new EvaluationResult(treatment, parsedCondition.label(), parsedSplit.changeNumber(), configForTreatment(parsedSplit, treatment)); + return new EvaluationResult(treatment, parsedCondition.label(), parsedSplit.changeNumber(), configForTreatment(parsedSplit, treatment), parsedSplit.trackImpressions()); } } - return new EvaluationResult(parsedSplit.defaultTreatment(), TreatmentLabels.DEFAULT_RULE, parsedSplit.changeNumber(), configForTreatment(parsedSplit, parsedSplit.defaultTreatment())); + return new EvaluationResult(parsedSplit.defaultTreatment(), TreatmentLabels.DEFAULT_RULE, parsedSplit.changeNumber(), configForTreatment(parsedSplit, parsedSplit.defaultTreatment()), parsedSplit.trackImpressions()); } catch (Exception e) { throw new ChangeNumberExceptionWrapper(e, parsedSplit.changeNumber()); } diff --git a/src/main/java/io/split/android/client/SplitClientFactoryImpl.java b/src/main/java/io/split/android/client/SplitClientFactoryImpl.java index 0956c1788..544aab48b 100644 --- a/src/main/java/io/split/android/client/SplitClientFactoryImpl.java +++ b/src/main/java/io/split/android/client/SplitClientFactoryImpl.java @@ -17,9 +17,9 @@ import io.split.android.client.service.mysegments.MySegmentsTaskFactory; import io.split.android.client.service.synchronizer.SyncManager; import io.split.android.client.shared.SplitClientContainer; -import io.split.android.client.storage.common.SplitStorageContainer; import io.split.android.client.storage.attributes.AttributesStorage; import io.split.android.client.storage.attributes.PersistentAttributesStorage; +import io.split.android.client.storage.common.SplitStorageContainer; import io.split.android.client.storage.splits.SplitsStorage; import io.split.android.client.telemetry.TelemetrySynchronizer; import io.split.android.client.telemetry.storage.TelemetryInitProducer; @@ -43,7 +43,7 @@ public class SplitClientFactoryImpl implements SplitClientFactory { private final SplitParser mSplitParser; private final AttributesManagerFactory mAttributesManagerFactory; private final TreatmentManagerFactory mTreatmentManagerFactory; - private final ImpressionListener mCustomerImpressionListener; + private final ImpressionListener.FederatedImpressionListener mCustomerImpressionListener; private final SplitValidatorImpl mSplitValidator; private final EventsTracker mEventsTracker; @@ -57,7 +57,7 @@ public SplitClientFactoryImpl(@NonNull SplitFactory splitFactory, @NonNull ValidationMessageLogger validationLogger, @NonNull KeyValidator keyValidator, @NonNull EventsTracker eventsTracker, - @NonNull ImpressionListener customerImpressionListener, + @NonNull ImpressionListener.FederatedImpressionListener customerImpressionListener, @Nullable FlagSetsFilter flagSetsFilter) { mSplitFactory = checkNotNull(splitFactory); mClientContainer = checkNotNull(clientContainer); diff --git a/src/main/java/io/split/android/client/SplitFactoryImpl.java b/src/main/java/io/split/android/client/SplitFactoryImpl.java index 058c1139d..32ea3963c 100644 --- a/src/main/java/io/split/android/client/SplitFactoryImpl.java +++ b/src/main/java/io/split/android/client/SplitFactoryImpl.java @@ -20,6 +20,7 @@ import io.split.android.client.events.EventsManagerCoordinator; import io.split.android.client.factory.FactoryMonitor; import io.split.android.client.factory.FactoryMonitorImpl; +import io.split.android.client.impressions.DecoratedImpressionListener; import io.split.android.client.impressions.ImpressionListener; import io.split.android.client.impressions.SyncImpressionListener; import io.split.android.client.lifecycle.SplitLifecycleManager; @@ -243,17 +244,16 @@ private SplitFactoryImpl(@NonNull String apiToken, @NonNull Key key, @NonNull Sp } ExecutorService impressionsLoggingTaskExecutor = factoryHelper.getImpressionsLoggingTaskExecutor(); - final ImpressionListener splitImpressionListener + final DecoratedImpressionListener splitImpressionListener = new SyncImpressionListener(mSyncManager, impressionsLoggingTaskExecutor); - final ImpressionListener customerImpressionListener; + final ImpressionListener.FederatedImpressionListener customerImpressionListener; + List impressionListeners = new ArrayList<>(); if (config.impressionListener() != null) { - List impressionListeners = new ArrayList<>(); - impressionListeners.add(splitImpressionListener); impressionListeners.add(config.impressionListener()); - customerImpressionListener = new ImpressionListener.FederatedImpressionListener(impressionListeners); + customerImpressionListener = new ImpressionListener.FederatedImpressionListener(splitImpressionListener, impressionListeners); } else { - customerImpressionListener = splitImpressionListener; + customerImpressionListener = new ImpressionListener.FederatedImpressionListener(splitImpressionListener, impressionListeners); } EventsTracker eventsTracker = buildEventsTracker(); mUserConsentManager = new UserConsentManagerImpl(config, diff --git a/src/main/java/io/split/android/client/impressions/DecoratedImpression.java b/src/main/java/io/split/android/client/impressions/DecoratedImpression.java new file mode 100644 index 000000000..ecfb0e03f --- /dev/null +++ b/src/main/java/io/split/android/client/impressions/DecoratedImpression.java @@ -0,0 +1,20 @@ +package io.split.android.client.impressions; + +public class DecoratedImpression { + + private final Impression mImpression; + private final boolean mTrackImpressions; + + public DecoratedImpression(Impression impression, boolean trackImpressions) { + mImpression = impression; + mTrackImpressions = trackImpressions; + } + + public Impression getImpression() { + return mImpression; + } + + public boolean getTrackImpressions() { + return mTrackImpressions; + } +} diff --git a/src/main/java/io/split/android/client/impressions/DecoratedImpressionListener.java b/src/main/java/io/split/android/client/impressions/DecoratedImpressionListener.java new file mode 100644 index 000000000..e063128c0 --- /dev/null +++ b/src/main/java/io/split/android/client/impressions/DecoratedImpressionListener.java @@ -0,0 +1,6 @@ +package io.split.android.client.impressions; + +public interface DecoratedImpressionListener { + + void log(DecoratedImpression impression); +} diff --git a/src/main/java/io/split/android/client/impressions/ImpressionListener.java b/src/main/java/io/split/android/client/impressions/ImpressionListener.java index 7db48a401..7f87e8d95 100644 --- a/src/main/java/io/split/android/client/impressions/ImpressionListener.java +++ b/src/main/java/io/split/android/client/impressions/ImpressionListener.java @@ -19,22 +19,29 @@ public interface ImpressionListener { */ void close(); - final class NoopImpressionListener implements ImpressionListener { + final class NoopImpressionListener implements ImpressionListener, DecoratedImpressionListener { @Override public void log(Impression impression) { // noop } + @Override + public void log(DecoratedImpression impression) { + + } + @Override public void close() { // noop } } - final class FederatedImpressionListener implements ImpressionListener { - private List _delegates; + final class FederatedImpressionListener implements ImpressionListener, DecoratedImpressionListener { + private final DecoratedImpressionListener mDecoratedImpressionListener; + private final List _delegates; - public FederatedImpressionListener(List delegates) { + public FederatedImpressionListener(DecoratedImpressionListener decoratedImpressionListener, List delegates) { + mDecoratedImpressionListener = decoratedImpressionListener; _delegates = delegates; } @@ -45,6 +52,11 @@ public void log(Impression impression) { } } + @Override + public void log(DecoratedImpression impression) { + mDecoratedImpressionListener.log(impression); + } + @Override public void close() { for (ImpressionListener listener : _delegates) { diff --git a/src/main/java/io/split/android/client/impressions/ImpressionLoggingTask.java b/src/main/java/io/split/android/client/impressions/ImpressionLoggingTask.java index ff80c8530..8ca6bc3b4 100644 --- a/src/main/java/io/split/android/client/impressions/ImpressionLoggingTask.java +++ b/src/main/java/io/split/android/client/impressions/ImpressionLoggingTask.java @@ -10,10 +10,10 @@ class ImpressionLoggingTask implements Runnable { private final SyncManager mSyncManager; - private final Impression mImpression; + private final DecoratedImpression mImpression; ImpressionLoggingTask(@NonNull SyncManager syncManager, - Impression impression) { + DecoratedImpression impression) { mSyncManager = checkNotNull(syncManager); mImpression = impression; } diff --git a/src/main/java/io/split/android/client/impressions/SyncImpressionListener.java b/src/main/java/io/split/android/client/impressions/SyncImpressionListener.java index 6bdf4fa14..147e940d2 100644 --- a/src/main/java/io/split/android/client/impressions/SyncImpressionListener.java +++ b/src/main/java/io/split/android/client/impressions/SyncImpressionListener.java @@ -9,7 +9,7 @@ import io.split.android.client.service.synchronizer.SyncManager; import io.split.android.client.utils.logger.Logger; -public class SyncImpressionListener implements ImpressionListener { +public class SyncImpressionListener implements DecoratedImpressionListener { private final SyncManager mSyncManager; private final ExecutorService mExecutorService; @@ -21,15 +21,11 @@ public SyncImpressionListener(@NonNull SyncManager syncManager, } @Override - public void log(Impression impression) { + public void log(DecoratedImpression impression) { try { mExecutorService.submit(new ImpressionLoggingTask(mSyncManager, impression)); } catch (Exception ex) { Logger.w("Error submitting impression logging task: " + ex.getLocalizedMessage()); } } - - @Override - public void close() { - } } diff --git a/src/main/java/io/split/android/client/localhost/LocalhostImpressionsListener.java b/src/main/java/io/split/android/client/localhost/LocalhostImpressionsListener.java index af7e053ca..846dd6a83 100644 --- a/src/main/java/io/split/android/client/localhost/LocalhostImpressionsListener.java +++ b/src/main/java/io/split/android/client/localhost/LocalhostImpressionsListener.java @@ -1,13 +1,19 @@ package io.split.android.client.localhost; +import io.split.android.client.impressions.DecoratedImpression; +import io.split.android.client.impressions.DecoratedImpressionListener; import io.split.android.client.impressions.Impression; import io.split.android.client.impressions.ImpressionListener; -public class LocalhostImpressionsListener implements ImpressionListener { +public class LocalhostImpressionsListener implements ImpressionListener, DecoratedImpressionListener { @Override public void log(Impression impression) { } + @Override + public void log(DecoratedImpression impression) { + } + @Override public void close() { } 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 5c3d31e64..77c394308 100644 --- a/src/main/java/io/split/android/client/localhost/LocalhostSplitClient.java +++ b/src/main/java/io/split/android/client/localhost/LocalhostSplitClient.java @@ -263,11 +263,11 @@ public boolean track(String eventType, double value, Map propert return false; } - private ImpressionListener getImpressionsListener(SplitClientConfig config) { + private ImpressionListener.FederatedImpressionListener getImpressionsListener(SplitClientConfig config) { if (config.impressionListener() != null) { - return config.impressionListener(); + return new ImpressionListener.FederatedImpressionListener(new ImpressionListener.NoopImpressionListener(), Collections.singletonList(config.impressionListener())); } else { - return new LocalhostImpressionsListener(); + return new ImpressionListener.FederatedImpressionListener(new ImpressionListener.NoopImpressionListener(), Collections.emptyList()); } } diff --git a/src/main/java/io/split/android/client/service/impressions/ImpressionManager.java b/src/main/java/io/split/android/client/service/impressions/ImpressionManager.java index ec990e2a6..511536d06 100644 --- a/src/main/java/io/split/android/client/service/impressions/ImpressionManager.java +++ b/src/main/java/io/split/android/client/service/impressions/ImpressionManager.java @@ -1,10 +1,10 @@ package io.split.android.client.service.impressions; -import io.split.android.client.impressions.Impression; +import io.split.android.client.impressions.DecoratedImpression; public interface ImpressionManager { void enableTracking(boolean enable); - void pushImpression(Impression impression); + void pushImpression(DecoratedImpression impression); } diff --git a/src/main/java/io/split/android/client/service/impressions/StrategyImpressionManager.java b/src/main/java/io/split/android/client/service/impressions/StrategyImpressionManager.java index 5eb2a104e..d291cc875 100644 --- a/src/main/java/io/split/android/client/service/impressions/StrategyImpressionManager.java +++ b/src/main/java/io/split/android/client/service/impressions/StrategyImpressionManager.java @@ -8,7 +8,7 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; -import io.split.android.client.impressions.Impression; +import io.split.android.client.impressions.DecoratedImpression; import io.split.android.client.service.impressions.strategy.PeriodicTracker; import io.split.android.client.service.impressions.strategy.ProcessStrategy; import io.split.android.client.utils.logger.Logger; @@ -33,16 +33,16 @@ public StrategyImpressionManager(Pair noneComp } @Override - public void pushImpression(Impression impression) { + public void pushImpression(DecoratedImpression impression) { if (!isTrackingEnabled.get()) { Logger.v("Impression not tracked because tracking is disabled"); return; } - if (track(impression)) { - mProcessStrategy.apply(impression); + if (impression.getTrackImpressions()) { + mProcessStrategy.apply(impression.getImpression()); } else { - mNoneStrategy.apply(impression); + mNoneStrategy.apply(impression.getImpression()); } } @@ -71,8 +71,4 @@ public void stopPeriodicRecording() { tracker.stopPeriodicRecording(); } } - - private static boolean track(Impression impression) { - return true; // TODO: Placeholder method - } } diff --git a/src/main/java/io/split/android/client/service/synchronizer/SyncManager.java b/src/main/java/io/split/android/client/service/synchronizer/SyncManager.java index 200862b3f..bb5904220 100644 --- a/src/main/java/io/split/android/client/service/synchronizer/SyncManager.java +++ b/src/main/java/io/split/android/client/service/synchronizer/SyncManager.java @@ -1,7 +1,7 @@ package io.split.android.client.service.synchronizer; import io.split.android.client.dtos.Event; -import io.split.android.client.impressions.Impression; +import io.split.android.client.impressions.DecoratedImpression; import io.split.android.client.lifecycle.SplitLifecycleAware; import io.split.android.client.shared.UserConsent; @@ -12,7 +12,7 @@ public interface SyncManager extends SplitLifecycleAware { void pushEvent(Event event); - void pushImpression(Impression impression); + void pushImpression(DecoratedImpression impression); void stop(); diff --git a/src/main/java/io/split/android/client/service/synchronizer/SyncManagerImpl.java b/src/main/java/io/split/android/client/service/synchronizer/SyncManagerImpl.java index 1a96ebff2..e96693539 100644 --- a/src/main/java/io/split/android/client/service/synchronizer/SyncManagerImpl.java +++ b/src/main/java/io/split/android/client/service/synchronizer/SyncManagerImpl.java @@ -9,6 +9,7 @@ import io.split.android.client.SplitClientConfig; import io.split.android.client.dtos.Event; +import io.split.android.client.impressions.DecoratedImpression; import io.split.android.client.impressions.Impression; import io.split.android.client.service.executor.SplitTask; import io.split.android.client.service.executor.SplitTaskExecutionInfo; @@ -163,7 +164,7 @@ public void pushEvent(Event event) { } @Override - public void pushImpression(Impression impression) { + public void pushImpression(DecoratedImpression impression) { mSynchronizer.pushImpression(impression); } diff --git a/src/main/java/io/split/android/client/service/synchronizer/Synchronizer.java b/src/main/java/io/split/android/client/service/synchronizer/Synchronizer.java index 27adf8d38..b16331b83 100644 --- a/src/main/java/io/split/android/client/service/synchronizer/Synchronizer.java +++ b/src/main/java/io/split/android/client/service/synchronizer/Synchronizer.java @@ -1,7 +1,7 @@ package io.split.android.client.service.synchronizer; import io.split.android.client.dtos.Event; -import io.split.android.client.impressions.Impression; +import io.split.android.client.impressions.DecoratedImpression; import io.split.android.client.lifecycle.SplitLifecycleAware; public interface Synchronizer extends SplitLifecycleAware { @@ -28,7 +28,7 @@ public interface Synchronizer extends SplitLifecycleAware { void pushEvent(Event event); - void pushImpression(Impression impression); + void pushImpression(DecoratedImpression impression); void flush(); diff --git a/src/main/java/io/split/android/client/service/synchronizer/SynchronizerImpl.java b/src/main/java/io/split/android/client/service/synchronizer/SynchronizerImpl.java index 0cdfe304e..930f5bd9b 100644 --- a/src/main/java/io/split/android/client/service/synchronizer/SynchronizerImpl.java +++ b/src/main/java/io/split/android/client/service/synchronizer/SynchronizerImpl.java @@ -13,6 +13,7 @@ import io.split.android.client.api.Key; import io.split.android.client.dtos.Event; import io.split.android.client.events.ISplitEventsManager; +import io.split.android.client.impressions.DecoratedImpression; import io.split.android.client.impressions.Impression; import io.split.android.client.service.ServiceConstants; import io.split.android.client.service.executor.SplitTaskExecutionInfo; @@ -255,7 +256,7 @@ public void pushEvent(Event event) { } @Override - public void pushImpression(Impression impression) { + public void pushImpression(DecoratedImpression impression) { mImpressionManager.pushImpression(impression); } diff --git a/src/main/java/io/split/android/client/shared/SplitClientContainerImpl.java b/src/main/java/io/split/android/client/shared/SplitClientContainerImpl.java index 9d50f645d..a49788bf5 100644 --- a/src/main/java/io/split/android/client/shared/SplitClientContainerImpl.java +++ b/src/main/java/io/split/android/client/shared/SplitClientContainerImpl.java @@ -67,7 +67,7 @@ public SplitClientContainerImpl(@NonNull String defaultMatchingKey, @NonNull SplitApiFacade splitApiFacade, @NonNull ValidationMessageLogger validationLogger, @NonNull KeyValidator keyValidator, - @NonNull ImpressionListener customerImpressionListener, + @NonNull ImpressionListener.FederatedImpressionListener customerImpressionListener, @Nullable PushNotificationManager pushNotificationManager, @NonNull ClientComponentsRegister clientComponentsRegister, @NonNull MySegmentsWorkManagerWrapper workManagerWrapper, 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 770461b09..3fce796d9 100644 --- a/src/main/java/io/split/android/client/validators/TreatmentManagerFactoryImpl.java +++ b/src/main/java/io/split/android/client/validators/TreatmentManagerFactoryImpl.java @@ -21,7 +21,7 @@ public class TreatmentManagerFactoryImpl implements TreatmentManagerFactory { private final KeyValidator mKeyValidator; private final SplitValidator mSplitValidator; - private final ImpressionListener mCustomerImpressionListener; + private final ImpressionListener.FederatedImpressionListener mCustomerImpressionListener; private final boolean mLabelsEnabled; private final AttributesMerger mAttributesMerger; private final TelemetryStorageProducer mTelemetryStorageProducer; @@ -33,7 +33,7 @@ public class TreatmentManagerFactoryImpl implements TreatmentManagerFactory { public TreatmentManagerFactoryImpl(@NonNull KeyValidator keyValidator, @NonNull SplitValidator splitValidator, - @NonNull ImpressionListener customerImpressionListener, + @NonNull ImpressionListener.FederatedImpressionListener customerImpressionListener, boolean labelsEnabled, @NonNull AttributesMerger attributesMerger, @NonNull TelemetryStorageProducer telemetryStorageProducer, 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 a88e81747..2adc8ace8 100644 --- a/src/main/java/io/split/android/client/validators/TreatmentManagerImpl.java +++ b/src/main/java/io/split/android/client/validators/TreatmentManagerImpl.java @@ -21,6 +21,7 @@ import io.split.android.client.attributes.AttributesMerger; import io.split.android.client.events.ListenableEventsManager; import io.split.android.client.events.SplitEvent; +import io.split.android.client.impressions.DecoratedImpression; import io.split.android.client.impressions.Impression; import io.split.android.client.impressions.ImpressionListener; import io.split.android.client.storage.splits.SplitsStorage; @@ -34,7 +35,7 @@ public class TreatmentManagerImpl implements TreatmentManager { private final Evaluator mEvaluator; private final KeyValidator mKeyValidator; private final SplitValidator mSplitValidator; - private final ImpressionListener mImpressionListener; + private final ImpressionListener.FederatedImpressionListener mImpressionListener; private final String mMatchingKey; private final String mBucketingKey; private final boolean mLabelsEnabled; @@ -54,7 +55,7 @@ public TreatmentManagerImpl(String matchingKey, Evaluator evaluator, KeyValidator keyValidator, SplitValidator splitValidator, - ImpressionListener impressionListener, + ImpressionListener.FederatedImpressionListener impressionListener, boolean labelsEnabled, ListenableEventsManager eventsManager, @NonNull AttributesManager attributesManager, @@ -294,7 +295,8 @@ private TreatmentResult getTreatmentWithConfigWithoutMetrics(String split, Map attributes) { + private void logImpression(String matchingKey, String bucketingKey, String splitName, String result, String label, Long changeNumber, Map attributes, boolean trackImpression) { try { - mImpressionListener.log(new Impression(matchingKey, bucketingKey, splitName, result, System.currentTimeMillis(), label, changeNumber, attributes)); + Impression impression = new Impression(matchingKey, bucketingKey, splitName, result, System.currentTimeMillis(), label, changeNumber, attributes); + DecoratedImpression decoratedImpression = new DecoratedImpression(impression, trackImpression); + mImpressionListener.log(decoratedImpression); + mImpressionListener.log(impression); } catch (Throwable t) { Logger.e("An error occurred logging impression: " + t.getLocalizedMessage()); } @@ -339,7 +345,7 @@ private EvaluationResult evaluateIfReady(String featureFlagName, mValidationLogger.w("the SDK is not ready, results may be incorrect for feature flag " + featureFlagName + ". Make sure to wait for SDK readiness before using this method", validationTag); mTelemetryStorageProducer.recordNonReadyUsage(); - return new EvaluationResult(Treatments.CONTROL, TreatmentLabels.NOT_READY, null, null); + return new EvaluationResult(Treatments.CONTROL, TreatmentLabels.NOT_READY, null, null, true); } return mEvaluator.getTreatment(mMatchingKey, mBucketingKey, featureFlagName, attributes); } diff --git a/src/test/java/io/split/android/client/TreatmentManagerExceptionsTest.java b/src/test/java/io/split/android/client/TreatmentManagerExceptionsTest.java index 74593a436..e130f233c 100644 --- a/src/test/java/io/split/android/client/TreatmentManagerExceptionsTest.java +++ b/src/test/java/io/split/android/client/TreatmentManagerExceptionsTest.java @@ -45,7 +45,7 @@ public class TreatmentManagerExceptionsTest { @Mock SplitValidator splitValidator; @Mock - ImpressionListener impressionListener; + ImpressionListener.FederatedImpressionListener impressionListener; @Mock ListenableEventsManager eventsManager; @Mock diff --git a/src/test/java/io/split/android/client/TreatmentManagerTelemetryTest.java b/src/test/java/io/split/android/client/TreatmentManagerTelemetryTest.java index dfe84c02e..d4b917084 100644 --- a/src/test/java/io/split/android/client/TreatmentManagerTelemetryTest.java +++ b/src/test/java/io/split/android/client/TreatmentManagerTelemetryTest.java @@ -41,7 +41,7 @@ public class TreatmentManagerTelemetryTest { @Mock SplitValidator splitValidator; @Mock - ImpressionListener impressionListener; + ImpressionListener.FederatedImpressionListener impressionListener; @Mock ListenableEventsManager eventsManager; @Mock diff --git a/src/test/java/io/split/android/client/TreatmentManagerTest.java b/src/test/java/io/split/android/client/TreatmentManagerTest.java index a13f3e287..1fb4d5307 100644 --- a/src/test/java/io/split/android/client/TreatmentManagerTest.java +++ b/src/test/java/io/split/android/client/TreatmentManagerTest.java @@ -2,6 +2,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -27,6 +28,7 @@ import io.split.android.client.dtos.Split; import io.split.android.client.events.ListenableEventsManager; import io.split.android.client.events.SplitEvent; +import io.split.android.client.impressions.DecoratedImpression; import io.split.android.client.impressions.ImpressionListener; import io.split.android.client.storage.mysegments.MySegmentsStorage; import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; @@ -43,7 +45,6 @@ import io.split.android.client.validators.ValidationMessageLogger; import io.split.android.client.validators.ValidationMessageLoggerImpl; import io.split.android.engine.experiments.SplitParser; -import io.split.android.fake.ImpressionListenerMock; import io.split.android.fake.SplitEventsManagerStub; import io.split.android.grammar.Treatments; import io.split.android.helpers.FileHelper; @@ -52,7 +53,7 @@ public class TreatmentManagerTest { Evaluator evaluator; - ImpressionListener impressionListener; + ImpressionListener.FederatedImpressionListener impressionListener; ListenableEventsManager eventsManagerStub; AttributesManager attributesManager = mock(AttributesManager.class); TelemetryStorageProducer telemetryStorageProducer = mock(TelemetryStorageProducer.class); @@ -89,7 +90,7 @@ public void loadSplitsFromFile() { evaluator = new EvaluatorImpl(splitsStorage, splitParser); } - impressionListener = mock(ImpressionListener.class); + impressionListener = mock(ImpressionListener.FederatedImpressionListener.class); eventsManagerStub = new SplitEventsManagerStub(); } @@ -316,6 +317,18 @@ public void evaluationWhenNotReadyLogsCorrectMessage() { 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()); } + @Test + public void trackValueFromEvaluationResultGetsPassedInToImpression() { + Evaluator evaluatorMock = mock(Evaluator.class); + when(evaluatorMock.getTreatment(eq("matching_key"), eq("bucketing_key"), eq("test_split"), anyMap())) + .thenReturn(new EvaluationResult("test", "test", true)); + TreatmentManagerImpl tManager = initializeTreatmentManager(evaluatorMock); + + tManager.getTreatment("test_split", null, false); + + verify(impressionListener).log(argThat(DecoratedImpression::getTrackImpressions)); + } + private void assertControl(List splitList, String treatment, Map treatmentList, SplitResult splitResult, Map splitResultList) { Assert.assertNotNull(treatment); Assert.assertEquals(Treatments.CONTROL, treatment); @@ -350,7 +363,7 @@ private TreatmentManager createTreatmentManager(String matchingKey, String bucke return new TreatmentManagerImpl( matchingKey, bucketingKey, evaluator, new KeyValidatorImpl(), splitValidator, - new ImpressionListenerMock(), config.labelsEnabled(), eventsManager, + mock(ImpressionListener.FederatedImpressionListener.class), config.labelsEnabled(), eventsManager, mock(AttributesManager.class), mock(AttributesMerger.class), mock(TelemetryStorageProducer.class), mFlagSetsFilter, mSplitsStorage, validationLogger, new FlagSetsValidatorImpl()); } @@ -374,7 +387,7 @@ private TreatmentManagerImpl initializeTreatmentManager(Evaluator evaluator) { evaluator, mock(KeyValidator.class), mock(SplitValidator.class), - mock(ImpressionListener.class), + impressionListener, SplitClientConfig.builder().build().labelsEnabled(), eventsManager, attributesManager, diff --git a/src/test/java/io/split/android/client/TreatmentManagerWithFlagSetsTest.java b/src/test/java/io/split/android/client/TreatmentManagerWithFlagSetsTest.java index adb7d46fd..bdcbec7db 100644 --- a/src/test/java/io/split/android/client/TreatmentManagerWithFlagSetsTest.java +++ b/src/test/java/io/split/android/client/TreatmentManagerWithFlagSetsTest.java @@ -45,7 +45,7 @@ public class TreatmentManagerWithFlagSetsTest { @Mock private SplitValidator mSplitValidator; @Mock - private ImpressionListener mImpressionListener; + private ImpressionListener.FederatedImpressionListener mImpressionListener; @Mock private ListenableEventsManager mEventsManager; @Mock 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 135d21544..713f38c13 100644 --- a/src/test/java/io/split/android/client/impressions/ImpressionLoggingTaskTest.java +++ b/src/test/java/io/split/android/client/impressions/ImpressionLoggingTaskTest.java @@ -24,7 +24,7 @@ public void setUp() { @Test public void executeLogsImpressionInListener() { - Impression impression = createImpression(); + DecoratedImpression impression = createImpression(); mImpressionsLoggingTask = new ImpressionLoggingTask(mSyncManager, impression); mImpressionsLoggingTask.run(); @@ -34,7 +34,7 @@ public void executeLogsImpressionInListener() { @Test public void successfulExecutionReturnsSuccessInfo() { - Impression impression = createImpression(); + DecoratedImpression impression = createImpression(); mImpressionsLoggingTask = new ImpressionLoggingTask(mSyncManager, impression); mImpressionsLoggingTask.run(); @@ -42,14 +42,14 @@ public void successfulExecutionReturnsSuccessInfo() { @Test public void unsuccessfulExecutionDoesNotCrash() { - doThrow(new RuntimeException("test")).when(mSyncManager).pushImpression(any(Impression.class)); - Impression impression = createImpression(); + doThrow(new RuntimeException("test")).when(mSyncManager).pushImpression(any(DecoratedImpression.class)); + DecoratedImpression impression = createImpression(); mImpressionsLoggingTask = new ImpressionLoggingTask(mSyncManager, impression); mImpressionsLoggingTask.run(); } - private static Impression createImpression() { - return new Impression("key", "feature", "treatment", "on", 1402040204L, "label", 123123L, new HashMap<>()); + private static DecoratedImpression createImpression() { + return new DecoratedImpression(new Impression("key", "feature", "treatment", "on", 1402040204L, "label", 123123L, new HashMap<>()), 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 3c6632098..4131e445f 100644 --- a/src/test/java/io/split/android/client/impressions/SyncImpressionListenerTest.java +++ b/src/test/java/io/split/android/client/impressions/SyncImpressionListenerTest.java @@ -27,7 +27,7 @@ public void setUp() { @Test public void logSubmitsImpressionLoggingTaskInExecutor() { SyncImpressionListener syncImpressionListener = new SyncImpressionListener(mSyncManager, mExecutorService); - Impression impression = createImpression(); + DecoratedImpression impression = createImpression(); syncImpressionListener.log(impression); @@ -37,13 +37,13 @@ public void logSubmitsImpressionLoggingTaskInExecutor() { @Test public void errorWhileSubmittingTaskIsHandled() { SyncImpressionListener syncImpressionListener = new SyncImpressionListener(mSyncManager, mExecutorService); - Impression impression = createImpression(); + DecoratedImpression impression = createImpression(); doThrow(new RuntimeException("test")).when(mExecutorService).submit(any(ImpressionLoggingTask.class), any()); syncImpressionListener.log(impression); } - private static Impression createImpression() { - return new Impression("key", "feature", "treatment", "on", 1402040204L, "label", 123123L, new HashMap<>()); + private static DecoratedImpression createImpression() { + return new DecoratedImpression(new Impression("key", "feature", "treatment", "on", 1402040204L, "label", 123123L, new HashMap<>()), true); } } 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 ebe456370..79347551f 100644 --- a/src/test/java/io/split/android/client/service/SynchronizerTest.java +++ b/src/test/java/io/split/android/client/service/SynchronizerTest.java @@ -44,6 +44,7 @@ import io.split.android.client.dtos.KeyImpression; import io.split.android.client.dtos.SplitChange; import io.split.android.client.events.SplitEventsManager; +import io.split.android.client.impressions.DecoratedImpression; import io.split.android.client.impressions.Impression; import io.split.android.client.service.events.EventsRecorderTask; import io.split.android.client.service.executor.SplitTask; @@ -433,8 +434,8 @@ public void pushImpression() throws InterruptedException { .impressionsQueueSize(3) .build(); setup(config); - Impression impression = createImpression(); - ArgumentCaptor impressionCaptor = ArgumentCaptor.forClass(Impression.class); + DecoratedImpression impression = createImpression(); + ArgumentCaptor impressionCaptor = ArgumentCaptor.forClass(DecoratedImpression.class); mSynchronizer.startPeriodicRecording(); mSynchronizer.pushImpression(impression); Thread.sleep(200); @@ -442,13 +443,14 @@ public void pushImpression() throws InterruptedException { any(ImpressionsRecorderTask.class), any(SplitTaskExecutionListener.class)); verify(mImpressionManager).pushImpression(impressionCaptor.capture()); - Assert.assertEquals("key", impressionCaptor.getValue().key()); - Assert.assertEquals("bkey", impressionCaptor.getValue().bucketingKey()); - Assert.assertEquals("split", impressionCaptor.getValue().split()); - Assert.assertEquals("on", impressionCaptor.getValue().treatment()); - Assert.assertEquals(100L, impressionCaptor.getValue().time()); - Assert.assertEquals("default rule", impressionCaptor.getValue().appliedRule()); - Assert.assertEquals(999, impressionCaptor.getValue().changeNumber().longValue()); + Impression capturedImpression = impressionCaptor.getValue().getImpression(); + Assert.assertEquals("key", capturedImpression.key()); + Assert.assertEquals("bkey", capturedImpression.bucketingKey()); + Assert.assertEquals("split", capturedImpression.split()); + Assert.assertEquals("on", capturedImpression.treatment()); + Assert.assertEquals(100L, capturedImpression.time()); + Assert.assertEquals("default rule", capturedImpression.appliedRule()); + Assert.assertEquals(999, capturedImpression.changeNumber().longValue()); } @Test @@ -465,7 +467,7 @@ public void pushImpressionReachQueueSizeImpDebug() throws InterruptedException { mSynchronizer.pushImpression(createImpression()); } Thread.sleep(200); - verify(mImpressionManager, times(8)).pushImpression(any(Impression.class)); + verify(mImpressionManager, times(8)).pushImpression(any(DecoratedImpression.class)); } @Test @@ -482,7 +484,7 @@ public void pushImpressionReachQueueSizeImpOptimized() throws InterruptedExcepti mSynchronizer.pushImpression(createUniqueImpression()); } Thread.sleep(200); - verify(mImpressionManager, times(8)).pushImpression(any(Impression.class)); + verify(mImpressionManager, times(8)).pushImpression(any(DecoratedImpression.class)); } @Test @@ -500,7 +502,7 @@ public void pushImpressionBytesLimitImpDebug() throws InterruptedException { mSynchronizer.pushImpression(createImpression()); } Thread.sleep(200); - verify(mImpressionManager, times(10)).pushImpression(any(Impression.class)); + verify(mImpressionManager, times(10)).pushImpression(any(DecoratedImpression.class)); } @Test @@ -518,7 +520,7 @@ public void pushImpressionBytesLimitImpOptimized() throws InterruptedException { mSynchronizer.pushImpression(createUniqueImpression()); } Thread.sleep(200); - verify(mImpressionManager, times(10)).pushImpression(any(Impression.class)); + verify(mImpressionManager, times(10)).pushImpression(any(DecoratedImpression.class)); } @Test @@ -794,14 +796,14 @@ public void synchronizeSplitsDelegatesToFeatureFlagsSynchronizer() { public void tearDown() { } - private Impression createImpression() { - return new Impression("key", "bkey", "split", "on", - 100L, "default rule", 999L, null); + private DecoratedImpression createImpression() { + return new DecoratedImpression(new Impression("key", "bkey", "split", "on", + 100L, "default rule", 999L, null), true); } - private Impression createUniqueImpression() { - return new Impression("key", "bkey", UUID.randomUUID().toString(), "on", - 100L, "default rule", 999L, null); + private DecoratedImpression createUniqueImpression() { + return new DecoratedImpression(new Impression("key", "bkey", UUID.randomUUID().toString(), "on", + 100L, "default rule", 999L, null), true); } private KeyImpression keyImpression(Impression impression) { diff --git a/src/test/java/io/split/android/client/service/impressions/StrategyImpressionManagerTest.kt b/src/test/java/io/split/android/client/service/impressions/StrategyImpressionManagerTest.kt index 563802099..d72103443 100644 --- a/src/test/java/io/split/android/client/service/impressions/StrategyImpressionManagerTest.kt +++ b/src/test/java/io/split/android/client/service/impressions/StrategyImpressionManagerTest.kt @@ -1,5 +1,6 @@ package io.split.android.client.service.impressions +import io.split.android.client.impressions.DecoratedImpression import io.split.android.client.impressions.Impression import io.split.android.client.service.impressions.strategy.PeriodicTracker import io.split.android.client.service.impressions.strategy.ProcessStrategy @@ -8,6 +9,8 @@ import org.junit.Test import org.mockito.Mock import org.mockito.Mockito.mock import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoInteractions +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations class StrategyImpressionManagerTest { @@ -37,6 +40,7 @@ class StrategyImpressionManagerTest { impressionManager.flush() verify(tracker).flush() + verify(noneTracker).flush() } @Test @@ -44,6 +48,7 @@ class StrategyImpressionManagerTest { impressionManager.startPeriodicRecording() verify(tracker).startPeriodicRecording() + verify(noneTracker).startPeriodicRecording() } @Test @@ -51,13 +56,61 @@ class StrategyImpressionManagerTest { impressionManager.stopPeriodicRecording() verify(tracker).stopPeriodicRecording() + verify(noneTracker).stopPeriodicRecording() } @Test fun `pushImpression calls apply on strategy`() { val impression = mock(Impression::class.java) - impressionManager.pushImpression(impression) + val decoratedImpression = DecoratedImpression(impression, true) + impressionManager.pushImpression(decoratedImpression) verify(strategy).apply(impression) + verifyNoInteractions(noneStrategy) + } + + @Test + fun `pushImpression calls apply on noneStrategy when trackImpressions is false`() { + val impression = mock(Impression::class.java) + val decoratedImpression = DecoratedImpression(impression, false) + impressionManager.pushImpression(decoratedImpression) + + verify(noneStrategy).apply(impression) + verifyNoInteractions(strategy) + } + + @Test + fun `pushImpression when it is decorated uses value from trackImpression to track`() { + val impression = mock(Impression::class.java) + val impression2 = mock(Impression::class.java) + val decoratedImpression = DecoratedImpression(impression, false) + val decoratedImpression2 = DecoratedImpression(impression2, true) + impressionManager.pushImpression(decoratedImpression) + impressionManager.pushImpression(decoratedImpression2) + + verify(strategy).apply(impression2) + verify(noneStrategy).apply(impression) + } + + @Test + fun `enableTracking set to true causes impressions to be sent to strategy`() { + impressionManager.enableTracking(true) + val impression = mock(Impression::class.java) + val decoratedImpression = DecoratedImpression(impression, true) + impressionManager.pushImpression(decoratedImpression) + + verify(strategy).apply(impression) + verifyNoInteractions(noneStrategy) + } + + @Test + fun `enableTracking set to false causes impressions to not be tracked`() { + impressionManager.enableTracking(false) + val impression = mock(Impression::class.java) + val decoratedImpression = DecoratedImpression(impression, true) + impressionManager.pushImpression(decoratedImpression) + + verifyNoInteractions(noneStrategy) + verifyNoInteractions(strategy) } } diff --git a/src/test/java/io/split/android/client/utils/SplitClientImplFactory.java b/src/test/java/io/split/android/client/utils/SplitClientImplFactory.java index 73f254230..36b8728a5 100644 --- a/src/test/java/io/split/android/client/utils/SplitClientImplFactory.java +++ b/src/test/java/io/split/android/client/utils/SplitClientImplFactory.java @@ -14,6 +14,7 @@ import io.split.android.client.attributes.AttributesMergerImpl; import io.split.android.client.events.SplitEventsManager; import io.split.android.client.events.SplitInternalEvent; +import io.split.android.client.impressions.DecoratedImpressionListener; import io.split.android.client.impressions.ImpressionListener; import io.split.android.client.shared.SplitClientContainer; import io.split.android.client.storage.mysegments.MySegmentsStorageContainer; @@ -37,7 +38,7 @@ public static SplitClientImpl get(Key key, SplitsStorage splitsStorage) { SplitParser splitParser = new SplitParser(mock(MySegmentsStorageContainer.class), mock(MySegmentsStorageContainer.class)); TelemetryStorage telemetryStorage = mock(TelemetryStorage.class); TreatmentManagerFactory treatmentManagerFactory = new TreatmentManagerFactoryImpl( - new KeyValidatorImpl(), new SplitValidatorImpl(), new ImpressionListener.NoopImpressionListener(), + new KeyValidatorImpl(), new SplitValidatorImpl(), new ImpressionListener.FederatedImpressionListener(mock(DecoratedImpressionListener.class), Collections.emptyList()), false, new AttributesMergerImpl(), telemetryStorage, splitParser, new FlagSetsFilterImpl(Collections.emptySet()), splitsStorage); diff --git a/src/test/java/io/split/android/fake/ImpressionListenerMock.java b/src/test/java/io/split/android/fake/ImpressionListenerMock.java index ce2ff7bd1..e9d53773f 100644 --- a/src/test/java/io/split/android/fake/ImpressionListenerMock.java +++ b/src/test/java/io/split/android/fake/ImpressionListenerMock.java @@ -1,13 +1,19 @@ package io.split.android.fake; +import io.split.android.client.impressions.DecoratedImpression; +import io.split.android.client.impressions.DecoratedImpressionListener; import io.split.android.client.impressions.Impression; import io.split.android.client.impressions.ImpressionListener; -public class ImpressionListenerMock implements ImpressionListener { +public class ImpressionListenerMock implements ImpressionListener, DecoratedImpressionListener { @Override public void log(Impression impression) { } + @Override + public void log(DecoratedImpression impression) { + } + @Override public void close() { }