SASUnit Examples  Version 1.3.0
_reporttcghtml.sas
Go to the documentation of this file.
1 
32 %macro _reporttcghtml(i_macroName=
33  ,i_macroLocation=
34  ,i_mCoverageName=
35  ,i_mCoverageLocation=
36  ,o_outputFile=
37  ,o_outputPath=
38  ,o_resVarName=
39  ,o_html=0
40  );
41 
42  %local l_MacroName;
43  %local l_MCoverageName;
44  %local l_linesize;
45 
46  %let l_MacroName=%lowcase(&i_macroName.);
47  %let l_MCoverageName=%lowcase(&i_mCoverageName.);
48 
49  /*** Check existence of input files */
50  %IF (NOT %SYSFUNC(FILEEXIST(&i_mCoverageLocation./&l_MCoverageName.)) OR &l_MCoverageName=) %THEN %DO;
51  %PUT ERROR(SASUNIT): Input file with coverage data does not exist.;
52  %GOTO _macExit;
53  %END;
54  %IF (NOT %SYSFUNC(FILEEXIST(&i_macroLocation./&l_MacroName.)) OR &l_MacroName=) %THEN %DO;
55  %PUT ERROR(SASUNIT): Input file with macro code does not exist.;
56  %GOTO _macExit;
57  %END;
58 
59  /*** Read records from flat file and keep only those of given macro ***/
60  data WORK._MCoverage1 (where=(upcase (MacName)="%scan(%upcase(&l_MacroName.),1,.)"));
61  length MacName $40;
62  infile "&i_mCoverageLocation./&l_MCoverageName.";
63  input;
64  RecordType = input (scan (_INFILE_, 1, ' '), ??8.);
65  FirstLine = input (scan (_INFILE_, 2, ' '), ??8.);
66  LastLine = input (scan (_INFILE_, 3, ' '), ??8.);
67  MacName = scan (_INFILE_, 4, ' ');
68  run;
69 
70  /*** Keep only one record per combination of record type first_line and last_line ***/
71  proc sort data=WORK._MCoverage1 out=WORK._MCoverage3 nodupkey;
72  by Firstline RecordType LastLine;
73  run;
74 
75  /*** Get the covered rows of the macro ***/;
76  data WORK._MCoverage4;
77  set WORK._MCoverage3;
78 
79  /*** Keep value of last record the detect changes from one observation to the other ***/
80  lag_LastLine = lag (LastLine);
81  lag_FirstLine = lag (FirstLine);
82 
83  /*** Generate line numbers for covered contributing rows ***/;
84  /*** 2 3 5 MacName . will be converted to: ***/
85  /*** 2 3 5 MacName 2 ***/
86  /*** 2 3 5 MacName 3 ***/
87  /*** 2 3 5 MacName 4 ***/
88  /*** 2 3 5 MacName 5 ***/
89  if (RecordType in (2)) then do;
90  do _line_ = FirstLine to LastLine;
91  output;
92  end;
93  end;
94 
95  /*** Generate line numbers for non-contributing rows ***/
96  /*** 3 3 5 MacName . will be converted to: ***/
97  /*** 3 3 5 MacName 2 ***/
98  /*** 3 3 5 MacName 3 ***/
99  /*** 3 3 5 MacName 4 ***/
100  /*** 3 3 5 MacName 5 ***/
101  if (RecordType in (3)) then do;
102  do nonEx = FirstLine to LastLine;
103  output;
104  end;
105  end;
106  run;
107 
108  /*** Due to the order of check in above data step, line numbers are not sorted properly ***/
109  /*** Sort lines and generate a second data set with non-contributing rows ***/
110  proc sort data=WORK._MCoverage4 out=WORK._MCoverage5 NODUPKEY;
111  by _line_;
112  run;
113  proc sort data=WORK._MCoverage4 out=WORK._NonEx NODUPKEY;
114  by nonEx;
115  run;
116 
117  /*** Enumerate lines in source code file , flagging all lines before %macro statement with -1 ***/
118  data WORK.rowsOfInputFile;
119  length srcrow $300 nCounter 8;
120  retain srcrow " " nCounter -1;
121  infile "&i_macroLocation./&l_MacroName.";
122  input;
123  srcrow = _INFILE_;
124  if (index (upcase (srcrow), "%nrstr(%MACRO )%scan(%upcase(&l_MacroName.),1,.)")) then do;
125  nCounter=0;
126  end;
127  if (nCounter >= 0) then do;
128  nCounter=nCounter+1;
129  end;
130  run;
131 
132  /*** Read all lines not explicitly marked as covered ***/
133  /*** This can result in selecting no rows! So we need to ***/
134  /*** preassign a value to missinglines. Does zero sound okay? ***/
135  %let MissingLines=0;
136  proc sql noprint;
137  select distinct nCounter into :MissingLines separated by ' ' from WORK.rowsOfInputFile
138  where nCounter not in (select distinct _line_ from WORK._MCoverage5 where _line_ not eq .);
139  quit;
140 
141  /*** If there is an %if-statement with %do and %end an adjustment is made: ***/
142  /*** If the %if-expression is evaluated to false then the corresponding ***/
143  /*** %end is not marked as covered... therefore it is marked manually, ***/
144  /*** same procedure for %mend ***/
145 
146  data WORK.MCoverage /*(keep=srcrow nCounter covered srcRowCopy)*/;
147  length srcrow $300 nCounter 8 srcRowCopy $2048 inExecutedBlock 8 inExecutedMBlock 8;
148  retain srcrow " " nCounter -1 inExecutedBlock 0 inExecutedMBlock 0;
149  label srcrow="Macrostatements";
150  infile "&i_macroLocation./&l_MacroName.";
151  input;
152  if (index (upcase (_INFILE_), "%nrstr(%MACRO )%scan(%upcase(&l_MacroName.),1,.)")) then do;
153  if not(1 in (&MissingLines.)) then do;
154  inExecutedMBlock = inExecutedMBlock + 1;
155  end;
156  nCounter=0;
157  end;
158  if (nCounter >= 0) then do;
159  nCounter=nCounter+1;
160  end;
161  srcrow = cats ("", _INFILE_, "");
162  srcRowCopy = _INFILE_;
163  covered = 1;
164 
165  if (nCounter in (&MissingLines.)) then do;
166  srcrow = cats ("", _INFILE_, "");
167  covered = 0;
168  _temp_row = compress (upcase (_INFILE_));
169  if (length (_temp_row) > 4) then do;
170  if ( (substr (_temp_row,1,5) = '%END;') or (substr (_temp_row,1,5) = '%END ') ) then do;
171  srcrow = cats ("", _INFILE_, "");
172  if inExecutedBlock gt 0 then do;
173  covered = 1;
174  end;
175  inExecutedBlock = inExecutedBlock - 1;
176  end;
177  end;
178  if (length (_temp_row) > 4) then do;
179  if ( (substr (_temp_row,1,6) = '%MEND;') or (substr (_temp_row,1,5) = '%MEND ') ) then do;
180  srcrow = cats ("", _INFILE_, "");
181  if inExecutedMBlock gt 0 then do;
182  covered = 1;
183  end;
184  inExecutedMBlock = inExecutedMBlock - 1;
185  end;
186  end;
187  end;
188  else do;
189  _temp_row = compress (upcase (_INFILE_));
190  if ( (count (_temp_row,'%DO') gt 0) ) then do;
191  inExecutedBlock = inExecutedBlock + count (_temp_row,'%DO');
192  end;
193  if (length (_temp_row) > 4) then do;
194  if ( (substr (_temp_row,1,5) = '%END;') or (substr (_temp_row,1,5) = '%END ') ) then do;
195  inExecutedBlock = inExecutedBlock - 1;
196  end;
197  end;
198  if (length (_temp_row) > 4) then do;
199  if ( (substr (_temp_row,1,6) = '%MEND;') or (substr (_temp_row,1,5) = '%MEND ') ) then do;
200  inExecutedMBlock = inExecutedMBlock - 1;
201  end;
202  end;
203  end;
204  run;
205 
206  /*** Scan rows for comment lines ***/
207  DATA _commentLines;
208  SET Rowsofinputfile(rename=(srcrow=srcline));
209  RETAIN inComment oneLineComment endCommentNextLine commentStartsNextLine
210  ;
211 
212  IF _N_=1 THEN DO;
213  inComment = 0;
214  oneLineComment = 0;
215  endCommentNextLine = 0;
216  commentStartsNextLine = 0;
217  END;
218 
219  IF oneLineComment = 1 THEN DO;
220  inComment = 0;
221  oneLineComment = 0;
222  END;
223  IF endCommentNextLine = 1 THEN DO;
224  inComment = 0;
225  endCommentNextLine =0;
226  END;
227  IF commentStartsNextLine = 1 THEN DO;
228  inComment = 1;
229  commentStartsNextLine =0;
230  END;
231 
232 
233  IF NOT ((index(srcline, '/*') > 0) AND (index(srcline, '*/') > 0))THEN DO;
234  IF index(srcline, '*/') > 0 THEN DO;
235  endCommentNextLine = 1;
236  END;
237  ELSE DO;
238  IF (index(srcline, '/*') GT 0) THEN DO;
239  IF index(compress(srcline,, 's'),'/*') EQ 1 THEN DO;
240  inComment=1;
241  END;
242  commentStartsNextLine=1;
243  END;
244  END;
245  END;
246  ELSE DO;
247  IF index(compress(srcline,, 's'),'/*') EQ 1 AND index(compress(srcline,, 's'),'*/') EQ length(compress(srcline,, 's'))-1 THEN DO;
248  inComment=1;
249  oneLineComment=1;
250  END;
251  ELSE IF count(srcline,'*/') gt count(srcline,'/*') THEN DO;
252  endCommentNextLine = 1;
253  END;
254  END;
255  RUN;
256 
257  /*** Update WORK.MCoverage to flag the non contributing rows identified by MCOVERAGE OPTION ***/
258  proc sql noprint;
259  update WORK.MCoverage
260  set covered = -2
261  where nCounter in (select distinct nonEx from WORK._nonex where nonEx not eq .);
262  update WORK.MCoverage
263  set covered = -1
264  where nCounter in ((select distinct nCounter from _commentLines where inComment eq 1 or compress(compress(srcline),"0D"x) eq ''));
265  quit;
266 
267  /*** Get sets of rows of different different types: covered contributing, non-covered contributing and non contributing ***/
268  proc sql noprint;
269 
270  create table rowNumbersCovered as
271  select distinct nCounter as row from WORK.MCoverage where covered EQ 1;
272  create table rowNumbersNonCovered as
273  select distinct nCounter as row from WORK.MCoverage where covered EQ 0;
274  create table rowNumbersNonCbuting as
275  select distinct nCounter as row from WORK.MCoverage where covered LE -1;
276 
277  select count(*) into:ContributingLocCovered from rowNumbersCovered;
278  select count(*) into:ContributingLocNonCovered from rowNumbersNonCovered;
279  quit;
280 
281  /*** Calculate the percentage of covered contributing rows ***/
282  %let Coverage = %sysevalf (&ContributingLocCovered. / (&ContributingLocCovered. + &ContributingLocNonCovered.));
283  %let CoveragePCT = %sysfunc (putn (&Coverage., nlpct));
284 
285  %if "&o_resVarName." NE "" %then %do;
286  %let &o_resVarName. = %sysevalf(%sysfunc (round(&Coverage.,0.01))*100);
287  %end;
288 
289  data work._tcg_legend;
290  length dummy $3 Text $140;
291  dummy=" ";Text="^{unicode 25CF} ^{style tcgCoveredData &g_nls_reportAuton_018.}";output;
292  dummy=" ";Text="^{unicode 25CF} ^{style tcgNonCoveredData &g_nls_reportAuton_019.}";output;
293  dummy=" ";Text="^{unicode 25CF} ^{style tcgCommentData &g_nls_reportAuton_020.}";output;
294  dummy=" ";Text="^{unicode 25CF} ^{style tcgNonContribData &g_nls_reportAuton_021.}";output;
295  run;
296 
297  data work._tcg_report;
298  LENGTH outputRow pgmSourceColumn $2048 RowNumberOut $200;
299  SET WORK.MCoverage;
300  RowNumber = _N_;
301  outputRow = trim(srcRowCopy);
302  outputRow = tranwrd (outputRow,'^{','^[');
303  outputRow = tranwrd (outputRow,'}',']');
304  %_render_dataColumn (i_sourceColumn=RowNumber
305  ,i_format=Z5.
306  ,i_columnType=tcgCommentData
307  ,o_targetColumn=RowNumberOut
308  );
309  IF covered = -1 THEN DO;
310  %_render_dataColumn (i_sourceColumn=outputRow
311  ,i_columnType=tcgCommentData
312  ,o_targetColumn=pgmSourceColumn
313  );
314  END;
315  ELSE IF covered = 1 THEN DO;
316  %_render_dataColumn (i_sourceColumn=outputRow
317  ,i_columnType=tcgCoveredData
318  ,o_targetColumn=pgmSourceColumn
319  );
320  END;
321  ELSE IF covered = 0 THEN DO;
322  %_render_dataColumn (i_sourceColumn=outputRow
323  ,i_columnType=tcgNonCoveredData
324  ,o_targetColumn=pgmSourceColumn
325  );
326  END;
327  ELSE DO;
328  %_render_dataColumn (i_sourceColumn=outputRow
329  ,i_columnType=tcgNonContribData
330  ,o_targetColumn=pgmSourceColumn
331  );
332  END;
333  RUN;
334 
335  options nocenter;
336  title;footnote;
337 
338  title j=c "&g_nls_reportAuton_005.: &i_macroName";
339  title2 "&g_nls_reportAuton_016.: &CoveragePCT.";
340 
341  %if (&o_html.) %then %do;
342  ods html4 file="&o_outputPath./&o_outputFile..html"
343  (TITLE="&l_title.")
344  headtext='<link href="tabs.css" rel="stylesheet" type="text/css"/><link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />'
345  metatext="http-equiv=""Content-Style-Type"" content=""text/css"" /><meta http-equiv=""Content-Language"" content=""&i_language."" /"
346  style=styles.SASUnit stylesheet=(URL="SAS_SASUnit.css");
347  %end;
348 
349  proc report data=work._tcg_legend nowd
350  style(report)=blindTable [borderwidth=0]
351  style(column)=blindData
352  style(lines) =blindData
353  style(header)=blindHeader;
354  columns dummy Text;
355 
356  compute before _page_;
357  line @1 "Color Legend:";
358  endcomp;
359  run;
360 
361  title;
362  %_reportFooter(o_html=&o_html.);
363 
364  *** Render separation line between legend and source code ***;
365  %if (&o_html.) %then %do;
366  ods html4 text="^{RAW <hr size=""1"">}";
367  %end;
368 
369  proc print data=work._tcg_report noobs
370  style(report)=blindTable [borderwidth=0]
371  style(column)=blindFixedFontData
372  style(header)=blindHeader;
373 
374  var RowNumberOut pgmSourceColumn;
375  run;
376 
377  %if (&o_html.) %then %do;
378  %_closeHtmlPage;
379  %end;
380 
381  options center;
382  title;footnote;
383  %_macExit:
384 %mend _reporttcghtml;