// Copyright (c) 2012 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 "media/audio/audio_output_controller.h"

#include <stdint.h>

#include <memory>
#include <vector>

#include "base/barrier_closure.h"
#include "base/bind.h"
#include "base/environment.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_piece.h"
#include "base/test/test_message_loop.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/unguessable_token.h"
#include "media/audio/audio_device_description.h"
#include "media/audio/audio_source_diverter.h"
#include "media/audio/test_audio_thread.h"
#include "media/base/audio_bus.h"
#include "media/base/audio_parameters.h"
#include "media/base/gmock_callback_support.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using ::testing::_;
using ::testing::AtLeast;
using ::testing::Bool;
using ::testing::TestWithParam;
using ::testing::DoAll;
using ::testing::Invoke;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::StrictMock;
using ::testing::Mock;

namespace media {

static const int kSampleRate = AudioParameters::kAudioCDSampleRate;
static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO;
static const int kSamplesPerPacket = kSampleRate / 1000;
static const double kTestVolume = 0.25;
static const float kBufferNonZeroData = 1.0f;

AudioParameters AOCTestParams() {
  return AudioParameters(AudioParameters::AUDIO_FAKE, kChannelLayout,
                         kSampleRate, kSamplesPerPacket);
}

class MockAudioOutputControllerEventHandler
    : public AudioOutputController::EventHandler {
 public:
  MockAudioOutputControllerEventHandler() = default;

  MOCK_METHOD0(OnControllerCreated, void());
  MOCK_METHOD0(OnControllerPlaying, void());
  MOCK_METHOD0(OnControllerPaused, void());
  MOCK_METHOD0(OnControllerError, void());
  void OnLog(base::StringPiece) override {}

 private:
  DISALLOW_COPY_AND_ASSIGN(MockAudioOutputControllerEventHandler);
};

class MockAudioOutputControllerSyncReader
    : public AudioOutputController::SyncReader {
 public:
  MockAudioOutputControllerSyncReader() = default;

  MOCK_METHOD3(RequestMoreData,
               void(base::TimeDelta delay,
                    base::TimeTicks delay_timestamp,
                    int prior_frames_skipped));
  MOCK_METHOD1(Read, void(AudioBus* dest));
  MOCK_METHOD0(Close, void());

 private:
  DISALLOW_COPY_AND_ASSIGN(MockAudioOutputControllerSyncReader);
};

class MockAudioOutputStream : public AudioOutputStream,
                              public AudioOutputStream::AudioSourceCallback {
 public:
  explicit MockAudioOutputStream(AudioManager* audio_manager)
      : audio_manager_(audio_manager) {}

  // We forward to a fake stream to get automatic OnMoreData callbacks,
  // required by some tests.
  MOCK_METHOD0(DidOpen, void());
  MOCK_METHOD0(DidStart, void());
  MOCK_METHOD0(DidStop, void());
  MOCK_METHOD0(DidClose, void());
  MOCK_METHOD1(SetVolume, void(double));
  MOCK_METHOD1(GetVolume, void(double* volume));

  bool Open() override {
    EXPECT_EQ(nullptr, impl_);
    impl_ =
        audio_manager_->MakeAudioOutputStreamProxy(AOCTestParams(), "default");
    impl_->Open();
    DidOpen();
    return true;
  }

  void Start(AudioOutputStream::AudioSourceCallback* cb) override {
    EXPECT_EQ(nullptr, callback_);
    callback_ = cb;
    impl_->Start(this);
    DidStart();
  }

  void Stop() override {
    impl_->Stop();
    callback_ = nullptr;
    DidStop();
  }

  void Close() override {
    impl_->Close();
    impl_ = nullptr;
    DidClose();
  }

 private:
  int OnMoreData(base::TimeDelta delay,
                 base::TimeTicks delay_timestamp,
                 int prior_frames_skipped,
                 AudioBus* dest) override {
    int res = callback_->OnMoreData(delay, delay_timestamp,
                                    prior_frames_skipped, dest);
    EXPECT_EQ(dest->channel(0)[0], kBufferNonZeroData);
    return res;
  }

  void OnError() override {
    // Fake stream doesn't send errors.
    NOTREACHED();
  }

  AudioManager* audio_manager_;
  AudioOutputStream* impl_ = nullptr;
  AudioOutputStream::AudioSourceCallback* callback_ = nullptr;

  DISALLOW_COPY_AND_ASSIGN(MockAudioOutputStream);
};

class MockAudioPushSink : public AudioPushSink {
 public:
  MockAudioPushSink() = default;

