910e62b5创建于 1月15日历史提交
// 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 "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/test/task_environment.h"
#include "media/audio/audio_system_test_util.h"
#include "media/audio/mock_audio_manager.h"
#include "media/audio/test_audio_thread.h"
#include "services/audio/in_process_audio_manager_accessor.h"
#include "services/audio/public/cpp/audio_system_to_service_adapter.h"
#include "services/audio/public/cpp/fake_system_info.h"
#include "services/audio/public/mojom/audio_service.mojom.h"
#include "services/audio/service.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::Exactly;
using testing::Invoke;

namespace audio {

class ServiceTestHelper {
 public:
  class AudioThreadContext
      : public base::RefCountedThreadSafe<AudioThreadContext> {
   public:
    explicit AudioThreadContext(media::AudioManager* audio_manager)
        : audio_manager_(audio_manager) {}

    AudioThreadContext(const AudioThreadContext&) = delete;
    AudioThreadContext& operator=(const AudioThreadContext&) = delete;

    void CreateServiceOnAudioThread(
        mojo::PendingReceiver<mojom::AudioService> receiver) {
      if (!audio_manager_->GetTaskRunner()->BelongsToCurrentThread()) {
        audio_manager_->GetTaskRunner()->PostTask(
            FROM_HERE,
            base::BindOnce(&AudioThreadContext::CreateServiceOnAudioThread,
                           this, std::move(receiver)));
        return;
      }
      DCHECK(!service_);
      service_ = std::make_unique<audio::Service>(
          std::make_unique<InProcessAudioManagerAccessor>(audio_manager_),
          /*device_notifications_enabled=*/false, std::move(receiver));
    }

    void QuitOnAudioThread() {
      DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
      service_.reset();
    }

   private:
    friend class base::RefCountedThreadSafe<AudioThreadContext>;
    virtual ~AudioThreadContext() = default;

    const raw_ptr<media::AudioManager> audio_manager_;
    std::unique_ptr<Service> service_;
  };

  explicit ServiceTestHelper(media::AudioManager* audio_manager)
      : audio_manager_(audio_manager),
        audio_thread_context_(new AudioThreadContext(audio_manager)) {
    audio_thread_context_->CreateServiceOnAudioThread(
        service_remote_.BindNewPipeAndPassReceiver());
  }

  ServiceTestHelper(const ServiceTestHelper&) = delete;
  ServiceTestHelper& operator=(const ServiceTestHelper&) = delete;

  ~ServiceTestHelper() {
    // Ensure that the AudioThreadContext is destroyed on the correct thread by
    // passing our only reference into a task posted there.
    audio_manager_->GetTaskRunner()->PostTask(
        FROM_HERE, base::BindOnce(&AudioThreadContext::QuitOnAudioThread,
                                  std::move(audio_thread_context_)));
  }

  mojom::AudioService& service() { return *service_remote_.get(); }

 private:
  const raw_ptr<media::AudioManager> audio_manager_;
  mojo::Remote<mojom::AudioService> service_remote_;
  scoped_refptr<AudioThreadContext> audio_thread_context_;
};

// if |use_audio_thread| is true, AudioManager has a dedicated audio thread and
// Audio service lives on it; otherwise audio thread is the main thread of the
// test fixture, and that's where Service lives. So in the former case the
// service is accessed from another thread, and in the latter case - from the
// thread it lives on (which imitates access to Audio service from UI thread on
// Mac).
template <bool use_audio_thread>
class InProcessServiceTest : public testing::Test {
 public:
  InProcessServiceTest()
      : audio_manager_(
            std::make_unique<media::TestAudioThread>(use_audio_thread)),
        helper_(std::make_unique<ServiceTestHelper>(&audio_manager_)),
        audio_system_(std::make_unique<AudioSystemToServiceAdapter>(
            base::BindRepeating(&InProcessServiceTest::BindSystemInfo,
                                base::Unretained(this)))) {}

  InProcessServiceTest(const InProcessServiceTest&) = delete;
  InProcessServiceTest& operator=(const InProcessServiceTest&) = delete;

  ~InProcessServiceTest() override = default;

 protected:
  void TearDown() override {
    audio_system_.reset();

    // Deletes ServiceTestHelper, which will result in posting
    // AuioThreadContext::QuitOnAudioThread() to AudioManager thread, so that
    // Service is delete there.
    helper_.reset();

    // Joins AudioManager thread if it is used.
    audio_manager_.Shutdown();
  }

 protected:
  media::MockAudioManager* audio_manager() { return &audio_manager_; }
  media::AudioSystem* audio_system() { return audio_system_.get(); }

 private:
  void BindSystemInfo(mojo::PendingReceiver<mojom::SystemInfo> receiver) {
    helper_->service().BindSystemInfo(std::move(receiver));
  }

  base::test::TaskEnvironment task_environment_;
  media::MockAudioManager audio_manager_;
  std::unique_ptr<ServiceTestHelper> helper_;
  std::unique_ptr<media::AudioSystem> audio_system_;
};

// Tests for FakeSystemInfo overriding the global binder.
class FakeSystemInfoTest : public InProcessServiceTest<false>,
                           public FakeSystemInfo {
 public:
  FakeSystemInfoTest() = default;

  FakeSystemInfoTest(const FakeSystemInfoTest&) = delete;
  FakeSystemInfoTest& operator=(const FakeSystemInfoTest&) = delete;

  ~FakeSystemInfoTest() override = default;

 protected:
  MOCK_METHOD0(MethodCalled, void());

 private:
  void HasInputDevices(HasInputDevicesCallback callback) override {
    std::move(callback).Run(true);
    MethodCalled();
  }
};

TEST_F(FakeSystemInfoTest, HasInputDevicesCalledOnGlobalBinderOverride) {
  FakeSystemInfo::OverrideGlobalBinderForAudioService(this);
  base::RunLoop wait_loop;
  EXPECT_CALL(*this, MethodCalled())
      .WillOnce(testing::Invoke(&wait_loop, &base::RunLoop::Quit));
  audio_system()->HasInputDevices(base::BindOnce([](bool) {}));
  wait_loop.Run();
  FakeSystemInfo::ClearGlobalBinderForAudioService();
}

}  // namespace audio

// AudioSystem interface conformance tests.
// AudioSystemTestTemplate is defined in media, so should be its instantiations.
namespace media {

using AudioSystemTestVariations =
    testing::Types<audio::InProcessServiceTest<false>,
                   audio::InProcessServiceTest<true>>;

INSTANTIATE_TYPED_TEST_SUITE_P(InProcessAudioService,
                               AudioSystemTestTemplate,
                               AudioSystemTestVariations);

}  // namespace media