Four stray insert lines and a missing one left the MPE_VALIDATIONS block for
MPE_DERIVED malformed, so makedata aborted and the control table was never
created - found by building the library on a real estate.
The format assertions in the test now read the emitted statement, because proc
contents on a view does not report a format in full ('DATE' for DATE9.).
297 lines
6.5 KiB
SAS
297 lines
6.5 KiB
SAS
/**
|
|
@file
|
|
@brief testing the mpe_derived macro
|
|
|
|
@details Covers the generated statements, the view, the metadata the callers
|
|
consume, and the dependency rules.
|
|
|
|
The configuration checks abort the run when they fail, so they are not
|
|
exercised here - this suite covers the behaviour that callers depend on.
|
|
|
|
<h4> SAS Macros </h4>
|
|
@li mf_existds.sas
|
|
@li mp_assert.sas
|
|
@li mp_assertcols.sas
|
|
@li mp_assertdsobs.sas
|
|
@li mp_assertscope.sas
|
|
@li mpe_derived.sas
|
|
|
|
@author 4GL Apps Ltd
|
|
@copyright 4GL Apps Ltd. This code may only be used within Data Controller
|
|
and may not be re-distributed or re-sold without the express permission of
|
|
4GL Apps Ltd.
|
|
|
|
**/
|
|
|
|
|
|
/* a source table, and a table with no derived variables */
|
|
data work.class;
|
|
set sashelp.class;
|
|
run;
|
|
data work.noconfig;
|
|
set sashelp.class;
|
|
run;
|
|
|
|
/* clean up any config left behind by an earlier run */
|
|
proc sql;
|
|
delete from &dclib..mpe_derived where upcase(libref)='WORK';
|
|
quit;
|
|
|
|
/**
|
|
* Test 1 - statements, view, chaining
|
|
*/
|
|
|
|
proc sql;
|
|
insert into &dclib..mpe_derived set
|
|
tx_from=0
|
|
,tx_to='31DEC5999:23:59:59'dt
|
|
,libref='WORK'
|
|
,dsn='CLASS'
|
|
,var_nm='BAND'
|
|
,var_deps='AGE'
|
|
,var_type='C'
|
|
,var_len=8
|
|
,var_fmt=''
|
|
,var_label='Age band'
|
|
,var_logic="ifc(AGE>13,'TEEN','JUNIOR')"
|
|
,active=1;
|
|
insert into &dclib..mpe_derived set
|
|
tx_from=0
|
|
,tx_to='31DEC5999:23:59:59'dt
|
|
,libref='WORK'
|
|
,dsn='CLASS'
|
|
,var_nm='BAND_AGE'
|
|
,var_deps='BAND AGE'
|
|
,var_type='C'
|
|
,var_len=16
|
|
,var_fmt=''
|
|
,var_logic="cats(BAND,'-',AGE)"
|
|
,active=1;
|
|
insert into &dclib..mpe_derived set
|
|
tx_from=0
|
|
,tx_to='31DEC5999:23:59:59'dt
|
|
,libref='WORK'
|
|
,dsn='CLASS'
|
|
,var_nm='DOUBLE_AGE'
|
|
,var_deps='AGE'
|
|
,var_type='N'
|
|
,var_len=8
|
|
,var_fmt=''
|
|
,var_logic='AGE * 2'
|
|
,active=1;
|
|
quit;
|
|
|
|
/* lower case libds on purpose - the config match must be case insensitive */
|
|
/* the setup SQL above creates the SQL automatic macro variables, so the
|
|
snapshot is taken here - the check is for the macro's own leakage */
|
|
%mp_assertscope(SNAPSHOT)
|
|
|
|
%mpe_derived(libds=work.class,
|
|
dclib=&dclib,
|
|
outref=derref1,
|
|
outview=work.vw_derived,
|
|
outvars=work.derived_vars
|
|
)
|
|
|
|
%mp_assertdsobs(work.derived_vars,
|
|
desc=Test1 - one row per derived variable,
|
|
test=EQUALS 3,
|
|
outds=work.test_results
|
|
)
|
|
|
|
%mp_assertcols(work.vw_derived,
|
|
desc=Test1 - the view carries the derived columns,
|
|
cols=BAND BAND_AGE DOUBLE_AGE,
|
|
test=ALL,
|
|
outds=work.test_results
|
|
)
|
|
|
|
data work.chk1;
|
|
set work.vw_derived;
|
|
where BAND='TEEN';
|
|
run;
|
|
%mp_assertdsobs(work.chk1,
|
|
desc=Test1 - the filter on a derived column selects rows,
|
|
test=HASOBS,
|
|
outds=work.test_results
|
|
)
|
|
|
|
/* the chained value only resolves if the statements run in order */
|
|
data work.chk1b;
|
|
set work.vw_derived;
|
|
where BAND='TEEN';
|
|
if BAND_AGE ne cats(BAND,'-',AGE) then output;
|
|
run;
|
|
%mp_assertdsobs(work.chk1b,
|
|
desc=Test1 - a derived variable can reference an earlier one,
|
|
test=EQUALS 0,
|
|
outds=work.test_results
|
|
)
|
|
|
|
data work.chk1c;
|
|
set work.vw_derived;
|
|
if DOUBLE_AGE ne AGE*2 then output;
|
|
run;
|
|
%mp_assertdsobs(work.chk1c,
|
|
desc=Test1 - the numeric derived value is correct,
|
|
test=EQUALS 0,
|
|
outds=work.test_results
|
|
)
|
|
|
|
/* proc contents on a view reports a format name without its width, so the
|
|
format is compared on the name only */
|
|
proc contents noprint data=work.vw_derived
|
|
out=work.vw_cols(keep=name label format);
|
|
run;
|
|
data work.chk1d;
|
|
set work.vw_cols;
|
|
where name='BAND' and label='Age band' and format=:'$';
|
|
run;
|
|
%mp_assertdsobs(work.chk1d,
|
|
desc=Test1 - the label and default format are applied,
|
|
test=EQUALS 1,
|
|
outds=work.test_results
|
|
)
|
|
|
|
/**
|
|
* Test 2 - a configured format is used as-is
|
|
*/
|
|
|
|
proc sql;
|
|
insert into &dclib..mpe_derived set
|
|
tx_from=0
|
|
,tx_to='31DEC5999:23:59:59'dt
|
|
,libref='WORK'
|
|
,dsn='CLASS'
|
|
,var_nm='FLAG_DT'
|
|
,var_deps=''
|
|
,var_type='N'
|
|
,var_len=8
|
|
,var_fmt='DATE9.'
|
|
,var_logic='mdy(1,1,2020)'
|
|
,active=1;
|
|
quit;
|
|
|
|
%mpe_derived(libds=WORK.CLASS,
|
|
dclib=&dclib,
|
|
outref=derref2,
|
|
outview=work.vw_derived2,
|
|
outvars=work.derived_vars2
|
|
)
|
|
|
|
/* asserted on the emitted statement - proc contents on a view does not
|
|
report the format in full */
|
|
data work.chk2;
|
|
infile derref2 truncover;
|
|
input line $4000.;
|
|
if index(line,'format=DATE9.')>0 then output;
|
|
run;
|
|
%mp_assertdsobs(work.chk2,
|
|
desc=Test2 - the configured format is applied,
|
|
test=EQUALS 1,
|
|
outds=work.test_results
|
|
)
|
|
|
|
data work.chk2b;
|
|
set work.vw_derived2;
|
|
if flag_dt ne mdy(1,1,2020) then output;
|
|
stop;
|
|
run;
|
|
%mp_assertdsobs(work.chk2b,
|
|
desc=Test2 - the numeric value is correct,
|
|
test=EQUALS 0,
|
|
outds=work.test_results
|
|
)
|
|
|
|
/**
|
|
* Test 3 - no configuration
|
|
*/
|
|
|
|
%mpe_derived(libds=WORK.NOCONFIG,
|
|
dclib=&dclib,
|
|
outref=derref3,
|
|
outview=work.vw_derived3,
|
|
outvars=work.derived_vars3
|
|
)
|
|
|
|
%mp_assertdsobs(work.derived_vars3,
|
|
desc=Test3 - no derived variables returned,
|
|
test=EQUALS 0,
|
|
outds=work.test_results
|
|
)
|
|
|
|
%mp_assert(iftrue=(%mf_existds(libds=work.vw_derived3)=0),
|
|
desc=Test3 - no view is built when there is no config,
|
|
outds=work.test_results
|
|
)
|
|
|
|
data _null_;
|
|
infile derref3 end=eof;
|
|
input;
|
|
if not eof or cats(_infile_) ne '' then do;
|
|
putlog 'ERR' 'OR: derived statement file is not empty';
|
|
call symputx('emptycheck','0','l');
|
|
end;
|
|
run;
|
|
%mp_assert(iftrue=(%symexist(emptycheck)=0),
|
|
desc=Test3 - no statements are generated,
|
|
outds=work.test_results
|
|
)
|
|
|
|
/**
|
|
* Test 4 - the wrapper scopes the config: only the current, active row of a
|
|
* table is used
|
|
*/
|
|
|
|
proc sql;
|
|
insert into &dclib..mpe_derived set
|
|
tx_from=0
|
|
,tx_to='01JAN2020:00:00:00'dt
|
|
,libref='WORK'
|
|
,dsn='CLASS'
|
|
,var_nm='CLOSED_OFF'
|
|
,var_deps='AGE'
|
|
,var_type='N'
|
|
,var_len=8
|
|
,var_fmt=''
|
|
,var_logic='AGE + 1'
|
|
,active=1;
|
|
insert into &dclib..mpe_derived set
|
|
tx_from=0
|
|
,tx_to='31DEC5999:23:59:59'dt
|
|
,libref='WORK'
|
|
,dsn='CLASS'
|
|
,var_nm='NOT_ACTIVE'
|
|
,var_deps='AGE'
|
|
,var_type='N'
|
|
,var_len=8
|
|
,var_fmt=''
|
|
,var_logic='AGE + 2'
|
|
,active=0;
|
|
quit;
|
|
|
|
%mpe_derived(libds=WORK.CLASS,
|
|
dclib=&dclib,
|
|
outref=derref4,
|
|
outview=work.vw_derived4,
|
|
outvars=work.derived_vars4
|
|
)
|
|
|
|
data work.chk4;
|
|
set work.derived_vars4;
|
|
where name in ('CLOSED_OFF','NOT_ACTIVE');
|
|
run;
|
|
|
|
%mp_assertdsobs(work.chk4,
|
|
desc=Test4 - a closed version and an inactive row are ignored,
|
|
test=EQUALS 0,
|
|
outds=work.test_results
|
|
)
|
|
|
|
/* tidy up so repeated runs start clean */
|
|
proc sql;
|
|
delete from &dclib..mpe_derived where upcase(libref)='WORK';
|
|
quit;
|
|
|
|
%mp_assertscope(COMPARE,ignorelist=_SASJS_WEBOUT_HEADERS,outds=work.test_results)
|