blob: 5a073b5b9605675268219afb77905cb31eec978a [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
toyoshim2f3a48f2016-10-17 01:54:13 -070027using mojom::Result;
28
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000029class FakeMidiManager : public MidiManager {
30 public:
toyoshim7a809662015-10-06 00:54:21 -070031 FakeMidiManager()
32 : start_initialization_is_called_(false), finalize_is_called_(false) {}
dcheng63ccbc32014-10-21 05:23:27 -070033 ~FakeMidiManager() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000034
35 // MidiManager implementation.
dcheng63ccbc32014-10-21 05:23:27 -070036 void StartInitialization() override {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000037 start_initialization_is_called_ = true;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000038 }
39
toyoshim7a809662015-10-06 00:54:21 -070040 void Finalize() override { finalize_is_called_ = true; }
41
dcheng63ccbc32014-10-21 05:23:27 -070042 void DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -050043 uint32_t port_index,
44 const std::vector<uint8_t>& data,
dcheng63ccbc32014-10-21 05:23:27 -070045 double timestamp) override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000046
47 // Utility functions for testing.
toyoshimf1b88962015-07-09 14:14:51 -070048 void CallCompleteInitialization(Result result) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000049 CompleteInitialization(result);
50 }
51
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000052 size_t GetClientCount() const {
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +000053 return clients_size_for_testing();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000054 }
55
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000056 size_t GetPendingClientCount() const {
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +000057 return pending_clients_size_for_testing();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000058 }
59
60 bool start_initialization_is_called_;
toyoshim7a809662015-10-06 00:54:21 -070061 bool finalize_is_called_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000062
63 private:
64 DISALLOW_COPY_AND_ASSIGN(FakeMidiManager);
65};
66
67class FakeMidiManagerClient : public MidiManagerClient {
68 public:
toyoshima485ff92014-10-23 00:17:59 -070069 FakeMidiManagerClient()
toyoshimf1b88962015-07-09 14:14:51 -070070 : result_(Result::NOT_SUPPORTED), wait_for_result_(true) {}
dcheng63ccbc32014-10-21 05:23:27 -070071 ~FakeMidiManagerClient() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000072
73 // MidiManagerClient implementation.
toyoshima62d6742014-10-23 09:05:03 -070074 void AddInputPort(const MidiPortInfo& info) override {}
75 void AddOutputPort(const MidiPortInfo& info) override {}
Avi Drissman3528fd02015-12-18 20:11:31 -050076 void SetInputPortState(uint32_t port_index, MidiPortState state) override {}
77 void SetOutputPortState(uint32_t port_index, MidiPortState state) override {}
toyoshima62d6742014-10-23 09:05:03 -070078
toyoshimf1b88962015-07-09 14:14:51 -070079 void CompleteStartSession(Result result) override {
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000080 EXPECT_TRUE(wait_for_result_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000081 result_ = result;
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000082 wait_for_result_ = false;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000083 }
84
Avi Drissman3528fd02015-12-18 20:11:31 -050085 void ReceiveMidiData(uint32_t port_index,
86 const uint8_t* data,
dcheng63ccbc32014-10-21 05:23:27 -070087 size_t size,
88 double timestamp) override {}
89 void AccumulateMidiBytesSent(size_t size) override {}
toyoshim7a809662015-10-06 00:54:21 -070090 void Detach() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000091
toyoshimf1b88962015-07-09 14:14:51 -070092 Result result() const { return result_; }
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000093
toyoshimf1b88962015-07-09 14:14:51 -070094 Result WaitForResult() {
toyoshim11f7d572014-10-20 02:37:10 -070095 while (wait_for_result_) {
96 base::RunLoop run_loop;
97 run_loop.RunUntilIdle();
98 }
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +000099 return result();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000100 }
101
102 private:
toyoshimf1b88962015-07-09 14:14:51 -0700103 Result result_;
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000104 bool wait_for_result_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000105
106 DISALLOW_COPY_AND_ASSIGN(FakeMidiManagerClient);
107};
108
109class MidiManagerTest : public ::testing::Test {
110 public:
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000111 MidiManagerTest()
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000112 : manager_(new FakeMidiManager),
113 message_loop_(new base::MessageLoop) {}
toyoshim7a809662015-10-06 00:54:21 -0700114 ~MidiManagerTest() override {
115 manager_->Shutdown();
116 base::RunLoop run_loop;
117 run_loop.RunUntilIdle();
118 EXPECT_EQ(manager_->start_initialization_is_called_,
119 manager_->finalize_is_called_);
120 }
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000121
122 protected:
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000123 void StartTheFirstSession(FakeMidiManagerClient* client) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000124 EXPECT_FALSE(manager_->start_initialization_is_called_);
125 EXPECT_EQ(0U, manager_->GetClientCount());
126 EXPECT_EQ(0U, manager_->GetPendingClientCount());
toyoshima485ff92014-10-23 00:17:59 -0700127 manager_->StartSession(client);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000128 EXPECT_EQ(0U, manager_->GetClientCount());
129 EXPECT_EQ(1U, manager_->GetPendingClientCount());
130 EXPECT_TRUE(manager_->start_initialization_is_called_);
131 EXPECT_EQ(0U, manager_->GetClientCount());
132 EXPECT_EQ(1U, manager_->GetPendingClientCount());
133 EXPECT_TRUE(manager_->start_initialization_is_called_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000134 }
135
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000136 void StartTheNthSession(FakeMidiManagerClient* client, size_t nth) {
137 EXPECT_EQ(nth != 1, manager_->start_initialization_is_called_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000138 EXPECT_EQ(0U, manager_->GetClientCount());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000139 EXPECT_EQ(nth - 1, manager_->GetPendingClientCount());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000140
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000141 // StartInitialization() should not be called for the second and later
142 // sessions.
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000143 manager_->start_initialization_is_called_ = false;
toyoshima485ff92014-10-23 00:17:59 -0700144 manager_->StartSession(client);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000145 EXPECT_EQ(nth == 1, manager_->start_initialization_is_called_);
146 manager_->start_initialization_is_called_ = true;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000147 }
148
149 void EndSession(FakeMidiManagerClient* client, size_t before, size_t after) {
150 EXPECT_EQ(before, manager_->GetClientCount());
151 manager_->EndSession(client);
152 EXPECT_EQ(after, manager_->GetClientCount());
153 }
154
toyoshimf1b88962015-07-09 14:14:51 -0700155 void CompleteInitialization(Result result) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000156 manager_->CallCompleteInitialization(result);
157 }
158
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000159 void RunLoopUntilIdle() {
160 base::RunLoop run_loop;
161 run_loop.RunUntilIdle();
162 }
163
164 protected:
danakj75afea02016-04-25 20:36:04 -0700165 std::unique_ptr<FakeMidiManager> manager_;
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000166
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000167 private:
danakj75afea02016-04-25 20:36:04 -0700168 std::unique_ptr<base::MessageLoop> message_loop_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000169
170 DISALLOW_COPY_AND_ASSIGN(MidiManagerTest);
171};
172
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000173TEST_F(MidiManagerTest, StartAndEndSession) {
danakj75afea02016-04-25 20:36:04 -0700174 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700175 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000176
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000177 StartTheFirstSession(client.get());
toyoshimf1b88962015-07-09 14:14:51 -0700178 CompleteInitialization(Result::OK);
179 EXPECT_EQ(Result::OK, client->WaitForResult());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000180 EndSession(client.get(), 1U, 0U);
181}
182
183TEST_F(MidiManagerTest, StartAndEndSessionWithError) {
danakj75afea02016-04-25 20:36:04 -0700184 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700185 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000186
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000187 StartTheFirstSession(client.get());
toyoshimf1b88962015-07-09 14:14:51 -0700188 CompleteInitialization(Result::INITIALIZATION_ERROR);
189 EXPECT_EQ(Result::INITIALIZATION_ERROR, client->WaitForResult());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000190 EndSession(client.get(), 0U, 0U);
191}
192
193TEST_F(MidiManagerTest, StartMultipleSessions) {
danakj75afea02016-04-25 20:36:04 -0700194 std::unique_ptr<FakeMidiManagerClient> client1;
195 std::unique_ptr<FakeMidiManagerClient> client2;
196 std::unique_ptr<FakeMidiManagerClient> client3;
toyoshima485ff92014-10-23 00:17:59 -0700197 client1.reset(new FakeMidiManagerClient);
198 client2.reset(new FakeMidiManagerClient);
199 client3.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000200
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000201 StartTheFirstSession(client1.get());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000202 StartTheNthSession(client2.get(), 2);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000203 StartTheNthSession(client3.get(), 3);
toyoshimf1b88962015-07-09 14:14:51 -0700204 CompleteInitialization(Result::OK);
205 EXPECT_EQ(Result::OK, client1->WaitForResult());
206 EXPECT_EQ(Result::OK, client2->WaitForResult());
207 EXPECT_EQ(Result::OK, client3->WaitForResult());
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000208 EndSession(client1.get(), 3U, 2U);
209 EndSession(client2.get(), 2U, 1U);
210 EndSession(client3.get(), 1U, 0U);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000211}
212
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000213// TODO(toyoshim): Add a test for a MidiManagerClient that has multiple
214// sessions with multiple client_id.
215
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000216TEST_F(MidiManagerTest, TooManyPendingSessions) {
217 // Push as many client requests for starting session as possible.
218 ScopedVector<FakeMidiManagerClient> many_existing_clients;
219 many_existing_clients.resize(MidiManager::kMaxPendingClientCount);
220 for (size_t i = 0; i < MidiManager::kMaxPendingClientCount; ++i) {
toyoshima485ff92014-10-23 00:17:59 -0700221 many_existing_clients[i] = new FakeMidiManagerClient;
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000222 StartTheNthSession(many_existing_clients[i], i + 1);
223 }
toyoshim7a809662015-10-06 00:54:21 -0700224 EXPECT_TRUE(manager_->start_initialization_is_called_);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000225
226 // Push the last client that should be rejected for too many pending requests.
danakj75afea02016-04-25 20:36:04 -0700227 std::unique_ptr<FakeMidiManagerClient> additional_client(
toyoshima485ff92014-10-23 00:17:59 -0700228 new FakeMidiManagerClient);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000229 manager_->start_initialization_is_called_ = false;
toyoshima485ff92014-10-23 00:17:59 -0700230 manager_->StartSession(additional_client.get());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000231 EXPECT_FALSE(manager_->start_initialization_is_called_);
toyoshim7a809662015-10-06 00:54:21 -0700232 manager_->start_initialization_is_called_ = true;
toyoshimf1b88962015-07-09 14:14:51 -0700233 EXPECT_EQ(Result::INITIALIZATION_ERROR, additional_client->result());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000234
235 // Other clients still should not receive a result.
236 RunLoopUntilIdle();
237 for (size_t i = 0; i < many_existing_clients.size(); ++i)
toyoshimf1b88962015-07-09 14:14:51 -0700238 EXPECT_EQ(Result::NOT_SUPPORTED, many_existing_clients[i]->result());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000239
toyoshimf1b88962015-07-09 14:14:51 -0700240 // The Result::OK should be distributed to other clients.
241 CompleteInitialization(Result::OK);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000242 for (size_t i = 0; i < many_existing_clients.size(); ++i)
toyoshimf1b88962015-07-09 14:14:51 -0700243 EXPECT_EQ(Result::OK, many_existing_clients[i]->WaitForResult());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000244
245 // Close all successful sessions in FIFO order.
246 size_t sessions = many_existing_clients.size();
247 for (size_t i = 0; i < many_existing_clients.size(); ++i, --sessions)
248 EndSession(many_existing_clients[i], sessions, sessions - 1);
249}
250
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000251TEST_F(MidiManagerTest, AbortSession) {
252 // A client starting a session can be destructed while an asynchronous
253 // initialization is performed.
danakj75afea02016-04-25 20:36:04 -0700254 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700255 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000256
257 StartTheFirstSession(client.get());
258 EndSession(client.get(), 0, 0);
259 client.reset();
260
261 // Following function should not call the destructed |client| function.
toyoshimf1b88962015-07-09 14:14:51 -0700262 CompleteInitialization(Result::OK);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000263 base::RunLoop run_loop;
264 run_loop.RunUntilIdle();
265}
266
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000267TEST_F(MidiManagerTest, CreateMidiManager) {
toyoshim71818732015-04-07 04:15:44 -0700268 // SystemMonitor is needed on Windows.
269 base::SystemMonitor system_monitor;
270
danakj75afea02016-04-25 20:36:04 -0700271 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700272 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000273
danakj75afea02016-04-25 20:36:04 -0700274 std::unique_ptr<MidiManager> manager(MidiManager::Create());
toyoshima485ff92014-10-23 00:17:59 -0700275 manager->StartSession(client.get());
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +0000276
toyoshimf1b88962015-07-09 14:14:51 -0700277 Result result = client->WaitForResult();
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +0000278 // This #ifdef needs to be identical to the one in media/midi/midi_manager.cc.
279 // Do not change the condition for disabling this test.
agoode7de413f2015-04-24 00:13:39 -0700280#if !defined(OS_MACOSX) && !defined(OS_WIN) && \
281 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
toyoshimf1b88962015-07-09 14:14:51 -0700282 EXPECT_EQ(Result::NOT_SUPPORTED, result);
agoode@chromium.org25227512014-06-08 05:12:05 +0000283#elif defined(USE_ALSA)
284 // Temporary until http://crbug.com/371230 is resolved.
toyoshimf1b88962015-07-09 14:14:51 -0700285 EXPECT_TRUE(result == Result::OK || result == Result::INITIALIZATION_ERROR);
dnicoara@chromium.orgc404df62014-05-06 22:56:00 +0000286#else
toyoshimf1b88962015-07-09 14:14:51 -0700287 EXPECT_EQ(Result::OK, result);
dnicoara@chromium.orgc404df62014-05-06 22:56:00 +0000288#endif
toyoshim7a809662015-10-06 00:54:21 -0700289
290 manager->Shutdown();
291 base::RunLoop run_loop;
292 run_loop.RunUntilIdle();
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000293}
294
toyoshim7a809662015-10-06 00:54:21 -0700295// TODO(toyoshim): Add multi-threaded unit tests to check races around
296// StartInitialization(), CompleteInitialization(), and Finalize().
297
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000298} // namespace
299
toyoshime147c5e2015-05-07 21:58:31 -0700300} // namespace midi