fix(mpe_abortmessage): read program and state by name, and trim the prefix
Three things, one of them a bug the review question uncovered. - msgvar is passed explicitly at the call site, so renaming the message variable fails loudly instead of quietly producing an abort text with no child message. - program and state are read with symget rather than interpolated into the data step, so a % or & in an endpoint name can no longer be macro-resolved, and a quote cannot break the literal. - _prefix is trimmed before the message is appended. Declared as a character variable it arrives padded to length, so the child's message was landing past the buffer end and disappearing - the wrapper text was right and the message was simply not there. That showed up as the comma and blank cases failing while the "names the program state" case passed.
This commit is contained in:
1 parent
7b09ade97c
commit
68b89caf63
3 files changed
+18
-6
No files matched your search
@@ -28,12 +28,20 @@
|
||||
data _null_;
|
||||
/* explicit lengths: an inline symget() is typed by the data step default,
|
||||
which truncates a long child message */
|
||||
length _msg $32767 _out $32767;
|
||||
length _prefix $32767 _msg $32767 _out $32767;
|
||||
/* every value is read by name: interpolating program or state into this
|
||||
data step would let a %, & or quote in an endpoint name be macro-resolved
|
||||
or break the literal */
|
||||
/* cat, not cats: cats strips the blanks around each argument, which would
|
||||
close up the space before 'did' */
|
||||
_prefix=cat(symget('program'),' did not complete: state=',symget('state'));
|
||||
_msg=symget("&msgvar");
|
||||
/* plain concatenation, not CATX: the child's message is kept byte for
|
||||
byte, blank and all, so an exact comparison downstream can rely on it */
|
||||
if missing(_msg) then _out="&program did not complete: state=&state";
|
||||
else _out="&program did not complete: state=&state "||_msg;
|
||||
/* trim the prefix: it is declared as a character variable, so it arrives
|
||||
padded to length and the child's message would land past the buffer end */
|
||||
if missing(_msg) then _out=trim(_prefix);
|
||||
else _out=trim(_prefix)||' '||_msg;
|
||||
call symputx('_mpe_abortmsg',_out,'g');
|
||||
run;
|
||||
%mend mpe_abortmessage;
|
||||
@@ -154,7 +154,7 @@
|
||||
mpe_abortmessage, which builds the text so a comma in the child's message
|
||||
cannot split the mp_abort call into extra parameters, and owns the global
|
||||
it writes the text to (_mpe_abortmsg) rather than taking a name */
|
||||
%mpe_abortmessage(program=&program, state=&state)
|
||||
%mpe_abortmessage(program=&program, state=&state, msgvar=msg)
|
||||
%mp_abort(
|
||||
iftrue=(1=1)
|
||||
,mac=&sysmacroname
|
||||
|
||||
@@ -134,13 +134,17 @@ run;
|
||||
* taking its value - a value argument would be split before the macro ran.
|
||||
*/
|
||||
/* a plain %let: %str would store its mask characters in the value, and the
|
||||
macro reads the value with symget() */
|
||||
macro reads the value with symget(). The same text is kept in csvmsg so both
|
||||
sides of the comparison below go through %superq - the macro's value carries
|
||||
its own quoting around the comma, and comparing that against a %str-masked
|
||||
literal does not match */
|
||||
%let msg=alpha, beta and gamma are not valid;
|
||||
%let csvmsg=alpha, beta and gamma are not valid;
|
||||
|
||||
%mpe_abortmessage(program=/services/example, state=ABORTED)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%index(%superq(_mpe_abortmsg),%str(alpha, beta and gamma are not valid))>0),
|
||||
iftrue=(%index(%superq(_mpe_abortmsg),%superq(csvmsg))>0),
|
||||
desc=%str(Checking a comma in the child message survives into the abort text),
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user