From d1a6582ad7aeeec1f8a440bee58b598d90275f2a Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 5 Sep 2019 14:39:18 -0400 Subject: [PATCH] Support independent application migration versions. --- .../securesms/ApplicationContext.java | 21 +++- .../PassphraseRequiredActionBarActivity.java | 10 +- .../database/helpers/SQLCipherOpenHelper.java | 4 + .../securesms/jobmanager/JobController.java | 2 +- .../securesms/jobmanager/JobManager.java | 2 + .../securesms/jobs/FastJobStorage.java | 2 +- .../logsubmit/SubmitLogFragment.java | 27 +++-- .../ApplicationMigrationActivity.java | 2 +- .../migrations/ApplicationMigrations.java | 113 +++++++++++------- .../securesms/util/TextSecurePreferences.java | 21 ++++ .../securesms/jobs/FastJobStorageTest.java | 17 +++ 11 files changed, 158 insertions(+), 63 deletions(-) diff --git a/src/org/thoughtcrime/securesms/ApplicationContext.java b/src/org/thoughtcrime/securesms/ApplicationContext.java index 206e34e6dc..f87ecfd3c1 100644 --- a/src/org/thoughtcrime/securesms/ApplicationContext.java +++ b/src/org/thoughtcrime/securesms/ApplicationContext.java @@ -36,6 +36,7 @@ import org.signal.aesgcmprovider.AesGcmProvider; import org.thoughtcrime.securesms.components.TypingStatusRepository; import org.thoughtcrime.securesms.components.TypingStatusSender; import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencyProvider; import org.thoughtcrime.securesms.gcm.FcmJobService; @@ -69,6 +70,7 @@ import org.thoughtcrime.securesms.service.RotateSenderCertificateListener; import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener; import org.thoughtcrime.securesms.service.UpdateApkRefreshListener; import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.VersionTracker; import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper; import org.webrtc.PeerConnectionFactory; import org.webrtc.PeerConnectionFactory.InitializationOptions; @@ -94,7 +96,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi private static final String TAG = ApplicationContext.class.getSimpleName(); private ExpiringMessageManager expiringMessageManager; - private ViewOnceMessageManager viewOnceMessageManager; + private ViewOnceMessageManager viewOnceMessageManager; private TypingStatusRepository typingStatusRepository; private TypingStatusSender typingStatusSender; private JobManager jobManager; @@ -114,6 +116,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi initializeSecurityProvider(); initializeLogging(); initializeCrashHandling(); + initializeFirstEverAppLaunch(); initializeAppDependencies(); initializeJobManager(); initializeApplicationMigrations(); @@ -223,7 +226,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi .setConstraintFactories(JobManagerFactories.getConstraintFactories(this)) .setConstraintObservers(JobManagerFactories.getConstraintObservers(this)) .setJobStorage(new FastJobStorage(DatabaseFactory.getJobDatabase(this))) - .setJobMigrator(new JobMigrator(TextSecurePreferences.getJobManagerVersion(this), 2, JobManagerFactories.getJobMigrations(this))) + .setJobMigrator(new JobMigrator(TextSecurePreferences.getJobManagerVersion(this), JobManager.CURRENT_VERSION, JobManagerFactories.getJobMigrations(this))) .build()); } @@ -239,6 +242,20 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi ApplicationDependencies.init(this, new ApplicationDependencyProvider(this, new SignalServiceNetworkAccess(this))); } + private void initializeFirstEverAppLaunch() { + if (TextSecurePreferences.getFirstInstallVersion(this) == -1) { + if (!SQLCipherOpenHelper.databaseFileExists(this)) { + Log.i(TAG, "First ever app launch!"); + + TextSecurePreferences.setAppMigrationVersion(this, ApplicationMigrations.CURRENT_VERSION); + TextSecurePreferences.setJobManagerVersion(this, JobManager.CURRENT_VERSION); + } + + Log.i(TAG, "Setting first install version to " + BuildConfig.CANONICAL_VERSION_CODE); + TextSecurePreferences.setFirstInstallVersion(this, BuildConfig.CANONICAL_VERSION_CODE); + } + } + private void initializeGcmCheck() { if (TextSecurePreferences.isPushRegistered(this)) { long nextSetTime = TextSecurePreferences.getFcmTokenLastSetTime(this) + TimeUnit.HOURS.toMillis(6); diff --git a/src/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java b/src/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java index ce2e48871e..60c989fb7e 100644 --- a/src/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java +++ b/src/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java @@ -30,7 +30,7 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA private static final int STATE_NORMAL = 0; private static final int STATE_CREATE_PASSPHRASE = 1; private static final int STATE_PROMPT_PASSPHRASE = 2; - private static final int STATE_UPGRADE_DATABASE = 3; + private static final int STATE_UI_BLOCKING_UPGRADE = 3; private static final int STATE_PROMPT_PUSH_REGISTRATION = 4; private static final int STATE_EXPERIENCE_UPGRADE = 5; private static final int STATE_WELCOME_SCREEN = 6; @@ -133,7 +133,7 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA switch (state) { case STATE_CREATE_PASSPHRASE: return getCreatePassphraseIntent(); case STATE_PROMPT_PASSPHRASE: return getPromptPassphraseIntent(); - case STATE_UPGRADE_DATABASE: return getUpgradeDatabaseIntent(); + case STATE_UI_BLOCKING_UPGRADE: return getUiBlockingUpgradeIntent(); case STATE_WELCOME_SCREEN: return getWelcomeIntent(); case STATE_PROMPT_PUSH_REGISTRATION: return getPushRegistrationIntent(); case STATE_EXPERIENCE_UPGRADE: return getExperienceUpgradeIntent(); @@ -146,8 +146,8 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA return STATE_CREATE_PASSPHRASE; } else if (locked) { return STATE_PROMPT_PASSPHRASE; - } else if (ApplicationMigrations.isUpdate(this)) { - return STATE_UPGRADE_DATABASE; + } else if (ApplicationMigrations.isUpdate(this) && ApplicationMigrations.isUiBlockingMigrationRunning()) { + return STATE_UI_BLOCKING_UPGRADE; } else if (!TextSecurePreferences.hasSeenWelcomeScreen(this)) { return STATE_WELCOME_SCREEN; } else if (!TextSecurePreferences.hasPromptedPushRegistration(this)) { @@ -167,7 +167,7 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA return getRoutedIntent(PassphrasePromptActivity.class, getIntent()); } - private Intent getUpgradeDatabaseIntent() { + private Intent getUiBlockingUpgradeIntent() { return getRoutedIntent(ApplicationMigrationActivity.class, TextSecurePreferences.hasPromptedPushRegistration(this) ? getConversationListIntent() diff --git a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 6e3aafe20e..baf6c23fbf 100644 --- a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -535,6 +535,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { db.setVersion(DATABASE_VERSION); } + public static boolean databaseFileExists(@NonNull Context context) { + return context.getDatabasePath(DATABASE_NAME).exists(); + } + private void executeStatements(SQLiteDatabase db, String[] statements) { for (String statement : statements) db.execSQL(statement); diff --git a/src/org/thoughtcrime/securesms/jobmanager/JobController.java b/src/org/thoughtcrime/securesms/jobmanager/JobController.java index 7ae55f0639..722a27fe65 100644 --- a/src/org/thoughtcrime/securesms/jobmanager/JobController.java +++ b/src/org/thoughtcrime/securesms/jobmanager/JobController.java @@ -261,7 +261,7 @@ class JobController { JobSpec jobSpec = new JobSpec(job.getId(), job.getFactoryKey(), job.getParameters().getQueue(), - job.getParameters().getCreateTime(), + System.currentTimeMillis(), job.getNextRunAttemptTime(), job.getRunAttempt(), job.getParameters().getMaxAttempts(), diff --git a/src/org/thoughtcrime/securesms/jobmanager/JobManager.java b/src/org/thoughtcrime/securesms/jobmanager/JobManager.java index 6f57639bc5..2dca45233e 100644 --- a/src/org/thoughtcrime/securesms/jobmanager/JobManager.java +++ b/src/org/thoughtcrime/securesms/jobmanager/JobManager.java @@ -33,6 +33,8 @@ public class JobManager implements ConstraintObserver.Notifier { private static final String TAG = JobManager.class.getSimpleName(); + public static final int CURRENT_VERSION = 2; + private final Application application; private final Configuration configuration; private final ExecutorService executor; diff --git a/src/org/thoughtcrime/securesms/jobs/FastJobStorage.java b/src/org/thoughtcrime/securesms/jobs/FastJobStorage.java index 4423ce7502..80360d264a 100644 --- a/src/org/thoughtcrime/securesms/jobs/FastJobStorage.java +++ b/src/org/thoughtcrime/securesms/jobs/FastJobStorage.java @@ -93,7 +93,7 @@ public class FastJobStorage implements JobStorage { public synchronized @NonNull List getPendingJobsWithNoDependenciesInCreatedOrder(long currentTime) { Optional migrationJob = getMigrationJob(); - if (migrationJob.isPresent() && !migrationJob.get().isRunning()) { + if (migrationJob.isPresent() && !migrationJob.get().isRunning() && migrationJob.get().getNextRunAttemptTime() <= currentTime) { return Collections.singletonList(migrationJob.get()); } else if (migrationJob.isPresent()) { return Collections.emptyList(); diff --git a/src/org/thoughtcrime/securesms/logsubmit/SubmitLogFragment.java b/src/org/thoughtcrime/securesms/logsubmit/SubmitLogFragment.java index eed5c4c96d..24e71b03be 100644 --- a/src/org/thoughtcrime/securesms/logsubmit/SubmitLogFragment.java +++ b/src/org/thoughtcrime/securesms/logsubmit/SubmitLogFragment.java @@ -59,6 +59,7 @@ import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logsubmit.util.Scrubber; import org.thoughtcrime.securesms.util.BucketInfo; +import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; @@ -513,19 +514,19 @@ public class SubmitLogFragment extends Fragment { final PackageManager pm = context.getPackageManager(); final StringBuilder builder = new StringBuilder(); - builder.append("Time : ").append(System.currentTimeMillis()).append('\n'); - builder.append("Device : ") - .append(Build.MANUFACTURER).append(" ") - .append(Build.MODEL).append(" (") - .append(Build.PRODUCT).append(")\n"); - builder.append("Android : ").append(VERSION.RELEASE).append(" (") - .append(VERSION.INCREMENTAL).append(", ") - .append(Build.DISPLAY).append(")\n"); - builder.append("ABIs : ").append(TextUtils.join(", ", getSupportedAbis())).append("\n"); - builder.append("Memory : ").append(getMemoryUsage(context)).append("\n"); - builder.append("Memclass: ").append(getMemoryClass(context)).append("\n"); - builder.append("OS Host : ").append(Build.HOST).append("\n"); - builder.append("App : "); + builder.append("Time : ").append(System.currentTimeMillis()).append('\n'); + builder.append("Device : ").append(Build.MANUFACTURER).append(" ") + .append(Build.MODEL).append(" (") + .append(Build.PRODUCT).append(")\n"); + builder.append("Android : ").append(VERSION.RELEASE).append(" (") + .append(VERSION.INCREMENTAL).append(", ") + .append(Build.DISPLAY).append(")\n"); + builder.append("ABIs : ").append(TextUtils.join(", ", getSupportedAbis())).append("\n"); + builder.append("Memory : ").append(getMemoryUsage(context)).append("\n"); + builder.append("Memclass : ").append(getMemoryClass(context)).append("\n"); + builder.append("OS Host : ").append(Build.HOST).append("\n"); + builder.append("First Version: ").append(TextSecurePreferences.getFirstInstallVersion(context)).append("\n"); + builder.append("App : "); try { builder.append(pm.getApplicationLabel(pm.getApplicationInfo(context.getPackageName(), 0))) .append(" ") diff --git a/src/org/thoughtcrime/securesms/migrations/ApplicationMigrationActivity.java b/src/org/thoughtcrime/securesms/migrations/ApplicationMigrationActivity.java index 4273d06653..2d7184d301 100644 --- a/src/org/thoughtcrime/securesms/migrations/ApplicationMigrationActivity.java +++ b/src/org/thoughtcrime/securesms/migrations/ApplicationMigrationActivity.java @@ -35,7 +35,7 @@ public class ApplicationMigrationActivity extends BaseActivity { public void onCreate(Bundle bundle) { super.onCreate(bundle); - ApplicationMigrations.isUiBlockingMigrationRunning().observe(this, running -> { + ApplicationMigrations.getUiBlockingMigrationStatus().observe(this, running -> { if (running == null) { return; } diff --git a/src/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java b/src/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java index 0485ed7fd2..a2ba9bdb0c 100644 --- a/src/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java +++ b/src/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java @@ -6,19 +6,17 @@ import androidx.annotation.NonNull; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; -import com.annimon.stream.Stream; - import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; -import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.VersionTracker; -import java.util.LinkedList; -import java.util.List; +import java.util.LinkedHashMap; +import java.util.Map; /** * Manages application-level migrations. @@ -38,10 +36,14 @@ public class ApplicationMigrations { private static final MutableLiveData UI_BLOCKING_MIGRATION_RUNNING = new MutableLiveData<>(); + private static final int LEGACY_CANONICAL_VERSION = 455; + + public static final int CURRENT_VERSION = 3; + private static final class Version { - static final int LEGACY = 455; - static final int RECIPIENT_ID = 525; // TODO [greyson] USE PROPER APPLICATION VERSION - static final int RECIPIENT_SEARCH = 525; // TODO [greyson] USE PROPER APPLICATION VERSION + static final int LEGACY = 1; + static final int RECIPIENT_ID = 2; + static final int RECIPIENT_SEARCH = 3; } /** @@ -50,58 +52,80 @@ public class ApplicationMigrations { * executing before we add the migration jobs. */ public static void onApplicationCreate(@NonNull Context context, @NonNull JobManager jobManager) { + if (isLegacyUpdate(context)) { + Log.i(TAG, "Detected the need for a legacy update. Last seen canonical version: " + VersionTracker.getLastSeenVersion(context)); + TextSecurePreferences.setAppMigrationVersion(context, 0); + } + if (!isUpdate(context)) { Log.d(TAG, "Not an update. Skipping."); return; } - final int currentVersion = Util.getCanonicalVersionCode(); - final int lastSeenVersion = VersionTracker.getLastSeenVersion(context); + final int lastSeenVersion = TextSecurePreferences.getAppMigrationVersion(context); + Log.d(TAG, "currentVersion: " + CURRENT_VERSION + ", lastSeenVersion: " + lastSeenVersion); - Log.d(TAG, "currentVersion: " + currentVersion + " lastSeenVersion: " + lastSeenVersion); - - List migrationJobs = getMigrationJobs(context, lastSeenVersion); + LinkedHashMap migrationJobs = getMigrationJobs(context, lastSeenVersion); if (migrationJobs.size() > 0) { Log.i(TAG, "About to enqueue " + migrationJobs.size() + " migration(s)."); - boolean uiBlocking = Stream.of(migrationJobs).reduce(false, (existing, job) -> existing || job.isUiBlocking()); - UI_BLOCKING_MIGRATION_RUNNING.postValue(uiBlocking); + boolean uiBlocking = true; + int uiBlockingVersion = lastSeenVersion; - if (uiBlocking) { - Log.i(TAG, "Migration set is UI-blocking."); + for (Map.Entry entry : migrationJobs.entrySet()) { + int version = entry.getKey(); + MigrationJob job = entry.getValue(); + + uiBlocking &= job.isUiBlocking(); + if (uiBlocking) { + uiBlockingVersion = version; + } + + jobManager.add(job); + jobManager.add(new MigrationCompleteJob(version)); + } + + if (uiBlockingVersion > lastSeenVersion) { + Log.i(TAG, "Migration set is UI-blocking through version " + uiBlockingVersion + "."); + UI_BLOCKING_MIGRATION_RUNNING.setValue(true); } else { Log.i(TAG, "Migration set is non-UI-blocking."); + UI_BLOCKING_MIGRATION_RUNNING.setValue(false); } - for (MigrationJob job : migrationJobs) { - jobManager.add(job); - } - - jobManager.add(new MigrationCompleteJob(currentVersion)); - final long startTime = System.currentTimeMillis(); + final int uiVersion = uiBlockingVersion; EventBus.getDefault().register(new Object() { @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) public void onMigrationComplete(MigrationCompleteEvent event) { - Log.i(TAG, "Received MigrationCompleteEvent for version " + event.getVersion() + "."); + Log.i(TAG, "Received MigrationCompleteEvent for version " + event.getVersion() + ". (Current: " + CURRENT_VERSION + ")"); - if (event.getVersion() == currentVersion) { + if (event.getVersion() > CURRENT_VERSION) { + throw new AssertionError("Received a higher version than the current version? App downgrades are not supported. (received: " + event.getVersion() + ", current: " + CURRENT_VERSION + ")"); + } + + Log.i(TAG, "Updating last migration version to " + event.getVersion()); + TextSecurePreferences.setAppMigrationVersion(context, event.getVersion()); + + if (event.getVersion() == CURRENT_VERSION) { Log.i(TAG, "Migration complete. Took " + (System.currentTimeMillis() - startTime) + " ms."); EventBus.getDefault().unregister(this); VersionTracker.updateLastSeenVersion(context); - UI_BLOCKING_MIGRATION_RUNNING.postValue(false); - } else { - Log.i(TAG, "Version doesn't match. Looking for " + currentVersion + ", but received " + event.getVersion() + "."); + UI_BLOCKING_MIGRATION_RUNNING.setValue(false); + } else if (event.getVersion() >= uiVersion) { + Log.i(TAG, "Version is >= the UI-blocking version. Posting 'false'."); + UI_BLOCKING_MIGRATION_RUNNING.setValue(false); } } }); } else { Log.d(TAG, "No migrations."); + TextSecurePreferences.setAppMigrationVersion(context, CURRENT_VERSION); VersionTracker.updateLastSeenVersion(context); - UI_BLOCKING_MIGRATION_RUNNING.postValue(false); + UI_BLOCKING_MIGRATION_RUNNING.setValue(false); } } @@ -109,36 +133,45 @@ public class ApplicationMigrations { * @return A {@link LiveData} object that will update with whether or not a UI blocking migration * is in progress. */ - public static LiveData isUiBlockingMigrationRunning() { + public static LiveData getUiBlockingMigrationStatus() { return UI_BLOCKING_MIGRATION_RUNNING; } + /** + * @return True if a UI blocking migration is running. + */ + public static boolean isUiBlockingMigrationRunning() { + Boolean value = UI_BLOCKING_MIGRATION_RUNNING.getValue(); + return value != null && value; + } + /** * @return Whether or not we're in the middle of an update, as determined by the last seen and * current version. */ - public static boolean isUpdate(Context context) { - int currentVersionCode = Util.getCanonicalVersionCode(); - int previousVersionCode = VersionTracker.getLastSeenVersion(context); - - return previousVersionCode < currentVersionCode; + public static boolean isUpdate(@NonNull Context context) { + return isLegacyUpdate(context) || TextSecurePreferences.getAppMigrationVersion(context) < CURRENT_VERSION; } - private static List getMigrationJobs(@NonNull Context context, int lastSeenVersion) { - List jobs = new LinkedList<>(); + private static LinkedHashMap getMigrationJobs(@NonNull Context context, int lastSeenVersion) { + LinkedHashMap jobs = new LinkedHashMap<>(); if (lastSeenVersion < Version.LEGACY) { - jobs.add(new LegacyMigrationJob()); + jobs.put(Version.LEGACY, new LegacyMigrationJob()); } if (lastSeenVersion < Version.RECIPIENT_ID) { - jobs.add(new DatabaseMigrationJob()); + jobs.put(Version.RECIPIENT_ID, new DatabaseMigrationJob()); } if (lastSeenVersion < Version.RECIPIENT_SEARCH) { - jobs.add(new RecipientSearchMigrationJob()); + jobs.put(Version.RECIPIENT_SEARCH, new RecipientSearchMigrationJob()); } return jobs; } + + private static boolean isLegacyUpdate(@NonNull Context context) { + return VersionTracker.getLastSeenVersion(context) < LEGACY_CANONICAL_VERSION; + } } diff --git a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java index ffb6758650..dd823c315d 100644 --- a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -189,6 +189,10 @@ public class TextSecurePreferences { private static final String JOB_MANAGER_VERSION = "pref_job_manager_version"; + private static final String APP_MIGRATION_VERSION = "pref_app_migration_version"; + + private static final String FIRST_INSTALL_VERSION = "pref_first_install_version"; + public static boolean isScreenLockEnabled(@NonNull Context context) { return getBooleanPreference(context, SCREEN_LOCK, false); } @@ -1128,6 +1132,23 @@ public class TextSecurePreferences { return getIntegerPreference(contex, JOB_MANAGER_VERSION, 1); } + public static void setAppMigrationVersion(Context context, int version) { + setIntegerPrefrence(context, APP_MIGRATION_VERSION, version); + } + + public static int getAppMigrationVersion(Context context) { + return getIntegerPreference(context, APP_MIGRATION_VERSION, 1); + } + + public static void setFirstInstallVersion(Context context, int version) { + setIntegerPrefrence(context, FIRST_INSTALL_VERSION, version); + } + + public static int getFirstInstallVersion(Context context) { + return getIntegerPreference(context, FIRST_INSTALL_VERSION, -1); + } + + public static void setBooleanPreference(Context context, String key, boolean value) { PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(key, value).apply(); } diff --git a/test/unitTest/java/org/thoughtcrime/securesms/jobs/FastJobStorageTest.java b/test/unitTest/java/org/thoughtcrime/securesms/jobs/FastJobStorageTest.java index 8b9717eef9..7cf4b9ee08 100644 --- a/test/unitTest/java/org/thoughtcrime/securesms/jobs/FastJobStorageTest.java +++ b/test/unitTest/java/org/thoughtcrime/securesms/jobs/FastJobStorageTest.java @@ -380,6 +380,23 @@ public class FastJobStorageTest { assertEquals("1", jobs.get(0).getId()); } + @Test + public void getPendingJobsWithNoDependenciesInCreatedOrder_onlyMigrationJobWithAppropriateNextRunTime() { + FullSpec migrationSpec1 = new FullSpec(new JobSpec("1", "f1", Job.Parameters.MIGRATION_QUEUE_KEY, 0, 999, 0, 0, 0, -1, -1, EMPTY_DATA, false), + Collections.emptyList(), + Collections.emptyList()); + FullSpec migrationSpec2 = new FullSpec(new JobSpec("2", "f2", Job.Parameters.MIGRATION_QUEUE_KEY, 5, 0, 0, 0, 0, -1, -1, EMPTY_DATA, false), + Collections.emptyList(), + Collections.emptyList()); + + FastJobStorage subject = new FastJobStorage(fixedDataDatabase(Arrays.asList(migrationSpec1, migrationSpec2))); + subject.init(); + + List jobs = subject.getPendingJobsWithNoDependenciesInCreatedOrder(10); + + assertTrue(jobs.isEmpty()); + } + @Test public void deleteJobs_writesToDatabase() { JobDatabase database = noopDatabase();