IOS-1521 next round testing IOS-1521+IOS-1495
authorbuff <andreas@pep-project.org>
Tue, 14 May 2019 23:08:50 +0200
branchIOS-1521+IOS-1495
changeset 86186ee1f9a0b281
parent 8617 863f7380e2d8
child 8619 908d9f21f84f
IOS-1521 next round testing
pEpForiOS.xcodeproj/project.pbxproj
pEpForiOSTests/Background/FetchMessagesOperationTest.swift
pEpForiOSTests/Background/FetchNumberOfNewMailsServiceTest.swift
pEpForiOSTests/Background/SyncFlagsToServerOperationTest.swift
pEpForiOSTests/HandshakePartnerTableViewCellViewModelTests.swift
pEpForiOSTests/Models/Filter/AttachmentFilterTest.swift
pEpForiOSTests/Models/Message+FakeMessageTest.swift
pEpForiOSTests/SimpleOperationsTest.swift
pEpForiOSTests/TestUtils/TestUtil.swift
pEpForiOSTests/UI/Compose/ViewModel/ComposeViewModelSectionTest.swift
pEpForiOSTests/UI/Compose/ViewModel/ComposeViewModelTest.swift
     1.1 --- a/pEpForiOS.xcodeproj/project.pbxproj	Tue May 14 22:31:30 2019 +0200
     1.2 +++ b/pEpForiOS.xcodeproj/project.pbxproj	Tue May 14 23:08:50 2019 +0200
     1.3 @@ -120,7 +120,6 @@
     1.4  		1541D7F01FC81ED900D52A5D /* URL+ExtensionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1541D7EF1FC81ED900D52A5D /* URL+ExtensionsTest.swift */; };
     1.5  		1541D7F31FC8292D00D52A5D /* URL+MIME.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1541D7F21FC8292D00D52A5D /* URL+MIME.swift */; };
     1.6  		1541D7F51FC82A4900D52A5D /* URL+MIME.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1541D7F41FC82A4900D52A5D /* URL+MIME.swift */; };
     1.7 -		1544BD0221524C9F0075C5A0 /* AttachmentFilterTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1544BD0121524C9F0075C5A0 /* AttachmentFilterTest.swift */; };
     1.8  		154D92CF20AC1745009A5868 /* MoveToFolderOperationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 154D92CE20AC1744009A5868 /* MoveToFolderOperationTest.swift */; };
     1.9  		154F0A8220874B3E00C77D72 /* ContentDispositionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 154F0A8120874B3E00C77D72 /* ContentDispositionTest.swift */; };
    1.10  		155050F01FE82356009CEAD2 /* UserNotificationTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 155050EF1FE82356009CEAD2 /* UserNotificationTool.swift */; };
    1.11 @@ -602,7 +601,6 @@
    1.12  		1541D7EF1FC81ED900D52A5D /* URL+ExtensionsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+ExtensionsTest.swift"; sourceTree = "<group>"; };
    1.13  		1541D7F21FC8292D00D52A5D /* URL+MIME.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+MIME.swift"; sourceTree = "<group>"; };
    1.14  		1541D7F41FC82A4900D52A5D /* URL+MIME.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+MIME.swift"; sourceTree = "<group>"; };
    1.15 -		1544BD0121524C9F0075C5A0 /* AttachmentFilterTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AttachmentFilterTest.swift; path = pEpForiOSTests/Models/Filter/AttachmentFilterTest.swift; sourceTree = SOURCE_ROOT; };
    1.16  		154D92CE20AC1744009A5868 /* MoveToFolderOperationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveToFolderOperationTest.swift; sourceTree = "<group>"; };
    1.17  		154F0A8120874B3E00C77D72 /* ContentDispositionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentDispositionTest.swift; sourceTree = "<group>"; };
    1.18  		155050EF1FE82356009CEAD2 /* UserNotificationTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationTool.swift; sourceTree = "<group>"; };
    1.19 @@ -1312,7 +1310,6 @@
    1.20  			isa = PBXGroup;
    1.21  			children = (
    1.22  				15BA536D20A095410090F126 /* UnifiedInboxTest.swift */,
    1.23 -				1544BD0121524C9F0075C5A0 /* AttachmentFilterTest.swift */,
    1.24  				00227E6921635DED000D9BDB /* FilterViewModelTest.swift */,
    1.25  			);
    1.26  			path = Filter;
    1.27 @@ -3006,7 +3003,6 @@
    1.28  				003C0FA720B5581A0093A987 /* SecretTestData.swift in Sources */,
    1.29  				1555361B207796CE00CDDAFA /* CWInternetAddress+TestUtils.swift in Sources */,
    1.30  				154D92CF20AC1745009A5868 /* MoveToFolderOperationTest.swift in Sources */,
    1.31 -				1544BD0221524C9F0075C5A0 /* AttachmentFilterTest.swift in Sources */,
    1.32  				15A536902155136800CF6204 /* PEPUtilTest.swift in Sources */,
    1.33  				4918EBFC1E783C70006207FC /* CdMessage+PantomimeTest.swift in Sources */,
    1.34  				151F7201202A06760057C74D /* CdAccount+TestUtils.swift in Sources */,
     2.1 --- a/pEpForiOSTests/Background/FetchMessagesOperationTest.swift	Tue May 14 22:31:30 2019 +0200
     2.2 +++ b/pEpForiOSTests/Background/FetchMessagesOperationTest.swift	Tue May 14 23:08:50 2019 +0200
     2.3 @@ -1,199 +1,164 @@
     2.4 -//!!!:erious application error.  Exception was caught during Core Data change processing.  This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.  -[__NSCFSet addObject:]: attempt to insert nil with userInfo (null)
     2.5 +//
     2.6 +//  FetchMessagesOperationTest.swift
     2.7 +//  pEpForiOS
     2.8 +//
     2.9 +//  Created by buff on 09.08.17.
    2.10 +//  Copyright © 2017 p≡p Security S.A. All rights reserved.
    2.11 +//
    2.12  
    2.13 -/*CoreData: error: Serious application error.  Exception was caught during Core Data change processing.  This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.  -[__NSCFSet addObject:]: attempt to insert nil with userInfo (null)
    2.14 -2019-04-04 20:42:20.009631+0200 pEp[68473:2055730] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFSet addObject:]: attempt to insert nil'
    2.15 -*** First throw call stack:
    2.16 -(
    2.17 -0   CoreFoundation                      0x000000010e9a26fb __exceptionPreprocess + 331
    2.18 -1   libobjc.A.dylib                     0x000000010d3baac5 objc_exception_throw + 48
    2.19 -2   CoreFoundation                      0x000000010e9a2555 +[NSException raise:format:] + 197
    2.20 -3   CoreFoundation                      0x000000010e97ea7a -[__NSCFSet addObject:] + 202
    2.21 -4   CoreData                            0x000000010e345811 -[NSManagedObjectContext(_NSInternalChangeProcessing) _processPendingUpdates:] + 385
    2.22 -5   CoreData                            0x000000010e33ff57 -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] + 1127
    2.23 -6   CoreData                            0x000000010e347415 -[NSManagedObjectContext(_NSInternalChangeProcessing) _prepareForPushChanges:] + 165
    2.24 -7   CoreData                            0x000000010e343dbb -[NSManagedObjectContext save:] + 523
    2.25 -8   MessageModel                        0x000000010ab00d55 $sSo22NSManagedObjectContextC12MessageModelE16saveAndLogErrorsyyF + 901
    2.26 -9   MessageModel                        0x000000010ac06230 $s12MessageModel28ManagedObjectWrapperProtocolPAAE4saveyyF + 784
    2.27 -10  MessageModel                        0x000000010ab6f856 $s12MessageModel02CdA0C10cdIdentity16pantomimeAddressAA0cE0CSo010CWInternetG0C_tFZ + 2534
    2.28 -11  MessageModel                        0x000000010ab6cd0f $s12MessageModel02CdA0C14insertOrUpdate09pantomimeA07account07messageF0ACSgSo13CWIMAPMessageC_AA0C7AccountCSo09CWMessageF0CtFZ + 5039
    2.29 -12  MessageModel                        0x000000010acc3f8a $s12MessageModel28StorePrefetchedMailOperationC14insertOrUpdate09pantomimeA07accountAA02CdA0CSgSo13CWIMAPMessageC_AA0L7AccountCtF + 650
    2.30 -13  MessageModel                        0x000000010acc30aa $s12MessageModel28StorePrefetchedMailOperationC05storeA07contextySo22NSManagedObjectContextC_tF + 1834
    2.31 -14  MessageModel                        0x000000010acc2705 $s12MessageModel28StorePrefetchedMailOperationC4mainyyFyycfU_ + 917
    2.32 -15  MessageModel                        0x000000010acc2871 $s12MessageModel28StorePrefetchedMailOperationC4mainyyFyycfU_TA + 17
    2.33 -16  MessageModel                        0x000000010a8f901e $sIeg_IeyB_TR + 142
    2.34 -17  CoreData                            0x000000010e35c84a developerSubmittedBlockToNSManagedObjectContextPerform + 170
    2.35 -18  libclang_rt.asan_iossim_dynamic.dylib 0x00000001098317b4 asan_dispatch_call_block_and_release + 260
    2.36 -19  libdispatch.dylib                   0x0000000111847d02 _dispatch_client_callout + 8
    2.37 -20  libdispatch.dylib                   0x000000011184e720 _dispatch_lane_serial_drain + 705
    2.38 -21  libdispatch.dylib                   0x000000011184f261 _dispatch_lane_invoke + 398
    2.39 -22  libdispatch.dylib                   0x0000000111857fcb _dispatch_workloop_worker_thread + 645
    2.40 -23  libsystem_pthread.dylib             0x0000000111c29611 _pthread_wqthread + 421
    2.41 -24  libsystem_pthread.dylib             0x0000000111c293fd start_wqthread + 13
    2.42 -)
    2.43 -libc++abi.dylib: terminating with uncaught exception of type NSException
    2.44 -*/
    2.45 +import XCTest
    2.46 +import CoreData
    2.47  
    2.48 -////
    2.49 -////  FetchMessagesOperationTest.swift
    2.50 -////  pEpForiOS
    2.51 -////
    2.52 -////  Created by buff on 09.08.17.
    2.53 -////  Copyright © 2017 p≡p Security S.A. All rights reserved.
    2.54 -////
    2.55 +@testable import MessageModel
    2.56 +@testable import pEpForiOS
    2.57 +
    2.58 +class FetchMessagesOperationTest: CoreDataDrivenTestBase {
    2.59 +    // IOS-671 pEp app has two accounts. Someone sends a mail to both
    2.60 +    // (with both accounts in receipients).
    2.61 +    // Message must exist twice, once for each account, after fetching mails from server.
    2.62 +    // Commented as randomly failing and crashing. See IOS-1465.
    2.63 +//    func testMailSentToBothPepAccounts() {
    2.64 +//        // Setup 2 accounts
    2.65 +//        cdAccount.createRequiredFoldersAndWait(testCase: self)
    2.66 +//        Record.saveAndWait()
    2.67  //
    2.68 -//import XCTest
    2.69 -//import CoreData
    2.70 +//        let cdAccount2 = SecretTestData().createWorkingCdAccount(number: 1)
    2.71 +//        Record.saveAndWait()
    2.72 +//        cdAccount2.createRequiredFoldersAndWait(testCase: self)
    2.73 +//        Record.saveAndWait()
    2.74  //
    2.75 -//@testable import MessageModel
    2.76 -//@testable import pEpForiOS
    2.77 -//
    2.78 -//class FetchMessagesOperationTest: CoreDataDrivenTestBase {
    2.79 -//    // IOS-671 pEp app has two accounts. Someone sends a mail to both
    2.80 -//    // (with both accounts in receipients).
    2.81 -//    // Message must exist twice, once for each account, after fetching mails from server.
    2.82 -//    // Commented as randomly failing and crashing. See IOS-1465.
    2.83 -////    func testMailSentToBothPepAccounts() {
    2.84 -////        // Setup 2 accounts
    2.85 -////        cdAccount.createRequiredFoldersAndWait(testCase: self)
    2.86 -////        Record.saveAndWait()
    2.87 -////
    2.88 -////        let cdAccount2 = SecretTestData().createWorkingCdAccount(number: 1)
    2.89 -////        Record.saveAndWait()
    2.90 -////        cdAccount2.createRequiredFoldersAndWait(testCase: self)
    2.91 -////        Record.saveAndWait()
    2.92 -////
    2.93 -////        guard let id1 = cdAccount.identity,
    2.94 -////            let id2 = cdAccount2.identity else {
    2.95 -////                XCTFail("We all loose identity ...")
    2.96 -////                return
    2.97 -////        }
    2.98 -////
    2.99 -////        // Sync both acocunts and remember what we got before starting the actual test
   2.100 -////        TestUtil.syncAndWait(numAccountsToSync: 2, testCase: self)
   2.101 -////        let msgsBefore1 = cdAccount.allMessages(inFolderOfType: .inbox, sendFrom: id2)
   2.102 -////        let msgsBefore2 = cdAccount2.allMessages(inFolderOfType: .inbox, sendFrom: id2)
   2.103 -////
   2.104 -////        // Create mails from cdAccount2 with both accounts in receipients (cdAccount & cdAccount2)
   2.105 -////        let numMailsToSend = 2
   2.106 -////        let mailsToSend = try! TestUtil.createOutgoingMails(
   2.107 -////            cdAccount: cdAccount2,
   2.108 -////            fromIdentity: id2,
   2.109 -////            toIdentity: id1,
   2.110 -////            testCase: self,
   2.111 -////            numberOfMails: numMailsToSend,
   2.112 -////            withAttachments: false,
   2.113 -////            encrypt: false)
   2.114 -////        XCTAssertEqual(mailsToSend.count, numMailsToSend)
   2.115 -////
   2.116 -////        for mail in mailsToSend {
   2.117 -////            mail.addToTo(id2)
   2.118 -////            mail.pEpProtected = false // force unencrypted
   2.119 -////        }
   2.120 -////        Record.saveAndWait()
   2.121 -////
   2.122 -////        // ... and send them.
   2.123 -////        TestUtil.syncAndWait(numAccountsToSync: 2, testCase: self)
   2.124 -////
   2.125 -////        // Sync once again to make sure we mirror the servers state (i.e. receive the sent mails)
   2.126 -////        TestUtil.syncAndWait(numAccountsToSync: 2, testCase: self)
   2.127 -////
   2.128 -////        // Now let's see what we got.
   2.129 -////        let msgsAfter1 = cdAccount.allMessages(inFolderOfType: .inbox, sendFrom: id2)
   2.130 -////        let msgsAfter2 = cdAccount2.allMessages(inFolderOfType: .inbox, sendFrom: id2)
   2.131 -////
   2.132 -////        XCTAssertEqual(msgsAfter1.count, msgsBefore1.count + numMailsToSend)
   2.133 -////        XCTAssertEqual(msgsAfter2.count, msgsBefore2.count + numMailsToSend)
   2.134 -////    }
   2.135 -//
   2.136 -//    // IOS-615 (Only) the first email in an Yahoo account gets duplicated locally
   2.137 -//    // on every sync cycle
   2.138 -//    func testMailsNotDuplicated() {
   2.139 -//        let imapSyncData = ImapSyncData(connectInfo: imapConnectInfo)
   2.140 -//        let errorContainer = ErrorContainer()
   2.141 -//
   2.142 -//        // fetch emails in inbox ...
   2.143 -//        let imapLogin = LoginImapOperation(parentName: #function, errorContainer: errorContainer,
   2.144 -//                                           imapSyncData: imapSyncData)
   2.145 -//        imapLogin.completionBlock = {
   2.146 -//            imapLogin.completionBlock = nil
   2.147 -//            XCTAssertNotNil(imapSyncData.sync)
   2.148 +//        guard let id1 = cdAccount.identity,
   2.149 +//            let id2 = cdAccount2.identity else {
   2.150 +//                XCTFail("We all loose identity ...")
   2.151 +//                return
   2.152  //        }
   2.153  //
   2.154 -//        let expFoldersFetched = expectation(description: "expFoldersFetched")
   2.155 -//        guard let syncFoldersOp = SyncFoldersFromServerOperation(parentName: #function,
   2.156 -//                                                                 imapSyncData: imapSyncData)
   2.157 -//            else {
   2.158 -//                XCTFail()
   2.159 -//                return
   2.160 +//        // Sync both acocunts and remember what we got before starting the actual test
   2.161 +//        TestUtil.syncAndWait(numAccountsToSync: 2, testCase: self)
   2.162 +//        let msgsBefore1 = cdAccount.allMessages(inFolderOfType: .inbox, sendFrom: id2)
   2.163 +//        let msgsBefore2 = cdAccount2.allMessages(inFolderOfType: .inbox, sendFrom: id2)
   2.164 +//
   2.165 +//        // Create mails from cdAccount2 with both accounts in receipients (cdAccount & cdAccount2)
   2.166 +//        let numMailsToSend = 2
   2.167 +//        let mailsToSend = try! TestUtil.createOutgoingMails(
   2.168 +//            cdAccount: cdAccount2,
   2.169 +//            fromIdentity: id2,
   2.170 +//            toIdentity: id1,
   2.171 +//            testCase: self,
   2.172 +//            numberOfMails: numMailsToSend,
   2.173 +//            withAttachments: false,
   2.174 +//            encrypt: false)
   2.175 +//        XCTAssertEqual(mailsToSend.count, numMailsToSend)
   2.176 +//
   2.177 +//        for mail in mailsToSend {
   2.178 +//            mail.addToTo(id2)
   2.179 +//            mail.pEpProtected = false // force unencrypted
   2.180  //        }
   2.181 -//        syncFoldersOp.addDependency(imapLogin)
   2.182 -//        syncFoldersOp.completionBlock = {
   2.183 -//            syncFoldersOp.completionBlock = nil
   2.184 -//            guard let _ = CdFolder.all() as? [CdFolder] else {
   2.185 -//                XCTFail("No folders?")
   2.186 -//                return
   2.187 -//            }
   2.188 -//            expFoldersFetched.fulfill()
   2.189 -//        }
   2.190 +//        Record.saveAndWait()
   2.191  //
   2.192 -//        let expFoldersCreated = expectation(description: "expFoldersCreated")
   2.193 -//        let createRequiredFoldersOp = CreateRequiredFoldersOperation(parentName: #function,
   2.194 -//                                                                     imapSyncData: imapSyncData)
   2.195 -//        createRequiredFoldersOp.addDependency(syncFoldersOp)
   2.196 -//        createRequiredFoldersOp.completionBlock = {
   2.197 -//            guard let _ = CdFolder.all() as? [CdFolder] else {
   2.198 -//                XCTFail("No folders?")
   2.199 -//                return
   2.200 -//            }
   2.201 -//            expFoldersCreated.fulfill()
   2.202 -//        }
   2.203 +//        // ... and send them.
   2.204 +//        TestUtil.syncAndWait(numAccountsToSync: 2, testCase: self)
   2.205  //
   2.206 -//        let queue = OperationQueue()
   2.207 -//        queue.addOperation(imapLogin)
   2.208 -//        queue.addOperation(syncFoldersOp)
   2.209 -//        queue.addOperation(createRequiredFoldersOp)
   2.210 +//        // Sync once again to make sure we mirror the servers state (i.e. receive the sent mails)
   2.211 +//        TestUtil.syncAndWait(numAccountsToSync: 2, testCase: self)
   2.212  //
   2.213 -//        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   2.214 -//            XCTAssertNil(error)
   2.215 -//            XCTAssertFalse(imapLogin.hasErrors())
   2.216 -//            XCTAssertFalse(syncFoldersOp.hasErrors())
   2.217 -//            XCTAssertFalse(createRequiredFoldersOp.hasErrors())
   2.218 -//        })
   2.219 +//        // Now let's see what we got.
   2.220 +//        let msgsAfter1 = cdAccount.allMessages(inFolderOfType: .inbox, sendFrom: id2)
   2.221 +//        let msgsAfter2 = cdAccount2.allMessages(inFolderOfType: .inbox, sendFrom: id2)
   2.222  //
   2.223 -//        var msgCountBefore: Int? = 0
   2.224 -//        // fetch messages
   2.225 -//        let expMessagesSynced = expectation(description: "expMessagesSynced")
   2.226 -//        let fetchOp = FetchMessagesOperation(parentName: #function, imapSyncData: imapSyncData)
   2.227 -//        fetchOp.completionBlock = {
   2.228 -//            guard let _ = CdMessage.all() as? [CdMessage] else {
   2.229 -//                XCTFail("No messages?")
   2.230 -//                return
   2.231 -//            }
   2.232 -//            // ... remember count ...
   2.233 -//            msgCountBefore = CdMessage.all()?.count
   2.234 -//            expMessagesSynced.fulfill()
   2.235 -//        }
   2.236 -//        queue.addOperation(fetchOp)
   2.237 -//
   2.238 -//        // ... and fetch again.
   2.239 -//        let expMessagesSynced2 = expectation(description: "expMessagesSynced2")
   2.240 -//        let fetch2Op = FetchMessagesOperation(parentName: #function, imapSyncData: imapSyncData)
   2.241 -//        fetch2Op.completionBlock = {
   2.242 -//            guard let _ = CdMessage.all() as? [CdMessage] else {
   2.243 -//                XCTFail("No messages?")
   2.244 -//                return
   2.245 -//            }
   2.246 -//            let msgCountAfter = CdMessage.all()?.count
   2.247 -//            // no mail should no have been dupliccated
   2.248 -//            XCTAssertEqual(msgCountBefore, msgCountAfter)
   2.249 -//            expMessagesSynced2.fulfill()
   2.250 -//        }
   2.251 -//        fetch2Op.addDependency(fetchOp)
   2.252 -//        queue.addOperation(fetch2Op)
   2.253 -//        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   2.254 -//            XCTAssertNil(error)
   2.255 -//            XCTAssertFalse(fetchOp.hasErrors())
   2.256 -//            XCTAssertFalse(fetch2Op.hasErrors())
   2.257 -//        })
   2.258 +//        XCTAssertEqual(msgsAfter1.count, msgsBefore1.count + numMailsToSend)
   2.259 +//        XCTAssertEqual(msgsAfter2.count, msgsBefore2.count + numMailsToSend)
   2.260  //    }
   2.261 -//}
   2.262 +
   2.263 +    // IOS-615 (Only) the first email in an Yahoo account gets duplicated locally
   2.264 +    // on every sync cycle
   2.265 +    func testMailsNotDuplicated() {
   2.266 +        let imapSyncData = ImapSyncData(connectInfo: imapConnectInfo)
   2.267 +        let errorContainer = ErrorContainer()
   2.268 +
   2.269 +        // fetch emails in inbox ...
   2.270 +        let imapLogin = LoginImapOperation(parentName: #function, errorContainer: errorContainer,
   2.271 +                                           imapSyncData: imapSyncData)
   2.272 +        imapLogin.completionBlock = {
   2.273 +            imapLogin.completionBlock = nil
   2.274 +            XCTAssertNotNil(imapSyncData.sync)
   2.275 +        }
   2.276 +
   2.277 +        let expFoldersFetched = expectation(description: "expFoldersFetched")
   2.278 +        guard let syncFoldersOp = SyncFoldersFromServerOperation(parentName: #function,
   2.279 +                                                                 imapSyncData: imapSyncData)
   2.280 +            else {
   2.281 +                XCTFail()
   2.282 +                return
   2.283 +        }
   2.284 +        syncFoldersOp.addDependency(imapLogin)
   2.285 +        syncFoldersOp.completionBlock = {
   2.286 +            syncFoldersOp.completionBlock = nil
   2.287 +            guard let _ = CdFolder.all() as? [CdFolder] else {
   2.288 +                XCTFail("No folders?")
   2.289 +                return
   2.290 +            }
   2.291 +            expFoldersFetched.fulfill()
   2.292 +        }
   2.293 +
   2.294 +        let expFoldersCreated = expectation(description: "expFoldersCreated")
   2.295 +        let createRequiredFoldersOp = CreateRequiredFoldersOperation(parentName: #function,
   2.296 +                                                                     imapSyncData: imapSyncData)
   2.297 +        createRequiredFoldersOp.addDependency(syncFoldersOp)
   2.298 +        createRequiredFoldersOp.completionBlock = {
   2.299 +            guard let _ = CdFolder.all() as? [CdFolder] else {
   2.300 +                XCTFail("No folders?")
   2.301 +                return
   2.302 +            }
   2.303 +            expFoldersCreated.fulfill()
   2.304 +        }
   2.305 +
   2.306 +        let queue = OperationQueue()
   2.307 +        queue.addOperation(imapLogin)
   2.308 +        queue.addOperation(syncFoldersOp)
   2.309 +        queue.addOperation(createRequiredFoldersOp)
   2.310 +
   2.311 +        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   2.312 +            XCTAssertNil(error)
   2.313 +            XCTAssertFalse(imapLogin.hasErrors())
   2.314 +            XCTAssertFalse(syncFoldersOp.hasErrors())
   2.315 +            XCTAssertFalse(createRequiredFoldersOp.hasErrors())
   2.316 +        })
   2.317 +
   2.318 +        var msgCountBefore: Int? = 0
   2.319 +        // fetch messages
   2.320 +        let expMessagesSynced = expectation(description: "expMessagesSynced")
   2.321 +        let fetchOp = FetchMessagesOperation(parentName: #function, imapSyncData: imapSyncData)
   2.322 +        fetchOp.completionBlock = {
   2.323 +            guard let _ = CdMessage.all() as? [CdMessage] else {
   2.324 +                XCTFail("No messages?")
   2.325 +                return
   2.326 +            }
   2.327 +            // ... remember count ...
   2.328 +            msgCountBefore = CdMessage.all()?.count
   2.329 +            expMessagesSynced.fulfill()
   2.330 +        }
   2.331 +        queue.addOperation(fetchOp)
   2.332 +
   2.333 +        // ... and fetch again.
   2.334 +        let expMessagesSynced2 = expectation(description: "expMessagesSynced2")
   2.335 +        let fetch2Op = FetchMessagesOperation(parentName: #function, imapSyncData: imapSyncData)
   2.336 +        fetch2Op.completionBlock = {
   2.337 +            guard let _ = CdMessage.all() as? [CdMessage] else {
   2.338 +                XCTFail("No messages?")
   2.339 +                return
   2.340 +            }
   2.341 +            let msgCountAfter = CdMessage.all()?.count
   2.342 +            // no mail should no have been dupliccated
   2.343 +            XCTAssertEqual(msgCountBefore, msgCountAfter)
   2.344 +            expMessagesSynced2.fulfill()
   2.345 +        }
   2.346 +        fetch2Op.addDependency(fetchOp)
   2.347 +        queue.addOperation(fetch2Op)
   2.348 +        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   2.349 +            XCTAssertNil(error)
   2.350 +            XCTAssertFalse(fetchOp.hasErrors())
   2.351 +            XCTAssertFalse(fetch2Op.hasErrors())
   2.352 +        })
   2.353 +    }
   2.354 +}
     3.1 --- a/pEpForiOSTests/Background/FetchNumberOfNewMailsServiceTest.swift	Tue May 14 22:31:30 2019 +0200
     3.2 +++ b/pEpForiOSTests/Background/FetchNumberOfNewMailsServiceTest.swift	Tue May 14 23:08:50 2019 +0200
     3.3 @@ -1,5 +1,3 @@
     3.4 -//!!!: crashes. Should be fixed with using Cd* (not MM)
     3.5 -////
     3.6  ////  FetchNumberOfNewMailsServiceTest.swift
     3.7  ////  pEpForiOSTests
     3.8  ////
     4.1 --- a/pEpForiOSTests/Background/SyncFlagsToServerOperationTest.swift	Tue May 14 22:31:30 2019 +0200
     4.2 +++ b/pEpForiOSTests/Background/SyncFlagsToServerOperationTest.swift	Tue May 14 23:08:50 2019 +0200
     4.3 @@ -1,1304 +1,1267 @@
     4.4 -//!!!: NSManagedObjectContextObjectsDidChangeNotification.  -[__NSCFSet addObject:]: attempt to insert nil with userInfo (null)
     4.5  
     4.6 -/*
     4.7 - [error] error: Serious application error.  Exception was caught during Core Data change processing.  This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.  -[__NSCFSet addObject:]: attempt to insert nil with userInfo (null)
     4.8 - CoreData: error: Serious application error.  Exception was caught during Core Data change processing.  This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.  -[__NSCFSet addObject:]: attempt to insert nil with userInfo (null)
     4.9 - 2019-04-04 20:46:32.038043+0200 pEp[68712:2062523] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFSet addObject:]: attempt to insert nil'
    4.10 - *** First throw call stack:
    4.11 - (
    4.12 - 0   CoreFoundation                      0x0000000110cb76fb __exceptionPreprocess + 331
    4.13 - 1   libobjc.A.dylib                     0x000000010f6c7ac5 objc_exception_throw + 48
    4.14 - 2   CoreFoundation                      0x0000000110cb7555 +[NSException raise:format:] + 197
    4.15 - 3   CoreFoundation                      0x0000000110c93a7a -[__NSCFSet addObject:] + 202
    4.16 - 4   CoreData                            0x000000011065a811 -[NSManagedObjectContext(_NSInternalChangeProcessing) _processPendingUpdates:] + 385
    4.17 - 5   CoreData                            0x0000000110654f57 -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] + 1127
    4.18 - 6   CoreData                            0x0000000110658d48 -[NSManagedObjectContext save:] + 408
    4.19 - 7   MessageModel                        0x000000010cf67d55 $sSo22NSManagedObjectContextC12MessageModelE16saveAndLogErrorsyyF + 901
    4.20 - 8   MessageModel                        0x000000010d06d230 $s12MessageModel28ManagedObjectWrapperProtocolPAAE4saveyyF + 784
    4.21 - 9   MessageModel                        0x000000010cfd6856 $s12MessageModel02CdA0C10cdIdentity16pantomimeAddressAA0cE0CSo010CWInternetG0C_tFZ + 2534
    4.22 - 10  MessageModel                        0x000000010cfd380b $s12MessageModel02CdA0C14insertOrUpdate09pantomimeA07account07messageF0ACSgSo13CWIMAPMessageC_AA0C7AccountCSo09CWMessageF0CtFZ + 3755
    4.23 - 11  MessageModel                        0x000000010d12af8a $s12MessageModel28StorePrefetchedMailOperationC14insertOrUpdate09pantomimeA07accountAA02CdA0CSgSo13CWIMAPMessageC_AA0L7AccountCtF + 650
    4.24 - 12  MessageModel                        0x000000010d12a0aa $s12MessageModel28StorePrefetchedMailOperationC05storeA07contextySo22NSManagedObjectContextC_tF + 1834
    4.25 - 13  MessageModel                        0x000000010d129705 $s12MessageModel28StorePrefetchedMailOperationC4mainyyFyycfU_ + 917
    4.26 - 14  MessageModel                        0x000000010d129871 $s12MessageModel28StorePrefetchedMailOperationC4mainyyFyycfU_TA + 17
    4.27 - 15  MessageModel                        0x000000010cd6001e $sIeg_IeyB_TR + 142
    4.28 - 16  CoreData                            0x000000011067184a developerSubmittedBlockToNSManagedObjectContextPerform + 170
    4.29 - 17  libclang_rt.asan_iossim_dynamic.dylib 0x000000010bc987b4 asan_dispatch_call_block_and_release + 260
    4.30 - 18  libdispatch.dylib                   0x0000000113cacd02 _dispatch_client_callout + 8
    4.31 - 19  libdispatch.dylib                   0x0000000113cb3720 _dispatch_lane_serial_drain + 705
    4.32 - 20  libdispatch.dylib                   0x0000000113cb4261 _dispatch_lane_invoke + 398
    4.33 - 21  libdispatch.dylib                   0x0000000113cbcfcb _dispatch_workloop_worker_thread + 645
    4.34 - 22  libsystem_pthread.dylib             0x000000011408e611 _pthread_wqthread + 421
    4.35 - 23  libsystem_pthread.dylib             0x000000011408e3fd start_wqthread + 13
    4.36 - )
    4.37 - libc++abi.dylib: terminating with uncaught exception of type NSException
    4.38 - (lldb)
    4.39 +//  SyncFlagsToServerOperationTest.swift
    4.40 +//  pEpForiOS
    4.41 +//
    4.42 +//  Created by buff on 28.07.17.
    4.43 +//  Copyright © 2017 p≡p Security S.A. All rights reserved.
    4.44 +//
    4.45  
    4.46 +import XCTest
    4.47 +import CoreData
    4.48  
    4.49 - */////
    4.50 -////  SyncFlagsToServerOperationTest.swift
    4.51 -////  pEpForiOS
    4.52 -////
    4.53 -////  Created by buff on 28.07.17.
    4.54 -////  Copyright © 2017 p≡p Security S.A. All rights reserved.
    4.55 -////
    4.56 -//
    4.57 -//import XCTest
    4.58 -//import CoreData
    4.59 -//
    4.60 -//@testable import MessageModel
    4.61 -//@testable import pEpForiOS
    4.62 -//
    4.63 -//class SyncFlagsToServerOperationTest: CoreDataDrivenTestBase {
    4.64 -//
    4.65 -//    // MARK: - SyncFlagsToServerOperation
    4.66 -//
    4.67 -//    func testEmpty() {
    4.68 -//        fetchMessages(parentName: #function)
    4.69 -//
    4.70 -//        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
    4.71 -//            XCTFail()
    4.72 -//            return
    4.73 -//        }
    4.74 -//        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
    4.75 -//        let expEmailsSynced = expectation(description: "expEmailsSynced")
    4.76 -//        op.completionBlock = {
    4.77 -//            op.completionBlock = nil
    4.78 -//            expEmailsSynced.fulfill()
    4.79 -//        }
    4.80 -//
    4.81 -//        op.start()
    4.82 -//        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
    4.83 -//            XCTAssertNil(error)
    4.84 -//            XCTAssertFalse(op.hasErrors())
    4.85 -//        })
    4.86 -//
    4.87 -//        XCTAssertEqual(op.numberOfMessagesSynced, 0)
    4.88 -//    }
    4.89 -//
    4.90 -//    func testSyncFlagsToServerOperation() {
    4.91 -//        fetchMessages(parentName: #function)
    4.92 -//
    4.93 -//        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
    4.94 -//            XCTFail()
    4.95 -//            return
    4.96 -//        }
    4.97 -//
    4.98 -//        guard let messages = inbox.messages?.sortedArray(
    4.99 -//            using: [NSSortDescriptor(key: "sent", ascending: true)])
   4.100 -//            as? [CdMessage] else {
   4.101 -//                XCTFail()
   4.102 -//                return
   4.103 -//        }
   4.104 -//
   4.105 -//        XCTAssertGreaterThan(messages.count, 0, "there are messages")
   4.106 -//
   4.107 -//        for m in messages {
   4.108 -//            XCTAssertNotNil(m.messageID)
   4.109 -//            XCTAssertGreaterThan(m.uid, 0)
   4.110 -//            guard let imap = m.imap else {
   4.111 -//                XCTFail()
   4.112 -//                break
   4.113 -//            }
   4.114 -//            let localFlags = imap.localFlags ?? CdImapFlags.create()
   4.115 -//            imap.localFlags = localFlags
   4.116 -//            localFlags.flagFlagged = !localFlags.flagFlagged
   4.117 -//        }
   4.118 -//
   4.119 -//        Record.saveAndWait()
   4.120 -//
   4.121 -//        // redundant check that flagFlagged really has changed
   4.122 -//        guard let messages2 = inbox.messages?.sortedArray(
   4.123 -//            using: [NSSortDescriptor(key: "sent", ascending: true)])
   4.124 -//            as? [CdMessage] else {
   4.125 -//                XCTFail()
   4.126 -//                return
   4.127 -//        }
   4.128 -//        for m in messages2 {
   4.129 -//            guard let imap = m.imap else {
   4.130 -//                XCTFail()
   4.131 -//                break
   4.132 -//            }
   4.133 -//            guard let serverFlags = imap.serverFlags else {
   4.134 -//                XCTFail()
   4.135 -//                break
   4.136 -//            }
   4.137 -//            guard let localFlags = imap.localFlags else {
   4.138 -//                XCTFail()
   4.139 -//                break
   4.140 -//            }
   4.141 -//            XCTAssertNotEqual(serverFlags.flagFlagged, localFlags.flagFlagged)
   4.142 -//        }
   4.143 -//
   4.144 -//        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.145 -//            folder: inbox, context: Record.Context.main)
   4.146 -//        XCTAssertEqual(messagesToBeSynced.count, messages.count)
   4.147 -//
   4.148 -//        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
   4.149 -//
   4.150 -//        let expEmailsSynced = expectation(description: "expEmailsSynced")
   4.151 -//        op.completionBlock = {
   4.152 -//            op.completionBlock = nil
   4.153 -//            expEmailsSynced.fulfill()
   4.154 -//        }
   4.155 -//        op.start()
   4.156 -//
   4.157 -//        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   4.158 -//            XCTAssertNil(error)
   4.159 -//            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
   4.160 -//        })
   4.161 -//
   4.162 -//        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.163 -//            folder: inbox, context: Record.Context.main)
   4.164 -//        XCTAssertEqual(messagesToBeSynced.count, 0)
   4.165 -//        XCTAssertEqual(op.numberOfMessagesSynced, messages.count)
   4.166 -//    }
   4.167 -//
   4.168 -//    func testAddFlags_changeAllFlagsExceptDelete() {
   4.169 -//        fetchMessages(parentName: #function)
   4.170 -//
   4.171 -//        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
   4.172 -//            XCTFail()
   4.173 -//            return
   4.174 -//        }
   4.175 -//
   4.176 -//        guard let messages = inbox.messages?.sortedArray(
   4.177 -//            using: [NSSortDescriptor(key: "sent", ascending: true)]) as? [CdMessage] else {
   4.178 -//                XCTFail()
   4.179 -//                return
   4.180 -//        }
   4.181 -//
   4.182 -//        XCTAssertGreaterThan(messages.count, 0, "there are messages")
   4.183 -//
   4.184 -//        for m in messages {
   4.185 -//            XCTAssertNotNil(m.messageID)
   4.186 -//            XCTAssertGreaterThan(m.uid, 0)
   4.187 -//            guard let imap = m.imap else {
   4.188 -//                XCTFail()
   4.189 -//                break
   4.190 -//            }
   4.191 -//
   4.192 -//            let localFlags = imap.localFlags ?? CdImapFlags.create()
   4.193 -//            imap.localFlags = localFlags
   4.194 -//
   4.195 -//            // all flags set locally, except delete
   4.196 -//            localFlags.flagAnswered = true
   4.197 -//            localFlags.flagDraft = true
   4.198 -//            localFlags.flagFlagged = true
   4.199 -//            localFlags.flagRecent = false
   4.200 -//            localFlags.flagSeen = true
   4.201 -//
   4.202 -//            // ...but no flags are set on server, so all flags have to be added
   4.203 -//            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
   4.204 -//            imap.serverFlags = serverFlags
   4.205 -//            serverFlags.update(rawValue16: ImapFlagsBits.imapNoFlagsSet())
   4.206 -//
   4.207 -//            XCTAssertNotEqual(m.imap?.localFlags?.flagAnswered, m.imap?.serverFlags?.flagAnswered)
   4.208 -//        }
   4.209 -//
   4.210 -//        Record.saveAndWait()
   4.211 -//
   4.212 -//        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.213 -//            folder: inbox, context: Record.Context.main)
   4.214 -//        XCTAssertEqual(messagesToBeSynced.count, messages.count)
   4.215 -//
   4.216 -//        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
   4.217 -//
   4.218 -//        let expEmailsSynced = expectation(description: "expEmailsSynced")
   4.219 -//        op.completionBlock = {
   4.220 -//            op.completionBlock = nil
   4.221 -//            expEmailsSynced.fulfill()
   4.222 -//        }
   4.223 -//
   4.224 -//        op.start()
   4.225 -//        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   4.226 -//            XCTAssertNil(error)
   4.227 -//            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
   4.228 -//        })
   4.229 -//
   4.230 -//        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.231 -//            folder: inbox, context: Record.Context.main)
   4.232 -//        XCTAssertEqual(messagesToBeSynced.count, 0)
   4.233 -//        XCTAssertEqual(op.numberOfMessagesSynced, messages.count)
   4.234 -//    }
   4.235 -//
   4.236 -//    func testAddFlags_allFlagsAlreadySetOnServer() {
   4.237 -//        fetchMessages(parentName: #function)
   4.238 -//
   4.239 -//        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
   4.240 -//            XCTFail()
   4.241 -//            return
   4.242 -//        }
   4.243 -//
   4.244 -//        guard let messages = inbox.messages?.sortedArray(
   4.245 -//            using: [NSSortDescriptor(key: "sent", ascending: true)])
   4.246 -//            as? [CdMessage] else {
   4.247 -//                XCTFail()
   4.248 -//                return
   4.249 -//        }
   4.250 -//
   4.251 -//        XCTAssertGreaterThan(messages.count, 0, "there are messages")
   4.252 -//
   4.253 -//        for m in messages {
   4.254 -//            XCTAssertNotNil(m.messageID)
   4.255 -//            XCTAssertGreaterThan(m.uid, 0)
   4.256 -//            guard let imap = m.imap else {
   4.257 -//                XCTFail()
   4.258 -//                break
   4.259 -//            }
   4.260 -//
   4.261 -//            let localFlags = imap.localFlags ?? CdImapFlags.create()
   4.262 -//            imap.localFlags = localFlags
   4.263 -//
   4.264 -//            // all flags set locally ...
   4.265 -//            localFlags.flagAnswered = true
   4.266 -//            localFlags.flagDraft = true
   4.267 -//            localFlags.flagFlagged = true
   4.268 -//            // the client must never change flagRecent according to RFC,
   4.269 -//            // so we set it in state of flagsServer
   4.270 -//            localFlags.flagRecent = true
   4.271 -//            localFlags.flagSeen = true
   4.272 -//            localFlags.flagDeleted = true
   4.273 -//
   4.274 -//            // ...and all flags are set on server, so nothing should be updated
   4.275 -//            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
   4.276 -//            imap.serverFlags = serverFlags
   4.277 -//            serverFlags.update(rawValue16: ImapFlagsBits.imapAllFlagsSet())
   4.278 -//        }
   4.279 -//
   4.280 -//        Record.saveAndWait()
   4.281 -//
   4.282 -//        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.283 -//            folder: inbox, context: Record.Context.main)
   4.284 -//        XCTAssertEqual(messagesToBeSynced.count, 0)
   4.285 -//
   4.286 -//        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
   4.287 -//
   4.288 -//        let expEmailsSynced = expectation(description: "expEmailsSynced")
   4.289 -//        op.completionBlock = {
   4.290 -//            op.completionBlock = nil
   4.291 -//            expEmailsSynced.fulfill()
   4.292 -//        }
   4.293 -//
   4.294 -//        op.start()
   4.295 -//        waitForExpectations(timeout: 300, handler: { error in
   4.296 -//            XCTAssertNil(error)
   4.297 -//            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
   4.298 -//        })
   4.299 -//
   4.300 -//        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.301 -//            folder: inbox, context: Record.Context.main)
   4.302 -//        XCTAssertEqual(messagesToBeSynced.count, 0, "all done")
   4.303 -//        XCTAssertEqual(op.numberOfMessagesSynced, 0,
   4.304 -//                       "no messages have been synced as all flag were already set before")
   4.305 -//    }
   4.306 -//
   4.307 -//    func testAddFlags_someFlagsAlreadySetOnServer() {
   4.308 -//        fetchMessages(parentName: #function)
   4.309 -//
   4.310 -//        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
   4.311 -//            XCTFail()
   4.312 -//            return
   4.313 -//        }
   4.314 -//
   4.315 -//        guard let messages = inbox.messages?.sortedArray(
   4.316 -//            using: [NSSortDescriptor(key: "sent", ascending: true)])
   4.317 -//            as? [CdMessage] else {
   4.318 -//                XCTFail()
   4.319 -//                return
   4.320 -//        }
   4.321 -//
   4.322 -//        XCTAssertGreaterThan(messages.count, 0,"Some messages exist to work with")
   4.323 -//
   4.324 -//        for m in messages {
   4.325 -//            XCTAssertNotNil(m.messageID)
   4.326 -//            XCTAssertGreaterThan(m.uid, 0)
   4.327 -//            guard let imap = m.imap else {
   4.328 -//                XCTFail()
   4.329 -//                break
   4.330 -//            }
   4.331 -//
   4.332 -//            let localFlags = imap.localFlags ?? CdImapFlags.create()
   4.333 -//            imap.localFlags = localFlags
   4.334 -//
   4.335 -//            // all flags set locally ...
   4.336 -//            localFlags.flagAnswered = true
   4.337 -//            localFlags.flagDraft = true
   4.338 -//            localFlags.flagFlagged = true
   4.339 -//            // the client must never change flagRecent according to RFC,
   4.340 -//            // so we set it in state of flagsServer
   4.341 -//            localFlags.flagRecent = false
   4.342 -//            localFlags.flagSeen = true
   4.343 -//            localFlags.flagDeleted = true
   4.344 -//
   4.345 -//            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
   4.346 -//            imap.serverFlags = serverFlags
   4.347 -//
   4.348 -//            var flagsFromServer = ImapFlagsBits.imapNoFlagsSet()
   4.349 -//            flagsFromServer.imapSetFlagBit(.answered)
   4.350 -//            flagsFromServer.imapSetFlagBit(.draft)
   4.351 -//            flagsFromServer.imapSetFlagBit(.flagged)
   4.352 -//            // flagSeen differs ...
   4.353 -//            flagsFromServer.imapSetFlagBit(.deleted)
   4.354 -//            serverFlags.update(rawValue16: flagsFromServer)
   4.355 -//        }
   4.356 -//
   4.357 -//        Record.saveAndWait()
   4.358 -//
   4.359 -//        // ...so all messages should need to be synced
   4.360 -//        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.361 -//            folder: inbox,
   4.362 -//            context: Record.Context.main)
   4.363 -//
   4.364 -//        XCTAssertEqual(messagesToBeSynced.count, messages.count,
   4.365 -//                       "all messages should need to be synced")
   4.366 -//
   4.367 -//        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
   4.368 -//
   4.369 -//        let expEmailsSynced = expectation(description: "expEmailsSynced")
   4.370 -//        op.completionBlock = {
   4.371 -//            op.completionBlock = nil
   4.372 -//            expEmailsSynced.fulfill()
   4.373 -//        }
   4.374 -//
   4.375 -//        op.start()
   4.376 -//        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   4.377 -//            XCTAssertNil(error)
   4.378 -//            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
   4.379 -//        })
   4.380 -//
   4.381 -//        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.382 -//            folder: inbox, context: Record.Context.main)
   4.383 -//        XCTAssertEqual(messagesToBeSynced.count, 0, "all done")
   4.384 -//        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
   4.385 -//                       "flagDeleted changes, so all messages should be updated")
   4.386 -//    }
   4.387 -//
   4.388 -//    func testAddFlags_addFlagAnswered() {
   4.389 -//        fetchMessages(parentName: #function)
   4.390 -//
   4.391 -//        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
   4.392 -//            XCTFail()
   4.393 -//            return
   4.394 -//        }
   4.395 -//
   4.396 -//        guard let messages = inbox.messages?.sortedArray(
   4.397 -//            using: [NSSortDescriptor(key: "sent", ascending: true)])
   4.398 -//            as? [CdMessage] else {
   4.399 -//                XCTFail()
   4.400 -//                return
   4.401 -//        }
   4.402 -//
   4.403 -//        XCTAssertGreaterThan(messages.count, 0, "there are messages")
   4.404 -//
   4.405 -//        for m in messages {
   4.406 -//            XCTAssertNotNil(m.messageID)
   4.407 -//            XCTAssertGreaterThan(m.uid, 0)
   4.408 -//            guard let imap = m.imap else {
   4.409 -//                XCTFail()
   4.410 -//                break
   4.411 -//            }
   4.412 -//
   4.413 -//            let localFlags = imap.localFlags ?? CdImapFlags.create()
   4.414 -//            imap.localFlags = localFlags
   4.415 -//
   4.416 -//            // one flag that is not set on server has been set by the client,
   4.417 -//            // so it has to be added.
   4.418 -//            localFlags.flagAnswered = true
   4.419 -//            localFlags.flagDraft = false
   4.420 -//            localFlags.flagFlagged = false
   4.421 -//            // (the client must never change flagRecent according to RFC,
   4.422 -//            // so we set it in state of flagsServer)
   4.423 -//            localFlags.flagRecent = false
   4.424 -//            localFlags.flagSeen = false
   4.425 -//            localFlags.flagDeleted = true
   4.426 -//
   4.427 -//            // set the flag on server side
   4.428 -//            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
   4.429 -//            imap.serverFlags = serverFlags
   4.430 -//
   4.431 -//            var theBits = ImapFlagsBits.imapNoFlagsSet()
   4.432 -//            theBits.imapSetFlagBit(.deleted)
   4.433 -//            serverFlags.update(rawValue16: theBits)
   4.434 -//        }
   4.435 -//
   4.436 -//        Record.saveAndWait()
   4.437 -//
   4.438 -//        // since a flag has be added on all messages, all messages need to be synced
   4.439 -//        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.440 -//            folder: inbox, context: Record.Context.main)
   4.441 -//        XCTAssertEqual(messagesToBeSynced.count, messages.count, "all messages need to be synced")
   4.442 -//
   4.443 -//        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
   4.444 -//
   4.445 -//        let expEmailsSynced = expectation(description: "expEmailsSynced")
   4.446 -//        op.completionBlock = {
   4.447 -//            op.completionBlock = nil
   4.448 -//            expEmailsSynced.fulfill()
   4.449 -//        }
   4.450 -//
   4.451 -//        op.start()
   4.452 -//        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   4.453 -//            XCTAssertNil(error)
   4.454 -//            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
   4.455 -//        })
   4.456 -//
   4.457 -//        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.458 -//            folder: inbox, context: Record.Context.main)
   4.459 -//        XCTAssertEqual(messagesToBeSynced.count, 0,
   4.460 -//                       "no messages have to be synced after syncing")
   4.461 -//        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
   4.462 -//                       "all messages have been processed")
   4.463 -//    }
   4.464 -//
   4.465 -//    func testAddFlags_addFlagDraft() {
   4.466 -//        fetchMessages(parentName: #function)
   4.467 -//
   4.468 -//        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
   4.469 -//            XCTFail()
   4.470 -//            return
   4.471 -//        }
   4.472 -//
   4.473 -//        guard let messages = inbox.messages?.sortedArray(
   4.474 -//            using: [NSSortDescriptor(key: "sent", ascending: true)])
   4.475 -//            as? [CdMessage] else {
   4.476 -//                XCTFail()
   4.477 -//                return
   4.478 -//        }
   4.479 -//
   4.480 -//        XCTAssertGreaterThan(messages.count, 0, "there are messages")
   4.481 -//
   4.482 -//        for m in messages {
   4.483 -//            XCTAssertNotNil(m.messageID)
   4.484 -//            XCTAssertGreaterThan(m.uid, 0)
   4.485 -//            guard let imap = m.imap else {
   4.486 -//                XCTFail()
   4.487 -//                break
   4.488 -//            }
   4.489 -//
   4.490 -//            let localFlags = imap.localFlags ?? CdImapFlags.create()
   4.491 -//            imap.localFlags = localFlags
   4.492 -//
   4.493 -//            // one flag that is not set on server has been set by the client,
   4.494 -//            // so it has to be added.
   4.495 -//            localFlags.flagAnswered = false
   4.496 -//            localFlags.flagDraft = true
   4.497 -//            localFlags.flagFlagged = false
   4.498 -//            // (the client must never change flagRecent according to RFC,
   4.499 -//            // so we set it in state of flagsServer)
   4.500 -//            localFlags.flagRecent = false
   4.501 -//            localFlags.flagSeen = false
   4.502 -//            localFlags.flagDeleted = true
   4.503 -//
   4.504 -//            // set the flag on server side
   4.505 -//            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
   4.506 -//            imap.serverFlags = serverFlags
   4.507 -//
   4.508 -//            var theBits = ImapFlagsBits.imapNoFlagsSet()
   4.509 -//            theBits.imapSetFlagBit(.deleted)
   4.510 -//            serverFlags.update(rawValue16: theBits)
   4.511 -//        }
   4.512 -//
   4.513 -//        Record.saveAndWait()
   4.514 -//
   4.515 -//        // since a flag has be added on all messages, all messages need to be synced
   4.516 -//        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.517 -//            folder: inbox, context: Record.Context.main)
   4.518 -//        XCTAssertEqual(messagesToBeSynced.count, messages.count, "all messages need to be synced")
   4.519 -//
   4.520 -//        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
   4.521 -//
   4.522 -//        let expEmailsSynced = expectation(description: "expEmailsSynced")
   4.523 -//        op.completionBlock = {
   4.524 -//            op.completionBlock = nil
   4.525 -//            expEmailsSynced.fulfill()
   4.526 -//        }
   4.527 -//
   4.528 -//        op.start()
   4.529 -//        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   4.530 -//            XCTAssertNil(error)
   4.531 -//            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
   4.532 -//        })
   4.533 -//
   4.534 -//        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.535 -//            folder: inbox, context: Record.Context.main)
   4.536 -//        XCTAssertEqual(messagesToBeSynced.count, 0,
   4.537 -//                       "no messages have to be synced after syncing")
   4.538 -//        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
   4.539 -//                       "all messages have been processed")
   4.540 -//    }
   4.541 -//
   4.542 -//    func testAddFlags_addFlagFlagged() {
   4.543 -//        fetchMessages(parentName: #function)
   4.544 -//
   4.545 -//        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
   4.546 -//            XCTFail()
   4.547 -//            return
   4.548 -//        }
   4.549 -//
   4.550 -//        guard let messages = inbox.messages?.sortedArray(
   4.551 -//            using: [NSSortDescriptor(key: "sent", ascending: true)])
   4.552 -//            as? [CdMessage] else {
   4.553 -//                XCTFail()
   4.554 -//                return
   4.555 -//        }
   4.556 -//
   4.557 -//        XCTAssertGreaterThan(messages.count, 0, "there are messages")
   4.558 -//
   4.559 -//        for m in messages {
   4.560 -//            XCTAssertNotNil(m.messageID)
   4.561 -//            XCTAssertGreaterThan(m.uid, 0)
   4.562 -//            guard let imap = m.imap else {
   4.563 -//                XCTFail()
   4.564 -//                break
   4.565 -//            }
   4.566 -//
   4.567 -//            let localFlags = imap.localFlags ?? CdImapFlags.create()
   4.568 -//            imap.localFlags = localFlags
   4.569 -//
   4.570 -//            // one flag that is not set on server has been set by the client,
   4.571 -//            // so it has to be added.
   4.572 -//            localFlags.flagAnswered = false
   4.573 -//            localFlags.flagDraft = false
   4.574 -//            localFlags.flagFlagged = true
   4.575 -//            // (the client must never change flagRecent according to RFC,
   4.576 -//            // so we set it in state of flagsServer)
   4.577 -//            localFlags.flagRecent = false
   4.578 -//            localFlags.flagSeen = false
   4.579 -//            localFlags.flagDeleted = true
   4.580 -//
   4.581 -//            // set the flag on server side
   4.582 -//            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
   4.583 -//            imap.serverFlags = serverFlags
   4.584 -//
   4.585 -//            var theBits = ImapFlagsBits.imapNoFlagsSet()
   4.586 -//            theBits.imapSetFlagBit(.deleted)
   4.587 -//            serverFlags.update(rawValue16: theBits)
   4.588 -//        }
   4.589 -//
   4.590 -//        Record.saveAndWait()
   4.591 -//
   4.592 -//        // since a flag has be added on all messages, all messages need to be synced
   4.593 -//        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.594 -//            folder: inbox, context: Record.Context.main)
   4.595 -//        XCTAssertEqual(messagesToBeSynced.count, messages.count, "all messages need to be synced")
   4.596 -//
   4.597 -//        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
   4.598 -//
   4.599 -//        let expEmailsSynced = expectation(description: "expEmailsSynced")
   4.600 -//        op.completionBlock = {
   4.601 -//            op.completionBlock = nil
   4.602 -//            expEmailsSynced.fulfill()
   4.603 -//        }
   4.604 -//
   4.605 -//        op.start()
   4.606 -//        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   4.607 -//            XCTAssertNil(error)
   4.608 -//            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
   4.609 -//        })
   4.610 -//
   4.611 -//        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.612 -//            folder: inbox, context: Record.Context.main)
   4.613 -//        XCTAssertEqual(messagesToBeSynced.count, 0,
   4.614 -//                       "no messages have to be synced after syncing")
   4.615 -//        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
   4.616 -//                       "all messages have been processed")
   4.617 -//    }
   4.618 -//
   4.619 -//    func testAddFlags_addFlagSeen() {
   4.620 -//        fetchMessages(parentName: #function)
   4.621 -//
   4.622 -//        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
   4.623 -//            XCTFail()
   4.624 -//            return
   4.625 -//        }
   4.626 -//
   4.627 -//        guard let messages = inbox.messages?.sortedArray(
   4.628 -//            using: [NSSortDescriptor(key: "sent", ascending: true)])
   4.629 -//            as? [CdMessage] else {
   4.630 -//                XCTFail()
   4.631 -//                return
   4.632 -//        }
   4.633 -//
   4.634 -//        XCTAssertGreaterThan(messages.count, 0, "there are messages")
   4.635 -//
   4.636 -//        for m in messages {
   4.637 -//            XCTAssertNotNil(m.messageID)
   4.638 -//            XCTAssertGreaterThan(m.uid, 0)
   4.639 -//            guard let imap = m.imap else {
   4.640 -//                XCTFail()
   4.641 -//                break
   4.642 -//            }
   4.643 -//            // one flag that is not set on server has been set by the client,
   4.644 -//            // so it has to be added.
   4.645 -//            let localFlags = imap.localFlags ?? CdImapFlags.create()
   4.646 -//            imap.localFlags = localFlags
   4.647 -//
   4.648 -//            localFlags.flagAnswered = false
   4.649 -//            localFlags.flagDraft = false
   4.650 -//            localFlags.flagFlagged = false
   4.651 -//            // (the client must never change flagRecent according to RFC,
   4.652 -//            // so we set it in state of flagsServer)
   4.653 -//            localFlags.flagRecent = false
   4.654 -//            localFlags.flagSeen = true
   4.655 -//            localFlags.flagDeleted = true
   4.656 -//
   4.657 -//            // set the flag on server side
   4.658 -//            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
   4.659 -//            imap.serverFlags = serverFlags
   4.660 -//
   4.661 -//            var theBits = ImapFlagsBits.imapNoFlagsSet()
   4.662 -//            theBits.imapSetFlagBit(.deleted)
   4.663 -//            serverFlags.update(rawValue16: theBits)
   4.664 -//        }
   4.665 -//
   4.666 -//        Record.saveAndWait()
   4.667 -//
   4.668 -//        // since a flag has be added on all messages, all messages need to be synced
   4.669 -//        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.670 -//            folder: inbox, context: Record.Context.main)
   4.671 -//        XCTAssertEqual(messagesToBeSynced.count, messages.count, "all messages need to be synced")
   4.672 -//
   4.673 -//        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
   4.674 -//
   4.675 -//        let expEmailsSynced = expectation(description: "expEmailsSynced")
   4.676 -//        op.completionBlock = {
   4.677 -//            op.completionBlock = nil
   4.678 -//            expEmailsSynced.fulfill()
   4.679 -//        }
   4.680 -//
   4.681 -//        op.start()
   4.682 -//        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   4.683 -//            XCTAssertNil(error)
   4.684 -//            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
   4.685 -//        })
   4.686 -//
   4.687 -//        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.688 -//            folder: inbox, context: Record.Context.main)
   4.689 -//        XCTAssertEqual(messagesToBeSynced.count, 0,
   4.690 -//                       "no messages have to be synced after syncing")
   4.691 -//        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
   4.692 -//                       "all messages have been processed")
   4.693 -//    }
   4.694 -//
   4.695 -//    func testRemoveFlags_allFlagsAlreadySetOnServer() {
   4.696 -//        fetchMessages(parentName: #function)
   4.697 -//
   4.698 -//        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
   4.699 -//            XCTFail()
   4.700 -//            return
   4.701 -//        }
   4.702 -//
   4.703 -//        guard let messages = inbox.messages?.sortedArray(
   4.704 -//            using: [NSSortDescriptor(key: "sent", ascending: true)])
   4.705 -//            as? [CdMessage] else {
   4.706 -//                XCTFail()
   4.707 -//                return
   4.708 -//        }
   4.709 -//
   4.710 -//        XCTAssertGreaterThan(messages.count, 0, "there are messages")
   4.711 -//
   4.712 -//        for m in messages {
   4.713 -//            XCTAssertNotNil(m.messageID)
   4.714 -//            XCTAssertGreaterThan(m.uid, 0)
   4.715 -//            guard let imap = m.imap else {
   4.716 -//                XCTFail()
   4.717 -//                break
   4.718 -//            }
   4.719 -//            // no flag set locally ...
   4.720 -//            let localFlags = imap.localFlags ?? CdImapFlags.create()
   4.721 -//            imap.localFlags = localFlags
   4.722 -//
   4.723 -//            localFlags.flagAnswered = false
   4.724 -//            localFlags.flagDraft = false
   4.725 -//            localFlags.flagFlagged = false
   4.726 -//            // \Recent should be ignored
   4.727 -//            localFlags.flagRecent = true
   4.728 -//            localFlags.flagSeen = false
   4.729 -//            localFlags.flagDeleted = false
   4.730 -//
   4.731 -//            // ... but all flags set on server, so all flags have to be removed
   4.732 -//            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
   4.733 -//            imap.serverFlags = serverFlags
   4.734 -//            serverFlags.update(rawValue16: ImapFlagsBits.imapNoFlagsSet())
   4.735 -//        }
   4.736 -//
   4.737 -//        Record.saveAndWait()
   4.738 -//
   4.739 -//        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.740 -//            folder: inbox, context: Record.Context.main)
   4.741 -//        XCTAssertEqual(messagesToBeSynced.count, 0)
   4.742 -//
   4.743 -//        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
   4.744 -//
   4.745 -//        let expEmailsSynced = expectation(description: "expEmailsSynced")
   4.746 -//        op.completionBlock = {
   4.747 -//            op.completionBlock = nil
   4.748 -//            expEmailsSynced.fulfill()
   4.749 -//        }
   4.750 -//
   4.751 -//        op.start()
   4.752 -//        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   4.753 -//            XCTAssertNil(error)
   4.754 -//            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
   4.755 -//        })
   4.756 -//
   4.757 -//        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.758 -//            folder: inbox, context: Record.Context.main)
   4.759 -//        XCTAssertEqual(messagesToBeSynced.count, 0)
   4.760 -//        XCTAssertEqual(op.numberOfMessagesSynced, 0, "no message has been synced")
   4.761 -//    }
   4.762 -//
   4.763 -//    func testRemoveFlags_noChanges() {
   4.764 -//        fetchMessages(parentName: #function)
   4.765 -//
   4.766 -//        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
   4.767 -//            XCTFail()
   4.768 -//            return
   4.769 -//        }
   4.770 -//
   4.771 -//        guard let messages = inbox.messages?.sortedArray(
   4.772 -//            using: [NSSortDescriptor(key: "sent", ascending: true)])
   4.773 -//            as? [CdMessage] else {
   4.774 -//                XCTFail()
   4.775 -//                return
   4.776 -//        }
   4.777 -//
   4.778 -//        XCTAssertGreaterThan(messages.count, 0, "there are messages")
   4.779 -//
   4.780 -//        for m in messages {
   4.781 -//            XCTAssertNotNil(m.messageID)
   4.782 -//            XCTAssertGreaterThan(m.uid, 0)
   4.783 -//            guard let imap = m.imap else {
   4.784 -//                XCTFail()
   4.785 -//                break
   4.786 -//            }
   4.787 -//            // flagsCurrent == flagsFromServer, so no syncing should take place
   4.788 -//            let localFlags = imap.localFlags ?? CdImapFlags.create()
   4.789 -//            imap.localFlags = localFlags
   4.790 -//
   4.791 -//            localFlags.flagAnswered = false
   4.792 -//            localFlags.flagDraft = true
   4.793 -//            localFlags.flagFlagged = false
   4.794 -//            // (the client must never change flagRecent according to RFC,
   4.795 -//            // so we set it in state of flagsServer)
   4.796 -//            localFlags.flagRecent = false
   4.797 -//            localFlags.flagSeen = true
   4.798 -//            localFlags.flagDeleted = false
   4.799 -//
   4.800 -//            // server flags
   4.801 -//            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
   4.802 -//            imap.serverFlags = serverFlags
   4.803 -//            var theBits = ImapFlagsBits.imapNoFlagsSet()
   4.804 -//            theBits.imapSetFlagBit(.draft)
   4.805 -//            theBits.imapSetFlagBit(.seen)
   4.806 -//            serverFlags.update(rawValue16: theBits)
   4.807 -//        }
   4.808 -//
   4.809 -//        Record.saveAndWait()
   4.810 -//
   4.811 -//        // nothing changed, so no sync should take place
   4.812 -//        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.813 -//            folder: inbox, context: Record.Context.main)
   4.814 -//        XCTAssertEqual(messagesToBeSynced.count, 0)
   4.815 -//
   4.816 -//        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
   4.817 -//
   4.818 -//        let expEmailsSynced = expectation(description: "expEmailsSynced")
   4.819 -//        op.completionBlock = {
   4.820 -//            op.completionBlock = nil
   4.821 -//            expEmailsSynced.fulfill()
   4.822 -//        }
   4.823 -//
   4.824 -//        op.start()
   4.825 -//        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   4.826 -//            XCTAssertNil(error)
   4.827 -//            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
   4.828 -//        })
   4.829 -//
   4.830 -//        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.831 -//            folder: inbox, context: Record.Context.main)
   4.832 -//        XCTAssertEqual(messagesToBeSynced.count, 0,
   4.833 -//                       "no messages have to be synced after syncing")
   4.834 -//        XCTAssertEqual(op.numberOfMessagesSynced, 0,
   4.835 -//                       "no message has been processed")
   4.836 -//    }
   4.837 -//
   4.838 -//    func testRemoveFlags_removeFlagAnswered() {
   4.839 -//        fetchMessages(parentName: #function)
   4.840 -//
   4.841 -//        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
   4.842 -//            XCTFail()
   4.843 -//            return
   4.844 -//        }
   4.845 -//
   4.846 -//        guard let messages = inbox.messages?.sortedArray(
   4.847 -//            using: [NSSortDescriptor(key: "sent", ascending: true)])
   4.848 -//            as? [CdMessage] else {
   4.849 -//                XCTFail()
   4.850 -//                return
   4.851 -//        }
   4.852 -//
   4.853 -//        XCTAssertGreaterThan(messages.count, 0, "there are messages")
   4.854 -//
   4.855 -//        for m in messages {
   4.856 -//            XCTAssertNotNil(m.messageID)
   4.857 -//            XCTAssertGreaterThan(m.uid, 0)
   4.858 -//            guard let imap = m.imap else {
   4.859 -//                XCTFail()
   4.860 -//                break
   4.861 -//            }
   4.862 -//            // one flag that is set on server has been unset by the client,
   4.863 -//            // so it has to be removed.
   4.864 -//            let localFlags = imap.localFlags ?? CdImapFlags.create()
   4.865 -//            imap.localFlags = localFlags
   4.866 -//
   4.867 -//            localFlags.flagAnswered = false
   4.868 -//            localFlags.flagDraft = true
   4.869 -//            localFlags.flagFlagged = true
   4.870 -//            // (the client must never change flagRecent according to RFC,
   4.871 -//            // so we set it in state of flagsServer)
   4.872 -//            localFlags.flagRecent = true
   4.873 -//            localFlags.flagSeen = true
   4.874 -//            localFlags.flagDeleted = false
   4.875 -//
   4.876 -//            // set the flag on server side
   4.877 -//            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
   4.878 -//            imap.serverFlags = serverFlags
   4.879 -//            var theBits = ImapFlagsBits.imapNoFlagsSet()
   4.880 -//            theBits.imapSetFlagBit(.deleted)
   4.881 -//            serverFlags.update(rawValue16: theBits)
   4.882 -//        }
   4.883 -//
   4.884 -//        Record.saveAndWait()
   4.885 -//
   4.886 -//        // since a flag has be removed on all messages, all messages need to be synced
   4.887 -//        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.888 -//            folder: inbox, context: Record.Context.main)
   4.889 -//        XCTAssertEqual(messagesToBeSynced.count, messages.count, "all messages need to be synced")
   4.890 -//
   4.891 -//        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
   4.892 -//
   4.893 -//        let expEmailsSynced = expectation(description: "expEmailsSynced")
   4.894 -//        op.completionBlock = {
   4.895 -//            op.completionBlock = nil
   4.896 -//            expEmailsSynced.fulfill()
   4.897 -//        }
   4.898 -//
   4.899 -//        op.start()
   4.900 -//        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   4.901 -//            XCTAssertNil(error)
   4.902 -//            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
   4.903 -//        })
   4.904 -//
   4.905 -//        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.906 -//            folder: inbox, context: Record.Context.main)
   4.907 -//        XCTAssertEqual(messagesToBeSynced.count, 0,
   4.908 -//                       "no messages have to be synced after syncing")
   4.909 -//        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
   4.910 -//                       "all messages have been processed")
   4.911 -//    }
   4.912 -//
   4.913 -//    func testRemoveFlags_removeFlagDraft() {
   4.914 -//        fetchMessages(parentName: #function)
   4.915 -//
   4.916 -//        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
   4.917 -//            XCTFail()
   4.918 -//            return
   4.919 -//        }
   4.920 -//
   4.921 -//        guard let messages = inbox.messages?.sortedArray(
   4.922 -//            using: [NSSortDescriptor(key: "sent", ascending: true)])
   4.923 -//            as? [CdMessage] else {
   4.924 -//                XCTFail()
   4.925 -//                return
   4.926 -//        }
   4.927 -//
   4.928 -//        XCTAssertGreaterThan(messages.count, 0, "there are messages")
   4.929 -//
   4.930 -//        for m in messages {
   4.931 -//            XCTAssertNotNil(m.messageID)
   4.932 -//            XCTAssertGreaterThan(m.uid, 0)
   4.933 -//            guard let imap = m.imap else {
   4.934 -//                XCTFail()
   4.935 -//                break
   4.936 -//            }
   4.937 -//            // one flag that is set on server has been unset by the client,
   4.938 -//            // so it has to be removed.
   4.939 -//            let localFlags = imap.localFlags ?? CdImapFlags.create()
   4.940 -//            imap.localFlags = localFlags
   4.941 -//
   4.942 -//            localFlags.flagAnswered = true
   4.943 -//            localFlags.flagDraft = false
   4.944 -//            localFlags.flagFlagged = true
   4.945 -//            // (the client must never change flagRecent according to RFC,
   4.946 -//            // so we set it in state of flagsServer)
   4.947 -//            localFlags.flagRecent = true
   4.948 -//            localFlags.flagSeen = true
   4.949 -//            localFlags.flagDeleted = false
   4.950 -//
   4.951 -//            // set the flag on server side
   4.952 -//            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
   4.953 -//            imap.serverFlags = serverFlags
   4.954 -//            var theBits = ImapFlagsBits.imapNoFlagsSet()
   4.955 -//            theBits.imapSetFlagBit(.deleted)
   4.956 -//            serverFlags.update(rawValue16: theBits)
   4.957 -//        }
   4.958 -//
   4.959 -//        Record.saveAndWait()
   4.960 -//
   4.961 -//        // since a flag has be removed on all messages, all messages need to be synced
   4.962 -//        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.963 -//            folder: inbox, context: Record.Context.main)
   4.964 -//        XCTAssertEqual(messagesToBeSynced.count, messages.count, "all messages need to be synced")
   4.965 -//
   4.966 -//        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
   4.967 -//
   4.968 -//        let expEmailsSynced = expectation(description: "expEmailsSynced")
   4.969 -//        op.completionBlock = {
   4.970 -//            op.completionBlock = nil
   4.971 -//            expEmailsSynced.fulfill()
   4.972 -//        }
   4.973 -//
   4.974 -//        op.start()
   4.975 -//        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   4.976 -//            XCTAssertNil(error)
   4.977 -//            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
   4.978 -//        })
   4.979 -//
   4.980 -//        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
   4.981 -//            folder: inbox, context: Record.Context.main)
   4.982 -//        XCTAssertEqual(messagesToBeSynced.count, 0,
   4.983 -//                       "no messages have to be synced after syncing")
   4.984 -//        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
   4.985 -//                       "all messages have been processed")
   4.986 -//    }
   4.987 -//
   4.988 -//    func testRemoveFlags_removeFlagFlagged() {
   4.989 -//        fetchMessages(parentName: #function)
   4.990 -//
   4.991 -//        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
   4.992 -//            XCTFail()
   4.993 -//            return
   4.994 -//        }
   4.995 -//
   4.996 -//        guard let messages = inbox.messages?.sortedArray(
   4.997 -//            using: [NSSortDescriptor(key: "sent", ascending: true)])
   4.998 -//            as? [CdMessage] else {
   4.999 -//                XCTFail()
  4.1000 -//                return
  4.1001 -//        }
  4.1002 -//
  4.1003 -//        XCTAssertGreaterThan(messages.count, 0, "there are messages")
  4.1004 -//
  4.1005 -//        for m in messages {
  4.1006 -//            XCTAssertNotNil(m.messageID)
  4.1007 -//            XCTAssertGreaterThan(m.uid, 0)
  4.1008 -//            guard let imap = m.imap else {
  4.1009 -//                XCTFail()
  4.1010 -//                break
  4.1011 -//            }
  4.1012 -//            // one flag that is set on server has been unset by the client,
  4.1013 -//            // so it has to be removed.
  4.1014 -//            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.1015 -//            imap.localFlags = localFlags
  4.1016 -//
  4.1017 -//            localFlags.flagAnswered = true
  4.1018 -//            localFlags.flagDraft = true
  4.1019 -//            localFlags.flagFlagged = false
  4.1020 -//            // (the client must never change flagRecent according to RFC,
  4.1021 -//            // so we set it in state of flagsServer)
  4.1022 -//            localFlags.flagRecent = true
  4.1023 -//            localFlags.flagSeen = true
  4.1024 -//            localFlags.flagDeleted = false
  4.1025 -//
  4.1026 -//            // set the flag on server side
  4.1027 -//            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
  4.1028 -//            imap.serverFlags = serverFlags
  4.1029 -//            var theBits = ImapFlagsBits.imapNoFlagsSet()
  4.1030 -//            theBits.imapSetFlagBit(.deleted)
  4.1031 -//            serverFlags.update(rawValue16: theBits)
  4.1032 -//        }
  4.1033 -//
  4.1034 -//        Record.saveAndWait()
  4.1035 -//
  4.1036 -//        // since a flag has be removed on all messages, all messages need to be synced
  4.1037 -//        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1038 -//            folder: inbox, context: Record.Context.main)
  4.1039 -//        XCTAssertEqual(messagesToBeSynced.count, messages.count, "all messages need to be synced")
  4.1040 -//
  4.1041 -//        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.1042 -//
  4.1043 -//        let expEmailsSynced = expectation(description: "expEmailsSynced")
  4.1044 -//        op.completionBlock = {
  4.1045 -//            op.completionBlock = nil
  4.1046 -//            expEmailsSynced.fulfill()
  4.1047 -//        }
  4.1048 -//
  4.1049 -//        op.start()
  4.1050 -//        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.1051 -//            XCTAssertNil(error)
  4.1052 -//            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
  4.1053 -//        })
  4.1054 -//
  4.1055 -//        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1056 -//            folder: inbox, context: Record.Context.main)
  4.1057 -//        XCTAssertEqual(messagesToBeSynced.count, 0,
  4.1058 -//                       "no messages have to be synced after syncing")
  4.1059 -//        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
  4.1060 -//                       "all messages have been processed")
  4.1061 -//    }
  4.1062 -//
  4.1063 -//    func testRemoveFlags_removeFlagSeen() {
  4.1064 -//        fetchMessages(parentName: #function)
  4.1065 -//
  4.1066 -//        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.1067 -//            XCTFail()
  4.1068 -//            return
  4.1069 -//        }
  4.1070 -//
  4.1071 -//        guard let messages = inbox.messages?.sortedArray(
  4.1072 -//            using: [NSSortDescriptor(key: "sent", ascending: true)])
  4.1073 -//            as? [CdMessage] else {
  4.1074 -//                XCTFail()
  4.1075 -//                return
  4.1076 -//        }
  4.1077 -//
  4.1078 -//        XCTAssertGreaterThan(messages.count, 0, "there are messages")
  4.1079 -//
  4.1080 -//        for m in messages {
  4.1081 -//            XCTAssertNotNil(m.messageID)
  4.1082 -//            XCTAssertGreaterThan(m.uid, 0)
  4.1083 -//            guard let imap = m.imap else {
  4.1084 -//                XCTFail()
  4.1085 -//                break
  4.1086 -//            }
  4.1087 -//            // one flag that is set on server has been unset by the client,
  4.1088 -//            // so it has to be removed.
  4.1089 -//            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.1090 -//            imap.localFlags = localFlags
  4.1091 -//
  4.1092 -//            localFlags.flagAnswered = true
  4.1093 -//            localFlags.flagDraft = true
  4.1094 -//            localFlags.flagFlagged = true
  4.1095 -//            // (the client must never change flagRecent according to RFC,
  4.1096 -//            // so we set it in state of flagsServer)
  4.1097 -//            localFlags.flagRecent = true
  4.1098 -//            localFlags.flagSeen = false
  4.1099 -//            localFlags.flagDeleted = false
  4.1100 -//
  4.1101 -//            // set the flag on server side
  4.1102 -//            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
  4.1103 -//            imap.serverFlags = serverFlags
  4.1104 -//            var theBits = ImapFlagsBits.imapNoFlagsSet()
  4.1105 -//            theBits.imapSetFlagBit(.deleted)
  4.1106 -//            serverFlags.update(rawValue16: theBits)
  4.1107 -//        }
  4.1108 -//
  4.1109 -//        Record.saveAndWait()
  4.1110 -//
  4.1111 -//        // since a flag has be removed on all messages, all messages need to be synced
  4.1112 -//        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1113 -//            folder: inbox, context: Record.Context.main)
  4.1114 -//        XCTAssertEqual(messagesToBeSynced.count, messages.count, "all messages need to be synced")
  4.1115 -//
  4.1116 -//        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.1117 -//
  4.1118 -//        let expEmailsSynced = expectation(description: "expEmailsSynced")
  4.1119 -//        op.completionBlock = {
  4.1120 -//            op.completionBlock = nil
  4.1121 -//            expEmailsSynced.fulfill()
  4.1122 -//        }
  4.1123 -//
  4.1124 -//        op.start()
  4.1125 -//        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.1126 -//            XCTAssertNil(error)
  4.1127 -//            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
  4.1128 -//        })
  4.1129 -//
  4.1130 -//        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1131 -//            folder: inbox, context: Record.Context.main)
  4.1132 -//        XCTAssertEqual(messagesToBeSynced.count, 0,
  4.1133 -//                       "no messages have to be synced after syncing")
  4.1134 -//        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
  4.1135 -//                       "all messages have been processed")
  4.1136 -//    }
  4.1137 -//
  4.1138 -//    func testRemoveFlags_removeFlagDeleted() {
  4.1139 -//        fetchMessages(parentName: #function)
  4.1140 -//
  4.1141 -//        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.1142 -//            XCTFail()
  4.1143 -//            return
  4.1144 -//        }
  4.1145 -//
  4.1146 -//        guard let messages = inbox.messages?.sortedArray(
  4.1147 -//            using: [NSSortDescriptor(key: "sent", ascending: true)])
  4.1148 -//            as? [CdMessage] else {
  4.1149 -//                XCTFail()
  4.1150 -//                return
  4.1151 -//        }
  4.1152 -//
  4.1153 -//        XCTAssertGreaterThan(messages.count, 0, "there are messages")
  4.1154 -//
  4.1155 -//        for m in messages {
  4.1156 -//            XCTAssertNotNil(m.messageID)
  4.1157 -//            XCTAssertGreaterThan(m.uid, 0)
  4.1158 -//            guard let imap = m.imap else {
  4.1159 -//                XCTFail()
  4.1160 -//                break
  4.1161 -//            }
  4.1162 -//            // one flag that is set on server has been unset by the client,
  4.1163 -//            // so it has to be removed.
  4.1164 -//            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.1165 -//            imap.localFlags = localFlags
  4.1166 -//
  4.1167 -//            localFlags.flagAnswered = false
  4.1168 -//            localFlags.flagDraft = true
  4.1169 -//            localFlags.flagFlagged = true
  4.1170 -//            // (the client must never change flagRecent according to RFC,
  4.1171 -//            // so we set it in state of flagsServer)
  4.1172 -//            localFlags.flagRecent = true
  4.1173 -//            localFlags.flagSeen = true
  4.1174 -//            localFlags.flagDeleted = false
  4.1175 -//
  4.1176 -//            // set the flag on server side
  4.1177 -//            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
  4.1178 -//            imap.serverFlags = serverFlags
  4.1179 -//            var theBits = ImapFlagsBits.imapNoFlagsSet()
  4.1180 -//            theBits.imapSetFlagBit(.answered)
  4.1181 -//            serverFlags.update(rawValue16: theBits)
  4.1182 -//        }
  4.1183 -//
  4.1184 -//        Record.saveAndWait()
  4.1185 -//
  4.1186 -//        // since a flag has be removed on all messages, all messages need to be synced
  4.1187 -//        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1188 -//            folder: inbox, context: Record.Context.main)
  4.1189 -//        XCTAssertEqual(messagesToBeSynced.count, messages.count, "all messages need to be synced")
  4.1190 -//
  4.1191 -//        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.1192 -//
  4.1193 -//        let expEmailsSynced = expectation(description: "expEmailsSynced")
  4.1194 -//        op.completionBlock = {
  4.1195 -//            op.completionBlock = nil
  4.1196 -//            expEmailsSynced.fulfill()
  4.1197 -//        }
  4.1198 -//
  4.1199 -//        op.start()
  4.1200 -//        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.1201 -//            XCTAssertNil(error)
  4.1202 -//            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
  4.1203 -//        })
  4.1204 -//
  4.1205 -//        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1206 -//            folder: inbox, context: Record.Context.main)
  4.1207 -//        XCTAssertEqual(messagesToBeSynced.count, 0,
  4.1208 -//                       "no messages have to be synced after syncing")
  4.1209 -//        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
  4.1210 -//                       "all messages have been processed")
  4.1211 -//    }
  4.1212 -//
  4.1213 -//    /**
  4.1214 -//     Proves that in the case of several `SyncFlagsToServerOperation`s
  4.1215 -//     scheduled very close to each other only the first will do the work,
  4.1216 -//     while the others will cancel early and not do anything.
  4.1217 -//     */
  4.1218 -//    func testSyncFlagsToServerOperationMulti() {
  4.1219 -//        fetchMessages(parentName: #function)
  4.1220 -//
  4.1221 -//        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.1222 -//            XCTFail()
  4.1223 -//            return
  4.1224 -//        }
  4.1225 -//
  4.1226 -//        guard let messages = inbox.messages?.sortedArray(
  4.1227 -//            using: [NSSortDescriptor(key: "sent", ascending: true)])
  4.1228 -//            as? [CdMessage] else {
  4.1229 -//                XCTFail()
  4.1230 -//                return
  4.1231 -//        }
  4.1232 -//
  4.1233 -//        XCTAssertGreaterThan(messages.count, 0, "there are messages")
  4.1234 -//
  4.1235 -//        for m in messages {
  4.1236 -//            XCTAssertNotNil(m.messageID)
  4.1237 -//            XCTAssertGreaterThan(m.uid, 0)
  4.1238 -//            guard let imap = m.imap else {
  4.1239 -//                XCTFail()
  4.1240 -//                break
  4.1241 -//            }
  4.1242 -//            // one flag that is set on server has been unset by the client,
  4.1243 -//            // so it has to be removed.
  4.1244 -//            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.1245 -//            imap.localFlags = localFlags
  4.1246 -//
  4.1247 -//            localFlags.flagAnswered = true
  4.1248 -//            localFlags.flagDraft = true
  4.1249 -//            localFlags.flagFlagged = true
  4.1250 -//            // (the client must never change flagRecent according to RFC,
  4.1251 -//            // so we set it in state of flagsServer)
  4.1252 -//            localFlags.flagRecent = true
  4.1253 -//            localFlags.flagSeen = false
  4.1254 -//            localFlags.flagDeleted = false
  4.1255 -//
  4.1256 -//            // set the flag on server side
  4.1257 -//            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
  4.1258 -//            imap.serverFlags = serverFlags
  4.1259 -//            var theBits = ImapFlagsBits.imapNoFlagsSet()
  4.1260 -//            theBits.imapSetFlagBit(.deleted)
  4.1261 -//            serverFlags.update(rawValue16: theBits)
  4.1262 -//        }
  4.1263 -//
  4.1264 -//        Record.saveAndWait()
  4.1265 -//
  4.1266 -//        // since a flag has be removed on all messages, all messages need to be synced
  4.1267 -//        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1268 -//            folder: inbox, context: Record.Context.main)
  4.1269 -//        XCTAssertEqual(messagesToBeSynced.count, messages.count)
  4.1270 -//
  4.1271 -//        let numSyncOpsToTrigger = 5
  4.1272 -//        var ops = [SyncFlagsToServerOperation]()
  4.1273 -//        for i in 1...numSyncOpsToTrigger {
  4.1274 -//            let op =
  4.1275 -//                SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.1276 -//            let expEmailsSynced = expectation(description: "expEmailsSynced\(i)")
  4.1277 -//            op.completionBlock = {
  4.1278 -//                op.completionBlock = nil
  4.1279 -//                expEmailsSynced.fulfill()
  4.1280 -//            }
  4.1281 -//            ops.append(op)
  4.1282 -//        }
  4.1283 -//
  4.1284 -//        let backgroundQueue = OperationQueue()
  4.1285 -//
  4.1286 -//        // Serialize all ops
  4.1287 -//        backgroundQueue.maxConcurrentOperationCount = 1
  4.1288 -//
  4.1289 -//        for op in ops {
  4.1290 -//            backgroundQueue.addOperation(op)
  4.1291 -//        }
  4.1292 -//
  4.1293 -//        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.1294 -//            XCTAssertNil(error)
  4.1295 -//            for op in ops {
  4.1296 -//                XCTAssertFalse(op.hasErrors())
  4.1297 -//            }
  4.1298 -//        })
  4.1299 -//
  4.1300 -//        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1301 -//            folder: inbox, context: Record.Context.main)
  4.1302 -//        XCTAssertEqual(messagesToBeSynced.count, 0)
  4.1303 -//
  4.1304 -//        var first = true
  4.1305 -//        for op in ops {
  4.1306 -//            if first {
  4.1307 -//                XCTAssertEqual(op.numberOfMessagesSynced, inbox.messages?.count)
  4.1308 -//                first = false
  4.1309 -//            } else {
  4.1310 -//                XCTAssertEqual(op.numberOfMessagesSynced, 0)
  4.1311 -//            }
  4.1312 -//        }
  4.1313 -//    }
  4.1314 -//
  4.1315 -//}
  4.1316 +@testable import MessageModel
  4.1317 +@testable import pEpForiOS
  4.1318 +
  4.1319 +class SyncFlagsToServerOperationTest: CoreDataDrivenTestBase {
  4.1320 +
  4.1321 +    // MARK: - SyncFlagsToServerOperation
  4.1322 +
  4.1323 +    func testEmpty() {
  4.1324 +        fetchMessages(parentName: #function)
  4.1325 +
  4.1326 +        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.1327 +            XCTFail()
  4.1328 +            return
  4.1329 +        }
  4.1330 +        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.1331 +        let expEmailsSynced = expectation(description: "expEmailsSynced")
  4.1332 +        op.completionBlock = {
  4.1333 +            op.completionBlock = nil
  4.1334 +            expEmailsSynced.fulfill()
  4.1335 +        }
  4.1336 +
  4.1337 +        op.start()
  4.1338 +        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.1339 +            XCTAssertNil(error)
  4.1340 +            XCTAssertFalse(op.hasErrors())
  4.1341 +        })
  4.1342 +
  4.1343 +        XCTAssertEqual(op.numberOfMessagesSynced, 0)
  4.1344 +    }
  4.1345 +
  4.1346 +    func testSyncFlagsToServerOperation() {
  4.1347 +        fetchMessages(parentName: #function)
  4.1348 +
  4.1349 +        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.1350 +            XCTFail()
  4.1351 +            return
  4.1352 +        }
  4.1353 +
  4.1354 +        guard let messages = inbox.messages?.sortedArray(
  4.1355 +            using: [NSSortDescriptor(key: "sent", ascending: true)])
  4.1356 +            as? [CdMessage] else {
  4.1357 +                XCTFail()
  4.1358 +                return
  4.1359 +        }
  4.1360 +
  4.1361 +        XCTAssertGreaterThan(messages.count, 0, "there are messages")
  4.1362 +
  4.1363 +        for m in messages {
  4.1364 +            XCTAssertNotNil(m.messageID)
  4.1365 +            XCTAssertGreaterThan(m.uid, 0)
  4.1366 +            guard let imap = m.imap else {
  4.1367 +                XCTFail()
  4.1368 +                break
  4.1369 +            }
  4.1370 +            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.1371 +            imap.localFlags = localFlags
  4.1372 +            localFlags.flagFlagged = !localFlags.flagFlagged
  4.1373 +        }
  4.1374 +
  4.1375 +        Record.saveAndWait()
  4.1376 +
  4.1377 +        // redundant check that flagFlagged really has changed
  4.1378 +        guard let messages2 = inbox.messages?.sortedArray(
  4.1379 +            using: [NSSortDescriptor(key: "sent", ascending: true)])
  4.1380 +            as? [CdMessage] else {
  4.1381 +                XCTFail()
  4.1382 +                return
  4.1383 +        }
  4.1384 +        for m in messages2 {
  4.1385 +            guard let imap = m.imap else {
  4.1386 +                XCTFail()
  4.1387 +                break
  4.1388 +            }
  4.1389 +            guard let serverFlags = imap.serverFlags else {
  4.1390 +                XCTFail()
  4.1391 +                break
  4.1392 +            }
  4.1393 +            guard let localFlags = imap.localFlags else {
  4.1394 +                XCTFail()
  4.1395 +                break
  4.1396 +            }
  4.1397 +            XCTAssertNotEqual(serverFlags.flagFlagged, localFlags.flagFlagged)
  4.1398 +        }
  4.1399 +
  4.1400 +        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1401 +            folder: inbox, context: Record.Context.main)
  4.1402 +        XCTAssertEqual(messagesToBeSynced.count, messages.count)
  4.1403 +
  4.1404 +        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.1405 +
  4.1406 +        let expEmailsSynced = expectation(description: "expEmailsSynced")
  4.1407 +        op.completionBlock = {
  4.1408 +            op.completionBlock = nil
  4.1409 +            expEmailsSynced.fulfill()
  4.1410 +        }
  4.1411 +        op.start()
  4.1412 +
  4.1413 +        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.1414 +            XCTAssertNil(error)
  4.1415 +            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
  4.1416 +        })
  4.1417 +
  4.1418 +        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1419 +            folder: inbox, context: Record.Context.main)
  4.1420 +        XCTAssertEqual(messagesToBeSynced.count, 0)
  4.1421 +        XCTAssertEqual(op.numberOfMessagesSynced, messages.count)
  4.1422 +    }
  4.1423 +
  4.1424 +    func testAddFlags_changeAllFlagsExceptDelete() {
  4.1425 +        fetchMessages(parentName: #function)
  4.1426 +
  4.1427 +        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.1428 +            XCTFail()
  4.1429 +            return
  4.1430 +        }
  4.1431 +
  4.1432 +        guard let messages = inbox.messages?.sortedArray(
  4.1433 +            using: [NSSortDescriptor(key: "sent", ascending: true)]) as? [CdMessage] else {
  4.1434 +                XCTFail()
  4.1435 +                return
  4.1436 +        }
  4.1437 +
  4.1438 +        XCTAssertGreaterThan(messages.count, 0, "there are messages")
  4.1439 +
  4.1440 +        for m in messages {
  4.1441 +            XCTAssertNotNil(m.messageID)
  4.1442 +            XCTAssertGreaterThan(m.uid, 0)
  4.1443 +            guard let imap = m.imap else {
  4.1444 +                XCTFail()
  4.1445 +                break
  4.1446 +            }
  4.1447 +
  4.1448 +            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.1449 +            imap.localFlags = localFlags
  4.1450 +
  4.1451 +            // all flags set locally, except delete
  4.1452 +            localFlags.flagAnswered = true
  4.1453 +            localFlags.flagDraft = true
  4.1454 +            localFlags.flagFlagged = true
  4.1455 +            localFlags.flagRecent = false
  4.1456 +            localFlags.flagSeen = true
  4.1457 +
  4.1458 +            // ...but no flags are set on server, so all flags have to be added
  4.1459 +            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
  4.1460 +            imap.serverFlags = serverFlags
  4.1461 +            serverFlags.update(rawValue16: ImapFlagsBits.imapNoFlagsSet())
  4.1462 +
  4.1463 +            XCTAssertNotEqual(m.imap?.localFlags?.flagAnswered, m.imap?.serverFlags?.flagAnswered)
  4.1464 +        }
  4.1465 +
  4.1466 +        Record.saveAndWait()
  4.1467 +
  4.1468 +        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1469 +            folder: inbox, context: Record.Context.main)
  4.1470 +        XCTAssertEqual(messagesToBeSynced.count, messages.count)
  4.1471 +
  4.1472 +        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.1473 +
  4.1474 +        let expEmailsSynced = expectation(description: "expEmailsSynced")
  4.1475 +        op.completionBlock = {
  4.1476 +            op.completionBlock = nil
  4.1477 +            expEmailsSynced.fulfill()
  4.1478 +        }
  4.1479 +
  4.1480 +        op.start()
  4.1481 +        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.1482 +            XCTAssertNil(error)
  4.1483 +            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
  4.1484 +        })
  4.1485 +
  4.1486 +        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1487 +            folder: inbox, context: Record.Context.main)
  4.1488 +        XCTAssertEqual(messagesToBeSynced.count, 0)
  4.1489 +        XCTAssertEqual(op.numberOfMessagesSynced, messages.count)
  4.1490 +    }
  4.1491 +
  4.1492 +    func testAddFlags_allFlagsAlreadySetOnServer() {
  4.1493 +        fetchMessages(parentName: #function)
  4.1494 +
  4.1495 +        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.1496 +            XCTFail()
  4.1497 +            return
  4.1498 +        }
  4.1499 +
  4.1500 +        guard let messages = inbox.messages?.sortedArray(
  4.1501 +            using: [NSSortDescriptor(key: "sent", ascending: true)])
  4.1502 +            as? [CdMessage] else {
  4.1503 +                XCTFail()
  4.1504 +                return
  4.1505 +        }
  4.1506 +
  4.1507 +        XCTAssertGreaterThan(messages.count, 0, "there are messages")
  4.1508 +
  4.1509 +        for m in messages {
  4.1510 +            XCTAssertNotNil(m.messageID)
  4.1511 +            XCTAssertGreaterThan(m.uid, 0)
  4.1512 +            guard let imap = m.imap else {
  4.1513 +                XCTFail()
  4.1514 +                break
  4.1515 +            }
  4.1516 +
  4.1517 +            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.1518 +            imap.localFlags = localFlags
  4.1519 +
  4.1520 +            // all flags set locally ...
  4.1521 +            localFlags.flagAnswered = true
  4.1522 +            localFlags.flagDraft = true
  4.1523 +            localFlags.flagFlagged = true
  4.1524 +            // the client must never change flagRecent according to RFC,
  4.1525 +            // so we set it in state of flagsServer
  4.1526 +            localFlags.flagRecent = true
  4.1527 +            localFlags.flagSeen = true
  4.1528 +            localFlags.flagDeleted = true
  4.1529 +
  4.1530 +            // ...and all flags are set on server, so nothing should be updated
  4.1531 +            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
  4.1532 +            imap.serverFlags = serverFlags
  4.1533 +            serverFlags.update(rawValue16: ImapFlagsBits.imapAllFlagsSet())
  4.1534 +        }
  4.1535 +
  4.1536 +        Record.saveAndWait()
  4.1537 +
  4.1538 +        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1539 +            folder: inbox, context: Record.Context.main)
  4.1540 +        XCTAssertEqual(messagesToBeSynced.count, 0)
  4.1541 +
  4.1542 +        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.1543 +
  4.1544 +        let expEmailsSynced = expectation(description: "expEmailsSynced")
  4.1545 +        op.completionBlock = {
  4.1546 +            op.completionBlock = nil
  4.1547 +            expEmailsSynced.fulfill()
  4.1548 +        }
  4.1549 +
  4.1550 +        op.start()
  4.1551 +        waitForExpectations(timeout: 300, handler: { error in
  4.1552 +            XCTAssertNil(error)
  4.1553 +            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
  4.1554 +        })
  4.1555 +
  4.1556 +        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1557 +            folder: inbox, context: Record.Context.main)
  4.1558 +        XCTAssertEqual(messagesToBeSynced.count, 0, "all done")
  4.1559 +        XCTAssertEqual(op.numberOfMessagesSynced, 0,
  4.1560 +                       "no messages have been synced as all flag were already set before")
  4.1561 +    }
  4.1562 +
  4.1563 +    func testAddFlags_someFlagsAlreadySetOnServer() {
  4.1564 +        fetchMessages(parentName: #function)
  4.1565 +
  4.1566 +        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.1567 +            XCTFail()
  4.1568 +            return
  4.1569 +        }
  4.1570 +
  4.1571 +        guard let messages = inbox.messages?.sortedArray(
  4.1572 +            using: [NSSortDescriptor(key: "sent", ascending: true)])
  4.1573 +            as? [CdMessage] else {
  4.1574 +                XCTFail()
  4.1575 +                return
  4.1576 +        }
  4.1577 +
  4.1578 +        XCTAssertGreaterThan(messages.count, 0,"Some messages exist to work with")
  4.1579 +
  4.1580 +        for m in messages {
  4.1581 +            XCTAssertNotNil(m.messageID)
  4.1582 +            XCTAssertGreaterThan(m.uid, 0)
  4.1583 +            guard let imap = m.imap else {
  4.1584 +                XCTFail()
  4.1585 +                break
  4.1586 +            }
  4.1587 +
  4.1588 +            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.1589 +            imap.localFlags = localFlags
  4.1590 +
  4.1591 +            // all flags set locally ...
  4.1592 +            localFlags.flagAnswered = true
  4.1593 +            localFlags.flagDraft = true
  4.1594 +            localFlags.flagFlagged = true
  4.1595 +            // the client must never change flagRecent according to RFC,
  4.1596 +            // so we set it in state of flagsServer
  4.1597 +            localFlags.flagRecent = false
  4.1598 +            localFlags.flagSeen = true
  4.1599 +            localFlags.flagDeleted = true
  4.1600 +
  4.1601 +            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
  4.1602 +            imap.serverFlags = serverFlags
  4.1603 +
  4.1604 +            var flagsFromServer = ImapFlagsBits.imapNoFlagsSet()
  4.1605 +            flagsFromServer.imapSetFlagBit(.answered)
  4.1606 +            flagsFromServer.imapSetFlagBit(.draft)
  4.1607 +            flagsFromServer.imapSetFlagBit(.flagged)
  4.1608 +            // flagSeen differs ...
  4.1609 +            flagsFromServer.imapSetFlagBit(.deleted)
  4.1610 +            serverFlags.update(rawValue16: flagsFromServer)
  4.1611 +        }
  4.1612 +
  4.1613 +        Record.saveAndWait()
  4.1614 +
  4.1615 +        // ...so all messages should need to be synced
  4.1616 +        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1617 +            folder: inbox,
  4.1618 +            context: Record.Context.main)
  4.1619 +
  4.1620 +        XCTAssertEqual(messagesToBeSynced.count, messages.count,
  4.1621 +                       "all messages should need to be synced")
  4.1622 +
  4.1623 +        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.1624 +
  4.1625 +        let expEmailsSynced = expectation(description: "expEmailsSynced")
  4.1626 +        op.completionBlock = {
  4.1627 +            op.completionBlock = nil
  4.1628 +            expEmailsSynced.fulfill()
  4.1629 +        }
  4.1630 +
  4.1631 +        op.start()
  4.1632 +        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.1633 +            XCTAssertNil(error)
  4.1634 +            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
  4.1635 +        })
  4.1636 +
  4.1637 +        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1638 +            folder: inbox, context: Record.Context.main)
  4.1639 +        XCTAssertEqual(messagesToBeSynced.count, 0, "all done")
  4.1640 +        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
  4.1641 +                       "flagDeleted changes, so all messages should be updated")
  4.1642 +    }
  4.1643 +
  4.1644 +    func testAddFlags_addFlagAnswered() {
  4.1645 +        fetchMessages(parentName: #function)
  4.1646 +
  4.1647 +        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.1648 +            XCTFail()
  4.1649 +            return
  4.1650 +        }
  4.1651 +
  4.1652 +        guard let messages = inbox.messages?.sortedArray(
  4.1653 +            using: [NSSortDescriptor(key: "sent", ascending: true)])
  4.1654 +            as? [CdMessage] else {
  4.1655 +                XCTFail()
  4.1656 +                return
  4.1657 +        }
  4.1658 +
  4.1659 +        XCTAssertGreaterThan(messages.count, 0, "there are messages")
  4.1660 +
  4.1661 +        for m in messages {
  4.1662 +            XCTAssertNotNil(m.messageID)
  4.1663 +            XCTAssertGreaterThan(m.uid, 0)
  4.1664 +            guard let imap = m.imap else {
  4.1665 +                XCTFail()
  4.1666 +                break
  4.1667 +            }
  4.1668 +
  4.1669 +            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.1670 +            imap.localFlags = localFlags
  4.1671 +
  4.1672 +            // one flag that is not set on server has been set by the client,
  4.1673 +            // so it has to be added.
  4.1674 +            localFlags.flagAnswered = true
  4.1675 +            localFlags.flagDraft = false
  4.1676 +            localFlags.flagFlagged = false
  4.1677 +            // (the client must never change flagRecent according to RFC,
  4.1678 +            // so we set it in state of flagsServer)
  4.1679 +            localFlags.flagRecent = false
  4.1680 +            localFlags.flagSeen = false
  4.1681 +            localFlags.flagDeleted = true
  4.1682 +
  4.1683 +            // set the flag on server side
  4.1684 +            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
  4.1685 +            imap.serverFlags = serverFlags
  4.1686 +
  4.1687 +            var theBits = ImapFlagsBits.imapNoFlagsSet()
  4.1688 +            theBits.imapSetFlagBit(.deleted)
  4.1689 +            serverFlags.update(rawValue16: theBits)
  4.1690 +        }
  4.1691 +
  4.1692 +        Record.saveAndWait()
  4.1693 +
  4.1694 +        // since a flag has be added on all messages, all messages need to be synced
  4.1695 +        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1696 +            folder: inbox, context: Record.Context.main)
  4.1697 +        XCTAssertEqual(messagesToBeSynced.count, messages.count, "all messages need to be synced")
  4.1698 +
  4.1699 +        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.1700 +
  4.1701 +        let expEmailsSynced = expectation(description: "expEmailsSynced")
  4.1702 +        op.completionBlock = {
  4.1703 +            op.completionBlock = nil
  4.1704 +            expEmailsSynced.fulfill()
  4.1705 +        }
  4.1706 +
  4.1707 +        op.start()
  4.1708 +        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.1709 +            XCTAssertNil(error)
  4.1710 +            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
  4.1711 +        })
  4.1712 +
  4.1713 +        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1714 +            folder: inbox, context: Record.Context.main)
  4.1715 +        XCTAssertEqual(messagesToBeSynced.count, 0,
  4.1716 +                       "no messages have to be synced after syncing")
  4.1717 +        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
  4.1718 +                       "all messages have been processed")
  4.1719 +    }
  4.1720 +
  4.1721 +    func testAddFlags_addFlagDraft() {
  4.1722 +        fetchMessages(parentName: #function)
  4.1723 +
  4.1724 +        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.1725 +            XCTFail()
  4.1726 +            return
  4.1727 +        }
  4.1728 +
  4.1729 +        guard let messages = inbox.messages?.sortedArray(
  4.1730 +            using: [NSSortDescriptor(key: "sent", ascending: true)])
  4.1731 +            as? [CdMessage] else {
  4.1732 +                XCTFail()
  4.1733 +                return
  4.1734 +        }
  4.1735 +
  4.1736 +        XCTAssertGreaterThan(messages.count, 0, "there are messages")
  4.1737 +
  4.1738 +        for m in messages {
  4.1739 +            XCTAssertNotNil(m.messageID)
  4.1740 +            XCTAssertGreaterThan(m.uid, 0)
  4.1741 +            guard let imap = m.imap else {
  4.1742 +                XCTFail()
  4.1743 +                break
  4.1744 +            }
  4.1745 +
  4.1746 +            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.1747 +            imap.localFlags = localFlags
  4.1748 +
  4.1749 +            // one flag that is not set on server has been set by the client,
  4.1750 +            // so it has to be added.
  4.1751 +            localFlags.flagAnswered = false
  4.1752 +            localFlags.flagDraft = true
  4.1753 +            localFlags.flagFlagged = false
  4.1754 +            // (the client must never change flagRecent according to RFC,
  4.1755 +            // so we set it in state of flagsServer)
  4.1756 +            localFlags.flagRecent = false
  4.1757 +            localFlags.flagSeen = false
  4.1758 +            localFlags.flagDeleted = true
  4.1759 +
  4.1760 +            // set the flag on server side
  4.1761 +            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
  4.1762 +            imap.serverFlags = serverFlags
  4.1763 +
  4.1764 +            var theBits = ImapFlagsBits.imapNoFlagsSet()
  4.1765 +            theBits.imapSetFlagBit(.deleted)
  4.1766 +            serverFlags.update(rawValue16: theBits)
  4.1767 +        }
  4.1768 +
  4.1769 +        Record.saveAndWait()
  4.1770 +
  4.1771 +        // since a flag has be added on all messages, all messages need to be synced
  4.1772 +        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1773 +            folder: inbox, context: Record.Context.main)
  4.1774 +        XCTAssertEqual(messagesToBeSynced.count, messages.count, "all messages need to be synced")
  4.1775 +
  4.1776 +        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.1777 +
  4.1778 +        let expEmailsSynced = expectation(description: "expEmailsSynced")
  4.1779 +        op.completionBlock = {
  4.1780 +            op.completionBlock = nil
  4.1781 +            expEmailsSynced.fulfill()
  4.1782 +        }
  4.1783 +
  4.1784 +        op.start()
  4.1785 +        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.1786 +            XCTAssertNil(error)
  4.1787 +            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
  4.1788 +        })
  4.1789 +
  4.1790 +        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1791 +            folder: inbox, context: Record.Context.main)
  4.1792 +        XCTAssertEqual(messagesToBeSynced.count, 0,
  4.1793 +                       "no messages have to be synced after syncing")
  4.1794 +        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
  4.1795 +                       "all messages have been processed")
  4.1796 +    }
  4.1797 +
  4.1798 +    func testAddFlags_addFlagFlagged() {
  4.1799 +        fetchMessages(parentName: #function)
  4.1800 +
  4.1801 +        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.1802 +            XCTFail()
  4.1803 +            return
  4.1804 +        }
  4.1805 +
  4.1806 +        guard let messages = inbox.messages?.sortedArray(
  4.1807 +            using: [NSSortDescriptor(key: "sent", ascending: true)])
  4.1808 +            as? [CdMessage] else {
  4.1809 +                XCTFail()
  4.1810 +                return
  4.1811 +        }
  4.1812 +
  4.1813 +        XCTAssertGreaterThan(messages.count, 0, "there are messages")
  4.1814 +
  4.1815 +        for m in messages {
  4.1816 +            XCTAssertNotNil(m.messageID)
  4.1817 +            XCTAssertGreaterThan(m.uid, 0)
  4.1818 +            guard let imap = m.imap else {
  4.1819 +                XCTFail()
  4.1820 +                break
  4.1821 +            }
  4.1822 +
  4.1823 +            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.1824 +            imap.localFlags = localFlags
  4.1825 +
  4.1826 +            // one flag that is not set on server has been set by the client,
  4.1827 +            // so it has to be added.
  4.1828 +            localFlags.flagAnswered = false
  4.1829 +            localFlags.flagDraft = false
  4.1830 +            localFlags.flagFlagged = true
  4.1831 +            // (the client must never change flagRecent according to RFC,
  4.1832 +            // so we set it in state of flagsServer)
  4.1833 +            localFlags.flagRecent = false
  4.1834 +            localFlags.flagSeen = false
  4.1835 +            localFlags.flagDeleted = true
  4.1836 +
  4.1837 +            // set the flag on server side
  4.1838 +            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
  4.1839 +            imap.serverFlags = serverFlags
  4.1840 +
  4.1841 +            var theBits = ImapFlagsBits.imapNoFlagsSet()
  4.1842 +            theBits.imapSetFlagBit(.deleted)
  4.1843 +            serverFlags.update(rawValue16: theBits)
  4.1844 +        }
  4.1845 +
  4.1846 +        Record.saveAndWait()
  4.1847 +
  4.1848 +        // since a flag has be added on all messages, all messages need to be synced
  4.1849 +        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1850 +            folder: inbox, context: Record.Context.main)
  4.1851 +        XCTAssertEqual(messagesToBeSynced.count, messages.count, "all messages need to be synced")
  4.1852 +
  4.1853 +        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.1854 +
  4.1855 +        let expEmailsSynced = expectation(description: "expEmailsSynced")
  4.1856 +        op.completionBlock = {
  4.1857 +            op.completionBlock = nil
  4.1858 +            expEmailsSynced.fulfill()
  4.1859 +        }
  4.1860 +
  4.1861 +        op.start()
  4.1862 +        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.1863 +            XCTAssertNil(error)
  4.1864 +            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
  4.1865 +        })
  4.1866 +
  4.1867 +        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1868 +            folder: inbox, context: Record.Context.main)
  4.1869 +        XCTAssertEqual(messagesToBeSynced.count, 0,
  4.1870 +                       "no messages have to be synced after syncing")
  4.1871 +        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
  4.1872 +                       "all messages have been processed")
  4.1873 +    }
  4.1874 +
  4.1875 +    func testAddFlags_addFlagSeen() {
  4.1876 +        fetchMessages(parentName: #function)
  4.1877 +
  4.1878 +        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.1879 +            XCTFail()
  4.1880 +            return
  4.1881 +        }
  4.1882 +
  4.1883 +        guard let messages = inbox.messages?.sortedArray(
  4.1884 +            using: [NSSortDescriptor(key: "sent", ascending: true)])
  4.1885 +            as? [CdMessage] else {
  4.1886 +                XCTFail()
  4.1887 +                return
  4.1888 +        }
  4.1889 +
  4.1890 +        XCTAssertGreaterThan(messages.count, 0, "there are messages")
  4.1891 +
  4.1892 +        for m in messages {
  4.1893 +            XCTAssertNotNil(m.messageID)
  4.1894 +            XCTAssertGreaterThan(m.uid, 0)
  4.1895 +            guard let imap = m.imap else {
  4.1896 +                XCTFail()
  4.1897 +                break
  4.1898 +            }
  4.1899 +            // one flag that is not set on server has been set by the client,
  4.1900 +            // so it has to be added.
  4.1901 +            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.1902 +            imap.localFlags = localFlags
  4.1903 +
  4.1904 +            localFlags.flagAnswered = false
  4.1905 +            localFlags.flagDraft = false
  4.1906 +            localFlags.flagFlagged = false
  4.1907 +            // (the client must never change flagRecent according to RFC,
  4.1908 +            // so we set it in state of flagsServer)
  4.1909 +            localFlags.flagRecent = false
  4.1910 +            localFlags.flagSeen = true
  4.1911 +            localFlags.flagDeleted = true
  4.1912 +
  4.1913 +            // set the flag on server side
  4.1914 +            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
  4.1915 +            imap.serverFlags = serverFlags
  4.1916 +
  4.1917 +            var theBits = ImapFlagsBits.imapNoFlagsSet()
  4.1918 +            theBits.imapSetFlagBit(.deleted)
  4.1919 +            serverFlags.update(rawValue16: theBits)
  4.1920 +        }
  4.1921 +
  4.1922 +        Record.saveAndWait()
  4.1923 +
  4.1924 +        // since a flag has be added on all messages, all messages need to be synced
  4.1925 +        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1926 +            folder: inbox, context: Record.Context.main)
  4.1927 +        XCTAssertEqual(messagesToBeSynced.count, messages.count, "all messages need to be synced")
  4.1928 +
  4.1929 +        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.1930 +
  4.1931 +        let expEmailsSynced = expectation(description: "expEmailsSynced")
  4.1932 +        op.completionBlock = {
  4.1933 +            op.completionBlock = nil
  4.1934 +            expEmailsSynced.fulfill()
  4.1935 +        }
  4.1936 +
  4.1937 +        op.start()
  4.1938 +        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.1939 +            XCTAssertNil(error)
  4.1940 +            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
  4.1941 +        })
  4.1942 +
  4.1943 +        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1944 +            folder: inbox, context: Record.Context.main)
  4.1945 +        XCTAssertEqual(messagesToBeSynced.count, 0,
  4.1946 +                       "no messages have to be synced after syncing")
  4.1947 +        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
  4.1948 +                       "all messages have been processed")
  4.1949 +    }
  4.1950 +
  4.1951 +    func testRemoveFlags_allFlagsAlreadySetOnServer() {
  4.1952 +        fetchMessages(parentName: #function)
  4.1953 +
  4.1954 +        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.1955 +            XCTFail()
  4.1956 +            return
  4.1957 +        }
  4.1958 +
  4.1959 +        guard let messages = inbox.messages?.sortedArray(
  4.1960 +            using: [NSSortDescriptor(key: "sent", ascending: true)])
  4.1961 +            as? [CdMessage] else {
  4.1962 +                XCTFail()
  4.1963 +                return
  4.1964 +        }
  4.1965 +
  4.1966 +        XCTAssertGreaterThan(messages.count, 0, "there are messages")
  4.1967 +
  4.1968 +        for m in messages {
  4.1969 +            XCTAssertNotNil(m.messageID)
  4.1970 +            XCTAssertGreaterThan(m.uid, 0)
  4.1971 +            guard let imap = m.imap else {
  4.1972 +                XCTFail()
  4.1973 +                break
  4.1974 +            }
  4.1975 +            // no flag set locally ...
  4.1976 +            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.1977 +            imap.localFlags = localFlags
  4.1978 +
  4.1979 +            localFlags.flagAnswered = false
  4.1980 +            localFlags.flagDraft = false
  4.1981 +            localFlags.flagFlagged = false
  4.1982 +            // \Recent should be ignored
  4.1983 +            localFlags.flagRecent = true
  4.1984 +            localFlags.flagSeen = false
  4.1985 +            localFlags.flagDeleted = false
  4.1986 +
  4.1987 +            // ... but all flags set on server, so all flags have to be removed
  4.1988 +            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
  4.1989 +            imap.serverFlags = serverFlags
  4.1990 +            serverFlags.update(rawValue16: ImapFlagsBits.imapNoFlagsSet())
  4.1991 +        }
  4.1992 +
  4.1993 +        Record.saveAndWait()
  4.1994 +
  4.1995 +        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.1996 +            folder: inbox, context: Record.Context.main)
  4.1997 +        XCTAssertEqual(messagesToBeSynced.count, 0)
  4.1998 +
  4.1999 +        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.2000 +
  4.2001 +        let expEmailsSynced = expectation(description: "expEmailsSynced")
  4.2002 +        op.completionBlock = {
  4.2003 +            op.completionBlock = nil
  4.2004 +            expEmailsSynced.fulfill()
  4.2005 +        }
  4.2006 +
  4.2007 +        op.start()
  4.2008 +        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.2009 +            XCTAssertNil(error)
  4.2010 +            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
  4.2011 +        })
  4.2012 +
  4.2013 +        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.2014 +            folder: inbox, context: Record.Context.main)
  4.2015 +        XCTAssertEqual(messagesToBeSynced.count, 0)
  4.2016 +        XCTAssertEqual(op.numberOfMessagesSynced, 0, "no message has been synced")
  4.2017 +    }
  4.2018 +
  4.2019 +    func testRemoveFlags_noChanges() {
  4.2020 +        fetchMessages(parentName: #function)
  4.2021 +
  4.2022 +        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.2023 +            XCTFail()
  4.2024 +            return
  4.2025 +        }
  4.2026 +
  4.2027 +        guard let messages = inbox.messages?.sortedArray(
  4.2028 +            using: [NSSortDescriptor(key: "sent", ascending: true)])
  4.2029 +            as? [CdMessage] else {
  4.2030 +                XCTFail()
  4.2031 +                return
  4.2032 +        }
  4.2033 +
  4.2034 +        XCTAssertGreaterThan(messages.count, 0, "there are messages")
  4.2035 +
  4.2036 +        for m in messages {
  4.2037 +            XCTAssertNotNil(m.messageID)
  4.2038 +            XCTAssertGreaterThan(m.uid, 0)
  4.2039 +            guard let imap = m.imap else {
  4.2040 +                XCTFail()
  4.2041 +                break
  4.2042 +            }
  4.2043 +            // flagsCurrent == flagsFromServer, so no syncing should take place
  4.2044 +            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.2045 +            imap.localFlags = localFlags
  4.2046 +
  4.2047 +            localFlags.flagAnswered = false
  4.2048 +            localFlags.flagDraft = true
  4.2049 +            localFlags.flagFlagged = false
  4.2050 +            // (the client must never change flagRecent according to RFC,
  4.2051 +            // so we set it in state of flagsServer)
  4.2052 +            localFlags.flagRecent = false
  4.2053 +            localFlags.flagSeen = true
  4.2054 +            localFlags.flagDeleted = false
  4.2055 +
  4.2056 +            // server flags
  4.2057 +            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
  4.2058 +            imap.serverFlags = serverFlags
  4.2059 +            var theBits = ImapFlagsBits.imapNoFlagsSet()
  4.2060 +            theBits.imapSetFlagBit(.draft)
  4.2061 +            theBits.imapSetFlagBit(.seen)
  4.2062 +            serverFlags.update(rawValue16: theBits)
  4.2063 +        }
  4.2064 +
  4.2065 +        Record.saveAndWait()
  4.2066 +
  4.2067 +        // nothing changed, so no sync should take place
  4.2068 +        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.2069 +            folder: inbox, context: Record.Context.main)
  4.2070 +        XCTAssertEqual(messagesToBeSynced.count, 0)
  4.2071 +
  4.2072 +        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.2073 +
  4.2074 +        let expEmailsSynced = expectation(description: "expEmailsSynced")
  4.2075 +        op.completionBlock = {
  4.2076 +            op.completionBlock = nil
  4.2077 +            expEmailsSynced.fulfill()
  4.2078 +        }
  4.2079 +
  4.2080 +        op.start()
  4.2081 +        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.2082 +            XCTAssertNil(error)
  4.2083 +            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
  4.2084 +        })
  4.2085 +
  4.2086 +        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.2087 +            folder: inbox, context: Record.Context.main)
  4.2088 +        XCTAssertEqual(messagesToBeSynced.count, 0,
  4.2089 +                       "no messages have to be synced after syncing")
  4.2090 +        XCTAssertEqual(op.numberOfMessagesSynced, 0,
  4.2091 +                       "no message has been processed")
  4.2092 +    }
  4.2093 +
  4.2094 +    func testRemoveFlags_removeFlagAnswered() {
  4.2095 +        fetchMessages(parentName: #function)
  4.2096 +
  4.2097 +        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.2098 +            XCTFail()
  4.2099 +            return
  4.2100 +        }
  4.2101 +
  4.2102 +        guard let messages = inbox.messages?.sortedArray(
  4.2103 +            using: [NSSortDescriptor(key: "sent", ascending: true)])
  4.2104 +            as? [CdMessage] else {
  4.2105 +                XCTFail()
  4.2106 +                return
  4.2107 +        }
  4.2108 +
  4.2109 +        XCTAssertGreaterThan(messages.count, 0, "there are messages")
  4.2110 +
  4.2111 +        for m in messages {
  4.2112 +            XCTAssertNotNil(m.messageID)
  4.2113 +            XCTAssertGreaterThan(m.uid, 0)
  4.2114 +            guard let imap = m.imap else {
  4.2115 +                XCTFail()
  4.2116 +                break
  4.2117 +            }
  4.2118 +            // one flag that is set on server has been unset by the client,
  4.2119 +            // so it has to be removed.
  4.2120 +            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.2121 +            imap.localFlags = localFlags
  4.2122 +
  4.2123 +            localFlags.flagAnswered = false
  4.2124 +            localFlags.flagDraft = true
  4.2125 +            localFlags.flagFlagged = true
  4.2126 +            // (the client must never change flagRecent according to RFC,
  4.2127 +            // so we set it in state of flagsServer)
  4.2128 +            localFlags.flagRecent = true
  4.2129 +            localFlags.flagSeen = true
  4.2130 +            localFlags.flagDeleted = false
  4.2131 +
  4.2132 +            // set the flag on server side
  4.2133 +            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
  4.2134 +            imap.serverFlags = serverFlags
  4.2135 +            var theBits = ImapFlagsBits.imapNoFlagsSet()
  4.2136 +            theBits.imapSetFlagBit(.deleted)
  4.2137 +            serverFlags.update(rawValue16: theBits)
  4.2138 +        }
  4.2139 +
  4.2140 +        Record.saveAndWait()
  4.2141 +
  4.2142 +        // since a flag has be removed on all messages, all messages need to be synced
  4.2143 +        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.2144 +            folder: inbox, context: Record.Context.main)
  4.2145 +        XCTAssertEqual(messagesToBeSynced.count, messages.count, "all messages need to be synced")
  4.2146 +
  4.2147 +        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.2148 +
  4.2149 +        let expEmailsSynced = expectation(description: "expEmailsSynced")
  4.2150 +        op.completionBlock = {
  4.2151 +            op.completionBlock = nil
  4.2152 +            expEmailsSynced.fulfill()
  4.2153 +        }
  4.2154 +
  4.2155 +        op.start()
  4.2156 +        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.2157 +            XCTAssertNil(error)
  4.2158 +            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
  4.2159 +        })
  4.2160 +
  4.2161 +        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.2162 +            folder: inbox, context: Record.Context.main)
  4.2163 +        XCTAssertEqual(messagesToBeSynced.count, 0,
  4.2164 +                       "no messages have to be synced after syncing")
  4.2165 +        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
  4.2166 +                       "all messages have been processed")
  4.2167 +    }
  4.2168 +
  4.2169 +    func testRemoveFlags_removeFlagDraft() {
  4.2170 +        fetchMessages(parentName: #function)
  4.2171 +
  4.2172 +        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.2173 +            XCTFail()
  4.2174 +            return
  4.2175 +        }
  4.2176 +
  4.2177 +        guard let messages = inbox.messages?.sortedArray(
  4.2178 +            using: [NSSortDescriptor(key: "sent", ascending: true)])
  4.2179 +            as? [CdMessage] else {
  4.2180 +                XCTFail()
  4.2181 +                return
  4.2182 +        }
  4.2183 +
  4.2184 +        XCTAssertGreaterThan(messages.count, 0, "there are messages")
  4.2185 +
  4.2186 +        for m in messages {
  4.2187 +            XCTAssertNotNil(m.messageID)
  4.2188 +            XCTAssertGreaterThan(m.uid, 0)
  4.2189 +            guard let imap = m.imap else {
  4.2190 +                XCTFail()
  4.2191 +                break
  4.2192 +            }
  4.2193 +            // one flag that is set on server has been unset by the client,
  4.2194 +            // so it has to be removed.
  4.2195 +            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.2196 +            imap.localFlags = localFlags
  4.2197 +
  4.2198 +            localFlags.flagAnswered = true
  4.2199 +            localFlags.flagDraft = false
  4.2200 +            localFlags.flagFlagged = true
  4.2201 +            // (the client must never change flagRecent according to RFC,
  4.2202 +            // so we set it in state of flagsServer)
  4.2203 +            localFlags.flagRecent = true
  4.2204 +            localFlags.flagSeen = true
  4.2205 +            localFlags.flagDeleted = false
  4.2206 +
  4.2207 +            // set the flag on server side
  4.2208 +            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
  4.2209 +            imap.serverFlags = serverFlags
  4.2210 +            var theBits = ImapFlagsBits.imapNoFlagsSet()
  4.2211 +            theBits.imapSetFlagBit(.deleted)
  4.2212 +            serverFlags.update(rawValue16: theBits)
  4.2213 +        }
  4.2214 +
  4.2215 +        Record.saveAndWait()
  4.2216 +
  4.2217 +        // since a flag has be removed on all messages, all messages need to be synced
  4.2218 +        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.2219 +            folder: inbox, context: Record.Context.main)
  4.2220 +        XCTAssertEqual(messagesToBeSynced.count, messages.count, "all messages need to be synced")
  4.2221 +
  4.2222 +        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.2223 +
  4.2224 +        let expEmailsSynced = expectation(description: "expEmailsSynced")
  4.2225 +        op.completionBlock = {
  4.2226 +            op.completionBlock = nil
  4.2227 +            expEmailsSynced.fulfill()
  4.2228 +        }
  4.2229 +
  4.2230 +        op.start()
  4.2231 +        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.2232 +            XCTAssertNil(error)
  4.2233 +            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
  4.2234 +        })
  4.2235 +
  4.2236 +        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.2237 +            folder: inbox, context: Record.Context.main)
  4.2238 +        XCTAssertEqual(messagesToBeSynced.count, 0,
  4.2239 +                       "no messages have to be synced after syncing")
  4.2240 +        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
  4.2241 +                       "all messages have been processed")
  4.2242 +    }
  4.2243 +
  4.2244 +    func testRemoveFlags_removeFlagFlagged() {
  4.2245 +        fetchMessages(parentName: #function)
  4.2246 +
  4.2247 +        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.2248 +            XCTFail()
  4.2249 +            return
  4.2250 +        }
  4.2251 +
  4.2252 +        guard let messages = inbox.messages?.sortedArray(
  4.2253 +            using: [NSSortDescriptor(key: "sent", ascending: true)])
  4.2254 +            as? [CdMessage] else {
  4.2255 +                XCTFail()
  4.2256 +                return
  4.2257 +        }
  4.2258 +
  4.2259 +        XCTAssertGreaterThan(messages.count, 0, "there are messages")
  4.2260 +
  4.2261 +        for m in messages {
  4.2262 +            XCTAssertNotNil(m.messageID)
  4.2263 +            XCTAssertGreaterThan(m.uid, 0)
  4.2264 +            guard let imap = m.imap else {
  4.2265 +                XCTFail()
  4.2266 +                break
  4.2267 +            }
  4.2268 +            // one flag that is set on server has been unset by the client,
  4.2269 +            // so it has to be removed.
  4.2270 +            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.2271 +            imap.localFlags = localFlags
  4.2272 +
  4.2273 +            localFlags.flagAnswered = true
  4.2274 +            localFlags.flagDraft = true
  4.2275 +            localFlags.flagFlagged = false
  4.2276 +            // (the client must never change flagRecent according to RFC,
  4.2277 +            // so we set it in state of flagsServer)
  4.2278 +            localFlags.flagRecent = true
  4.2279 +            localFlags.flagSeen = true
  4.2280 +            localFlags.flagDeleted = false
  4.2281 +
  4.2282 +            // set the flag on server side
  4.2283 +            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
  4.2284 +            imap.serverFlags = serverFlags
  4.2285 +            var theBits = ImapFlagsBits.imapNoFlagsSet()
  4.2286 +            theBits.imapSetFlagBit(.deleted)
  4.2287 +            serverFlags.update(rawValue16: theBits)
  4.2288 +        }
  4.2289 +
  4.2290 +        Record.saveAndWait()
  4.2291 +
  4.2292 +        // since a flag has be removed on all messages, all messages need to be synced
  4.2293 +        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.2294 +            folder: inbox, context: Record.Context.main)
  4.2295 +        XCTAssertEqual(messagesToBeSynced.count, messages.count, "all messages need to be synced")
  4.2296 +
  4.2297 +        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.2298 +
  4.2299 +        let expEmailsSynced = expectation(description: "expEmailsSynced")
  4.2300 +        op.completionBlock = {
  4.2301 +            op.completionBlock = nil
  4.2302 +            expEmailsSynced.fulfill()
  4.2303 +        }
  4.2304 +
  4.2305 +        op.start()
  4.2306 +        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.2307 +            XCTAssertNil(error)
  4.2308 +            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
  4.2309 +        })
  4.2310 +
  4.2311 +        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.2312 +            folder: inbox, context: Record.Context.main)
  4.2313 +        XCTAssertEqual(messagesToBeSynced.count, 0,
  4.2314 +                       "no messages have to be synced after syncing")
  4.2315 +        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
  4.2316 +                       "all messages have been processed")
  4.2317 +    }
  4.2318 +
  4.2319 +    func testRemoveFlags_removeFlagSeen() {
  4.2320 +        fetchMessages(parentName: #function)
  4.2321 +
  4.2322 +        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.2323 +            XCTFail()
  4.2324 +            return
  4.2325 +        }
  4.2326 +
  4.2327 +        guard let messages = inbox.messages?.sortedArray(
  4.2328 +            using: [NSSortDescriptor(key: "sent", ascending: true)])
  4.2329 +            as? [CdMessage] else {
  4.2330 +                XCTFail()
  4.2331 +                return
  4.2332 +        }
  4.2333 +
  4.2334 +        XCTAssertGreaterThan(messages.count, 0, "there are messages")
  4.2335 +
  4.2336 +        for m in messages {
  4.2337 +            XCTAssertNotNil(m.messageID)
  4.2338 +            XCTAssertGreaterThan(m.uid, 0)
  4.2339 +            guard let imap = m.imap else {
  4.2340 +                XCTFail()
  4.2341 +                break
  4.2342 +            }
  4.2343 +            // one flag that is set on server has been unset by the client,
  4.2344 +            // so it has to be removed.
  4.2345 +            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.2346 +            imap.localFlags = localFlags
  4.2347 +
  4.2348 +            localFlags.flagAnswered = true
  4.2349 +            localFlags.flagDraft = true
  4.2350 +            localFlags.flagFlagged = true
  4.2351 +            // (the client must never change flagRecent according to RFC,
  4.2352 +            // so we set it in state of flagsServer)
  4.2353 +            localFlags.flagRecent = true
  4.2354 +            localFlags.flagSeen = false
  4.2355 +            localFlags.flagDeleted = false
  4.2356 +
  4.2357 +            // set the flag on server side
  4.2358 +            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
  4.2359 +            imap.serverFlags = serverFlags
  4.2360 +            var theBits = ImapFlagsBits.imapNoFlagsSet()
  4.2361 +            theBits.imapSetFlagBit(.deleted)
  4.2362 +            serverFlags.update(rawValue16: theBits)
  4.2363 +        }
  4.2364 +
  4.2365 +        Record.saveAndWait()
  4.2366 +
  4.2367 +        // since a flag has be removed on all messages, all messages need to be synced
  4.2368 +        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.2369 +            folder: inbox, context: Record.Context.main)
  4.2370 +        XCTAssertEqual(messagesToBeSynced.count, messages.count, "all messages need to be synced")
  4.2371 +
  4.2372 +        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.2373 +
  4.2374 +        let expEmailsSynced = expectation(description: "expEmailsSynced")
  4.2375 +        op.completionBlock = {
  4.2376 +            op.completionBlock = nil
  4.2377 +            expEmailsSynced.fulfill()
  4.2378 +        }
  4.2379 +
  4.2380 +        op.start()
  4.2381 +        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.2382 +            XCTAssertNil(error)
  4.2383 +            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
  4.2384 +        })
  4.2385 +
  4.2386 +        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.2387 +            folder: inbox, context: Record.Context.main)
  4.2388 +        XCTAssertEqual(messagesToBeSynced.count, 0,
  4.2389 +                       "no messages have to be synced after syncing")
  4.2390 +        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
  4.2391 +                       "all messages have been processed")
  4.2392 +    }
  4.2393 +
  4.2394 +    func testRemoveFlags_removeFlagDeleted() {
  4.2395 +        fetchMessages(parentName: #function)
  4.2396 +
  4.2397 +        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.2398 +            XCTFail()
  4.2399 +            return
  4.2400 +        }
  4.2401 +
  4.2402 +        guard let messages = inbox.messages?.sortedArray(
  4.2403 +            using: [NSSortDescriptor(key: "sent", ascending: true)])
  4.2404 +            as? [CdMessage] else {
  4.2405 +                XCTFail()
  4.2406 +                return
  4.2407 +        }
  4.2408 +
  4.2409 +        XCTAssertGreaterThan(messages.count, 0, "there are messages")
  4.2410 +
  4.2411 +        for m in messages {
  4.2412 +            XCTAssertNotNil(m.messageID)
  4.2413 +            XCTAssertGreaterThan(m.uid, 0)
  4.2414 +            guard let imap = m.imap else {
  4.2415 +                XCTFail()
  4.2416 +                break
  4.2417 +            }
  4.2418 +            // one flag that is set on server has been unset by the client,
  4.2419 +            // so it has to be removed.
  4.2420 +            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.2421 +            imap.localFlags = localFlags
  4.2422 +
  4.2423 +            localFlags.flagAnswered = false
  4.2424 +            localFlags.flagDraft = true
  4.2425 +            localFlags.flagFlagged = true
  4.2426 +            // (the client must never change flagRecent according to RFC,
  4.2427 +            // so we set it in state of flagsServer)
  4.2428 +            localFlags.flagRecent = true
  4.2429 +            localFlags.flagSeen = true
  4.2430 +            localFlags.flagDeleted = false
  4.2431 +
  4.2432 +            // set the flag on server side
  4.2433 +            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
  4.2434 +            imap.serverFlags = serverFlags
  4.2435 +            var theBits = ImapFlagsBits.imapNoFlagsSet()
  4.2436 +            theBits.imapSetFlagBit(.answered)
  4.2437 +            serverFlags.update(rawValue16: theBits)
  4.2438 +        }
  4.2439 +
  4.2440 +        Record.saveAndWait()
  4.2441 +
  4.2442 +        // since a flag has be removed on all messages, all messages need to be synced
  4.2443 +        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.2444 +            folder: inbox, context: Record.Context.main)
  4.2445 +        XCTAssertEqual(messagesToBeSynced.count, messages.count, "all messages need to be synced")
  4.2446 +
  4.2447 +        let op = SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.2448 +
  4.2449 +        let expEmailsSynced = expectation(description: "expEmailsSynced")
  4.2450 +        op.completionBlock = {
  4.2451 +            op.completionBlock = nil
  4.2452 +            expEmailsSynced.fulfill()
  4.2453 +        }
  4.2454 +
  4.2455 +        op.start()
  4.2456 +        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.2457 +            XCTAssertNil(error)
  4.2458 +            XCTAssertFalse(op.hasErrors(), "\(op.error!)")
  4.2459 +        })
  4.2460 +
  4.2461 +        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.2462 +            folder: inbox, context: Record.Context.main)
  4.2463 +        XCTAssertEqual(messagesToBeSynced.count, 0,
  4.2464 +                       "no messages have to be synced after syncing")
  4.2465 +        XCTAssertEqual(op.numberOfMessagesSynced, messages.count,
  4.2466 +                       "all messages have been processed")
  4.2467 +    }
  4.2468 +
  4.2469 +    /**
  4.2470 +     Proves that in the case of several `SyncFlagsToServerOperation`s
  4.2471 +     scheduled very close to each other only the first will do the work,
  4.2472 +     while the others will cancel early and not do anything.
  4.2473 +     */
  4.2474 +    func testSyncFlagsToServerOperationMulti() {
  4.2475 +        fetchMessages(parentName: #function)
  4.2476 +
  4.2477 +        guard let inbox = CdFolder.by(folderType: .inbox, account: cdAccount) else {
  4.2478 +            XCTFail()
  4.2479 +            return
  4.2480 +        }
  4.2481 +
  4.2482 +        guard let messages = inbox.messages?.sortedArray(
  4.2483 +            using: [NSSortDescriptor(key: "sent", ascending: true)])
  4.2484 +            as? [CdMessage] else {
  4.2485 +                XCTFail()
  4.2486 +                return
  4.2487 +        }
  4.2488 +
  4.2489 +        XCTAssertGreaterThan(messages.count, 0, "there are messages")
  4.2490 +
  4.2491 +        for m in messages {
  4.2492 +            XCTAssertNotNil(m.messageID)
  4.2493 +            XCTAssertGreaterThan(m.uid, 0)
  4.2494 +            guard let imap = m.imap else {
  4.2495 +                XCTFail()
  4.2496 +                break
  4.2497 +            }
  4.2498 +            // one flag that is set on server has been unset by the client,
  4.2499 +            // so it has to be removed.
  4.2500 +            let localFlags = imap.localFlags ?? CdImapFlags.create()
  4.2501 +            imap.localFlags = localFlags
  4.2502 +
  4.2503 +            localFlags.flagAnswered = true
  4.2504 +            localFlags.flagDraft = true
  4.2505 +            localFlags.flagFlagged = true
  4.2506 +            // (the client must never change flagRecent according to RFC,
  4.2507 +            // so we set it in state of flagsServer)
  4.2508 +            localFlags.flagRecent = true
  4.2509 +            localFlags.flagSeen = false
  4.2510 +            localFlags.flagDeleted = false
  4.2511 +
  4.2512 +            // set the flag on server side
  4.2513 +            let serverFlags = imap.serverFlags ?? CdImapFlags.create()
  4.2514 +            imap.serverFlags = serverFlags
  4.2515 +            var theBits = ImapFlagsBits.imapNoFlagsSet()
  4.2516 +            theBits.imapSetFlagBit(.deleted)
  4.2517 +            serverFlags.update(rawValue16: theBits)
  4.2518 +        }
  4.2519 +
  4.2520 +        Record.saveAndWait()
  4.2521 +
  4.2522 +        // since a flag has be removed on all messages, all messages need to be synced
  4.2523 +        var messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.2524 +            folder: inbox, context: Record.Context.main)
  4.2525 +        XCTAssertEqual(messagesToBeSynced.count, messages.count)
  4.2526 +
  4.2527 +        let numSyncOpsToTrigger = 5
  4.2528 +        var ops = [SyncFlagsToServerOperation]()
  4.2529 +        for i in 1...numSyncOpsToTrigger {
  4.2530 +            let op =
  4.2531 +                SyncFlagsToServerOperation(imapSyncData: imapSyncData, folderID: inbox.objectID)
  4.2532 +            let expEmailsSynced = expectation(description: "expEmailsSynced\(i)")
  4.2533 +            op.completionBlock = {
  4.2534 +                op.completionBlock = nil
  4.2535 +                expEmailsSynced.fulfill()
  4.2536 +            }
  4.2537 +            ops.append(op)
  4.2538 +        }
  4.2539 +
  4.2540 +        let backgroundQueue = OperationQueue()
  4.2541 +
  4.2542 +        // Serialize all ops
  4.2543 +        backgroundQueue.maxConcurrentOperationCount = 1
  4.2544 +
  4.2545 +        for op in ops {
  4.2546 +            backgroundQueue.addOperation(op)
  4.2547 +        }
  4.2548 +
  4.2549 +        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  4.2550 +            XCTAssertNil(error)
  4.2551 +            for op in ops {
  4.2552 +                XCTAssertFalse(op.hasErrors())
  4.2553 +            }
  4.2554 +        })
  4.2555 +
  4.2556 +        messagesToBeSynced = SyncFlagsToServerOperation.messagesToBeSynced(
  4.2557 +            folder: inbox, context: Record.Context.main)
  4.2558 +        XCTAssertEqual(messagesToBeSynced.count, 0)
  4.2559 +
  4.2560 +        var first = true
  4.2561 +        for op in ops {
  4.2562 +            if first {
  4.2563 +                XCTAssertEqual(op.numberOfMessagesSynced, inbox.messages?.count)
  4.2564 +                first = false
  4.2565 +            } else {
  4.2566 +                XCTAssertEqual(op.numberOfMessagesSynced, 0)
  4.2567 +            }
  4.2568 +        }
  4.2569 +    }
  4.2570 +
  4.2571 +}
     5.1 --- a/pEpForiOSTests/HandshakePartnerTableViewCellViewModelTests.swift	Tue May 14 22:31:30 2019 +0200
     5.2 +++ b/pEpForiOSTests/HandshakePartnerTableViewCellViewModelTests.swift	Tue May 14 23:08:50 2019 +0200
     5.3 @@ -1,208 +1,206 @@
     5.4 -//!!!: dispatch crash
     5.5 -////!!!: dispatch crash
     5.6 -////
     5.7 -////  HandshakePartnerTableViewCellViewModelTests.swift
     5.8 -////  pEpForiOS
     5.9 -////
    5.10 -////  Created by Dirk Zimmermann on 18.09.17.
    5.11 -////  Copyright © 2017 p≡p Security S.A. All rights reserved.
    5.12 -////
    5.13  //
    5.14 -//import XCTest
    5.15 +//  HandshakePartnerTableViewCellViewModelTests.swift
    5.16 +//  pEpForiOS
    5.17  //
    5.18 -//@testable import MessageModel //FIXME:
    5.19 -//@testable import pEpForiOS
    5.20 -//import PEPObjCAdapterFramework
    5.21 +//  Created by Dirk Zimmermann on 18.09.17.
    5.22 +//  Copyright © 2017 p≡p Security S.A. All rights reserved.
    5.23  //
    5.24 -//class DecryptionDelegate: DecryptionAttemptCounterDelegate {
    5.25 -//    var decryptedMessageDict: NSDictionary?
    5.26 -//
    5.27 -//    override func decrypted(originalCdMessage: CdMessage, decryptedMessageDict: NSDictionary?,
    5.28 -//                   rating: PEPRating, keys: [String]) {
    5.29 -//        super.decrypted(
    5.30 -//            originalCdMessage: originalCdMessage, decryptedMessageDict: decryptedMessageDict,
    5.31 -//            rating: rating, keys: keys)
    5.32 -//        self.decryptedMessageDict = decryptedMessageDict
    5.33 -//    }
    5.34 -//}
    5.35 -//
    5.36 -//class HandshakePartnerTableViewCellViewModelTests: XCTestCase {
    5.37 -//    var persistentSetup: PersistentSetup!
    5.38 -//
    5.39 -//    override func setUp() {
    5.40 -//        super.setUp()
    5.41 -//
    5.42 -//        XCTAssertTrue(PEPUtil.pEpClean())
    5.43 -//
    5.44 -//        persistentSetup = PersistentSetup()
    5.45 -//    }
    5.46 -//
    5.47 -//    override func tearDown() {
    5.48 -//        PEPSession.cleanup()
    5.49 -//        persistentSetup = nil
    5.50 -//        super.tearDown()
    5.51 -//    }
    5.52 -//
    5.53 -//    func importMail(session: PEPSession = PEPSession()) ->
    5.54 -//        (message: Message, mySelfID: Identity, partnerID: Identity)? {
    5.55 -//            let decryptDelegate = DecryptionDelegate()
    5.56 -//
    5.57 -//            guard
    5.58 -//                let (mySelf: mySelfID, partner: partnerID, message: message) =
    5.59 -//                TestUtil.setUpPepFromMail(
    5.60 -//                    emailFilePath: "HandshakeTests_mail_001.txt",
    5.61 -//                    decryptDelegate: decryptDelegate) else {
    5.62 -//                        XCTFail()
    5.63 -//                        return nil
    5.64 -//            }
    5.65 -//            XCTAssertNotEqual(mySelfID.address, partnerID.address)
    5.66 -//
    5.67 -//            let meIdent = mySelfID.pEpIdentity()
    5.68 -//            let partnerIdent = partnerID.pEpIdentity()
    5.69 -//
    5.70 -//            try! session.mySelf(meIdent)
    5.71 -//            try! session.update(partnerIdent)
    5.72 -//
    5.73 -//            XCTAssertNotNil(meIdent.fingerPrint)
    5.74 -//            XCTAssertNotNil(partnerIdent.fingerPrint)
    5.75 -//            XCTAssertTrue(try! partnerIdent.isPEPUser(session).boolValue)
    5.76 -//
    5.77 -//            XCTAssertEqual(partnerIdent.fingerPrint, "97F0E744CCDC15BA127A3EE76BAAAC039FB13487")
    5.78 -//
    5.79 -//            return (message: message, mySelfID: mySelfID, partnerID: partnerID)
    5.80 -//    }
    5.81 -//
    5.82 -//    /**
    5.83 -//     Tests trust/reset cycle without view model.
    5.84 -//     */
    5.85 -//    func testBasicTrustReset() {
    5.86 -//        let session = PEPSession()
    5.87 -//
    5.88 -//        guard
    5.89 -//            let (message: _, mySelfID: _, partnerID: partnerID) = importMail(session: session) else
    5.90 -//        {
    5.91 -//            XCTFail()
    5.92 -//            return
    5.93 -//        }
    5.94 -//
    5.95 -//        let partnerIdent = partnerID.pEpIdentity()
    5.96 -//        try! session.update(partnerIdent)
    5.97 -//
    5.98 -//        try! session.trustPersonalKey(partnerIdent)
    5.99 -//        try! session.update(partnerIdent)
   5.100 -//        XCTAssertTrue(try! partnerIdent.isPEPUser(session).boolValue)
   5.101 -//
   5.102 -//        try! session.keyResetTrust(partnerIdent)
   5.103 -//        try! session.trustPersonalKey(partnerIdent)
   5.104 -//        try! session.update(partnerIdent)
   5.105 -//        XCTAssertTrue(try! partnerIdent.isPEPUser(session).boolValue)
   5.106 -//    }
   5.107 -//
   5.108 -//    /**
   5.109 -//     Tests trust/reset/mistrust/resut cycle without view model, using a backup
   5.110 -//     to keep the comm type.
   5.111 -//     */
   5.112 -//    func testBasicTrustMistrustCycleUsingBackup() {
   5.113 -//        let session = PEPSession()
   5.114 -//
   5.115 -//        guard
   5.116 -//            let (message: _, mySelfID: mySelfID,
   5.117 -//                 partnerID: partnerID) = importMail(session: session) else {
   5.118 -//                    XCTFail()
   5.119 -//                    return
   5.120 -//        }
   5.121 -//
   5.122 -//        let meIdent = mySelfID.pEpIdentity()
   5.123 -//        var partnerIdent = partnerID.pEpIdentity()
   5.124 -//        try! session.mySelf(meIdent)
   5.125 -//        try! session.update(partnerIdent)
   5.126 -//
   5.127 -//        // back up the original
   5.128 -//        let partnerIdentOrig = PEPIdentity(identity: partnerIdent)
   5.129 -//        XCTAssertTrue(try! session.isPEPUser(partnerIdentOrig).boolValue)
   5.130 -//
   5.131 -//        try! session.trustPersonalKey(partnerIdent)
   5.132 -//        try! session.update(partnerIdent)
   5.133 -//        XCTAssertTrue(try! session.isPEPUser(partnerIdent).boolValue)
   5.134 -//
   5.135 -//        partnerIdent = PEPIdentity(identity: partnerIdentOrig) // restore backup
   5.136 -//        try! session.keyResetTrust(partnerIdent)
   5.137 -//        try! session.update(partnerIdent)
   5.138 -//        XCTAssertTrue(try! session.isPEPUser(partnerIdent).boolValue)
   5.139 -//
   5.140 -//        partnerIdent = PEPIdentity(identity: partnerIdentOrig) // restore backup
   5.141 -//        try! session.keyMistrusted(partnerIdent)
   5.142 -//        try! session.update(partnerIdent)
   5.143 -//        XCTAssertTrue(try! session.isPEPUser(partnerIdent).boolValue)
   5.144 -//    }
   5.145 -//
   5.146 -//    /**
   5.147 -//     Test trust/reset/mistrust cycle using view model.
   5.148 -//     */
   5.149 -//    func testViewModelTrustMistrustCycles() {
   5.150 -//        let session = PEPSession()
   5.151 -//
   5.152 -//        guard
   5.153 -//            let (message: _, mySelfID: mySelfID,
   5.154 -//                 partnerID: partnerID) = importMail(session: session) else {
   5.155 -//                    XCTFail()
   5.156 -//                    return
   5.157 -//        }
   5.158 -//
   5.159 -//        let vm = HandshakePartnerTableViewCellViewModel(ownIdentity: mySelfID,
   5.160 -//                                                        partner: partnerID,
   5.161 -//                                                        session: session)
   5.162 -//
   5.163 -//        XCTAssertEqual(vm.partnerRating, .reliable)
   5.164 -//
   5.165 -//        vm.confirmTrust()
   5.166 -//        XCTAssertEqual(vm.partnerRating, .trustedAndAnonymized)
   5.167 -//
   5.168 -//        vm.resetOrUndoTrustOrMistrust()
   5.169 -//        XCTAssertEqual(vm.partnerRating, .reliable)
   5.170 -//
   5.171 -//        vm.denyTrust()
   5.172 -//        XCTAssertEqual(vm.partnerRating, .haveNoKey)
   5.173 -//
   5.174 -//        vm.resetOrUndoTrustOrMistrust()
   5.175 -//        XCTAssertEqual(vm.partnerRating, .reliable)
   5.176 -//
   5.177 -//        vm.confirmTrust()
   5.178 -//        XCTAssertEqual(vm.partnerRating, .trustedAndAnonymized)
   5.179 -//
   5.180 -//        vm.resetOrUndoTrustOrMistrust()
   5.181 -//        XCTAssertEqual(vm.partnerRating, .reliable)
   5.182 -//    }
   5.183 -//
   5.184 -//    /**
   5.185 -//     Test mistrust/reset cycle using view model.
   5.186 -//     */
   5.187 -//    func testViewModelMistrustResetTrustCycle() {
   5.188 -//        let session = PEPSession()
   5.189 -//
   5.190 -//        guard
   5.191 -//            let (message: _, mySelfID: mySelfID,
   5.192 -//                 partnerID: partnerID) = importMail(session: session) else {
   5.193 -//                    XCTFail()
   5.194 -//                    return
   5.195 -//        }
   5.196 -//
   5.197 -//        let vm = HandshakePartnerTableViewCellViewModel(ownIdentity: mySelfID,
   5.198 -//                                                        partner: partnerID,
   5.199 -//                                                        session: session)
   5.200 -//
   5.201 -//        XCTAssertEqual(vm.partnerRating, .reliable)
   5.202 -//
   5.203 -//        vm.denyTrust()
   5.204 -//        XCTAssertEqual(vm.partnerRating, .haveNoKey)
   5.205 -//
   5.206 -//        vm.resetOrUndoTrustOrMistrust()
   5.207 -//        XCTAssertEqual(vm.partnerRating, .reliable)
   5.208 -//
   5.209 -//        vm.confirmTrust()
   5.210 -//        XCTAssertEqual(vm.partnerRating, .trustedAndAnonymized)
   5.211 -//
   5.212 -//        vm.resetOrUndoTrustOrMistrust()
   5.213 -//        XCTAssertEqual(vm.partnerRating, .reliable)
   5.214 -//    }
   5.215 -//}
   5.216 +
   5.217 +import XCTest
   5.218 +
   5.219 +@testable import MessageModel //FIXME:
   5.220 +@testable import pEpForiOS
   5.221 +import PEPObjCAdapterFramework
   5.222 +
   5.223 +class DecryptionDelegate: DecryptionAttemptCounterDelegate {
   5.224 +    var decryptedMessageDict: NSDictionary?
   5.225 +
   5.226 +    override func decrypted(originalCdMessage: CdMessage, decryptedMessageDict: NSDictionary?,
   5.227 +                   rating: PEPRating, keys: [String]) {
   5.228 +        super.decrypted(
   5.229 +            originalCdMessage: originalCdMessage, decryptedMessageDict: decryptedMessageDict,
   5.230 +            rating: rating, keys: keys)
   5.231 +        self.decryptedMessageDict = decryptedMessageDict
   5.232 +    }
   5.233 +}
   5.234 +
   5.235 +class HandshakePartnerTableViewCellViewModelTests: XCTestCase {
   5.236 +    var persistentSetup: PersistentSetup!
   5.237 +
   5.238 +    override func setUp() {
   5.239 +        super.setUp()
   5.240 +
   5.241 +        XCTAssertTrue(PEPUtil.pEpClean())
   5.242 +
   5.243 +        persistentSetup = PersistentSetup()
   5.244 +    }
   5.245 +
   5.246 +    override func tearDown() {
   5.247 +        PEPSession.cleanup()
   5.248 +        persistentSetup = nil
   5.249 +        super.tearDown()
   5.250 +    }
   5.251 +
   5.252 +    func importMail(session: PEPSession = PEPSession()) ->
   5.253 +        (message: Message, mySelfID: Identity, partnerID: Identity)? {
   5.254 +            let decryptDelegate = DecryptionDelegate()
   5.255 +
   5.256 +            guard
   5.257 +                let (mySelf: mySelfID, partner: partnerID, message: message) =
   5.258 +                TestUtil.setUpPepFromMail(
   5.259 +                    emailFilePath: "HandshakeTests_mail_001.txt",
   5.260 +                    decryptDelegate: decryptDelegate) else {
   5.261 +                        XCTFail()
   5.262 +                        return nil
   5.263 +            }
   5.264 +            XCTAssertNotEqual(mySelfID.address, partnerID.address)
   5.265 +
   5.266 +            let meIdent = mySelfID.pEpIdentity()
   5.267 +            let partnerIdent = partnerID.pEpIdentity()
   5.268 +
   5.269 +            try! session.mySelf(meIdent)
   5.270 +            try! session.update(partnerIdent)
   5.271 +
   5.272 +            XCTAssertNotNil(meIdent.fingerPrint)
   5.273 +            XCTAssertNotNil(partnerIdent.fingerPrint)
   5.274 +            XCTAssertTrue(try! partnerIdent.isPEPUser(session).boolValue)
   5.275 +
   5.276 +            XCTAssertEqual(partnerIdent.fingerPrint, "97F0E744CCDC15BA127A3EE76BAAAC039FB13487")
   5.277 +
   5.278 +            return (message: message, mySelfID: mySelfID, partnerID: partnerID)
   5.279 +    }
   5.280 +
   5.281 +    /**
   5.282 +     Tests trust/reset cycle without view model.
   5.283 +     */
   5.284 +    func testBasicTrustReset() {
   5.285 +        let session = PEPSession()
   5.286 +
   5.287 +        guard
   5.288 +            let (message: _, mySelfID: _, partnerID: partnerID) = importMail(session: session) else
   5.289 +        {
   5.290 +            XCTFail()
   5.291 +            return
   5.292 +        }
   5.293 +
   5.294 +        let partnerIdent = partnerID.pEpIdentity()
   5.295 +        try! session.update(partnerIdent)
   5.296 +
   5.297 +        try! session.trustPersonalKey(partnerIdent)
   5.298 +        try! session.update(partnerIdent)
   5.299 +        XCTAssertTrue(try! partnerIdent.isPEPUser(session).boolValue)
   5.300 +
   5.301 +        try! session.keyResetTrust(partnerIdent)
   5.302 +        try! session.trustPersonalKey(partnerIdent)
   5.303 +        try! session.update(partnerIdent)
   5.304 +        XCTAssertTrue(try! partnerIdent.isPEPUser(session).boolValue)
   5.305 +    }
   5.306 +
   5.307 +    /**
   5.308 +     Tests trust/reset/mistrust/resut cycle without view model, using a backup
   5.309 +     to keep the comm type.
   5.310 +     */
   5.311 +    func testBasicTrustMistrustCycleUsingBackup() {
   5.312 +        let session = PEPSession()
   5.313 +
   5.314 +        guard
   5.315 +            let (message: _, mySelfID: mySelfID,
   5.316 +                 partnerID: partnerID) = importMail(session: session) else {
   5.317 +                    XCTFail()
   5.318 +                    return
   5.319 +        }
   5.320 +
   5.321 +        let meIdent = mySelfID.pEpIdentity()
   5.322 +        var partnerIdent = partnerID.pEpIdentity()
   5.323 +        try! session.mySelf(meIdent)
   5.324 +        try! session.update(partnerIdent)
   5.325 +
   5.326 +        // back up the original
   5.327 +        let partnerIdentOrig = PEPIdentity(identity: partnerIdent)
   5.328 +        XCTAssertTrue(try! session.isPEPUser(partnerIdentOrig).boolValue)
   5.329 +
   5.330 +        try! session.trustPersonalKey(partnerIdent)
   5.331 +        try! session.update(partnerIdent)
   5.332 +        XCTAssertTrue(try! session.isPEPUser(partnerIdent).boolValue)
   5.333 +
   5.334 +        partnerIdent = PEPIdentity(identity: partnerIdentOrig) // restore backup
   5.335 +        try! session.keyResetTrust(partnerIdent)
   5.336 +        try! session.update(partnerIdent)
   5.337 +        XCTAssertTrue(try! session.isPEPUser(partnerIdent).boolValue)
   5.338 +
   5.339 +        partnerIdent = PEPIdentity(identity: partnerIdentOrig) // restore backup
   5.340 +        try! session.keyMistrusted(partnerIdent)
   5.341 +        try! session.update(partnerIdent)
   5.342 +        XCTAssertTrue(try! session.isPEPUser(partnerIdent).boolValue)
   5.343 +    }
   5.344 +
   5.345 +    /**
   5.346 +     Test trust/reset/mistrust cycle using view model.
   5.347 +     */
   5.348 +    func testViewModelTrustMistrustCycles() {
   5.349 +        let session = PEPSession()
   5.350 +
   5.351 +        guard
   5.352 +            let (message: _, mySelfID: mySelfID,
   5.353 +                 partnerID: partnerID) = importMail(session: session) else {
   5.354 +                    XCTFail()
   5.355 +                    return
   5.356 +        }
   5.357 +
   5.358 +        let vm = HandshakePartnerTableViewCellViewModel(ownIdentity: mySelfID,
   5.359 +                                                        partner: partnerID,
   5.360 +                                                        session: session)
   5.361 +
   5.362 +        XCTAssertEqual(vm.partnerRating, .reliable)
   5.363 +
   5.364 +        vm.confirmTrust()
   5.365 +        XCTAssertEqual(vm.partnerRating, .trustedAndAnonymized)
   5.366 +
   5.367 +        vm.resetOrUndoTrustOrMistrust()
   5.368 +        XCTAssertEqual(vm.partnerRating, .reliable)
   5.369 +
   5.370 +        vm.denyTrust()
   5.371 +        XCTAssertEqual(vm.partnerRating, .haveNoKey)
   5.372 +
   5.373 +        vm.resetOrUndoTrustOrMistrust()
   5.374 +        XCTAssertEqual(vm.partnerRating, .reliable)
   5.375 +
   5.376 +        vm.confirmTrust()
   5.377 +        XCTAssertEqual(vm.partnerRating, .trustedAndAnonymized)
   5.378 +
   5.379 +        vm.resetOrUndoTrustOrMistrust()
   5.380 +        XCTAssertEqual(vm.partnerRating, .reliable)
   5.381 +    }
   5.382 +
   5.383 +    /**
   5.384 +     Test mistrust/reset cycle using view model.
   5.385 +     */
   5.386 +    func testViewModelMistrustResetTrustCycle() {
   5.387 +        let session = PEPSession()
   5.388 +
   5.389 +        guard
   5.390 +            let (message: _, mySelfID: mySelfID,
   5.391 +                 partnerID: partnerID) = importMail(session: session) else {
   5.392 +                    XCTFail()
   5.393 +                    return
   5.394 +        }
   5.395 +
   5.396 +        let vm = HandshakePartnerTableViewCellViewModel(ownIdentity: mySelfID,
   5.397 +                                                        partner: partnerID,
   5.398 +                                                        session: session)
   5.399 +
   5.400 +        XCTAssertEqual(vm.partnerRating, .reliable)
   5.401 +
   5.402 +        vm.denyTrust()
   5.403 +        XCTAssertEqual(vm.partnerRating, .haveNoKey)
   5.404 +
   5.405 +        vm.resetOrUndoTrustOrMistrust()
   5.406 +        XCTAssertEqual(vm.partnerRating, .reliable)
   5.407 +
   5.408 +        vm.confirmTrust()
   5.409 +        XCTAssertEqual(vm.partnerRating, .trustedAndAnonymized)
   5.410 +
   5.411 +        vm.resetOrUndoTrustOrMistrust()
   5.412 +        XCTAssertEqual(vm.partnerRating, .reliable)
   5.413 +    }
   5.414 +}
     6.1 --- a/pEpForiOSTests/Models/Filter/AttachmentFilterTest.swift	Tue May 14 22:31:30 2019 +0200
     6.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.3 @@ -1,91 +0,0 @@
     6.4 -//!!!: filter has been rewritten from scratch. Is probably obsolete. RM if so after IOS-1495 is done (use new filter)
     6.5 -
     6.6 -////
     6.7 -////  AttachmentFilterTest.swift
     6.8 -////  pEpForiOSTests
     6.9 -////
    6.10 -////  Created by Andreas Buff on 19.09.18.
    6.11 -////  Copyright © 2018 p≡p Security S.A. All rights reserved.
    6.12 -////
    6.13 -//
    6.14 -//import XCTest
    6.15 -//
    6.16 -//import MessageModel
    6.17 -//import pEpForiOS
    6.18 -//import PEPObjCAdapterFramework
    6.19 -//
    6.20 -//class AttachmentFilterTest: CoreDataDrivenTestBase {
    6.21 -//
    6.22 -//    func testGetMessagesWithAttatchemnts() {
    6.23 -//        let f1 = Folder(name: "inbox", parent: nil, account: account, folderType: .inbox)
    6.24 -//        f1.save()
    6.25 -//        let messages = createDecryptedMessages(in: f1, numMessages: 2)
    6.26 -//
    6.27 -//        let attachment = Attachment.create(data: nil, mimeType: "type", fileName: "name")
    6.28 -//        let firstMessage = messages.first!
    6.29 -//        firstMessage.replaceAttachments(with: [attachment])
    6.30 -//        firstMessage.save()
    6.31 -//
    6.32 -//        let cf = CompositeFilter<FilterBase>()
    6.33 -//        cf.add(filter: AttachmentFilter())
    6.34 -//        let _ = f1.updateFilter(filter: cf)
    6.35 -//
    6.36 -//         let numOfEncryptableMessagesWithAttchment = 1
    6.37 -//        XCTAssertEqual(f1.allCdMessages().count, numOfEncryptableMessagesWithAttchment)
    6.38 -//    }
    6.39 -//
    6.40 -//    // MARK: - Undecryptable Messages
    6.41 -//
    6.42 -//    func testFilterShouldIgnoreUndecryptable_haveNoKey() {
    6.43 -//        assureMessagesDoNotPassFilter(with: .haveNoKey)
    6.44 -//    }
    6.45 -//
    6.46 -//    func testFilterShouldIgnoreUndecryptable_canNotDecrypt() {
    6.47 -//        assureMessagesDoNotPassFilter(with: .cannotDecrypt)
    6.48 -//    }
    6.49 -//
    6.50 -//    // MARK: - Helper
    6.51 -//
    6.52 -//    func assureMessagesDoNotPassFilter(with pEpRating: PEPRating) {
    6.53 -//        let f1 = Folder(name: "inbox", parent: nil, account: account, folderType: .inbox)
    6.54 -//        f1.save()
    6.55 -//        let messages = createMessages(in: f1, numMessages: 2)
    6.56 -//        let attachment = Attachment.create(data: nil, mimeType: "type", fileName: "name")
    6.57 -//        let firstMessage = messages.first!
    6.58 -//        firstMessage.replaceAttachments(with: [attachment])
    6.59 -//        firstMessage.save()
    6.60 -//
    6.61 -//        let _ = createMessages(in: f1,
    6.62 -//                               numMessages: 3,
    6.63 -//                               pEpRating: pEpRating)
    6.64 -//
    6.65 -//        let cf = CompositeFilter<FilterBase>()
    6.66 -//        cf.add(filter: AttachmentFilter())
    6.67 -//        let _ = f1.updateFilter(filter: cf)
    6.68 -//
    6.69 -//        let numOfEncryptableMessagesWithAttchment = 1
    6.70 -//        XCTAssertEqual(f1.allCdMessages().count, numOfEncryptableMessagesWithAttchment)
    6.71 -//    }
    6.72 -//
    6.73 -//    private func createDecryptedMessages(in folder: Folder, numMessages: Int) -> [Message] {
    6.74 -//        return createMessages(in: folder, numMessages: numMessages)
    6.75 -//    }
    6.76 -//
    6.77 -//    private func createMessages(in folder: Folder, numMessages: Int,
    6.78 -//                                pEpRating: PEPRating = .trusted) -> [Message] {
    6.79 -//        let id = Identity(address: "fake@mail.com")
    6.80 -//        id.save()
    6.81 -//
    6.82 -//        var messages = [Message]()
    6.83 -//        for i in 1...numMessages {
    6.84 -//            let message = Message(uuid: UUID().uuidString, uid: i, parentFolder: folder)
    6.85 -//            message.from = id
    6.86 -//            message.replaceTo(with: [account.user])
    6.87 -//            message.imapFlags.seen = false
    6.88 -//            message.pEpRatingInt = Int(pEpRating.rawValue)
    6.89 -//            message.save()
    6.90 -//            messages.append(message)
    6.91 -//        }
    6.92 -//        return messages
    6.93 -//    }
    6.94 -//}
     7.1 --- a/pEpForiOSTests/Models/Message+FakeMessageTest.swift	Tue May 14 22:31:30 2019 +0200
     7.2 +++ b/pEpForiOSTests/Models/Message+FakeMessageTest.swift	Tue May 14 23:08:50 2019 +0200
     7.3 @@ -1,256 +1,255 @@
     7.4 -//!!!: crash saving NSValidationErrorKey=serverFlags,
     7.5 -////
     7.6 -////  Message+FakeMessageTest.swift
     7.7 -////  pEpForiOSTests
     7.8 -////
     7.9 -////  Created by Andreas Buff on 08.01.19.
    7.10 -////  Copyright © 2019 p≡p Security S.A. All rights reserved.
    7.11 -////
    7.12  //
    7.13 +//  Message+FakeMessageTest.swift
    7.14 +//  pEpForiOSTests
    7.15  //
    7.16 -//import XCTest
    7.17 +//  Created by Andreas Buff on 08.01.19.
    7.18 +//  Copyright © 2019 p≡p Security S.A. All rights reserved.
    7.19  //
    7.20 -////@testable import pEpForiOS
    7.21 -//@testable import MessageModel
    7.22 -//import PEPObjCAdapterFramework
    7.23 -//
    7.24 -//class Message_FakeMessageTest: CoreDataDrivenTestBase {
    7.25 -//    let testUuid = UUID().uuidString + #file
    7.26 -//
    7.27 -//    override func setUp() {
    7.28 -//        super.setUp()
    7.29 -//        cdAccount.createRequiredFoldersAndWait(testCase: self)
    7.30 -//        deleteAllMessages()
    7.31 -//    }
    7.32 -//
    7.33 -//    // MARK: - Fake messages are shown
    7.34 -//
    7.35 -//    func testFakeMsgIsShownInAllFolderTypes() {
    7.36 -//        for folderTpe in FolderType.allCases {
    7.37 -//            deleteAllMessages()
    7.38 -//
    7.39 -//            guard
    7.40 -//                let folder = assureCleanFolderContainingExactlyOneFakeMessage(folderType: folderTpe),
    7.41 -//                let allCdMesgs = CdMessage.all() as? [CdMessage] else {
    7.42 -//                    // That is a valid case. E.g. folderType .normal.
    7.43 -//                    return
    7.44 -//            }
    7.45 -//            XCTAssertEqual(allCdMesgs.count, 1, "Exactly one faked message exists in CD")
    7.46 -//            let all = folder.allMessagesNonThreaded()
    7.47 -//            XCTAssertEqual(all.count, 1, "Fake message is shown")
    7.48 -//            guard let testee = all.first else {
    7.49 -//                XCTFail()
    7.50 -//                return
    7.51 -//            }
    7.52 -//            XCTAssertEqual(testee.uid, Message.uidFakeResponsivenes, "fake message is contained")
    7.53 -//        }
    7.54 -//    }
    7.55 -//
    7.56 -//    // MARK: - isFakeMessage
    7.57 -//
    7.58 -//    func testIsFakeMessage() {
    7.59 -//        for folderTpe in FolderType.allCases {
    7.60 -//            deleteAllMessages()
    7.61 -//            guard
    7.62 -//                let folder = assureCleanFolderContainingExactlyOneFakeMessage(folderType: folderTpe)
    7.63 -//                else {
    7.64 -//                    return
    7.65 -//            }
    7.66 -//            let all = folder.allMessagesNonThreaded()
    7.67 -//            guard let testee = all.first else {
    7.68 -//                XCTFail()
    7.69 -//                return
    7.70 -//            }
    7.71 -//            XCTAssertTrue(testee.isFakeMessage,
    7.72 -//                          "All fake messages in all folder types MUST be recognized")
    7.73 -//        }
    7.74 -//    }
    7.75 -//
    7.76 -//    // MARK: - saveForAppend
    7.77 -//
    7.78 -//    func testSaveForAppend() {
    7.79 -//        for folderType in FolderType.allCases {
    7.80 -//            if folderType.isLocalFolder || !FolderType.requiredTypes.contains(folderType) {
    7.81 -//                continue
    7.82 -//            }
    7.83 -//            deleteAllMessages()
    7.84 -//            guard let folder = Folder.by(account: account, folderType: folderType) else {
    7.85 -//                XCTFail()
    7.86 -//                return
    7.87 -//            }
    7.88 -//            let msg = Message(uuid: testUuid, parentFolder: folder)
    7.89 -//            msg.from = account.user
    7.90 -//            Message.saveForAppend(msg: msg)
    7.91 -//            assureMessageToAppendExistence(in: folder)
    7.92 -//            assureFakeMessageExistence(in: folder)
    7.93 -//        }
    7.94 -//    }
    7.95 -//
    7.96 -//    // MARK: - createCdFakeMessage
    7.97 -//
    7.98 -//    func testCreateCdFakeMessage() {
    7.99 -//        let folderType = FolderType.inbox
   7.100 -//        guard let folder = Folder.by(account: account, folderType: folderType) else {
   7.101 -//            XCTFail()
   7.102 -//            return
   7.103 -//        }
   7.104 -//        let msg = Message(uuid: testUuid, parentFolder: folder)
   7.105 -//        msg.from = account.user
   7.106 -////        Message.createCdFakeMessage(for: msg)
   7.107 -//        assureFakeMessageExistence(in: folder)
   7.108 -//    }
   7.109 -//
   7.110 -//    // MARK: - saveFakeMessage
   7.111 -//
   7.112 -//    func testSaveFakeMessage() {
   7.113 -//        let folderType = FolderType.inbox
   7.114 -//        guard let folder = Folder.by(account: account, folderType: folderType) else {
   7.115 -//            XCTFail()
   7.116 -//            return
   7.117 -//        }
   7.118 -//        let msg = Message(uuid: testUuid, parentFolder: folder)
   7.119 -//        msg.from = account.user
   7.120 -//        msg.saveFakeMessage(in: folder)
   7.121 -//        assureFakeMessageExistence(in: folder)
   7.122 -//    }
   7.123 -//
   7.124 -//    // MARK: - findAndDeleteFakeMessage
   7.125 -//
   7.126 -//    func testFindAndDeleteFakeMessage() {
   7.127 -//        let folderType = FolderType.inbox
   7.128 -//        guard let folder = Folder.by(account: account, folderType: folderType) else {
   7.129 -//            XCTFail()
   7.130 -//            return
   7.131 -//        }
   7.132 -//        let msg = Message(uuid: testUuid, parentFolder: folder)
   7.133 -//        msg.from = account.user
   7.134 -//        msg.saveFakeMessage(in: folder)
   7.135 -//        guard let fakeMsg = assureFakeMessageExistence(mustExist: true, in: folder) else {
   7.136 -//            XCTFail()
   7.137 -//            return
   7.138 -//        }
   7.139 -//        Message.findAndDeleteFakeMessage(withUuid: fakeMsg.uuid, in: folder)
   7.140 -//        assureFakeMessageExistence(mustExist: false, in: folder)
   7.141 -//    }
   7.142 -//
   7.143 -//    // MARK: - Helper
   7.144 -//
   7.145 -//    @discardableResult private func assureFakeMessageExistence(mustExist: Bool = true, in folder: Folder) -> Message? {
   7.146 -//        return assureMessagesExistence(mustExist: mustExist,
   7.147 -//                                       withUid: Message.uidFakeResponsivenes,
   7.148 -//                                       in: folder)
   7.149 -//    }
   7.150 -//
   7.151 -//    @discardableResult private func assureMessageToAppendExistence(mustExist: Bool = true, in folder: Folder)  -> Message? {
   7.152 -//        return assureMessagesExistence(mustExist: mustExist,
   7.153 -//                                       withUid: Message.uidNeedsAppend,
   7.154 -//                                       in: folder)
   7.155 -//    }
   7.156 -//
   7.157 -//    @discardableResult private func assureMessagesExistence(mustExist: Bool = true,
   7.158 -//                                                            withUid uid: Int,
   7.159 -//                                                            in folder: Folder) -> Message? {
   7.160 -//        var result: Message? = nil
   7.161 -//        let moc = Record.Context.main
   7.162 -//        moc.performAndWait {
   7.163 -//            guard let cdFolder =  folder.cdFolder() else {
   7.164 -//                XCTFail()
   7.165 -//                return
   7.166 -//            }
   7.167 -//            let p  = NSPredicate(format: "%K = %d AND %K = %@",
   7.168 -//                                 CdMessage.AttributeName.uid,
   7.169 -//                                 uid,
   7.170 -//                                 CdMessage.RelationshipName.parent,
   7.171 -//                                 cdFolder)
   7.172 -//            guard
   7.173 -//                let allCdMesgs = CdMessage.all(predicate: p) as? [CdMessage],
   7.174 -//                let msg = allCdMesgs.first?.message()
   7.175 -//                else {
   7.176 -//                    if mustExist {
   7.177 -//                        XCTFail()
   7.178 -//                    }
   7.179 -//                    return
   7.180 -//            }
   7.181 -//            XCTAssertEqual(allCdMesgs.count, 1)
   7.182 -//            result = msg
   7.183 -//        }
   7.184 -//        return result
   7.185 -//    }
   7.186 -//
   7.187 -//    private func createMessage(inFolderOfType type: FolderType) -> Message? {
   7.188 -//        guard let folder = Folder.by(account: account, folderType: type) else {
   7.189 -//            XCTFail()
   7.190 -//            return nil
   7.191 -//        }
   7.192 -//        return Message(uuid: testUuid, parentFolder: folder)
   7.193 -//    }
   7.194 -//
   7.195 -//    private func assureCleanFolderContainingExactlyOneFakeMessage(folderType: FolderType) -> Folder? {
   7.196 -//        guard let folder = Folder.by(account: account, folderType: folderType) else {
   7.197 -//            return nil
   7.198 -//        }
   7.199 -//        deleteAllMessages(in: folder)
   7.200 -//        createFakeMessage(in: folder)
   7.201 -//        simulateSeenByEngine(forAllMessagesIn: folder)
   7.202 -//        return folder
   7.203 -//    }
   7.204 -//
   7.205 -//    private func createFakeMessage(in folder: Folder) {
   7.206 -//        Message(uuid: UUID().uuidString + #function, parentFolder: folder).saveFakeMessage(in: folder)
   7.207 -//    }
   7.208 -//
   7.209 -//    private func deleteAllMessages() {
   7.210 -//        let moc = Record.Context.main
   7.211 -//        moc.performAndWait {
   7.212 -//            guard let allCdMesgs = CdMessage.all() as? [CdMessage] else {
   7.213 -//                return
   7.214 -//            }
   7.215 -//            for cdMsg in allCdMesgs {
   7.216 -//                moc.delete(cdMsg)
   7.217 -//            }
   7.218 -//        }
   7.219 -//        do {
   7.220 -//            try moc.save()
   7.221 -//        } catch {
   7.222 -//            XCTFail()
   7.223 -//        }
   7.224 -//    }
   7.225 -//
   7.226 -//    private func deleteAllMessages(in folder: Folder) {
   7.227 -//        let moc = Record.Context.main
   7.228 -//        moc.performAndWait {
   7.229 -//            guard let cdFolder = folder.cdFolder() else {
   7.230 -//                XCTFail()
   7.231 -//                return
   7.232 -//            }
   7.233 -//            let allCdMessages = cdFolder.allMessages()
   7.234 -//            for cdMsg in allCdMessages {
   7.235 -//                moc.delete(cdMsg)
   7.236 -//            }
   7.237 -//        }
   7.238 -//        do {
   7.239 -//            try moc.save()
   7.240 -//        } catch {
   7.241 -//            XCTFail()
   7.242 -//        }
   7.243 -//    }
   7.244 -//
   7.245 -//    private func simulateSeenByEngine(forAllMessagesIn folder: Folder) {
   7.246 -//        let moc = Record.Context.main
   7.247 -//        moc.performAndWait {
   7.248 -//            guard let cdFolder = folder.cdFolder() else {
   7.249 -//                XCTFail()
   7.250 -//                return
   7.251 -//            }
   7.252 -//            let allCdMessages = cdFolder.allMessages()
   7.253 -//            for cdMsg in allCdMessages {
   7.254 -//                cdMsg.pEpRating = Int16(PEPRating.trusted.rawValue)
   7.255 -//            }
   7.256 -//        }
   7.257 -//        do {
   7.258 -//            try moc.save()
   7.259 -//        } catch {
   7.260 -//            XCTFail()
   7.261 -//        }
   7.262 -//    }
   7.263 -//}
   7.264 +
   7.265 +
   7.266 +import XCTest
   7.267 +
   7.268 +//@testable import pEpForiOS
   7.269 +@testable import MessageModel
   7.270 +import PEPObjCAdapterFramework
   7.271 +
   7.272 +class Message_FakeMessageTest: CoreDataDrivenTestBase {
   7.273 +    let testUuid = UUID().uuidString + #file
   7.274 +
   7.275 +    override func setUp() {
   7.276 +        super.setUp()
   7.277 +        cdAccount.createRequiredFoldersAndWait(testCase: self)
   7.278 +        deleteAllMessages()
   7.279 +    }
   7.280 +
   7.281 +    // MARK: - Fake messages are shown
   7.282 +
   7.283 +    func testFakeMsgIsShownInAllFolderTypes() {
   7.284 +        for folderTpe in FolderType.allCases {
   7.285 +            deleteAllMessages()
   7.286 +
   7.287 +            guard
   7.288 +                let folder = assureCleanFolderContainingExactlyOneFakeMessage(folderType: folderTpe),
   7.289 +                let allCdMesgs = CdMessage.all() as? [CdMessage] else {
   7.290 +                    // That is a valid case. E.g. folderType .normal.
   7.291 +                    return
   7.292 +            }
   7.293 +            XCTAssertEqual(allCdMesgs.count, 1, "Exactly one faked message exists in CD")
   7.294 +            let all = folder.allMessagesNonThreaded()
   7.295 +            XCTAssertEqual(all.count, 1, "Fake message is shown")
   7.296 +            guard let testee = all.first else {
   7.297 +                XCTFail()
   7.298 +                return
   7.299 +            }
   7.300 +            XCTAssertEqual(testee.uid, Message.uidFakeResponsivenes, "fake message is contained")
   7.301 +        }
   7.302 +    }
   7.303 +
   7.304 +    // MARK: - isFakeMessage
   7.305 +
   7.306 +    func testIsFakeMessage() {
   7.307 +        for folderTpe in FolderType.allCases {
   7.308 +            deleteAllMessages()
   7.309 +            guard
   7.310 +                let folder = assureCleanFolderContainingExactlyOneFakeMessage(folderType: folderTpe)
   7.311 +                else {
   7.312 +                    return
   7.313 +            }
   7.314 +            let all = folder.allMessagesNonThreaded()
   7.315 +            guard let testee = all.first else {
   7.316 +                XCTFail()
   7.317 +                return
   7.318 +            }
   7.319 +            XCTAssertTrue(testee.isFakeMessage,
   7.320 +                          "All fake messages in all folder types MUST be recognized")
   7.321 +        }
   7.322 +    }
   7.323 +
   7.324 +    // MARK: - saveForAppend
   7.325 +
   7.326 +    func testSaveForAppend() {
   7.327 +        for folderType in FolderType.allCases {
   7.328 +            if folderType.isLocalFolder || !FolderType.requiredTypes.contains(folderType) {
   7.329 +                continue
   7.330 +            }
   7.331 +            deleteAllMessages()
   7.332 +            guard let folder = Folder.by(account: account, folderType: folderType) else {
   7.333 +                XCTFail()
   7.334 +                return
   7.335 +            }
   7.336 +            let msg = Message(uuid: testUuid, parentFolder: folder)
   7.337 +            msg.from = account.user
   7.338 +            Message.saveForAppend(msg: msg)
   7.339 +            assureMessageToAppendExistence(in: folder)
   7.340 +            assureFakeMessageExistence(in: folder)
   7.341 +        }
   7.342 +    }
   7.343 +
   7.344 +    // MARK: - createCdFakeMessage
   7.345 +
   7.346 +    func testCreateCdFakeMessage() {
   7.347 +        let folderType = FolderType.inbox
   7.348 +        guard let folder = Folder.by(account: account, folderType: folderType) else {
   7.349 +            XCTFail()
   7.350 +            return
   7.351 +        }
   7.352 +        let msg = Message(uuid: testUuid, parentFolder: folder)
   7.353 +        msg.from = account.user
   7.354 +//        Message.createCdFakeMessage(for: msg)
   7.355 +        assureFakeMessageExistence(in: folder)
   7.356 +    }
   7.357 +
   7.358 +    // MARK: - saveFakeMessage
   7.359 +
   7.360 +    func testSaveFakeMessage() {
   7.361 +        let folderType = FolderType.inbox
   7.362 +        guard let folder = Folder.by(account: account, folderType: folderType) else {
   7.363 +            XCTFail()
   7.364 +            return
   7.365 +        }
   7.366 +        let msg = Message(uuid: testUuid, parentFolder: folder)
   7.367 +        msg.from = account.user
   7.368 +        msg.saveFakeMessage(in: folder)
   7.369 +        assureFakeMessageExistence(in: folder)
   7.370 +    }
   7.371 +
   7.372 +    // MARK: - findAndDeleteFakeMessage
   7.373 +
   7.374 +    func testFindAndDeleteFakeMessage() {
   7.375 +        let folderType = FolderType.inbox
   7.376 +        guard let folder = Folder.by(account: account, folderType: folderType) else {
   7.377 +            XCTFail()
   7.378 +            return
   7.379 +        }
   7.380 +        let msg = Message(uuid: testUuid, parentFolder: folder)
   7.381 +        msg.from = account.user
   7.382 +        msg.saveFakeMessage(in: folder)
   7.383 +        guard let fakeMsg = assureFakeMessageExistence(mustExist: true, in: folder) else {
   7.384 +            XCTFail()
   7.385 +            return
   7.386 +        }
   7.387 +        Message.findAndDeleteFakeMessage(withUuid: fakeMsg.uuid, in: folder)
   7.388 +        assureFakeMessageExistence(mustExist: false, in: folder)
   7.389 +    }
   7.390 +
   7.391 +    // MARK: - Helper
   7.392 +
   7.393 +    @discardableResult private func assureFakeMessageExistence(mustExist: Bool = true, in folder: Folder) -> Message? {
   7.394 +        return assureMessagesExistence(mustExist: mustExist,
   7.395 +                                       withUid: Message.uidFakeResponsivenes,
   7.396 +                                       in: folder)
   7.397 +    }
   7.398 +
   7.399 +    @discardableResult private func assureMessageToAppendExistence(mustExist: Bool = true, in folder: Folder)  -> Message? {
   7.400 +        return assureMessagesExistence(mustExist: mustExist,
   7.401 +                                       withUid: Message.uidNeedsAppend,
   7.402 +                                       in: folder)
   7.403 +    }
   7.404 +
   7.405 +    @discardableResult private func assureMessagesExistence(mustExist: Bool = true,
   7.406 +                                                            withUid uid: Int,
   7.407 +                                                            in folder: Folder) -> Message? {
   7.408 +        var result: Message? = nil
   7.409 +        let moc = Record.Context.main
   7.410 +        moc.performAndWait {
   7.411 +            guard let cdFolder =  folder.cdFolder() else {
   7.412 +                XCTFail()
   7.413 +                return
   7.414 +            }
   7.415 +            let p  = NSPredicate(format: "%K = %d AND %K = %@",
   7.416 +                                 CdMessage.AttributeName.uid,
   7.417 +                                 uid,
   7.418 +                                 CdMessage.RelationshipName.parent,
   7.419 +                                 cdFolder)
   7.420 +            guard
   7.421 +                let allCdMesgs = CdMessage.all(predicate: p) as? [CdMessage],
   7.422 +                let msg = allCdMesgs.first?.message()
   7.423 +                else {
   7.424 +                    if mustExist {
   7.425 +                        XCTFail()
   7.426 +                    }
   7.427 +                    return
   7.428 +            }
   7.429 +            XCTAssertEqual(allCdMesgs.count, 1)
   7.430 +            result = msg
   7.431 +        }
   7.432 +        return result
   7.433 +    }
   7.434 +
   7.435 +    private func createMessage(inFolderOfType type: FolderType) -> Message? {
   7.436 +        guard let folder = Folder.by(account: account, folderType: type) else {
   7.437 +            XCTFail()
   7.438 +            return nil
   7.439 +        }
   7.440 +        return Message(uuid: testUuid, parentFolder: folder)
   7.441 +    }
   7.442 +
   7.443 +    private func assureCleanFolderContainingExactlyOneFakeMessage(folderType: FolderType) -> Folder? {
   7.444 +        guard let folder = Folder.by(account: account, folderType: folderType) else {
   7.445 +            return nil
   7.446 +        }
   7.447 +        deleteAllMessages(in: folder)
   7.448 +        createFakeMessage(in: folder)
   7.449 +        simulateSeenByEngine(forAllMessagesIn: folder)
   7.450 +        return folder
   7.451 +    }
   7.452 +
   7.453 +    private func createFakeMessage(in folder: Folder) {
   7.454 +        Message(uuid: UUID().uuidString + #function, parentFolder: folder).saveFakeMessage(in: folder)
   7.455 +    }
   7.456 +
   7.457 +    private func deleteAllMessages() {
   7.458 +        let moc = Record.Context.main
   7.459 +        moc.performAndWait {
   7.460 +            guard let allCdMesgs = CdMessage.all() as? [CdMessage] else {
   7.461 +                return
   7.462 +            }
   7.463 +            for cdMsg in allCdMesgs {
   7.464 +                moc.delete(cdMsg)
   7.465 +            }
   7.466 +        }
   7.467 +        do {
   7.468 +            try moc.save()
   7.469 +        } catch {
   7.470 +            XCTFail()
   7.471 +        }
   7.472 +    }
   7.473 +
   7.474 +    private func deleteAllMessages(in folder: Folder) {
   7.475 +        let moc = Record.Context.main
   7.476 +        moc.performAndWait {
   7.477 +            guard let cdFolder = folder.cdFolder() else {
   7.478 +                XCTFail()
   7.479 +                return
   7.480 +            }
   7.481 +            let allCdMessages = cdFolder.allMessages()
   7.482 +            for cdMsg in allCdMessages {
   7.483 +                moc.delete(cdMsg)
   7.484 +            }
   7.485 +        }
   7.486 +        do {
   7.487 +            try moc.save()
   7.488 +        } catch {
   7.489 +            XCTFail()
   7.490 +        }
   7.491 +    }
   7.492 +
   7.493 +    private func simulateSeenByEngine(forAllMessagesIn folder: Folder) {
   7.494 +        let moc = Record.Context.main
   7.495 +        moc.performAndWait {
   7.496 +            guard let cdFolder = folder.cdFolder() else {
   7.497 +                XCTFail()
   7.498 +                return
   7.499 +            }
   7.500 +            let allCdMessages = cdFolder.allMessages()
   7.501 +            for cdMsg in allCdMessages {
   7.502 +                cdMsg.pEpRating = Int16(PEPRating.trusted.rawValue)
   7.503 +            }
   7.504 +        }
   7.505 +        do {
   7.506 +            try moc.save()
   7.507 +        } catch {
   7.508 +            XCTFail()
   7.509 +        }
   7.510 +    }
   7.511 +}
     8.1 --- a/pEpForiOSTests/SimpleOperationsTest.swift	Tue May 14 22:31:30 2019 +0200
     8.2 +++ b/pEpForiOSTests/SimpleOperationsTest.swift	Tue May 14 23:08:50 2019 +0200
     8.3 @@ -542,7 +542,7 @@
     8.4      }
     8.5  
     8.6      func testOutgoingMessageColor() {
     8.7 -        let moc = Stack.shared.newPrivateConcurrentContext
     8.8 +        let moc: NSManagedObjectContext = Stack.shared.mainContext
     8.9          let account = SecretTestData().createWorkingCdAccount(context: moc)
    8.10          moc.saveAndLogErrors()
    8.11  
     9.1 --- a/pEpForiOSTests/TestUtils/TestUtil.swift	Tue May 14 22:31:30 2019 +0200
     9.2 +++ b/pEpForiOSTests/TestUtils/TestUtil.swift	Tue May 14 23:08:50 2019 +0200
     9.3 @@ -376,86 +376,7 @@
     9.4  
     9.5              messagesInTheQueue.append(message)
     9.6          }
     9.7 -        context.saveAndLogErrors() //!!!: crash saving. Probalby mandatory field missing. Repro: testSyncOutgoing
     9.8 -        /*
     9.9 -         //!!!:
    9.10 -         Fatal error: ERROR #file - saveAndLogErrors()[23]: Error Domain=NSCocoaErrorDomain Code=1570 "The operation couldn’t be completed. (Cocoa error 1570.)" UserInfo={NSValidationErrorObject=<CdMessage: 0x607000045370> (entity: CdMessage; id: 0x6030001aeea0 <x-coredata:///CdMessage/t04E43D11-80A8-4C4D-BA8E-60F54AD8F79187> ; data: {
    9.11 -         attachments =     (
    9.12 -         "0x6030001b0310 <x-coredata:///CdAttachment/t04E43D11-80A8-4C4D-BA8E-60F54AD8F79188>"
    9.13 -         );
    9.14 -         bcc =     (
    9.15 -         );
    9.16 -         cc =     (
    9.17 -         );
    9.18 -         comments = nil;
    9.19 -         from = "0x60300019d800 <x-coredata:///CdIdentity/t04E43D11-80A8-4C4D-BA8E-60F54AD8F79185>";
    9.20 -         imap = nil;
    9.21 -         keysFromDecryption =     (
    9.22 -         );
    9.23 -         keywords =     (
    9.24 -         );
    9.25 -         longMessage = "Long message 1";
    9.26 -         longMessageFormatted = "<h1>Long HTML 1</h1>";
    9.27 -         optionalFields =     (
    9.28 -         );
    9.29 -         pEpProtected = 1;
    9.30 -         pEpRating = "-32768";
    9.31 -         parent = "0x6030000dfc30 <x-coredata://960270F0-7D2C-4D2A-A99E-BAED675E33EA/CdFolder/p12>";
    9.32 -         received = nil;
    9.33 -         receivedBy = nil;
    9.34 -         references =     (
    9.35 -         );
    9.36 -         replyTo =     (
    9.37 -         );
    9.38 -         sent = "2019-04-04 18:13:24 +0000";
    9.39 -         shortMessage = "Some subject 1";
    9.40 -         targetFolder = nil;
    9.41 -         to =     (
    9.42 -         "0x6030001aecf0 <x-coredata:///CdIdentity/t04E43D11-80A8-4C4D-BA8E-60F54AD8F79186>"
    9.43 -         );
    9.44 -         uid = 0;
    9.45 -         underAttack = 0;
    9.46 -         uuid = "7C44DC5B.C2EF.4C19.B166.FBC3978ECCAB@pretty.Easy.privacy";
    9.47 -         }), NSValidationErrorKey=imap, NSLocalizedDescription=The operation couldn’t be completed. (Cocoa error 1570.)}: file /Users/buff/workspace/pEp/src/MessageModel/MessageModel/MessageModel/Util/SystemUtils.swift, line 15
    9.48 -         2019-04-04 20:13:24.708745+0200 pEp[66099:2009000] Fatal error: ERROR #file - saveAndLogErrors()[23]: Error Domain=NSCocoaErrorDomain Code=1570 "The operation couldn’t be completed. (Cocoa error 1570.)" UserInfo={NSValidationErrorObject=<CdMessage: 0x607000045370> (entity: CdMessage; id: 0x6030001aeea0 <x-coredata:///CdMessage/t04E43D11-80A8-4C4D-BA8E-60F54AD8F79187> ; data: {
    9.49 -         attachments =     (
    9.50 -         "0x6030001b0310 <x-coredata:///CdAttachment/t04E43D11-80A8-4C4D-BA8E-60F54AD8F79188>"
    9.51 -         );
    9.52 -         bcc =     (
    9.53 -         );
    9.54 -         cc =     (
    9.55 -         );
    9.56 -         comments = nil;
    9.57 -         from = "0x60300019d800 <x-coredata:///CdIdentity/t04E43D11-80A8-4C4D-BA8E-60F54AD8F79185>";
    9.58 -         imap = nil;
    9.59 -         keysFromDecryption =     (
    9.60 -         );
    9.61 -         keywords =     (
    9.62 -         );
    9.63 -         longMessage = "Long message 1";
    9.64 -         longMessageFormatted = "<h1>Long HTML 1</h1>";
    9.65 -         optionalFields =     (
    9.66 -         );
    9.67 -         pEpProtected = 1;
    9.68 -         pEpRating = "-32768";
    9.69 -         parent = "0x6030000dfc30 <x-coredata://960270F0-7D2C-4D2A-A99E-BAED675E33EA/CdFolder/p12>";
    9.70 -         received = nil;
    9.71 -         receivedBy = nil;
    9.72 -         references =     (
    9.73 -         );
    9.74 -         replyTo =     (
    9.75 -         );
    9.76 -         sent = "2019-04-04 18:13:24 +0000";
    9.77 -         shortMessage = "Some subject 1";
    9.78 -         targetFolder = nil;
    9.79 -         to =     (
    9.80 -         "0x6030001aecf0 <x-coredata:///CdIdentity/t04E43D11-80A8-4C4D-BA8E-60F54AD8F79186>"
    9.81 -         );
    9.82 -         uid = 0;
    9.83 -         underAttack = 0;
    9.84 -         uuid = "7C44DC5B.C2EF.4C19.B166.FBC3978ECCAB@pretty.Easy.privacy";
    9.85 -         }), NSValidationErrorKey=imap, NSLocalizedDescription=The operation couldn’t be completed. (Cocoa error 1570.)}: file /Users/buff/workspace/pEp/src/MessageModel/MessageModel/MessageModel/Util/SystemUtils.swift, line 15
    9.86 -         */
    9.87 +        context.saveAndLogErrors() 
    9.88  
    9.89          if let cdOutgoingMsgs = outbox.messages?.sortedArray(
    9.90              using: [NSSortDescriptor(key: "uid", ascending: true)]) as? [CdMessage] {
    10.1 --- a/pEpForiOSTests/UI/Compose/ViewModel/ComposeViewModelSectionTest.swift	Tue May 14 22:31:30 2019 +0200
    10.2 +++ b/pEpForiOSTests/UI/Compose/ViewModel/ComposeViewModelSectionTest.swift	Tue May 14 23:08:50 2019 +0200
    10.3 @@ -1,232 +1,231 @@
    10.4 -//!!!: Huston! ComoseViewModel definately causes serious crashes in tets. The Dispatch .. in calculatePepRatingproblem! fires whenever after the actual test ran and causes postentionally every later test to crash!
    10.5 -////
    10.6 -////  ComposeViewModelSectionTest.swift
    10.7 -////  pEpForiOSTests
    10.8 -////
    10.9 -////  Created by Andreas Buff on 15.11.18.
   10.10 -////  Copyright © 2018 p≡p Security S.A. All rights reserved.
   10.11 -////
   10.12  //
   10.13 -//import XCTest
   10.14 +//  ComposeViewModelSectionTest.swift
   10.15 +//  pEpForiOSTests
   10.16  //
   10.17 -//@testable import pEpForiOS
   10.18 -//import MessageModel
   10.19 +//  Created by Andreas Buff on 15.11.18.
   10.20 +//  Copyright © 2018 p≡p Security S.A. All rights reserved.
   10.21  //
   10.22 -//class ComposeViewModelSectionTest: CoreDataDrivenTestBase {
   10.23 -//    var state: ComposeViewModel.ComposeViewModelState?
   10.24 -//
   10.25 -//    override func setUp() {
   10.26 -//        super.setUp()
   10.27 -//        state = setupSimpleState()
   10.28 -//    }
   10.29 -//
   10.30 -//    override func tearDown() {
   10.31 -//        state = nil
   10.32 -//        super.tearDown()
   10.33 -//    }
   10.34 -//
   10.35 -//    // MARK: - recipients
   10.36 -//
   10.37 -//    func test_recipients_bccWrapped() {
   10.38 -//        let to = 1
   10.39 -//        assert(forSectionType: .recipients,
   10.40 -//               expectedRowType: RecipientCellViewModel.self,
   10.41 -//               expectedNumRows: to)
   10.42 -//    }
   10.43 -//
   10.44 -//    func test_recipients_bccUnwrapped() {
   10.45 -//        state?.setBccUnwrapped()
   10.46 -//        let to = 1
   10.47 -//        let cc = 1
   10.48 -//        let bcc = 1
   10.49 -//        assert(forSectionType: .recipients,
   10.50 -//               expectedRowType: RecipientCellViewModel.self,
   10.51 -//               expectedNumRows: to + cc + bcc)
   10.52 -//    }
   10.53 -//
   10.54 -//    // MARK: - wrapped
   10.55 -//
   10.56 -//    func testWrapped_bccWrapped() {
   10.57 -//        let wrappedCcBcc = 1
   10.58 -//        assert(forSectionType: .wrapped,
   10.59 -//               expectedRowType: WrappedBccViewModel.self,
   10.60 -//               expectedNumRows: wrappedCcBcc)
   10.61 -//    }
   10.62 -//
   10.63 -//    func testWrapped_bccUnwrapped() {
   10.64 -//        state?.setBccUnwrapped()
   10.65 -//        let showsCcAndBccInsteadOfWrapper = 0
   10.66 -//        assert(forSectionType: .wrapped,
   10.67 -//               expectedRowType: WrappedBccViewModel.self,
   10.68 -//               expectedNumRows: showsCcAndBccInsteadOfWrapper)
   10.69 -//    }
   10.70 -//
   10.71 -//    // MARK: - subject
   10.72 -//
   10.73 -//    func testSubject() {
   10.74 -//        let allwaysShown = 1
   10.75 -//        assert(forSectionType: .subject,
   10.76 -//               expectedRowType: SubjectCellViewModel.self,
   10.77 -//               expectedNumRows: allwaysShown)
   10.78 -//    }
   10.79 -//
   10.80 -//    func testSubject_noMatterWhat() {
   10.81 -//        state?.setBccUnwrapped()
   10.82 -//        let allwaysShown = 1
   10.83 -//        assert(forSectionType: .subject,
   10.84 -//               expectedRowType: SubjectCellViewModel.self,
   10.85 -//               expectedNumRows: allwaysShown)
   10.86 -//    }
   10.87 -//
   10.88 -//    // MARK: - account
   10.89 -//
   10.90 -//    func testAccount_oneExisting() {
   10.91 -//        assertAccountSection()
   10.92 -//    }
   10.93 -//
   10.94 -//    func testAccount_twoExisting() {
   10.95 -//        let account = SecretTestData().createWorkingAccount(number: 1)
   10.96 -//        account.save()
   10.97 -//        assertAccountSection()
   10.98 -//    }
   10.99 -//
  10.100 -//    // MARK: - body
  10.101 -//
  10.102 -//    func testbody() {
  10.103 -//        let allwaysShown = 1
  10.104 -//        assert(forSectionType: .body,
  10.105 -//               expectedRowType: BodyCellViewModel.self,
  10.106 -//               expectedNumRows: allwaysShown)
  10.107 -//    }
  10.108 -//
  10.109 -//    func testbody_noMatterWhat() {
  10.110 -//        state?.setBccUnwrapped()
  10.111 -//        let allwaysShown = 1
  10.112 -//        assert(forSectionType: .body,
  10.113 -//               expectedRowType: BodyCellViewModel.self,
  10.114 -//               expectedNumRows: allwaysShown)
  10.115 -//    }
  10.116 -//
  10.117 -//    // MARK: - attachments
  10.118 -//
  10.119 -//    func testAttachments_none() {
  10.120 -//        assertAttchments(numInlinedAttachments: 0, numNonInlinedAttachments: 0)
  10.121 -//    }
  10.122 -//
  10.123 -//    func testAttachments_nonInllinedAttachment() {
  10.124 -//        assertAttchments(numInlinedAttachments: 0, numNonInlinedAttachments: 1)
  10.125 -//    }
  10.126 -//
  10.127 -//    func testAttachments_nonInllinedAttachments() {
  10.128 -//        assertAttchments(numInlinedAttachments: 0, numNonInlinedAttachments: 2)
  10.129 -//    }
  10.130 -//
  10.131 -//    func testAttachments_inllinedAttachment() {
  10.132 -//        assertAttchments(numInlinedAttachments: 1, numNonInlinedAttachments: 0)
  10.133 -//    }
  10.134 -//
  10.135 -//    func testAttachments_inllinedAttachments() {
  10.136 -//        assertAttchments(numInlinedAttachments: 2, numNonInlinedAttachments: 0)
  10.137 -//    }
  10.138 -//
  10.139 -//    func testAttachments_inllinedAndNonInlinedAttachments() {
  10.140 -//        assertAttchments(numInlinedAttachments: 1, numNonInlinedAttachments: 1)
  10.141 -//    }
  10.142 -//
  10.143 -//    func testAttachments_inllinedAndNonInlinedAttachments_2() {
  10.144 -//        assertAttchments(numInlinedAttachments: 2, numNonInlinedAttachments: 2)
  10.145 -//    }
  10.146 -//
  10.147 -//    private func assertAttchments(numInlinedAttachments: Int = 0,
  10.148 -//                                  numNonInlinedAttachments: Int = 0) {
  10.149 -//        guard let state = state else {
  10.150 -//            XCTFail("No State")
  10.151 -//            return
  10.152 -//        }
  10.153 -//        add(numAttachments: numNonInlinedAttachments, ofType: .attachment, to: state)
  10.154 -//        add(numAttachments: numInlinedAttachments, ofType: .inline, to: state)
  10.155 -//
  10.156 -//        let expectedNumAttachmentRows = numNonInlinedAttachments
  10.157 -//        assert(forSectionType: .attachments,
  10.158 -//               expectedRowType: AttachmentViewModel.self,
  10.159 -//               expectedNumRows: expectedNumAttachmentRows)
  10.160 -//    }
  10.161 -//
  10.162 -//    private func add(numAttachments: Int,
  10.163 -//                     ofType type: Attachment.ContentDispositionType,
  10.164 -//                     to state: ComposeViewModel.ComposeViewModelState) {
  10.165 -//        for i in 0..<numAttachments {
  10.166 -//            let createe = Attachment(data: Data(),
  10.167 -//                                     mimeType: "image/jpg",
  10.168 -//                                     contentDisposition: type)
  10.169 -//            createe.fileName = "fileName \(i)"
  10.170 -//            if type == .inline {
  10.171 -//                state.inlinedAttachments.append(createe)
  10.172 -//            } else {
  10.173 -//                state.nonInlinedAttachments.append(createe)
  10.174 -//            }
  10.175 -//        }
  10.176 -//    }
  10.177 -//
  10.178 -//    // MARK: - Helper
  10.179 -//
  10.180 -//    func assertAccountSection() {
  10.181 -//        let numexistingAccounts = Account.all().count
  10.182 -//        let expectedAccountsRowShouldExist = numexistingAccounts == 1 ? 0 : 1
  10.183 -//        assert(forSectionType: .account,
  10.184 -//               expectedRowType: AccountCellViewModel.self,
  10.185 -//               expectedNumRows: expectedAccountsRowShouldExist)
  10.186 -//    }
  10.187 -//
  10.188 -//    private func assert(forSectionType sectionType: ComposeViewModel.Section.SectionType,
  10.189 -//                        expectedRowType: AnyClass,
  10.190 -//                        expectedNumRows: Int) {
  10.191 -//        guard let state = state else {
  10.192 -//            XCTFail("No State")
  10.193 -//            return
  10.194 -//        }
  10.195 -//        let createe = ComposeViewModel.Section(type: sectionType,
  10.196 -//                                               for: state,
  10.197 -//                                               cellVmDelegate: nil)
  10.198 -//        if expectedNumRows == 0 {
  10.199 -//            XCTAssertNil(createe)
  10.200 -//            return
  10.201 -//        }
  10.202 -//        guard let testee = createe else {
  10.203 -//            XCTFail("Section is expected non-empty (expectedNumRows != 0)")
  10.204 -//            return
  10.205 -//        }
  10.206 -//        XCTAssertEqual(testee.rows.count, expectedNumRows)
  10.207 -//        for row in testee.rows {
  10.208 -//            XCTAssertTrue(type(of:row) == expectedRowType, "row is correct type")
  10.209 -//        }
  10.210 -//    }
  10.211 -//
  10.212 -//    // MARK: - Setup
  10.213 -//
  10.214 -//    private func setupSimpleState(toRecipients: [Identity] = [],
  10.215 -//                                  ccRecipients: [Identity] = [],
  10.216 -//                                  bccRecipients: [Identity] = [],
  10.217 -//                                  isWapped: Bool = true) -> ComposeViewModel.ComposeViewModelState {
  10.218 -//        let drafts = Folder(name: "Inbox", parent: nil, account: account, folderType: .drafts)
  10.219 -//        drafts.save()
  10.220 -//        let msg = Message(uuid: UUID().uuidString, parentFolder: drafts)
  10.221 -//        msg.from = account.user
  10.222 -//        msg.replaceTo(with: toRecipients)
  10.223 -//        msg.replaceCc(with: ccRecipients)
  10.224 -//        msg.replaceBcc(with: bccRecipients)
  10.225 -//        msg.shortMessage = "shortMessage"
  10.226 -//        msg.longMessage = "longMessage"
  10.227 -//        msg.longMessageFormatted = "longMessageFormatted"
  10.228 -//        msg.replaceAttachments(with: [])
  10.229 -//        msg.save()
  10.230 -//        let initData = ComposeViewModel.InitData(withPrefilledToRecipient: nil,
  10.231 -//                                                 orForOriginalMessage: msg,
  10.232 -//                                                 composeMode: .normal)
  10.233 -//        let createe = ComposeViewModel.ComposeViewModelState(initData: initData, delegate: nil)
  10.234 -//        if !isWapped {
  10.235 -//            createe.setBccUnwrapped()
  10.236 -//        }
  10.237 -//        return createe
  10.238 -//    }
  10.239 -//}
  10.240 +
  10.241 +import XCTest
  10.242 +
  10.243 +@testable import pEpForiOS
  10.244 +@testable import MessageModel
  10.245 +
  10.246 +class ComposeViewModelSectionTest: CoreDataDrivenTestBase {
  10.247 +    var state: ComposeViewModel.ComposeViewModelState?
  10.248 +
  10.249 +    override func setUp() {
  10.250 +        super.setUp()
  10.251 +        state = setupSimpleState()
  10.252 +    }
  10.253 +
  10.254 +    override func tearDown() {
  10.255 +        state = nil
  10.256 +        super.tearDown()
  10.257 +    }
  10.258 +
  10.259 +    // MARK: - recipients
  10.260 +
  10.261 +    func test_recipients_bccWrapped() {
  10.262 +        let to = 1
  10.263 +        assert(forSectionType: .recipients,
  10.264 +               expectedRowType: RecipientCellViewModel.self,
  10.265 +               expectedNumRows: to)
  10.266 +    }
  10.267 +
  10.268 +    func test_recipients_bccUnwrapped() {
  10.269 +        state?.setBccUnwrapped()
  10.270 +        let to = 1
  10.271 +        let cc = 1
  10.272 +        let bcc = 1
  10.273 +        assert(forSectionType: .recipients,
  10.274 +               expectedRowType: RecipientCellViewModel.self,
  10.275 +               expectedNumRows: to + cc + bcc)
  10.276 +    }
  10.277 +
  10.278 +    // MARK: - wrapped
  10.279 +
  10.280 +    func testWrapped_bccWrapped() {
  10.281 +        let wrappedCcBcc = 1
  10.282 +        assert(forSectionType: .wrapped,
  10.283 +               expectedRowType: WrappedBccViewModel.self,
  10.284 +               expectedNumRows: wrappedCcBcc)
  10.285 +    }
  10.286 +
  10.287 +    func testWrapped_bccUnwrapped() {
  10.288 +        state?.setBccUnwrapped()
  10.289 +        let showsCcAndBccInsteadOfWrapper = 0
  10.290 +        assert(forSectionType: .wrapped,
  10.291 +               expectedRowType: WrappedBccViewModel.self,
  10.292 +               expectedNumRows: showsCcAndBccInsteadOfWrapper)
  10.293 +    }
  10.294 +
  10.295 +    // MARK: - subject
  10.296 +
  10.297 +    func testSubject() {
  10.298 +        let allwaysShown = 1
  10.299 +        assert(forSectionType: .subject,
  10.300 +               expectedRowType: SubjectCellViewModel.self,
  10.301 +               expectedNumRows: allwaysShown)
  10.302 +    }
  10.303 +
  10.304 +    func testSubject_noMatterWhat() {
  10.305 +        state?.setBccUnwrapped()
  10.306 +        let allwaysShown = 1
  10.307 +        assert(forSectionType: .subject,
  10.308 +               expectedRowType: SubjectCellViewModel.self,
  10.309 +               expectedNumRows: allwaysShown)
  10.310 +    }
  10.311 +
  10.312 +    // MARK: - account
  10.313 +
  10.314 +    func testAccount_oneExisting() {
  10.315 +        assertAccountSection()
  10.316 +    }
  10.317 +
  10.318 +    func testAccount_twoExisting() {
  10.319 +        let account = SecretTestData().createWorkingAccount(number: 1)
  10.320 +        account.save()
  10.321 +        assertAccountSection()
  10.322 +    }
  10.323 +
  10.324 +    // MARK: - body
  10.325 +
  10.326 +    func testbody() {
  10.327 +        let allwaysShown = 1
  10.328 +        assert(forSectionType: .body,
  10.329 +               expectedRowType: BodyCellViewModel.self,
  10.330 +               expectedNumRows: allwaysShown)
  10.331 +    }
  10.332 +
  10.333 +    func testbody_noMatterWhat() {
  10.334 +        state?.setBccUnwrapped()
  10.335 +        let allwaysShown = 1
  10.336 +        assert(forSectionType: .body,
  10.337 +               expectedRowType: BodyCellViewModel.self,
  10.338 +               expectedNumRows: allwaysShown)
  10.339 +    }
  10.340 +
  10.341 +    // MARK: - attachments
  10.342 +
  10.343 +    func testAttachments_none() {
  10.344 +        assertAttchments(numInlinedAttachments: 0, numNonInlinedAttachments: 0)
  10.345 +    }
  10.346 +
  10.347 +    func testAttachments_nonInllinedAttachment() {
  10.348 +        assertAttchments(numInlinedAttachments: 0, numNonInlinedAttachments: 1)
  10.349 +    }
  10.350 +
  10.351 +    func testAttachments_nonInllinedAttachments() {
  10.352 +        assertAttchments(numInlinedAttachments: 0, numNonInlinedAttachments: 2)
  10.353 +    }
  10.354 +
  10.355 +    func testAttachments_inllinedAttachment() {
  10.356 +        assertAttchments(numInlinedAttachments: 1, numNonInlinedAttachments: 0)
  10.357 +    }
  10.358 +
  10.359 +    func testAttachments_inllinedAttachments() {
  10.360 +        assertAttchments(numInlinedAttachments: 2, numNonInlinedAttachments: 0)
  10.361 +    }
  10.362 +
  10.363 +    func testAttachments_inllinedAndNonInlinedAttachments() {
  10.364 +        assertAttchments(numInlinedAttachments: 1, numNonInlinedAttachments: 1)
  10.365 +    }
  10.366 +
  10.367 +    func testAttachments_inllinedAndNonInlinedAttachments_2() {
  10.368 +        assertAttchments(numInlinedAttachments: 2, numNonInlinedAttachments: 2)
  10.369 +    }
  10.370 +
  10.371 +    private func assertAttchments(numInlinedAttachments: Int = 0,
  10.372 +                                  numNonInlinedAttachments: Int = 0) {
  10.373 +        guard let state = state else {
  10.374 +            XCTFail("No State")
  10.375 +            return
  10.376 +        }
  10.377 +        add(numAttachments: numNonInlinedAttachments, ofType: .attachment, to: state)
  10.378 +        add(numAttachments: numInlinedAttachments, ofType: .inline, to: state)
  10.379 +
  10.380 +        let expectedNumAttachmentRows = numNonInlinedAttachments
  10.381 +        assert(forSectionType: .attachments,
  10.382 +               expectedRowType: AttachmentViewModel.self,
  10.383 +               expectedNumRows: expectedNumAttachmentRows)
  10.384 +    }
  10.385 +
  10.386 +    private func add(numAttachments: Int,
  10.387 +                     ofType type: Attachment.ContentDispositionType,
  10.388 +                     to state: ComposeViewModel.ComposeViewModelState) {
  10.389 +        for i in 0..<numAttachments {
  10.390 +            let createe = Attachment(data: Data(),
  10.391 +                                     mimeType: "image/jpg",
  10.392 +                                     contentDisposition: type)
  10.393 +            createe.fileName = "fileName \(i)"
  10.394 +            if type == .inline {
  10.395 +                state.inlinedAttachments.append(createe)
  10.396 +            } else {
  10.397 +                state.nonInlinedAttachments.append(createe)
  10.398 +            }
  10.399 +        }
  10.400 +    }
  10.401 +
  10.402 +    // MARK: - Helper
  10.403 +
  10.404 +    func assertAccountSection() {
  10.405 +        let numexistingAccounts = Account.all().count
  10.406 +        let expectedAccountsRowShouldExist = numexistingAccounts == 1 ? 0 : 1
  10.407 +        assert(forSectionType: .account,
  10.408 +               expectedRowType: AccountCellViewModel.self,
  10.409 +               expectedNumRows: expectedAccountsRowShouldExist)
  10.410 +    }
  10.411 +
  10.412 +    private func assert(forSectionType sectionType: ComposeViewModel.Section.SectionType,
  10.413 +                        expectedRowType: AnyClass,
  10.414 +                        expectedNumRows: Int) {
  10.415 +        guard let state = state else {
  10.416 +            XCTFail("No State")
  10.417 +            return
  10.418 +        }
  10.419 +        let createe = ComposeViewModel.Section(type: sectionType,
  10.420 +                                               for: state,
  10.421 +                                               cellVmDelegate: nil)
  10.422 +        if expectedNumRows == 0 {
  10.423 +            XCTAssertNil(createe)
  10.424 +            return
  10.425 +        }
  10.426 +        guard let testee = createe else {
  10.427 +            XCTFail("Section is expected non-empty (expectedNumRows != 0)")
  10.428 +            return
  10.429 +        }
  10.430 +        XCTAssertEqual(testee.rows.count, expectedNumRows)
  10.431 +        for row in testee.rows {
  10.432 +            XCTAssertTrue(type(of:row) == expectedRowType, "row is correct type")
  10.433 +        }
  10.434 +    }
  10.435 +
  10.436 +    // MARK: - Setup
  10.437 +
  10.438 +    private func setupSimpleState(toRecipients: [Identity] = [],
  10.439 +                                  ccRecipients: [Identity] = [],
  10.440 +                                  bccRecipients: [Identity] = [],
  10.441 +                                  isWapped: Bool = true) -> ComposeViewModel.ComposeViewModelState {
  10.442 +        let drafts = Folder(name: "Inbox", parent: nil, account: account, folderType: .drafts)
  10.443 +        drafts.save()
  10.444 +        let msg = Message(uuid: UUID().uuidString, parentFolder: drafts)
  10.445 +        msg.from = account.user
  10.446 +        msg.replaceTo(with: toRecipients)
  10.447 +        msg.replaceCc(with: ccRecipients)
  10.448 +        msg.replaceBcc(with: bccRecipients)
  10.449 +        msg.shortMessage = "shortMessage"
  10.450 +        msg.longMessage = "longMessage"
  10.451 +        msg.longMessageFormatted = "longMessageFormatted"
  10.452 +        msg.replaceAttachments(with: [])
  10.453 +        msg.save()
  10.454 +        let initData = ComposeViewModel.InitData(withPrefilledToRecipient: nil,
  10.455 +                                                 orForOriginalMessage: msg,
  10.456 +                                                 composeMode: .normal)
  10.457 +        let createe = ComposeViewModel.ComposeViewModelState(initData: initData, delegate: nil)
  10.458 +        if !isWapped {
  10.459 +            createe.setBccUnwrapped()
  10.460 +        }
  10.461 +        return createe
  10.462 +    }
  10.463 +}
    11.1 --- a/pEpForiOSTests/UI/Compose/ViewModel/ComposeViewModelTest.swift	Tue May 14 22:31:30 2019 +0200
    11.2 +++ b/pEpForiOSTests/UI/Compose/ViewModel/ComposeViewModelTest.swift	Tue May 14 23:08:50 2019 +0200
    11.3 @@ -1,1914 +1,1912 @@
    11.4 -//!!!: needs love!
    11.5 +//
    11.6 +//  ComposeViewModelTest.swift
    11.7 +//  pEpForiOSTests
    11.8 +//
    11.9 +//  Created by Andreas Buff on 15.11.18.
   11.10 +//  Copyright © 2018 p≡p Security S.A. All rights reserved.
   11.11 +//
   11.12  
   11.13 -////
   11.14 -////  ComposeViewModelTest.swift
   11.15 -////  pEpForiOSTests
   11.16 -////
   11.17 -////  Created by Andreas Buff on 15.11.18.
   11.18 -////  Copyright © 2018 p≡p Security S.A. All rights reserved.
   11.19 -////
   11.20 -//
   11.21 -//import XCTest
   11.22 -//
   11.23 -//@testable import pEpForiOS
   11.24 -//@testable import MessageModel
   11.25 -//import PEPObjCAdapterFramework
   11.26 -//
   11.27 -//class ComposeViewModelTest: CoreDataDrivenTestBase {
   11.28 -//    private var testDelegate: TestDelegate?
   11.29 -//    private var testResultDelegate: TestResultDelegate?
   11.30 -//    var vm: ComposeViewModel?
   11.31 -//    var outbox: Folder? {
   11.32 -//        return account.firstFolder(ofType: .outbox)
   11.33 -//    }
   11.34 -//    var drafts: Folder? {
   11.35 -//        return account.firstFolder(ofType: .drafts)
   11.36 -//    }
   11.37 -//    var sent: Folder? {
   11.38 -//        return account.firstFolder(ofType: .sent)
   11.39 -//    }
   11.40 -//
   11.41 -//    override func setUp() {
   11.42 -//        super.setUp()
   11.43 -//        vm = ComposeViewModel(resultDelegate: nil,
   11.44 -//                              composeMode: nil,
   11.45 -//                              prefilledTo: nil,
   11.46 -//                              originalMessage: nil)
   11.47 -//        assureOutboxExists()
   11.48 -//        assureDraftsExists()
   11.49 -//        assureSentExists()
   11.50 -//    }
   11.51 -//
   11.52 -//    // MARK: - Test the Test Helper
   11.53 -//
   11.54 -//    func testAssertHelperTest_doNothing_noCallback() {
   11.55 -//        assert(contentChangedMustBeCalled: false,
   11.56 -//               focusSwitchedMustBeCalled: false,
   11.57 -//               validatedStateChangedMustBeCalled: false,
   11.58 -//               modelChangedMustBeCalled: false,
   11.59 -//               sectionChangedMustBeCalled: false,
   11.60 -//               colorBatchNeedsUpdateMustBeCalled: false,
   11.61 -//               hideSuggestionsMustBeCalled: false,
   11.62 -//               showSuggestionsMustBeCalled: false,
   11.63 -//               showMediaAttachmentPickerMustBeCalled: false,
   11.64 -//               hideMediaAttachmentPickerMustBeCalled: false,
   11.65 -//               showDocumentAttachmentPickerMustBeCalled: false,
   11.66 -//               documentAttachmentPickerDonePickerCalled: false,
   11.67 -//               didComposeNewMailMustBeCalled: false,
   11.68 -//               didModifyMessageMustBeCalled: false,
   11.69 -//               didDeleteMessageMustBeCalled: false)
   11.70 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
   11.71 -//    }
   11.72 -//
   11.73 -//    // MARK: - init
   11.74 -//
   11.75 -//    func testInit_resultDelegateSet() {
   11.76 -//        let resultDelegate = TestResultDelegate()
   11.77 -//        let vm = ComposeViewModel(resultDelegate: resultDelegate,
   11.78 -//                                  composeMode: nil,
   11.79 -//                                  prefilledTo: nil,
   11.80 -//                                  originalMessage: nil)
   11.81 -//        guard let testee = vm.resultDelegate else {
   11.82 -//            XCTFail()
   11.83 -//            return
   11.84 -//        }
   11.85 -//        XCTAssertTrue(testee === resultDelegate)
   11.86 -//    }
   11.87 -//
   11.88 -//    func testInit_stateSetupCorrectly() {
   11.89 -//        let mode = ComposeUtil.ComposeMode.replyAll
   11.90 -//        let vm = ComposeViewModel(resultDelegate: nil,
   11.91 -//                                  composeMode: mode,
   11.92 -//                                  prefilledTo: nil,
   11.93 -//                                  originalMessage: nil)
   11.94 -//        guard
   11.95 -//            let testee = vm.state.initData,
   11.96 -//            let testeeMode = vm.state.initData?.composeMode,
   11.97 -//            let stateDelegate = vm.state.delegate else {
   11.98 -//            XCTFail()
   11.99 -//            return
  11.100 -//        }
  11.101 -//        XCTAssertNotNil(testee)
  11.102 -//        XCTAssertEqual(testeeMode, mode)
  11.103 -//        XCTAssertTrue(vm === stateDelegate)
  11.104 -//    }
  11.105 -//
  11.106 -//    // MARK: - Sections
  11.107 -//
  11.108 -//    func testSections_setupCorrectly() {
  11.109 -//        let testOriginalMessage = draftMessage(bccSet: false, attachmentsSet: false)
  11.110 -//        assertSections(forVMIniitaliizedWith: testOriginalMessage,
  11.111 -//                       expectBccWrapperSectionExists: true,
  11.112 -//                       expectAccountSectionExists: false,
  11.113 -//                       expectAttachmentSectionExists: false)
  11.114 -//    }
  11.115 -//
  11.116 -//    func testSections_unwrappedbcc() {
  11.117 -//        let testOriginalMessage = draftMessage(bccSet: true, attachmentsSet: false)
  11.118 -//        assertSections(forVMIniitaliizedWith: testOriginalMessage,
  11.119 -//                       expectBccWrapperSectionExists: false,
  11.120 -//                       expectAccountSectionExists: false,
  11.121 -//                       expectAttachmentSectionExists: false)
  11.122 -//    }
  11.123 -//
  11.124 -//    func testSections_accountSelector() {
  11.125 -//        let testOriginalMessage = draftMessage(bccSet: false, attachmentsSet: false)
  11.126 -//        let secondAccount = SecretTestData().createWorkingAccount(number: 1)
  11.127 -//        secondAccount.save()
  11.128 -//        assertSections(forVMIniitaliizedWith: testOriginalMessage,
  11.129 -//                       expectBccWrapperSectionExists: true,
  11.130 -//                       expectAccountSectionExists: true,
  11.131 -//                       expectAttachmentSectionExists: false)
  11.132 -//    }
  11.133 -//
  11.134 -//    func testSections_attachments() {
  11.135 -//        let testOriginalMessage = draftMessage(bccSet: false, attachmentsSet: true)
  11.136 -//        assertSections(forVMIniitaliizedWith: testOriginalMessage,
  11.137 -//                       expectBccWrapperSectionExists: true,
  11.138 -//                       expectAccountSectionExists: false,
  11.139 -//                       expectAttachmentSectionExists: true)
  11.140 -//    }
  11.141 -//
  11.142 -//    // MARK: - DocumentAttachmentPickerResultDelegate Handling
  11.143 -//
  11.144 -//    func testDocumentAttachmentPickerViewModel() {
  11.145 -//        let testee = vm?.documentAttachmentPickerViewModel()
  11.146 -//        XCTAssertNotNil(testee)
  11.147 -//    }
  11.148 -//
  11.149 -//    func testDidPickDocumentAttachment() {
  11.150 -//        let attachmentSectionSection = 4
  11.151 -//        assert(contentChangedMustBeCalled: false,
  11.152 -//               focusSwitchedMustBeCalled: false,
  11.153 -//               validatedStateChangedMustBeCalled: false,
  11.154 -//               modelChangedMustBeCalled: false,
  11.155 -//               sectionChangedMustBeCalled: true,
  11.156 -//               expectedSection: attachmentSectionSection,
  11.157 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.158 -//               hideSuggestionsMustBeCalled: false,
  11.159 -//               showSuggestionsMustBeCalled: false,
  11.160 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.161 -//               hideMediaAttachmentPickerMustBeCalled: false,
  11.162 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.163 -//               documentAttachmentPickerDonePickerCalled: true,
  11.164 -//               didComposeNewMailMustBeCalled: false,
  11.165 -//               didModifyMessageMustBeCalled: false,
  11.166 -//               didDeleteMessageMustBeCalled: false)
  11.167 -//        let countBefore = vm?.state.nonInlinedAttachments.count ?? -1
  11.168 -//        let att = attachment()
  11.169 -//        vm?.documentAttachmentPickerViewModel(TestDocumentAttachmentPickerViewModel(), didPick: att)
  11.170 -//        let countAfter = vm?.state.nonInlinedAttachments.count ?? -1
  11.171 -//        XCTAssertEqual(countAfter, countBefore + 1)
  11.172 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.173 -//    }
  11.174 -//
  11.175 -//    func testDocumentAttachmentPickerDone() {
  11.176 -//        assert(contentChangedMustBeCalled: false,
  11.177 -//               focusSwitchedMustBeCalled: false,
  11.178 -//               validatedStateChangedMustBeCalled: false,
  11.179 -//               modelChangedMustBeCalled: false,
  11.180 -//               sectionChangedMustBeCalled: false,
  11.181 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.182 -//               hideSuggestionsMustBeCalled: false,
  11.183 -//               showSuggestionsMustBeCalled: false,
  11.184 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.185 -//               hideMediaAttachmentPickerMustBeCalled: false,
  11.186 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.187 -//               documentAttachmentPickerDonePickerCalled: true,
  11.188 -//               didComposeNewMailMustBeCalled: false,
  11.189 -//               didModifyMessageMustBeCalled: false,
  11.190 -//               didDeleteMessageMustBeCalled: false)
  11.191 -//        vm?.documentAttachmentPickerViewModelDidCancel(TestDocumentAttachmentPickerViewModel())
  11.192 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.193 -//    }
  11.194 -//
  11.195 -//    // MARK: - MediaAttachmentPickerProviderViewModelResultDelegate Handling
  11.196 -//
  11.197 -//    func testMediaAttachmentPickerProviderViewModelFactory() {
  11.198 -//        let testee = vm?.mediaAttachmentPickerProviderViewModel()
  11.199 -//        XCTAssertNotNil(testee)
  11.200 -//    }
  11.201 -//
  11.202 -//    func testDidSelectMediaAttachment_image() {
  11.203 -//        let msg = draftMessage()
  11.204 -//        let imageAttachment = attachment(ofType: .inline)
  11.205 -//        msg.replaceAttachments(with: [imageAttachment])
  11.206 -//        assert(originalMessage: msg,
  11.207 -//               contentChangedMustBeCalled: false,
  11.208 -//               focusSwitchedMustBeCalled: false,
  11.209 -//               validatedStateChangedMustBeCalled: false,
  11.210 -//               modelChangedMustBeCalled: false,
  11.211 -//               sectionChangedMustBeCalled: false,
  11.212 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.213 -//               hideSuggestionsMustBeCalled: false,
  11.214 -//               showSuggestionsMustBeCalled: false,
  11.215 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.216 -//               hideMediaAttachmentPickerMustBeCalled: true,
  11.217 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.218 -//               documentAttachmentPickerDonePickerCalled: false,
  11.219 -//               didComposeNewMailMustBeCalled: false,
  11.220 -//               didModifyMessageMustBeCalled: false,
  11.221 -//               didDeleteMessageMustBeCalled: false)
  11.222 -//        let mediaAtt =
  11.223 -//            MediaAttachmentPickerProviderViewModel.MediaAttachment(type: .image,
  11.224 -//                                                                   attachment: imageAttachment)
  11.225 -//        let countBefore = vm?.state.inlinedAttachments.count ?? -1
  11.226 -//        vm?.mediaAttachmentPickerProviderViewModel(
  11.227 -//            TestMediaAttachmentPickerProviderViewModel(resultDelegate: nil),
  11.228 -//            didSelect: mediaAtt)
  11.229 -//        let countAfter = vm?.state.inlinedAttachments.count ?? -1
  11.230 -//        XCTAssertEqual(countAfter, countBefore + 1)
  11.231 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.232 -//
  11.233 -//    }
  11.234 -//
  11.235 -//    func testDidSelectMediaAttachment_video() {
  11.236 -//        let msg = draftMessage()
  11.237 -//        let imageAttachment = attachment(ofType: .inline)
  11.238 -//        msg.replaceAttachments(with: [imageAttachment])
  11.239 -//
  11.240 -//        let attachmentSectionSection = 4
  11.241 -//
  11.242 -//        assert(originalMessage: msg,
  11.243 -//               contentChangedMustBeCalled: false,
  11.244 -//               focusSwitchedMustBeCalled: false,
  11.245 -//               validatedStateChangedMustBeCalled: false,
  11.246 -//               modelChangedMustBeCalled: false,
  11.247 -//               sectionChangedMustBeCalled: true,
  11.248 -//               expectedSection: attachmentSectionSection,
  11.249 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.250 -//               hideSuggestionsMustBeCalled: false,
  11.251 -//               showSuggestionsMustBeCalled: false,
  11.252 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.253 -//               hideMediaAttachmentPickerMustBeCalled: true,
  11.254 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.255 -//               documentAttachmentPickerDonePickerCalled: false,
  11.256 -//               didComposeNewMailMustBeCalled: false,
  11.257 -//               didModifyMessageMustBeCalled: false,
  11.258 -//               didDeleteMessageMustBeCalled: false)
  11.259 -//        let mediaAtt =
  11.260 -//            MediaAttachmentPickerProviderViewModel.MediaAttachment(type: .movie,
  11.261 -//                                                                   attachment: imageAttachment)
  11.262 -//        let countBefore = vm?.state.nonInlinedAttachments.count ?? -1
  11.263 -//        vm?.mediaAttachmentPickerProviderViewModel(
  11.264 -//            TestMediaAttachmentPickerProviderViewModel(resultDelegate: nil),
  11.265 -//            didSelect: mediaAtt)
  11.266 -//        let countAfter = vm?.state.nonInlinedAttachments.count ?? -1
  11.267 -//        XCTAssertEqual(countAfter, countBefore + 1)
  11.268 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.269 -//    }
  11.270 -//
  11.271 -//    func testMediaPickerDidCancel() {
  11.272 -//        assert(contentChangedMustBeCalled: false,
  11.273 -//               focusSwitchedMustBeCalled: false,
  11.274 -//               validatedStateChangedMustBeCalled: false,
  11.275 -//               modelChangedMustBeCalled: false,
  11.276 -//               sectionChangedMustBeCalled: false,
  11.277 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.278 -//               hideSuggestionsMustBeCalled: false,
  11.279 -//               showSuggestionsMustBeCalled: false,
  11.280 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.281 -//               hideMediaAttachmentPickerMustBeCalled: true,
  11.282 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.283 -//               documentAttachmentPickerDonePickerCalled: false,
  11.284 -//               didComposeNewMailMustBeCalled: false,
  11.285 -//               didModifyMessageMustBeCalled: false,
  11.286 -//               didDeleteMessageMustBeCalled: false)
  11.287 -//      vm?.mediaAttachmentPickerProviderViewModelDidCancel(
  11.288 -//        TestMediaAttachmentPickerProviderViewModel(resultDelegate: nil))
  11.289 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.290 -//    }
  11.291 -//
  11.292 -//    // MARK: - BodyCellViewModelResultDelegate handling
  11.293 -//
  11.294 -//    private var bodyVm: BodyCellViewModel {
  11.295 -//        return vm?.bodyVM ?? BodyCellViewModel(resultDelegate: nil)
  11.296 -//    }
  11.297 -//
  11.298 -//    func testBodyVM() {
  11.299 -//        let testee = vm?.bodyVM
  11.300 -//        XCTAssertNotNil(testee)
  11.301 -//    }
  11.302 -//
  11.303 -//    func testBodyCellViewModelUserWantsToAddMedia() {
  11.304 -//        assert(contentChangedMustBeCalled: false,
  11.305 -//               focusSwitchedMustBeCalled: false,
  11.306 -//               validatedStateChangedMustBeCalled: false,
  11.307 -//               modelChangedMustBeCalled: false,
  11.308 -//               sectionChangedMustBeCalled: false,
  11.309 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.310 -//               hideSuggestionsMustBeCalled: false,
  11.311 -//               showSuggestionsMustBeCalled: false,
  11.312 -//               showMediaAttachmentPickerMustBeCalled: true,
  11.313 -//               hideMediaAttachmentPickerMustBeCalled: false,
  11.314 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.315 -//               documentAttachmentPickerDonePickerCalled: false,
  11.316 -//               didComposeNewMailMustBeCalled: false,
  11.317 -//               didModifyMessageMustBeCalled: false,
  11.318 -//               didDeleteMessageMustBeCalled: false)
  11.319 -//        vm?.bodyCellViewModelUserWantsToAddMedia(bodyVm)
  11.320 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.321 -//    }
  11.322 -//
  11.323 -//    func testBodyCellViewModelUserWantsToAddDocument() {
  11.324 -//        assert(contentChangedMustBeCalled: false,
  11.325 -//               focusSwitchedMustBeCalled: false,
  11.326 -//               validatedStateChangedMustBeCalled: false,
  11.327 -//               modelChangedMustBeCalled: false,
  11.328 -//               sectionChangedMustBeCalled: false,
  11.329 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.330 -//               hideSuggestionsMustBeCalled: false,
  11.331 -//               showSuggestionsMustBeCalled: false,
  11.332 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.333 -//               hideMediaAttachmentPickerMustBeCalled: false,
  11.334 -//               showDocumentAttachmentPickerMustBeCalled: true,
  11.335 -//               documentAttachmentPickerDonePickerCalled: false,
  11.336 -//               didComposeNewMailMustBeCalled: false,
  11.337 -//               didModifyMessageMustBeCalled: false,
  11.338 -//               didDeleteMessageMustBeCalled: false)
  11.339 -//        vm?.bodyCellViewModelUserWantsToAddDocument(bodyVm)
  11.340 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.341 -//    }
  11.342 -//
  11.343 -//    func testBodyCellViewModelInlinedAttachmentsChanged_moreAttachments() {
  11.344 -//        assert(contentChangedMustBeCalled: false,
  11.345 -//               focusSwitchedMustBeCalled: false,
  11.346 -//               validatedStateChangedMustBeCalled: false,
  11.347 -//               modelChangedMustBeCalled: false,
  11.348 -//               sectionChangedMustBeCalled: false,
  11.349 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.350 -//               hideSuggestionsMustBeCalled: false,
  11.351 -//               showSuggestionsMustBeCalled: false,
  11.352 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.353 -//               hideMediaAttachmentPickerMustBeCalled: true,
  11.354 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.355 -//               documentAttachmentPickerDonePickerCalled: false,
  11.356 -//               didComposeNewMailMustBeCalled: false,
  11.357 -//               didModifyMessageMustBeCalled: false,
  11.358 -//               didDeleteMessageMustBeCalled: false)
  11.359 -//        let countBefore = vm?.state.inlinedAttachments.count ?? -1
  11.360 -//        vm?.bodyCellViewModel(bodyVm,
  11.361 -//                              inlinedAttachmentsChanged: [attachment()])
  11.362 -//        let countAfter = vm?.state.inlinedAttachments.count ?? -1
  11.363 -//        XCTAssertEqual(countAfter, countBefore + 1)
  11.364 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.365 -//    }
  11.366 -//
  11.367 -//    func testBodyCellViewModelInlinedAttachmentsChanged_lessAttachments() {
  11.368 -//        let msg = draftMessage()
  11.369 -//        let imageAttachment = attachment(ofType: .inline)
  11.370 -//        msg.replaceAttachments(with: [imageAttachment])
  11.371 -//        assert(originalMessage: msg,
  11.372 -//               contentChangedMustBeCalled: false,
  11.373 -//               focusSwitchedMustBeCalled: false,
  11.374 -//               validatedStateChangedMustBeCalled: false,
  11.375 -//               modelChangedMustBeCalled: false,
  11.376 -//               sectionChangedMustBeCalled: false,
  11.377 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.378 -//               hideSuggestionsMustBeCalled: false,
  11.379 -//               showSuggestionsMustBeCalled: false,
  11.380 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.381 -//               hideMediaAttachmentPickerMustBeCalled: true,
  11.382 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.383 -//               documentAttachmentPickerDonePickerCalled: false,
  11.384 -//               didComposeNewMailMustBeCalled: false,
  11.385 -//               didModifyMessageMustBeCalled: false,
  11.386 -//               didDeleteMessageMustBeCalled: false)
  11.387 -//        let lessAttachments = [Attachment]()
  11.388 -//        vm?.bodyCellViewModel(bodyVm,
  11.389 -//                              inlinedAttachmentsChanged: lessAttachments)
  11.390 -//        XCTAssertEqual(vm?.state.inlinedAttachments.count, lessAttachments.count)
  11.391 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.392 -//    }
  11.393 -//
  11.394 -//    func testBodyChangedToPlaintextHtml() {
  11.395 -//        assert(contentChangedMustBeCalled: true,
  11.396 -//               focusSwitchedMustBeCalled: false,
  11.397 -//               validatedStateChangedMustBeCalled: false,
  11.398 -//               modelChangedMustBeCalled: false,
  11.399 -//               sectionChangedMustBeCalled: false,
  11.400 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.401 -//               hideSuggestionsMustBeCalled: false,
  11.402 -//               showSuggestionsMustBeCalled: false,
  11.403 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.404 -//               hideMediaAttachmentPickerMustBeCalled: false,
  11.405 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.406 -//               documentAttachmentPickerDonePickerCalled: false,
  11.407 -//               didComposeNewMailMustBeCalled: false,
  11.408 -//               didModifyMessageMustBeCalled: false,
  11.409 -//               didDeleteMessageMustBeCalled: false)
  11.410 -//        let newPlaintext = "newPlaitext"
  11.411 -//        let newHtml = "<p>fake</p>"
  11.412 -//        vm?.bodyCellViewModel(bodyVm,
  11.413 -//                              bodyChangedToPlaintext: newPlaintext,
  11.414 -//                              html: newHtml)
  11.415 -//        XCTAssertEqual(vm?.state.bodyHtml, newHtml)
  11.416 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.417 -//    }
  11.418 -//
  11.419 -//    // MARK: - SubjectCellViewModelResultDelegate Handling
  11.420 -//
  11.421 -//    private var subjectCellViewModel: SubjectCellViewModel? {
  11.422 -//        return viewmodel(ofType: SubjectCellViewModel.self) as? SubjectCellViewModel
  11.423 -//    }
  11.424 -//
  11.425 -//    func testSubjectCellViewModelDidChangeSubject() {
  11.426 -//        assert(contentChangedMustBeCalled: true,
  11.427 -//               focusSwitchedMustBeCalled: false,
  11.428 -//               validatedStateChangedMustBeCalled: false,
  11.429 -//               modelChangedMustBeCalled: false,
  11.430 -//               sectionChangedMustBeCalled: false,
  11.431 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.432 -//               hideSuggestionsMustBeCalled: false,
  11.433 -//               showSuggestionsMustBeCalled: false,
  11.434 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.435 -//               hideMediaAttachmentPickerMustBeCalled: false,
  11.436 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.437 -//               documentAttachmentPickerDonePickerCalled: false,
  11.438 -//               didComposeNewMailMustBeCalled: false,
  11.439 -//               didModifyMessageMustBeCalled: false,
  11.440 -//               didDeleteMessageMustBeCalled: false)
  11.441 -//        guard let subjectVm = subjectCellViewModel  else {
  11.442 -//            XCTFail()
  11.443 -//            return
  11.444 -//        }
  11.445 -//        let newSubject = "testSubjectCellViewModelDidChangeSubject content"
  11.446 -//        subjectVm.content = newSubject
  11.447 -//        vm?.subjectCellViewModelDidChangeSubject(subjectVm)
  11.448 -//        XCTAssertEqual(vm?.state.subject, newSubject)
  11.449 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.450 -//    }
  11.451 -//
  11.452 -//    // MARK: - AccountCellViewModelResultDelegate handling
  11.453 -//
  11.454 -//    private var accountCellViewModel: AccountCellViewModel? {
  11.455 -//        return viewmodel(ofType: AccountCellViewModel.self) as? AccountCellViewModel
  11.456 -//    }
  11.457 -//
  11.458 -//    func testAccountCellViewModelAccountChangedTo() {
  11.459 -//        let secondAccount = SecretTestData().createWorkingAccount(number: 1)
  11.460 -//        secondAccount.save()
  11.461 -//        assert(contentChangedMustBeCalled: true,
  11.462 -//               focusSwitchedMustBeCalled: false,
  11.463 -//               validatedStateChangedMustBeCalled: true,
  11.464 -//               modelChangedMustBeCalled: false,
  11.465 -//               sectionChangedMustBeCalled: false,
  11.466 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.467 -//               hideSuggestionsMustBeCalled: false,
  11.468 -//               showSuggestionsMustBeCalled: false,
  11.469 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.470 -//               hideMediaAttachmentPickerMustBeCalled: false,
  11.471 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.472 -//               documentAttachmentPickerDonePickerCalled: false,
  11.473 -//               didComposeNewMailMustBeCalled: false,
  11.474 -//               didModifyMessageMustBeCalled: false,
  11.475 -//               didDeleteMessageMustBeCalled: false)
  11.476 -//        guard let accountVm = accountCellViewModel else {
  11.477 -//            XCTFail()
  11.478 -//            return
  11.479 -//        }
  11.480 -//        vm?.accountCellViewModel(accountVm, accountChangedTo: secondAccount)
  11.481 -//        XCTAssertEqual(vm?.state.from, secondAccount.user)
  11.482 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.483 -//    }
  11.484 -//
  11.485 -//    // MARK: - RecipientCellViewModelResultDelegate Handling
  11.486 -//
  11.487 -//    private func recipientCellViewModel(type: RecipientCellViewModel.FieldType) -> RecipientCellViewModel? {
  11.488 -//        guard let sections = vm?.sections else {
  11.489 -//            XCTFail()
  11.490 -//            return nil
  11.491 -//        }
  11.492 -//        for section in sections {
  11.493 -//            for row in section.rows where row is RecipientCellViewModel {
  11.494 -//                if let row = row as? RecipientCellViewModel, row.type == type {
  11.495 -//                    return row
  11.496 -//                }
  11.497 -//            }
  11.498 -//        }
  11.499 -//        return nil
  11.500 -//    }
  11.501 -//
  11.502 -//    func testRecipientCellViewModelDidChangeRecipients_to() {
  11.503 -//        assertRecipientCellViewModelDidChangeRecipients(fieldType: .to)
  11.504 -//    }
  11.505 -//
  11.506 -//    func testRecipientCellViewModelDidChangeRecipients_cc() {
  11.507 -//        assertRecipientCellViewModelDidChangeRecipients(fieldType: .cc)
  11.508 -//    }
  11.509 -//
  11.510 -//    func testRecipientCellViewModelDidChangeRecipients_bcc() {
  11.511 -//        assertRecipientCellViewModelDidChangeRecipients(fieldType: .bcc)
  11.512 -//    }
  11.513 -//
  11.514 -//    func testRecipientCellViewModelDidEndEditing() {
  11.515 -//        assert(contentChangedMustBeCalled: false,
  11.516 -//               focusSwitchedMustBeCalled: true,
  11.517 -//               validatedStateChangedMustBeCalled: true,
  11.518 -//               modelChangedMustBeCalled: false,
  11.519 -//               sectionChangedMustBeCalled: false,
  11.520 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.521 -//               hideSuggestionsMustBeCalled: true,
  11.522 -//               showSuggestionsMustBeCalled: false,
  11.523 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.524 -//               hideMediaAttachmentPickerMustBeCalled: false,
  11.525 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.526 -//               documentAttachmentPickerDonePickerCalled: false,
  11.527 -//               didComposeNewMailMustBeCalled: false,
  11.528 -//               didModifyMessageMustBeCalled: false,
  11.529 -//               didDeleteMessageMustBeCalled: false)
  11.530 -//        guard let recipientVm = recipientCellViewModel(type: .to) else {
  11.531 -//            XCTFail()
  11.532 -//            return
  11.533 -//        }
  11.534 -//        vm?.recipientCellViewModelDidEndEditing(recipientVm)
  11.535 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.536 -//    }
  11.537 -//
  11.538 -//    func testRecipientCellViewModelDidBeginEditing() {
  11.539 -//        assert(contentChangedMustBeCalled: false,
  11.540 -//               focusSwitchedMustBeCalled: false,
  11.541 -//               validatedStateChangedMustBeCalled: false,
  11.542 -//               modelChangedMustBeCalled: false,
  11.543 -//               sectionChangedMustBeCalled: false,
  11.544 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.545 -//               hideSuggestionsMustBeCalled: false,
  11.546 -//               showSuggestionsMustBeCalled: true,
  11.547 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.548 -//               hideMediaAttachmentPickerMustBeCalled: false,
  11.549 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.550 -//               documentAttachmentPickerDonePickerCalled: false,
  11.551 -//               didComposeNewMailMustBeCalled: false,
  11.552 -//               didModifyMessageMustBeCalled: false,
  11.553 -//               didDeleteMessageMustBeCalled: false)
  11.554 -//        guard let recipientVm = recipientCellViewModel(type: .to) else {
  11.555 -//            XCTFail()
  11.556 -//            return
  11.557 -//        }
  11.558 -//        let text = "testRecipientCellViewModelDidBeginEditing text"
  11.559 -//        vm?.recipientCellViewModel(recipientVm, didBeginEditing: text)
  11.560 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.561 -//    }
  11.562 -//
  11.563 -//    func testRecipientCellViewModelTextChanged() {
  11.564 -//        assert(contentChangedMustBeCalled: true,
  11.565 -//               focusSwitchedMustBeCalled: false,
  11.566 -//               validatedStateChangedMustBeCalled: true,
  11.567 -//               modelChangedMustBeCalled: false,
  11.568 -//               sectionChangedMustBeCalled: false,
  11.569 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.570 -//               hideSuggestionsMustBeCalled: false,
  11.571 -//               showSuggestionsMustBeCalled: true,
  11.572 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.573 -//               hideMediaAttachmentPickerMustBeCalled: false,
  11.574 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.575 -//               documentAttachmentPickerDonePickerCalled: false,
  11.576 -//               didComposeNewMailMustBeCalled: false,
  11.577 -//               didModifyMessageMustBeCalled: false,
  11.578 -//               didDeleteMessageMustBeCalled: false)
  11.579 -//        guard let recipientVm = recipientCellViewModel(type: .to) else {
  11.580 -//            XCTFail()
  11.581 -//            return
  11.582 -//        }
  11.583 -//        let text = "testRecipientCellViewModelDidBeginEditing text"
  11.584 -//        vm?.recipientCellViewModel(recipientVm, textChanged: text)
  11.585 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.586 -//    }
  11.587 -//
  11.588 -//    // MARK: - Cancel Actions
  11.589 -//
  11.590 -//    /*
  11.591 -//    func testShowKeepInOutbox() {
  11.592 -//        FolderType.allCases.forEach {
  11.593 -//            assertShowKeepInOutbox(forMessageInfolderOfType: $0)
  11.594 -//        }
  11.595 -//    }
  11.596 -// */
  11.597 -//
  11.598 -//    func testShowCancelActionsv() {
  11.599 -//        let msg = message()
  11.600 -//        assert(originalMessage: msg)
  11.601 -//        guard let testee = vm?.showCancelActions else {
  11.602 -//            XCTFail()
  11.603 -//            return
  11.604 -//        }
  11.605 -//        XCTAssertFalse(testee)
  11.606 -//    }
  11.607 -//
  11.608 -//    func testShowCancelActions_edited() {
  11.609 -//        let msg = message()
  11.610 -//        assert(originalMessage: msg)
  11.611 -//        vm?.state.toRecipients = [Identity(address: "testShow@Cancel.Actions")]
  11.612 -//        guard let testee = vm?.showCancelActions else {
  11.613 -//            XCTFail()
  11.614 -//            return
  11.615 -//        }
  11.616 -//        XCTAssertTrue(testee)
  11.617 -//    }
  11.618 -//
  11.619 -//    func testHandleSaveActionTriggered() {
  11.620 -//        assert(originalMessage: nil,
  11.621 -//               contentChangedMustBeCalled: false,
  11.622 -//               focusSwitchedMustBeCalled: false,
  11.623 -//               validatedStateChangedMustBeCalled: false,
  11.624 -//               modelChangedMustBeCalled: false,
  11.625 -//               sectionChangedMustBeCalled: false,
  11.626 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.627 -//               hideSuggestionsMustBeCalled: false,
  11.628 -//               showSuggestionsMustBeCalled: false,
  11.629 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.630 -//               hideMediaAttachmentPickerMustBeCalled: false,
  11.631 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.632 -//               documentAttachmentPickerDonePickerCalled: false,
  11.633 -//               didComposeNewMailMustBeCalled: false,
  11.634 -//               didModifyMessageMustBeCalled: false,
  11.635 -//               didDeleteMessageMustBeCalled: false)
  11.636 -//
  11.637 -//        let testSubject = UUID().uuidString + "testSubject"
  11.638 -//        vm?.state.subject = testSubject
  11.639 -//
  11.640 -//        vm?.handleSaveActionTriggered()
  11.641 -//
  11.642 -//        guard
  11.643 -//            let draftsFolder = drafts,
  11.644 -//            let testeeDrafted = Message.by(uid: 0,
  11.645 -//                                           folderName: draftsFolder.name,
  11.646 -//                                           accountAddress: account.user.address)
  11.647 -//            else {
  11.648 -//                XCTFail("Message not saved to drafts")
  11.649 -//                return
  11.650 -//        }
  11.651 -//        XCTAssertEqual(testeeDrafted.shortMessage, testSubject)
  11.652 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.653 -//    }
  11.654 -//
  11.655 -//    func testHandleSaveActionTriggered_origOutbox() {
  11.656 -//        let testMessageId = UUID().uuidString + "testHandleSaveActionTriggered"
  11.657 -//        let originalMessage = message(inFolderOfType: .outbox)
  11.658 -//        originalMessage.messageID = testMessageId
  11.659 -//        originalMessage.from = account.user
  11.660 -//
  11.661 -//        assert(originalMessage: originalMessage,
  11.662 -//               contentChangedMustBeCalled: false,
  11.663 -//               focusSwitchedMustBeCalled: false,
  11.664 -//               validatedStateChangedMustBeCalled: false,
  11.665 -//               modelChangedMustBeCalled: false,
  11.666 -//               sectionChangedMustBeCalled: false,
  11.667 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.668 -//               hideSuggestionsMustBeCalled: false,
  11.669 -//               showSuggestionsMustBeCalled: false,
  11.670 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.671 -//               hideMediaAttachmentPickerMustBeCalled: false,
  11.672 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.673 -//               documentAttachmentPickerDonePickerCalled: false,
  11.674 -//               didComposeNewMailMustBeCalled: false,
  11.675 -//               didModifyMessageMustBeCalled: false,
  11.676 -//               didDeleteMessageMustBeCalled: true)
  11.677 -//        vm?.handleSaveActionTriggered()
  11.678 -//        let msgWithTestMessageId = Message.by(uid: originalMessage.uid,
  11.679 -//                                              uuid: originalMessage.uuid,
  11.680 -//                                              folderName: originalMessage.parent.name,
  11.681 -//                                              accountAddress: account.user.address)
  11.682 -//        XCTAssertNil(msgWithTestMessageId,
  11.683 -//                     "original message must be deleted, a copy is safed to drafts")
  11.684 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.685 -//    }
  11.686 -//
  11.687 -//    func testHandleSaveActionTriggered_origDrafts() {
  11.688 -//        let testMessageId = UUID().uuidString + "testHandleSaveActionTriggered"
  11.689 -//        let originalMessage = message(inFolderOfType: .drafts)
  11.690 -//        originalMessage.messageID = testMessageId
  11.691 -//        originalMessage.from = account.user
  11.692 -//
  11.693 -//        assert(originalMessage: originalMessage,
  11.694 -//               contentChangedMustBeCalled: false,
  11.695 -//               focusSwitchedMustBeCalled: false,
  11.696 -//               validatedStateChangedMustBeCalled: false,
  11.697 -//               modelChangedMustBeCalled: false,
  11.698 -//               sectionChangedMustBeCalled: false,
  11.699 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.700 -//               hideSuggestionsMustBeCalled: false,
  11.701 -//               showSuggestionsMustBeCalled: false,
  11.702 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.703 -//               hideMediaAttachmentPickerMustBeCalled: false,
  11.704 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.705 -//               documentAttachmentPickerDonePickerCalled: false,
  11.706 -//               didComposeNewMailMustBeCalled: false,
  11.707 -//               didModifyMessageMustBeCalled: true,
  11.708 -//               didDeleteMessageMustBeCalled: true)
  11.709 -//        vm?.handleSaveActionTriggered()
  11.710 -//        let msgWithTestMessageId = Message.by(uid: originalMessage.uid,
  11.711 -//                                              uuid: originalMessage.uuid,
  11.712 -//                                              folderName: originalMessage.parent.name,
  11.713 -//                                              accountAddress: account.user.address,
  11.714 -//                                              includingDeleted: true)
  11.715 -//        XCTAssertTrue(msgWithTestMessageId?.imapFlags.deleted ?? false,
  11.716 -//                     "The user edited draft. Technically we save a new message, thus the original" +
  11.717 -//            " must be deleted.")
  11.718 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.719 -//    }
  11.720 -//
  11.721 -////    func testHandleDeleteActionTriggered_normal() {
  11.722 -////        assert(originalMessage: nil,
  11.723 -////               contentChangedMustBeCalled: false,
  11.724 -////               focusSwitchedMustBeCalled: false,
  11.725 -////               validatedStateChangedMustBeCalled: false,
  11.726 -////               modelChangedMustBeCalled: false,
  11.727 -////               sectionChangedMustBeCalled: false,
  11.728 -////               colorBatchNeedsUpdateMustBeCalled: false,
  11.729 -////               hideSuggestionsMustBeCalled: false,
  11.730 -////               showSuggestionsMustBeCalled: false,
  11.731 -////               showMediaAttachmentPickerMustBeCalled: false,
  11.732 -////               hideMediaAttachmentPickerMustBeCalled: false,
  11.733 -////               showDocumentAttachmentPickerMustBeCalled: false,
  11.734 -////               documentAttachmentPickerDonePickerCalled: false,
  11.735 -////               didComposeNewMailMustBeCalled: false,
  11.736 -////               didModifyMessageMustBeCalled: false,
  11.737 -////               didDeleteMessageMustBeCalled: false)
  11.738 -////        vm?.handleSaveActionTriggered() //!!!: BUFF: crash, 1570 means that mandatory fields are not filled
  11.739 -////        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.740 -////    }
  11.741 -//
  11.742 -//    func testHandleDeleteActionTriggered_origOutbox() {
  11.743 -//        let testMessageId = UUID().uuidString + "testHandleDeleteActionTriggered_origOutbox"
  11.744 -//        let originalMessage = message(inFolderOfType: .outbox)
  11.745 -//        originalMessage.messageID = testMessageId
  11.746 -//        originalMessage.from = account.user
  11.747 -//
  11.748 -//        assert(originalMessage: originalMessage,
  11.749 -//               contentChangedMustBeCalled: false,
  11.750 -//               focusSwitchedMustBeCalled: false,
  11.751 -//               validatedStateChangedMustBeCalled: false,
  11.752 -//               modelChangedMustBeCalled: false,
  11.753 -//               sectionChangedMustBeCalled: false,
  11.754 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.755 -//               hideSuggestionsMustBeCalled: false,
  11.756 -//               showSuggestionsMustBeCalled: false,
  11.757 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.758 -//               hideMediaAttachmentPickerMustBeCalled: false,
  11.759 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.760 -//               documentAttachmentPickerDonePickerCalled: false,
  11.761 -//               didComposeNewMailMustBeCalled: false,
  11.762 -//               didModifyMessageMustBeCalled: false,
  11.763 -//               didDeleteMessageMustBeCalled: true)
  11.764 -//        vm?.handleSaveActionTriggered()
  11.765 -//        let msgWithTestMessageId = Message.by(uid: originalMessage.uid,
  11.766 -//                                              uuid: originalMessage.uuid,
  11.767 -//                                              folderName: originalMessage.parent.name,
  11.768 -//                                              accountAddress: account.user.address)
  11.769 -//        XCTAssertNil(msgWithTestMessageId,
  11.770 -//                     "original message must be deleted, a copy is safed to drafts")
  11.771 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.772 -//    }
  11.773 -//
  11.774 -//    // MARK: - Suggestions
  11.775 -//
  11.776 -//    func testSuggestViewModel() {
  11.777 -//        let testee = vm?.suggestViewModel()
  11.778 -//        XCTAssertNotNil(testee)
  11.779 -//        XCTAssertTrue(testee?.resultDelegate === vm)
  11.780 -//    }
  11.781 -//
  11.782 -//    // showSuggestions and hideSuggestions are tested altering recipients
  11.783 -//
  11.784 -//    func testShowSuggestionsScrollFocus_nonEmpty() {
  11.785 -//        let expectedSuggestionsVisibility = true
  11.786 -//        assert(contentChangedMustBeCalled: false,
  11.787 -//               focusSwitchedMustBeCalled: false,
  11.788 -//               validatedStateChangedMustBeCalled: false,
  11.789 -//               expectedIsValidated: nil,
  11.790 -//               modelChangedMustBeCalled: false,
  11.791 -//               sectionChangedMustBeCalled: false,
  11.792 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.793 -//               hideSuggestionsMustBeCalled: false,
  11.794 -//               showSuggestionsMustBeCalled: false,
  11.795 -//               suggestionsScrollFocusChangedMustBeCalled: true,
  11.796 -//               expectedNewSuggestionsScrollFocusIsVisible: expectedSuggestionsVisibility,
  11.797 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.798 -//               hideMediaAttachmentPickerMustBeCalled: false,
  11.799 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.800 -//               documentAttachmentPickerDonePickerCalled: false,
  11.801 -//               didComposeNewMailMustBeCalled: false,
  11.802 -//               didModifyMessageMustBeCalled: false,
  11.803 -//               didDeleteMessageMustBeCalled: false)
  11.804 -//        let _ = vm?.suggestViewModel(SuggestViewModel(),
  11.805 -//                                     didToggleVisibilityTo: expectedSuggestionsVisibility)
  11.806 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.807 -//    }
  11.808 -//
  11.809 -//    func testShowSuggestionsScrollFocus_empty() {
  11.810 -//        let expectedSuggestionsVisibility = false
  11.811 -//        assert(contentChangedMustBeCalled: false,
  11.812 -//               focusSwitchedMustBeCalled: false,
  11.813 -//               validatedStateChangedMustBeCalled: false,
  11.814 -//               expectedIsValidated: nil,
  11.815 -//               modelChangedMustBeCalled: false,
  11.816 -//               sectionChangedMustBeCalled: false,
  11.817 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.818 -//               hideSuggestionsMustBeCalled: false,
  11.819 -//               showSuggestionsMustBeCalled: false,
  11.820 -//               suggestionsScrollFocusChangedMustBeCalled: true,
  11.821 -//               expectedNewSuggestionsScrollFocusIsVisible: expectedSuggestionsVisibility,
  11.822 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.823 -//               hideMediaAttachmentPickerMustBeCalled: false,
  11.824 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.825 -//               documentAttachmentPickerDonePickerCalled: false,
  11.826 -//               didComposeNewMailMustBeCalled: false,
  11.827 -//               didModifyMessageMustBeCalled: false,
  11.828 -//               didDeleteMessageMustBeCalled: false)
  11.829 -//        let _ = vm?.suggestViewModel(SuggestViewModel(),
  11.830 -//                                     didToggleVisibilityTo: expectedSuggestionsVisibility)
  11.831 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.832 -//    }
  11.833 -//
  11.834 -//// MARK: - ComposeViewModelStateDelegate Handling
  11.835 -//
  11.836 -//    func testComposeViewModelStateDidChangeValidationStateTo() {
  11.837 -//        let expectedIsValid = true
  11.838 -//        assert(contentChangedMustBeCalled: false,
  11.839 -//               focusSwitchedMustBeCalled: false,
  11.840 -//               validatedStateChangedMustBeCalled: true,
  11.841 -//               expectedIsValidated: expectedIsValid,
  11.842 -//               modelChangedMustBeCalled: false,
  11.843 -//               sectionChangedMustBeCalled: false,
  11.844 -//               colorBatchNeedsUpdateMustBeCalled: false,
  11.845 -//               hideSuggestionsMustBeCalled: false,
  11.846 -//               showSuggestionsMustBeCalled: false,
  11.847 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.848 -//               hideMediaAttachmentPickerMustBeCalled: false,
  11.849 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.850 -//               documentAttachmentPickerDonePickerCalled: false,
  11.851 -//               didComposeNewMailMustBeCalled: false,
  11.852 -//               didModifyMessageMustBeCalled: false,
  11.853 -//               didDeleteMessageMustBeCalled: false)
  11.854 -//        guard let state = vm?.state else {
  11.855 -//            XCTFail()
  11.856 -//            return
  11.857 -//        }
  11.858 -//        vm?.composeViewModelState(state, didChangeValidationStateTo: expectedIsValid)
  11.859 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.860 -//    }
  11.861 -//
  11.862 -//    func testComposeViewModelDidChangePEPRatingTo() {
  11.863 -//        let expectedRating = PEPRating.reliable
  11.864 -//        vm?.state.pEpProtection = true
  11.865 -//        let expectedProtection = vm?.state.pEpProtection ?? false
  11.866 -//        assert(contentChangedMustBeCalled: false,
  11.867 -//               focusSwitchedMustBeCalled: false,
  11.868 -//               validatedStateChangedMustBeCalled: false,
  11.869 -//               modelChangedMustBeCalled: false,
  11.870 -//               sectionChangedMustBeCalled: false,
  11.871 -//               colorBatchNeedsUpdateMustBeCalled: true,
  11.872 -//               expectedRating: expectedRating,
  11.873 -//               expectedProtectionEnabled: expectedProtection,
  11.874 -//               hideSuggestionsMustBeCalled: false,
  11.875 -//               showSuggestionsMustBeCalled: false,
  11.876 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.877 -//               hideMediaAttachmentPickerMustBeCalled: false,
  11.878 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.879 -//               documentAttachmentPickerDonePickerCalled: false,
  11.880 -//               didComposeNewMailMustBeCalled: false,
  11.881 -//               didModifyMessageMustBeCalled: false,
  11.882 -//               didDeleteMessageMustBeCalled: false)
  11.883 -//        guard let state = vm?.state else {
  11.884 -//            XCTFail()
  11.885 -//            return
  11.886 -//        }
  11.887 -//        vm?.composeViewModelState(state, didChangePEPRatingTo: expectedRating)
  11.888 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.889 -//    }
  11.890 -//
  11.891 -//    // MARK: - Delegate Setter Side Effect
  11.892 -//
  11.893 -//    func testDelegateSetter() {
  11.894 -//        let expectedRating = PEPRating.undefined
  11.895 -//        let expectedProtection = true
  11.896 -//        assert(contentChangedMustBeCalled: false,
  11.897 -//               focusSwitchedMustBeCalled: false,
  11.898 -//               validatedStateChangedMustBeCalled: false,
  11.899 -//               modelChangedMustBeCalled: false,
  11.900 -//               sectionChangedMustBeCalled: false,
  11.901 -//               colorBatchNeedsUpdateMustBeCalled: true,
  11.902 -//               expectedRating: expectedRating,
  11.903 -//               expectedProtectionEnabled: expectedProtection,
  11.904 -//               hideSuggestionsMustBeCalled: false,
  11.905 -//               showSuggestionsMustBeCalled: false,
  11.906 -//               showMediaAttachmentPickerMustBeCalled: false,
  11.907 -//               hideMediaAttachmentPickerMustBeCalled: false,
  11.908 -//               showDocumentAttachmentPickerMustBeCalled: false,
  11.909 -//               documentAttachmentPickerDonePickerCalled: false,
  11.910 -//               didComposeNewMailMustBeCalled: false,
  11.911 -//               didModifyMessageMustBeCalled: false,
  11.912 -//               didDeleteMessageMustBeCalled: false)
  11.913 -//        vm?.delegate = testDelegate
  11.914 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  11.915 -//    }
  11.916 -//
  11.917 -//    // MARK: - isAttachmentSection
  11.918 -//
  11.919 -//    func testIsAttachmentSection() {
  11.920 -//        let msgWithAttachment = draftMessage(attachmentsSet: true)
  11.921 -//        assert(originalMessage: msgWithAttachment)
  11.922 -//        guard
  11.923 -//            let lastSection = vm?.sections.last,
  11.924 -//            let numSections = vm?.sections.count else {
  11.925 -//                XCTFail()
  11.926 -//                return
  11.927 -//        }
  11.928 -//        let numRows = lastSection.rows.count
  11.929 -//        let idxPath = IndexPath(row: numRows - 1, section: numSections - 1)
  11.930 -//        XCTAssertTrue(vm?.isAttachmentSection(indexPath: idxPath) ?? false,
  11.931 -//                      "Last row in last section must be attachment")
  11.932 -//    }
  11.933 -//
  11.934 -//    // MARK: - handleRemovedRow
  11.935 -//
  11.936 -//    func testHandleRemovedRow_removeAttachment() {
  11.937 -//        let msgWithAttachments = draftMessage(attachmentsSet: true)
  11.938 -//        msgWithAttachments.appendToAttachments(attachment(ofType: .attachment))
  11.939 -//        msgWithAttachments.save()
  11.940 -//        assert(originalMessage: msgWithAttachments)
  11.941 -//        vm?.state.nonInlinedAttachments = msgWithAttachments.attachments.array
  11.942 -//        guard
  11.943 -//            let lastSectionBefore = vm?.sections.last,
  11.944 -//            let numSectionsBefore = vm?.sections.count,
  11.945 -//            let numNonIlinedAttachmentsBefore = vm?.state.nonInlinedAttachments.count
  11.946 -//            else {
  11.947 -//                XCTFail()
  11.948 -//                return
  11.949 -//        }
  11.950 -//        let numRowsBefore  = lastSectionBefore.rows.count
  11.951 -//        let attachmentIdxPath = IndexPath(row: numRowsBefore - 1,
  11.952 -//                                          section: numSectionsBefore - 1)
  11.953 -//        // Test
  11.954 -//        vm?.handleRemovedRow(at: attachmentIdxPath)
  11.955 -//        guard
  11.956 -//            let lastSectionAfter = vm?.sections.last,
  11.957 -//            let numSectionsAfter = vm?.sections.count,
  11.958 -//            let numNonIlinedAttachmentsAfter = vm?.state.nonInlinedAttachments.count
  11.959 -//            else {
  11.960 -//                XCTFail()
  11.961 -//                return
  11.962 -//        }
  11.963 -//        let numRowsAfter = lastSectionAfter.rows.count
  11.964 -//        XCTAssertEqual(numNonIlinedAttachmentsAfter, numNonIlinedAttachmentsBefore - 1)
  11.965 -//        XCTAssertEqual(numSectionsAfter, numSectionsBefore)
  11.966 -//        XCTAssertEqual(numRowsAfter, numRowsBefore - 1, "Attachment is removed")
  11.967 -//    }
  11.968 -//
  11.969 -//    // MARK: - handleUserClickedSendButton
  11.970 -//
  11.971 -//    func testHandleUserClickedSendButton() {
  11.972 -//        assert()
  11.973 -//        let toRecipient = Identity(address: "testHandleUserClickedSend@Butt.on")
  11.974 -//        vm?.state.toRecipients = [toRecipient]
  11.975 -//        vm?.state.from = account.user
  11.976 -//        let outMsgsBefore = Folder.by(account: account, folderType: .outbox)?
  11.977 -//            .allMessagesNonThreaded()
  11.978 -//            .count ?? -1
  11.979 -//        vm?.handleUserClickedSendButton()
  11.980 -//        let outMsgsAfter = Folder.by(account: account, folderType: .outbox)?
  11.981 -//            .allMessagesNonThreaded()
  11.982 -//            .count ?? -1
  11.983 -//        XCTAssertEqual(outMsgsAfter, outMsgsBefore + 1)
  11.984 -//        XCTAssertGreaterThan(outMsgsAfter, 0)
  11.985 -//    }
  11.986 -//
  11.987 -//    func testHandleUserClickedSendButton_origDraft() {
  11.988 -//        let testMessageId = UUID().uuidString + #function
  11.989 -//        let originalMessage = draftMessage()
  11.990 -//        originalMessage.messageID = testMessageId
  11.991 -//        originalMessage.from = account.user
  11.992 -//        originalMessage.save()
  11.993 -//        XCTAssertNotNil(Message.by(uid: originalMessage.uid,
  11.994 -//                                   uuid: originalMessage.uuid,
  11.995 -//                                   folderName: originalMessage.parent.name,
  11.996 -//                                   accountAddress: account.user.address))
  11.997 -//        assert(originalMessage: originalMessage)
  11.998 -//        let toRecipient = Identity(address: "testHandleUserClickedSend@Butt.on")
  11.999 -//        vm?.state.toRecipients = [toRecipient]
 11.1000 -//        vm?.state.from = account.user
 11.1001 -//        vm?.handleUserClickedSendButton()
 11.1002 -//        guard
 11.1003 -//            let originalDraftedMessageDeleted =
 11.1004 -//            Message.by(uid: originalMessage.uid,
 11.1005 -//                       uuid: originalMessage.uuid,
 11.1006 -//                       folderName: originalMessage.parent.name,
 11.1007 -//                       accountAddress: account.user.address,
 11.1008 -//                       includingDeleted: true)?.imapFlags.deleted
 11.1009 -//            else {
 11.1010 -//                XCTFail()
 11.1011 -//                return
 11.1012 -//        }
 11.1013 -//        XCTAssertTrue(originalDraftedMessageDeleted,
 11.1014 -//                      "original drafted message must be flagged deleted")
 11.1015 -//    }
 11.1016 -//
 11.1017 -//    func testHandleUserClickedSendButton_origOutbox() {
 11.1018 -//        let testMessageId = UUID().uuidString + #function
 11.1019 -//        let originalMessage = message(inFolderOfType: .outbox)
 11.1020 -//        originalMessage.messageID = testMessageId
 11.1021 -//        originalMessage.from = account.user
 11.1022 -//        originalMessage.save()
 11.1023 -//        XCTAssertNotNil(Message.by(uid: originalMessage.uid,
 11.1024 -//                                   uuid: originalMessage.uuid,
 11.1025 -//                                   folderName: originalMessage.parent.name,
 11.1026 -//                                   accountAddress: account.user.address))
 11.1027 -//        assert(originalMessage: originalMessage)
 11.1028 -//        let toRecipient = Identity(address: "testHandleUserClickedSend@Butt.on")
 11.1029 -//        vm?.state.toRecipients = [toRecipient]
 11.1030 -//        vm?.state.from = account.user
 11.1031 -//        vm?.handleUserClickedSendButton()
 11.1032 -//        if let _ = Message.by(uid: originalMessage.uid,
 11.1033 -//                              uuid: originalMessage.uuid,
 11.1034 -//                              folderName: originalMessage.parent.name,
 11.1035 -//                              accountAddress: account.user.address) {
 11.1036 -//            XCTFail("original message must not exist (must be deleted)")
 11.1037 -//            return
 11.1038 -//        }
 11.1039 -//    }
 11.1040 -//
 11.1041 -//    // MARK: - handleUserChangedProtectionStatus
 11.1042 -//
 11.1043 -//    func testHandleUserChangedProtectionStatus_change() {
 11.1044 -//        let expectedRating = PEPRating.undefined
 11.1045 -//        let expectedProtection = false
 11.1046 -//        assert(contentChangedMustBeCalled: false,
 11.1047 -//               focusSwitchedMustBeCalled: false,
 11.1048 -//               validatedStateChangedMustBeCalled: false,
 11.1049 -//               modelChangedMustBeCalled: false,
 11.1050 -//               sectionChangedMustBeCalled: false,
 11.1051 -//               colorBatchNeedsUpdateMustBeCalled: true,
 11.1052 -//               expectedRating: expectedRating,
 11.1053 -//               expectedProtectionEnabled: expectedProtection,
 11.1054 -//               hideSuggestionsMustBeCalled: false,
 11.1055 -//               showSuggestionsMustBeCalled: false,
 11.1056 -//               showMediaAttachmentPickerMustBeCalled: false,
 11.1057 -//               hideMediaAttachmentPickerMustBeCalled: false,
 11.1058 -//               showDocumentAttachmentPickerMustBeCalled: false,
 11.1059 -//               documentAttachmentPickerDonePickerCalled: false,
 11.1060 -//               didComposeNewMailMustBeCalled: false,
 11.1061 -//               didModifyMessageMustBeCalled: false,
 11.1062 -//               didDeleteMessageMustBeCalled: false)
 11.1063 -//        vm?.handleUserChangedProtectionStatus(to: false)
 11.1064 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.1065 -//    }
 11.1066 -//
 11.1067 -//    func testHandleUserChangedProtectionStatus_noChange() {
 11.1068 -//        assert(contentChangedMustBeCalled: false,
 11.1069 -//               focusSwitchedMustBeCalled: false,
 11.1070 -//               validatedStateChangedMustBeCalled: false,
 11.1071 -//               modelChangedMustBeCalled: false,
 11.1072 -//               sectionChangedMustBeCalled: false,
 11.1073 -//               colorBatchNeedsUpdateMustBeCalled: false,
 11.1074 -//               hideSuggestionsMustBeCalled: false,
 11.1075 -//               showSuggestionsMustBeCalled: false,
 11.1076 -//               showMediaAttachmentPickerMustBeCalled: false,
 11.1077 -//               hideMediaAttachmentPickerMustBeCalled: false,
 11.1078 -//               showDocumentAttachmentPickerMustBeCalled: false,
 11.1079 -//               documentAttachmentPickerDonePickerCalled: false,
 11.1080 -//               didComposeNewMailMustBeCalled: false,
 11.1081 -//               didModifyMessageMustBeCalled: false,
 11.1082 -//               didDeleteMessageMustBeCalled: false)
 11.1083 -//        vm?.handleUserChangedProtectionStatus(to: true)
 11.1084 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.1085 -//    }
 11.1086 -//
 11.1087 -//    // MARK: - handleUserSelectedRow
 11.1088 -//
 11.1089 -//    func testHandleUserSelectedRow_ccWrapped_wrapperCellSelected() {
 11.1090 -//        assert(contentChangedMustBeCalled: false,
 11.1091 -//               focusSwitchedMustBeCalled: false,
 11.1092 -//               validatedStateChangedMustBeCalled: false,
 11.1093 -//               modelChangedMustBeCalled: false,
 11.1094 -//               sectionChangedMustBeCalled: true,
 11.1095 -//               colorBatchNeedsUpdateMustBeCalled: false,
 11.1096 -//               hideSuggestionsMustBeCalled: false,
 11.1097 -//               showSuggestionsMustBeCalled: false,
 11.1098 -//               showMediaAttachmentPickerMustBeCalled: false,
 11.1099 -//               hideMediaAttachmentPickerMustBeCalled: false,
 11.1100 -//               showDocumentAttachmentPickerMustBeCalled: false,
 11.1101 -//               documentAttachmentPickerDonePickerCalled: false,
 11.1102 -//               didComposeNewMailMustBeCalled: false,
 11.1103 -//               didModifyMessageMustBeCalled: false,
 11.1104 -//               didDeleteMessageMustBeCalled: false)
 11.1105 -//        guard
 11.1106 -//            let wrapperVM = viewmodel(ofType: WrappedBccViewModel.self),
 11.1107 -//            let idxPath = indexPath(for: wrapperVM) else {
 11.1108 -//                XCTFail("No VM")
 11.1109 -//                return
 11.1110 -//        }
 11.1111 -//        vm?.handleUserSelectedRow(at: idxPath)
 11.1112 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.1113 -//    }
 11.1114 -//
 11.1115 -//    func testHandleUserSelectedRow_ccWrapped_recipientCellSelected() {
 11.1116 -//        assert(contentChangedMustBeCalled: false,
 11.1117 -//               focusSwitchedMustBeCalled: false,
 11.1118 -//               validatedStateChangedMustBeCalled: false,
 11.1119 -//               modelChangedMustBeCalled: false,
 11.1120 -//               sectionChangedMustBeCalled: false,
 11.1121 -//               colorBatchNeedsUpdateMustBeCalled: false,
 11.1122 -//               hideSuggestionsMustBeCalled: false,
 11.1123 -//               showSuggestionsMustBeCalled: false,
 11.1124 -//               showMediaAttachmentPickerMustBeCalled: false,
 11.1125 -//               hideMediaAttachmentPickerMustBeCalled: false,
 11.1126 -//               showDocumentAttachmentPickerMustBeCalled: false,
 11.1127 -//               documentAttachmentPickerDonePickerCalled: false,
 11.1128 -//               didComposeNewMailMustBeCalled: false,
 11.1129 -//               didModifyMessageMustBeCalled: false,
 11.1130 -//               didDeleteMessageMustBeCalled: false)
 11.1131 -//        let idxPathToRecipients = IndexPath(row: 0, section: 0)
 11.1132 -//        vm?.handleUserSelectedRow(at: idxPathToRecipients)
 11.1133 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.1134 -//    }
 11.1135 -//
 11.1136 -//    func testHandleUserSelectedRow_ccUnwrapped_recipientCellSelected() {
 11.1137 -//        let originalMessage = draftMessage()
 11.1138 -//        assert(originalMessage: originalMessage,
 11.1139 -//               contentChangedMustBeCalled: false,
 11.1140 -//               focusSwitchedMustBeCalled: false,
 11.1141 -//               validatedStateChangedMustBeCalled: false,
 11.1142 -//               modelChangedMustBeCalled: false,
 11.1143 -//               sectionChangedMustBeCalled: false,
 11.1144 -//               colorBatchNeedsUpdateMustBeCalled: false,
 11.1145 -//               hideSuggestionsMustBeCalled: false,
 11.1146 -//               showSuggestionsMustBeCalled: false,
 11.1147 -//               showMediaAttachmentPickerMustBeCalled: false,
 11.1148 -//               hideMediaAttachmentPickerMustBeCalled: false,
 11.1149 -//               showDocumentAttachmentPickerMustBeCalled: false,
 11.1150 -//               documentAttachmentPickerDonePickerCalled: false,
 11.1151 -//               didComposeNewMailMustBeCalled: false,
 11.1152 -//               didModifyMessageMustBeCalled: false,
 11.1153 -//               didDeleteMessageMustBeCalled: false)
 11.1154 -//        let idxPathToRecipients = IndexPath(row: 0, section: 0)
 11.1155 -//        vm?.handleUserSelectedRow(at: idxPathToRecipients)
 11.1156 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.1157 -//    }
 11.1158 -//
 11.1159 -//    // MARK: - viewModel(for:)
 11.1160 -//
 11.1161 -//    func testViewModelForIndexPath() {
 11.1162 -//        assert(contentChangedMustBeCalled: false,
 11.1163 -//               focusSwitchedMustBeCalled: false,
 11.1164 -//               validatedStateChangedMustBeCalled: false,
 11.1165 -//               modelChangedMustBeCalled: false,
 11.1166 -//               sectionChangedMustBeCalled: false,
 11.1167 -//               colorBatchNeedsUpdateMustBeCalled: false,
 11.1168 -//               hideSuggestionsMustBeCalled: false,
 11.1169 -//               showSuggestionsMustBeCalled: false,
 11.1170 -//               showMediaAttachmentPickerMustBeCalled: false,
 11.1171 -//               hideMediaAttachmentPickerMustBeCalled: false,
 11.1172 -//               showDocumentAttachmentPickerMustBeCalled: false,
 11.1173 -//               documentAttachmentPickerDonePickerCalled: false,
 11.1174 -//               didComposeNewMailMustBeCalled: false,
 11.1175 -//               didModifyMessageMustBeCalled: false,
 11.1176 -//               didDeleteMessageMustBeCalled: false)
 11.1177 -//        guard
 11.1178 -//            let wrapperVM = viewmodel(ofType: WrappedBccViewModel.self),
 11.1179 -//            let idxPath = indexPath(for: wrapperVM) else {
 11.1180 -//                XCTFail("No VM")
 11.1181 -//                return
 11.1182 -//        }
 11.1183 -//        guard let testee = vm?.viewModel(for: idxPath) else {
 11.1184 -//            XCTFail()
 11.1185 -//            return
 11.1186 -//        }
 11.1187 -//        XCTAssertTrue(testee === wrapperVM)
 11.1188 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.1189 -//    }
 11.1190 -//
 11.1191 -//    func testViewModelForIndexPath_notAlwaysWrapper() {
 11.1192 -//        assert()
 11.1193 -//        guard let wrapperVM = viewmodel(ofType: WrappedBccViewModel.self) else {
 11.1194 -//                XCTFail("No VM")
 11.1195 -//                return
 11.1196 -//        }
 11.1197 -//        let toRecipientsIdxPath = IndexPath(row: 0, section: 0)
 11.1198 -//        guard let testee = vm?.viewModel(for: toRecipientsIdxPath) else {
 11.1199 -//            XCTFail()
 11.1200 -//            return
 11.1201 -//        }
 11.1202 -//        XCTAssertFalse(testee === wrapperVM)
 11.1203 -//    }
 11.1204 -//
 11.1205 -//    // MARK: - beforePickerFocus
 11.1206 -//
 11.1207 -//    func testBeforePickerFocus() {
 11.1208 -//        assert()
 11.1209 -//        guard let bodyVm = vm?.bodyVM else {
 11.1210 -//            XCTFail()
 11.1211 -//            return
 11.1212 -//        }
 11.1213 -//        let beforeFocus = indexPath(for: bodyVm)
 11.1214 -//        let testee = vm?.beforePickerFocus()
 11.1215 -//        XCTAssertEqual(testee, beforeFocus)
 11.1216 -//        let toRecipientsIndPath = IndexPath(row: 0, section: 0)
 11.1217 -//        XCTAssertNotEqual(testee, toRecipientsIndPath)
 11.1218 -//    }
 11.1219 -//
 11.1220 -//    // MARK: - initialFocus
 11.1221 -//
 11.1222 -//    func testInitialFocus_emptyTo() {
 11.1223 -//        let originalMessage = draftMessage()
 11.1224 -//        originalMessage.replaceTo(with: [])
 11.1225 -//        originalMessage.save()
 11.1226 -//        assert(originalMessage: originalMessage,
 11.1227 -//               contentChangedMustBeCalled: false,
 11.1228 -//               focusSwitchedMustBeCalled: false,
 11.1229 -//               validatedStateChangedMustBeCalled: false,
 11.1230 -//               modelChangedMustBeCalled: false,
 11.1231 -//               sectionChangedMustBeCalled: false,
 11.1232 -//               colorBatchNeedsUpdateMustBeCalled: false,
 11.1233 -//               hideSuggestionsMustBeCalled: false,
 11.1234 -//               showSuggestionsMustBeCalled: false,
 11.1235 -//               showMediaAttachmentPickerMustBeCalled: false,
 11.1236 -//               hideMediaAttachmentPickerMustBeCalled: false,
 11.1237 -//               showDocumentAttachmentPickerMustBeCalled: false,
 11.1238 -//               documentAttachmentPickerDonePickerCalled: false,
 11.1239 -//               didComposeNewMailMustBeCalled: false,
 11.1240 -//               didModifyMessageMustBeCalled: false,
 11.1241 -//               didDeleteMessageMustBeCalled: false)
 11.1242 -//        guard let testee = vm?.initialFocus() else {
 11.1243 -//            XCTFail()
 11.1244 -//            return
 11.1245 -//        }
 11.1246 -//        let toRecipientsIndPath = IndexPath(row: 0, section: 0)
 11.1247 -//        XCTAssertEqual(testee, toRecipientsIndPath)
 11.1248 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.1249 -//    }
 11.1250 -//
 11.1251 -//    func testInitialFocus_toSet() {
 11.1252 -//        let originalMessage = draftMessage()
 11.1253 -//        originalMessage.replaceTo(with: [account.user])
 11.1254 -//        originalMessage.save()
 11.1255 -//        assert(originalMessage: originalMessage,
 11.1256 -//               contentChangedMustBeCalled: false,
 11.1257 -//               focusSwitchedMustBeCalled: false,
 11.1258 -//               validatedStateChangedMustBeCalled: false,
 11.1259 -//               modelChangedMustBeCalled: false,
 11.1260 -//               sectionChangedMustBeCalled: false,
 11.1261 -//               colorBatchNeedsUpdateMustBeCalled: false,
 11.1262 -//               hideSuggestionsMustBeCalled: false,
 11.1263 -//               showSuggestionsMustBeCalled: false,
 11.1264 -//               showMediaAttachmentPickerMustBeCalled: false,
 11.1265 -//               hideMediaAttachmentPickerMustBeCalled: false,
 11.1266 -//               showDocumentAttachmentPickerMustBeCalled: false,
 11.1267 -//               documentAttachmentPickerDonePickerCalled: false,
 11.1268 -//               didComposeNewMailMustBeCalled: false,
 11.1269 -//               didModifyMessageMustBeCalled: false,
 11.1270 -//               didDeleteMessageMustBeCalled: false)
 11.1271 -//        guard
 11.1272 -//            let testee = vm?.initialFocus(),
 11.1273 -//            let bodyVM = vm?.bodyVM,
 11.1274 -//            let bodyIndexPath = indexPath(for: bodyVM)
 11.1275 -//            else {
 11.1276 -//                XCTFail()
 11.1277 -//                return
 11.1278 -//        }
 11.1279 -//        let toRecipientsIndexPath = IndexPath(row: 0, section: 0)
 11.1280 -//        XCTAssertEqual(testee, bodyIndexPath)
 11.1281 -//        XCTAssertNotEqual(testee, toRecipientsIndexPath)
 11.1282 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.1283 -//    }
 11.1284 -//
 11.1285 -//    // MARK: - Helper
 11.1286 -//
 11.1287 -//    private func indexPath(for cellViewModel: CellViewModel) -> IndexPath? {
 11.1288 -//        guard let vm = vm else {
 11.1289 -//            XCTFail("No VM")
 11.1290 -//            return nil
 11.1291 -//        }
 11.1292 -//        for s in 0..<vm.sections.count {
 11.1293 -//            let section = vm.sections[s]
 11.1294 -//            for r in 0..<section.rows.count {
 11.1295 -//                let row = section.rows[r]
 11.1296 -//                if row === cellViewModel {
 11.1297 -//                    return IndexPath(row: r, section: s)
 11.1298 -//                }
 11.1299 -//            }
 11.1300 -//        }
 11.1301 -//        return nil
 11.1302 -//    }
 11.1303 -//
 11.1304 -//    private func assureOutboxExists() {
 11.1305 -//        if outbox == nil {
 11.1306 -//            let createe = Folder(name: "outbox", parent: nil, account: account, folderType: .outbox)
 11.1307 -//            createe.save()
 11.1308 -//        }
 11.1309 -//        XCTAssertNotNil(outbox)
 11.1310 -//    }
 11.1311 -//
 11.1312 -//    private func assureDraftsExists() {
 11.1313 -//        if drafts == nil {
 11.1314 -//            let createe = Folder(name: "drafts",
 11.1315 -//                                 parent: nil,
 11.1316 -//                                 account: account,
 11.1317 -//                                 folderType: .drafts)
 11.1318 -//            createe.save()
 11.1319 -//        }
 11.1320 -//        XCTAssertNotNil(drafts)
 11.1321 -//    }
 11.1322 -//
 11.1323 -//    private func assureSentExists() {
 11.1324 -//        if sent == nil {
 11.1325 -//            let createe = Folder(name: "sent",
 11.1326 -//                                 parent: nil,
 11.1327 -//                                 account: account,
 11.1328 -//                                 folderType: .sent)
 11.1329 -//            createe.save()
 11.1330 -//        }
 11.1331 -//        XCTAssertNotNil(sent)
 11.1332 -//    }
 11.1333 -//
 11.1334 -//    private func assertShowKeepInOutbox(forMessageInfolderOfType type: FolderType) {
 11.1335 -//        let msg = message(inFolderOfType: type)
 11.1336 -//        assert(originalMessage: msg,
 11.1337 -//               contentChangedMustBeCalled: false,
 11.1338 -//               focusSwitchedMustBeCalled: false,
 11.1339 -//               validatedStateChangedMustBeCalled: false,
 11.1340 -//               modelChangedMustBeCalled: false,
 11.1341 -//               sectionChangedMustBeCalled: false,
 11.1342 -//               colorBatchNeedsUpdateMustBeCalled: false,
 11.1343 -//               hideSuggestionsMustBeCalled: false,
 11.1344 -//               showSuggestionsMustBeCalled: false,
 11.1345 -//               showMediaAttachmentPickerMustBeCalled: false,
 11.1346 -//               hideMediaAttachmentPickerMustBeCalled: false,
 11.1347 -//               showDocumentAttachmentPickerMustBeCalled: false,
 11.1348 -//               documentAttachmentPickerDonePickerCalled: false,
 11.1349 -//               didComposeNewMailMustBeCalled: false,
 11.1350 -//               didModifyMessageMustBeCalled: false,
 11.1351 -//               didDeleteMessageMustBeCalled: false)
 11.1352 -//        let testee = vm?.showKeepInOutbox
 11.1353 -//        XCTAssertEqual(testee, type == .outbox)
 11.1354 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.1355 -//    }
 11.1356 -//
 11.1357 -//    private func assertRecipientCellViewModelDidChangeRecipients(
 11.1358 -//        fieldType type: RecipientCellViewModel.FieldType) {
 11.1359 -//        let secondAccount = SecretTestData().createWorkingAccount(number: 1)
 11.1360 -//        secondAccount.save()
 11.1361 -//        let om = draftMessage(bccSet: true, attachmentsSet: false)
 11.1362 -//        assert(originalMessage: om,
 11.1363 -//               contentChangedMustBeCalled: false,
 11.1364 -//               focusSwitchedMustBeCalled: false,
 11.1365 -//               validatedStateChangedMustBeCalled: true,
 11.1366 -//               modelChangedMustBeCalled: false,
 11.1367 -//               sectionChangedMustBeCalled: false,
 11.1368 -//               colorBatchNeedsUpdateMustBeCalled: true,
 11.1369 -//               hideSuggestionsMustBeCalled: false,
 11.1370 -//               showSuggestionsMustBeCalled: false,
 11.1371 -//               showMediaAttachmentPickerMustBeCalled: false,
 11.1372 -//               hideMediaAttachmentPickerMustBeCalled: false,
 11.1373 -//               showDocumentAttachmentPickerMustBeCalled: false,
 11.1374 -//               documentAttachmentPickerDonePickerCalled: false,
 11.1375 -//               didComposeNewMailMustBeCalled: false,
 11.1376 -//               didModifyMessageMustBeCalled: false,
 11.1377 -//               didDeleteMessageMustBeCalled: false)
 11.1378 -//        guard let recipientVm = recipientCellViewModel(type: type) else {
 11.1379 -//            XCTFail()
 11.1380 -//            return
 11.1381 -//        }
 11.1382 -//
 11.1383 -//        let beforeCount: Int
 11.1384 -//        switch type {
 11.1385 -//        case .to:
 11.1386 -//            beforeCount = vm?.state.toRecipients.count ?? -2
 11.1387 -//        case .cc:
 11.1388 -//            beforeCount = vm?.state.ccRecipients.count ?? -2
 11.1389 -//        case .bcc:
 11.1390 -//            beforeCount = vm?.state.bccRecipients.count ?? -2
 11.1391 -//        }
 11.1392 -//
 11.1393 -//        let newRecipients = [account.user, secondAccount.user]
 11.1394 -//        vm?.recipientCellViewModel(recipientVm, didChangeRecipients: newRecipients)
 11.1395 -//
 11.1396 -//        let afterCount: Int
 11.1397 -//        switch type {
 11.1398 -//        case .to:
 11.1399 -//            afterCount = vm?.state.toRecipients.count ?? -2
 11.1400 -//        case .cc:
 11.1401 -//            afterCount = vm?.state.ccRecipients.count ?? -2
 11.1402 -//        case .bcc:
 11.1403 -//            afterCount = vm?.state.bccRecipients.count ?? -2
 11.1404 -//        }
 11.1405 -//        XCTAssertNotEqual(afterCount, beforeCount)
 11.1406 -//        XCTAssertEqual(afterCount, newRecipients.count)
 11.1407 -//        waitForExpectations(timeout: UnitTestUtils.asyncWaitTime) // Async calls involved (get pEp color)
 11.1408 -//    }
 11.1409 -//
 11.1410 -//    private func viewmodel(ofType vmType: AnyClass) -> CellViewModel? {
 11.1411 -//        guard let sections = vm?.sections else {
 11.1412 -//            XCTFail()
 11.1413 -//            return nil
 11.1414 -//        }
 11.1415 -//        for section in sections {
 11.1416 -//            guard let vm = section.rows.first else {
 11.1417 -//                continue
 11.1418 -//            }
 11.1419 -//            if type(of: vm) == vmType {
 11.1420 -//                return  section.rows.first
 11.1421 -//            }
 11.1422 -//        }
 11.1423 -//        return nil
 11.1424 -//    }
 11.1425 -//
 11.1426 -//    private func draftMessage(bccSet: Bool = false, attachmentsSet: Bool = false) -> Message {
 11.1427 -//        return message(inFolderOfType: .drafts, bccSet: bccSet, attachmentsSet: attachmentsSet)
 11.1428 -//    }
 11.1429 -//
 11.1430 -//    private func message(inFolderOfType parentType: FolderType = .inbox,
 11.1431 -//                         bccSet: Bool = false,
 11.1432 -//                         attachmentsSet: Bool = false) -> Message {
 11.1433 -//        let folder = Folder(name: "\(parentType)",
 11.1434 -//            parent: parentType == .inbox ? nil : account.firstFolder(ofType: .inbox),
 11.1435 -//            account: account,
 11.1436 -//            folderType: parentType)
 11.1437 -//        folder.save()
 11.1438 -//        let createe = Message(uuid: UUID().uuidString, parentFolder: folder)
 11.1439 -//        if bccSet {
 11.1440 -//            createe.replaceBcc(with: [account.user])
 11.1441 -//        }
 11.1442 -//        if attachmentsSet {
 11.1443 -//            let att = attachment()
 11.1444 -//            createe.replaceAttachments(with: [att])
 11.1445 -//        }
 11.1446 -//        createe.save()
 11.1447 -//        return createe
 11.1448 -//    }
 11.1449 -//
 11.1450 -//    private func attachment(
 11.1451 -//        ofType type: Attachment.ContentDispositionType = .attachment ) -> Attachment {
 11.1452 -//        let imageFileName = "PorpoiseGalaxy_HubbleFraile_960.jpg"
 11.1453 -//        guard
 11.1454 -//            let imageData = TestUtil.loadData(fileName: imageFileName),
 11.1455 -//            let image = UIImage(data: imageData) else {
 11.1456 -//            XCTFail()
 11.1457 -//            return Attachment(data: nil, mimeType: "meh", contentDisposition: .attachment)
 11.1458 -//        }
 11.1459 -//        let createe: Attachment
 11.1460 -//        if type == .inline {
 11.1461 -//            createe = Attachment(data: image.jpegData(compressionQuality: 1.0), mimeType: "image/jpg", contentDisposition: type)
 11.1462 -//            createe.image = image
 11.1463 -//        } else {
 11.1464 -//            createe = Attachment(data: imageData,
 11.1465 -//                                 mimeType: "video/quicktime",
 11.1466 -//                                 contentDisposition: type)
 11.1467 -//        }
 11.1468 -//        createe.fileName = UUID().uuidString
 11.1469 -//        return createe
 11.1470 -//    }
 11.1471 -//
 11.1472 -//    private func assertSections(forVMIniitaliizedWith originalMessage: Message,
 11.1473 -//                                expectBccWrapperSectionExists: Bool = true,
 11.1474 -//                                expectAccountSectionExists: Bool = false,
 11.1475 -//                                expectAttachmentSectionExists: Bool = false) {
 11.1476 -//        let vm = ComposeViewModel(resultDelegate: nil,
 11.1477 -//                                  composeMode: .normal,
 11.1478 -//                                  prefilledTo: nil,
 11.1479 -//                                  originalMessage: originalMessage)
 11.1480 -//        let testee = vm.sections
 11.1481 -//        let recipientSection = 1
 11.1482 -//        let bccWrapperSection = expectBccWrapperSectionExists ? 1 : 0
 11.1483 -//        let accountSelectorSection = expectAccountSectionExists ? 1 : 0
 11.1484 -//        let subjectSection = 1
 11.1485 -//        let bodySection = 1
 11.1486 -//        let attachmentSection = expectAttachmentSectionExists ? 1 : 0
 11.1487 -//        XCTAssertEqual(testee.count,  recipientSection +
 11.1488 -//            bccWrapperSection +
 11.1489 -//            accountSelectorSection +
 11.1490 -//            subjectSection +
 11.1491 -//            bodySection +
 11.1492 -//            attachmentSection)
 11.1493 -//    }
 11.1494 -//
 11.1495 -//    private func assert(composeMode: ComposeUtil.ComposeMode? = nil,
 11.1496 -//                        prefilledTo: Identity? = nil,
 11.1497 -//                        originalMessage: Message? = nil,
 11.1498 -//                        contentChangedMustBeCalled: Bool? = nil,/*TestDelegate realted params*/
 11.1499 -//                        expectedContentChangedIndexPath: IndexPath? = nil,
 11.1500 -//                        focusSwitchedMustBeCalled: Bool? = nil,
 11.1501 -//                        validatedStateChangedMustBeCalled: Bool? = nil,
 11.1502 -//                        expectedIsValidated: Bool? = nil,
 11.1503 -//                        modelChangedMustBeCalled: Bool? = nil,
 11.1504 -//                        sectionChangedMustBeCalled: Bool? = nil,
 11.1505 -//                        expectedSection: Int? = nil,
 11.1506 -//                        colorBatchNeedsUpdateMustBeCalled: Bool? = nil,
 11.1507 -//                        expectedRating: PEPRating? = nil,
 11.1508 -//                        expectedProtectionEnabled: Bool? = nil,
 11.1509 -//                        hideSuggestionsMustBeCalled: Bool? = nil,
 11.1510 -//                        showSuggestionsMustBeCalled: Bool? = nil,
 11.1511 -//                        expectedShowSuggestionsIndexPath: IndexPath? = nil,
 11.1512 -//                        suggestionsScrollFocusChangedMustBeCalled: Bool? = nil,
 11.1513 -//                        expectedNewSuggestionsScrollFocusIsVisible: Bool? = nil,
 11.1514 -//                        showMediaAttachmentPickerMustBeCalled: Bool? = nil,
 11.1515 -//                        hideMediaAttachmentPickerMustBeCalled: Bool? = nil,
 11.1516 -//                        showDocumentAttachmentPickerMustBeCalled: Bool? = nil,
 11.1517 -//                        documentAttachmentPickerDonePickerCalled: Bool? = nil,
 11.1518 -//                        didComposeNewMailMustBeCalled: Bool? = nil,/*TestResultDelegate realted params*/
 11.1519 -//                        didModifyMessageMustBeCalled: Bool? = nil,
 11.1520 -//                        didDeleteMessageMustBeCalled: Bool? = nil) {
 11.1521 -//        // TestDelegate
 11.1522 -//        var expContentChangedCalled: XCTestExpectation? = nil
 11.1523 -//        if let exp = contentChangedMustBeCalled {
 11.1524 -//            expContentChangedCalled =
 11.1525 -//                expectation(description: "expContentChangedCalled")
 11.1526 -//            expContentChangedCalled?.isInverted = !exp
 11.1527 -//        }
 11.1528 -//
 11.1529 -//        var expFocusSwitchedCalled: XCTestExpectation? = nil
 11.1530 -//        if let exp = focusSwitchedMustBeCalled {
 11.1531 -//            expFocusSwitchedCalled =
 11.1532 -//                expectation(description: "expFocusSwitchedCalled")
 11.1533 -//            expFocusSwitchedCalled?.isInverted = !exp
 11.1534 -//        }
 11.1535 -//
 11.1536 -//        var expValidatedStateChangedCalled: XCTestExpectation? = nil
 11.1537 -//        if let exp = validatedStateChangedMustBeCalled {
 11.1538 -//            expValidatedStateChangedCalled =
 11.1539 -//                expectation(description: "expValidatedStateChangedCalled")
 11.1540 -//            expValidatedStateChangedCalled?.isInverted = !exp
 11.1541 -//        }
 11.1542 -//
 11.1543 -//        var expModelChangedCalled: XCTestExpectation? = nil
 11.1544 -//        if let exp = modelChangedMustBeCalled {
 11.1545 -//            expModelChangedCalled =
 11.1546 -//                expectation(description: "expModelChangedCalled")
 11.1547 -//            expModelChangedCalled?.isInverted = !exp
 11.1548 -//        }
 11.1549 -//
 11.1550 -//        var expSectionChangedCalled: XCTestExpectation? = nil
 11.1551 -//        if let exp = sectionChangedMustBeCalled {
 11.1552 -//            expSectionChangedCalled =
 11.1553 -//                expectation(description: "expSectionChangedCalled")
 11.1554 -//            expSectionChangedCalled?.isInverted = !exp
 11.1555 -//            // Overfulfill is espected. When unwrapping CcBcc fields, wrapper section AND
 11.1556 -//            // recipinets section changes.
 11.1557 -//            expSectionChangedCalled?.assertForOverFulfill = false
 11.1558 -//        }
 11.1559 -//
 11.1560 -//        var expColorBatchNeedsUpdateCalled: XCTestExpectation? = nil
 11.1561 -//        if let exp = colorBatchNeedsUpdateMustBeCalled {
 11.1562 -//            expColorBatchNeedsUpdateCalled =
 11.1563 -//                expectation(description: "expColorBatchNeedsUpdateCalled")
 11.1564 -//            expColorBatchNeedsUpdateCalled?.isInverted = !exp
 11.1565 -//        }
 11.1566 -//
 11.1567 -//        var expHideSuggestionsCalled: XCTestExpectation? = nil
 11.1568 -//        if let exp = hideSuggestionsMustBeCalled {
 11.1569 -//            expHideSuggestionsCalled =
 11.1570 -//                expectation(description: "expHideSuggestionsCalled")
 11.1571 -//            expHideSuggestionsCalled?.isInverted = !exp
 11.1572 -//        }
 11.1573 -//
 11.1574 -//        var expSuggestionsScrollFocusChangedCalled: XCTestExpectation? = nil
 11.1575 -//        if let exp = suggestionsScrollFocusChangedMustBeCalled {
 11.1576 -//            expSuggestionsScrollFocusChangedCalled =
 11.1577 -//                expectation(description: "expSuggestionsScrollFocusChangedCalled")
 11.1578 -//            expSuggestionsScrollFocusChangedCalled?.isInverted = !exp
 11.1579 -//        }
 11.1580 -//
 11.1581 -//        var expShowSuggestionsCalled: XCTestExpectation? = nil
 11.1582 -//        if let exp = showSuggestionsMustBeCalled {
 11.1583 -//            expShowSuggestionsCalled =
 11.1584 -//                expectation(description: "expShowSuggestionsCalled")
 11.1585 -//            expShowSuggestionsCalled?.isInverted = !exp
 11.1586 -//        }
 11.1587 -//
 11.1588 -//        var expShowMediaAttachmentPickerCalled: XCTestExpectation? = nil
 11.1589 -//        if let exp = showMediaAttachmentPickerMustBeCalled {
 11.1590 -//            expShowMediaAttachmentPickerCalled =
 11.1591 -//                expectation(description: "expShowMediaAttachmentPickerCalled")
 11.1592 -//            expShowMediaAttachmentPickerCalled?.isInverted = !exp
 11.1593 -//        }
 11.1594 -//
 11.1595 -//        var expHideMediaAttachmentPickerCalled: XCTestExpectation? = nil
 11.1596 -//        if let exp = hideMediaAttachmentPickerMustBeCalled {
 11.1597 -//            expHideMediaAttachmentPickerCalled =
 11.1598 -//                expectation(description: "expHideMediaAttachmentPickerCalled")
 11.1599 -//            expHideMediaAttachmentPickerCalled?.isInverted = !exp
 11.1600 -//        }
 11.1601 -//
 11.1602 -//        var expShowDocumentAttachmentPickerCalled: XCTestExpectation? = nil
 11.1603 -//        if let exp = showDocumentAttachmentPickerMustBeCalled {
 11.1604 -//            expShowDocumentAttachmentPickerCalled =
 11.1605 -//                expectation(description: "expShowDocumentAttachmentPickerCalled")
 11.1606 -//            expShowDocumentAttachmentPickerCalled?.isInverted = !exp
 11.1607 -//        }
 11.1608 -//
 11.1609 -//        var expDocumentAttachmentPickerDonePickerCalled: XCTestExpectation? = nil
 11.1610 -//        if let exp = documentAttachmentPickerDonePickerCalled {
 11.1611 -//            expDocumentAttachmentPickerDonePickerCalled =
 11.1612 -//                expectation(description: "expDocumentAttachmentPickerDonePickerCalled")
 11.1613 -//            expDocumentAttachmentPickerDonePickerCalled?.isInverted = !exp
 11.1614 -//        }
 11.1615 -//
 11.1616 -//        testDelegate =
 11.1617 -//            TestDelegate(expContentChangedCalled: expContentChangedCalled,
 11.1618 -//                         expectedContentChangedIndexPath: expectedContentChangedIndexPath,
 11.1619 -//                         expFocusSwitchedCalled: expFocusSwitchedCalled,
 11.1620 -//                         expValidatedStateChangedCalled: expValidatedStateChangedCalled,
 11.1621 -//                         expectedIsValidated: expectedIsValidated,
 11.1622 -//                         expModelChangedCalled: expModelChangedCalled,
 11.1623 -//                         expSectionChangedCalled: expSectionChangedCalled,
 11.1624 -//                         expectedSection: expectedSection,
 11.1625 -//                         expColorBatchNeedsUpdateCalled: nil,
 11.1626 -//                         expectedRating: expectedRating,
 11.1627 -//                         expectedProtectionEnabled: expectedProtectionEnabled,
 11.1628 -//                         expHideSuggestionsCalled: expHideSuggestionsCalled,
 11.1629 -//                         expShowSuggestionsCalled: expShowSuggestionsCalled,
 11.1630 -//                         expSuggestionsScrollFocusChangedCalled: expSuggestionsScrollFocusChangedCalled,
 11.1631 -//                         expectedScrollFocus: expectedNewSuggestionsScrollFocusIsVisible,
 11.1632 -//                         expectedShowSuggestionsIndexPath: expectedShowSuggestionsIndexPath,
 11.1633 -//                         expShowMediaAttachmentPickerCalled: expShowMediaAttachmentPickerCalled,
 11.1634 -//                         expHideMediaAttachmentPickerCalled: expHideMediaAttachmentPickerCalled,
 11.1635 -//                         expShowDocumentAttachmentPickerCalled: expShowDocumentAttachmentPickerCalled,
 11.1636 -//                         expDocumentAttachmentPickerDonePickerCalled:
 11.1637 -//                expDocumentAttachmentPickerDonePickerCalled)
 11.1638 -//
 11.1639 -//        // TestResultDelegate
 11.1640 -//
 11.1641 -//        var expDidComposeNewMailCalled: XCTestExpectation? = nil
 11.1642 -//        if let exp = didComposeNewMailMustBeCalled {
 11.1643 -//            expDidComposeNewMailCalled =
 11.1644 -//                expectation(description: "expDidComposeNewMailCalled")
 11.1645 -//            expDidComposeNewMailCalled?.isInverted = !exp
 11.1646 -//        }
 11.1647 -//
 11.1648 -//        var expDidModifyMessageCalled: XCTestExpectation? = nil
 11.1649 -//        if let exp = didModifyMessageMustBeCalled {
 11.1650 -//            expDidModifyMessageCalled =
 11.1651 -//                expectation(description: "expDidModifyMessageCalled")
 11.1652 -//            expDidModifyMessageCalled?.isInverted = !exp
 11.1653 -//        }
 11.1654 -//
 11.1655 -//        var expDidDeleteMessageCalled: XCTestExpectation? = nil
 11.1656 -//        if let exp = didDeleteMessageMustBeCalled {
 11.1657 -//            expDidDeleteMessageCalled =
 11.1658 -//                expectation(description: "expDidDeleteMessageCalled")
 11.1659 -//            expDidDeleteMessageCalled?.isInverted = !exp
 11.1660 -//            expDidDeleteMessageCalled?.assertForOverFulfill = false
 11.1661 -//        }
 11.1662 -//
 11.1663 -//        testResultDelegate =
 11.1664 -//            TestResultDelegate(expDidComposeNewMailCalled: expDidComposeNewMailCalled,
 11.1665 -//                               expDidModifyMessageCalled: expDidModifyMessageCalled,
 11.1666 -//                               expDidDeleteMessageCalled: expDidDeleteMessageCalled)
 11.1667 -//        vm = ComposeViewModel(resultDelegate: testResultDelegate,
 11.1668 -//                              composeMode: composeMode,
 11.1669 -//                              prefilledTo: prefilledTo,
 11.1670 -//                              originalMessage: originalMessage)
 11.1671 -//        vm?.delegate = testDelegate
 11.1672 -//        // Set _after_ the delegate is set because at this point we are not interested in callbacks
 11.1673 -//        // triggered by setting the delegate.
 11.1674 -//        testDelegate?.expColorBatchNeedsUpdateCalled = expColorBatchNeedsUpdateCalled
 11.1675 -//    }
 11.1676 -//
 11.1677 -//    private class TestResultDelegate: ComposeViewModelResultDelegate {
 11.1678 -//        let expDidComposeNewMailCalled: XCTestExpectation?
 11.1679 -//        let expDidModifyMessageCalled: XCTestExpectation?
 11.1680 -//        let expDidDeleteMessageCalled: XCTestExpectation?
 11.1681 -//
 11.1682 -//        init(expDidComposeNewMailCalled: XCTestExpectation? = nil,
 11.1683 -//             expDidModifyMessageCalled: XCTestExpectation? = nil,
 11.1684 -//             expDidDeleteMessageCalled: XCTestExpectation? = nil) {
 11.1685 -//            self.expDidComposeNewMailCalled = expDidComposeNewMailCalled
 11.1686 -//            self.expDidModifyMessageCalled = expDidModifyMessageCalled
 11.1687 -//            self.expDidDeleteMessageCalled = expDidDeleteMessageCalled
 11.1688 -//
 11.1689 -//        }
 11.1690 -//
 11.1691 -//        func composeViewModelDidComposeNewMail(message: Message) {
 11.1692 -//            guard let exp = expDidComposeNewMailCalled else {
 11.1693 -//                // We ignore called or not
 11.1694 -//                return
 11.1695 -//            }
 11.1696 -//            exp.fulfill()
 11.1697 -//        }
 11.1698 -//
 11.1699 -//        func composeViewModelDidModifyMessage(message: Message) {
 11.1700 -//            guard let exp = expDidModifyMessageCalled else {
 11.1701 -//                // We ignore called or not
 11.1702 -//                return
 11.1703 -//            }
 11.1704 -//            exp.fulfill()
 11.1705 -//        }
 11.1706 -//
 11.1707 -//        func composeViewModelDidDeleteMessage(message: Message) {
 11.1708 -//            guard let exp = expDidDeleteMessageCalled else {
 11.1709 -//                // We ignore called or not
 11.1710 -//                return
 11.1711 -//            }
 11.1712 -//            exp.fulfill()
 11.1713 -//        }
 11.1714 -//    }
 11.1715 -//
 11.1716 -//    private class TestDelegate:  ComposeViewModelDelegate {
 11.1717 -//        let expContentChangedCalled: XCTestExpectation?
 11.1718 -//        let expectedContentChangedIndexPath: IndexPath?
 11.1719 -//
 11.1720 -//        let expFocusSwitchedCalled: XCTestExpectation?
 11.1721 -//
 11.1722 -//        let expValidatedStateChangedCalled: XCTestExpectation?
 11.1723 -//        let expectedIsValidated: Bool?
 11.1724 -//
 11.1725 -//        let expModelChangedCalled: XCTestExpectation?
 11.1726 -//
 11.1727 -//        let expSectionChangedCalled: XCTestExpectation?
 11.1728 -//        let expectedSection: Int?
 11.1729 -//
 11.1730 -//        var expColorBatchNeedsUpdateCalled: XCTestExpectation?
 11.1731 -//        let expectedRating: PEPRating?
 11.1732 -//        let expectedProtectionEnabled: Bool?
 11.1733 -//
 11.1734 -//        let expHideSuggestionsCalled: XCTestExpectation?
 11.1735 -//
 11.1736 -//        let expShowSuggestionsCalled: XCTestExpectation?
 11.1737 -//        let expectedShowSuggestionsIndexPath: IndexPath?
 11.1738 -//
 11.1739 -//        let expSuggestionsScrollFocusChangedCalled: XCTestExpectation?
 11.1740 -//        let expectedScrollFocus: Bool?
 11.1741 -//
 11.1742 -//        let expShowMediaAttachmentPickerCalled: XCTestExpectation?
 11.1743 -//
 11.1744 -//        let expHideMediaAttachmentPickerCalled: XCTestExpectation?
 11.1745 -//
 11.1746 -//        let expShowDocumentAttachmentPickerCalled: XCTestExpectation?
 11.1747 -//
 11.1748 -//        let expDocumentAttachmentPickerDonePickerCalled: XCTestExpectation?
 11.1749 -//
 11.1750 -//        init(expContentChangedCalled: XCTestExpectation?,
 11.1751 -//             expectedContentChangedIndexPath: IndexPath?,
 11.1752 -//             expFocusSwitchedCalled: XCTestExpectation?,
 11.1753 -//             expValidatedStateChangedCalled: XCTestExpectation?,
 11.1754 -//             expectedIsValidated: Bool?,
 11.1755 -//             expModelChangedCalled: XCTestExpectation?,
 11.1756 -//             expSectionChangedCalled: XCTestExpectation?,
 11.1757 -//             expectedSection: Int?,
 11.1758 -//             expColorBatchNeedsUpdateCalled: XCTestExpectation?,
 11.1759 -//             expectedRating: PEPRating?,
 11.1760 -//             expectedProtectionEnabled: Bool?,
 11.1761 -//             expHideSuggestionsCalled: XCTestExpectation?,
 11.1762 -//             expShowSuggestionsCalled: XCTestExpectation?,
 11.1763 -//             expSuggestionsScrollFocusChangedCalled: XCTestExpectation?,
 11.1764 -//             expectedScrollFocus: Bool?,
 11.1765 -//             expectedShowSuggestionsIndexPath: IndexPath?,
 11.1766 -//             expShowMediaAttachmentPickerCalled: XCTestExpectation?,
 11.1767 -//             expHideMediaAttachmentPickerCalled: XCTestExpectation?,
 11.1768 -//             expShowDocumentAttachmentPickerCalled: XCTestExpectation?,
 11.1769 -//             expDocumentAttachmentPickerDonePickerCalled: XCTestExpectation?) {
 11.1770 -//            self.expContentChangedCalled = expContentChangedCalled
 11.1771 -//            self.expectedContentChangedIndexPath = expectedContentChangedIndexPath
 11.1772 -//            self.expFocusSwitchedCalled = expFocusSwitchedCalled
 11.1773 -//            self.expValidatedStateChangedCalled = expValidatedStateChangedCalled
 11.1774 -//            self.expectedIsValidated = expectedIsValidated
 11.1775 -//            self.expModelChangedCalled = expModelChangedCalled
 11.1776 -//            self.expSectionChangedCalled = expSectionChangedCalled
 11.1777 -//            self.expectedSection = expectedSection
 11.1778 -//            self.expColorBatchNeedsUpdateCalled =  expColorBatchNeedsUpdateCalled
 11.1779 -//            self.expectedRating = expectedRating
 11.1780 -//            self.expectedProtectionEnabled = expectedProtectionEnabled
 11.1781 -//            self.expHideSuggestionsCalled = expHideSuggestionsCalled
 11.1782 -//            self.expShowSuggestionsCalled = expShowSuggestionsCalled
 11.1783 -//            self.expSuggestionsScrollFocusChangedCalled = expSuggestionsScrollFocusChangedCalled
 11.1784 -//            self.expectedScrollFocus = expectedScrollFocus
 11.1785 -//            self.expectedShowSuggestionsIndexPath = expectedShowSuggestionsIndexPath
 11.1786 -//            self.expShowMediaAttachmentPickerCalled = expShowMediaAttachmentPickerCalled
 11.1787 -//            self.expHideMediaAttachmentPickerCalled = expHideMediaAttachmentPickerCalled
 11.1788 -//            self.expShowDocumentAttachmentPickerCalled = expShowDocumentAttachmentPickerCalled
 11.1789 -//            self.expDocumentAttachmentPickerDonePickerCalled =
 11.1790 -//            expDocumentAttachmentPickerDonePickerCalled
 11.1791 -//        }
 11.1792 -//
 11.1793 -//        func contentChanged(inRowAt indexPath: IndexPath) {
 11.1794 -//            guard let exp = expContentChangedCalled else {
 11.1795 -//                // We ignore called or not
 11.1796 -//                return
 11.1797 -//            }
 11.1798 -//            exp.fulfill()
 11.1799 -//            if let expected = expectedContentChangedIndexPath {
 11.1800 -//                XCTAssertEqual(indexPath, expected)
 11.1801 -//            }
 11.1802 -//        }
 11.1803 -//
 11.1804 -//        func focusSwitched() {
 11.1805 -//            guard let exp = expFocusSwitchedCalled else {
 11.1806 -//                // We ignore called or not
 11.1807 -//                return
 11.1808 -//            }
 11.1809 -//            exp.fulfill()
 11.1810 -//        }
 11.1811 -//
 11.1812 -//        func validatedStateChanged(to isValidated: Bool) {
 11.1813 -//            guard let exp = expValidatedStateChangedCalled else {
 11.1814 -//                // We ignore called or not
 11.1815 -//                return
 11.1816 -//            }
 11.1817 -//            exp.fulfill()
 11.1818 -//            if let expected = expectedIsValidated {
 11.1819 -//                XCTAssertEqual(isValidated, expected)
 11.1820 -//            }
 11.1821 -//        }
 11.1822 -//
 11.1823 -//        func modelChanged() {
 11.1824 -//            guard let exp = expModelChangedCalled else {
 11.1825 -//                // We ignore called or not
 11.1826 -//                return
 11.1827 -//            }
 11.1828 -//            exp.fulfill()
 11.1829 -//        }
 11.1830 -//
 11.1831 -//        func sectionChanged(section: Int) {
 11.1832 -//            guard let exp = expSectionChangedCalled else {
 11.1833 -//                // We ignore called or not
 11.1834 -//                return
 11.1835 -//            }
 11.1836 -//            exp.fulfill()
 11.1837 -//            if let expected = expectedSection {
 11.1838 -//                XCTAssertEqual(section, expected)
 11.1839 -//            }
 11.1840 -//        }
 11.1841 -//
 11.1842 -//        func colorBatchNeedsUpdate(for rating: PEPRating, protectionEnabled: Bool) {
 11.1843 -//            guard let exp = expColorBatchNeedsUpdateCalled else {
 11.1844 -//                // We ignore called or not
 11.1845 -//                return
 11.1846 -//            }
 11.1847 -//            exp.fulfill()
 11.1848 -//            if let expected = expectedRating {
 11.1849 -//                XCTAssertEqual(rating, expected)
 11.1850 -//            }
 11.1851 -//            if let expectedProtection = expectedProtectionEnabled {
 11.1852 -//                XCTAssertEqual(protectionEnabled, expectedProtection)
 11.1853 -//            }
 11.1854 -//        }
 11.1855 -//
 11.1856 -//        func hideSuggestions() {
 11.1857 -//            guard let exp = expHideSuggestionsCalled else {
 11.1858 -//                // We ignore called or not
 11.1859 -//                return
 11.1860 -//            }
 11.1861 -//            exp.fulfill()
 11.1862 -//        }
 11.1863 -//
 11.1864 -//        func showSuggestions(forRowAt indexPath: IndexPath) {
 11.1865 -//            guard let exp = expShowSuggestionsCalled else {
 11.1866 -//                // We ignore called or not
 11.1867 -//                return
 11.1868 -//            }
 11.1869 -//            exp.fulfill()
 11.1870 -//            if let expected = expectedShowSuggestionsIndexPath {
 11.1871 -//                XCTAssertEqual(indexPath, expected)
 11.1872 -//            }
 11.1873 -//        }
 11.1874 -//
 11.1875 -//        func suggestions(haveScrollFocus: Bool) {
 11.1876 -//            guard let exp = expSuggestionsScrollFocusChangedCalled else {
 11.1877 -//                // We ignore called or not
 11.1878 -//                return
 11.1879 -//            }
 11.1880 -//            exp.fulfill()
 11.1881 -//            if let expected = expectedScrollFocus {
 11.1882 -//                XCTAssertEqual(haveScrollFocus, expected)
 11.1883 -//            }
 11.1884 -//        }
 11.1885 -//
 11.1886 -//        func showMediaAttachmentPicker() {
 11.1887 -//            guard let exp = expShowMediaAttachmentPickerCalled else {
 11.1888 -//                // We ignore called or not
 11.1889 -//                return
 11.1890 -//            }
 11.1891 -//            exp.fulfill()
 11.1892 -//        }
 11.1893 -//
 11.1894 -//        func hideMediaAttachmentPicker() {
 11.1895 -//            guard let exp = expHideMediaAttachmentPickerCalled else {
 11.1896 -//                // We ignore called or not
 11.1897 -//                return
 11.1898 -//            }
 11.1899 -//            exp.fulfill()
 11.1900 -//        }
 11.1901 -//
 11.1902 -//        func showDocumentAttachmentPicker() {
 11.1903 -//            guard let exp = expShowDocumentAttachmentPickerCalled else {
 11.1904 -//                // We ignore called or not
 11.1905 -//                return
 11.1906 -//            }
 11.1907 -//            exp.fulfill()
 11.1908 -//        }
 11.1909 -//
 11.1910 -//        func documentAttachmentPickerDone() {
 11.1911 -//            guard let exp = expDocumentAttachmentPickerDonePickerCalled else {
 11.1912 -//                // We ignore called or not
 11.1913 -//                return
 11.1914 -//            }
 11.1915 -//            exp.fulfill()
 11.1916 -//        }
 11.1917 -//    }
 11.1918 -//
 11.1919 -//    // MARK: - DocumentAttachmentPickerViewModel
 11.1920 -//    class TestDocumentAttachmentPickerViewModel: DocumentAttachmentPickerViewModel {} // Dummy to pass something
 11.1921 -//
 11.1922 -//    // MARK: - MediaAttachmentPickerProviderViewModel
 11.1923 -//    class TestMediaAttachmentPickerProviderViewModel: MediaAttachmentPickerProviderViewModel {} // Dummy to pass something
 11.1924 -//}
 11.1925 +import XCTest
 11.1926 +
 11.1927 +@testable import pEpForiOS
 11.1928 +@testable import MessageModel
 11.1929 +import PEPObjCAdapterFramework
 11.1930 +
 11.1931 +class ComposeViewModelTest: CoreDataDrivenTestBase {
 11.1932 +    private var testDelegate: TestDelegate?
 11.1933 +    private var testResultDelegate: TestResultDelegate?
 11.1934 +    var vm: ComposeViewModel?
 11.1935 +    var outbox: Folder? {
 11.1936 +        return account.firstFolder(ofType: .outbox)
 11.1937 +    }
 11.1938 +    var drafts: Folder? {
 11.1939 +        return account.firstFolder(ofType: .drafts)
 11.1940 +    }
 11.1941 +    var sent: Folder? {
 11.1942 +        return account.firstFolder(ofType: .sent)
 11.1943 +    }
 11.1944 +
 11.1945 +    override func setUp() {
 11.1946 +        super.setUp()
 11.1947 +        vm = ComposeViewModel(resultDelegate: nil,
 11.1948 +                              composeMode: nil,
 11.1949 +                              prefilledTo: nil,
 11.1950 +                              originalMessage: nil)
 11.1951 +        assureOutboxExists()
 11.1952 +        assureDraftsExists()
 11.1953 +        assureSentExists()
 11.1954 +    }
 11.1955 +
 11.1956 +    // MARK: - Test the Test Helper
 11.1957 +
 11.1958 +    func testAssertHelperTest_doNothing_noCallback() {
 11.1959 +        assert(contentChangedMustBeCalled: false,
 11.1960 +               focusSwitchedMustBeCalled: false,
 11.1961 +               validatedStateChangedMustBeCalled: false,
 11.1962 +               modelChangedMustBeCalled: false,
 11.1963 +               sectionChangedMustBeCalled: false,
 11.1964 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.1965 +               hideSuggestionsMustBeCalled: false,
 11.1966 +               showSuggestionsMustBeCalled: false,
 11.1967 +               showMediaAttachmentPickerMustBeCalled: false,
 11.1968 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.1969 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.1970 +               documentAttachmentPickerDonePickerCalled: false,
 11.1971 +               didComposeNewMailMustBeCalled: false,
 11.1972 +               didModifyMessageMustBeCalled: false,
 11.1973 +               didDeleteMessageMustBeCalled: false)
 11.1974 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.1975 +    }
 11.1976 +
 11.1977 +    // MARK: - init
 11.1978 +
 11.1979 +    func testInit_resultDelegateSet() {
 11.1980 +        let resultDelegate = TestResultDelegate()
 11.1981 +        let vm = ComposeViewModel(resultDelegate: resultDelegate,
 11.1982 +                                  composeMode: nil,
 11.1983 +                                  prefilledTo: nil,
 11.1984 +                                  originalMessage: nil)
 11.1985 +        guard let testee = vm.resultDelegate else {
 11.1986 +            XCTFail()
 11.1987 +            return
 11.1988 +        }
 11.1989 +        XCTAssertTrue(testee === resultDelegate)
 11.1990 +    }
 11.1991 +
 11.1992 +    func testInit_stateSetupCorrectly() {
 11.1993 +        let mode = ComposeUtil.ComposeMode.replyAll
 11.1994 +        let vm = ComposeViewModel(resultDelegate: nil,
 11.1995 +                                  composeMode: mode,
 11.1996 +                                  prefilledTo: nil,
 11.1997 +                                  originalMessage: nil)
 11.1998 +        guard
 11.1999 +            let testee = vm.state.initData,
 11.2000 +            let testeeMode = vm.state.initData?.composeMode,
 11.2001 +            let stateDelegate = vm.state.delegate else {
 11.2002 +            XCTFail()
 11.2003 +            return
 11.2004 +        }
 11.2005 +        XCTAssertNotNil(testee)
 11.2006 +        XCTAssertEqual(testeeMode, mode)
 11.2007 +        XCTAssertTrue(vm === stateDelegate)
 11.2008 +    }
 11.2009 +
 11.2010 +    // MARK: - Sections
 11.2011 +
 11.2012 +    func testSections_setupCorrectly() {
 11.2013 +        let testOriginalMessage = draftMessage(bccSet: false, attachmentsSet: false)
 11.2014 +        assertSections(forVMIniitaliizedWith: testOriginalMessage,
 11.2015 +                       expectBccWrapperSectionExists: true,
 11.2016 +                       expectAccountSectionExists: false,
 11.2017 +                       expectAttachmentSectionExists: false)
 11.2018 +    }
 11.2019 +
 11.2020 +    func testSections_unwrappedbcc() {
 11.2021 +        let testOriginalMessage = draftMessage(bccSet: true, attachmentsSet: false)
 11.2022 +        assertSections(forVMIniitaliizedWith: testOriginalMessage,
 11.2023 +                       expectBccWrapperSectionExists: false,
 11.2024 +                       expectAccountSectionExists: false,
 11.2025 +                       expectAttachmentSectionExists: false)
 11.2026 +    }
 11.2027 +
 11.2028 +    func testSections_accountSelector() {
 11.2029 +        let testOriginalMessage = draftMessage(bccSet: false, attachmentsSet: false)
 11.2030 +        let secondAccount = SecretTestData().createWorkingAccount(number: 1)
 11.2031 +        secondAccount.save()
 11.2032 +        assertSections(forVMIniitaliizedWith: testOriginalMessage,
 11.2033 +                       expectBccWrapperSectionExists: true,
 11.2034 +                       expectAccountSectionExists: true,
 11.2035 +                       expectAttachmentSectionExists: false)
 11.2036 +    }
 11.2037 +
 11.2038 +    func testSections_attachments() {
 11.2039 +        let testOriginalMessage = draftMessage(bccSet: false, attachmentsSet: true)
 11.2040 +        assertSections(forVMIniitaliizedWith: testOriginalMessage,
 11.2041 +                       expectBccWrapperSectionExists: true,
 11.2042 +                       expectAccountSectionExists: false,
 11.2043 +                       expectAttachmentSectionExists: true)
 11.2044 +    }
 11.2045 +
 11.2046 +    // MARK: - DocumentAttachmentPickerResultDelegate Handling
 11.2047 +
 11.2048 +    func testDocumentAttachmentPickerViewModel() {
 11.2049 +        let testee = vm?.documentAttachmentPickerViewModel()
 11.2050 +        XCTAssertNotNil(testee)
 11.2051 +    }
 11.2052 +
 11.2053 +    func testDidPickDocumentAttachment() {
 11.2054 +        let attachmentSectionSection = 4
 11.2055 +        assert(contentChangedMustBeCalled: false,
 11.2056 +               focusSwitchedMustBeCalled: false,
 11.2057 +               validatedStateChangedMustBeCalled: false,
 11.2058 +               modelChangedMustBeCalled: false,
 11.2059 +               sectionChangedMustBeCalled: true,
 11.2060 +               expectedSection: attachmentSectionSection,
 11.2061 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2062 +               hideSuggestionsMustBeCalled: false,
 11.2063 +               showSuggestionsMustBeCalled: false,
 11.2064 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2065 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2066 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2067 +               documentAttachmentPickerDonePickerCalled: true,
 11.2068 +               didComposeNewMailMustBeCalled: false,
 11.2069 +               didModifyMessageMustBeCalled: false,
 11.2070 +               didDeleteMessageMustBeCalled: false)
 11.2071 +        let countBefore = vm?.state.nonInlinedAttachments.count ?? -1
 11.2072 +        let att = attachment()
 11.2073 +        vm?.documentAttachmentPickerViewModel(TestDocumentAttachmentPickerViewModel(), didPick: att)
 11.2074 +        let countAfter = vm?.state.nonInlinedAttachments.count ?? -1
 11.2075 +        XCTAssertEqual(countAfter, countBefore + 1)
 11.2076 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2077 +    }
 11.2078 +
 11.2079 +    func testDocumentAttachmentPickerDone() {
 11.2080 +        assert(contentChangedMustBeCalled: false,
 11.2081 +               focusSwitchedMustBeCalled: false,
 11.2082 +               validatedStateChangedMustBeCalled: false,
 11.2083 +               modelChangedMustBeCalled: false,
 11.2084 +               sectionChangedMustBeCalled: false,
 11.2085 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2086 +               hideSuggestionsMustBeCalled: false,
 11.2087 +               showSuggestionsMustBeCalled: false,
 11.2088 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2089 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2090 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2091 +               documentAttachmentPickerDonePickerCalled: true,
 11.2092 +               didComposeNewMailMustBeCalled: false,
 11.2093 +               didModifyMessageMustBeCalled: false,
 11.2094 +               didDeleteMessageMustBeCalled: false)
 11.2095 +        vm?.documentAttachmentPickerViewModelDidCancel(TestDocumentAttachmentPickerViewModel())
 11.2096 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2097 +    }
 11.2098 +
 11.2099 +    // MARK: - MediaAttachmentPickerProviderViewModelResultDelegate Handling
 11.2100 +
 11.2101 +    func testMediaAttachmentPickerProviderViewModelFactory() {
 11.2102 +        let testee = vm?.mediaAttachmentPickerProviderViewModel()
 11.2103 +        XCTAssertNotNil(testee)
 11.2104 +    }
 11.2105 +
 11.2106 +    func testDidSelectMediaAttachment_image() {
 11.2107 +        let msg = draftMessage()
 11.2108 +        let imageAttachment = attachment(ofType: .inline)
 11.2109 +        msg.replaceAttachments(with: [imageAttachment])
 11.2110 +        assert(originalMessage: msg,
 11.2111 +               contentChangedMustBeCalled: false,
 11.2112 +               focusSwitchedMustBeCalled: false,
 11.2113 +               validatedStateChangedMustBeCalled: false,
 11.2114 +               modelChangedMustBeCalled: false,
 11.2115 +               sectionChangedMustBeCalled: false,
 11.2116 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2117 +               hideSuggestionsMustBeCalled: false,
 11.2118 +               showSuggestionsMustBeCalled: false,
 11.2119 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2120 +               hideMediaAttachmentPickerMustBeCalled: true,
 11.2121 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2122 +               documentAttachmentPickerDonePickerCalled: false,
 11.2123 +               didComposeNewMailMustBeCalled: false,
 11.2124 +               didModifyMessageMustBeCalled: false,
 11.2125 +               didDeleteMessageMustBeCalled: false)
 11.2126 +        let mediaAtt =
 11.2127 +            MediaAttachmentPickerProviderViewModel.MediaAttachment(type: .image,
 11.2128 +                                                                   attachment: imageAttachment)
 11.2129 +        let countBefore = vm?.state.inlinedAttachments.count ?? -1
 11.2130 +        vm?.mediaAttachmentPickerProviderViewModel(
 11.2131 +            TestMediaAttachmentPickerProviderViewModel(resultDelegate: nil),
 11.2132 +            didSelect: mediaAtt)
 11.2133 +        let countAfter = vm?.state.inlinedAttachments.count ?? -1
 11.2134 +        XCTAssertEqual(countAfter, countBefore + 1)
 11.2135 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2136 +
 11.2137 +    }
 11.2138 +
 11.2139 +    func testDidSelectMediaAttachment_video() {
 11.2140 +        let msg = draftMessage()
 11.2141 +        let imageAttachment = attachment(ofType: .inline)
 11.2142 +        msg.replaceAttachments(with: [imageAttachment])
 11.2143 +
 11.2144 +        let attachmentSectionSection = 4
 11.2145 +
 11.2146 +        assert(originalMessage: msg,
 11.2147 +               contentChangedMustBeCalled: false,
 11.2148 +               focusSwitchedMustBeCalled: false,
 11.2149 +               validatedStateChangedMustBeCalled: false,
 11.2150 +               modelChangedMustBeCalled: false,
 11.2151 +               sectionChangedMustBeCalled: true,
 11.2152 +               expectedSection: attachmentSectionSection,
 11.2153 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2154 +               hideSuggestionsMustBeCalled: false,
 11.2155 +               showSuggestionsMustBeCalled: false,
 11.2156 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2157 +               hideMediaAttachmentPickerMustBeCalled: true,
 11.2158 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2159 +               documentAttachmentPickerDonePickerCalled: false,
 11.2160 +               didComposeNewMailMustBeCalled: false,
 11.2161 +               didModifyMessageMustBeCalled: false,
 11.2162 +               didDeleteMessageMustBeCalled: false)
 11.2163 +        let mediaAtt =
 11.2164 +            MediaAttachmentPickerProviderViewModel.MediaAttachment(type: .movie,
 11.2165 +                                                                   attachment: imageAttachment)
 11.2166 +        let countBefore = vm?.state.nonInlinedAttachments.count ?? -1
 11.2167 +        vm?.mediaAttachmentPickerProviderViewModel(
 11.2168 +            TestMediaAttachmentPickerProviderViewModel(resultDelegate: nil),
 11.2169 +            didSelect: mediaAtt)
 11.2170 +        let countAfter = vm?.state.nonInlinedAttachments.count ?? -1
 11.2171 +        XCTAssertEqual(countAfter, countBefore + 1)
 11.2172 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2173 +    }
 11.2174 +
 11.2175 +    func testMediaPickerDidCancel() {
 11.2176 +        assert(contentChangedMustBeCalled: false,
 11.2177 +               focusSwitchedMustBeCalled: false,
 11.2178 +               validatedStateChangedMustBeCalled: false,
 11.2179 +               modelChangedMustBeCalled: false,
 11.2180 +               sectionChangedMustBeCalled: false,
 11.2181 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2182 +               hideSuggestionsMustBeCalled: false,
 11.2183 +               showSuggestionsMustBeCalled: false,
 11.2184 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2185 +               hideMediaAttachmentPickerMustBeCalled: true,
 11.2186 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2187 +               documentAttachmentPickerDonePickerCalled: false,
 11.2188 +               didComposeNewMailMustBeCalled: false,
 11.2189 +               didModifyMessageMustBeCalled: false,
 11.2190 +               didDeleteMessageMustBeCalled: false)
 11.2191 +      vm?.mediaAttachmentPickerProviderViewModelDidCancel(
 11.2192 +        TestMediaAttachmentPickerProviderViewModel(resultDelegate: nil))
 11.2193 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2194 +    }
 11.2195 +
 11.2196 +    // MARK: - BodyCellViewModelResultDelegate handling
 11.2197 +
 11.2198 +    private var bodyVm: BodyCellViewModel {
 11.2199 +        return vm?.bodyVM ?? BodyCellViewModel(resultDelegate: nil)
 11.2200 +    }
 11.2201 +
 11.2202 +    func testBodyVM() {
 11.2203 +        let testee = vm?.bodyVM
 11.2204 +        XCTAssertNotNil(testee)
 11.2205 +    }
 11.2206 +
 11.2207 +    func testBodyCellViewModelUserWantsToAddMedia() {
 11.2208 +        assert(contentChangedMustBeCalled: false,
 11.2209 +               focusSwitchedMustBeCalled: false,
 11.2210 +               validatedStateChangedMustBeCalled: false,
 11.2211 +               modelChangedMustBeCalled: false,
 11.2212 +               sectionChangedMustBeCalled: false,
 11.2213 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2214 +               hideSuggestionsMustBeCalled: false,
 11.2215 +               showSuggestionsMustBeCalled: false,
 11.2216 +               showMediaAttachmentPickerMustBeCalled: true,
 11.2217 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2218 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2219 +               documentAttachmentPickerDonePickerCalled: false,
 11.2220 +               didComposeNewMailMustBeCalled: false,
 11.2221 +               didModifyMessageMustBeCalled: false,
 11.2222 +               didDeleteMessageMustBeCalled: false)
 11.2223 +        vm?.bodyCellViewModelUserWantsToAddMedia(bodyVm)
 11.2224 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2225 +    }
 11.2226 +
 11.2227 +    func testBodyCellViewModelUserWantsToAddDocument() {
 11.2228 +        assert(contentChangedMustBeCalled: false,
 11.2229 +               focusSwitchedMustBeCalled: false,
 11.2230 +               validatedStateChangedMustBeCalled: false,
 11.2231 +               modelChangedMustBeCalled: false,
 11.2232 +               sectionChangedMustBeCalled: false,
 11.2233 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2234 +               hideSuggestionsMustBeCalled: false,
 11.2235 +               showSuggestionsMustBeCalled: false,
 11.2236 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2237 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2238 +               showDocumentAttachmentPickerMustBeCalled: true,
 11.2239 +               documentAttachmentPickerDonePickerCalled: false,
 11.2240 +               didComposeNewMailMustBeCalled: false,
 11.2241 +               didModifyMessageMustBeCalled: false,
 11.2242 +               didDeleteMessageMustBeCalled: false)
 11.2243 +        vm?.bodyCellViewModelUserWantsToAddDocument(bodyVm)
 11.2244 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2245 +    }
 11.2246 +
 11.2247 +    func testBodyCellViewModelInlinedAttachmentsChanged_moreAttachments() {
 11.2248 +        assert(contentChangedMustBeCalled: false,
 11.2249 +               focusSwitchedMustBeCalled: false,
 11.2250 +               validatedStateChangedMustBeCalled: false,
 11.2251 +               modelChangedMustBeCalled: false,
 11.2252 +               sectionChangedMustBeCalled: false,
 11.2253 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2254 +               hideSuggestionsMustBeCalled: false,
 11.2255 +               showSuggestionsMustBeCalled: false,
 11.2256 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2257 +               hideMediaAttachmentPickerMustBeCalled: true,
 11.2258 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2259 +               documentAttachmentPickerDonePickerCalled: false,
 11.2260 +               didComposeNewMailMustBeCalled: false,
 11.2261 +               didModifyMessageMustBeCalled: false,
 11.2262 +               didDeleteMessageMustBeCalled: false)
 11.2263 +        let countBefore = vm?.state.inlinedAttachments.count ?? -1
 11.2264 +        vm?.bodyCellViewModel(bodyVm,
 11.2265 +                              inlinedAttachmentsChanged: [attachment()])
 11.2266 +        let countAfter = vm?.state.inlinedAttachments.count ?? -1
 11.2267 +        XCTAssertEqual(countAfter, countBefore + 1)
 11.2268 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2269 +    }
 11.2270 +
 11.2271 +    func testBodyCellViewModelInlinedAttachmentsChanged_lessAttachments() {
 11.2272 +        let msg = draftMessage()
 11.2273 +        let imageAttachment = attachment(ofType: .inline)
 11.2274 +        msg.replaceAttachments(with: [imageAttachment])
 11.2275 +        assert(originalMessage: msg,
 11.2276 +               contentChangedMustBeCalled: false,
 11.2277 +               focusSwitchedMustBeCalled: false,
 11.2278 +               validatedStateChangedMustBeCalled: false,
 11.2279 +               modelChangedMustBeCalled: false,
 11.2280 +               sectionChangedMustBeCalled: false,
 11.2281 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2282 +               hideSuggestionsMustBeCalled: false,
 11.2283 +               showSuggestionsMustBeCalled: false,
 11.2284 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2285 +               hideMediaAttachmentPickerMustBeCalled: true,
 11.2286 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2287 +               documentAttachmentPickerDonePickerCalled: false,
 11.2288 +               didComposeNewMailMustBeCalled: false,
 11.2289 +               didModifyMessageMustBeCalled: false,
 11.2290 +               didDeleteMessageMustBeCalled: false)
 11.2291 +        let lessAttachments = [Attachment]()
 11.2292 +        vm?.bodyCellViewModel(bodyVm,
 11.2293 +                              inlinedAttachmentsChanged: lessAttachments)
 11.2294 +        XCTAssertEqual(vm?.state.inlinedAttachments.count, lessAttachments.count)
 11.2295 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2296 +    }
 11.2297 +
 11.2298 +    func testBodyChangedToPlaintextHtml() {
 11.2299 +        assert(contentChangedMustBeCalled: true,
 11.2300 +               focusSwitchedMustBeCalled: false,
 11.2301 +               validatedStateChangedMustBeCalled: false,
 11.2302 +               modelChangedMustBeCalled: false,
 11.2303 +               sectionChangedMustBeCalled: false,
 11.2304 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2305 +               hideSuggestionsMustBeCalled: false,
 11.2306 +               showSuggestionsMustBeCalled: false,
 11.2307 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2308 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2309 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2310 +               documentAttachmentPickerDonePickerCalled: false,
 11.2311 +               didComposeNewMailMustBeCalled: false,
 11.2312 +               didModifyMessageMustBeCalled: false,
 11.2313 +               didDeleteMessageMustBeCalled: false)
 11.2314 +        let newPlaintext = "newPlaitext"
 11.2315 +        let newHtml = "<p>fake</p>"
 11.2316 +        vm?.bodyCellViewModel(bodyVm,
 11.2317 +                              bodyChangedToPlaintext: newPlaintext,
 11.2318 +                              html: newHtml)
 11.2319 +        XCTAssertEqual(vm?.state.bodyHtml, newHtml)
 11.2320 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2321 +    }
 11.2322 +
 11.2323 +    // MARK: - SubjectCellViewModelResultDelegate Handling
 11.2324 +
 11.2325 +    private var subjectCellViewModel: SubjectCellViewModel? {
 11.2326 +        return viewmodel(ofType: SubjectCellViewModel.self) as? SubjectCellViewModel
 11.2327 +    }
 11.2328 +
 11.2329 +    func testSubjectCellViewModelDidChangeSubject() {
 11.2330 +        assert(contentChangedMustBeCalled: true,
 11.2331 +               focusSwitchedMustBeCalled: false,
 11.2332 +               validatedStateChangedMustBeCalled: false,
 11.2333 +               modelChangedMustBeCalled: false,
 11.2334 +               sectionChangedMustBeCalled: false,
 11.2335 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2336 +               hideSuggestionsMustBeCalled: false,
 11.2337 +               showSuggestionsMustBeCalled: false,
 11.2338 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2339 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2340 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2341 +               documentAttachmentPickerDonePickerCalled: false,
 11.2342 +               didComposeNewMailMustBeCalled: false,
 11.2343 +               didModifyMessageMustBeCalled: false,
 11.2344 +               didDeleteMessageMustBeCalled: false)
 11.2345 +        guard let subjectVm = subjectCellViewModel  else {
 11.2346 +            XCTFail()
 11.2347 +            return
 11.2348 +        }
 11.2349 +        let newSubject = "testSubjectCellViewModelDidChangeSubject content"
 11.2350 +        subjectVm.content = newSubject
 11.2351 +        vm?.subjectCellViewModelDidChangeSubject(subjectVm)
 11.2352 +        XCTAssertEqual(vm?.state.subject, newSubject)
 11.2353 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2354 +    }
 11.2355 +
 11.2356 +    // MARK: - AccountCellViewModelResultDelegate handling
 11.2357 +
 11.2358 +    private var accountCellViewModel: AccountCellViewModel? {
 11.2359 +        return viewmodel(ofType: AccountCellViewModel.self) as? AccountCellViewModel
 11.2360 +    }
 11.2361 +
 11.2362 +    func testAccountCellViewModelAccountChangedTo() {
 11.2363 +        let secondAccount = SecretTestData().createWorkingAccount(number: 1)
 11.2364 +        secondAccount.save()
 11.2365 +        assert(contentChangedMustBeCalled: true,
 11.2366 +               focusSwitchedMustBeCalled: false,
 11.2367 +               validatedStateChangedMustBeCalled: true,
 11.2368 +               modelChangedMustBeCalled: false,
 11.2369 +               sectionChangedMustBeCalled: false,
 11.2370 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2371 +               hideSuggestionsMustBeCalled: false,
 11.2372 +               showSuggestionsMustBeCalled: false,
 11.2373 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2374 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2375 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2376 +               documentAttachmentPickerDonePickerCalled: false,
 11.2377 +               didComposeNewMailMustBeCalled: false,
 11.2378 +               didModifyMessageMustBeCalled: false,
 11.2379 +               didDeleteMessageMustBeCalled: false)
 11.2380 +        guard let accountVm = accountCellViewModel else {
 11.2381 +            XCTFail()
 11.2382 +            return
 11.2383 +        }
 11.2384 +        vm?.accountCellViewModel(accountVm, accountChangedTo: secondAccount)
 11.2385 +        XCTAssertEqual(vm?.state.from, secondAccount.user)
 11.2386 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2387 +    }
 11.2388 +
 11.2389 +    // MARK: - RecipientCellViewModelResultDelegate Handling
 11.2390 +
 11.2391 +    private func recipientCellViewModel(type: RecipientCellViewModel.FieldType) -> RecipientCellViewModel? {
 11.2392 +        guard let sections = vm?.sections else {
 11.2393 +            XCTFail()
 11.2394 +            return nil
 11.2395 +        }
 11.2396 +        for section in sections {
 11.2397 +            for row in section.rows where row is RecipientCellViewModel {
 11.2398 +                if let row = row as? RecipientCellViewModel, row.type == type {
 11.2399 +                    return row
 11.2400 +                }
 11.2401 +            }
 11.2402 +        }
 11.2403 +        return nil
 11.2404 +    }
 11.2405 +
 11.2406 +    func testRecipientCellViewModelDidChangeRecipients_to() {
 11.2407 +        assertRecipientCellViewModelDidChangeRecipients(fieldType: .to)
 11.2408 +    }
 11.2409 +
 11.2410 +    func testRecipientCellViewModelDidChangeRecipients_cc() {
 11.2411 +        assertRecipientCellViewModelDidChangeRecipients(fieldType: .cc)
 11.2412 +    }
 11.2413 +
 11.2414 +    func testRecipientCellViewModelDidChangeRecipients_bcc() {
 11.2415 +        assertRecipientCellViewModelDidChangeRecipients(fieldType: .bcc)
 11.2416 +    }
 11.2417 +
 11.2418 +    func testRecipientCellViewModelDidEndEditing() {
 11.2419 +        assert(contentChangedMustBeCalled: false,
 11.2420 +               focusSwitchedMustBeCalled: true,
 11.2421 +               validatedStateChangedMustBeCalled: true,
 11.2422 +               modelChangedMustBeCalled: false,
 11.2423 +               sectionChangedMustBeCalled: false,
 11.2424 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2425 +               hideSuggestionsMustBeCalled: true,
 11.2426 +               showSuggestionsMustBeCalled: false,
 11.2427 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2428 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2429 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2430 +               documentAttachmentPickerDonePickerCalled: false,
 11.2431 +               didComposeNewMailMustBeCalled: false,
 11.2432 +               didModifyMessageMustBeCalled: false,
 11.2433 +               didDeleteMessageMustBeCalled: false)
 11.2434 +        guard let recipientVm = recipientCellViewModel(type: .to) else {
 11.2435 +            XCTFail()
 11.2436 +            return
 11.2437 +        }
 11.2438 +        vm?.recipientCellViewModelDidEndEditing(recipientVm)
 11.2439 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2440 +    }
 11.2441 +
 11.2442 +    func testRecipientCellViewModelDidBeginEditing() {
 11.2443 +        assert(contentChangedMustBeCalled: false,
 11.2444 +               focusSwitchedMustBeCalled: false,
 11.2445 +               validatedStateChangedMustBeCalled: false,
 11.2446 +               modelChangedMustBeCalled: false,
 11.2447 +               sectionChangedMustBeCalled: false,
 11.2448 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2449 +               hideSuggestionsMustBeCalled: false,
 11.2450 +               showSuggestionsMustBeCalled: true,
 11.2451 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2452 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2453 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2454 +               documentAttachmentPickerDonePickerCalled: false,
 11.2455 +               didComposeNewMailMustBeCalled: false,
 11.2456 +               didModifyMessageMustBeCalled: false,
 11.2457 +               didDeleteMessageMustBeCalled: false)
 11.2458 +        guard let recipientVm = recipientCellViewModel(type: .to) else {
 11.2459 +            XCTFail()
 11.2460 +            return
 11.2461 +        }
 11.2462 +        let text = "testRecipientCellViewModelDidBeginEditing text"
 11.2463 +        vm?.recipientCellViewModel(recipientVm, didBeginEditing: text)
 11.2464 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2465 +    }
 11.2466 +
 11.2467 +    func testRecipientCellViewModelTextChanged() {
 11.2468 +        assert(contentChangedMustBeCalled: true,
 11.2469 +               focusSwitchedMustBeCalled: false,
 11.2470 +               validatedStateChangedMustBeCalled: true,
 11.2471 +               modelChangedMustBeCalled: false,
 11.2472 +               sectionChangedMustBeCalled: false,
 11.2473 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2474 +               hideSuggestionsMustBeCalled: false,
 11.2475 +               showSuggestionsMustBeCalled: true,
 11.2476 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2477 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2478 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2479 +               documentAttachmentPickerDonePickerCalled: false,
 11.2480 +               didComposeNewMailMustBeCalled: false,
 11.2481 +               didModifyMessageMustBeCalled: false,
 11.2482 +               didDeleteMessageMustBeCalled: false)
 11.2483 +        guard let recipientVm = recipientCellViewModel(type: .to) else {
 11.2484 +            XCTFail()
 11.2485 +            return
 11.2486 +        }
 11.2487 +        let text = "testRecipientCellViewModelDidBeginEditing text"
 11.2488 +        vm?.recipientCellViewModel(recipientVm, textChanged: text)
 11.2489 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2490 +    }
 11.2491 +
 11.2492 +    // MARK: - Cancel Actions
 11.2493 +
 11.2494 +    /*
 11.2495 +    func testShowKeepInOutbox() {
 11.2496 +        FolderType.allCases.forEach {
 11.2497 +            assertShowKeepInOutbox(forMessageInfolderOfType: $0)
 11.2498 +        }
 11.2499 +    }
 11.2500 + */
 11.2501 +
 11.2502 +    func testShowCancelActionsv() {
 11.2503 +        let msg = message()
 11.2504 +        assert(originalMessage: msg)
 11.2505 +        guard let testee = vm?.showCancelActions else {
 11.2506 +            XCTFail()
 11.2507 +            return
 11.2508 +        }
 11.2509 +        XCTAssertFalse(testee)
 11.2510 +    }
 11.2511 +
 11.2512 +    func testShowCancelActions_edited() {
 11.2513 +        let msg = message()
 11.2514 +        assert(originalMessage: msg)
 11.2515 +        vm?.state.toRecipients = [Identity(address: "testShow@Cancel.Actions")]
 11.2516 +        guard let testee = vm?.showCancelActions else {
 11.2517 +            XCTFail()
 11.2518 +            return
 11.2519 +        }
 11.2520 +        XCTAssertTrue(testee)
 11.2521 +    }
 11.2522 +
 11.2523 +    func testHandleSaveActionTriggered() {
 11.2524 +        assert(originalMessage: nil,
 11.2525 +               contentChangedMustBeCalled: false,
 11.2526 +               focusSwitchedMustBeCalled: false,
 11.2527 +               validatedStateChangedMustBeCalled: false,
 11.2528 +               modelChangedMustBeCalled: false,
 11.2529 +               sectionChangedMustBeCalled: false,
 11.2530 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2531 +               hideSuggestionsMustBeCalled: false,
 11.2532 +               showSuggestionsMustBeCalled: false,
 11.2533 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2534 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2535 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2536 +               documentAttachmentPickerDonePickerCalled: false,
 11.2537 +               didComposeNewMailMustBeCalled: false,
 11.2538 +               didModifyMessageMustBeCalled: false,
 11.2539 +               didDeleteMessageMustBeCalled: false)
 11.2540 +
 11.2541 +        let testSubject = UUID().uuidString + "testSubject"
 11.2542 +        vm?.state.subject = testSubject
 11.2543 +
 11.2544 +        vm?.handleSaveActionTriggered()
 11.2545 +
 11.2546 +        guard
 11.2547 +            let draftsFolder = drafts,
 11.2548 +            let testeeDrafted = Message.by(uid: 0,
 11.2549 +                                           folderName: draftsFolder.name,
 11.2550 +                                           accountAddress: account.user.address)
 11.2551 +            else {
 11.2552 +                XCTFail("Message not saved to drafts")
 11.2553 +                return
 11.2554 +        }
 11.2555 +        XCTAssertEqual(testeeDrafted.shortMessage, testSubject)
 11.2556 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2557 +    }
 11.2558 +
 11.2559 +    func testHandleSaveActionTriggered_origOutbox() {
 11.2560 +        let testMessageId = UUID().uuidString + "testHandleSaveActionTriggered"
 11.2561 +        let originalMessage = message(inFolderOfType: .outbox)
 11.2562 +        originalMessage.messageID = testMessageId
 11.2563 +        originalMessage.from = account.user
 11.2564 +
 11.2565 +        assert(originalMessage: originalMessage,
 11.2566 +               contentChangedMustBeCalled: false,
 11.2567 +               focusSwitchedMustBeCalled: false,
 11.2568 +               validatedStateChangedMustBeCalled: false,
 11.2569 +               modelChangedMustBeCalled: false,
 11.2570 +               sectionChangedMustBeCalled: false,
 11.2571 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2572 +               hideSuggestionsMustBeCalled: false,
 11.2573 +               showSuggestionsMustBeCalled: false,
 11.2574 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2575 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2576 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2577 +               documentAttachmentPickerDonePickerCalled: false,
 11.2578 +               didComposeNewMailMustBeCalled: false,
 11.2579 +               didModifyMessageMustBeCalled: false,
 11.2580 +               didDeleteMessageMustBeCalled: true)
 11.2581 +        vm?.handleSaveActionTriggered()
 11.2582 +        let msgWithTestMessageId = Message.by(uid: originalMessage.uid,
 11.2583 +                                              uuid: originalMessage.uuid,
 11.2584 +                                              folderName: originalMessage.parent.name,
 11.2585 +                                              accountAddress: account.user.address)
 11.2586 +        XCTAssertNil(msgWithTestMessageId,
 11.2587 +                     "original message must be deleted, a copy is safed to drafts")
 11.2588 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2589 +    }
 11.2590 +
 11.2591 +    func testHandleSaveActionTriggered_origDrafts() {
 11.2592 +        let testMessageId = UUID().uuidString + "testHandleSaveActionTriggered"
 11.2593 +        let originalMessage = message(inFolderOfType: .drafts)
 11.2594 +        originalMessage.messageID = testMessageId
 11.2595 +        originalMessage.from = account.user
 11.2596 +
 11.2597 +        assert(originalMessage: originalMessage,
 11.2598 +               contentChangedMustBeCalled: false,
 11.2599 +               focusSwitchedMustBeCalled: false,
 11.2600 +               validatedStateChangedMustBeCalled: false,
 11.2601 +               modelChangedMustBeCalled: false,
 11.2602 +               sectionChangedMustBeCalled: false,
 11.2603 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2604 +               hideSuggestionsMustBeCalled: false,
 11.2605 +               showSuggestionsMustBeCalled: false,
 11.2606 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2607 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2608 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2609 +               documentAttachmentPickerDonePickerCalled: false,
 11.2610 +               didComposeNewMailMustBeCalled: false,
 11.2611 +               didModifyMessageMustBeCalled: true,
 11.2612 +               didDeleteMessageMustBeCalled: true)
 11.2613 +        vm?.handleSaveActionTriggered()
 11.2614 +        let msgWithTestMessageId = Message.by(uid: originalMessage.uid,
 11.2615 +                                              uuid: originalMessage.uuid,
 11.2616 +                                              folderName: originalMessage.parent.name,
 11.2617 +                                              accountAddress: account.user.address,
 11.2618 +                                              includingDeleted: true)
 11.2619 +        XCTAssertTrue(msgWithTestMessageId?.imapFlags.deleted ?? false,
 11.2620 +                     "The user edited draft. Technically we save a new message, thus the original" +
 11.2621 +            " must be deleted.")
 11.2622 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2623 +    }
 11.2624 +
 11.2625 +    func testHandleDeleteActionTriggered_normal() {
 11.2626 +        assert(originalMessage: nil,
 11.2627 +               contentChangedMustBeCalled: false,
 11.2628 +               focusSwitchedMustBeCalled: false,
 11.2629 +               validatedStateChangedMustBeCalled: false,
 11.2630 +               modelChangedMustBeCalled: false,
 11.2631 +               sectionChangedMustBeCalled: false,
 11.2632 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2633 +               hideSuggestionsMustBeCalled: false,
 11.2634 +               showSuggestionsMustBeCalled: false,
 11.2635 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2636 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2637 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2638 +               documentAttachmentPickerDonePickerCalled: false,
 11.2639 +               didComposeNewMailMustBeCalled: false,
 11.2640 +               didModifyMessageMustBeCalled: false,
 11.2641 +               didDeleteMessageMustBeCalled: false)
 11.2642 +        vm?.handleSaveActionTriggered() 
 11.2643 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2644 +    }
 11.2645 +
 11.2646 +    func testHandleDeleteActionTriggered_origOutbox() {
 11.2647 +        let testMessageId = UUID().uuidString + "testHandleDeleteActionTriggered_origOutbox"
 11.2648 +        let originalMessage = message(inFolderOfType: .outbox)
 11.2649 +        originalMessage.messageID = testMessageId
 11.2650 +        originalMessage.from = account.user
 11.2651 +
 11.2652 +        assert(originalMessage: originalMessage,
 11.2653 +               contentChangedMustBeCalled: false,
 11.2654 +               focusSwitchedMustBeCalled: false,
 11.2655 +               validatedStateChangedMustBeCalled: false,
 11.2656 +               modelChangedMustBeCalled: false,
 11.2657 +               sectionChangedMustBeCalled: false,
 11.2658 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2659 +               hideSuggestionsMustBeCalled: false,
 11.2660 +               showSuggestionsMustBeCalled: false,
 11.2661 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2662 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2663 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2664 +               documentAttachmentPickerDonePickerCalled: false,
 11.2665 +               didComposeNewMailMustBeCalled: false,
 11.2666 +               didModifyMessageMustBeCalled: false,
 11.2667 +               didDeleteMessageMustBeCalled: true)
 11.2668 +        vm?.handleSaveActionTriggered()
 11.2669 +        let msgWithTestMessageId = Message.by(uid: originalMessage.uid,
 11.2670 +                                              uuid: originalMessage.uuid,
 11.2671 +                                              folderName: originalMessage.parent.name,
 11.2672 +                                              accountAddress: account.user.address)
 11.2673 +        XCTAssertNil(msgWithTestMessageId,
 11.2674 +                     "original message must be deleted, a copy is safed to drafts")
 11.2675 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2676 +    }
 11.2677 +
 11.2678 +    // MARK: - Suggestions
 11.2679 +
 11.2680 +    func testSuggestViewModel() {
 11.2681 +        let testee = vm?.suggestViewModel()
 11.2682 +        XCTAssertNotNil(testee)
 11.2683 +        XCTAssertTrue(testee?.resultDelegate === vm)
 11.2684 +    }
 11.2685 +
 11.2686 +    // showSuggestions and hideSuggestions are tested altering recipients
 11.2687 +
 11.2688 +    func testShowSuggestionsScrollFocus_nonEmpty() {
 11.2689 +        let expectedSuggestionsVisibility = true
 11.2690 +        assert(contentChangedMustBeCalled: false,
 11.2691 +               focusSwitchedMustBeCalled: false,
 11.2692 +               validatedStateChangedMustBeCalled: false,
 11.2693 +               expectedIsValidated: nil,
 11.2694 +               modelChangedMustBeCalled: false,
 11.2695 +               sectionChangedMustBeCalled: false,
 11.2696 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2697 +               hideSuggestionsMustBeCalled: false,
 11.2698 +               showSuggestionsMustBeCalled: false,
 11.2699 +               suggestionsScrollFocusChangedMustBeCalled: true,
 11.2700 +               expectedNewSuggestionsScrollFocusIsVisible: expectedSuggestionsVisibility,
 11.2701 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2702 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2703 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2704 +               documentAttachmentPickerDonePickerCalled: false,
 11.2705 +               didComposeNewMailMustBeCalled: false,
 11.2706 +               didModifyMessageMustBeCalled: false,
 11.2707 +               didDeleteMessageMustBeCalled: false)
 11.2708 +        let _ = vm?.suggestViewModel(SuggestViewModel(),
 11.2709 +                                     didToggleVisibilityTo: expectedSuggestionsVisibility)
 11.2710 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2711 +    }
 11.2712 +
 11.2713 +    func testShowSuggestionsScrollFocus_empty() {
 11.2714 +        let expectedSuggestionsVisibility = false
 11.2715 +        assert(contentChangedMustBeCalled: false,
 11.2716 +               focusSwitchedMustBeCalled: false,
 11.2717 +               validatedStateChangedMustBeCalled: false,
 11.2718 +               expectedIsValidated: nil,
 11.2719 +               modelChangedMustBeCalled: false,
 11.2720 +               sectionChangedMustBeCalled: false,
 11.2721 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2722 +               hideSuggestionsMustBeCalled: false,
 11.2723 +               showSuggestionsMustBeCalled: false,
 11.2724 +               suggestionsScrollFocusChangedMustBeCalled: true,
 11.2725 +               expectedNewSuggestionsScrollFocusIsVisible: expectedSuggestionsVisibility,
 11.2726 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2727 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2728 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2729 +               documentAttachmentPickerDonePickerCalled: false,
 11.2730 +               didComposeNewMailMustBeCalled: false,
 11.2731 +               didModifyMessageMustBeCalled: false,
 11.2732 +               didDeleteMessageMustBeCalled: false)
 11.2733 +        let _ = vm?.suggestViewModel(SuggestViewModel(),
 11.2734 +                                     didToggleVisibilityTo: expectedSuggestionsVisibility)
 11.2735 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2736 +    }
 11.2737 +
 11.2738 +// MARK: - ComposeViewModelStateDelegate Handling
 11.2739 +
 11.2740 +    func testComposeViewModelStateDidChangeValidationStateTo() {
 11.2741 +        let expectedIsValid = true
 11.2742 +        assert(contentChangedMustBeCalled: false,
 11.2743 +               focusSwitchedMustBeCalled: false,
 11.2744 +               validatedStateChangedMustBeCalled: true,
 11.2745 +               expectedIsValidated: expectedIsValid,
 11.2746 +               modelChangedMustBeCalled: false,
 11.2747 +               sectionChangedMustBeCalled: false,
 11.2748 +               colorBatchNeedsUpdateMustBeCalled: false,
 11.2749 +               hideSuggestionsMustBeCalled: false,
 11.2750 +               showSuggestionsMustBeCalled: false,
 11.2751 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2752 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2753 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2754 +               documentAttachmentPickerDonePickerCalled: false,
 11.2755 +               didComposeNewMailMustBeCalled: false,
 11.2756 +               didModifyMessageMustBeCalled: false,
 11.2757 +               didDeleteMessageMustBeCalled: false)
 11.2758 +        guard let state = vm?.state else {
 11.2759 +            XCTFail()
 11.2760 +            return
 11.2761 +        }
 11.2762 +        vm?.composeViewModelState(state, didChangeValidationStateTo: expectedIsValid)
 11.2763 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2764 +    }
 11.2765 +
 11.2766 +    func testComposeViewModelDidChangePEPRatingTo() {
 11.2767 +        let expectedRating = PEPRating.reliable
 11.2768 +        vm?.state.pEpProtection = true
 11.2769 +        let expectedProtection = vm?.state.pEpProtection ?? false
 11.2770 +        assert(contentChangedMustBeCalled: false,
 11.2771 +               focusSwitchedMustBeCalled: false,
 11.2772 +               validatedStateChangedMustBeCalled: false,
 11.2773 +               modelChangedMustBeCalled: false,
 11.2774 +               sectionChangedMustBeCalled: false,
 11.2775 +               colorBatchNeedsUpdateMustBeCalled: true,
 11.2776 +               expectedRating: expectedRating,
 11.2777 +               expectedProtectionEnabled: expectedProtection,
 11.2778 +               hideSuggestionsMustBeCalled: false,
 11.2779 +               showSuggestionsMustBeCalled: false,
 11.2780 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2781 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2782 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2783 +               documentAttachmentPickerDonePickerCalled: false,
 11.2784 +               didComposeNewMailMustBeCalled: false,
 11.2785 +               didModifyMessageMustBeCalled: false,
 11.2786 +               didDeleteMessageMustBeCalled: false)
 11.2787 +        guard let state = vm?.state else {
 11.2788 +            XCTFail()
 11.2789 +            return
 11.2790 +        }
 11.2791 +        vm?.composeViewModelState(state, didChangePEPRatingTo: expectedRating)
 11.2792 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2793 +    }
 11.2794 +
 11.2795 +    // MARK: - Delegate Setter Side Effect
 11.2796 +
 11.2797 +    func testDelegateSetter() {
 11.2798 +        let expectedRating = PEPRating.undefined
 11.2799 +        let expectedProtection = true
 11.2800 +        assert(contentChangedMustBeCalled: false,
 11.2801 +               focusSwitchedMustBeCalled: false,
 11.2802 +               validatedStateChangedMustBeCalled: false,
 11.2803 +               modelChangedMustBeCalled: false,
 11.2804 +               sectionChangedMustBeCalled: false,
 11.2805 +               colorBatchNeedsUpdateMustBeCalled: true,
 11.2806 +               expectedRating: expectedRating,
 11.2807 +               expectedProtectionEnabled: expectedProtection,
 11.2808 +               hideSuggestionsMustBeCalled: false,
 11.2809 +               showSuggestionsMustBeCalled: false,
 11.2810 +               showMediaAttachmentPickerMustBeCalled: false,
 11.2811 +               hideMediaAttachmentPickerMustBeCalled: false,
 11.2812 +               showDocumentAttachmentPickerMustBeCalled: false,
 11.2813 +               documentAttachmentPickerDonePickerCalled: false,
 11.2814 +               didComposeNewMailMustBeCalled: false,
 11.2815 +               didModifyMessageMustBeCalled: false,
 11.2816 +               didDeleteMessageMustBeCalled: false)
 11.2817 +        vm?.delegate = testDelegate
 11.2818 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
 11.2819 +    }
 11.2820 +
 11.2821 +    // MARK: - isAttachmentSection
 11.2822 +
 11.2823 +    func testIsAttachmentSection() {
 11.2824 +        let msgWithAttachment = draftMessage(attachmentsSet: true)
 11.2825 +        assert(originalMessage: msgWithAttachment)
 11.2826 +        guard
 11.2827 +            let lastSection = vm?.sections.last,
 11.2828 +            let numSections = vm?.sections.count else {
 11.2829 +                XCTFail()
 11.2830 +                return
 11.2831 +        }
 11.2832 +        let numRows = lastSection.rows.count
 11.2833 +        let idxPath = IndexPath(row: numRows - 1, section: numSections - 1)
 11.2834 +        XCTAssertTrue(vm?.isAttachmentSection(indexPath: idxPath) ?? false,
 11.2835 +                      "Last row in last section must be attachment")
 11.2836 +    }
 11.2837 +
 11.2838 +    // MARK: - handleRemovedRow
 11.2839 +
 11.2840 +    func testHandleRemovedRow_removeAttachment() {
 11.2841 +        let msgWithAttachments = draftMessage(attachmentsSet: true)
 11.2842 +        msgWithAttachments.appendToAttachments(attachment(ofType: .attachment))
 11.2843 +        msgWithAttachments.save()
 11.2844 +        assert(originalMessage: msgWithAttachments)
 11.2845 +        vm?.state.nonInlinedAttachments = msgWithAttachments.attachments.array
 11.2846 +        guard
 11.2847 +            let lastSectionBefore = vm?.sections.last,
 11.2848 +            let numSectionsBefore = vm?.sections.count,
 11.2849 +            let numNonIlinedAttachmentsBefore = vm?.state.nonInlinedAttachments.count
 11.2850 +            else {
 11.2851 +                XCTFail()
 11.2852 +                return
 11.2853 +        }
 11.2854 +        let numRowsBefore  = lastSectionBefore.rows.count
 11.2855 +        let attachmentIdxPath = IndexPath(row: numRowsBefore - 1,
 11.2856 +                                          section: numSectionsBefore - 1)
 11.2857 +        // Test
 11.2858 +        vm?.handleRemovedRow(at: attachmentIdxPath)
 11.2859 +        guard
 11.2860 +            let lastSectionAfter = vm?.sections.last,
 11.2861 +            let numSectionsAfter = vm?.sections.count,
 11.2862 +            let numNonIlinedAttachmentsAfter = vm?.state.nonInlinedAttachments.count
 11.2863 +            else {
 11.2864 +                XCTFail()
 11.2865 +                return
 11.2866 +        }
 11.2867 +        let numRowsAfter = lastSectionAfter.rows.count
 11.2868 +        XCTAssertEqual(numNonIlinedAttachmentsAfter, numNonIlinedAttachmentsBefore - 1)
 11.2869 +        XCTAssertEqual(numSectionsAfter, numSectionsBefore)
 11.2870 +        XCTAssertEqual(numRowsAfter, numRowsBefore - 1, "Attachment is removed")
 11.2871 +    }
 11.2872 +
 11.2873 +    // MARK: - handleUserClickedSendButton
 11.2874 +
 11.2875 +    func testHandleUserClickedSendButton() {
 11.2876 +        assert()
 11.2877 +        let toRecipient = Identity(address: "testHandleUserClickedSend@Butt.on")
 11.2878 +        vm?.state.toRecipients = [toRecipient]
 11.2879 +        vm?.state.from = account.user
 11.2880 +        let outMsgsBefore = Folder.by(account: account, folderType: .outbox)?
 11.2881 +            .allMessagesNonThreaded()
 11.2882 +            .count ?? -1
 11.2883 +        vm?.handleUserClickedSendButton()
 11.2884 +        let outMsgsAfter = Folder.by(account: account, folderType: .outbox)?
 11.2885 +            .allMessagesNonThreaded()
 11.2886 +            .count ?? -1
 11.2887 +        XCTAssertEqual(outMsgsAfter, outMsgsBefore + 1)
 11.2888 +        XCTAssertGreaterThan(outMsgsAfter, 0)
 11.2889 +    }
 11.2890 +
 11.2891 +    func testHandleUserClickedSendButton_origDraft() {
 11.2892 +        let testMessageId = UUID().uuidString + #function
 11.2893 +        let originalMessage = draftMessage()
 11.2894 +        originalMessage.messageID = testMessageId
 11.2895 +        originalMessage.from = account.user
 11.2896 +        originalMessage.save()
 11.2897 +        XCTAssertNotNil(Message.by(uid: originalMessage.uid,
 11.2898 +                                   uuid: originalMessage.uuid,
 11.2899 +                                   folderName: originalMessage.parent.name,
 11.2900 +                                   accountAddress: account.user.address))
 11.2901 +        assert(originalMessage: originalMessage)
 11.2902 +        let toRecipient = Identity(address: "testHandleUserClickedSend@Butt.on")
 11.2903 +        vm?.state.toRecipients = [toRecipient]
 11.2904 +        vm?.state.from = account.user
 11.2905 +        vm?.handleUserClickedSendButton()
 11.2906 +        guard
 11.2907 +            let originalDraftedMessageDeleted =
 11.2908 +            Message.by(uid: originalMessage.uid,
 11.2909 +                       uuid: originalMessage.uuid,
 11.2910 +                       folderName: originalMessage.parent.name,
 11.2911 +                       accountAddress: account.user.address,
 11.2912 +                       includingDeleted: true)?.imapFlags.deleted
 11.2913 +            else {
 11.2914 +                XCTFail()
 11.2915 +                return
 11.2916 +        }
 11.2917 +        XCTAssertTrue(originalDraftedMessageDeleted,
 11.2918 +                      "original drafted message must be flagged deleted")
 11.2919 +    }
 11.2920 +
 11.2921 +    func testHandleUserClickedSendButton_origOutbox() {
 11