importunittestimportserialimportpyfirmatafrompyfirmataimportmockupfrompyfirmata.boardsimportBOARDSfrompyfirmata.utilimportstr_to_two_byte_iter# Messages todo left:# type command channel first byte second byte # ---------------------------------------------------------------------------# set pin mode(I/O) 0xF4 pin # (0-127) pin state(0=in)# system reset 0xFFclassBoardBaseTest(unittest.TestCase):defsetUp(self):# Test with the MockupSerial so no real connection is neededpyfirmata.pyfirmata.serial.Serial=mockup.MockupSerialself.board=pyfirmata.Board('',BOARDS['arduino'])self.board._stored_data=[]# FIXME How can it be that a fresh instance sometimes still contains data?classTestBoardMessages(BoardBaseTest):# TODO Test layout of Board Megadefassert_serial(self,*list_of_chrs):res=self.board.sp.read()serial_msg=reswhileres:res=self.board.sp.read()serial_msg+=resself.assertEqual(''.join(list_of_chrs),serial_msg)# First test the handlersdeftest_handle_analog_message(self):self.board.analog[3].reporting=Trueself.assertEqual(self.board.analog[3].read(),None)# This sould set it correctly. 1023 (127, 7 in to 7 bit bytes) is the# max value an analog pin will send and it should result in a value 1self.board._handle_analog_message(3,127,7)self.assertEqual(self.board.analog[3].read(),1.0)deftest_handle_digital_message(self):# A digital message sets the value for a whole port. We will set pin# 5 (That is on port 0) to 1 to test if this is working.self.board.digital_ports[0].reporting=Trueself.board.digital[5]._mode=0# Set it to input# Create the maskmask=0mask|=1<<5# set the bit for pin 5 to to 1self.assertEqual(self.board.digital[5].read(),None)self.board._handle_digital_message(0,mask%128,mask>>7)self.assertEqual(self.board.digital[5].read(),True)deftest_handle_report_version(self):self.assertEqual(self.board.firmata_version,None)self.board._handle_report_version(2,1)self.assertEqual(self.board.firmata_version,(2,1))deftest_handle_report_firmware(self):self.assertEqual(self.board.firmware,None)data=[2,1]+str_to_two_byte_iter('Firmware_name')self.board._handle_report_firmware(*data)self.assertEqual(self.board.firmware,'Firmware_name')self.assertEqual(self.board.firmata_version,(2,1))# type command channel first byte second byte # ---------------------------------------------------------------------------# analog I/O message 0xE0 pin # LSB(bits 0-6) MSB(bits 7-13)deftest_incoming_analog_message(self):self.assertEqual(self.board.analog[4].read(),None)self.assertEqual(self.board.analog[4].reporting,False)# Should do nothing as the pin isn't set to reportself.board.sp.write([chr(pyfirmata.ANALOG_MESSAGE+4),chr(127),chr(7)])self.board.iterate()self.assertEqual(self.board.analog[4].read(),None)self.board.analog[4].enable_reporting()self.board.sp.clear()# This should set analog port 4 to 1self.board.sp.write([chr(pyfirmata.ANALOG_MESSAGE+4),chr(127),chr(7)])self.board.iterate()self.assertEqual(self.board.analog[4].read(),1.0)self.board._stored_data=[]# type command channel first byte second byte # ---------------------------------------------------------------------------# digital I/O message 0x90 port LSB(bits 0-6) MSB(bits 7-13)deftest_incoming_digital_message(self):# A digital message sets the value for a whole port. We will set pin# 9 (on port 1) to 1 to test if this is working.self.board.digital[9].mode=pyfirmata.INPUTself.board.sp.clear()# clear mode sent over the wire.# Create the maskmask=0mask|=1<<(9-8)# set the bit for pin 9 to to 1self.assertEqual(self.board.digital[9].read(),None)self.board.sp.write([chr(pyfirmata.DIGITAL_MESSAGE+1),chr(mask%128),chr(mask>>7)])self.board.iterate()self.assertEqual(self.board.digital[9].read(),True)# version report format# -------------------------------------------------# 0 version report header (0xF9) (MIDI Undefined)# 1 major version (0-127)# 2 minor version (0-127)deftest_incoming_report_version(self):self.assertEqual(self.board.firmata_version,None)self.board.sp.write([chr(pyfirmata.REPORT_VERSION),chr(2),chr(1)])self.board.iterate()self.assertEqual(self.board.firmata_version,(2,1))# Receive Firmware Name and Version (after query)# 0 START_SYSEX (0xF0)# 1 queryFirmware (0x79)# 2 major version (0-127)# 3 minor version (0-127)# 4 first 7-bits of firmware name# 5 second 7-bits of firmware name# x ...for as many bytes as it needs)# 6 END_SYSEX (0xF7)deftest_incoming_report_firmware(self):self.assertEqual(self.board.firmware,None)self.assertEqual(self.board.firmata_version,None)msg=[chr(pyfirmata.START_SYSEX),chr(pyfirmata.REPORT_FIRMWARE),chr(2),chr(1)]+str_to_two_byte_iter('Firmware_name')+ \
[chr(pyfirmata.END_SYSEX)]self.board.sp.write(msg)self.board.iterate()self.assertEqual(self.board.firmware,'Firmware_name')self.assertEqual(self.board.firmata_version,(2,1))# type command channel first byte second byte # ---------------------------------------------------------------------------# report analog pin 0xC0 pin # disable/enable(0/1) - n/a -deftest_report_analog(self):self.board.analog[1].enable_reporting()self.assert_serial(chr(0xC0+1),chr(1))self.assertTrue(self.board.analog[1].reporting)self.board.analog[1].disable_reporting()self.assert_serial(chr(0xC0+1),chr(0))self.assertFalse(self.board.analog[1].reporting)# type command channel first byte second byte # ---------------------------------------------------------------------------# report digital port 0xD0 port disable/enable(0/1) - n/a -deftest_report_digital(self):# This should enable reporting of whole port 1self.board.digital[8]._mode=pyfirmata.INPUT# Outputs can't reportself.board.digital[8].enable_reporting()self.assert_serial(chr(0xD0+1),chr(1))self.assertTrue(self.board.digital_ports[1].reporting)self.board.digital[8].disable_reporting()self.assert_serial(chr(0xD0+1),chr(0))# Generic Sysex Message# 0 START_SYSEX (0xF0)# 1 sysex command (0x00-0x7F)# x between 0 and MAX_DATA_BYTES 7-bit bytes of arbitrary data# last END_SYSEX (0xF7)deftest_send_sysex_message(self):# 0x79 is queryFirmware, but that doesn't matter for nowself.board.send_sysex(0x79,[1,2,3])sysex=(chr(0xF0),chr(0x79),chr(1),chr(2),chr(3),chr(0xF7))self.assert_serial(*sysex)deftest_receive_sysex_message(self):sysex=(chr(0xF0),chr(0x79),chr(2),chr(1),'a','\x00','b','\x00','c','\x00',chr(0xF7))self.board.sp.write(sysex)whilelen(self.board.sp):self.board.iterate()self.assertEqual(self.board.firmata_version,(2,1))self.assertEqual(self.board.firmware,'abc')deftest_too_much_data(self):""" When we send random bytes, before or after a command, they should be ignored to prevent cascading errors when missing a byte. """self.board.analog[4].enable_reporting()self.board.sp.clear()# Crapself.board.sp.write([chr(i)foriinrange(10)])# This should set analog port 4 to 1self.board.sp.write([chr(pyfirmata.ANALOG_MESSAGE+4),chr(127),chr(7)])# Crapself.board.sp.write([chr(10-i)foriinrange(10)])whilelen(self.board.sp):self.board.iterate()self.assertEqual(self.board.analog[4].read(),1.0)classTestBoardLayout(BoardBaseTest):deftest_pwm_layout(self):pins=[]forpininself.board.digital:ifpin.PWM_CAPABLE:pins.append(self.board.get_pin('d:%d:p'%pin.pin_number))forpininpins:self.assertEqual(pin.mode,pyfirmata.PWM)self.assertTrue(pin.pin_numberinBOARDS['arduino']['pwm'])self.assertTrue(len(pins)==len(BOARDS['arduino']['pwm']))deftest_get_pin_digital(self):pin=self.board.get_pin('d:13:o')self.assertEqual(pin.pin_number,13)self.assertEqual(pin.mode,pyfirmata.OUTPUT)self.assertEqual(pin.port.port_number,1)self.assertEqual(pin.port.reporting,False)deftest_get_pin_analog(self):pin=self.board.get_pin('a:5:i')self.assertEqual(pin.pin_number,5)self.assertEqual(pin.reporting,True)self.assertEqual(pin.value,None)deftearDown(self):self.board.exit()pyfirmata.serial.Serial=serial.SerialclassTestMockupBoardLayout(TestBoardLayout,TestBoardMessages):""" TestMockupBoardLayout is subclassed from TestBoardLayout and TestBoardMessages as it should pass the same tests, but with the MockupBoard. """defsetUp(self):self.board=mockup.MockupBoard('test',BOARDS['arduino'])board_messages=unittest.TestLoader().loadTestsFromTestCase(TestBoardMessages)board_layout=unittest.TestLoader().loadTestsFromTestCase(TestBoardLayout)default=unittest.TestSuite([board_messages,board_layout])mockup_suite=unittest.TestLoader().loadTestsFromTestCase(TestMockupBoardLayout)if__name__=='__main__':fromoptparseimportOptionParserparser=OptionParser()parser.add_option("-m","--mockup",dest="mockup",action="store_true",help="Also run the mockup tests")options,args=parser.parse_args()ifnotoptions.mockup:print"Running normal suite. Also consider running the mockup (-m, --mockup) suite"unittest.TextTestRunner(verbosity=3).run(default)ifoptions.mockup:print"Running the mockup test suite"unittest.TextTestRunner(verbosity=2).run(mockup_suite)