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