
    i(                        d Z ddlZddlmc mZ ddlZddlZddl	m
Z
mZmZ ddlZddlZddlmZ ddlmZ  G d d      Zej(                  d        Zd	ed
efdZej0                  j                  d        Zej0                  j                  d        Zej0                  j                  d        Zej0                  j                  d        Zej0                  j                  d        Zy)z1Tests for the FR loop and /fr WebSocket endpoint.    N)	AsyncMock	MagicMockpatch)FRLoop)EdgeProxyServerc                   <   e Zd ZdZd
dedz  fdZej                  j                  d        Z	ej                  j                  d        Z
ej                  j                  d        Zej                  j                  d        Zej                  j                  d	        Zy)
TestFRLoopz=Unit tests for FRLoop with mocked FrameCapture and FR server.frameNc                 <    t               }||j                  _        |S N)r   capturereturn_value)selfr
   fcs      @/home/nelsen/Projects/kognitive/edge-proxy/tests/test_fr_loop.py_make_frame_capturezTestFRLoop._make_frame_capture   s    ["'

	    c                   K   | j                         g fd}t        j                  ddg ddgdd      }t        dd	
      j	                  |       t               }t               |_        t        |      |_        dfd}t        j                  d|      5  t        d      5 }t        |      |j                  _        t        d      |j                  _        j                          d{    t        j                  d       d{    ddd       ddd       t!              }d}||k(  }|st#        j$                  d|fd||f      dt'        j(                         v st#        j*                  t               rt#        j,                  t               nddt'        j(                         v st#        j*                        rt#        j,                        ndt#        j,                  |      t#        j,                  |      dz  }	dd|	iz  }
t/        t#        j0                  |
            dx}x}}d   d   d   }d}||k(  }|slt#        j$                  d|fd||f      t#        j,                  |      t#        j,                  |      dz  }dd|iz  }	t/        t#        j0                  |	            dx}x}}y7 7 # 1 sw Y   xY w# 1 sw Y   xY ww) z:FRLoop should invoke registered callbacks with detections.c                 0   K   j                  |        y wr   append)detsdetections_receiveds    r   on_detz<TestFRLoop.test_callback_receives_detections.<locals>.on_det!   s     &&t,s   Alicegffffff?
      d      identity
confidencebboxg  eA)
detections	timestampws://localhost:9999/r   frame_capturefr_endpointfpsr   r   c                    K   j                         }|5j                  | |       d {   }|j                  |       d {    dz  j                          y 7 57 w)N   )r   _send_to_fr_notifystop)wsr
   r   
call_countr   loops      r   fake_capture_loopzGTestFRLoop.test_callback_receives_detections.<locals>.fake_capture_loop3   sb      JJLE !--b%88#,,t,,,!OJIIK	 9,s!   (A%A!A%A#A%#A%_capture_loopz%edge_proxy.fr_loop.websockets.connectFNg?r.   ==z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenr   py0py1py3py6assert %(py8)spy8r"   z%(py1)s == %(py4)sr=   py4assert %(py6)sr?   )r   jsondumpsr   on_detectionsr   sendrecvr   objectr   
__aenter__	__aexit__startasynciosleepr:   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanation)r   r   fr_responsemock_wsr5   mock_connect@py_assert2@py_assert5@py_assert4@py_format7@py_format9@py_assert0@py_assert3@py_format5r3   r   r   r4   s                 @@@@r   !test_callback_receives_detectionsz,TestFRLoop.test_callback_receives_detections   s     %%' 	- jj(/tM_`a%"
 
 B4JPST6" + { k:
		 \\$1BC 	)>? )<7@g7V))46?U6S))3jjl""mmC((()	) &',1,'1,,,,'1,,,,,,s,,,s,,,,,,&,,,&,,,',,,1,,,,,,,"1%a(4??4????4???4?????????? #() )	) 	)s\   B K%&K2A
K<K=KKKK&FK%KKK	KK"K%c                   K   | j                  d      }t               }t        |dd      }|j                  |       t	        j
                  |dt              5 }t               }d|_        t        j                         }|j                  d|j                         d{   }d}||u }	|	st        j                  d	|	fd
||f      dt        j                         v st        j                  |      rt        j                   |      ndt        j                   |      dz  }
dd|
iz  }t#        t        j$                  |            dx}	}|j'                          ddd       y7 # 1 sw Y   yxY ww)z4FRLoop should skip frames when capture returns None.N)r
   r'   r   r(   r/   )new_callableTisz%(py0)s is %(py3)sr
   r<   r>   assert %(py5)spy5)r   r   r   rH   r   rK   _runningrO   get_event_looprun_in_executorr   rQ   rR   rS   rT   rU   rV   rW   rX   assert_not_called)r   r   callbackr4   	mock_sendrZ   eloopr
   r\   @py_assert1@py_format4@py_format6s               r   "test_skip_frame_on_capture_failurez-TestFRLoop.test_skip_frame_on_capture_failureI   s     %%D%1;B4JPST8$ \\$IF 		*)kG DM **,E//bjjAAE  5D=   5D      5   5   D       '')		* 		* B		* 		*s2   AE0AE$E" B9E$	E0"E$$E-)E0c           	      J  K   | j                         }t        |dd      }t               }t        t        j                  dddg ddgi      	      |_        |j                  |d
       d{   }|j                  j                  d
       t        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t!        t        j"                  |	            dx}x}}|d   d   }
