blob: eed774d667e5814151489043bbf2b63a01a328fd [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
danakj75afea02016-04-25 20:36:04 -070010#include <memory>
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000011#include <vector>
12
toyoshimc9f52132014-10-15 05:50:07 -070013#include "base/bind.h"
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000014#include "base/logging.h"
avi793390d2015-12-22 22:22:36 -080015#include "base/macros.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
toyoshime147c5e2015-05-07 21:58:31 -070023namespace midi {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000024
25namespace {
26
27class FakeMidiManager : public MidiManager {
28 public:
toyoshim7a809662015-10-06 00:54:21 -070029 FakeMidiManager()
30 : start_initialization_is_called_(false), finalize_is_called_(false) {}
dcheng63ccbc32014-10-21 05:23:27 -070031 ~FakeMidiManager() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000032
33 // MidiManager implementation.
dcheng63ccbc32014-10-21 05:23:27 -070034 void StartInitialization() override {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000035 start_initialization_is_called_ = true;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000036 }
37
toyoshim7a809662015-10-06 00:54:21 -070038 void Finalize() override { finalize_is_called_ = true; }
39
dcheng63ccbc32014-10-21 05:23:27 -070040 void DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -050041 uint32_t port_index,
42 const std::vector<uint8_t>& data,
dcheng63ccbc32014-10-21 05:23:27 -070043 double timestamp) override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000044
45 // Utility functions for testing.
toyoshimf1b88962015-07-09 14:14:51 -070046 void CallCompleteInitialization(Result result) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000047 CompleteInitialization(result);
48 }
49
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000050 size_t GetClientCount() const {
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +000051 return clients_size_for_testing();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000052 }
53
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000054 size_t GetPendingClientCount() const {
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +000055 return pending_clients_size_for_testing();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000056 }
57
58 bool start_initialization_is_called_;
toyoshim7a809662015-10-06 00:54:21 -070059 bool finalize_is_called_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000060
61 private:
62 DISALLOW_COPY_AND_ASSIGN(FakeMidiManager);
63};
64
65class FakeMidiManagerClient : public MidiManagerClient {
66 public:
toyoshima485ff92014-10-23 00:17:59 -070067 FakeMidiManagerClient()
toyoshimf1b88962015-07-09 14:14:51 -070068 : result_(Result::NOT_SUPPORTED), wait_for_result_(true) {}
dcheng63ccbc32014-10-21 05:23:27 -070069 ~FakeMidiManagerClient() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000070
71 // MidiManagerClient implementation.
toyoshima62d6742014-10-23 09:05:03 -070072 void AddInputPort(const MidiPortInfo& info) override {}
73 void AddOutputPort(const MidiPortInfo& info) override {}
Avi Drissman3528fd02015-12-18 20:11:31 -050074 void SetInputPortState(uint32_t port_index, MidiPortState state) override {}
75 void SetOutputPortState(uint32_t port_index, MidiPortState state) override {}
toyoshima62d6742014-10-23 09:05:03 -070076
toyoshimf1b88962015-07-09 14:14:51 -070077 void CompleteStartSession(Result result) override {
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000078 EXPECT_TRUE(wait_for_result_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000079 result_ = result;
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000080 wait_for_result_ = false;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000081 }
82
Avi Drissman3528fd02015-12-18 20:11:31 -050083 void ReceiveMidiData(uint32_t port_index,
84 const uint8_t* data,
dcheng63ccbc32014-10-21 05:23:27 -070085 size_t size,
86 double timestamp) override {}
87 void AccumulateMidiBytesSent(size_t size) override {}
toyoshim7a809662015-10-06 00:54:21 -070088 void Detach() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000089
toyoshimf1b88962015-07-09 14:14:51 -070090 Result result() const { return result_; }
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000091
toyoshimf1b88962015-07-09 14:14:51 -070092 Result WaitForResult() {
toyoshim11f7d572014-10-20 02:37:10 -070093 while (wait_for_result_) {
94 base::RunLoop run_loop;
95 run_loop.RunUntilIdle();
96 }
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +000097 return result();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000098 }
99
100 private:
toyoshimf1b88962015-07-09 14:14:51 -0700101 Result result_;
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000102 bool wait_for_result_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000103
104 DISALLOW_COPY_AND_ASSIGN(FakeMidiManagerClient);
105};
106
107class MidiManagerTest : public ::testing::Test {
108 public:
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000109 MidiManagerTest()
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000110 : manager_(new FakeMidiManager),
111 message_loop_(new base::MessageLoop) {}
toyoshim7a809662015-10-06 00:54:21 -0700112 ~MidiManagerTest() override {
113 manager_->Shutdown();
114 base::RunLoop run_loop;
115 run_loop.RunUntilIdle();
116 EXPECT_EQ(manager_->start_initialization_is_called_,
117 manager_->finalize_is_called_);
118 }
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000119
120 protected:
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000121 void StartTheFirstSession(FakeMidiManagerClient* client) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000122 EXPECT_FALSE(manager_->start_initialization_is_called_);
123 EXPECT_EQ(0U, manager_->GetClientCount());
124 EXPECT_EQ(0U, manager_->GetPendingClientCount());
toyoshima485ff92014-10-23 00:17:59 -0700125 manager_->StartSession(client);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000126 EXPECT_EQ(0U, manager_->GetClientCount());
127 EXPECT_EQ(1U, manager_->GetPendingClientCount());
128 EXPECT_TRUE(manager_->start_initialization_is_called_);
129 EXPECT_EQ(0U, manager_->GetClientCount());
130 EXPECT_EQ(1U, manager_->GetPendingClientCount());
131 EXPECT_TRUE(manager_->start_initialization_is_called_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000132 }
133
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000134 void StartTheNthSession(FakeMidiManagerClient* client, size_t nth) {
135 EXPECT_EQ(nth != 1, manager_->start_initialization_is_called_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000136 EXPECT_EQ(0U, manager_->GetClientCount());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000137 EXPECT_EQ(nth - 1, manager_->GetPendingClientCount());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000138
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000139 // StartInitialization() should not be called for the second and later
140 // sessions.
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000141 manager_->start_initialization_is_called_ = false;
toyoshima485ff92014-10-23 00:17:59 -0700142 manager_->StartSession(client);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000143 EXPECT_EQ(nth == 1, manager_->start_initialization_is_called_);
144 manager_->start_initialization_is_called_ = true;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000145 }
146
147 void EndSession(FakeMidiManagerClient* client, size_t before, size_t after) {
148 EXPECT_EQ(before, manager_->GetClientCount());
149 manager_->EndSession(client);
150 EXPECT_EQ(after, manager_->GetClientCount());
151 }
152
toyoshimf1b88962015-07-09 14:14:51 -0700153 void CompleteInitialization(Result result) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000154 manager_->CallCompleteInitialization(result);
155 }
156
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000157 void RunLoopUntilIdle() {
158 base::RunLoop run_loop;
159 run_loop.RunUntilIdle();
160 }
161
162 protected:
danakj75afea02016-04-25 20:36:04 -0700163 std::unique_ptr<FakeMidiManager> manager_;
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000164
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000165 private:
danakj75afea02016-04-25 20:36:04 -0700166 std::unique_ptr<base::MessageLoop> message_loop_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000167
168 DISALLOW_COPY_AND_ASSIGN(MidiManagerTest);
169};
170
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000171TEST_F(MidiManagerTest, StartAndEndSession) {
danakj75afea02016-04-25 20:36:04 -0700172 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700173 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000174
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000175 StartTheFirstSession(client.get());
toyoshimf1b88962015-07-09 14:14:51 -0700176 CompleteInitialization(Result::OK);
177 EXPECT_EQ(Result::OK, client->WaitForResult());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000178 EndSession(client.get(), 1U, 0U);
179}
180
181TEST_F(MidiManagerTest, StartAndEndSessionWithError) {
danakj75afea02016-04-25 20:36:04 -0700182 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700183 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000184
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000185 StartTheFirstSession(client.get());
toyoshimf1b88962015-07-09 14:14:51 -0700186 CompleteInitialization(Result::INITIALIZATION_ERROR);
187 EXPECT_EQ(Result::INITIALIZATION_ERROR, client->WaitForResult());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000188 EndSession(client.get(), 0U, 0U);
189}
190
191TEST_F(MidiManagerTest, StartMultipleSessions) {
danakj75afea02016-04-25 20:36:04 -0700192 std::unique_ptr<FakeMidiManagerClient> client1;
193 std::unique_ptr<FakeMidiManagerClient> client2;
194 std::unique_ptr<FakeMidiManagerClient> client3;
toyoshima485ff92014-10-23 00:17:59 -0700195 client1.reset(new FakeMidiManagerClient);
196 client2.reset(new FakeMidiManagerClient);
197 client3.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000198
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000199 StartTheFirstSession(client1.get());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000200 StartTheNthSession(client2.get(), 2);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000201 StartTheNthSession(client3.get(), 3);
toyoshimf1b88962015-07-09 14:14:51 -0700202 CompleteInitialization(Result::OK);
203 EXPECT_EQ(Result::OK, client1->WaitForResult());
204 EXPECT_EQ(Result::OK, client2->WaitForResult());
205 EXPECT_EQ(Result::OK, client3->WaitForResult());
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000206 EndSession(client1.get(), 3U, 2U);
207 EndSession(client2.get(), 2U, 1U);
208 EndSession(client3.get(), 1U, 0U);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000209}
210
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000211// TODO(toyoshim): Add a test for a MidiManagerClient that has multiple
212// sessions with multiple client_id.
213
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000214TEST_F(MidiManagerTest, TooManyPendingSessions) {
215 // Push as many client requests for starting session as possible.
216 ScopedVector<FakeMidiManagerClient> many_existing_clients;
217 many_existing_clients.resize(MidiManager::kMaxPendingClientCount);
218 for (size_t i = 0; i < MidiManager::kMaxPendingClientCount; ++i) {
toyoshima485ff92014-10-23 00:17:59 -0700219 many_existing_clients[i] = new FakeMidiManagerClient;
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000220 StartTheNthSession(many_existing_clients[i], i + 1);
221 }
toyoshim7a809662015-10-06 00:54:21 -0700222 EXPECT_TRUE(manager_->start_initialization_is_called_);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000223
224 // Push the last client that should be rejected for too many pending requests.
danakj75afea02016-04-25 20:36:04 -0700225 std::unique_ptr<FakeMidiManagerClient> additional_client(
toyoshima485ff92014-10-23 00:17:59 -0700226 new FakeMidiManagerClient);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000227 manager_->start_initialization_is_called_ = false;
toyoshima485ff92014-10-23 00:17:59 -0700228 manager_->StartSession(additional_client.get());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000229 EXPECT_FALSE(manager_->start_initialization_is_called_);
toyoshim7a809662015-10-06 00:54:21 -0700230 manager_->start_initialization_is_called_ = true;
toyoshimf1b88962015-07-09 14:14:51 -0700231 EXPECT_EQ(Result::INITIALIZATION_ERROR, additional_client->result());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000232
233 // Other clients still should not receive a result.
234 RunLoopUntilIdle();
235 for (size_t i = 0; i < many_existing_clients.size(); ++i)
toyoshimf1b88962015-07-09 14:14:51 -0700236 EXPECT_EQ(Result::NOT_SUPPORTED, many_existing_clients[i]->result());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000237
toyoshimf1b88962015-07-09 14:14:51 -0700238 // The Result::OK should be distributed to other clients.
239 CompleteInitialization(Result::OK);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000240 for (size_t i = 0; i < many_existing_clients.size(); ++i)
toyoshimf1b88962015-07-09 14:14:51 -0700241 EXPECT_EQ(Result::OK, many_existing_clients[i]->WaitForResult());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000242
243 // Close all successful sessions in FIFO order.
244 size_t sessions = many_existing_clients.size();
245 for (size_t i = 0; i < many_existing_clients.size(); ++i, --sessions)
246 EndSession(many_existing_clients[i], sessions, sessions - 1);
247}
248
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000249TEST_F(MidiManagerTest, AbortSession) {
250 // A client starting a session can be destructed while an asynchronous
251 // initialization is performed.
danakj75afea02016-04-25 20:36:04 -0700252 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700253 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000254
255 StartTheFirstSession(client.get());
256 EndSession(client.get(), 0, 0);
257 client.reset();
258
259 // Following function should not call the destructed |client| function.
toyoshimf1b88962015-07-09 14:14:51 -0700260 CompleteInitialization(Result::OK);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000261 base::RunLoop run_loop;
262 run_loop.RunUntilIdle();
263}
264
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000265TEST_F(MidiManagerTest, CreateMidiManager) {
toyoshim71818732015-04-07 04:15:44 -0700266 // SystemMonitor is needed on Windows.
267 base::SystemMonitor system_monitor;
268
danakj75afea02016-04-25 20:36:04 -0700269 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700270 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000271
danakj75afea02016-04-25 20:36:04 -0700272 std::unique_ptr<MidiManager> manager(MidiManager::Create());
toyoshima485ff92014-10-23 00:17:59 -0700273 manager->StartSession(client.get());
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +0000274
toyoshimf1b88962015-07-09 14:14:51 -0700275 Result result = client->WaitForResult();
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +0000276 // This #ifdef needs to be identical to the one in media/midi/midi_manager.cc.
277 // Do not change the condition for disabling this test.
agoode7de413f2015-04-24 00:13:39 -0700278#if !defined(OS_MACOSX) && !defined(OS_WIN) && \
279 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
toyoshimf1b88962015-07-09 14:14:51 -0700280 EXPECT_EQ(Result::NOT_SUPPORTED, result);
agoode@chromium.org25227512014-06-08 05:12:05 +0000281#elif defined(USE_ALSA)
282 // Temporary until http://crbug.com/371230 is resolved.
toyoshimf1b88962015-07-09 14:14:51 -0700283 EXPECT_TRUE(result == Result::OK || result == Result::INITIALIZATION_ERROR);
dnicoara@chromium.orgc404df62014-05-06 22:56:00 +0000284#else
toyoshimf1b88962015-07-09 14:14:51 -0700285 EXPECT_EQ(Result::OK, result);
dnicoara@chromium.orgc404df62014-05-06 22:56:00 +0000286#endif
toyoshim7a809662015-10-06 00:54:21 -0700287
288 manager->Shutdown();
289 base::RunLoop run_loop;
290 run_loop.RunUntilIdle();
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000291}
292
toyoshim7a809662015-10-06 00:54:21 -0700293// TODO(toyoshim): Add multi-threaded unit tests to check races around
294// StartInitialization(), CompleteInitialization(), and Finalize().
295
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000296} // namespace
297
toyoshime147c5e2015-05-07 21:58:31 -0700298} // namespace midi