blob: 9975c0ab1421aa213ca059df4143cc174eaee643 [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"
toyoshimf4d61522017-02-10 02:03:32 -080016#include "base/memory/ptr_util.h"
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000017#include "base/memory/scoped_vector.h"
toyoshimc9f52132014-10-15 05:50:07 -070018#include "base/message_loop/message_loop.h"
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000019#include "base/run_loop.h"
toyoshim71818732015-04-07 04:15:44 -070020#include "base/system_monitor/system_monitor.h"
avi793390d2015-12-22 22:22:36 -080021#include "build/build_config.h"
toyoshimf4d61522017-02-10 02:03:32 -080022#include "media/midi/midi_service.h"
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000023#include "testing/gtest/include/gtest/gtest.h"
24
toyoshime147c5e2015-05-07 21:58:31 -070025namespace midi {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000026
27namespace {
28
toyoshimec2570a2016-10-21 02:15:27 -070029using mojom::PortState;
toyoshim2f3a48f2016-10-17 01:54:13 -070030using mojom::Result;
31
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000032class FakeMidiManager : public MidiManager {
33 public:
toyoshim7a809662015-10-06 00:54:21 -070034 FakeMidiManager()
toyoshimf4d61522017-02-10 02:03:32 -080035 : MidiManager(nullptr),
36 start_initialization_is_called_(false),
37 finalize_is_called_(false) {}
dcheng63ccbc32014-10-21 05:23:27 -070038 ~FakeMidiManager() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000039
40 // MidiManager implementation.
dcheng63ccbc32014-10-21 05:23:27 -070041 void StartInitialization() override {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000042 start_initialization_is_called_ = true;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000043 }
44
toyoshim7a809662015-10-06 00:54:21 -070045 void Finalize() override { finalize_is_called_ = true; }
46
dcheng63ccbc32014-10-21 05:23:27 -070047 void DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -050048 uint32_t port_index,
49 const std::vector<uint8_t>& data,
dcheng63ccbc32014-10-21 05:23:27 -070050 double timestamp) override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000051
52 // Utility functions for testing.
toyoshimf1b88962015-07-09 14:14:51 -070053 void CallCompleteInitialization(Result result) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000054 CompleteInitialization(result);
55 }
56
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000057 size_t GetClientCount() const {
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +000058 return clients_size_for_testing();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000059 }
60
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000061 size_t GetPendingClientCount() const {
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +000062 return pending_clients_size_for_testing();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000063 }
64
65 bool start_initialization_is_called_;
toyoshim7a809662015-10-06 00:54:21 -070066 bool finalize_is_called_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000067
68 private:
69 DISALLOW_COPY_AND_ASSIGN(FakeMidiManager);
70};
71
72class FakeMidiManagerClient : public MidiManagerClient {
73 public:
toyoshima485ff92014-10-23 00:17:59 -070074 FakeMidiManagerClient()
toyoshimf1b88962015-07-09 14:14:51 -070075 : result_(Result::NOT_SUPPORTED), wait_for_result_(true) {}
dcheng63ccbc32014-10-21 05:23:27 -070076 ~FakeMidiManagerClient() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000077
78 // MidiManagerClient implementation.
toyoshima62d6742014-10-23 09:05:03 -070079 void AddInputPort(const MidiPortInfo& info) override {}
80 void AddOutputPort(const MidiPortInfo& info) override {}
toyoshimec2570a2016-10-21 02:15:27 -070081 void SetInputPortState(uint32_t port_index, PortState state) override {}
82 void SetOutputPortState(uint32_t port_index, PortState state) override {}
toyoshima62d6742014-10-23 09:05:03 -070083
toyoshimf1b88962015-07-09 14:14:51 -070084 void CompleteStartSession(Result result) override {
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000085 EXPECT_TRUE(wait_for_result_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000086 result_ = result;
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000087 wait_for_result_ = false;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000088 }
89
Avi Drissman3528fd02015-12-18 20:11:31 -050090 void ReceiveMidiData(uint32_t port_index,
91 const uint8_t* data,
dcheng63ccbc32014-10-21 05:23:27 -070092 size_t size,
93 double timestamp) override {}
94 void AccumulateMidiBytesSent(size_t size) override {}
toyoshim7a809662015-10-06 00:54:21 -070095 void Detach() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000096
toyoshimf1b88962015-07-09 14:14:51 -070097 Result result() const { return result_; }
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000098
toyoshimf1b88962015-07-09 14:14:51 -070099 Result WaitForResult() {
toyoshim11f7d572014-10-20 02:37:10 -0700100 while (wait_for_result_) {
101 base::RunLoop run_loop;
102 run_loop.RunUntilIdle();
103 }
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +0000104 return result();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000105 }
106
107 private:
toyoshimf1b88962015-07-09 14:14:51 -0700108 Result result_;
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000109 bool wait_for_result_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000110
111 DISALLOW_COPY_AND_ASSIGN(FakeMidiManagerClient);
112};
113
114class MidiManagerTest : public ::testing::Test {
115 public:
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000116 MidiManagerTest()
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000117 : manager_(new FakeMidiManager),
toyoshimf4d61522017-02-10 02:03:32 -0800118 service_(new MidiService(base::WrapUnique(manager_))),
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000119 message_loop_(new base::MessageLoop) {}
toyoshim7a809662015-10-06 00:54:21 -0700120 ~MidiManagerTest() override {
121 manager_->Shutdown();
122 base::RunLoop run_loop;
123 run_loop.RunUntilIdle();
124 EXPECT_EQ(manager_->start_initialization_is_called_,
125 manager_->finalize_is_called_);
126 }
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000127
128 protected:
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000129 void StartTheFirstSession(FakeMidiManagerClient* client) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000130 EXPECT_FALSE(manager_->start_initialization_is_called_);
131 EXPECT_EQ(0U, manager_->GetClientCount());
132 EXPECT_EQ(0U, manager_->GetPendingClientCount());
toyoshima485ff92014-10-23 00:17:59 -0700133 manager_->StartSession(client);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000134 EXPECT_EQ(0U, manager_->GetClientCount());
135 EXPECT_EQ(1U, manager_->GetPendingClientCount());
136 EXPECT_TRUE(manager_->start_initialization_is_called_);
137 EXPECT_EQ(0U, manager_->GetClientCount());
138 EXPECT_EQ(1U, manager_->GetPendingClientCount());
139 EXPECT_TRUE(manager_->start_initialization_is_called_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000140 }
141
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000142 void StartTheNthSession(FakeMidiManagerClient* client, size_t nth) {
143 EXPECT_EQ(nth != 1, manager_->start_initialization_is_called_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000144 EXPECT_EQ(0U, manager_->GetClientCount());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000145 EXPECT_EQ(nth - 1, manager_->GetPendingClientCount());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000146
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000147 // StartInitialization() should not be called for the second and later
148 // sessions.
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000149 manager_->start_initialization_is_called_ = false;
toyoshima485ff92014-10-23 00:17:59 -0700150 manager_->StartSession(client);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000151 EXPECT_EQ(nth == 1, manager_->start_initialization_is_called_);
152 manager_->start_initialization_is_called_ = true;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000153 }
154
155 void EndSession(FakeMidiManagerClient* client, size_t before, size_t after) {
156 EXPECT_EQ(before, manager_->GetClientCount());
157 manager_->EndSession(client);
158 EXPECT_EQ(after, manager_->GetClientCount());
159 }
160
toyoshimf1b88962015-07-09 14:14:51 -0700161 void CompleteInitialization(Result result) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000162 manager_->CallCompleteInitialization(result);
163 }
164
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000165 void RunLoopUntilIdle() {
166 base::RunLoop run_loop;
167 run_loop.RunUntilIdle();
168 }
169
170 protected:
toyoshimf4d61522017-02-10 02:03:32 -0800171 FakeMidiManager* manager_; // Owned by |service_|.
172 std::unique_ptr<MidiService> service_;
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000173
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000174 private:
danakj75afea02016-04-25 20:36:04 -0700175 std::unique_ptr<base::MessageLoop> message_loop_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000176
177 DISALLOW_COPY_AND_ASSIGN(MidiManagerTest);
178};
179
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000180TEST_F(MidiManagerTest, StartAndEndSession) {
danakj75afea02016-04-25 20:36:04 -0700181 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700182 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000183
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000184 StartTheFirstSession(client.get());
toyoshimf1b88962015-07-09 14:14:51 -0700185 CompleteInitialization(Result::OK);
186 EXPECT_EQ(Result::OK, client->WaitForResult());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000187 EndSession(client.get(), 1U, 0U);
188}
189
190TEST_F(MidiManagerTest, StartAndEndSessionWithError) {
danakj75afea02016-04-25 20:36:04 -0700191 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700192 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000193
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000194 StartTheFirstSession(client.get());
toyoshimf1b88962015-07-09 14:14:51 -0700195 CompleteInitialization(Result::INITIALIZATION_ERROR);
196 EXPECT_EQ(Result::INITIALIZATION_ERROR, client->WaitForResult());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000197 EndSession(client.get(), 0U, 0U);
198}
199
200TEST_F(MidiManagerTest, StartMultipleSessions) {
danakj75afea02016-04-25 20:36:04 -0700201 std::unique_ptr<FakeMidiManagerClient> client1;
202 std::unique_ptr<FakeMidiManagerClient> client2;
203 std::unique_ptr<FakeMidiManagerClient> client3;
toyoshima485ff92014-10-23 00:17:59 -0700204 client1.reset(new FakeMidiManagerClient);
205 client2.reset(new FakeMidiManagerClient);
206 client3.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000207
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000208 StartTheFirstSession(client1.get());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000209 StartTheNthSession(client2.get(), 2);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000210 StartTheNthSession(client3.get(), 3);
toyoshimf1b88962015-07-09 14:14:51 -0700211 CompleteInitialization(Result::OK);
212 EXPECT_EQ(Result::OK, client1->WaitForResult());
213 EXPECT_EQ(Result::OK, client2->WaitForResult());
214 EXPECT_EQ(Result::OK, client3->WaitForResult());
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000215 EndSession(client1.get(), 3U, 2U);
216 EndSession(client2.get(), 2U, 1U);
217 EndSession(client3.get(), 1U, 0U);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000218}
219
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000220// TODO(toyoshim): Add a test for a MidiManagerClient that has multiple
221// sessions with multiple client_id.
222
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000223TEST_F(MidiManagerTest, TooManyPendingSessions) {
224 // Push as many client requests for starting session as possible.
225 ScopedVector<FakeMidiManagerClient> many_existing_clients;
226 many_existing_clients.resize(MidiManager::kMaxPendingClientCount);
227 for (size_t i = 0; i < MidiManager::kMaxPendingClientCount; ++i) {
toyoshima485ff92014-10-23 00:17:59 -0700228 many_existing_clients[i] = new FakeMidiManagerClient;
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000229 StartTheNthSession(many_existing_clients[i], i + 1);
230 }
toyoshim7a809662015-10-06 00:54:21 -0700231 EXPECT_TRUE(manager_->start_initialization_is_called_);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000232
233 // Push the last client that should be rejected for too many pending requests.
danakj75afea02016-04-25 20:36:04 -0700234 std::unique_ptr<FakeMidiManagerClient> additional_client(
toyoshima485ff92014-10-23 00:17:59 -0700235 new FakeMidiManagerClient);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000236 manager_->start_initialization_is_called_ = false;
toyoshima485ff92014-10-23 00:17:59 -0700237 manager_->StartSession(additional_client.get());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000238 EXPECT_FALSE(manager_->start_initialization_is_called_);
toyoshim7a809662015-10-06 00:54:21 -0700239 manager_->start_initialization_is_called_ = true;
toyoshimf1b88962015-07-09 14:14:51 -0700240 EXPECT_EQ(Result::INITIALIZATION_ERROR, additional_client->result());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000241
242 // Other clients still should not receive a result.
243 RunLoopUntilIdle();
244 for (size_t i = 0; i < many_existing_clients.size(); ++i)
toyoshimf1b88962015-07-09 14:14:51 -0700245 EXPECT_EQ(Result::NOT_SUPPORTED, many_existing_clients[i]->result());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000246
toyoshimf1b88962015-07-09 14:14:51 -0700247 // The Result::OK should be distributed to other clients.
248 CompleteInitialization(Result::OK);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000249 for (size_t i = 0; i < many_existing_clients.size(); ++i)
toyoshimf1b88962015-07-09 14:14:51 -0700250 EXPECT_EQ(Result::OK, many_existing_clients[i]->WaitForResult());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000251
252 // Close all successful sessions in FIFO order.
253 size_t sessions = many_existing_clients.size();
254 for (size_t i = 0; i < many_existing_clients.size(); ++i, --sessions)
255 EndSession(many_existing_clients[i], sessions, sessions - 1);
256}
257
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000258TEST_F(MidiManagerTest, AbortSession) {
259 // A client starting a session can be destructed while an asynchronous
260 // initialization is performed.
danakj75afea02016-04-25 20:36:04 -0700261 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700262 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000263
264 StartTheFirstSession(client.get());
265 EndSession(client.get(), 0, 0);
266 client.reset();
267
268 // Following function should not call the destructed |client| function.
toyoshimf1b88962015-07-09 14:14:51 -0700269 CompleteInitialization(Result::OK);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000270 base::RunLoop run_loop;
271 run_loop.RunUntilIdle();
272}
273
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000274TEST_F(MidiManagerTest, CreateMidiManager) {
toyoshim71818732015-04-07 04:15:44 -0700275 // SystemMonitor is needed on Windows.
276 base::SystemMonitor system_monitor;
277
toyoshimf4d61522017-02-10 02:03:32 -0800278 std::unique_ptr<FakeMidiManagerClient> client(
279 base::MakeUnique<FakeMidiManagerClient>());
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000280
toyoshimf4d61522017-02-10 02:03:32 -0800281 std::unique_ptr<MidiService> service(base::MakeUnique<MidiService>());
282 service->StartSession(client.get());
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +0000283
toyoshimf1b88962015-07-09 14:14:51 -0700284 Result result = client->WaitForResult();
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +0000285 // This #ifdef needs to be identical to the one in media/midi/midi_manager.cc.
286 // Do not change the condition for disabling this test.
agoode7de413f2015-04-24 00:13:39 -0700287#if !defined(OS_MACOSX) && !defined(OS_WIN) && \
288 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
toyoshimf1b88962015-07-09 14:14:51 -0700289 EXPECT_EQ(Result::NOT_SUPPORTED, result);
agoode@chromium.org25227512014-06-08 05:12:05 +0000290#elif defined(USE_ALSA)
291 // Temporary until http://crbug.com/371230 is resolved.
toyoshimf1b88962015-07-09 14:14:51 -0700292 EXPECT_TRUE(result == Result::OK || result == Result::INITIALIZATION_ERROR);
dnicoara@chromium.orgc404df62014-05-06 22:56:00 +0000293#else
toyoshimf1b88962015-07-09 14:14:51 -0700294 EXPECT_EQ(Result::OK, result);
dnicoara@chromium.orgc404df62014-05-06 22:56:00 +0000295#endif
toyoshim7a809662015-10-06 00:54:21 -0700296
toyoshimf4d61522017-02-10 02:03:32 -0800297 service->Shutdown();
toyoshim7a809662015-10-06 00:54:21 -0700298 base::RunLoop run_loop;
299 run_loop.RunUntilIdle();
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000300}
301
toyoshim7a809662015-10-06 00:54:21 -0700302// TODO(toyoshim): Add multi-threaded unit tests to check races around
303// StartInitialization(), CompleteInitialization(), and Finalize().
304
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000305} // namespace
306
toyoshime147c5e2015-05-07 21:58:31 -0700307} // namespace midi