blob: a3ae92495fbbabb03f5256e1f246a7a16f26fbc9 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <utility>
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/sequenced_task_runner.h"
#include "base/test/task_environment.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "media/audio/alive_checker.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace {
int kCheckIntervalMs = 10;
int kTimeoutMs = 50;
} // namespace
class MockPowerObserverHelper : public PowerObserverHelper {
public:
MockPowerObserverHelper(scoped_refptr<base::SequencedTaskRunner> task_runner,
base::RepeatingClosure suspend_callback,
base::RepeatingClosure resume_callback)
: PowerObserverHelper(std::move(task_runner),
std::move(suspend_callback),
std::move(resume_callback)) {}
bool IsSuspending() const override {
DCHECK(TaskRunnerForTesting()->RunsTasksInCurrentSequence());
return is_suspending_;
}
void Suspend() {
DCHECK(TaskRunnerForTesting()->RunsTasksInCurrentSequence());
is_suspending_ = true;
SuspendCallbackForTesting()->Run();
}
void Resume() {
DCHECK(TaskRunnerForTesting()->RunsTasksInCurrentSequence());
is_suspending_ = false;
ResumeCallbackForTesting()->Run();
}
private:
bool is_suspending_ = false;
};
class AliveCheckerTest : public testing::Test {
public:
AliveCheckerTest()
: alive_checker_thread_("AliveCheckerThread"),
detected_dead_event_(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED) {
alive_checker_thread_.StartAndWaitForTesting();
}
AliveCheckerTest(const AliveCheckerTest&) = delete;
AliveCheckerTest& operator=(const AliveCheckerTest&) = delete;
void OnDetectedDead() {
EXPECT_TRUE(alive_checker_thread_.task_runner()->BelongsToCurrentThread());
detected_dead_event_.Signal();
}
std::unique_ptr<PowerObserverHelper> CreatePowerObserverHelper(
scoped_refptr<base::SequencedTaskRunner> task_runner,
base::RepeatingClosure suspend_callback,
base::RepeatingClosure resume_callback) {
std::unique_ptr<MockPowerObserverHelper> mock_power_observer_helper =
std::make_unique<MockPowerObserverHelper>(std::move(task_runner),
std::move(suspend_callback),
std::move(resume_callback));
mock_power_observer_helper_ = mock_power_observer_helper.get();
return mock_power_observer_helper;
}
protected:
~AliveCheckerTest() override {
base::WaitableEvent done(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
alive_checker_thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&AliveCheckerTest::ResetAliveCheckerOnAliveCheckerThread,
base::Unretained(this), &done));
done.Wait();
}
void CreateAliveChecker(bool stop_at_first_alive_notification,
bool pause_check_during_suspend) {
base::WaitableEvent done(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
alive_checker_thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(
&AliveCheckerTest::CreateAliveCheckerOnAliveCheckerThread,
base::Unretained(this), stop_at_first_alive_notification,
pause_check_during_suspend, &done));
done.Wait();
}
void StartAliveChecker() {
alive_checker_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&AliveChecker::Start,
base::Unretained(alive_checker_.get())));
}
void StopAliveChecker() {
alive_checker_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&AliveChecker::Stop,
base::Unretained(alive_checker_.get())));
}
// Notifies |alive_checker_| that we're alive, and if
// |remaining_notifications| > 1, posts a delayed task to itself on
// |alive_checker_thread_| with |remaining_notifications| decreased by 1. Can
// be called on any task runner.
void NotifyAliveMultipleTimes(int remaining_notifications,
base::TimeDelta delay) {
alive_checker_->NotifyAlive();
if (remaining_notifications > 1) {
alive_checker_thread_.task_runner()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&AliveCheckerTest::NotifyAliveMultipleTimes,
base::Unretained(this), remaining_notifications - 1,
delay),
delay);
}
}
void WaitUntilDetectedDead() {
detected_dead_event_.Wait();
detected_dead_event_.Reset();
}
// Returns true if the dead callback (AliveCheckerTest::OnDetectedDead) is run
// by the AliveChecker, false if timed out.
bool WaitUntilDetectedDeadWithTimeout(base::TimeDelta timeout) {
bool signaled = detected_dead_event_.TimedWait(timeout);
detected_dead_event_.Reset();
return signaled;
}
// Calls AliveChecker::DetectedDead() on the |alive_checker_thread_| and
// returns the result.
bool GetDetectedDead() {
bool detected_dead = false;
base::WaitableEvent done(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
alive_checker_thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&AliveCheckerTest::GetDetectedDeadOnAliveCheckerThread,
base::Unretained(this), &detected_dead, &done));
done.Wait();
return detected_dead;
}
// The test task environment.
base::test::TaskEnvironment task_environment_;
// The thread the checker is run on.
base::Thread alive_checker_thread_;
// AliveChecker under test.
std::unique_ptr<AliveChecker> alive_checker_;
// Mocks suspend status. Set in CreatePowerObserverHelper, owned by
// |alive_checker_|.
raw_ptr<MockPowerObserverHelper> mock_power_observer_helper_;
private:
void CreateAliveCheckerOnAliveCheckerThread(
bool stop_at_first_alive_notification,
bool pause_check_during_suspend,
base::WaitableEvent* done) {
EXPECT_TRUE(alive_checker_thread_.task_runner()->BelongsToCurrentThread());
if (pause_check_during_suspend) {
alive_checker_ = std::make_unique<AliveChecker>(
base::BindRepeating(&AliveCheckerTest::OnDetectedDead,
base::Unretained(this)),
base::Milliseconds(kCheckIntervalMs), base::Milliseconds(kTimeoutMs),
stop_at_first_alive_notification,
base::BindOnce(&AliveCheckerTest::CreatePowerObserverHelper,
base::Unretained(this)));
} else {
alive_checker_ = std::make_unique<AliveChecker>(
base::BindRepeating(&AliveCheckerTest::OnDetectedDead,
base::Unretained(this)),
base::Milliseconds(kCheckIntervalMs), base::Milliseconds(kTimeoutMs),
stop_at_first_alive_notification, false);
}
done->Signal();
}
void GetDetectedDeadOnAliveCheckerThread(bool* detected_dead,
base::WaitableEvent* done) {
EXPECT_TRUE(alive_checker_thread_.task_runner()->BelongsToCurrentThread());
*detected_dead = alive_checker_->DetectedDead();
done->Signal();
}
void ResetAliveCheckerOnAliveCheckerThread(base::WaitableEvent* done) {
EXPECT_TRUE(alive_checker_thread_.task_runner()->BelongsToCurrentThread());
mock_power_observer_helper_ = nullptr;
alive_checker_.reset();
done->Signal();
}
// Event to signal that we got a dead detection callback.
base::WaitableEvent detected_dead_event_;
};
// Start the checker, don't send alive notifications, and run until it detects
// dead. Verify that it only detects once. Repeat once.
TEST_F(AliveCheckerTest, NoAliveNotificationsDetectTwice) {
CreateAliveChecker(false, false);
StartAliveChecker();
EXPECT_FALSE(GetDetectedDead());
WaitUntilDetectedDead();
EXPECT_TRUE(GetDetectedDead());
// Verify that AliveChecker doesn't detect (runs the callback) a second time.
// It can take up to the timeout + the check interval until detection. Add a
// margin to this. The detect state should still be that we have detected
// dead.
EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
EXPECT_TRUE(GetDetectedDead());
// Start again, the detect state should be reset.
StartAliveChecker();
EXPECT_FALSE(GetDetectedDead());
WaitUntilDetectedDead();
EXPECT_TRUE(GetDetectedDead());
}
// Setup the checker to stop at first alive notification. Start it and notify
// that the client is alive once. Verify that we get no dead detection.
TEST_F(AliveCheckerTest, StopAtFirstAliveNotification_DoNotify) {
CreateAliveChecker(true, false);
StartAliveChecker();
alive_checker_->NotifyAlive();
// It can take up to the timeout + the check interval until detection. Add a
// margin to this.
EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
EXPECT_FALSE(GetDetectedDead());
}
// Setup the checker to stop at first alive notification. Start it and run until
// it detects dead.
TEST_F(AliveCheckerTest, StopAtFirstAliveNotification_DontNotify) {
CreateAliveChecker(true, false);
StartAliveChecker();
WaitUntilDetectedDead();
EXPECT_TRUE(GetDetectedDead());
}
// Setup the checker to pause checking when suspended. Suspend and verify that
// it doesn't detect dead. Start the checker, don't send alive notifications,
// and and verify that it doesn't detect dead. Resume and run until it detects
// dead.
TEST_F(AliveCheckerTest, SuspendResume_StartBetweenSuspendAndResume) {
CreateAliveChecker(false, true);
ASSERT_TRUE(mock_power_observer_helper_);
alive_checker_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Suspend,
base::Unretained(mock_power_observer_helper_)));
StartAliveChecker();
// It can take up to the timeout + the check interval until detection. Add a
// margin to this.
EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
EXPECT_FALSE(GetDetectedDead());
alive_checker_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Resume,
base::Unretained(mock_power_observer_helper_)));
WaitUntilDetectedDead();
EXPECT_TRUE(GetDetectedDead());
}
// Setup the checker to stop at first alive notification and pause checking when
// suspended. Start the checker, send one alive notifications, and verify it
// doesn't detect dead. Suspend and verify that it doesn't detect dead. Resume
// and and verify that it doesn't detect dead.
TEST_F(AliveCheckerTest, SuspendResumeWithAutoStop_NotifyBeforeSuspend) {
CreateAliveChecker(true, true);
ASSERT_TRUE(mock_power_observer_helper_);
StartAliveChecker();
alive_checker_->NotifyAlive();
// It can take up to the timeout + the check interval until detection. Add a
// margin to this.
EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
EXPECT_FALSE(GetDetectedDead());
alive_checker_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Suspend,
base::Unretained(mock_power_observer_helper_)));
EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
EXPECT_FALSE(GetDetectedDead());
alive_checker_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Resume,
base::Unretained(mock_power_observer_helper_)));
EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
EXPECT_FALSE(GetDetectedDead());
}
// Setup the checker to stop at first alive notification and pause checking when
// suspended. Start the checker, send one alive notifications, and verify it
// doesn't detect dead. Start it again, suspend and verify that it doesn't
// detect dead. Resume and run until detected dead.
TEST_F(AliveCheckerTest,
SuspendResumeWithAutoStop_NotifyBeforeSuspendAndRestart) {
CreateAliveChecker(true, true);
ASSERT_TRUE(mock_power_observer_helper_);
StartAliveChecker();
alive_checker_->NotifyAlive();
// It can take up to the timeout + the check interval until detection. Add a
// margin to this.
EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
EXPECT_FALSE(GetDetectedDead());
StartAliveChecker();
EXPECT_FALSE(GetDetectedDead());
alive_checker_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Suspend,
base::Unretained(mock_power_observer_helper_)));
EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
EXPECT_FALSE(GetDetectedDead());
alive_checker_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Resume,
base::Unretained(mock_power_observer_helper_)));
WaitUntilDetectedDead();
EXPECT_TRUE(GetDetectedDead());
}
// Setup the checker to stop at first alive notification and pause checking when
// suspended. Start the checker, suspend. Send one alive notification and
// verify it doesn't detected dead. Resume and verify it doesn't detected dead.
TEST_F(AliveCheckerTest,
SuspendResumeWithAutoStop_NotifyBetweenSuspendAndResume) {
CreateAliveChecker(true, true);
ASSERT_TRUE(mock_power_observer_helper_);
StartAliveChecker();
alive_checker_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Suspend,
base::Unretained(mock_power_observer_helper_)));
alive_checker_->NotifyAlive();
// It can take up to the timeout + the check interval until detection. Add a
// margin to this.
EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
EXPECT_FALSE(GetDetectedDead());
alive_checker_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Resume,
base::Unretained(mock_power_observer_helper_)));
EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
EXPECT_FALSE(GetDetectedDead());
}
// Setup the checker to stop at first alive notification and pause checking when
// suspended. Start the checker, suspend, resume, send one alive notification
// and verify it doesn't detected dead.
TEST_F(AliveCheckerTest, SuspendResumeWithAutoStop_NotifyAfterResume) {
CreateAliveChecker(true, true);
ASSERT_TRUE(mock_power_observer_helper_);
StartAliveChecker();
alive_checker_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Suspend,
base::Unretained(mock_power_observer_helper_)));
alive_checker_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Resume,
base::Unretained(mock_power_observer_helper_)));
alive_checker_->NotifyAlive();
// It can take up to the timeout + the check interval until detection. Add a
// margin to this.
EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
EXPECT_FALSE(GetDetectedDead());
}
// Setup the checker to stop at first alive notification and pause checking when
// suspended. Start the checker suspend, and and verify it doesn't detected
// dead. Resume and run until it detects dead.
TEST_F(AliveCheckerTest, SuspendResumeWithAutoStop_DontNotify) {
CreateAliveChecker(true, true);
ASSERT_TRUE(mock_power_observer_helper_);
StartAliveChecker();
alive_checker_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Suspend,
base::Unretained(mock_power_observer_helper_)));
// It can take up to the timeout + the check interval until detection. Add a
// margin to this.
EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
EXPECT_FALSE(GetDetectedDead());
alive_checker_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Resume,
base::Unretained(mock_power_observer_helper_)));
WaitUntilDetectedDead();
EXPECT_TRUE(GetDetectedDead());
}
} // namespace media