123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405 |
- // Copyright 2014 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/midi/midi_manager.h"
- #include <stddef.h>
- #include <stdint.h>
- #include <memory>
- #include <vector>
- #include "base/bind.h"
- #include "base/check_op.h"
- #include "base/memory/weak_ptr.h"
- #include "base/run_loop.h"
- #include "base/system/system_monitor.h"
- #include "base/test/task_environment.h"
- #include "build/build_config.h"
- #include "media/midi/midi_service.h"
- #include "media/midi/task_service.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #if BUILDFLAG(IS_WIN)
- #include "media/midi/midi_manager_win.h"
- #endif // BUILDFLAG(IS_WIN)
- namespace midi {
- namespace {
- using mojom::PortState;
- using mojom::Result;
- class FakeMidiManager : public MidiManager {
- public:
- explicit FakeMidiManager(MidiService* service) : MidiManager(service) {}
- FakeMidiManager(const FakeMidiManager&) = delete;
- FakeMidiManager& operator=(const FakeMidiManager&) = delete;
- ~FakeMidiManager() override = default;
- base::WeakPtr<FakeMidiManager> GetWeakPtr() {
- return weak_factory_.GetWeakPtr();
- }
- // MidiManager implementation.
- void StartInitialization() override {
- DCHECK(!initialized_);
- initialized_ = true;
- }
- void DispatchSendMidiData(MidiManagerClient* client,
- uint32_t port_index,
- const std::vector<uint8_t>& data,
- base::TimeTicks timestamp) override {}
- // Utility functions for testing.
- void CallCompleteInitialization(Result result) {
- CompleteInitialization(result);
- }
- size_t GetClientCount() { return GetClientCountForTesting(); }
- size_t GetPendingClientCount() { return GetPendingClientCountForTesting(); }
- bool IsInitialized() const { return initialized_; }
- private:
- bool initialized_ = false;
- base::WeakPtrFactory<FakeMidiManager> weak_factory_{this};
- };
- class FakeMidiManagerFactory : public MidiService::ManagerFactory {
- public:
- FakeMidiManagerFactory() {}
- FakeMidiManagerFactory(const FakeMidiManagerFactory&) = delete;
- FakeMidiManagerFactory& operator=(const FakeMidiManagerFactory&) = delete;
- ~FakeMidiManagerFactory() override = default;
- std::unique_ptr<MidiManager> Create(MidiService* service) override {
- std::unique_ptr<FakeMidiManager> manager =
- std::make_unique<FakeMidiManager>(service);
- manager_ = manager->GetWeakPtr();
- return manager;
- }
- base::WeakPtr<FakeMidiManagerFactory> GetWeakPtr() {
- return weak_factory_.GetWeakPtr();
- }
- base::WeakPtr<FakeMidiManager> manager() {
- #if BUILDFLAG(IS_MAC)
- // To avoid Core MIDI issues, MidiManager won't be destructed on macOS.
- // See https://crbug.com/718140.
- if (!manager_ ||
- (!manager_->GetClientCount() && !manager_->GetPendingClientCount())) {
- return nullptr;
- }
- #endif
- return manager_;
- }
- private:
- base::WeakPtr<FakeMidiManager> manager_ = nullptr;
- base::WeakPtrFactory<FakeMidiManagerFactory> weak_factory_{this};
- };
- class FakeMidiManagerClient : public MidiManagerClient {
- public:
- FakeMidiManagerClient() = default;
- FakeMidiManagerClient(const FakeMidiManagerClient&) = delete;
- FakeMidiManagerClient& operator=(const FakeMidiManagerClient&) = delete;
- ~FakeMidiManagerClient() override = default;
- // MidiManagerClient implementation.
- void AddInputPort(const mojom::PortInfo& info) override {}
- void AddOutputPort(const mojom::PortInfo& info) override {}
- void SetInputPortState(uint32_t port_index, PortState state) override {}
- void SetOutputPortState(uint32_t port_index, PortState state) override {}
- void CompleteStartSession(Result result) override {
- EXPECT_TRUE(wait_for_result_);
- result_ = result;
- wait_for_result_ = false;
- }
- void ReceiveMidiData(uint32_t port_index,
- const uint8_t* data,
- size_t size,
- base::TimeTicks timestamp) override {}
- void AccumulateMidiBytesSent(size_t size) override {}
- void Detach() override {}
- Result result() const { return result_; }
- Result WaitForResult() {
- while (wait_for_result_) {
- base::RunLoop run_loop;
- run_loop.RunUntilIdle();
- }
- return result();
- }
- private:
- Result result_ = Result::NOT_SUPPORTED;
- bool wait_for_result_ = true;
- };
- class MidiManagerTest : public ::testing::Test {
- public:
- MidiManagerTest() {
- std::unique_ptr<FakeMidiManagerFactory> factory =
- std::make_unique<FakeMidiManagerFactory>();
- factory_ = factory->GetWeakPtr();
- service_ = std::make_unique<MidiService>(std::move(factory));
- }
- MidiManagerTest(const MidiManagerTest&) = delete;
- MidiManagerTest& operator=(const MidiManagerTest&) = delete;
- ~MidiManagerTest() override {
- service_->Shutdown();
- base::RunLoop run_loop;
- run_loop.RunUntilIdle();
- }
- protected:
- void StartTheFirstSession(FakeMidiManagerClient* client) {
- DCHECK(factory_);
- EXPECT_FALSE(factory_->manager());
- service_->StartSession(client);
- ASSERT_TRUE(factory_->manager());
- EXPECT_TRUE(factory_->manager()->IsInitialized());
- EXPECT_EQ(0U, factory_->manager()->GetClientCount());
- EXPECT_EQ(1U, factory_->manager()->GetPendingClientCount());
- }
- void StartTheNthSession(FakeMidiManagerClient* client, size_t nth) {
- DCHECK(factory_);
- DCHECK_NE(1U, nth);
- ASSERT_TRUE(factory_->manager());
- EXPECT_TRUE(factory_->manager()->IsInitialized());
- EXPECT_EQ(0U, factory_->manager()->GetClientCount());
- EXPECT_EQ(nth - 1U, factory_->manager()->GetPendingClientCount());
- service_->StartSession(client);
- EXPECT_EQ(nth, factory_->manager()->GetPendingClientCount());
- }
- void StartSession(FakeMidiManagerClient* client) {
- service_->StartSession(client);
- }
- void EndSession(FakeMidiManagerClient* client, size_t before, size_t after) {
- DCHECK(factory_);
- ASSERT_TRUE(factory_->manager());
- EXPECT_EQ(before, factory_->manager()->GetClientCount());
- EXPECT_TRUE(service_->EndSession(client));
- if (after) {
- ASSERT_TRUE(factory_->manager());
- EXPECT_EQ(after, factory_->manager()->GetClientCount());
- } else {
- EXPECT_FALSE(factory_->manager());
- }
- }
- bool CompleteInitialization(Result result) {
- DCHECK(factory_);
- if (!factory_->manager())
- return false;
- factory_->manager()->CallCompleteInitialization(result);
- return true;
- }
- void RunLoopUntilIdle() {
- base::RunLoop run_loop;
- run_loop.RunUntilIdle();
- }
- base::WeakPtr<FakeMidiManagerFactory> factory() { return factory_; }
- private:
- base::test::TaskEnvironment env_;
- base::WeakPtr<FakeMidiManagerFactory> factory_;
- std::unique_ptr<MidiService> service_;
- };
- TEST_F(MidiManagerTest, StartAndEndSession) {
- std::unique_ptr<FakeMidiManagerClient> client =
- std::make_unique<FakeMidiManagerClient>();
- StartTheFirstSession(client.get());
- EXPECT_TRUE(CompleteInitialization(Result::OK));
- EXPECT_EQ(Result::OK, client->WaitForResult());
- EndSession(client.get(), 1U, 0U);
- }
- TEST_F(MidiManagerTest, StartAndEndSessionWithError) {
- std::unique_ptr<FakeMidiManagerClient> client =
- std::make_unique<FakeMidiManagerClient>();
- StartTheFirstSession(client.get());
- EXPECT_TRUE(CompleteInitialization(Result::INITIALIZATION_ERROR));
- EXPECT_EQ(Result::INITIALIZATION_ERROR, client->WaitForResult());
- EndSession(client.get(), 1U, 0U);
- }
- TEST_F(MidiManagerTest, StartMultipleSessions) {
- std::unique_ptr<FakeMidiManagerClient> client1 =
- std::make_unique<FakeMidiManagerClient>();
- std::unique_ptr<FakeMidiManagerClient> client2 =
- std::make_unique<FakeMidiManagerClient>();
- std::unique_ptr<FakeMidiManagerClient> client3 =
- std::make_unique<FakeMidiManagerClient>();
- StartTheFirstSession(client1.get());
- StartTheNthSession(client2.get(), 2);
- StartTheNthSession(client3.get(), 3);
- EXPECT_TRUE(CompleteInitialization(Result::OK));
- EXPECT_EQ(Result::OK, client1->WaitForResult());
- EXPECT_EQ(Result::OK, client2->WaitForResult());
- EXPECT_EQ(Result::OK, client3->WaitForResult());
- EndSession(client1.get(), 3U, 2U);
- EndSession(client2.get(), 2U, 1U);
- EndSession(client3.get(), 1U, 0U);
- }
- TEST_F(MidiManagerTest, TooManyPendingSessions) {
- // Push as many client requests for starting session as possible.
- std::vector<std::unique_ptr<FakeMidiManagerClient>> many_existing_clients;
- many_existing_clients.resize(MidiManager::kMaxPendingClientCount);
- many_existing_clients[0] = std::make_unique<FakeMidiManagerClient>();
- StartTheFirstSession(many_existing_clients[0].get());
- for (size_t i = 1; i < MidiManager::kMaxPendingClientCount; ++i) {
- many_existing_clients[i] = std::make_unique<FakeMidiManagerClient>();
- StartTheNthSession(many_existing_clients[i].get(), i + 1);
- }
- ASSERT_TRUE(factory()->manager());
- EXPECT_TRUE(factory()->manager()->IsInitialized());
- // Push the last client that should be rejected for too many pending requests.
- std::unique_ptr<FakeMidiManagerClient> additional_client =
- std::make_unique<FakeMidiManagerClient>();
- StartSession(additional_client.get());
- EXPECT_EQ(Result::INITIALIZATION_ERROR, additional_client->result());
- // Other clients still should not receive a result.
- RunLoopUntilIdle();
- for (size_t i = 0; i < many_existing_clients.size(); ++i)
- EXPECT_EQ(Result::NOT_SUPPORTED, many_existing_clients[i]->result());
- // The Result::OK should be distributed to other clients.
- EXPECT_TRUE(CompleteInitialization(Result::OK));
- for (size_t i = 0; i < many_existing_clients.size(); ++i)
- EXPECT_EQ(Result::OK, many_existing_clients[i]->WaitForResult());
- // Close all successful sessions in FIFO order.
- size_t sessions = many_existing_clients.size();
- for (size_t i = 0; i < many_existing_clients.size(); ++i, --sessions)
- EndSession(many_existing_clients[i].get(), sessions, sessions - 1);
- }
- TEST_F(MidiManagerTest, AbortSession) {
- // A client starting a session can be destructed while an asynchronous
- // initialization is performed.
- std::unique_ptr<FakeMidiManagerClient> client =
- std::make_unique<FakeMidiManagerClient>();
- StartTheFirstSession(client.get());
- EndSession(client.get(), 0, 0);
- client.reset();
- // Following function should not call the destructed |client| function.
- EXPECT_FALSE(CompleteInitialization(Result::OK));
- base::RunLoop run_loop;
- run_loop.RunUntilIdle();
- }
- class PlatformMidiManagerTest : public ::testing::Test {
- public:
- PlatformMidiManagerTest()
- : client_(std::make_unique<FakeMidiManagerClient>()),
- service_(std::make_unique<MidiService>()) {
- //
- }
- PlatformMidiManagerTest(const PlatformMidiManagerTest&) = delete;
- PlatformMidiManagerTest& operator=(const PlatformMidiManagerTest&) = delete;
- ~PlatformMidiManagerTest() override {
- service_->Shutdown();
- base::RunLoop run_loop;
- run_loop.RunUntilIdle();
- }
- MidiService* service() { return service_.get(); }
- void StartSession() { service_->StartSession(client_.get()); }
- void EndSession() { service_->EndSession(client_.get()); }
- Result WaitForResult() { return client_->WaitForResult(); }
- // This #ifdef needs to be identical to the one in media/midi/midi_manager.cc.
- // Do not change the condition for disabling this test.
- bool IsSupported() {
- #if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_WIN) && \
- !(defined(USE_ALSA) && defined(USE_UDEV)) && !BUILDFLAG(IS_ANDROID)
- return false;
- #else
- return true;
- #endif
- }
- private:
- // SystemMonitor is needed on Windows.
- base::SystemMonitor system_monitor;
- base::test::TaskEnvironment env_;
- std::unique_ptr<FakeMidiManagerClient> client_;
- std::unique_ptr<MidiService> service_;
- };
- #if BUILDFLAG(IS_ANDROID)
- // The test sometimes fails on Android. https://crbug.com/844027
- #define MAYBE_CreatePlatformMidiManager DISABLED_CreatePlatformMidiManager
- #else
- #define MAYBE_CreatePlatformMidiManager CreatePlatformMidiManager
- #endif
- TEST_F(PlatformMidiManagerTest, MAYBE_CreatePlatformMidiManager) {
- StartSession();
- Result result = WaitForResult();
- #if defined(USE_ALSA)
- // Temporary until http://crbug.com/371230 is resolved.
- EXPECT_TRUE(result == Result::OK || result == Result::INITIALIZATION_ERROR);
- #else
- EXPECT_EQ(IsSupported() ? Result::OK : Result::NOT_SUPPORTED, result);
- #endif
- }
- TEST_F(PlatformMidiManagerTest, InstanceIdOverflow) {
- service()->task_service()->OverflowInstanceIdForTesting();
- #if BUILDFLAG(IS_WIN)
- MidiManagerWin::OverflowInstanceIdForTesting();
- #endif // BUILDFLAG(IS_WIN)
- StartSession();
- EXPECT_EQ(
- IsSupported() ? Result::INITIALIZATION_ERROR : Result::NOT_SUPPORTED,
- WaitForResult());
- EndSession();
- }
- } // namespace
- } // namespace midi
|