fix(mpe_assert_abort): take the abort's own MSG, tolerate a missing response

Three defects in the scan:

- 's/.*"MSG" *: *"([^"]*)".*/$1/' is greedy, so a response carrying more
  than one MSG compared the last one rather than the abort's.  The pattern is
  now matched with prxmatch and the capture read with prxposn, which returns
  the first match at or after the sasjsAbort marker.
- the whole response was concatenated into $32767, so a longer response
  truncated silently and an abort near the tail was missed.  The file is now
  scanned a line at a time and the scan stops at the first MSG.
- 'infile &outref' had no fileref check, so a child that died before writing a
  response raised an unassigned-fileref error instead of recording a FAIL.
This commit is contained in:
dc@4gl.io committed 2026-10-27 14:49:55 +00:00
1 parent a03638667f
commit 0a52d20c5f
1 file changed
+38 -16
+38 -16
View File
@@ -7,6 +7,14 @@
match it, so the assertion proves the service aborted for the expected reason
rather than for any reason at all.
The response is scanned a line at a time and only the first MSG that follows
the sasjsAbort marker is taken: a single 32767 buffer both truncated longer
responses and, with a leading `.*`, returned the last MSG in the text rather
than the abort's own.
An unassigned outref (a child that died before writing a response) records a
FAIL rather than raising an unassigned-fileref error in the data step.
Usage:
%mpe_execute(&_program,
@@ -33,31 +41,45 @@
**/
%macro mpe_assert_abort(desc, outref, msg=0, outds=work.test_results);
%local ok seen;
%local ok seen abortseen;
%let ok=0;
%let seen=;
%let abortseen=0;
%if %sysfunc(fileref(&outref))>0 %then %do;
%put WARNING: &sysmacroname: fileref &outref is not assigned - the child left no response to read;
%end;
%else %do;
data _null_;
length resp $32767 got $256;
retain resp '';
length line $32767 got $256;
retain prxid 0 inabort 0 found 0 abortseen 0;
if _n_=1 then prxid=prxparse('/"MSG" *: *"([^"]*)"/');
infile &outref end=eof lrecl=32767;
/* the abort object can span lines, so read the whole response first */
do while(not eof);
input;
resp=cats(resp,_infile_);
end;
if index(resp,'sasjsAbort')>0 then do;
if prxmatch('/"MSG" *: *"([^"]*)"/',resp) then
got=prxchange('s/.*"MSG" *: *"([^"]*)".*/$1/',-1,resp);
call symputx('seen',got);
%if "%superq(msg)" ne "0" %then %do;
if got="%superq(msg)" then call symputx('ok',1);
%end;
%else %do;
call symputx('ok',1);
%end;
line=_infile_;
if index(line,'sasjsAbort')>0 then do;
abortseen=1;
inabort=1;
end;
/* first MSG at or after the abort marker - stops at the first found */
if inabort=1 and found=0 then do;
if prxmatch(prxid,line)>0 then do;
got=prxposn(prxid,1,line);
found=1;
end;
end;
end;
call symputx('abortseen',abortseen,'l');
if found=1 then call symputx('seen',got,'l');
stop;
run;
%end;
%if &abortseen=1 %then %do;
%if "%superq(msg)"="0" %then %let ok=1;
%else %if "&seen"="%superq(msg)" %then %let ok=1;
%end;
%if "&seen" ne "" %then %put NOTE: &sysmacroname: sasjsAbort MSG=&seen;
%mp_assert(