blob: 74e458673430af1ac5941b4754a7c2cd7162ef1c [file] [log] [blame]
// Copyright 2021 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/power/power_metrics_reporter.h"
#include <memory>
#include <optional>
#include <queue>
#include "base/memory/raw_ptr.h"
#include "base/strings/strcat.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/lifetime/browser_shutdown.h"
#include "chrome/browser/metrics/power/power_metrics_constants.h"
#include "chrome/browser/metrics/power/process_monitor.h"
#include "chrome/browser/metrics/usage_scenario/usage_scenario_data_store.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/test/browser_task_environment.h"
#include "services/metrics/public/cpp/metrics_utils.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
base::BatteryLevelProvider::BatteryState MakeBatteryDischargingState(
int battery_percent) {
return base::BatteryLevelProvider::BatteryState{
.battery_count = 1,
.is_external_power_connected = false,
.current_capacity = battery_percent,
.full_charged_capacity = 100,
.charge_unit = base::BatteryLevelProvider::BatteryLevelUnit::kMWh,
.capture_time = base::TimeTicks::Now()};
}
ProcessMonitor::Metrics GetFakeProcessMetrics(bool with_cpu_usage = true) {
ProcessMonitor::Metrics metrics;
if (with_cpu_usage) {
metrics.cpu_usage = 5;
}
return metrics;
}
struct HistogramSampleExpectation {
std::string histogram_name_prefix;
std::optional<base::Histogram::Sample32> sample;
};
// For each histogram named after the combination of prefixes from
// `expectations` and suffixes from `suffixes`, verifies that there is a unique
// sample `expectation.sample`, or no sample if `expectation.sample` is nullopt.
void ExpectHistogramSamples(
base::HistogramTester* histogram_tester,
const std::vector<const char*>& suffixes,
const std::vector<HistogramSampleExpectation>& expectations) {
for (const char* suffix : suffixes) {
for (const auto& expectation : expectations) {
std::string histogram_name =
base::StrCat({expectation.histogram_name_prefix, suffix});
SCOPED_TRACE(histogram_name);
if (expectation.sample.has_value()) {
histogram_tester->ExpectUniqueSample(histogram_name,
expectation.sample.value(), 1);
} else {
histogram_tester->ExpectTotalCount(histogram_name, 0);
}
}
}
}
using UkmEntry = ukm::builders::PowerUsageScenariosIntervalData;
class FakeBatteryLevelProvider : public base::BatteryLevelProvider {
public:
explicit FakeBatteryLevelProvider(
std::queue<std::optional<base::BatteryLevelProvider::BatteryState>>*
battery_states)
: battery_states_(battery_states) {}
void GetBatteryState(
base::OnceCallback<void(const std::optional<BatteryState>&)> callback)
override {
DCHECK(!battery_states_->empty());
auto state = battery_states_->front();
battery_states_->pop();
std::move(callback).Run(state);
}
private:
raw_ptr<std::queue<std::optional<base::BatteryLevelProvider::BatteryState>>>
battery_states_;
};
class TestProcessMonitor : public ProcessMonitor {
public:
TestProcessMonitor() = default;
TestProcessMonitor(const TestProcessMonitor& rhs) = delete;
TestProcessMonitor& operator=(const TestProcessMonitor& rhs) = delete;
~TestProcessMonitor() override = default;
void SetMetricsToReturn(const Metrics& metrics) { metrics_ = metrics; }
void SampleAllProcesses(Observer* observer) override {
if (!periodic_sampling_enabled_)
return;
observer->OnAggregatedMetricsSampled(metrics_);
}
// Used to force a call to `OnAggregatedMetricsSampled` at any time, instead
// of being called automatically by the interval timer.
void ForceSampleAllProcessesIn(Observer* observer,
base::test::TaskEnvironment* task_environment,
base::TimeDelta time_delta) {
periodic_sampling_enabled_ = false;
task_environment->FastForwardBy(time_delta);
observer->OnAggregatedMetricsSampled(metrics_);
}
private:
Metrics metrics_;
// Indicates if the periodic sampling driven by the interval timer is enabled.
// Only disabled when `ForceSampleAllProcessesIn` is used.
bool periodic_sampling_enabled_ = true;
};
class TestUsageScenarioDataStoreImpl : public UsageScenarioDataStoreImpl {
public:
TestUsageScenarioDataStoreImpl() = default;
TestUsageScenarioDataStoreImpl(const TestUsageScenarioDataStoreImpl& rhs) =
delete;
TestUsageScenarioDataStoreImpl& operator=(
const TestUsageScenarioDataStoreImpl& rhs) = delete;
~TestUsageScenarioDataStoreImpl() override = default;
IntervalData ResetIntervalData() override { return fake_data_; }
void SetIntervalDataToReturn(IntervalData data) { fake_data_ = data; }
private:
IntervalData fake_data_;
};
// This doesn't use the typical {class being tested}Test name pattern because
// there's already a PowerMetricsReporterTest class in the chromeos namespace
// and this conflicts with it.
class PowerMetricsReporterUnitTestBase : public testing::Test {
public:
PowerMetricsReporterUnitTestBase() = default;
PowerMetricsReporterUnitTestBase(
const PowerMetricsReporterUnitTestBase& rhs) = delete;
PowerMetricsReporterUnitTestBase& operator=(
const PowerMetricsReporterUnitTestBase& rhs) = delete;
~PowerMetricsReporterUnitTestBase() override = default;
void SetUp() override {
auto battery_provider = CreateBatteryLevelProvider();
battery_provider_ = battery_provider.get();
power_metrics_reporter_ = std::make_unique<PowerMetricsReporter>(
&process_monitor_, &long_data_store_, std::move(battery_provider));
// Ensure the first battery state is sampled.
task_environment_.RunUntilIdle();
}
protected:
virtual std::unique_ptr<base::BatteryLevelProvider>
CreateBatteryLevelProvider() = 0;
content::BrowserTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
TestProcessMonitor process_monitor_;
TestUsageScenarioDataStoreImpl long_data_store_;
base::HistogramTester histogram_tester_;
ukm::TestAutoSetUkmRecorder test_ukm_recorder_;
raw_ptr<base::BatteryLevelProvider, DanglingUntriaged> battery_provider_;
std::unique_ptr<PowerMetricsReporter> power_metrics_reporter_;
};
class PowerMetricsReporterUnitTest : public PowerMetricsReporterUnitTestBase {
public:
~PowerMetricsReporterUnitTest() override = default;
std::unique_ptr<base::BatteryLevelProvider> CreateBatteryLevelProvider()
override {
// Start with a half-full battery
battery_states_.push(MakeBatteryDischargingState(50));
auto battery_provider =
std::make_unique<FakeBatteryLevelProvider>(&battery_states_);
return std::move(battery_provider);
}
protected:
std::queue<std::optional<base::BatteryLevelProvider::BatteryState>>
battery_states_;
};
class PowerMetricsReporterWithoutBatteryLevelProviderUnitTest
: public PowerMetricsReporterUnitTestBase {
public:
~PowerMetricsReporterWithoutBatteryLevelProviderUnitTest() override = default;
std::unique_ptr<base::BatteryLevelProvider> CreateBatteryLevelProvider()
override {
return nullptr;
}
};
} // namespace
TEST_F(PowerMetricsReporterWithoutBatteryLevelProviderUnitTest,
CPUTimeRecorded) {
process_monitor_.SetMetricsToReturn(GetFakeProcessMetrics());
UsageScenarioDataStore::IntervalData interval_data;
interval_data.max_tab_count = 1;
interval_data.max_visible_window_count = 1;
interval_data.time_capturing_video = base::Seconds(1);
long_data_store_.SetIntervalDataToReturn(interval_data);
task_environment_.FastForwardBy(kLongPowerMetricsIntervalDuration);
const char* kScenarioSuffix = ".VideoCapture";
const std::vector<const char*> suffixes({"", kScenarioSuffix});
#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_ARM64)
// Windows ARM64 does not support Constant Rate TSC so
// PerformanceMonitor.AverageCPU9.Total is not recorded there.
ExpectHistogramSamples(
&histogram_tester_, suffixes,
{{"PerformanceMonitor.AverageCPU9.Total", std::nullopt}});
#else
ExpectHistogramSamples(&histogram_tester_, suffixes,
{{"PerformanceMonitor.AverageCPU9.Total", 500}});
#endif
}
TEST_F(PowerMetricsReporterWithoutBatteryLevelProviderUnitTest,
CPUTimeMissing) {
process_monitor_.SetMetricsToReturn(
GetFakeProcessMetrics(/*with_cpu_usage=*/false));
UsageScenarioDataStore::IntervalData interval_data;
interval_data.max_tab_count = 1;
interval_data.max_visible_window_count = 1;
interval_data.time_capturing_video = base::Seconds(1);
long_data_store_.SetIntervalDataToReturn(interval_data);
task_environment_.FastForwardBy(kLongPowerMetricsIntervalDuration);
const char* kScenarioSuffix = ".VideoCapture";
const std::vector<const char*> suffixes({"", kScenarioSuffix});
#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_ARM64)
// Windows ARM64 does not support Constant Rate TSC so
// PerformanceMonitor.AverageCPU9.Total is not recorded there.
ExpectHistogramSamples(
&histogram_tester_, suffixes,
{{"PerformanceMonitor.AverageCPU9.Total", std::nullopt}});
#else
// Missing `cpu_usage` recorded as 0.
ExpectHistogramSamples(&histogram_tester_, suffixes,
{{"PerformanceMonitor.AverageCPU9.Total", 0}});
#endif
}
TEST_F(PowerMetricsReporterUnitTest, LongIntervalHistograms) {
process_monitor_.SetMetricsToReturn(GetFakeProcessMetrics());
battery_states_.push(MakeBatteryDischargingState(30));
UsageScenarioDataStore::IntervalData interval_data;
interval_data.max_tab_count = 1;
interval_data.max_visible_window_count = 1;
interval_data.time_capturing_video = base::Seconds(1);
long_data_store_.SetIntervalDataToReturn(interval_data);
task_environment_.FastForwardBy(kLongPowerMetricsIntervalDuration);
const char* kScenarioSuffix = ".VideoCapture";
const std::vector<const char*> suffixes({"", kScenarioSuffix});
#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_ARM64)
// Windows ARM64 does not support Constant Rate TSC so
// PerformanceMonitor.AverageCPU9.Total is not recorded there.
ExpectHistogramSamples(
&histogram_tester_, suffixes,
{{"PerformanceMonitor.AverageCPU9.Total", std::nullopt}});
#else
ExpectHistogramSamples(&histogram_tester_, suffixes,
{{"PerformanceMonitor.AverageCPU9.Total", 500}});
#endif
}
TEST_F(PowerMetricsReporterUnitTest, UKMs) {
int fake_value = 42;
ProcessMonitor::Metrics fake_metrics = {};
fake_metrics.cpu_usage = ++fake_value * 0.01;
#if BUILDFLAG(IS_MAC)
fake_metrics.idle_wakeups = ++fake_value;
fake_metrics.package_idle_wakeups = ++fake_value;
#endif
process_monitor_.SetMetricsToReturn(fake_metrics);
// Pretend that the battery has dropped by 20% in 2 minutes, for a rate of
// 10% per minute.
battery_states_.push(MakeBatteryDischargingState(30));
UsageScenarioDataStore::IntervalData fake_interval_data;
fake_interval_data.uptime_at_interval_end = base::Hours(++fake_value);
fake_interval_data.max_tab_count = ++fake_value;
fake_interval_data.max_visible_window_count = ++fake_value;
fake_interval_data.top_level_navigation_count = ++fake_value;
fake_interval_data.tabs_closed_during_interval = ++fake_value;
fake_interval_data.user_interaction_count = ++fake_value;
fake_interval_data.time_playing_video_full_screen_single_monitor =
base::Seconds(++fake_value);
fake_interval_data.time_with_open_webrtc_connection =
base::Seconds(++fake_value);
fake_interval_data.source_id_for_longest_visible_origin =
ukm::ConvertToSourceId(42, ukm::SourceIdType::NAVIGATION_ID);
fake_interval_data.source_id_for_longest_visible_origin_duration =
base::Seconds(++fake_value);
fake_interval_data.time_playing_video_in_visible_tab =
base::Seconds(++fake_value);
fake_interval_data.time_since_last_user_interaction_with_browser =
base::Seconds(++fake_value);
fake_interval_data.time_capturing_video = base::Seconds(++fake_value);
fake_interval_data.time_playing_audio = base::Seconds(++fake_value);
fake_interval_data.longest_visible_origin_duration =
base::Seconds(++fake_value);
fake_interval_data.sleep_events = 0;
long_data_store_.SetIntervalDataToReturn(fake_interval_data);
task_environment_.FastForwardBy(kLongPowerMetricsIntervalDuration);
auto entries = test_ukm_recorder_.GetEntriesByName(
ukm::builders::PowerUsageScenariosIntervalData::kEntryName);
EXPECT_EQ(1u, entries.size());
EXPECT_EQ(entries[0]->source_id,
fake_interval_data.source_id_for_longest_visible_origin);
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kUptimeSecondsName,
ukm::GetExponentialBucketMinForUserTiming(
fake_interval_data.uptime_at_interval_end.InSeconds()));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kBatteryDischargeRateName, 1000);
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kBatteryDischargeModeName,
static_cast<int64_t>(BatteryDischargeMode::kDischarging));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kCPUTimeMsName,
kLongPowerMetricsIntervalDuration.InSeconds() * 1000 *
fake_metrics.cpu_usage.value());
#if BUILDFLAG(IS_MAC)
test_ukm_recorder_.ExpectEntryMetric(entries[0], UkmEntry::kIdleWakeUpsName,
fake_metrics.idle_wakeups);
test_ukm_recorder_.ExpectEntryMetric(entries[0], UkmEntry::kPackageExitsName,
fake_metrics.package_idle_wakeups);
#endif
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kMaxTabCountName,
ukm::GetExponentialBucketMinForCounts1000(
fake_interval_data.max_tab_count));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kMaxVisibleWindowCountName,
ukm::GetExponentialBucketMin(fake_interval_data.max_visible_window_count,
1.05));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kTabClosedName,
ukm::GetExponentialBucketMinForCounts1000(
fake_interval_data.tabs_closed_during_interval));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kTopLevelNavigationEventsName,
ukm::GetExponentialBucketMinForCounts1000(
fake_interval_data.top_level_navigation_count));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kUserInteractionCountName,
ukm::GetExponentialBucketMinForCounts1000(
fake_interval_data.user_interaction_count));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kFullscreenVideoSingleMonitorSecondsName,
PowerMetricsReporter::GetBucketForSampleForTesting(
fake_interval_data.time_playing_video_full_screen_single_monitor));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kTimeWithOpenWebRTCConnectionSecondsName,
PowerMetricsReporter::GetBucketForSampleForTesting(
fake_interval_data.time_with_open_webrtc_connection));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kTimePlayingVideoInVisibleTabName,
PowerMetricsReporter::GetBucketForSampleForTesting(
fake_interval_data.time_playing_video_in_visible_tab));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kIntervalDurationSecondsName,
kLongPowerMetricsIntervalDuration.InSeconds());
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kTimeSinceInteractionWithBrowserSecondsName,
PowerMetricsReporter::GetBucketForSampleForTesting(
fake_interval_data.time_since_last_user_interaction_with_browser));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kVideoCaptureSecondsName,
PowerMetricsReporter::GetBucketForSampleForTesting(
fake_interval_data.time_capturing_video));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kBrowserShuttingDownName, false);
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kPlayingAudioSecondsName,
PowerMetricsReporter::GetBucketForSampleForTesting(
fake_interval_data.time_playing_audio));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kOriginVisibilityTimeSecondsName,
PowerMetricsReporter::GetBucketForSampleForTesting(
fake_interval_data.longest_visible_origin_duration));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kDeviceSleptDuringIntervalName, false);
}
TEST_F(PowerMetricsReporterUnitTest, UKMsBrowserShuttingDown) {
ProcessMonitor::Metrics fake_metrics = {};
fake_metrics.cpu_usage = 0.5;
#if BUILDFLAG(IS_MAC)
fake_metrics.idle_wakeups = 42;
fake_metrics.package_idle_wakeups = 43;
#endif
process_monitor_.SetMetricsToReturn(fake_metrics);
battery_states_.push(MakeBatteryDischargingState(50));
const ukm::SourceId kTestSourceId =
ukm::ConvertToSourceId(42, ukm::SourceIdType::NAVIGATION_ID);
UsageScenarioDataStore::IntervalData fake_interval_data = {};
fake_interval_data.source_id_for_longest_visible_origin = kTestSourceId;
long_data_store_.SetIntervalDataToReturn(fake_interval_data);
{
auto fake_shutdown = browser_shutdown::SetShutdownTypeForTesting(
browser_shutdown::ShutdownType::kBrowserExit);
EXPECT_TRUE(browser_shutdown::HasShutdownStarted());
// Advance time while `HasShutdownStarted` has been overridden.
task_environment_.FastForwardBy(kLongPowerMetricsIntervalDuration);
}
auto entries = test_ukm_recorder_.GetEntriesByName(
ukm::builders::PowerUsageScenariosIntervalData::kEntryName);
EXPECT_EQ(1u, entries.size());
EXPECT_EQ(entries[0]->source_id, kTestSourceId);
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kBrowserShuttingDownName, true);
}
TEST_F(PowerMetricsReporterUnitTest, UKMsPluggedIn) {
// Update the latest reported battery state to pretend that the system isn't
// running on battery.
power_metrics_reporter_->battery_state_for_testing()
->is_external_power_connected = true;
process_monitor_.SetMetricsToReturn({});
// Push a battery state that indicates that the system is still not running
// on battery.
battery_states_.push(base::BatteryLevelProvider::BatteryState{
.battery_count = 1,
.is_external_power_connected = true,
.current_capacity = 50,
.full_charged_capacity = 100,
.charge_unit = base::BatteryLevelProvider::BatteryLevelUnit::kMWh,
.capture_time = base::TimeTicks::Now()});
UsageScenarioDataStore::IntervalData fake_interval_data;
fake_interval_data.source_id_for_longest_visible_origin =
ukm::ConvertToSourceId(42, ukm::SourceIdType::NAVIGATION_ID);
long_data_store_.SetIntervalDataToReturn(fake_interval_data);
task_environment_.FastForwardBy(kLongPowerMetricsIntervalDuration);
auto entries = test_ukm_recorder_.GetEntriesByName(
ukm::builders::PowerUsageScenariosIntervalData::kEntryName);
EXPECT_EQ(1u, entries.size());
EXPECT_FALSE(test_ukm_recorder_.EntryHasMetric(
entries[0], UkmEntry::kBatteryDischargeRateName));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kBatteryDischargeModeName,
static_cast<int64_t>(BatteryDischargeMode::kPluggedIn));
}
TEST_F(PowerMetricsReporterUnitTest, UKMsBatteryStateChanges) {
process_monitor_.SetMetricsToReturn({});
// The initial battery state indicates that the system is running on battery,
// pretends that this has changed.
battery_states_.push(base::BatteryLevelProvider::BatteryState{
.battery_count = 1,
.is_external_power_connected = true,
.current_capacity = 100,
.full_charged_capacity = 100,
.charge_unit = base::BatteryLevelProvider::BatteryLevelUnit::kMWh,
.capture_time = base::TimeTicks::Now()});
UsageScenarioDataStore::IntervalData fake_interval_data;
fake_interval_data.source_id_for_longest_visible_origin =
ukm::ConvertToSourceId(42, ukm::SourceIdType::NAVIGATION_ID);
long_data_store_.SetIntervalDataToReturn(fake_interval_data);
task_environment_.FastForwardBy(kLongPowerMetricsIntervalDuration);
auto entries = test_ukm_recorder_.GetEntriesByName(
ukm::builders::PowerUsageScenariosIntervalData::kEntryName);
EXPECT_EQ(1u, entries.size());
EXPECT_FALSE(test_ukm_recorder_.EntryHasMetric(
entries[0], UkmEntry::kBatteryDischargeRateName));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kBatteryDischargeModeName,
static_cast<int64_t>(BatteryDischargeMode::kStateChanged));
}
TEST_F(PowerMetricsReporterUnitTest, UKMsBatteryStateUnavailable) {
process_monitor_.SetMetricsToReturn({});
// A nullopt battery value indicates that the battery level is unavailable.
battery_states_.push(std::nullopt);
UsageScenarioDataStore::IntervalData fake_interval_data;
fake_interval_data.source_id_for_longest_visible_origin =
ukm::ConvertToSourceId(42, ukm::SourceIdType::NAVIGATION_ID);
long_data_store_.SetIntervalDataToReturn(fake_interval_data);
task_environment_.FastForwardBy(kLongPowerMetricsIntervalDuration);
auto entries = test_ukm_recorder_.GetEntriesByName(
ukm::builders::PowerUsageScenariosIntervalData::kEntryName);
EXPECT_EQ(1u, entries.size());
EXPECT_FALSE(test_ukm_recorder_.EntryHasMetric(
entries[0], UkmEntry::kBatteryDischargeRateName));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kBatteryDischargeModeName,
static_cast<int64_t>(BatteryDischargeMode::kRetrievalError));
}
TEST_F(PowerMetricsReporterUnitTest, UKMsNoBattery) {
power_metrics_reporter_->battery_state_for_testing()->battery_count = 0;
power_metrics_reporter_->battery_state_for_testing()
->is_external_power_connected = true;
process_monitor_.SetMetricsToReturn({});
// Indicates that the system has no battery interface.
battery_states_.push(base::BatteryLevelProvider::BatteryState{
.battery_count = 0,
.is_external_power_connected = true,
.current_capacity = std::nullopt,
.full_charged_capacity = std::nullopt,
.charge_unit = std::nullopt,
.capture_time = base::TimeTicks::Now()});
UsageScenarioDataStore::IntervalData fake_interval_data;
fake_interval_data.source_id_for_longest_visible_origin =
ukm::ConvertToSourceId(42, ukm::SourceIdType::NAVIGATION_ID);
long_data_store_.SetIntervalDataToReturn(fake_interval_data);
task_environment_.FastForwardBy(kLongPowerMetricsIntervalDuration);
auto entries = test_ukm_recorder_.GetEntriesByName(
ukm::builders::PowerUsageScenariosIntervalData::kEntryName);
EXPECT_EQ(1u, entries.size());
EXPECT_FALSE(test_ukm_recorder_.EntryHasMetric(
entries[0], UkmEntry::kBatteryDischargeRateName));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kBatteryDischargeModeName,
static_cast<int64_t>(BatteryDischargeMode::kNoBattery));
}
#if BUILDFLAG(IS_MAC)
// Tests that on MacOS, a full |charge_level| while not plugged does not result
// in a kDischarging value emitted. See https://crbug.com/1249830.
TEST_F(PowerMetricsReporterUnitTest, UKMsMacFullyCharged) {
// Set the initial battery level at 100%.
power_metrics_reporter_->battery_state_for_testing()->current_capacity = 100;
process_monitor_.SetMetricsToReturn({});
battery_states_.push(MakeBatteryDischargingState(100));
UsageScenarioDataStore::IntervalData fake_interval_data;
fake_interval_data.source_id_for_longest_visible_origin =
ukm::ConvertToSourceId(42, ukm::SourceIdType::NAVIGATION_ID);
long_data_store_.SetIntervalDataToReturn(fake_interval_data);
task_environment_.FastForwardBy(kLongPowerMetricsIntervalDuration);
auto entries = test_ukm_recorder_.GetEntriesByName(
ukm::builders::PowerUsageScenariosIntervalData::kEntryName);
EXPECT_EQ(1u, entries.size());
EXPECT_FALSE(test_ukm_recorder_.EntryHasMetric(
entries[0], UkmEntry::kBatteryDischargeRateName));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kBatteryDischargeModeName,
static_cast<int64_t>(BatteryDischargeMode::kMacFullyCharged));
}
#endif // BUILDFLAG(IS_MAC)
TEST_F(PowerMetricsReporterUnitTest, UKMsBatteryStateIncrease) {
process_monitor_.SetMetricsToReturn({});
// Set the new battery state at 75%.
battery_states_.push(MakeBatteryDischargingState(75));
UsageScenarioDataStore::IntervalData fake_interval_data;
fake_interval_data.source_id_for_longest_visible_origin =
ukm::ConvertToSourceId(42, ukm::SourceIdType::NAVIGATION_ID);
long_data_store_.SetIntervalDataToReturn(fake_interval_data);
task_environment_.FastForwardBy(kLongPowerMetricsIntervalDuration);
auto entries = test_ukm_recorder_.GetEntriesByName(
ukm::builders::PowerUsageScenariosIntervalData::kEntryName);
EXPECT_EQ(1u, entries.size());
// An increase in charge level is reported as an invalid discharge rate.
EXPECT_FALSE(test_ukm_recorder_.EntryHasMetric(
entries[0], UkmEntry::kBatteryDischargeRateName));
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kBatteryDischargeModeName,
static_cast<int64_t>(BatteryDischargeMode::kBatteryLevelIncreased));
}
TEST_F(PowerMetricsReporterUnitTest, UKMsNoTab) {
process_monitor_.SetMetricsToReturn(GetFakeProcessMetrics());
battery_states_.push(MakeBatteryDischargingState(50));
UsageScenarioDataStore::IntervalData fake_interval_data;
fake_interval_data.max_tab_count = 0;
fake_interval_data.max_visible_window_count = 0;
fake_interval_data.source_id_for_longest_visible_origin =
ukm::kInvalidSourceId;
long_data_store_.SetIntervalDataToReturn(fake_interval_data);
task_environment_.FastForwardBy(kLongPowerMetricsIntervalDuration);
auto entries = test_ukm_recorder_.GetEntriesByName(
ukm::builders::PowerUsageScenariosIntervalData::kEntryName);
EXPECT_EQ(1u, entries.size());
EXPECT_EQ(entries[0]->source_id, ukm::kInvalidSourceId);
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kUptimeSecondsName,
ukm::GetExponentialBucketMinForUserTiming(
fake_interval_data.uptime_at_interval_end.InSeconds()));
}
TEST_F(PowerMetricsReporterUnitTest, DurationsLongerThanIntervalAreCapped) {
process_monitor_.SetMetricsToReturn(GetFakeProcessMetrics());
battery_states_.push(MakeBatteryDischargingState(50));
UsageScenarioDataStore::IntervalData fake_interval_data;
fake_interval_data.time_playing_video_full_screen_single_monitor =
kLongPowerMetricsIntervalDuration * 100;
long_data_store_.SetIntervalDataToReturn(fake_interval_data);
task_environment_.FastForwardBy(kLongPowerMetricsIntervalDuration);
auto entries = test_ukm_recorder_.GetEntriesByName(
ukm::builders::PowerUsageScenariosIntervalData::kEntryName);
EXPECT_EQ(1u, entries.size());
EXPECT_EQ(entries[0]->source_id, ukm::kInvalidSourceId);
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kFullscreenVideoSingleMonitorSecondsName,
// Every value greater than |kLongPowerMetricsIntervalDuration|
// should fall in the same overflow bucket.
PowerMetricsReporter::GetBucketForSampleForTesting(
kLongPowerMetricsIntervalDuration * 2));
}
TEST_F(PowerMetricsReporterUnitTest, UKMsWithSleepEvent) {
process_monitor_.SetMetricsToReturn({});
battery_states_.push(MakeBatteryDischargingState(50));
UsageScenarioDataStore::IntervalData fake_interval_data = {};
fake_interval_data.sleep_events = 1;
long_data_store_.SetIntervalDataToReturn(fake_interval_data);
task_environment_.FastForwardBy(kLongPowerMetricsIntervalDuration);
auto entries = test_ukm_recorder_.GetEntriesByName(
ukm::builders::PowerUsageScenariosIntervalData::kEntryName);
EXPECT_EQ(1u, entries.size());
test_ukm_recorder_.ExpectEntryMetric(
entries[0], UkmEntry::kDeviceSleptDuringIntervalName, true);
}
TEST_F(PowerMetricsReporterUnitTest, UKMsWithoutCPU) {
process_monitor_.SetMetricsToReturn(
GetFakeProcessMetrics(/*with_cpu_usage=*/false));
battery_states_.push(MakeBatteryDischargingState(30));
UsageScenarioDataStore::IntervalData fake_interval_data = {};
fake_interval_data.source_id_for_longest_visible_origin =
ukm::ConvertToSourceId(42, ukm::SourceIdType::NAVIGATION_ID);
long_data_store_.SetIntervalDataToReturn(fake_interval_data);
task_environment_.FastForwardBy(kLongPowerMetricsIntervalDuration);
auto entries = test_ukm_recorder_.GetEntriesByName(
ukm::builders::PowerUsageScenariosIntervalData::kEntryName);
ASSERT_EQ(1u, entries.size());
EXPECT_EQ(entries[0]->source_id,
fake_interval_data.source_id_for_longest_visible_origin);
// Missing `cpu_usage` should be skipped, not logged as 0% CPU.
EXPECT_FALSE(
test_ukm_recorder_.EntryHasMetric(entries[0], UkmEntry::kCPUTimeMsName));
}