diff --git a/src/androidTest/java/fake/SynchronizerSpyImpl.java b/src/androidTest/java/fake/SynchronizerSpyImpl.java index 6971258eb..2203f3913 100644 --- a/src/androidTest/java/fake/SynchronizerSpyImpl.java +++ b/src/androidTest/java/fake/SynchronizerSpyImpl.java @@ -3,6 +3,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; +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.service.synchronizer.Synchronizer; @@ -122,12 +123,12 @@ public void unregisterAttributesSynchronizer(String userKey) { } @Override - public void registerMySegmentsSynchronizer(String userKey, MySegmentsSynchronizer mySegmentsSynchronizer) { - ((MySegmentsSynchronizerRegistry) mSynchronizer).registerMySegmentsSynchronizer(userKey, mySegmentsSynchronizer); + public void registerMySegmentsSynchronizer(Key key, MySegmentsSynchronizer mySegmentsSynchronizer) { + ((MySegmentsSynchronizerRegistry) mSynchronizer).registerMySegmentsSynchronizer(key, mySegmentsSynchronizer); } @Override - public void unregisterMySegmentsSynchronizer(String userKey) { - ((MySegmentsSynchronizerRegistry) mSynchronizer).unregisterMySegmentsSynchronizer(userKey); + public void unregisterMySegmentsSynchronizer(Key key) { + ((MySegmentsSynchronizerRegistry) mSynchronizer).unregisterMySegmentsSynchronizer(key); } } diff --git a/src/androidTest/java/tests/database/DatabaseInitializationTest.java b/src/androidTest/java/tests/database/DatabaseInitializationTest.java index 440dde5f5..a4201e1a9 100644 --- a/src/androidTest/java/tests/database/DatabaseInitializationTest.java +++ b/src/androidTest/java/tests/database/DatabaseInitializationTest.java @@ -182,6 +182,11 @@ public void usingNullPrefixResultsInIgnoredPrefix() { private static String[] getDbList(Context context) { // remove -journal dbs since we're not interested in them + try { + Thread.sleep(500); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } return Arrays.stream(context.databaseList()).filter(db -> !db.endsWith("-journal")).toArray(String[]::new); } } diff --git a/src/androidTest/java/tests/integration/attributes/AttributesIntegrationTest.java b/src/androidTest/java/tests/integration/attributes/AttributesIntegrationTest.java index 9b9a47401..085d99cb3 100644 --- a/src/androidTest/java/tests/integration/attributes/AttributesIntegrationTest.java +++ b/src/androidTest/java/tests/integration/attributes/AttributesIntegrationTest.java @@ -108,7 +108,7 @@ public void testPersistentAttributes2() throws InterruptedException { // 3. Perform clear and verify there are no attributes on DB client.clearAttributes(); - countDownLatch.await(1, TimeUnit.SECONDS); + countDownLatch.await(7, TimeUnit.SECONDS); Assert.assertNull(mRoomDb.attributesDao().getByUserKey(userKey)); } @@ -153,7 +153,7 @@ public void testPersistentAttributesWithMultiClient2() throws InterruptedExcepti // 2. Clear second client's attributes and check DB entry has been cleared client2.clearAttributes(); - countDownLatch.await(1, TimeUnit.SECONDS); // waiting since DB operations are async + countDownLatch.await(7, TimeUnit.SECONDS); // waiting since DB operations are async Assert.assertNull(mRoomDb.attributesDao().getByUserKey("new_key")); // 3. Verify evaluation with first client uses attribute @@ -162,7 +162,7 @@ public void testPersistentAttributesWithMultiClient2() throws InterruptedExcepti // 4. Perform clear and verify there are no attributes on DB client.clearAttributes(); - countDownLatch.await(1, TimeUnit.SECONDS); + countDownLatch.await(7, TimeUnit.SECONDS); Assert.assertNull(mRoomDb.attributesDao().getByUserKey(matchingKey)); } diff --git a/src/androidTest/java/tests/integration/encryption/EncryptionTest.java b/src/androidTest/java/tests/integration/encryption/EncryptionTest.java index b8ef6be27..6a8561825 100644 --- a/src/androidTest/java/tests/integration/encryption/EncryptionTest.java +++ b/src/androidTest/java/tests/integration/encryption/EncryptionTest.java @@ -127,7 +127,7 @@ public void onPostExecutionView(SplitClient client) { } }); - assertTrue(latch.await(2, TimeUnit.SECONDS)); + assertTrue(latch.await(5, TimeUnit.SECONDS)); mLifecycleManager.simulateOnPause(); Thread.sleep(200); mLifecycleManager.simulateOnResume(); diff --git a/src/androidTest/java/tests/integration/largesegments/LargeSegmentsStreamingTest.java b/src/androidTest/java/tests/integration/largesegments/LargeSegmentsStreamingTest.java index 2b95f70ca..f5cfe3cdc 100644 --- a/src/androidTest/java/tests/integration/largesegments/LargeSegmentsStreamingTest.java +++ b/src/androidTest/java/tests/integration/largesegments/LargeSegmentsStreamingTest.java @@ -67,8 +67,8 @@ public void setUp() throws IOException, InterruptedException { public void unboundedLargeSegmentsUpdateTriggersSdkUpdate() throws IOException, InterruptedException { TestSetup testSetup = getTestSetup(); - boolean mySegmentsAwait = mLatches.get(MY_SEGMENTS).await(10, TimeUnit.SECONDS); - boolean splitsAwait = mLatches.get(SPLIT_CHANGES).await(10, TimeUnit.SECONDS); + boolean mySegmentsAwait = mLatches.get(MY_SEGMENTS).await(15, TimeUnit.SECONDS); + boolean splitsAwait = mLatches.get(SPLIT_CHANGES).await(15, TimeUnit.SECONDS); String initialSegmentList = testSetup.database.myLargeSegmentDao().getByUserKey(IntegrationHelper.dummyUserKey().matchingKey()).getSegmentList(); mRandomizeMyLargeSegments.set(true); diff --git a/src/androidTest/java/tests/integration/shared/SharedClientsIntegrationTest.java b/src/androidTest/java/tests/integration/shared/SharedClientsIntegrationTest.java index feecae9a8..a12642bee 100644 --- a/src/androidTest/java/tests/integration/shared/SharedClientsIntegrationTest.java +++ b/src/androidTest/java/tests/integration/shared/SharedClientsIntegrationTest.java @@ -159,8 +159,8 @@ public void onPostExecutionView(SplitClient client) { } }); insertSplitsIntoDb(); - readyLatch.await(10, TimeUnit.SECONDS); - readyLatch2.await(10, TimeUnit.SECONDS); + readyLatch.await(15, TimeUnit.SECONDS); + readyLatch2.await(15, TimeUnit.SECONDS); assertEquals(1, readyCount.get()); assertEquals(1, readyCount2.get()); diff --git a/src/androidTest/java/tests/integration/telemetry/TelemetryOccupancyTest.java b/src/androidTest/java/tests/integration/telemetry/TelemetryOccupancyTest.java index 87cf5669b..e13080d3a 100644 --- a/src/androidTest/java/tests/integration/telemetry/TelemetryOccupancyTest.java +++ b/src/androidTest/java/tests/integration/telemetry/TelemetryOccupancyTest.java @@ -6,16 +6,16 @@ import org.junit.Test; import java.io.IOException; +import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CountDownLatch; import helper.TestableSplitConfigBuilder; import io.split.android.client.SplitClientConfig; -import io.split.android.client.storage.db.StorageFactory; import io.split.android.client.telemetry.model.streaming.OccupancyPriStreamingEvent; import io.split.android.client.telemetry.model.streaming.OccupancySecStreamingEvent; import io.split.android.client.telemetry.model.streaming.StreamingEvent; import io.split.android.client.telemetry.model.streaming.TokenRefreshStreamingEvent; -import io.split.android.client.telemetry.storage.TelemetryStorage; import tests.integration.streaming.OccupancyBaseTest; public class TelemetryOccupancyTest extends OccupancyBaseTest { @@ -35,12 +35,19 @@ public class TelemetryOccupancyTest extends OccupancyBaseTest { @Test public void telemetryOccupancyPriStreamingEvent() throws InterruptedException, IOException { + new CountDownLatch(1); getSplitFactory(mTelemetryEnabledConfig); pushOccupancy(PRIMARY_CHANNEL, 1); - sleep(2000); - List streamingEvents = mTelemetryStorage.popStreamingEvents(); + long startTime = System.currentTimeMillis(); + List streamingEvents = new ArrayList<>(); + streamingEvents = mTelemetryStorage.popStreamingEvents(); + while (System.currentTimeMillis() - startTime < 5000 && + !streamingEvents.stream().anyMatch(event -> event instanceof OccupancyPriStreamingEvent)) { + Thread.sleep(100); + streamingEvents = mTelemetryStorage.popStreamingEvents(); + } assertTrue(streamingEvents.stream().anyMatch(event -> event instanceof OccupancyPriStreamingEvent)); } @@ -51,7 +58,14 @@ public void telemetryOccupancySecStreamingEvent() throws InterruptedException, I pushOccupancy(SECONDARY_CHANNEL, 1); sleep(2000); - List streamingEvents = mTelemetryStorage.popStreamingEvents(); + long startTime = System.currentTimeMillis(); + List streamingEvents = new ArrayList<>(); + streamingEvents = mTelemetryStorage.popStreamingEvents(); + while (System.currentTimeMillis() - startTime < 5000 && + !streamingEvents.stream().anyMatch(event -> event instanceof OccupancySecStreamingEvent)) { + Thread.sleep(100); + streamingEvents = mTelemetryStorage.popStreamingEvents(); + } assertTrue(streamingEvents.stream().anyMatch(event -> event instanceof OccupancySecStreamingEvent)); } diff --git a/src/main/java/io/split/android/client/SplitFactoryHelper.java b/src/main/java/io/split/android/client/SplitFactoryHelper.java index 3757b633c..bfb9b6bde 100644 --- a/src/main/java/io/split/android/client/SplitFactoryHelper.java +++ b/src/main/java/io/split/android/client/SplitFactoryHelper.java @@ -4,6 +4,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.core.util.Pair; import androidx.work.WorkManager; @@ -22,11 +23,15 @@ import io.split.android.client.common.CompressionUtilProvider; import io.split.android.client.events.EventsManagerCoordinator; +import io.split.android.client.events.SplitInternalEvent; +import io.split.android.client.lifecycle.SplitLifecycleManager; import io.split.android.client.network.HttpClient; import io.split.android.client.network.SdkTargetPath; import io.split.android.client.network.SplitHttpHeadersBuilder; import io.split.android.client.service.ServiceFactory; import io.split.android.client.service.SplitApiFacade; +import io.split.android.client.service.executor.SplitSingleThreadTaskExecutor; +import io.split.android.client.service.executor.SplitTaskExecutionInfo; import io.split.android.client.service.executor.SplitTaskExecutionListener; import io.split.android.client.service.executor.SplitTaskExecutor; import io.split.android.client.service.executor.SplitTaskFactory; @@ -56,6 +61,8 @@ import io.split.android.client.service.sseclient.sseclient.SseHandler; import io.split.android.client.service.sseclient.sseclient.SseRefreshTokenTimer; import io.split.android.client.service.sseclient.sseclient.StreamingComponents; +import io.split.android.client.service.synchronizer.RolloutCacheManager; +import io.split.android.client.service.synchronizer.RolloutCacheManagerImpl; import io.split.android.client.service.synchronizer.SyncGuardian; import io.split.android.client.service.synchronizer.SyncGuardianImpl; import io.split.android.client.service.synchronizer.SyncManager; @@ -70,7 +77,6 @@ import io.split.android.client.shared.ClientComponentsRegisterImpl; import io.split.android.client.shared.UserConsent; import io.split.android.client.storage.attributes.PersistentAttributesStorage; -import io.split.android.client.storage.cipher.EncryptionMigrationTask; import io.split.android.client.storage.cipher.SplitCipher; import io.split.android.client.storage.cipher.SplitCipherFactory; import io.split.android.client.storage.cipher.SplitEncryptionLevel; @@ -86,6 +92,7 @@ import io.split.android.client.telemetry.storage.TelemetryRuntimeProducer; import io.split.android.client.telemetry.storage.TelemetryStorage; import io.split.android.client.utils.Utils; +import io.split.android.client.utils.logger.Logger; class SplitFactoryHelper { private static final int DB_MAGIC_CHARS_COUNT = 4; @@ -399,21 +406,10 @@ public ProcessStrategy getImpressionStrategy(SplitTaskExecutor splitTaskExecutor .getStrategy(config.impressionsMode()); } - SplitCipher migrateEncryption(String apiKey, - SplitRoomDatabase splitDatabase, - SplitTaskExecutor splitTaskExecutor, - final boolean encryptionEnabled, - SplitTaskExecutionListener executionListener) { - - SplitCipher toCipher = SplitCipherFactory.create(apiKey, encryptionEnabled ? SplitEncryptionLevel.AES_128_CBC : + @Nullable + SplitCipher getCipher(String apiKey, boolean encryptionEnabled) { + return SplitCipherFactory.create(apiKey, encryptionEnabled ? SplitEncryptionLevel.AES_128_CBC : SplitEncryptionLevel.NONE); - splitTaskExecutor.submit(new EncryptionMigrationTask(apiKey, - splitDatabase, - encryptionEnabled, - toCipher), - executionListener); - - return toCipher; } @Nullable @@ -486,4 +482,75 @@ public URI build(String matchingKey) throws URISyntaxException { return SdkTargetPath.mySegments(mEndpoint, matchingKey); } } + + static class Initializer implements Runnable { + + private final RolloutCacheManager mRolloutCacheManager; + private final SplitTaskExecutionListener mListener; + + Initializer( + String apiToken, + SplitClientConfig config, + SplitTaskFactory splitTaskFactory, + SplitRoomDatabase splitDatabase, + SplitCipher splitCipher, + EventsManagerCoordinator eventsManagerCoordinator, + SplitTaskExecutor splitTaskExecutor, + SplitSingleThreadTaskExecutor splitSingleThreadTaskExecutor, + SplitStorageContainer storageContainer, + SyncManager syncManager, + SplitLifecycleManager lifecycleManager) { + + this(new RolloutCacheManagerImpl(config, + storageContainer, + splitTaskFactory.createCleanUpDatabaseTask(System.currentTimeMillis() / 1000), + splitTaskFactory.createEncryptionMigrationTask(apiToken, splitDatabase, config.encryptionEnabled(), splitCipher)), + new Listener(eventsManagerCoordinator, splitTaskExecutor, splitSingleThreadTaskExecutor, syncManager, lifecycleManager)); + } + + @VisibleForTesting + Initializer(RolloutCacheManager rolloutCacheManager, SplitTaskExecutionListener listener) { + mRolloutCacheManager = rolloutCacheManager; + mListener = listener; + } + + @Override + public void run() { + mRolloutCacheManager.validateCache(mListener); + } + + static class Listener implements SplitTaskExecutionListener { + + private final EventsManagerCoordinator mEventsManagerCoordinator; + private final SplitTaskExecutor mSplitTaskExecutor; + private final SplitSingleThreadTaskExecutor mSplitSingleThreadTaskExecutor; + private final SyncManager mSyncManager; + private final SplitLifecycleManager mLifecycleManager; + + Listener(EventsManagerCoordinator eventsManagerCoordinator, + SplitTaskExecutor splitTaskExecutor, + SplitSingleThreadTaskExecutor splitSingleThreadTaskExecutor, + SyncManager syncManager, + SplitLifecycleManager lifecycleManager) { + mEventsManagerCoordinator = eventsManagerCoordinator; + mSplitTaskExecutor = splitTaskExecutor; + mSplitSingleThreadTaskExecutor = splitSingleThreadTaskExecutor; + mSyncManager = syncManager; + mLifecycleManager = lifecycleManager; + } + + @Override + public void taskExecuted(@NonNull SplitTaskExecutionInfo taskInfo) { + mEventsManagerCoordinator.notifyInternalEvent(SplitInternalEvent.ENCRYPTION_MIGRATION_DONE); + + mSplitTaskExecutor.resume(); + mSplitSingleThreadTaskExecutor.resume(); + + mSyncManager.start(); + mLifecycleManager.register(mSyncManager); + + Logger.i("Android SDK initialized!"); + } + } + } } diff --git a/src/main/java/io/split/android/client/SplitFactoryImpl.java b/src/main/java/io/split/android/client/SplitFactoryImpl.java index b5c0252e3..34d3abd63 100644 --- a/src/main/java/io/split/android/client/SplitFactoryImpl.java +++ b/src/main/java/io/split/android/client/SplitFactoryImpl.java @@ -18,7 +18,6 @@ import io.split.android.client.api.Key; import io.split.android.client.common.CompressionUtilProvider; import io.split.android.client.events.EventsManagerCoordinator; -import io.split.android.client.events.SplitInternalEvent; import io.split.android.client.factory.FactoryMonitor; import io.split.android.client.factory.FactoryMonitorImpl; import io.split.android.client.impressions.ImpressionListener; @@ -29,8 +28,6 @@ import io.split.android.client.network.HttpClientImpl; import io.split.android.client.service.SplitApiFacade; import io.split.android.client.service.executor.SplitSingleThreadTaskExecutor; -import io.split.android.client.service.executor.SplitTaskExecutionInfo; -import io.split.android.client.service.executor.SplitTaskExecutionListener; import io.split.android.client.service.executor.SplitTaskExecutor; import io.split.android.client.service.executor.SplitTaskExecutorImpl; import io.split.android.client.service.executor.SplitTaskFactory; @@ -84,9 +81,6 @@ public class SplitFactoryImpl implements SplitFactory { private final SplitClientContainer mClientContainer; private final UserConsentManager mUserConsentManager; - @SuppressWarnings("FieldCanBeLocal") // keeping the reference on purpose - private final SplitTaskExecutionListener mMigrationExecutionListener; - public SplitFactoryImpl(@NonNull String apiToken, @NonNull Key key, @NonNull SplitClientConfig config, @NonNull Context context) throws URISyntaxException { this(apiToken, key, config, context, @@ -156,27 +150,17 @@ private SplitFactoryImpl(@NonNull String apiToken, @NonNull Key key, @NonNull Sp } else { splitDatabase = testDatabase; Logger.d("Using test database"); - System.out.println("USING TEST DB: " + testDatabase); } defaultHttpClient.addHeaders(factoryHelper.buildHeaders(config, apiToken)); defaultHttpClient.addStreamingHeaders(factoryHelper.buildStreamingHeaders(apiToken)); SplitTaskExecutor splitTaskExecutor = new SplitTaskExecutorImpl(); + splitTaskExecutor.pause(); EventsManagerCoordinator mEventsManagerCoordinator = new EventsManagerCoordinator(); - mMigrationExecutionListener = new SplitTaskExecutionListener() { - @Override - public void taskExecuted(@NonNull SplitTaskExecutionInfo taskInfo) { - mEventsManagerCoordinator.notifyInternalEvent(SplitInternalEvent.ENCRYPTION_MIGRATION_DONE); - } - }; - SplitCipher splitCipher = factoryHelper.migrateEncryption(mApiKey, - splitDatabase, - splitTaskExecutor, - config.encryptionEnabled(), - mMigrationExecutionListener); + SplitCipher splitCipher = factoryHelper.getCipher(apiToken, config.encryptionEnabled()); ScheduledThreadPoolExecutor impressionsObserverExecutor = new ScheduledThreadPoolExecutor(1, new ThreadPoolExecutor.CallerRunsPolicy()); @@ -197,9 +181,9 @@ public void taskExecuted(@NonNull SplitTaskExecutionInfo taskInfo) { config, splitApiFacade, mStorageContainer, splitsFilterQueryStringFromConfig, getFlagsSpec(testingConfig), mEventsManagerCoordinator, filters, flagSetsFilter, testingConfig); - cleanUpDabase(splitTaskExecutor, splitTaskFactory); WorkManagerWrapper workManagerWrapper = factoryHelper.buildWorkManagerWrapper(context, config, apiToken, databaseName, filters); SplitSingleThreadTaskExecutor splitSingleThreadTaskExecutor = new SplitSingleThreadTaskExecutor(); + splitSingleThreadTaskExecutor.pause(); ImpressionManager impressionManager = new StrategyImpressionManager(factoryHelper.getImpressionStrategy(splitTaskExecutor, splitTaskFactory, mStorageContainer, config)); final RetryBackoffCounterTimerFactory retryBackoffCounterTimerFactory = new RetryBackoffCounterTimerFactory(); @@ -254,8 +238,6 @@ public void taskExecuted(@NonNull SplitTaskExecutionInfo taskInfo) { mLifecycleManager = testLifecycleManager; } - mLifecycleManager.register(mSyncManager); - ExecutorService impressionsLoggingTaskExecutor = factoryHelper.getImpressionsLoggingTaskExecutor(); final ImpressionListener splitImpressionListener = new SyncImpressionListener(mSyncManager, impressionsLoggingTaskExecutor); @@ -328,21 +310,35 @@ public void run() { } }); - // Initialize default client - client(); - SplitParser mSplitParser = new SplitParser(mStorageContainer.getMySegmentsStorageContainer(), mStorageContainer.getMyLargeSegmentsStorageContainer()); - mManager = new SplitManagerImpl( - mStorageContainer.getSplitsStorage(), - new SplitValidatorImpl(), mSplitParser); + // Set up async initialization + final SplitFactoryHelper.Initializer initializer = new SplitFactoryHelper.Initializer(apiToken, + config, + splitTaskFactory, + splitDatabase, + splitCipher, + mEventsManagerCoordinator, + splitTaskExecutor, + splitSingleThreadTaskExecutor, + mStorageContainer, + mSyncManager, + mLifecycleManager); - mSyncManager.start(); if (config.shouldRecordTelemetry()) { int activeFactoriesCount = mFactoryMonitor.count(mApiKey); mStorageContainer.getTelemetryStorage().recordActiveFactories(activeFactoriesCount); mStorageContainer.getTelemetryStorage().recordRedundantFactories(activeFactoriesCount - 1); } - Logger.i("Android SDK initialized!"); + // Run initializer + new Thread(initializer).start(); + + // Initialize default client + client(); + SplitParser mSplitParser = new SplitParser(mStorageContainer.getMySegmentsStorageContainer(), mStorageContainer.getMyLargeSegmentsStorageContainer()); + mManager = new SplitManagerImpl( + mStorageContainer.getSplitsStorage(), + new SplitValidatorImpl(), mSplitParser); + } private static String getFlagsSpec(@Nullable TestingConfig testingConfig) { diff --git a/src/main/java/io/split/android/client/attributes/AttributesManagerImpl.java b/src/main/java/io/split/android/client/attributes/AttributesManagerImpl.java index 24169f66b..5b2327883 100644 --- a/src/main/java/io/split/android/client/attributes/AttributesManagerImpl.java +++ b/src/main/java/io/split/android/client/attributes/AttributesManagerImpl.java @@ -118,13 +118,13 @@ public boolean clearAttributes() { private void submitUpdateTask(PersistentAttributesStorage persistentStorage, Map mInMemoryAttributes) { if (persistentStorage != null && mSplitTaskExecutor != null && mAttributeTaskFactory != null) { - mSplitTaskExecutor.submit(mAttributeTaskFactory.createAttributeUpdateTask(persistentStorage, mInMemoryAttributes), null); + mSplitTaskExecutor.schedule(mAttributeTaskFactory.createAttributeUpdateTask(persistentStorage, mInMemoryAttributes), 5L, null); } } private void submitClearTask(PersistentAttributesStorage persistentStorage) { if (persistentStorage != null && mSplitTaskExecutor != null && mAttributeTaskFactory != null) { - mSplitTaskExecutor.submit(mAttributeTaskFactory.createAttributeClearTask(persistentStorage), null); + mSplitTaskExecutor.schedule(mAttributeTaskFactory.createAttributeClearTask(persistentStorage), 5L, null); } } } diff --git a/src/main/java/io/split/android/client/service/executor/SplitTaskFactory.java b/src/main/java/io/split/android/client/service/executor/SplitTaskFactory.java index e23407f55..989557cd9 100644 --- a/src/main/java/io/split/android/client/service/executor/SplitTaskFactory.java +++ b/src/main/java/io/split/android/client/service/executor/SplitTaskFactory.java @@ -11,6 +11,9 @@ import io.split.android.client.service.splits.SplitsSyncTask; import io.split.android.client.service.splits.SplitsUpdateTask; import io.split.android.client.service.telemetry.TelemetryTaskFactory; +import io.split.android.client.storage.cipher.EncryptionMigrationTask; +import io.split.android.client.storage.cipher.SplitCipher; +import io.split.android.client.storage.db.SplitRoomDatabase; public interface SplitTaskFactory extends TelemetryTaskFactory, ImpressionsTaskFactory { @@ -29,4 +32,6 @@ public interface SplitTaskFactory extends TelemetryTaskFactory, ImpressionsTaskF FilterSplitsInCacheTask createFilterSplitsInCacheTask(); CleanUpDatabaseTask createCleanUpDatabaseTask(long maxTimestamp); + + EncryptionMigrationTask createEncryptionMigrationTask(String sdkKey, SplitRoomDatabase splitRoomDatabase, boolean encryptionEnabled, SplitCipher splitCipher); } diff --git a/src/main/java/io/split/android/client/service/executor/SplitTaskFactoryImpl.java b/src/main/java/io/split/android/client/service/executor/SplitTaskFactoryImpl.java index 2c167cda0..75ecc45fc 100644 --- a/src/main/java/io/split/android/client/service/executor/SplitTaskFactoryImpl.java +++ b/src/main/java/io/split/android/client/service/executor/SplitTaskFactoryImpl.java @@ -44,7 +44,10 @@ import io.split.android.client.service.telemetry.TelemetryStatsRecorderTask; import io.split.android.client.service.telemetry.TelemetryTaskFactory; import io.split.android.client.service.telemetry.TelemetryTaskFactoryImpl; +import io.split.android.client.storage.cipher.EncryptionMigrationTask; +import io.split.android.client.storage.cipher.SplitCipher; import io.split.android.client.storage.common.SplitStorageContainer; +import io.split.android.client.storage.db.SplitRoomDatabase; import io.split.android.client.telemetry.storage.TelemetryRuntimeProducer; import io.split.android.client.telemetry.storage.TelemetryStorage; @@ -203,6 +206,11 @@ public SplitInPlaceUpdateTask createSplitsUpdateTask(Split featureFlag, long sin return new SplitInPlaceUpdateTask(mSplitsStorageContainer.getSplitsStorage(), mSplitChangeProcessor, mEventsManager, mTelemetryRuntimeProducer, featureFlag, since); } + @Override + public EncryptionMigrationTask createEncryptionMigrationTask(String sdkKey, SplitRoomDatabase splitRoomDatabase, boolean encryptionEnabled, SplitCipher splitCipher) { + return new EncryptionMigrationTask(sdkKey, splitRoomDatabase, encryptionEnabled, splitCipher); + } + @NonNull private TelemetryTaskFactory initializeTelemetryTaskFactory(@NonNull SplitClientConfig splitClientConfig, @Nullable Map filters, TelemetryStorage telemetryStorage) { final TelemetryTaskFactory mTelemetryTaskFactory; diff --git a/src/main/java/io/split/android/client/service/synchronizer/RolloutCacheManager.java b/src/main/java/io/split/android/client/service/synchronizer/RolloutCacheManager.java index 372b376d2..72cf4477e 100644 --- a/src/main/java/io/split/android/client/service/synchronizer/RolloutCacheManager.java +++ b/src/main/java/io/split/android/client/service/synchronizer/RolloutCacheManager.java @@ -2,7 +2,7 @@ import io.split.android.client.service.executor.SplitTaskExecutionListener; -interface RolloutCacheManager { +public interface RolloutCacheManager { void validateCache(SplitTaskExecutionListener listener); } diff --git a/src/main/java/io/split/android/client/service/synchronizer/RolloutCacheManagerImpl.java b/src/main/java/io/split/android/client/service/synchronizer/RolloutCacheManagerImpl.java index 254ace2cc..f211ae62b 100644 --- a/src/main/java/io/split/android/client/service/synchronizer/RolloutCacheManagerImpl.java +++ b/src/main/java/io/split/android/client/service/synchronizer/RolloutCacheManagerImpl.java @@ -4,16 +4,18 @@ import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; +import androidx.annotation.WorkerThread; import java.util.concurrent.TimeUnit; import io.split.android.client.SplitClientConfig; +import io.split.android.client.service.CleanUpDatabaseTask; import io.split.android.client.service.executor.SplitTask; import io.split.android.client.service.executor.SplitTaskExecutionInfo; import io.split.android.client.service.executor.SplitTaskExecutionListener; -import io.split.android.client.service.executor.SplitTaskExecutor; import io.split.android.client.service.executor.SplitTaskType; import io.split.android.client.storage.RolloutDefinitionsCache; +import io.split.android.client.storage.cipher.EncryptionMigrationTask; import io.split.android.client.storage.common.SplitStorageContainer; import io.split.android.client.storage.general.GeneralInfoStorage; import io.split.android.client.utils.logger.Logger; @@ -27,30 +29,53 @@ public class RolloutCacheManagerImpl implements RolloutCacheManager, SplitTask { @NonNull private final RolloutCacheManagerConfig mConfig; @NonNull - private final SplitTaskExecutor mTaskExecutor; - @NonNull private final RolloutDefinitionsCache[] mStorages; + @NonNull + private final CleanUpDatabaseTask mCleanUpDatabaseTask; + @NonNull + private final EncryptionMigrationTask mEncryptionMigrationTask; - public RolloutCacheManagerImpl(@NonNull SplitClientConfig splitClientConfig, @NonNull SplitTaskExecutor splitTaskExecutor, @NonNull SplitStorageContainer storageContainer) { + public RolloutCacheManagerImpl(@NonNull SplitClientConfig splitClientConfig, @NonNull SplitStorageContainer storageContainer, + @NonNull CleanUpDatabaseTask cleanUpDatabaseTask, + @NonNull EncryptionMigrationTask encryptionMigrationTask) { this(storageContainer.getGeneralInfoStorage(), RolloutCacheManagerConfig.from(splitClientConfig), - splitTaskExecutor, + cleanUpDatabaseTask, + encryptionMigrationTask, storageContainer.getSplitsStorage(), storageContainer.getMySegmentsStorageContainer(), storageContainer.getMyLargeSegmentsStorageContainer()); } @VisibleForTesting - RolloutCacheManagerImpl(@NonNull GeneralInfoStorage generalInfoStorage, @NonNull RolloutCacheManagerConfig config, @NonNull SplitTaskExecutor splitTaskExecutor, @NonNull RolloutDefinitionsCache... storages) { + RolloutCacheManagerImpl(@NonNull GeneralInfoStorage generalInfoStorage, + @NonNull RolloutCacheManagerConfig config, + @NonNull CleanUpDatabaseTask clean, + @NonNull EncryptionMigrationTask encryptionMigrationTask, + @NonNull RolloutDefinitionsCache... storages) { mGeneralInfoStorage = checkNotNull(generalInfoStorage); + mCleanUpDatabaseTask = checkNotNull(clean); + mEncryptionMigrationTask = checkNotNull(encryptionMigrationTask); mStorages = checkNotNull(storages); mConfig = checkNotNull(config); - mTaskExecutor = checkNotNull(splitTaskExecutor); } + @WorkerThread @Override public void validateCache(SplitTaskExecutionListener listener) { - mTaskExecutor.submit(this, listener); + try { + Logger.v("Rollout cache manager: Executing clearing task"); + mCleanUpDatabaseTask.execute(); + Logger.v("Rollout cache manager: Validating cache"); + execute(); + Logger.v("Rollout cache manager: Migrating encryption"); + mEncryptionMigrationTask.execute(); + Logger.v("Rollout cache manager: validation finished"); + listener.taskExecuted(SplitTaskExecutionInfo.success(SplitTaskType.GENERIC_TASK)); + } catch (Exception ex) { + Logger.e("Error occurred validating cache: " + ex.getMessage()); + listener.taskExecuted(SplitTaskExecutionInfo.error(SplitTaskType.GENERIC_TASK)); + } } @NonNull 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 5f069bee9..b18fd0b50 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 @@ -10,6 +10,7 @@ import io.split.android.client.RetryBackoffCounterTimerFactory; import io.split.android.client.SplitClientConfig; +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.Impression; @@ -259,13 +260,13 @@ public void pushImpression(Impression impression) { } @Override - public void registerMySegmentsSynchronizer(String userKey, MySegmentsSynchronizer mySegmentsSynchronizer) { - mMySegmentsSynchronizerRegistry.registerMySegmentsSynchronizer(userKey, mySegmentsSynchronizer); + public void registerMySegmentsSynchronizer(Key key, MySegmentsSynchronizer mySegmentsSynchronizer) { + mMySegmentsSynchronizerRegistry.registerMySegmentsSynchronizer(key, mySegmentsSynchronizer); } @Override - public void unregisterMySegmentsSynchronizer(String userKey) { - mMySegmentsSynchronizerRegistry.unregisterMySegmentsSynchronizer(userKey); + public void unregisterMySegmentsSynchronizer(Key key) { + mMySegmentsSynchronizerRegistry.unregisterMySegmentsSynchronizer(key); } @Override diff --git a/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistry.java b/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistry.java index a6cd8c763..d91c47f06 100644 --- a/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistry.java +++ b/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistry.java @@ -1,8 +1,10 @@ package io.split.android.client.service.synchronizer.mysegments; +import io.split.android.client.api.Key; + public interface MySegmentsSynchronizerRegistry { - void registerMySegmentsSynchronizer(String userKey, MySegmentsSynchronizer mySegmentsSynchronizer); + void registerMySegmentsSynchronizer(Key key, MySegmentsSynchronizer mySegmentsSynchronizer); - void unregisterMySegmentsSynchronizer(String userKey); + void unregisterMySegmentsSynchronizer(Key key); } diff --git a/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistryImpl.java b/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistryImpl.java index 5aba4ce82..8007e570e 100644 --- a/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistryImpl.java +++ b/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistryImpl.java @@ -6,6 +6,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; +import io.split.android.client.api.Key; import io.split.android.client.service.mysegments.MySegmentUpdateParams; public class MySegmentsSynchronizerRegistryImpl implements MySegmentsSynchronizerRegistry, @@ -15,23 +16,23 @@ public class MySegmentsSynchronizerRegistryImpl implements MySegmentsSynchronize private final AtomicBoolean mSynchronizedSegments = new AtomicBoolean(false); private final AtomicBoolean mScheduledSegmentsSyncTask = new AtomicBoolean(false); private final AtomicBoolean mStoppedPeriodicFetching = new AtomicBoolean(false); - private final ConcurrentMap mMySegmentsSynchronizers = new ConcurrentHashMap<>(); + private final ConcurrentMap mMySegmentsSynchronizers = new ConcurrentHashMap<>(); @Override - public synchronized void registerMySegmentsSynchronizer(String userKey, MySegmentsSynchronizer mySegmentsSynchronizer) { - mMySegmentsSynchronizers.put(userKey, mySegmentsSynchronizer); + public synchronized void registerMySegmentsSynchronizer(Key key, MySegmentsSynchronizer mySegmentsSynchronizer) { + mMySegmentsSynchronizers.put(key, mySegmentsSynchronizer); triggerPendingActions(mySegmentsSynchronizer); } @Override - public synchronized void unregisterMySegmentsSynchronizer(String userKey) { - MySegmentsSynchronizer mySegmentsSynchronizer = mMySegmentsSynchronizers.get(userKey); + public synchronized void unregisterMySegmentsSynchronizer(Key key) { + MySegmentsSynchronizer mySegmentsSynchronizer = mMySegmentsSynchronizers.get(key); if (mySegmentsSynchronizer != null) { mySegmentsSynchronizer.stopPeriodicFetching(); mySegmentsSynchronizer.destroy(); } - mMySegmentsSynchronizers.remove(userKey); + mMySegmentsSynchronizers.remove(key); } @Override diff --git a/src/main/java/io/split/android/client/shared/ClientComponentsRegisterImpl.java b/src/main/java/io/split/android/client/shared/ClientComponentsRegisterImpl.java index de1d53414..af0dc3449 100644 --- a/src/main/java/io/split/android/client/shared/ClientComponentsRegisterImpl.java +++ b/src/main/java/io/split/android/client/shared/ClientComponentsRegisterImpl.java @@ -95,7 +95,7 @@ public void registerComponents(Key key, SplitEventsManager eventsManager, MySegm @Override public void unregisterComponentsForKey(Key key) { mAttributesSynchronizerRegistry.unregisterAttributesSynchronizer(key.matchingKey()); - mMySegmentsSynchronizerRegistry.unregisterMySegmentsSynchronizer(key.matchingKey()); + mMySegmentsSynchronizerRegistry.unregisterMySegmentsSynchronizer(key); mEventsManagerRegistry.unregisterEventsManager(key); if (isSyncEnabled()) { @@ -116,7 +116,7 @@ private void registerAttributesSynchronizer(Key key, SplitEventsManager eventsMa } private void registerMySegmentsSynchronizer(Key key, MySegmentsSynchronizer mySegmentsSynchronizer) { - mMySegmentsSynchronizerRegistry.registerMySegmentsSynchronizer(key.matchingKey(), + mMySegmentsSynchronizerRegistry.registerMySegmentsSynchronizer(key, mySegmentsSynchronizer); } diff --git a/src/main/java/io/split/android/client/storage/cipher/ApplyCipherTask.java b/src/main/java/io/split/android/client/storage/cipher/ApplyCipherTask.java index adb47d63a..e1e5928a1 100644 --- a/src/main/java/io/split/android/client/storage/cipher/ApplyCipherTask.java +++ b/src/main/java/io/split/android/client/storage/cipher/ApplyCipherTask.java @@ -49,6 +49,7 @@ public SplitTaskExecutionInfo execute() { mSplitDatabase.runInTransaction(new Runnable() { @Override public void run() { + updateAttributes(mSplitDatabase.attributesDao()); updateSplits(mSplitDatabase.splitDao()); updateSegments(mSplitDatabase.mySegmentDao()); updateLargeSegments(mSplitDatabase.myLargeSegmentDao()); @@ -56,7 +57,6 @@ public void run() { updateEvents(mSplitDatabase.eventDao()); updateImpressionsCount(mSplitDatabase.impressionsCountDao()); updateUniqueKeys(mSplitDatabase.uniqueKeysDao()); - updateAttributes(mSplitDatabase.attributesDao()); } }); @@ -87,7 +87,6 @@ private void updateAttributes(AttributesDao attributesDao) { private void updateUniqueKeys(UniqueKeysDao uniqueKeysDao) { List items = uniqueKeysDao.getAll(); - for (UniqueKeyEntity item : items) { String fromUserKey = mFromCipher.decrypt(item.getUserKey()); String fromFeatureList = mFromCipher.decrypt(item.getFeatureList()); diff --git a/src/test/java/io/split/android/client/SplitFactoryHelperTest.kt b/src/test/java/io/split/android/client/SplitFactoryHelperTest.kt index 7c123c8dd..7ff5e004b 100644 --- a/src/test/java/io/split/android/client/SplitFactoryHelperTest.kt +++ b/src/test/java/io/split/android/client/SplitFactoryHelperTest.kt @@ -1,10 +1,17 @@ package io.split.android.client import android.content.Context +import io.split.android.client.SplitFactoryHelper.Initializer.Listener +import io.split.android.client.events.EventsManagerCoordinator +import io.split.android.client.events.SplitInternalEvent +import io.split.android.client.lifecycle.SplitLifecycleManager +import io.split.android.client.service.executor.SplitSingleThreadTaskExecutor +import io.split.android.client.service.executor.SplitTaskExecutionInfo import io.split.android.client.service.executor.SplitTaskExecutionListener import io.split.android.client.service.executor.SplitTaskExecutor -import io.split.android.client.storage.cipher.EncryptionMigrationTask -import io.split.android.client.storage.db.SplitRoomDatabase +import io.split.android.client.service.executor.SplitTaskType +import io.split.android.client.service.synchronizer.RolloutCacheManager +import io.split.android.client.service.synchronizer.SyncManager import junit.framework.TestCase.assertEquals import org.junit.After import org.junit.Before @@ -12,7 +19,6 @@ import org.junit.Test import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.any -import org.mockito.Mockito.argThat import org.mockito.Mockito.mock import org.mockito.Mockito.verify import org.mockito.Mockito.`when` @@ -24,12 +30,6 @@ class SplitFactoryHelperTest { private lateinit var mocks: AutoCloseable - @Mock - private lateinit var splitRoomDatabase: SplitRoomDatabase - @Mock - private lateinit var splitTaskExecutor: SplitTaskExecutor - @Mock - private lateinit var taskListener: SplitTaskExecutionListener @Mock private lateinit var context: Context @@ -46,22 +46,6 @@ class SplitFactoryHelperTest { mocks.close() } - @Test - fun migrateEncryption() { - - helper.migrateEncryption( - "abcdedfghijklmnopqrstuvwxyz", - splitRoomDatabase, - splitTaskExecutor, - true, - taskListener, - ) - - verify(splitTaskExecutor).submit( - argThat { it is EncryptionMigrationTask }, - argThat { it?.equals(taskListener) == true }) - } - @Test fun generateDatabaseNameWithoutPrefixAndKeyLongerThan4() { val path = mock(File::class.java) @@ -152,4 +136,44 @@ class SplitFactoryHelperTest { verify(existingPath).renameTo(nonExistingPath) assertEquals("abcdwxyz", databaseName) } + + @Test + fun `Initializer test`() { + val rolloutCacheManager = mock(RolloutCacheManager::class.java) + val splitTaskExecutionListener = mock(SplitTaskExecutionListener::class.java) + + val initializer = SplitFactoryHelper.Initializer( + rolloutCacheManager, + splitTaskExecutionListener + ) + + initializer.run() + + verify(rolloutCacheManager).validateCache(splitTaskExecutionListener) + } + + @Test + fun `Initializer Listener test`() { + val eventsManagerCoordinator = mock(EventsManagerCoordinator::class.java) + val taskExecutor = mock(SplitTaskExecutor::class.java) + val singleThreadTaskExecutor = mock(SplitSingleThreadTaskExecutor::class.java) + val syncManager = mock(SyncManager::class.java) + val lifecycleManager = mock(SplitLifecycleManager::class.java) + + val listener = Listener( + eventsManagerCoordinator, + taskExecutor, + singleThreadTaskExecutor, + syncManager, + lifecycleManager + ) + + listener.taskExecuted(SplitTaskExecutionInfo.success(SplitTaskType.GENERIC_TASK)) + + verify(eventsManagerCoordinator).notifyInternalEvent(SplitInternalEvent.ENCRYPTION_MIGRATION_DONE) + verify(taskExecutor).resume() + verify(singleThreadTaskExecutor).resume() + verify(syncManager).start() + verify(lifecycleManager).register(syncManager) + } } diff --git a/src/test/java/io/split/android/client/attributes/AttributesManagerImplTest.java b/src/test/java/io/split/android/client/attributes/AttributesManagerImplTest.java index 1e2bb618e..afcd377ed 100644 --- a/src/test/java/io/split/android/client/attributes/AttributesManagerImplTest.java +++ b/src/test/java/io/split/android/client/attributes/AttributesManagerImplTest.java @@ -92,7 +92,7 @@ public void setAttributeLaunchesAttributeUpdateTaskIfValueIsValid() { attributeClient.setAttribute(name, attribute); verify(attributeTaskFactory).createAttributeUpdateTask(persistentAttributesStorage, attributeMap); - verify(splitTaskExecutor).submit(updateAttributesInPersistentStorageTask, null); + verify(splitTaskExecutor).schedule(updateAttributesInPersistentStorageTask, 5L, null); } @Test @@ -170,7 +170,7 @@ public void setAttributesLaunchesAttributeUpdateTaskIfValuesAreValid() { attributeClient.setAttributes(attributeMap); verify(attributeTaskFactory).createAttributeUpdateTask(persistentAttributesStorage, attributeMap); - verify(splitTaskExecutor).submit(updateAttributesInPersistentStorageTask, null); + verify(splitTaskExecutor).schedule(updateAttributesInPersistentStorageTask, 5L, null); } @Test @@ -225,7 +225,7 @@ public void clearLaunchesAttributeClearTask() { attributeClient.clearAttributes(); verify(attributeTaskFactory).createAttributeClearTask(persistentAttributesStorage); - verify(splitTaskExecutor).submit(clearAttributesInPersistentStorageTask, null); + verify(splitTaskExecutor).schedule(clearAttributesInPersistentStorageTask, 5L, null); } @Test @@ -250,7 +250,7 @@ public void removeLaunchesAttributeUpdateTask() { attributeClient.removeAttribute("key"); verify(attributeTaskFactory).createAttributeUpdateTask(persistentAttributesStorage, attributeMap); - verify(splitTaskExecutor).submit(updateAttributesInPersistentStorageTask, null); + verify(splitTaskExecutor).schedule(updateAttributesInPersistentStorageTask, 5L, null); } private Map getDefaultValues() { 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 c0c32eabb..58b19db94 100644 --- a/src/test/java/io/split/android/client/service/SynchronizerTest.java +++ b/src/test/java/io/split/android/client/service/SynchronizerTest.java @@ -39,6 +39,7 @@ import io.split.android.client.RetryBackoffCounterTimerFactory; import io.split.android.client.SplitClientConfig; +import io.split.android.client.api.Key; import io.split.android.client.dtos.Event; import io.split.android.client.dtos.KeyImpression; import io.split.android.client.dtos.SplitChange; @@ -243,7 +244,7 @@ public void splitExecutorSchedule() { verify(mWorkManagerWrapper).removeWork(); verify(mWorkManagerWrapper, never()).scheduleWork(); - mSynchronizer.unregisterMySegmentsSynchronizer("userKey"); + mSynchronizer.unregisterMySegmentsSynchronizer(new Key("userKey")); } @Test @@ -544,7 +545,7 @@ public void loadLocalData() { when(loadMySegmentsTask.execute()).thenReturn(SplitTaskExecutionInfo.success(SplitTaskType.LOAD_LOCAL_MY_SEGMENTS)); when(mMySegmentsTaskFactory.createLoadMySegmentsTask()).thenReturn(loadMySegmentsTask); - ((MySegmentsSynchronizerRegistry) mSynchronizer).registerMySegmentsSynchronizer("", mMySegmentsSynchronizer); + ((MySegmentsSynchronizerRegistry) mSynchronizer).registerMySegmentsSynchronizer(new Key(""), mMySegmentsSynchronizer); mSynchronizer.loadAndSynchronizeSplits(); mSynchronizer.loadMySegmentsFromCache(); @@ -775,9 +776,9 @@ public void reschedulingEventsTaskCancelsPreviousWhenCallingSequentially() { public void registerMySegmentsSynchronizerDelegatesToRegistry() { setup(SplitClientConfig.builder().synchronizeInBackground(false).build()); - mSynchronizer.registerMySegmentsSynchronizer("userKey", mMySegmentsSynchronizer); + mSynchronizer.registerMySegmentsSynchronizer(new Key("userKey"), mMySegmentsSynchronizer); - verify(mMySegmentsSynchronizerRegistry).registerMySegmentsSynchronizer("userKey", mMySegmentsSynchronizer); + verify(mMySegmentsSynchronizerRegistry).registerMySegmentsSynchronizer(new Key("userKey"), mMySegmentsSynchronizer); } @Test diff --git a/src/test/java/io/split/android/client/service/synchronizer/RolloutCacheManagerTest.kt b/src/test/java/io/split/android/client/service/synchronizer/RolloutCacheManagerTest.kt index 41dcc6422..20e1b2c24 100644 --- a/src/test/java/io/split/android/client/service/synchronizer/RolloutCacheManagerTest.kt +++ b/src/test/java/io/split/android/client/service/synchronizer/RolloutCacheManagerTest.kt @@ -1,15 +1,14 @@ package io.split.android.client.service.synchronizer +import io.split.android.client.service.CleanUpDatabaseTask import io.split.android.client.service.executor.SplitTaskExecutionListener -import io.split.android.client.service.executor.SplitTaskExecutor import io.split.android.client.storage.RolloutDefinitionsCache +import io.split.android.client.storage.cipher.EncryptionMigrationTask import io.split.android.client.storage.general.GeneralInfoStorage -import io.split.android.fake.SplitTaskExecutorStub import org.junit.Before import org.junit.Test import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyLong -import org.mockito.ArgumentMatchers.argThat import org.mockito.Mockito.longThat import org.mockito.Mockito.mock import org.mockito.Mockito.times @@ -21,14 +20,16 @@ class RolloutCacheManagerTest { private lateinit var mRolloutCacheManager: RolloutCacheManager private lateinit var mGeneralInfoStorage: GeneralInfoStorage - private lateinit var mSplitTaskExecutor: SplitTaskExecutor private lateinit var mSplitsCache: RolloutDefinitionsCache private lateinit var mSegmentsCache: RolloutDefinitionsCache + private lateinit var mEncryptionMigrationTask: EncryptionMigrationTask + private lateinit var mCleanUpDatabaseTask: CleanUpDatabaseTask @Before fun setup() { mGeneralInfoStorage = mock(GeneralInfoStorage::class.java) - mSplitTaskExecutor = SplitTaskExecutorStub() + mEncryptionMigrationTask = mock(EncryptionMigrationTask::class.java) + mCleanUpDatabaseTask = mock(CleanUpDatabaseTask::class.java) mSplitsCache = mock(RolloutDefinitionsCache::class.java) mSegmentsCache = mock(RolloutDefinitionsCache::class.java) } @@ -132,8 +133,26 @@ class RolloutCacheManagerTest { verify(mGeneralInfoStorage, times(0)).setRolloutCacheLastClearTimestamp(anyLong()) } + @Test + fun `validateCache executes cleanUpDatabaseTask`() { + mRolloutCacheManager = getCacheManager(10L, false) + + mRolloutCacheManager.validateCache(mock(SplitTaskExecutionListener::class.java)) + + verify(mCleanUpDatabaseTask).execute() + } + + @Test + fun `validateCache executes encryptionMigrationTask`() { + mRolloutCacheManager = getCacheManager(10L, false) + + mRolloutCacheManager.validateCache(mock(SplitTaskExecutionListener::class.java)) + + verify(mEncryptionMigrationTask).execute() + } + private fun getCacheManager(expiration: Long, clearOnInit: Boolean): RolloutCacheManager { - return RolloutCacheManagerImpl(mGeneralInfoStorage, RolloutCacheManagerConfig(expiration, clearOnInit), mSplitTaskExecutor, mSplitsCache, mSegmentsCache) + return RolloutCacheManagerImpl(mGeneralInfoStorage, RolloutCacheManagerConfig(expiration, clearOnInit), mCleanUpDatabaseTask, mEncryptionMigrationTask, mSplitsCache, mSegmentsCache) } private fun createMockedTimestamp(period: Long): Long { diff --git a/src/test/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistryImplTest.java b/src/test/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistryImplTest.java index 9052de36a..a24909553 100644 --- a/src/test/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistryImplTest.java +++ b/src/test/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistryImplTest.java @@ -6,6 +6,7 @@ import org.junit.Before; import org.junit.Test; +import io.split.android.client.api.Key; import io.split.android.client.service.mysegments.MySegmentUpdateParams; public class MySegmentsSynchronizerRegistryImplTest { @@ -21,7 +22,7 @@ public void setUp() { public void loadMySegmentsFromCacheGetCalledInEveryRegisteredSync() { MySegmentsSynchronizer syncMock = mock(MySegmentsSynchronizer.class); - mRegistry.registerMySegmentsSynchronizer("key", syncMock); + mRegistry.registerMySegmentsSynchronizer(new Key("key"), syncMock); mRegistry.loadMySegmentsFromCache(); verify(syncMock).loadMySegmentsFromCache(); @@ -31,7 +32,7 @@ public void loadMySegmentsFromCacheGetCalledInEveryRegisteredSync() { public void synchronizeMySegmentsGetCalledInEveryRegisteredSync() { MySegmentsSynchronizer syncMock = mock(MySegmentsSynchronizer.class); - mRegistry.registerMySegmentsSynchronizer("key", syncMock); + mRegistry.registerMySegmentsSynchronizer(new Key("key"), syncMock); mRegistry.synchronizeMySegments(); verify(syncMock).synchronizeMySegments(); @@ -42,7 +43,7 @@ public void forceMySegmentsSyncGetCalledInEveryRegisteredSync() { MySegmentsSynchronizer syncMock = mock(MySegmentsSynchronizer.class); MySegmentUpdateParams params = new MySegmentUpdateParams(4L, 1L, 2L); - mRegistry.registerMySegmentsSynchronizer("key", syncMock); + mRegistry.registerMySegmentsSynchronizer(new Key("key"), syncMock); mRegistry.forceMySegmentsSync(params); verify(syncMock).forceMySegmentsSync(params); @@ -52,7 +53,7 @@ public void forceMySegmentsSyncGetCalledInEveryRegisteredSync() { public void destroyGetCalledInEveryRegisteredSync() { MySegmentsSynchronizer syncMock = mock(MySegmentsSynchronizer.class); - mRegistry.registerMySegmentsSynchronizer("key", syncMock); + mRegistry.registerMySegmentsSynchronizer(new Key("key"), syncMock); mRegistry.destroy(); verify(syncMock).destroy(); @@ -62,7 +63,7 @@ public void destroyGetCalledInEveryRegisteredSync() { public void scheduleSegmentsSyncTaskGetCalledInEveryRegisteredSync() { MySegmentsSynchronizer syncMock = mock(MySegmentsSynchronizer.class); - mRegistry.registerMySegmentsSynchronizer("key", syncMock); + mRegistry.registerMySegmentsSynchronizer(new Key("key"), syncMock); mRegistry.scheduleSegmentsSyncTask(); verify(syncMock).scheduleSegmentsSyncTask(); @@ -72,7 +73,7 @@ public void scheduleSegmentsSyncTaskGetCalledInEveryRegisteredSync() { public void submitMySegmentsLoadingTaskGetCalledInEveryRegisteredSync() { MySegmentsSynchronizer syncMock = mock(MySegmentsSynchronizer.class); - mRegistry.registerMySegmentsSynchronizer("key", syncMock); + mRegistry.registerMySegmentsSynchronizer(new Key("key"), syncMock); mRegistry.submitMySegmentsLoadingTask(); verify(syncMock).submitMySegmentsLoadingTask(); @@ -82,7 +83,7 @@ public void submitMySegmentsLoadingTaskGetCalledInEveryRegisteredSync() { public void stopPeriodicFetchingGetCalledInEveryRegisteredSync() { MySegmentsSynchronizer syncMock = mock(MySegmentsSynchronizer.class); - mRegistry.registerMySegmentsSynchronizer("key", syncMock); + mRegistry.registerMySegmentsSynchronizer(new Key("key"), syncMock); mRegistry.stopPeriodicFetching(); verify(syncMock).stopPeriodicFetching(); @@ -92,8 +93,8 @@ public void stopPeriodicFetchingGetCalledInEveryRegisteredSync() { public void unregisterStopsTasksBeforeRemovingSync() { MySegmentsSynchronizer syncMock = mock(MySegmentsSynchronizer.class); - mRegistry.registerMySegmentsSynchronizer("key", syncMock); - mRegistry.unregisterMySegmentsSynchronizer("key"); + mRegistry.registerMySegmentsSynchronizer(new Key("key"), syncMock); + mRegistry.unregisterMySegmentsSynchronizer(new Key("key")); verify(syncMock).stopPeriodicFetching(); verify(syncMock).destroy(); @@ -105,10 +106,10 @@ public void callLoadSegmentsFromCacheForNewlyRegisteredSyncIfNecessary() { MySegmentsSynchronizer syncMock2 = mock(MySegmentsSynchronizer.class); MySegmentsSynchronizer syncMock3 = mock(MySegmentsSynchronizer.class); - mRegistry.registerMySegmentsSynchronizer("key", syncMock); + mRegistry.registerMySegmentsSynchronizer(new Key("key"), syncMock); mRegistry.loadMySegmentsFromCache(); - mRegistry.registerMySegmentsSynchronizer("new_key", syncMock2); - mRegistry.registerMySegmentsSynchronizer("new_key", syncMock3); + mRegistry.registerMySegmentsSynchronizer(new Key("new_key"), syncMock2); + mRegistry.registerMySegmentsSynchronizer(new Key("new_key"), syncMock3); verify(syncMock2).loadMySegmentsFromCache(); verify(syncMock3).loadMySegmentsFromCache(); @@ -120,10 +121,10 @@ public void callSynchronizeMySegmentsForNewlyRegisteredSyncIfNecessary() { MySegmentsSynchronizer syncMock2 = mock(MySegmentsSynchronizer.class); MySegmentsSynchronizer syncMock3 = mock(MySegmentsSynchronizer.class); - mRegistry.registerMySegmentsSynchronizer("key", syncMock); + mRegistry.registerMySegmentsSynchronizer(new Key("key"), syncMock); mRegistry.synchronizeMySegments(); - mRegistry.registerMySegmentsSynchronizer("new_key", syncMock2); - mRegistry.registerMySegmentsSynchronizer("new_key", syncMock3); + mRegistry.registerMySegmentsSynchronizer(new Key("new_key"), syncMock2); + mRegistry.registerMySegmentsSynchronizer(new Key("new_key"), syncMock3); verify(syncMock2).synchronizeMySegments(); verify(syncMock3).synchronizeMySegments(); @@ -135,10 +136,10 @@ public void callScheduleSegmentsSyncTaskForNewlyRegisteredSyncIfNecessary() { MySegmentsSynchronizer syncMock2 = mock(MySegmentsSynchronizer.class); MySegmentsSynchronizer syncMock3 = mock(MySegmentsSynchronizer.class); - mRegistry.registerMySegmentsSynchronizer("key", syncMock); + mRegistry.registerMySegmentsSynchronizer(new Key("key"), syncMock); mRegistry.scheduleSegmentsSyncTask(); - mRegistry.registerMySegmentsSynchronizer("new_key", syncMock2); - mRegistry.registerMySegmentsSynchronizer("new_key", syncMock3); + mRegistry.registerMySegmentsSynchronizer(new Key("new_key"), syncMock2); + mRegistry.registerMySegmentsSynchronizer(new Key("new_key"), syncMock3); verify(syncMock2).scheduleSegmentsSyncTask(); verify(syncMock3).scheduleSegmentsSyncTask(); diff --git a/src/test/java/io/split/android/client/shared/ClientComponentsRegisterImplTest.java b/src/test/java/io/split/android/client/shared/ClientComponentsRegisterImplTest.java index fac2fbd93..b78c13cd1 100644 --- a/src/test/java/io/split/android/client/shared/ClientComponentsRegisterImplTest.java +++ b/src/test/java/io/split/android/client/shared/ClientComponentsRegisterImplTest.java @@ -91,7 +91,7 @@ public void attributesSynchronizerIsRegistered() { public void mySegmentsSynchronizerIsRegistered() { register.registerComponents(mMatchingKey, mSplitEventsManager, mMySegmentsTaskFactory); - verify(mMySegmentsSynchronizerRegistry).registerMySegmentsSynchronizer("matching_key", mMySegmentsSynchronizer); + verify(mMySegmentsSynchronizerRegistry).registerMySegmentsSynchronizer(new Key("matching_key", "bucketing_key"), mMySegmentsSynchronizer); } @Test @@ -120,7 +120,7 @@ public void componentsAreCorrectlyUnregistered() { register.unregisterComponentsForKey(mMatchingKey); verify(mAttributesSynchronizerRegistry).unregisterAttributesSynchronizer("matching_key"); - verify(mMySegmentsSynchronizerRegistry).unregisterMySegmentsSynchronizer("matching_key"); + verify(mMySegmentsSynchronizerRegistry).unregisterMySegmentsSynchronizer(new Key("matching_key", "bucketing_key")); verify(mMySegmentsUpdateWorkerRegistry).unregisterMySegmentsUpdateWorker("matching_key"); verify(mMySegmentsNotificationProcessorRegistry).unregisterMembershipsProcessor("matching_key"); verify(mEventsManagerRegistry).unregisterEventsManager(mMatchingKey);