blob: 16ec8b53bc9216975bd721245050cd3e27bd2f22 [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"
toyoshimc9f52132014-10-15 05:50:07 -070016#include "base/message_loop/message_loop.h"
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000017#include "base/run_loop.h"
toyoshim71818732015-04-07 04:15:44 -070018#include "base/system_monitor/system_monitor.h"
avi793390d2015-12-22 22:22:36 -080019#include "build/build_config.h"
toyoshimf4d61522017-02-10 02:03:32 -080020#include "media/midi/midi_service.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
toyoshimec2570a2016-10-21 02:15:27 -070027using mojom::PortState;
toyoshim2f3a48f2016-10-17 01:54:13 -070028using mojom::Result;
29
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000030class FakeMidiManager : public MidiManager {
31 public:
Takashi Toyoshimaa88997d2017-09-07 08:30:18 +000032 explicit FakeMidiManager(MidiService* service)
33 : MidiManager(service),
toyoshimf4d61522017-02-10 02:03:32 -080034 start_initialization_is_called_(false),
35 finalize_is_called_(false) {}
dcheng63ccbc32014-10-21 05:23:27 -070036 ~FakeMidiManager() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000037
38 // MidiManager implementation.
dcheng63ccbc32014-10-21 05:23:27 -070039 void StartInitialization() override {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000040 start_initialization_is_called_ = true;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000041 }
42
toyoshim7a809662015-10-06 00:54:21 -070043 void Finalize() override { finalize_is_called_ = true; }
44
dcheng63ccbc32014-10-21 05:23:27 -070045 void DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -050046 uint32_t port_index,
47 const std::vector<uint8_t>& data,
dcheng63ccbc32014-10-21 05:23:27 -070048 double timestamp) override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000049
50 // Utility functions for testing.
toyoshimf1b88962015-07-09 14:14:51 -070051 void CallCompleteInitialization(Result result) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000052 CompleteInitialization(result);
53 }
54
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000055 size_t GetClientCount() const {
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +000056 return clients_size_for_testing();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000057 }
58
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000059 size_t GetPendingClientCount() const {
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +000060 return pending_clients_size_for_testing();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000061 }
62
63 bool start_initialization_is_called_;
toyoshim7a809662015-10-06 00:54:21 -070064 bool finalize_is_called_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000065
66 private:
67 DISALLOW_COPY_AND_ASSIGN(FakeMidiManager);
68};
69
Takashi Toyoshimaa88997d2017-09-07 08:30:18 +000070class FakeMidiManagerFactory : public MidiService::ManagerFactory {
71 public:
72 FakeMidiManagerFactory() = default;
73 ~FakeMidiManagerFactory() override = default;
74 std::unique_ptr<MidiManager> Create(MidiService* service) override {
75 std::unique_ptr<FakeMidiManager> manager =
76 std::make_unique<FakeMidiManager>(service);
77 // |manaegr| will be owned by the caller MidiService instance, and valid
78 // while the MidiService instance is running.
79 // MidiService::Shutdown() or destructor will destruct it, and |manager_|
80 // get to be invalid after that.
81 manager_ = manager.get();
82 return manager;
83 }
84 FakeMidiManager* GetCreatedManager() {
85 DCHECK(manager_);
86 return manager_;
87 }
88
89 private:
90 FakeMidiManager* manager_ = nullptr;
91};
92
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000093class FakeMidiManagerClient : public MidiManagerClient {
94 public:
toyoshima485ff92014-10-23 00:17:59 -070095 FakeMidiManagerClient()
toyoshimf1b88962015-07-09 14:14:51 -070096 : result_(Result::NOT_SUPPORTED), wait_for_result_(true) {}
dcheng63ccbc32014-10-21 05:23:27 -070097 ~FakeMidiManagerClient() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000098
99 // MidiManagerClient implementation.
toyoshima62d6742014-10-23 09:05:03 -0700100 void AddInputPort(const MidiPortInfo& info) override {}
101 void AddOutputPort(const MidiPortInfo& info) override {}
toyoshimec2570a2016-10-21 02:15:27 -0700102 void SetInputPortState(uint32_t port_index, PortState state) override {}
103 void SetOutputPortState(uint32_t port_index, PortState state) override {}
toyoshima62d6742014-10-23 09:05:03 -0700104
toyoshimf1b88962015-07-09 14:14:51 -0700105 void CompleteStartSession(Result result) override {
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000106 EXPECT_TRUE(wait_for_result_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000107 result_ = result;
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000108 wait_for_result_ = false;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000109 }
110
Avi Drissman3528fd02015-12-18 20:11:31 -0500111 void ReceiveMidiData(uint32_t port_index,
112 const uint8_t* data,
dcheng63ccbc32014-10-21 05:23:27 -0700113 size_t size,
114 double timestamp) override {}
115 void AccumulateMidiBytesSent(size_t size) override {}
toyoshim7a809662015-10-06 00:54:21 -0700116 void Detach() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000117
toyoshimf1b88962015-07-09 14:14:51 -0700118 Result result() const { return result_; }
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000119
toyoshimf1b88962015-07-09 14:14:51 -0700120 Result WaitForResult() {
toyoshim11f7d572014-10-20 02:37:10 -0700121 while (wait_for_result_) {
122 base::RunLoop run_loop;
123 run_loop.RunUntilIdle();
124 }
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +0000125 return result();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000126 }
127
128 private:
toyoshimf1b88962015-07-09 14:14:51 -0700129 Result result_;
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000130 bool wait_for_result_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000131
132 DISALLOW_COPY_AND_ASSIGN(FakeMidiManagerClient);
133};
134
135class MidiManagerTest : public ::testing::Test {
136 public:
Takashi Toyoshimaa88997d2017-09-07 08:30:18 +0000137 MidiManagerTest() : message_loop_(std::make_unique<base::MessageLoop>()) {
138 std::unique_ptr<FakeMidiManagerFactory> factory =
139 std::make_unique<FakeMidiManagerFactory>();
140 factory_ = factory.get();
141 service_ = std::make_unique<MidiService>(std::move(factory));
142 manager_ = factory_->GetCreatedManager();
143 }
toyoshim7a809662015-10-06 00:54:21 -0700144 ~MidiManagerTest() override {
145 manager_->Shutdown();
146 base::RunLoop run_loop;
147 run_loop.RunUntilIdle();
148 EXPECT_EQ(manager_->start_initialization_is_called_,
149 manager_->finalize_is_called_);
150 }
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000151
152 protected:
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000153 void StartTheFirstSession(FakeMidiManagerClient* client) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000154 EXPECT_FALSE(manager_->start_initialization_is_called_);
155 EXPECT_EQ(0U, manager_->GetClientCount());
156 EXPECT_EQ(0U, manager_->GetPendingClientCount());
toyoshima485ff92014-10-23 00:17:59 -0700157 manager_->StartSession(client);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000158 EXPECT_EQ(0U, manager_->GetClientCount());
159 EXPECT_EQ(1U, manager_->GetPendingClientCount());
160 EXPECT_TRUE(manager_->start_initialization_is_called_);
161 EXPECT_EQ(0U, manager_->GetClientCount());
162 EXPECT_EQ(1U, manager_->GetPendingClientCount());
163 EXPECT_TRUE(manager_->start_initialization_is_called_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000164 }
165
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000166 void StartTheNthSession(FakeMidiManagerClient* client, size_t nth) {
167 EXPECT_EQ(nth != 1, manager_->start_initialization_is_called_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000168 EXPECT_EQ(0U, manager_->GetClientCount());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000169 EXPECT_EQ(nth - 1, manager_->GetPendingClientCount());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000170
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000171 // StartInitialization() should not be called for the second and later
172 // sessions.
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000173 manager_->start_initialization_is_called_ = false;
toyoshima485ff92014-10-23 00:17:59 -0700174 manager_->StartSession(client);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000175 EXPECT_EQ(nth == 1, manager_->start_initialization_is_called_);
176 manager_->start_initialization_is_called_ = true;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000177 }
178
179 void EndSession(FakeMidiManagerClient* client, size_t before, size_t after) {
180 EXPECT_EQ(before, manager_->GetClientCount());
181 manager_->EndSession(client);
182 EXPECT_EQ(after, manager_->GetClientCount());
183 }
184
toyoshimf1b88962015-07-09 14:14:51 -0700185 void CompleteInitialization(Result result) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000186 manager_->CallCompleteInitialization(result);
187 }
188
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000189 void RunLoopUntilIdle() {
190 base::RunLoop run_loop;
191 run_loop.RunUntilIdle();
192 }
193
194 protected:
toyoshimf4d61522017-02-10 02:03:32 -0800195 FakeMidiManager* manager_; // Owned by |service_|.
Takashi Toyoshimaa88997d2017-09-07 08:30:18 +0000196 FakeMidiManagerFactory* factory_; // Owned by |service_|.
toyoshimf4d61522017-02-10 02:03:32 -0800197 std::unique_ptr<MidiService> service_;
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000198
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000199 private:
danakj75afea02016-04-25 20:36:04 -0700200 std::unique_ptr<base::MessageLoop> message_loop_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000201
202 DISALLOW_COPY_AND_ASSIGN(MidiManagerTest);
203};
204
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000205TEST_F(MidiManagerTest, StartAndEndSession) {
danakj75afea02016-04-25 20:36:04 -0700206 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700207 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000208
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000209 StartTheFirstSession(client.get());
toyoshimf1b88962015-07-09 14:14:51 -0700210 CompleteInitialization(Result::OK);
211 EXPECT_EQ(Result::OK, client->WaitForResult());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000212 EndSession(client.get(), 1U, 0U);
213}
214
215TEST_F(MidiManagerTest, StartAndEndSessionWithError) {
danakj75afea02016-04-25 20:36:04 -0700216 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700217 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000218
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000219 StartTheFirstSession(client.get());
toyoshimf1b88962015-07-09 14:14:51 -0700220 CompleteInitialization(Result::INITIALIZATION_ERROR);
221 EXPECT_EQ(Result::INITIALIZATION_ERROR, client->WaitForResult());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000222 EndSession(client.get(), 0U, 0U);
223}
224
225TEST_F(MidiManagerTest, StartMultipleSessions) {
danakj75afea02016-04-25 20:36:04 -0700226 std::unique_ptr<FakeMidiManagerClient> client1;
227 std::unique_ptr<FakeMidiManagerClient> client2;
228 std::unique_ptr<FakeMidiManagerClient> client3;
toyoshima485ff92014-10-23 00:17:59 -0700229 client1.reset(new FakeMidiManagerClient);
230 client2.reset(new FakeMidiManagerClient);
231 client3.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000232
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000233 StartTheFirstSession(client1.get());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000234 StartTheNthSession(client2.get(), 2);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000235 StartTheNthSession(client3.get(), 3);
toyoshimf1b88962015-07-09 14:14:51 -0700236 CompleteInitialization(Result::OK);
237 EXPECT_EQ(Result::OK, client1->WaitForResult());
238 EXPECT_EQ(Result::OK, client2->WaitForResult());
239 EXPECT_EQ(Result::OK, client3->WaitForResult());
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000240 EndSession(client1.get(), 3U, 2U);
241 EndSession(client2.get(), 2U, 1U);
242 EndSession(client3.get(), 1U, 0U);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000243}
244
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000245// TODO(toyoshim): Add a test for a MidiManagerClient that has multiple
246// sessions with multiple client_id.
247
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000248TEST_F(MidiManagerTest, TooManyPendingSessions) {
249 // Push as many client requests for starting session as possible.
xiaofeng.zhang06152d82017-05-21 04:39:34 -0700250 std::vector<std::unique_ptr<FakeMidiManagerClient>> many_existing_clients;
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000251 many_existing_clients.resize(MidiManager::kMaxPendingClientCount);
252 for (size_t i = 0; i < MidiManager::kMaxPendingClientCount; ++i) {
Takashi Toyoshimaa88997d2017-09-07 08:30:18 +0000253 many_existing_clients[i] = std::make_unique<FakeMidiManagerClient>();
xiaofeng.zhang06152d82017-05-21 04:39:34 -0700254 StartTheNthSession(many_existing_clients[i].get(), i + 1);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000255 }
toyoshim7a809662015-10-06 00:54:21 -0700256 EXPECT_TRUE(manager_->start_initialization_is_called_);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000257
258 // Push the last client that should be rejected for too many pending requests.
danakj75afea02016-04-25 20:36:04 -0700259 std::unique_ptr<FakeMidiManagerClient> additional_client(
toyoshima485ff92014-10-23 00:17:59 -0700260 new FakeMidiManagerClient);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000261 manager_->start_initialization_is_called_ = false;
toyoshima485ff92014-10-23 00:17:59 -0700262 manager_->StartSession(additional_client.get());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000263 EXPECT_FALSE(manager_->start_initialization_is_called_);
toyoshim7a809662015-10-06 00:54:21 -0700264 manager_->start_initialization_is_called_ = true;
toyoshimf1b88962015-07-09 14:14:51 -0700265 EXPECT_EQ(Result::INITIALIZATION_ERROR, additional_client->result());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000266
267 // Other clients still should not receive a result.
268 RunLoopUntilIdle();
269 for (size_t i = 0; i < many_existing_clients.size(); ++i)
toyoshimf1b88962015-07-09 14:14:51 -0700270 EXPECT_EQ(Result::NOT_SUPPORTED, many_existing_clients[i]->result());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000271
toyoshimf1b88962015-07-09 14:14:51 -0700272 // The Result::OK should be distributed to other clients.
273 CompleteInitialization(Result::OK);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000274 for (size_t i = 0; i < many_existing_clients.size(); ++i)
toyoshimf1b88962015-07-09 14:14:51 -0700275 EXPECT_EQ(Result::OK, many_existing_clients[i]->WaitForResult());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000276
277 // Close all successful sessions in FIFO order.
278 size_t sessions = many_existing_clients.size();
279 for (size_t i = 0; i < many_existing_clients.size(); ++i, --sessions)
xiaofeng.zhang06152d82017-05-21 04:39:34 -0700280 EndSession(many_existing_clients[i].get(), sessions, sessions - 1);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000281}
282
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000283TEST_F(MidiManagerTest, AbortSession) {
284 // A client starting a session can be destructed while an asynchronous
285 // initialization is performed.
danakj75afea02016-04-25 20:36:04 -0700286 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700287 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000288
289 StartTheFirstSession(client.get());
290 EndSession(client.get(), 0, 0);
291 client.reset();
292
293 // Following function should not call the destructed |client| function.
toyoshimf1b88962015-07-09 14:14:51 -0700294 CompleteInitialization(Result::OK);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000295 base::RunLoop run_loop;
296 run_loop.RunUntilIdle();
297}
298
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000299TEST_F(MidiManagerTest, CreateMidiManager) {
toyoshim71818732015-04-07 04:15:44 -0700300 // SystemMonitor is needed on Windows.
301 base::SystemMonitor system_monitor;
302
toyoshimf4d61522017-02-10 02:03:32 -0800303 std::unique_ptr<FakeMidiManagerClient> client(
Takashi Toyoshimaa88997d2017-09-07 08:30:18 +0000304 std::make_unique<FakeMidiManagerClient>());
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000305
Takashi Toyoshimaa88997d2017-09-07 08:30:18 +0000306 std::unique_ptr<MidiService> service(std::make_unique<MidiService>());
toyoshimf4d61522017-02-10 02:03:32 -0800307 service->StartSession(client.get());
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +0000308
toyoshimf1b88962015-07-09 14:14:51 -0700309 Result result = client->WaitForResult();
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +0000310 // This #ifdef needs to be identical to the one in media/midi/midi_manager.cc.
311 // Do not change the condition for disabling this test.
agoode7de413f2015-04-24 00:13:39 -0700312#if !defined(OS_MACOSX) && !defined(OS_WIN) && \
313 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
toyoshimf1b88962015-07-09 14:14:51 -0700314 EXPECT_EQ(Result::NOT_SUPPORTED, result);
agoode@chromium.org25227512014-06-08 05:12:05 +0000315#elif defined(USE_ALSA)
316 // Temporary until http://crbug.com/371230 is resolved.
toyoshimf1b88962015-07-09 14:14:51 -0700317 EXPECT_TRUE(result == Result::OK || result == Result::INITIALIZATION_ERROR);
dnicoara@chromium.orgc404df62014-05-06 22:56:00 +0000318#else
toyoshimf1b88962015-07-09 14:14:51 -0700319 EXPECT_EQ(Result::OK, result);
dnicoara@chromium.orgc404df62014-05-06 22:56:00 +0000320#endif
toyoshim7a809662015-10-06 00:54:21 -0700321
toyoshimf4d61522017-02-10 02:03:32 -0800322 service->Shutdown();
toyoshim7a809662015-10-06 00:54:21 -0700323 base::RunLoop run_loop;
324 run_loop.RunUntilIdle();
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000325}
326
toyoshim7a809662015-10-06 00:54:21 -0700327// TODO(toyoshim): Add multi-threaded unit tests to check races around
328// StartInitialization(), CompleteInitialization(), and Finalize().
329
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000330} // namespace
331
toyoshime147c5e2015-05-07 21:58:31 -0700332} // namespace midi