Index: ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm |
diff --git a/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm b/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..969d354269b60aaabfad9971a9fe0bc1b32e6eb5 |
--- /dev/null |
+++ b/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm |
@@ -0,0 +1,956 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#import <EarlGrey/EarlGrey.h> |
+#import <XCTest/XCTest.h> |
+ |
+#include "base/mac/bind_objc_block.h" |
+#include "base/memory/ptr_util.h" |
+#include "base/strings/stringprintf.h" |
+#include "base/strings/sys_string_conversions.h" |
+#include "base/strings/utf_string_conversions.h" |
+#import "base/test/ios/wait_util.h" |
+#include "components/strings/grit/components_strings.h" |
+#include "ios/chrome/browser/experimental_flags.h" |
+#import "ios/chrome/browser/metrics/tab_usage_recorder.h" |
+#import "ios/chrome/browser/ui/settings/privacy_collection_view_controller.h" |
+#import "ios/chrome/browser/ui/settings/settings_collection_view_controller.h" |
+#import "ios/chrome/browser/ui/toolbar/toolbar_controller.h" |
+#import "ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.h" |
+#include "ios/chrome/browser/ui/ui_util.h" |
+#import "ios/chrome/browser/ui/uikit_ui_util.h" |
+#include "ios/chrome/grit/ios_strings.h" |
+#import "ios/chrome/test/app/chrome_test_util.h" |
+#import "ios/chrome/test/app/histogram_test_util.h" |
+#include "ios/chrome/test/app/navigation_test_util.h" |
+#import "ios/chrome/test/app/tab_test_util.h" |
+#include "ios/chrome/test/app/web_view_interaction_test_util.h" |
+#import "ios/chrome/test/earl_grey/chrome_assertions.h" |
+#import "ios/chrome/test/earl_grey/chrome_earl_grey.h" |
+#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h" |
+#import "ios/chrome/test/earl_grey/chrome_matchers.h" |
+#import "ios/chrome/test/earl_grey/chrome_test_case.h" |
+#import "ios/testing/wait_util.h" |
+#import "ios/web/public/test/http_server.h" |
+#import "ios/web/public/test/http_server_util.h" |
+#include "ios/web/public/test/response_providers/delayed_response_provider.h" |
+#include "ios/web/public/test/response_providers/html_response_provider.h" |
+#include "ui/base/l10n/l10n_util.h" |
+#include "ui/base/l10n/l10n_util_mac.h" |
+ |
+namespace { |
+ |
+const char kTestUrl1[] = |
+ "http://ios/testing/data/http_server_files/memory_usage.html"; |
+const char kURL1FirstWord[] = "Page"; |
+const char kTestUrl2[] = |
+ "http://ios/testing/data/http_server_files/fullscreen.html"; |
+const char kURL2FirstWord[] = "Rugby"; |
+const char kClearPageScript[] = "document.body.innerHTML='';"; |
+ |
+// The delay to use to serve slow URLs. |
+const CGFloat kSlowURLDelay = 3; |
+ |
+// The delay to wait for an element to appear before tapping on it. |
+const CGFloat kWaitElementTimeout = 3; |
+ |
+void ResetTabUsageRecorder() { |
+ GREYAssertTrue(chrome_test_util::ResetTabUsageRecorder(), |
+ @"Fail to reset the TabUsageRecorder"); |
+} |
+ |
+// Wait until |matcher| is accessible (not nil). |
+void Wait(id<GREYMatcher> matcher, NSString* name) { |
+ ConditionBlock condition = ^{ |
+ NSError* error = nil; |
+ [[EarlGrey selectElementWithMatcher:matcher] assertWithMatcher:grey_notNil() |
+ error:&error]; |
+ return error == nil; |
+ }; |
+ GREYAssert( |
+ testing::WaitUntilConditionOrTimeout(kWaitElementTimeout, condition), |
+ [NSString stringWithFormat:@"Waiting for matcher %@ failed.", name]); |
+} |
+ |
+// Wait until |matcher| is accessible (not nil) and tap on it. |
+void WaitAndTap(id<GREYMatcher> matcher, NSString* name) { |
+ Wait(matcher, name); |
+ [[EarlGrey selectElementWithMatcher:matcher] performAction:grey_tap()]; |
+} |
+ |
+// Creates a new main tab and load |url|. Wait until |word| is visible on the |
+// page. |
+void NewMainTabWithURL(const GURL& url, const std::string& word) { |
+ int number_of_tabs = chrome_test_util::GetMainTabCount(); |
+ chrome_test_util::OpenNewTab(); |
+ [ChromeEarlGrey loadURL:url]; |
+ [[EarlGrey |
+ selectElementWithMatcher:chrome_test_util::webViewContainingText(word)] |
+ assertWithMatcher:grey_notNil()]; |
+ chrome_test_util::AssertMainTabCount(number_of_tabs + 1); |
+} |
+ |
+// Opens 2 new tabs with different URLs. |
+void OpenTwoTabs() { |
+ chrome_test_util::CloseAllTabsInCurrentMode(); |
+ |
+ const GURL url1 = web::test::HttpServer::MakeUrl(kTestUrl1); |
+ const GURL url2 = web::test::HttpServer::MakeUrl(kTestUrl2); |
+ NewMainTabWithURL(url1, kURL1FirstWord); |
+ NewMainTabWithURL(url2, kURL2FirstWord); |
+} |
+ |
+// Opens a new main tab using the UI. This method is using ad-hoc |
+// synchronization. |
+void OpenNewMainTabUsingUIUnsynced() { |
+ int nb_main_tab = chrome_test_util::GetMainTabCount(); |
+ id<GREYMatcher> tool_menu_matcher = |
+ grey_accessibilityID(kToolbarToolsMenuButtonIdentifier); |
+ WaitAndTap(tool_menu_matcher, @"Tool menu"); |
+ id<GREYMatcher> new_main_tab_button_matcher = |
+ grey_accessibilityID(kToolsMenuNewTabId); |
+ WaitAndTap(new_main_tab_button_matcher, @"New tab button"); |
+ |
+ chrome_test_util::AssertMainTabCount(nb_main_tab + 1); |
+} |
+ |
+// Opens a new incognito tab using the UI and evicts any main tab model tabs. |
+void OpenNewIncognitoTabUsingUIAndEvictMainTabs() { |
+ int nb_incognito_tab = chrome_test_util::GetIncognitoTabCount(); |
+ [ChromeEarlGreyUI openToolsMenu]; |
+ id<GREYMatcher> new_incognito_tab_button_matcher = |
+ grey_accessibilityID(kToolsMenuNewIncognitoTabId); |
+ [[EarlGrey selectElementWithMatcher:new_incognito_tab_button_matcher] |
+ performAction:grey_tap()]; |
+ chrome_test_util::AssertIncognitoTabCount(nb_incognito_tab + 1); |
+ |
+ chrome_test_util::EvictOtherTabModelTabs(); |
+} |
+ |
+// Closes a tab in the current tab model. Synchronize on tab number afterwards. |
+void CloseTabAtIndexAndSync(NSUInteger i) { |
+ NSUInteger nb_main_tab = chrome_test_util::GetMainTabCount(); |
+ chrome_test_util::CloseTabAtIndex(i); |
+ ConditionBlock condition = ^{ |
+ return chrome_test_util::GetMainTabCount() == (nb_main_tab - 1); |
+ }; |
+ GREYAssert( |
+ testing::WaitUntilConditionOrTimeout(kWaitElementTimeout, condition), |
+ @"Waiting for tab to close"); |
+} |
+ |
+// Closes the tabs switcher. |
+void CloseTabSwitcher() { |
+ id<GREYMatcher> matcher = chrome_test_util::buttonWithAccessibilityLabelId( |
+ IDS_IOS_TAB_STRIP_LEAVE_TAB_SWITCHER); |
+ [[EarlGrey selectElementWithMatcher:matcher] performAction:grey_tap()]; |
+} |
+ |
+// Swithches to normal mode using swith button (iPad) or stack view (iPhone). |
+// Assumes current mode is Incognito. |
+void SwitchToNormalMode() { |
+ GREYAssertTrue(chrome_test_util::IsIncognitoMode(), |
+ @"Switching to normal mode is only allowed from Incognito."); |
+ if (IsIPadIdiom()) { |
+ if (experimental_flags::IsTabSwitcherEnabled()) { |
+ // Enter the tab switcher. |
+ id<GREYMatcher> tabSwitcherEnterButton = |
+ grey_accessibilityLabel(l10n_util::GetNSStringWithFixup( |
+ IDS_IOS_TAB_STRIP_ENTER_TAB_SWITCHER)); |
+ [[EarlGrey selectElementWithMatcher:tabSwitcherEnterButton] |
+ performAction:grey_tap()]; |
+ |
+ // Select the non incognito panel. |
+ id<GREYMatcher> tabSwitcherHeaderPanelButton = |
+ grey_accessibilityLabel(l10n_util::GetNSStringWithFixup( |
+ IDS_IOS_TAB_SWITCHER_HEADER_NON_INCOGNITO_TABS)); |
+ [[EarlGrey selectElementWithMatcher:tabSwitcherHeaderPanelButton] |
+ performAction:grey_tap()]; |
+ |
+ // Leave the tab switcher. |
+ CloseTabSwitcher(); |
+ } else { |
+ [[EarlGrey selectElementWithMatcher: |
+ chrome_test_util::buttonWithAccessibilityLabelId( |
+ IDS_IOS_SWITCH_BROWSER_MODE_LEAVE_INCOGNITO)] |
+ performAction:grey_tap()]; |
+ } |
+ } else { |
+ [[EarlGrey selectElementWithMatcher: |
+ chrome_test_util::buttonWithAccessibilityLabelId( |
+ IDS_IOS_TOOLBAR_SHOW_TABS)] performAction:grey_tap()]; |
+ [[EarlGrey selectElementWithMatcher: |
+ chrome_test_util::buttonWithAccessibilityLabelId( |
+ IDS_IOS_TOOLS_MENU_NEW_INCOGNITO_TAB)] |
+ performAction:grey_swipeFastInDirection(kGREYDirectionRight)]; |
+ [[EarlGrey selectElementWithMatcher: |
+ chrome_test_util::buttonWithAccessibilityLabelId( |
+ IDS_IOS_TOOLBAR_SHOW_TABS)] performAction:grey_tap()]; |
+ } |
+ GREYAssertFalse(chrome_test_util::IsIncognitoMode(), |
+ @"Switching to normal mode failed."); |
+} |
+ |
+// Check that the error page is visible. |
+void CheckErrorPageIsVisible() { |
+ // The DNS error page is static HTML content, so it isn't part of the webview |
+ // owned by the webstate. |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util:: |
+ webViewBelongingToWebController()] |
+ assertWithMatcher:grey_nil()]; |
+ NSString* const kError = |
+ l10n_util::GetNSString(IDS_ERRORPAGES_HEADING_NOT_AVAILABLE); |
+ [[EarlGrey |
+ selectElementWithMatcher:chrome_test_util::staticHtmlViewContainingText( |
+ kError)] assertWithMatcher:grey_notNil()]; |
+} |
+ |
+// Open the settings submenu. Assumes that settings menu is visible. |
+void OpenSettingsSubMenuUnsynced(int submenu) { |
+ id<GREYMatcher> settings_button_matcher = |
+ grey_text(l10n_util::GetNSString(submenu)); |
+ [[[EarlGrey selectElementWithMatcher:settings_button_matcher] |
+ usingSearchAction:grey_swipeSlowInDirection(kGREYDirectionUp) |
+ onElementWithMatcher:grey_accessibilityID(kSettingsCollectionViewId)] |
+ performAction:grey_tap()]; |
+} |
+ |
+// Open the settings menu. Wait for the settings menu to appear. |
+void OpenSettingsMenuUnsynced() { |
+ id<GREYMatcher> tool_menu_matcher = |
+ grey_accessibilityID(kToolbarToolsMenuButtonIdentifier); |
+ WaitAndTap(tool_menu_matcher, @"Tool menu"); |
+ |
+ id<GREYMatcher> settings_button_matcher = |
+ grey_accessibilityID(kToolsMenuSettingsId); |
+ |
+ WaitAndTap(settings_button_matcher, @"Settings menu"); |
+ Wait(grey_accessibilityID(kSettingsCollectionViewId), @"Setting view"); |
+} |
+ |
+// Select the tab with title |title| using UI (tab strip on iPad, stack view on |
+// iPhone). |
+void SelectTabUsingUI(NSString* title) { |
+ if (IsCompact()) { |
+ WaitAndTap(chrome_test_util::buttonWithAccessibilityLabelId( |
+ IDS_IOS_TOOLBAR_SHOW_TABS), |
+ @"Tab switcher"); |
+ } |
+ WaitAndTap(grey_text(title), |
+ [NSString stringWithFormat:@"tab with title %@", title]); |
+} |
+} // namespace |
+ |
+// Test for the TabUsageRecorder class. |
+@interface TabUsageRecorderTestCase : ChromeTestCase |
+@end |
+ |
+@implementation TabUsageRecorderTestCase |
+ |
+- (void)tearDown { |
+ [[GREYConfiguration sharedInstance] |
+ setValue:@(YES) |
+ forConfigKey:kGREYConfigKeySynchronizationEnabled]; |
+ [super tearDown]; |
+} |
+ |
+// Tests that the recorder actual recorde tab state. |
+- (void)testTabSwitchRecorder { |
+ web::test::SetUpFileBasedHttpServer(); |
+ chrome_test_util::HistogramTester histogramTester; |
+ ResetTabUsageRecorder(); |
+ FailureBlock failureBlock = ^(NSString* error) { |
+ GREYFail(error); |
+ }; |
+ |
+ // Open two tabs with urls. |
+ OpenTwoTabs(); |
+ chrome_test_util::AssertMainTabCount(2); |
+ // Switch between the two tabs. Both are currently in memory. |
+ chrome_test_util::SelectTabAtIndexInCurrentMode(0); |
+ |
+ // Verify that one in-memory tab switch has been recorded. |
+ // histogramTester.ExpectTotalCount(kSelectedTabHistogramName, 1, |
+ // failureBlock); |
+ histogramTester.ExpectUniqueSample( |
+ kSelectedTabHistogramName, TabUsageRecorder::IN_MEMORY, 1, failureBlock); |
+ |
+ // Evict the tab. |
+ OpenNewIncognitoTabUsingUIAndEvictMainTabs(); |
+ GREYAssertTrue(chrome_test_util::IsIncognitoMode(), |
+ @"Failed to switch to incognito mode"); |
+ |
+ // Switch back to the normal tabs. Should be on tab one. |
+ SwitchToNormalMode(); |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::webViewContainingText( |
+ kURL1FirstWord)] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ histogramTester.ExpectTotalCount(kSelectedTabHistogramName, 2, failureBlock); |
+ histogramTester.ExpectBucketCount(kSelectedTabHistogramName, |
+ TabUsageRecorder::EVICTED, 1, failureBlock); |
+} |
+ |
+// Verifies the UMA metric for page loads before a tab eviction by loading |
+// some tabs, forcing a tab eviction, then checking the histogram. |
+- (void)testPageLoadCountBeforeEvictedTab { |
+ web::test::SetUpFileBasedHttpServer(); |
+ chrome_test_util::HistogramTester histogramTester; |
+ ResetTabUsageRecorder(); |
+ const GURL url1 = web::test::HttpServer::MakeUrl(kTestUrl1); |
+ FailureBlock failureBlock = ^(NSString* error) { |
+ GREYFail(error); |
+ }; |
+ |
+ // This test opens three tabs. |
+ const int numberOfTabs = 3; |
+ chrome_test_util::CloseAllTabsInCurrentMode(); |
+ // Open three tabs with http:// urls. |
+ for (NSUInteger i = 0; i < numberOfTabs; i++) { |
+ chrome_test_util::OpenNewTab(); |
+ [ChromeEarlGrey loadURL:url1]; |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::webViewContainingText( |
+ kURL1FirstWord)] |
+ assertWithMatcher:grey_notNil()]; |
+ } |
+ chrome_test_util::AssertMainTabCount(numberOfTabs); |
+ |
+ // Switch between the tabs. They are currently in memory. |
+ chrome_test_util::SelectTabAtIndexInCurrentMode(0); |
+ |
+ // Verify that no page-load count has been recorded. |
+ histogramTester.ExpectTotalCount(kPageLoadsBeforeEvictedTabSelected, 0, |
+ failureBlock); |
+ |
+ // Reload each tab. |
+ for (NSUInteger i = 0; i < numberOfTabs; i++) { |
+ chrome_test_util::SelectTabAtIndexInCurrentMode(i); |
+ // Clear the page so that we can check when pade reload is complete. |
+ __block bool finished = false; |
+ chrome_test_util::GetCurrentWebState()->ExecuteJavaScript( |
+ base::UTF8ToUTF16(kClearPageScript), |
+ base::BindBlock(^(const base::Value*) { |
+ finished = true; |
+ })); |
+ |
+ GREYAssert(testing::WaitUntilConditionOrTimeout(1.0, |
+ ^{ |
+ return finished; |
+ }), |
+ @"JavaScript to reload each tab did not finish"); |
+ [ChromeEarlGreyUI reload]; |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::webViewContainingText( |
+ kURL1FirstWord)] |
+ assertWithMatcher:grey_notNil()]; |
+ } |
+ |
+ // Evict the tab. Create a dummy tab so that switching back to normal mode |
+ // does not trigger a reload immediatly. |
+ chrome_test_util::OpenNewTab(); |
+ OpenNewIncognitoTabUsingUIAndEvictMainTabs(); |
+ chrome_test_util::AssertIncognitoTabCount(1); |
+ |
+ // Switch back to the normal tabs. Should be on tab one. |
+ SwitchToNormalMode(); |
+ chrome_test_util::SelectTabAtIndexInCurrentMode(0); |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::webViewContainingText( |
+ kURL1FirstWord)] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Verify that one page-load count has been recorded. It should contain two |
+ // page loads for each tab created. |
+ histogramTester.ExpectTotalCount(kPageLoadsBeforeEvictedTabSelected, 1, |
+ failureBlock); |
+ |
+ std::unique_ptr<base::HistogramSamples> samples = |
+ histogramTester.GetHistogramSamplesSinceCreation( |
+ kPageLoadsBeforeEvictedTabSelected); |
+ int sampleSum = samples ? samples->sum() : 0; |
+ GREYAssertEqual( |
+ sampleSum, numberOfTabs * 2, |
+ [NSString stringWithFormat:@"Expected page loads is %d, actual %d.", |
+ numberOfTabs * 2, sampleSum]); |
+} |
+ |
+// Tests that tabs reloaded on cold start are reported as |
+// EVICTED_DUE_TO_COLD_START. |
+- (void)testColdLaunchReloadCount { |
+ web::test::SetUpFileBasedHttpServer(); |
+ chrome_test_util::HistogramTester histogramTester; |
+ ResetTabUsageRecorder(); |
+ |
+ // Open two tabs with urls. |
+ OpenTwoTabs(); |
+ chrome_test_util::AssertMainTabCount(2); |
+ // Set the normal tabs as 'cold start' tabs. |
+ GREYAssertTrue(chrome_test_util::SetCurrentTabsToBeColdStartTabs(), |
+ @"Fail to state tabs as cold start tabs"); |
+ |
+ // Open two incognito tabs with urls, clearing normal tabs from memory. |
+ OpenNewIncognitoTabUsingUIAndEvictMainTabs(); |
+ OpenNewIncognitoTabUsingUIAndEvictMainTabs(); |
+ chrome_test_util::AssertIncognitoTabCount(2); |
+ |
+ // Switch back to the normal tabs. |
+ SwitchToNormalMode(); |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::webViewContainingText( |
+ kURL2FirstWord)] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Select the other one so it also reloads. |
+ chrome_test_util::SelectTabAtIndexInCurrentMode(0); |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::webViewContainingText( |
+ kURL1FirstWord)] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ FailureBlock failureBlock = ^(NSString* error) { |
+ GREYFail(error); |
+ }; |
+ // Make sure that one of the 2 tab loads (excluding the selected tab) is |
+ // counted as a cold start eviction. |
+ histogramTester.ExpectBucketCount(kSelectedTabHistogramName, |
+ TabUsageRecorder::EVICTED_DUE_TO_COLD_START, |
+ 1, failureBlock); |
+ |
+ histogramTester.ExpectBucketCount( |
+ kSelectedTabHistogramName, TabUsageRecorder::IN_MEMORY, 0, failureBlock); |
+ // Re-select the same tab and make sure it is not counted again as evicted. |
+ chrome_test_util::SelectTabAtIndexInCurrentMode(1); |
+ chrome_test_util::SelectTabAtIndexInCurrentMode(0); |
+ |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::webViewContainingText( |
+ kURL1FirstWord)] |
+ assertWithMatcher:grey_notNil()]; |
+ histogramTester.ExpectBucketCount(kSelectedTabHistogramName, |
+ TabUsageRecorder::EVICTED_DUE_TO_COLD_START, |
+ 1, failureBlock); |
+ |
+ histogramTester.ExpectBucketCount( |
+ kSelectedTabHistogramName, TabUsageRecorder::IN_MEMORY, 2, failureBlock); |
+} |
+ |
+// Tests that tabs reloads after backgrounding and eviction. |
+- (void)testBackgroundingReloadCount { |
+ web::test::SetUpFileBasedHttpServer(); |
+ chrome_test_util::HistogramTester histogramTester; |
+ ResetTabUsageRecorder(); |
+ FailureBlock failureBlock = ^(NSString* error) { |
+ GREYFail(error); |
+ }; |
+ |
+ // Open two tabs with urls. |
+ OpenTwoTabs(); |
+ chrome_test_util::AssertMainTabCount(2); |
+ |
+ // Simulate going into the background. |
+ GREYAssertTrue(chrome_test_util::SimulateTabsBackgrounding(), |
+ @"Fail to simulate tab backgrounding."); |
+ |
+ // Open incognito and clear normal tabs from memory. |
+ OpenNewIncognitoTabUsingUIAndEvictMainTabs(); |
+ GREYAssertTrue(chrome_test_util::IsIncognitoMode(), |
+ @"Failed to switch to incognito mode"); |
+ histogramTester.ExpectTotalCount(kEvictedTabReloadTime, 0, failureBlock); |
+ |
+ // Switch back to the normal tabs. |
+ SwitchToNormalMode(); |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::webViewContainingText( |
+ kURL2FirstWord)] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ const GURL url1 = web::test::HttpServer::MakeUrl(kTestUrl1); |
+ const GURL url2 = web::test::HttpServer::MakeUrl(kTestUrl2); |
+ [[EarlGrey |
+ selectElementWithMatcher:chrome_test_util::omniboxText(url2.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+ histogramTester.ExpectTotalCount(kEvictedTabReloadTime, 1, failureBlock); |
+ |
+ chrome_test_util::SelectTabAtIndexInCurrentMode(0); |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::webViewContainingText( |
+ kURL1FirstWord)] |
+ assertWithMatcher:grey_notNil()]; |
+ [[EarlGrey |
+ selectElementWithMatcher:chrome_test_util::omniboxText(url1.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+ histogramTester.ExpectTotalCount(kEvictedTabReloadTime, 2, failureBlock); |
+} |
+ |
+// Verify correct recording of metrics when the reloading of an evicted tab |
+// succeeds. |
+- (void)testEvictedTabReloadSuccess { |
+ web::test::SetUpFileBasedHttpServer(); |
+ chrome_test_util::HistogramTester histogramTester; |
+ FailureBlock failureBlock = ^(NSString* error) { |
+ GREYFail(error); |
+ }; |
+ |
+ GURL URL = web::test::HttpServer::MakeUrl(kTestUrl1); |
+ NewMainTabWithURL(URL, kURL1FirstWord); |
+ OpenNewIncognitoTabUsingUIAndEvictMainTabs(); |
+ SwitchToNormalMode(); |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::webViewContainingText( |
+ kURL1FirstWord)] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ histogramTester.ExpectUniqueSample(kEvictedTabReloadSuccessRate, |
+ TabUsageRecorder::LOAD_SUCCESS, 1, |
+ failureBlock); |
+ histogramTester.ExpectUniqueSample(kDidUserWaitForEvictedTabReload, |
+ TabUsageRecorder::USER_WAITED, 1, |
+ failureBlock); |
+ histogramTester.ExpectTotalCount(kEvictedTabReloadTime, 1, failureBlock); |
+} |
+ |
+// Verify correct recording of metrics when the reloading of an evicted tab |
+// fails. |
+- (void)testEvictedTabReloadFailure { |
+ web::test::SetUpFileBasedHttpServer(); |
+ chrome_test_util::HistogramTester histogramTester; |
+ FailureBlock failureBlock = ^(NSString* error) { |
+ GREYFail(error); |
+ }; |
+ |
+ // This URL is purposely invalid so it triggers a navigation error. |
+ GURL invalidURL(kTestUrl1); |
+ |
+ chrome_test_util::OpenNewTab(); |
+ [ChromeEarlGrey loadURL:invalidURL]; |
+ CheckErrorPageIsVisible(); |
+ OpenNewIncognitoTabUsingUIAndEvictMainTabs(); |
+ |
+ SwitchToNormalMode(); |
+ CheckErrorPageIsVisible(); |
+ |
+ histogramTester.ExpectUniqueSample(kEvictedTabReloadSuccessRate, |
+ TabUsageRecorder::LOAD_FAILURE, 1, |
+ failureBlock); |
+ histogramTester.ExpectUniqueSample(kDidUserWaitForEvictedTabReload, |
+ TabUsageRecorder::USER_WAITED, 1, |
+ failureBlock); |
+ histogramTester.ExpectTotalCount(kEvictedTabReloadTime, 0, failureBlock); |
+} |
+ |
+// Test that USER_DID_NOT_WAIT is reported if the user does not wait for the |
+// reload to be complete after eviction. |
+- (void)testEvictedTabSlowReload { |
+ std::map<GURL, std::string> responses; |
+ const GURL slowURL = web::test::HttpServer::MakeUrl("http://slow"); |
+ responses[slowURL] = "Slow Page"; |
+ |
+ web::test::SetUpHttpServer(base::MakeUnique<web::DelayedResponseProvider>( |
+ base::MakeUnique<HtmlResponseProvider>(responses), kSlowURLDelay)); |
+ |
+ chrome_test_util::HistogramTester histogramTester; |
+ FailureBlock failureBlock = ^(NSString* error) { |
+ GREYFail(error); |
+ }; |
+ |
+ // A blank tab needed to switch to it after reloading. |
+ chrome_test_util::OpenNewTab(); |
+ chrome_test_util::OpenNewTab(); |
+ chrome_test_util::LoadUrl(slowURL); |
+ OpenNewIncognitoTabUsingUIAndEvictMainTabs(); |
+ |
+ web::test::SetUpHttpServer(base::MakeUnique<web::DelayedResponseProvider>( |
+ base::MakeUnique<HtmlResponseProvider>(responses), kSlowURLDelay)); |
+ |
+ SwitchToNormalMode(); |
+ |
+ GREYAssert( |
+ [[GREYCondition conditionWithName:@"Wait for tab to restart loading." |
+ block:^BOOL() { |
+ return chrome_test_util::IsLoading(); |
+ }] waitWithTimeout:kWaitElementTimeout], |
+ @"Tab did not start loading."); |
+ |
+ // This method is not synced on EarlGrey. |
+ chrome_test_util::SelectTabAtIndexInCurrentMode(0); |
+ |
+ // Do not test the kEvictedTabReloadSuccessRate, as the timing of the two |
+ // page loads cannot be guaranteed. The test would be flaky. |
+ histogramTester.ExpectBucketCount(kDidUserWaitForEvictedTabReload, |
+ TabUsageRecorder::USER_DID_NOT_WAIT, 1, |
+ failureBlock); |
+} |
+ |
+// Test that the USER_DID_NOT_WAIT metric is logged when the user opens an NTP |
+// while the evicted tab is still reloading. |
+- (void)testEvictedTabReloadSwitchToNTP { |
+ std::map<GURL, std::string> responses; |
+ const GURL slowURL = web::test::HttpServer::MakeUrl("http://slow"); |
+ responses[slowURL] = "Slow Page"; |
+ |
+ web::test::SetUpHttpServer(base::MakeUnique<HtmlResponseProvider>(responses)); |
+ |
+ chrome_test_util::HistogramTester histogramTester; |
+ FailureBlock failureBlock = ^(NSString* error) { |
+ GREYFail(error); |
+ }; |
+ |
+ NewMainTabWithURL(slowURL, "Slow"); |
+ |
+ OpenNewIncognitoTabUsingUIAndEvictMainTabs(); |
+ web::test::SetUpHttpServer(base::MakeUnique<web::DelayedResponseProvider>( |
+ base::MakeUnique<HtmlResponseProvider>(responses), kSlowURLDelay)); |
+ |
+ SwitchToNormalMode(); |
+ |
+ // TODO(crbug.com/640977): EarlGrey synchronize on some animations when a |
+ // page is loading. Need to handle synchronization manually for this test. |
+ [[GREYConfiguration sharedInstance] |
+ setValue:@(NO) |
+ forConfigKey:kGREYConfigKeySynchronizationEnabled]; |
+ OpenNewMainTabUsingUIUnsynced(); |
+ [[GREYConfiguration sharedInstance] |
+ setValue:@(YES) |
+ forConfigKey:kGREYConfigKeySynchronizationEnabled]; |
+ histogramTester.ExpectBucketCount(kDidUserWaitForEvictedTabReload, |
+ TabUsageRecorder::USER_DID_NOT_WAIT, 1, |
+ failureBlock); |
+} |
+ |
+// Test that the USER_DID_NOT_WAIT metric is not logged when the user opens |
+// and closes the settings UI while the evicted tab is still reloading. |
+- (void)testEvictedTabReloadSettingsAndBack { |
+ std::map<GURL, std::string> responses; |
+ const GURL slowURL = web::test::HttpServer::MakeUrl("http://slow"); |
+ responses[slowURL] = "Slow Page"; |
+ |
+ web::test::SetUpHttpServer(base::MakeUnique<web::DelayedResponseProvider>( |
+ base::MakeUnique<HtmlResponseProvider>(responses), kSlowURLDelay)); |
+ |
+ chrome_test_util::HistogramTester histogramTester; |
+ FailureBlock failureBlock = ^(NSString* error) { |
+ GREYFail(error); |
+ }; |
+ |
+ NewMainTabWithURL(slowURL, responses[slowURL]); |
+ |
+ OpenNewIncognitoTabUsingUIAndEvictMainTabs(); |
+ |
+ SwitchToNormalMode(); |
+ // TODO(crbug.com/640977): EarlGrey synchronize on some animations when a |
+ // page is loading. Need to handle synchronization manually for this test. |
+ [[GREYConfiguration sharedInstance] |
+ setValue:@(NO) |
+ forConfigKey:kGREYConfigKeySynchronizationEnabled]; |
+ OpenSettingsMenuUnsynced(); |
+ OpenSettingsSubMenuUnsynced(IDS_OPTIONS_ADVANCED_SECTION_TITLE_PRIVACY); |
+ Wait(grey_accessibilityID(kPrivacyCollectionViewId), |
+ @"Privacy settings view."); |
+ |
+ WaitAndTap(grey_accessibilityLabel( |
+ l10n_util::GetNSString(IDS_IOS_NAVIGATION_BAR_DONE_BUTTON)), |
+ @"Close settings"); |
+ [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::webViewContainingText( |
+ responses[slowURL])] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ [[GREYConfiguration sharedInstance] |
+ setValue:@(YES) |
+ forConfigKey:kGREYConfigKeySynchronizationEnabled]; |
+ |
+ histogramTester.ExpectBucketCount(kDidUserWaitForEvictedTabReload, |
+ TabUsageRecorder::USER_DID_NOT_WAIT, 0, |
+ failureBlock); |
+ histogramTester.ExpectBucketCount(kDidUserWaitForEvictedTabReload, |
+ TabUsageRecorder::USER_WAITED, 1, |
+ failureBlock); |
+} |
+ |
+// Tests that leaving Chrome while an evicted tab is reloading triggers the |
+// recording of the USER_LEFT_CHROME metric. |
+- (void)testEvictedTabReloadBackgrounded { |
+ std::map<GURL, std::string> responses; |
+ const GURL slowURL = web::test::HttpServer::MakeUrl("http://slow"); |
+ responses[slowURL] = "Slow Page"; |
+ |
+ web::test::SetUpHttpServer(base::MakeUnique<HtmlResponseProvider>(responses)); |
+ |
+ chrome_test_util::HistogramTester histogramTester; |
+ chrome_test_util::OpenNewTab(); |
+ chrome_test_util::LoadUrl(slowURL); |
+ |
+ OpenNewIncognitoTabUsingUIAndEvictMainTabs(); |
+ |
+ web::test::SetUpHttpServer(base::MakeUnique<web::DelayedResponseProvider>( |
+ base::MakeUnique<HtmlResponseProvider>(responses), kSlowURLDelay)); |
+ SwitchToNormalMode(); |
+ |
+ // TODO(crbug.com/640977): EarlGrey synchronize on some animations when a |
+ // page is loading. Need to handle synchronization manually for this test. |
+ [[GREYConfiguration sharedInstance] |
+ setValue:@(NO) |
+ forConfigKey:kGREYConfigKeySynchronizationEnabled]; |
+ id<GREYMatcher> toolMenuMatcher = |
+ grey_accessibilityID(kToolbarToolsMenuButtonIdentifier); |
+ Wait(toolMenuMatcher, @"Tool Menu"); |
+ |
+ GREYAssertTrue(chrome_test_util::SimulateTabsBackgrounding(), |
+ @"Failed to simulate tab backgrounding."); |
+ [[GREYConfiguration sharedInstance] |
+ setValue:@(YES) |
+ forConfigKey:kGREYConfigKeySynchronizationEnabled]; |
+ |
+ FailureBlock failureBlock = ^(NSString* error) { |
+ GREYFail(error); |
+ }; |
+ histogramTester.ExpectBucketCount(kDidUserWaitForEvictedTabReload, |
+ TabUsageRecorder::USER_LEFT_CHROME, 1, |
+ failureBlock); |
+} |
+ |
+// Tests that backgrounding a tab that was not evicted while it is loading does |
+// not record the USER_LEFT_CHROME metric. |
+- (void)testLiveTabReloadBackgrounded { |
+ std::map<GURL, std::string> responses; |
+ const GURL slowURL = web::test::HttpServer::MakeUrl("http://slow"); |
+ responses[slowURL] = "Slow Page"; |
+ |
+ web::test::SetUpHttpServer(base::MakeUnique<web::DelayedResponseProvider>( |
+ base::MakeUnique<HtmlResponseProvider>(responses), kSlowURLDelay)); |
+ |
+ chrome_test_util::HistogramTester histogramTester; |
+ |
+ // We need two tabs to be able to switch. |
+ chrome_test_util::OpenNewTab(); |
+ [[GREYConfiguration sharedInstance] |
+ setValue:@(NO) |
+ forConfigKey:kGREYConfigKeySynchronizationEnabled]; |
+ chrome_test_util::LoadUrl(slowURL); |
+ |
+ // Ensure loading starts but is not finished. |
+ base::test::ios::SpinRunLoopWithMaxDelay(base::TimeDelta::FromSeconds(1)); |
+ chrome_test_util::SelectTabAtIndexInCurrentMode(0); |
+ [[GREYConfiguration sharedInstance] |
+ setValue:@(YES) |
+ forConfigKey:kGREYConfigKeySynchronizationEnabled]; |
+ |
+ FailureBlock failureBlock = ^(NSString* error) { |
+ GREYFail(error); |
+ }; |
+ histogramTester.ExpectBucketCount(kDidUserWaitForEvictedTabReload, |
+ TabUsageRecorder::USER_LEFT_CHROME, 0, |
+ failureBlock); |
+} |
+ |
+// Tests that redirecting pages are not reloaded after eviction. |
+- (void)testPageRedirect { |
+ GURL redirectURL = web::test::HttpServer::MakeUrl( |
+ "http://ios/testing/data/http_server_files/redirect_refresh.html"); |
+ GURL destinationURL = web::test::HttpServer::MakeUrl( |
+ "http://ios/testing/data/http_server_files/destination.html"); |
+ web::test::SetUpFileBasedHttpServer(); |
+ chrome_test_util::HistogramTester histogramTester; |
+ ResetTabUsageRecorder(); |
+ |
+ NewMainTabWithURL(redirectURL, "arrived"); |
+ |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::omniboxText( |
+ destinationURL.GetContent())] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ NSUInteger tabIndex = chrome_test_util::GetMainTabCount() - 1; |
+ chrome_test_util::OpenNewTab(); |
+ OpenNewIncognitoTabUsingUIAndEvictMainTabs(); |
+ SwitchToNormalMode(); |
+ chrome_test_util::SelectTabAtIndexInCurrentMode(tabIndex); |
+ [[EarlGrey selectElementWithMatcher:chrome_test_util::webViewContainingText( |
+ "arrived")] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ FailureBlock failureBlock = ^(NSString* error) { |
+ GREYFail(error); |
+ }; |
+ // Verify that one page-load count has been recorded. It should contain a |
+ // sum of 1 - one sample with 1 page load. |
+ histogramTester.ExpectTotalCount(kPageLoadsBeforeEvictedTabSelected, 1, |
+ failureBlock); |
+ |
+ std::unique_ptr<base::HistogramSamples> samples = |
+ histogramTester.GetHistogramSamplesSinceCreation( |
+ kPageLoadsBeforeEvictedTabSelected); |
+ int sampleSum = samples->sum(); |
+ GREYAssertEqual( |
+ sampleSum, 1, |
+ [NSString stringWithFormat:@"Expected page loads is %d, actual is %d.", 1, |
+ sampleSum]); |
+} |
+ |
+// Tests that navigations are correctly reported in |
+// Tab.PageLoadsSinceLastSwitchToEvictedTab histogram. |
+- (void)testLinkClickNavigation { |
+ // Create map of canned responses and set up the test HTML server. |
+ std::map<GURL, std::string> responses; |
+ const GURL initialURL = |
+ web::test::HttpServer::MakeUrl("http://scenarioTestLinkClickNavigation"); |
+ const GURL destinationURL = |
+ web::test::HttpServer::MakeUrl("http://destination"); |
+ responses[initialURL] = base::StringPrintf( |
+ "<body><a style='margin-left:50px' href='%s' id='link'>link</a></body>", |
+ destinationURL.spec().c_str()); |
+ responses[destinationURL] = "Whee!"; |
+ web::test::SetUpHttpServer(base::MakeUnique<HtmlResponseProvider>(responses)); |
+ chrome_test_util::HistogramTester histogramTester; |
+ ResetTabUsageRecorder(); |
+ |
+ // Open a tab with a link to click. |
+ NewMainTabWithURL(initialURL, "link"); |
+ // Click the link. |
+ chrome_test_util::TapWebViewElementWithId("link"); |
+ |
+ [[EarlGrey |
+ selectElementWithMatcher:chrome_test_util::webViewContainingText("Whee")] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ NSUInteger tabIndex = chrome_test_util::GetMainTabCount() - 1; |
+ chrome_test_util::OpenNewTab(); |
+ OpenNewIncognitoTabUsingUIAndEvictMainTabs(); |
+ SwitchToNormalMode(); |
+ chrome_test_util::SelectTabAtIndexInCurrentMode(tabIndex); |
+ [[EarlGrey |
+ selectElementWithMatcher:chrome_test_util::webViewContainingText("Whee")] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Verify that the page-load count has been recorded. It should contain a |
+ // sum of 2 - one sample with 2 page loads. |
+ std::unique_ptr<base::HistogramSamples> samples = |
+ histogramTester.GetHistogramSamplesSinceCreation( |
+ kPageLoadsBeforeEvictedTabSelected); |
+ int sampleSum = samples->sum(); |
+ GREYAssertEqual( |
+ sampleSum, 2, |
+ [NSString stringWithFormat:@"Expected page loads is %d, actual %d.", 2, |
+ sampleSum]); |
+ |
+ FailureBlock failureBlock = ^(NSString* error) { |
+ GREYFail(error); |
+ }; |
+ // Verify that only one evicted tab was selected. This is to make sure the |
+ // link click did not generate an evicted-tab-reload count. |
+ histogramTester.ExpectBucketCount(kSelectedTabHistogramName, |
+ TabUsageRecorder::EVICTED, 1, failureBlock); |
+} |
+ |
+// Tests that opening links in a new tab will not evict the source tab. |
+- (void)testOpenLinkInNewTab { |
+ // Create map of canned responses and set up the test HTML server. |
+ std::map<GURL, std::string> responses; |
+ const GURL initialURL = |
+ web::test::HttpServer::MakeUrl("http://scenarioTestOpenLinkInNewTab"); |
+ const GURL destinationURL = |
+ web::test::HttpServer::MakeUrl("http://destination"); |
+ // Make the link that cover the whole page so that long pressing the web view |
+ // will trigger the link context menu. |
+ responses[initialURL] = base::StringPrintf( |
+ "<body style='width:auto; height:auto;'><a href='%s' " |
+ "id='link'><div style='width:100%%; " |
+ "height:100%%;'>link</div></a></body>", |
+ destinationURL.spec().c_str()); |
+ responses[destinationURL] = "Whee!"; |
+ web::test::SetUpHttpServer(base::MakeUnique<HtmlResponseProvider>(responses)); |
+ chrome_test_util::HistogramTester histogramTester; |
+ ResetTabUsageRecorder(); |
+ |
+ // Open a tab with a link to click. |
+ NewMainTabWithURL(initialURL, "link"); |
+ |
+ int numberOfTabs = chrome_test_util::GetMainTabCount(); |
+ [[EarlGrey |
+ selectElementWithMatcher:chrome_test_util::webViewContainingText("link")] |
+ performAction:grey_longPress()]; |
+ |
+ [[EarlGrey |
+ selectElementWithMatcher:grey_text(l10n_util::GetNSString( |
+ IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWTAB))] |
+ performAction:grey_tap()]; |
+ chrome_test_util::AssertMainTabCount(numberOfTabs + 1); |
+ |
+ SelectTabUsingUI(base::SysUTF8ToNSString(destinationURL.GetContent())); |
+ |
+ [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; |
+ [[EarlGrey |
+ selectElementWithMatcher:chrome_test_util::webViewContainingText("Whee")] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ FailureBlock failureBlock = ^(NSString* error) { |
+ GREYFail(error); |
+ }; |
+ histogramTester.ExpectTotalCount(kSelectedTabHistogramName, 1, failureBlock); |
+ histogramTester.ExpectBucketCount( |
+ kSelectedTabHistogramName, TabUsageRecorder::IN_MEMORY, 1, failureBlock); |
+} |
+ |
+// Tests that opening tabs from external app will not cause tab eviction. |
+- (void)testOpenFromApp { |
+ web::test::SetUpFileBasedHttpServer(); |
+ chrome_test_util::HistogramTester histogramTester; |
+ ResetTabUsageRecorder(); |
+ |
+ chrome_test_util::OpenNewTab(); |
+ GURL url(kTestUrl1); |
+ |
+ chrome_test_util::OpenChromeFromExternalApp(url); |
+ |
+ // Add a delay to ensure the tab has fully opened. Because the check below |
+ // is for zero metrics recorded, it adds no flakiness. However, this pause |
+ // makes the step more likely to fail in failure cases. I.e. without it, this |
+ // test would sometimes pass even when it should fail. |
+ base::test::ios::SpinRunLoopWithMaxDelay( |
+ base::TimeDelta::FromMilliseconds(500)); |
+ |
+ FailureBlock failureBlock = ^(NSString* error) { |
+ GREYFail(error); |
+ }; |
+ // Verify that zero Tab.StatusWhenSwitchedBackToForeground metrics were |
+ // recorded. Tabs created at the time the user switches to them should not |
+ // be counted in this metric. |
+ histogramTester.ExpectTotalCount(kSelectedTabHistogramName, 0, failureBlock); |
+} |
+ |
+// Verify that evicted tabs that are deleted are removed from the evicted tabs |
+// map. |
+- (void)testTabDeletion { |
+ web::test::SetUpFileBasedHttpServer(); |
+ chrome_test_util::HistogramTester histogramTester; |
+ ResetTabUsageRecorder(); |
+ // Add an autorelease pool to delete the closed tabs before the end of the |
+ // test. |
+ @autoreleasepool { |
+ // Open two tabs with urls. |
+ OpenTwoTabs(); |
+ // Set the normal tabs as 'cold start' tabs. |
+ chrome_test_util::SetCurrentTabsToBeColdStartTabs(); |
+ // One more tab. |
+ const GURL url1 = web::test::HttpServer::MakeUrl(kTestUrl1); |
+ NewMainTabWithURL(url1, kURL1FirstWord); |
+ |
+ GREYAssertEqual(chrome_test_util::GetMainTabCount(), 3, |
+ @"Check number of normal tabs"); |
+ // The cold start tab which was not active will still be evicted. |
+ GREYAssertEqual(chrome_test_util::GetEvictedMainTabCount(), 1, |
+ @"Check number of evicted tabs"); |
+ |
+ // Close two of the three open tabs without selecting them first. |
+ // This should delete the tab objects, even though they're still being |
+ // tracked |
+ // by the tab usage recorder in its |evicted_tabs_| map. |
+ CloseTabAtIndexAndSync(1); |
+ |
+ GREYAssertEqual(chrome_test_util::GetMainTabCount(), 2, |
+ @"Check number of normal tabs"); |
+ CloseTabAtIndexAndSync(0); |
+ GREYAssertEqual(chrome_test_util::GetMainTabCount(), 1, |
+ @"Check number of normal tabs"); |
+ [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; |
+ } |
+ // The deleted tabs are purged during foregrounding and backgrounding. |
+ chrome_test_util::SimulateTabsBackgrounding(); |
+ // Make sure |evicted_tabs_| purged the deleted tabs. |
+ int evicted = chrome_test_util::GetEvictedMainTabCount(); |
+ GREYAssertEqual(evicted, 0, @"Check number of evicted tabs"); |
+} |
+ |
+@end |