@@ -904,3 +904,203 @@ async def query_record(index):
904
904
log (f"PARENT: Join completed with exitcode { process .exitcode } " )
905
905
if process .exitcode is None :
906
906
pytest .fail ("Process did not terminate" )
907
+
908
+ class TestGetSetField :
909
+ """Tests related to get_field and set_field on records"""
910
+
911
+ test_result_rec = "TestResult"
912
+
913
+ def test_set_field_before_init_fails (self ):
914
+ """Test that calling set_field before iocInit() raises an exception"""
915
+
916
+ ao = builder .aOut ("testAOut" )
917
+
918
+ with pytest .raises (AssertionError ) as e :
919
+ ao .set_field ("EGU" , "Deg" )
920
+
921
+ assert "set_field may only be called after iocInit" in str (e .value )
922
+
923
+ def test_get_field_before_init_fails (self ):
924
+ """Test that calling get_field before iocInit() raises an exception"""
925
+
926
+ ao = builder .aOut ("testAOut" )
927
+
928
+ with pytest .raises (AssertionError ) as e :
929
+ ao .get_field ("EGU" )
930
+
931
+ assert "get_field may only be called after iocInit" in str (e .value )
932
+
933
+ def get_set_test_func (self , device_name , conn ):
934
+ """Run an IOC and do simple get_field/set_field calls"""
935
+
936
+ builder .SetDeviceName (device_name )
937
+
938
+ lo = builder .longOut ("TestLongOut" , EGU = "unset" , DRVH = 12 )
939
+
940
+ # Record to indicate success/failure
941
+ bi = builder .boolIn (self .test_result_rec , ZNAM = "FAILED" , ONAM = "SUCCESS" )
942
+
943
+ dispatcher = asyncio_dispatcher .AsyncioDispatcher ()
944
+ builder .LoadDatabase ()
945
+ softioc .iocInit (dispatcher )
946
+
947
+ conn .send ("R" ) # "Ready"
948
+
949
+ log ("CHILD: Sent R over Connection to Parent" )
950
+
951
+ # Set and then get the EGU field
952
+ egu = "TEST"
953
+ lo .set_field ("EGU" , egu )
954
+ log ("CHILD: set_field successful" )
955
+ readback_egu = lo .get_field ("EGU" )
956
+ log (f"CHILD: get_field returned { readback_egu } " )
957
+ assert readback_egu == egu , \
958
+ f"EGU field was not { egu } , was { readback_egu } "
959
+
960
+ log ("CHILD: assert passed" )
961
+
962
+ # Test completed, report to listening camonitor
963
+ bi .set (True )
964
+
965
+ # Keep process alive while main thread works.
966
+ while (True ):
967
+ if conn .poll (TIMEOUT ):
968
+ val = conn .recv ()
969
+ if val == "D" : # "Done"
970
+ break
971
+
972
+ log ("CHILD: Received exit command, child exiting" )
973
+
974
+
975
+ @pytest .mark .asyncio
976
+ async def test_get_set (self ):
977
+ """Test a simple set_field/get_field is successful"""
978
+ ctx = get_multiprocessing_context ()
979
+ parent_conn , child_conn = ctx .Pipe ()
980
+
981
+ device_name = create_random_prefix ()
982
+
983
+ process = ctx .Process (
984
+ target = self .get_set_test_func ,
985
+ args = (device_name , child_conn ),
986
+ )
987
+
988
+ process .start ()
989
+
990
+ log ("PARENT: Child started, waiting for R command" )
991
+
992
+ from aioca import camonitor
993
+
994
+ try :
995
+ # Wait for message that IOC has started
996
+ select_and_recv (parent_conn , "R" )
997
+
998
+ log ("PARENT: received R command" )
999
+
1000
+ queue = asyncio .Queue ()
1001
+ record = device_name + ":" + self .test_result_rec
1002
+ monitor = camonitor (record , queue .put )
1003
+
1004
+ log (f"PARENT: monitoring { record } " )
1005
+ new_val = await asyncio .wait_for (queue .get (), TIMEOUT )
1006
+ log (f"PARENT: new_val is { new_val } " )
1007
+ assert new_val == 1 , \
1008
+ f"Test failed, value was not 1(True), was { new_val } "
1009
+
1010
+
1011
+ finally :
1012
+ monitor .close ()
1013
+ # Clear the cache before stopping the IOC stops
1014
+ # "channel disconnected" error messages
1015
+ aioca_cleanup ()
1016
+
1017
+ log ("PARENT: Sending Done command to child" )
1018
+ parent_conn .send ("D" ) # "Done"
1019
+ process .join (timeout = TIMEOUT )
1020
+ log (f"PARENT: Join completed with exitcode { process .exitcode } " )
1021
+ if process .exitcode is None :
1022
+ pytest .fail ("Process did not terminate" )
1023
+
1024
+ def get_set_too_long_value (self , device_name , conn ):
1025
+ """Run an IOC and deliberately call set_field with a too-long value"""
1026
+
1027
+ builder .SetDeviceName (device_name )
1028
+
1029
+ lo = builder .longOut ("TestLongOut" , EGU = "unset" , DRVH = 12 )
1030
+
1031
+ # Record to indicate success/failure
1032
+ bi = builder .boolIn (self .test_result_rec , ZNAM = "FAILED" , ONAM = "SUCCESS" )
1033
+
1034
+ dispatcher = asyncio_dispatcher .AsyncioDispatcher ()
1035
+ builder .LoadDatabase ()
1036
+ softioc .iocInit (dispatcher )
1037
+
1038
+ conn .send ("R" ) # "Ready"
1039
+
1040
+ log ("CHILD: Sent R over Connection to Parent" )
1041
+
1042
+ # Set a too-long value and confirm it reports an error
1043
+ try :
1044
+ lo .set_field ("EGU" , "ThisStringIsFarTooLongToFitIntoTheEguField" )
1045
+ except ValueError as e :
1046
+ # Expected error, report success to listening camonitor
1047
+ assert "byte string too long" in e .args [0 ]
1048
+ bi .set (True )
1049
+
1050
+ # Keep process alive while main thread works.
1051
+ while (True ):
1052
+ if conn .poll (TIMEOUT ):
1053
+ val = conn .recv ()
1054
+ if val == "D" : # "Done"
1055
+ break
1056
+
1057
+ log ("CHILD: Received exit command, child exiting" )
1058
+
1059
+ @pytest .mark .asyncio
1060
+ async def test_set_too_long_value (self ):
1061
+ """Test that set_field with a too-long value raises the expected
1062
+ error"""
1063
+ ctx = get_multiprocessing_context ()
1064
+ parent_conn , child_conn = ctx .Pipe ()
1065
+
1066
+ device_name = create_random_prefix ()
1067
+
1068
+ process = ctx .Process (
1069
+ target = self .get_set_too_long_value ,
1070
+ args = (device_name , child_conn ),
1071
+ )
1072
+
1073
+ process .start ()
1074
+
1075
+ log ("PARENT: Child started, waiting for R command" )
1076
+
1077
+ from aioca import camonitor
1078
+
1079
+ try :
1080
+ # Wait for message that IOC has started
1081
+ select_and_recv (parent_conn , "R" )
1082
+
1083
+ log ("PARENT: received R command" )
1084
+
1085
+ queue = asyncio .Queue ()
1086
+ record = device_name + ":" + self .test_result_rec
1087
+ monitor = camonitor (record , queue .put )
1088
+
1089
+ log (f"PARENT: monitoring { record } " )
1090
+ new_val = await asyncio .wait_for (queue .get (), TIMEOUT )
1091
+ log (f"PARENT: new_val is { new_val } " )
1092
+ assert new_val == 1 , \
1093
+ f"Test failed, value was not 1(True), was { new_val } "
1094
+
1095
+ finally :
1096
+ monitor .close ()
1097
+ # Clear the cache before stopping the IOC stops
1098
+ # "channel disconnected" error messages
1099
+ aioca_cleanup ()
1100
+
1101
+ log ("PARENT: Sending Done command to child" )
1102
+ parent_conn .send ("D" ) # "Done"
1103
+ process .join (timeout = TIMEOUT )
1104
+ log (f"PARENT: Join completed with exitcode { process .exitcode } " )
1105
+ if process .exitcode is None :
1106
+ pytest .fail ("Process did not terminate" )
0 commit comments