  MOCK_METHOD0(Close, void());
  MOCK_METHOD1(OnDataCheck, void(float));

  void OnData(std::unique_ptr<AudioBus> source,
              base::TimeTicks reference_time) override {
    OnDataCheck(source->channel(0)[0]);
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(MockAudioPushSink);
};

ACTION(PopulateBuffer) {
  arg0->Zero();
  // Note: To confirm the buffer will be populated in these tests, it's
  // sufficient that only the first float in channel 0 is set to the value.
  arg0->channel(0)[0] = kBufferNonZeroData;
}

class AudioOutputControllerTest : public TestWithParam<bool> {
 public:
  AudioOutputControllerTest()
      : synchronous_use_(GetParam()),
        audio_manager_(AudioManager::CreateForTesting(
            // Make sure that the audio manager thread == the main thread with
            // synchronous use of the controller.
            std::make_unique<TestAudioThread>(!synchronous_use_))),
        mock_stream_(audio_manager_.get()) {}

  ~AudioOutputControllerTest() override { audio_manager_->Shutdown(); }

 protected:
  void Create() {
    EXPECT_CALL(mock_event_handler_, OnControllerCreated());

    controller_ = AudioOutputController::Create(
        audio_manager_.get(), &mock_event_handler_, AOCTestParams(),
        std::string(), base::UnguessableToken(), &mock_sync_reader_);
    EXPECT_NE(nullptr, controller_.get());
    controller_->SetVolume(kTestVolume);
  }

  void Play() {
    base::RunLoop loop;
    // The barrier is used to wait for all of the expectations to be fulfilled.
    base::RepeatingClosure barrier =
        base::BarrierClosure(3, loop.QuitClosure());
    EXPECT_CALL(mock_event_handler_, OnControllerPlaying())
        .WillOnce(RunClosure(barrier));
    EXPECT_CALL(mock_sync_reader_, RequestMoreData(_, _, _))
        .WillOnce(RunClosure(barrier))
        .WillRepeatedly(Return());
    EXPECT_CALL(mock_sync_reader_, Read(_))
        .WillOnce(Invoke([barrier](AudioBus* data) {
          data->channel(0)[0] = kBufferNonZeroData;
          barrier.Run();
        }))
        .WillRepeatedly(PopulateBuffer());
    controller_->Play();

    // Waits for all gmock expectations to be satisfied.
    loop.Run();
  }

  void PlayWhileDiverting() {
    base::RunLoop loop;
    // The barrier is used to wait for all of the expectations to be fulfilled.
    base::RepeatingClosure barrier =
        base::BarrierClosure(4, loop.QuitClosure());
    EXPECT_CALL(mock_stream_, DidStart()).WillOnce(RunClosure(barrier));
    EXPECT_CALL(mock_event_handler_, OnControllerPlaying())
        .WillOnce(RunClosure(barrier));
    // The mock stream will start pulling data. We verify that the calls are
    // forwarded to SyncReader, and write some data to the buffer that we can
    // verify later.
    EXPECT_CALL(mock_sync_reader_, RequestMoreData(_, _, _))
        .WillOnce(RunClosure(barrier))
        .WillRepeatedly(Return());
    EXPECT_CALL(mock_sync_reader_, Read(_))
        .WillOnce(Invoke([barrier](AudioBus* data) {
          data->channel(0)[0] = kBufferNonZeroData;
          barrier.Run();
        }))
        .WillRepeatedly(PopulateBuffer());
    controller_->Play();

    // Waits for all gmock expectations to be satisfied.
    loop.Run();
    // At some point in the future, the stream must be stopped.
    EXPECT_CALL(mock_stream_, DidStop());
  }

  void Pause() {
    base::RunLoop loop;
    EXPECT_CALL(mock_event_handler_, OnControllerPaused())
        .WillOnce(RunOnceClosure(loop.QuitClosure()));
    controller_->Pause();
    loop.Run();
    Mock::VerifyAndClearExpectations(&mock_event_handler_);
  }

