@@ -33,8 +33,9 @@ def test_close(api):
3333def test_commands ():
3434 import string
3535 anum = string .ascii_letters + string .digits + '_'
36+ commands = {** xbee_api .COMMAND_REQUESTS , ** xbee_api .COMMAND_RESPONSES }
3637
37- for cmd_name , cmd_opts in xbee_api . COMMANDS .items ():
38+ for cmd_name , cmd_opts in commands .items ():
3839 assert isinstance (cmd_name , str ) is True
3940 assert all ([c in anum for c in cmd_name ]), cmd_name
4041 assert len (cmd_opts ) == 3
@@ -47,12 +48,12 @@ def test_commands():
4748@pytest .mark .asyncio
4849async def test_command (api ):
4950 def mock_api_frame (name , * args ):
50- c = xbee_api .COMMANDS [name ]
51+ c = xbee_api .COMMAND_REQUESTS [name ]
5152 return mock .sentinel .api_frame_data , c [2 ]
5253 api ._api_frame = mock .MagicMock (side_effect = mock_api_frame )
5354 api ._uart .send = mock .MagicMock ()
5455
55- for cmd_name , cmd_opts in xbee_api .COMMANDS .items ():
56+ for cmd_name , cmd_opts in xbee_api .COMMAND_REQUESTS .items ():
5657 cmd_id , schema , expect_reply = cmd_opts
5758 ret = api ._command (cmd_name , mock .sentinel .cmd_data )
5859 if expect_reply :
@@ -62,28 +63,30 @@ def mock_api_frame(name, *args):
6263 assert ret is None
6364 assert api ._api_frame .call_count == 1
6465 assert api ._api_frame .call_args [0 ][0 ] == cmd_name
65- assert api ._api_frame .call_args [0 ][1 ] == mock .sentinel .cmd_data
66+ assert api ._api_frame .call_args [0 ][1 ] == api ._seq - 1
67+ assert api ._api_frame .call_args [0 ][2 ] == mock .sentinel .cmd_data
6668 assert api ._uart .send .call_count == 1
6769 assert api ._uart .send .call_args [0 ][0 ] == mock .sentinel .api_frame_data
6870 api ._api_frame .reset_mock ()
6971 api ._uart .send .reset_mock ()
7072
71-
72- def test_seq_command (api ):
73- api ._command = mock .MagicMock ()
74- api ._seq = mock .sentinel .seq
75- api ._seq_command (mock .sentinel .cmd_name , mock .sentinel .args )
76- assert api ._command .call_count == 1
77- assert api ._command .call_args [0 ][0 ] == mock .sentinel .cmd_name
78- assert api ._command .call_args [0 ][1 ] == mock .sentinel .seq
79- assert api ._command .call_args [0 ][2 ] == mock .sentinel .args
73+ ret = api ._command (cmd_name , mock .sentinel .cmd_data , mask_frame_id = True )
74+ assert ret is None
75+ assert api ._api_frame .call_count == 1
76+ assert api ._api_frame .call_args [0 ][0 ] == cmd_name
77+ assert api ._api_frame .call_args [0 ][1 ] == 0
78+ assert api ._api_frame .call_args [0 ][2 ] == mock .sentinel .cmd_data
79+ assert api ._uart .send .call_count == 1
80+ assert api ._uart .send .call_args [0 ][0 ] == mock .sentinel .api_frame_data
81+ api ._api_frame .reset_mock ()
82+ api ._uart .send .reset_mock ()
8083
8184
8285async def _test_at_or_queued_at_command (api , cmd , monkeypatch , do_reply = True ):
8386 monkeypatch .setattr (t , 'serialize' , mock .MagicMock (return_value = mock .sentinel .serialize ))
8487
8588 def mock_command (name , * args ):
86- rsp = xbee_api .COMMANDS [name ][2 ]
89+ rsp = xbee_api .COMMAND_REQUESTS [name ][2 ]
8790 ret = None
8891 if rsp :
8992 ret = asyncio .Future ()
@@ -99,9 +102,8 @@ def mock_command(name, *args):
99102 assert t .serialize .call_count == 1
100103 assert api ._command .call_count == 1
101104 assert api ._command .call_args [0 ][0 ] in ('at' , 'queued_at' )
102- assert api ._command .call_args [0 ][1 ] == mock .sentinel .seq
103- assert api ._command .call_args [0 ][2 ] == at_cmd .encode ('ascii' )
104- assert api ._command .call_args [0 ][3 ] == mock .sentinel .serialize
105+ assert api ._command .call_args [0 ][1 ] == at_cmd .encode ('ascii' )
106+ assert api ._command .call_args [0 ][2 ] == mock .sentinel .serialize
105107 assert res == mock .sentinel .at_result
106108 t .serialize .reset_mock ()
107109 api ._command .reset_mock ()
@@ -125,23 +127,79 @@ async def test_queued_at_command(api, monkeypatch):
125127 await _test_at_or_queued_at_command (api , api ._queued_at , monkeypatch )
126128
127129
130+ async def _test_remote_at_command (api , monkeypatch , do_reply = True ):
131+ monkeypatch .setattr (t , 'serialize' , mock .MagicMock (return_value = mock .sentinel .serialize ))
132+
133+ def mock_command (name , * args ):
134+ rsp = xbee_api .COMMAND_REQUESTS [name ][2 ]
135+ ret = None
136+ if rsp :
137+ ret = asyncio .Future ()
138+ if do_reply :
139+ ret .set_result (mock .sentinel .at_result )
140+ return ret
141+
142+ api ._command = mock .MagicMock (side_effect = mock_command )
143+ api ._seq = mock .sentinel .seq
144+
145+ for at_cmd in xbee_api .AT_COMMANDS :
146+ res = await api ._remote_at_command (
147+ mock .sentinel .ieee , mock .sentinel .nwk , mock .sentinel .opts , at_cmd ,
148+ mock .sentinel .args )
149+ assert t .serialize .call_count == 1
150+ assert api ._command .call_count == 1
151+ assert api ._command .call_args [0 ][0 ] == 'remote_at'
152+ assert api ._command .call_args [0 ][1 ] == mock .sentinel .ieee
153+ assert api ._command .call_args [0 ][2 ] == mock .sentinel .nwk
154+ assert api ._command .call_args [0 ][3 ] == mock .sentinel .opts
155+ assert api ._command .call_args [0 ][4 ] == at_cmd .encode ('ascii' )
156+ assert api ._command .call_args [0 ][5 ] == mock .sentinel .serialize
157+ assert res == mock .sentinel .at_result
158+ t .serialize .reset_mock ()
159+ api ._command .reset_mock ()
160+
161+
162+ @pytest .mark .asyncio
163+ async def test_remote_at_cmd (api , monkeypatch ):
164+ await _test_remote_at_command (api , monkeypatch )
165+
166+
167+ @pytest .mark .asyncio
168+ async def test_remote_at_cmd_no_rsp (api , monkeypatch ):
169+ monkeypatch .setattr (xbee_api , 'REMOTE_AT_COMMAND_TIMEOUT' , 0.1 )
170+ with pytest .raises (asyncio .TimeoutError ):
171+ await _test_remote_at_command (api , monkeypatch , do_reply = False )
172+
173+
128174def test_api_frame (api ):
129175 ieee = t .EUI64 ([t .uint8_t (a ) for a in range (0 , 8 )])
130- for cmd_name , cmd_opts in xbee_api .COMMANDS .items ():
176+ for cmd_name , cmd_opts in xbee_api .COMMAND_REQUESTS .items ():
131177 cmd_id , schema , repl = cmd_opts
132178 if schema :
133- args = [ieee if isinstance ( a () , t .EUI64 ) else a () for a in schema ]
179+ args = [ieee if issubclass ( a , t .EUI64 ) else a () for a in schema ]
134180 frame , repl = api ._api_frame (cmd_name , * args )
135181 else :
136182 frame , repl = api ._api_frame (cmd_name )
137183
138184
139185def test_frame_received (api , monkeypatch ):
140186 monkeypatch .setattr (t , 'deserialize' , mock .MagicMock (
141- return_value = (mock .sentinel .deserialize_data , b'' )))
187+ return_value = (
188+ [
189+ mock .sentinel .arg_0 ,
190+ mock .sentinel .arg_1 ,
191+ mock .sentinel .arg_2 ,
192+ mock .sentinel .arg_3 ,
193+ mock .sentinel .arg_4 ,
194+ mock .sentinel .arg_5 ,
195+ mock .sentinel .arg_6 ,
196+ mock .sentinel .arg_7 ,
197+ mock .sentinel .arg_8 ,
198+ ], b'' )
199+ ))
142200 my_handler = mock .MagicMock ()
143201
144- for cmd , cmd_opts in xbee_api .COMMANDS .items ():
202+ for cmd , cmd_opts in xbee_api .COMMAND_RESPONSES .items ():
145203 cmd_id = cmd_opts [0 ]
146204 payload = b'\x01 \x02 \x03 \x04 '
147205 data = cmd_id .to_bytes (1 , 'big' ) + payload
@@ -150,7 +208,10 @@ def test_frame_received(api, monkeypatch):
150208 assert t .deserialize .call_count == 1
151209 assert t .deserialize .call_args [0 ][0 ] == payload
152210 assert my_handler .call_count == 1
153- assert my_handler .call_args [0 ][0 ] == mock .sentinel .deserialize_data
211+ assert my_handler .call_args [0 ][0 ] == mock .sentinel .arg_0
212+ assert my_handler .call_args [0 ][1 ] == mock .sentinel .arg_1
213+ assert my_handler .call_args [0 ][2 ] == mock .sentinel .arg_2
214+ assert my_handler .call_args [0 ][3 ] == mock .sentinel .arg_3
154215 t .deserialize .reset_mock ()
155216 my_handler .reset_mock ()
156217
@@ -161,10 +222,10 @@ def test_frame_received_no_handler(api, monkeypatch):
161222 my_handler = mock .MagicMock ()
162223 cmd = 'no_handler'
163224 cmd_id = 0x00
164- xbee_api .COMMANDS [cmd ] = (cmd_id , (), None )
225+ xbee_api .COMMAND_RESPONSES [cmd ] = (cmd_id , (), None )
165226 api ._commands_by_id [cmd_id ] = cmd
166227
167- cmd_opts = xbee_api .COMMANDS [cmd ]
228+ cmd_opts = xbee_api .COMMAND_RESPONSES [cmd ]
168229 cmd_id = cmd_opts [0 ]
169230 payload = b'\x01 \x02 \x03 \x04 '
170231 data = cmd_id .to_bytes (1 , 'big' ) + payload
@@ -178,7 +239,7 @@ def _handle_at_response(api, tsn, status, at_response=b''):
178239 data = (tsn , 'AI' .encode ('ascii' ), status , at_response )
179240 response = asyncio .Future ()
180241 api ._awaiting [tsn ] = (response , )
181- api ._handle_at_response (data )
242+ api ._handle_at_response (* data )
182243 return response
183244
184245
@@ -215,9 +276,21 @@ def test_handle_at_response_undef_error(api):
215276 assert fut .exception () is not None
216277
217278
279+ def test_handle_remote_at_rsp (api ):
280+ api ._handle_at_response = mock .MagicMock ()
281+ s = mock .sentinel
282+ api ._handle_remote_at_response (s .frame_id , s .ieee , s .nwk , s .cmd ,
283+ s .status , s .data )
284+ assert api ._handle_at_response .call_count == 1
285+ assert api ._handle_at_response .call_args [0 ][0 ] == s .frame_id
286+ assert api ._handle_at_response .call_args [0 ][1 ] == s .cmd
287+ assert api ._handle_at_response .call_args [0 ][2 ] == s .status
288+ assert api ._handle_at_response .call_args [0 ][3 ] == s .data
289+
290+
218291def _send_modem_event (api , event ):
219292 api ._app = mock .MagicMock (spec = ControllerApplication )
220- api ._handle_modem_status ([ event ] )
293+ api ._handle_modem_status (event )
221294 assert api ._app .handle_modem_status .call_count == 1
222295 assert api ._app .handle_modem_status .call_args [0 ][0 ] == event
223296
@@ -243,15 +316,60 @@ def test_handle_modem_status(api):
243316
244317
245318def test_handle_explicit_rx_indicator (api ):
246- data = b'\x00 \x01 \x02 \x03 \x04 \x05 \x06 \x07 '
319+ s = mock .sentinel
320+ data = [s .src_ieee , s .src_nwk , s .src_ep , s .dst_ep , s .cluster_id , s .profile ,
321+ s .opts , b'abcdef' ]
247322 api ._app = mock .MagicMock ()
248323 api ._app .handle_rx = mock .MagicMock ()
249- api ._handle_explicit_rx_indicator (data )
324+ api ._handle_explicit_rx_indicator (* data )
250325 assert api ._app .handle_rx .call_count == 1
251326
252327
253- def test_handle_tx_status (api ):
254- api ._handle_tx_status (b'\x01 \x02 \x03 \x04 ' )
328+ def _handle_tx_status (api , status , wrong_frame_id = False ):
329+ status = t .TXStatus (status )
330+ frame_id = 0x12
331+ send_fut = mock .MagicMock (spec = asyncio .Future )
332+ api ._awaiting [frame_id ] = (send_fut , )
333+ s = mock .sentinel
334+ if wrong_frame_id :
335+ frame_id += 1
336+ api ._handle_tx_status (frame_id , s .dst_nwk , s .retries , status ,
337+ t .DiscoveryStatus ())
338+ return send_fut
339+
340+
341+ def test_handle_tx_status_success (api ):
342+ fut = _handle_tx_status (api , t .TXStatus .SUCCESS )
343+ assert len (api ._awaiting ) == 0
344+ assert fut .set_result .call_count == 1
345+ assert fut .set_exception .call_count == 0
346+
347+
348+ def test_handle_tx_status_except (api ):
349+ fut = _handle_tx_status (api , t .TXStatus .ADDRESS_NOT_FOUND )
350+ assert len (api ._awaiting ) == 0
351+ assert fut .set_result .call_count == 0
352+ assert fut .set_exception .call_count == 1
353+
354+
355+ def test_handle_tx_status_unexpected (api ):
356+ fut = _handle_tx_status (api , 1 , wrong_frame_id = True )
357+ assert len (api ._awaiting ) == 1
358+ assert fut .set_result .call_count == 0
359+ assert fut .set_exception .call_count == 0
360+
361+
362+ def test_handle_tx_status_duplicate (api ):
363+ status = t .TXStatus .SUCCESS
364+ frame_id = 0x12
365+ send_fut = mock .MagicMock (spec = asyncio .Future )
366+ send_fut .set_result .side_effect = asyncio .InvalidStateError
367+ api ._awaiting [frame_id ] = (send_fut , )
368+ s = mock .sentinel
369+ api ._handle_tx_status (frame_id , s .dst_nwk , s .retries , status , s .disc )
370+ assert len (api ._awaiting ) == 0
371+ assert send_fut .set_result .call_count == 1
372+ assert send_fut .set_exception .call_count == 0
255373
256374
257375@pytest .mark .asyncio
@@ -364,10 +482,11 @@ def test_set_application(api):
364482
365483
366484def test_handle_route_record_indicator (api ):
367- api ._handle_route_record_indicator (mock .sentinel .ri )
485+ s = mock .sentinel
486+ api ._handle_route_record_indicator (s .ieee , s .src , s .rx_opts , s .hops )
368487
369488
370489def test_handle_many_to_one_rri (api ):
371490 ieee = t .EUI64 ([t .uint8_t (a ) for a in range (0 , 8 )])
372491 nwk = 0x1234
373- api ._handle_many_to_one_rri ([ ieee , nwk , 0 ] )
492+ api ._handle_many_to_one_rri (ieee , nwk , 0 )
0 commit comments