blob: cc57a5ff9692ed3c4a2d4017c8d648731f8f5680 [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"
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"
toyoshimf4d61522017-02-10 02:03:32 -080021#include "media/midi/midi_service.h"
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000022#include "testing/gtest/include/gtest/gtest.h"
23
toyoshime147c5e2015-05-07 21:58:31 -070024namespace midi {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000025
26namespace {
27
toyoshimec2570a2016-10-21 02:15:27 -070028using mojom::PortState;
toyoshim2f3a48f2016-10-17 01:54:13 -070029using mojom::Result;
30
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000031class FakeMidiManager : public MidiManager {
32 public:
toyoshim7a809662015-10-06 00:54:21 -070033 FakeMidiManager()
toyoshimf4d61522017-02-10 02:03:32 -080034 : MidiManager(nullptr),
35 start_initialization_is_called_(false),
36 finalize_is_called_(false) {}
dcheng63ccbc32014-10-21 05:23:27 -070037 ~FakeMidiManager() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000038
39 // MidiManager implementation.
dcheng63ccbc32014-10-21 05:23:27 -070040 void StartInitialization() override {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000041 start_initialization_is_called_ = true;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000042 }
43
toyoshim7a809662015-10-06 00:54:21 -070044 void Finalize() override { finalize_is_called_ = true; }
45
dcheng63ccbc32014-10-21 05:23:27 -070046 void DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -050047 uint32_t port_index,
48 const std::vector<uint8_t>& data,
dcheng63ccbc32014-10-21 05:23:27 -070049 double timestamp) override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000050
51 // Utility functions for testing.
toyoshimf1b88962015-07-09 14:14:51 -070052 void CallCompleteInitialization(Result result) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000053 CompleteInitialization(result);
54 }
55
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000056 size_t GetClientCount() const {
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +000057 return clients_size_for_testing();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000058 }
59
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000060 size_t GetPendingClientCount() const {
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +000061 return pending_clients_size_for_testing();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000062 }
63
64 bool start_initialization_is_called_;
toyoshim7a809662015-10-06 00:54:21 -070065 bool finalize_is_called_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000066
67 private:
68 DISALLOW_COPY_AND_ASSIGN(FakeMidiManager);
69};
70
71class FakeMidiManagerClient : public MidiManagerClient {
72 public:
toyoshima485ff92014-10-23 00:17:59 -070073 FakeMidiManagerClient()
toyoshimf1b88962015-07-09 14:14:51 -070074 : result_(Result::NOT_SUPPORTED), wait_for_result_(true) {}
dcheng63ccbc32014-10-21 05:23:27 -070075 ~FakeMidiManagerClient() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000076
77 // MidiManagerClient implementation.
toyoshima62d6742014-10-23 09:05:03 -070078 void AddInputPort(const MidiPortInfo& info) override {}
79 void AddOutputPort(const MidiPortInfo& info) override {}
toyoshimec2570a2016-10-21 02:15:27 -070080 void SetInputPortState(uint32_t port_index, PortState state) override {}
81 void SetOutputPortState(uint32_t port_index, PortState state) override {}
toyoshima62d6742014-10-23 09:05:03 -070082
toyoshimf1b88962015-07-09 14:14:51 -070083 void CompleteStartSession(Result result) override {
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000084 EXPECT_TRUE(wait_for_result_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000085 result_ = result;
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000086 wait_for_result_ = false;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000087 }
88
Avi Drissman3528fd02015-12-18 20:11:31 -050089 void ReceiveMidiData(uint32_t port_index,
90 const uint8_t* data,
dcheng63ccbc32014-10-21 05:23:27 -070091 size_t size,
92 double timestamp) override {}
93 void AccumulateMidiBytesSent(size_t size) override {}
toyoshim7a809662015-10-06 00:54:21 -070094 void Detach() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000095
toyoshimf1b88962015-07-09 14:14:51 -070096 Result result() const { return result_; }
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000097
toyoshimf1b88962015-07-09 14:14:51 -070098 Result WaitForResult() {
toyoshim11f7d572014-10-20 02:37:10 -070099 while (wait_for_result_) {
100 base::RunLoop run_loop;
101 run_loop.RunUntilIdle();
102 }
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +0000103 return result();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000104 }
105
106 private:
toyoshimf1b88962015-07-09 14:14:51 -0700107 Result result_;
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000108 bool wait_for_result_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000109
110 DISALLOW_COPY_AND_ASSIGN(FakeMidiManagerClient);
111};
112
113class MidiManagerTest : public ::testing::Test {
114 public:
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000115 MidiManagerTest()
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000116 : manager_(new FakeMidiManager),
toyoshimf4d61522017-02-10 02:03:32 -0800117 service_(new MidiService(base::WrapUnique(manager_))),
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000118 message_loop_(new base::MessageLoop) {}
toyoshim7a809662015-10-06 00:54:21 -0700119 ~MidiManagerTest() override {
120 manager_->Shutdown();
121 base::RunLoop run_loop;
122 run_loop.RunUntilIdle();
123 EXPECT_EQ(manager_->start_initialization_is_called_,
124 manager_->finalize_is_called_);
125 }
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000126
127 protected:
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000128 void StartTheFirstSession(FakeMidiManagerClient* client) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000129 EXPECT_FALSE(manager_->start_initialization_is_called_);
130 EXPECT_EQ(0U, manager_->GetClientCount());
131 EXPECT_EQ(0U, manager_->GetPendingClientCount());
toyoshima485ff92014-10-23 00:17:59 -0700132 manager_->StartSession(client);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000133 EXPECT_EQ(0U, manager_->GetClientCount());
134 EXPECT_EQ(1U, manager_->GetPendingClientCount());
135 EXPECT_TRUE(manager_->start_initialization_is_called_);
136 EXPECT_EQ(0U, manager_->GetClientCount());
137 EXPECT_EQ(1U, manager_->GetPendingClientCount());
138 EXPECT_TRUE(manager_->start_initialization_is_called_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000139 }
140
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000141 void StartTheNthSession(FakeMidiManagerClient* client, size_t nth) {
142 EXPECT_EQ(nth != 1, manager_->start_initialization_is_called_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000143 EXPECT_EQ(0U, manager_->GetClientCount());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000144 EXPECT_EQ(nth - 1, manager_->GetPendingClientCount());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000145
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000146 // StartInitialization() should not be called for the second and later
147 // sessions.
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000148 manager_->start_initialization_is_called_ = false;
toyoshima485ff92014-10-23 00:17:59 -0700149 manager_->StartSession(client);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000150 EXPECT_EQ(nth == 1, manager_->start_initialization_is_called_);
151 manager_->start_initialization_is_called_ = true;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000152 }
153
154 void EndSession(FakeMidiManagerClient* client, size_t before, size_t after) {
155 EXPECT_EQ(before, manager_->GetClientCount());
156 manager_->EndSession(client);
157 EXPECT_EQ(after, manager_->GetClientCount());
158 }
159
toyoshimf1b88962015-07-09 14:14:51 -0700160 void CompleteInitialization(Result result) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000161 manager_->CallCompleteInitialization(result);
162 }
163
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000164 void RunLoopUntilIdle() {
165 base::RunLoop run_loop;
166 run_loop.RunUntilIdle();
167 }
168
169 protected:
toyoshimf4d61522017-02-10 02:03:32 -0800170 FakeMidiManager* manager_; // Owned by |service_|.
171 std::unique_ptr<MidiService> service_;
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000172
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000173 private:
danakj75afea02016-04-25 20:36:04 -0700174 std::unique_ptr<base::MessageLoop> message_loop_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000175
176 DISALLOW_COPY_AND_ASSIGN(MidiManagerTest);
177};
178
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000179TEST_F(MidiManagerTest, StartAndEndSession) {
danakj75afea02016-04-25 20:36:04 -0700180 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700181 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000182
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000183 StartTheFirstSession(client.get());
toyoshimf1b88962015-07-09 14:14:51 -0700184 CompleteInitialization(Result::OK);
185 EXPECT_EQ(Result::OK, client->WaitForResult());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000186 EndSession(client.get(), 1U, 0U);
187}
188
189TEST_F(MidiManagerTest, StartAndEndSessionWithError) {
danakj75afea02016-04-25 20:36:04 -0700190 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700191 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000192
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000193 StartTheFirstSession(client.get());
toyoshimf1b88962015-07-09 14:14:51 -0700194 CompleteInitialization(Result::INITIALIZATION_ERROR);
195 EXPECT_EQ(Result::INITIALIZATION_ERROR, client->WaitForResult());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000196 EndSession(client.get(), 0U, 0U);
197}
198
199TEST_F(MidiManagerTest, StartMultipleSessions) {
danakj75afea02016-04-25 20:36:04 -0700200 std::unique_ptr<FakeMidiManagerClient> client1;
201 std::unique_ptr<FakeMidiManagerClient> client2;
202 std::unique_ptr<FakeMidiManagerClient> client3;
toyoshima485ff92014-10-23 00:17:59 -0700203 client1.reset(new FakeMidiManagerClient);
204 client2.reset(new FakeMidiManagerClient);
205 client3.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000206
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000207 StartTheFirstSession(client1.get());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000208 StartTheNthSession(client2.get(), 2);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000209 StartTheNthSession(client3.get(), 3);
toyoshimf1b88962015-07-09 14:14:51 -0700210 CompleteInitialization(Result::OK);
211 EXPECT_EQ(Result::OK, client1->WaitForResult());
212 EXPECT_EQ(Result::OK, client2->WaitForResult());
213 EXPECT_EQ(Result::OK, client3->WaitForResult());
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000214 EndSession(client1.get(), 3U, 2U);
215 EndSession(client2.get(), 2U, 1U);
216 EndSession(client3.get(), 1U, 0U);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000217}
218
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000219// TODO(toyoshim): Add a test for a MidiManagerClient that has multiple
220// sessions with multiple client_id.
221
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000222TEST_F(MidiManagerTest, TooManyPendingSessions) {
223 // Push as many client requests for starting session as possible.
xiaofeng.zhang06152d82017-05-21 04:39:34 -0700224 std::vector<std::unique_ptr<FakeMidiManagerClient>> many_existing_clients;
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000225 many_existing_clients.resize(MidiManager::kMaxPendingClientCount);
226 for (size_t i = 0; i < MidiManager::kMaxPendingClientCount; ++i) {
xiaofeng.zhang06152d82017-05-21 04:39:34 -0700227 many_existing_clients[i] = base::MakeUnique<FakeMidiManagerClient>();
228 StartTheNthSession(many_existing_clients[i].get(), i + 1);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000229 }
toyoshim7a809662015-10-06 00:54:21 -0700230 EXPECT_TRUE(manager_->start_initialization_is_called_);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000231
232 // Push the last client that should be rejected for too many pending requests.
danakj75afea02016-04-25 20:36:04 -0700233 std::unique_ptr<FakeMidiManagerClient> additional_client(
toyoshima485ff92014-10-23 00:17:59 -0700234 new FakeMidiManagerClient);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000235 manager_->start_initialization_is_called_ = false;
toyoshima485ff92014-10-23 00:17:59 -0700236 manager_->StartSession(additional_client.get());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000237 EXPECT_FALSE(manager_->start_initialization_is_called_);
toyoshim7a809662015-10-06 00:54:21 -0700238 manager_->start_initialization_is_called_ = true;
toyoshimf1b88962015-07-09 14:14:51 -0700239 EXPECT_EQ(Result::INITIALIZATION_ERROR, additional_client->result());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000240
241 // Other clients still should not receive a result.
242 RunLoopUntilIdle();
243 for (size_t i = 0; i < many_existing_clients.size(); ++i)
toyoshimf1b88962015-07-09 14:14:51 -0700244 EXPECT_EQ(Result::NOT_SUPPORTED, many_existing_clients[i]->result());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000245
toyoshimf1b88962015-07-09 14:14:51 -0700246 // The Result::OK should be distributed to other clients.
247 CompleteInitialization(Result::OK);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000248 for (size_t i = 0; i < many_existing_clients.size(); ++i)
toyoshimf1b88962015-07-09 14:14:51 -0700249 EXPECT_EQ(Result::OK, many_existing_clients[i]->WaitForResult());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000250
251 // Close all successful sessions in FIFO order.
252 size_t sessions = many_existing_clients.size();
253 for (size_t i = 0; i < many_existing_clients.size(); ++i, --sessions)
xiaofeng.zhang06152d82017-05-21 04:39:34 -0700254 EndSession(many_existing_clients[i].get(), sessions, sessions - 1);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000255}
256
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000257TEST_F(MidiManagerTest, AbortSession) {
258 // A client starting a session can be destructed while an asynchronous
259 // initialization is performed.
danakj75afea02016-04-25 20:36:04 -0700260 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700261 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000262
263 StartTheFirstSession(client.get());
264 EndSession(client.get(), 0, 0);
265 client.reset();
266
267 // Following function should not call the destructed |client| function.
toyoshimf1b88962015-07-09 14:14:51 -0700268 CompleteInitialization(Result::OK);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000269 base::RunLoop run_loop;
270 run_loop.RunUntilIdle();
271}
272
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000273TEST_F(MidiManagerTest, CreateMidiManager) {
toyoshim71818732015-04-07 04:15:44 -0700274 // SystemMonitor is needed on Windows.
275 base::SystemMonitor system_monitor;
276
toyoshimf4d61522017-02-10 02:03:32 -0800277 std::unique_ptr<FakeMidiManagerClient> client(
278 base::MakeUnique<FakeMidiManagerClient>());
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000279
toyoshimf4d61522017-02-10 02:03:32 -0800280 std::unique_ptr<MidiService> service(base::MakeUnique<MidiService>());
281 service->StartSession(client.get());
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +0000282
toyoshimf1b88962015-07-09 14:14:51 -0700283 Result result = client->WaitForResult();
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +0000284 // This #ifdef needs to be identical to the one in media/midi/midi_manager.cc.
285 // Do not change the condition for disabling this test.
agoode7de413f2015-04-24 00:13:39 -0700286#if !defined(OS_MACOSX) && !defined(OS_WIN) && \
287 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
toyoshimf1b88962015-07-09 14:14:51 -0700288 EXPECT_EQ(Result::NOT_SUPPORTED, result);
agoode@chromium.org25227512014-06-08 05:12:05 +0000289#elif defined(USE_ALSA)
290 // Temporary until http://crbug.com/371230 is resolved.
toyoshimf1b88962015-07-09 14:14:51 -0700291 EXPECT_TRUE(result == Result::OK || result == Result::INITIALIZATION_ERROR);
dnicoara@chromium.orgc404df62014-05-06 22:56:00 +0000292#else
toyoshimf1b88962015-07-09 14:14:51 -0700293 EXPECT_EQ(Result::OK, result);
dnicoara@chromium.orgc404df62014-05-06 22:56:00 +0000294#endif
toyoshim7a809662015-10-06 00:54:21 -0700295
toyoshimf4d61522017-02-10 02:03:32 -0800296 service->Shutdown();
toyoshim7a809662015-10-06 00:54:21 -0700297 base::RunLoop run_loop;
298 run_loop.RunUntilIdle();
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000299}
300
toyoshim7a809662015-10-06 00:54:21 -0700301// TODO(toyoshim): Add multi-threaded unit tests to check races around
302// StartInitialization(), CompleteInitialization(), and Finalize().
303
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000304} // namespace
305
toyoshime147c5e2015-05-07 21:58:31 -0700306} // namespace midi