d}|
|u }|slt        j                  d|fd|
|f      t        j                  |
      t        j                  |      dz  }dd|iz  }t!        t        j"                  |            dx}
x}}y7 w)z<_send_to_fr should send binary JPEG and parse JSON response.r'      r(   r%   Ng333333?)r   r   2   rz   r!   r,   s   jpeg_datar.   r7   r9   r:   resultr;   r@   rA   r   r"   rg   )z%(py1)s is %(py4)srC   rE   r?   )r   r   r   rF   rG   rJ   r/   rI   assert_called_once_withr:   rQ   rR   rS   rT   rU   rV   rW   rX   )r   r   r4   rZ   r{   r\   r]   r^   r_   r`   ra   rb   rc   s                r   test_send_to_fr_sends_binaryz'TestFRLoop.test_send_to_fr_sends_binary^   sk     %%'B4JPQR+ djjCXY:
 /  ''1EFF,,-AB6{a{a{ass66{aay$,,$,,,,$,,,$,,,,,,,,,,	 Gs   A+H#-H .F3H#c                 t  K   | j                         }t        |dd      }d }t        j                  |d|      5  |j	                          d{    |j
                  }d}||u }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j                  }d}||u}|st        j                  d|fd||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j                          |j
                  }d}||u }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}t!        j"                  d       d{    |j                  }|j$                  } |       }	|	sdd
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      t        j                  |	      dz  }
t        t        j                  |
            dx}x}}	ddd       y7 X7 # 1 sw Y   yxY ww)z)stop() should cancel the background task.r'   ry   r(   c                  J   K   t        j                  d       d {    y 7 w)Ni  )rO   rP    r   r   fake_runz3TestFRLoop.test_stop_cancels_task.<locals>.fake_runv   s     --$$$s   #!#_runNTrg   )z0%(py2)s
{%(py2)s = %(py0)s._running
} is %(py5)sr4   )r<   py2rl   zassert %(py7)spy7is not)z1%(py2)s
{%(py2)s = %(py0)s._task
} is not %(py5)sF皙?zZassert %(py6)s
{%(py6)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s._task
}.done
}()
})r<   r   rD   r?   )r   r   r   rK   rN   rm   rQ   rR   rS   rT   rU   rV   rW   rX   _taskr1   rO   rP   done)r   r   r4   r   rt   r^   rb   rv   @py_format8r]   r_   s              r   test_stop_cancels_taskz!TestFRLoop.test_stop_cancels_tasko   s?     %%'B4JPQR	% \\$1 		%**,==(D(=D((((=D((((((4(((4(((=(((D(((((((::)T):T)))):T))))))4)))4))):)))T)))))))IIK==)E)=E))))=E))))))4)))4)))=)))E)))))))--%%%::$:??$?$$$$$$$$4$$$4$$$:$$$?$$$$$$$$$$		% 		% &		% 		%sB   9N8N,N'JN,N*CN,	N8'N,*N,,N51N8c                   K   | j                         }t        |dd      }g fd}|j                  |       |j                  ddg ddg       d	{    t	              }d
}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                        rt        j                        ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}d   d   d   }	d}
|	|
k(  }|slt        j                  d|fd|	|
f      t        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            d	x}	x}}
y	7 w)z7on_detections should support synchronous callbacks too.r'   ry   r(   c                 (    j                  |        y r   r   )r   resultss    r   sync_cbz<TestFRLoop.test_on_detections_sync_callback.<locals>.sync_cb   s    NN4 r   Bobg?)r   r   r.   r.   r!   Nr.   r7   r9   r:   r   r;   r@   rA   r   r"   rB   rC   rE   r?   )r   r   rH   r0   r:   rQ   rR   rS   rT   rU   rV   rW   rX   )r   r   r4   r   r\   r]   r^   r_   r`   ra   rb   rc   r   s               @r    test_on_detections_sync_callbackz+TestFRLoop.test_on_detections_sync_callback   sI     %%'B4JPQR	! 	7#llc<XYZZZ7| q |q    |q      s   s      7   7   |   q       qz!}Z(1E1(E1111(E111(111E1111111 	[s   AG4G1FG4)s   fake_jpeg)__name__
__module____qualname____doc__bytesr   pytestmarkrO   rd   rw   r}   r   r   r   r   r   r	   r	      s    G 
 [[+@ +@Z [[* *( [[- -  [[% %( [[2 2r   r	   c                 D    | dz  }|j                  d       t        |      S )Nzwps.yamlz:waypoints:
  - name: lobby
    x: 0
    y: 0
    theta: 0
)
write_textstr)tmp_pathwps     r   waypoints_filer      s"    	J	BMMSTr7Nr   r   returnc           	          t        d      5 }t               }t               |_        t               |_        t               |_        ||_        t        ddddd|       }ddd       |S # 1 sw Y   S xY w)	z9Create an EdgeProxyServer with FR loop disabled (mocked).zedge_proxy.server.FRLoopz	127.0.0.1r   /edgez/healthmock)hostportws_pathhealth_pathbackendwaypoints_pathN)r   r   r   rN   r1   rH   r   r   )r   
MockFRLoopmock_frservers       r   _make_serverr      sr    	)	* 
j+! { )")
 !)

 M
 Ms   AA&&A0c                   K   t        |       }|j                  j                          d{    t        j                  |j
                  |j                  d|j                         d{   }	 |j                  d   j                         d   }t        j                  d| d      4 d{   }t        j                  d       d{    dd	g d
dg}|j                  |       d{    t        j                  t        j                   |j#                         d       d{         }|d   }d}||k(  }	|	slt%        j&                  d|	fd||f      t%        j(                  |      t%        j(                  |      dz  }
dd|
iz  }t+        t%        j,                  |            dx}x}	}|d   }t/        |      }d}||k(  }|st%        j&                  d|fd||f      dt1        j2                         v st%        j4                  t.              rt%        j(                  t.              ndt%        j(                  |      t%        j(                  |      t%        j(                  |      dz  }dd|iz  }t+        t%        j,                  |            dx}x}x}}|d   d   d   }d}||k(  }	|	slt%        j&                  d|	fd||f      t%        j(                  |      t%        j(                  |      dz  }
dd|
iz  }t+        t%        j,                  |            dx}x}	}d}||v }	|	st%        j&                  d|	fd||f      t%        j(                  |      dt1        j2                         v st%        j4                  |      rt%        j(                  |      ndd z  }d!d"|iz  }t+        t%        j,                  |            dx}}	ddd      d{    |j7                          |j9                          d{    y7 7 7 X7 >7 7 7 ?# 1 d{  7  sw Y   OxY w7 .# |j7                          |j9                          d{  7   w xY ww)#zBA client connected to /fr should receive fr_detections broadcasts.Nr   process_requestr.   ws://127.0.0.1:/frr   r   g?r   r!          @timeouttypefr_detectionsr7   rB   rC   rE   r?   r%   )z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)sr:   )r<   r   rD   r   zassert %(py9)spy9r"   r&   )in)z%(py1)s in %(py3)smsg)r=   r>   rk   rl   )r   _backendrN   
websocketsserve_route_handlerr   _process_requestsocketsgetsocknameconnectrO   rP   _broadcast_fr_detectionsrF   loadswait_forrJ   rQ   rR   rV   rW   rX   r:   rS   rT   rU   closewait_closed)r   r   	ws_serverr   r2   test_detectionsr   ra   rb   r\   rc   r_   rt   @py_assert6r]   r   @py_format10ru   rv   s                      r   $test_fr_endpoint_receives_detectionsr      s     .)F
//


