blob: ca63cc5260f3260b33e94abcde04ed946ac9fa19 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "build/build_config.h"
#include "chrome/browser/battery/battery_metrics.h"
#include "chrome/browser/browser_process.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/metrics/content/subprocess_metrics_provider.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "services/device/public/mojom/battery_monitor.mojom.h"
#include "services/device/public/mojom/battery_status.mojom.h"
namespace {
// Retries fetching |histogram_name| until it contains at least |count| samples.
void RetryForHistogramBucketUntilCountReached(
base::HistogramTester* histogram_tester,
const std::string& histogram_name,
base::HistogramBase::Sample32 target_bucket,
size_t count) {
base::RunLoop().RunUntilIdle();
for (size_t attempt = 0; attempt < 50; ++attempt) {
const std::vector<base::Bucket> buckets =
histogram_tester->GetAllSamples(histogram_name);
size_t total_count = 0;
for (const auto& bucket : buckets) {
if (bucket.min == target_bucket)
total_count += bucket.count;
}
if (total_count >= count)
return;
content::FetchHistogramsFromChildProcesses();
metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
base::RunLoop().RunUntilIdle();
}
}
// Replaces the platform specific implementation of BatteryMonitor.
class MockBatteryMonitor : public device::mojom::BatteryMonitor {
public:
MockBatteryMonitor() = default;
MockBatteryMonitor(const MockBatteryMonitor&) = delete;
MockBatteryMonitor& operator=(const MockBatteryMonitor&) = delete;
~MockBatteryMonitor() override = default;
void Bind(mojo::PendingReceiver<device::mojom::BatteryMonitor> receiver) {
DCHECK(!receiver_.is_bound());
receiver_.Bind(std::move(receiver));
}
void DidChange(const device::mojom::BatteryStatus& battery_status) {
status_ = battery_status;
status_to_report_ = true;
if (!callback_.is_null())
ReportStatus();
}
void CloseReceiver() { receiver_.reset(); }
private:
// mojom::BatteryMonitor methods:
void QueryNextStatus(QueryNextStatusCallback callback) override {
if (!callback_.is_null()) {
receiver_.reset();
return;
}
callback_ = std::move(callback);
if (status_to_report_)
ReportStatus();
}
void ReportStatus() {
std::move(callback_).Run(status_.Clone());
status_to_report_ = false;
}
QueryNextStatusCallback callback_;
device::mojom::BatteryStatus status_;
bool status_to_report_ = false;
mojo::Receiver<device::mojom::BatteryMonitor> receiver_{this};
};
// Test that the metricss around battery usage are recorded correctly.
class BatteryMetricsBrowserTest : public InProcessBrowserTest {
public:
BatteryMetricsBrowserTest() = default;
BatteryMetricsBrowserTest(const BatteryMetricsBrowserTest&) = delete;
BatteryMetricsBrowserTest& operator=(const BatteryMetricsBrowserTest&) =
delete;
~BatteryMetricsBrowserTest() override = default;
protected:
MockBatteryMonitor* mock_battery_monitor() {
return mock_battery_monitor_.get();
}
private:
void SetUpOnMainThread() override {
mock_battery_monitor_ = std::make_unique<MockBatteryMonitor>();
BatteryMetrics::OverrideBatteryMonitorBinderForTesting(
base::BindRepeating(&MockBatteryMonitor::Bind,
base::Unretained(mock_battery_monitor_.get())));
}
void TearDownOnMainThread() override {
BatteryMetrics::OverrideBatteryMonitorBinderForTesting(
base::NullCallback());
InProcessBrowserTest::TearDownOnMainThread();
}
std::unique_ptr<MockBatteryMonitor> mock_battery_monitor_;
};
IN_PROC_BROWSER_TEST_F(BatteryMetricsBrowserTest, BatteryDropUMA) {
// Verify that drops in battery level are recorded, and drops by less than 1%
// are aggregated together until there is a full percentage drop.
device::mojom::BatteryStatus status;
status.charging = false;
status.charging_time = std::numeric_limits<double>::infinity();
status.discharging_time = 100;
status.level = 0.6;
mock_battery_monitor()->DidChange(status);
base::HistogramTester histogram_tester;
// A drop of 10.9% should record in the 10 bucket.
status.level = 0.491;
mock_battery_monitor()->DidChange(status);
RetryForHistogramBucketUntilCountReached(&histogram_tester,
"Power.BatteryPercentDrop", 10, 1);
// .9% should be stored from the previous drop, so an additional drop to 1%
// should be recorded in the 1% bucket.
status.level = 0.49;
mock_battery_monitor()->DidChange(status);
RetryForHistogramBucketUntilCountReached(&histogram_tester,
"Power.BatteryPercentDrop", 1, 1);
}
} // namespace