blob: 115e8c3253558d523691e2099e6a392201a81f84 [file] [log] [blame]
// 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 "chrome/browser/ui/tabs/tab_ukm_test_helper.h"
#include "chrome/test/base/testing_profile.h"
#include "components/ukm/ukm_source.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/web_contents_tester.h"
#include "services/metrics/public/interfaces/ukm_interface.mojom.h"
using content::WebContentsTester;
namespace {
// Verifies each expected metric's value. Metrics not in |expected_metrics| are
// ignored. A metric value of |nullopt| implies the metric shouldn't exist.
void ExpectEntryMetrics(const ukm::mojom::UkmEntry& entry,
const UkmMetricMap& expected_metrics) {
// Each expected metric should match a named value in the UKM entry.
for (const UkmMetricMap::value_type pair : expected_metrics) {
if (pair.second.has_value()) {
ukm::TestUkmRecorder::ExpectEntryMetric(&entry, pair.first,
pair.second.value());
} else {
// The metric shouldn't exist.
EXPECT_FALSE(ukm::TestUkmRecorder::EntryHasMetric(&entry, pair.first))
<< " for metric: " << pair.first;
}
}
}
} // namespace
// Helper class to respond to WebContents lifecycle events we can't
// trigger/simulate.
class TestWebContentsObserver : public content::WebContentsObserver {
public:
explicit TestWebContentsObserver(content::WebContents* web_contents);
// content::WebContentsObserver:
void WebContentsDestroyed() override;
private:
DISALLOW_COPY_AND_ASSIGN(TestWebContentsObserver);
};
TestWebContentsObserver::TestWebContentsObserver(
content::WebContents* web_contents)
: content::WebContentsObserver(web_contents) {}
void TestWebContentsObserver::WebContentsDestroyed() {
// Simulate the WebContents hiding during destruction. This lets tests
// validate what is logged when a tab is destroyed.
web_contents()->WasHidden();
}
TabActivityTestBase::TabActivityTestBase() = default;
TabActivityTestBase::~TabActivityTestBase() = default;
void TabActivityTestBase::Navigate(
content::WebContents* web_contents,
const GURL& url,
ui::PageTransition page_transition = ui::PAGE_TRANSITION_LINK) {
std::unique_ptr<content::NavigationSimulator> navigation =
content::NavigationSimulator::CreateBrowserInitiated(url, web_contents);
navigation->SetTransition(page_transition);
navigation->Commit();
}
content::WebContents* TabActivityTestBase::AddWebContentsAndNavigate(
TabStripModel* tab_strip_model,
const GURL& initial_url,
ui::PageTransition page_transition) {
content::WebContents::CreateParams params(profile(), nullptr);
// Create as a background tab if there are other tabs in the tab strip.
params.initially_hidden = tab_strip_model->count() > 0;
content::WebContents* test_contents =
WebContentsTester::CreateTestWebContents(params);
// Create the TestWebContentsObserver to observe |test_contents|. When the
// WebContents is destroyed, the observer will be reset automatically.
observers_.push_back(
std::make_unique<TestWebContentsObserver>(test_contents));
tab_strip_model->AppendWebContents(test_contents, false);
Navigate(test_contents, initial_url, page_transition);
return test_contents;
}
void TabActivityTestBase::SwitchToTabAt(TabStripModel* tab_strip_model,
int new_index) {
int active_index = tab_strip_model->active_index();
EXPECT_NE(new_index, active_index);
content::WebContents* active_contents =
tab_strip_model->GetWebContentsAt(active_index);
ASSERT_TRUE(active_contents);
content::WebContents* new_contents =
tab_strip_model->GetWebContentsAt(new_index);
ASSERT_TRUE(new_contents);
// Activate the tab. Normally this would hide the active tab's aura::Window,
// which is what actually triggers TabActivityWatcher to log the change. For
// a TestWebContents, we must manually call WasHidden(), and do the reverse
// for the newly activated tab.
tab_strip_model->ActivateTabAt(new_index, true /* user_gesture */);
active_contents->WasHidden();
new_contents->WasShown();
}
UkmEntryChecker::UkmEntryChecker() = default;
UkmEntryChecker::~UkmEntryChecker() = default;
void UkmEntryChecker::ExpectNewEntry(const std::string& entry_name,
const GURL& source_url,
const UkmMetricMap& expected_metrics) {
num_entries_[entry_name]++; // There should only be 1 more entry than before.
std::vector<const ukm::mojom::UkmEntry*> entries =
ukm_recorder_.GetEntriesByName(entry_name);
EXPECT_EQ(NumEntries(entry_name), entries.size());
// Verify the entry is associated with the correct URL.
const ukm::mojom::UkmEntry* entry = entries.back();
if (!source_url.is_empty())
ukm_recorder_.ExpectEntrySourceHasUrl(entry, source_url);
ExpectEntryMetrics(*entry, expected_metrics);
}
void UkmEntryChecker::ExpectNewEntries(
const std::string& entry_name,
const SourceUkmMetricMap& expected_data) {
std::vector<const ukm::mojom::UkmEntry*> entries =
ukm_recorder_.GetEntriesByName(entry_name);
const size_t num_new_entries = expected_data.size();
const size_t num_entries = entries.size();
num_entries_[entry_name] += num_new_entries;
EXPECT_EQ(NumEntries(entry_name), entries.size());
std::set<ukm::SourceId> found_source_ids;
for (size_t i = 0; i < num_new_entries; ++i) {
const ukm::mojom::UkmEntry* entry =
entries[num_entries - num_new_entries + i];
const ukm::SourceId& source_id = entry->source_id;
const auto& expected_data_for_id = expected_data.find(source_id);
EXPECT_TRUE(expected_data_for_id != expected_data.end());
EXPECT_EQ(0u, found_source_ids.count(source_id));
found_source_ids.insert(source_id);
const std::pair<GURL, UkmMetricMap>& expected_url_metrics =
expected_data_for_id->second;
const GURL& source_url = expected_url_metrics.first;
const UkmMetricMap& expected_metrics = expected_url_metrics.second;
if (!source_url.is_empty())
ukm_recorder_.ExpectEntrySourceHasUrl(entry, source_url);
// Each expected metric should match a named value in the UKM entry.
ExpectEntryMetrics(*entry, expected_metrics);
}
}
int UkmEntryChecker::NumNewEntriesRecorded(
const std::string& entry_name) const {
const size_t current_ukm_entries =
ukm_recorder_.GetEntriesByName(entry_name).size();
const size_t previous_num_entries = NumEntries(entry_name);
CHECK(current_ukm_entries >= previous_num_entries);
return current_ukm_entries - previous_num_entries;
}
size_t UkmEntryChecker::NumEntries(const std::string& entry_name) const {
const auto it = num_entries_.find(entry_name);
return it != num_entries_.end() ? it->second : 0;
}
const ukm::mojom::UkmEntry* UkmEntryChecker::LastUkmEntry(
const std::string& entry_name) const {
std::vector<const ukm::mojom::UkmEntry*> entries =
ukm_recorder_.GetEntriesByName(entry_name);
CHECK(!entries.empty());
return entries.back();
}
ukm::SourceId UkmEntryChecker::GetSourceIdForUrl(const GURL& source_url) const {
const ukm::UkmSource* source = ukm_recorder_.GetSourceForUrl(source_url);
CHECK(source);
return source->id();
}