#!/usr/bin/env python3 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  unittest 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								from  cereal  import  car ,  log 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								from  common . realtime  import  DT_CTRL 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								from  selfdrive . car . car_helpers  import  interfaces 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								from  selfdrive . controls . controlsd  import  Controls ,  SOFT_DISABLE_TIME 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								from  selfdrive . controls . lib . events  import  Events ,  ET ,  Alert ,  Priority ,  AlertSize ,  AlertStatus ,  VisualAlert ,  \
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                          AudibleAlert ,  EVENTS 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								State  =  log . ControlsState . OpenpilotState 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								# The event types that maintain the current state 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								MAINTAIN_STATES  =  { State . enabled :  ( None , ) ,  State . disabled :  ( None , ) ,  State . softDisabling :  ( ET . SOFT_DISABLE , ) , 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                   State . preEnabled :  ( ET . PRE_ENABLE , ) ,  State . overriding :  ( ET . OVERRIDE_LATERAL ,  ET . OVERRIDE_LONGITUDINAL ) } 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								ALL_STATES  =  tuple ( State . schema . enumerants . values ( ) ) 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								# The event types checked in DISABLED section of state machine 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								ENABLE_EVENT_TYPES  =  ( ET . ENABLE ,  ET . PRE_ENABLE ,  ET . OVERRIDE_LATERAL ,  ET . OVERRIDE_LONGITUDINAL ) 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								def  make_event ( event_types ) : 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  event  =  { } 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  for  ev  in  event_types : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    event [ ev ]  =  Alert ( " " ,  " " ,  AlertStatus . normal ,  AlertSize . small ,  Priority . LOW , 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                      VisualAlert . none ,  AudibleAlert . none ,  1. ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  EVENTS [ 0 ]  =  event 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  return  0 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								class  TestStateMachine ( unittest . TestCase ) : 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  setUp ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    CarInterface ,  CarController ,  CarState  =  interfaces [ " mock " ] 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    CP  =  CarInterface . get_params ( " mock " ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    CI  =  CarInterface ( CP ,  CarController ,  CarState ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . controlsd  =  Controls ( CI = CI ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . controlsd . events  =  Events ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . controlsd . soft_disable_timer  =  int ( SOFT_DISABLE_TIME  /  DT_CTRL ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . CS  =  car . CarState ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  test_immediate_disable ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    for  state  in  ALL_STATES : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								      for  et  in  MAINTAIN_STATES [ state ] : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . controlsd . events . add ( make_event ( [ et ,  ET . IMMEDIATE_DISABLE ] ) ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . controlsd . state  =  state 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . controlsd . state_transition ( self . CS ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . assertEqual ( State . disabled ,  self . controlsd . state ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . controlsd . events . clear ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  test_user_disable ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    for  state  in  ALL_STATES : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								      for  et  in  MAINTAIN_STATES [ state ] : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . controlsd . events . add ( make_event ( [ et ,  ET . USER_DISABLE ] ) ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . controlsd . state  =  state 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . controlsd . state_transition ( self . CS ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . assertEqual ( State . disabled ,  self . controlsd . state ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . controlsd . events . clear ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  test_soft_disable ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    for  state  in  ALL_STATES : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      if  state  ==  State . preEnabled :   # preEnabled considers NO_ENTRY instead 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        continue 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								      for  et  in  MAINTAIN_STATES [ state ] : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . controlsd . events . add ( make_event ( [ et ,  ET . SOFT_DISABLE ] ) ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . controlsd . state  =  state 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . controlsd . state_transition ( self . CS ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . assertEqual ( self . controlsd . state ,  State . disabled  if  state  ==  State . disabled  else  State . softDisabling ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . controlsd . events . clear ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  test_soft_disable_timer ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . controlsd . state  =  State . enabled 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . controlsd . events . add ( make_event ( [ ET . SOFT_DISABLE ] ) ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . controlsd . state_transition ( self . CS ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    for  _  in  range ( int ( SOFT_DISABLE_TIME  /  DT_CTRL ) ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      self . assertEqual ( self . controlsd . state ,  State . softDisabling ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      self . controlsd . state_transition ( self . CS ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . assertEqual ( self . controlsd . state ,  State . disabled ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  test_no_entry ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    # disabled with enable events 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    for  et  in  ENABLE_EVENT_TYPES : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      self . controlsd . events . add ( make_event ( [ ET . NO_ENTRY ,  et ] ) ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      self . controlsd . state_transition ( self . CS ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      self . assertEqual ( self . controlsd . state ,  State . disabled ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      self . controlsd . events . clear ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  test_no_entry_pre_enable ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    # preEnabled with preEnabled event 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . controlsd . state  =  State . preEnabled 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . controlsd . events . add ( make_event ( [ ET . NO_ENTRY ,  ET . PRE_ENABLE ] ) ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . controlsd . state_transition ( self . CS ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . assertEqual ( self . controlsd . state ,  State . disabled ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  test_maintain_states ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    # Given current state's event type, we should maintain state 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    for  state  in  ALL_STATES : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								      for  et  in  MAINTAIN_STATES [ state ] : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . controlsd . state  =  state 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . controlsd . events . add ( make_event ( [ et ] ) ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . controlsd . state_transition ( self . CS ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . assertEqual ( self . controlsd . state ,  state ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . controlsd . events . clear ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								if  __name__  ==  " __main__ " : 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  unittest . main ( )