test_ieee.py
Go to the documentation of this file.
00001 #! /usr/bin/python
00002 """
00003 test_ieee.py
00004 
00005 By Paul Malmsten, 2010
00006 pmalmsten@gmail.com
00007 
00008 Tests the XBee (IEEE 802.15.4) implementation class for XBee API compliance
00009 """
00010 import unittest
00011 from xbee.tests.Fake import FakeDevice, FakeReadDevice
00012 from xbee.ieee import XBee
00013 
00014 class InitXBee(unittest.TestCase):
00015     """
00016     Base initalization class
00017     """
00018     def setUp(self):
00019         """
00020         Initialize XBee object
00021         """
00022         self.xbee = XBee(None)
00023 
00024 class TestBuildCommand(InitXBee):
00025     """
00026     _build_command should properly build a command packet
00027     """
00028     
00029     def test_build_at_data_mismatch(self):
00030         """
00031         if not enough or incorrect data is provided, an exception should
00032         be raised.
00033         """
00034         try:
00035             self.xbee._build_command("at")
00036         except KeyError:
00037             # Test passes
00038             return
00039     
00040         # No exception? Fail.
00041         self.fail(
00042             "An exception was not raised with improper data supplied"
00043         )
00044         
00045     def test_build_at_data_len_mismatch(self):
00046         """
00047         if data of incorrect length is provided, an exception should be 
00048         raised
00049         """
00050         try:
00051             self.xbee._build_command("at", frame_id="AB", command="MY")
00052         except ValueError:
00053             # Test passes
00054             return
00055     
00056         # No exception? Fail.
00057         self.fail(
00058             "An exception was not raised with improper data length"
00059         )
00060         
00061     def test_build_at(self):
00062         """
00063         _build_command should build a valid at command packet which has
00064         no parameter data to be saved
00065         """
00066         
00067         at_command = "MY"
00068         frame = chr(43)
00069         data = self.xbee._build_command(
00070             "at", 
00071             frame_id=frame, 
00072             command=at_command
00073         ) 
00074 
00075         expected_data = '\x08+MY'
00076         self.assertEqual(data, expected_data)
00077         
00078     def test_build_at_with_default(self):
00079         """
00080         _build_command should build a valid at command packet which has
00081         no parameter data to be saved and no frame specified (the 
00082         default value of \x00 should be used)
00083         """
00084         
00085         at_command = "MY"
00086         data = self.xbee._build_command("at", command=at_command) 
00087 
00088         expected_data = '\x08\x00MY'
00089         self.assertEqual(data, expected_data)
00090         
00091 class TestSplitResponse(InitXBee):
00092     """
00093     _split_response should properly split a response packet
00094     """
00095     
00096     def test_unrecognized_response(self):
00097         """
00098         if a response begins with an unrecognized id byte, 
00099         _split_response should raise an exception
00100         """
00101         data = '\x23\x00\x00\x00'
00102         
00103         try:
00104             self.xbee._split_response(data)
00105         except KeyError:
00106             # Passes
00107             return
00108             
00109         # Test Fails
00110         self.fail()
00111     
00112     def test_bad_data_long(self):
00113         """
00114         if a response doesn't match the specification's layout, 
00115         _split_response should raise an exception
00116         """
00117         # Over length
00118         data = '\x8a\x00\x00\x00'
00119         self.assertRaises(ValueError, self.xbee._split_response, data)
00120         
00121     def test_bad_data_short(self):
00122         """
00123         if a response doesn't match the specification's layout, 
00124         _split_response should raise an exception
00125         """
00126         # Under length
00127         data = '\x8a'
00128         self.assertRaises(ValueError, self.xbee._split_response, data)
00129     
00130     def test_split_status_response(self):
00131         """
00132         _split_response should properly split a status response packet
00133         """
00134         data = '\x8a\x01'
00135         
00136         info = self.xbee._split_response(data)
00137         expected_info = {'id':'status',
00138                          'status':'\x01'}
00139         
00140         self.assertEqual(info, expected_info)
00141         
00142     def test_split_short_at_response(self):
00143         """
00144         _split_response should properly split an at_response packet which
00145         has no parameter data
00146         """
00147         
00148         data = '\x88DMY\x01'
00149         info = self.xbee._split_response(data)
00150         expected_info = {'id':'at_response',
00151                          'frame_id':'D',
00152                          'command':'MY',
00153                          'status':'\x01'}
00154         self.assertEqual(info, expected_info)
00155         
00156     def test_split_at_resp_with_param(self):
00157         """
00158         _split_response should properly split an at_response packet which
00159         has parameter data
00160         """
00161         
00162         data = '\x88DMY\x01ABCDEF'
00163         info = self.xbee._split_response(data)
00164         expected_info = {'id':'at_response',
00165                          'frame_id':'D',
00166                          'command':'MY',
00167                          'status':'\x01',
00168                          'parameter':'ABCDEF'}
00169         self.assertEqual(info, expected_info)
00170         
00171         
00172 class TestParseIOData(InitXBee):
00173     """
00174     XBee class should properly parse IO data received from an XBee 
00175     device
00176     """
00177     
00178     def test_parse_single_dio(self):
00179         """
00180         _parse_samples should properly parse a packet containing a single 
00181         sample of only digital io data
00182         """
00183         # One sample, ADC disabled and DIO8 enabled, DIO 0-7 enabled
00184         header = '\x01\x01\xFF'
00185         
00186         # First 7 bits ignored, DIO8 high, DIO 0-7 high
00187         sample = '\x01\xFF'
00188         data = header + sample
00189         
00190         expected_results = [{'dio-0':True,
00191                              'dio-1':True,
00192                              'dio-2':True,
00193                              'dio-3':True,
00194                              'dio-4':True,
00195                              'dio-5':True,
00196                              'dio-6':True,
00197                              'dio-7':True,
00198                              'dio-8':True}]
00199                              
00200         results = self.xbee._parse_samples(data)
00201         
00202         self.assertEqual(results, expected_results)
00203         
00204     def test_parse_single_dio_again(self):
00205         """
00206         _parse_samples should properly parse a packet containing a single 
00207         sample of only digital io data, which alternates between on and 
00208         off
00209         """
00210         # One sample, ADC disabled and DIO8 enabled, DIO 0-7 enabled
00211         header = '\x01\x01\xFF'
00212         
00213         # First 7 bits ignored, DIO8 low, DIO 0-7 alternating
00214         sample = '\x00\xAA'
00215         data = header + sample
00216         
00217         expected_results = [{'dio-0':False,
00218                              'dio-1':True,
00219                              'dio-2':False,
00220                              'dio-3':True,
00221                              'dio-4':False,
00222                              'dio-5':True,
00223                              'dio-6':False,
00224                              'dio-7':True,
00225                              'dio-8':False}]
00226                              
00227         results = self.xbee._parse_samples(data)
00228         
00229         self.assertEqual(results, expected_results)
00230         
00231     def test_parse_single_dio_subset(self):
00232         """
00233         _parse_samples should properly parse a packet containing a single 
00234         sample of only digital io data for only a subset of the 
00235         available pins
00236         """
00237         # One sample, ADC disabled
00238         # DIO 1,3,5,7 enabled
00239         header = '\x01\x00\xAA'
00240         
00241         # First 7 bits ignored, DIO8 low, DIO 0-7 alternating
00242         sample = '\x00\xAA'
00243         data = header + sample
00244         
00245         expected_results = [{'dio-1':True,
00246                              'dio-3':True,
00247                              'dio-5':True,
00248                              'dio-7':True}]
00249                              
00250         results = self.xbee._parse_samples(data)
00251         
00252         self.assertEqual(results, expected_results)
00253         
00254     def test_parse_single_dio_subset_again(self):
00255         """
00256         _parse_samples should properly parse a packet containing a single 
00257         sample of only digital io data for only a subset of the 
00258         available pins
00259         """
00260         # One sample, ADC disabled
00261         # DIO 0 enabled
00262         header = '\x01\x00\x01'
00263         
00264         # First 7 bits ignored, DIO8 low, DIO 0-7 alternating
00265         sample = '\x00\xAA'
00266         data = header + sample
00267         
00268         expected_results = [{'dio-0':False}]
00269                              
00270         results = self.xbee._parse_samples(data)
00271         
00272         self.assertEqual(results, expected_results)
00273         
00274     def test_parse_multiple_dio_subset(self):
00275         """
00276         _parse_samples should properly parse a packet containing two 
00277         samples of only digital io data for one dio line
00278         """
00279         # Two samples, ADC disabled
00280         # DIO 0 enabled
00281         header = '\x02\x00\x01'
00282         
00283         # First 7 bits ignored, DIO8 low, DIO 0-7 alternating
00284         sample = '\x00\xAA' + '\x00\x01'
00285         data = header + sample
00286         
00287         expected_results = [{'dio-0':False},
00288                             {'dio-0':True}]
00289                              
00290         results = self.xbee._parse_samples(data)
00291         
00292         self.assertEqual(results, expected_results)
00293         
00294     def test_parse_multiple_dio(self):
00295         """
00296         _parse_samples should properly parse a packet containing three 
00297         samples of only digital io data
00298         """
00299         # Three samples, ADC disabled and DIO8 enabled, DIO 0-7 enabled
00300         header = '\x03\x01\xFF'
00301         
00302         # First 7 bits ignored
00303         # First sample: all bits on
00304         # Second sample: alternating bits on
00305         # Third sample: all bits off
00306         sample = '\x01\xFF' + '\x00\xAA' + '\x00\x00'
00307         data = header + sample
00308         
00309         expected_results = [{'dio-0':True,
00310                              'dio-1':True,
00311                              'dio-2':True,
00312                              'dio-3':True,
00313                              'dio-4':True,
00314                              'dio-5':True,
00315                              'dio-6':True,
00316                              'dio-7':True,
00317                              'dio-8':True},
00318                              {'dio-0':False,
00319                              'dio-1':True,
00320                              'dio-2':False,
00321                              'dio-3':True,
00322                              'dio-4':False,
00323                              'dio-5':True,
00324                              'dio-6':False,
00325                              'dio-7':True,
00326                              'dio-8':False},
00327                              {'dio-0':False,
00328                              'dio-1':False,
00329                              'dio-2':False,
00330                              'dio-3':False,
00331                              'dio-4':False,
00332                              'dio-5':False,
00333                              'dio-6':False,
00334                              'dio-7':False,
00335                              'dio-8':False}]
00336                              
00337         results = self.xbee._parse_samples(data)
00338         
00339         self.assertEqual(results, expected_results)
00340         
00341     def test_parse_multiple_adc_subset(self):
00342         """
00343         _parse_samples should parse a data packet containing multiple
00344         samples of adc data from multiple pins in the proper order
00345         """
00346         # One sample, ADC 0,1 enabled
00347         # DIO disabled
00348         header = '\x02\x06\x00'
00349         
00350         # No dio data
00351         # ADC0 value of 0
00352         # ADC1 value of 255
00353         # ADC0 value of 5
00354         # ADC1 value of 7
00355         sample = '\x00\x00' + '\x00\xFF' + '\x00\x05' + '\x00\x07'
00356         data = header + sample
00357         
00358         expected_results = [{'adc-0':0,
00359                              'adc-1':255},
00360                             {'adc-0':5,
00361                              'adc-1':7}]
00362         
00363         results = self.xbee._parse_samples(data)
00364         
00365         self.assertEqual(results, expected_results)
00366         
00367     def test_parse_single_dio_adc_subset(self):
00368         """
00369         _parse_samples should properly parse a packet containing a single 
00370         sample of digital and analog io data for only a subset of the 
00371         available pins
00372         """
00373         # One sample, ADC 0 enabled
00374         # DIO 1,3,5,7 enabled
00375         header = '\x01\x02\xAA'
00376         
00377         # First 7 bits ignored, DIO8 low, DIO 0-7 alternating
00378         # ADC0 value of 255
00379         sample = '\x00\xAA\x00\xFF'
00380         data = header + sample
00381         
00382         expected_results = [{'dio-1':True,
00383                              'dio-3':True,
00384                              'dio-5':True,
00385                              'dio-7':True,
00386                              'adc-0':255}]
00387                              
00388         results = self.xbee._parse_samples(data)
00389         
00390         self.assertEqual(results, expected_results)
00391 
00392 class TestWriteToDevice(unittest.TestCase):
00393     """
00394     XBee class should properly write binary data in a valid API
00395     frame to a given serial device, including a valid command packet.
00396     """
00397     
00398     def test_send_at_command(self):
00399         """
00400         calling send should write a full API frame containing the
00401         API AT command packet to the serial device.
00402         """
00403         
00404         serial_port = FakeDevice()
00405         xbee = XBee(serial_port)
00406         
00407         # Send an AT command
00408         xbee.send('at', frame_id='A', command='MY')
00409         
00410         # Expect a full packet to be written to the device
00411         expected_data = '\x7E\x00\x04\x08AMY\x10'
00412         self.assertEqual(serial_port.data, expected_data)
00413         
00414         
00415     def test_send_at_command_with_param(self):
00416         """
00417         calling send should write a full API frame containing the
00418         API AT command packet to the serial device.
00419         """
00420         
00421         serial_port = FakeDevice()
00422         xbee = XBee(serial_port)
00423         
00424         # Send an AT command
00425         xbee.send(
00426             'at', 
00427             frame_id='A', 
00428             command='MY', 
00429             parameter='\x00\x00'
00430         )
00431         
00432         # Expect a full packet to be written to the device
00433         expected_data = '\x7E\x00\x06\x08AMY\x00\x00\x10'
00434         self.assertEqual(serial_port.data, expected_data)
00435         
00436 class TestSendShorthand(unittest.TestCase):
00437     """
00438     Tests shorthand for sending commands to an XBee provided by
00439     XBee.__getattr__
00440     """
00441     
00442     def setUp(self):
00443         """
00444         Prepare a fake device to read from
00445         """
00446         self.ser = FakeDevice()
00447         self.xbee = XBee(self.ser)
00448     
00449     def test_send_at_command(self):
00450         """
00451         Send an AT command with a shorthand call
00452         """
00453         # Send an AT command
00454         self.xbee.at(frame_id='A', command='MY')
00455         
00456         # Expect a full packet to be written to the device
00457         expected_data = '\x7E\x00\x04\x08AMY\x10'
00458         self.assertEqual(self.ser.data, expected_data)
00459         
00460     def test_send_at_command_with_param(self):
00461         """
00462         calling send should write a full API frame containing the
00463         API AT command packet to the serial device.
00464         """
00465         
00466         # Send an AT command
00467         self.xbee.at(frame_id='A', command='MY', parameter='\x00\x00')
00468         
00469         # Expect a full packet to be written to the device
00470         expected_data = '\x7E\x00\x06\x08AMY\x00\x00\x10'
00471         self.assertEqual(self.ser.data, expected_data)
00472         
00473     def test_shorthand_disabled(self):
00474         """
00475         When shorthand is disabled, any attempt at calling a 
00476         non-existant attribute should raise AttributeError
00477         """
00478         self.xbee = XBee(self.ser, shorthand=False)
00479         
00480         try:
00481             self.xbee.at
00482         except AttributeError:
00483             pass
00484         else:
00485             self.fail("Specified shorthand command should not exist")
00486 
00487 class TestReadFromDevice(unittest.TestCase):
00488     """
00489     XBee class should properly read and parse binary data from a serial 
00490     port device.
00491     """
00492     def test_read_at(self):
00493         """
00494         read and parse a parameterless AT command
00495         """
00496         device = FakeReadDevice('\x7E\x00\x05\x88DMY\x01\x8c')
00497         xbee = XBee(device)
00498         
00499         info = xbee.wait_read_frame()
00500         expected_info = {'id':'at_response',
00501                          'frame_id':'D',
00502                          'command':'MY',
00503                          'status':'\x01'}
00504         self.assertEqual(info, expected_info)
00505         
00506     def test_read_at_params(self):
00507         """
00508         read and parse an AT command with a parameter
00509         """
00510         device = FakeReadDevice(
00511             '\x7E\x00\x08\x88DMY\x01\x00\x00\x00\x8c'
00512         )
00513         xbee = XBee(device)
00514         
00515         info = xbee.wait_read_frame()
00516         expected_info = {'id':'at_response',
00517                          'frame_id':'D',
00518                          'command':'MY',
00519                          'status':'\x01',
00520                          'parameter':'\x00\x00\x00'}
00521         self.assertEqual(info, expected_info)
00522         
00523     def test_read_io_data(self):
00524         """
00525         XBee class should properly read and parse incoming IO data
00526         """
00527         ## Build IO data
00528         # One sample, ADC 0 enabled
00529         # DIO 1,3,5,7 enabled
00530         header = '\x01\x02\xAA'
00531         
00532         # First 7 bits ignored, DIO8 low, DIO 0-7 alternating
00533         # ADC0 value of 255
00534         sample = '\x00\xAA\x00\xFF'
00535         data = header + sample
00536         
00537         ## Wrap data in frame
00538         # RX frame data
00539         rx_io_resp = '\x83\x00\x01\x28\x00'
00540     
00541         device = FakeReadDevice(
00542             '\x7E\x00\x0C'+ rx_io_resp + data + '\xfd'
00543         )
00544         xbee = XBee(device)
00545         
00546         info = xbee.wait_read_frame()
00547         expected_info = {'id':'rx_io_data',
00548                          'source_addr':'\x00\x01',
00549                          'rssi':'\x28',
00550                          'options':'\x00',
00551                          'samples': [{'dio-1':True,
00552                                       'dio-3':True,
00553                                       'dio-5':True,
00554                                       'dio-7':True,
00555                                       'adc-0':255}]
00556                         }
00557         self.assertEqual(info, expected_info)
00558                     
00559 
00560 if __name__ == '__main__':
00561     unittest.main()


rosserial_xbee
Author(s): Adam Stambler
autogenerated on Sat Oct 7 2017 03:09:02