@sasjs/utils 2.48.2 → 2.49.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fs/generateCompileProgram.d.ts +1 -0
- package/fs/generateCompileProgram.js +154 -0
- package/fs/generateCompileProgram.js.map +1 -0
- package/fs/hash.d.ts +22 -0
- package/fs/hash.js +225 -0
- package/fs/hash.js.map +1 -0
- package/fs/index.d.ts +3 -0
- package/fs/index.js +13 -0
- package/fs/index.js.map +1 -0
- package/fs/internal/helper.d.ts +4 -0
- package/fs/internal/helper.js +125 -0
- package/fs/internal/helper.js.map +1 -0
- package/fs/sync.d.ts +6 -0
- package/fs/sync.js +208 -0
- package/fs/sync.js.map +1 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/macros/mf_abort.sas +27 -0
- package/macros/mf_dedup.sas +53 -0
- package/macros/mf_deletefile.sas +31 -0
- package/macros/mf_existds.sas +30 -0
- package/macros/mf_existfeature.sas +59 -0
- package/macros/mf_existfileref.sas +33 -0
- package/macros/mf_existfunction.sas +37 -0
- package/macros/mf_existvar.sas +39 -0
- package/macros/mf_existvarlist.sas +56 -0
- package/macros/mf_fmtdttm.sas +42 -0
- package/macros/mf_getapploc.sas +78 -0
- package/macros/mf_getattrc.sas +34 -0
- package/macros/mf_getattrn.sas +34 -0
- package/macros/mf_getengine.sas +54 -0
- package/macros/mf_getfilesize.sas +64 -0
- package/macros/mf_getfmtlist.sas +61 -0
- package/macros/mf_getfmtname.sas +44 -0
- package/macros/mf_getkeyvalue.sas +32 -0
- package/macros/mf_getplatform.sas +75 -0
- package/macros/mf_getquotedstr.sas +57 -0
- package/macros/mf_getschema.sas +43 -0
- package/macros/mf_getuniquefileref.sas +60 -0
- package/macros/mf_getuniquelibref.sas +57 -0
- package/macros/mf_getuniquename.sas +22 -0
- package/macros/mf_getuser.sas +44 -0
- package/macros/mf_getvalue.sas +36 -0
- package/macros/mf_getvarcount.sas +49 -0
- package/macros/mf_getvarformat.sas +72 -0
- package/macros/mf_getvarlen.sas +56 -0
- package/macros/mf_getvarlist.sas +74 -0
- package/macros/mf_getvarnum.sas +58 -0
- package/macros/mf_getvartype.sas +52 -0
- package/macros/mf_getxengine.sas +43 -0
- package/macros/mf_increment.sas +29 -0
- package/macros/mf_isblank.sas +27 -0
- package/macros/mf_isdir.sas +34 -0
- package/macros/mf_isint.sas +36 -0
- package/macros/mf_islibds.sas +40 -0
- package/macros/mf_loc.sas +29 -0
- package/macros/mf_mkdir.sas +67 -0
- package/macros/mf_mval.sas +19 -0
- package/macros/mf_nobs.sas +26 -0
- package/macros/mf_trimstr.sas +50 -0
- package/macros/mf_uid.sas +21 -0
- package/macros/mf_verifymacvars.sas +72 -0
- package/macros/mf_wordsinstr1andstr2.sas +53 -0
- package/macros/mf_wordsinstr1butnotstr2.sas +54 -0
- package/macros/mf_writefile.sas +67 -0
- package/macros/mp_abort.sas +301 -0
- package/macros/mp_appendfile.sas +57 -0
- package/macros/mp_applyformats.sas +181 -0
- package/macros/mp_assert.sas +56 -0
- package/macros/mp_assertcols.sas +145 -0
- package/macros/mp_assertcolvals.sas +168 -0
- package/macros/mp_assertdsobs.sas +121 -0
- package/macros/mp_assertscope.sas +147 -0
- package/macros/mp_base64copy.sas +117 -0
- package/macros/mp_binarycopy.sas +79 -0
- package/macros/mp_chop.sas +194 -0
- package/macros/mp_cleancsv.sas +78 -0
- package/macros/mp_cntlout.sas +85 -0
- package/macros/mp_copyfolder.sas +84 -0
- package/macros/mp_coretable.sas +73 -0
- package/macros/mp_createconstraints.sas +70 -0
- package/macros/mp_createwebservice.sas +32 -0
- package/macros/mp_csv2ds.sas +140 -0
- package/macros/mp_deleteconstraints.sas +52 -0
- package/macros/mp_deletefolder.sas +70 -0
- package/macros/mp_dictionary.sas +52 -0
- package/macros/mp_dirlist.sas +263 -0
- package/macros/mp_distinctfmtvalues.sas +50 -0
- package/macros/mp_dropmembers.sas +46 -0
- package/macros/mp_ds2cards.sas +291 -0
- package/macros/mp_ds2csv.sas +212 -0
- package/macros/mp_ds2ddl.sas +30 -0
- package/macros/mp_ds2fmtds.sas +105 -0
- package/macros/mp_ds2inserts.sas +180 -0
- package/macros/mp_ds2md.sas +103 -0
- package/macros/mp_ds2squeeze.sas +120 -0
- package/macros/mp_dsmeta.sas +110 -0
- package/macros/mp_filtercheck.sas +251 -0
- package/macros/mp_filtergenerate.sas +105 -0
- package/macros/mp_filterstore.sas +254 -0
- package/macros/mp_filtervalidate.sas +105 -0
- package/macros/mp_getcols.sas +67 -0
- package/macros/mp_getconstraints.sas +115 -0
- package/macros/mp_getdbml.sas +335 -0
- package/macros/mp_getddl.sas +400 -0
- package/macros/mp_getformats.sas +124 -0
- package/macros/mp_getmaxvarlengths.sas +130 -0
- package/macros/mp_getpk.sas +281 -0
- package/macros/mp_gsubfile.sas +58 -0
- package/macros/mp_guesspk.sas +332 -0
- package/macros/mp_hashdataset.sas +91 -0
- package/macros/mp_hashdirectory.sas +162 -0
- package/macros/mp_include.sas +106 -0
- package/macros/mp_init.sas +76 -0
- package/macros/mp_jsonout.sas +406 -0
- package/macros/mp_lib2cards.sas +78 -0
- package/macros/mp_lib2inserts.sas +77 -0
- package/macros/mp_loadformat.sas +303 -0
- package/macros/mp_lockanytable.sas +243 -0
- package/macros/mp_lockfilecheck.sas +103 -0
- package/macros/mp_makedata.sas +107 -0
- package/macros/mp_md5.sas +58 -0
- package/macros/mp_perflog.sas +43 -0
- package/macros/mp_prevobs.sas +88 -0
- package/macros/mp_recursivejoin.sas +90 -0
- package/macros/mp_replace.sas +151 -0
- package/macros/mp_reseterror.sas +27 -0
- package/macros/mp_resetoption.sas +39 -0
- package/macros/mp_retainedkey.sas +247 -0
- package/macros/mp_runddl.sas +49 -0
- package/macros/mp_searchcols.sas +103 -0
- package/macros/mp_searchdata.sas +131 -0
- package/macros/mp_setkeyvalue.sas +55 -0
- package/macros/mp_sortinplace.sas +122 -0
- package/macros/mp_stackdiffs.sas +592 -0
- package/macros/mp_storediffs.sas +230 -0
- package/macros/mp_stprequests.sas +74 -0
- package/macros/mp_streamfile.sas +212 -0
- package/macros/mp_testjob.sas +92 -0
- package/macros/mp_testservice.sas +39 -0
- package/macros/mp_testwritespeedlibrary.sas +59 -0
- package/macros/mp_tree.sas +71 -0
- package/macros/mp_unzip.sas +90 -0
- package/macros/mp_updatevarlength.sas +93 -0
- package/macros/mp_validatecol.sas +97 -0
- package/macros/mp_wait4file.sas +35 -0
- package/macros/mp_webin.sas +59 -0
- package/macros/mp_zip.sas +85 -0
- package/package.json +5 -2
- package/scripts/copySASjsCore.ts +36 -0
- package/types/hash.d.ts +9 -0
- package/types/hash.js +3 -0
- package/types/hash.js.map +1 -0
- package/types/index.d.ts +1 -0
- package/types/index.js +1 -0
- package/types/index.js.map +1 -1
- package/utils/index.d.ts +1 -1
- package/utils/index.js +2 -1
- package/utils/index.js.map +1 -1
- package/utils/utils.d.ts +1 -0
- package/utils/utils.js +7 -1
- package/utils/utils.js.map +1 -1
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Asserts the existence (or not) of columns
|
|
4
|
+
@details Useful in the context of writing sasjs tests. The results of the
|
|
5
|
+
test are _appended_ to the &outds. table.
|
|
6
|
+
|
|
7
|
+
Example usage:
|
|
8
|
+
|
|
9
|
+
%mp_assertcols(sashelp.class,
|
|
10
|
+
cols=name age sex,
|
|
11
|
+
test=ALL,
|
|
12
|
+
desc=check all columns exist
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
%mp_assertcols(sashelp.class,
|
|
16
|
+
cols=a b c,
|
|
17
|
+
test=NONE
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
%mp_assertcols(sashelp.class,
|
|
21
|
+
cols=age depth,
|
|
22
|
+
test=ANY
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
<h4> SAS Macros </h4>
|
|
26
|
+
@li mf_existds.sas
|
|
27
|
+
@li mf_existvarlist.sas
|
|
28
|
+
@li mf_getvarlist.sas
|
|
29
|
+
@li mf_wordsinstr1butnotstr2.sas
|
|
30
|
+
@li mp_abort.sas
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@param [in] inds The input library.dataset to test for values
|
|
34
|
+
@param [in] cols= The list of columns to check for
|
|
35
|
+
@param [in] desc= (Testing observations) The user provided test description
|
|
36
|
+
@param [in] test= (ALL) The test to apply. Valid values are:
|
|
37
|
+
@li ALL - Test is a PASS if ALL columns exist in &inds
|
|
38
|
+
@li ANY - Test is a PASS if ANY of the columns exist in &inds
|
|
39
|
+
@li NONE - Test is a PASS if NONE of the columns exist in &inds
|
|
40
|
+
@param [out] outds= (work.test_results) The output dataset to contain the
|
|
41
|
+
results. If it does not exist, it will be created, with the following format:
|
|
42
|
+
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
|
|
43
|
+
|---|---|---|
|
|
44
|
+
|User Provided description|PASS|Column &inds contained ALL columns|
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
<h4> Related Macros </h4>
|
|
48
|
+
@li mp_assertdsobs.sas
|
|
49
|
+
@li mp_assertcolvals.sas
|
|
50
|
+
@li mp_assertdsobs.sas
|
|
51
|
+
|
|
52
|
+
@version 9.2
|
|
53
|
+
@author Allan Bowe
|
|
54
|
+
|
|
55
|
+
**/
|
|
56
|
+
|
|
57
|
+
%macro mp_assertcols(inds,
|
|
58
|
+
cols=0,
|
|
59
|
+
test=ALL,
|
|
60
|
+
desc=0,
|
|
61
|
+
outds=work.test_results
|
|
62
|
+
)/*/STORE SOURCE*/;
|
|
63
|
+
|
|
64
|
+
%mp_abort(iftrue= (&syscc ne 0)
|
|
65
|
+
,mac=&sysmacroname
|
|
66
|
+
,msg=%str(syscc=&syscc - on macro entry)
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
%local lib ds ;
|
|
70
|
+
%let lib=%scan(&inds,1,%str(.));
|
|
71
|
+
%let ds=%scan(&inds,2,%str(.));
|
|
72
|
+
%let cols=%upcase(&cols);
|
|
73
|
+
|
|
74
|
+
%mp_abort(iftrue= (%mf_existds(&lib..&ds)=0)
|
|
75
|
+
,mac=&sysmacroname
|
|
76
|
+
,msg=%str(&lib..&ds not found!)
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
%mp_abort(iftrue= (&cols=0)
|
|
80
|
+
,mac=&sysmacroname
|
|
81
|
+
,msg=%str(No cols provided)
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
%let test=%upcase(&test);
|
|
86
|
+
|
|
87
|
+
%if &test ne ANY and &test ne ALL and &test ne NONE %then %do;
|
|
88
|
+
%mp_abort(
|
|
89
|
+
mac=&sysmacroname,
|
|
90
|
+
msg=%str(Invalid test - &test)
|
|
91
|
+
)
|
|
92
|
+
%end;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* now do the actual test!
|
|
96
|
+
*/
|
|
97
|
+
%local result;
|
|
98
|
+
%if %mf_existVarList(&inds,&cols)=1 %then %let result=ALL;
|
|
99
|
+
%else %do;
|
|
100
|
+
%local targetcols compare;
|
|
101
|
+
%let targetcols=%upcase(%mf_getvarlist(&inds));
|
|
102
|
+
%let compare=%mf_wordsinstr1butnotstr2(
|
|
103
|
+
Str1=&cols,
|
|
104
|
+
Str2=&targetcols
|
|
105
|
+
);
|
|
106
|
+
%if %cmpres(&compare)=%cmpres(&cols) %then %let result=NONE;
|
|
107
|
+
%else %let result=SOME;
|
|
108
|
+
%end;
|
|
109
|
+
|
|
110
|
+
data;
|
|
111
|
+
length test_description $256 test_result $4 test_comments $256;
|
|
112
|
+
test_description=symget('desc');
|
|
113
|
+
if test_description='0'
|
|
114
|
+
then test_description="Testing &inds for existence of &test of: &cols";
|
|
115
|
+
|
|
116
|
+
test_result='FAIL';
|
|
117
|
+
test_comments="&sysmacroname: &inds has &result columns ";
|
|
118
|
+
%if &test=ALL %then %do;
|
|
119
|
+
%if &result=ALL %then %do;
|
|
120
|
+
test_result='PASS';
|
|
121
|
+
%end;
|
|
122
|
+
%end;
|
|
123
|
+
%else %if &test=ANY %then %do;
|
|
124
|
+
%if &result=SOME %then %do;
|
|
125
|
+
test_result='PASS';
|
|
126
|
+
%end;
|
|
127
|
+
%end;
|
|
128
|
+
%else %if &test=NONE %then %do;
|
|
129
|
+
%if &result=NONE %then %do;
|
|
130
|
+
test_result='PASS';
|
|
131
|
+
%end;
|
|
132
|
+
%end;
|
|
133
|
+
%else %do;
|
|
134
|
+
test_comments="&sysmacroname: Unsatisfied test condition - &test";
|
|
135
|
+
%end;
|
|
136
|
+
run;
|
|
137
|
+
|
|
138
|
+
%local ds;
|
|
139
|
+
%let ds=&syslast;
|
|
140
|
+
proc append base=&outds data=&ds;
|
|
141
|
+
run;
|
|
142
|
+
proc sql;
|
|
143
|
+
drop table &ds;
|
|
144
|
+
|
|
145
|
+
%mend mp_assertcols;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Asserts the values in a column
|
|
4
|
+
@details Useful in the context of writing sasjs tests. The results of the
|
|
5
|
+
test are _appended_ to the &outds. table.
|
|
6
|
+
|
|
7
|
+
Example usage:
|
|
8
|
+
|
|
9
|
+
data work.checkds;
|
|
10
|
+
do checkval='Jane','James','Jill';
|
|
11
|
+
output;
|
|
12
|
+
end;
|
|
13
|
+
run;
|
|
14
|
+
%mp_assertcolvals(sashelp.class.name,
|
|
15
|
+
checkvals=work.checkds.checkval,
|
|
16
|
+
desc=At least one value has a match,
|
|
17
|
+
test=ANYVAL
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
data work.check;
|
|
21
|
+
do val='M','F';
|
|
22
|
+
output;
|
|
23
|
+
end;
|
|
24
|
+
run;
|
|
25
|
+
%mp_assertcolvals(sashelp.class.sex,
|
|
26
|
+
checkvals=work.check.val,
|
|
27
|
+
desc=All values have a match,
|
|
28
|
+
test=ALLVALS
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
<h4> SAS Macros </h4>
|
|
32
|
+
@li mf_existds.sas
|
|
33
|
+
@li mf_getuniquename.sas
|
|
34
|
+
@li mf_nobs.sas
|
|
35
|
+
@li mp_abort.sas
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@param [in] indscol The input library.dataset.column to test for values
|
|
39
|
+
@param [in] checkvals= A library.dataset.column value containing a UNIQUE
|
|
40
|
+
list of values to be compared against the source (indscol).
|
|
41
|
+
@param [in] desc= (Testing observations) The user provided test description
|
|
42
|
+
@param [in] test= (ALLVALS) The test to apply. Valid values are:
|
|
43
|
+
@li ALLVALS - Test is a PASS if ALL values have a match in checkvals
|
|
44
|
+
@li ANYVAL - Test is a PASS if at least 1 value has a match in checkvals
|
|
45
|
+
@param [out] outds= (work.test_results) The output dataset to contain the
|
|
46
|
+
results. If it does not exist, it will be created, with the following format:
|
|
47
|
+
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
|
|
48
|
+
|---|---|---|
|
|
49
|
+
|User Provided description|PASS|Column &indscol contained ALL target vals|
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
<h4> Related Macros </h4>
|
|
53
|
+
@li mp_assertdsobs.sas
|
|
54
|
+
|
|
55
|
+
@version 9.2
|
|
56
|
+
@author Allan Bowe
|
|
57
|
+
|
|
58
|
+
**/
|
|
59
|
+
|
|
60
|
+
%macro mp_assertcolvals(indscol,
|
|
61
|
+
checkvals=0,
|
|
62
|
+
test=ALLVALS,
|
|
63
|
+
desc=mp_assertcolvals - no desc provided,
|
|
64
|
+
outds=work.test_results
|
|
65
|
+
)/*/STORE SOURCE*/;
|
|
66
|
+
|
|
67
|
+
%mp_abort(iftrue= (&syscc ne 0)
|
|
68
|
+
,mac=&sysmacroname
|
|
69
|
+
,msg=%str(syscc=&syscc - on macro entry)
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
%local lib ds col clib cds ccol nobs;
|
|
73
|
+
%let lib=%scan(&indscol,1,%str(.));
|
|
74
|
+
%let ds=%scan(&indscol,2,%str(.));
|
|
75
|
+
%let col=%scan(&indscol,3,%str(.));
|
|
76
|
+
%mp_abort(iftrue= (%mf_existds(&lib..&ds)=0)
|
|
77
|
+
,mac=&sysmacroname
|
|
78
|
+
,msg=%str(&lib..&ds not found!)
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
%mp_abort(iftrue= (&checkvals=0)
|
|
82
|
+
,mac=&sysmacroname
|
|
83
|
+
,msg=%str(Set CHECKVALS to a library.dataset.column containing check vals)
|
|
84
|
+
)
|
|
85
|
+
%let clib=%scan(&checkvals,1,%str(.));
|
|
86
|
+
%let cds=%scan(&checkvals,2,%str(.));
|
|
87
|
+
%let ccol=%scan(&checkvals,3,%str(.));
|
|
88
|
+
%mp_abort(iftrue= (%mf_existds(&clib..&cds)=0)
|
|
89
|
+
,mac=&sysmacroname
|
|
90
|
+
,msg=%str(&clib..&cds not found!)
|
|
91
|
+
)
|
|
92
|
+
%let nobs=%mf_nobs(&clib..&cds);
|
|
93
|
+
%mp_abort(iftrue= (&nobs=0)
|
|
94
|
+
,mac=&sysmacroname
|
|
95
|
+
,msg=%str(&clib..&cds is empty!)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
%let test=%upcase(&test);
|
|
99
|
+
|
|
100
|
+
%if &test ne ALLVALS and &test ne ANYVAL %then %do;
|
|
101
|
+
%mp_abort(
|
|
102
|
+
mac=&sysmacroname,
|
|
103
|
+
msg=%str(Invalid test - &test)
|
|
104
|
+
)
|
|
105
|
+
%end;
|
|
106
|
+
|
|
107
|
+
%local result orig;
|
|
108
|
+
%let result=-1;
|
|
109
|
+
%let orig=-1;
|
|
110
|
+
proc sql noprint;
|
|
111
|
+
select count(*) into: result
|
|
112
|
+
from &lib..&ds
|
|
113
|
+
where &col not in (
|
|
114
|
+
select &ccol from &clib..&cds
|
|
115
|
+
);
|
|
116
|
+
select count(*) into: orig from &lib..&ds;
|
|
117
|
+
quit;
|
|
118
|
+
|
|
119
|
+
%local notfound tmp1 tmp2;
|
|
120
|
+
%let tmp1=%mf_getuniquename();
|
|
121
|
+
%let tmp2=%mf_getuniquename();
|
|
122
|
+
|
|
123
|
+
/* this is a bit convoluted - but using sql outobs=10 throws warnings */
|
|
124
|
+
proc sql noprint;
|
|
125
|
+
create view &tmp1 as
|
|
126
|
+
select distinct &col
|
|
127
|
+
from &lib..&ds
|
|
128
|
+
where &col not in (
|
|
129
|
+
select &ccol from &clib..&cds
|
|
130
|
+
);
|
|
131
|
+
data &tmp2;
|
|
132
|
+
set &tmp1;
|
|
133
|
+
if _n_>10 then stop;
|
|
134
|
+
run;
|
|
135
|
+
proc sql;
|
|
136
|
+
select distinct &col into: notfound separated by ' ' from &tmp2;
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
%mp_abort(iftrue= (&syscc ne 0)
|
|
140
|
+
,mac=&sysmacroname
|
|
141
|
+
,msg=%str(syscc=&syscc after macro query)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
data;
|
|
145
|
+
length test_description $256 test_result $4 test_comments $256;
|
|
146
|
+
test_description=symget('desc');
|
|
147
|
+
test_result='FAIL';
|
|
148
|
+
test_comments="&sysmacroname: &lib..&ds..&col has &result values "
|
|
149
|
+
!!"not in &clib..&cds..&ccol.. First 10 vals:"!!symget('notfound');
|
|
150
|
+
%if &test=ANYVAL %then %do;
|
|
151
|
+
if &result < &orig then test_result='PASS';
|
|
152
|
+
%end;
|
|
153
|
+
%else %if &test=ALLVALS %then %do;
|
|
154
|
+
if &result=0 then test_result='PASS';
|
|
155
|
+
%end;
|
|
156
|
+
%else %do;
|
|
157
|
+
test_comments="&sysmacroname: Unsatisfied test condition - &test";
|
|
158
|
+
%end;
|
|
159
|
+
run;
|
|
160
|
+
|
|
161
|
+
%local ds;
|
|
162
|
+
%let ds=&syslast;
|
|
163
|
+
proc append base=&outds data=&ds;
|
|
164
|
+
run;
|
|
165
|
+
proc sql;
|
|
166
|
+
drop table &ds;
|
|
167
|
+
|
|
168
|
+
%mend mp_assertcolvals;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Asserts the number of observations in a dataset
|
|
4
|
+
@details Useful in the context of writing sasjs tests. The results of the
|
|
5
|
+
test are _appended_ to the &outds. table.
|
|
6
|
+
|
|
7
|
+
Example usage:
|
|
8
|
+
|
|
9
|
+
%mp_assertdsobs(sashelp.class) %* tests if any observations are present;
|
|
10
|
+
|
|
11
|
+
%mp_assertdsobs(sashelp.class,test=ATLEAST 10) %* pass if >9 obs present;
|
|
12
|
+
|
|
13
|
+
%mp_assertdsobs(sashelp.class,test=ATMOST 20) %* pass if <21 obs present;
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@param [in] inds input dataset to test for presence of observations
|
|
17
|
+
@param [in] desc= (Testing observations) The user provided test description
|
|
18
|
+
@param [in] test= (HASOBS) The test to apply. Valid values are:
|
|
19
|
+
@li HASOBS - Test is a PASS if the input dataset has any observations
|
|
20
|
+
@li EMPTY - Test is a PASS if input dataset is empty
|
|
21
|
+
@li EQUALS [integer] - Test passes if row count matches the provided integer
|
|
22
|
+
@li ATLEAST [integer] - Test passes if row count is more than or equal to
|
|
23
|
+
the provided integer
|
|
24
|
+
@li ATMOST [integer] - Test passes if row count is less than or equal to
|
|
25
|
+
the provided integer
|
|
26
|
+
@param [out] outds= (work.test_results) The output dataset to contain the
|
|
27
|
+
results. If it does not exist, it will be created, with the following format:
|
|
28
|
+
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
|
|
29
|
+
|---|---|---|
|
|
30
|
+
|User Provided description|PASS|Dataset &inds has XX obs|
|
|
31
|
+
|
|
32
|
+
<h4> SAS Macros </h4>
|
|
33
|
+
@li mf_getuniquename.sas
|
|
34
|
+
@li mf_nobs.sas
|
|
35
|
+
@li mp_abort.sas
|
|
36
|
+
|
|
37
|
+
<h4> Related Macros </h4>
|
|
38
|
+
@li mp_assertcolvals.sas
|
|
39
|
+
@li mp_assert.sas
|
|
40
|
+
@li mp_assertcols.sas
|
|
41
|
+
|
|
42
|
+
@version 9.2
|
|
43
|
+
@author Allan Bowe
|
|
44
|
+
|
|
45
|
+
**/
|
|
46
|
+
|
|
47
|
+
%macro mp_assertdsobs(inds,
|
|
48
|
+
test=HASOBS,
|
|
49
|
+
desc=Testing observations,
|
|
50
|
+
outds=work.test_results
|
|
51
|
+
)/*/STORE SOURCE*/;
|
|
52
|
+
|
|
53
|
+
%local nobs ds;
|
|
54
|
+
%let nobs=%mf_nobs(&inds);
|
|
55
|
+
%let test=%upcase(&test);
|
|
56
|
+
%let ds=%mf_getuniquename(prefix=mp_assertdsobs);
|
|
57
|
+
|
|
58
|
+
%if %substr(&test.xxxxx,1,6)=EQUALS %then %do;
|
|
59
|
+
%let val=%scan(&test,2,%str( ));
|
|
60
|
+
%mp_abort(iftrue= (%DATATYP(&val)=CHAR)
|
|
61
|
+
,mac=&sysmacroname
|
|
62
|
+
,msg=%str(Invalid test - &test, expected EQUALS [integer])
|
|
63
|
+
)
|
|
64
|
+
%let test=EQUALS;
|
|
65
|
+
%end;
|
|
66
|
+
%else %if %substr(&test.xxxxxxx,1,7)=ATLEAST %then %do;
|
|
67
|
+
%let val=%scan(&test,2,%str( ));
|
|
68
|
+
%mp_abort(iftrue= (%DATATYP(&val)=CHAR)
|
|
69
|
+
,mac=&sysmacroname
|
|
70
|
+
,msg=%str(Invalid test - &test, expected ATLEAST [integer])
|
|
71
|
+
)
|
|
72
|
+
%let test=ATLEAST;
|
|
73
|
+
%end;
|
|
74
|
+
%else %if %substr(&test.xxxxxxx,1,7)=ATMOST %then %do;
|
|
75
|
+
%let val=%scan(&test,2,%str( ));
|
|
76
|
+
%mp_abort(iftrue= (%DATATYP(&val)=CHAR)
|
|
77
|
+
,mac=&sysmacroname
|
|
78
|
+
,msg=%str(Invalid test - &test, expected ATMOST [integer])
|
|
79
|
+
)
|
|
80
|
+
%let test=ATMOST;
|
|
81
|
+
%end;
|
|
82
|
+
%else %if &test ne HASOBS and &test ne EMPTY %then %do;
|
|
83
|
+
%mp_abort(
|
|
84
|
+
mac=&sysmacroname,
|
|
85
|
+
msg=%str(Invalid test - &test)
|
|
86
|
+
)
|
|
87
|
+
%end;
|
|
88
|
+
|
|
89
|
+
data &ds;
|
|
90
|
+
length test_description $256 test_result $4 test_comments $256;
|
|
91
|
+
test_description=symget('desc');
|
|
92
|
+
test_result='FAIL';
|
|
93
|
+
test_comments="&sysmacroname: Dataset &inds has &nobs observations.";
|
|
94
|
+
test_comments=test_comments!!" Test was "!!symget('test');
|
|
95
|
+
%if &test=HASOBS %then %do;
|
|
96
|
+
if &nobs>0 then test_result='PASS';
|
|
97
|
+
%end;
|
|
98
|
+
%else %if &test=EMPTY %then %do;
|
|
99
|
+
if &nobs=0 then test_result='PASS';
|
|
100
|
+
%end;
|
|
101
|
+
%else %if &test=EQUALS %then %do;
|
|
102
|
+
if &nobs=&val then test_result='PASS';
|
|
103
|
+
%end;
|
|
104
|
+
%else %if &test=ATLEAST %then %do;
|
|
105
|
+
if &nobs ge &val then test_result='PASS';
|
|
106
|
+
%end;
|
|
107
|
+
%else %if &test=ATMOST %then %do;
|
|
108
|
+
if &nobs le &val then test_result='PASS';
|
|
109
|
+
%end;
|
|
110
|
+
%else %do;
|
|
111
|
+
test_comments="&sysmacroname: Unsatisfied test condition - &test";
|
|
112
|
+
%end;
|
|
113
|
+
run;
|
|
114
|
+
|
|
115
|
+
proc append base=&outds data=&ds;
|
|
116
|
+
run;
|
|
117
|
+
|
|
118
|
+
proc sql;
|
|
119
|
+
drop table &ds;
|
|
120
|
+
|
|
121
|
+
%mend mp_assertdsobs;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Used to capture scope leakage of macro variables
|
|
4
|
+
@details
|
|
5
|
+
|
|
6
|
+
A common 'difficult to detect' bug in macros is where a nested macro
|
|
7
|
+
over-writes variables in a higher level macro.
|
|
8
|
+
|
|
9
|
+
This assertion takes a snapshot of the macro variables before and after
|
|
10
|
+
a macro invocation. Differences are captured in the `&outds` table. This
|
|
11
|
+
makes it easy to detect whether any macro variables were modified or
|
|
12
|
+
changed.
|
|
13
|
+
|
|
14
|
+
The following variables are NOT tested (as they are known, global variables
|
|
15
|
+
used in SASjs):
|
|
16
|
+
|
|
17
|
+
@li &sasjs_prefix._FUNCTIONS
|
|
18
|
+
|
|
19
|
+
Global variables are initialised in mp_init.sas - which will also trigger
|
|
20
|
+
"strict mode" in your SAS session. Whilst this is a default in SASjs
|
|
21
|
+
produced apps, if you prefer not to use this mode, simply instantiate the
|
|
22
|
+
following variable to prevent the macro from running: `SASJS_PREFIX`
|
|
23
|
+
|
|
24
|
+
Example usage:
|
|
25
|
+
|
|
26
|
+
%mp_assertscope(SNAPSHOT)
|
|
27
|
+
|
|
28
|
+
%let oops=I did it again;
|
|
29
|
+
|
|
30
|
+
%mp_assertscope(COMPARE,
|
|
31
|
+
desc=Checking macro variables against previous snapshot
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
This macro is designed to work alongside `sasjs test` - for more information
|
|
35
|
+
about this facility, visit [cli.sasjs.io/test](https://cli.sasjs.io/test).
|
|
36
|
+
|
|
37
|
+
@param [in] action (SNAPSHOT) The action to take. Valid values:
|
|
38
|
+
@li SNAPSHOT - take a copy of the current macro variables
|
|
39
|
+
@li COMPARE - compare the current macro variables against previous values
|
|
40
|
+
@param [in] scope= (GLOBAL) The scope of the variables to be checked. This
|
|
41
|
+
corresponds to the values in the SCOPE column in `sashelp.vmacro`.
|
|
42
|
+
@param [in] desc= (Testing scope leakage) The user provided test description
|
|
43
|
+
@param [in] ignorelist= Provide a list of macro variable names to ignore from
|
|
44
|
+
the comparison
|
|
45
|
+
@param [in,out] scopeds= (work.mp_assertscope) The dataset to contain the
|
|
46
|
+
scope snapshot
|
|
47
|
+
@param [out] outds= (work.test_results) The output dataset to contain the
|
|
48
|
+
results. If it does not exist, it will be created, with the following format:
|
|
49
|
+
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
|
|
50
|
+
|---|---|---|
|
|
51
|
+
|User Provided description|PASS|No out of scope variables created or modified|
|
|
52
|
+
|
|
53
|
+
<h4> SAS Macros </h4>
|
|
54
|
+
@li mf_getquotedstr.sas
|
|
55
|
+
@li mp_init.sas
|
|
56
|
+
|
|
57
|
+
<h4> Related Macros </h4>
|
|
58
|
+
@li mp_assert.sas
|
|
59
|
+
@li mp_assertcols.sas
|
|
60
|
+
@li mp_assertcolvals.sas
|
|
61
|
+
@li mp_assertdsobs.sas
|
|
62
|
+
@li mp_assertscope.test.sas
|
|
63
|
+
|
|
64
|
+
@version 9.2
|
|
65
|
+
@author Allan Bowe
|
|
66
|
+
|
|
67
|
+
**/
|
|
68
|
+
|
|
69
|
+
%macro mp_assertscope(action,
|
|
70
|
+
desc=Testing Scope Leakage,
|
|
71
|
+
scope=GLOBAL,
|
|
72
|
+
scopeds=work.mp_assertscope,
|
|
73
|
+
ignorelist=,
|
|
74
|
+
outds=work.test_results
|
|
75
|
+
)/*/STORE SOURCE*/;
|
|
76
|
+
%local ds test_result test_comments del add mod ilist;
|
|
77
|
+
%let ilist=%upcase(&sasjs_prefix._FUNCTIONS SYS_PROCHTTP_STATUS_CODE
|
|
78
|
+
SYS_PROCHTTP_STATUS_CODE SYS_PROCHTTP_STATUS_PHRASE &ignorelist);
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* this sets up the global vars, it will also enter STRICT mode. If this
|
|
82
|
+
* behaviour is not desired, simply initiate the following global macro
|
|
83
|
+
* variable to prevent the macro from running: SASJS_PREFIX
|
|
84
|
+
*/
|
|
85
|
+
%mp_init()
|
|
86
|
+
|
|
87
|
+
/* get current variables */
|
|
88
|
+
%if &action=SNAPSHOT %then %do;
|
|
89
|
+
proc sql;
|
|
90
|
+
create table &scopeds as
|
|
91
|
+
select name,offset,value
|
|
92
|
+
from dictionary.macros
|
|
93
|
+
where scope="&scope" and upcase(name) not in (%mf_getquotedstr(&ilist))
|
|
94
|
+
order by name,offset;
|
|
95
|
+
%end;
|
|
96
|
+
%else %if &action=COMPARE %then %do;
|
|
97
|
+
|
|
98
|
+
proc sql;
|
|
99
|
+
create table _data_ as
|
|
100
|
+
select name,offset,value
|
|
101
|
+
from dictionary.macros
|
|
102
|
+
where scope="&scope" and upcase(name) not in (%mf_getquotedstr(&ilist))
|
|
103
|
+
order by name,offset;
|
|
104
|
+
|
|
105
|
+
%let ds=&syslast;
|
|
106
|
+
|
|
107
|
+
proc compare
|
|
108
|
+
base=&scopeds(where=(upcase(name) not in (%mf_getquotedstr(&ilist))))
|
|
109
|
+
compare=&ds noprint;
|
|
110
|
+
run;
|
|
111
|
+
|
|
112
|
+
%if &sysinfo=0 %then %do;
|
|
113
|
+
%let test_result=PASS;
|
|
114
|
+
%let test_comments=&scope Variables Unmodified;
|
|
115
|
+
%end;
|
|
116
|
+
%else %do;
|
|
117
|
+
proc sql noprint undo_policy=none;
|
|
118
|
+
select distinct name into: del separated by ' ' from &scopeds
|
|
119
|
+
where name not in (select name from &ds);
|
|
120
|
+
select distinct name into: add separated by ' ' from &ds
|
|
121
|
+
where name not in (select name from &scopeds);
|
|
122
|
+
select distinct a.name into: mod separated by ' '
|
|
123
|
+
from &scopeds a
|
|
124
|
+
inner join &ds b
|
|
125
|
+
on a.name=b.name
|
|
126
|
+
and a.offset=b.offset
|
|
127
|
+
where a.value ne b.value;
|
|
128
|
+
%let test_result=FAIL;
|
|
129
|
+
%let test_comments=%str(Mod:(&mod) Add:(&add) Del:(&del));
|
|
130
|
+
%end;
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
data ;
|
|
134
|
+
length test_description $256 test_result $4 test_comments $256;
|
|
135
|
+
test_description=symget('desc');
|
|
136
|
+
test_comments=symget('test_comments');
|
|
137
|
+
test_result=symget('test_result');
|
|
138
|
+
run;
|
|
139
|
+
|
|
140
|
+
%let ds=&syslast;
|
|
141
|
+
proc append base=&outds data=&ds;
|
|
142
|
+
run;
|
|
143
|
+
proc sql;
|
|
144
|
+
drop table &ds;
|
|
145
|
+
%end;
|
|
146
|
+
|
|
147
|
+
%mend mp_assertscope;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Convert a file to/from base64 format
|
|
4
|
+
@details Creates a new version of a file either encoded or decoded using
|
|
5
|
+
Base64. Inspired by this post by Michael Dixon:
|
|
6
|
+
https://support.selerity.com.au/hc/en-us/articles/223345708-Tip-SAS-and-Base64
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
|
|
10
|
+
filename tmp temp;
|
|
11
|
+
data _null_;
|
|
12
|
+
file tmp;
|
|
13
|
+
put 'base ik ally';
|
|
14
|
+
run;
|
|
15
|
+
%mp_base64copy(inref=tmp, outref=myref, action=ENCODE)
|
|
16
|
+
|
|
17
|
+
data _null_;
|
|
18
|
+
infile myref;
|
|
19
|
+
input;
|
|
20
|
+
put _infile_;
|
|
21
|
+
run;
|
|
22
|
+
|
|
23
|
+
%mp_base64copy(inref=myref, outref=mynewref, action=DECODE)
|
|
24
|
+
|
|
25
|
+
data _null_;
|
|
26
|
+
infile mynewref;
|
|
27
|
+
input;
|
|
28
|
+
put _infile_;
|
|
29
|
+
run;
|
|
30
|
+
|
|
31
|
+
@param [in] inref= Fileref of the input file (should exist)
|
|
32
|
+
@param [out] outref= Output fileref. If it does not exist, it is created.
|
|
33
|
+
@param [in] action= (ENCODE) The action to take. Valid values:
|
|
34
|
+
@li ENCODE - Convert the file to base64 format
|
|
35
|
+
@li DECODE - Decode the file from base64 format
|
|
36
|
+
|
|
37
|
+
@version 9.2
|
|
38
|
+
@author Allan Bowe, source: https://github.com/sasjs/core
|
|
39
|
+
|
|
40
|
+
<h4> SAS Macros </h4>
|
|
41
|
+
@li mp_abort.sas
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
**/
|
|
45
|
+
|
|
46
|
+
%macro mp_base64copy(
|
|
47
|
+
inref=0,
|
|
48
|
+
outref=0,
|
|
49
|
+
action=ENCODE
|
|
50
|
+
)/*/STORE SOURCE*/;
|
|
51
|
+
|
|
52
|
+
%let inref=%upcase(&inref);
|
|
53
|
+
%let outref=%upcase(&outref);
|
|
54
|
+
%let action=%upcase(&action);
|
|
55
|
+
%local infound outfound;
|
|
56
|
+
%let infound=0;
|
|
57
|
+
%let outfound=0;
|
|
58
|
+
data _null_;
|
|
59
|
+
set sashelp.vextfl(where=(fileref="&inref" or fileref="&outref"));
|
|
60
|
+
if fileref="&inref" then call symputx('infound',1,'l');
|
|
61
|
+
if fileref="&outref" then call symputx('outfound',1,'l');
|
|
62
|
+
run;
|
|
63
|
+
|
|
64
|
+
%mp_abort(iftrue= (&infound=0)
|
|
65
|
+
,mac=&sysmacroname
|
|
66
|
+
,msg=%str(INREF &inref NOT FOUND!)
|
|
67
|
+
)
|
|
68
|
+
%mp_abort(iftrue= (&outref=0)
|
|
69
|
+
,mac=&sysmacroname
|
|
70
|
+
,msg=%str(OUTREF NOT PROVIDED!)
|
|
71
|
+
)
|
|
72
|
+
%mp_abort(iftrue= (&action ne ENCODE and &action ne DECODE)
|
|
73
|
+
,mac=&sysmacroname
|
|
74
|
+
,msg=%str(Invalid action! Should be ENCODE OR DECODE)
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
%if &outfound=0 %then %do;
|
|
78
|
+
filename &outref temp lrecl=2097088;
|
|
79
|
+
%end;
|
|
80
|
+
|
|
81
|
+
%if &action=ENCODE %then %do;
|
|
82
|
+
data _null_;
|
|
83
|
+
length b64 $ 76 line $ 57;
|
|
84
|
+
retain line "";
|
|
85
|
+
infile &inref recfm=F lrecl= 1 end=eof;
|
|
86
|
+
input @1 stream $char1.;
|
|
87
|
+
file &outref recfm=N;
|
|
88
|
+
substr(line,(_N_-(CEIL(_N_/57)-1)*57),1) = byte(rank(stream));
|
|
89
|
+
if mod(_N_,57)=0 or EOF then do;
|
|
90
|
+
if eof then b64=put(trim(line),$base64X76.);
|
|
91
|
+
else b64=put(line, $base64X76.);
|
|
92
|
+
put b64 + (-1) @;
|
|
93
|
+
line="";
|
|
94
|
+
end;
|
|
95
|
+
run;
|
|
96
|
+
%end;
|
|
97
|
+
%else %if &action=DECODE %then %do;
|
|
98
|
+
data _null_;
|
|
99
|
+
length filein 8 fileout 8;
|
|
100
|
+
filein = fopen("&inref",'I',4,'B');
|
|
101
|
+
fileout = fopen("&outref",'O',3,'B');
|
|
102
|
+
char= '20'x;
|
|
103
|
+
do while(fread(filein)=0);
|
|
104
|
+
length raw $4;
|
|
105
|
+
do i=1 to 4;
|
|
106
|
+
rc=fget(filein,char,1);
|
|
107
|
+
substr(raw,i,1)=char;
|
|
108
|
+
end;
|
|
109
|
+
rc = fput(fileout,input(raw,$base64X4.));
|
|
110
|
+
rc = fwrite(fileout);
|
|
111
|
+
end;
|
|
112
|
+
rc = fclose(filein);
|
|
113
|
+
rc = fclose(fileout);
|
|
114
|
+
run;
|
|
115
|
+
%end;
|
|
116
|
+
|
|
117
|
+
%mend mp_base64copy;
|