  void ChangeDevice() {
    // Expect the event handler to receive one OnControllerPaying() call and no
    // OnControllerPaused() call.
    EXPECT_CALL(mock_event_handler_, OnControllerPlaying());
    EXPECT_CALL(mock_event_handler_, OnControllerPaused()).Times(0);

    // Simulate a device change event to AudioOutputController from the
    // AudioManager.
    audio_manager_->GetTaskRunner()->PostTask(
        FROM_HERE,
        base::BindOnce(&AudioOutputController::OnDeviceChange, controller_));

    // Wait for device change to take effect.
    base::RunLoop loop;
    audio_manager_->GetTaskRunner()->PostTask(FROM_HERE, loop.QuitClosure());
    loop.Run();
  }

  void Divert() {
    EXPECT_CALL(mock_stream_, DidOpen());
    EXPECT_CALL(mock_stream_, SetVolume(kTestVolume));

    controller_->StartDiverting(&mock_stream_);
    base::RunLoop loop;
    // Wait for controller to start diverting.
    audio_manager_->GetTaskRunner()->PostTask(FROM_HERE, loop.QuitClosure());
    loop.Run();
  }

  void DivertWhilePlaying() {
    base::RunLoop loop;
    // The barrier is used to wait for all of the expectations to be fulfilled.
    base::RepeatingClosure barrier =
        base::BarrierClosure(4, loop.QuitClosure());
    // Expect mock streams to be initialized and started.
    EXPECT_CALL(mock_stream_, DidOpen()).WillOnce(RunClosure(barrier));
    EXPECT_CALL(mock_stream_, SetVolume(kTestVolume))
        .WillOnce(RunClosure(barrier));
    EXPECT_CALL(mock_stream_, DidStart()).WillOnce(RunClosure(barrier));
    // Expect event handler to be informed.
    EXPECT_CALL(mock_event_handler_, OnControllerPlaying())
        .WillOnce(RunClosure(barrier));

    controller_->StartDiverting(&mock_stream_);
    // Wait until callbacks has started.
    loop.Run();
    // At some point in the future, the stream must be stopped.
    EXPECT_CALL(mock_stream_, DidStop());
  }

  void StartDuplicating(MockAudioPushSink* sink) {
    base::RunLoop loop;
    EXPECT_CALL(*sink, OnDataCheck(kBufferNonZeroData))
        .WillOnce(RunOnceClosure(loop.QuitClosure()))
        .WillRepeatedly(Return());
    controller_->StartDuplicating(sink);
    loop.Run();
  }

  void Revert(bool was_playing) {
    if (was_playing) {
      // Expect the handler to receive one OnControllerPlaying() call as a
      // result of the stream switching back.
      EXPECT_CALL(mock_event_handler_, OnControllerPlaying());
    }

    EXPECT_CALL(mock_stream_, DidClose());

    controller_->StopDiverting();
    base::RunLoop loop;
    audio_manager_->GetTaskRunner()->PostTask(FROM_HERE, loop.QuitClosure());
    loop.Run();
  }

  void StopDuplicating(MockAudioPushSink* sink) {
    {
      // First, verify we're still getting callbacks. Must be done on the AM
      // task runner, since it may be a separate thread, and EXPECTing from the
      // main thread would be racy.
      base::RunLoop loop;
      audio_manager_->GetTaskRunner()->PostTask(
          FROM_HERE,
          base::BindOnce(
              [](MockAudioPushSink* sink, base::RepeatingClosure done_closure) {
                Mock::VerifyAndClear(sink);
                EXPECT_CALL(*sink, OnDataCheck(kBufferNonZeroData))
                    .WillOnce(RunClosure(done_closure))
                    .WillRepeatedly(Return());
              },
              sink, loop.QuitClosure()));
      loop.Run();
    }

    {
      EXPECT_CALL(*sink, Close());
      controller_->StopDuplicating(sink);
      base::RunLoop loop;
      audio_manager_->GetTaskRunner()->PostTask(FROM_HERE, loop.QuitClosure());
      loop.Run();
    }
  }

  void Close() {
    EXPECT_CALL(mock_sync_reader_, Close());

    if (synchronous_use_) {
      controller_->Close(base::OnceClosure());
      return;
    }
    base::RunLoop run_loop;
    base::ThreadTaskRunnerHandle::Get()->PostTask(
        FROM_HERE, base::BindOnce(&AudioOutputController::Close, controller_,
                                  run_loop.QuitClosure()));
    run_loop.Run();
  }

