chromium/chromeos/ash/components/throttle/throttle_service_unittest.cc

// 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 "chromeos/ash/components/throttle/throttle_service.h"

#include <utility>

#include "content/public/test/test_browser_context.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash {

namespace {

constexpr const char kFirstObserverName[] = "o1";
constexpr const char kSecondObserverName[] = "o2";

class TestObserver : public ThrottleService::ServiceObserver {
 public:
  TestObserver() = default;
  ~TestObserver() override = default;

  // ThrottleService::Observer:
  void OnThrottle(bool was_throttled) override {
    last_was_throttled_ = was_throttled;
    ++update_count_;
  }

  int GetUpdateCountAndReset() {
    const int update_count = update_count_;
    update_count_ = 0;
    return update_count;
  }

  bool last_was_throttled() const { return last_was_throttled_; }

 private:
  int update_count_ = 0;
  bool last_was_throttled_ = false;

  TestObserver(TestObserver const&) = delete;
  TestObserver& operator=(TestObserver const&) = delete;
};

}  // namespace

class TestThrottleService : public ThrottleService {
 public:
  using ThrottleService::ThrottleService;

  size_t throttle_instance_count() const { return throttle_instance_count_; }

  size_t uma_count() { return record_uma_counter_; }

  bool last_should_throttle() const { return last_should_throttle_; }

  const std::string& last_recorded_observer_name() {
    return last_recorded_observer_name_;
  }

 private:
  void ThrottleInstance(bool should_throttle) override {
    ++throttle_instance_count_;
    last_should_throttle_ = should_throttle;
  }

  void RecordCpuRestrictionDisabledUMA(const std::string& observer_name,
                                       base::TimeDelta delta) override {
    ++record_uma_counter_;
    last_recorded_observer_name_ = observer_name;
  }

  size_t throttle_instance_count_{0};
  size_t record_uma_counter_{0};
  std::string last_recorded_observer_name_;
  bool last_should_throttle_ = false;
};

class ThrottleServiceTest : public testing::Test {
 public:
  ThrottleServiceTest() {
    std::vector<std::unique_ptr<ThrottleObserver>> observers;
    observers.push_back(std::make_unique<ThrottleObserver>(kFirstObserverName));
    observers.push_back(
        std::make_unique<ThrottleObserver>(kSecondObserverName));
    service_.SetObserversForTesting(std::move(observers));
  }

  ThrottleServiceTest(const ThrottleServiceTest&) = delete;
  ThrottleServiceTest& operator=(const ThrottleServiceTest&) = delete;

 protected:
  TestThrottleService* service() { return &service_; }

 private:
  content::BrowserTaskEnvironment task_environment_;
  content::TestBrowserContext browser_context_;
  TestThrottleService service_{&browser_context_};
};

TEST_F(ThrottleServiceTest, TestConstructDestruct) {}

// Tests that the ThrottleService calls ThrottleInstance with the correct
// throttling when there is a change in observers, but skips the call if new
// throttling is same as before.
TEST_F(ThrottleServiceTest, TestOnObserverStateChanged) {
  EXPECT_EQ(0U, service()->throttle_instance_count());

  // Initially, it is throttled.
  service()->NotifyObserverStateChangedForTesting();
  EXPECT_EQ(1U, service()->throttle_instance_count());
  EXPECT_TRUE(service()->last_should_throttle());

  // Activate one of two observers. Verify it is unthrottled.
  service()->observers_for_testing()[0]->SetActive(true);
  EXPECT_EQ(2U, service()->throttle_instance_count());
  EXPECT_FALSE(service()->last_should_throttle());

  // Activate the other observer too. Verify ThrottleInstance() is not called.
  service()->observers_for_testing()[1]->SetActive(true);
  EXPECT_EQ(2U, service()->throttle_instance_count());
  EXPECT_FALSE(service()->last_should_throttle());

  // Deactivate one observer. Verify ThrottleInstance() is not called.
  service()->observers_for_testing()[1]->SetActive(false);
  EXPECT_EQ(2U, service()->throttle_instance_count());
  EXPECT_FALSE(service()->last_should_throttle());

  // Deactivate the other observer too. Verify ThrottleInstance() is called.
  service()->observers_for_testing()[0]->SetActive(false);
  EXPECT_EQ(3U, service()->throttle_instance_count());
  EXPECT_TRUE(service()->last_should_throttle());
}

