blob: eb394e77f63fb13be6bbdfae41bc9c7d1a922089 [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/power_scheduler/power_mode_arbiter.h"
#include "base/test/task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace power_scheduler {
TEST(PowerModeArbiterTest, SingleVote) {
PowerModeArbiter arbiter;
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
std::unique_ptr<PowerModeVoter> voter1 = arbiter.NewVoter("voter1");
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
for (PowerMode mode = PowerMode::kIdle; mode <= PowerMode::kMaxValue;) {
mode = static_cast<PowerMode>(static_cast<int>(mode) + 1);
voter1->VoteFor(mode);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), mode);
}
voter1.reset();
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
}
TEST(PowerModeArbiterTest, MultipleVotes) {
PowerModeArbiter arbiter;
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
std::unique_ptr<PowerModeVoter> voter1 = arbiter.NewVoter("voter1");
std::unique_ptr<PowerModeVoter> voter2 = arbiter.NewVoter("voter2");
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
auto vote_and_expect = [&](PowerMode vote1, PowerMode vote2,
PowerMode expected) {
voter1->VoteFor(vote1);
voter2->VoteFor(vote2);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), expected)
<< "vote1: " << PowerModeToString(vote1)
<< ", vote2: " << PowerModeToString(vote2)
<< ", expected: " << PowerModeToString(expected)
<< ", actual: " << PowerModeToString(arbiter.GetActiveModeForTesting());
};
// Two votes for the same mode result in that mode.
for (PowerMode mode = PowerMode::kIdle; mode <= PowerMode::kMaxValue;) {
vote_and_expect(mode, mode, mode);
mode = static_cast<PowerMode>(static_cast<int>(mode) + 1);
}
// Charging trumps anything.
vote_and_expect(PowerMode::kCharging, PowerMode::kIdle, PowerMode::kCharging);
vote_and_expect(PowerMode::kCharging, PowerMode::kAudible,
PowerMode::kCharging);
vote_and_expect(PowerMode::kCharging, PowerMode::kLoading,
PowerMode::kCharging);
vote_and_expect(PowerMode::kCharging, PowerMode::kAnimation,
PowerMode::kCharging);
vote_and_expect(PowerMode::kCharging, PowerMode::kResponse,
PowerMode::kCharging);
vote_and_expect(PowerMode::kCharging, PowerMode::kBackground,
PowerMode::kCharging);
// Background trumps remaining modes, but not audible.
vote_and_expect(PowerMode::kBackground, PowerMode::kIdle,
PowerMode::kBackground);
vote_and_expect(PowerMode::kBackground, PowerMode::kAudible,
PowerMode::kAudible);
vote_and_expect(PowerMode::kBackground, PowerMode::kLoading,
PowerMode::kBackground);
vote_and_expect(PowerMode::kBackground, PowerMode::kAnimation,
PowerMode::kBackground);
vote_and_expect(PowerMode::kBackground, PowerMode::kResponse,
PowerMode::kBackground);
// Response trumps remaining modes.
vote_and_expect(PowerMode::kResponse, PowerMode::kIdle, PowerMode::kResponse);
vote_and_expect(PowerMode::kResponse, PowerMode::kAudible,
PowerMode::kResponse);
vote_and_expect(PowerMode::kResponse, PowerMode::kLoading,
PowerMode::kResponse);
vote_and_expect(PowerMode::kResponse, PowerMode::kAnimation,
PowerMode::kResponse);
// Animation trumps remaining modes.
vote_and_expect(PowerMode::kAnimation, PowerMode::kIdle,
PowerMode::kAnimation);
vote_and_expect(PowerMode::kAnimation, PowerMode::kAudible,
PowerMode::kAnimation);
vote_and_expect(PowerMode::kAnimation, PowerMode::kLoading,
PowerMode::kAnimation);
// Loading trumps remaining modes.
vote_and_expect(PowerMode::kLoading, PowerMode::kIdle, PowerMode::kLoading);
vote_and_expect(PowerMode::kLoading, PowerMode::kAudible,
PowerMode::kLoading);
// Audible trumps idle.
vote_and_expect(PowerMode::kAudible, PowerMode::kIdle, PowerMode::kAudible);
}
namespace {
class MockObserver : public PowerModeArbiter::Observer {
public:
~MockObserver() override = default;
MOCK_METHOD(void,
OnPowerModeChanged,
(PowerMode old_mode, PowerMode new_mode),
(override));
};
} // namespace
TEST(PowerModeArbiterTest, Observer) {
base::test::TaskEnvironment env;
PowerModeArbiter arbiter;
MockObserver observer;
// Observer is notified of initial mode right away.
EXPECT_CALL(observer, OnPowerModeChanged(PowerMode::kIdle, PowerMode::kIdle));
arbiter.AddObserver(&observer);
testing::Mock::VerifyAndClearExpectations(&observer);
std::unique_ptr<PowerModeVoter> voter1 = arbiter.NewVoter("voter1");
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
EXPECT_CALL(observer,
OnPowerModeChanged(PowerMode::kIdle, PowerMode::kAnimation));
voter1->VoteFor(PowerMode::kAnimation);
env.RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&observer);
EXPECT_CALL(observer,
OnPowerModeChanged(PowerMode::kAnimation, PowerMode::kIdle));
voter1.reset();
env.RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&observer);
arbiter.RemoveObserver(&observer);
}
TEST(PowerModeArbiterTest, ResetVoteAfterTimeout) {
base::test::TaskEnvironment env(
base::test::TaskEnvironment::TimeSource::MOCK_TIME);
PowerModeArbiter arbiter;
base::TimeDelta delta1s = base::TimeDelta::FromSeconds(1);
base::TimeDelta delta2s = base::TimeDelta::FromSeconds(2);
std::unique_ptr<PowerModeVoter> voter1 = arbiter.NewVoter("voter1");
voter1->VoteFor(PowerMode::kAnimation);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
// Reset requests before the thread pool is available are queued and executed
// on thread pool becoming available.
voter1->ResetVoteAfterTimeout(delta1s);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
env.FastForwardBy(delta1s); // Advance the time before the task is queued.
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
arbiter.OnThreadPoolAvailable();
env.RunUntilIdle(); // Execute the (non-delayed) reset task.
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
// If VoteFor() is not called before the task executes, the mode is reset.
voter1->VoteFor(PowerMode::kAnimation);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
voter1->ResetVoteAfterTimeout(delta1s);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
env.FastForwardBy(delta1s); // Execute the reset task.
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
// If the VoteFor() is called before the task executes, the mode is not reset.
voter1->VoteFor(PowerMode::kAnimation);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
voter1->ResetVoteAfterTimeout(delta1s);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
voter1->VoteFor(PowerMode::kAnimation);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
env.FastForwardBy(delta1s); // Execute the reset task.
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
// Handles multiple pending resets.
voter1->VoteFor(PowerMode::kAnimation);
std::unique_ptr<PowerModeVoter> voter2 = arbiter.NewVoter("voter2");
voter2->VoteFor(PowerMode::kCharging);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kCharging);
voter2->ResetVoteAfterTimeout(delta1s);
voter1->ResetVoteAfterTimeout(delta2s);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kCharging);
env.FastForwardBy(delta1s); // Execute the first reset task.
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
env.FastForwardBy(delta1s); // Execute the second reset task.
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
// Same thing, with reset requests scheduled in reverse order.
voter1->VoteFor(PowerMode::kAnimation);
voter2->VoteFor(PowerMode::kCharging);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kCharging);
voter1->ResetVoteAfterTimeout(delta2s);
voter2->ResetVoteAfterTimeout(delta1s);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kCharging);
env.FastForwardBy(delta1s); // Execute the first reset task.
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
env.FastForwardBy(delta1s); // Execute the second reset task.
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
// If the voter is destroyed, the task doesn't cause crashes.
voter1->VoteFor(PowerMode::kAnimation);
voter1->ResetVoteAfterTimeout(delta1s);
voter1.reset();
env.FastForwardBy(delta1s); // Execute the reset task.
}
} // namespace power_scheduler