CI: Add silent cmake SonarCloud scan
[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 if (( noexec ))
447 then
448         echo "no exec mode; would test these:"
449         for tf in $flist
450         do
451                 echo "  $tf"
452         done
453         exit 0
454 fi
455
456 rm -fr *.gcov                   # ditch the previous coverage files
457 ut_errors=0                     # unit test errors (not coverage errors)
458 errors=0
459
460 if ! touch /tmp/PPID$$.noise
461 then
462         echo "<ERR> unable to write to /tmp???"
463 fi
464
465 for tfile in $flist
466 do
467         for x in *.gcov
468         do
469                 if [[ -e $x ]]
470                 then
471                         cp $x $x-
472                 fi
473         done
474
475         echo "$tfile --------------------------------------"
476         (       # all noise is now captured into a tmp file to support quiet mode
477                 bcmd=$( printf "$builder" "${tfile%.c}" )
478                 if ! $bcmd >/tmp/PID$$.log 2>&1
479                 then
480                         echo "[FAIL] cannot build $tfile"
481                         cat /tmp/PID$$.log
482                         # do NOT remove tmp files; bash seens to not gen a new PID for subshells
483                         exit 1
484                 fi
485
486                 iflist="main sig_clean_exit "           # ignore external functions from our tools
487                 add_ignored_func $tfile                         # ignore all static functions in our test driver
488                 add_ignored_func test_support.c         # ignore all static functions in our test tools
489                 add_ignored_func test_nng_em.c          # the nng/nano emulated things
490                 add_ignored_func test_si95_em.c         # the si emulated things
491                 add_ignored_func test_common_em.c       # the common emulation functions
492                 for f in *_static_test.c                        # all static modules here
493                 do
494                         if(( ! run_nano_tests )) && [[ $f == *"nano"* ]]
495                         then
496                                 continue
497                         fi
498
499                         add_ignored_func $f
500                 done
501
502                 if ! ./${tfile%.c} >/tmp/PID$$.log 2>&1
503                 then
504                         echo "[FAIL] unit test failed for: $tfile"
505                         if [[ -n $capture_file ]]
506                         then
507                                 echo "all errors captured in $capture_file, listing only fail message on tty"
508                                 echo "$tfile --------------------------------------" >>$capture_file
509                                 cat /tmp/PID$$.log >>$capture_file
510                                 grep "^<FAIL>" /tmp/PID$$.log
511                                 echo ""
512                         else
513                                 if (( quiet ))
514                                 then
515                                         grep "^<" /tmp/PID$$.log|egrep -v "^<SIEM>|^<EM>"       # in quiet mode just dump <...> messages which are assumed from the test programme not appl
516                                 else
517                                         cat /tmp/PID$$.log
518                                 fi
519                         fi
520                         (( ut_errors++ ))                               # cause failure even if not in strict mode
521                         if (( ! always_gcov ))
522                         then
523                                 exit 1                                          # we are in a subshell, must exit bad
524                         fi
525                 else
526                         if (( show_output ))
527                         then
528                                 printf "\n============= test programme output =======================\n"
529                                 cat /tmp/PID$$.log
530                                 printf "===========================================================\n"
531                         else
532                                 grep "SUMMARY" /tmp/PID$$.log
533                         fi
534                 fi
535
536                 (
537                         touch ./.targets
538                         sed '/^#/ d; /^$/ d; s/^/TARGET: /' ./.targets
539                         gcov -f ${tfile%.c} | sed "s/'//g"
540                 ) | awk \
541                         -v cfail=$cfail \
542                         -v show_all=$show_all \
543                         -v ignore_list="$iflist" \
544                         -v module_cov_target=$module_cov_target \
545                         -v chatty=$verbose \
546                         '
547                         BEGIN {
548                                 announce_target = 1;
549                                 nignore = split( ignore_list, ignore, " " )
550                                 for( i = 1; i <= nignore; i++ ) {
551                                         imap[ignore[i]] = 1
552                                 }
553
554                                 exit_code = 0           # assume good
555                         }
556
557                         /^TARGET:/ {
558                                 if( NF > 1 ) {
559                                         target[$2] = $NF
560                                 }
561                                 next;
562                         }
563
564                         /File.*_test/ || /File.*test_/ {                # dont report on test files
565                                 skip = 1
566                                 file = 1
567                                 fname = $2
568                                 next
569                         }
570
571                         /File/ {
572                                 skip = 0
573                                 file = 1
574                                 fname = $2
575                                 next
576                         }
577
578                         /Function/ {
579                                 fname = $2
580                                 file = 0
581                                 if( imap[fname] ) {
582                                         fname = "skipped: " fname               # should never see and make it smell if we do
583                                         skip = 1
584                                 } else {
585                                         skip = 0
586                                 }
587                                 next
588                         }
589
590                         skip { next }
591
592                         /Lines executed/ {
593                                 split( $0, a, ":" )
594                                 pct = a[2]+0
595
596                                 if( file ) {
597                                         if( announce_target ) {                         # announce default once at start
598                                                 announce_target = 0;
599                                                 printf( "\n[INFO] default target coverage for modules is %d%%\n", module_cov_target )
600                                         }
601
602                                         if( target[fname] ) {
603                                                 mct = target[fname]
604                                                 announce_target = 1;
605                                         } else {
606                                                 mct = module_cov_target
607                                         }
608
609                                         if( announce_target ) {                                 # annoucne for module if different from default
610                                                 printf( "[INFO] target coverage for %s is %d%%\n", fname, mct )
611                                         }
612
613                                         if( pct < mct ) {
614                                                 printf( "[%s] %3d%% %s\n", cfail, pct, fname )  # CAUTION: write only 3 things  here
615                                                 exit_code = 1
616                                         } else {
617                                                 printf( "[PASS] %3d%% %s\n", pct, fname )
618                                         }
619
620                                         announce_target = 0;
621                                 } else {
622                                         if( pct < 70 ) {
623                                                 printf( "[LOW]  %3d%% %s\n", pct, fname )
624                                         } else {
625                                                 if( pct < 80 ) {
626                                                         printf( "[MARG] %3d%% %s\n", pct, fname )
627                                                 } else {
628                                                         if( show_all ) {
629                                                                 printf( "[OK]   %3d%% %s\n", pct, fname )
630                                                         }
631                                                 }
632                                         }
633                                 }
634                         }
635
636                         END {
637                                 printf( "\n" );
638                                 exit( exit_code )
639                         }
640                 ' >/tmp/PID$$.log                                       # capture output to run discount on failures
641                 rc=$?
642                 cat /tmp/PID$$.log
643
644                 if (( rc  || force_discounting ))       # didn't pass, or forcing, see if discounting helps
645                 then
646                         if (( ! verbose ))              # if verbose is on we don't need this (! is not a mistake!!)
647                         then
648                                 echo "[INFO] checking to see if discounting improves coverage for failures listed above"
649                         fi
650
651                         # preferred, but breaks under bash
652                         #egrep "$trigger_discount_str"  /tmp/PID$$.log | while read state junk  name
653                         egrep "$trigger_discount_str"  /tmp/PID$$.log | while read stuff
654                         do
655                                 set stuff                       # this hack required because bash cant read into mult vars
656                                 state="$1"
657                                 name="$3"
658
659                                 if ! discount_an_checks $name.gcov >/tmp/PID$$.disc
660                                 then
661                                         (( errors++ ))
662                                 fi
663
664                                 tail -1 /tmp/PID$$.disc | grep '\['
665
666                                 if (( verbose > 1 ))                    # updated file was generated, keep here
667                                 then
668                                         echo "[INFO] discounted coverage info in: ${tfile##*/}.dcov"
669                                 fi
670
671                                 mv /tmp/PID$$.disc ${name##*/}.dcov
672                         done
673                 fi
674         )>/tmp/PID$$.noise 2>&1
675         if (( $? != 0 ))
676         then
677                 (( ut_errors++ ))
678                 cat /tmp/PID$$.noise
679                 continue
680         fi
681
682         for x in *.gcov                                                 # merge any previous coverage file with this one
683         do
684                 if [[ -e $x && -e $x- ]]
685                 then
686                         merge_cov $x $x- >/tmp/PID$$.mc
687                         cp /tmp/PID$$.mc $x
688                         rm $x-
689                 fi
690         done
691
692         if (( ! quiet ))
693         then
694                 cat /tmp/PID$$.noise
695         else
696                 grep "SUMMARY" /tmp/PID$$.noise
697         fi
698 done
699
700 echo ""
701 echo "[INFO] final discount checks on merged gcov files"
702 show_all=1
703 for xx in *.gcov
704 do
705         if [[ $xx != *"test"* ]]
706         then
707                 of=${xx%.gcov}.dcov
708                 discount_an_checks $xx  >$of
709                 if [[ -n $of ]]
710                 then
711                         tail -1 $of |  grep '\['
712                 fi
713         fi
714 done
715
716 if (( save_gcov ))
717 then
718         echo ""
719         ok=1
720         if [[ ! -d $outdir ]]
721         then
722                 if ! mkdir -p $out_dir
723                 then
724                         echo "[WARN] unable to save .gcov files in $out_dir"
725                         ok=0
726                 fi
727         fi
728
729         if (( ok ))
730         then
731                 rm -fr $out_dir/*
732                 echo "[INFO] gcov files saved in $out_dir for push to remote system(s)"
733                 cp *.gcov $out_dir/
734                 rm -f $out_dir/*_test.c.gcov $out_dir/test_*.c.gcov
735                 rm -f ./*_test.c.gcov ./test_*.c.gcov
736         fi
737 else
738         echo "[INFO] .gcov files were not saved for remote system"
739 fi
740
741 state=0                                         # final state
742 rm -f /tmp/PID$$.*
743 if (( strict ))                         # fail if some coverage failed too
744 then
745         if (( errors + ut_errors ))
746         then
747                 state=1
748         fi
749 else                                            # not strict; fail only if unit tests themselves failed
750         if (( ut_errors ))
751         then
752                 state=1
753         fi
754 fi
755
756 # finally run any "vetters" which run programmes and analyse the output
757 echo "[INFO] running vetters...."
758 if ! make vet
759 then
760         echo "[FAIL] one or more vetters failed"
761         state=1
762 else
763         echo "[INFO] vetters all passed"
764 fi
765
766 echo""
767 if (( state ))
768 then
769         echo "[FAIL] overall unit testing fails: coverage errors=$errors   unit test errors=$ut_errors"
770 else
771         echo "[PASS] overall unit testing passes"
772         if (( gen_xml ))
773         then
774                 mk_xml
775         fi
776 fi
777
778 exit $state
779