| // Copyright 2018 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. |
| |
| #include <memory> |
| |
| #include "base/macros.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/resource_coordinator/tab_metrics_event.pb.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/tabs/tab_ukm_test_helper.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/interactive_test_utils.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/ukm/test_ukm_recorder.h" |
| #include "services/metrics/public/cpp/ukm_builders.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using metrics::WindowMetricsEvent; |
| using ukm::builders::TabManager_TabMetrics; |
| using ukm::builders::TabManager_WindowMetrics; |
| |
| namespace { |
| |
| const char* kEntryName = TabManager_WindowMetrics::kEntryName; |
| const char* kTestUrl = "https://example.com/"; |
| |
| } // namespace |
| |
| // Tests UKM entries generated by WindowActivityWatcher due to interactive |
| // changes to window state. |
| class WindowActivityWatcherTest : public InProcessBrowserTest { |
| protected: |
| WindowActivityWatcherTest() = default; |
| |
| // InProcessBrowserTest: |
| void PreRunTestOnMainThread() override { |
| InProcessBrowserTest::PreRunTestOnMainThread(); |
| ukm_entry_checker_ = std::make_unique<UkmEntryChecker>(); |
| } |
| |
| void SetUpOnMainThread() override { |
| // Browser is created in BrowserMain() before the test UKM recorder. |
| ASSERT_EQ(0u, ukm_entry_checker_->NumEntries(kEntryName)); |
| |
| #if defined(OS_MACOSX) |
| // On Mac, the browser window needs to be forced to the front. This will |
| // create a UKM entry for the activation because it happens after the |
| // WindowActivityWatcher creation. On other platforms, activation happens |
| // before creation, and as a result, no UKM entry is created. |
| // TODO(crbug.com/650859): Reassess after activation is restored in the |
| // focus manager. |
| ui_test_utils::BrowserActivationWaiter waiter(browser()); |
| ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); |
| waiter.WaitForActivation(); |
| ASSERT_TRUE(browser()->window()->IsActive()); |
| |
| UkmMetricMap expected_metrics({ |
| {TabManager_WindowMetrics::kWindowIdName, browser()->session_id().id()}, |
| {TabManager_WindowMetrics::kShowStateName, |
| WindowMetricsEvent::SHOW_STATE_NORMAL}, |
| {TabManager_WindowMetrics::kTypeName, WindowMetricsEvent::TYPE_TABBED}, |
| {TabManager_WindowMetrics::kIsActiveName, 1}, |
| {TabManager_WindowMetrics::kTabCountName, 1}, |
| }); |
| ukm_entry_checker_->ExpectNewEntry(kEntryName, GURL(), expected_metrics); |
| #endif |
| } |
| |
| protected: |
| std::unique_ptr<UkmEntryChecker> ukm_entry_checker_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(WindowActivityWatcherTest); |
| }; |
| |
| // Tests WindowMetrics UKMs logged by the current browser window. |
| IN_PROC_BROWSER_TEST_F(WindowActivityWatcherTest, Basic) { |
| UkmMetricMap expected_metrics({ |
| {TabManager_WindowMetrics::kWindowIdName, browser()->session_id().id()}, |
| {TabManager_WindowMetrics::kShowStateName, |
| WindowMetricsEvent::SHOW_STATE_NORMAL}, |
| {TabManager_WindowMetrics::kTypeName, WindowMetricsEvent::TYPE_TABBED}, |
| {TabManager_WindowMetrics::kIsActiveName, 1}, |
| {TabManager_WindowMetrics::kTabCountName, 1}, |
| }); |
| |
| // Updated metrics are logged after adding tabs. |
| { |
| SCOPED_TRACE(""); |
| AddTabAtIndex(1, GURL(kTestUrl), ui::PAGE_TRANSITION_LINK); |
| expected_metrics[TabManager_WindowMetrics::kTabCountName] = 2; |
| ukm_entry_checker_->ExpectNewEntry(kEntryName, GURL(), expected_metrics); |
| } |
| { |
| SCOPED_TRACE(""); |
| AddTabAtIndex(0, GURL(kTestUrl), ui::PAGE_TRANSITION_LINK); |
| expected_metrics[TabManager_WindowMetrics::kTabCountName] = 3; |
| ukm_entry_checker_->ExpectNewEntry(kEntryName, GURL(), expected_metrics); |
| } |
| |
| // Closing the window doesn't log more WindowMetrics UKMs. |
| CloseBrowserSynchronously(browser()); |
| } |
| |
| // TODO(https://crbug.com/51364): Implement BrowserWindow::Deactivate() on Mac. |
| #if !defined(OS_MACOSX) |
| // Tests WindowMetrics UKMs logged by activating/deactivating the window. |
| IN_PROC_BROWSER_TEST_F(WindowActivityWatcherTest, WindowActivation) { |
| UkmMetricMap expected_metrics({ |
| {TabManager_WindowMetrics::kWindowIdName, browser()->session_id().id()}, |
| {TabManager_WindowMetrics::kShowStateName, |
| WindowMetricsEvent::SHOW_STATE_NORMAL}, |
| {TabManager_WindowMetrics::kTypeName, WindowMetricsEvent::TYPE_TABBED}, |
| {TabManager_WindowMetrics::kIsActiveName, 0}, |
| {TabManager_WindowMetrics::kTabCountName, 1}, |
| }); |
| |
| { |
| SCOPED_TRACE(""); |
| ui_test_utils::BrowserDeactivationWaiter waiter(browser()); |
| browser()->window()->Deactivate(); |
| waiter.WaitForDeactivation(); |
| ukm_entry_checker_->ExpectNewEntry(kEntryName, GURL(), expected_metrics); |
| } |
| |
| { |
| SCOPED_TRACE(""); |
| ui_test_utils::BrowserActivationWaiter waiter(browser()); |
| browser()->window()->Activate(); |
| waiter.WaitForActivation(); |
| ASSERT_TRUE(browser()->window()->IsActive()); |
| expected_metrics[TabManager_WindowMetrics::kIsActiveName] = 1; |
| ukm_entry_checker_->ExpectNewEntry(kEntryName, GURL(), expected_metrics); |
| } |
| |
| // Closing the window doesn't log more WindowMetrics UKMs. |
| CloseBrowserSynchronously(browser()); |
| } |
| |
| // Tests WindowMetrics UKMs logged when switching between windows. |
| IN_PROC_BROWSER_TEST_F(WindowActivityWatcherTest, MultipleWindows) { |
| // Create a new browser window. |
| Browser* browser_2 = CreateBrowser(browser()->profile()); |
| { |
| SCOPED_TRACE(""); |
| ukm_entry_checker_->ExpectNewEntry( |
| kEntryName, GURL(), |
| { |
| {TabManager_WindowMetrics::kWindowIdName, |
| browser_2->session_id().id()}, |
| {TabManager_WindowMetrics::kIsActiveName, 0}, |
| }); |
| } |
| |
| // Wait for the old window to be deactivated and the new window to be |
| // activated if they aren't yet. |
| { |
| ui_test_utils::BrowserActivationWaiter waiter(browser_2); |
| waiter.WaitForActivation(); |
| } |
| { |
| ui_test_utils::BrowserDeactivationWaiter waiter(browser()); |
| waiter.WaitForDeactivation(); |
| } |
| { |
| SCOPED_TRACE(""); |
| // Check for activation and deactivation events. The exact order is |
| // platform-dependent. |
| ukm_entry_checker_->ExpectNewEntries( |
| kEntryName, {{ |
| {TabManager_WindowMetrics::kWindowIdName, |
| browser_2->session_id().id()}, |
| {TabManager_WindowMetrics::kIsActiveName, 1}, |
| }, |
| { |
| {TabManager_WindowMetrics::kWindowIdName, |
| browser()->session_id().id()}, |
| {TabManager_WindowMetrics::kIsActiveName, 0}, |
| }}); |
| } |
| |
| { |
| SCOPED_TRACE(""); |
| ui_test_utils::BrowserActivationWaiter activation_waiter(browser()); |
| ui_test_utils::BrowserDeactivationWaiter deactivation_waiter(browser_2); |
| |
| // Switch back to the first window. |
| // Note: use Activate() rather than Show() because on some platforms, Show() |
| // calls SetLastActive() before doing anything else. |
| browser()->window()->Activate(); |
| |
| activation_waiter.WaitForActivation(); |
| deactivation_waiter.WaitForDeactivation(); |
| |
| // Check for activation and deactivation events. The exact order is |
| // platform-dependent. |
| ukm_entry_checker_->ExpectNewEntries( |
| kEntryName, {{ |
| {TabManager_WindowMetrics::kWindowIdName, |
| browser()->session_id().id()}, |
| {TabManager_WindowMetrics::kIsActiveName, 1}, |
| }, |
| { |
| {TabManager_WindowMetrics::kWindowIdName, |
| browser_2->session_id().id()}, |
| {TabManager_WindowMetrics::kIsActiveName, 0}, |
| }}); |
| } |
| |
| // Closing the active window activates the second window. |
| ASSERT_EQ(0, ukm_entry_checker_->NumNewEntriesRecorded(kEntryName)); |
| |
| CloseBrowserSynchronously(browser()); |
| |
| // GetLastActive could return browser2 if browser1 was active and then was |
| // removed, so browser2 might not actually be active. |
| { |
| ui_test_utils::BrowserActivationWaiter activation_waiter(browser_2); |
| browser_2->window()->Activate(); |
| activation_waiter.WaitForActivation(); |
| } |
| EXPECT_TRUE(BrowserList::GetInstance()->GetLastActive() == browser_2); |
| EXPECT_TRUE(browser_2->window()->IsActive()); |
| { |
| SCOPED_TRACE(""); |
| // Check for activation and deactivation events. The exact order is |
| // platform-dependent. |
| ukm_entry_checker_->ExpectNewEntries( |
| kEntryName, {{ |
| {TabManager_WindowMetrics::kWindowIdName, |
| browser_2->session_id().id()}, |
| {TabManager_WindowMetrics::kIsActiveName, 1}, |
| }}); |
| } |
| |
| // Occasionally, X sends an extraneous deactivate/reactivate cycle. |
| if (ukm_entry_checker_->NumNewEntriesRecorded(kEntryName) == 2) { |
| SCOPED_TRACE(""); |
| LOG(WARNING) << "Extra deactivate/reactivate detected."; |
| ukm_entry_checker_->ExpectNewEntries( |
| kEntryName, {{ |
| {TabManager_WindowMetrics::kWindowIdName, |
| browser_2->session_id().id()}, |
| {TabManager_WindowMetrics::kIsActiveName, 0}, |
| }, |
| { |
| {TabManager_WindowMetrics::kWindowIdName, |
| browser_2->session_id().id()}, |
| {TabManager_WindowMetrics::kIsActiveName, 1}, |
| }}); |
| } |
| |
| // Ignore UKMs that might be logged from spurious activation events. |
| ukm_entry_checker_.reset(); |
| } |
| |
| // Tests we don't emit a ridiculous number of UKMs, which may indicate |
| // unintended log entries or unexpected interactions between events. |
| IN_PROC_BROWSER_TEST_F(WindowActivityWatcherTest, DontFloodUkm) { |
| // TODO(michaelpg): Update once window metrics are folded into tab metrics. |
| constexpr int kTooManyWindowMetricsEntries = 40; |
| constexpr int kTooManyTabMetricsEntries = 20; |
| |
| // Add and activate some tabs. |
| for (int i = 0; i < 3; i++) |
| AddTabAtIndex(1, GURL(kTestUrl), ui::PAGE_TRANSITION_LINK); |
| |
| // Make more windows with tabs. |
| constexpr bool kCheckNavigationSuccess = false; |
| Browser* browser_2 = CreateBrowser(browser()->profile()); |
| for (int i = 0; i < 3; i++) { |
| AddTabAtIndexToBrowser(browser_2, 1, GURL(kTestUrl), |
| ui::PAGE_TRANSITION_LINK, kCheckNavigationSuccess); |
| } |
| Browser* browser_3 = CreateBrowser(browser()->profile()); |
| for (int i = 0; i < 3; i++) { |
| AddTabAtIndexToBrowser(browser_3, 1, GURL(kTestUrl), |
| ui::PAGE_TRANSITION_LINK, kCheckNavigationSuccess); |
| } |
| |
| // Cycle between windows. |
| for (Browser* next_browser : |
| {browser_2, browser_3, browser(), browser_2, browser_3}) { |
| next_browser->window()->Activate(); |
| ui_test_utils::BrowserActivationWaiter(next_browser).WaitForActivation(); |
| } |
| |
| // Manage windows. |
| browser_2->window()->Minimize(); |
| browser_2->window()->Maximize(); |
| browser_3->window()->Maximize(); |
| browser_2->window()->Restore(); |
| browser_3->window()->Minimize(); |
| |
| CloseBrowserSynchronously(browser()); |
| CloseBrowserSynchronously(browser_2); |
| CloseBrowserSynchronously(browser_3); |
| |
| ASSERT_GT(ukm_entry_checker_->NumNewEntriesRecorded(kEntryName), 0); |
| ASSERT_LT(ukm_entry_checker_->NumNewEntriesRecorded(kEntryName), |
| kTooManyWindowMetricsEntries); |
| |
| ASSERT_GT(ukm_entry_checker_->NumNewEntriesRecorded( |
| TabManager_TabMetrics::kEntryName), |
| 0); |
| ASSERT_LT(ukm_entry_checker_->NumNewEntriesRecorded( |
| TabManager_TabMetrics::kEntryName), |
| kTooManyTabMetricsEntries); |
| } |
| #endif // !defined(OS_MACOSX) |