#include "unit-tests.h" #include "unit-tests_Settings.h" #include #include BEGIN_MIDI_NAMESPACE END_MIDI_NAMESPACE // ----------------------------------------------------------------------------- BEGIN_UNNAMED_NAMESPACE using namespace testing; USING_NAMESPACE_UNIT_TESTS template struct VariableSysExSettings : midi::DefaultSettings { static const unsigned SysExMaxSize = Size; }; typedef test_mocks::SerialMock<256> SerialMock; typedef midi::SerialMIDI Transport; typedef VariableSysExSettings<256> Settings; typedef midi::MidiInterface MidiInterface; MidiInterface* midi; class MidiInputCallbacks : public Test { public: MidiInputCallbacks() : mTransport(mSerial) , mMidi(mTransport) { } virtual ~MidiInputCallbacks() { } protected: virtual void SetUp() { midi = &mMidi; } virtual void TearDown() { midi = nullptr; } protected: SerialMock mSerial; Transport mTransport; MidiInterface mMidi; }; // -- void handleNoteOn(byte inChannel, byte inPitch, byte inVelocity) { EXPECT_NE(midi, nullptr); midi->sendNoteOn(inPitch, inVelocity, inChannel); } TEST_F(MidiInputCallbacks, noteOn) { mMidi.setHandleNoteOn(handleNoteOn); mMidi.begin(MIDI_CHANNEL_OMNI); mMidi.turnThruOff(); static const unsigned rxSize = 3; static const byte rxData[rxSize] = { 0x9b, 12, 34 }; mSerial.mRxBuffer.write(rxData, rxSize); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), true); EXPECT_EQ(mMidi.getType(), midi::NoteOn); EXPECT_EQ(mMidi.getChannel(), 12); EXPECT_EQ(mMidi.getData1(), 12); EXPECT_EQ(mMidi.getData2(), 34); EXPECT_EQ(mSerial.mTxBuffer.getLength(), 3); byte buffer[3] = { 0 }; mSerial.mTxBuffer.read(buffer, 3); EXPECT_THAT(buffer, ContainerEq(rxData)); // Test null velocity note on EXPECT_EQ(MidiInterface::Settings::HandleNullVelocityNoteOnAsNoteOff, true); mSerial.mRxBuffer.write(0x9b); mSerial.mRxBuffer.write(12); mSerial.mRxBuffer.write(0); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), true); EXPECT_EQ(mMidi.getType(), midi::NoteOff); EXPECT_EQ(mMidi.getChannel(), 12); EXPECT_EQ(mMidi.getData1(), 12); EXPECT_EQ(mMidi.getData2(), 0); EXPECT_EQ(mSerial.mTxBuffer.getLength(), 0); } // -- void handleNoteOff(byte inChannel, byte inPitch, byte inVelocity) { EXPECT_NE(midi, nullptr); midi->sendNoteOff(inPitch, inVelocity, inChannel); } TEST_F(MidiInputCallbacks, noteOff) { mMidi.setHandleNoteOff(handleNoteOff); mMidi.begin(MIDI_CHANNEL_OMNI); mMidi.turnThruOff(); static const unsigned rxSize = 3; static const byte rxData[rxSize] = { 0x8b, 12, 34 }; mSerial.mRxBuffer.write(rxData, rxSize); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), true); EXPECT_EQ(mMidi.getType(), midi::NoteOff); EXPECT_EQ(mMidi.getChannel(), 12); EXPECT_EQ(mMidi.getData1(), 12); EXPECT_EQ(mMidi.getData2(), 34); EXPECT_EQ(mSerial.mTxBuffer.getLength(), 3); byte buffer[3] = { 0 }; mSerial.mTxBuffer.read(buffer, 3); EXPECT_THAT(buffer, ContainerEq(rxData)); // Test null velocity note on EXPECT_EQ(MidiInterface::Settings::HandleNullVelocityNoteOnAsNoteOff, true); mSerial.mRxBuffer.write(0x9b); mSerial.mRxBuffer.write(12); mSerial.mRxBuffer.write(0); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), true); EXPECT_EQ(mMidi.getType(), midi::NoteOff); EXPECT_EQ(mMidi.getChannel(), 12); EXPECT_EQ(mMidi.getData1(), 12); EXPECT_EQ(mMidi.getData2(), 0); EXPECT_EQ(mSerial.mTxBuffer.getLength(), 3); mSerial.mTxBuffer.read(buffer, 3); EXPECT_THAT(buffer, ElementsAreArray({ 0x8b, 12, 0 })); } // -- void handleAfterTouchPoly(byte inChannel, byte inNote, byte inValue) { EXPECT_NE(midi, nullptr); midi->sendAfterTouch(inNote, inValue, inChannel); } TEST_F(MidiInputCallbacks, afterTouchPoly) { mMidi.setHandleAfterTouchPoly(handleAfterTouchPoly); mMidi.begin(MIDI_CHANNEL_OMNI); mMidi.turnThruOff(); static const unsigned rxSize = 3; static const byte rxData[rxSize] = { 0xab, 12, 34 }; mSerial.mRxBuffer.write(rxData, rxSize); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), true); EXPECT_EQ(mMidi.getType(), midi::AfterTouchPoly); EXPECT_EQ(mMidi.getChannel(), 12); EXPECT_EQ(mMidi.getData1(), 12); EXPECT_EQ(mMidi.getData2(), 34); EXPECT_EQ(mSerial.mTxBuffer.getLength(), 3); byte buffer[3] = { 0 }; mSerial.mTxBuffer.read(buffer, 3); EXPECT_THAT(buffer, ContainerEq(rxData)); } // -- void handleControlChange(byte inChannel, byte inNumber, byte inValue) { EXPECT_NE(midi, nullptr); midi->sendControlChange(inNumber, inValue, inChannel); } TEST_F(MidiInputCallbacks, controlChange) { mMidi.setHandleControlChange(handleControlChange); mMidi.begin(MIDI_CHANNEL_OMNI); mMidi.turnThruOff(); static const unsigned rxSize = 3; static const byte rxData[rxSize] = { 0xbb, 12, 34 }; mSerial.mRxBuffer.write(rxData, rxSize); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), true); EXPECT_EQ(mMidi.getType(), midi::ControlChange); EXPECT_EQ(mMidi.getChannel(), 12); EXPECT_EQ(mMidi.getData1(), 12); EXPECT_EQ(mMidi.getData2(), 34); EXPECT_EQ(mSerial.mTxBuffer.getLength(), 3); byte buffer[3] = { 0 }; mSerial.mTxBuffer.read(buffer, 3); EXPECT_THAT(buffer, ContainerEq(rxData)); } // -- void handleProgramChange(byte inChannel, byte inNumber) { EXPECT_NE(midi, nullptr); midi->sendProgramChange(inNumber, inChannel); } TEST_F(MidiInputCallbacks, programChange) { mMidi.setHandleProgramChange(handleProgramChange); mMidi.begin(MIDI_CHANNEL_OMNI); mMidi.turnThruOff(); static const unsigned rxSize = 2; static const byte rxData[rxSize] = { 0xcb, 12 }; mSerial.mRxBuffer.write(rxData, rxSize); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), true); EXPECT_EQ(mMidi.getType(), midi::ProgramChange); EXPECT_EQ(mMidi.getChannel(), 12); EXPECT_EQ(mMidi.getData1(), 12); EXPECT_EQ(mMidi.getData2(), 0); EXPECT_EQ(mSerial.mTxBuffer.getLength(), 2); byte buffer[2] = { 0 }; mSerial.mTxBuffer.read(buffer, 2); EXPECT_THAT(buffer, ContainerEq(rxData)); } // -- void handleAfterTouchChannel(byte inChannel, byte inPressure) { EXPECT_NE(midi, nullptr); midi->sendAfterTouch(inPressure, inChannel); } TEST_F(MidiInputCallbacks, afterTouchChannel) { mMidi.setHandleAfterTouchChannel(handleAfterTouchChannel); mMidi.begin(MIDI_CHANNEL_OMNI); mMidi.turnThruOff(); static const unsigned rxSize = 2; static const byte rxData[rxSize] = { 0xdb, 12 }; mSerial.mRxBuffer.write(rxData, rxSize); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), true); EXPECT_EQ(mMidi.getType(), midi::AfterTouchChannel); EXPECT_EQ(mMidi.getChannel(), 12); EXPECT_EQ(mMidi.getData1(), 12); EXPECT_EQ(mMidi.getData2(), 0); EXPECT_EQ(mSerial.mTxBuffer.getLength(), 2); byte buffer[2] = { 0 }; mSerial.mTxBuffer.read(buffer, 2); EXPECT_THAT(buffer, ContainerEq(rxData)); } // -- void handlePitchBend(byte inChannel, int inValue) { EXPECT_NE(midi, nullptr); midi->sendPitchBend(inValue, inChannel); } TEST_F(MidiInputCallbacks, pitchBend) { mMidi.setHandlePitchBend(handlePitchBend); mMidi.begin(MIDI_CHANNEL_OMNI); mMidi.turnThruOff(); static const unsigned rxSize = 3; static const byte rxData[rxSize] = { 0xeb, 12, 34 }; mSerial.mRxBuffer.write(rxData, rxSize); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), true); EXPECT_EQ(mMidi.getType(), midi::PitchBend); EXPECT_EQ(mMidi.getChannel(), 12); EXPECT_EQ(mMidi.getData1(), 12); EXPECT_EQ(mMidi.getData2(), 34); EXPECT_EQ(mSerial.mTxBuffer.getLength(), 3); byte buffer[3] = { 0 }; mSerial.mTxBuffer.read(buffer, 3); EXPECT_THAT(buffer, ContainerEq(rxData)); } // -- void handleSysEx(byte* inData, unsigned inSize) { EXPECT_NE(midi, nullptr); midi->sendSysEx(inSize, inData, true); } TEST_F(MidiInputCallbacks, sysEx) { mMidi.setHandleSystemExclusive(handleSysEx); mMidi.begin(MIDI_CHANNEL_OMNI); mMidi.turnThruOff(); static const unsigned rxSize = 15; static const byte rxData[rxSize] = { 0xf0, 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 0xf7 }; mSerial.mRxBuffer.write(rxData, rxSize); for (unsigned i = 0; i < rxSize - 1; ++i) { EXPECT_EQ(mMidi.read(), false); } EXPECT_EQ(mMidi.read(), true); EXPECT_EQ(mMidi.getType(), midi::SystemExclusive); EXPECT_EQ(mMidi.getChannel(), 0); EXPECT_EQ(mMidi.getSysExArrayLength(), rxSize); EXPECT_EQ(unsigned(mSerial.mTxBuffer.getLength()), rxSize); const std::vector sysExData(mMidi.getSysExArray(), mMidi.getSysExArray() + rxSize); EXPECT_THAT(sysExData, ElementsAreArray(rxData)); } TEST_F(MidiInputCallbacks, sysExLong) { mMidi.setHandleSystemExclusive(handleSysEx); mMidi.begin(MIDI_CHANNEL_OMNI); mMidi.turnThruOff(); static const unsigned rxSize = 210; static const byte rxData[rxSize] = { 0xf0, 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 0xf7 }; mSerial.mRxBuffer.write(rxData, rxSize); for (unsigned i = 0; i < rxSize - 1; ++i) { EXPECT_EQ(mMidi.read(), false); } EXPECT_EQ(mMidi.read(), true); EXPECT_EQ(mMidi.getType(), midi::SystemExclusive); EXPECT_EQ(mMidi.getChannel(), 0); EXPECT_EQ(mMidi.getSysExArrayLength(), rxSize); EXPECT_EQ(unsigned(mSerial.mTxBuffer.getLength()), rxSize); const std::vector sysExData(mMidi.getSysExArray(), mMidi.getSysExArray() + rxSize); EXPECT_THAT(sysExData, ElementsAreArray(rxData)); } // -- void handleMtcQuarterFrame(byte inData) { EXPECT_NE(midi, nullptr); midi->sendTimeCodeQuarterFrame(inData); } TEST_F(MidiInputCallbacks, mtcQuarterFrame) { mMidi.setHandleTimeCodeQuarterFrame(handleMtcQuarterFrame); mMidi.begin(MIDI_CHANNEL_OMNI); mMidi.turnThruOff(); static const unsigned rxSize = 2; static const byte rxData[rxSize] = { 0xf1, 12 }; mSerial.mRxBuffer.write(rxData, rxSize); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), true); EXPECT_EQ(mMidi.getType(), midi::TimeCodeQuarterFrame); EXPECT_EQ(mMidi.getChannel(), 0); EXPECT_EQ(mMidi.getData1(), 12); EXPECT_EQ(mMidi.getData2(), 0); EXPECT_EQ(mSerial.mTxBuffer.getLength(), 2); byte buffer[2] = { 0 }; mSerial.mTxBuffer.read(buffer, 2); EXPECT_THAT(buffer, ContainerEq(rxData)); } // -- void handleSongPosition(unsigned inBeats) { EXPECT_NE(midi, nullptr); midi->sendSongPosition(inBeats); } TEST_F(MidiInputCallbacks, songPosition) { mMidi.setHandleSongPosition(handleSongPosition); mMidi.begin(MIDI_CHANNEL_OMNI); mMidi.turnThruOff(); static const unsigned rxSize = 3; static const byte rxData[rxSize] = { 0xf2, 12, 34 }; mSerial.mRxBuffer.write(rxData, rxSize); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), true); EXPECT_EQ(mMidi.getType(), midi::SongPosition); EXPECT_EQ(mMidi.getChannel(), 0); EXPECT_EQ(mMidi.getData1(), 12); EXPECT_EQ(mMidi.getData2(), 34); EXPECT_EQ(mSerial.mTxBuffer.getLength(), 3); byte buffer[3] = { 0 }; mSerial.mTxBuffer.read(buffer, 3); EXPECT_THAT(buffer, ContainerEq(rxData)); } // -- void handleSongSelect(byte inSongNumber) { EXPECT_NE(midi, nullptr); midi->sendSongSelect(inSongNumber); } TEST_F(MidiInputCallbacks, songSelect) { mMidi.setHandleSongSelect(handleSongSelect); mMidi.begin(MIDI_CHANNEL_OMNI); mMidi.turnThruOff(); static const unsigned rxSize = 2; static const byte rxData[rxSize] = { 0xf3, 12 }; mSerial.mRxBuffer.write(rxData, rxSize); EXPECT_EQ(mMidi.read(), false); EXPECT_EQ(mMidi.read(), true); EXPECT_EQ(mMidi.getType(), midi::SongSelect); EXPECT_EQ(mMidi.getChannel(), 0); EXPECT_EQ(mMidi.getData1(), 12); EXPECT_EQ(mMidi.getData2(), 0); EXPECT_EQ(mSerial.mTxBuffer.getLength(), 2); byte buffer[2] = { 0 }; mSerial.mTxBuffer.read(buffer, 2); EXPECT_THAT(buffer, ContainerEq(rxData)); } // -- void handleTuneRequest() { EXPECT_NE(midi, nullptr); midi->sendTuneRequest(); } TEST_F(MidiInputCallbacks, tuneRequest) { mMidi.setHandleTuneRequest(handleTuneRequest); mMidi.begin(MIDI_CHANNEL_OMNI); mMidi.turnThruOff(); mSerial.mRxBuffer.write(0xf6); EXPECT_EQ(mMidi.read(), true); EXPECT_EQ(mMidi.getType(), midi::TuneRequest); EXPECT_EQ(mMidi.getChannel(), 0); EXPECT_EQ(mMidi.getData1(), 0); EXPECT_EQ(mMidi.getData2(), 0); EXPECT_EQ(mSerial.mTxBuffer.getLength(), 1); EXPECT_EQ(mSerial.mTxBuffer.read(), 0xf6); } // -- void handleClock() { EXPECT_NE(midi, nullptr); midi->sendRealTime(midi::Clock); } void handleStart() { EXPECT_NE(midi, nullptr); midi->sendRealTime(midi::Start); } void handleContinue() { EXPECT_NE(midi, nullptr); midi->sendRealTime(midi::Continue); } void handleStop() { EXPECT_NE(midi, nullptr); midi->sendRealTime(midi::Stop); } void handleActiveSensing() { EXPECT_NE(midi, nullptr); midi->sendRealTime(midi::ActiveSensing); } void handleSystemReset() { EXPECT_NE(midi, nullptr); midi->sendRealTime(midi::SystemReset); } TEST_F(MidiInputCallbacks, realTime) { mMidi.setHandleClock(handleClock); mMidi.setHandleStart(handleStart); mMidi.setHandleContinue(handleContinue); mMidi.setHandleStop(handleStop); mMidi.setHandleActiveSensing(handleActiveSensing); mMidi.setHandleSystemReset(handleSystemReset); mMidi.begin(MIDI_CHANNEL_OMNI); mMidi.turnThruOff(); static const unsigned rxSize = 6; static const byte rxData[rxSize] = { 0xf8, 0xfa, 0xfb, 0xfc, 0xfe, 0xff }; mSerial.mRxBuffer.write(rxData, rxSize); static const midi::MidiType types[rxSize] = { midi::Clock, midi::Start, midi::Continue, midi::Stop, midi::ActiveSensing, midi::SystemReset, }; for (unsigned i = 0; i < rxSize; ++i) { EXPECT_EQ(mMidi.read(), true); EXPECT_EQ(mMidi.getType(), types[i]); EXPECT_EQ(mMidi.getChannel(), 0); EXPECT_EQ(mMidi.getData1(), 0); EXPECT_EQ(mMidi.getData2(), 0); EXPECT_EQ(mSerial.mTxBuffer.getLength(), 1); const byte read = mSerial.mTxBuffer.read(); EXPECT_EQ(read, rxData[i]); EXPECT_EQ(read, types[i]); } } END_UNNAMED_NAMESPACE