diff --git a/NextcloudTalk/AppDelegate.m b/NextcloudTalk/AppDelegate.m index 933e84f27..eeb89986a 100644 --- a/NextcloudTalk/AppDelegate.m +++ b/NextcloudTalk/AppDelegate.m @@ -51,6 +51,8 @@ @interface AppDelegate () @property (nonatomic, strong) NSTimer *keepAliveTimer; @property (nonatomic, strong) BGTaskHelper *keepAliveBGTask; +@property (nonatomic, strong) UILabel *debugLabel; +@property (nonatomic, strong) NSTimer *debugLabelTimer; @end @@ -92,6 +94,20 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( [NCUserInterfaceController sharedInstance].mainViewController.displayModeButtonVisibility = UISplitViewControllerDisplayModeButtonVisibilityNever; } + NSArray *arguments = [[NSProcessInfo processInfo] arguments]; + + if ([arguments containsObject:@"-TestEnvironment"]) { + self.debugLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 30, 200, 50)]; + [[NCUserInterfaceController sharedInstance].mainViewController.view addSubview:self.debugLabel]; + + __weak typeof(self) weakSelf = self; + self.debugLabelTimer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) { + long numChatVC = [NCUserInterfaceController sharedInstance].numberOfAllocatedChatViewControllers; + long numCallVC = [NCUserInterfaceController sharedInstance].numberOfAllocatedCallViewControllers; + [weakSelf.debugLabel setText:[NSString stringWithFormat:@"ChatVC: %ld / CallVC: %ld", numChatVC, numCallVC]]; + }]; + } + // When we include VLCKit we need to manually call this because otherwise, device rotation might not work [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; @@ -149,6 +165,9 @@ - (void)applicationWillTerminate:(UIApplication *)application { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; + + // Invalidate a potentially existing label timer + [self.debugLabelTimer invalidate]; } - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options diff --git a/NextcloudTalk/CallViewController.m b/NextcloudTalk/CallViewController.m index bac53bbe5..1d6e17822 100644 --- a/NextcloudTalk/CallViewController.m +++ b/NextcloudTalk/CallViewController.m @@ -172,6 +172,8 @@ - (instancetype)initCallInRoom:(NCRoom *)room asUser:(NSString *)displayName aud [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioSessionDidChangeRoutingInformation:) name:AudioSessionDidChangeRoutingInformationNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil]; + + [NCUserInterfaceController sharedInstance].numberOfAllocatedCallViewControllers += 1; return self; } @@ -346,6 +348,7 @@ - (UIStatusBarStyle)preferredStatusBarStyle - (void)dealloc { NSLog(@"CallViewController dealloc"); + [NCUserInterfaceController sharedInstance].numberOfAllocatedCallViewControllers -= 1; [[NSNotificationCenter defaultCenter] removeObserver:self]; } diff --git a/NextcloudTalk/ChatMessageTableViewCell.m b/NextcloudTalk/ChatMessageTableViewCell.m index f64bdb224..e4742cd62 100644 --- a/NextcloudTalk/ChatMessageTableViewCell.m +++ b/NextcloudTalk/ChatMessageTableViewCell.m @@ -427,12 +427,14 @@ - (void)setDeliveryState:(ChatMessageDeliveryState)state [checkView setImage:[UIImage imageNamed:@"check"]]; checkView.image = [checkView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; [checkView setTintColor:[UIColor lightGrayColor]]; + [checkView setAccessibilityValue:@"MessageSent"]; [self.statusView addSubview:checkView]; } else if (state == ChatMessageDeliveryStateRead) { UIImageView *checkAllView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)]; [checkAllView setImage:[UIImage imageNamed:@"check-all"]]; checkAllView.image = [checkAllView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; [checkAllView setTintColor:[UIColor lightGrayColor]]; + [checkAllView setAccessibilityValue:@"MessageSent"]; [self.statusView addSubview:checkAllView]; } } diff --git a/NextcloudTalk/NCChatViewController.m b/NextcloudTalk/NCChatViewController.m index 889899243..c45ed76ac 100644 --- a/NextcloudTalk/NCChatViewController.m +++ b/NextcloudTalk/NCChatViewController.m @@ -241,6 +241,8 @@ - (instancetype)initForRoom:(NCRoom *)room // Notifications when runing on Mac [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidBecomeActive:) name:@"NSApplicationDidBecomeActiveNotification" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActive:) name:@"NSApplicationDidResignActiveNotification" object:nil]; + + [NCUserInterfaceController sharedInstance].numberOfAllocatedChatViewControllers += 1; } return self; @@ -249,6 +251,7 @@ - (instancetype)initForRoom:(NCRoom *)room - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; + [NCUserInterfaceController sharedInstance].numberOfAllocatedChatViewControllers -= 1; NSLog(@"Dealloc NCChatViewController"); } diff --git a/NextcloudTalk/NCUserInterfaceController.h b/NextcloudTalk/NCUserInterfaceController.h index 82901dbee..aace9b577 100644 --- a/NextcloudTalk/NCUserInterfaceController.h +++ b/NextcloudTalk/NCUserInterfaceController.h @@ -38,6 +38,8 @@ typedef void (^PresentCallControllerCompletionBlock)(void); @property (nonatomic, strong) NCSplitViewController *mainViewController; @property (nonatomic, strong) RoomsTableViewController *roomsTableViewController; +@property (nonatomic, assign) NSInteger numberOfAllocatedChatViewControllers; +@property (nonatomic, assign) NSInteger numberOfAllocatedCallViewControllers; + (instancetype)sharedInstance; - (void)presentConversationsList; diff --git a/NextcloudTalkUITests/NextcloudTalkUITests.swift b/NextcloudTalkUITests/NextcloudTalkUITests.swift index dd2ec62ad..026e87f0a 100644 --- a/NextcloudTalkUITests/NextcloudTalkUITests.swift +++ b/NextcloudTalkUITests/NextcloudTalkUITests.swift @@ -54,6 +54,7 @@ final class NextcloudTalkUITests: XCTestCase { let app = XCUIApplication() app.launchArguments += ["-AppleLanguages", "(en-US)"] app.launchArguments += ["-AppleLocale", "\"en-US\""] + app.launchArguments += ["-TestEnvironment"] app.launch() let accountSwitcherButton = app.buttons["Nextcloud Talk"] @@ -161,4 +162,52 @@ final class NextcloudTalkUITests: XCTestCase { // Check if the conversation appears in the conversation list XCTAssert(app.tables.cells.staticTexts[newConversationName].waitForExistence(timeout: timeoutLong)) } + + func testChatViewControllerDeallocation() { + let app = launchAndLogin() + let newConversationName = "DeAllocTest" + + // Create a new test conversion + app.navigationBars["Nextcloud Talk"].buttons["Create a new conversation"].tap() + XCTAssert(app.tables.cells.staticTexts["Create a new group conversation"].waitForExistence(timeout: timeoutShort)) + XCTAssert(app.tables.cells.staticTexts["Create a new public conversation"].waitForExistence(timeout: timeoutShort)) + XCTAssert(app.tables.cells.staticTexts["Show list of open conversations"].waitForExistence(timeout: timeoutShort)) + app.tables.cells.staticTexts["Create a new group conversation"].tap() + app.navigationBars["RoomCreationTableView"].buttons["Next"].tap() + app.typeText(newConversationName) + app.navigationBars["New group conversation"].buttons["Create"].tap() + + // Check if we have one chat view controller allocated + XCTAssert(app.staticTexts["ChatVC: 1 / CallVC: 0"].waitForExistence(timeout: timeoutShort)) + + // Send a test message + let testMessage = "TestMessage" + let toolbar = app.toolbars["Toolbar"] + let textView = toolbar.textViews["Write message, @ to mention someone …"] + XCTAssert(textView.waitForExistence(timeout: timeoutShort)) + textView.tap() + app.typeText(testMessage) + let sendMessageButton = toolbar.buttons["Send message"] + sendMessageButton.tap() + + // Wait for temporary message to be replaced + XCTAssert(app.images["MessageSent"].waitForExistence(timeout: timeoutShort)) + + // Open context menu + let tables = app.tables + XCTAssert(tables.staticTexts[username].waitForExistence(timeout: timeoutShort)) + let message = tables.staticTexts[username] + message.press(forDuration: 2.0) + + // Add a reaction to close the context menu + XCTAssert(app.staticTexts["👍"].waitForExistence(timeout: timeoutShort)) + app.staticTexts["👍"].tap() + + // Go back to the main view controller + let chatNavBar = app.navigationBars["NCChatView"] + chatNavBar.buttons["Back"].tap() + + // Check if all chat view controllers are deallocated + XCTAssert(app.staticTexts["ChatVC: 0 / CallVC: 0"].waitForExistence(timeout: timeoutShort)) + } }