diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 354c761..cce4c7b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,7 +23,7 @@ repos: - id: pylint name: pylint (library code) types: [python] - exclude: "^(docs/|examples/|setup.py$)" + exclude: "^(docs/|tests/|examples/|setup.py$)" - repo: local hooks: - id: pylint_examples @@ -32,3 +32,11 @@ repos: entry: /usr/bin/env bash -c args: ['([[ ! -d "examples" ]] || for example in $(find . -path "./examples/*.py"); do pylint --disable=missing-docstring,invalid-name $example; done)'] language: system +- repo: local + hooks: + - id: pylint_tests + name: pylint (tests code) + description: Run pylint rules on "tests/*.py" files + entry: /usr/bin/env bash -c + args: ['([[ ! -d "tests" ]] || for test in $(find . -path "./tests/*.py"); do pylint --disable=missing-docstring $test; done)'] + language: system diff --git a/adafruit_midi/note_off.py b/adafruit_midi/note_off.py index 9e89061..2cbbb5a 100644 --- a/adafruit_midi/note_off.py +++ b/adafruit_midi/note_off.py @@ -22,7 +22,7 @@ __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_MIDI.git" -class NoteOff(MIDIMessage): +class NoteOff(MIDIMessage): # pylint: disable=duplicate-code """Note Off Change MIDI message. :param note: The note (key) number either as an ``int`` (0-127) or a @@ -36,15 +36,19 @@ class NoteOff(MIDIMessage): LENGTH = 3 def __init__(self, note, velocity=0, *, channel=None): - self.note = note_parser(note) - self.velocity = velocity + self._note = note_parser(note) + self._velocity = velocity super().__init__(channel=channel) - if not 0 <= self.note <= 127 or not 0 <= self.velocity <= 127: + if not 0 <= self._note <= 127 or not 0 <= self._velocity <= 127: raise self._EX_VALUEERROR_OOR def __bytes__(self): return bytes( - [self._STATUS | (self.channel & self.CHANNELMASK), self.note, self.velocity] + [ + self._STATUS | (self.channel & self.CHANNELMASK), + self._note, + self._velocity, + ] ) @classmethod diff --git a/tests/test_MIDIMessage_unittests.py b/tests/test_MIDIMessage_unittests.py index d7db66a..1f464bf 100644 --- a/tests/test_MIDIMessage_unittests.py +++ b/tests/test_MIDIMessage_unittests.py @@ -1,15 +1,17 @@ +# pylint: disable=invalid-name # SPDX-FileCopyrightText: 2019 Kevin J. Walters for Adafruit Industries # # SPDX-License-Identifier: MIT +# pylint: enable=invalid-name import unittest -from unittest.mock import Mock, MagicMock import os verbose = int(os.getenv("TESTVERBOSE", "2")) +# pylint: disable=wrong-import-position # adafruit_midi had an import usb_midi import sys @@ -22,21 +24,17 @@ import adafruit_midi # Full monty -from adafruit_midi.channel_pressure import ChannelPressure -from adafruit_midi.control_change import ControlChange from adafruit_midi.note_off import NoteOff from adafruit_midi.note_on import NoteOn -from adafruit_midi.pitch_bend import PitchBend -from adafruit_midi.polyphonic_key_pressure import PolyphonicKeyPressure -from adafruit_midi.program_change import ProgramChange -from adafruit_midi.start import Start -from adafruit_midi.stop import Stop from adafruit_midi.system_exclusive import SystemExclusive -from adafruit_midi.timing_clock import TimingClock +# pylint: enable=wrong-import-position +# pylint: disable=invalid-name class Test_MIDIMessage_from_message_byte_tests(unittest.TestCase): - def test_NoteOn_basic(self): + # pylint: enable=invalid-name + def test_NoteOn_basic(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name data = bytes([0x90, 0x30, 0x7F]) ichannel = 0 @@ -51,7 +49,8 @@ def test_NoteOn_basic(self): self.assertEqual(skipped, 0) self.assertEqual(msg.channel, 0) - def test_NoteOn_awaitingthirdbyte(self): + def test_NoteOn_awaitingthirdbyte(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name data = bytes([0x90, 0x30]) ichannel = 0 @@ -71,7 +70,8 @@ def test_NoteOn_awaitingthirdbyte(self): ) self.assertEqual(skipped, 0) - def test_NoteOn_predatajunk(self): + def test_NoteOn_predatajunk(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name data = bytes([0x20, 0x64, 0x90, 0x30, 0x32]) ichannel = 0 @@ -90,7 +90,8 @@ def test_NoteOn_predatajunk(self): self.assertEqual(skipped, 2) self.assertEqual(msg.channel, 0) - def test_NoteOn_prepartialsysex(self): + def test_NoteOn_prepartialsysex(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name data = bytes([0x01, 0x02, 0x03, 0x04, 0xF7, 0x90, 0x30, 0x32]) ichannel = 0 @@ -118,7 +119,7 @@ def test_NoteOn_prepartialsysex(self): self.assertIsInstance( msg, NoteOn, - "NoteOn is expected if SystemExclusive is loaded otherwise it would be MIDIUnknownEvent", + "NoteOn is expected if SystemExclusive is loaded otherwise it'd be MIDIUnknownEvent", ) self.assertEqual(msg.note, 0x30) self.assertEqual(msg.velocity, 0x32) @@ -126,7 +127,8 @@ def test_NoteOn_prepartialsysex(self): self.assertEqual(skipped, 0) self.assertEqual(msg.channel, 0) - def test_NoteOn_postNoteOn(self): + def test_NoteOn_postNoteOn(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name data = bytes([0x90 | 0x08, 0x30, 0x7F, 0x90 | 0x08, 0x37, 0x64]) ichannel = 8 @@ -141,7 +143,8 @@ def test_NoteOn_postNoteOn(self): self.assertEqual(skipped, 0) self.assertEqual(msg.channel, 8) - def test_NoteOn_postpartialNoteOn(self): + def test_NoteOn_postpartialNoteOn(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name data = bytes([0x90, 0x30, 0x7F, 0x90, 0x37]) ichannel = 0 @@ -156,7 +159,8 @@ def test_NoteOn_postpartialNoteOn(self): self.assertEqual(skipped, 0) self.assertEqual(msg.channel, 0) - def test_NoteOn_preotherchannel(self): + def test_NoteOn_preotherchannel(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name data = bytes([0x90 | 0x05, 0x30, 0x7F, 0x90 | 0x03, 0x37, 0x64]) ichannel = 3 @@ -171,7 +175,10 @@ def test_NoteOn_preotherchannel(self): self.assertEqual(skipped, 0) self.assertEqual(msg.channel, 3) - def test_NoteOn_preotherchannelplusintermediatejunk(self): + def test_NoteOn_preotherchannelplusintermediatejunk( + self, + ): # pylint: disable=invalid-name + # pylint: enable=invalid-name data = bytes([0x90 | 0x05, 0x30, 0x7F, 0x00, 0x00, 0x90 | 0x03, 0x37, 0x64]) ichannel = 3 @@ -188,7 +195,8 @@ def test_NoteOn_preotherchannelplusintermediatejunk(self): self.assertEqual(skipped, 0) self.assertEqual(msg.channel, 3) - def test_NoteOn_wrongchannel(self): + def test_NoteOn_wrongchannel(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name data = bytes([0x95, 0x30, 0x7F]) ichannel = 3 @@ -200,7 +208,8 @@ def test_NoteOn_wrongchannel(self): self.assertEqual(msgendidxplusone, 3, "wrong channel message discarded") self.assertEqual(skipped, 0) - def test_NoteOn_partialandpreotherchannel1(self): + def test_NoteOn_partialandpreotherchannel1(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name data = bytes([0x95, 0x30, 0x7F, 0x93]) ichannel = 3 @@ -214,7 +223,8 @@ def test_NoteOn_partialandpreotherchannel1(self): ) self.assertEqual(skipped, 0) - def test_NoteOn_partialandpreotherchannel2(self): + def test_NoteOn_partialandpreotherchannel2(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name data = bytes([0x95, 0x30, 0x7F, 0x93, 0x37]) ichannel = 3 @@ -228,7 +238,8 @@ def test_NoteOn_partialandpreotherchannel2(self): ) self.assertEqual(skipped, 0) - def test_NoteOn_constructor_int(self): + def test_NoteOn_constructor_int(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name object1 = NoteOn(60, 0x7F) self.assertEqual(object1.note, 60) @@ -253,7 +264,8 @@ def test_NoteOn_constructor_int(self): self.assertEqual(object4.velocity, 127) self.assertIsNone(object4.channel) - def test_SystemExclusive_NoteOn(self): + def test_SystemExclusive_NoteOn(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name data = bytes([0xF0, 0x42, 0x01, 0x02, 0x03, 0x04, 0xF7, 0x90 | 14, 0x30, 0x60]) ichannel = 14 @@ -281,7 +293,10 @@ def test_SystemExclusive_NoteOn(self): self.assertEqual(skipped, 0) self.assertEqual(msg.channel, 14) - def test_SystemExclusive_NoteOn_premalterminatedsysex(self): + def test_SystemExclusive_NoteOn_premalterminatedsysex( + self, + ): # pylint: disable=invalid-name + # pylint: enable=invalid-name data = bytes([0xF0, 0x42, 0x01, 0x02, 0x03, 0x04, 0xF0, 0x90, 0x30, 0x32]) ichannel = 0 @@ -296,7 +311,8 @@ def test_SystemExclusive_NoteOn_premalterminatedsysex(self): skipped, 0, "If SystemExclusive class is imported then this must be 0" ) - def test_Unknown_SinglebyteStatus(self): + def test_Unknown_SinglebyteStatus(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name data = bytes([0xFD]) ichannel = 0 @@ -309,7 +325,8 @@ def test_Unknown_SinglebyteStatus(self): self.assertEqual(skipped, 0) self.assertIsNone(msg.channel) - def test_Empty(self): + def test_Empty(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name data = bytes([]) ichannel = 0 @@ -322,8 +339,11 @@ def test_Empty(self): self.assertEqual(skipped, 0) -class Test_MIDIMessage_NoteOn_constructor(unittest.TestCase): - def test_NoteOn_constructor_string(self): +class Test_MIDIMessage_NoteOn_constructor( + unittest.TestCase +): # pylint: disable=invalid-name + def test_NoteOn_constructor_string(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name object1 = NoteOn("C4", 0x64) self.assertEqual(object1.note, 60) self.assertEqual(object1.velocity, 0x64) @@ -336,35 +356,39 @@ def test_NoteOn_constructor_string(self): self.assertEqual(object3.note, 61) self.assertEqual(object3.velocity, 0) - def test_NoteOn_constructor_valueerror1(self): + def test_NoteOn_constructor_valueerror1(self): # pylint: disable=invalid-name with self.assertRaises(ValueError): NoteOn(60, 0x80) # pylint is happier if return value not stored - def test_NoteOn_constructor_valueerror2(self): + def test_NoteOn_constructor_valueerror2(self): # pylint: disable=invalid-name with self.assertRaises(ValueError): NoteOn(-1, 0x7F) - def test_NoteOn_constructor_valueerror3(self): + def test_NoteOn_constructor_valueerror3(self): # pylint: disable=invalid-name with self.assertRaises(ValueError): NoteOn(128, 0x7F) - def test_NoteOn_constructor_upperrange1(self): + def test_NoteOn_constructor_upperrange1(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name object1 = NoteOn("G9", 0x7F) self.assertEqual(object1.note, 127) self.assertEqual(object1.velocity, 0x7F) - def test_NoteOn_constructor_upperrange2(self): + def test_NoteOn_constructor_upperrange2(self): # pylint: disable=invalid-name with self.assertRaises(ValueError): NoteOn("G#9", 0x7F) # just above max note - def test_NoteOn_constructor_bogusstring(self): + def test_NoteOn_constructor_bogusstring(self): # pylint: disable=invalid-name with self.assertRaises(ValueError): NoteOn("CC4", 0x7F) -class Test_MIDIMessage_NoteOff_constructor(unittest.TestCase): +class Test_MIDIMessage_NoteOff_constructor( + unittest.TestCase +): # pylint: disable=invalid-name # mostly cut and paste from NoteOn above - def test_NoteOff_constructor_string(self): + def test_NoteOff_constructor_string(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name object1 = NoteOff("C4", 0x64) self.assertEqual(object1.note, 60) self.assertEqual(object1.velocity, 0x64) @@ -381,28 +405,29 @@ def test_NoteOff_constructor_string(self): self.assertEqual(object4.note, 61) self.assertEqual(object4.velocity, 0) - def test_NoteOff_constructor_valueerror1(self): + def test_NoteOff_constructor_valueerror1(self): # pylint: disable=invalid-name with self.assertRaises(ValueError): NoteOff(60, 0x80) - def test_NoteOff_constructor_valueerror2(self): + def test_NoteOff_constructor_valueerror2(self): # pylint: disable=invalid-name with self.assertRaises(ValueError): NoteOff(-1, 0x7F) - def test_NoteOff_constructor_valueerror3(self): + def test_NoteOff_constructor_valueerror3(self): # pylint: disable=invalid-name with self.assertRaises(ValueError): NoteOff(128, 0x7F) - def test_NoteOff_constructor_upperrange1(self): + def test_NoteOff_constructor_upperrange1(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name object1 = NoteOff("G9", 0x7F) self.assertEqual(object1.note, 127) self.assertEqual(object1.velocity, 0x7F) - def test_NoteOff_constructor_upperrange2(self): + def test_NoteOff_constructor_upperrange2(self): # pylint: disable=invalid-name with self.assertRaises(ValueError): NoteOff("G#9", 0x7F) # just above max note - def test_NoteOff_constructor_bogusstring(self): + def test_NoteOff_constructor_bogusstring(self): # pylint: disable=invalid-name with self.assertRaises(ValueError): NoteOff("CC4", 0x7F) diff --git a/tests/test_MIDI_unittests.py b/tests/test_MIDI_unittests.py index 1106092..55d470d 100644 --- a/tests/test_MIDI_unittests.py +++ b/tests/test_MIDI_unittests.py @@ -1,15 +1,18 @@ +# pylint: disable=invalid-name # SPDX-FileCopyrightText: 2019 Kevin J. Walters for Adafruit Industries # # SPDX-License-Identifier: MIT +# pylint: enable=invalid-name import unittest -from unittest.mock import Mock, MagicMock, call +from unittest.mock import Mock, call import random import os verbose = int(os.getenv("TESTVERBOSE", "2")) +# pylint: disable=wrong-import-position # adafruit_midi had an import usb_midi import sys @@ -24,19 +27,17 @@ from adafruit_midi.note_off import NoteOff from adafruit_midi.note_on import NoteOn from adafruit_midi.pitch_bend import PitchBend -from adafruit_midi.polyphonic_key_pressure import PolyphonicKeyPressure -from adafruit_midi.program_change import ProgramChange -from adafruit_midi.start import Start -from adafruit_midi.stop import Stop from adafruit_midi.system_exclusive import SystemExclusive -from adafruit_midi.timing_clock import TimingClock # Import after messages - opposite to other test file import adafruit_midi +# pylint: enable=wrong-import-position + # For loopback/echo tests -def MIDI_mocked_both_loopback(in_c, out_c): +def MIDI_mocked_both_loopback(in_c, out_c): # pylint: disable=invalid-name + # pylint: enable=invalid-name usb_data = bytearray() def write(buffer, length): @@ -49,17 +50,18 @@ def read(length): usb_data = usb_data[len(poppedbytes) :] return bytes(poppedbytes) - mockedPortIn = Mock() - mockedPortIn.read = read - mockedPortOut = Mock() - mockedPortOut.write = write - m = adafruit_midi.MIDI( - midi_out=mockedPortOut, midi_in=mockedPortIn, out_channel=out_c, in_channel=in_c + mockedportin = Mock() + mockedportin.read = read + mockedportout = Mock() + mockedportout.write = write + midi = adafruit_midi.MIDI( + midi_out=mockedportout, midi_in=mockedportin, out_channel=out_c, in_channel=in_c ) - return m + return midi -def MIDI_mocked_receive(in_c, data, read_sizes): +def MIDI_mocked_receive(in_c, data, read_sizes): # pylint: disable=invalid-name + # pylint: enable=invalid-name usb_data = bytearray(data) chunks = read_sizes chunk_idx = 0 @@ -79,16 +81,17 @@ def read(length): else: return bytes() - mockedPortIn = Mock() - mockedPortIn.read = read + mockedportin = Mock() + mockedportin.read = read - m = adafruit_midi.MIDI( - midi_out=None, midi_in=mockedPortIn, out_channel=in_c, in_channel=in_c + midi = adafruit_midi.MIDI( + midi_out=None, midi_in=mockedportin, out_channel=in_c, in_channel=in_c ) - return m + return midi -class Test_MIDI_constructor(unittest.TestCase): +class Test_MIDI_constructor(unittest.TestCase): # pylint: disable=invalid-name + # pylint: enable=invalid-name def test_no_inout(self): # constructor likes a bit of in out with self.assertRaises(ValueError): @@ -97,8 +100,9 @@ def test_no_inout(self): class Test_MIDI(unittest.TestCase): # pylint: disable=too-many-branches - def test_captured_data_one_byte_reads(self): - c = 0 + def test_captured_data_one_byte_reads(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name + channel = 0 # From an M-Audio AXIOM controller raw_data = bytearray( [0x90, 0x3E, 0x5F] @@ -108,130 +112,133 @@ def test_captured_data_one_byte_reads(self): + [0x90, 0x41, 0x74] + [0xE0, 0x03, 0x40] ) - m = MIDI_mocked_receive(c, raw_data, [1] * len(raw_data)) + midi = MIDI_mocked_receive(channel, raw_data, [1] * len(raw_data)) for unused in range(100): # pylint: disable=unused-variable - msg = m.receive() + msg = midi.receive() if msg is not None: break self.assertIsInstance(msg, NoteOn) self.assertEqual(msg.note, 0x3E) self.assertEqual(msg.velocity, 0x5F) - self.assertEqual(msg.channel, c) + self.assertEqual(msg.channel, channel) # for loops currently absorb any Nones but could # be set to read precisely the expected number... for unused in range(100): # pylint: disable=unused-variable - msg = m.receive() + msg = midi.receive() if msg is not None: break self.assertIsInstance(msg, ChannelPressure) self.assertEqual(msg.pressure, 0x10) - self.assertEqual(msg.channel, c) + self.assertEqual(msg.channel, channel) for unused in range(100): # pylint: disable=unused-variable - msg = m.receive() + msg = midi.receive() if msg is not None: break self.assertIsInstance(msg, NoteOn) self.assertEqual(msg.note, 0x40) self.assertEqual(msg.velocity, 0x66) - self.assertEqual(msg.channel, c) + self.assertEqual(msg.channel, channel) for unused in range(100): # pylint: disable=unused-variable - msg = m.receive() + msg = midi.receive() if msg is not None: break self.assertIsInstance(msg, ControlChange) self.assertEqual(msg.control, 0x01) self.assertEqual(msg.value, 0x08) - self.assertEqual(msg.channel, c) + self.assertEqual(msg.channel, channel) for unused in range(100): # pylint: disable=unused-variable - msg = m.receive() + msg = midi.receive() if msg is not None: break self.assertIsInstance(msg, NoteOn) self.assertEqual(msg.note, 0x41) self.assertEqual(msg.velocity, 0x74) - self.assertEqual(msg.channel, c) + self.assertEqual(msg.channel, channel) for unused in range(100): # pylint: disable=unused-variable - msg = m.receive() + msg = midi.receive() if msg is not None: break self.assertIsInstance(msg, PitchBend) self.assertEqual(msg.pitch_bend, 8195) - self.assertEqual(msg.channel, c) + self.assertEqual(msg.channel, channel) for unused in range(100): # pylint: disable=unused-variable - msg = m.receive() + msg = midi.receive() self.assertIsNone(msg) - def test_unknown_before_NoteOn(self): - c = 0 + def test_unknown_before_NoteOn(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name + channel = 0 # From an M-Audio AXIOM controller raw_data = bytes( [0b11110011, 0x10] # Song Select (not yet implemented) + [0b11110011, 0x20] + [0b11110100] + [0b11110101] - ) + bytes(NoteOn("C5", 0x7F, channel=c)) - m = MIDI_mocked_receive(c, raw_data, [2, 2, 1, 1, 3]) + ) + bytes(NoteOn("C5", 0x7F, channel=channel)) + midi = MIDI_mocked_receive(channel, raw_data, [2, 2, 1, 1, 3]) for unused in range(4): # pylint: disable=unused-variable - msg = m.receive() + msg = midi.receive() self.assertIsInstance(msg, adafruit_midi.midi_message.MIDIUnknownEvent) self.assertIsNone(msg.channel) - msg = m.receive() + msg = midi.receive() self.assertIsInstance(msg, NoteOn) self.assertEqual(msg.note, 0x48) # 0x48 is C5 self.assertEqual(msg.velocity, 0x7F) - self.assertEqual(msg.channel, c) + self.assertEqual(msg.channel, channel) # See https://github.com/adafruit/Adafruit_CircuitPython_MIDI/issues/8 - def test_running_status_when_implemented(self): - c = 8 + def test_running_status_when_implemented(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name + channel = 8 raw_data = ( - bytes(NoteOn("C5", 0x7F, channel=c)) + bytes(NoteOn("C5", 0x7F, channel=channel)) + bytes([0xE8, 0x72, 0x40] + [0x6D, 0x40] + [0x05, 0x41]) - + bytes(NoteOn("D5", 0x7F, channel=c)) + + bytes(NoteOn("D5", 0x7F, channel=channel)) ) - m = MIDI_mocked_receive(c, raw_data, [3 + 3 + 2 + 3 + 3]) - self.assertIsInstance(m, adafruit_midi.MIDI) # silence pylint! + midi = MIDI_mocked_receive(channel, raw_data, [3 + 3 + 2 + 3 + 3]) + self.assertIsInstance(midi, adafruit_midi.MIDI) # silence pylint! # self.assertEqual(TOFINISH, WHENIMPLEMENTED) - def test_somegood_somemissing_databytes(self): - c = 8 + def test_somegood_somemissing_databytes(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name + channel = 8 raw_data = ( - bytes(NoteOn("C5", 0x7F, channel=c)) + bytes(NoteOn("C5", 0x7F, channel=channel)) + bytes( [0xE8, 0x72, 0x40] + [0xE8, 0x6D] # Missing last data byte + [0xE8, 0x5, 0x41] ) - + bytes(NoteOn("D5", 0x7F, channel=c)) + + bytes(NoteOn("D5", 0x7F, channel=channel)) ) - m = MIDI_mocked_receive(c, raw_data, [3 + 3 + 2 + 3 + 3]) + midi = MIDI_mocked_receive(channel, raw_data, [3 + 3 + 2 + 3 + 3]) - msg1 = m.receive() + msg1 = midi.receive() self.assertIsInstance(msg1, NoteOn) self.assertEqual(msg1.note, 72) self.assertEqual(msg1.velocity, 0x7F) - self.assertEqual(msg1.channel, c) + self.assertEqual(msg1.channel, channel) - msg2 = m.receive() + msg2 = midi.receive() self.assertIsInstance(msg2, PitchBend) self.assertEqual(msg2.pitch_bend, 8306) - self.assertEqual(msg2.channel, c) + self.assertEqual(msg2.channel, channel) # The current implementation will read status bytes for data # In most cases it would be a faster recovery with fewer messages # lost if the next status byte wasn't consumed # and parsing restarted from that byte - msg3 = m.receive() + msg3 = midi.receive() self.assertIsInstance(msg3, adafruit_midi.midi_message.MIDIBadEvent) self.assertIsInstance(msg3.data, bytes) self.assertEqual(msg3.data, bytes([0xE8, 0x6D, 0xE8])) @@ -242,19 +249,19 @@ def test_somegood_somemissing_databytes(self): # self.assertEqual(msg4.pitch_bend, 72) # self.assertEqual(channel4, c) - msg5 = m.receive() + msg5 = midi.receive() self.assertIsInstance(msg5, NoteOn) self.assertEqual(msg5.note, 74) self.assertEqual(msg5.velocity, 0x7F) - self.assertEqual(msg5.channel, c) + self.assertEqual(msg5.channel, channel) - msg6 = m.receive() + msg6 = midi.receive() self.assertIsNone(msg6) def test_smallsysex_between_notes(self): - m = MIDI_mocked_both_loopback(3, 3) + midi = MIDI_mocked_both_loopback(3, 3) - m.send( + midi.send( [ NoteOn("C4", 0x7F), SystemExclusive([0x1F], [1, 2, 3, 4, 5, 6, 7, 8]), @@ -262,74 +269,74 @@ def test_smallsysex_between_notes(self): ] ) - msg1 = m.receive() + msg1 = midi.receive() self.assertIsInstance(msg1, NoteOn) self.assertEqual(msg1.note, 60) self.assertEqual(msg1.velocity, 0x7F) self.assertEqual(msg1.channel, 3) - msg2 = m.receive() + msg2 = midi.receive() self.assertIsInstance(msg2, SystemExclusive) self.assertEqual(msg2.manufacturer_id, bytes([0x1F])) self.assertEqual(msg2.data, bytes([1, 2, 3, 4, 5, 6, 7, 8])) self.assertEqual(msg2.channel, None) # SysEx does not have a channel - msg3 = m.receive() + msg3 = midi.receive() self.assertIsInstance(msg3, NoteOff) self.assertEqual(msg3.note, 60) self.assertEqual(msg3.velocity, 0x28) self.assertEqual(msg3.channel, 3) - msg4 = m.receive() + msg4 = midi.receive() self.assertIsNone(msg4) def test_smallsysex_bytes_type(self): - s = SystemExclusive([0x1F], [100, 150, 200]) + message = SystemExclusive([0x1F], [100, 150, 200]) - self.assertIsInstance(s, SystemExclusive) - self.assertEqual(s.manufacturer_id, bytes([0x1F])) - self.assertIsInstance(s.manufacturer_id, bytes) + self.assertIsInstance(message, SystemExclusive) + self.assertEqual(message.manufacturer_id, bytes([0x1F])) + self.assertIsInstance(message.manufacturer_id, bytes) # check this really is immutable (pylint also picks this up!) with self.assertRaises(TypeError): - s.data[0] = 0 # pylint: disable=unsupported-assignment-operation + message.data[0] = 0 # pylint: disable=unsupported-assignment-operation - self.assertEqual(s.data, bytes([100, 150, 200])) - self.assertIsInstance(s.data, bytes) + self.assertEqual(message.data, bytes([100, 150, 200])) + self.assertIsInstance(message.data, bytes) # pylint: disable=too-many-locals def test_larger_than_buffer_sysex(self): - c = 0 + channel = 0 monster_data_len = 500 raw_data = ( - bytes(NoteOn("C5", 0x7F, channel=c)) + bytes(NoteOn("C5", 0x7F, channel=channel)) + bytes( SystemExclusive([0x02], [d & 0x7F for d in range(monster_data_len)]) ) - + bytes(NoteOn("D5", 0x7F, channel=c)) + + bytes(NoteOn("D5", 0x7F, channel=channel)) ) - m = MIDI_mocked_receive(c, raw_data, [len(raw_data)]) - buffer_len = m._in_buf_size # pylint: disable=protected-access + midi = MIDI_mocked_receive(channel, raw_data, [len(raw_data)]) + buffer_len = midi._in_buf_size # pylint: disable=protected-access self.assertTrue( monster_data_len > buffer_len, "checking our SysEx truly is larger than buffer", ) - msg1 = m.receive() + msg1 = midi.receive() self.assertIsInstance(msg1, NoteOn) self.assertEqual(msg1.note, 72) self.assertEqual(msg1.velocity, 0x7F) - self.assertEqual(msg1.channel, c) + self.assertEqual(msg1.channel, channel) # (Ab)using python's rounding down for negative division # pylint: disable=unused-variable for unused in range(-(-(1 + 1 + monster_data_len + 1) // buffer_len) - 1): - msg2 = m.receive() + msg2 = midi.receive() self.assertIsNone(msg2) # The current implementation will read SysEx end status byte # and report it as an unknown - msg3 = m.receive() + msg3 = midi.receive() self.assertIsInstance(msg3, adafruit_midi.midi_message.MIDIUnknownEvent) self.assertEqual(msg3.status, 0xF7) self.assertIsNone(msg3.channel) @@ -339,13 +346,13 @@ def test_larger_than_buffer_sysex(self): # self.assertEqual(msg4.pitch_bend, 72) # self.assertEqual(channel4, c) - msg5 = m.receive() + msg5 = midi.receive() self.assertIsInstance(msg5, NoteOn) self.assertEqual(msg5.note, 74) self.assertEqual(msg5.velocity, 0x7F) - self.assertEqual(msg5.channel, c) + self.assertEqual(msg5.channel, channel) - msg6 = m.receive() + msg6 = midi.receive() self.assertIsNone(msg6) @@ -355,123 +362,124 @@ class Test_MIDI_send(unittest.TestCase): def test_send_basic_single(self): # def printit(buffer, len): # print(buffer[0:len]) - mockedPortOut = Mock() + mockedportout = Mock() # mockedPortOut.write = printit - m = adafruit_midi.MIDI(midi_out=mockedPortOut, out_channel=2) + midi = adafruit_midi.MIDI(midi_out=mockedportout, out_channel=2) # Test sending some NoteOn and NoteOff to various channels nextcall = 0 - m.send(NoteOn(0x60, 0x7F)) + midi.send(NoteOn(0x60, 0x7F)) self.assertEqual( - mockedPortOut.write.mock_calls[nextcall], call(b"\x92\x60\x7f", 3) + mockedportout.write.mock_calls[nextcall], call(b"\x92\x60\x7f", 3) ) nextcall += 1 - m.send(NoteOn(0x64, 0x3F)) + midi.send(NoteOn(0x64, 0x3F)) self.assertEqual( - mockedPortOut.write.mock_calls[nextcall], call(b"\x92\x64\x3f", 3) + mockedportout.write.mock_calls[nextcall], call(b"\x92\x64\x3f", 3) ) nextcall += 1 - m.send(NoteOn(0x67, 0x1F)) + midi.send(NoteOn(0x67, 0x1F)) self.assertEqual( - mockedPortOut.write.mock_calls[nextcall], call(b"\x92\x67\x1f", 3) + mockedportout.write.mock_calls[nextcall], call(b"\x92\x67\x1f", 3) ) nextcall += 1 - m.send(NoteOn(0x60, 0x00)) # Alternative to NoteOff + midi.send(NoteOn(0x60, 0x00)) # Alternative to NoteOff self.assertEqual( - mockedPortOut.write.mock_calls[nextcall], call(b"\x92\x60\x00", 3) + mockedportout.write.mock_calls[nextcall], call(b"\x92\x60\x00", 3) ) nextcall += 1 - m.send(NoteOff(0x64, 0x01)) + midi.send(NoteOff(0x64, 0x01)) self.assertEqual( - mockedPortOut.write.mock_calls[nextcall], call(b"\x82\x64\x01", 3) + mockedportout.write.mock_calls[nextcall], call(b"\x82\x64\x01", 3) ) nextcall += 1 - m.send(NoteOff(0x67, 0x02)) + midi.send(NoteOff(0x67, 0x02)) self.assertEqual( - mockedPortOut.write.mock_calls[nextcall], call(b"\x82\x67\x02", 3) + mockedportout.write.mock_calls[nextcall], call(b"\x82\x67\x02", 3) ) nextcall += 1 # Setting channel to non default - m.send(NoteOn(0x6C, 0x7F), channel=9) + midi.send(NoteOn(0x6C, 0x7F), channel=9) self.assertEqual( - mockedPortOut.write.mock_calls[nextcall], call(b"\x99\x6c\x7f", 3) + mockedportout.write.mock_calls[nextcall], call(b"\x99\x6c\x7f", 3) ) nextcall += 1 - m.send(NoteOff(0x6C, 0x7F), channel=9) + midi.send(NoteOff(0x6C, 0x7F), channel=9) self.assertEqual( - mockedPortOut.write.mock_calls[nextcall], call(b"\x89\x6c\x7f", 3) + mockedportout.write.mock_calls[nextcall], call(b"\x89\x6c\x7f", 3) ) nextcall += 1 def test_send_badnotes(self): - mockedPortOut = Mock() + mockedportout = Mock() - m = adafruit_midi.MIDI(midi_out=mockedPortOut, out_channel=2) + midi = adafruit_midi.MIDI(midi_out=mockedportout, out_channel=2) # Test sending some NoteOn and NoteOff to various channels nextcall = 0 - m.send(NoteOn(60, 0x7F)) + midi.send(NoteOn(60, 0x7F)) self.assertEqual( - mockedPortOut.write.mock_calls[nextcall], call(b"\x92\x3c\x7f", 3) + mockedportout.write.mock_calls[nextcall], call(b"\x92\x3c\x7f", 3) ) nextcall += 1 with self.assertRaises(ValueError): - m.send(NoteOn(64, 0x80)) # Velocity > 127 - illegal value + midi.send(NoteOn(64, 0x80)) # Velocity > 127 - illegal value with self.assertRaises(ValueError): - m.send(NoteOn(67, -1)) + midi.send(NoteOn(67, -1)) # test after exceptions to ensure sending is still ok - m.send(NoteOn(72, 0x7F)) + midi.send(NoteOn(72, 0x7F)) self.assertEqual( - mockedPortOut.write.mock_calls[nextcall], call(b"\x92\x48\x7f", 3) + mockedportout.write.mock_calls[nextcall], call(b"\x92\x48\x7f", 3) ) nextcall += 1 def test_send_basic_sequences(self): # def printit(buffer, len): # print(buffer[0:len]) - mockedPortOut = Mock() - # mockedPortOut.write = printit + mockedportout = Mock() + # mockedportout.write = printit - m = adafruit_midi.MIDI(midi_out=mockedPortOut, out_channel=2) + midi = adafruit_midi.MIDI(midi_out=mockedportout, out_channel=2) # Test sending some NoteOn and NoteOff to various channels nextcall = 0 # Test sequences with list syntax and pass a tuple too note_list = [NoteOn(0x6C, 0x51), NoteOn(0x70, 0x52), NoteOn(0x73, 0x53)] note_tuple = tuple(note_list) - m.send(note_list, channel=10) + midi.send(note_list, channel=10) self.assertEqual( - mockedPortOut.write.mock_calls[nextcall], + mockedportout.write.mock_calls[nextcall], call(b"\x9a\x6c\x51\x9a\x70\x52\x9a\x73\x53", 9), "The implementation writes in one go, single 9 byte write expected", ) nextcall += 1 - m.send(note_tuple, channel=11) + midi.send(note_tuple, channel=11) self.assertEqual( - mockedPortOut.write.mock_calls[nextcall], + mockedportout.write.mock_calls[nextcall], call(b"\x9b\x6c\x51\x9b\x70\x52\x9b\x73\x53", 9), "The implementation writes in one go, single 9 byte write expected", ) nextcall += 1 - def test_termination_with_random_data(self): + def test_termination_with_random_data(self): # pylint: disable=invalid-name + # pylint: enable=invalid-name """Test with a random stream of bytes to ensure that the parsing code termates and returns, i.e. does not go into any infinite loops. """ - c = 0 + channel = 0 random.seed(303808) raw_data = bytearray([random.randint(0, 255) for i in range(50000)]) - m = MIDI_mocked_receive(c, raw_data, [len(raw_data)]) + midi = MIDI_mocked_receive(channel, raw_data, [len(raw_data)]) noinfiniteloops = False for unused in range(len(raw_data)): # pylint: disable=unused-variable - m.receive() # not interested in returned tuple + midi.receive() # not interested in returned tuple noinfiniteloops = True # interested in getting to here self.assertTrue(noinfiniteloops) diff --git a/tests/test_note_parser.py b/tests/test_note_parser.py index 3be5176..fe0e279 100644 --- a/tests/test_note_parser.py +++ b/tests/test_note_parser.py @@ -3,13 +3,12 @@ # SPDX-License-Identifier: MIT import unittest -from unittest.mock import Mock, MagicMock, call -import random import os verbose = int(os.getenv("TESTVERBOSE", "2")) +# pylint: disable=wrong-import-position # adafruit_midi had an import usb_midi import sys @@ -20,6 +19,8 @@ from adafruit_midi.midi_message import note_parser +# pylint: enable=wrong-import-position + class Test_note_parser(unittest.TestCase): def text_int_passthru(self):