From 80eb0396b20f4ec501a618eaf5eb408f82587189 Mon Sep 17 00:00:00 2001 From: Mark Winkel Date: Mon, 12 Feb 2024 16:40:24 -0500 Subject: [PATCH 1/3] Build: enhanced IDL tests now use argparse --- idl/testing/run_tests.py | 362 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 360 insertions(+), 2 deletions(-) mode change 100755 => 100644 idl/testing/run_tests.py diff --git a/idl/testing/run_tests.py b/idl/testing/run_tests.py old mode 100755 new mode 100644 index 86f5ce1d36..eb2a505afe --- a/idl/testing/run_tests.py +++ b/idl/testing/run_tests.py @@ -58,8 +58,216 @@ help='The value of evaluating the --node2 expression, ignoring leading/trailing whitespace' ) +parser.add_argument( + '--text', + default='ADMIN.LOGBOOK.STATISTICS:ANAL_DONE', + help='An expression to evaluate and compare against --text-value' +) + +parser.add_argument( + '--text-value', + default='9-SEP-2009 11:29:41.00', + help='The value of evaluating the --text expression, ignoring leading/trailing whitespace' +) + +parser.add_argument( + '--numeric', + default='SEQUENCE_NUM', + help='An expression to evaluate and compare against --numeric-value' +) + +parser.add_argument( + '--numeric-value', + default='35575', + help='The value of evaluating the --numeric expression, ignoring leading/trailing whitespace' +) + +parser.add_argument( + '--signal', + default='ADMIN.FAST_WINDOW.XTOMO:OPTIONS', + help='An expression to evaluate and compare against --signal-value' +) + +parser.add_argument( + '--signal-value', + default='none 25kHz 50kHz 83.3kHz', + help='The value of evaluating the --signal expression, ignoring leading/trailing whitespace' +) + +parser.add_argument( + '--fullpath', + default='\\CMOD::TOP.ELECTRONS.ECE.RESULTS:APERTURE', + help='An expression to evaluate and compare against --fullpath-value' +) + +parser.add_argument( + '--fullpath-value', + default='39.1000', + help='The value of evaluating the --fullpath expression, ignoring leading/trailing whitespace' +) + +parser.add_argument( + '--relative-def', + default='\CMOD::TOP.ELECTRONS.ECE', + help='An expression to change the default position in the tree' +) + +parser.add_argument( + '--relative', + default='.RESULTS:ZPD', + help='An expression to evaluate and compare against --relative-value' +) + +parser.add_argument( + '--relative-value', + default='57', + help='The value of evaluating the --relative expression, ignoring leading/trailing whitespace' +) + +parser.add_argument( + '--reset-def', + default='\CMOD::TOP', + help='An expression to reset the default position to the top of the tree' +) + +parser.add_argument( + '--tag', + default='\\DNB::TSTART', + help='An expression to evaluate and compare against --tag-value' +) + +parser.add_argument( + '--tag-value', + default='-4.00000', + help='The value of evaluating the --tag expression, ignoring leading/trailing whitespace' +) + +# The parser is converting "\\" into "\", so to get desired path of "\\CMOD::" must use "\\\\" +parser.add_argument( + '--wildcard', + default='SUBSCRIPT(GETNCI("\\\\CMOD::TOP.***:CPLD_START", "FULLPATH"),0)', + help='An expression to evaluate and compare against --wildcard-value' +) + +parser.add_argument( + '--wildcard-value', + default='\CMOD::TOP.DNB.MIT_CXRS:CPLD_START', + help='The value of evaluating the --wildcard expression, ignoring leading/trailing whitespace' +) + +# The parser is converting "\\" into "\", so to get desired path of "\\CMOD::" must use "\\\\" +parser.add_argument( + '--getnci', + default=' SUBSCRIPT(GETNCI("\\\\CMOD::TOP.DNB.MIT_CXRS:CPLD_START", "USAGE"),0) ', + help='An expression to evaluate and compare against --getnci-value' +) + +parser.add_argument( + '--getnci-value', + default='5', + help='The value of evaluating the --getnci expression, ignoring leading/trailing whitespace' +) + +parser.add_argument( + '--rank', + default='RANK(\\IP)', + help='An expression to evaluate and compare against --rank-value' +) + +parser.add_argument( + '--rank-value', + default='1', + help='The value of evaluating the --rank expression, ignoring leading/trailing whitespace' +) + +parser.add_argument( + '--ndims', + default='NDIMS(\\IP)', + help='An expression to evaluate and compare against --ndims-value' +) + +parser.add_argument( + '--ndims-value', + default='1', + help='The value of evaluating the --ndims expression, ignoring leading/trailing whitespace' +) + +parser.add_argument( + '--dim-of', + default='DIM_OF(\\CMOD::TOP.ADMIN.FAST_WINDOW.XTOMO:OPTIONS, 0)', + help='An expression to evaluate and compare against --dim-of-value' +) + +parser.add_argument( + '--dim-of-value', + default='1 1 1 1', + help='The value of evaluating the --dim-of expression, ignoring leading/trailing whitespace' +) + +parser.add_argument( + '--units-of', + default='UNITS_OF(\\CMOD::TOP.MHD.XTOMO.SIGNALS.ARRAY_1:CHORD_01)', + help='An expression to evaluate and compare against --units-of-value' +) + +parser.add_argument( + '--units-of-value', + default='watts', + help='The value of evaluating the --units-of expression, ignoring leading/trailing whitespace' +) + args = parser.parse_args() + +#--------------------------------------------------------------------------- +# Each IDL write test should start with a clean tree. +# Write tests use a local tree, but eventually will be upgraded to use mdsip. +# TODO: The default_tree_path is temporary; will change when switch to mdsip. +# +def build_write_tree(tree, shot): + import os + import MDSplus as mds + + test_dir = os.getenv('MDSPLUS_DIR') + '/idl/testing/' + os.environ['default_tree_path'] = test_dir # required to write tree + + tree_name = tree + '_' + str(shot) + tree_file = test_dir + tree_name + + tc = tree_file + '.characteristics' + td = tree_file + '.datafile' + tt = tree_file + '.tree' + + if os.path.exists(tc): + os.remove(tc) + if os.path.exists(td): + os.remove(td) + if os.path.exists(tt): + os.remove(tt) + + t = mds.Tree(tree, shot, 'new') + + nid = t.addNode('A_TEXT', 'text') + nid = t.addNode('B_NUM', 'numeric') + nid = t.addNode('C_SIGNAL', 'signal') + + nid = t.addNode('SUBTREE_1', 'structure') + nid = t.addNode('SUBTREE_1.D_TEXT', 'text') + nid = t.addNode('SUBTREE_1.E_UNITS', 'numeric') + + nid = t.addNode('SUBTREE_2', 'structure') + nid = t.addNode('SUBTREE_2.F_NUM', 'numeric') + nid = t.addNode('SUBTREE_2.G_NUM', 'numeric') + nid.addTag('TAG_G') + + t.write() + t.close() + +write_tree = 'write' +write_shot = 123 +build_write_tree(write_tree, write_shot) + + all_tests_passed = True def idl_test(code, expected_output): global all_tests_passed @@ -139,6 +347,7 @@ def idl_test(code, expected_output): print() +#------------------------------------------------------------------------------- # Tree open / read / close idl_test(f''' @@ -166,6 +375,8 @@ def idl_test(code, expected_output): ''') +#------------------------------------------------------------------------------- +# https://github.com/MDSplus/mdsplus/issues/2580 # Issue 2580: first connect should be on socket 0. # Very rare for returned status to be zero. idl_test(f''' @@ -210,6 +421,9 @@ def idl_test(code, expected_output): if args.database_name != '': + + #--------------------------------------------------------------------------- + # https://github.com/MDSplus/mdsplus/issues/2625 # Issue #2625: database on socket 0, subsequent connect doesn't break proxy. # If the queries work, the "val" variables will be changed to a text timestamp. idl_test(f''' @@ -259,6 +473,8 @@ def idl_test(code, expected_output): ''') + #--------------------------------------------------------------------------- + # https://github.com/MDSplus/mdsplus/issues/2625 # Issue #2625: usual pattern at GA is connect, then database. idl_test(f''' @@ -305,6 +521,8 @@ def idl_test(code, expected_output): ''') + #--------------------------------------------------------------------------- + # https://github.com/MDSplus/mdsplus/issues/2625 # Issue #2625: second database call not affected. idl_test(f''' @@ -357,6 +575,8 @@ def idl_test(code, expected_output): ''') + #--------------------------------------------------------------------------- + # https://github.com/MDSplus/mdsplus/issues/2625 # Issue #2625: stress test database proxy with many connects. # NLOOPS should be more than 64 (see Issue #2638). If the # disconnect works, will never exceed the concurrent limit. @@ -510,6 +730,9 @@ def idl_test(code, expected_output): ''') + +#------------------------------------------------------------------------------- +# https://github.com/MDSplus/mdsplus/issues/2638 # Issue #2638: crashes with too many concurrent sockets. # NLOOPS should be more than 64. idl_test(f''' @@ -644,6 +867,8 @@ def idl_test(code, expected_output): ''') +#------------------------------------------------------------------------------- +# https://github.com/MDSplus/mdsplus/issues/2639 # Issue #2639: mdsvalue works without a socket idl_test(f''' @@ -678,6 +903,8 @@ def idl_test(code, expected_output): ''') +#------------------------------------------------------------------------------- +# https://github.com/MDSplus/mdsplus/issues/2639 # Issue #2639: mdsvalue and interaction with killed socket. # idl_test(f''' @@ -723,6 +950,8 @@ def idl_test(code, expected_output): # ''') +#------------------------------------------------------------------------------- +# https://github.com/MDSplus/mdsplus/issues/2639 # Issue #2639: mdsvalue and interaction with killed socket 0. idl_test(f''' @@ -768,6 +997,8 @@ def idl_test(code, expected_output): ''') +#------------------------------------------------------------------------------- +# https://github.com/MDSplus/mdsplus/issues/2639 # Issue #2639: mdsvalue and kill default socket. idl_test(f''' @@ -814,6 +1045,8 @@ def idl_test(code, expected_output): ''') +#------------------------------------------------------------------------------- +# https://github.com/MDSplus/mdsplus/issues/2639 # Issue #2639: mdsvalue, one connect, and kill socket. idl_test(f''' @@ -858,6 +1091,8 @@ def idl_test(code, expected_output): ''') +#------------------------------------------------------------------------------- +# https://github.com/MDSplus/mdsplus/issues/2639 # Issue #2639: mdsvalue, one connect, and kill 0 socket. # Note different behavior from IDL-2639-kill-single-socket test. # idl_test(f''' @@ -903,6 +1138,8 @@ def idl_test(code, expected_output): # ''') +#------------------------------------------------------------------------------- +# https://github.com/MDSplus/mdsplus/issues/2640 # Issue #2640: disconnect returns correct status. # First disconnect should succeed and thus return True (1). # But disconnecting an already disconnected socket should return False (0). @@ -950,6 +1187,8 @@ def idl_test(code, expected_output): if args.database_name != '': + + #--------------------------------------------------------------------------- # Database: dbdisconnect kills database proxy idl_test(f''' @@ -1003,6 +1242,7 @@ def idl_test(code, expected_output): ''') + #--------------------------------------------------------------------------- # Database: regular disconnect kills database proxy. # GA usually has mdsconnect followed by set_database. # Note different behavior compared to IDL-db-dbdisconnect test. @@ -1061,13 +1301,16 @@ def idl_test(code, expected_output): ''') - # + + #*************************************************************************** # The following "socket" tests establish a baseline for the current code. # The whole concept of the IDL API (!MDS_SOCKET, !MDSDB_SOCKET, etc.) doesn't # reflect the full features of the underlying C code. And thus IDL sockets # are apt to be sensitive to any changes made to the IDL API. - # + #*************************************************************************** + + #--------------------------------------------------------------------------- # Sockets: a sequence of socket operations. idl_test(f''' @@ -1146,6 +1389,7 @@ def idl_test(code, expected_output): ''') + #--------------------------------------------------------------------------- # Sockets: reset of the !MDS* system variables idl_test(f''' @@ -1200,6 +1444,7 @@ def idl_test(code, expected_output): ''') + #--------------------------------------------------------------------------- # Sockets: explicitly reset socket 0. # Note different behavior than IDL-socket-reset test. idl_test(f''' @@ -1254,6 +1499,119 @@ def idl_test(code, expected_output): ''') + +#--------------------------------------------------------------------------- +# Read: various permutations of reading data from a tree +idl_test(f''' + +testid = 'IDL-read-various' + +mdsconnect, '{args.mdsip_server}' +mdsopen, '{args.tree}', {args.shot} +print, mdsvalue('{args.text}') +print, mdsvalue('{args.numeric}') +print, mdsvalue('{args.signal}') +print, mdsvalue('{args.fullpath}') +mdsset_def, '{args.relative_def}' +print, mdsvalue('{args.relative}') +mdsset_def, '{args.reset_def}' +print, mdsvalue('{args.tag}') +print, mdsvalue('{args.wildcard}') +print, mdsvalue('{args.getnci}') +print, mdsvalue('{args.rank}') +print, mdsvalue('{args.ndims}') +print, mdsvalue('{args.dim_of}') +print, mdsvalue('{args.units_of}') +mdsclose, '{args.tree}', '{args.shot}' + +''', +f''' + +% Compiled module: MDSCONNECT. +% Compiled module: MDS_KEYWORD_SET. +% Compiled module: MDSDISCONNECT. +% Compiled module: MDSOPEN. +% Compiled module: MDSVALUE. +% Compiled module: MDSCHECKARG. +% Compiled module: MDSISCLIENT. +{args.text_value} +{args.numeric_value} +{args.signal_value} +{args.fullpath_value} +% Compiled module: MDSSET_DEF. +% Compiled module: MDSSETDEFAULT. +{args.relative_value} +{args.tag_value} +{args.wildcard_value} +{args.getnci_value} +{args.rank_value} +{args.ndims_value} +{args.dim_of_value} +{args.units_of_value} +% Compiled module: MDSCLOSE. + +''') + + +#--------------------------------------------------------------------------- +# Write: various permutations of writing data to a tree +# +# Note: Uses local tree on the build server (i.e., not using mdsip). +# The $default_tree_path must point to the "idl/testing" directory. +idl_test(f''' + +testid = 'IDL-write-various' + +mdsopen, '{write_tree}', '{write_shot}' +mdsput, 'A_TEXT', ' "string_a" ' +mdsput, 'B_NUM', '22' +mdsput, 'C_SIGNAL', 'build_signal([10,-10,5,-5,0],[10.2,-10.2,5.4,-5.4,0.0], [0 .. 4])' +mdsput, '\TOP.SUBTREE_1:D_TEXT', ' "string_d" ' +mdsput, 'SUBTREE_1.E_UNITS', 'build_with_units(55, "volts")' +mdsset_def, 'SUBTREE_1' +mdsput, '.-.SUBTREE_2:F_NUM', '66' +mdsput, '\TAG_G', '77' +mdsclose, '{write_tree}', '{write_shot}' + +mdsopen, '{write_tree}', '{write_shot}' +print, mdsvalue('A_TEXT') +print, mdsvalue('B_NUM') +print, mdsvalue('DATA(C_SIGNAL)') +print, mdsvalue('RAW_OF(C_SIGNAL)') +print, mdsvalue('DIM_OF(C_SIGNAL)') +print, mdsvalue('SUBTREE_1:D_TEXT') +print, mdsvalue('SUBTREE_1.E_UNITS') +print, mdsvalue('UNITS_OF(SUBTREE_1.E_UNITS)') +print, mdsvalue('SUBTREE_2:F_NUM') +print, mdsvalue('SUBTREE_2:G_NUM') +mdsclose, '{write_tree}', '{write_shot}' + +''', +''' + +% Compiled module: MDSOPEN. +% Compiled module: MDSVALUE. +% Compiled module: MDSCHECKARG. +% Compiled module: MDSISCLIENT. +% Compiled module: MDS_KEYWORD_SET. +% Compiled module: MDSIDLIMAGE. +% Compiled module: MDSPUT. +% Compiled module: MDSSET_DEF. +% Compiled module: MDSSETDEFAULT. +% Compiled module: MDSCLOSE. +string_a +22 +10 -10 5 -5 0 +10.2000 -10.2000 5.40000 -5.40000 0.00000 +0 1 2 3 4 +string_d +55 +volts +66 +77 + +''') + if not all_tests_passed: exit(1) From 5ca795a06a382ad54ac644336eed7f6eb54ffd3d Mon Sep 17 00:00:00 2001 From: Mark Winkel Date: Tue, 13 Feb 2024 14:38:04 -0500 Subject: [PATCH 2/3] Build: parameterize the "write" tree for IDL tests - no longer uses hard coded tree name and shot number - also changed file permissions on run_tests.py --- idl/testing/run_tests.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) mode change 100644 => 100755 idl/testing/run_tests.py diff --git a/idl/testing/run_tests.py b/idl/testing/run_tests.py old mode 100644 new mode 100755 index eb2a505afe..981ae62b35 --- a/idl/testing/run_tests.py +++ b/idl/testing/run_tests.py @@ -216,6 +216,18 @@ help='The value of evaluating the --units-of expression, ignoring leading/trailing whitespace' ) +parser.add_argument( + '--write-tree', + default='idl_tests', + help='Name of tree to create for exercising write functions of the API' +) + +parser.add_argument( + '--write-shot', + default=100, + help='Shot number of tree to create for exercising write functions of the API' +) + args = parser.parse_args() @@ -263,9 +275,8 @@ def build_write_tree(tree, shot): t.write() t.close() -write_tree = 'write' -write_shot = 123 -build_write_tree(write_tree, write_shot) + +build_write_tree(args.write_tree, args.write_shot) all_tests_passed = True @@ -1562,7 +1573,7 @@ def idl_test(code, expected_output): testid = 'IDL-write-various' -mdsopen, '{write_tree}', '{write_shot}' +mdsopen, '{args.write_tree}', '{args.write_shot}' mdsput, 'A_TEXT', ' "string_a" ' mdsput, 'B_NUM', '22' mdsput, 'C_SIGNAL', 'build_signal([10,-10,5,-5,0],[10.2,-10.2,5.4,-5.4,0.0], [0 .. 4])' @@ -1571,9 +1582,9 @@ def idl_test(code, expected_output): mdsset_def, 'SUBTREE_1' mdsput, '.-.SUBTREE_2:F_NUM', '66' mdsput, '\TAG_G', '77' -mdsclose, '{write_tree}', '{write_shot}' +mdsclose, '{args.write_tree}', '{args.write_shot}' -mdsopen, '{write_tree}', '{write_shot}' +mdsopen, '{args.write_tree}', '{args.write_shot}' print, mdsvalue('A_TEXT') print, mdsvalue('B_NUM') print, mdsvalue('DATA(C_SIGNAL)') @@ -1584,7 +1595,7 @@ def idl_test(code, expected_output): print, mdsvalue('UNITS_OF(SUBTREE_1.E_UNITS)') print, mdsvalue('SUBTREE_2:F_NUM') print, mdsvalue('SUBTREE_2:G_NUM') -mdsclose, '{write_tree}', '{write_shot}' +mdsclose, '{args.write_tree}', '{args.write_shot}' ''', ''' From cc20db0675b55c6662fd260200dccf202f49772e Mon Sep 17 00:00:00 2001 From: Mark Winkel Date: Wed, 14 Feb 2024 17:06:50 -0500 Subject: [PATCH 3/3] Build: use temporary directory for transient files --- idl/testing/run_tests.py | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/idl/testing/run_tests.py b/idl/testing/run_tests.py index 981ae62b35..244d6bdc4e 100755 --- a/idl/testing/run_tests.py +++ b/idl/testing/run_tests.py @@ -3,6 +3,7 @@ import os import subprocess import argparse +import tempfile # The default values are intended to be used from within the PSFC network # If you want to run these tests on your own infrastructure, provide the @@ -237,26 +238,8 @@ # TODO: The default_tree_path is temporary; will change when switch to mdsip. # def build_write_tree(tree, shot): - import os import MDSplus as mds - test_dir = os.getenv('MDSPLUS_DIR') + '/idl/testing/' - os.environ['default_tree_path'] = test_dir # required to write tree - - tree_name = tree + '_' + str(shot) - tree_file = test_dir + tree_name - - tc = tree_file + '.characteristics' - td = tree_file + '.datafile' - tt = tree_file + '.tree' - - if os.path.exists(tc): - os.remove(tc) - if os.path.exists(td): - os.remove(td) - if os.path.exists(tt): - os.remove(tt) - t = mds.Tree(tree, shot, 'new') nid = t.addNode('A_TEXT', 'text') @@ -276,8 +259,12 @@ def build_write_tree(tree, shot): t.close() -build_write_tree(args.write_tree, args.write_shot) +# Temporary directory for transient test scripts and artifacts +TEST_DIR = tempfile.TemporaryDirectory(prefix='test_idl_', dir='/tmp') +os.environ['default_tree_path'] = TEST_DIR.name +os.environ['IDL_PATH'] = os.getenv('IDL_PATH') + ':' + TEST_DIR.name +build_write_tree(args.write_tree, args.write_shot) all_tests_passed = True def idl_test(code, expected_output): @@ -291,7 +278,8 @@ def idl_test(code, expected_output): # weird differences in the evaluation, so we use a test.pro file code = 'pro test\n' + code + '\nend' - open('test.pro', 'wt').write(code) + test_file = TEST_DIR.name + '/test.pro' + open(test_file, 'wt').write(code) expected_lines = [ line.strip() for line in expected_output.splitlines() ] expected_lines = list(filter(None, expected_lines))