ENH: keep cleaning up Tcl/Tk modules
[cmake.git] / Source / CTest / cmCTestCoverageHandler.cxx
blob1524965b70768ac1528e1caf158452c75943d8d8
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCTestCoverageHandler.cxx,v $
5 Language: C++
6 Date: $Date: 2007-11-16 16:32:38 $
7 Version: $Revision: 1.50 $
9 Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
10 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
12 This software is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 PURPOSE. See the above copyright notices for more information.
16 =========================================================================*/
17 #include "cmCTestCoverageHandler.h"
18 #include "cmCTest.h"
19 #include "cmake.h"
20 #include "cmSystemTools.h"
21 #include "cmGeneratedFileStream.h"
23 #include <cmsys/Process.h>
24 #include <cmsys/RegularExpression.hxx>
25 #include <cmsys/Glob.hxx>
27 #include <stdlib.h>
28 #include <math.h>
29 #include <float.h>
31 #define SAFEDIV(x,y) (((y)!=0)?((x)/(y)):(0))
33 class cmCTestRunProcess
35 public:
36 cmCTestRunProcess()
38 this->Process = cmsysProcess_New();
39 this->PipeState = -1;
40 this->TimeOut = -1;
42 ~cmCTestRunProcess()
44 if(!(this->PipeState == -1)
45 && !(this->PipeState == cmsysProcess_Pipe_None )
46 && !(this->PipeState == cmsysProcess_Pipe_Timeout))
48 this->WaitForExit();
50 cmsysProcess_Delete(this->Process);
52 void SetCommand(const char* command)
54 this->CommandLineStrings.clear();
55 this->CommandLineStrings.push_back(command);;
57 void AddArgument(const char* arg)
59 if(arg)
61 this->CommandLineStrings.push_back(arg);
64 void SetWorkingDirectory(const char* dir)
66 this->WorkingDirectory = dir;
68 void SetTimeout(double t)
70 this->TimeOut = t;
72 bool StartProcess()
74 std::vector<const char*> args;
75 for(std::vector<std::string>::iterator i =
76 this->CommandLineStrings.begin();
77 i != this->CommandLineStrings.end(); ++i)
79 args.push_back(i->c_str());
81 args.push_back(0); // null terminate
82 cmsysProcess_SetCommand(this->Process, &*args.begin());
83 if(this->WorkingDirectory.size())
85 cmsysProcess_SetWorkingDirectory(this->Process,
86 this->WorkingDirectory.c_str());
89 cmsysProcess_SetOption(this->Process,
90 cmsysProcess_Option_HideWindow, 1);
91 if(this->TimeOut != -1)
93 cmsysProcess_SetTimeout(this->Process, this->TimeOut);
95 cmsysProcess_Execute(this->Process);
96 this->PipeState = cmsysProcess_GetState(this->Process);
97 // if the process is running or exited return true
98 if(this->PipeState == cmsysProcess_State_Executing
99 || this->PipeState == cmsysProcess_State_Exited)
101 return true;
103 return false;
105 void SetStdoutFile(const char* fname)
107 cmsysProcess_SetPipeFile(this->Process, cmsysProcess_Pipe_STDOUT, fname);
109 void SetStderrFile(const char* fname)
111 cmsysProcess_SetPipeFile(this->Process, cmsysProcess_Pipe_STDERR, fname);
113 int WaitForExit(double* timeout =0)
115 this->PipeState = cmsysProcess_WaitForExit(this->Process,
116 timeout);
117 return this->PipeState;
119 int GetProcessState() { return this->PipeState;}
120 private:
121 int PipeState;
122 cmsysProcess* Process;
123 std::vector<std::string> CommandLineStrings;
124 std::string WorkingDirectory;
125 double TimeOut;
129 //----------------------------------------------------------------------
130 //**********************************************************************
131 class cmCTestCoverageHandlerContainer
133 public:
134 int Error;
135 std::string SourceDir;
136 std::string BinaryDir;
137 typedef std::vector<int> SingleFileCoverageVector;
138 typedef std::map<std::string, SingleFileCoverageVector> TotalCoverageMap;
139 TotalCoverageMap TotalCoverage;
140 std::ostream* OFS;
142 //**********************************************************************
143 //----------------------------------------------------------------------
145 //----------------------------------------------------------------------
146 cmCTestCoverageHandler::cmCTestCoverageHandler()
150 //----------------------------------------------------------------------
151 void cmCTestCoverageHandler::Initialize()
153 this->Superclass::Initialize();
154 this->CustomCoverageExclude.empty();
157 //----------------------------------------------------------------------
158 bool cmCTestCoverageHandler::StartCoverageLogFile(
159 cmGeneratedFileStream& covLogFile, int logFileCount)
161 char covLogFilename[1024];
162 sprintf(covLogFilename, "CoverageLog-%d", logFileCount);
163 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Open file: "
164 << covLogFilename << std::endl);
165 if (!this->StartResultingXML(covLogFilename, covLogFile) )
167 cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open log file: "
168 << covLogFilename << std::endl);
169 return false;
171 std::string local_start_time = this->CTest->CurrentTime();
172 this->CTest->StartXML(covLogFile);
173 covLogFile << "<CoverageLog>" << std::endl
174 << "\t<StartDateTime>" << local_start_time << "</StartDateTime>"
175 << std::endl;
176 return true;
179 //----------------------------------------------------------------------
180 void cmCTestCoverageHandler::EndCoverageLogFile(cmGeneratedFileStream& ostr,
181 int logFileCount)
183 std::string local_end_time = this->CTest->CurrentTime();
184 ostr << "\t<EndDateTime>" << local_end_time << "</EndDateTime>" << std::endl
185 << "</CoverageLog>" << std::endl;
186 this->CTest->EndXML(ostr);
187 char covLogFilename[1024];
188 sprintf(covLogFilename, "CoverageLog-%d.xml", logFileCount);
189 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Close file: "
190 << covLogFilename << std::endl);
191 ostr.Close();
194 //----------------------------------------------------------------------
195 bool cmCTestCoverageHandler::ShouldIDoCoverage(const char* file,
196 const char* srcDir,
197 const char* binDir)
199 std::vector<cmsys::RegularExpression>::iterator sit;
200 for ( sit = this->CustomCoverageExcludeRegex.begin();
201 sit != this->CustomCoverageExcludeRegex.end(); ++ sit )
203 if ( sit->find(file) )
205 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " File " << file
206 << " is excluded in CTestCustom.ctest" << std::endl;);
207 return false;
211 std::string fSrcDir = cmSystemTools::CollapseFullPath(srcDir);
212 std::string fBinDir = cmSystemTools::CollapseFullPath(binDir);
213 std::string fFile = cmSystemTools::CollapseFullPath(file);
214 bool sourceSubDir = cmSystemTools::IsSubDirectory(fFile.c_str(),
215 fSrcDir.c_str());
216 bool buildSubDir = cmSystemTools::IsSubDirectory(fFile.c_str(),
217 fBinDir.c_str());
218 // Always check parent directory of the file.
219 std::string fileDir = cmSystemTools::GetFilenamePath(fFile.c_str());
220 std::string checkDir;
222 // We also need to check the binary/source directory pair.
223 if ( sourceSubDir && buildSubDir )
225 if ( fSrcDir.size() > fBinDir.size() )
227 checkDir = fSrcDir;
229 else
231 checkDir = fBinDir;
234 else if ( sourceSubDir )
236 checkDir = fSrcDir;
238 else if ( buildSubDir )
240 checkDir = fBinDir;
242 std::string ndc
243 = cmSystemTools::FileExistsInParentDirectories(".NoDartCoverage",
244 fFile.c_str(), checkDir.c_str());
245 if ( ndc.size() )
247 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Found: " << ndc.c_str()
248 << " so skip coverage of " << file << std::endl);
249 return false;
252 // By now checkDir should be set to parent directory of the file.
253 // Get the relative path to the file an apply it to the opposite directory.
254 // If it is the same as fileDir, then ignore, otherwise check.
255 std::string relPath;
256 if(checkDir.size() )
258 relPath = cmSystemTools::RelativePath(checkDir.c_str(),
259 fFile.c_str());
261 else
263 relPath = fFile;
265 if ( checkDir == fSrcDir )
267 checkDir = fBinDir;
269 else
271 checkDir = fSrcDir;
273 fFile = checkDir + "/" + relPath;
274 fFile = cmSystemTools::GetFilenamePath(fFile.c_str());
276 if ( fileDir == fFile )
278 // This is in-source build, so we trust the previous check.
279 return true;
282 ndc = cmSystemTools::FileExistsInParentDirectories(".NoDartCoverage",
283 fFile.c_str(), checkDir.c_str());
284 if ( ndc.size() )
286 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Found: " << ndc.c_str()
287 << " so skip coverage of: " << file << std::endl);
288 return false;
290 // Ok, nothing in source tree, nothing in binary tree
291 return true;
294 //----------------------------------------------------------------------
295 //clearly it would be nice if this were broken up into a few smaller
296 //functions and commented...
297 int cmCTestCoverageHandler::ProcessHandler()
299 int error = 0;
300 // do we have time for this
301 if (this->CTest->GetRemainingTimeAllowed() < 120)
303 return error;
306 std::string coverage_start_time = this->CTest->CurrentTime();
308 std::string sourceDir
309 = this->CTest->GetCTestConfiguration("SourceDirectory");
310 std::string binaryDir
311 = this->CTest->GetCTestConfiguration("BuildDirectory");
313 cmGeneratedFileStream ofs;
314 double elapsed_time_start = cmSystemTools::GetTime();
315 if ( !this->StartLogFile("Coverage", ofs) )
317 cmCTestLog(this->CTest, ERROR_MESSAGE,
318 "Cannot create LastCoverage.log file" << std::endl);
321 ofs << "Performing coverage: " << elapsed_time_start << std::endl;
323 cmSystemTools::ConvertToUnixSlashes(sourceDir);
324 cmSystemTools::ConvertToUnixSlashes(binaryDir);
326 std::string asfGlob = sourceDir + "/*";
327 std::string abfGlob = binaryDir + "/*";
329 cmCTestLog(this->CTest, HANDLER_OUTPUT, "Performing coverage" << std::endl);
331 cmCTestCoverageHandlerContainer cont;
332 cont.Error = error;
333 cont.SourceDir = sourceDir;
334 cont.BinaryDir = binaryDir;
335 cont.OFS = &ofs;
337 // setup the regex exclude stuff
338 this->CustomCoverageExcludeRegex.empty();
339 std::vector<cmStdString>::iterator rexIt;
340 for ( rexIt = this->CustomCoverageExclude.begin();
341 rexIt != this->CustomCoverageExclude.end();
342 ++ rexIt )
344 this->CustomCoverageExcludeRegex.push_back(
345 cmsys::RegularExpression(rexIt->c_str()));
349 if(this->HandleBullseyeCoverage(&cont))
351 return cont.Error;
353 int file_count = 0;
354 file_count += this->HandleGCovCoverage(&cont);
355 if ( file_count < 0 )
357 return error;
359 file_count += this->HandleTracePyCoverage(&cont);
360 if ( file_count < 0 )
362 return error;
364 error = cont.Error;
367 if ( file_count == 0 )
369 cmCTestLog(this->CTest, WARNING,
370 " Cannot find any coverage files. Ignoring Coverage request."
371 << std::endl);
372 return error;
374 cmGeneratedFileStream covSumFile;
375 cmGeneratedFileStream covLogFile;
377 if (!this->StartResultingXML("Coverage", covSumFile))
379 cmCTestLog(this->CTest, ERROR_MESSAGE,
380 "Cannot open coverage summary file." << std::endl);
381 return -1;
384 this->CTest->StartXML(covSumFile);
385 // Produce output xml files
387 covSumFile << "<Coverage>" << std::endl
388 << "\t<StartDateTime>" << coverage_start_time << "</StartDateTime>"
389 << std::endl;
390 int logFileCount = 0;
391 if ( !this->StartCoverageLogFile(covLogFile, logFileCount) )
393 return -1;
395 cmCTestCoverageHandlerContainer::TotalCoverageMap::iterator fileIterator;
396 int cnt = 0;
397 long total_tested = 0;
398 long total_untested = 0;
399 //std::string fullSourceDir = sourceDir + "/";
400 //std::string fullBinaryDir = binaryDir + "/";
401 cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl);
402 cmCTestLog(this->CTest, HANDLER_OUTPUT,
403 " Acumulating results (each . represents one file):" << std::endl);
404 cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
406 std::vector<std::string> errorsWhileAccumulating;
408 file_count = 0;
409 for ( fileIterator = cont.TotalCoverage.begin();
410 fileIterator != cont.TotalCoverage.end();
411 ++fileIterator )
413 cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
414 file_count ++;
415 if ( file_count % 50 == 0 )
417 cmCTestLog(this->CTest, HANDLER_OUTPUT, " processed: " << file_count
418 << " out of "
419 << cont.TotalCoverage.size() << std::endl);
420 cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
422 if ( cnt % 100 == 0 )
424 this->EndCoverageLogFile(covLogFile, logFileCount);
425 logFileCount ++;
426 if ( !this->StartCoverageLogFile(covLogFile, logFileCount) )
428 return -1;
431 const std::string fullFileName = fileIterator->first;
432 const std::string fileName
433 = cmSystemTools::GetFilenameName(fullFileName.c_str());
434 std::string fullFilePath
435 = cmSystemTools::GetFilenamePath(fullFileName.c_str());
436 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
437 "Process file: " << fullFileName << std::endl);
439 cmSystemTools::ConvertToUnixSlashes(fullFilePath);
441 if ( !cmSystemTools::FileExists(fullFileName.c_str()) )
443 cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find file: "
444 << fullFileName.c_str() << std::endl);
445 continue;
448 bool shouldIDoCoverage
449 = this->ShouldIDoCoverage(fullFileName.c_str(),
450 sourceDir.c_str(), binaryDir.c_str());
451 if ( !shouldIDoCoverage )
453 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
454 ".NoDartCoverage found, so skip coverage check for: "
455 << fullFileName.c_str()
456 << std::endl);
457 continue;
460 const cmCTestCoverageHandlerContainer::SingleFileCoverageVector& fcov
461 = fileIterator->second;
462 covLogFile << "\t<File Name=\""
463 << this->CTest->MakeXMLSafe(fileName.c_str())
464 << "\" FullPath=\"" << this->CTest->MakeXMLSafe(
465 this->CTest->GetShortPathToFile(
466 fileIterator->first.c_str())) << "\">" << std::endl
467 << "\t\t<Report>" << std::endl;
469 std::ifstream ifs(fullFileName.c_str());
470 if ( !ifs)
472 cmOStringStream ostr;
473 ostr << "Cannot open source file: " << fullFileName.c_str();
474 errorsWhileAccumulating.push_back(ostr.str());
475 error ++;
476 continue;
479 int tested = 0;
480 int untested = 0;
482 cmCTestCoverageHandlerContainer::SingleFileCoverageVector::size_type cc;
483 std::string line;
484 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
485 "Actually perfoming coverage for: " << fullFileName << std::endl);
486 for ( cc= 0; cc < fcov.size(); cc ++ )
488 if ( !cmSystemTools::GetLineFromStream(ifs, line) &&
489 cc != fcov.size() -1 )
491 cmOStringStream ostr;
492 ostr << "Problem reading source file: " << fullFileName.c_str()
493 << " line:" << cc;
494 errorsWhileAccumulating.push_back(ostr.str());
495 error ++;
496 break;
498 covLogFile << "\t\t<Line Number=\"" << cc << "\" Count=\"" << fcov[cc]
499 << "\">"
500 << this->CTest->MakeXMLSafe(line.c_str()) << "</Line>" << std::endl;
501 if ( fcov[cc] == 0 )
503 untested ++;
505 else if ( fcov[cc] > 0 )
507 tested ++;
510 if ( cmSystemTools::GetLineFromStream(ifs, line) )
512 cmOStringStream ostr;
513 ostr << "Looks like there are more lines in the file: " << line;
514 errorsWhileAccumulating.push_back(ostr.str());
516 float cper = 0;
517 float cmet = 0;
518 if ( tested + untested > 0 )
520 cper = (100 * SAFEDIV(static_cast<float>(tested),
521 static_cast<float>(tested + untested)));
522 cmet = ( SAFEDIV(static_cast<float>(tested + 10),
523 static_cast<float>(tested + untested + 10)));
525 total_tested += tested;
526 total_untested += untested;
527 covLogFile << "\t\t</Report>" << std::endl
528 << "\t</File>" << std::endl;
529 covSumFile << "\t<File Name=\"" << this->CTest->MakeXMLSafe(fileName)
530 << "\" FullPath=\"" << this->CTest->MakeXMLSafe(
531 this->CTest->GetShortPathToFile(fullFileName.c_str()))
532 << "\" Covered=\"" << (tested==0?"true":"false") << "\">\n"
533 << "\t\t<LOCTested>" << tested << "</LOCTested>\n"
534 << "\t\t<LOCUnTested>" << untested << "</LOCUnTested>\n"
535 << "\t\t<PercentCoverage>";
536 covSumFile.setf(std::ios::fixed, std::ios::floatfield);
537 covSumFile.precision(2);
538 covSumFile << (cper) << "</PercentCoverage>\n"
539 << "\t\t<CoverageMetric>";
540 covSumFile.setf(std::ios::fixed, std::ios::floatfield);
541 covSumFile.precision(2);
542 covSumFile << (cmet) << "</CoverageMetric>\n"
543 << "\t</File>" << std::endl;
544 cnt ++;
546 this->EndCoverageLogFile(covLogFile, logFileCount);
548 if ( errorsWhileAccumulating.size() > 0 )
550 cmCTestLog(this->CTest, ERROR_MESSAGE, std::endl);
551 cmCTestLog(this->CTest, ERROR_MESSAGE,
552 "Error(s) while acumulating results:" << std::endl);
553 std::vector<std::string>::iterator erIt;
554 for ( erIt = errorsWhileAccumulating.begin();
555 erIt != errorsWhileAccumulating.end();
556 ++ erIt )
558 cmCTestLog(this->CTest, ERROR_MESSAGE,
559 " " << erIt->c_str() << std::endl);
563 int total_lines = total_tested + total_untested;
564 float percent_coverage = 100 * SAFEDIV(static_cast<float>(total_tested),
565 static_cast<float>(total_lines));
566 if ( total_lines == 0 )
568 percent_coverage = 0;
571 std::string end_time = this->CTest->CurrentTime();
573 covSumFile << "\t<LOCTested>" << total_tested << "</LOCTested>\n"
574 << "\t<LOCUntested>" << total_untested << "</LOCUntested>\n"
575 << "\t<LOC>" << total_lines << "</LOC>\n"
576 << "\t<PercentCoverage>";
577 covSumFile.setf(std::ios::fixed, std::ios::floatfield);
578 covSumFile.precision(2);
579 covSumFile << (percent_coverage)<< "</PercentCoverage>\n"
580 << "\t<EndDateTime>" << end_time << "</EndDateTime>\n";
581 covSumFile << "<ElapsedMinutes>" <<
582 static_cast<int>((cmSystemTools::GetTime() - elapsed_time_start)/6)/10.0
583 << "</ElapsedMinutes>"
584 << "</Coverage>" << std::endl;
585 this->CTest->EndXML(covSumFile);
587 cmCTestLog(this->CTest, HANDLER_OUTPUT, "" << std::endl
588 << "\tCovered LOC: "
589 << total_tested << std::endl
590 << "\tNot covered LOC: " << total_untested << std::endl
591 << "\tTotal LOC: " << total_lines << std::endl
592 << "\tPercentage Coverage: "
593 << std::setiosflags(std::ios::fixed)
594 << std::setprecision(2)
595 << (percent_coverage) << "%" << std::endl);
597 ofs << "\tCovered LOC: " << total_tested << std::endl
598 << "\tNot covered LOC: " << total_untested << std::endl
599 << "\tTotal LOC: " << total_lines << std::endl
600 << "\tPercentage Coverage: "
601 << std::setiosflags(std::ios::fixed)
602 << std::setprecision(2)
603 << (percent_coverage) << "%" << std::endl;
606 if ( error )
608 return -1;
610 return 0;
613 //----------------------------------------------------------------------
614 void cmCTestCoverageHandler::PopulateCustomVectors(cmMakefile *mf)
616 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
617 " Add coverage exclude regular expressions." << std::endl);
618 this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_COVERAGE_EXCLUDE",
619 this->CustomCoverageExclude);
620 std::vector<cmStdString>::iterator it;
621 for ( it = this->CustomCoverageExclude.begin();
622 it != this->CustomCoverageExclude.end();
623 ++ it )
625 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Add coverage exclude: "
626 << it->c_str() << std::endl);
630 //----------------------------------------------------------------------
631 int cmCTestCoverageHandler::HandleGCovCoverage(
632 cmCTestCoverageHandlerContainer* cont)
634 std::string gcovCommand
635 = this->CTest->GetCTestConfiguration("CoverageCommand");
637 // Style 1
638 std::string st1gcovOutputRex1
639 = "[0-9]+\\.[0-9]+% of [0-9]+ (source |)lines executed in file (.*)$";
640 std::string st1gcovOutputRex2 = "^Creating (.*\\.gcov)\\.";
641 cmsys::RegularExpression st1re1(st1gcovOutputRex1.c_str());
642 cmsys::RegularExpression st1re2(st1gcovOutputRex2.c_str());
645 // Style 2
646 std::string st2gcovOutputRex1 = "^File *[`'](.*)'$";
647 std::string st2gcovOutputRex2
648 = "Lines executed: *[0-9]+\\.[0-9]+% of [0-9]+$";
649 std::string st2gcovOutputRex3 = "^(.*):creating [`'](.*\\.gcov)'";
650 std::string st2gcovOutputRex4 = "^(.*):unexpected EOF *$";
651 std::string st2gcovOutputRex5 = "^(.*):cannot open source file*$";
652 std::string st2gcovOutputRex6
653 = "^(.*):source file is newer than graph file `(.*)'$";
654 cmsys::RegularExpression st2re1(st2gcovOutputRex1.c_str());
655 cmsys::RegularExpression st2re2(st2gcovOutputRex2.c_str());
656 cmsys::RegularExpression st2re3(st2gcovOutputRex3.c_str());
657 cmsys::RegularExpression st2re4(st2gcovOutputRex4.c_str());
658 cmsys::RegularExpression st2re5(st2gcovOutputRex5.c_str());
659 cmsys::RegularExpression st2re6(st2gcovOutputRex6.c_str());
662 cmsys::Glob gl;
663 gl.RecurseOn();
664 std::string daGlob = cont->BinaryDir + "/*.da";
665 gl.FindFiles(daGlob);
666 std::vector<std::string> files = gl.GetFiles();
667 daGlob = cont->BinaryDir + "/*.gcda";
668 gl.FindFiles(daGlob);
669 std::vector<std::string>& moreFiles = gl.GetFiles();
670 files.insert(files.end(), moreFiles.begin(), moreFiles.end());
671 std::vector<std::string>::iterator it;
673 if ( files.size() == 0 )
675 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
676 " Cannot find any GCov coverage files."
677 << std::endl);
678 // No coverage files is a valid thing, so the exit code is 0
679 return 0;
682 std::string testingDir = this->CTest->GetBinaryDir() + "/Testing";
683 std::string tempDir = testingDir + "/CoverageInfo";
684 std::string currentDirectory = cmSystemTools::GetCurrentWorkingDirectory();
685 cmSystemTools::MakeDirectory(tempDir.c_str());
686 cmSystemTools::ChangeDirectory(tempDir.c_str());
688 int gcovStyle = 0;
690 std::set<std::string> missingFiles;
692 std::string actualSourceFile = "";
693 cmCTestLog(this->CTest, HANDLER_OUTPUT,
694 " Processing coverage (each . represents one file):" << std::endl);
695 cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
696 int file_count = 0;
697 for ( it = files.begin(); it != files.end(); ++ it )
699 cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
700 std::string fileDir = cmSystemTools::GetFilenamePath(it->c_str());
701 std::string command = "\"" + gcovCommand + "\" -l -o \"" + fileDir
702 + "\" \"" + *it + "\"";
703 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, command.c_str()
704 << std::endl);
705 std::string output = "";
706 std::string errors = "";
707 int retVal = 0;
708 *cont->OFS << "* Run coverage for: " << fileDir.c_str() << std::endl;
709 *cont->OFS << " Command: " << command.c_str() << std::endl;
710 int res = this->CTest->RunCommand(command.c_str(), &output, &errors,
711 &retVal, tempDir.c_str(), 0 /*this->TimeOut*/);
713 *cont->OFS << " Output: " << output.c_str() << std::endl;
714 *cont->OFS << " Errors: " << errors.c_str() << std::endl;
715 if ( ! res )
717 cmCTestLog(this->CTest, ERROR_MESSAGE,
718 "Problem running coverage on file: " << it->c_str() << std::endl);
719 cmCTestLog(this->CTest, ERROR_MESSAGE,
720 "Command produced error: " << errors << std::endl);
721 cont->Error ++;
722 continue;
724 if ( retVal != 0 )
726 cmCTestLog(this->CTest, ERROR_MESSAGE, "Coverage command returned: "
727 << retVal << " while processing: " << it->c_str() << std::endl);
728 cmCTestLog(this->CTest, ERROR_MESSAGE,
729 "Command produced error: " << cont->Error << std::endl);
731 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
732 "--------------------------------------------------------------"
733 << std::endl
734 << output << std::endl
735 << "--------------------------------------------------------------"
736 << std::endl);
737 std::vector<cmStdString> lines;
738 std::vector<cmStdString>::iterator line;
741 // Globals for storing current source file and current gcov file;
742 cmSystemTools::Split(output.c_str(), lines);
743 for ( line = lines.begin(); line != lines.end(); ++line)
745 std::string sourceFile;
746 std::string gcovFile;
747 cmCTestLog(this->CTest, DEBUG, "Line: [" << line->c_str() << "]"
748 << std::endl);
749 if ( line->size() == 0 )
751 // Ignore empty line; probably style 2
753 else if ( st1re1.find(line->c_str()) )
755 if ( gcovStyle != 0 )
757 if ( gcovStyle != 1 )
759 cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
760 << std::endl);
761 cont->Error ++;
762 break;
764 gcovStyle = 1;
767 actualSourceFile = "";
768 sourceFile = st1re1.match(2);
770 else if ( st1re2.find(line->c_str() ) )
772 if ( gcovStyle != 0 )
774 if ( gcovStyle != 1 )
776 cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
777 << std::endl);
778 cont->Error ++;
779 break;
781 gcovStyle = 1;
784 gcovFile = st1re2.match(1);
786 else if ( st2re1.find(line->c_str() ) )
788 if ( gcovStyle != 0 )
790 if ( gcovStyle != 2 )
792 cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
793 << std::endl);
794 cont->Error ++;
795 break;
797 gcovStyle = 2;
800 actualSourceFile = "";
801 sourceFile = st2re1.match(1);
803 else if ( st2re2.find(line->c_str() ) )
805 if ( gcovStyle != 0 )
807 if ( gcovStyle != 2 )
809 cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
810 << std::endl);
811 cont->Error ++;
812 break;
814 gcovStyle = 2;
817 else if ( st2re3.find(line->c_str() ) )
819 if ( gcovStyle != 0 )
821 if ( gcovStyle != 2 )
823 cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
824 << std::endl);
825 cont->Error ++;
826 break;
828 gcovStyle = 2;
831 gcovFile = st2re3.match(2);
833 else if ( st2re4.find(line->c_str() ) )
835 if ( gcovStyle != 0 )
837 if ( gcovStyle != 2 )
839 cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
840 << std::endl);
841 cont->Error ++;
842 break;
844 gcovStyle = 2;
847 cmCTestLog(this->CTest, WARNING, "Warning: " << st2re4.match(1)
848 << " had unexpected EOF" << std::endl);
850 else if ( st2re5.find(line->c_str() ) )
852 if ( gcovStyle != 0 )
854 if ( gcovStyle != 2 )
856 cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
857 << std::endl);
858 cont->Error ++;
859 break;
861 gcovStyle = 2;
864 cmCTestLog(this->CTest, WARNING, "Warning: Cannot open file: "
865 << st2re5.match(1) << std::endl);
867 else if ( st2re6.find(line->c_str() ) )
869 if ( gcovStyle != 0 )
871 if ( gcovStyle != 2 )
873 cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
874 << std::endl);
875 cont->Error ++;
876 break;
878 gcovStyle = 2;
881 cmCTestLog(this->CTest, WARNING, "Warning: File: " << st2re6.match(1)
882 << " is newer than " << st2re6.match(2) << std::endl);
884 else
886 cmCTestLog(this->CTest, ERROR_MESSAGE,
887 "Unknown line: [" << line->c_str() << "]" << std::endl);
888 cont->Error ++;
889 //abort();
891 if ( !gcovFile.empty() && actualSourceFile.size() )
893 cmCTestCoverageHandlerContainer::SingleFileCoverageVector* vec
894 = &cont->TotalCoverage[actualSourceFile];
895 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " in file: "
896 << gcovFile << std::endl);
897 std::ifstream ifile(gcovFile.c_str());
898 if ( ! ifile )
900 cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open file: "
901 << gcovFile << std::endl);
903 else
905 long cnt = -1;
906 std::string nl;
907 while ( cmSystemTools::GetLineFromStream(ifile, nl) )
909 cnt ++;
911 //TODO: Handle gcov 3.0 non-coverage lines
913 // Skip empty lines
914 if ( !nl.size() )
916 continue;
919 // Skip unused lines
920 if ( nl.size() < 12 )
922 continue;
925 // Read the coverage count from the beginning of the gcov output
926 // line
927 std::string prefix = nl.substr(0, 12);
928 int cov = atoi(prefix.c_str());
929 // Read the line number starting at the 10th character of the gcov
930 // output line
931 std::string lineNumber = nl.substr(10, 5);
932 int lineIdx = atoi(lineNumber.c_str())-1;
933 if ( lineIdx >= 0 )
935 while ( vec->size() <=
936 static_cast<size_t>(lineIdx) )
938 vec->push_back(-1);
940 // Initially all entries are -1 (not used). If we get coverage
941 // information, increment it to 0 first.
942 if ( (*vec)[lineIdx] < 0 )
944 if ( cov > 0 || prefix.find("#") != prefix.npos )
946 (*vec)[lineIdx] = 0;
949 (*vec)[lineIdx] += cov;
953 actualSourceFile = "";
955 if ( !sourceFile.empty() && actualSourceFile.empty() )
957 gcovFile = "";
958 // Is it in the source dir?
959 if ( sourceFile.size() > cont->SourceDir.size() &&
960 sourceFile.substr(0, cont->SourceDir.size()) == cont->SourceDir &&
961 sourceFile[cont->SourceDir.size()] == '/' )
963 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " produced s: "
964 << sourceFile.c_str() << std::endl);
965 *cont->OFS << " produced in source dir: " << sourceFile.c_str()
966 << std::endl;
967 actualSourceFile
968 = cmSystemTools::CollapseFullPath(sourceFile.c_str());
970 // Binary dir?
971 if ( sourceFile.size() > cont->BinaryDir.size() &&
972 sourceFile.substr(0, cont->BinaryDir.size()) == cont->BinaryDir &&
973 sourceFile[cont->BinaryDir.size()] == '/' )
975 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " produced b: "
976 << sourceFile.c_str() << std::endl);
977 *cont->OFS << " produced in binary dir: " << sourceFile.c_str()
978 << std::endl;
979 actualSourceFile
980 = cmSystemTools::CollapseFullPath(sourceFile.c_str());
982 if ( actualSourceFile.empty() )
984 if ( missingFiles.find(actualSourceFile) == missingFiles.end() )
986 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
987 "Something went wrong" << std::endl);
988 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "File: ["
989 << sourceFile.c_str() << "]" << std::endl);
990 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "s: ["
991 << sourceFile.substr(0, cont->SourceDir.size()) << "]"
992 << std::endl);
993 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "b: ["
994 << sourceFile.substr(0, cont->BinaryDir.size()) << "]"
995 << std::endl);
996 *cont->OFS << " Something went wrong. Cannot find: "
997 << sourceFile.c_str()
998 << " in source dir: " << cont->SourceDir.c_str()
999 << " or binary dir: " << cont->BinaryDir.c_str() << std::endl;
1000 missingFiles.insert(actualSourceFile);
1005 file_count ++;
1006 if ( file_count % 50 == 0 )
1008 cmCTestLog(this->CTest, HANDLER_OUTPUT, " processed: " << file_count
1009 << " out of " << files.size() << std::endl);
1010 cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
1013 cmSystemTools::ChangeDirectory(currentDirectory.c_str());
1014 return file_count;
1017 //----------------------------------------------------------------------
1018 int cmCTestCoverageHandler::HandleTracePyCoverage(
1019 cmCTestCoverageHandlerContainer* cont)
1021 cmsys::Glob gl;
1022 gl.RecurseOn();
1023 std::string daGlob = cont->BinaryDir + "/*.cover";
1024 gl.FindFiles(daGlob);
1025 std::vector<std::string> files = gl.GetFiles();
1027 if ( files.size() == 0 )
1029 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1030 " Cannot find any Python Trace.py coverage files."
1031 << std::endl);
1032 // No coverage files is a valid thing, so the exit code is 0
1033 return 0;
1036 std::string testingDir = this->CTest->GetBinaryDir() + "/Testing";
1037 std::string tempDir = testingDir + "/CoverageInfo";
1038 std::string currentDirectory = cmSystemTools::GetCurrentWorkingDirectory();
1039 cmSystemTools::MakeDirectory(tempDir.c_str());
1040 cmSystemTools::ChangeDirectory(tempDir.c_str());
1042 cmSystemTools::ChangeDirectory(currentDirectory.c_str());
1044 std::vector<std::string>::iterator fileIt;
1045 int file_count = 0;
1046 for ( fileIt = files.begin(); fileIt != files.end(); ++ fileIt )
1048 std::string fileName = this->FindFile(cont, *fileIt);
1049 if ( fileName.empty() )
1051 cmCTestLog(this->CTest, ERROR_MESSAGE,
1052 "Cannot find source Python file corresponding to: "
1053 << fileIt->c_str() << std::endl);
1054 continue;
1057 std::string actualSourceFile
1058 = cmSystemTools::CollapseFullPath(fileName.c_str());
1059 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1060 " Check coverage for file: " << actualSourceFile.c_str()
1061 << std::endl);
1062 cmCTestCoverageHandlerContainer::SingleFileCoverageVector* vec
1063 = &cont->TotalCoverage[actualSourceFile];
1064 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1065 " in file: " << fileIt->c_str() << std::endl);
1066 std::ifstream ifile(fileIt->c_str());
1067 if ( ! ifile )
1069 cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open file: "
1070 << fileIt->c_str() << std::endl);
1072 else
1074 long cnt = -1;
1075 std::string nl;
1076 while ( cmSystemTools::GetLineFromStream(ifile, nl) )
1078 cnt ++;
1080 // Skip empty lines
1081 if ( !nl.size() )
1083 continue;
1086 // Skip unused lines
1087 if ( nl.size() < 12 )
1089 continue;
1092 // Read the coverage count from the beginning of the Trace.py output
1093 // line
1094 std::string prefix = nl.substr(0, 6);
1095 if ( prefix[5] != ' ' && prefix[5] != ':' )
1097 // This is a hack. We should really do something more elaborate
1098 prefix = nl.substr(0, 7);
1099 if ( prefix[6] != ' ' && prefix[6] != ':' )
1101 prefix = nl.substr(0, 8);
1102 if ( prefix[7] != ' ' && prefix[7] != ':' )
1104 cmCTestLog(this->CTest, ERROR_MESSAGE,
1105 "Currently the limit is maximum coverage of 999999"
1106 << std::endl);
1110 int cov = atoi(prefix.c_str());
1111 if ( prefix[prefix.size()-1] != ':' )
1113 // This line does not have ':' so no coverage here. That said,
1114 // Trace.py does not handle not covered lines versus comments etc.
1115 // So, this will be set to 0.
1116 cov = 0;
1118 cmCTestLog(this->CTest, DEBUG, "Prefix: " << prefix.c_str()
1119 << " cov: " << cov
1120 << std::endl);
1121 // Read the line number starting at the 10th character of the gcov
1122 // output line
1123 int lineIdx = cnt;
1124 if ( lineIdx >= 0 )
1126 while ( vec->size() <=
1127 static_cast<size_t>(lineIdx) )
1129 vec->push_back(-1);
1131 // Initially all entries are -1 (not used). If we get coverage
1132 // information, increment it to 0 first.
1133 if ( (*vec)[lineIdx] < 0 )
1135 if ( cov >= 0 )
1137 (*vec)[lineIdx] = 0;
1140 (*vec)[lineIdx] += cov;
1144 ++ file_count;
1146 cmSystemTools::ChangeDirectory(currentDirectory.c_str());
1147 return file_count;
1150 //----------------------------------------------------------------------
1151 std::string cmCTestCoverageHandler::FindFile(
1152 cmCTestCoverageHandlerContainer* cont,
1153 std::string fileName)
1155 std::string fileNameNoE
1156 = cmSystemTools::GetFilenameWithoutLastExtension(fileName);
1157 // First check in source and binary directory
1158 std::string fullName = cont->SourceDir + "/" + fileNameNoE + ".py";
1159 if ( cmSystemTools::FileExists(fullName.c_str()) )
1161 return fullName;
1163 fullName = cont->BinaryDir + "/" + fileNameNoE + ".py";
1164 if ( cmSystemTools::FileExists(fullName.c_str()) )
1166 return fullName;
1168 return "";
1171 // This is a header put on each marked up source file
1172 namespace
1174 const char* bullseyeHelp[] =
1175 {" Coverage produced by bullseye covbr tool: ",
1176 " www.bullseye.com/help/ref_covbr.html",
1177 " * An arrow --> indicates incomplete coverage.",
1178 " * An X indicates a function that was invoked, a switch label that ",
1179 " was exercised, a try-block that finished, or an exception handler ",
1180 " that was invoked.",
1181 " * A T or F indicates a boolean decision that evaluated true or false,",
1182 " respectively.",
1183 " * A t or f indicates a boolean condition within a decision if the ",
1184 " condition evaluated true or false, respectively.",
1185 " * A k indicates a constant decision or condition.",
1186 " * The slash / means this probe is excluded from summary results. ",
1190 //----------------------------------------------------------------------
1191 int cmCTestCoverageHandler::RunBullseyeCoverageBranch(
1192 cmCTestCoverageHandlerContainer* cont,
1193 std::set<cmStdString>& coveredFileNames,
1194 std::vector<std::string>& files,
1195 std::vector<std::string>& filesFullPath)
1197 if(files.size() != filesFullPath.size())
1199 cmCTestLog(this->CTest, ERROR_MESSAGE,
1200 "Files and full path files not the same size?:\n");
1201 return 0;
1203 // create the output stream for the CoverageLog-N.xml file
1204 cmGeneratedFileStream covLogFile;
1205 int logFileCount = 0;
1206 if ( !this->StartCoverageLogFile(covLogFile, logFileCount) )
1208 return -1;
1210 // for each file run covbr on that file to get the coverage
1211 // information for that file
1212 std::string outputFile;
1213 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1214 "run covbr: "
1215 << std::endl);
1217 if(!this->RunBullseyeCommand(cont, "covbr", 0, outputFile))
1219 cmCTestLog(this->CTest, ERROR_MESSAGE, "error running covbr for." << "\n");
1220 return -1;
1222 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1223 "covbr output in " << outputFile
1224 << std::endl);
1225 // open the output file
1226 std::ifstream fin(outputFile.c_str());
1227 if(!fin)
1229 cmCTestLog(this->CTest, ERROR_MESSAGE,
1230 "Cannot open coverage file: " <<
1231 outputFile.c_str() << std::endl);
1232 return 0;
1234 std::map<cmStdString, cmStdString> fileMap;
1235 std::vector<std::string>::iterator fp = filesFullPath.begin();
1236 for(std::vector<std::string>::iterator f = files.begin();
1237 f != files.end(); ++f, ++fp)
1239 fileMap[*f] = *fp;
1242 int count =0; // keep count of the number of files
1243 // Now parse each line from the bullseye cov log file
1244 std::string lineIn;
1245 bool valid = false; // are we in a valid output file
1246 int line = 0; // line of the current file
1247 cmStdString file;
1248 while(cmSystemTools::GetLineFromStream(fin, lineIn))
1250 bool startFile = false;
1251 if(lineIn.size() > 1 && lineIn[lineIn.size()-1] == ':')
1253 file = lineIn.substr(0, lineIn.size()-1);
1254 if(coveredFileNames.find(file) != coveredFileNames.end())
1256 startFile = true;
1259 if(startFile)
1261 // if we are in a valid file close it because a new one started
1262 if(valid)
1264 covLogFile << "\t\t</Report>" << std::endl
1265 << "\t</File>" << std::endl;
1267 // only allow 100 files in each log file
1268 if ( count != 0 && count % 100 == 0 )
1270 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1271 "start a new log file: "
1272 << count
1273 << std::endl);
1274 this->EndCoverageLogFile(covLogFile, logFileCount);
1275 logFileCount ++;
1276 if ( !this->StartCoverageLogFile(covLogFile, logFileCount) )
1278 return -1;
1280 count++; // move on one
1282 std::map<cmStdString, cmStdString>::iterator
1283 i = fileMap.find(file);
1284 // if the file should be covered write out the header for that file
1285 if(i != fileMap.end())
1287 // we have a new file so count it in the output
1288 count++;
1289 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1290 "Produce coverage for file: "
1291 << file.c_str() << " " << count
1292 << std::endl);
1293 // start the file output
1294 covLogFile << "\t<File Name=\""
1295 << this->CTest->MakeXMLSafe(i->first.c_str())
1296 << "\" FullPath=\"" << this->CTest->MakeXMLSafe(
1297 this->CTest->GetShortPathToFile(
1298 i->second.c_str())) << "\">" << std::endl
1299 << "\t\t<Report>" << std::endl;
1300 // write the bullseye header
1301 line =0;
1302 for(int k =0; bullseyeHelp[k] != 0; ++k)
1304 covLogFile << "\t\t<Line Number=\"" << line << "\" Count=\"-1\">"
1305 << this->CTest->MakeXMLSafe(bullseyeHelp[k])
1306 << "</Line>" << std::endl;
1307 line++;
1309 valid = true; // we are in a valid file section
1311 else
1313 // this is not a file that we want coverage for
1314 valid = false;
1317 // we are not at a start file, and we are in a valid file output the line
1318 else if(valid)
1320 covLogFile << "\t\t<Line Number=\"" << line << "\" Count=\"-1\">"
1321 << this->CTest->MakeXMLSafe(lineIn.c_str())
1322 << "</Line>" << std::endl;
1323 line++;
1326 // if we ran out of lines a valid file then close that file
1327 if(valid)
1329 covLogFile << "\t\t</Report>" << std::endl
1330 << "\t</File>" << std::endl;
1332 this->EndCoverageLogFile(covLogFile, logFileCount);
1333 return 1;
1336 //----------------------------------------------------------------------
1337 int cmCTestCoverageHandler::RunBullseyeCommand(
1338 cmCTestCoverageHandlerContainer* cont,
1339 const char* cmd,
1340 const char* arg,
1341 std::string& outputFile)
1343 std::string program = cmSystemTools::FindProgram(cmd);
1344 if(program.size() == 0)
1346 cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find :" << cmd << "\n");
1347 return 0;
1349 if(arg)
1351 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1352 "Run : " << program.c_str() << " " << arg << "\n");
1354 else
1356 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1357 "Run : " << program.c_str() << "\n");
1359 // create a process object and start it
1360 cmCTestRunProcess runCoverageSrc;
1361 runCoverageSrc.SetCommand(program.c_str());
1362 runCoverageSrc.AddArgument(arg);
1363 std::string stdoutFile = cont->BinaryDir + "/Testing/Temporary/";
1364 stdoutFile += this->GetCTestInstance()->GetCurrentTag();
1365 stdoutFile += "-";
1366 stdoutFile += cmd;
1367 std::string stderrFile = stdoutFile;
1368 stdoutFile += ".stdout";
1369 stderrFile += ".stderr";
1370 runCoverageSrc.SetStdoutFile(stdoutFile.c_str());
1371 runCoverageSrc.SetStderrFile(stderrFile.c_str());
1372 if(!runCoverageSrc.StartProcess())
1374 cmCTestLog(this->CTest, ERROR_MESSAGE, "Could not run : "
1375 << program.c_str() << " " << arg << "\n"
1376 << "kwsys process state : "
1377 << runCoverageSrc.GetProcessState());
1378 return 0;
1380 // since we set the output file names wait for it to end
1381 runCoverageSrc.WaitForExit();
1382 outputFile = stdoutFile;
1383 return 1;
1386 //----------------------------------------------------------------------
1387 int cmCTestCoverageHandler::RunBullseyeSourceSummary(
1388 cmCTestCoverageHandlerContainer* cont)
1390 // Run the covsrc command and create a temp outputfile
1391 std::string outputFile;
1392 if(!this->RunBullseyeCommand(cont, "covsrc", "-c", outputFile))
1394 cmCTestLog(this->CTest, ERROR_MESSAGE, "error running covsrc:\n");
1395 return 0;
1398 std::ostream& tmpLog = *cont->OFS;
1399 // copen the Coverage.xml file in the Testing directory
1400 cmGeneratedFileStream covSumFile;
1401 if (!this->StartResultingXML("Coverage", covSumFile))
1403 cmCTestLog(this->CTest, ERROR_MESSAGE,
1404 "Cannot open coverage summary file." << std::endl);
1405 return 0;
1407 this->CTest->StartXML(covSumFile);
1408 double elapsed_time_start = cmSystemTools::GetTime();
1409 std::string coverage_start_time = this->CTest->CurrentTime();
1410 covSumFile << "<Coverage>" << std::endl
1411 << "\t<StartDateTime>"
1412 << coverage_start_time << "</StartDateTime>"
1413 << std::endl;
1414 std::string stdline;
1415 std::string errline;
1416 // expected output:
1417 // first line is:
1418 // "Source","Function Coverage","out of","%","C/D Coverage","out of","%"
1419 // after that data follows in that format
1420 std::string sourceFile;
1421 int functionsCalled = 0;
1422 int totalFunctions = 0;
1423 int percentFunction = 0;
1424 int branchCovered = 0;
1425 int totalBranches = 0;
1426 int percentBranch = 0;
1427 double total_tested = 0;
1428 double total_untested = 0;
1429 double total_functions = 0;
1430 double percent_coverage =0;
1431 double number_files = 0;
1432 std::vector<std::string> coveredFiles;
1433 std::vector<std::string> coveredFilesFullPath;
1434 // Read and parse the summary output file
1435 std::ifstream fin(outputFile.c_str());
1436 if(!fin)
1438 cmCTestLog(this->CTest, ERROR_MESSAGE,
1439 "Cannot open coverage summary file: " <<
1440 outputFile.c_str() << std::endl);
1441 return 0;
1443 std::set<cmStdString> coveredFileNames;
1444 while(cmSystemTools::GetLineFromStream(fin, stdline))
1446 // if we have a line of output from stdout
1447 if(stdline.size())
1449 // parse the comma separated output
1450 this->ParseBullsEyeCovsrcLine(stdline,
1451 sourceFile,
1452 functionsCalled,
1453 totalFunctions,
1454 percentFunction,
1455 branchCovered,
1456 totalBranches,
1457 percentBranch);
1458 // The first line is the header
1459 if(sourceFile == "Source" || sourceFile == "Total")
1461 continue;
1463 std::string file = sourceFile;
1464 coveredFileNames.insert(file);
1465 if(!cmSystemTools::FileIsFullPath(sourceFile.c_str()))
1467 // file will be relative to the binary dir
1468 file = cont->BinaryDir;
1469 file += "/";
1470 file += sourceFile;
1472 file = cmSystemTools::CollapseFullPath(file.c_str());
1473 bool shouldIDoCoverage
1474 = this->ShouldIDoCoverage(file.c_str(),
1475 cont->SourceDir.c_str(),
1476 cont->BinaryDir.c_str());
1477 if ( !shouldIDoCoverage )
1479 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1480 ".NoDartCoverage found, so skip coverage check for: "
1481 << file.c_str()
1482 << std::endl);
1483 continue;
1486 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1487 "Doing coverage for: "
1488 << file.c_str()
1489 << std::endl);
1491 coveredFiles.push_back(sourceFile);
1492 coveredFilesFullPath.push_back(file);
1494 number_files++;
1495 total_functions += totalFunctions;
1496 total_tested += functionsCalled;
1497 total_untested += (totalFunctions - functionsCalled);
1499 std::string fileName = cmSystemTools::GetFilenameName(file.c_str());
1501 float cper = percentBranch + percentFunction;
1502 if(totalBranches > 0)
1504 cper /= 2.0f;
1506 percent_coverage += cper;
1507 float cmet = percentFunction + percentBranch;
1508 if(totalBranches > 0)
1510 cmet /= 2.0f;
1512 cmet /= 100.0f;
1513 tmpLog << stdline.c_str() << "\n";
1514 tmpLog << fileName << "\n";
1515 tmpLog << "functionsCalled: " << functionsCalled/100 << "\n";
1516 tmpLog << "totalFunctions: " << totalFunctions/100 << "\n";
1517 tmpLog << "percentFunction: " << percentFunction << "\n";
1518 tmpLog << "branchCovered: " << branchCovered << "\n";
1519 tmpLog << "totalBranches: " << totalBranches << "\n";
1520 tmpLog << "percentBranch: " << percentBranch << "\n";
1521 tmpLog << "percentCoverage: " << percent_coverage << "\n";
1522 tmpLog << "coverage metric: " << cmet << "\n";
1523 covSumFile << "\t<File Name=\"" << this->CTest->MakeXMLSafe(sourceFile)
1524 << "\" FullPath=\"" << this->CTest->MakeXMLSafe(
1525 this->CTest->GetShortPathToFile(file.c_str()))
1526 << "\" Covered=\"" << (cmet>0?"true":"false") << "\">\n"
1527 << "\t\t<BranchesTested>"
1528 << branchCovered
1529 << "</BranchesTested>\n"
1530 << "\t\t<BranchesUnTested>"
1531 << totalBranches - branchCovered
1532 << "</BranchesUnTested>\n"
1533 << "\t\t<FunctionsTested>"
1534 << functionsCalled
1535 << "</FunctionsTested>\n"
1536 << "\t\t<FunctionsUnTested>"
1537 << totalFunctions - functionsCalled
1538 << "</FunctionsUnTested>\n"
1539 // Hack for conversion of function to loc assume a function
1540 // has 100 lines of code
1541 << "\t\t<LOCTested>" << functionsCalled *100
1542 << "</LOCTested>\n"
1543 << "\t\t<LOCUnTested>"
1544 << (totalFunctions - functionsCalled)*100
1545 << "</LOCUnTested>\n"
1546 << "\t\t<PercentCoverage>";
1547 covSumFile.setf(std::ios::fixed, std::ios::floatfield);
1548 covSumFile.precision(2);
1549 covSumFile << (cper) << "</PercentCoverage>\n"
1550 << "\t\t<CoverageMetric>";
1551 covSumFile.setf(std::ios::fixed, std::ios::floatfield);
1552 covSumFile.precision(2);
1553 covSumFile << (cmet) << "</CoverageMetric>\n"
1554 << "\t</File>" << std::endl;
1557 std::string end_time = this->CTest->CurrentTime();
1558 covSumFile << "\t<LOCTested>" << total_tested << "</LOCTested>\n"
1559 << "\t<LOCUntested>" << total_untested << "</LOCUntested>\n"
1560 << "\t<LOC>" << total_functions << "</LOC>\n"
1561 << "\t<PercentCoverage>";
1562 covSumFile.setf(std::ios::fixed, std::ios::floatfield);
1563 covSumFile.precision(2);
1564 covSumFile << SAFEDIV(percent_coverage,number_files)<< "</PercentCoverage>\n"
1565 << "\t<EndDateTime>" << end_time << "</EndDateTime>\n";
1566 covSumFile << "<ElapsedMinutes>" <<
1567 static_cast<int>((cmSystemTools::GetTime() - elapsed_time_start)/6)/10.0
1568 << "</ElapsedMinutes>"
1569 << "</Coverage>" << std::endl;
1570 this->CTest->EndXML(covSumFile);
1571 // Now create the coverage information for each file
1572 return this->RunBullseyeCoverageBranch(cont,
1573 coveredFileNames,
1574 coveredFiles,
1575 coveredFilesFullPath);
1578 //----------------------------------------------------------------------
1579 int cmCTestCoverageHandler::HandleBullseyeCoverage(
1580 cmCTestCoverageHandlerContainer* cont)
1582 const char* covfile = cmSystemTools::GetEnv("COVFILE");
1583 if(!covfile)
1585 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1586 " COVFILE environment variable not found, not running "
1587 " bullseye\n");
1588 return 0;
1590 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1591 " run covsrc with COVFILE=["
1592 << covfile
1593 << "]" << std::endl);
1594 if(!this->RunBullseyeSourceSummary(cont))
1596 cmCTestLog(this->CTest, ERROR_MESSAGE,
1597 "Error running bullseye summary.\n");
1598 return 0;
1600 return 1;
1603 bool cmCTestCoverageHandler::GetNextInt(std::string const& inputLine,
1604 std::string::size_type& pos,
1605 int& value)
1607 std::string::size_type start = pos;
1608 pos = inputLine.find(',', start);
1609 value = atoi(inputLine.substr(start, pos).c_str());
1610 if(pos == inputLine.npos)
1612 return true;
1614 pos++;
1615 return true;
1618 bool cmCTestCoverageHandler::ParseBullsEyeCovsrcLine(
1619 std::string const& inputLine,
1620 std::string& sourceFile,
1621 int& functionsCalled,
1622 int& totalFunctions,
1623 int& percentFunction,
1624 int& branchCovered,
1625 int& totalBranches,
1626 int& percentBranch)
1628 // find the first comma
1629 std::string::size_type pos = inputLine.find(',');
1630 if(pos == inputLine.npos)
1632 cmCTestLog(this->CTest, ERROR_MESSAGE, "Error parsing string : "
1633 << inputLine.c_str() << "\n");
1634 return false;
1636 // the source file has "" around it so extract out the file name
1637 sourceFile = inputLine.substr(1,pos-2);
1638 pos++;
1639 if(!this->GetNextInt(inputLine, pos, functionsCalled))
1641 return false;
1643 if(!this->GetNextInt(inputLine, pos, totalFunctions))
1645 return false;
1647 if(!this->GetNextInt(inputLine, pos, percentFunction))
1649 return false;
1651 if(!this->GetNextInt(inputLine, pos, branchCovered))
1653 return false;
1655 if(!this->GetNextInt(inputLine, pos, totalBranches))
1657 return false;
1659 if(!this->GetNextInt(inputLine, pos, percentBranch))
1661 return false;
1663 // should be at the end now
1664 if(pos != inputLine.npos)
1666 cmCTestLog(this->CTest, ERROR_MESSAGE, "Error parsing input : "
1667 << inputLine.c_str() << " last pos not npos = " << pos <<
1668 "\n");
1670 return true;