blob: 6ae5defd5febfd7f6bd268dd12ec51be4c5935ba [file] [log] [blame]
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +00001// 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
avi793390d2015-12-22 22:22:36 -08007#include <stddef.h>
8#include <stdint.h>
9
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000010#include <vector>
11
toyoshimc9f52132014-10-15 05:50:07 -070012#include "base/bind.h"
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000013#include "base/logging.h"
avi793390d2015-12-22 22:22:36 -080014#include "base/macros.h"
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000015#include "base/memory/scoped_ptr.h"
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000016#include "base/memory/scoped_vector.h"
toyoshimc9f52132014-10-15 05:50:07 -070017#include "base/message_loop/message_loop.h"
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000018#include "base/run_loop.h"
toyoshim71818732015-04-07 04:15:44 -070019#include "base/system_monitor/system_monitor.h"
avi793390d2015-12-22 22:22:36 -080020#include "build/build_config.h"
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000021#include "testing/gtest/include/gtest/gtest.h"
22
23namespace media {
toyoshime147c5e2015-05-07 21:58:31 -070024namespace midi {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000025
26namespace {
27
28class FakeMidiManager : public MidiManager {
29 public:
toyoshim7a809662015-10-06 00:54:21 -070030 FakeMidiManager()
31 : start_initialization_is_called_(false), finalize_is_called_(false) {}
dcheng63ccbc32014-10-21 05:23:27 -070032 ~FakeMidiManager() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000033
34 // MidiManager implementation.
dcheng63ccbc32014-10-21 05:23:27 -070035 void StartInitialization() override {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000036 start_initialization_is_called_ = true;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000037 }
38
toyoshim7a809662015-10-06 00:54:21 -070039 void Finalize() override { finalize_is_called_ = true; }
40
dcheng63ccbc32014-10-21 05:23:27 -070041 void DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -050042 uint32_t port_index,
43 const std::vector<uint8_t>& data,
dcheng63ccbc32014-10-21 05:23:27 -070044 double timestamp) override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000045
46 // Utility functions for testing.
toyoshimf1b88962015-07-09 14:14:51 -070047 void CallCompleteInitialization(Result result) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000048 CompleteInitialization(result);
49 }
50
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000051 size_t GetClientCount() const {
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +000052 return clients_size_for_testing();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000053 }
54
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000055 size_t GetPendingClientCount() const {
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +000056 return pending_clients_size_for_testing();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000057 }
58
59 bool start_initialization_is_called_;
toyoshim7a809662015-10-06 00:54:21 -070060 bool finalize_is_called_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000061
62 private:
63 DISALLOW_COPY_AND_ASSIGN(FakeMidiManager);
64};
65
66class FakeMidiManagerClient : public MidiManagerClient {
67 public:
toyoshima485ff92014-10-23 00:17:59 -070068 FakeMidiManagerClient()
toyoshimf1b88962015-07-09 14:14:51 -070069 : result_(Result::NOT_SUPPORTED), wait_for_result_(true) {}
dcheng63ccbc32014-10-21 05:23:27 -070070 ~FakeMidiManagerClient() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000071
72 // MidiManagerClient implementation.
toyoshima62d6742014-10-23 09:05:03 -070073 void AddInputPort(const MidiPortInfo& info) override {}
74 void AddOutputPort(const MidiPortInfo& info) override {}
Avi Drissman3528fd02015-12-18 20:11:31 -050075 void SetInputPortState(uint32_t port_index, MidiPortState state) override {}
76 void SetOutputPortState(uint32_t port_index, MidiPortState state) override {}
toyoshima62d6742014-10-23 09:05:03 -070077
toyoshimf1b88962015-07-09 14:14:51 -070078 void CompleteStartSession(Result result) override {
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000079 EXPECT_TRUE(wait_for_result_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000080 result_ = result;
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000081 wait_for_result_ = false;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000082 }
83
Avi Drissman3528fd02015-12-18 20:11:31 -050084 void ReceiveMidiData(uint32_t port_index,
85 const uint8_t* data,
dcheng63ccbc32014-10-21 05:23:27 -070086 size_t size,
87 double timestamp) override {}
88 void AccumulateMidiBytesSent(size_t size) override {}
toyoshim7a809662015-10-06 00:54:21 -070089 void Detach() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000090
toyoshimf1b88962015-07-09 14:14:51 -070091 Result result() const { return result_; }
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000092
toyoshimf1b88962015-07-09 14:14:51 -070093 Result WaitForResult() {
toyoshim11f7d572014-10-20 02:37:10 -070094 while (wait_for_result_) {
95 base::RunLoop run_loop;
96 run_loop.RunUntilIdle();
97 }
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +000098 return result();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000099 }
100
101 private:
toyoshimf1b88962015-07-09 14:14:51 -0700102 Result result_;
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000103 bool wait_for_result_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000104
105 DISALLOW_COPY_AND_ASSIGN(FakeMidiManagerClient);
106};
107
108class MidiManagerTest : public ::testing::Test {
109 public:
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000110 MidiManagerTest()
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000111 : manager_(new FakeMidiManager),
112 message_loop_(new base::MessageLoop) {}
toyoshim7a809662015-10-06 00:54:21 -0700113 ~MidiManagerTest() override {
114 manager_->Shutdown();
115 base::RunLoop run_loop;
116 run_loop.RunUntilIdle();
117 EXPECT_EQ(manager_->start_initialization_is_called_,
118 manager_->finalize_is_called_);
119 }
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000120
121 protected:
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000122 void StartTheFirstSession(FakeMidiManagerClient* client) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000123 EXPECT_FALSE(manager_->start_initialization_is_called_);
124 EXPECT_EQ(0U, manager_->GetClientCount());
125 EXPECT_EQ(0U, manager_->GetPendingClientCount());
toyoshima485ff92014-10-23 00:17:59 -0700126 manager_->StartSession(client);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000127 EXPECT_EQ(0U, manager_->GetClientCount());
128 EXPECT_EQ(1U, manager_->GetPendingClientCount());
129 EXPECT_TRUE(manager_->start_initialization_is_called_);
130 EXPECT_EQ(0U, manager_->GetClientCount());
131 EXPECT_EQ(1U, manager_->GetPendingClientCount());
132 EXPECT_TRUE(manager_->start_initialization_is_called_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000133 }
134
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000135 void StartTheNthSession(FakeMidiManagerClient* client, size_t nth) {
136 EXPECT_EQ(nth != 1, manager_->start_initialization_is_called_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000137 EXPECT_EQ(0U, manager_->GetClientCount());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000138 EXPECT_EQ(nth - 1, manager_->GetPendingClientCount());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000139
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000140 // StartInitialization() should not be called for the second and later
141 // sessions.
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000142 manager_->start_initialization_is_called_ = false;
toyoshima485ff92014-10-23 00:17:59 -0700143 manager_->StartSession(client);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000144 EXPECT_EQ(nth == 1, manager_->start_initialization_is_called_);
145 manager_->start_initialization_is_called_ = true;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000146 }
147
148 void EndSession(FakeMidiManagerClient* client, size_t before, size_t after) {
149 EXPECT_EQ(before, manager_->GetClientCount());
150 manager_->EndSession(client);
151 EXPECT_EQ(after, manager_->GetClientCount());
152 }
153
toyoshimf1b88962015-07-09 14:14:51 -0700154 void CompleteInitialization(Result result) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000155 manager_->CallCompleteInitialization(result);
156 }
157
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000158 void RunLoopUntilIdle() {
159 base::RunLoop run_loop;
160 run_loop.RunUntilIdle();
161 }
162
163 protected:
164 scoped_ptr<FakeMidiManager> manager_;
165
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000166 private:
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000167 scoped_ptr<base::MessageLoop> message_loop_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000168
169 DISALLOW_COPY_AND_ASSIGN(MidiManagerTest);
170};
171
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000172TEST_F(MidiManagerTest, StartAndEndSession) {
173 scoped_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700174 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000175
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000176 StartTheFirstSession(client.get());
toyoshimf1b88962015-07-09 14:14:51 -0700177 CompleteInitialization(Result::OK);
178 EXPECT_EQ(Result::OK, client->WaitForResult());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000179 EndSession(client.get(), 1U, 0U);
180}
181
182TEST_F(MidiManagerTest, StartAndEndSessionWithError) {
183 scoped_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700184 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000185
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000186 StartTheFirstSession(client.get());
toyoshimf1b88962015-07-09 14:14:51 -0700187 CompleteInitialization(Result::INITIALIZATION_ERROR);
188 EXPECT_EQ(Result::INITIALIZATION_ERROR, client->WaitForResult());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000189 EndSession(client.get(), 0U, 0U);
190}
191
192TEST_F(MidiManagerTest, StartMultipleSessions) {
193 scoped_ptr<FakeMidiManagerClient> client1;
194 scoped_ptr<FakeMidiManagerClient> client2;
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000195 scoped_ptr<FakeMidiManagerClient> client3;
toyoshima485ff92014-10-23 00:17:59 -0700196 client1.reset(new FakeMidiManagerClient);
197 client2.reset(new FakeMidiManagerClient);
198 client3.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000199
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000200 StartTheFirstSession(client1.get());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000201 StartTheNthSession(client2.get(), 2);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000202 StartTheNthSession(client3.get(), 3);
toyoshimf1b88962015-07-09 14:14:51 -0700203 CompleteInitialization(Result::OK);
204 EXPECT_EQ(Result::OK, client1->WaitForResult());
205 EXPECT_EQ(Result::OK, client2->WaitForResult());
206 EXPECT_EQ(Result::OK, client3->WaitForResult());
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000207 EndSession(client1.get(), 3U, 2U);
208 EndSession(client2.get(), 2U, 1U);
209 EndSession(client3.get(), 1U, 0U);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000210}
211
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000212// TODO(toyoshim): Add a test for a MidiManagerClient that has multiple
213// sessions with multiple client_id.
214
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000215TEST_F(MidiManagerTest, TooManyPendingSessions) {
216 // Push as many client requests for starting session as possible.
217 ScopedVector<FakeMidiManagerClient> many_existing_clients;
218 many_existing_clients.resize(MidiManager::kMaxPendingClientCount);
219 for (size_t i = 0; i < MidiManager::kMaxPendingClientCount; ++i) {
toyoshima485ff92014-10-23 00:17:59 -0700220 many_existing_clients[i] = new FakeMidiManagerClient;
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000221 StartTheNthSession(many_existing_clients[i], i + 1);
222 }
toyoshim7a809662015-10-06 00:54:21 -0700223 EXPECT_TRUE(manager_->start_initialization_is_called_);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000224
225 // Push the last client that should be rejected for too many pending requests.
226 scoped_ptr<FakeMidiManagerClient> additional_client(
toyoshima485ff92014-10-23 00:17:59 -0700227 new FakeMidiManagerClient);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000228 manager_->start_initialization_is_called_ = false;
toyoshima485ff92014-10-23 00:17:59 -0700229 manager_->StartSession(additional_client.get());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000230 EXPECT_FALSE(manager_->start_initialization_is_called_);
toyoshim7a809662015-10-06 00:54:21 -0700231 manager_->start_initialization_is_called_ = true;
toyoshimf1b88962015-07-09 14:14:51 -0700232 EXPECT_EQ(Result::INITIALIZATION_ERROR, additional_client->result());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000233
234 // Other clients still should not receive a result.
235 RunLoopUntilIdle();
236 for (size_t i = 0; i < many_existing_clients.size(); ++i)
toyoshimf1b88962015-07-09 14:14:51 -0700237 EXPECT_EQ(Result::NOT_SUPPORTED, many_existing_clients[i]->result());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000238
toyoshimf1b88962015-07-09 14:14:51 -0700239 // The Result::OK should be distributed to other clients.
240 CompleteInitialization(Result::OK);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000241 for (size_t i = 0; i < many_existing_clients.size(); ++i)
toyoshimf1b88962015-07-09 14:14:51 -0700242 EXPECT_EQ(Result::OK, many_existing_clients[i]->WaitForResult());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000243
244 // Close all successful sessions in FIFO order.
245 size_t sessions = many_existing_clients.size();
246 for (size_t i = 0; i < many_existing_clients.size(); ++i, --sessions)
247 EndSession(many_existing_clients[i], sessions, sessions - 1);
248}
249
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000250TEST_F(MidiManagerTest, AbortSession) {
251 // A client starting a session can be destructed while an asynchronous
252 // initialization is performed.
253 scoped_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700254 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000255
256 StartTheFirstSession(client.get());
257 EndSession(client.get(), 0, 0);
258 client.reset();
259
260 // Following function should not call the destructed |client| function.
toyoshimf1b88962015-07-09 14:14:51 -0700261 CompleteInitialization(Result::OK);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000262 base::RunLoop run_loop;
263 run_loop.RunUntilIdle();
264}
265
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000266TEST_F(MidiManagerTest, CreateMidiManager) {
toyoshim71818732015-04-07 04:15:44 -0700267 // SystemMonitor is needed on Windows.
268 base::SystemMonitor system_monitor;
269
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000270 scoped_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700271 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000272
273 scoped_ptr<MidiManager> manager(MidiManager::Create());
toyoshima485ff92014-10-23 00:17:59 -0700274 manager->StartSession(client.get());
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +0000275
toyoshimf1b88962015-07-09 14:14:51 -0700276 Result result = client->WaitForResult();
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +0000277 // This #ifdef needs to be identical to the one in media/midi/midi_manager.cc.
278 // Do not change the condition for disabling this test.
agoode7de413f2015-04-24 00:13:39 -0700279#if !defined(OS_MACOSX) && !defined(OS_WIN) && \
280 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
toyoshimf1b88962015-07-09 14:14:51 -0700281 EXPECT_EQ(Result::NOT_SUPPORTED, result);
agoode@chromium.org25227512014-06-08 05:12:05 +0000282#elif defined(USE_ALSA)
283 // Temporary until http://crbug.com/371230 is resolved.
toyoshimf1b88962015-07-09 14:14:51 -0700284 EXPECT_TRUE(result == Result::OK || result == Result::INITIALIZATION_ERROR);
dnicoara@chromium.orgc404df62014-05-06 22:56:00 +0000285#else
toyoshimf1b88962015-07-09 14:14:51 -0700286 EXPECT_EQ(Result::OK, result);
dnicoara@chromium.orgc404df62014-05-06 22:56:00 +0000287#endif
toyoshim7a809662015-10-06 00:54:21 -0700288
289 manager->Shutdown();
290 base::RunLoop run_loop;
291 run_loop.RunUntilIdle();
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000292}
293
toyoshim7a809662015-10-06 00:54:21 -0700294// TODO(toyoshim): Add multi-threaded unit tests to check races around
295// StartInitialization(), CompleteInitialization(), and Finalize().
296
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000297} // namespace
298
toyoshime147c5e2015-05-07 21:58:31 -0700299} // namespace midi
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000300} // namespace media