// Copyright 2017 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 <memory>

#include "base/bind.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread.h"
#include "media/audio/power_observer_helper.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace media {

class PowerObserverHelperTest : public testing::Test {
 public:
  PowerObserverHelperTest()
      : power_observer_helper_thread_("AliveCheckerThread"),
        suspend_event_(base::WaitableEvent::ResetPolicy::MANUAL,
                       base::WaitableEvent::InitialState::NOT_SIGNALED),
        resume_event_(base::WaitableEvent::ResetPolicy::MANUAL,
                      base::WaitableEvent::InitialState::NOT_SIGNALED) {
    power_observer_helper_thread_.StartAndWaitForTesting();
  }

  void OnSuspend() {
    EXPECT_TRUE(
        power_observer_helper_thread_.task_runner()->BelongsToCurrentThread());
    suspend_event_.Signal();
  }

  void OnResume() {
    EXPECT_TRUE(
        power_observer_helper_thread_.task_runner()->BelongsToCurrentThread());
    resume_event_.Signal();
  }

 protected:
  ~PowerObserverHelperTest() override {
    base::WaitableEvent done(base::WaitableEvent::ResetPolicy::MANUAL,
                             base::WaitableEvent::InitialState::NOT_SIGNALED);
    power_observer_helper_thread_.task_runner()->PostTask(
        FROM_HERE,
        base::BindOnce(&PowerObserverHelperTest::
                           ResetPowerObserverHelperOnPowerObserverHelperThread,
                       base::Unretained(this), &done));
    done.Wait();
  }

  void CreatePowerObserverHelper() {
    DCHECK(!power_observer_helper_);
    power_observer_helper_ = std::make_unique<PowerObserverHelper>(
        power_observer_helper_thread_.task_runner(),
        base::BindRepeating(&PowerObserverHelperTest::OnSuspend,
                            base::Unretained(this)),
        base::BindRepeating(&PowerObserverHelperTest::OnResume,
                            base::Unretained(this)));
  }

  void WaitUntilSuspendNotification() {
    suspend_event_.Wait();
    suspend_event_.Reset();
  }

  void WaitUntilResumeNotification() {
    resume_event_.Wait();
    resume_event_.Reset();
  }

  bool IsSuspending() {
    bool is_suspending = false;
    base::WaitableEvent did_check(
        base::WaitableEvent::ResetPolicy::MANUAL,
        base::WaitableEvent::InitialState::NOT_SIGNALED);
    power_observer_helper_thread_.task_runner()->PostTask(
        FROM_HERE,
        base::BindOnce(&PowerObserverHelperTest::CheckIfSuspending,
                       base::Unretained(this), &is_suspending, &did_check));
    did_check.Wait();
    return is_suspending;
  }

  PowerObserverHelper* power_observer_helper() const {
    return power_observer_helper_.get();
  }

 private:
  void CheckIfSuspending(bool* is_suspending, base::WaitableEvent* done) {
    EXPECT_TRUE(
        power_observer_helper_thread_.task_runner()->BelongsToCurrentThread());
    *is_suspending = power_observer_helper_->IsSuspending();
    done->Signal();
  }

  void ResetPowerObserverHelperOnPowerObserverHelperThread(
      base::WaitableEvent* done) {
    EXPECT_TRUE(
        power_observer_helper_thread_.task_runner()->BelongsToCurrentThread());
    power_observer_helper_.reset();
    done->Signal();
  }

  // The test task environment.
  base::test::ScopedTaskEnvironment scoped_task_environment_;

  // The thread the helper is run on.
  base::Thread power_observer_helper_thread_;

  // PowerObserverHelper under test.
  std::unique_ptr<PowerObserverHelper> power_observer_helper_;

  // Events to signal a notifications.
  base::WaitableEvent suspend_event_;
  base::WaitableEvent resume_event_;

  DISALLOW_COPY_AND_ASSIGN(PowerObserverHelperTest);
};

// Suspend and resume notifications.
TEST_F(PowerObserverHelperTest, SuspendAndResumeNotificationsTwice) {
  CreatePowerObserverHelper();
  EXPECT_FALSE(IsSuspending());

  power_observer_helper()->OnSuspend();
  WaitUntilSuspendNotification();
  EXPECT_TRUE(IsSuspending());

  power_observer_helper()->OnResume();
  WaitUntilResumeNotification();
  EXPECT_FALSE(IsSuspending());

  power_observer_helper()->OnSuspend();
  WaitUntilSuspendNotification();
  EXPECT_TRUE(IsSuspending());

  power_observer_helper()->OnResume();
  WaitUntilResumeNotification();
  EXPECT_FALSE(IsSuspending());
}

// Two suspend and two resume notifications.
TEST_F(PowerObserverHelperTest, TwoSuspendAndTwoResumeNotifications) {
  CreatePowerObserverHelper();
  EXPECT_FALSE(IsSuspending());

  power_observer_helper()->OnSuspend();
  WaitUntilSuspendNotification();
  EXPECT_TRUE(IsSuspending());

  power_observer_helper()->OnSuspend();
  WaitUntilSuspendNotification();
  EXPECT_TRUE(IsSuspending());

  power_observer_helper()->OnResume();
  WaitUntilResumeNotification();
  EXPECT_FALSE(IsSuspending());

  power_observer_helper()->OnResume();
  WaitUntilResumeNotification();
  EXPECT_FALSE(IsSuspending());
}

}  // namespace media
