|  |  |  | // | 
					
						
							|  |  |  | //  Copyright (c) 2019 Open Whisper Systems. All rights reserved. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #import "OWSOrphanDataCleaner.h" | 
					
						
							|  |  |  | #import "SignalBaseTest.h" | 
					
						
							|  |  |  | #import <SessionServiceKit/OWSDevice.h> | 
					
						
							|  |  |  | #import <SessionServiceKit/OWSPrimaryStorage.h> | 
					
						
							|  |  |  | #import <SessionServiceKit/TSAttachmentStream.h> | 
					
						
							|  |  |  | #import <SessionServiceKit/TSContactThread.h> | 
					
						
							|  |  |  | #import <SessionServiceKit/TSIncomingMessage.h> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @interface OWSOrphanDataCleaner (Test) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + (nullable NSSet<NSString *> *)filePathsInDirectorySafe:(NSString *)dirPath; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #pragma mark - | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @interface OWSOrphanDataCleanerTest : SignalBaseTest | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #pragma mark - | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @implementation OWSOrphanDataCleanerTest | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef BROKEN_TESTS | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)setUp | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     [super setUp]; | 
					
						
							|  |  |  |     // Register views, etc. | 
					
						
							|  |  |  |     [OWSPrimaryStorage registerExtensionsWithMigrationBlock:^{ | 
					
						
							|  |  |  |     }]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Set up initial conditions & Sanity check | 
					
						
							|  |  |  |     XCTAssertEqual(0, [self numberOfItemsInAttachmentsFolder]); | 
					
						
							|  |  |  |     XCTAssertEqual(0, [TSAttachmentStream numberOfKeysInCollection]); | 
					
						
							|  |  |  |     XCTAssertEqual(0, [TSIncomingMessage numberOfKeysInCollection]); | 
					
						
							|  |  |  |     XCTAssertEqual(0, [TSThread numberOfKeysInCollection]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)tearDown | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     [super tearDown]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (int)numberOfItemsInAttachmentsFolder | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     NSString *legacyAttachmentsDirPath = TSAttachmentStream.legacyAttachmentsDirPath; | 
					
						
							|  |  |  |     NSSet<NSString *> *_Nullable legacyAttachmentFilePaths = | 
					
						
							|  |  |  |         [OWSOrphanDataCleaner filePathsInDirectorySafe:legacyAttachmentsDirPath]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSString *sharedDataAttachmentsDirPath = TSAttachmentStream.sharedDataAttachmentsDirPath; | 
					
						
							|  |  |  |     NSSet<NSString *> *_Nullable sharedDataAttachmentFilePaths = | 
					
						
							|  |  |  |         [OWSOrphanDataCleaner filePathsInDirectorySafe:sharedDataAttachmentsDirPath]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSMutableSet<NSString *> *attachmentFilePaths = [NSMutableSet new]; | 
					
						
							|  |  |  |     if (legacyAttachmentFilePaths) { | 
					
						
							|  |  |  |         [attachmentFilePaths unionSet:legacyAttachmentFilePaths]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (sharedDataAttachmentFilePaths) { | 
					
						
							|  |  |  |         [attachmentFilePaths unionSet:sharedDataAttachmentFilePaths]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return (int)attachmentFilePaths.count; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (TSIncomingMessage *)createIncomingMessageWithThread:(TSThread *)thread | 
					
						
							|  |  |  |                                          attachmentIds:(NSArray<NSString *> *)attachmentIds | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     TSIncomingMessage *incomingMessage = | 
					
						
							|  |  |  |         [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:1 | 
					
						
							|  |  |  |                                                            inThread:thread | 
					
						
							|  |  |  |                                                            authorId:@"fake-author-id" | 
					
						
							|  |  |  |                                                      sourceDeviceId:OWSDevicePrimaryDeviceId | 
					
						
							|  |  |  |                                                         messageBody:@"footch" | 
					
						
							|  |  |  |                                                       attachmentIds:attachmentIds | 
					
						
							|  |  |  |                                                    expiresInSeconds:0 | 
					
						
							|  |  |  |                                                       quotedMessage:nil | 
					
						
							|  |  |  |                                                        contactShare:nil | 
					
						
							|  |  |  |                                                         linkPreview:nil]; | 
					
						
							|  |  |  |     [incomingMessage save]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return incomingMessage; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (TSAttachmentStream *)createAttachmentStream | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     NSError *error; | 
					
						
							|  |  |  |     TSAttachmentStream *attachmentStream = | 
					
						
							|  |  |  |         [[TSAttachmentStream alloc] initWithContentType:@"image/jpeg" byteCount:12 sourceFilename:nil]; | 
					
						
							|  |  |  |     [attachmentStream writeData:[NSData new] error:&error]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTAssertNil(error); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     [attachmentStream save]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return attachmentStream; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testInteractionsWithoutThreadAreDeleted | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // This thread is intentionally not saved. It's meant to recreate a situation we've seen where interactions exist | 
					
						
							|  |  |  |     // that reference the id of a thread that no longer exists. Presumably this is the result of a deleted thread not | 
					
						
							|  |  |  |     // properly deleting it's interactions. | 
					
						
							|  |  |  |     TSContactThread *unsavedThread = [[TSContactThread alloc] initWithUniqueId:@"this-thread-does-not-exist"]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     __unused TSIncomingMessage *incomingMessage = | 
					
						
							|  |  |  |         [self createIncomingMessageWithThread:unsavedThread attachmentIds:@[]]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTAssertEqual(1, [TSIncomingMessage numberOfKeysInCollection]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTestExpectation *expectation = [self expectationWithDescription:@"Cleanup"]; | 
					
						
							|  |  |  |     [OWSOrphanDataCleaner auditAndCleanup:YES | 
					
						
							|  |  |  |                                completion:^{ | 
					
						
							|  |  |  |                                    [expectation fulfill]; | 
					
						
							|  |  |  |                                }]; | 
					
						
							|  |  |  |     [self waitForExpectationsWithTimeout:5.0 | 
					
						
							|  |  |  |                                  handler:^(NSError *error) { | 
					
						
							|  |  |  |                                      if (error) { | 
					
						
							|  |  |  |                                          XCTFail(@"Expectation Failed with error: %@", error); | 
					
						
							|  |  |  |                                      } | 
					
						
							|  |  |  |                                  }]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTAssertEqual(0, [TSIncomingMessage numberOfKeysInCollection]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testInteractionsWithThreadAreNotDeleted | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     TSContactThread *savedThread = [[TSContactThread alloc] initWithUniqueId:@"this-thread-exists"]; | 
					
						
							|  |  |  |     [savedThread save]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     __unused TSIncomingMessage *incomingMessage = [self createIncomingMessageWithThread:savedThread attachmentIds:@[]]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTAssertEqual(1, [TSIncomingMessage numberOfKeysInCollection]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTestExpectation *expectation = [self expectationWithDescription:@"Cleanup"]; | 
					
						
							|  |  |  |     [OWSOrphanDataCleaner auditAndCleanup:YES | 
					
						
							|  |  |  |                                completion:^{ | 
					
						
							|  |  |  |                                    [expectation fulfill]; | 
					
						
							|  |  |  |                                }]; | 
					
						
							|  |  |  |     [self waitForExpectationsWithTimeout:5.0 | 
					
						
							|  |  |  |                                  handler:^(NSError *error) { | 
					
						
							|  |  |  |                                      if (error) { | 
					
						
							|  |  |  |                                          XCTFail(@"Expectation Failed with error: %@", error); | 
					
						
							|  |  |  |                                      } | 
					
						
							|  |  |  |                                  }]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTAssertEqual(1, [TSIncomingMessage numberOfKeysInCollection]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testFilesWithoutInteractionsAreDeleted | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // sanity check | 
					
						
							|  |  |  |     XCTAssertEqual(0, [self numberOfItemsInAttachmentsFolder]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     TSAttachmentStream *attachmentStream = [self createAttachmentStream]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSString *orphanFilePath = [attachmentStream originalFilePath]; | 
					
						
							|  |  |  |     BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:orphanFilePath]; | 
					
						
							|  |  |  |     XCTAssert(fileExists); | 
					
						
							|  |  |  |     XCTAssertEqual(1, [self numberOfItemsInAttachmentsFolder]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Do multiple cleanup passes. | 
					
						
							|  |  |  |     for (int i = 0; i < 2; i++) { | 
					
						
							|  |  |  |         XCTestExpectation *expectation = [self expectationWithDescription:@"Cleanup"]; | 
					
						
							|  |  |  |         [OWSOrphanDataCleaner auditAndCleanup:YES | 
					
						
							|  |  |  |                                    completion:^{ | 
					
						
							|  |  |  |                                        [expectation fulfill]; | 
					
						
							|  |  |  |                                    }]; | 
					
						
							|  |  |  |         [self waitForExpectationsWithTimeout:5.0 | 
					
						
							|  |  |  |                                      handler:^(NSError *error) { | 
					
						
							|  |  |  |                                          if (error) { | 
					
						
							|  |  |  |                                              XCTFail(@"Expectation Failed with error: %@", error); | 
					
						
							|  |  |  |                                          } | 
					
						
							|  |  |  |                                      }]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fileExists = [[NSFileManager defaultManager] fileExistsAtPath:orphanFilePath]; | 
					
						
							|  |  |  |     XCTAssertFalse(fileExists); | 
					
						
							|  |  |  |     XCTAssertEqual(0, [self numberOfItemsInAttachmentsFolder]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testFilesWithInteractionsAreNotDeleted | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     TSContactThread *savedThread = [[TSContactThread alloc] initWithUniqueId:@"this-thread-exists"]; | 
					
						
							|  |  |  |     [savedThread save]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     TSAttachmentStream *attachmentStream = [self createAttachmentStream]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     __unused TSIncomingMessage *incomingMessage = | 
					
						
							|  |  |  |         [self createIncomingMessageWithThread:savedThread attachmentIds:@[ attachmentStream.uniqueId ]]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSString *attachmentFilePath = [attachmentStream originalFilePath]; | 
					
						
							|  |  |  |     BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:attachmentFilePath]; | 
					
						
							|  |  |  |     XCTAssert(fileExists); | 
					
						
							|  |  |  |     XCTAssertEqual(1, [self numberOfItemsInAttachmentsFolder]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTestExpectation *expectation = [self expectationWithDescription:@"Cleanup"]; | 
					
						
							|  |  |  |     [OWSOrphanDataCleaner auditAndCleanup:YES | 
					
						
							|  |  |  |                                completion:^{ | 
					
						
							|  |  |  |                                    [expectation fulfill]; | 
					
						
							|  |  |  |                                }]; | 
					
						
							|  |  |  |     [self waitForExpectationsWithTimeout:5.0 | 
					
						
							|  |  |  |                                  handler:^(NSError *error) { | 
					
						
							|  |  |  |                                      if (error) { | 
					
						
							|  |  |  |                                          XCTFail(@"Expectation Failed with error: %@", error); | 
					
						
							|  |  |  |                                      } | 
					
						
							|  |  |  |                                  }]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fileExists = [[NSFileManager defaultManager] fileExistsAtPath:attachmentFilePath]; | 
					
						
							|  |  |  |     XCTAssert(fileExists); | 
					
						
							|  |  |  |     XCTAssertEqual(1, [self numberOfItemsInAttachmentsFolder]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testFilesWithoutAttachmentStreamsAreDeleted | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     NSError *error; | 
					
						
							|  |  |  |     TSAttachmentStream *attachmentStream = | 
					
						
							|  |  |  |         [[TSAttachmentStream alloc] initWithContentType:@"image/jpeg" byteCount:0 sourceFilename:nil]; | 
					
						
							|  |  |  |     [attachmentStream writeData:[NSData new] error:&error]; | 
					
						
							|  |  |  |     // Intentionally not saved, because we want a lingering file. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSString *orphanFilePath = [attachmentStream originalFilePath]; | 
					
						
							|  |  |  |     BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:orphanFilePath]; | 
					
						
							|  |  |  |     XCTAssert(fileExists); | 
					
						
							|  |  |  |     XCTAssertEqual(1, [self numberOfItemsInAttachmentsFolder]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTestExpectation *expectation = [self expectationWithDescription:@"Cleanup"]; | 
					
						
							|  |  |  |     [OWSOrphanDataCleaner auditAndCleanup:YES | 
					
						
							|  |  |  |                                completion:^{ | 
					
						
							|  |  |  |                                    [expectation fulfill]; | 
					
						
							|  |  |  |                                }]; | 
					
						
							|  |  |  |     [self waitForExpectationsWithTimeout:5.0 | 
					
						
							|  |  |  |                                  handler:^(NSError *error) { | 
					
						
							|  |  |  |                                      if (error) { | 
					
						
							|  |  |  |                                          XCTFail(@"Expectation Failed with error: %@", error); | 
					
						
							|  |  |  |                                      } | 
					
						
							|  |  |  |                                  }]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fileExists = [[NSFileManager defaultManager] fileExistsAtPath:orphanFilePath]; | 
					
						
							|  |  |  |     XCTAssertFalse(fileExists); | 
					
						
							|  |  |  |     XCTAssertEqual(0, [self numberOfItemsInAttachmentsFolder]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @end |