1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
|
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/metrics/tab_footprint_aggregator.h"
#include <string_view>
#include <utility>
#include "components/ukm/test_ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "testing/gtest/include/gtest/gtest.h"
using ukm::builders::Memory_TabFootprint;
namespace {
static const ukm::SourceId kSourceId1 = 1;
static const ukm::SourceId kSourceId2 = 2;
static const base::ProcessId kProcId1 = 3;
static const base::ProcessId kProcId2 = 4;
static const base::ProcessId kProcId3 = 5;
static const TabFootprintAggregator::PageId kPageId1 = 6;
static const TabFootprintAggregator::PageId kPageId2 = 7;
// For a given metric, a |ResultMap| encodes what value that metric should have
// on a per ukm::SourceId basis.
// Use a multi-map because we want to be able to test against multiple records
// being emitted for a single URL. This scenario resembles a user having
// multiple tabs open for the same top-level-navigation.
using ResultMap = std::multimap<ukm::SourceId, int64_t>;
} // namespace
class TabFootprintAggregatorTest : public testing::Test {
protected:
// Walk through |mock_recorder_|'s UKM entries to collect |metric_name|
// values.
ResultMap CollectResults(std::string_view metric_name) const {
ResultMap result;
for (const ukm::mojom::UkmEntry* entry :
mock_recorder_.GetEntriesByName(Memory_TabFootprint::kEntryName)) {
const int64_t* metric_value =
mock_recorder_.GetEntryMetric(entry, metric_name);
if (metric_value == nullptr) {
// Undefined attributes are signalled with a null pointer from
// |GetEntryMetric|. Memory_TabFootprint events are supposed to leave
// some attributes undefined in certain circumstances so we won't add
// an entry to |result| in this case.
continue;
}
result.insert(std::make_pair(entry->source_id, *metric_value));
}
return result;
}
ResultMap MainFrameResults() const {
return CollectResults(Memory_TabFootprint::kMainFrameProcessPMFName);
}
ResultMap SubFrameTotalResults() const {
return CollectResults(Memory_TabFootprint::kSubFrameProcessPMF_TotalName);
}
ResultMap SubFrameIncludedResults() const {
return CollectResults(
Memory_TabFootprint::kSubFrameProcessPMF_IncludedName);
}
ResultMap SubFrameExcludedResults() const {
return CollectResults(
Memory_TabFootprint::kSubFrameProcessPMF_ExcludedName);
}
ResultMap TabTotalResults() const {
return CollectResults(Memory_TabFootprint::kTabPMFName);
}
ukm::TestUkmRecorder mock_recorder_;
};
TEST_F(TabFootprintAggregatorTest, TestEmpty) {
TabFootprintAggregator empty;
// All renderers were excluded from analysis.
empty.RecordPmfs(&mock_recorder_);
EXPECT_EQ(0u, mock_recorder_.entries_count());
// With no calls to |AssociateMainFrame| or |AssociateSubFrame| we expect no
// UKM events to exist.
EXPECT_EQ(ResultMap(), MainFrameResults());
EXPECT_EQ(ResultMap(), SubFrameTotalResults());
EXPECT_EQ(ResultMap(), SubFrameIncludedResults());
EXPECT_EQ(ResultMap(), SubFrameExcludedResults());
EXPECT_EQ(ResultMap(), TabTotalResults());
}
TEST_F(TabFootprintAggregatorTest, TestSimple) {
TabFootprintAggregator accumulator;
// One page with a main frame renderer.
accumulator.AssociateMainFrame(kSourceId1, kProcId1, kPageId1, 11 * 1024);
accumulator.RecordPmfs(&mock_recorder_);
EXPECT_EQ(1u, mock_recorder_.entries_count());
EXPECT_EQ(ResultMap({{kSourceId1, 11}}), MainFrameResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}}), SubFrameTotalResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}}), SubFrameIncludedResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}}), SubFrameExcludedResults());
EXPECT_EQ(ResultMap({{kSourceId1, 11}}), TabTotalResults());
}
TEST_F(TabFootprintAggregatorTest, TestSubOnly) {
TabFootprintAggregator accumulator;
// One page with one sub-frame renderer.
// This case shouldn't happen in practice right now (there's always a main
// frame if there's a sub-frame) but the class under test supports this.
accumulator.AssociateSubFrame(kSourceId1, kProcId1, kPageId1, 11 * 1024);
accumulator.RecordPmfs(&mock_recorder_);
EXPECT_EQ(1u, mock_recorder_.entries_count());
EXPECT_EQ(ResultMap(), MainFrameResults());
EXPECT_EQ(ResultMap({{kSourceId1, 11}}), SubFrameTotalResults());
EXPECT_EQ(ResultMap({{kSourceId1, 1}}), SubFrameIncludedResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}}), SubFrameExcludedResults());
EXPECT_EQ(ResultMap(), TabTotalResults());
}
TEST_F(TabFootprintAggregatorTest, TestMainWithSub) {
TabFootprintAggregator accumulator;
// One page with a main frame and a sub-frame that are co-hosted by one
// renderer.
accumulator.AssociateMainFrame(kSourceId1, kProcId1, kPageId1, 11 * 1024);
accumulator.AssociateSubFrame(kSourceId1, kProcId2, kPageId1, 22 * 1024);
accumulator.RecordPmfs(&mock_recorder_);
EXPECT_EQ(1u, mock_recorder_.entries_count());
EXPECT_EQ(ResultMap({{kSourceId1, 11}}), MainFrameResults());
EXPECT_EQ(ResultMap({{kSourceId1, 22}}), SubFrameTotalResults());
EXPECT_EQ(ResultMap({{kSourceId1, 1}}), SubFrameIncludedResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}}), SubFrameExcludedResults());
EXPECT_EQ(ResultMap({{kSourceId1, 33}}), TabTotalResults());
}
TEST_F(TabFootprintAggregatorTest, TestTwoPages) {
TabFootprintAggregator accumulator;
// Two pages with distinct URLs and their main frames have their own renderer
// process.
accumulator.AssociateMainFrame(kSourceId1, kProcId1, kPageId1, 11 * 1024);
accumulator.AssociateMainFrame(kSourceId2, kProcId2, kPageId2, 22 * 1024);
accumulator.RecordPmfs(&mock_recorder_);
EXPECT_EQ(2u, mock_recorder_.entries_count());
EXPECT_EQ(ResultMap({{kSourceId1, 11}, {kSourceId2, 22}}),
MainFrameResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}, {kSourceId2, 0}}),
SubFrameTotalResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}, {kSourceId2, 0}}),
SubFrameIncludedResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}, {kSourceId2, 0}}),
SubFrameExcludedResults());
EXPECT_EQ(ResultMap({{kSourceId1, 11}, {kSourceId2, 22}}), TabTotalResults());
}
TEST_F(TabFootprintAggregatorTest, TestMainFrameOverload) {
TabFootprintAggregator accumulator;
// Two pages with distinct URLs but their main frames are co-hosted by one
// renderer.
accumulator.AssociateMainFrame(kSourceId1, kProcId1, kPageId1, 11 * 1024);
accumulator.AssociateMainFrame(kSourceId2, kProcId1, kPageId2, 11 * 1024);
// Note that we're emitting records with an undefined MainFrameProcessPMF on
// purpose.
accumulator.RecordPmfs(&mock_recorder_);
EXPECT_EQ(2u, mock_recorder_.entries_count());
EXPECT_EQ(ResultMap(), MainFrameResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}, {kSourceId2, 0}}),
SubFrameTotalResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}, {kSourceId2, 0}}),
SubFrameIncludedResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}, {kSourceId2, 0}}),
SubFrameExcludedResults());
EXPECT_EQ(ResultMap(), TabTotalResults());
}
TEST_F(TabFootprintAggregatorTest, TestSharedSubframeRenderer) {
TabFootprintAggregator accumulator;
// Two pages with distinct URLs and their own main frame hosts but they each
// have sub-frames that are co-hosted in a third render process.
accumulator.AssociateMainFrame(kSourceId1, kProcId1, kPageId1, 11 * 1024);
accumulator.AssociateMainFrame(kSourceId2, kProcId2, kPageId2, 22 * 1024);
accumulator.AssociateSubFrame(kSourceId1, kProcId3, kPageId1, 33 * 1024);
accumulator.AssociateSubFrame(kSourceId2, kProcId3, kPageId2, 33 * 1024);
accumulator.RecordPmfs(&mock_recorder_);
EXPECT_EQ(2u, mock_recorder_.entries_count());
EXPECT_EQ(ResultMap({{kSourceId1, 11}, {kSourceId2, 22}}),
MainFrameResults());
// Since the sub-frames are on separate pages but share a renderer process,
// their contribution to SubFrameProcessPMF.Total is skipped.
EXPECT_EQ(ResultMap({{kSourceId1, 0}, {kSourceId2, 0}}),
SubFrameTotalResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}, {kSourceId2, 0}}),
SubFrameIncludedResults());
EXPECT_EQ(ResultMap({{kSourceId1, 1}, {kSourceId2, 1}}),
SubFrameExcludedResults());
EXPECT_EQ(ResultMap(), TabTotalResults());
}
TEST_F(TabFootprintAggregatorTest, TestCohostedDuplicateTabs) {
TabFootprintAggregator accumulator;
// Two pages with the same URL which share a render process.
accumulator.AssociateMainFrame(kSourceId1, kProcId1, kPageId1, 11 * 1024);
accumulator.AssociateMainFrame(kSourceId1, kProcId1, kPageId2, 11 * 1024);
accumulator.RecordPmfs(&mock_recorder_);
EXPECT_EQ(2u, mock_recorder_.entries_count());
EXPECT_EQ(ResultMap(), MainFrameResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}, {kSourceId1, 0}}),
SubFrameTotalResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}, {kSourceId1, 0}}),
SubFrameIncludedResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}, {kSourceId1, 0}}),
SubFrameExcludedResults());
EXPECT_EQ(ResultMap(), TabTotalResults());
}
TEST_F(TabFootprintAggregatorTest, TestDuplicateMainframeTabs) {
TabFootprintAggregator accumulator;
// Two pages with the same URL but their own render process.
accumulator.AssociateMainFrame(kSourceId1, kProcId1, kPageId1, 11 * 1024);
accumulator.AssociateMainFrame(kSourceId1, kProcId2, kPageId2, 22 * 1024);
accumulator.RecordPmfs(&mock_recorder_);
EXPECT_EQ(2u, mock_recorder_.entries_count());
EXPECT_EQ(ResultMap({{kSourceId1, 11}, {kSourceId1, 22}}),
MainFrameResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}, {kSourceId1, 0}}),
SubFrameTotalResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}, {kSourceId1, 0}}),
SubFrameIncludedResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}, {kSourceId1, 0}}),
SubFrameExcludedResults());
EXPECT_EQ(ResultMap({{kSourceId1, 11}, {kSourceId1, 22}}), TabTotalResults());
}
TEST_F(TabFootprintAggregatorTest, TestDuplicateTabsSharedSubframes) {
TabFootprintAggregator accumulator;
// Two pages with the same URL and their own main-frame render process but a
// third render process with subframes from each of the pages.
accumulator.AssociateMainFrame(kSourceId1, kProcId1, kPageId1, 11 * 1024);
accumulator.AssociateMainFrame(kSourceId1, kProcId2, kPageId2, 22 * 1024);
accumulator.AssociateSubFrame(kSourceId1, kProcId3, kPageId1, 33 * 1024);
accumulator.AssociateSubFrame(kSourceId1, kProcId3, kPageId2, 33 * 1024);
accumulator.RecordPmfs(&mock_recorder_);
EXPECT_EQ(2u, mock_recorder_.entries_count());
EXPECT_EQ(ResultMap({{kSourceId1, 11}, {kSourceId1, 22}}),
MainFrameResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}, {kSourceId1, 0}}),
SubFrameTotalResults());
EXPECT_EQ(ResultMap({{kSourceId1, 0}, {kSourceId1, 0}}),
SubFrameIncludedResults());
EXPECT_EQ(ResultMap({{kSourceId1, 1}, {kSourceId1, 1}}),
SubFrameExcludedResults());
EXPECT_EQ(ResultMap(), TabTotalResults());
}
|