| // Copyright 2017 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/macros.h" |
| #include "base/stl_util.h" |
| #import "base/test/ios/wait_util.h" |
| #include "components/metrics/metrics_service.h" |
| #include "components/metrics_services_manager/metrics_services_manager.h" |
| #include "components/strings/grit/components_strings.h" |
| #include "components/ukm/ukm_service.h" |
| #include "components/unified_consent/feature.h" |
| #include "ios/chrome/browser/application_context.h" |
| #include "ios/chrome/browser/metrics/ios_chrome_metrics_service_accessor.h" |
| #import "ios/chrome/browser/ui/authentication/cells/signin_promo_view.h" |
| #import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h" |
| #import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h" |
| #import "ios/chrome/browser/ui/tab_grid/tab_grid_egtest_util.h" |
| #include "ios/chrome/browser/ui/util/ui_util.h" |
| #include "ios/chrome/grit/ios_strings.h" |
| #import "ios/chrome/test/app/chrome_test_util.h" |
| #import "ios/chrome/test/app/tab_test_util.h" |
| #import "ios/chrome/test/earl_grey/chrome_actions.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_error_util.h" |
| #import "ios/chrome/test/earl_grey/chrome_matchers.h" |
| #import "ios/chrome/test/earl_grey/chrome_test_case.h" |
| #import "ios/public/provider/chrome/browser/signin/fake_chrome_identity.h" |
| #import "ios/public/provider/chrome/browser/signin/fake_chrome_identity_service.h" |
| #include "services/metrics/public/cpp/ukm_recorder.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| using chrome_test_util::AccountsSyncButton; |
| using chrome_test_util::ButtonWithAccessibilityLabel; |
| using chrome_test_util::ButtonWithAccessibilityLabelId; |
| using chrome_test_util::ClearBrowsingDataView; |
| using chrome_test_util::GetIncognitoTabCount; |
| using chrome_test_util::GoogleServicesSettingsButton; |
| using chrome_test_util::IsIncognitoMode; |
| using chrome_test_util::SettingsAccountButton; |
| using chrome_test_util::SettingsDoneButton; |
| using chrome_test_util::SettingsMenuPrivacyButton; |
| using chrome_test_util::SignOutAccountsButton; |
| using chrome_test_util::SyncSwitchCell; |
| using chrome_test_util::TurnSyncSwitchOn; |
| |
| namespace metrics { |
| |
| // Helper class that provides access to UKM internals. |
| class UkmEGTestHelper { |
| public: |
| UkmEGTestHelper() {} |
| |
| static bool ukm_enabled() { |
| auto* service = ukm_service(); |
| return service ? service->recording_enabled_ : false; |
| } |
| |
| static uint64_t client_id() { |
| auto* service = ukm_service(); |
| return service ? service->client_id_ : 0; |
| } |
| |
| static bool HasDummySource(ukm::SourceId source_id) { |
| auto* service = ukm_service(); |
| return service && base::ContainsKey(service->sources(), source_id); |
| } |
| |
| static void RecordDummySource(ukm::SourceId source_id) { |
| auto* service = ukm_service(); |
| if (service) |
| service->UpdateSourceURL(source_id, GURL("http://example.com")); |
| } |
| |
| private: |
| static ukm::UkmService* ukm_service() { |
| return GetApplicationContext() |
| ->GetMetricsServicesManager() |
| ->GetUkmService(); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(UkmEGTestHelper); |
| }; |
| |
| } // namespace metrics |
| |
| namespace { |
| |
| bool g_metrics_enabled = false; |
| |
| // Constant for timeout while waiting for asynchronous sync and UKM operations. |
| const NSTimeInterval kSyncUKMOperationsTimeout = 10.0; |
| |
| void AssertUKMEnabled(bool is_enabled) { |
| ConditionBlock condition = ^{ |
| return metrics::UkmEGTestHelper::ukm_enabled() == is_enabled; |
| }; |
| GREYAssert(base::test::ios::WaitUntilConditionOrTimeout( |
| kSyncUKMOperationsTimeout, condition), |
| @"Failed to assert whether UKM was enabled or not."); |
| } |
| |
| // Matcher for the Clear Browsing Data cell on the Privacy screen. |
| id<GREYMatcher> ClearBrowsingDataCell() { |
| return ButtonWithAccessibilityLabelId(IDS_IOS_CLEAR_BROWSING_DATA_TITLE); |
| } |
| // Matcher for the clear browsing data button on the clear browsing data panel. |
| id<GREYMatcher> ClearBrowsingDataButton() { |
| return ButtonWithAccessibilityLabelId(IDS_IOS_CLEAR_BUTTON); |
| } |
| |
| void ClearBrowsingData() { |
| [ChromeEarlGreyUI openSettingsMenu]; |
| [ChromeEarlGreyUI tapSettingsMenuButton:SettingsMenuPrivacyButton()]; |
| [ChromeEarlGreyUI tapPrivacyMenuButton:ClearBrowsingDataCell()]; |
| [ChromeEarlGreyUI tapClearBrowsingDataMenuButton:ClearBrowsingDataButton()]; |
| [[EarlGrey selectElementWithMatcher:chrome_test_util:: |
| ConfirmClearBrowsingDataButton()] |
| performAction:grey_tap()]; |
| |
| // Before returning, make sure that the top of the Clear Browsing Data |
| // settings screen is visible to match the state at the start of the method. |
| [[EarlGrey selectElementWithMatcher:ClearBrowsingDataView()] |
| performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)]; |
| [[EarlGrey selectElementWithMatcher:SettingsDoneButton()] |
| performAction:grey_tap()]; |
| } |
| |
| void OpenNewIncognitoTab() { |
| NSUInteger incognito_tab_count = GetIncognitoTabCount(); |
| CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewIncognitoTab]); |
| CHROME_EG_ASSERT_NO_ERROR( |
| [ChromeEarlGrey waitForIncognitoTabCount:(incognito_tab_count + 1)]); |
| GREYAssert(IsIncognitoMode(), @"Failed to switch to incognito mode."); |
| } |
| |
| void CloseCurrentIncognitoTab() { |
| NSUInteger incognito_tab_count = GetIncognitoTabCount(); |
| [ChromeEarlGrey closeCurrentTab]; |
| CHROME_EG_ASSERT_NO_ERROR( |
| [ChromeEarlGrey waitForIncognitoTabCount:(incognito_tab_count - 1)]); |
| } |
| |
| void CloseAllIncognitoTabs() { |
| CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey closeAllIncognitoTabs]); |
| CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey waitForIncognitoTabCount:0]); |
| |
| // The user is dropped into the tab grid after closing the last incognito tab. |
| // Therefore this test must manually switch back to showing the normal tabs. |
| [[EarlGrey |
| selectElementWithMatcher:chrome_test_util::TabGridOpenTabsPanelButton()] |
| performAction:grey_tap()]; |
| [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridDoneButton()] |
| performAction:grey_tap()]; |
| GREYAssert(!IsIncognitoMode(), @"Failed to switch to normal mode."); |
| } |
| |
| void OpenNewRegularTab() { |
| NSUInteger tab_count = chrome_test_util::GetMainTabCount(); |
| CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewTab]); |
| CHROME_EG_ASSERT_NO_ERROR( |
| [ChromeEarlGrey waitForMainTabCount:(tab_count + 1)]); |
| } |
| |
| // Grant/revoke metrics consent and update MetricsServicesManager. |
| void UpdateMetricsConsent(bool new_state) { |
| g_metrics_enabled = new_state; |
| GetApplicationContext()->GetMetricsServicesManager()->UpdateUploadPermissions( |
| true); |
| } |
| |
| // Signs out of sync. |
| void SignOut() { |
| [ChromeEarlGreyUI openSettingsMenu]; |
| [ChromeEarlGreyUI tapSettingsMenuButton:SettingsAccountButton()]; |
| |
| // Remove |identity| from the device. |
| ChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1]; |
| [[EarlGrey |
| selectElementWithMatcher:ButtonWithAccessibilityLabel(identity.userEmail)] |
| performAction:grey_tap()]; |
| [[EarlGrey |
| selectElementWithMatcher:ButtonWithAccessibilityLabel(@"Remove account")] |
| performAction:grey_tap()]; |
| |
| [[EarlGrey selectElementWithMatcher:SettingsDoneButton()] |
| performAction:grey_tap()]; |
| |
| CHROME_EG_ASSERT_NO_ERROR([SigninEarlGreyUtils checkSignedOut]); |
| } |
| |
| } // namespace |
| |
| // UKM tests. |
| @interface UKMTestCase : ChromeTestCase |
| |
| @end |
| |
| @implementation UKMTestCase |
| |
| + (void)setUp { |
| [super setUp]; |
| if (!base::FeatureList::IsEnabled(ukm::kUkmFeature)) { |
| // ukm::kUkmFeature feature is not enabled. You need to pass |
| // --enable-features=Ukm command line argument in order to run this test. |
| DCHECK(false); |
| } |
| } |
| |
| - (void)setUp { |
| [super setUp]; |
| |
| CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey |
| waitForSyncInitialized:NO |
| syncTimeout:kSyncUKMOperationsTimeout]); |
| AssertUKMEnabled(false); |
| |
| // Enable sync. |
| [SigninEarlGreyUI signinWithIdentity:[SigninEarlGreyUtils fakeIdentity1]]; |
| CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey |
| waitForSyncInitialized:YES |
| syncTimeout:kSyncUKMOperationsTimeout]); |
| |
| // Grant metrics consent and update MetricsServicesManager. |
| GREYAssert(!g_metrics_enabled, @"Unpaired set/reset of user consent."); |
| g_metrics_enabled = true; |
| IOSChromeMetricsServiceAccessor::SetMetricsAndCrashReportingForTesting( |
| &g_metrics_enabled); |
| GetApplicationContext()->GetMetricsServicesManager()->UpdateUploadPermissions( |
| true); |
| AssertUKMEnabled(true); |
| } |
| |
| - (void)tearDown { |
| CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey |
| waitForSyncInitialized:YES |
| syncTimeout:kSyncUKMOperationsTimeout]); |
| AssertUKMEnabled(true); |
| |
| // Revoke metrics consent and update MetricsServicesManager. |
| GREYAssert(g_metrics_enabled, @"Unpaired set/reset of user consent."); |
| g_metrics_enabled = false; |
| GetApplicationContext()->GetMetricsServicesManager()->UpdateUploadPermissions( |
| true); |
| IOSChromeMetricsServiceAccessor::SetMetricsAndCrashReportingForTesting( |
| nullptr); |
| AssertUKMEnabled(false); |
| |
| // Disable sync. |
| SignOut(); |
| CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey |
| waitForSyncInitialized:NO |
| syncTimeout:kSyncUKMOperationsTimeout]); |
| [ChromeEarlGrey clearSyncServerData]; |
| |
| [super tearDown]; |
| } |
| |
| // The tests in this file should correspond with the ones in |
| // //chrome/browser/metrics/ukm_browsertest.cc |
| |
| // Make sure that UKM is disabled while an incognito tab is open. |
| - (void)testRegularPlusIncognito { |
| uint64_t original_client_id = metrics::UkmEGTestHelper::client_id(); |
| |
| OpenNewIncognitoTab(); |
| AssertUKMEnabled(false); |
| |
| // Opening another regular tab mustn't enable UKM. |
| OpenNewRegularTab(); |
| AssertUKMEnabled(false); |
| |
| // Opening and closing an incognito tab mustn't enable UKM. |
| OpenNewIncognitoTab(); |
| AssertUKMEnabled(false); |
| CloseCurrentIncognitoTab(); |
| AssertUKMEnabled(false); |
| |
| CloseAllIncognitoTabs(); |
| AssertUKMEnabled(true); |
| |
| // Client ID should not have been reset. |
| GREYAssert(original_client_id == metrics::UkmEGTestHelper::client_id(), |
| @"Client ID was reset."); |
| } |
| |
| // Make sure opening a real tab after Incognito doesn't enable UKM. |
| - (void)testIncognitoPlusRegular { |
| uint64_t original_client_id = metrics::UkmEGTestHelper::client_id(); |
| chrome_test_util::CloseAllTabs(); |
| CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey waitForMainTabCount:(0)]); |
| |
| OpenNewIncognitoTab(); |
| AssertUKMEnabled(false); |
| |
| // Opening another regular tab mustn't enable UKM. |
| OpenNewRegularTab(); |
| AssertUKMEnabled(false); |
| |
| CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey closeAllIncognitoTabs]); |
| CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey waitForIncognitoTabCount:0]); |
| AssertUKMEnabled(true); |
| |
| // Client ID should not have been reset. |
| GREYAssert(original_client_id == metrics::UkmEGTestHelper::client_id(), |
| @"Client ID was reset."); |
| } |
| |
| // testOpenNonSync not needed, since there can't be multiple profiles. |
| |
| // Make sure that UKM is disabled when metrics consent is revoked. |
| - (void)testMetricsConsent { |
| uint64_t original_client_id = metrics::UkmEGTestHelper::client_id(); |
| |
| UpdateMetricsConsent(false); |
| |
| AssertUKMEnabled(false); |
| |
| UpdateMetricsConsent(true); |
| |
| AssertUKMEnabled(true); |
| // Client ID should have been reset. |
| GREYAssert(original_client_id != metrics::UkmEGTestHelper::client_id(), |
| @"Client ID was not reset."); |
| } |
| |
| // Make sure that providing metrics consent doesn't enable UKM w/o sync. |
| - (void)testConsentAddedButNoSync { |
| SignOut(); |
| UpdateMetricsConsent(false); |
| AssertUKMEnabled(false); |
| |
| UpdateMetricsConsent(true); |
| AssertUKMEnabled(false); |
| |
| [SigninEarlGreyUI signinWithIdentity:[SigninEarlGreyUtils fakeIdentity1]]; |
| AssertUKMEnabled(true); |
| } |
| |
| // Make sure that UKM is disabled when sync is disabled. |
| - (void)testSingleDisableSync { |
| uint64_t original_client_id = metrics::UkmEGTestHelper::client_id(); |
| |
| [ChromeEarlGreyUI openSettingsMenu]; |
| if (unified_consent::IsUnifiedConsentFeatureEnabled()) { |
| // Open Sync and Google services settings |
| [ChromeEarlGreyUI tapSettingsMenuButton:GoogleServicesSettingsButton()]; |
| // Toggle "Make searches and browsing better" switch off. |
| [[EarlGrey |
| selectElementWithMatcher:chrome_test_util::SettingsSwitchCell( |
| @"betterSearchAndBrowsingItem_switch", |
| YES)] |
| performAction:chrome_test_util::TurnSettingsSwitchOn(NO)]; |
| } else { |
| // Open accounts settings, then sync settings. |
| [[EarlGrey selectElementWithMatcher:SettingsAccountButton()] |
| performAction:grey_tap()]; |
| [[EarlGrey selectElementWithMatcher:AccountsSyncButton()] |
| performAction:grey_tap()]; |
| // Toggle "Sync Everything" then "History" switches off. |
| [[EarlGrey selectElementWithMatcher:SyncSwitchCell( |
| l10n_util::GetNSString( |
| IDS_IOS_SYNC_EVERYTHING_TITLE), |
| YES)] |
| performAction:TurnSyncSwitchOn(NO)]; |
| [[EarlGrey selectElementWithMatcher:SyncSwitchCell( |
| l10n_util::GetNSString( |
| IDS_SYNC_DATATYPE_TYPED_URLS), |
| YES)] |
| performAction:TurnSyncSwitchOn(NO)]; |
| } |
| AssertUKMEnabled(false); |
| |
| if (unified_consent::IsUnifiedConsentFeatureEnabled()) { |
| // Toggle "Make searches and browsing better" switch on. |
| [[EarlGrey |
| selectElementWithMatcher:chrome_test_util::SettingsSwitchCell( |
| @"betterSearchAndBrowsingItem_switch", NO)] |
| performAction:chrome_test_util::TurnSettingsSwitchOn(YES)]; |
| } else { |
| // Toggle "History" then "Sync Everything" switches on. |
| [[EarlGrey selectElementWithMatcher:SyncSwitchCell( |
| l10n_util::GetNSString( |
| IDS_SYNC_DATATYPE_TYPED_URLS), |
| NO)] |
| performAction:TurnSyncSwitchOn(YES)]; |
| [[EarlGrey selectElementWithMatcher:SyncSwitchCell( |
| l10n_util::GetNSString( |
| IDS_IOS_SYNC_EVERYTHING_TITLE), |
| NO)] |
| performAction:TurnSyncSwitchOn(YES)]; |
| } |
| |
| AssertUKMEnabled(true); |
| // Client ID should have been reset. |
| GREYAssert(original_client_id != metrics::UkmEGTestHelper::client_id(), |
| @"Client ID was not reset."); |
| |
| [[EarlGrey selectElementWithMatcher:SettingsDoneButton()] |
| performAction:grey_tap()]; |
| } |
| |
| // testMultiDisableSync not needed, since there can't be multiple profiles. |
| |
| // Make sure that UKM is disabled when a secondary passphrase is used. |
| - (void)testSecondaryPassphrase { |
| if (unified_consent::IsUnifiedConsentFeatureEnabled()) { |
| EARL_GREY_TEST_DISABLED( |
| @"When Unified Consent feature is enabled, setting a custom passphrase " |
| "does not disable UKM anymore, so this test is not needed"); |
| } |
| |
| uint64_t original_client_id = metrics::UkmEGTestHelper::client_id(); |
| |
| [ChromeEarlGreyUI openSettingsMenu]; |
| // Open accounts settings, then sync settings. |
| [[EarlGrey selectElementWithMatcher:SettingsAccountButton()] |
| performAction:grey_tap()]; |
| [[EarlGrey selectElementWithMatcher:AccountsSyncButton()] |
| performAction:grey_tap()]; |
| // Open sync encryption menu. |
| [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"kSettingsSyncId")] |
| performAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)]; |
| [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabelId( |
| |
| IDS_IOS_SYNC_ENCRYPTION_TITLE)] |
| performAction:grey_tap()]; |
| // Select passphrase encryption. |
| [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabelId( |
| IDS_SYNC_FULL_ENCRYPTION_DATA)] |
| performAction:grey_tap()]; |
| // Type and confirm passphrase, then submit. |
| [[EarlGrey selectElementWithMatcher:grey_accessibilityValue(@"Passphrase")] |
| performAction:grey_replaceText(@"mypassphrase")]; |
| [[EarlGrey |
| selectElementWithMatcher:grey_accessibilityValue(@"Confirm passphrase")] |
| performAction:grey_replaceText(@"mypassphrase")]; |
| |
| AssertUKMEnabled(false); |
| // Client ID should have been reset. |
| GREYAssert(original_client_id != metrics::UkmEGTestHelper::client_id(), |
| @"Client ID was not reset."); |
| |
| [[EarlGrey selectElementWithMatcher:SettingsDoneButton()] |
| performAction:grey_tap()]; |
| |
| // Reset sync back to original state. |
| SignOut(); |
| [ChromeEarlGrey clearSyncServerData]; |
| [SigninEarlGreyUI signinWithIdentity:[SigninEarlGreyUtils fakeIdentity1]]; |
| AssertUKMEnabled(true); |
| } |
| |
| // Make sure that UKM is disabled when sync is not enabled. |
| - (void)testSingleSyncSignout { |
| uint64_t original_client_id = metrics::UkmEGTestHelper::client_id(); |
| |
| SignOut(); |
| |
| AssertUKMEnabled(false); |
| // Client ID should have been reset by signout. |
| GREYAssert(original_client_id != metrics::UkmEGTestHelper::client_id(), |
| @"Client ID was not reset."); |
| |
| original_client_id = metrics::UkmEGTestHelper::client_id(); |
| [SigninEarlGreyUI signinWithIdentity:[SigninEarlGreyUtils fakeIdentity1]]; |
| |
| AssertUKMEnabled(true); |
| // Client ID should not have been reset. |
| GREYAssert(original_client_id == metrics::UkmEGTestHelper::client_id(), |
| @"Client ID was reset."); |
| } |
| |
| // testMultiSyncSignout not needed, since there can't be multiple profiles. |
| |
| // testMetricsReporting not needed, since iOS doesn't use sampling. |
| |
| // TODO(crbug.com/866598): Re-enable this test. |
| - (void)DISABLED_testHistoryDelete { |
| uint64_t original_client_id = metrics::UkmEGTestHelper::client_id(); |
| |
| const ukm::SourceId kDummySourceId = 0x54321; |
| metrics::UkmEGTestHelper::RecordDummySource(kDummySourceId); |
| GREYAssert(metrics::UkmEGTestHelper::HasDummySource(kDummySourceId), |
| @"Dummy source failed to record."); |
| |
| ClearBrowsingData(); |
| |
| // Other sources may already have been recorded since the data was cleared, |
| // but the dummy source should be gone. |
| GREYAssert(!metrics::UkmEGTestHelper::HasDummySource(kDummySourceId), |
| @"Dummy source was not purged."); |
| GREYAssert(original_client_id == metrics::UkmEGTestHelper::client_id(), |
| @"Client ID was reset."); |
| AssertUKMEnabled(true); |
| } |
| |
| @end |