910e62b5创建于 1月15日历史提交
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/android/battery_metrics.h"

#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace content {

using Delta = AndroidBatteryMetrics::EnergyConsumedTracker::Delta;

bool operator==(const Delta& lhs, const Delta& rhs) {
  return lhs.subsystem == rhs.subsystem &&
         lhs.energy_consumed_mwh == rhs.energy_consumed_mwh;
}

namespace {

using LoadingScenario = performance_scenarios::LoadingScenario;
using InputScenario = performance_scenarios::InputScenario;
using Subsystem = AndroidBatteryMetrics::EnergyConsumedTracker::Subsystem;
using ::testing::IsEmpty;
using ::testing::UnorderedElementsAre;

TEST(PerformanceScenarioTracker, UnknownScenarioByDefault) {
  AndroidBatteryMetrics::PerformanceScenarioTracker tracker;
  EXPECT_EQ(tracker.GetMetricSuffix(),
            "UnknownLoadingScenario_UnknownInputScenario");
}

TEST(PerformanceScenarioTracker, UpdateSetsNewScenario) {
  AndroidBatteryMetrics::PerformanceScenarioTracker tracker;
  tracker.UpdateLoadingScenario(LoadingScenario::kNoPageLoading);
  tracker.UpdateInputScenario(InputScenario::kNoInput);
  EXPECT_EQ(tracker.GetMetricSuffix(), "NoPageLoading_NoInput");
}

TEST(PerformanceScenarioTracker, InputTypingTakesPrecedenceOverNoInput) {
  AndroidBatteryMetrics::PerformanceScenarioTracker tracker;
  tracker.UpdateInputScenario(InputScenario::kNoInput);
  tracker.UpdateInputScenario(InputScenario::kTyping);
  tracker.UpdateInputScenario(InputScenario::kNoInput);
  EXPECT_EQ(tracker.GetMetricSuffix(), "UnknownLoadingScenario_Typing");
}

TEST(PerformanceScenarioTracker, InputTapTakesPrecedenceOverTyping) {
  AndroidBatteryMetrics::PerformanceScenarioTracker tracker;
  tracker.UpdateInputScenario(InputScenario::kNoInput);
  tracker.UpdateInputScenario(InputScenario::kTyping);
  tracker.UpdateInputScenario(InputScenario::kTap);
  tracker.UpdateInputScenario(InputScenario::kTyping);
  tracker.UpdateInputScenario(InputScenario::kNoInput);
  EXPECT_EQ(tracker.GetMetricSuffix(), "UnknownLoadingScenario_Tap");
}

TEST(PerformanceScenarioTracker, InputScrollTakesPrecedence) {
  AndroidBatteryMetrics::PerformanceScenarioTracker tracker;
  tracker.UpdateInputScenario(InputScenario::kNoInput);
  tracker.UpdateInputScenario(InputScenario::kTyping);
  tracker.UpdateInputScenario(InputScenario::kTap);
  tracker.UpdateInputScenario(InputScenario::kScroll);
  tracker.UpdateInputScenario(InputScenario::kTap);
  tracker.UpdateInputScenario(InputScenario::kTyping);
  tracker.UpdateInputScenario(InputScenario::kNoInput);
  EXPECT_EQ(tracker.GetMetricSuffix(), "UnknownLoadingScenario_Scroll");
}

TEST(PerformanceScenarioTracker, FocusedLoadingTakesPrecedence) {
  AndroidBatteryMetrics::PerformanceScenarioTracker tracker;
  tracker.UpdateLoadingScenario(LoadingScenario::kNoPageLoading);
  tracker.UpdateLoadingScenario(LoadingScenario::kBackgroundPageLoading);
  tracker.UpdateLoadingScenario(LoadingScenario::kVisiblePageLoading);
  tracker.UpdateLoadingScenario(LoadingScenario::kFocusedPageLoading);
  tracker.UpdateLoadingScenario(LoadingScenario::kVisiblePageLoading);
  tracker.UpdateLoadingScenario(LoadingScenario::kBackgroundPageLoading);
  tracker.UpdateLoadingScenario(LoadingScenario::kNoPageLoading);
  // No loading -> background loading -> visible loading -> focused loading ->
  // visible loading -> background loading -> no loading. The overall scenario
  // is "FocusedPageLoading".
  EXPECT_EQ(tracker.GetMetricSuffix(),
            "FocusedPageLoading_UnknownInputScenario");
}

TEST(PerformanceScenarioTracker,
     VisibleLoadingTakesPrecedenceOverBackgroundLoading) {
  AndroidBatteryMetrics::PerformanceScenarioTracker tracker;
  tracker.UpdateLoadingScenario(LoadingScenario::kNoPageLoading);
  tracker.UpdateLoadingScenario(LoadingScenario::kBackgroundPageLoading);
  tracker.UpdateLoadingScenario(LoadingScenario::kVisiblePageLoading);
  tracker.UpdateLoadingScenario(LoadingScenario::kBackgroundPageLoading);
  tracker.UpdateLoadingScenario(LoadingScenario::kNoPageLoading);
  // No loading -> background loading -> visible loading ->
  // background loading -> no loading. The overall scenario is
  // "VisiblePageLoading".
  EXPECT_EQ(tracker.GetMetricSuffix(),
            "VisiblePageLoading_UnknownInputScenario");
}

TEST(PerformanceScenarioTracker,
     BackgroundLoadingTakesPrecedenceOverNoLoading) {
  AndroidBatteryMetrics::PerformanceScenarioTracker tracker;
  tracker.UpdateLoadingScenario(LoadingScenario::kNoPageLoading);
  tracker.UpdateLoadingScenario(LoadingScenario::kBackgroundPageLoading);
  tracker.UpdateLoadingScenario(LoadingScenario::kNoPageLoading);
  // No loading -> background loading -> no loading. The overall scenario is
  // "BackgroundPageLoading".
  EXPECT_EQ(tracker.GetMetricSuffix(),
            "BackgroundPageLoading_UnknownInputScenario");
}

TEST(PerformanceScenarioTracker, ResetSetsLatestScenario) {
  AndroidBatteryMetrics::PerformanceScenarioTracker tracker;
  // Visible loading + typing first.
  tracker.UpdateLoadingScenario(LoadingScenario::kVisiblePageLoading);
  tracker.UpdateInputScenario(InputScenario::kTyping);
  // No loading and no input after it.
  tracker.UpdateLoadingScenario(LoadingScenario::kNoPageLoading);
  tracker.UpdateInputScenario(InputScenario::kNoInput);
  // Visible loading and typing take precedence (see Precedence tests above).
  ASSERT_EQ(tracker.GetMetricSuffix(), "VisiblePageLoading_Typing");

  tracker.UseLatestScenarios();
  // Visible loading + typing is no longer in the tracked window.
  EXPECT_EQ(tracker.GetMetricSuffix(), "NoPageLoading_NoInput");
}

TEST(EnergyConsumedTracker, InitiallyEmptyDeltas) {
  AndroidBatteryMetrics::EnergyConsumedTracker tracker;
  // No previous values in the tracker, so no deltas to report.
  EXPECT_THAT(tracker.GetDeltas({{"CPU", 10000}}), IsEmpty());
}

TEST(EnergyConsumedTracker, AllKnownSubsystems) {
  AndroidBatteryMetrics::EnergyConsumedTracker tracker;
  tracker.UpdatePowerMonitorReadings(
      {{"CPU", 3600}, {"GPU", 10 * 3600}, {"DISPLAY", 100 * 3600}});
  EXPECT_THAT(
      tracker.GetDeltas(
          {{"CPU", 2 * 3600}, {"GPU", 20 * 3600}, {"DISPLAY", 200 * 3600}}),
      UnorderedElementsAre(Delta{Subsystem::kCpu, 1},
                           Delta{Subsystem::kGpu, 10},
                           Delta{Subsystem::kDisplay, 100}));
}

TEST(EnergyConsumedTracker, UnknownSubsystemsReportedAsOther) {
  AndroidBatteryMetrics::EnergyConsumedTracker tracker;
  tracker.UpdatePowerMonitorReadings(
      {{"Unknown1", 3600}, {"Unknown2", 10 * 3600}});
  // All deltas from unknown subsystems reported together as "Other".
  EXPECT_THAT(
      tracker.GetDeltas({{"Unknown1", 2 * 3600}, {"Unknown2", 20 * 3600}}),
      UnorderedElementsAre(Delta{Subsystem::kOther, 11}));
}

TEST(EnergyConsumedTracker, SumsUpDifferentCpus) {
  AndroidBatteryMetrics::EnergyConsumedTracker tracker;
  tracker.UpdatePowerMonitorReadings(
      {{"CPU/0", 3600}, {"CPU/1", 10 * 3600}, {"CPU/2", 100 * 3600}});
  EXPECT_THAT(
      tracker.GetDeltas(
          {{"CPU/0", 2 * 3600}, {"CPU/1", 20 * 3600}, {"CPU/2", 200 * 3600}}),
      UnorderedElementsAre(Delta{Subsystem::kCpu, 111}));
}

TEST(EnergyConsumedTracker, DoesNotReportNegativeValues) {
  AndroidBatteryMetrics::EnergyConsumedTracker tracker;
  tracker.UpdatePowerMonitorReadings({{"CPU", 10 * 3600}});
  // The new value is lower than the previous one. The tracker should report 0,
  // not a negative value.
  EXPECT_THAT(tracker.GetDeltas({{"CPU", 3600}}),
              UnorderedElementsAre(Delta{Subsystem::kCpu, 0}));
}

TEST(EnergyConsumedTracker, TreatsZerosAsErrors) {
  AndroidBatteryMetrics::EnergyConsumedTracker tracker;
  tracker.UpdatePowerMonitorReadings({{"CPU", 3600}});
  // 0 value for total energy is an error. The tracker should not report any
  // deltas.
  EXPECT_THAT(tracker.GetDeltas({{"CPU", 0}}), IsEmpty());
}

}  // namespace
}  // namespace content