// Tests that ArcInstanceThrottle records the duration that the effective
// observer is active.
TEST_F(ThrottleServiceTest, RecordCpuRestrictionDisabledUMA) {
  EXPECT_EQ(0U, service()->uma_count());

  // The effective observer transitions from null to the first one; no UMA
  // is recorded yet.
  service()->observers_for_testing()[0]->SetActive(true);
  EXPECT_EQ(0U, service()->uma_count());

  // The effective observer is still the first one.
  service()->observers_for_testing()[1]->SetActive(true);
  EXPECT_EQ(0U, service()->uma_count());

  // The effective observer transitions from the first one to the second one.
  // UMA should be recorded for the first one.
  service()->observers_for_testing()[0]->SetActive(false);
  EXPECT_EQ(1U, service()->uma_count());
  EXPECT_EQ(service()->observers_for_testing()[0]->name(),
            service()->last_recorded_observer_name());

  // Effective observer transitions from the second one to the first one. UMA
  // should be recorded for the second one.
  service()->observers_for_testing()[0]->SetActive(true);
  EXPECT_EQ(2U, service()->uma_count());
  EXPECT_EQ(service()->observers_for_testing()[1]->name(),
            service()->last_recorded_observer_name());

  // Effective observer transitions from the first one to null; UMA should
  // be recorded for critical_observer.
  service()->observers_for_testing()[1]->SetActive(false);
  service()->observers_for_testing()[0]->SetActive(false);
  EXPECT_EQ(3U, service()->uma_count());
  EXPECT_EQ(service()->observers_for_testing()[0]->name(),
            service()->last_recorded_observer_name());
}

// Tests that verifies enforcement mode.
TEST_F(ThrottleServiceTest, TestEnforced) {
  service()->observers_for_testing()[0]->SetActive(false);
  service()->observers_for_testing()[1]->SetActive(true);
  EXPECT_FALSE(service()->should_throttle());

  // Enforce the first observer which is not active. Verify the service is
  // throttled.
  service()->observers_for_testing()[0]->SetEnforced(true);
  EXPECT_TRUE(service()->should_throttle());

  // Stop enforcing it and verify the service is the service is unthrottled.
  service()->observers_for_testing()[0]->SetEnforced(false);
  EXPECT_FALSE(service()->should_throttle());
}

// Tests that verifies observer notifications.
TEST_F(ThrottleServiceTest, TestObservers) {
  TestObserver test_observer;
  service()->AddServiceObserver(&test_observer);

  // Activate the second observer. Verify that OnThrottle() is called.
  EXPECT_EQ(0, test_observer.GetUpdateCountAndReset());
  service()->observers_for_testing()[1]->SetActive(true);
  EXPECT_FALSE(service()->should_throttle());
  EXPECT_FALSE(test_observer.last_was_throttled());
  EXPECT_EQ(1, test_observer.GetUpdateCountAndReset());

  // Activate the first observer too. Verify that OnThrottle() is NOT called
  // because the throttling is not changed.
  service()->observers_for_testing()[0]->SetActive(true);
  EXPECT_FALSE(service()->should_throttle());
  EXPECT_FALSE(test_observer.last_was_throttled());
  EXPECT_EQ(0, test_observer.GetUpdateCountAndReset());

  // Deactivate both. Verify that OnThrottle() is called.
  service()->observers_for_testing()[0]->SetActive(false);
  EXPECT_EQ(0, test_observer.GetUpdateCountAndReset());  // not yet called
  service()->observers_for_testing()[1]->SetActive(false);
  EXPECT_TRUE(service()->should_throttle());
  EXPECT_TRUE(test_observer.last_was_throttled());
  EXPECT_EQ(1, test_observer.GetUpdateCountAndReset());

  // Remove the observer. Verify that OnThrottle() is no longer called.
  service()->RemoveServiceObserver(&test_observer);
  service()->observers_for_testing()[1]->SetActive(true);
  EXPECT_FALSE(service()->should_throttle());
  EXPECT_EQ(0, test_observer.GetUpdateCountAndReset());
}

// Tests that getting an observer by its name works.
TEST_F(ThrottleServiceTest, TestGetObserverByName) {
  auto* first_observer = service()->GetObserverByName(kFirstObserverName);
  auto* second_observer = service()->GetObserverByName(kSecondObserverName);
  EXPECT_NE(nullptr, first_observer);
  EXPECT_NE(nullptr, second_observer);
  EXPECT_NE(first_observer, second_observer);
  EXPECT_EQ(nullptr, service()->GetObserverByName("NonExistentObserverName"));
}

}  // namespace ash