More changes for scan corrections and unit test coverage
[ric-plt/lib/rmr.git] / test / unit_test.ksh
1 #!/usr/bin/env ksh
2 # this has been hacked to work with bash; ksh is preferred
3
4 #==================================================================================
5 #        Copyright (c) 2019 Nokia
6 #        Copyright (c) 2018-2019 AT&T Intellectual Property.
7 #
8 #   Licensed under the Apache License, Version 2.0 (the "License");
9 #   you may not use this file except in compliance with the License.
10 #   You may obtain a copy of the License at
11 #
12 #       http://www.apache.org/licenses/LICENSE-2.0
13 #
14 #   Unless required by applicable law or agreed to in writing, software
15 #   distributed under the License is distributed on an "AS IS" BASIS,
16 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 #   See the License for the specific language governing permissions and
18 #   limitations under the License.
19 #==================================================================================
20
21
22 #
23 #       Mnemonic:       unit_test.ksh
24 #       Abstract:       Execute unit test(s) in the directory and produce a more
25 #                               meaningful summary than gcov gives by default (exclude
26 #                               coverage  on the unit test functions).
27 #
28 #                               Test files must be named *_test.c, or must explicitly be
29 #                               supplied on the command line. Functions in the test
30 #                               files will not be reported on provided that they have
31 #                               their prototype (all on the SAME line) as:
32 #                                       static type name() {
33 #
34 #                               Functions with coverage less than 80% will be reported as
35 #                               [LOW] in the output.  A file is considered to pass if the
36 #                               overall execution percentage for the file is >= 80% regardless
37 #                               of the number of functions that reported low.
38 #
39 #                               Test programmes are built prior to execution. Plan-9 mk is
40 #                               the preferred builder, but as it's not widly adopted (sigh)
41 #                               make is assumed and -M will shift to Plan-9. Use -C xxx to
42 #                               invoke a customised builder.
43 #
44 #                               For a module which does not pass, we will attempt to boost
45 #                               the coverage by discounting the unexecuted lines which are
46 #                               inside of if() statements that are checking return from
47 #                               (m)alloc() calls or are checking for nil pointers as these
48 #                               cases are likely impossible to drive. When discount testing
49 #                               is done both the failure message from the original analysis
50 #                               and a pass/fail message from the discount test are listed,
51 #                               but only the result of the discount test is taken into
52 #                               consideration with regard to overall success.
53 #
54 #                               Overall Pass/Fail
55 #                               By default the overall state is based only on the success
56 #                               or failure of the unit tests and NOT on the perceived
57 #                               state of coverage.  If the -s (strict) option is given, then
58 #                               overall state will be failure if code coverage expectations
59 #                               are not met.
60 #
61 #       Date:           16 January 2018
62 #       Author:         E. Scott Daniels
63 # -------------------------------------------------------------------------
64
65 function usage {
66         echo "usage: $0 [-G|-M|-C custom-command-string] [-a] [-c cov-target]  [-f] [-F] [-v] [-x]  [files]"
67         echo "  if -C is used to provide a custom build command then it must "
68         echo "  contain a %s which will be replaced with the unit test file name."
69         echo '  e.g.:  -C "mk -a %s"'
70         echo "  -a always run coverage (even on failed modules)"
71         echo "  -c allows user to set the target coverage for a module to pass; default is 80"
72         echo "  -f forces a discount check (normally done only if coverage < target)"
73         echo "  -F show only failures at the function level"
74         echo "  -Q turns off quiet mode. Quiet mode (default) is less chatty about intermediate"
75         echo "     coverage results and test programme output when failures detected"
76         echo "  -q enable quiet mode (default, so this is no longer needed)"
77         echo "  -s strict mode; code coverage must also pass to result in a good exit code"
78         echo "  -v will write additional information to the tty and save the disccounted file if discount run or -f given"
79         echo "  -x generates the coverage XML files for Sonar (implies -f)"
80 }
81
82 # read through the given file and add any functions that are static to the
83 # ignored list.  Only test and test tools files should be parsed.
84 #
85 function add_ignored_func {
86         if [[ ! -r $1 ]]
87         then
88                 echo ">>>> can't find file to ignore: $1"
89                 return
90         fi
91
92         typeset f=""
93         goop=$(
94                 grep "^static.*(.*).*{" $1 | awk '              # get list of test functions to ignore
95                         {
96                                 gsub( "[(].*", "" )
97                                 gsub( "[*]", "" )
98                                 if( $2 == "struct" ) {                  # static struct goober function
99                                         printf( "%s ", $4 )
100                                 } else {
101                                         printf( "%s ", $3 )                     # static goober-type funct
102                                 }
103                         }
104                 ' )
105
106         iflist="$iflist $goop"                  # this goop hack because bash can't read from a loop
107 }
108
109
110 # Merge two coverage files to preserve the total lines covered by different
111 # test programmes.
112 #
113 function merge_cov {
114         if [[ -z $1 || -z $2 ]]
115         then
116                 return
117         fi
118
119         if [[ ! -e $1 || ! -e $2 ]]
120         then
121                 return
122         fi
123
124         (
125                 cat $1
126                 echo "==merge=="
127                 cat $2
128         ) | awk '
129                 /^==merge==/ {
130                         merge = 1
131                         next
132                 }
133
134                 merge && /#####:/ {
135                         line = $2+0
136                         if( executed[line] ) {
137                                 $1 = sprintf( "%9d:", executed[line] )
138                         }
139                 }
140
141                 merge {
142                         print
143                         next
144                 }
145
146                 {
147                         line = $2+0
148                         if( $1+0 > 0 ) {
149                                 executed[line] = $1+0
150                         }
151                 }
152         '
153 }
154
155 #
156 #       Parse the .gcov file and discount any unexecuted lines which are in if()
157 #       blocks that are testing the result of alloc/malloc calls, or testing for
158 #       nil pointers.  The feeling is that these might not be possible to drive
159 #       and shoudn't contribute to coverage deficiencies.
160 #
161 #       In verbose mode, the .gcov file is written to stdout and any unexecuted
162 #       line which is discounted is marked with ===== replacing the ##### marking
163 #       that gcov wrote.
164 #
165 #       The return value is 0 for pass; non-zero for fail.
166 function discount_an_checks {
167         typeset f="$1"
168
169         mct=$( get_mct ${1%.gcov} )                     # see if a special coverage target is defined for this
170
171         if [[ ! -f $1 ]]
172         then
173                 if [[ -f ${1##*/} ]]
174                 then
175                         f=${1##*/}
176                 else
177                         echo "cant find: $f"
178                         return
179                 fi
180         fi
181
182         awk -v module_cov_target=$mct \
183                 -v cfail=${cfail:-WARN} \
184                 -v show_all=$show_all \
185                 -v full_name="${1}"  \
186                 -v module="${f%.*}"  \
187                 -v chatty=$chatty \
188                 -v replace_flags=$replace_flags \
189         '
190         function spit_line( ) {
191                 if( chatty ) {
192                         printf( "%s\n", $0 )
193                 }
194         }
195
196         /-:/ {                          # skip unexecutable lines
197                 spit_line()
198                 seq++                                   # allow blank lines in a sequence group
199                 next
200         }
201
202         {
203                 nexec++                 # number of executable lines
204         }
205
206         /#####:/ {
207                 unexec++;
208                 if( $2+0 != seq+1 ) {
209                         prev_malloc = 0
210                         prev_if = 0
211                         seq = 0
212                         spit_line()
213                         next
214                 }
215
216                 if( prev_if && prev_malloc ) {
217                         if( prev_malloc ) {
218                                 #printf( "allow discount: %s\n", $0 )
219                                 if( replace_flags ) {
220                                         gsub( "#####", "    1", $0 )
221                                 }
222                                 discount++;
223                         }
224                 }
225
226                 seq++;;
227                 spit_line()
228                 next;
229         }
230
231         /if[(].*alloc.*{/ {                     # if( (x = malloc( ... )) != NULL ) or if( (p = sym_alloc(...)) != NULL )
232                 seq = $2+0
233                 prev_malloc = 1
234                 prev_if = 1
235                 spit_line()
236                 next
237         }
238
239         /if[(].* == NULL/ {                             # a nil check likely not easily forced if it wasnt driven
240                 prev_malloc = 1
241                 prev_if = 1
242                 spit_line()
243                 seq = $2+0
244                 next
245         }
246
247         /if[(]/ {
248                 if( seq+1 == $2+0 && prev_malloc ) {            // malloc on previous line
249                         prev_if = 1
250                 } else {
251                         prev_malloc = 0
252                         prev_if = 0
253                 }
254                 spit_line()
255                 next
256         }
257
258         /alloc[(]/ {
259                 seq = $2+0
260                 prev_malloc = 1
261                 spit_line()
262                 next
263         }
264
265         {
266                 spit_line()
267         }
268
269         END {
270                 net = unexec - discount
271                 orig_cov = ((nexec-unexec)/nexec)*100           # original coverage
272                 adj_cov = ((nexec-net)/nexec)*100                       # coverage after discount
273                 pass_fail = adj_cov < module_cov_target ? cfail : "PASS"
274                 rc = adj_cov < module_cov_target ? 1 : 0
275                 if( pass_fail == cfail || show_all ) {
276                         if( chatty ) {
277                                 printf( "[%s] %s executable=%d unexecuted=%d discounted=%d net_unex=%d  cov=%d%% ==> %d%%  target=%d%%\n",
278                                         pass_fail, full_name ? full_name : module, nexec, unexec, discount, net, orig_cov, adj_cov, module_cov_target )
279                         } else {
280                                 printf( "[%s] %d%% (%d%%) %s\n", pass_fail, adj_cov, orig_cov, full_name ? full_name : module )
281                         }
282                 }
283
284                 exit( rc )
285         }
286         ' $f
287 }
288
289 # Given a file name ($1) see if it is in the ./.targets file. If it is
290 # return the coverage listed, else return (echo)  the default $module_cov_target
291 #
292 function get_mct {
293         typeset v=$module_cov_target
294
295         if [[ -f ./.targets ]]
296         then
297                 grep "^$1 " ./.targets | head -1 | read stuff
298                 tv="${stuff##* }"                                       # assume junk tv; ditch junk
299         fi
300
301         echo ${tv:-$v}
302 }
303
304 # Remove unneeded coverage files, then generate the xml files that can be given
305 # to sonar.  gcov.xml is based on the "raw" coverage and dcov.xml is based on
306 # the discounted coverage.
307 #
308 function mk_xml {
309         rm -fr *_test.c.gcov test_*.c.gcov *_test.c.dcov test_*.c.dcov          # we don't report on the unit test code, so ditch
310         cat *.gcov | cov2xml.ksh >gcov.xml
311         cat *.dcov | cov2xml.ksh >dcov.xml
312 }
313
314
315 # -----------------------------------------------------------------------------------------------------------------
316
317 if [[ -z $BUILD_PATH ]]
318 then
319
320         # we assume that the project has been built in the ../[.]build directory
321         if [[ -d ../build ]]
322         then
323                 export BUILD_PATH=../build
324         else
325                 if [[ -d ../.build ]]
326                 then
327                         export BUILD_PATH=../.build
328                 else
329                         echo "[WARN] cannot find build directory (tried ../build and ../.build); things might not work"
330                         echo ""
331                 fi
332         fi
333 fi
334
335 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$BUILD_PATH/lib:$BUILD_PATH/lib64
336 export C_INCLUDE_PATH=$C_INCLUDE_PATH:$BUILD_PATH/include
337 export LIBRARY_PATH=$LD_LIBRARY_PATH
338
339 # The Makefile sets specific includes for things
340 #export C_INCLUDE_PATH="../src/rmr/common/include:../src/rmr/si/include:$C_INCLUDE_PATH"
341
342 module_cov_target=80
343 builder="make -B %s"                                                    # default to plain ole make
344 verbose=0
345 show_all=1                                                                              # show all things -F sets to show failures only
346 strict=0                                                                                # -s (strict) will set; when off, coverage state ignored in final pass/fail
347 show_output=0                                                                   # show output from each test execution (-S)
348 quiet=1                                                                                 # less chatty with result output (only overall coverage, no intermediate coverage) -Q turns off
349 gen_xml=0
350 replace_flags=1                                                                 # replace ##### in gcov for discounted lines
351 run_nano_tests=0                                                                # can nolonger be turned on
352 run_nng_tests=0                                                                 # -N will enable
353 always_gcov=0                                                                   # -a sets to always run gcov even if failure
354 save_gcov=1                                                                             # -o turns this off
355 out_dir=${UT_COVERAGE_DIR:-/tmp/rmr_gcov}               # -O changes output directory
356
357 export RMR_WARNING=1                                                    # turn on warnings
358
359 ulimit -c unlimited
360
361 while [[ $1 == "-"* ]]
362 do
363         case $1 in
364                 -C)     builder="$2"; shift;;           # custom build command
365                 -G)     builder="gmake %s";;
366                 -M)     builder="mk -a %s";;            # use plan-9 mk (better, but sadly not widly used)
367                 -N)     run_nng_tests=1;;
368                 -O)     out_dir=$2; shift;;
369
370                 -a)     always_gcov=1;;
371                 -c)     module_cov_target=$2; shift;;
372                 -e)     capture_file=$2; >$capture_file; shift;;                # capture errors from failed tests rather than spewing on tty
373                 -f)     force_discounting=1;
374                         trigger_discount_str="WARN|FAIL|PASS"           # check all outcomes for each module
375                         ;;
376
377                 -F)     show_all=0;;
378
379                 -n)     noexec=1;;
380                 -o)     save_gcov=0;;
381                 -s)     strict=1;;                                      # coverage counts toward pass/fail state
382                 -S)     show_output=1;;                         # test output shown even on success
383                 -v)     (( verbose++ ));;
384                 -q)     quiet=1;;                                       # less chatty when spilling error log files
385                 -Q)     quiet=0;;                                       # disable quiet mode
386                 -x)     gen_xml=1
387                         force_discounting=1
388                         trigger_discount_str="WARN|FAIL|PASS"           # check all outcomes for each module
389                         rm -fr *cov.xml
390                         ;;
391
392
393                 -h)             usage; exit 0;;
394                 --help) usage; exit 0;;
395                 -\?)    usage; exit 0;;
396
397                 *)      echo "unrecognised option: $1" >&2
398                         usage >&2
399                         exit 1
400                         ;;
401         esac
402
403         shift
404 done
405
406
407 if (( strict ))                 # if in strict mode, coverage shortcomings are failures
408 then
409         cfail="FAIL"
410 else
411         cfail="WARN"
412 fi
413 if [[ -z $trigger_discount_str ]]
414 then
415         trigger_discount_str="$cfail"
416 fi
417
418
419 if [[ -z $1 ]]
420 then
421         flist=""
422         for tfile in *_test.c
423         do
424                 if [[ $tfile != *"static_test.c" ]]
425                 then
426                         if (( ! run_nng_tests )) && [[ $tfile == *"nng"* ]]             # drop any nng file unless -N given
427                         then
428                                 continue
429                         fi
430                         if [[ $tfile == *"nano"* ]]                     # no longer support nano tests; drop regardless
431                         then
432                                 continue
433                         fi
434
435                         if (( ! quiet ))
436                         then
437                                 echo "<INFO> add test: $tfile" >&2
438                         fi
439                         flist="${flist}$tfile "
440                 fi
441         done
442 else
443         flist="$@"
444 fi
445
446
447 if (( noexec ))
448 then
449         echo "no exec mode; would test these:"
450         for tf in $flist
451         do
452                 echo "  $tf"
453         done
454         exit 0
455 fi
456
457 rm -fr *.gcov                   # ditch the previous coverage files
458 ut_errors=0                     # unit test errors (not coverage errors)
459 errors=0
460
461 if ! touch /tmp/PPID$$.noise
462 then
463         echo "<ERR> unable to write to /tmp???"
464 fi
465
466 for tfile in $flist
467 do
468         for x in *.gcov
469         do
470                 if [[ -e $x ]]
471                 then
472                         cp $x $x-
473                 fi
474         done
475
476         echo "$tfile --------------------------------------"
477         (       # all noise is now captured into a tmp file to support quiet mode
478                 bcmd=$( printf "$builder" "${tfile%.c}" )
479                 if ! $bcmd >/tmp/PID$$.log 2>&1
480                 then
481                         echo "[FAIL] cannot build $tfile"
482                         cat /tmp/PID$$.log
483                         # do NOT remove tmp files; bash seens to not gen a new PID for subshells
484                         exit 1
485                 fi
486
487                 iflist="main sig_clean_exit "           # ignore external functions from our tools
488                 add_ignored_func $tfile                         # ignore all static functions in our test driver
489                 add_ignored_func test_support.c         # ignore all static functions in our test tools
490                 add_ignored_func test_nng_em.c          # the nng/nano emulated things
491                 add_ignored_func test_si95_em.c         # the si emulated things
492                 add_ignored_func test_common_em.c       # the common emulation functions
493                 for f in *_static_test.c                        # all static modules here
494                 do
495                         if(( ! run_nano_tests )) && [[ $f == *"nano"* ]]
496                         then
497                                 continue
498                         fi
499
500                         add_ignored_func $f
501                 done
502
503                 if ! ./${tfile%.c} >/tmp/PID$$.log 2>&1
504                 then
505                         echo "[FAIL] unit test failed for: $tfile"
506                         if [[ -n $capture_file ]]
507                         then
508                                 echo "all errors captured in $capture_file, listing only fail message on tty"
509                                 echo "$tfile --------------------------------------" >>$capture_file
510                                 cat /tmp/PID$$.log >>$capture_file
511                                 grep "^<FAIL>" /tmp/PID$$.log
512                                 echo ""
513                         else
514                                 if (( quiet ))
515                                 then
516                                         grep "^<" /tmp/PID$$.log|egrep -v "^<SIEM>|^<EM>"       # in quiet mode just dump <...> messages which are assumed from the test programme not appl
517                                 else
518                                         cat /tmp/PID$$.log
519                                 fi
520                         fi
521                         (( ut_errors++ ))                               # cause failure even if not in strict mode
522                         if (( ! always_gcov ))
523                         then
524                                 exit 1                                          # we are in a subshell, must exit bad
525                         fi
526                 else
527                         if (( show_output ))
528                         then
529                                 printf "\n============= test programme output =======================\n"
530                                 cat /tmp/PID$$.log
531                                 printf "===========================================================\n"
532                         else
533                                 grep "SUMMARY" /tmp/PID$$.log
534                         fi
535                 fi
536
537                 (
538                         touch ./.targets
539                         sed '/^#/ d; /^$/ d; s/^/TARGET: /' ./.targets
540                         gcov -f ${tfile%.c} | sed "s/'//g"
541                 ) | awk \
542                         -v cfail=$cfail \
543                         -v show_all=$show_all \
544                         -v ignore_list="$iflist" \
545                         -v module_cov_target=$module_cov_target \
546                         -v chatty=$verbose \
547                         '
548                         BEGIN {
549                                 announce_target = 1;
550                                 nignore = split( ignore_list, ignore, " " )
551                                 for( i = 1; i <= nignore; i++ ) {
552                                         imap[ignore[i]] = 1
553                                 }
554
555                                 exit_code = 0           # assume good
556                         }
557
558                         /^TARGET:/ {
559                                 if( NF > 1 ) {
560                                         target[$2] = $NF
561                                 }
562                                 next;
563                         }
564
565                         /File.*_test/ || /File.*test_/ {                # dont report on test files
566                                 skip = 1
567                                 file = 1
568                                 fname = $2
569                                 next
570                         }
571
572                         /File/ {
573                                 skip = 0
574                                 file = 1
575                                 fname = $2
576                                 next
577                         }
578
579                         /Function/ {
580                                 fname = $2
581                                 file = 0
582                                 if( imap[fname] ) {
583                                         fname = "skipped: " fname               # should never see and make it smell if we do
584                                         skip = 1
585                                 } else {
586                                         skip = 0
587                                 }
588                                 next
589                         }
590
591                         skip { next }
592
593                         /Lines executed/ {
594                                 split( $0, a, ":" )
595                                 pct = a[2]+0
596
597                                 if( file ) {
598                                         if( announce_target ) {                         # announce default once at start
599                                                 announce_target = 0;
600                                                 printf( "\n[INFO] default target coverage for modules is %d%%\n", module_cov_target )
601                                         }
602
603                                         if( target[fname] ) {
604                                                 mct = target[fname]
605                                                 announce_target = 1;
606                                         } else {
607                                                 mct = module_cov_target
608                                         }
609
610                                         if( announce_target ) {                                 # annoucne for module if different from default
611                                                 printf( "[INFO] target coverage for %s is %d%%\n", fname, mct )
612                                         }
613
614                                         if( pct < mct ) {
615                                                 printf( "[%s] %3d%% %s\n", cfail, pct, fname )  # CAUTION: write only 3 things  here
616                                                 exit_code = 1
617                                         } else {
618                                                 printf( "[PASS] %3d%% %s\n", pct, fname )
619                                         }
620
621                                         announce_target = 0;
622                                 } else {
623                                         if( pct < 70 ) {
624                                                 printf( "[LOW]  %3d%% %s\n", pct, fname )
625                                         } else {
626                                                 if( pct < 80 ) {
627                                                         printf( "[MARG] %3d%% %s\n", pct, fname )
628                                                 } else {
629                                                         if( show_all ) {
630                                                                 printf( "[OK]   %3d%% %s\n", pct, fname )
631                                                         }
632                                                 }
633                                         }
634                                 }
635                         }
636
637                         END {
638                                 printf( "\n" );
639                                 exit( exit_code )
640                         }
641                 ' >/tmp/PID$$.log                                       # capture output to run discount on failures
642                 rc=$?
643                 cat /tmp/PID$$.log
644
645                 if (( rc  || force_discounting ))       # didn't pass, or forcing, see if discounting helps
646                 then
647                         if (( ! verbose ))              # if verbose is on we don't need this (! is not a mistake!!)
648                         then
649                                 echo "[INFO] checking to see if discounting improves coverage for failures listed above"
650                         fi
651
652                         # preferred, but breaks under bash
653                         #egrep "$trigger_discount_str"  /tmp/PID$$.log | while read state junk  name
654                         egrep "$trigger_discount_str"  /tmp/PID$$.log | while read stuff
655                         do
656                                 set stuff                       # this hack required because bash cant read into mult vars
657                                 state="$1"
658                                 name="$3"
659
660                                 if ! discount_an_checks $name.gcov >/tmp/PID$$.disc
661                                 then
662                                         (( errors++ ))
663                                 fi
664
665                                 tail -1 /tmp/PID$$.disc | grep '\['
666
667                                 if (( verbose > 1 ))                    # updated file was generated, keep here
668                                 then
669                                         echo "[INFO] discounted coverage info in: ${tfile##*/}.dcov"
670                                 fi
671
672                                 mv /tmp/PID$$.disc ${name##*/}.dcov
673                         done
674                 fi
675         )>/tmp/PID$$.noise 2>&1
676         if (( $? != 0 ))
677         then
678                 (( ut_errors++ ))
679                 cat /tmp/PID$$.noise
680                 continue
681         fi
682
683         for x in *.gcov                                                 # merge any previous coverage file with this one
684         do
685                 if [[ -e $x && -e $x- ]]
686                 then
687                         merge_cov $x $x- >/tmp/PID$$.mc
688                         cp /tmp/PID$$.mc $x
689                         rm $x-
690                 fi
691         done
692
693         if (( ! quiet ))
694         then
695                 cat /tmp/PID$$.noise
696         else
697                 grep "SUMMARY" /tmp/PID$$.noise
698         fi
699 done
700
701 echo ""
702 echo "[INFO] final discount checks on merged gcov files"
703 show_all=1
704 for xx in *.gcov
705 do
706         if [[ $xx != *"test"* ]]
707         then
708                 of=${xx%.gcov}.dcov
709                 discount_an_checks $xx  >$of
710                 if [[ -n $of ]]
711                 then
712                         tail -1 $of |  grep '\['
713                 fi
714         fi
715 done
716
717 if (( save_gcov ))
718 then
719         echo ""
720         ok=1
721         if [[ ! -d $outdir ]]
722         then
723                 if ! mkdir -p $out_dir
724                 then
725                         echo "[WARN] unable to save .gcov files in $out_dir"
726                         ok=0
727                 fi
728         fi
729
730         if (( ok ))
731         then
732                 rm -fr $out_dir/*
733                 echo "[INFO] gcov files saved in $out_dir for push to remote system(s)"
734                 cp *.gcov $out_dir/
735                 rm -f $out_dir/*_test.c.gcov $out_dir/test_*.c.gcov
736                 rm -f ./*_test.c.gcov ./test_*.c.gcov
737         fi
738 else
739         echo "[INFO] .gcov files were not saved for remote system"
740 fi
741
742 state=0                                         # final state
743 rm -f /tmp/PID$$.*
744 if (( strict ))                         # fail if some coverage failed too
745 then
746         if (( errors + ut_errors ))
747         then
748                 state=1
749         fi
750 else                                            # not strict; fail only if unit tests themselves failed
751         if (( ut_errors ))
752         then
753                 state=1
754         fi
755 fi
756
757 # finally run any "vetters" which run programmes and analyse the output
758 echo "[INFO] running vetters...."
759 if ! make vet
760 then
761         echo "[FAIL] one or more vetters failed"
762         state=1
763 else
764         echo "[INFO] vetters all passed"
765 fi
766
767 echo""
768 if (( state ))
769 then
770         echo "[FAIL] overall unit testing fails: coverage errors=$errors   unit test errors=$ut_errors"
771 else
772         echo "[PASS] overall unit testing passes"
773         if (( gen_xml ))
774         then
775                 mk_xml
776         fi
777 fi
778
779 exit $state
780