@@ -683,8 +683,8 @@ def test_on_update_true_false(self, out_records):
683683 always_update is True and the put'ed value is always different"""
684684 self .on_update_runner (out_records , True , False )
685685
686- def on_update_recursive_set_test_func (
687- self , device_name , conn
686+ def on_update_recursive_set_value_test_func (
687+ self , device_name , conn ,
688688 ):
689689 log ("CHILD: Child started" )
690690
@@ -718,8 +718,8 @@ async def on_update_func(new_val):
718718
719719 log ("CHILD: Received exit command, child exiting" )
720720
721- async def test_on_update_recursive_set (self ):
722- """Test that on_update functions correctly when the on_update
721+ async def test_on_update_recursive_set_value (self ):
722+ """Test that on_update sets values correctly when the on_update
723723 callback sets the value of the record again (with process=False).
724724 See issue #201"""
725725
@@ -729,7 +729,7 @@ async def test_on_update_recursive_set(self):
729729 device_name = create_random_prefix ()
730730
731731 process = ctx .Process (
732- target = self .on_update_recursive_set_test_func ,
732+ target = self .on_update_recursive_set_value_test_func ,
733733 args = (device_name , child_conn ),
734734 )
735735
@@ -773,6 +773,127 @@ async def test_on_update_recursive_set(self):
773773 pytest .fail ("Process did not terminate" )
774774
775775
776+ def on_update_recursive_set_alarm_test_func (
777+ self , device_name , conn , severity , alrm
778+ ):
779+ log ("CHILD: Child started" )
780+
781+ builder .SetDeviceName (device_name )
782+
783+ async def on_update_func (new_val ):
784+ log (f"CHILD: on_update_func started value { new_val } " )
785+ if new_val == 2 :
786+ record .set (
787+ new_val ,
788+ process = False ,
789+ severity = severity ,
790+ alarm = alrm
791+ )
792+ elif new_val == 3 :
793+ record .set (
794+ new_val ,
795+ process = False ,
796+ severity = alarm .NO_ALARM ,
797+ alarm = alarm .NO_ALARM
798+ )
799+ conn .send ("C" ) # "Callback"
800+ log ("CHILD: on_update_func ended" )
801+
802+ record = builder .longOut (
803+ "ACTION" ,
804+ on_update = on_update_func ,
805+ blocking = True ,
806+ initial_value = 1 # A non-zero value, to check it changes
807+ )
808+
809+ dispatcher = asyncio_dispatcher .AsyncioDispatcher ()
810+ builder .LoadDatabase ()
811+ softioc .iocInit (dispatcher )
812+
813+ conn .send ("R" ) # "Ready"
814+
815+ log ("CHILD: Sent R over Connection to Parent" )
816+
817+ # Keep process alive while main thread runs CAGET
818+ if conn .poll (TIMEOUT ):
819+ val = conn .recv ()
820+ assert val == "D" , "Did not receive expected Done character"
821+
822+ log ("CHILD: Received exit command, child exiting" )
823+
824+ async def test_on_update_recursive_set_alarm (self ):
825+ """Test that on_update sets alarms correctly when the on_update
826+ callback sets the value of the record again (with process=False).
827+ See PR #209"""
828+
829+ ctx = get_multiprocessing_context ()
830+ parent_conn , child_conn = ctx .Pipe ()
831+
832+ device_name = create_random_prefix ()
833+
834+ expected_sevr = alarm .MAJOR_ALARM
835+ expected_alarm = alarm .HIGH_ALARM # arbitrary choice
836+
837+ process = ctx .Process (
838+ target = self .on_update_recursive_set_alarm_test_func ,
839+ args = (device_name , child_conn , expected_sevr , expected_alarm ),
840+ )
841+
842+ process .start ()
843+
844+ log ("PARENT: Child started, waiting for R command" )
845+
846+ from aioca import caget , caput
847+
848+ try :
849+ # Wait for message that IOC has started
850+ select_and_recv (parent_conn , "R" )
851+
852+ log ("PARENT: received R command" )
853+
854+ record = f"{ device_name } :ACTION"
855+
856+ val = await caget (record )
857+
858+ assert val == 1 , "ACTION record did not start with value 1"
859+
860+ # Trigger callback to set alarm states
861+ await caput (record , 2 , wait = True )
862+
863+ sevr = await caget (record + ".SEVR" )
864+ assert sevr == expected_sevr
865+
866+ sevr = await caget (record + ".STAT" )
867+ assert sevr == expected_alarm
868+
869+ # Expect one "C"
870+ select_and_recv (parent_conn , "C" )
871+
872+ # Trigger another callback to clear alarm states
873+ await caput (record , 3 , wait = True )
874+
875+ sevr = await caget (record + ".SEVR" )
876+ assert sevr == alarm .NO_ALARM
877+
878+ sevr = await caget (record + ".STAT" )
879+ assert sevr == alarm .NO_ALARM
880+
881+ # Expect second "C"
882+ select_and_recv (parent_conn , "C" )
883+
884+ # ...But if we receive another we know there's a problem
885+ if parent_conn .poll (5 ): # Shorter timeout to make this quicker
886+ pytest .fail ("Received unexpected second message" )
887+
888+ finally :
889+ log ("PARENT:Sending Done command to child" )
890+ parent_conn .send ("D" ) # "Done"
891+ process .join (timeout = TIMEOUT )
892+ log (f"PARENT: Join completed with exitcode { process .exitcode } " )
893+ if process .exitcode is None :
894+ pytest .fail ("Process did not terminate" )
895+
896+
776897
777898class TestBlocking :
778899 """Tests related to the Blocking functionality"""
0 commit comments