package com.fsck.k9.mailstore.migrations; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import com.fsck.k9.controller.MessagingControllerCommands.PendingAppend; import com.fsck.k9.controller.MessagingControllerCommands.PendingCommand; import com.fsck.k9.controller.MessagingControllerCommands.PendingEmptyTrash; import com.fsck.k9.controller.MessagingControllerCommands.PendingExpunge; import com.fsck.k9.controller.MessagingControllerCommands.PendingMarkAllAsRead; import com.fsck.k9.controller.MessagingControllerCommands.PendingMoveOrCopy; import com.fsck.k9.controller.MessagingControllerCommands.PendingSetFlag; import com.fsck.k9.mail.Flag; import com.fsck.k9.mailstore.migrations.MigrationTo60.OldPendingCommand; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE) public class MigrationTo60Test { private static final String PENDING_COMMAND_MOVE_OR_COPY = "com.fsck.k9.MessagingController.moveOrCopy"; private static final String PENDING_COMMAND_MOVE_OR_COPY_BULK = "com.fsck.k9.MessagingController.moveOrCopyBulk"; private static final String PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW = "com.fsck.k9.MessagingController.moveOrCopyBulkNew"; private static final String PENDING_COMMAND_EMPTY_TRASH = "com.fsck.k9.MessagingController.emptyTrash"; private static final String PENDING_COMMAND_SET_FLAG_BULK = "com.fsck.k9.MessagingController.setFlagBulk"; private static final String PENDING_COMMAND_SET_FLAG = "com.fsck.k9.MessagingController.setFlag"; private static final String PENDING_COMMAND_APPEND = "com.fsck.k9.MessagingController.append"; private static final String PENDING_COMMAND_MARK_ALL_AS_READ = "com.fsck.k9.MessagingController.markAllAsRead"; private static final String PENDING_COMMAND_EXPUNGE = "com.fsck.k9.MessagingController.expunge"; private static final String SOURCE_FOLDER = "source_folder"; private static final String DEST_FOLDER = "dest_folder"; private static final boolean IS_COPY = true; private static final String[] UID_ARRAY = new String[] { "uid_1", "uid_2" }; private static final boolean FLAG_STATE = true; private static final Flag FLAG = Flag.X_DESTROYED; private static final String UID = "uid"; private static final HashMap<String, String> UID_MAP = new HashMap<>(); static { UID_MAP.put("uid_1", "uid_other_1"); UID_MAP.put("uid_2", "uid_other_2"); } @Test public void migratePendingCommands_shouldChangeTableStructure() { SQLiteDatabase database = createV59Table(); MigrationTo60.migratePendingCommands(database); List<String> columns = getColumnList(database, "pending_commands"); assertEquals(asList("id", "command", "data"), columns); } @Test public void migratePendingCommands_withMultipleRuns_shouldNotThrow() { SQLiteDatabase database = createV59Table(); MigrationTo60.migratePendingCommands(database); MigrationTo60.migratePendingCommands(database); } @Test public void migrateMoveOrCopy_withUidArray() { OldPendingCommand command = queueMoveOrCopy(SOURCE_FOLDER, DEST_FOLDER, IS_COPY, UID_ARRAY); PendingMoveOrCopy pendingCommand = (PendingMoveOrCopy) MigrationTo60.migratePendingCommand(command); assertEquals(SOURCE_FOLDER, pendingCommand.srcFolder); assertEquals(DEST_FOLDER, pendingCommand.destFolder); assertEquals(IS_COPY, pendingCommand.isCopy); assertEquals(asList(UID_ARRAY), pendingCommand.uids); assertNull(pendingCommand.newUidMap); } @Test public void migrateMoveOrCopy_withUidMap() { OldPendingCommand command = queueMoveOrCopy(SOURCE_FOLDER, DEST_FOLDER, IS_COPY, UID_MAP); PendingMoveOrCopy pendingCommand = (PendingMoveOrCopy) MigrationTo60.migratePendingCommand(command); assertEquals(SOURCE_FOLDER, pendingCommand.srcFolder); assertEquals(DEST_FOLDER, pendingCommand.destFolder); assertEquals(IS_COPY, pendingCommand.isCopy); assertEquals(UID_MAP, pendingCommand.newUidMap); assertNull(pendingCommand.uids); } @Test public void migrateMoveOrCopy_withOldFormat() { OldPendingCommand command = queueMoveOrCopyOld(SOURCE_FOLDER, DEST_FOLDER, IS_COPY, UID_ARRAY); PendingMoveOrCopy pendingCommand = (PendingMoveOrCopy) MigrationTo60.migratePendingCommand(command); assertEquals(SOURCE_FOLDER, pendingCommand.srcFolder); assertEquals(DEST_FOLDER, pendingCommand.destFolder); assertEquals(IS_COPY, pendingCommand.isCopy); assertEquals(asList(UID_ARRAY), pendingCommand.uids); assertNull(pendingCommand.newUidMap); } @Test public void migrateMoveOrCopy__withEvenOlderFormat() { OldPendingCommand command = queueMoveOrCopyEvenOlder(SOURCE_FOLDER, DEST_FOLDER, UID, IS_COPY); PendingMoveOrCopy pendingCommand = (PendingMoveOrCopy) MigrationTo60.migratePendingCommand(command); assertEquals(SOURCE_FOLDER, pendingCommand.srcFolder); assertEquals(DEST_FOLDER, pendingCommand.destFolder); assertEquals(IS_COPY, pendingCommand.isCopy); assertEquals(Collections.singletonList(UID), pendingCommand.uids); assertNull(pendingCommand.newUidMap); } @Test public void migrateSetFlag() { OldPendingCommand command = queueSetFlagBulk(SOURCE_FOLDER, FLAG_STATE, FLAG, UID_ARRAY); PendingSetFlag pendingCommand = (PendingSetFlag) MigrationTo60.migratePendingCommand(command); assertEquals(SOURCE_FOLDER, pendingCommand.folder); assertEquals(FLAG_STATE, pendingCommand.newState); assertEquals(FLAG, pendingCommand.flag); assertEquals(asList(UID_ARRAY), pendingCommand.uids); } @Test public void migrateSetFlag_oldFormat() { OldPendingCommand command = queueSetFlagOld(SOURCE_FOLDER, FLAG_STATE, FLAG, UID); PendingSetFlag pendingCommand = (PendingSetFlag) MigrationTo60.migratePendingCommand(command); assertEquals(SOURCE_FOLDER, pendingCommand.folder); assertEquals(FLAG_STATE, pendingCommand.newState); assertEquals(FLAG, pendingCommand.flag); assertEquals(Collections.singletonList(UID), pendingCommand.uids); } @Test public void migrateExpunge() { OldPendingCommand command = queueExpunge(SOURCE_FOLDER); PendingExpunge pendingCommand = (PendingExpunge) MigrationTo60.migratePendingCommand(command); assertEquals(SOURCE_FOLDER, pendingCommand.folder); } @Test public void migrateEmptyTrash() { OldPendingCommand command = queueEmptyTrash(); PendingCommand pendingCommand = MigrationTo60.migratePendingCommand(command); assertTrue(pendingCommand instanceof PendingEmptyTrash); } @Test public void migrateMarkAllMessagesRead() { OldPendingCommand command = queueMarkAllMessagesRead(SOURCE_FOLDER); PendingMarkAllAsRead pendingCommand = (PendingMarkAllAsRead) MigrationTo60.migratePendingCommand(command); assertEquals(SOURCE_FOLDER, pendingCommand.folder); } @Test public void migrateAppend() { OldPendingCommand command = queueAppend(SOURCE_FOLDER, UID); PendingAppend pendingCommand = (PendingAppend) MigrationTo60.migratePendingCommand(command); assertEquals(SOURCE_FOLDER, pendingCommand.folder); assertEquals(UID, pendingCommand.uid); } OldPendingCommand queueAppend(String srcFolder, String uid) { OldPendingCommand command = new OldPendingCommand(); command.command = PENDING_COMMAND_APPEND; command.arguments = new String[] { srcFolder, uid }; return command; } OldPendingCommand queueMoveOrCopy(String srcFolder, String destFolder, boolean isCopy, String uids[]) { OldPendingCommand command = new OldPendingCommand(); command.command = PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW; int length = 4 + uids.length; command.arguments = new String[length]; command.arguments[0] = srcFolder; command.arguments[1] = destFolder; command.arguments[2] = Boolean.toString(isCopy); command.arguments[3] = Boolean.toString(false); System.arraycopy(uids, 0, command.arguments, 4, uids.length); return command; } OldPendingCommand queueMoveOrCopy(String srcFolder, String destFolder, boolean isCopy, Map<String, String> uidMap) { OldPendingCommand command = new OldPendingCommand(); command.command = PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW; int length = 4 + uidMap.keySet().size() + uidMap.values().size(); command.arguments = new String[length]; command.arguments[0] = srcFolder; command.arguments[1] = destFolder; command.arguments[2] = Boolean.toString(isCopy); command.arguments[3] = Boolean.toString(true); Set<String> strings = uidMap.keySet(); System.arraycopy(strings.toArray(new String[strings.size()]), 0, command.arguments, 4, uidMap.keySet().size()); Collection<String> values = uidMap.values(); System.arraycopy(values.toArray(new String[values.size()]), 0, command.arguments, 4 + uidMap.keySet().size(), uidMap.values().size()); return command; } OldPendingCommand queueMoveOrCopyOld(String srcFolder, String destFolder, boolean isCopy, String uids[]) { OldPendingCommand command = new OldPendingCommand(); command.command = PENDING_COMMAND_MOVE_OR_COPY_BULK; int length = 3 + uids.length; command.arguments = new String[length]; command.arguments[0] = srcFolder; command.arguments[1] = destFolder; command.arguments[2] = Boolean.toString(isCopy); System.arraycopy(uids, 0, command.arguments, 3, uids.length); return command; } OldPendingCommand queueMoveOrCopyEvenOlder(String srcFolder, String destFolder, String uid, boolean isCopy) { OldPendingCommand command = new OldPendingCommand(); command.command = PENDING_COMMAND_MOVE_OR_COPY; command.arguments = new String[4]; command.arguments[0] = srcFolder; command.arguments[1] = uid; command.arguments[2] = destFolder; command.arguments[3] = Boolean.toString(isCopy); return command; } OldPendingCommand queueSetFlagBulk(String folderName, boolean newState, Flag flag, String[] uids) { OldPendingCommand command = new OldPendingCommand(); command.command = PENDING_COMMAND_SET_FLAG_BULK; int length = 3 + uids.length; command.arguments = new String[length]; command.arguments[0] = folderName; command.arguments[1] = Boolean.toString(newState); command.arguments[2] = flag.toString(); System.arraycopy(uids, 0, command.arguments, 3, uids.length); return command; } OldPendingCommand queueSetFlagOld(String folderName, boolean newState, Flag flag, String uid) { OldPendingCommand command = new OldPendingCommand(); command.command = PENDING_COMMAND_SET_FLAG; command.arguments = new String[4]; command.arguments[0] = folderName; command.arguments[1] = uid; command.arguments[2] = Boolean.toString(newState); command.arguments[3] = flag.toString(); return command; } OldPendingCommand queueExpunge(String folderName) { OldPendingCommand command = new OldPendingCommand(); command.command = PENDING_COMMAND_EXPUNGE; command.arguments = new String[1]; command.arguments[0] = folderName; return command; } OldPendingCommand queueEmptyTrash() { OldPendingCommand command = new OldPendingCommand(); command.command = PENDING_COMMAND_EMPTY_TRASH; command.arguments = new String[0]; return command; } OldPendingCommand queueMarkAllMessagesRead(final String folder) { OldPendingCommand command = new OldPendingCommand(); command.command = PENDING_COMMAND_MARK_ALL_AS_READ; command.arguments = new String[] { folder }; return command; } private List<String> getColumnList(SQLiteDatabase db, String table) { List<String> columns = new ArrayList<>(); Cursor columnCursor = db.rawQuery("PRAGMA table_info(" + table + ")", null); try { while (columnCursor.moveToNext()) { columns.add(columnCursor.getString(1)); } } finally { columnCursor.close(); } return columns; } private SQLiteDatabase createV59Table() { SQLiteDatabase database = SQLiteDatabase.create(null); database.execSQL("CREATE TABLE pending_commands (" + "id INTEGER PRIMARY KEY, " + "command TEXT, " + "arguments TEXT" + ")"); return database; } }