1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "media/midi/midi_manager.h" 6 7#include <vector> 8 9#include "base/logging.h" 10#include "base/memory/scoped_ptr.h" 11#include "base/memory/scoped_vector.h" 12#include "base/run_loop.h" 13#include "testing/gtest/include/gtest/gtest.h" 14 15namespace media { 16 17namespace { 18 19class FakeMidiManager : public MidiManager { 20 public: 21 FakeMidiManager() : start_initialization_is_called_(false) {} 22 virtual ~FakeMidiManager() {} 23 24 // MidiManager implementation. 25 virtual void StartInitialization() OVERRIDE { 26 start_initialization_is_called_ = true; 27 } 28 29 virtual void DispatchSendMidiData(MidiManagerClient* client, 30 uint32 port_index, 31 const std::vector<uint8>& data, 32 double timestamp) OVERRIDE {} 33 34 // Utility functions for testing. 35 void CallCompleteInitialization(MidiResult result) { 36 CompleteInitialization(result); 37 } 38 39 size_t GetClientCount() const { 40 return clients_size_for_testing(); 41 } 42 43 size_t GetPendingClientCount() const { 44 return pending_clients_size_for_testing(); 45 } 46 47 bool start_initialization_is_called_; 48 49 private: 50 DISALLOW_COPY_AND_ASSIGN(FakeMidiManager); 51}; 52 53class FakeMidiManagerClient : public MidiManagerClient { 54 public: 55 explicit FakeMidiManagerClient(int client_id) 56 : client_id_(client_id), 57 result_(MIDI_NOT_SUPPORTED), 58 wait_for_result_(true) {} 59 virtual ~FakeMidiManagerClient() {} 60 61 // MidiManagerClient implementation. 62 virtual void CompleteStartSession(int client_id, MidiResult result) OVERRIDE { 63 EXPECT_TRUE(wait_for_result_); 64 CHECK_EQ(client_id_, client_id); 65 result_ = result; 66 wait_for_result_ = false; 67 } 68 69 virtual void ReceiveMidiData(uint32 port_index, const uint8* data, 70 size_t size, double timestamp) OVERRIDE {} 71 virtual void AccumulateMidiBytesSent(size_t size) OVERRIDE {} 72 73 int client_id() const { return client_id_; } 74 MidiResult result() const { return result_; } 75 76 MidiResult WaitForResult() { 77 base::RunLoop run_loop; 78 // CompleteStartSession() is called inside the message loop on the same 79 // thread. Protection for |wait_for_result_| is not needed. 80 while (wait_for_result_) 81 run_loop.RunUntilIdle(); 82 return result(); 83 } 84 85 private: 86 int client_id_; 87 MidiResult result_; 88 bool wait_for_result_; 89 90 DISALLOW_COPY_AND_ASSIGN(FakeMidiManagerClient); 91}; 92 93class MidiManagerTest : public ::testing::Test { 94 public: 95 MidiManagerTest() 96 : manager_(new FakeMidiManager), 97 message_loop_(new base::MessageLoop) {} 98 virtual ~MidiManagerTest() {} 99 100 protected: 101 void StartTheFirstSession(FakeMidiManagerClient* client) { 102 EXPECT_FALSE(manager_->start_initialization_is_called_); 103 EXPECT_EQ(0U, manager_->GetClientCount()); 104 EXPECT_EQ(0U, manager_->GetPendingClientCount()); 105 manager_->StartSession(client, client->client_id()); 106 EXPECT_EQ(0U, manager_->GetClientCount()); 107 EXPECT_EQ(1U, manager_->GetPendingClientCount()); 108 EXPECT_TRUE(manager_->start_initialization_is_called_); 109 EXPECT_EQ(0U, manager_->GetClientCount()); 110 EXPECT_EQ(1U, manager_->GetPendingClientCount()); 111 EXPECT_TRUE(manager_->start_initialization_is_called_); 112 } 113 114 void StartTheNthSession(FakeMidiManagerClient* client, size_t nth) { 115 EXPECT_EQ(nth != 1, manager_->start_initialization_is_called_); 116 EXPECT_EQ(0U, manager_->GetClientCount()); 117 EXPECT_EQ(nth - 1, manager_->GetPendingClientCount()); 118 119 // StartInitialization() should not be called for the second and later 120 // sessions. 121 manager_->start_initialization_is_called_ = false; 122 manager_->StartSession(client, client->client_id()); 123 EXPECT_EQ(nth == 1, manager_->start_initialization_is_called_); 124 manager_->start_initialization_is_called_ = true; 125 } 126 127 void EndSession(FakeMidiManagerClient* client, size_t before, size_t after) { 128 EXPECT_EQ(before, manager_->GetClientCount()); 129 manager_->EndSession(client); 130 EXPECT_EQ(after, manager_->GetClientCount()); 131 } 132 133 void CompleteInitialization(MidiResult result) { 134 manager_->CallCompleteInitialization(result); 135 } 136 137 void RunLoopUntilIdle() { 138 base::RunLoop run_loop; 139 run_loop.RunUntilIdle(); 140 } 141 142 protected: 143 scoped_ptr<FakeMidiManager> manager_; 144 145 private: 146 scoped_ptr<base::MessageLoop> message_loop_; 147 148 DISALLOW_COPY_AND_ASSIGN(MidiManagerTest); 149}; 150 151TEST_F(MidiManagerTest, StartAndEndSession) { 152 scoped_ptr<FakeMidiManagerClient> client; 153 client.reset(new FakeMidiManagerClient(0)); 154 155 StartTheFirstSession(client.get()); 156 CompleteInitialization(MIDI_OK); 157 EXPECT_EQ(MIDI_OK, client->WaitForResult()); 158 EndSession(client.get(), 1U, 0U); 159} 160 161TEST_F(MidiManagerTest, StartAndEndSessionWithError) { 162 scoped_ptr<FakeMidiManagerClient> client; 163 client.reset(new FakeMidiManagerClient(1)); 164 165 StartTheFirstSession(client.get()); 166 CompleteInitialization(MIDI_INITIALIZATION_ERROR); 167 EXPECT_EQ(MIDI_INITIALIZATION_ERROR, client->WaitForResult()); 168 EndSession(client.get(), 0U, 0U); 169} 170 171TEST_F(MidiManagerTest, StartMultipleSessions) { 172 scoped_ptr<FakeMidiManagerClient> client1; 173 scoped_ptr<FakeMidiManagerClient> client2; 174 scoped_ptr<FakeMidiManagerClient> client3; 175 client1.reset(new FakeMidiManagerClient(0)); 176 client2.reset(new FakeMidiManagerClient(1)); 177 client3.reset(new FakeMidiManagerClient(1)); 178 179 StartTheFirstSession(client1.get()); 180 StartTheNthSession(client2.get(), 2); 181 StartTheNthSession(client3.get(), 3); 182 CompleteInitialization(MIDI_OK); 183 EXPECT_EQ(MIDI_OK, client1->WaitForResult()); 184 EXPECT_EQ(MIDI_OK, client2->WaitForResult()); 185 EXPECT_EQ(MIDI_OK, client3->WaitForResult()); 186 EndSession(client1.get(), 3U, 2U); 187 EndSession(client2.get(), 2U, 1U); 188 EndSession(client3.get(), 1U, 0U); 189} 190 191// TODO(toyoshim): Add a test for a MidiManagerClient that has multiple 192// sessions with multiple client_id. 193 194TEST_F(MidiManagerTest, TooManyPendingSessions) { 195 // Push as many client requests for starting session as possible. 196 ScopedVector<FakeMidiManagerClient> many_existing_clients; 197 many_existing_clients.resize(MidiManager::kMaxPendingClientCount); 198 for (size_t i = 0; i < MidiManager::kMaxPendingClientCount; ++i) { 199 many_existing_clients[i] = new FakeMidiManagerClient(i); 200 StartTheNthSession(many_existing_clients[i], i + 1); 201 } 202 203 // Push the last client that should be rejected for too many pending requests. 204 scoped_ptr<FakeMidiManagerClient> additional_client( 205 new FakeMidiManagerClient(MidiManager::kMaxPendingClientCount)); 206 manager_->start_initialization_is_called_ = false; 207 manager_->StartSession(additional_client.get(), 208 additional_client->client_id()); 209 EXPECT_FALSE(manager_->start_initialization_is_called_); 210 EXPECT_EQ(MIDI_INITIALIZATION_ERROR, additional_client->result()); 211 212 // Other clients still should not receive a result. 213 RunLoopUntilIdle(); 214 for (size_t i = 0; i < many_existing_clients.size(); ++i) 215 EXPECT_EQ(MIDI_NOT_SUPPORTED, many_existing_clients[i]->result()); 216 217 // The result MIDI_OK should be distributed to other clients. 218 CompleteInitialization(MIDI_OK); 219 for (size_t i = 0; i < many_existing_clients.size(); ++i) 220 EXPECT_EQ(MIDI_OK, many_existing_clients[i]->WaitForResult()); 221 222 // Close all successful sessions in FIFO order. 223 size_t sessions = many_existing_clients.size(); 224 for (size_t i = 0; i < many_existing_clients.size(); ++i, --sessions) 225 EndSession(many_existing_clients[i], sessions, sessions - 1); 226} 227 228TEST_F(MidiManagerTest, AbortSession) { 229 // A client starting a session can be destructed while an asynchronous 230 // initialization is performed. 231 scoped_ptr<FakeMidiManagerClient> client; 232 client.reset(new FakeMidiManagerClient(0)); 233 234 StartTheFirstSession(client.get()); 235 EndSession(client.get(), 0, 0); 236 client.reset(); 237 238 // Following function should not call the destructed |client| function. 239 CompleteInitialization(MIDI_OK); 240 base::RunLoop run_loop; 241 run_loop.RunUntilIdle(); 242} 243 244TEST_F(MidiManagerTest, CreateMidiManager) { 245 scoped_ptr<FakeMidiManagerClient> client; 246 client.reset(new FakeMidiManagerClient(0)); 247 248 scoped_ptr<MidiManager> manager(MidiManager::Create()); 249 manager->StartSession(client.get(), client->client_id()); 250 251 MidiResult result = client->WaitForResult(); 252 // This #ifdef needs to be identical to the one in media/midi/midi_manager.cc. 253 // Do not change the condition for disabling this test. 254#if !defined(OS_MACOSX) && !defined(OS_WIN) && !defined(USE_ALSA) && \ 255 !defined(OS_ANDROID) && !defined(OS_CHROMEOS) 256 EXPECT_EQ(MIDI_NOT_SUPPORTED, result); 257#elif defined(USE_ALSA) 258 // Temporary until http://crbug.com/371230 is resolved. 259 EXPECT_TRUE((result == MIDI_OK) || (result == MIDI_INITIALIZATION_ERROR)); 260#else 261 EXPECT_EQ(MIDI_OK, result); 262#endif 263} 264 265} // namespace 266 267} // namespace media 268