大致原理步骤:
1)编译阶段:加入编译选项 –fprofile-arcs –ftest-coverage。会在目标代码文件中加入对应的钩子,采集执行的统计来实现覆盖率的统计。
2)编译后会生成gcno,执行后生成gcda文件。
3)使用gcov,lcov可实现行覆盖、分支覆盖等计算,xx.info。
4)结合git diff生成的diff文件,计算新增代码覆盖率。
[UT/FT]
1. clone
|
|---UTCA ===> dev_zxsdr_v2.17.00.03
|
|---utility/sdr_utft ===> dev_zxsdr_v2.17.00.03
2. build & run
[yangpeng@yangpeng sdr_utft]$./cmp_utft_BuildAndRun.sh
3. add testcase
相关文件介绍:
(1)utility/sdr_utft/testcases/cmp/ut/testdata用于存放用例基线
(2)utility/sdr_utft/testcases/cmp/ut/testsuite/include source用于存放用例头文件、源文件
目前用例处理调试阶段,main工程不是包含source目录,如果新增用例源文件,需要在用例工程的CMakeList.txt对应增加。后期调试完毕,不需再加。
debug
utility/sdr_utft/build_tmp/lib# ./main --gtest_filter=*xxxxx* 可以执行制定用例,调试类似
[COV]
1. gcov gcov is a test coverage program.
compile option: -fprofile-arcs -ftest-coverage
|| ||
/ /
.gcda .gcno
run compile
xx.cpp.gcda yy.cpp.gcno
2. lcov lcov - a graphical GCOV front-end
ftcov.pl、NewCodeCoverData.ini
pha_entry.py
pha_dopatch.py
WORKSPACE
[PHA]
Jenkins Server: http://xxx.xxx.xxx.xxx:xxxx/
1 #!/usr/bin/perl -w 2 #modify 11.03 by ywq 3 #modify 2015.03.12 by zlb 4 #modify 2018.06.12 by lyj 5 use Cwd; 6 use Encode; 7 use Date::Calc qw(:all); 8 9 ##### 调试信息开关 ###### 10 my $gDebugMsg; 11 12 #####################全局变量##################### 13 my $Config_File = "CodeCoverCfg.ini"; 14 my $gCoverFile = ""; 15 my $gCodePath = ""; 16 my $gBuildCodePath = ""; 17 my $gNodeBase = ""; 18 my $gKeyWordsStr = ""; 19 my $gUpdateDateMethod = "weekly"; 20 my $gTestcasesStat = ""; 21 #log文件 22 my $RecodResultFile = ""; 23 my $ReportHtmlFile = ""; 24 25 my $gCiHomePath = ""; 26 my $gCmakeBuildPath = ""; 27 my $gProjectPath = ""; 28 my $gMyVersion = ""; 29 30 31 my $gCiVersionCfgFile = ReadValueCfg("$Config_File", "DIFF_CONFIG", "CiVersionCfg"); 32 my $gDifFile = ReadValueCfg("$Config_File", "DIFF_CONFIG", "DifFile"); 33 my $gLcovFile = ReadValueCfg("$Config_File", "DIFF_CONFIG", "LcovFile"); 34 my $gBranchVersion = ReadValueCfg("$Config_File","DIFF_CONFIG","BranchVersion"); 35 my $gCodeVersion = ReadValueCfg("$Config_File","DIFF_CONFIG","CodeVersion"); 36 37 my $gIgnoreFile = ReadValueCfg("$Config_File", "IGNORE_FILE_CONFIG", "IgnoreFile"); 38 if (defined $gIgnoreFile){ 39 print "Ignore File list [G]: ".$gIgnoreFile." "; 40 } 41 my $gIgnorelinesection = ReadValueCfg("$Config_File", "IGNORE_FILE_CONFIG", "Ignorelinesection"); 42 if (defined $gIgnorelinesection){ 43 print "Ignore line sect [G]: ".$gIgnorelinesection." "; 44 } 45 my $gIgnorelineno = ReadValueCfg("$Config_File", "IGNORE_FILE_CONFIG", "Ignorelineno"); 46 if (defined $gIgnorelineno){ 47 print "Ignore line no [G]: ".$gIgnorelineno." "; 48 } 49 50 my @gIgnoreArr = (); 51 my @gIgnoreLineSection = (); 52 my @gKeyWordsArr = (); 53 54 my $Root = getcwd; 55 chdir($Root); 56 57 58 @gArray = (); 59 #@gSArray = (); 60 my $gArrayCount = 0; 61 #my $gSArrayCount = 0; 62 63 %gDiffFileLineHash = (); 64 %gIgnoreLineHash = (); 65 my $Content = "";#新增代码详细内容 66 67 #分支覆盖 68 my $gDecisionCount = 0; 69 my $gDecisionCoverCount = 0; 70 71 #记录当前时间 72 my $gDateTimeNow = GetDateTime(); 73 my $gCurrDate = GetDate(); 74 75 #新增覆盖率 76 my $gNewOutofNum = 0; 77 my $gNewCoverRage = 0; 78 my $gProjectCoverage = 0; 79 80 #更新状态文件信息 81 my $gGitPrevDate = 0; 82 83 my $proj_stats=""; 84 my $all_stats=""; 85 86 my (%gLcovHash); 87 88 ########################################### 89 90 main(@ARGV); 91 92 ########################################### 93 94 sub main { 95 my (@ARGV) = @_; 96 my $Endtime = ""; 97 my $html_begin=""; 98 my $html_end=""; 99 my $errorlogfile = "errorlog.txt"; 100 open($errorlogfile, "> $errorlogfile"); 101 102 if($ARGV[0] eq "") { 103 print $errorlogfile("Usage: ftcov.pl INI_SECTION [project_path [build_debug_path [admin_home_path]]] "); 104 print $errorlogfile(" INI_SECTION: $Config_File file section. project_path: project path, like .../dev_21703/ build_debug_path: cmakek build path, like .../BRS_EM/cmake-debug-build/ admin_home_path: CI config home path, like /home/brs/"); 105 close($errorlogfile); 106 return; 107 } else { 108 $gTestProject = $ARGV[0]; 109 $gdiffId = $ARGV[1]; 110 $gbaseBranch = $ARGV[2]; 111 print $gTestProject; 112 print $gbaseBranch; 113 print $gdiffId; 114 #$gProjectPath = $ARGV[1]; #.../dev_21703/ 115 #$gProjectPath = "./../../../"; #.../dev_21703/ 116 #$gCmakeBuildPath = $ARGV[2]; #.../BRS_EM/cmake-build-debug 117 #$gCiHomePath = $ARGV[3]; #/home/brs/ 118 } 119 120 if (defined $gProjectPath) { 121 #$gDifFile = $gProjectPath.$gDifFile; 122 #$gMyVersion = $gProjectPath."/myversion"; 123 124 # These TWO vars rebuild 125 #$gBranchVersion 126 #$gCodeVersion 127 # BRSCI have some version config 128 # read from these file 129 ReadProjectCfgFromCiCfg(); 130 } 131 132 133 if (ChkIniSection($Config_File,$gTestProject) == 0) { 134 print "-------- "; 135 print "Error $Config_File,$gTestProject not exist! "; 136 print "-------- "; 137 close($errorlogfile); 138 return; 139 }else{ 140 print "-------- "; 141 print "Starting $Config_File,$gTestProject ... "; 142 print "-------- "; 143 } 144 145 if (GetProjectParameter($Config_File,$gTestProject) == 0){ 146 close($errorlogfile); 147 return; 148 } 149 150 if (defined $gIgnoreFile) { 151 @gIgnoreArr = split /;+/, $gIgnoreFile; 152 } 153 if (defined $gIgnorelinesection) { 154 @gIgnoreLineSection = split /;+/, $gIgnorelinesection; 155 } 156 157 #输出文件 158 $RecodResultFile = "$gTestProject".".txt"; 159 open($RecodResultFile, "> $RecodResultFile") || die("Could not open $RecodResultFile"); 160 161 162 $html_begin = '<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> </head> <body>'; 163 $html_end = '</body> </html>'; 164 $ReportHtmlFile = "$gTestProject".".html"; 165 open($ReportHtmlFile, ">:encoding(utf-8)","$ReportHtmlFile") || die("Could not open $ReportHtmlFile"); 166 print $ReportHtmlFile decode('gbk',"$html_begin"); 167 168 print $RecodResultFile("***********************************Start:$gDateTimeNow********************************** "); 169 print $RecodResultFile("模块号:$gTestProject。 "); 170 171 UpdateDiffFile(); 172 173 changecovfile(); 174 175 CalcCovs(); 176 177 $Endtime = GetDateTime(); 178 print $RecodResultFile("***********************************End:$Endtime********************************** "); 179 180 181 print $ReportHtmlFile decode('utf-8',"$html_end"); 182 close($RecodResultFile); 183 close($ReportHtmlFile); 184 close($errorlogfile); 185 } 186 187 sub PickCoverLine { 188 my $fileHandle; 189 190 if(-e $gCoverFile) { 191 open($fileHandle, "$gCoverFile"); 192 } else { 193 print $RecodResultFile("PickCoverLine : open $gCoverFile fail. "); 194 return; 195 } 196 197 my $CurFile; 198 my $PreFile = ""; 199 my $IgnoreLine = 0; 200 my $IgnoreFunc = 0; 201 202 my $space = " "; 203 my $color = "<font color=0x99FF size=6.5>"; 204 my $colorsect = "<font color=0x44AA size=4.5>"; 205 my $nextline = "<br>"; 206 my $font = "</font>"; 207 my $red = "RED"; 208 my $gray ="BLUE"; 209 my $black = "GREEN"; 210 my $nocover = "<font color=$red size=4>New Uncovered</font>"; 211 my $colorTF = ""; 212 my $statistics = ""; 213 my $linesectionbegin = 1; 214 my $linesection = ""; 215 216 my $note_fir = "<font color=$red size=3>NOTE:The first column: Line numbers</font>"; 217 my $note_sec = "<font color=$red size=3>The Second column: Coverage(true first: + be coveraged,- not coveraged)</font>"; 218 $Content = $Content.$nextline.$note_fir.$nextline; 219 $Content = $Content.$nextline.$note_sec.$nextline; 220 221 while(<$fileHandle>) { 222 #文件比较开始 223 if($_ =~ /^.+/(.+?),(d+),(d+),(d+),(.*)$/) { 224 # $1 文件名 $2 行号 225 # $3 行总分支数 $4 覆盖分支数 226 # $5 覆盖信息 227 #分析完的文件从hash中删除 228 if(lc($1) ne $PreFile && exists $gDiffFileLineHash{$PreFile}) { 229 delete $gDiffFileLineHash{$PreFile}; 230 $CurFile = ""; 231 #当hash为空时停止循环 232 my $length = keys %gDiffFileLineHash; 233 if($length eq 0) { 234 last; 235 } 236 } 237 #如果当前文件名与前一个文件名不同 238 if(lc($1) ne $PreFile && exists $gDiffFileLineHash{lc($1)}) { 239 #第一次的时候$PreFile不在hash表里 240 $FileBeginFlag = 1; 241 } 242 $PreFile = lc($1); 243 244 if(exists $gDiffFileLineHash{lc($1)}) { 245 $CurFile = lc($1); 246 } else {#如果不是要分析的文件,继续查找 247 next; 248 } 249 250 #根据文件名从哈希表中找到新增代码的起始行&&末行 251 my @LineNoArray = @{$gDiffFileLineHash{$CurFile}}; 252 253 my $LineNoBegin = $LineNoArray[0]; 254 my $LineNoEnd = $LineNoArray[-1]; 255 256 #如果找到行号小于修改行号最小值,继续查找 257 if($2<$LineNoBegin) { 258 next; 259 } 260 #如果找到行号大于修改行号最大值,结束查找 261 if($2>$LineNoEnd) { 262 next; 263 } 264 foreach $LineNo (@LineNoArray) { 265 #如果找到行号小于当前修改行号,结束查找 266 if($2<$LineNo) { 267 last; 268 } 269 #如果当前修改行号为0,继续查找 270 if($LineNo==0) { 271 next; 272 } 273 #如果找到行号大于当前修改行号,继续查找 274 if($2>$LineNo) { 275 next; 276 } 277 278 #如果覆盖未TF,tf,更新LineCoverCount,更新数组中当前修改行号为0,避免重复计算 279 { 280 $gDecisionCount = $gDecisionCount + $3; 281 $gDecisionCoverCount = $gDecisionCoverCount + $4; 282 if ($4 eq "0") { 283 $colorTF = "<font color=$red size=4>($5)</font>"; 284 }elsif ($4 eq $3) { 285 $colorTF = "<font color=$black size=4>($5)</font>"; 286 }else { 287 $colorTF = "<font color=$gray size=4>($5)</font>"; 288 } 289 290 my $colorString = $colorTF; 291 292 for($i = 0; $i < $gArrayCount; $i++) { 293 if(lc($gArray[$i][0]) eq lc($CurFile) && $gArray[$i][1] == $2) { 294 if($FileBeginFlag == 1) { 295 $Content = $Content.$nextline.$color.$CurFile.$font.$nextline; 296 $FileBeginFlag = 0; 297 } 298 299 if($linesectionbegin == 1) { 300 $linesection = $gArray[$i][3]; 301 $Content = $Content.$colorsect.$linesection.$font.$nextline; 302 $linesectionbegin = 0; 303 }elsif($linesection ne $gArray[$i][3]) { 304 $linesection = $gArray[$i][3]; 305 $Content = $Content.$colorsect.$linesection.$font.$nextline; 306 } 307 308 $Content = $Content.$space.$LineNo.$space.$colorString.$space.$gArray[$i][2].$nextline; 309 310 last; 311 } 312 } 313 } 314 315 } 316 } else { 317 next; 318 } 319 } 320 321 close($fileHandle); 322 } 323 324 325 sub GetDiffFileInfo { 326 my $fp; 327 my($filename, $arrayflag) = @_; 328 329 print "----------------------- "; 330 if(-e $filename) { 331 open($fp, "$filename"); 332 print "open $filename ok! "; 333 }else { 334 print "open $filename error! "; 335 return; 336 } 337 338 my $CurFile = ""; 339 my $FilePath = ""; 340 my $LineString; 341 my $LineCount = 0; 342 my $hashflag = 0; 343 my $catchbegin = 0; 344 my $beginline = 0; 345 my $continueline = 0; 346 my $linesection = ""; 347 my $strtmp = "BRS_SECURITY"; 348 my $strtmpnull = "NULL"; 349 my $strtmpprintf = "rintf"; 350 351 if($arrayflag eq 0) { 352 $hashflag = 0; 353 }else { 354 $hashflag = 1; 355 } 356 357 @tmp = (); 358 359 PutIgnorelinenoInHash(); 360 361 while(<$fp>) { 362 #文件比较开始一行一行比较 363 if($_ =~ /^diffs--gits.+/(.+?.cpp)s*$/) { 364 $CurFile = $1; 365 $FilePath = $_; 366 } elsif($_ =~ /^diffs--gits.+/(.+?.h)s*$/) { 367 $CurFile = $1; 368 $FilePath = $_; 369 } elsif($_ =~ /^diffs--gits.+/(.+?.c)s*$/) { 370 $CurFile = $1; 371 $FilePath = $_; 372 #print "path:$FilePath,file:$CurFile " 373 } elsif($_ =~ /^@@s.++(d+),(d+)s@@.*$/) {#增加或修改 374 $LineCount = 0; 375 $beginline = $1; 376 $continueline = $1+$2; 377 $linesection = "($beginline,$continueline)"; 378 379 #print "Diff file modify section: $linesection "; 380 } elsif($_ =~ /^+s*$/) {#增加的空行。。。 381 $LineCount++; 382 } elsif($_ =~ /^s.*$/) {#不变的任何行 383 $LineCount++; 384 } elsif($_ =~ /^---s(.+)$/) {#---行 385 #print $1." "; 386 next; 387 } elsif($_ =~ /^+++s(.+)$/) {#+++行 388 #print $1." "; 389 next; 390 } elsif($_ =~ /^+(.+)$/) {#增加修改的具体函数内容 391 $tmp[0] = $CurFile; 392 $tmp[1] = $beginline+$LineCount; 393 $tmp[2] = $1; 394 $tmp[3] = $linesection; 395 396 $LineCount++; 397 398 if (IsKeyWordsPath($FilePath) == 0) { 399 if (defined $gDebugMsg){ 400 print "$CurFile : keywordpath ignored[$tmp[1],$tmp[3],$tmp[2]]. "; 401 } 402 next; 403 }elsif (IsIgnore($tmp[0],$tmp[3],$tmp[1]) == 1) { 404 if (defined $gDebugMsg){ 405 print "$CurFile : ignore ignored[$tmp[1],$tmp[3],$tmp[2]]. "; 406 } 407 next; 408 }elsif ($1 =~ /$strtmp/) { 409 if (defined $gDebugMsg){ 410 print "$CurFile : strtmp ignored[$tmp[1],$tmp[3],$tmp[2]]. "; 411 } 412 next; 413 }elsif ($1 =~ /$strtmpnull/) { 414 if (defined $gDebugMsg){ 415 print "$CurFile : strtmpnull ignored[$tmp[1],$tmp[3],$tmp[2]]. "; 416 } 417 next; 418 }elsif ($1 =~ /$strtmpprintf/) { 419 if (defined $gDebugMsg){ 420 print "$CurFile : strtmpprintf ignored[$tmp[1],$tmp[3],$tmp[2]]. "; 421 } 422 next; 423 } 424 425 push @{$gDiffFileLineHash{$CurFile}}, $tmp[1]; 426 427 $gArray[$gArrayCount] = [$tmp[0], $tmp[1], $tmp[2],$tmp[3]]; 428 $gArrayCount++; 429 } else { 430 next; 431 } 432 } 433 434 close($fp); 435 } 436 437 sub GetProjectParameter { 438 my ($fileName,$Projectname) = @_; 439 440 my $gSectionIgnoreFile; 441 442 $gCoverFile = ReadValueCfg($fileName, $Projectname, "CoverFile"); 443 print "CoverResult file : ".$gCoverFile." "; 444 445 $gCodePath = ReadValueCfg($fileName, $Projectname, "CodePath"); 446 if (defined $gProjectPath){ 447 $gCodePath = $gProjectPath.$gCodePath; 448 } 449 print "Source code path : ".$gCodePath." "; 450 451 $gBuildCodePath = ReadValueCfg($fileName, $Projectname, "BuildCodePath"); 452 @gBuildCodePathArr = split /;+/, $gBuildCodePath; 453 if (defined $gCmakeBuildPath){ 454 for ( $idx = 0; $idx < @gBuildCodePathArr; $idx ++){ 455 $gBuildCodePathArr[$idx] = $gCmakeBuildPath.$gBuildCodePathArr[$idx]; 456 } 457 } elsif (defined $gProjectPath) { 458 for ( $idx = 0; $idx < @gBuildCodePathArr; $idx ++){ 459 $gBuildCodePathArr[$idx] = $gProjectPath.$gBuildCodePathArr[$idx]; 460 } 461 } 462 print "Gcov parse code path : ".@gBuildCodePathArr." "; 463 464 $gNodeBase = ReadValueCfg($fileName, $Projectname, "NodeBase"); 465 print $gNodeBase." "; 466 467 $gUpdateDateMethod = ReadValueCfg($fileName, $Projectname, "UpdateDateMethod"); 468 print "FT update type : ".$gUpdateDateMethod." "; 469 470 $gSectionIgnoreFile = ReadValueCfg($fileName, $Projectname, "IgnoreFile"); 471 if (defined $gSectionIgnoreFile){ 472 $gIgnoreFile = $gIgnoreFile.";".$gSectionIgnoreFile; 473 print "Ignore File list : ".$gIgnoreFile." "; 474 } 475 476 $gKeyWordsStr = ReadValueCfg($fileName, $Projectname, "keywords"); 477 if (defined $gKeyWordsStr){ 478 print "FT Keyword list : ".$gKeyWordsStr." "; 479 }else { 480 print "Error: not define keyswords!! You should add "keywords = all;". "; 481 return 0; 482 } 483 484 $gTestcasesStat = ReadValueCfg($fileName, $Projectname, "TestcasesStat"); 485 if (defined $gTestcasesStat){ 486 print "TestCase stats path : ".$gTestcasesStat." "; 487 } 488 489 if (defined $gKeyWordsStr){ 490 @gKeyWordsArr = split /;+/, $gKeyWordsStr; 491 } 492 493 return 1; 494 } 495 496 497 sub ChkIniSection { 498 my($fileName, $labelName) = @_; 499 my $value = 0; 500 my $fileHandle; 501 502 if(-e $fileName) { 503 open($fileHandle, "$fileName"); 504 }else { 505 return; 506 } 507 508 while(defined($_ = <$fileHandle>)) { 509 next if /^#/; # 丢弃注释 510 chomp; 511 if(/^[.*]/){ 512 if(/[$labelName]/) { 513 $value = 1; 514 last; 515 } 516 } 517 next; 518 } 519 close $fileHandle; 520 return ($value); 521 } 522 523 524 #从配置文件中读取值类型的配置 525 sub ReadValueCfg { 526 my($fileName, $labelName, $keyName) = @_; 527 my $isNeedLabel = 0; 528 my $value; 529 my $fileHandle; 530 531 if(-e $fileName) { 532 open($fileHandle, "$fileName"); 533 }else { 534 return; 535 } 536 537 while(defined($_ = <$fileHandle>)) { 538 next if /^#/; # 丢弃注释 539 chomp; 540 541 if ($isNeedLabel == 1){ 542 if(/^[.*]/){ 543 # 进入另外一个段,退出 544 last; 545 } 546 if(!$isNeedLabel) { 547 next; 548 } 549 if(/$keyNames*=s*(.+);s*$/) { 550 $value = $1; 551 last; 552 } 553 }else{ 554 if(/^[.*]/){ 555 if(/[$labelName]/) { 556 $isNeedLabel = 1; 557 } 558 } 559 next; 560 } 561 } 562 close $fileHandle; 563 return ($value); 564 } 565 566 567 sub GetDate { 568 ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); 569 $now = sprintf("%d-%02d-%02d",$year+1900,$mon+1,$mday); 570 return $now; 571 } 572 573 sub GetDateTime { 574 ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); 575 $now = sprintf("%d-%02d-%02d %02d:%02d:%02d",$year+1900,$mon+1,$mday,$hour,$min,$sec); 576 return $now; 577 } 578 579 580 sub CalcCovs { 581 my $flag = 0; 582 my $red = "RED"; 583 my $blue = "BLUE"; 584 my $str = ""; 585 my $CurPreNodefile; 586 587 ParseTestCasesStat(); 588 589 GetDiffFileInfo($gDifFile, $flag); 590 591 PickCoverLine(); 592 593 my $sum = $gDecisionCount; 594 $gNewOutofNum = $sum; 595 my $covercount = $gDecisionCoverCount; 596 my $nocovercount = $sum - $covercount; 597 598 my $total_cases_stat = "<font color=$blue size=5>Total TestCases:$all_stats</font><br>"; 599 my $per_project_stat = "<font color=$blue size=5>Project TestCases:$proj_stats</font><br>"; 600 601 if($gDecisionCount == 0) { 602 $gNewCoverRage = 60; 603 $Content = "<font color=$red size=5>The Branch of Code No Change,So New Code Coverage:60%(项目达标线)</font>"."<br>"; 604 605 if(defined $gTestcasesStat){ 606 if(-e $gTestcasesStat) { 607 $Content .= $total_cases_stat; 608 $Content .= $per_project_stat; 609 } 610 } 611 $str = $gNewCoverRage; 612 } else { 613 $gNewCoverRage = ($gDecisionCoverCount)*100/($gDecisionCount); 614 $str = substr(lc($gNewCoverRage), 0, 6); 615 616 my $web_new = "<font color=$blue size=5>Sum Of New Branch:$sum</font><br>"; 617 my $web_cover = "<font color=$blue size=5>Covered Branch:$covercount</font><br>"; 618 my $web_uncover = "<font color=$blue size=5>UnCovered Branch:$nocovercount</font><br>"; 619 my $web_newcov = "<font color=$blue size=5>New Code Coverage:$str%</font><br>"; 620 621 if(defined $gTestcasesStat){ 622 if(-e $gTestcasesStat) { 623 $Content = $per_project_stat.$Content; 624 $Content = $total_cases_stat.$Content; 625 } 626 } 627 628 $Content = $web_new.$Content; 629 $Content = $web_cover.$Content; 630 $Content = $web_uncover.$Content; 631 $Content = $web_newcov.$Content; 632 } 633 634 if($Content ne "") { 635 print $ReportHtmlFile decode('gbk',"$Content"); 636 } 637 638 if($gDecisionCount == 0) { 639 print $RecodResultFile("两个节点间分支数未变化,新增覆盖率为 ---. "); 640 } else { 641 print $RecodResultFile("新增覆盖率:$gNewCoverRage% ---(新增代码被覆盖分支数/新增代码总分支数). "); 642 print $RecodResultFile("总的新增分支数:$sum,新增代码被覆盖分支数:$covercount,新增代码未覆盖分支数:$nocovercount. "); 643 } 644 645 if($gNewCoverRage < 60){ 646 print $RecodResultFile("新增覆盖率:未达项目线. "); 647 } 648 649 print "----------------------- "; 650 print "Total CHANGED branch count:".$gDecisionCount." "; 651 print "----------------------- "; 652 } 653 654 my $FoundSameFile = 0; 655 my $FoundSameKey = 0; 656 my $linecount=0; 657 658 sub merge_calc{ 659 my ($fileLine)=@_; 660 my (%zero_hash); 661 my (%branchHash); 662 my (@tmpArr); 663 my $tmp = 0; 664 my $tmpBranch= {}; 665 $linecount += 1; 666 # 新增加一个文件的分支数据 667 if($fileLine =~ /^SF:(.*/([^/]+[a-zA-Z]))[^a-zA-Z]*$/) { 668 #print "found file key:".$1.",".$2.",".$linecount." "; 669 # 清空 670 $branchHash = {}; 671 # 置位 672 $FoundSameKey = 0; 673 $FoundSameFile = 0; 674 675 if (exists $gLcovHash->{$2}){ 676 @tmpArr = @{$gLcovHash->{$2}}; 677 if ($#tmpArr >= 0){ 678 $FoundSameKey = 1; 679 #print "found same key ($#tmpArr): $tmpArr[0] "; 680 $tmpBranch= {}; 681 for $tmpBranch (@tmpArr){ 682 if ($1 eq $tmpBranch->{SF} ){ 683 # 文件完全一致,需要合并信息 684 $FoundSameFile = 1; 685 # 取出已存在的branch数据 686 $branchHash = $tmpBranch; 687 #print "found same file:".$branchHash->{SF}.",".$tmpBranch->{SF}.",".$linecount." "; 688 #last; 689 #}else{ 690 #print "same key but file diffrent: $1 ** $tmpBranch->{SF} "; 691 } 692 } 693 #}else{ 694 #print "array len invalid($#tmpArr), (".$2.") "; 695 } 696 #}else{ 697 #print "new key "; 698 } 699 $branchHash->{SF}=$1; 700 $branchHash->{fn}=$2; 701 }elsif($fileLine =~ /^(BRDA:d+,d+,d+),(.+?) * *$/) { 702 # BRDA:line,mod,condition = "0/1/...." 703 if (($2 eq "-")||($2 eq "0")) { 704 $tmp = 0; 705 }else{ 706 $tmp = $2; 707 } 708 709 if ($FoundSameFile) { 710 if (defined $branchHash->{$1}){ 711 #print "same file update BRDA:".$1.","".$branchHash->{$1}; 712 $branchHash->{$1} += $tmp; 713 #print "<=".$tmp."=>".$branchHash->{$1}."" "; 714 return; 715 } 716 } 717 $branchHash->{$1} = $tmp; 718 }elsif($fileLine =~ /^BRF:(d+)$/) { 719 # 一个文件的所有分支信息收集完成 720 @tmpArr=(); 721 if ( $FoundSameKey ){ 722 if ($FoundSameFile){ 723 @tmpArr = @{$gLcovHash->{$branchHash->{fn}}}; 724 for ( $idx = 0; $idx <= $#tmpArr; $idx ++){ 725 $tmp = $tmpArr[$idx]; 726 if ($tmp->{SF} eq $branchHash->{SF} ){ 727 #print "same file, node updated:$idx,$tmp "; 728 $gLcovHash->{$branchHash->{fn}}[$idx] = $branchHash; 729 last; 730 #}else{ 731 #print "diff file: $tmp->{SF},$branchHash->{SF} "; 732 } 733 } 734 }else{ 735 @tmpArr = @{$gLcovHash->{$branchHash->{fn}}}; 736 push (@tmpArr,$branchHash); 737 $gLcovHash->{$branchHash->{fn}} = [ @tmpArr ]; 738 #print "same key, new node pushed:@tmpArr "; 739 } 740 }else{ 741 push (@tmpArr,$branchHash); 742 $gLcovHash->{$branchHash->{fn}} = [ @tmpArr ]; 743 744 #print "new key pushed:@tmpArr,$branchHash->{fn},$branchHash->{SF} "; 745 } 746 #print "------------ "; 747 } 748 } 749 750 sub merge_lcov_info{ 751 my $fileHandle; 752 my $mergedLcovFile; 753 my $buildPath = ""; 754 755 for ( $cp_idx = 0; $cp_idx < @gBuildCodePathArr; $cp_idx ++){ 756 $linecount = 0; 757 # 遍历lcov的文件 758 $buildPath=$gBuildCodePathArr[$cp_idx]; 759 $lcovFile=$gLcovFile."_".$cp_idx; 760 print "----------------------- "; 761 print " lcov parse build path: $buildPath "; 762 system("lcov -q -c -d $buildPath -o $lcovFile -t fttest --ignore-errors gcov,source,graph --rc lcov_branch_coverage=1"); 763 764 if(-e $lcovFile) { 765 open($fileHandle, "$lcovFile"); 766 }else { 767 print "open $lcovFile fail. "; 768 return; 769 } 770 771 # 读取lcov文件,添加或者合并源文件的覆盖数据 772 print "merge_calc --- "; 773 while(<$fileHandle>) { 774 merge_calc($_); 775 } 776 close($fileHandle); 777 } 778 779 # 合并后的数据,进行输出 780 open($mergedLcovFile, "> $gLcovFile"); 781 782 print "----------------------- "; 783 print "export merged gLcov:$gLcovFile "; 784 for $fn (keys %{$gLcovHash}){ 785 for $tmp (@{$gLcovHash->{$fn}}){ 786 print $mergedLcovFile("SF:$tmp->{SF} "); 787 for $brdaKey (sort keys %{$tmp}){ 788 if ($brdaKey eq "SF" ){ 789 next; 790 } 791 print $mergedLcovFile("$brdaKey,$tmp->{$brdaKey} "); 792 } 793 } 794 } 795 796 close($mergedLcovFile); 797 } 798 799 sub changecovfile { 800 my $fileHandle; 801 my $filename = ""; 802 my $currfilename = ""; 803 my $lineNum = 0; 804 my $lineLastNum = 0; 805 my $linebegin = 1; 806 my $condition; 807 my $covflag; 808 my $covcontext=""; 809 my $resultfile; 810 my $totalbranch = 0; 811 my $covbranch = 0; 812 813 merge_lcov_info(); 814 815 if(-e $gLcovFile) { 816 open($fileHandle, "$gLcovFile"); 817 }else { 818 print "open $gLcovFile fail. "; 819 return; 820 } 821 822 open($resultfile, "> $gCoverFile"); 823 824 while(<$fileHandle>) { 825 #文件比较开始 826 if($_ =~ /^SF:*+/(.+?[a-zA-Z])[^a-zA-Z]*$/) { 827 $filename = $1; 828 } 829 if($_ =~ /^BRDA:(d+),(d+),(d+),(.+?)$/) { 830 $lineNum = $1; 831 $condition = $3; 832 833 if ($linebegin == 1) { 834 $linebegin = 0; 835 $lineLastNum = $lineNum; 836 } 837 838 if (($4 eq "-")||($4 eq "0")) { 839 $covflag = " -"; 840 }else { 841 $covflag = " +"; 842 } 843 844 if ($lineNum eq $lineLastNum) { 845 $covcontext = $covcontext.$covflag; 846 $totalbranch++; 847 if ($covflag eq " +") { 848 $covbranch++; 849 } 850 $currfilename = $filename; 851 }else { 852 $Content = $currfilename.",".$lineLastNum.",".$totalbranch.",".$covbranch.",".$covcontext." "; 853 print $resultfile("$Content"); 854 855 $covcontext = $covflag; 856 $lineLastNum = $lineNum; 857 $totalbranch = 1; 858 if ($covflag eq " +") { 859 $covbranch = 1; 860 }else { 861 $covbranch = 0; 862 } 863 } 864 } 865 } 866 867 $Content = $filename.",".$lineLastNum.",".$totalbranch.",".$covbranch.",".$covcontext." "; 868 print $resultfile("$Content"); 869 870 close($fileHandle); 871 872 close($resultfile); 873 $Content = ""; 874 } 875 876 sub ParseTestCasesStat{ 877 my $readFile; 878 879 print "----------------------- "; 880 if(defined $gTestcasesStat){ 881 if(-e $gTestcasesStat) { 882 open($readFile, "$gTestcasesStat"); 883 }else { 884 print $RecodResultFile("open $gTestcasesStat fail. "); 885 return; 886 } 887 while(<$readFile>) { 888 if($_ =~ /^all_cases:(d+) *$/) { 889 $all_stats = $1; 890 }elsif ($_ =~ /^project_cases:(.*) $/){ 891 $proj_stats = $1; 892 } 893 } 894 print "Got TestCaseStats info: $all_stats . $proj_stats "; 895 close($readFile); 896 } else { 897 print "gTestcaseStat not defined, stop parse testcases stat. " 898 } 899 } 900 901 902 sub GetYesterday(){ 903 my $ret= ""; 904 ($tmpY,$tmpM,$tmpD)=Add_Delta_Days(split("-",$gCurrDate),-1); 905 $ret = $tmpY."-".$tmpM."-".$tmpD; 906 print "Got Yestoday is ".$ret." "; 907 return $ret; 908 } 909 910 sub GetLastSunday(){ 911 my $delta=0; 912 my $ret= ""; 913 #($tmpY,$tmpM,$tmpD) = split("-",$gCurrDate); 914 $delta=Day_of_Week(split("-",$gCurrDate)); 915 ($tmpY,$tmpM,$tmpD)=Add_Delta_Days(split("-",$gCurrDate),-$delta); 916 $ret = $tmpY."-".$tmpM."-".$tmpD; 917 print "Got Last Sunday is ".$ret." "; 918 return $ret; 919 } 920 921 sub GetMonth1st(){ 922 my $ret= ""; 923 ($tmpY,$tmpM,$tmpD) = split("-",$gCurrDate); 924 $ret = $tmpY."-".$tmpM."-1"; 925 print "Got MONTH first day is ".$ret." "; 926 return $ret; 927 } 928 929 sub GetYear1st(){ 930 my $ret= ""; 931 ($tmpY,$tmpM,$tmpD) = split("-",$gCurrDate); 932 $ret = $tmpY."-1-1"; 933 print "Got YEAR first day is ".$ret." "; 934 return $ret; 935 } 936 937 sub GetDiffStartDate(){ 938 if ($gUpdateDateMethod eq "versionly") { 939 return $gCurrDate; # no use return value 940 }elsif ($gUpdateDateMethod eq "changely"){ 941 return $gCurrDate; # no use return value 942 }elsif ($gUpdateDateMethod eq "daily"){ 943 return GetYesterday(); 944 }elsif ($gUpdateDateMethod eq "weekly"){ 945 return GetLastSunday(); 946 }elsif ($gUpdateDateMethod eq "monthly"){ 947 return GetMonth1st(); 948 }elsif ($gUpdateDateMethod eq "yearly"){ 949 return GetYear1st(); 950 }else{ 951 print "UpdateMethod is WRONG!! "; 952 return 0; 953 } 954 } 955 956 sub PrintDiffParams{ 957 my ($Param)=@_; 958 print "----------------------- "; 959 print "Projects: $gCodeVersion GitBegin: $Param GitTrunk: $gBranchVersion DiffText: $gDifFile CodePath: $gCodePath "; 960 print "----------------------- "; 961 } 962 963 sub UpdateDiffFile { 964 my $writefile; 965 966 $gGitPrevDate = GetDiffStartDate(); 967 968 system("cd $gCodePath;git branch"); 969 970 if ($gUpdateDateMethod eq "versionly" ) { 971 PrintDiffParams($gNodeBase); 972 print "git diff $gNodeBase $gdiffId"; 973 system("cd $gCodePath;git diff -w -E $gNodeBase $gdiffId . > $gDifFile"); 974 }elsif ($gUpdateDateMethod eq "changely" ){ 975 system("cd $gCodePath;git diff -w -E origin/$gBranchVersion . > $gDifFile"); 976 }else{ 977 PrintDiffParams($gGitPrevDate); 978 system("cd $gCodePath;git log --after="$gGitPrevDate" --pretty="%h" --date-order | tail -n1 | xargs -n1 -i git diff -w -E {} origin/$gBranchVersion . > $gDifFile"); 979 } 980 981 #system("cd /home/brs/gitrepo/dev_21701/cov") 982 } 983 984 sub PutIgnorelinenoInHash { 985 my $filename; 986 if (defined $gIgnorelineno){ 987 my @IgnoreArr = split /;+/, $gIgnorelineno; 988 989 foreach(@IgnoreArr) { 990 if ($_ =~ /^(.*)|(.*)$/) { 991 $filename = $1; 992 #print $filename." "; 993 my @LineArr = split /:+/, $2; 994 foreach (@LineArr) { 995 push @{$gIgnoreLineHash{$filename}},$_; 996 } 997 } 998 } 999 } 1000 } 1001 1002 sub IsKeyWordsPath { 1003 my ($filepath) = @_; 1004 my $flag = 0; 1005 my $keyword = ""; 1006 1007 foreach(@gKeyWordsArr) { 1008 $keyword = $_; 1009 if (($keyword eq "all")||($filepath =~ /$keyword/)) { 1010 $flag = 1; 1011 return $flag; 1012 } 1013 } 1014 #返回0说明没有命中关键字,需要忽略 1015 return $flag; 1016 } 1017 1018 sub IsIgnore { 1019 my ($filename,$sectionname,$lineno) = @_; 1020 my $flag = 0; 1021 1022 foreach(@gIgnoreArr) { 1023 if ($_ eq $filename) { 1024 $flag = 1; 1025 return $flag; 1026 } 1027 } 1028 1029 foreach(@gIgnoreLineSection) { 1030 if ($_ eq $sectionname) { 1031 $flag = 1; 1032 return $flag; 1033 } 1034 } 1035 1036 if(exists $gIgnoreLineHash{$filename}) { 1037 my @LineNoArray = @{$gIgnoreLineHash{$filename}}; 1038 foreach(@LineNoArray) { 1039 if($lineno eq $_) { 1040 #print $funcname." "; 1041 $flag = 1; 1042 return $flag; 1043 } 1044 } 1045 } 1046 1047 return $flag; 1048 } 1049 1050 sub ReadProjectCfgFromCiCfg 1051 { 1052 my $myversionfile; 1053 my $projectfilehandle; 1054 my $version=""; 1055 1056 if(-e $gMyVersion) 1057 { 1058 open($myversionfile, "$gMyVersion"); 1059 }else 1060 { 1061 print $RecodResultFile("open $gMyVersion fail. "); 1062 return; 1063 } 1064 1065 while(<$myversionfile>) 1066 { 1067 if($_ =~ /^(.*)$/) 1068 { 1069 $version = $1; 1070 } 1071 } 1072 close($myversionfile); 1073 1074 $gCiVersionCfgFile=$gCiHomePath.$gCiVersionCfgFile; 1075 if(-e $gCiVersionCfgFile) 1076 { 1077 open($projectfilehandle, "$gCiVersionCfgFile"); 1078 }else 1079 { 1080 print $RecodResultFile("open $gCiVersionCfgFile fail. "); 1081 return; 1082 } 1083 1084 while(<$projectfilehandle>) 1085 { 1086 if($_ =~ /^(.*) (.*) (.*) (.*) (.*)$/) 1087 { 1088 if ($1 eq $version) 1089 { 1090 $gBranchVersion = $2; 1091 $gCodeVersion = $4; 1092 print 1093 $1. 1094 "----".$2. 1095 "----".$3. 1096 "----".$4. 1097 "----".$5. 1098 " "; 1099 } 1100 } 1101 } 1102 close($projectfilehandle); 1103 1104 }
https://blog.csdn.net/yyw794/article/details/77963310 原因 Lcov(1.10及往后)默认是关闭 分支覆盖率的。 需要在参数中加入 --rc lcov_branch_coverage=1 才能使能。 (即修改lcovrc的配置信息) 解决措施 ENABLE_BRANCH="--rc lcov_branch_coverage=1" lcov -c -d $TESTCASE_DIR -o $info_file ${ENABLE_BRANCH} 1>/dev/null 另外,genhtml中要添加上面的参数 genhtml --branch-coverage -o $COVERAGE_DIR $GENHTML_OPTIONS ${ENABLE_BRANCH} $info_file 1>/dev/null 你看看jenkins的单元测试的html输出,是否新增了分支覆盖率统计。
Executed 3 out of 3 tests: 3 tests pass.
run test succ
cp dbg succ
find: `bazel-out/local-opt/bin/modules/': No such file or directory
cp opt succ
Capturing coverage data from data/cov/objs
Found gcov version: 8.1.0
Scanning data/cov/objs for .gcda files ...
Found 150 data files in data/cov/objs
Processing data/proto/static_info.pb.pic.gcda
geninfo: ERROR: /apollo/data/cov/objs/modules/data/proto/static_info.pb.pic.gcno: reached unexpected end of file
============================
[ERROR] lcov failed!
(standard_in) 2: syntax error
[INFO] Took seconds
============================
gcc -fprofile-arcs -ftest-coverage -o test test.c
lcov --directory . --capture --output-file test.info
genhtml -o results test.info
https://github.com/linux-test-project/lcov
lcov release version cannot resolve gcc 8. Use the github code and install 1.13.
google test lcov genhtml 产生覆盖率xml文件,去除不需要的文件(include),或者包含需要的(source)
https://blog.csdn.net/hanshuai584044490/article/details/83374617
问题:在产生了.gcno 和 .gcda两个文件后,使用lcov -c -d Debug/source/ -o Debug/coverage.info 产生中间文件coverage.info文件,然后用genhtml -o output/ Debug/coverage.info产生html文件,发现产生的index.xml文件包含了include,甚至/usr/*下的公共头文件,怎么去除这些不需要统计覆盖率的文件?
1。正向提取需要的文件:
//比如希望把source相关的路径提取出来
lcov --extract Debug/coverage.info '*source/*' -o Debug/finalresult.info
//然后产生的xml就包含所有source相关的文件
genhtml -o output/ Debug/finalresult.info
2。反向去除不需要的文件:
//比如希望去除UnitTest 和/usr/相关文件:
lcov --remove Debug/coverage.info '*UnitTest/*' '/usr/*' -o Debug/finalresult.info
//然后产生的xml就去除了UnitTest 和/usr/相关的文件
genhtml -o output/ Debug/finalresult.info
注意:lcov 不允许同时使用--extract 和 --remove