blob: 6b680c35abc417abe7589a898e4d1cd15ce03f6d [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
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:
toyoshim7a809662015-10-06 00:54:21 -070032 FakeMidiManager()
33 : start_initialization_is_called_(false), finalize_is_called_(false) {}
dcheng63ccbc32014-10-21 05:23:27 -070034 ~FakeMidiManager() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000035
36 // MidiManager implementation.
dcheng63ccbc32014-10-21 05:23:27 -070037 void StartInitialization() override {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000038 start_initialization_is_called_ = true;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000039 }
40
toyoshim7a809662015-10-06 00:54:21 -070041 void Finalize() override { finalize_is_called_ = true; }
42
dcheng63ccbc32014-10-21 05:23:27 -070043 void DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -050044 uint32_t port_index,
45 const std::vector<uint8_t>& data,
dcheng63ccbc32014-10-21 05:23:27 -070046 double timestamp) override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000047
48 // Utility functions for testing.
toyoshimf1b88962015-07-09 14:14:51 -070049 void CallCompleteInitialization(Result result) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000050 CompleteInitialization(result);
51 }
52
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000053 size_t GetClientCount() const {
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +000054 return clients_size_for_testing();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000055 }
56
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000057 size_t GetPendingClientCount() const {
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +000058 return pending_clients_size_for_testing();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000059 }
60
61 bool start_initialization_is_called_;
toyoshim7a809662015-10-06 00:54:21 -070062 bool finalize_is_called_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000063
64 private:
65 DISALLOW_COPY_AND_ASSIGN(FakeMidiManager);
66};
67
68class FakeMidiManagerClient : public MidiManagerClient {
69 public:
toyoshima485ff92014-10-23 00:17:59 -070070 FakeMidiManagerClient()
toyoshimf1b88962015-07-09 14:14:51 -070071 : result_(Result::NOT_SUPPORTED), wait_for_result_(true) {}
dcheng63ccbc32014-10-21 05:23:27 -070072 ~FakeMidiManagerClient() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000073
74 // MidiManagerClient implementation.
toyoshima62d6742014-10-23 09:05:03 -070075 void AddInputPort(const MidiPortInfo& info) override {}
76 void AddOutputPort(const MidiPortInfo& info) override {}
toyoshimec2570a2016-10-21 02:15:27 -070077 void SetInputPortState(uint32_t port_index, PortState state) override {}
78 void SetOutputPortState(uint32_t port_index, PortState state) override {}
toyoshima62d6742014-10-23 09:05:03 -070079
toyoshimf1b88962015-07-09 14:14:51 -070080 void CompleteStartSession(Result result) override {
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000081 EXPECT_TRUE(wait_for_result_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000082 result_ = result;
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000083 wait_for_result_ = false;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000084 }
85
Avi Drissman3528fd02015-12-18 20:11:31 -050086 void ReceiveMidiData(uint32_t port_index,
87 const uint8_t* data,
dcheng63ccbc32014-10-21 05:23:27 -070088 size_t size,
89 double timestamp) override {}
90 void AccumulateMidiBytesSent(size_t size) override {}
toyoshim7a809662015-10-06 00:54:21 -070091 void Detach() override {}
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000092
toyoshimf1b88962015-07-09 14:14:51 -070093 Result result() const { return result_; }
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000094
toyoshimf1b88962015-07-09 14:14:51 -070095 Result WaitForResult() {
toyoshim11f7d572014-10-20 02:37:10 -070096 while (wait_for_result_) {
97 base::RunLoop run_loop;
98 run_loop.RunUntilIdle();
99 }
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +0000100 return result();
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000101 }
102
103 private:
toyoshimf1b88962015-07-09 14:14:51 -0700104 Result result_;
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000105 bool wait_for_result_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000106
107 DISALLOW_COPY_AND_ASSIGN(FakeMidiManagerClient);
108};
109
110class MidiManagerTest : public ::testing::Test {
111 public:
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000112 MidiManagerTest()
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000113 : manager_(new FakeMidiManager),
114 message_loop_(new base::MessageLoop) {}
toyoshim7a809662015-10-06 00:54:21 -0700115 ~MidiManagerTest() override {
116 manager_->Shutdown();
117 base::RunLoop run_loop;
118 run_loop.RunUntilIdle();
119 EXPECT_EQ(manager_->start_initialization_is_called_,
120 manager_->finalize_is_called_);
121 }
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000122
123 protected:
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000124 void StartTheFirstSession(FakeMidiManagerClient* client) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000125 EXPECT_FALSE(manager_->start_initialization_is_called_);
126 EXPECT_EQ(0U, manager_->GetClientCount());
127 EXPECT_EQ(0U, manager_->GetPendingClientCount());
toyoshima485ff92014-10-23 00:17:59 -0700128 manager_->StartSession(client);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000129 EXPECT_EQ(0U, manager_->GetClientCount());
130 EXPECT_EQ(1U, manager_->GetPendingClientCount());
131 EXPECT_TRUE(manager_->start_initialization_is_called_);
132 EXPECT_EQ(0U, manager_->GetClientCount());
133 EXPECT_EQ(1U, manager_->GetPendingClientCount());
134 EXPECT_TRUE(manager_->start_initialization_is_called_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000135 }
136
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000137 void StartTheNthSession(FakeMidiManagerClient* client, size_t nth) {
138 EXPECT_EQ(nth != 1, manager_->start_initialization_is_called_);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000139 EXPECT_EQ(0U, manager_->GetClientCount());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000140 EXPECT_EQ(nth - 1, manager_->GetPendingClientCount());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000141
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000142 // StartInitialization() should not be called for the second and later
143 // sessions.
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000144 manager_->start_initialization_is_called_ = false;
toyoshima485ff92014-10-23 00:17:59 -0700145 manager_->StartSession(client);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000146 EXPECT_EQ(nth == 1, manager_->start_initialization_is_called_);
147 manager_->start_initialization_is_called_ = true;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000148 }
149
150 void EndSession(FakeMidiManagerClient* client, size_t before, size_t after) {
151 EXPECT_EQ(before, manager_->GetClientCount());
152 manager_->EndSession(client);
153 EXPECT_EQ(after, manager_->GetClientCount());
154 }
155
toyoshimf1b88962015-07-09 14:14:51 -0700156 void CompleteInitialization(Result result) {
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000157 manager_->CallCompleteInitialization(result);
158 }
159
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000160 void RunLoopUntilIdle() {
161 base::RunLoop run_loop;
162 run_loop.RunUntilIdle();
163 }
164
165 protected:
danakj75afea02016-04-25 20:36:04 -0700166 std::unique_ptr<FakeMidiManager> manager_;
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000167
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000168 private:
danakj75afea02016-04-25 20:36:04 -0700169 std::unique_ptr<base::MessageLoop> message_loop_;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000170
171 DISALLOW_COPY_AND_ASSIGN(MidiManagerTest);
172};
173
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000174TEST_F(MidiManagerTest, StartAndEndSession) {
danakj75afea02016-04-25 20:36:04 -0700175 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700176 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000177
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000178 StartTheFirstSession(client.get());
toyoshimf1b88962015-07-09 14:14:51 -0700179 CompleteInitialization(Result::OK);
180 EXPECT_EQ(Result::OK, client->WaitForResult());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000181 EndSession(client.get(), 1U, 0U);
182}
183
184TEST_F(MidiManagerTest, StartAndEndSessionWithError) {
danakj75afea02016-04-25 20:36:04 -0700185 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700186 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000187
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000188 StartTheFirstSession(client.get());
toyoshimf1b88962015-07-09 14:14:51 -0700189 CompleteInitialization(Result::INITIALIZATION_ERROR);
190 EXPECT_EQ(Result::INITIALIZATION_ERROR, client->WaitForResult());
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000191 EndSession(client.get(), 0U, 0U);
192}
193
194TEST_F(MidiManagerTest, StartMultipleSessions) {
danakj75afea02016-04-25 20:36:04 -0700195 std::unique_ptr<FakeMidiManagerClient> client1;
196 std::unique_ptr<FakeMidiManagerClient> client2;
197 std::unique_ptr<FakeMidiManagerClient> client3;
toyoshima485ff92014-10-23 00:17:59 -0700198 client1.reset(new FakeMidiManagerClient);
199 client2.reset(new FakeMidiManagerClient);
200 client3.reset(new FakeMidiManagerClient);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000201
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000202 StartTheFirstSession(client1.get());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000203 StartTheNthSession(client2.get(), 2);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000204 StartTheNthSession(client3.get(), 3);
toyoshimf1b88962015-07-09 14:14:51 -0700205 CompleteInitialization(Result::OK);
206 EXPECT_EQ(Result::OK, client1->WaitForResult());
207 EXPECT_EQ(Result::OK, client2->WaitForResult());
208 EXPECT_EQ(Result::OK, client3->WaitForResult());
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000209 EndSession(client1.get(), 3U, 2U);
210 EndSession(client2.get(), 2U, 1U);
211 EndSession(client3.get(), 1U, 0U);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000212}
213
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000214// TODO(toyoshim): Add a test for a MidiManagerClient that has multiple
215// sessions with multiple client_id.
216
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000217TEST_F(MidiManagerTest, TooManyPendingSessions) {
218 // Push as many client requests for starting session as possible.
219 ScopedVector<FakeMidiManagerClient> many_existing_clients;
220 many_existing_clients.resize(MidiManager::kMaxPendingClientCount);
221 for (size_t i = 0; i < MidiManager::kMaxPendingClientCount; ++i) {
toyoshima485ff92014-10-23 00:17:59 -0700222 many_existing_clients[i] = new FakeMidiManagerClient;
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000223 StartTheNthSession(many_existing_clients[i], i + 1);
224 }
toyoshim7a809662015-10-06 00:54:21 -0700225 EXPECT_TRUE(manager_->start_initialization_is_called_);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000226
227 // Push the last client that should be rejected for too many pending requests.
danakj75afea02016-04-25 20:36:04 -0700228 std::unique_ptr<FakeMidiManagerClient> additional_client(
toyoshima485ff92014-10-23 00:17:59 -0700229 new FakeMidiManagerClient);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000230 manager_->start_initialization_is_called_ = false;
toyoshima485ff92014-10-23 00:17:59 -0700231 manager_->StartSession(additional_client.get());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000232 EXPECT_FALSE(manager_->start_initialization_is_called_);
toyoshim7a809662015-10-06 00:54:21 -0700233 manager_->start_initialization_is_called_ = true;
toyoshimf1b88962015-07-09 14:14:51 -0700234 EXPECT_EQ(Result::INITIALIZATION_ERROR, additional_client->result());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000235
236 // Other clients still should not receive a result.
237 RunLoopUntilIdle();
238 for (size_t i = 0; i < many_existing_clients.size(); ++i)
toyoshimf1b88962015-07-09 14:14:51 -0700239 EXPECT_EQ(Result::NOT_SUPPORTED, many_existing_clients[i]->result());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000240
toyoshimf1b88962015-07-09 14:14:51 -0700241 // The Result::OK should be distributed to other clients.
242 CompleteInitialization(Result::OK);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000243 for (size_t i = 0; i < many_existing_clients.size(); ++i)
toyoshimf1b88962015-07-09 14:14:51 -0700244 EXPECT_EQ(Result::OK, many_existing_clients[i]->WaitForResult());
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000245
246 // Close all successful sessions in FIFO order.
247 size_t sessions = many_existing_clients.size();
248 for (size_t i = 0; i < many_existing_clients.size(); ++i, --sessions)
249 EndSession(many_existing_clients[i], sessions, sessions - 1);
250}
251
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000252TEST_F(MidiManagerTest, AbortSession) {
253 // A client starting a session can be destructed while an asynchronous
254 // initialization is performed.
danakj75afea02016-04-25 20:36:04 -0700255 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700256 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000257
258 StartTheFirstSession(client.get());
259 EndSession(client.get(), 0, 0);
260 client.reset();
261
262 // Following function should not call the destructed |client| function.
toyoshimf1b88962015-07-09 14:14:51 -0700263 CompleteInitialization(Result::OK);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000264 base::RunLoop run_loop;
265 run_loop.RunUntilIdle();
266}
267
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000268TEST_F(MidiManagerTest, CreateMidiManager) {
toyoshim71818732015-04-07 04:15:44 -0700269 // SystemMonitor is needed on Windows.
270 base::SystemMonitor system_monitor;
271
danakj75afea02016-04-25 20:36:04 -0700272 std::unique_ptr<FakeMidiManagerClient> client;
toyoshima485ff92014-10-23 00:17:59 -0700273 client.reset(new FakeMidiManagerClient);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000274
danakj75afea02016-04-25 20:36:04 -0700275 std::unique_ptr<MidiManager> manager(MidiManager::Create());
toyoshima485ff92014-10-23 00:17:59 -0700276 manager->StartSession(client.get());
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +0000277
toyoshimf1b88962015-07-09 14:14:51 -0700278 Result result = client->WaitForResult();
toyoshim@chromium.orga25e6832014-05-07 18:06:25 +0000279 // This #ifdef needs to be identical to the one in media/midi/midi_manager.cc.
280 // Do not change the condition for disabling this test.
agoode7de413f2015-04-24 00:13:39 -0700281#if !defined(OS_MACOSX) && !defined(OS_WIN) && \
282 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
toyoshimf1b88962015-07-09 14:14:51 -0700283 EXPECT_EQ(Result::NOT_SUPPORTED, result);
agoode@chromium.org25227512014-06-08 05:12:05 +0000284#elif defined(USE_ALSA)
285 // Temporary until http://crbug.com/371230 is resolved.
toyoshimf1b88962015-07-09 14:14:51 -0700286 EXPECT_TRUE(result == Result::OK || result == Result::INITIALIZATION_ERROR);
dnicoara@chromium.orgc404df62014-05-06 22:56:00 +0000287#else
toyoshimf1b88962015-07-09 14:14:51 -0700288 EXPECT_EQ(Result::OK, result);
dnicoara@chromium.orgc404df62014-05-06 22:56:00 +0000289#endif
toyoshim7a809662015-10-06 00:54:21 -0700290
291 manager->Shutdown();
292 base::RunLoop run_loop;
293 run_loop.RunUntilIdle();
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000294}
295
toyoshim7a809662015-10-06 00:54:21 -0700296// TODO(toyoshim): Add multi-threaded unit tests to check races around
297// StartInitialization(), CompleteInitialization(), and Finalize().
298
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000299} // namespace
300
toyoshime147c5e2015-05-07 21:58:31 -0700301} // namespace midi