From 15f3ba6219056ab45053c68119e1f6e83c1c6507 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 10 Aug 2016 13:43:26 +0200 Subject: [PATCH 01/12] serialize PendingCommands as JSON --- k9mail/build.gradle | 1 + .../k9/controller/MessagingController.java | 355 ++++-------------- .../MessagingControllerCommands.java | 137 +++++++ .../controller/PendingCommandSerializer.java | 70 ++++ .../com/fsck/k9/mailstore/LocalStore.java | 50 +-- .../k9/mailstore/StoreSchemaDefinition.java | 2 +- .../mailstore/migrations/MigrationTo56.java | 14 + .../k9/mailstore/migrations/Migrations.java | 3 + 8 files changed, 305 insertions(+), 327 deletions(-) create mode 100644 k9mail/src/main/java/com/fsck/k9/controller/MessagingControllerCommands.java create mode 100644 k9mail/src/main/java/com/fsck/k9/controller/PendingCommandSerializer.java create mode 100644 k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java diff --git a/k9mail/build.gradle b/k9mail/build.gradle index c868605bd..0ec745af7 100644 --- a/k9mail/build.gradle +++ b/k9mail/build.gradle @@ -31,6 +31,7 @@ dependencies { compile 'com.splitwise:tokenautocomplete:2.0.7' compile 'de.cketti.safecontentresolver:safe-content-resolver-v14:0.9.0' compile 'com.github.amlcurran.showcaseview:library:5.4.1' + compile 'com.squareup.moshi:moshi:1.2.0' androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1' diff --git a/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java b/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java index a1ec80e87..c74492dae 100644 --- a/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java +++ b/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java @@ -56,6 +56,13 @@ import com.fsck.k9.R; import com.fsck.k9.activity.MessageReference; import com.fsck.k9.activity.setup.AccountSetupCheckSettings.CheckDirection; import com.fsck.k9.cache.EmailProviderCache; +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.helper.Contacts; import com.fsck.k9.mail.Address; import com.fsck.k9.mail.AuthenticationFailedException; @@ -86,7 +93,6 @@ import com.fsck.k9.mailstore.LocalFolder; import com.fsck.k9.mailstore.LocalFolder.MoreMessages; import com.fsck.k9.mailstore.LocalMessage; import com.fsck.k9.mailstore.LocalStore; -import com.fsck.k9.mailstore.LocalStore.PendingCommand; import com.fsck.k9.mailstore.MessageRemovalListener; import com.fsck.k9.mailstore.UnavailableStorageException; import com.fsck.k9.notification.NotificationController; @@ -115,19 +121,8 @@ import com.fsck.k9.search.SqlQueryBuilder; public class MessagingController { public static final long INVALID_MESSAGE_ID = -1; - private static final String[] EMPTY_STRING_ARRAY = new String[0]; private static final Set SYNC_FLAGS = EnumSet.of(Flag.SEEN, Flag.FLAGGED, Flag.ANSWERED, Flag.FORWARDED); - 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 MessagingController inst = null; @@ -1726,13 +1721,11 @@ public class MessagingController { try { for (PendingCommand command : commands) { processingCommand = command; - if (K9.DEBUG) + if (K9.DEBUG) { Log.d(K9.LOG_TAG, "Processing pending command '" + command + "'"); - - String[] components = command.command.split("\\."); - String commandTitle = components[components.length - 1]; + } for (MessagingListener l : getListeners()) { - l.pendingCommandStarted(account, commandTitle); + l.pendingCommandStarted(account, command.getCommandName()); } /* * We specifically do not catch any exceptions here. If a command fails it is @@ -1740,28 +1733,23 @@ public class MessagingController { * other command processes. This maintains the order of the commands. */ try { - if (PENDING_COMMAND_APPEND.equals(command.command)) { - processPendingAppend(command, account); - } else if (PENDING_COMMAND_SET_FLAG_BULK.equals(command.command)) { - processPendingSetFlag(command, account); - } else if (PENDING_COMMAND_SET_FLAG.equals(command.command)) { - processPendingSetFlagOld(command, account); - } else if (PENDING_COMMAND_MARK_ALL_AS_READ.equals(command.command)) { - processPendingMarkAllAsRead(command, account); - } else if (PENDING_COMMAND_MOVE_OR_COPY_BULK.equals(command.command)) { - processPendingMoveOrCopyOld2(command, account); - } else if (PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW.equals(command.command)) { - processPendingMoveOrCopy(command, account); - } else if (PENDING_COMMAND_MOVE_OR_COPY.equals(command.command)) { - processPendingMoveOrCopyOld(command, account); - } else if (PENDING_COMMAND_EMPTY_TRASH.equals(command.command)) { - processPendingEmptyTrash(command, account); - } else if (PENDING_COMMAND_EXPUNGE.equals(command.command)) { - processPendingExpunge(command, account); + if (command instanceof PendingAppend) { + processPendingAppend((PendingAppend) command, account); + } else if (command instanceof PendingSetFlag) { + processPendingSetFlag((PendingSetFlag) command, account); + } else if (command instanceof PendingMarkAllAsRead) { + processPendingMarkAllAsRead((PendingMarkAllAsRead) command, account); + } else if (command instanceof PendingMoveOrCopy) { + processPendingMoveOrCopy((PendingMoveOrCopy) command, account); + } else if (command instanceof PendingEmptyTrash) { + processPendingEmptyTrash((PendingEmptyTrash) command, account); + } else if (command instanceof PendingExpunge) { + processPendingExpunge((PendingExpunge) command, account); } localStore.removePendingCommand(command); - if (K9.DEBUG) + if (K9.DEBUG) { Log.d(K9.LOG_TAG, "Done processing pending command '" + command + "'"); + } } catch (MessagingException me) { if (me.isPermanentFailure()) { addErrorMessage(account, null, me); @@ -1774,7 +1762,7 @@ public class MessagingController { progress++; for (MessagingListener l : getListeners()) { l.synchronizeMailboxProgress(account, null, progress, todo); - l.pendingCommandCompleted(account, commandTitle); + l.pendingCommandCompleted(account, command.getCommandName()); } } } @@ -1798,13 +1786,13 @@ public class MessagingController { * created. * TODO update the local message UID instead of deleteing it */ - private void processPendingAppend(PendingCommand command, Account account) throws MessagingException { + private void processPendingAppend(PendingAppend command, Account account) throws MessagingException { Folder remoteFolder = null; LocalFolder localFolder = null; try { - String folder = command.arguments[0]; - String uid = command.arguments[1]; + String folder = command.folder; + String uid = command.uid; if (account.getErrorFolderName().equals(folder)) { return; @@ -1924,16 +1912,7 @@ public class MessagingController { if (account.getErrorFolderName().equals(srcFolder)) { return; } - PendingCommand command = new PendingCommand(); - 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); + PendingCommand command = MessagingControllerCommands.createMoveOrCopyBulk(srcFolder, destFolder, isCopy, uids); queuePendingCommand(account, command); } @@ -1945,72 +1924,22 @@ public class MessagingController { if (account.getErrorFolderName().equals(srcFolder)) { return; } - PendingCommand command = new PendingCommand(); - 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); - System.arraycopy(uidMap.keySet().toArray(EMPTY_STRING_ARRAY), 0, command.arguments, 4, uidMap.keySet().size()); - System.arraycopy(uidMap.values().toArray(EMPTY_STRING_ARRAY), 0, command.arguments, 4 + uidMap.keySet().size(), uidMap.values().size()); + PendingCommand command = MessagingControllerCommands.createMoveOrCopyBulk(srcFolder, destFolder, isCopy, uidMap); queuePendingCommand(account, command); } } - /** - * Convert pending command to new format and call - * {@link #processPendingMoveOrCopy(PendingCommand, Account)}. - * - *

- * TODO: This method is obsolete and is only for transition from K-9 4.0 to K-9 4.2 - * Eventually, it should be removed. - *

- * - * @param command - * Pending move/copy command in old format. - * @param account - * The account the pending command belongs to. - * - * @throws MessagingException - * In case of an error. - */ - private void processPendingMoveOrCopyOld2(PendingCommand command, Account account) throws MessagingException { - PendingCommand newCommand = new PendingCommand(); - int len = command.arguments.length; - newCommand.command = PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW; - newCommand.arguments = new String[len + 1]; - newCommand.arguments[0] = command.arguments[0]; - newCommand.arguments[1] = command.arguments[1]; - newCommand.arguments[2] = command.arguments[2]; - newCommand.arguments[3] = Boolean.toString(false); - System.arraycopy(command.arguments, 3, newCommand.arguments, 4, len - 3); - - processPendingMoveOrCopy(newCommand, account); - } - - /** - * Process a pending trash message command. - */ - private void processPendingMoveOrCopy(PendingCommand command, Account account) throws MessagingException { + private void processPendingMoveOrCopy(PendingMoveOrCopy command, Account account) throws MessagingException { Folder remoteSrcFolder = null; Folder remoteDestFolder = null; LocalFolder localDestFolder; try { - String srcFolder = command.arguments[0]; + String srcFolder = command.srcFolder; if (account.getErrorFolderName().equals(srcFolder)) { return; } - String destFolder = command.arguments[1]; - String isCopyS = command.arguments[2]; - String hasNewUidsS = command.arguments[3]; - - boolean hasNewUids = false; - if (hasNewUidsS != null) { - hasNewUids = Boolean.parseBoolean(hasNewUidsS); - } + String destFolder = command.destFolder; + boolean isCopy = command.isCopy; Store remoteStore = account.getRemoteStore(); remoteSrcFolder = remoteStore.getFolder(srcFolder); @@ -2019,34 +1948,11 @@ public class MessagingController { localDestFolder = (LocalFolder) localStore.getFolder(destFolder); List messages = new ArrayList<>(); - /* - * We split up the localUidMap into two parts while sending the command, here we assemble it back. - */ - Map localUidMap = new HashMap<>(); - if (hasNewUids) { - int offset = (command.arguments.length - 4) / 2; - - for (int i = 4; i < 4 + offset; i++) { - localUidMap.put(command.arguments[i], command.arguments[i + offset]); - - String uid = command.arguments[i]; - if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) { - messages.add(remoteSrcFolder.getMessage(uid)); - } + Collection uids = command.newUidMap != null ? command.newUidMap.keySet() : Arrays.asList(command.uids); + for (String uid : uids) { + if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) { + messages.add(remoteSrcFolder.getMessage(uid)); } - - } else { - for (int i = 4; i < command.arguments.length; i++) { - String uid = command.arguments[i]; - if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) { - messages.add(remoteSrcFolder.getMessage(uid)); - } - } - } - - boolean isCopy = false; - if (isCopyS != null) { - isCopy = Boolean.parseBoolean(isCopyS); } if (!remoteSrcFolder.exists()) { @@ -2092,11 +1998,14 @@ public class MessagingController { * This next part is used to bring the local UIDs of the local destination folder * upto speed with the remote UIDs of remote destination folder. */ - if (!localUidMap.isEmpty() && remoteUidMap != null && !remoteUidMap.isEmpty()) { + if (command.newUidMap != null && remoteUidMap != null && !remoteUidMap.isEmpty()) { for (Map.Entry entry : remoteUidMap.entrySet()) { String remoteSrcUid = entry.getKey(); - String localDestUid = localUidMap.get(remoteSrcUid); String newUid = entry.getValue(); + String localDestUid = command.newUidMap.get(remoteSrcUid); + if (localDestUid == null) { + continue; + } Message localDestMessage = localDestFolder.getMessage(localDestUid); if (localDestMessage != null) { @@ -2115,18 +2024,11 @@ public class MessagingController { } private void queueSetFlag(final Account account, final String folderName, - final String newState, final String flag, final String[] uids) { + final boolean newState, final Flag flag, final String[] uids) { putBackground("queueSetFlag " + account.getDescription() + ":" + folderName, null, new Runnable() { @Override public void run() { - PendingCommand command = new PendingCommand(); - command.command = PENDING_COMMAND_SET_FLAG_BULK; - int length = 3 + uids.length; - command.arguments = new String[length]; - command.arguments[0] = folderName; - command.arguments[1] = newState; - command.arguments[2] = flag; - System.arraycopy(uids, 0, command.arguments, 3, uids.length); + PendingCommand command = MessagingControllerCommands.createSetFlag(folderName, newState, flag, uids); queuePendingCommand(account, command); processPendingCommands(account); } @@ -2135,16 +2037,15 @@ public class MessagingController { /** * Processes a pending mark read or unread command. */ - private void processPendingSetFlag(PendingCommand command, Account account) throws MessagingException { - String folder = command.arguments[0]; + private void processPendingSetFlag(PendingSetFlag command, Account account) throws MessagingException { + String folder = command.folder; if (account.getErrorFolderName().equals(folder)) { return; } - boolean newState = Boolean.parseBoolean(command.arguments[1]); - - Flag flag = Flag.valueOf(command.arguments[2]); + boolean newState = command.newState; + Flag flag = command.flag; Store remoteStore = account.getRemoteStore(); Folder remoteFolder = remoteStore.getFolder(folder); @@ -2158,8 +2059,7 @@ public class MessagingController { return; } List messages = new ArrayList<>(); - for (int i = 3; i < command.arguments.length; i++) { - String uid = command.arguments[i]; + for (String uid : command.uids) { if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) { messages.add(remoteFolder.getMessage(uid)); } @@ -2174,61 +2074,18 @@ public class MessagingController { } } - // TODO: This method is obsolete and is only for transition from K-9 2.0 to K-9 2.1 - // Eventually, it should be removed - private void processPendingSetFlagOld(PendingCommand command, Account account) throws MessagingException { - String folder = command.arguments[0]; - String uid = command.arguments[1]; - - if (account.getErrorFolderName().equals(folder)) { - return; - } - if (K9.DEBUG) - Log.d(K9.LOG_TAG, "processPendingSetFlagOld: folder = " + folder + ", uid = " + uid); - - boolean newState = Boolean.parseBoolean(command.arguments[2]); - - Flag flag = Flag.valueOf(command.arguments[3]); - Folder remoteFolder = null; - try { - Store remoteStore = account.getRemoteStore(); - remoteFolder = remoteStore.getFolder(folder); - if (!remoteFolder.exists()) { - return; - } - remoteFolder.open(Folder.OPEN_MODE_RW); - if (remoteFolder.getMode() != Folder.OPEN_MODE_RW) { - return; - } - Message remoteMessage = null; - if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) { - remoteMessage = remoteFolder.getMessage(uid); - } - if (remoteMessage == null) { - return; - } - remoteMessage.setFlag(flag, newState); - } finally { - closeFolder(remoteFolder); - } - } private void queueExpunge(final Account account, final String folderName) { putBackground("queueExpunge " + account.getDescription() + ":" + folderName, null, new Runnable() { @Override public void run() { - PendingCommand command = new PendingCommand(); - command.command = PENDING_COMMAND_EXPUNGE; - - command.arguments = new String[1]; - - command.arguments[0] = folderName; + PendingCommand command = MessagingControllerCommands.createExpunge(folderName); queuePendingCommand(account, command); processPendingCommands(account); } }); } - private void processPendingExpunge(PendingCommand command, Account account) throws MessagingException { - String folder = command.arguments[0]; + private void processPendingExpunge(PendingExpunge command, Account account) throws MessagingException { + String folder = command.folder; if (account.getErrorFolderName().equals(folder)) { return; @@ -2254,73 +2111,8 @@ public class MessagingController { } } - - // TODO: This method is obsolete and is only for transition from K-9 2.0 to K-9 2.1 - // Eventually, it should be removed - private void processPendingMoveOrCopyOld(PendingCommand command, Account account) throws MessagingException { - String srcFolder = command.arguments[0]; - String uid = command.arguments[1]; - String destFolder = command.arguments[2]; - String isCopyS = command.arguments[3]; - - boolean isCopy = false; - if (isCopyS != null) { - isCopy = Boolean.parseBoolean(isCopyS); - } - - if (account.getErrorFolderName().equals(srcFolder)) { - return; - } - - Store remoteStore = account.getRemoteStore(); - Folder remoteSrcFolder = remoteStore.getFolder(srcFolder); - Folder remoteDestFolder = remoteStore.getFolder(destFolder); - - if (!remoteSrcFolder.exists()) { - throw new MessagingException("processPendingMoveOrCopyOld: remoteFolder " + srcFolder + " does not exist", true); - } - remoteSrcFolder.open(Folder.OPEN_MODE_RW); - if (remoteSrcFolder.getMode() != Folder.OPEN_MODE_RW) { - throw new MessagingException("processPendingMoveOrCopyOld: could not open remoteSrcFolder " + srcFolder + " read/write", true); - } - - Message remoteMessage = null; - if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) { - remoteMessage = remoteSrcFolder.getMessage(uid); - } - if (remoteMessage == null) { - throw new MessagingException("processPendingMoveOrCopyOld: remoteMessage " + uid + " does not exist", true); - } - - if (K9.DEBUG) - Log.d(K9.LOG_TAG, "processPendingMoveOrCopyOld: source folder = " + srcFolder - + ", uid = " + uid + ", destination folder = " + destFolder + ", isCopy = " + isCopy); - - if (!isCopy && destFolder.equals(account.getTrashFolderName())) { - if (K9.DEBUG) - Log.d(K9.LOG_TAG, "processPendingMoveOrCopyOld doing special case for deleting message"); - - remoteMessage.delete(account.getTrashFolderName()); - remoteSrcFolder.close(); - return; - } - - remoteDestFolder.open(Folder.OPEN_MODE_RW); - if (remoteDestFolder.getMode() != Folder.OPEN_MODE_RW) { - throw new MessagingException("processPendingMoveOrCopyOld: could not open remoteDestFolder " + srcFolder + " read/write", true); - } - - if (isCopy) { - remoteSrcFolder.copyMessages(Collections.singletonList(remoteMessage), remoteDestFolder); - } else { - remoteSrcFolder.moveMessages(Collections.singletonList(remoteMessage), remoteDestFolder); - } - remoteSrcFolder.close(); - remoteDestFolder.close(); - } - - private void processPendingMarkAllAsRead(PendingCommand command, Account account) throws MessagingException { - String folder = command.arguments[0]; + private void processPendingMarkAllAsRead(PendingMarkAllAsRead command, Account account) throws MessagingException { + String folder = command.folder; Folder remoteFolder = null; LocalFolder localFolder = null; try { @@ -2443,9 +2235,7 @@ public class MessagingController { if (K9.DEBUG) { Log.i(K9.LOG_TAG, "Marking all messages in " + account.getDescription() + ":" + folder + " as read"); } - PendingCommand command = new PendingCommand(); - command.command = PENDING_COMMAND_MARK_ALL_AS_READ; - command.arguments = new String[] { folder }; + PendingCommand command = MessagingControllerCommands.createMarkAllAsRead(folder); queuePendingCommand(account, command); processPendingCommands(account); } @@ -2534,7 +2324,7 @@ public class MessagingController { // Send flag change to server List value = entry.getValue(); String[] uids = value.toArray(new String[value.size()]); - queueSetFlag(account, folderName, Boolean.toString(newState), flag.toString(), uids); + queueSetFlag(account, folderName, newState, flag, uids); processPendingCommands(account); } } @@ -2602,7 +2392,7 @@ public class MessagingController { uids[i] = messages.get(i).getUid(); } - queueSetFlag(account, folderName, Boolean.toString(newState), flag.toString(), uids); + queueSetFlag(account, folderName, newState, flag, uids); processPendingCommands(account); } catch (MessagingException me) { addErrorMessage(account, null, me); @@ -3034,9 +2824,7 @@ public class MessagingController { if (K9.DEBUG) Log.i(K9.LOG_TAG, "Moved sent message to folder '" + account.getSentFolderName() + "' (" + localSentFolder.getId() + ") "); - PendingCommand command = new PendingCommand(); - command.command = PENDING_COMMAND_APPEND; - command.arguments = new String[] { localSentFolder.getName(), message.getUid() }; + PendingCommand command = MessagingControllerCommands.createAppend(localSentFolder.getName(), message.getUid()); queuePendingCommand(account, command); processPendingCommands(account); } @@ -3635,25 +3423,19 @@ public class MessagingController { for (Message message : messages) { // If the message was in the Outbox, then it has been copied to local Trash, and has // to be copied to remote trash - PendingCommand command = new PendingCommand(); - command.command = PENDING_COMMAND_APPEND; - command.arguments = - new String[] { - account.getTrashFolderName(), - message.getUid() - }; + PendingCommand command = MessagingControllerCommands.createAppend(account.getTrashFolderName(), message.getUid()); queuePendingCommand(account, command); } processPendingCommands(account); } else if (account.getDeletePolicy() == DeletePolicy.ON_DELETE) { if (folder.equals(account.getTrashFolderName())) { - queueSetFlag(account, folder, Boolean.toString(true), Flag.DELETED.toString(), uids); + queueSetFlag(account, folder, true, Flag.DELETED, uids); } else { queueMoveOrCopy(account, folder, account.getTrashFolderName(), false, uids, uidMap); } processPendingCommands(account); } else if (account.getDeletePolicy() == DeletePolicy.MARK_AS_READ) { - queueSetFlag(account, folder, Boolean.toString(true), Flag.SEEN.toString(), uids); + queueSetFlag(account, folder, true, Flag.SEEN, uids); processPendingCommands(account); } else { if (K9.DEBUG) @@ -3683,7 +3465,7 @@ public class MessagingController { } @SuppressWarnings("UnusedParameters") // for consistency with other PendingCommand methods - private void processPendingEmptyTrash(PendingCommand command, Account account) throws MessagingException { + private void processPendingEmptyTrash(PendingEmptyTrash command, Account account) throws MessagingException { Store remoteStore = account.getRemoteStore(); Folder remoteFolder = remoteStore.getFolder(account.getTrashFolderName()); @@ -3729,9 +3511,7 @@ public class MessagingController { } if (!isTrashLocalOnly) { - PendingCommand command = new PendingCommand(); - command.command = PENDING_COMMAND_EMPTY_TRASH; - command.arguments = EMPTY_STRING_ARRAY; + PendingCommand command = MessagingControllerCommands.createEmptyTrash(); queuePendingCommand(account, command); processPendingCommands(account); } @@ -4224,12 +4004,7 @@ public class MessagingController { localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true); if (saveRemotely) { - PendingCommand command = new PendingCommand(); - command.command = PENDING_COMMAND_APPEND; - command.arguments = new String[] { - localFolder.getName(), - localMessage.getUid() - }; + PendingCommand command = MessagingControllerCommands.createAppend(localFolder.getName(), localMessage.getUid()); queuePendingCommand(account, command); processPendingCommands(account); } diff --git a/k9mail/src/main/java/com/fsck/k9/controller/MessagingControllerCommands.java b/k9mail/src/main/java/com/fsck/k9/controller/MessagingControllerCommands.java new file mode 100644 index 000000000..ef8c1bf55 --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/controller/MessagingControllerCommands.java @@ -0,0 +1,137 @@ +package com.fsck.k9.controller; + + +import java.util.Map; + +import com.fsck.k9.mail.Flag; + + +public class MessagingControllerCommands { + static final String COMMAND_APPEND = "append"; + static final String COMMAND_MARK_ALL_AS_READ = "mark_all_as_read"; + static final String COMMAND_SET_FLAG = "set_flag"; + static final String COMMAND_EXPUNGE = "expunge"; + static final String COMMAND_MOVE_OR_COPY = "move_or_copy"; + static final String COMMAND_EMPTY_TRASH = "empty_trash"; + + + public static PendingSetFlag createSetFlag(String folder, boolean newState, Flag flag, String[] uids) { + return new PendingSetFlag(folder, newState, flag, uids); + } + + public static PendingExpunge createExpunge(String folderName) { + return new PendingExpunge(folderName); + } + + public static PendingMarkAllAsRead createMarkAllAsRead(String folder) { + return new PendingMarkAllAsRead(folder); + } + + public static PendingAppend createAppend(String folderName, String uid) { + return new PendingAppend(folderName, uid); + } + + public static PendingEmptyTrash createEmptyTrash() { + return new PendingEmptyTrash(); + } + + public static PendingMoveOrCopy createMoveOrCopyBulk(String srcFolder, String destFolder, boolean isCopy, Map uidMap) { + return new PendingMoveOrCopy(srcFolder, destFolder, isCopy, null, uidMap); + } + + public static PendingMoveOrCopy createMoveOrCopyBulk(String srcFolder, String destFolder, boolean isCopy, String[] uids) { + return new PendingMoveOrCopy(srcFolder, destFolder, isCopy, uids, null); + } + + + public static abstract class PendingCommand { + public transient long databaseId; + + public abstract String getCommandName(); + + private PendingCommand() { } + } + + public static class PendingMoveOrCopy extends PendingCommand { + public final String srcFolder; + public final String destFolder; + public final boolean isCopy; + public final String[] uids; + public final Map newUidMap; + + public PendingMoveOrCopy( + String srcFolder, String destFolder, boolean isCopy, String[] uids, Map newUidMap) { + this.srcFolder = srcFolder; + this.destFolder = destFolder; + this.isCopy = isCopy; + this.uids = uids; + this.newUidMap = newUidMap; + } + + public String getCommandName() { + return COMMAND_MOVE_OR_COPY; + } + } + + public static class PendingEmptyTrash extends PendingCommand { + public String getCommandName() { + return COMMAND_EMPTY_TRASH; + } + } + + public static class PendingSetFlag extends PendingCommand { + public final String folder; + public final boolean newState; + public final Flag flag; + public final String[] uids; + + public PendingSetFlag(String folder, boolean newState, Flag flag, String[] uids) { + this.folder = folder; + this.newState = newState; + this.flag = flag; + this.uids = uids; + } + + public String getCommandName() { + return COMMAND_SET_FLAG; + } + } + + public static class PendingAppend extends PendingCommand { + public final String folder; + public final String uid; + + public PendingAppend(String folder, String uid) { + this.folder = folder; + this.uid = uid; + } + + public String getCommandName() { + return COMMAND_APPEND; + } + } + + public static class PendingMarkAllAsRead extends PendingCommand { + public final String folder; + + public PendingMarkAllAsRead(String folder) { + this.folder = folder; + } + + public String getCommandName() { + return COMMAND_MARK_ALL_AS_READ; + } + } + + public static class PendingExpunge extends PendingCommand { + public final String folder; + + public PendingExpunge(String folder) { + this.folder = folder; + } + + public String getCommandName() { + return COMMAND_EXPUNGE; + } + } +} diff --git a/k9mail/src/main/java/com/fsck/k9/controller/PendingCommandSerializer.java b/k9mail/src/main/java/com/fsck/k9/controller/PendingCommandSerializer.java new file mode 100644 index 000000000..27beff3d7 --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/controller/PendingCommandSerializer.java @@ -0,0 +1,70 @@ +package com.fsck.k9.controller; + + +import java.io.IOError; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +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.squareup.moshi.JsonAdapter; +import com.squareup.moshi.Moshi; + + +public class PendingCommandSerializer { + private static final PendingCommandSerializer INSTANCE = new PendingCommandSerializer(); + + + private final Map> adapters; + + + private PendingCommandSerializer() { + Moshi moshi = new Moshi.Builder().build(); + HashMap> adapters = new HashMap<>(); + + adapters.put(MessagingControllerCommands.COMMAND_MOVE_OR_COPY, moshi.adapter(PendingMoveOrCopy.class)); + adapters.put(MessagingControllerCommands.COMMAND_APPEND, moshi.adapter(PendingAppend.class)); + adapters.put(MessagingControllerCommands.COMMAND_EMPTY_TRASH, moshi.adapter(PendingEmptyTrash.class)); + adapters.put(MessagingControllerCommands.COMMAND_EXPUNGE, moshi.adapter(PendingExpunge.class)); + adapters.put(MessagingControllerCommands.COMMAND_MARK_ALL_AS_READ, moshi.adapter(PendingMarkAllAsRead.class)); + adapters.put(MessagingControllerCommands.COMMAND_SET_FLAG, moshi.adapter(PendingSetFlag.class)); + + this.adapters = Collections.unmodifiableMap(adapters); + } + + + public static PendingCommandSerializer getInstance() { + return INSTANCE; + } + + + public String serialize(T command) { + // noinspection unchecked, we know the map has correctly matching adapters + JsonAdapter adapter = (JsonAdapter) adapters.get(command.getCommandName()); + if (adapter == null) { + throw new IllegalArgumentException("Unsupported pending command type!"); + } + return adapter.toJson(command); + } + + public PendingCommand unserialize(long databaseId, String commandName, String data) { + JsonAdapter adapter = adapters.get(commandName); + if (adapter == null) { + throw new IllegalArgumentException("Unsupported pending command type!"); + } + try { + PendingCommand command = adapter.fromJson(data); + command.databaseId = databaseId; + return command; + } catch (IOException e) { + throw new IOError(e); + } + } +} diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/LocalStore.java b/k9mail/src/main/java/com/fsck/k9/mailstore/LocalStore.java index 267b4e6da..0bde477fc 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/LocalStore.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/LocalStore.java @@ -32,7 +32,8 @@ import android.util.Log; import com.fsck.k9.Account; import com.fsck.k9.K9; import com.fsck.k9.Preferences; -import com.fsck.k9.helper.UrlEncodingHelper; +import com.fsck.k9.controller.PendingCommandSerializer; +import com.fsck.k9.controller.MessagingControllerCommands.PendingCommand; import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Folder; @@ -134,7 +135,7 @@ public class LocalStore extends Store implements Serializable { */ private static final int THREAD_FLAG_UPDATE_BATCH_SIZE = 500; - public static final int DB_VERSION = 55; + public static final int DB_VERSION = 56; public static String getColumnNameForFlag(Flag flag) { @@ -169,6 +170,7 @@ public class LocalStore extends Store implements Serializable { private final MessagePreviewCreator messagePreviewCreator; private final MessageFulltextCreator messageFulltextCreator; private final AttachmentCounter attachmentCounter; + private final PendingCommandSerializer pendingCommandSerializer; /** * local://localhost/path/to/database/uuid.db @@ -187,6 +189,7 @@ public class LocalStore extends Store implements Serializable { messagePreviewCreator = MessagePreviewCreator.newInstance(); messageFulltextCreator = MessageFulltextCreator.newInstance(); attachmentCounter = AttachmentCounter.newInstance(); + pendingCommandSerializer = PendingCommandSerializer.getInstance(); database.open(); } @@ -464,7 +467,7 @@ public class LocalStore extends Store implements Serializable { Cursor cursor = null; try { cursor = db.query("pending_commands", - new String[] { "id", "command", "arguments" }, + new String[] { "id", "command", "data" }, null, null, null, @@ -472,14 +475,11 @@ public class LocalStore extends Store implements Serializable { "id ASC"); List commands = new ArrayList<>(); while (cursor.moveToNext()) { - PendingCommand command = new PendingCommand(); - command.mId = cursor.getLong(0); - command.command = cursor.getString(1); - String arguments = cursor.getString(2); - command.arguments = arguments.split(","); - for (int i = 0; i < command.arguments.length; i++) { - command.arguments[i] = Utility.fastUrlDecode(command.arguments[i]); - } + long databaseId = cursor.getLong(0); + String commandName = cursor.getString(1); + String data = cursor.getString(2); + PendingCommand command = pendingCommandSerializer.unserialize( + databaseId, commandName, data); commands.add(command); } return commands; @@ -491,12 +491,9 @@ public class LocalStore extends Store implements Serializable { } public void addPendingCommand(PendingCommand command) throws MessagingException { - for (int i = 0; i < command.arguments.length; i++) { - command.arguments[i] = UrlEncodingHelper.encodeUtf8(command.arguments[i]); - } final ContentValues cv = new ContentValues(); - cv.put("command", command.command); - cv.put("arguments", Utility.combine(command.arguments, ',')); + cv.put("command", command.getCommandName()); + cv.put("data", pendingCommandSerializer.serialize(command)); database.execute(false, new DbCallback() { @Override public Void doDbWork(final SQLiteDatabase db) throws WrappedException { @@ -510,7 +507,7 @@ public class LocalStore extends Store implements Serializable { database.execute(false, new DbCallback() { @Override public Void doDbWork(final SQLiteDatabase db) throws WrappedException { - db.delete("pending_commands", "id = ?", new String[] { Long.toString(command.mId) }); + db.delete("pending_commands", "id = ?", new String[] { Long.toString(command.databaseId) }); return null; } }); @@ -526,25 +523,6 @@ public class LocalStore extends Store implements Serializable { }); } - public static class PendingCommand { - private long mId; - public String command; - public String[] arguments; - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(command); - sb.append(": "); - for (String argument : arguments) { - sb.append(", "); - sb.append(argument); - //sb.append("\n"); - } - return sb.toString(); - } - } - @Override public boolean isMoveCapable() { return true; diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/StoreSchemaDefinition.java b/k9mail/src/main/java/com/fsck/k9/mailstore/StoreSchemaDefinition.java index 50afda705..f14000209 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/StoreSchemaDefinition.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/StoreSchemaDefinition.java @@ -195,7 +195,7 @@ class StoreSchemaDefinition implements LockableDatabase.SchemaDefinition { db.execSQL("DROP TABLE IF EXISTS pending_commands"); db.execSQL("CREATE TABLE pending_commands " + - "(id INTEGER PRIMARY KEY, command TEXT, arguments TEXT)"); + "(id INTEGER PRIMARY KEY, command TEXT, data TEXT)"); db.execSQL("DROP TRIGGER IF EXISTS delete_folder"); db.execSQL("CREATE TRIGGER delete_folder BEFORE DELETE ON folders BEGIN DELETE FROM messages WHERE old.id = folder_id; END;"); diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java b/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java new file mode 100644 index 000000000..2b0554418 --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java @@ -0,0 +1,14 @@ +package com.fsck.k9.mailstore.migrations; + + +import android.database.sqlite.SQLiteDatabase; + + +public class MigrationTo56 { + public static void migratePendingCommands(SQLiteDatabase db) { + // TODO actually migrate + db.execSQL("DROP TABLE IF EXISTS pending_commands"); + db.execSQL("CREATE TABLE pending_commands " + + "(id INTEGER PRIMARY KEY, command TEXT, data TEXT)"); + } +} diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/Migrations.java b/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/Migrations.java index 4c8aec597..ca61590b7 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/Migrations.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/Migrations.java @@ -64,6 +64,9 @@ public class Migrations { MigrationTo54.addPreviewTypeColumn(db); case 54: MigrationTo55.createFtsSearchTable(db, migrationsHelper); + case 55: + MigrationTo56.migratePendingCommands(db); + } } } From c4ab70ca0b5902e6c3facadd46089929ff47e907 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 10 Aug 2016 13:44:57 +0200 Subject: [PATCH 02/12] reorder methods in MemorizingMessagingListener (no semantic changes) --- .../MemorizingMessagingListener.java | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/k9mail/src/main/java/com/fsck/k9/controller/MemorizingMessagingListener.java b/k9mail/src/main/java/com/fsck/k9/controller/MemorizingMessagingListener.java index e19dff811..9dfbfbb7e 100644 --- a/k9mail/src/main/java/com/fsck/k9/controller/MemorizingMessagingListener.java +++ b/k9mail/src/main/java/com/fsck/k9/controller/MemorizingMessagingListener.java @@ -12,15 +12,6 @@ import com.fsck.k9.Account; class MemorizingMessagingListener extends MessagingListener { Map memories = new HashMap<>(31); - private Memory getMemory(Account account, String folderName) { - Memory memory = memories.get(getMemoryKey(account, folderName)); - if (memory == null) { - memory = new Memory(account, folderName); - memories.put(getMemoryKey(memory.account, memory.folderName), memory); - } - return memory; - } - synchronized void removeAccount(Account account) { Iterator> memIt = memories.entrySet().iterator(); @@ -35,32 +26,6 @@ class MemorizingMessagingListener extends MessagingListener { } } - @Override - public synchronized void synchronizeMailboxStarted(Account account, String folder) { - Memory memory = getMemory(account, folder); - memory.syncingState = MemorizingState.STARTED; - memory.folderCompleted = 0; - memory.folderTotal = 0; - } - - @Override - public synchronized void synchronizeMailboxFinished(Account account, String folder, - int totalMessagesInMailbox, int numNewMessages) { - Memory memory = getMemory(account, folder); - memory.syncingState = MemorizingState.FINISHED; - memory.syncingTotalMessagesInMailbox = totalMessagesInMailbox; - memory.syncingNumNewMessages = numNewMessages; - } - - @Override - public synchronized void synchronizeMailboxFailed(Account account, String folder, - String message) { - - Memory memory = getMemory(account, folder); - memory.syncingState = MemorizingState.FAILED; - memory.failureMessage = message; - } - synchronized void refreshOther(MessagingListener other) { if (other != null) { @@ -151,6 +116,32 @@ class MemorizingMessagingListener extends MessagingListener { } } + @Override + public synchronized void synchronizeMailboxStarted(Account account, String folder) { + Memory memory = getMemory(account, folder); + memory.syncingState = MemorizingState.STARTED; + memory.folderCompleted = 0; + memory.folderTotal = 0; + } + + @Override + public synchronized void synchronizeMailboxFinished(Account account, String folder, + int totalMessagesInMailbox, int numNewMessages) { + Memory memory = getMemory(account, folder); + memory.syncingState = MemorizingState.FINISHED; + memory.syncingTotalMessagesInMailbox = totalMessagesInMailbox; + memory.syncingNumNewMessages = numNewMessages; + } + + @Override + public synchronized void synchronizeMailboxFailed(Account account, String folder, + String message) { + + Memory memory = getMemory(account, folder); + memory.syncingState = MemorizingState.FAILED; + memory.failureMessage = message; + } + @Override public synchronized void setPushActive(Account account, String folderName, boolean active) { Memory memory = getMemory(account, folderName); @@ -213,6 +204,15 @@ class MemorizingMessagingListener extends MessagingListener { memory.processingCommandTitle = null; } + private Memory getMemory(Account account, String folderName) { + Memory memory = memories.get(getMemoryKey(account, folderName)); + if (memory == null) { + memory = new Memory(account, folderName); + memories.put(getMemoryKey(memory.account, memory.folderName), memory); + } + return memory; + } + private static String getMemoryKey(Account taccount, String tfolderName) { return taccount.getDescription() + ":" + tfolderName; } From 3eb949e6744272c5f84bf0e0e53faae08eaddc4f Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 6 Aug 2016 13:02:13 +0200 Subject: [PATCH 03/12] use lists instead of arrays in PendingCommands --- .../k9/controller/MessagingController.java | 30 ++++++++----------- .../MessagingControllerCommands.java | 13 ++++---- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java b/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java index c74492dae..f3d8717cc 100644 --- a/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java +++ b/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java @@ -1908,7 +1908,7 @@ public class MessagingController { closeFolder(localFolder); } } - private void queueMoveOrCopy(Account account, String srcFolder, String destFolder, boolean isCopy, String uids[]) { + private void queueMoveOrCopy(Account account, String srcFolder, String destFolder, boolean isCopy, List uids) { if (account.getErrorFolderName().equals(srcFolder)) { return; } @@ -1917,7 +1917,7 @@ public class MessagingController { } private void queueMoveOrCopy(Account account, String srcFolder, String destFolder, - boolean isCopy, String uids[], Map uidMap) { + boolean isCopy, List uids, Map uidMap) { if (uidMap == null || uidMap.isEmpty()) { queueMoveOrCopy(account, srcFolder, destFolder, isCopy, uids); } else { @@ -1948,7 +1948,7 @@ public class MessagingController { localDestFolder = (LocalFolder) localStore.getFolder(destFolder); List messages = new ArrayList<>(); - Collection uids = command.newUidMap != null ? command.newUidMap.keySet() : Arrays.asList(command.uids); + Collection uids = command.newUidMap != null ? command.newUidMap.keySet() : command.uids; for (String uid : uids) { if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) { messages.add(remoteSrcFolder.getMessage(uid)); @@ -2024,7 +2024,7 @@ public class MessagingController { } private void queueSetFlag(final Account account, final String folderName, - final boolean newState, final Flag flag, final String[] uids) { + final boolean newState, final Flag flag, final List uids) { putBackground("queueSetFlag " + account.getDescription() + ":" + folderName, null, new Runnable() { @Override public void run() { @@ -2322,9 +2322,7 @@ public class MessagingController { } // Send flag change to server - List value = entry.getValue(); - String[] uids = value.toArray(new String[value.size()]); - queueSetFlag(account, folderName, newState, flag, uids); + queueSetFlag(account, folderName, newState, flag, entry.getValue()); processPendingCommands(account); } } @@ -2387,11 +2385,7 @@ public class MessagingController { return; } - String[] uids = new String[messages.size()]; - for (int i = 0, end = uids.length; i < end; i++) { - uids[i] = messages.get(i).getUid(); - } - + List uids = getUidsFromMessages(messages); queueSetFlag(account, folderName, newState, flag, uids); processPendingCommands(account); } catch (MessagingException me) { @@ -3234,8 +3228,8 @@ public class MessagingController { } } - Set origUidKeys = origUidMap.keySet(); - queueMoveOrCopy(account, srcFolder, destFolder, isCopy, origUidKeys.toArray(new String[origUidKeys.size()]), uidMap); + List origUidKeys = new ArrayList<>(origUidMap.keySet()); + queueMoveOrCopy(account, srcFolder, destFolder, isCopy, origUidKeys, uidMap); } processPendingCommands(account); @@ -3378,7 +3372,7 @@ public class MessagingController { MessagingListener listener) { Folder localFolder = null; Folder localTrashFolder = null; - String[] uids = getUidsFromMessages(messages); + List uids = getUidsFromMessages(messages); try { //We need to make these callbacks before moving the messages to the trash //as messages get a new UID after being moved @@ -3456,10 +3450,10 @@ public class MessagingController { } } - private static String[] getUidsFromMessages(List messages) { - String[] uids = new String[messages.size()]; + private static List getUidsFromMessages(List messages) { + List uids = new ArrayList<>(messages.size()); for (int i = 0; i < messages.size(); i++) { - uids[i] = messages.get(i).getUid(); + uids.add(messages.get(i).getUid()); } return uids; } diff --git a/k9mail/src/main/java/com/fsck/k9/controller/MessagingControllerCommands.java b/k9mail/src/main/java/com/fsck/k9/controller/MessagingControllerCommands.java index ef8c1bf55..b2d0fed66 100644 --- a/k9mail/src/main/java/com/fsck/k9/controller/MessagingControllerCommands.java +++ b/k9mail/src/main/java/com/fsck/k9/controller/MessagingControllerCommands.java @@ -1,6 +1,7 @@ package com.fsck.k9.controller; +import java.util.List; import java.util.Map; import com.fsck.k9.mail.Flag; @@ -15,7 +16,7 @@ public class MessagingControllerCommands { static final String COMMAND_EMPTY_TRASH = "empty_trash"; - public static PendingSetFlag createSetFlag(String folder, boolean newState, Flag flag, String[] uids) { + public static PendingSetFlag createSetFlag(String folder, boolean newState, Flag flag, List uids) { return new PendingSetFlag(folder, newState, flag, uids); } @@ -39,7 +40,7 @@ public class MessagingControllerCommands { return new PendingMoveOrCopy(srcFolder, destFolder, isCopy, null, uidMap); } - public static PendingMoveOrCopy createMoveOrCopyBulk(String srcFolder, String destFolder, boolean isCopy, String[] uids) { + public static PendingMoveOrCopy createMoveOrCopyBulk(String srcFolder, String destFolder, boolean isCopy, List uids) { return new PendingMoveOrCopy(srcFolder, destFolder, isCopy, uids, null); } @@ -56,11 +57,11 @@ public class MessagingControllerCommands { public final String srcFolder; public final String destFolder; public final boolean isCopy; - public final String[] uids; + public final List uids; public final Map newUidMap; public PendingMoveOrCopy( - String srcFolder, String destFolder, boolean isCopy, String[] uids, Map newUidMap) { + String srcFolder, String destFolder, boolean isCopy, List uids, Map newUidMap) { this.srcFolder = srcFolder; this.destFolder = destFolder; this.isCopy = isCopy; @@ -83,9 +84,9 @@ public class MessagingControllerCommands { public final String folder; public final boolean newState; public final Flag flag; - public final String[] uids; + public final List uids; - public PendingSetFlag(String folder, boolean newState, Flag flag, String[] uids) { + public PendingSetFlag(String folder, boolean newState, Flag flag, List uids) { this.folder = folder; this.newState = newState; this.flag = flag; From fb1dfd5786e9dc71f6c202c23882dda36d33ba5b Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 6 Aug 2016 12:19:18 +0200 Subject: [PATCH 04/12] add migration for new pending commands --- .../mailstore/migrations/MigrationTo56.java | 182 +++++++++++++++++- 1 file changed, 181 insertions(+), 1 deletion(-) diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java b/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java index 2b0554418..bf7d810f4 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java @@ -1,14 +1,194 @@ package com.fsck.k9.mailstore.migrations; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import android.content.ContentValues; +import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; +import android.support.annotation.VisibleForTesting; + +import com.fsck.k9.controller.MessagingControllerCommands; +import com.fsck.k9.controller.MessagingControllerCommands.PendingCommand; +import com.fsck.k9.controller.PendingCommandSerializer; +import com.fsck.k9.helper.Utility; +import com.fsck.k9.mail.Flag; public class MigrationTo56 { + 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"; + + public static void migratePendingCommands(SQLiteDatabase db) { - // TODO actually migrate + List pendingCommmands = new ArrayList<>(); + for (OldPendingCommand oldPendingCommand : getPendingCommands(db)) { + PendingCommand newPendingCommand = migratePendingCommand(oldPendingCommand); + pendingCommmands.add(newPendingCommand); + } + db.execSQL("DROP TABLE IF EXISTS pending_commands"); db.execSQL("CREATE TABLE pending_commands " + "(id INTEGER PRIMARY KEY, command TEXT, data TEXT)"); + + PendingCommandSerializer pendingCommandSerializer = PendingCommandSerializer.getInstance(); + for (PendingCommand pendingCommand : pendingCommmands) { + final ContentValues cv = new ContentValues(); + cv.put("command", pendingCommand.getCommandName()); + cv.put("data", pendingCommandSerializer.serialize(pendingCommand)); + db.insert("pending_commands", "command", cv); + } + } + + @VisibleForTesting + static PendingCommand migratePendingCommand(OldPendingCommand oldPendingCommand) { + switch (oldPendingCommand.command) { + case PENDING_COMMAND_APPEND: + return migrateCommandAppend(oldPendingCommand); + case PENDING_COMMAND_SET_FLAG_BULK: + return migrateCommandSetFlagBulk(oldPendingCommand); + case PENDING_COMMAND_SET_FLAG: + return migrateCommandSetFlag(oldPendingCommand); + case PENDING_COMMAND_MARK_ALL_AS_READ: + return migrateCommandMarkAllAsRead(oldPendingCommand); + case PENDING_COMMAND_MOVE_OR_COPY_BULK: + return migrateCommandMoveOrCopyBulk(oldPendingCommand); + case PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW: + return migrateCommandMoveOrCopyBulkNew(oldPendingCommand); + case PENDING_COMMAND_MOVE_OR_COPY: + return migrateCommandMoveOrCopy(oldPendingCommand); + case PENDING_COMMAND_EMPTY_TRASH: + return migrateCommandEmptyTrash(); + case PENDING_COMMAND_EXPUNGE: + return migrateCommandExpunge(oldPendingCommand); + default: + throw new IllegalArgumentException("Tried to migrate unknown pending command!"); + } + } + + private static PendingCommand migrateCommandExpunge(OldPendingCommand command) { + String folder = command.arguments[0]; + return MessagingControllerCommands.createExpunge(folder); + } + + private static PendingCommand migrateCommandEmptyTrash() { + return MessagingControllerCommands.createEmptyTrash(); + } + + private static PendingCommand migrateCommandMoveOrCopy(OldPendingCommand command) { + String srcFolder = command.arguments[0]; + String uid = command.arguments[1]; + String destFolder = command.arguments[2]; + boolean isCopy = Boolean.parseBoolean(command.arguments[3]); + + return MessagingControllerCommands.createMoveOrCopyBulk(srcFolder, destFolder, isCopy, + Collections.singletonList(uid)); + } + + private static PendingCommand migrateCommandMoveOrCopyBulkNew(OldPendingCommand command) { + String srcFolder = command.arguments[0]; + String destFolder = command.arguments[1]; + boolean isCopy = Boolean.parseBoolean(command.arguments[2]); + boolean hasNewUids = Boolean.parseBoolean(command.arguments[3]); + + if (hasNewUids) { + Map uidMap = new HashMap<>(); + int offset = (command.arguments.length - 4) / 2; + for (int i = 4; i < 4 + offset; i++) { + uidMap.put(command.arguments[i], command.arguments[i + offset]); + } + + return MessagingControllerCommands.createMoveOrCopyBulk(srcFolder, destFolder, isCopy, uidMap); + } else { + List uids = new ArrayList<>(command.arguments.length -4); + uids.addAll(Arrays.asList(command.arguments).subList(4, command.arguments.length)); + + return MessagingControllerCommands.createMoveOrCopyBulk(srcFolder, destFolder, isCopy, uids); + } + + } + + private static PendingCommand migrateCommandMoveOrCopyBulk(OldPendingCommand command) { + int len = command.arguments.length; + + OldPendingCommand newCommand = new OldPendingCommand(); + newCommand.command = PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW; + newCommand.arguments = new String[len + 1]; + newCommand.arguments[0] = command.arguments[0]; + newCommand.arguments[1] = command.arguments[1]; + newCommand.arguments[2] = command.arguments[2]; + newCommand.arguments[3] = Boolean.toString(false); + System.arraycopy(command.arguments, 3, newCommand.arguments, 4, len - 3); + + return migratePendingCommand(newCommand); + } + + private static PendingCommand migrateCommandMarkAllAsRead(OldPendingCommand command) { + return MessagingControllerCommands.createMarkAllAsRead(command.arguments[0]); + } + + private static PendingCommand migrateCommandSetFlag(OldPendingCommand command) { + String folder = command.arguments[0]; + String uid = command.arguments[1]; + boolean newState = Boolean.parseBoolean(command.arguments[2]); + Flag flag = Flag.valueOf(command.arguments[3]); + + return MessagingControllerCommands.createSetFlag(folder, newState, flag, Collections.singletonList(uid)); + } + + private static PendingCommand migrateCommandSetFlagBulk(OldPendingCommand command) { + String folder = command.arguments[0]; + boolean newState = Boolean.parseBoolean(command.arguments[1]); + Flag flag = Flag.valueOf(command.arguments[2]); + + List uids = new ArrayList<>(command.arguments.length -3); + uids.addAll(Arrays.asList(command.arguments).subList(3, command.arguments.length)); + + return MessagingControllerCommands.createSetFlag(folder, newState, flag, uids); + } + + private static PendingCommand migrateCommandAppend(OldPendingCommand command) { + String folder = command.arguments[0]; + String uid = command.arguments[1]; + return MessagingControllerCommands.createAppend(folder, uid); + } + + private static List getPendingCommands(SQLiteDatabase db) { + Cursor cursor = null; + try { + cursor = db.query("pending_commands", + new String[] { "id", "command", "arguments" }, null, null, null, null, "id ASC"); + List commands = new ArrayList<>(); + while (cursor.moveToNext()) { + OldPendingCommand command = new OldPendingCommand(); + command.command = cursor.getString(1); + String arguments = cursor.getString(2); + command.arguments = arguments.split(","); + for (int i = 0; i < command.arguments.length; i++) { + command.arguments[i] = Utility.fastUrlDecode(command.arguments[i]); + } + commands.add(command); + } + return commands; + } finally { + Utility.closeQuietly(cursor); + } + } + + static class OldPendingCommand { + public String command; + public String[] arguments; } } From 73c2b1e9adaee70b7b7c6976fd0f1659928cc1f0 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 6 Aug 2016 13:46:29 +0200 Subject: [PATCH 05/12] add unit tests for PendingCommand migration --- .../mailstore/migrations/MigrationTo56.java | 1 + .../migrations/MigrationTo56Test.java | 283 ++++++++++++++++++ 2 files changed, 284 insertions(+) create mode 100644 k9mail/src/test/java/com/fsck/k9/mailstore/migrations/MigrationTo56Test.java diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java b/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java index bf7d810f4..5743efed9 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java @@ -187,6 +187,7 @@ public class MigrationTo56 { } } + @VisibleForTesting static class OldPendingCommand { public String command; public String[] arguments; diff --git a/k9mail/src/test/java/com/fsck/k9/mailstore/migrations/MigrationTo56Test.java b/k9mail/src/test/java/com/fsck/k9/mailstore/migrations/MigrationTo56Test.java new file mode 100644 index 000000000..02b8ccc4b --- /dev/null +++ b/k9mail/src/test/java/com/fsck/k9/mailstore/migrations/MigrationTo56Test.java @@ -0,0 +1,283 @@ +package com.fsck.k9.mailstore.migrations; + + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +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.MigrationTo56.OldPendingCommand; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + + +public class MigrationTo56Test { + static final String PENDING_COMMAND_MOVE_OR_COPY = "com.fsck.k9.MessagingController.moveOrCopy"; + static final String PENDING_COMMAND_MOVE_OR_COPY_BULK = "com.fsck.k9.MessagingController.moveOrCopyBulk"; + static final String PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW = "com.fsck.k9.MessagingController.moveOrCopyBulkNew"; + static final String PENDING_COMMAND_EMPTY_TRASH = "com.fsck.k9.MessagingController.emptyTrash"; + static final String PENDING_COMMAND_SET_FLAG_BULK = "com.fsck.k9.MessagingController.setFlagBulk"; + static final String PENDING_COMMAND_SET_FLAG = "com.fsck.k9.MessagingController.setFlag"; + static final String PENDING_COMMAND_APPEND = "com.fsck.k9.MessagingController.append"; + static final String PENDING_COMMAND_MARK_ALL_AS_READ = "com.fsck.k9.MessagingController.markAllAsRead"; + static final String PENDING_COMMAND_EXPUNGE = "com.fsck.k9.MessagingController.expunge"; + + static final String SOURCE_FOLDER = "source_folder"; + static final String DEST_FOLDER = "dest_folder"; + static final boolean IS_COPY = true; + static final String[] UID_ARRAY = new String[] { "uid_1", "uid_2" }; + static final boolean FLAG_STATE = true; + static final Flag FLAG = Flag.X_DESTROYED; + static final String UID = "uid"; + static final HashMap UID_MAP = new HashMap<>(); + static { + UID_MAP.put("uid_1", "uid_other_1"); + UID_MAP.put("uid_2", "uid_other_2"); + } + + + @Test + public void testMigrateMoveOrCopy__withUidArray() { + OldPendingCommand command = queueMoveOrCopy(SOURCE_FOLDER, DEST_FOLDER, IS_COPY, UID_ARRAY); + + PendingMoveOrCopy pendingCommand = (PendingMoveOrCopy) MigrationTo56.migratePendingCommand(command); + + assertEquals(SOURCE_FOLDER, pendingCommand.srcFolder); + assertEquals(DEST_FOLDER, pendingCommand.destFolder); + assertEquals(IS_COPY, pendingCommand.isCopy); + assertEquals(Arrays.asList(UID_ARRAY), pendingCommand.uids); + assertNull(pendingCommand.newUidMap); + } + + 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; + } + + + @Test + public void testMigrateMoveOrCopy__withUidMap() { + OldPendingCommand command = queueMoveOrCopy(SOURCE_FOLDER, DEST_FOLDER, IS_COPY, UID_MAP); + + PendingMoveOrCopy pendingCommand = (PendingMoveOrCopy) MigrationTo56.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); + } + + OldPendingCommand queueMoveOrCopy( + String srcFolder, String destFolder, boolean isCopy, Map 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 strings = uidMap.keySet(); + System.arraycopy(strings.toArray(new String[strings.size()]), 0, command.arguments, 4, uidMap.keySet().size()); + Collection values = uidMap.values(); + System.arraycopy(values.toArray(new String[values.size()]), 0, command.arguments, 4 + uidMap.keySet().size(), uidMap.values().size()); + return command; + } + + + @Test + public void testMigrateMoveOrCopy__withOldFormat() { + OldPendingCommand command = queueMoveOrCopyOld(SOURCE_FOLDER, DEST_FOLDER, IS_COPY, UID_ARRAY); + + PendingMoveOrCopy pendingCommand = (PendingMoveOrCopy) MigrationTo56.migratePendingCommand(command); + + assertEquals(SOURCE_FOLDER, pendingCommand.srcFolder); + assertEquals(DEST_FOLDER, pendingCommand.destFolder); + assertEquals(IS_COPY, pendingCommand.isCopy); + assertEquals(Arrays.asList(UID_ARRAY), pendingCommand.uids); + assertNull(pendingCommand.newUidMap); + } + + 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; + } + + + @Test + public void testMigrateMoveOrCopy__withEvenOlderFormat() { + OldPendingCommand command = queueMoveOrCopyEvenOlder(SOURCE_FOLDER, DEST_FOLDER, UID, IS_COPY); + + PendingMoveOrCopy pendingCommand = (PendingMoveOrCopy) MigrationTo56.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); + } + + 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; + } + + + @Test + public void testMigrateSetFlag() { + OldPendingCommand command = queueSetFlagBulk(SOURCE_FOLDER, FLAG_STATE, FLAG, UID_ARRAY); + + PendingSetFlag pendingCommand = (PendingSetFlag) MigrationTo56.migratePendingCommand(command); + + assertEquals(SOURCE_FOLDER, pendingCommand.folder); + assertEquals(FLAG_STATE, pendingCommand.newState); + assertEquals(FLAG, pendingCommand.flag); + assertEquals(Arrays.asList(UID_ARRAY), pendingCommand.uids); + } + + 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; + } + + + @Test + public void testMigrateSetFlag__oldFormat() { + OldPendingCommand command = queueSetFlagOld(SOURCE_FOLDER, FLAG_STATE, FLAG, UID); + + PendingSetFlag pendingCommand = (PendingSetFlag) MigrationTo56.migratePendingCommand(command); + + assertEquals(SOURCE_FOLDER, pendingCommand.folder); + assertEquals(FLAG_STATE, pendingCommand.newState); + assertEquals(FLAG, pendingCommand.flag); + assertEquals(Collections.singletonList(UID), pendingCommand.uids); + } + + 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; + } + + + @Test + public void testMigrateExpunge() { + OldPendingCommand command = queueExpunge(SOURCE_FOLDER); + + PendingExpunge pendingCommand = (PendingExpunge) MigrationTo56.migratePendingCommand(command); + + assertEquals(SOURCE_FOLDER, pendingCommand.folder); + } + + OldPendingCommand queueExpunge(String folderName) { + OldPendingCommand command = new OldPendingCommand(); + command.command = PENDING_COMMAND_EXPUNGE; + command.arguments = new String[1]; + command.arguments[0] = folderName; + return command; + } + + + @Test + public void testMigrateEmptyTrash() { + OldPendingCommand command = queueEmptyTrash(); + + PendingCommand pendingCommand = MigrationTo56.migratePendingCommand(command); + + assertTrue(pendingCommand instanceof PendingEmptyTrash); + } + + OldPendingCommand queueEmptyTrash() { + OldPendingCommand command = new OldPendingCommand(); + command.command = PENDING_COMMAND_EMPTY_TRASH; + command.arguments = new String[0]; + return command; + } + + + @Test + public void testMigrateMarkAllMessagesRead() { + OldPendingCommand command = queueMarkAllMessagesRead(SOURCE_FOLDER); + + PendingMarkAllAsRead pendingCommand = (PendingMarkAllAsRead) MigrationTo56.migratePendingCommand(command); + + assertEquals(SOURCE_FOLDER, pendingCommand.folder); + } + + OldPendingCommand queueMarkAllMessagesRead(final String folder) { + OldPendingCommand command = new OldPendingCommand(); + command.command = PENDING_COMMAND_MARK_ALL_AS_READ; + command.arguments = new String[] { folder }; + return command; + } + + + @Test + public void testMigrateAppend() { + OldPendingCommand command = queueAppend(SOURCE_FOLDER, UID); + + PendingAppend pendingCommand = (PendingAppend) MigrationTo56.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; + } + +} \ No newline at end of file From d6c50b4db6e6ab36315d4a22da1ab9c1d15dd2e0 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 6 Aug 2016 14:57:45 +0200 Subject: [PATCH 06/12] add unit tests for PendingCommand serialization --- .../PendingCommandSerializerTest.java | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 k9mail/src/test/java/com/fsck/k9/controller/PendingCommandSerializerTest.java diff --git a/k9mail/src/test/java/com/fsck/k9/controller/PendingCommandSerializerTest.java b/k9mail/src/test/java/com/fsck/k9/controller/PendingCommandSerializerTest.java new file mode 100644 index 000000000..eccdb8f54 --- /dev/null +++ b/k9mail/src/test/java/com/fsck/k9/controller/PendingCommandSerializerTest.java @@ -0,0 +1,80 @@ +package com.fsck.k9.controller; + + +import java.util.HashMap; + +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.PendingMoveOrCopy; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + + + +public class PendingCommandSerializerTest { + static final int DATABASE_ID = 123; + static final String UID = "uid"; + static final String SOURCE_FOLDER = "source_folder"; + static final String DEST_FOLDER = "dest_folder"; + static final HashMap UID_MAP = new HashMap<>(); + public static final boolean IS_COPY = true; + + static { + UID_MAP.put("uid_1", "uid_other_1"); + UID_MAP.put("uid_2", "uid_other_2"); + } + + + PendingCommandSerializer pendingCommandSerializer = PendingCommandSerializer.getInstance(); + + + @Test + public void testSerializeDeserialize__withoutArguments() { + PendingCommand pendingCommand = MessagingControllerCommands.createEmptyTrash(); + + String serializedCommand = pendingCommandSerializer.serialize(pendingCommand); + PendingEmptyTrash unserializedCommand = (PendingEmptyTrash) pendingCommandSerializer.unserialize( + DATABASE_ID, pendingCommand.getCommandName(), serializedCommand); + + assertEquals(DATABASE_ID, unserializedCommand.databaseId); + } + + @Test + public void testSerializeDeserialize__withArguments() { + PendingCommand pendingCommand = MessagingControllerCommands.createAppend(SOURCE_FOLDER, UID); + + String serializedCommand = pendingCommandSerializer.serialize(pendingCommand); + PendingAppend unserializedCommand = (PendingAppend) pendingCommandSerializer.unserialize( + DATABASE_ID, pendingCommand.getCommandName(), serializedCommand); + + assertEquals(DATABASE_ID, unserializedCommand.databaseId); + assertEquals(SOURCE_FOLDER, unserializedCommand.folder); + assertEquals(UID, unserializedCommand.uid); + } + + @Test + public void testSerializeDeserialize__withComplexArguments() { + PendingCommand pendingCommand = MessagingControllerCommands.createMoveOrCopyBulk( + SOURCE_FOLDER, DEST_FOLDER, IS_COPY, UID_MAP); + + String serializedCommand = pendingCommandSerializer.serialize(pendingCommand); + PendingMoveOrCopy unserializedCommand = (PendingMoveOrCopy) pendingCommandSerializer.unserialize( + DATABASE_ID, pendingCommand.getCommandName(), serializedCommand); + + assertEquals(DATABASE_ID, unserializedCommand.databaseId); + assertEquals(SOURCE_FOLDER, unserializedCommand.srcFolder); + assertEquals(DEST_FOLDER, unserializedCommand.destFolder); + assertEquals(UID_MAP, unserializedCommand.newUidMap); + } + + @Test(expected = IllegalArgumentException.class) + public void testDeserialize__withUnknownCommandName__shouldFail() { + PendingCommand pendingCommand = MessagingControllerCommands.createEmptyTrash(); + + String serializedCommand = pendingCommandSerializer.serialize(pendingCommand); + pendingCommandSerializer.unserialize(DATABASE_ID, "BAD_COMMAND_NAME", serializedCommand); + } + +} \ No newline at end of file From 902506b2b8d28cbdcd949d9dbf3b98dd31d62af8 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 10 Aug 2016 13:50:25 +0200 Subject: [PATCH 07/12] move legacy utility method into migration class --- .../main/java/com/fsck/k9/helper/Utility.java | 34 ------------------- .../mailstore/migrations/MigrationTo56.java | 32 ++++++++++++++++- 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/k9mail/src/main/java/com/fsck/k9/helper/Utility.java b/k9mail/src/main/java/com/fsck/k9/helper/Utility.java index 69d906f11..f0b4e9f59 100644 --- a/k9mail/src/main/java/com/fsck/k9/helper/Utility.java +++ b/k9mail/src/main/java/com/fsck/k9/helper/Utility.java @@ -128,40 +128,6 @@ public class Utility { return false; } - /** - * A fast version of URLDecoder.decode() that works only with UTF-8 and does only two - * allocations. This version is around 3x as fast as the standard one and I'm using it - * hundreds of times in places that slow down the UI, so it helps. - */ - public static String fastUrlDecode(String s) { - - byte[] bytes = s.getBytes(Charset.forName("UTF-8")); - byte ch; - int length = 0; - for (int i = 0, count = bytes.length; i < count; i++) { - ch = bytes[i]; - if (ch == '%') { - int h = (bytes[i + 1] - '0'); - int l = (bytes[i + 2] - '0'); - if (h > 9) { - h -= 7; - } - if (l > 9) { - l -= 7; - } - bytes[length] = (byte)((h << 4) | l); - i += 2; - } else if (ch == '+') { - bytes[length] = ' '; - } else { - bytes[length] = bytes[i]; - } - length++; - } - return new String(bytes, 0, length, Charset.forName("UTF-8")); - - } - /* * TODO disabled this method globally. It is used in all the settings screens but I just * noticed that an unrelated icon was dimmed. Android must share drawables internally. diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java b/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java index 5743efed9..2577de515 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java @@ -1,6 +1,7 @@ package com.fsck.k9.mailstore.migrations; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -177,7 +178,7 @@ public class MigrationTo56 { String arguments = cursor.getString(2); command.arguments = arguments.split(","); for (int i = 0; i < command.arguments.length; i++) { - command.arguments[i] = Utility.fastUrlDecode(command.arguments[i]); + command.arguments[i] = fastUrlDecode(command.arguments[i]); } commands.add(command); } @@ -192,4 +193,33 @@ public class MigrationTo56 { public String command; public String[] arguments; } + + + private static String fastUrlDecode(String s) { + byte[] bytes = s.getBytes(Charset.forName("UTF-8")); + byte ch; + int length = 0; + for (int i = 0, count = bytes.length; i < count; i++) { + ch = bytes[i]; + if (ch == '%') { + int h = (bytes[i + 1] - '0'); + int l = (bytes[i + 2] - '0'); + if (h > 9) { + h -= 7; + } + if (l > 9) { + l -= 7; + } + bytes[length] = (byte)((h << 4) | l); + i += 2; + } else if (ch == '+') { + bytes[length] = ' '; + } else { + bytes[length] = bytes[i]; + } + length++; + } + return new String(bytes, 0, length, Charset.forName("UTF-8")); + } + } From 2f6a2e7c5f103a1dd3d280c78a769deea30a8199 Mon Sep 17 00:00:00 2001 From: Philip Whitehouse Date: Sun, 20 Nov 2016 19:55:31 +0000 Subject: [PATCH 08/12] Fix multi-run upgrade bug in Migration --- .../mailstore/migrations/MigrationTo56.java | 44 +++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java b/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java index 2577de515..fa0f74d0f 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo56.java @@ -35,24 +35,42 @@ public class MigrationTo56 { public static void migratePendingCommands(SQLiteDatabase db) { List pendingCommmands = new ArrayList<>(); - for (OldPendingCommand oldPendingCommand : getPendingCommands(db)) { - PendingCommand newPendingCommand = migratePendingCommand(oldPendingCommand); - pendingCommmands.add(newPendingCommand); - } - db.execSQL("DROP TABLE IF EXISTS pending_commands"); - db.execSQL("CREATE TABLE pending_commands " + - "(id INTEGER PRIMARY KEY, command TEXT, data TEXT)"); + if (columnExists(db, "pending_commands", "arguments")) { + for (OldPendingCommand oldPendingCommand : getPendingCommands(db)) { + PendingCommand newPendingCommand = migratePendingCommand(oldPendingCommand); + pendingCommmands.add(newPendingCommand); + } - PendingCommandSerializer pendingCommandSerializer = PendingCommandSerializer.getInstance(); - for (PendingCommand pendingCommand : pendingCommmands) { - final ContentValues cv = new ContentValues(); - cv.put("command", pendingCommand.getCommandName()); - cv.put("data", pendingCommandSerializer.serialize(pendingCommand)); - db.insert("pending_commands", "command", cv); + db.execSQL("DROP TABLE IF EXISTS pending_commands"); + db.execSQL("CREATE TABLE pending_commands " + + "(id INTEGER PRIMARY KEY, command TEXT, data TEXT)"); + + PendingCommandSerializer pendingCommandSerializer = PendingCommandSerializer.getInstance(); + for (PendingCommand pendingCommand : pendingCommmands) { + final ContentValues cv = new ContentValues(); + cv.put("command", pendingCommand.getCommandName()); + cv.put("data", pendingCommandSerializer.serialize(pendingCommand)); + db.insert("pending_commands", "command", cv); + } } } + private static boolean columnExists(SQLiteDatabase db, String table, String columnName) { + Cursor columnCursor = db.rawQuery("PRAGMA table_info("+table+")", null); + columnCursor.moveToFirst(); + boolean foundColumn = false; + while (!columnCursor.isAfterLast()) { + if(columnCursor.getString(1).equals(columnName)) { + foundColumn = true; + break; + } + columnCursor.moveToNext(); + } + columnCursor.close(); + return foundColumn; + } + @VisibleForTesting static PendingCommand migratePendingCommand(OldPendingCommand oldPendingCommand) { switch (oldPendingCommand.command) { From 8179d88113f8ab8c60c2b00a4aa7d0392594fc5e Mon Sep 17 00:00:00 2001 From: cketti Date: Sun, 5 Feb 2017 06:05:28 +0100 Subject: [PATCH 09/12] Clean up MigrationTo60 --- .../mailstore/migrations/MigrationTo60.java | 86 +++++++++++-------- 1 file changed, 49 insertions(+), 37 deletions(-) diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo60.java b/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo60.java index ec655cbeb..42734d681 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo60.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo60.java @@ -1,9 +1,9 @@ package com.fsck.k9.mailstore.migrations; + import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -19,8 +19,10 @@ import com.fsck.k9.controller.PendingCommandSerializer; import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.Flag; +import static java.util.Collections.singletonList; -public class MigrationTo60 { + +class MigrationTo60 { 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"; @@ -33,21 +35,24 @@ public class MigrationTo60 { public static void migratePendingCommands(SQLiteDatabase db) { - List pendingCommmands = new ArrayList<>(); + List pendingCommands = new ArrayList<>(); if (columnExists(db, "pending_commands", "arguments")) { for (OldPendingCommand oldPendingCommand : getPendingCommands(db)) { PendingCommand newPendingCommand = migratePendingCommand(oldPendingCommand); - pendingCommmands.add(newPendingCommand); + pendingCommands.add(newPendingCommand); } db.execSQL("DROP TABLE IF EXISTS pending_commands"); - db.execSQL("CREATE TABLE pending_commands " + - "(id INTEGER PRIMARY KEY, command TEXT, data TEXT)"); + db.execSQL("CREATE TABLE pending_commands (" + + "id INTEGER PRIMARY KEY, " + + "command TEXT, " + + "data TEXT" + + ")"); PendingCommandSerializer pendingCommandSerializer = PendingCommandSerializer.getInstance(); - for (PendingCommand pendingCommand : pendingCommmands) { - final ContentValues cv = new ContentValues(); + for (PendingCommand pendingCommand : pendingCommands) { + ContentValues cv = new ContentValues(); cv.put("command", pendingCommand.getCommandName()); cv.put("data", pendingCommandSerializer.serialize(pendingCommand)); db.insert("pending_commands", "command", cv); @@ -56,15 +61,14 @@ public class MigrationTo60 { } private static boolean columnExists(SQLiteDatabase db, String table, String columnName) { - Cursor columnCursor = db.rawQuery("PRAGMA table_info("+table+")", null); - columnCursor.moveToFirst(); + Cursor columnCursor = db.rawQuery("PRAGMA table_info(" + table + ")", null); boolean foundColumn = false; - while (!columnCursor.isAfterLast()) { - if(columnCursor.getString(1).equals(columnName)) { + while (columnCursor.moveToNext()) { + String currentColumnName = columnCursor.getString(1); + if (currentColumnName.equals(columnName)) { foundColumn = true; break; } - columnCursor.moveToNext(); } columnCursor.close(); return foundColumn; @@ -73,26 +77,36 @@ public class MigrationTo60 { @VisibleForTesting static PendingCommand migratePendingCommand(OldPendingCommand oldPendingCommand) { switch (oldPendingCommand.command) { - case PENDING_COMMAND_APPEND: + case PENDING_COMMAND_APPEND: { return migrateCommandAppend(oldPendingCommand); - case PENDING_COMMAND_SET_FLAG_BULK: + } + case PENDING_COMMAND_SET_FLAG_BULK: { return migrateCommandSetFlagBulk(oldPendingCommand); - case PENDING_COMMAND_SET_FLAG: + } + case PENDING_COMMAND_SET_FLAG: { return migrateCommandSetFlag(oldPendingCommand); - case PENDING_COMMAND_MARK_ALL_AS_READ: + } + case PENDING_COMMAND_MARK_ALL_AS_READ: { return migrateCommandMarkAllAsRead(oldPendingCommand); - case PENDING_COMMAND_MOVE_OR_COPY_BULK: + } + case PENDING_COMMAND_MOVE_OR_COPY_BULK: { return migrateCommandMoveOrCopyBulk(oldPendingCommand); - case PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW: + } + case PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW: { return migrateCommandMoveOrCopyBulkNew(oldPendingCommand); - case PENDING_COMMAND_MOVE_OR_COPY: + } + case PENDING_COMMAND_MOVE_OR_COPY: { return migrateCommandMoveOrCopy(oldPendingCommand); - case PENDING_COMMAND_EMPTY_TRASH: + } + case PENDING_COMMAND_EMPTY_TRASH: { return migrateCommandEmptyTrash(); - case PENDING_COMMAND_EXPUNGE: + } + case PENDING_COMMAND_EXPUNGE: { return migrateCommandExpunge(oldPendingCommand); - default: + } + default: { throw new IllegalArgumentException("Tried to migrate unknown pending command!"); + } } } @@ -111,8 +125,7 @@ public class MigrationTo60 { String destFolder = command.arguments[2]; boolean isCopy = Boolean.parseBoolean(command.arguments[3]); - return MessagingControllerCommands.createMoveOrCopyBulk(srcFolder, destFolder, isCopy, - Collections.singletonList(uid)); + return MessagingControllerCommands.createMoveOrCopyBulk(srcFolder, destFolder, isCopy, singletonList(uid)); } private static PendingCommand migrateCommandMoveOrCopyBulkNew(OldPendingCommand command) { @@ -130,12 +143,11 @@ public class MigrationTo60 { return MessagingControllerCommands.createMoveOrCopyBulk(srcFolder, destFolder, isCopy, uidMap); } else { - List uids = new ArrayList<>(command.arguments.length -4); + List uids = new ArrayList<>(command.arguments.length - 4); uids.addAll(Arrays.asList(command.arguments).subList(4, command.arguments.length)); return MessagingControllerCommands.createMoveOrCopyBulk(srcFolder, destFolder, isCopy, uids); } - } private static PendingCommand migrateCommandMoveOrCopyBulk(OldPendingCommand command) { @@ -163,7 +175,7 @@ public class MigrationTo60 { boolean newState = Boolean.parseBoolean(command.arguments[2]); Flag flag = Flag.valueOf(command.arguments[3]); - return MessagingControllerCommands.createSetFlag(folder, newState, flag, Collections.singletonList(uid)); + return MessagingControllerCommands.createSetFlag(folder, newState, flag, singletonList(uid)); } private static PendingCommand migrateCommandSetFlagBulk(OldPendingCommand command) { @@ -171,7 +183,7 @@ public class MigrationTo60 { boolean newState = Boolean.parseBoolean(command.arguments[1]); Flag flag = Flag.valueOf(command.arguments[2]); - List uids = new ArrayList<>(command.arguments.length -3); + List uids = new ArrayList<>(command.arguments.length - 3); uids.addAll(Arrays.asList(command.arguments).subList(3, command.arguments.length)); return MessagingControllerCommands.createSetFlag(folder, newState, flag, uids); @@ -205,13 +217,6 @@ public class MigrationTo60 { } } - @VisibleForTesting - static class OldPendingCommand { - public String command; - public String[] arguments; - } - - private static String fastUrlDecode(String s) { byte[] bytes = s.getBytes(Charset.forName("UTF-8")); byte ch; @@ -227,7 +232,7 @@ public class MigrationTo60 { if (l > 9) { l -= 7; } - bytes[length] = (byte)((h << 4) | l); + bytes[length] = (byte) ((h << 4) | l); i += 2; } else if (ch == '+') { bytes[length] = ' '; @@ -238,4 +243,11 @@ public class MigrationTo60 { } return new String(bytes, 0, length, Charset.forName("UTF-8")); } + + + @VisibleForTesting + static class OldPendingCommand { + public String command; + public String[] arguments; + } } From 5d2e28e8ed269082eb4012515c93e2a025d6f848 Mon Sep 17 00:00:00 2001 From: cketti Date: Sun, 5 Feb 2017 07:13:25 +0100 Subject: [PATCH 10/12] Avoid runtime type checks/casts --- .../k9/controller/MessagingController.java | 46 +++---- .../MessagingControllerCommands.java | 119 ++++++++++++------ .../mailstore/migrations/MigrationTo60.java | 25 ++-- .../PendingCommandSerializerTest.java | 10 +- 4 files changed, 119 insertions(+), 81 deletions(-) diff --git a/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java b/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java index 36a6e5bab..6f896ab68 100644 --- a/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java +++ b/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java @@ -1736,19 +1736,8 @@ public class MessagingController { * other command processes. This maintains the order of the commands. */ try { - if (command instanceof PendingAppend) { - processPendingAppend((PendingAppend) command, account); - } else if (command instanceof PendingSetFlag) { - processPendingSetFlag((PendingSetFlag) command, account); - } else if (command instanceof PendingMarkAllAsRead) { - processPendingMarkAllAsRead((PendingMarkAllAsRead) command, account); - } else if (command instanceof PendingMoveOrCopy) { - processPendingMoveOrCopy((PendingMoveOrCopy) command, account); - } else if (command instanceof PendingEmptyTrash) { - processPendingEmptyTrash((PendingEmptyTrash) command, account); - } else if (command instanceof PendingExpunge) { - processPendingExpunge((PendingExpunge) command, account); - } + command.execute(this, account); + localStore.removePendingCommand(command); if (K9.DEBUG) { Log.d(K9.LOG_TAG, "Done processing pending command '" + command + "'"); @@ -1789,7 +1778,7 @@ public class MessagingController { * created. * TODO update the local message UID instead of deleteing it */ - private void processPendingAppend(PendingAppend command, Account account) throws MessagingException { + void processPendingAppend(PendingAppend command, Account account) throws MessagingException { Folder remoteFolder = null; LocalFolder localFolder = null; try { @@ -1915,7 +1904,7 @@ public class MessagingController { if (account.getErrorFolderName().equals(srcFolder)) { return; } - PendingCommand command = MessagingControllerCommands.createMoveOrCopyBulk(srcFolder, destFolder, isCopy, uids); + PendingCommand command = PendingMoveOrCopy.create(srcFolder, destFolder, isCopy, uids); queuePendingCommand(account, command); } @@ -1927,12 +1916,12 @@ public class MessagingController { if (account.getErrorFolderName().equals(srcFolder)) { return; } - PendingCommand command = MessagingControllerCommands.createMoveOrCopyBulk(srcFolder, destFolder, isCopy, uidMap); + PendingCommand command = PendingMoveOrCopy.create(srcFolder, destFolder, isCopy, uidMap); queuePendingCommand(account, command); } } - private void processPendingMoveOrCopy(PendingMoveOrCopy command, Account account) throws MessagingException { + void processPendingMoveOrCopy(PendingMoveOrCopy command, Account account) throws MessagingException { Folder remoteSrcFolder = null; Folder remoteDestFolder = null; LocalFolder localDestFolder; @@ -2031,7 +2020,7 @@ public class MessagingController { putBackground("queueSetFlag " + account.getDescription() + ":" + folderName, null, new Runnable() { @Override public void run() { - PendingCommand command = MessagingControllerCommands.createSetFlag(folderName, newState, flag, uids); + PendingCommand command = PendingSetFlag.create(folderName, newState, flag, uids); queuePendingCommand(account, command); processPendingCommands(account); } @@ -2040,7 +2029,7 @@ public class MessagingController { /** * Processes a pending mark read or unread command. */ - private void processPendingSetFlag(PendingSetFlag command, Account account) throws MessagingException { + void processPendingSetFlag(PendingSetFlag command, Account account) throws MessagingException { String folder = command.folder; if (account.getErrorFolderName().equals(folder) || account.getOutboxFolderName().equals(folder)) { @@ -2081,13 +2070,13 @@ public class MessagingController { putBackground("queueExpunge " + account.getDescription() + ":" + folderName, null, new Runnable() { @Override public void run() { - PendingCommand command = MessagingControllerCommands.createExpunge(folderName); + PendingCommand command = PendingExpunge.create(folderName); queuePendingCommand(account, command); processPendingCommands(account); } }); } - private void processPendingExpunge(PendingExpunge command, Account account) throws MessagingException { + void processPendingExpunge(PendingExpunge command, Account account) throws MessagingException { String folder = command.folder; if (account.getErrorFolderName().equals(folder)) { @@ -2114,7 +2103,7 @@ public class MessagingController { } } - private void processPendingMarkAllAsRead(PendingMarkAllAsRead command, Account account) throws MessagingException { + void processPendingMarkAllAsRead(PendingMarkAllAsRead command, Account account) throws MessagingException { String folder = command.folder; Folder remoteFolder = null; LocalFolder localFolder = null; @@ -2238,7 +2227,7 @@ public class MessagingController { if (K9.DEBUG) { Log.i(K9.LOG_TAG, "Marking all messages in " + account.getDescription() + ":" + folder + " as read"); } - PendingCommand command = MessagingControllerCommands.createMarkAllAsRead(folder); + PendingCommand command = PendingMarkAllAsRead.create(folder); queuePendingCommand(account, command); processPendingCommands(account); } @@ -2821,7 +2810,7 @@ public class MessagingController { if (K9.DEBUG) Log.i(K9.LOG_TAG, "Moved sent message to folder '" + account.getSentFolderName() + "' (" + localSentFolder.getId() + ") "); - PendingCommand command = MessagingControllerCommands.createAppend(localSentFolder.getName(), message.getUid()); + PendingCommand command = PendingAppend.create(localSentFolder.getName(), message.getUid()); queuePendingCommand(account, command); processPendingCommands(account); } @@ -3420,7 +3409,7 @@ public class MessagingController { for (Message message : messages) { // If the message was in the Outbox, then it has been copied to local Trash, and has // to be copied to remote trash - PendingCommand command = MessagingControllerCommands.createAppend(account.getTrashFolderName(), message.getUid()); + PendingCommand command = PendingAppend.create(account.getTrashFolderName(), message.getUid()); queuePendingCommand(account, command); } processPendingCommands(account); @@ -3461,8 +3450,7 @@ public class MessagingController { return uids; } - @SuppressWarnings("UnusedParameters") // for consistency with other PendingCommand methods - private void processPendingEmptyTrash(PendingEmptyTrash command, Account account) throws MessagingException { + void processPendingEmptyTrash(Account account) throws MessagingException { Store remoteStore = account.getRemoteStore(); Folder remoteFolder = remoteStore.getFolder(account.getTrashFolderName()); @@ -3508,7 +3496,7 @@ public class MessagingController { } if (!isTrashLocalOnly) { - PendingCommand command = MessagingControllerCommands.createEmptyTrash(); + PendingCommand command = PendingEmptyTrash.create(); queuePendingCommand(account, command); processPendingCommands(account); } @@ -4001,7 +3989,7 @@ public class MessagingController { localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true); if (saveRemotely) { - PendingCommand command = MessagingControllerCommands.createAppend(localFolder.getName(), localMessage.getUid()); + PendingCommand command = PendingAppend.create(localFolder.getName(), localMessage.getUid()); queuePendingCommand(account, command); processPendingCommands(account); } diff --git a/k9mail/src/main/java/com/fsck/k9/controller/MessagingControllerCommands.java b/k9mail/src/main/java/com/fsck/k9/controller/MessagingControllerCommands.java index b2d0fed66..fc1552a95 100644 --- a/k9mail/src/main/java/com/fsck/k9/controller/MessagingControllerCommands.java +++ b/k9mail/src/main/java/com/fsck/k9/controller/MessagingControllerCommands.java @@ -4,7 +4,9 @@ package com.fsck.k9.controller; import java.util.List; import java.util.Map; +import com.fsck.k9.Account; import com.fsck.k9.mail.Flag; +import com.fsck.k9.mail.MessagingException; public class MessagingControllerCommands { @@ -16,41 +18,14 @@ public class MessagingControllerCommands { static final String COMMAND_EMPTY_TRASH = "empty_trash"; - public static PendingSetFlag createSetFlag(String folder, boolean newState, Flag flag, List uids) { - return new PendingSetFlag(folder, newState, flag, uids); - } - - public static PendingExpunge createExpunge(String folderName) { - return new PendingExpunge(folderName); - } - - public static PendingMarkAllAsRead createMarkAllAsRead(String folder) { - return new PendingMarkAllAsRead(folder); - } - - public static PendingAppend createAppend(String folderName, String uid) { - return new PendingAppend(folderName, uid); - } - - public static PendingEmptyTrash createEmptyTrash() { - return new PendingEmptyTrash(); - } - - public static PendingMoveOrCopy createMoveOrCopyBulk(String srcFolder, String destFolder, boolean isCopy, Map uidMap) { - return new PendingMoveOrCopy(srcFolder, destFolder, isCopy, null, uidMap); - } - - public static PendingMoveOrCopy createMoveOrCopyBulk(String srcFolder, String destFolder, boolean isCopy, List uids) { - return new PendingMoveOrCopy(srcFolder, destFolder, isCopy, uids, null); - } + public abstract static class PendingCommand { + public long databaseId; - public static abstract class PendingCommand { - public transient long databaseId; + PendingCommand() { } public abstract String getCommandName(); - - private PendingCommand() { } + public abstract void execute(MessagingController controller, Account account) throws MessagingException; } public static class PendingMoveOrCopy extends PendingCommand { @@ -60,8 +35,18 @@ public class MessagingControllerCommands { public final List uids; public final Map newUidMap; - public PendingMoveOrCopy( - String srcFolder, String destFolder, boolean isCopy, List uids, Map newUidMap) { + + public static PendingMoveOrCopy create(String srcFolder, String destFolder, boolean isCopy, + Map uidMap) { + return new PendingMoveOrCopy(srcFolder, destFolder, isCopy, null, uidMap); + } + + public static PendingMoveOrCopy create(String srcFolder, String destFolder, boolean isCopy, List uids) { + return new PendingMoveOrCopy(srcFolder, destFolder, isCopy, uids, null); + } + + private PendingMoveOrCopy(String srcFolder, String destFolder, boolean isCopy, List uids, + Map newUidMap) { this.srcFolder = srcFolder; this.destFolder = destFolder; this.isCopy = isCopy; @@ -69,15 +54,31 @@ public class MessagingControllerCommands { this.newUidMap = newUidMap; } + @Override public String getCommandName() { return COMMAND_MOVE_OR_COPY; } + + @Override + public void execute(MessagingController controller, Account account) throws MessagingException { + controller.processPendingMoveOrCopy(this, account); + } } public static class PendingEmptyTrash extends PendingCommand { + public static PendingEmptyTrash create() { + return new PendingEmptyTrash(); + } + + @Override public String getCommandName() { return COMMAND_EMPTY_TRASH; } + + @Override + public void execute(MessagingController controller, Account account) throws MessagingException { + controller.processPendingEmptyTrash(account); + } } public static class PendingSetFlag extends PendingCommand { @@ -86,53 +87,97 @@ public class MessagingControllerCommands { public final Flag flag; public final List uids; - public PendingSetFlag(String folder, boolean newState, Flag flag, List uids) { + + public static PendingSetFlag create(String folder, boolean newState, Flag flag, List uids) { + return new PendingSetFlag(folder, newState, flag, uids); + } + + private PendingSetFlag(String folder, boolean newState, Flag flag, List uids) { this.folder = folder; this.newState = newState; this.flag = flag; this.uids = uids; } + @Override public String getCommandName() { return COMMAND_SET_FLAG; } + + @Override + public void execute(MessagingController controller, Account account) throws MessagingException { + controller.processPendingSetFlag(this, account); + } } public static class PendingAppend extends PendingCommand { public final String folder; public final String uid; - public PendingAppend(String folder, String uid) { + + public static PendingAppend create(String folderName, String uid) { + return new PendingAppend(folderName, uid); + } + + private PendingAppend(String folder, String uid) { this.folder = folder; this.uid = uid; } + @Override public String getCommandName() { return COMMAND_APPEND; } + + @Override + public void execute(MessagingController controller, Account account) throws MessagingException { + controller.processPendingAppend(this, account); + } } public static class PendingMarkAllAsRead extends PendingCommand { public final String folder; - public PendingMarkAllAsRead(String folder) { + + public static PendingMarkAllAsRead create(String folder) { + return new PendingMarkAllAsRead(folder); + } + + private PendingMarkAllAsRead(String folder) { this.folder = folder; } + @Override public String getCommandName() { return COMMAND_MARK_ALL_AS_READ; } + + @Override + public void execute(MessagingController controller, Account account) throws MessagingException { + controller.processPendingMarkAllAsRead(this, account); + } } public static class PendingExpunge extends PendingCommand { public final String folder; - public PendingExpunge(String folder) { + + public static PendingExpunge create(String folderName) { + return new PendingExpunge(folderName); + } + + private PendingExpunge(String folder) { this.folder = folder; } + @Override public String getCommandName() { return COMMAND_EXPUNGE; } + + @Override + public void execute(MessagingController controller, Account account) throws MessagingException { + controller.processPendingExpunge(this, account); + } } } diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo60.java b/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo60.java index 42734d681..36e4c54ad 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo60.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/migrations/MigrationTo60.java @@ -13,8 +13,13 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.support.annotation.VisibleForTesting; -import com.fsck.k9.controller.MessagingControllerCommands; +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.controller.PendingCommandSerializer; import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.Flag; @@ -112,11 +117,11 @@ class MigrationTo60 { private static PendingCommand migrateCommandExpunge(OldPendingCommand command) { String folder = command.arguments[0]; - return MessagingControllerCommands.createExpunge(folder); + return PendingExpunge.create(folder); } private static PendingCommand migrateCommandEmptyTrash() { - return MessagingControllerCommands.createEmptyTrash(); + return PendingEmptyTrash.create(); } private static PendingCommand migrateCommandMoveOrCopy(OldPendingCommand command) { @@ -125,7 +130,7 @@ class MigrationTo60 { String destFolder = command.arguments[2]; boolean isCopy = Boolean.parseBoolean(command.arguments[3]); - return MessagingControllerCommands.createMoveOrCopyBulk(srcFolder, destFolder, isCopy, singletonList(uid)); + return PendingMoveOrCopy.create(srcFolder, destFolder, isCopy, singletonList(uid)); } private static PendingCommand migrateCommandMoveOrCopyBulkNew(OldPendingCommand command) { @@ -141,12 +146,12 @@ class MigrationTo60 { uidMap.put(command.arguments[i], command.arguments[i + offset]); } - return MessagingControllerCommands.createMoveOrCopyBulk(srcFolder, destFolder, isCopy, uidMap); + return PendingMoveOrCopy.create(srcFolder, destFolder, isCopy, uidMap); } else { List uids = new ArrayList<>(command.arguments.length - 4); uids.addAll(Arrays.asList(command.arguments).subList(4, command.arguments.length)); - return MessagingControllerCommands.createMoveOrCopyBulk(srcFolder, destFolder, isCopy, uids); + return PendingMoveOrCopy.create(srcFolder, destFolder, isCopy, uids); } } @@ -166,7 +171,7 @@ class MigrationTo60 { } private static PendingCommand migrateCommandMarkAllAsRead(OldPendingCommand command) { - return MessagingControllerCommands.createMarkAllAsRead(command.arguments[0]); + return PendingMarkAllAsRead.create(command.arguments[0]); } private static PendingCommand migrateCommandSetFlag(OldPendingCommand command) { @@ -175,7 +180,7 @@ class MigrationTo60 { boolean newState = Boolean.parseBoolean(command.arguments[2]); Flag flag = Flag.valueOf(command.arguments[3]); - return MessagingControllerCommands.createSetFlag(folder, newState, flag, singletonList(uid)); + return PendingSetFlag.create(folder, newState, flag, singletonList(uid)); } private static PendingCommand migrateCommandSetFlagBulk(OldPendingCommand command) { @@ -186,13 +191,13 @@ class MigrationTo60 { List uids = new ArrayList<>(command.arguments.length - 3); uids.addAll(Arrays.asList(command.arguments).subList(3, command.arguments.length)); - return MessagingControllerCommands.createSetFlag(folder, newState, flag, uids); + return PendingSetFlag.create(folder, newState, flag, uids); } private static PendingCommand migrateCommandAppend(OldPendingCommand command) { String folder = command.arguments[0]; String uid = command.arguments[1]; - return MessagingControllerCommands.createAppend(folder, uid); + return PendingAppend.create(folder, uid); } private static List getPendingCommands(SQLiteDatabase db) { diff --git a/k9mail/src/test/java/com/fsck/k9/controller/PendingCommandSerializerTest.java b/k9mail/src/test/java/com/fsck/k9/controller/PendingCommandSerializerTest.java index eccdb8f54..56ba9be9a 100644 --- a/k9mail/src/test/java/com/fsck/k9/controller/PendingCommandSerializerTest.java +++ b/k9mail/src/test/java/com/fsck/k9/controller/PendingCommandSerializerTest.java @@ -32,7 +32,7 @@ public class PendingCommandSerializerTest { @Test public void testSerializeDeserialize__withoutArguments() { - PendingCommand pendingCommand = MessagingControllerCommands.createEmptyTrash(); + PendingCommand pendingCommand = PendingEmptyTrash.create(); String serializedCommand = pendingCommandSerializer.serialize(pendingCommand); PendingEmptyTrash unserializedCommand = (PendingEmptyTrash) pendingCommandSerializer.unserialize( @@ -43,7 +43,7 @@ public class PendingCommandSerializerTest { @Test public void testSerializeDeserialize__withArguments() { - PendingCommand pendingCommand = MessagingControllerCommands.createAppend(SOURCE_FOLDER, UID); + PendingCommand pendingCommand = PendingAppend.create(SOURCE_FOLDER, UID); String serializedCommand = pendingCommandSerializer.serialize(pendingCommand); PendingAppend unserializedCommand = (PendingAppend) pendingCommandSerializer.unserialize( @@ -56,7 +56,7 @@ public class PendingCommandSerializerTest { @Test public void testSerializeDeserialize__withComplexArguments() { - PendingCommand pendingCommand = MessagingControllerCommands.createMoveOrCopyBulk( + PendingCommand pendingCommand = PendingMoveOrCopy.create( SOURCE_FOLDER, DEST_FOLDER, IS_COPY, UID_MAP); String serializedCommand = pendingCommandSerializer.serialize(pendingCommand); @@ -71,10 +71,10 @@ public class PendingCommandSerializerTest { @Test(expected = IllegalArgumentException.class) public void testDeserialize__withUnknownCommandName__shouldFail() { - PendingCommand pendingCommand = MessagingControllerCommands.createEmptyTrash(); + PendingCommand pendingCommand = PendingEmptyTrash.create(); String serializedCommand = pendingCommandSerializer.serialize(pendingCommand); pendingCommandSerializer.unserialize(DATABASE_ID, "BAD_COMMAND_NAME", serializedCommand); } -} \ No newline at end of file +} From af0c47c3257a5ff03537d16eb227468179f10b78 Mon Sep 17 00:00:00 2001 From: Philip Whitehouse Date: Sun, 5 Feb 2017 21:23:43 +0000 Subject: [PATCH 11/12] Additional tests for migration --- .../migrations/MigrationTo60Test.java | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/k9mail/src/test/java/com/fsck/k9/mailstore/migrations/MigrationTo60Test.java b/k9mail/src/test/java/com/fsck/k9/mailstore/migrations/MigrationTo60Test.java index 05d2dd465..1d1ed7366 100644 --- a/k9mail/src/test/java/com/fsck/k9/mailstore/migrations/MigrationTo60Test.java +++ b/k9mail/src/test/java/com/fsck/k9/mailstore/migrations/MigrationTo60Test.java @@ -1,10 +1,15 @@ package com.fsck.k9.mailstore.migrations; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; @@ -18,12 +23,17 @@ 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 org.junit.Assert.assertArrayEquals; 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"; @@ -48,6 +58,43 @@ public class MigrationTo60Test { UID_MAP.put("uid_2", "uid_other_2"); } + @Test + public void migratePendingCommands_changesTableStructure() { + SQLiteDatabase database = SQLiteDatabase.create(null); + createV59Table(database); + MigrationTo60.migratePendingCommands(database); + + List columns = getColumnList(database, "pending_commands"); + + assertArrayEquals(new String[]{"id", "command", "data"}, columns.toArray()); + } + + @Test + public void migratePendingCommands_isMultiRunSafe() { + SQLiteDatabase database = SQLiteDatabase.create(null); + createV59Table(database); + MigrationTo60.migratePendingCommands(database); + + MigrationTo60.migratePendingCommands(database); + } + + private List getColumnList(SQLiteDatabase db, String table) { + List columns = new ArrayList<>(); + Cursor columnCursor = db.rawQuery("PRAGMA table_info("+table+")", null); + while (columnCursor.moveToNext()) { + columns.add(columnCursor.getString(1)); + } + return columns; + } + + private void createV59Table(SQLiteDatabase db) { + db.execSQL("CREATE TABLE pending_commands (" + + "id INTEGER PRIMARY KEY, " + + "command TEXT, " + + "arguments TEXT" + + ")"); + } + @Test public void testMigrateMoveOrCopy__withUidArray() { From 78c7f01719254f45505bfc7c7caa542276a93256 Mon Sep 17 00:00:00 2001 From: cketti Date: Mon, 6 Feb 2017 01:41:03 +0100 Subject: [PATCH 12/12] Clean up MigrationTo60Test --- .../migrations/MigrationTo60Test.java | 295 +++++++++--------- 1 file changed, 145 insertions(+), 150 deletions(-) diff --git a/k9mail/src/test/java/com/fsck/k9/mailstore/migrations/MigrationTo60Test.java b/k9mail/src/test/java/com/fsck/k9/mailstore/migrations/MigrationTo60Test.java index 1d1ed7366..0f8796838 100644 --- a/k9mail/src/test/java/com/fsck/k9/mailstore/migrations/MigrationTo60Test.java +++ b/k9mail/src/test/java/com/fsck/k9/mailstore/migrations/MigrationTo60Test.java @@ -1,11 +1,7 @@ package com.fsck.k9.mailstore.migrations; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; - import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -13,6 +9,9 @@ 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; @@ -27,11 +26,12 @@ import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; -import static org.junit.Assert.assertArrayEquals; +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 { @@ -53,51 +53,33 @@ public class MigrationTo60Test { private static final Flag FLAG = Flag.X_DESTROYED; private static final String UID = "uid"; private static final HashMap 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_changesTableStructure() { - SQLiteDatabase database = SQLiteDatabase.create(null); - createV59Table(database); + public void migratePendingCommands_shouldChangeTableStructure() { + SQLiteDatabase database = createV59Table(); + MigrationTo60.migratePendingCommands(database); List columns = getColumnList(database, "pending_commands"); - - assertArrayEquals(new String[]{"id", "command", "data"}, columns.toArray()); + assertEquals(asList("id", "command", "data"), columns); } @Test - public void migratePendingCommands_isMultiRunSafe() { - SQLiteDatabase database = SQLiteDatabase.create(null); - createV59Table(database); + public void migratePendingCommands_withMultipleRuns_shouldNotThrow() { + SQLiteDatabase database = createV59Table(); MigrationTo60.migratePendingCommands(database); MigrationTo60.migratePendingCommands(database); } - private List getColumnList(SQLiteDatabase db, String table) { - List columns = new ArrayList<>(); - Cursor columnCursor = db.rawQuery("PRAGMA table_info("+table+")", null); - while (columnCursor.moveToNext()) { - columns.add(columnCursor.getString(1)); - } - return columns; - } - - private void createV59Table(SQLiteDatabase db) { - db.execSQL("CREATE TABLE pending_commands (" + - "id INTEGER PRIMARY KEY, " + - "command TEXT, " + - "arguments TEXT" + - ")"); - } - - @Test - public void testMigrateMoveOrCopy__withUidArray() { + public void migrateMoveOrCopy_withUidArray() { OldPendingCommand command = queueMoveOrCopy(SOURCE_FOLDER, DEST_FOLDER, IS_COPY, UID_ARRAY); PendingMoveOrCopy pendingCommand = (PendingMoveOrCopy) MigrationTo60.migratePendingCommand(command); @@ -105,10 +87,117 @@ public class MigrationTo60Test { assertEquals(SOURCE_FOLDER, pendingCommand.srcFolder); assertEquals(DEST_FOLDER, pendingCommand.destFolder); assertEquals(IS_COPY, pendingCommand.isCopy); - assertEquals(Arrays.asList(UID_ARRAY), pendingCommand.uids); + 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; @@ -123,22 +212,7 @@ public class MigrationTo60Test { return command; } - - @Test - public void testMigrateMoveOrCopy__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); - } - - OldPendingCommand queueMoveOrCopy( - String srcFolder, String destFolder, boolean isCopy, Map uidMap) { + OldPendingCommand queueMoveOrCopy(String srcFolder, String destFolder, boolean isCopy, Map uidMap) { OldPendingCommand command = new OldPendingCommand(); command.command = PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW; @@ -151,24 +225,11 @@ public class MigrationTo60Test { Set strings = uidMap.keySet(); System.arraycopy(strings.toArray(new String[strings.size()]), 0, command.arguments, 4, uidMap.keySet().size()); Collection values = uidMap.values(); - System.arraycopy(values.toArray(new String[values.size()]), 0, command.arguments, 4 + uidMap.keySet().size(), uidMap.values().size()); + System.arraycopy(values.toArray(new String[values.size()]), 0, command.arguments, 4 + uidMap.keySet().size(), + uidMap.values().size()); return command; } - - @Test - public void testMigrateMoveOrCopy__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(Arrays.asList(UID_ARRAY), pendingCommand.uids); - assertNull(pendingCommand.newUidMap); - } - OldPendingCommand queueMoveOrCopyOld(String srcFolder, String destFolder, boolean isCopy, String uids[]) { OldPendingCommand command = new OldPendingCommand(); command.command = PENDING_COMMAND_MOVE_OR_COPY_BULK; @@ -182,20 +243,6 @@ public class MigrationTo60Test { return command; } - - @Test - public void testMigrateMoveOrCopy__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); - } - OldPendingCommand queueMoveOrCopyEvenOlder(String srcFolder, String destFolder, String uid, boolean isCopy) { OldPendingCommand command = new OldPendingCommand(); command.command = PENDING_COMMAND_MOVE_OR_COPY; @@ -208,19 +255,6 @@ public class MigrationTo60Test { return command; } - - @Test - public void testMigrateSetFlag() { - 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(Arrays.asList(UID_ARRAY), pendingCommand.uids); - } - OldPendingCommand queueSetFlagBulk(String folderName, boolean newState, Flag flag, String[] uids) { OldPendingCommand command = new OldPendingCommand(); command.command = PENDING_COMMAND_SET_FLAG_BULK; @@ -233,19 +267,6 @@ public class MigrationTo60Test { return command; } - - @Test - public void testMigrateSetFlag__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); - } - OldPendingCommand queueSetFlagOld(String folderName, boolean newState, Flag flag, String uid) { OldPendingCommand command = new OldPendingCommand(); command.command = PENDING_COMMAND_SET_FLAG; @@ -257,16 +278,6 @@ public class MigrationTo60Test { return command; } - - @Test - public void testMigrateExpunge() { - OldPendingCommand command = queueExpunge(SOURCE_FOLDER); - - PendingExpunge pendingCommand = (PendingExpunge) MigrationTo60.migratePendingCommand(command); - - assertEquals(SOURCE_FOLDER, pendingCommand.folder); - } - OldPendingCommand queueExpunge(String folderName) { OldPendingCommand command = new OldPendingCommand(); command.command = PENDING_COMMAND_EXPUNGE; @@ -275,16 +286,6 @@ public class MigrationTo60Test { return command; } - - @Test - public void testMigrateEmptyTrash() { - OldPendingCommand command = queueEmptyTrash(); - - PendingCommand pendingCommand = MigrationTo60.migratePendingCommand(command); - - assertTrue(pendingCommand instanceof PendingEmptyTrash); - } - OldPendingCommand queueEmptyTrash() { OldPendingCommand command = new OldPendingCommand(); command.command = PENDING_COMMAND_EMPTY_TRASH; @@ -292,16 +293,6 @@ public class MigrationTo60Test { return command; } - - @Test - public void testMigrateMarkAllMessagesRead() { - OldPendingCommand command = queueMarkAllMessagesRead(SOURCE_FOLDER); - - PendingMarkAllAsRead pendingCommand = (PendingMarkAllAsRead) MigrationTo60.migratePendingCommand(command); - - assertEquals(SOURCE_FOLDER, pendingCommand.folder); - } - OldPendingCommand queueMarkAllMessagesRead(final String folder) { OldPendingCommand command = new OldPendingCommand(); command.command = PENDING_COMMAND_MARK_ALL_AS_READ; @@ -309,22 +300,26 @@ public class MigrationTo60Test { return command; } - - @Test - public void testMigrateAppend() { - OldPendingCommand command = queueAppend(SOURCE_FOLDER, UID); - - PendingAppend pendingCommand = (PendingAppend) MigrationTo60.migratePendingCommand(command); - - assertEquals(SOURCE_FOLDER, pendingCommand.folder); - assertEquals(UID, pendingCommand.uid); + private List getColumnList(SQLiteDatabase db, String table) { + List 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; } - OldPendingCommand queueAppend(String srcFolder, String uid) { - OldPendingCommand command = new OldPendingCommand(); - command.command = PENDING_COMMAND_APPEND; - command.arguments = new String[] { srcFolder, uid }; - return command; + private SQLiteDatabase createV59Table() { + SQLiteDatabase database = SQLiteDatabase.create(null); + database.execSQL("CREATE TABLE pending_commands (" + + "id INTEGER PRIMARY KEY, " + + "command TEXT, " + + "arguments TEXT" + + ")"); + return database; } - -} \ No newline at end of file +}