  void SimulateErrorThenDeviceChange() {
    audio_manager_->GetTaskRunner()->PostTask(
        FROM_HERE,
        base::BindOnce(&AudioOutputControllerTest::TriggerErrorThenDeviceChange,
                       base::Unretained(this)));

    base::RunLoop loop;
    audio_manager_->GetTaskRunner()->PostTask(FROM_HERE, loop.QuitClosure());
    loop.Run();
  }

  // These help make test sequences more readable.
  void RevertWasNotPlaying() { Revert(false); }
  void RevertWhilePlaying() { Revert(true); }

  void TriggerErrorThenDeviceChange() {
    DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());

    // Errors should be deferred; the device change should ensure it's dropped.
    EXPECT_CALL(mock_event_handler_, OnControllerError()).Times(0);
    controller_->OnError();

    EXPECT_CALL(mock_event_handler_, OnControllerPlaying());
    EXPECT_CALL(mock_event_handler_, OnControllerPaused()).Times(0);
    controller_->OnDeviceChange();
  }

  base::TestMessageLoop message_loop_;
  const bool synchronous_use_;
  std::unique_ptr<AudioManager> audio_manager_;
  StrictMock<MockAudioOutputControllerEventHandler> mock_event_handler_;
  StrictMock<MockAudioOutputControllerSyncReader> mock_sync_reader_;
  StrictMock<MockAudioOutputStream> mock_stream_;
  scoped_refptr<AudioOutputController> controller_;

 private:
  DISALLOW_COPY_AND_ASSIGN(AudioOutputControllerTest);
};

TEST_P(AudioOutputControllerTest, CreateAndClose) {
  Create();
  Close();
}

TEST_P(AudioOutputControllerTest, PlayAndClose) {
  Create();
  Play();
  Close();
}

TEST_P(AudioOutputControllerTest, PlayPauseClose) {
  Create();
  Play();
  Pause();
  Close();
}

TEST_P(AudioOutputControllerTest, PlayPausePlayClose) {
  Create();
  Play();
  Pause();
  Play();
  Close();
}

TEST_P(AudioOutputControllerTest, PlayDeviceChangeClose) {
  Create();
  Play();
  ChangeDevice();
  Close();
}

TEST_P(AudioOutputControllerTest, PlayDeviceChangeError) {
  Create();
  Play();
  SimulateErrorThenDeviceChange();
  Close();
}

TEST_P(AudioOutputControllerTest, PlayDivertRevertClose) {
  Create();
  Play();
  DivertWhilePlaying();
  RevertWhilePlaying();
  Close();
}

TEST_P(AudioOutputControllerTest, PlayDivertRevertDivertRevertClose) {
  Create();
  Play();
  DivertWhilePlaying();
  RevertWhilePlaying();
  DivertWhilePlaying();
  RevertWhilePlaying();
  Close();
}

TEST_P(AudioOutputControllerTest, DivertPlayPausePlayRevertClose) {
  Create();
  Divert();
  PlayWhileDiverting();
  Pause();
  PlayWhileDiverting();
  RevertWhilePlaying();
  Close();
}

TEST_P(AudioOutputControllerTest, DivertRevertClose) {
  Create();
  Divert();
  RevertWasNotPlaying();
  Close();
}

TEST_P(AudioOutputControllerTest, PlayDuplicateStopClose) {
  Create();
  MockAudioPushSink mock_sink;
  Play();
  StartDuplicating(&mock_sink);
  StopDuplicating(&mock_sink);
  Close();
}

TEST_P(AudioOutputControllerTest, TwoDuplicates) {
  Create();
  MockAudioPushSink mock_sink_1;
  MockAudioPushSink mock_sink_2;
  Play();
  StartDuplicating(&mock_sink_1);
  StartDuplicating(&mock_sink_2);
  StopDuplicating(&mock_sink_1);
  StopDuplicating(&mock_sink_2);
  Close();
}

TEST_P(AudioOutputControllerTest, DuplicateDivertInteract) {
  Create();
  MockAudioPushSink mock_sink;
  Play();
  StartDuplicating(&mock_sink);
  DivertWhilePlaying();
  StopDuplicating(&mock_sink);
  RevertWhilePlaying();
  Close();
}

INSTANTIATE_TEST_CASE_P(AOC, AudioOutputControllerTest, Bool());

}  // namespace media