!!! &&v{{A// I&  #//1!4%%vS&AB 	& 	&b--%%% -43PbcdO11/BBB**7#3#3BGGIs#KKLCv;1/1;/1111;/111;111/1111111<(.3().Q.)Q....)Q......3...3...(...)...Q.......|$Q'
3>w>3w>>>>3w>>>3>>>w>>>>>>>%;#%%%%;#%%%;%%%%%%#%%%#%%%%%%%	& 	& 	##%%%1 "	&% CK	& 	& 	& 	& 	& 	##%%%s   )QO?>Q*P+Q0=P* -P.P* 1P
P!P,P-;P(P
)JPP* PP* #Q9P(:QQP* PPPP* P%PP%!P* (Q*$QQQQc                   K   t        |       }|j                  j                          d{    t        j                  |j
                  |j                  d|j                         d{   }	 |j                  d   j                         d   }t        j                  d| d      4 d{   }t        j                  |j                         d       d{    t        j                  |j                         d       d{    d	d
g ddg}|j                  |       d{    t        j                         j!                         dz   }	 t        j                         j!                         |kD  rt#        d      t%        j&                  t        j                  |j                         d       d{         }|j)                  d      dk7  r|d   d   d   }d	}	||	k(  }
|
slt+        j,                  d|
fd||	f      t+        j.                  |      t+        j.                  |	      dz  }dd|iz  }t#        t+        j0                  |            dx}x}
}		 ddd      d{    |j3                          |j5                          d{    y7 n7 27 7 7 7 7 7 A# 1 d{  7  sw Y   QxY w7 0# |j3                          |j5                          d{  7   w xY ww)z;FR detections should be broadcast to /edge clients as well.Nr   r   r.   r   r   r   r   r   g333333?)ry   ry   rz   rz   r!   z+Timed out waiting for fr_detections messager   r   r%   r"   r7   rB   rC   rE   r?   )r   r   rN   r   r   r   r   r   r   r   r   rO   r   rJ   r   rn   timerW   rF   r   getrQ   rR   rV   rX   r   r   )r   r   r   r   r2   r   deadliner   ra   rb   r\   rc   r_   s                r   ,test_fr_detections_also_sent_to_edge_clientsr      s]     .)F
//


!!! &&v{{A// I&  #//1!4%%vU&CD 	 	""2779c:::""2779c:::,1~^_O11/BBB--/446<H))+002X=()VWWjjw'7'7	3'O!OP776?o5<(+J7@5@75@@@@75@@@7@@@5@@@@@@@!	 	$ 	##%%%9 "	:: C "P	 	 	 	& 	& 	##%%%s   )K4J>K4*J+K40=K -J.K 1)J/J",J/J%!J/)J(*BJ/;J+
<B$J/ K +J-,K 0#K4KK4K4K "J/%J/(J/+J/-K /K5J86K=K K4$K1*K-+K11K4c                 b  K   t        |       }|j                  j                          d{    t        j                  |j
                  |j                  d|j                         d{   }	 |j                  d   j                         d   }t        j                  d| d      4 d{   }|j                  d       d{    |j                  t        j                  dd	i             d{    t        j                  d
       d{    |j                   }|syddt#        j$                         v st'        j(                  |      rt'        j*                  |      ndt'        j*                  |      dz  }t-        t'        j.                  |            d}ddd      d{    |j1                          |j3                          d{    y7 7 q7 17 7 7 7 =# 1 d{  7  sw Y   MxY w7 ,# |j1                          |j3                          d{  7   w xY ww)z@The /fr endpoint should accept but ignore any incoming messages.Nr   r   r.   r   r   hellor   pingr   z(assert %(py2)s
{%(py2)s = %(py0)s.open
}r2   )r<   r   )r   r   rN   r   r   r   r   r   r   r   r   rI   rF   rG   rO   rP   openrS   rT   rQ   rU   rV   rW   rX   r   r   )r   r   r   r   r2   rt   @py_format3s          r   *test_fr_endpoint_ignores_incoming_messagesr      s     .)F
//


!!! &&v{{A// I&  #//1!4%%vS&AB 	 	b'''"""''$**ff%56777--%%%77N7NNNNN2NNN2NNN7NNNNNN	 	 	##%%%% "	"7%	 	 	 	 	& 	##%%%s   )H/G>H/*G+H/0=H -G.H 1G*G!-G*4G$5G*G&BG*H *G(+H /#H/G?H/H/H !G*$G*&G*(H *G<0G31G<8H ?H/$H,%H(&H,,H/c                   K   t        |       }|j                  di       }d}||u }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}yw)	z9_process_request should accept /fr path (not return 404).r   Nrg   ri   r{   rj   rk   rl   )
r   r   rQ   rR   rS   rT   rU   rV   rW   rX   )r   r   r{   r\   rt   ru   rv   s          r   $test_process_request_accepts_fr_pathr     s|      .)F$$UB/F6T>6T66Ts   CCc                   K   t        |       }|j                  di       }d}||u}|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}|d	   }|j                  }d
}||k(  }	|	st        j                  d|	fd||f      t        j                  |      t        j                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}x}	}yw)z6_process_request should reject unknown paths with 404.z/unknownNr   )z%(py0)s is not %(py3)sr{   rj   rk   rl   r   i  r7   )z-%(py3)s
{%(py3)s = %(py1)s.value
} == %(py6)s)r=   r>   r?   r@   rA   )r   r   rQ   rR   rS   rT   rU   rV   rW   rX   value)r   r   r{   r\   rt   ru   rv   ra   r]   r^   r_   r`   s               r   )test_process_request_rejects_unknown_pathr     s      .)F$$Z4F6666!9!9??!c!?c!!!!?c!!!9!!!?!!!c!!!!!!!s   E&E()r   builtinsrS   _pytest.assertion.rewrite	assertionrewriterQ   rO   rF   unittest.mockr   r   r   r   r   edge_proxy.fr_loopr   edge_proxy.serverr   r	   fixturer   r   r   r   r   r   r   r   r   r   r   r   <module>r      s    7     5 5   % -2 2N    ( & &< & &D & &0   " "r   