1 #include <gtest/gtest.h>
2 #include <gmock/gmock.h>
3
4 #include <QScopedPointer>
5
6 #include "controllers/midi/portmidicontroller.h"
7 #include "controllers/midi/portmididevice.h"
8 #include "test/mixxxtest.h"
9
10 using ::testing::_;
11 using ::testing::DoAll;
12 using ::testing::NotNull;
13 using ::testing::Return;
14 using ::testing::Sequence;
15 using ::testing::SetArrayArgument;
16
17 class MockPortMidiController : public PortMidiController {
18 public:
MockPortMidiController(const PmDeviceInfo * inputDeviceInfo,const PmDeviceInfo * outputDeviceInfo,int inputDeviceIndex,int outputDeviceIndex,UserSettingsPointer pConfig)19 MockPortMidiController(const PmDeviceInfo* inputDeviceInfo,
20 const PmDeviceInfo* outputDeviceInfo,
21 int inputDeviceIndex,
22 int outputDeviceIndex,
23 UserSettingsPointer pConfig)
24 : PortMidiController(inputDeviceInfo,
25 outputDeviceInfo,
26 inputDeviceIndex,
27 outputDeviceIndex,
28 pConfig) {
29 }
~MockPortMidiController()30 ~MockPortMidiController() override {
31 }
32
sendShortMsg(unsigned char status,unsigned char byte1,unsigned char byte2)33 void sendShortMsg(unsigned char status, unsigned char byte1, unsigned char byte2) override {
34 PortMidiController::sendShortMsg(status, byte1, byte2);
35 }
36
sendSysexMsg(const QList<int> & data,unsigned int length)37 void sendSysexMsg(const QList<int>& data, unsigned int length) {
38 PortMidiController::sendSysexMsg(data, length);
39 }
40
41 MOCK_METHOD4(receive, void(unsigned char, unsigned char, unsigned char,
42 mixxx::Duration));
43 MOCK_METHOD2(receive, void(const QByteArray&, mixxx::Duration));
44 };
45
46 class MockPortMidiDevice : public PortMidiDevice {
47 public:
MockPortMidiDevice(PmDeviceInfo * info,int index)48 MockPortMidiDevice(PmDeviceInfo* info, int index)
49 : PortMidiDevice(info, index) {
50 }
51
52 MOCK_CONST_METHOD0(isOpen, bool());
53 MOCK_METHOD1(openInput, PmError(int32_t));
54 MOCK_METHOD0(openOutput, PmError());
55 MOCK_METHOD0(close, PmError());
56 MOCK_METHOD0(poll, PmError());
57 MOCK_METHOD2(read, int(PmEvent*, int32_t));
58 MOCK_METHOD1(writeShort, PmError(int32_t));
59 MOCK_METHOD1(writeSysEx, PmError(unsigned char*));
60 };
61
62 class PortMidiControllerTest : public MixxxTest {
63 protected:
PortMidiControllerTest()64 PortMidiControllerTest()
65 : m_mockInput(new MockPortMidiDevice(&m_inputDeviceInfo, 0)),
66 m_mockOutput(new MockPortMidiDevice(&m_outputDeviceInfo, 0)) {
67 m_inputDeviceInfo.name = "Test Input Device";
68 m_inputDeviceInfo.interf = "Test";
69 m_inputDeviceInfo.input = 1;
70 m_inputDeviceInfo.output = 0;
71 m_inputDeviceInfo.opened = 0;
72
73 m_outputDeviceInfo.name = "Test Output Device";
74 m_outputDeviceInfo.interf = "Test";
75 m_outputDeviceInfo.input = 0;
76 m_outputDeviceInfo.output = 1;
77 m_outputDeviceInfo.opened = 0;
78
79 m_pController.reset(new MockPortMidiController(
80 &m_inputDeviceInfo, &m_outputDeviceInfo, 0, 0, config()));
81 m_pController->setPortMidiInputDevice(m_mockInput);
82 m_pController->setPortMidiOutputDevice(m_mockOutput);
83 }
84
openDevice()85 void openDevice() {
86 m_pController->open();
87 }
88
closeDevice()89 void closeDevice() {
90 m_pController->close();
91 }
92
pollDevice()93 void pollDevice() {
94 m_pController->poll();
95 }
96
97 PmDeviceInfo m_inputDeviceInfo;
98 PmDeviceInfo m_outputDeviceInfo;
99 MockPortMidiDevice* m_mockInput;
100 MockPortMidiDevice* m_mockOutput;
101 QScopedPointer<MockPortMidiController> m_pController;
102 };
103
MakeEvent(PmMessage message,PmTimestamp timestamp)104 PmEvent MakeEvent(PmMessage message, PmTimestamp timestamp) {
105 PmEvent event;
106 event.message = message;
107 event.timestamp = timestamp;
108 return event;
109 }
110
111 MATCHER_P(ByteArrayEquals, value,
112 "Checks that the non-NULL terminated argument array exactly equals "
113 "the provided byte container.") {
114 for (int i = 0; i < value.size(); ++i) {
115 if (arg[i] != value.at(i))
116 return false;
117 }
118 return true;
119 }
120
TEST_F(PortMidiControllerTest,OpenClose)121 TEST_F(PortMidiControllerTest, OpenClose) {
122 Sequence input;
123 ON_CALL(*m_mockInput, isOpen())
124 .WillByDefault(Return(false));
125 EXPECT_CALL(*m_mockInput, openInput(MIXXX_PORTMIDI_BUFFER_LEN))
126 .InSequence(input)
127 .WillOnce(Return(pmNoError));
128 EXPECT_CALL(*m_mockInput, isOpen())
129 .InSequence(input)
130 .WillRepeatedly(Return(true));
131 EXPECT_CALL(*m_mockInput, close())
132 .InSequence(input)
133 .WillOnce(Return(pmNoError));
134
135 Sequence output;
136 ON_CALL(*m_mockOutput, isOpen())
137 .WillByDefault(Return(false));
138 EXPECT_CALL(*m_mockOutput, openOutput())
139 .WillOnce(Return(pmNoError));
140 EXPECT_CALL(*m_mockOutput, isOpen())
141 .InSequence(output)
142 .WillRepeatedly(Return(true));
143 EXPECT_CALL(*m_mockOutput, close())
144 .InSequence(output)
145 .WillOnce(Return(pmNoError));
146
147 openDevice();
148 EXPECT_TRUE(m_pController->isOpen());
149 closeDevice();
150 EXPECT_FALSE(m_pController->isOpen());
151 };
152
TEST_F(PortMidiControllerTest,WriteShort)153 TEST_F(PortMidiControllerTest, WriteShort) {
154 // Note that Pm_WriteShort takes an int32_t formatted as 0x00B2B1SS where SS
155 // is the status byte, B1 is the first message byte and B2 is the second
156 // message byte.
157 Sequence output;
158 EXPECT_CALL(*m_mockOutput, isOpen())
159 .WillRepeatedly(Return(true));
160 EXPECT_CALL(*m_mockOutput, writeShort(0x403C90))
161 .InSequence(output)
162 .WillOnce(Return(pmNoError));
163 EXPECT_CALL(*m_mockOutput, writeShort(0xFFFFFF))
164 .InSequence(output)
165 .WillOnce(Return(pmBadData));
166 EXPECT_CALL(*m_mockOutput, writeShort(0x403C80))
167 .InSequence(output)
168 .WillOnce(Return(pmNoError));
169
170 m_pController->sendShortMsg(0x90, 0x3C, 0x40);
171 m_pController->sendShortMsg(0xFF, 0xFF, 0xFF);
172 m_pController->sendShortMsg(0x80, 0x3C, 0x40);
173 };
174
TEST_F(PortMidiControllerTest,WriteSysex)175 TEST_F(PortMidiControllerTest, WriteSysex) {
176 QList<int> sysex;
177 sysex.append(0xF0);
178 sysex.append(0x12);
179 sysex.append(0xF7);
180
181 EXPECT_CALL(*m_mockOutput, isOpen())
182 .WillRepeatedly(Return(true));
183 EXPECT_CALL(*m_mockOutput, writeSysEx(ByteArrayEquals(sysex)))
184 .WillOnce(Return(pmNoError));
185 m_pController->sendSysexMsg(sysex, sysex.length());
186 };
187
TEST_F(PortMidiControllerTest,WriteSysex_Malformed)188 TEST_F(PortMidiControllerTest, WriteSysex_Malformed) {
189 QList<int> sysex;
190 sysex.append(0xF0);
191 sysex.append(0x12);
192
193 EXPECT_CALL(*m_mockOutput, isOpen())
194 .WillRepeatedly(Return(true));
195 EXPECT_CALL(*m_mockOutput, writeSysEx(_))
196 .Times(0);
197 m_pController->sendSysexMsg(sysex, sysex.length());
198 };
199
200
TEST_F(PortMidiControllerTest,Poll_Read_Basic)201 TEST_F(PortMidiControllerTest, Poll_Read_Basic) {
202 std::vector<PmEvent> messages;
203 messages.push_back(MakeEvent(0x403C90, 0x0));
204 messages.push_back(MakeEvent(0x403C80, 0x1));
205
206 Sequence read;
207 EXPECT_CALL(*m_mockInput, isOpen())
208 .WillRepeatedly(Return(true));
209 EXPECT_CALL(*m_mockInput, read(NotNull(), _))
210 .InSequence(read)
211 .WillOnce(DoAll(SetArrayArgument<0>(messages.begin(), messages.end()),
212 Return(messages.size())));
213
214 EXPECT_CALL(*m_pController, receive(0x90, 0x3C, 0x40, _))
215 .InSequence(read);
216 EXPECT_CALL(*m_pController, receive(0x80, 0x3C, 0x40, _))
217 .InSequence(read);
218
219 pollDevice();
220 };
221
TEST_F(PortMidiControllerTest,Poll_Read_SysExWithRealtime)222 TEST_F(PortMidiControllerTest, Poll_Read_SysExWithRealtime) {
223 std::vector<PmEvent> messages;
224 messages.push_back(MakeEvent(0x332211F0, 0x0));
225 messages.push_back(MakeEvent(0x000000F8, 0x1));
226 messages.push_back(MakeEvent(0x77665544, 0x0));
227 messages.push_back(MakeEvent(0x000000FA, 0x2));
228 messages.push_back(MakeEvent(0x000000F7, 0x0));
229
230 QByteArray sysex;
231 sysex.append('\xF0');
232 sysex.append('\x11');
233 sysex.append('\x22');
234 sysex.append('\x33');
235 sysex.append('\x44');
236 sysex.append('\x55');
237 sysex.append('\x66');
238 sysex.append('\x77');
239 sysex.append('\xF7');
240
241 Sequence read;
242 EXPECT_CALL(*m_mockInput, isOpen())
243 .WillRepeatedly(Return(true));
244 EXPECT_CALL(*m_mockInput, read(NotNull(), _))
245 .InSequence(read)
246 .WillOnce(DoAll(SetArrayArgument<0>(messages.begin(), messages.end()),
247 Return(messages.size())));
248 EXPECT_CALL(*m_pController, receive(0xF8, 0x00, 0x00, _))
249 .InSequence(read);
250 EXPECT_CALL(*m_pController, receive(0xFA, 0x00, 0x00, _))
251 .InSequence(read);
252 EXPECT_CALL(*m_pController, receive(sysex, _))
253 .InSequence(read);
254
255 pollDevice();
256 };
257
TEST_F(PortMidiControllerTest,Poll_Read_SysEx)258 TEST_F(PortMidiControllerTest, Poll_Read_SysEx) {
259 std::vector<PmEvent> messages;
260 messages.push_back(MakeEvent(0x332211F0, 0x0));
261 messages.push_back(MakeEvent(0xF7665544, 0x1));
262
263 QByteArray sysex;
264 sysex.append('\xF0');
265 sysex.append('\x11');
266 sysex.append('\x22');
267 sysex.append('\x33');
268 sysex.append('\x44');
269 sysex.append('\x55');
270 sysex.append('\x66');
271 sysex.append('\xF7');
272
273 Sequence read;
274 EXPECT_CALL(*m_mockInput, isOpen())
275 .WillRepeatedly(Return(true));
276 EXPECT_CALL(*m_mockInput, read(NotNull(), _))
277 .InSequence(read)
278 .WillOnce(DoAll(SetArrayArgument<0>(messages.begin(), messages.end()),
279 Return(messages.size())));
280 EXPECT_CALL(*m_pController, receive(sysex, _))
281 .InSequence(read);
282
283 pollDevice();
284 };
285
TEST_F(PortMidiControllerTest,Poll_Read_SysExWithRealtime_CoincidentalRealtimeByte)286 TEST_F(PortMidiControllerTest,
287 Poll_Read_SysExWithRealtime_CoincidentalRealtimeByte) {
288 // We used to incorrectly treat an 0xF8 occurring in a SysEx message as a
289 // realtime message. This test verifies that we do not do this anymore.
290 std::vector<PmEvent> messages;
291 messages.push_back(MakeEvent(0x332211F0, 0x0));
292 messages.push_back(MakeEvent(0x6655F844, 0x0));
293 messages.push_back(MakeEvent(0x0000F777, 0x0));
294
295 QByteArray sysex;
296 sysex.append('\xF0');
297 sysex.append('\x11');
298 sysex.append('\x22');
299 sysex.append('\x33');
300 sysex.append('\x44');
301 sysex.append('\xF8');
302 sysex.append('\x55');
303 sysex.append('\x66');
304 sysex.append('\x77');
305 sysex.append('\xF7');
306
307 Sequence read;
308 EXPECT_CALL(*m_mockInput, isOpen())
309 .WillRepeatedly(Return(true));
310 EXPECT_CALL(*m_mockInput, read(NotNull(), _))
311 .InSequence(read)
312 .WillOnce(DoAll(SetArrayArgument<0>(messages.begin(), messages.end()),
313 Return(messages.size())));
314 EXPECT_CALL(*m_pController, receive(sysex, _))
315 .InSequence(read);
316
317 pollDevice();
318 };
319
TEST_F(PortMidiControllerTest,Poll_Read_SysExInterrupted_FollowedByNormalMessage)320 TEST_F(PortMidiControllerTest, Poll_Read_SysExInterrupted_FollowedByNormalMessage) {
321 // According to the PortMIDI documentation when a SysEx message is
322 // interrupted, we will expect to see a non-realtime status byte as a new
323 // message before seeing an EOX terminating SysEx. In this event we drop the
324 // SysEx message and process the new message as normal.
325
326 std::vector<PmEvent> messages;
327 messages.push_back(MakeEvent(0x332211F0, 0x0));
328 messages.push_back(MakeEvent(0x00403C90, 0x0));
329
330 Sequence read;
331 EXPECT_CALL(*m_mockInput, isOpen())
332 .WillRepeatedly(Return(true));
333 EXPECT_CALL(*m_mockInput, read(NotNull(), _))
334 .InSequence(read)
335 .WillOnce(DoAll(SetArrayArgument<0>(messages.begin(), messages.end()),
336 Return(messages.size())));
337 EXPECT_CALL(*m_pController, receive(0x90, 0x3C, 0x40, _))
338 .InSequence(read);
339
340 pollDevice();
341 };
342
TEST_F(PortMidiControllerTest,Poll_Read_SysExInterrupted_FollowedBySysExMessage)343 TEST_F(PortMidiControllerTest, Poll_Read_SysExInterrupted_FollowedBySysExMessage) {
344 // According to the PortMIDI documentation when a SysEx message is
345 // interrupted, we will expect to see a non-realtime status byte as a new
346 // message before seeing an EOX terminating SysEx. In this event we drop the
347 // SysEx message and process the new message as normal.
348
349 std::vector<PmEvent> messages;
350 messages.push_back(MakeEvent(0x332211F0, 0x0));
351 messages.push_back(MakeEvent(0x77665544, 0x0));
352 messages.push_back(MakeEvent(0x332211F0, 0x1));
353 messages.push_back(MakeEvent(0xF7665544, 0x0));
354
355 QByteArray sysex;
356 sysex.append('\xF0');
357 sysex.append('\x11');
358 sysex.append('\x22');
359 sysex.append('\x33');
360 sysex.append('\x44');
361 sysex.append('\x55');
362 sysex.append('\x66');
363 sysex.append('\xF7');
364
365 Sequence read;
366 EXPECT_CALL(*m_mockInput, isOpen())
367 .WillRepeatedly(Return(true));
368 EXPECT_CALL(*m_mockInput, read(NotNull(), _))
369 .InSequence(read)
370 .WillOnce(DoAll(SetArrayArgument<0>(messages.begin(), messages.end()),
371 Return(messages.size())));
372 EXPECT_CALL(*m_pController, receive(sysex, _))
373 .InSequence(read);
374
375 pollDevice();
376 };
377
378
TEST_F(PortMidiControllerTest,Poll_Read_SysEx_BufferOverflow)379 TEST_F(PortMidiControllerTest, Poll_Read_SysEx_BufferOverflow) {
380 // According to the PortMIDI documentation when a SysEx message is
381 // interrupted, we will expect to see a non-realtime status byte as a new
382 // message before seeing an EOX terminating SysEx. In this event we drop the
383 // SysEx message and process the new message as normal.
384
385 std::vector<PmEvent> messages1;
386 messages1.push_back(MakeEvent(0x332211F0, 0x0));
387 messages1.push_back(MakeEvent(0x77665544, 0x0));
388
389 std::vector<PmEvent> messages2;
390 messages2.push_back(MakeEvent(0x332211F0, 0x1));
391
392 std::vector<PmEvent> messages3;
393 messages3.push_back(MakeEvent(0xF7665544, 0x2));
394
395 QByteArray sysex;
396 sysex.append('\xF0');
397 sysex.append('\x11');
398 sysex.append('\x22');
399 sysex.append('\x33');
400 sysex.append('\x44');
401 sysex.append('\x55');
402 sysex.append('\x66');
403 sysex.append('\xF7');
404
405 Sequence read;
406 EXPECT_CALL(*m_mockInput, isOpen())
407 .WillRepeatedly(Return(true));
408
409 // Poll 1 -- returns messages1.
410 EXPECT_CALL(*m_mockInput, read(NotNull(), _))
411 .InSequence(read)
412 .WillOnce(DoAll(SetArrayArgument<0>(messages1.begin(), messages1.end()),
413 Return(messages1.size())));
414
415 // Poll 2 -- buffer overflow.
416 EXPECT_CALL(*m_mockInput, read(NotNull(), _))
417 .InSequence(read)
418 .WillOnce(Return(pmBufferOverflow));
419
420 // Poll 3 -- returns messages2.
421 EXPECT_CALL(*m_mockInput, read(NotNull(), _))
422 .InSequence(read)
423 .WillOnce(DoAll(SetArrayArgument<0>(messages2.begin(), messages2.end()),
424 Return(messages2.size())));
425
426 // Poll 4 -- returns messages3.
427 EXPECT_CALL(*m_mockInput, read(NotNull(), _))
428 .InSequence(read)
429 .WillOnce(DoAll(SetArrayArgument<0>(messages3.begin(), messages3.end()),
430 Return(messages3.size())));
431 EXPECT_CALL(*m_pController, receive(sysex, _))
432 .InSequence(read);
433
434 pollDevice();
435 pollDevice();
436 pollDevice();
437 pollDevice();
438 };
439