chromium/services/audio/public/cpp/sounds/audio_stream_handler_unittest.cc

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "services/audio/public/cpp/sounds/audio_stream_handler.h"

#include <memory>
#include <string_view>
#include <utility>

#include "base/check.h"
#include "base/compiler_specific.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/task_environment.h"
#include "media/audio/audio_io.h"
#include "media/audio/simple_sources.h"
#include "media/audio/test_audio_thread.h"
#include "media/base/audio_codecs.h"
#include "media/base/channel_layout.h"
#include "media/base/test_data_util.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "services/audio/public/cpp/output_device.h"
#include "services/audio/public/cpp/sounds/test_data.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace audio {

namespace {

const char kTestBadWavAudioData[] = "RIFF1234WAVEjunkjunkjunkjunk";
const size_t kTestBadWavAudioDataSize = std::size(kTestBadWavAudioData) - 1;

struct TestParams {
  const media::AudioCodec codec;
  const bool is_bad;
  const bool is_file;
  const char* const source;
  const size_t data_size = 0;
};

constexpr TestParams kTestParamsWav[] = {
    {media::AudioCodec::kPCM, /*is_bad=*/false, /*is_file=*/false,
     kTestAudioData, kTestAudioDataSize},
    {media::AudioCodec::kPCM, /*is_bad=*/true, /*is_file=*/false,
     kTestBadWavAudioData, kTestBadWavAudioDataSize},
};

constexpr TestParams kTestParamsFlac[] = {
    {media::AudioCodec::kFLAC, /*is_bad=*/false, /*is_file=*/true, "bear.flac"},
};

}  // namespace

class AudioStreamHandlerTest : public ::testing::TestWithParam<TestParams> {
 public:
  AudioStreamHandlerTest()
      : codec_(GetParam().codec),
        is_bad_(GetParam().is_bad),
        is_file_(GetParam().is_file),
        source_(GetParam().source),
        data_size_(GetParam().data_size) {}

  ~AudioStreamHandlerTest() override = default;

  bool is_bad() const { return is_bad_; }
  AudioStreamHandler* handler() { return audio_stream_handler_.get(); }

  void SetUp() override {
    CreateHandler();
    DCHECK(audio_stream_handler_);
  }

  void CreateHandler() {
    if (is_file_) {
      const base::FilePath file_path = media::GetTestDataFilePath(source_);
      EXPECT_TRUE(base::ReadFileToString(file_path, &bitstream_));
      audio_stream_handler_ = std::make_unique<AudioStreamHandler>(
          base::DoNothing(), bitstream_, codec_);
    } else {
      std::string_view data(source_, data_size_);
      audio_stream_handler_ =
          std::make_unique<AudioStreamHandler>(base::DoNothing(), data, codec_);
    }
  }

  void SetObserverForTesting(AudioStreamHandler::TestObserver* observer) {
    AudioStreamHandler::SetObserverForTesting(observer);
  }

 private:
  base::test::TaskEnvironment env_;
  const media::AudioCodec codec_;
  const bool is_bad_;
  const bool is_file_;
  const char* const source_;
  const size_t data_size_;
  std::string bitstream_;
  std::unique_ptr<AudioStreamHandler> audio_stream_handler_;
};

TEST_P(AudioStreamHandlerTest, Play) {
  if (is_bad()) {
    return;
  }

  base::RunLoop run_loop;
  TestObserver observer(run_loop.QuitClosure());
  SetObserverForTesting(&observer);

  ASSERT_TRUE(handler()->IsInitialized());
  ASSERT_TRUE(handler()->Play());

  run_loop.Run();

  SetObserverForTesting(nullptr);

  ASSERT_EQ(1, observer.num_play_requests());
  ASSERT_EQ(1, observer.num_stop_requests());
}

TEST_P(AudioStreamHandlerTest, ConsecutivePlayRequests) {
  if (is_bad()) {
    return;
  }

  base::RunLoop run_loop;
  TestObserver observer(run_loop.QuitClosure());
  SetObserverForTesting(&observer);

  ASSERT_TRUE(handler()->IsInitialized());
  ASSERT_TRUE(handler()->Play());

  base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
      FROM_HERE,
      base::BindOnce(base::IgnoreResult(&AudioStreamHandler::Play),
                     base::Unretained(handler())),
      base::Seconds(1));
  base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
      FROM_HERE,
      base::BindOnce(&AudioStreamHandler::Stop, base::Unretained(handler())),
      base::Seconds(2));

  run_loop.Run();

  SetObserverForTesting(nullptr);

  ASSERT_EQ(1, observer.num_play_requests());
  ASSERT_EQ(1, observer.num_stop_requests());
}

TEST_P(AudioStreamHandlerTest, BadDataDoesNotInitialize) {
  if (!is_bad()) {
    return;
  }
  // The class members and SetUp() will be ignored for this test. Create a
  // handler on the stack with some bad WAV or FLAC data.
  EXPECT_FALSE(handler()->IsInitialized());
  EXPECT_FALSE(handler()->Play());

  // Call Stop() to ensure that there is no crash.
  handler()->Stop();
}

INSTANTIATE_TEST_SUITE_P(Wav,
                         AudioStreamHandlerTest,
                         testing::ValuesIn(kTestParamsWav));
INSTANTIATE_TEST_SUITE_P(Flac,
                         AudioStreamHandlerTest,
                         testing::ValuesIn(kTestParamsFlac));

}  // namespace audio