1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/power_metrics/system_power_monitor.h"
7 #include "base/memory/raw_ptr.h"
8 #include "base/test/task_environment.h"
9 #include "base/test/test_future.h"
10 #include "testing/gtest/include/gtest/gtest.h"
12 namespace power_metrics {
14 class FakeProvider : public EnergyMetricsProvider {
16 void set_metrics(EnergyMetrics metrics) { metrics_ = metrics; }
18 absl::optional<EnergyMetrics> CaptureMetrics() override { return metrics_; }
21 absl::optional<EnergyMetrics> metrics_;
24 class FakeDelegate : public SystemPowerMonitorDelegate {
26 void set_trace_category_enabled(bool enabled) {
27 trace_category_enabled_ = enabled;
30 void RecordSystemPower(const char* category,
31 base::TimeTicks timestamp,
32 int64_t power) override {
33 timestamp_ = timestamp;
34 if (strcmp(category, "Package Power (mW)") == 0) {
35 system_power_.package_nanojoules = static_cast<uint64_t>(power);
36 } else if (strcmp(category, "CPU Power (mW)") == 0) {
37 system_power_.cpu_nanojoules = static_cast<uint64_t>(power);
38 } else if (strcmp(category, "iGPU Power (mW)") == 0) {
39 system_power_.gpu_nanojoules = static_cast<uint64_t>(power);
40 } else if (strcmp(category, "DRAM Power (mW)") == 0) {
41 system_power_.dram_nanojoules = static_cast<uint64_t>(power);
42 } else if (strcmp(category, "Psys Power (mW)") == 0) {
43 system_power_.psys_nanojoules = static_cast<uint64_t>(power);
44 } else if (strcmp(category, "VDDCR VDD (mW)") == 0) {
45 system_power_.vdd_nanojoules = static_cast<uint64_t>(power);
46 } else if (strcmp(category, "VDDCR SOC (mW)") == 0) {
47 system_power_.soc_nanojoules = static_cast<uint64_t>(power);
48 } else if (strcmp(category, "Current Socket (mW)") == 0) {
49 system_power_.socket_nanojoules = static_cast<uint64_t>(power);
50 } else if (strcmp(category, "APU Power (mW)") == 0) {
51 system_power_.apu_nanojoules = static_cast<uint64_t>(power);
55 bool IsTraceCategoryEnabled() const override {
56 return trace_category_enabled_;
59 EnergyMetricsProvider::EnergyMetrics& SystemPower() { return system_power_; }
61 base::TimeTicks timestamp() { return timestamp_; }
64 // We use EnergyMetrics to save recorded power data in milliwatts for
66 EnergyMetricsProvider::EnergyMetrics system_power_;
67 base::TimeTicks timestamp_;
68 bool trace_category_enabled_{true};
71 class SystemPowerMonitorHelperTest : public testing::Test {
73 SystemPowerMonitorHelperTest()
74 : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
76 void SetUp() override {
77 auto provider = std::make_unique<FakeProvider>();
78 provider_ = provider.get();
79 auto delegate = std::make_unique<FakeDelegate>();
80 delegate_ = delegate.get();
81 helper_ = std::make_unique<SystemPowerMonitorHelper>(std::move(provider),
85 void TearDown() override { helper_.reset(); }
87 base::test::TaskEnvironment& task_environment() { return task_environment_; }
88 SystemPowerMonitorHelper* helper() { return helper_.get(); }
89 FakeDelegate* delegate() { return delegate_.get(); }
90 FakeProvider* provider() { return provider_.get(); }
93 base::test::TaskEnvironment task_environment_;
96 std::unique_ptr<SystemPowerMonitorHelper> helper_;
97 raw_ptr<FakeDelegate, DanglingUntriaged> delegate_;
98 raw_ptr<FakeProvider, DanglingUntriaged> provider_;
101 class SystemPowerMonitorTest : public testing::Test {
103 SystemPowerMonitorTest() : task_environment_() {}
105 void SetUp() override {
106 auto provider = std::make_unique<FakeProvider>();
108 // Assign a valid metric to provider, so the timer can start successfully.
109 provider->set_metrics({1llu});
110 monitor_.reset(new SystemPowerMonitor(std::move(provider),
111 std::make_unique<FakeDelegate>()));
114 void TearDown() override { monitor_.reset(); }
116 SystemPowerMonitor* monitor() { return monitor_.get(); }
117 base::SequenceBound<SystemPowerMonitorHelper>* helper() {
118 return monitor_->GetHelperForTesting();
122 base::test::TaskEnvironment task_environment_;
125 std::unique_ptr<SystemPowerMonitor> monitor_;
128 TEST_F(SystemPowerMonitorHelperTest, MonitorHelperStartStop) {
129 provider()->set_metrics({1llu});
132 ASSERT_TRUE(helper()->IsTimerRunningForTesting());
134 ASSERT_FALSE(helper()->IsTimerRunningForTesting());
136 ASSERT_TRUE(helper()->IsTimerRunningForTesting());
138 ASSERT_FALSE(helper()->IsTimerRunningForTesting());
141 TEST_F(SystemPowerMonitorHelperTest, TimerStartFailed_InvalidSample) {
142 // We haven't set metrics for provider, so monitor gets an
143 // absl::nullopt sample at the beginning and it will not start.
145 ASSERT_FALSE(helper()->IsTimerRunningForTesting());
148 TEST_F(SystemPowerMonitorHelperTest, TimerStartFailed_MetricsAllZero) {
149 // If the metrics are all 0, we determine that there is no valid metric
150 // provided, so monitor will not start.
151 provider()->set_metrics({});
153 ASSERT_FALSE(helper()->IsTimerRunningForTesting());
156 TEST_F(SystemPowerMonitorHelperTest, TraceCategoryEnableDisable) {
157 provider()->set_metrics({1llu});
159 delegate()->set_trace_category_enabled(false);
160 ASSERT_FALSE(delegate()->IsTraceCategoryEnabled());
162 ASSERT_FALSE(helper()->IsTimerRunningForTesting());
164 delegate()->set_trace_category_enabled(true);
165 ASSERT_TRUE(delegate()->IsTraceCategoryEnabled());
167 ASSERT_TRUE(helper()->IsTimerRunningForTesting());
170 TEST_F(SystemPowerMonitorHelperTest, TestSample) {
171 EnergyMetricsProvider::EnergyMetrics sample1 = {
172 100000llu, 100000llu, 100000llu, 100000llu, 100000llu,
173 100000llu, 100000llu, 100000llu, 100000llu};
174 EnergyMetricsProvider::EnergyMetrics sample2 = {
175 200000llu, 300000llu, 400000llu, 500000llu, 600000llu,
176 700000llu, 800000llu, 900000llu, 1000000llu};
178 provider()->set_metrics(sample1);
180 ASSERT_TRUE(helper()->IsTimerRunningForTesting());
182 provider()->set_metrics(sample2);
183 task_environment().FastForwardBy(
184 SystemPowerMonitorHelper::kDefaultSampleInterval);
185 auto power = delegate()->SystemPower();
186 EXPECT_EQ(delegate()->timestamp() +
187 SystemPowerMonitorHelper::kDefaultSampleInterval,
188 task_environment().NowTicks());
190 power.package_nanojoules,
191 (sample2.package_nanojoules - sample1.package_nanojoules) /
192 SystemPowerMonitorHelper::kDefaultSampleInterval.InMicroseconds());
194 power.cpu_nanojoules,
195 (sample2.cpu_nanojoules - sample1.cpu_nanojoules) /
196 SystemPowerMonitorHelper::kDefaultSampleInterval.InMicroseconds());
198 power.gpu_nanojoules,
199 (sample2.gpu_nanojoules - sample1.gpu_nanojoules) /
200 SystemPowerMonitorHelper::kDefaultSampleInterval.InMicroseconds());
202 power.dram_nanojoules,
203 (sample2.dram_nanojoules - sample1.dram_nanojoules) /
204 SystemPowerMonitorHelper::kDefaultSampleInterval.InMicroseconds());
206 power.psys_nanojoules,
207 (sample2.psys_nanojoules - sample1.psys_nanojoules) /
208 SystemPowerMonitorHelper::kDefaultSampleInterval.InMicroseconds());
210 power.vdd_nanojoules,
211 (sample2.vdd_nanojoules - sample1.vdd_nanojoules) /
212 SystemPowerMonitorHelper::kDefaultSampleInterval.InMicroseconds());
214 power.soc_nanojoules,
215 (sample2.soc_nanojoules - sample1.soc_nanojoules) /
216 SystemPowerMonitorHelper::kDefaultSampleInterval.InMicroseconds());
218 power.socket_nanojoules,
219 (sample2.socket_nanojoules - sample1.socket_nanojoules) /
220 SystemPowerMonitorHelper::kDefaultSampleInterval.InMicroseconds());
222 power.apu_nanojoules,
223 (sample2.apu_nanojoules - sample1.apu_nanojoules) /
224 SystemPowerMonitorHelper::kDefaultSampleInterval.InMicroseconds());
227 TEST_F(SystemPowerMonitorTest, TraceLogEnableDisable) {
228 ASSERT_NE(helper(), nullptr);
230 base::test::TestFuture<bool> future_enable;
231 monitor()->OnTraceLogEnabled();
233 ->AsyncCall(&SystemPowerMonitorHelper::IsTimerRunningForTesting)
234 .Then(base::BindOnce(
235 [](base::OnceCallback<void(bool)> callback, bool is_running) {
236 std::move(callback).Run(is_running);
238 future_enable.GetCallback()));
239 EXPECT_TRUE(future_enable.Get());
241 base::test::TestFuture<bool> future_disable;
242 monitor()->OnTraceLogDisabled();
244 ->AsyncCall(&SystemPowerMonitorHelper::IsTimerRunningForTesting)
245 .Then(base::BindOnce(
246 [](base::OnceCallback<void(bool)> callback, bool is_running) {
247 std::move(callback).Run(is_running);
249 future_disable.GetCallback()));
250 EXPECT_FALSE(future_disable.Get());
253 } // namespace power_metrics