ENH: add more debug stuff to CTestCTest2 so I can figure out redwall
[cmake.git] / Source / CTest / cmCTestCoverageHandler.cxx
blob024b7462e59d3a87ce26c5b8d085b32c95217641
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCTestCoverageHandler.cxx,v $
5 Language: C++
6 Date: $Date: 2008-11-23 15:49:46 $
7 Version: $Revision: 1.57 $
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 << "\t<StartTime>"
176 << static_cast<unsigned int>(cmSystemTools::GetTime())
177 << "</StartTime>"
178 << std::endl;
179 return true;
182 //----------------------------------------------------------------------
183 void cmCTestCoverageHandler::EndCoverageLogFile(cmGeneratedFileStream& ostr,
184 int logFileCount)
186 std::string local_end_time = this->CTest->CurrentTime();
187 ostr << "\t<EndDateTime>" << local_end_time << "</EndDateTime>" << std::endl
188 << "\t<EndTime>" <<
189 static_cast<unsigned int>(cmSystemTools::GetTime())
190 << "</EndTime>" << std::endl
191 << "</CoverageLog>" << std::endl;
192 this->CTest->EndXML(ostr);
193 char covLogFilename[1024];
194 sprintf(covLogFilename, "CoverageLog-%d.xml", logFileCount);
195 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Close file: "
196 << covLogFilename << std::endl);
197 ostr.Close();
200 //----------------------------------------------------------------------
201 bool cmCTestCoverageHandler::ShouldIDoCoverage(const char* file,
202 const char* srcDir,
203 const char* binDir)
205 std::vector<cmsys::RegularExpression>::iterator sit;
206 for ( sit = this->CustomCoverageExcludeRegex.begin();
207 sit != this->CustomCoverageExcludeRegex.end(); ++ sit )
209 if ( sit->find(file) )
211 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " File " << file
212 << " is excluded in CTestCustom.ctest" << std::endl;);
213 return false;
217 std::string fSrcDir = cmSystemTools::CollapseFullPath(srcDir);
218 std::string fBinDir = cmSystemTools::CollapseFullPath(binDir);
219 std::string fFile = cmSystemTools::CollapseFullPath(file);
220 bool sourceSubDir = cmSystemTools::IsSubDirectory(fFile.c_str(),
221 fSrcDir.c_str());
222 bool buildSubDir = cmSystemTools::IsSubDirectory(fFile.c_str(),
223 fBinDir.c_str());
224 // Always check parent directory of the file.
225 std::string fileDir = cmSystemTools::GetFilenamePath(fFile.c_str());
226 std::string checkDir;
228 // We also need to check the binary/source directory pair.
229 if ( sourceSubDir && buildSubDir )
231 if ( fSrcDir.size() > fBinDir.size() )
233 checkDir = fSrcDir;
235 else
237 checkDir = fBinDir;
240 else if ( sourceSubDir )
242 checkDir = fSrcDir;
244 else if ( buildSubDir )
246 checkDir = fBinDir;
248 std::string ndc
249 = cmSystemTools::FileExistsInParentDirectories(".NoDartCoverage",
250 fFile.c_str(), checkDir.c_str());
251 if ( ndc.size() )
253 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Found: " << ndc.c_str()
254 << " so skip coverage of " << file << std::endl);
255 return false;
258 // By now checkDir should be set to parent directory of the file.
259 // Get the relative path to the file an apply it to the opposite directory.
260 // If it is the same as fileDir, then ignore, otherwise check.
261 std::string relPath;
262 if(checkDir.size() )
264 relPath = cmSystemTools::RelativePath(checkDir.c_str(),
265 fFile.c_str());
267 else
269 relPath = fFile;
271 if ( checkDir == fSrcDir )
273 checkDir = fBinDir;
275 else
277 checkDir = fSrcDir;
279 fFile = checkDir + "/" + relPath;
280 fFile = cmSystemTools::GetFilenamePath(fFile.c_str());
282 if ( fileDir == fFile )
284 // This is in-source build, so we trust the previous check.
285 return true;
288 ndc = cmSystemTools::FileExistsInParentDirectories(".NoDartCoverage",
289 fFile.c_str(), checkDir.c_str());
290 if ( ndc.size() )
292 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Found: " << ndc.c_str()
293 << " so skip coverage of: " << file << std::endl);
294 return false;
296 // Ok, nothing in source tree, nothing in binary tree
297 return true;
300 //----------------------------------------------------------------------
301 //clearly it would be nice if this were broken up into a few smaller
302 //functions and commented...
303 int cmCTestCoverageHandler::ProcessHandler()
305 int error = 0;
306 // do we have time for this
307 if (this->CTest->GetRemainingTimeAllowed() < 120)
309 return error;
312 std::string coverage_start_time = this->CTest->CurrentTime();
313 unsigned int coverage_start_time_time = static_cast<unsigned int>(
314 cmSystemTools::GetTime());
315 std::string sourceDir
316 = this->CTest->GetCTestConfiguration("SourceDirectory");
317 std::string binaryDir
318 = this->CTest->GetCTestConfiguration("BuildDirectory");
320 cmGeneratedFileStream ofs;
321 double elapsed_time_start = cmSystemTools::GetTime();
322 if ( !this->StartLogFile("Coverage", ofs) )
324 cmCTestLog(this->CTest, ERROR_MESSAGE,
325 "Cannot create LastCoverage.log file" << std::endl);
328 ofs << "Performing coverage: " << elapsed_time_start << std::endl;
330 cmSystemTools::ConvertToUnixSlashes(sourceDir);
331 cmSystemTools::ConvertToUnixSlashes(binaryDir);
333 std::string asfGlob = sourceDir + "/*";
334 std::string abfGlob = binaryDir + "/*";
336 cmCTestLog(this->CTest, HANDLER_OUTPUT, "Performing coverage" << std::endl);
338 cmCTestCoverageHandlerContainer cont;
339 cont.Error = error;
340 cont.SourceDir = sourceDir;
341 cont.BinaryDir = binaryDir;
342 cont.OFS = &ofs;
344 // setup the regex exclude stuff
345 this->CustomCoverageExcludeRegex.empty();
346 std::vector<cmStdString>::iterator rexIt;
347 for ( rexIt = this->CustomCoverageExclude.begin();
348 rexIt != this->CustomCoverageExclude.end();
349 ++ rexIt )
351 this->CustomCoverageExcludeRegex.push_back(
352 cmsys::RegularExpression(rexIt->c_str()));
356 if(this->HandleBullseyeCoverage(&cont))
358 return cont.Error;
360 int file_count = 0;
361 file_count += this->HandleGCovCoverage(&cont);
362 if ( file_count < 0 )
364 return error;
366 file_count += this->HandleTracePyCoverage(&cont);
367 if ( file_count < 0 )
369 return error;
371 error = cont.Error;
374 if ( file_count == 0 )
376 cmCTestLog(this->CTest, WARNING,
377 " Cannot find any coverage files. Ignoring Coverage request."
378 << std::endl);
379 return error;
381 cmGeneratedFileStream covSumFile;
382 cmGeneratedFileStream covLogFile;
384 if (!this->StartResultingXML("Coverage", covSumFile))
386 cmCTestLog(this->CTest, ERROR_MESSAGE,
387 "Cannot open coverage summary file." << std::endl);
388 return -1;
391 this->CTest->StartXML(covSumFile);
392 // Produce output xml files
394 covSumFile << "<Coverage>" << std::endl
395 << "\t<StartDateTime>" << coverage_start_time << "</StartDateTime>"
396 << std::endl
397 << "\t<StartTime>" << coverage_start_time_time << "</StartTime>"
398 << std::endl;
399 int logFileCount = 0;
400 if ( !this->StartCoverageLogFile(covLogFile, logFileCount) )
402 return -1;
404 cmCTestCoverageHandlerContainer::TotalCoverageMap::iterator fileIterator;
405 int cnt = 0;
406 long total_tested = 0;
407 long total_untested = 0;
408 //std::string fullSourceDir = sourceDir + "/";
409 //std::string fullBinaryDir = binaryDir + "/";
410 cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl);
411 cmCTestLog(this->CTest, HANDLER_OUTPUT,
412 " Acumulating results (each . represents one file):" << std::endl);
413 cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
415 std::vector<std::string> errorsWhileAccumulating;
417 file_count = 0;
418 for ( fileIterator = cont.TotalCoverage.begin();
419 fileIterator != cont.TotalCoverage.end();
420 ++fileIterator )
422 cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
423 file_count ++;
424 if ( file_count % 50 == 0 )
426 cmCTestLog(this->CTest, HANDLER_OUTPUT, " processed: " << file_count
427 << " out of "
428 << cont.TotalCoverage.size() << std::endl);
429 cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
431 if ( cnt % 100 == 0 )
433 this->EndCoverageLogFile(covLogFile, logFileCount);
434 logFileCount ++;
435 if ( !this->StartCoverageLogFile(covLogFile, logFileCount) )
437 return -1;
440 const std::string fullFileName = fileIterator->first;
441 const std::string fileName
442 = cmSystemTools::GetFilenameName(fullFileName.c_str());
443 std::string fullFilePath
444 = cmSystemTools::GetFilenamePath(fullFileName.c_str());
445 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
446 "Process file: " << fullFileName << std::endl);
448 cmSystemTools::ConvertToUnixSlashes(fullFilePath);
450 if ( !cmSystemTools::FileExists(fullFileName.c_str()) )
452 cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find file: "
453 << fullFileName.c_str() << std::endl);
454 continue;
457 bool shouldIDoCoverage
458 = this->ShouldIDoCoverage(fullFileName.c_str(),
459 sourceDir.c_str(), binaryDir.c_str());
460 if ( !shouldIDoCoverage )
462 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
463 ".NoDartCoverage found, so skip coverage check for: "
464 << fullFileName.c_str()
465 << std::endl);
466 continue;
469 const cmCTestCoverageHandlerContainer::SingleFileCoverageVector& fcov
470 = fileIterator->second;
471 covLogFile << "\t<File Name=\""
472 << this->CTest->MakeXMLSafe(fileName.c_str())
473 << "\" FullPath=\"" << this->CTest->MakeXMLSafe(
474 this->CTest->GetShortPathToFile(
475 fileIterator->first.c_str())) << "\">" << std::endl
476 << "\t\t<Report>" << std::endl;
478 std::ifstream ifs(fullFileName.c_str());
479 if ( !ifs)
481 cmOStringStream ostr;
482 ostr << "Cannot open source file: " << fullFileName.c_str();
483 errorsWhileAccumulating.push_back(ostr.str());
484 error ++;
485 continue;
488 int tested = 0;
489 int untested = 0;
491 cmCTestCoverageHandlerContainer::SingleFileCoverageVector::size_type cc;
492 std::string line;
493 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
494 "Actually perfoming coverage for: " << fullFileName << std::endl);
495 for ( cc= 0; cc < fcov.size(); cc ++ )
497 if ( !cmSystemTools::GetLineFromStream(ifs, line) &&
498 cc != fcov.size() -1 )
500 cmOStringStream ostr;
501 ostr << "Problem reading source file: " << fullFileName.c_str()
502 << " line:" << cc;
503 errorsWhileAccumulating.push_back(ostr.str());
504 error ++;
505 break;
507 covLogFile << "\t\t<Line Number=\"" << cc << "\" Count=\"" << fcov[cc]
508 << "\">"
509 << this->CTest->MakeXMLSafe(line.c_str()) << "</Line>" << std::endl;
510 if ( fcov[cc] == 0 )
512 untested ++;
514 else if ( fcov[cc] > 0 )
516 tested ++;
519 if ( cmSystemTools::GetLineFromStream(ifs, line) )
521 cmOStringStream ostr;
522 ostr << "Looks like there are more lines in the file: " << line;
523 errorsWhileAccumulating.push_back(ostr.str());
525 float cper = 0;
526 float cmet = 0;
527 if ( tested + untested > 0 )
529 cper = (100 * SAFEDIV(static_cast<float>(tested),
530 static_cast<float>(tested + untested)));
531 cmet = ( SAFEDIV(static_cast<float>(tested + 10),
532 static_cast<float>(tested + untested + 10)));
534 total_tested += tested;
535 total_untested += untested;
536 covLogFile << "\t\t</Report>" << std::endl
537 << "\t</File>" << std::endl;
538 covSumFile << "\t<File Name=\"" << this->CTest->MakeXMLSafe(fileName)
539 << "\" FullPath=\"" << this->CTest->MakeXMLSafe(
540 this->CTest->GetShortPathToFile(fullFileName.c_str()))
541 << "\" Covered=\"" << (tested > 0 ? "true":"false") << "\">\n"
542 << "\t\t<LOCTested>" << tested << "</LOCTested>\n"
543 << "\t\t<LOCUnTested>" << untested << "</LOCUnTested>\n"
544 << "\t\t<PercentCoverage>";
545 covSumFile.setf(std::ios::fixed, std::ios::floatfield);
546 covSumFile.precision(2);
547 covSumFile << (cper) << "</PercentCoverage>\n"
548 << "\t\t<CoverageMetric>";
549 covSumFile.setf(std::ios::fixed, std::ios::floatfield);
550 covSumFile.precision(2);
551 covSumFile << (cmet) << "</CoverageMetric>\n"
552 << "\t</File>" << std::endl;
553 cnt ++;
555 this->EndCoverageLogFile(covLogFile, logFileCount);
557 if ( errorsWhileAccumulating.size() > 0 )
559 cmCTestLog(this->CTest, ERROR_MESSAGE, std::endl);
560 cmCTestLog(this->CTest, ERROR_MESSAGE,
561 "Error(s) while acumulating results:" << std::endl);
562 std::vector<std::string>::iterator erIt;
563 for ( erIt = errorsWhileAccumulating.begin();
564 erIt != errorsWhileAccumulating.end();
565 ++ erIt )
567 cmCTestLog(this->CTest, ERROR_MESSAGE,
568 " " << erIt->c_str() << std::endl);
572 int total_lines = total_tested + total_untested;
573 float percent_coverage = 100 * SAFEDIV(static_cast<float>(total_tested),
574 static_cast<float>(total_lines));
575 if ( total_lines == 0 )
577 percent_coverage = 0;
580 std::string end_time = this->CTest->CurrentTime();
582 covSumFile << "\t<LOCTested>" << total_tested << "</LOCTested>\n"
583 << "\t<LOCUntested>" << total_untested << "</LOCUntested>\n"
584 << "\t<LOC>" << total_lines << "</LOC>\n"
585 << "\t<PercentCoverage>";
586 covSumFile.setf(std::ios::fixed, std::ios::floatfield);
587 covSumFile.precision(2);
588 covSumFile << (percent_coverage)<< "</PercentCoverage>\n"
589 << "\t<EndDateTime>" << end_time << "</EndDateTime>\n"
590 << "\t<EndTime>" <<
591 static_cast<unsigned int>(cmSystemTools::GetTime())
592 << "</EndTime>\n";
593 covSumFile << "<ElapsedMinutes>" <<
594 static_cast<int>((cmSystemTools::GetTime() - elapsed_time_start)/6)/10.0
595 << "</ElapsedMinutes>"
596 << "</Coverage>" << std::endl;
597 this->CTest->EndXML(covSumFile);
599 cmCTestLog(this->CTest, HANDLER_OUTPUT, "" << std::endl
600 << "\tCovered LOC: "
601 << total_tested << std::endl
602 << "\tNot covered LOC: " << total_untested << std::endl
603 << "\tTotal LOC: " << total_lines << std::endl
604 << "\tPercentage Coverage: "
605 << std::setiosflags(std::ios::fixed)
606 << std::setprecision(2)
607 << (percent_coverage) << "%" << std::endl);
609 ofs << "\tCovered LOC: " << total_tested << std::endl
610 << "\tNot covered LOC: " << total_untested << std::endl
611 << "\tTotal LOC: " << total_lines << std::endl
612 << "\tPercentage Coverage: "
613 << std::setiosflags(std::ios::fixed)
614 << std::setprecision(2)
615 << (percent_coverage) << "%" << std::endl;
618 if ( error )
620 return -1;
622 return 0;
625 //----------------------------------------------------------------------
626 void cmCTestCoverageHandler::PopulateCustomVectors(cmMakefile *mf)
628 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
629 " Add coverage exclude regular expressions." << std::endl);
630 this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_COVERAGE_EXCLUDE",
631 this->CustomCoverageExclude);
632 std::vector<cmStdString>::iterator it;
633 for ( it = this->CustomCoverageExclude.begin();
634 it != this->CustomCoverageExclude.end();
635 ++ it )
637 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Add coverage exclude: "
638 << it->c_str() << std::endl);
642 //----------------------------------------------------------------------
643 // Fix for issue #4971 where the case of the drive letter component of
644 // the filenames might be different when analyzing gcov output.
646 // Compare file names: fnc(fn1) == fnc(fn2) // fnc == file name compare
648 #ifdef _WIN32
649 #define fnc(s) cmSystemTools::LowerCase(s)
650 #else
651 #define fnc(s) s
652 #endif
654 //----------------------------------------------------------------------
655 int cmCTestCoverageHandler::HandleGCovCoverage(
656 cmCTestCoverageHandlerContainer* cont)
658 std::string gcovCommand
659 = this->CTest->GetCTestConfiguration("CoverageCommand");
661 // Style 1
662 std::string st1gcovOutputRex1
663 = "[0-9]+\\.[0-9]+% of [0-9]+ (source |)lines executed in file (.*)$";
664 std::string st1gcovOutputRex2 = "^Creating (.*\\.gcov)\\.";
665 cmsys::RegularExpression st1re1(st1gcovOutputRex1.c_str());
666 cmsys::RegularExpression st1re2(st1gcovOutputRex2.c_str());
669 // Style 2
670 std::string st2gcovOutputRex1 = "^File *[`'](.*)'$";
671 std::string st2gcovOutputRex2
672 = "Lines executed: *[0-9]+\\.[0-9]+% of [0-9]+$";
673 std::string st2gcovOutputRex3 = "^(.*):creating [`'](.*\\.gcov)'";
674 std::string st2gcovOutputRex4 = "^(.*):unexpected EOF *$";
675 std::string st2gcovOutputRex5 = "^(.*):cannot open source file*$";
676 std::string st2gcovOutputRex6
677 = "^(.*):source file is newer than graph file `(.*)'$";
678 cmsys::RegularExpression st2re1(st2gcovOutputRex1.c_str());
679 cmsys::RegularExpression st2re2(st2gcovOutputRex2.c_str());
680 cmsys::RegularExpression st2re3(st2gcovOutputRex3.c_str());
681 cmsys::RegularExpression st2re4(st2gcovOutputRex4.c_str());
682 cmsys::RegularExpression st2re5(st2gcovOutputRex5.c_str());
683 cmsys::RegularExpression st2re6(st2gcovOutputRex6.c_str());
686 cmsys::Glob gl;
687 gl.RecurseOn();
688 gl.RecurseThroughSymlinksOff();
689 std::string daGlob = cont->BinaryDir + "/*.da";
690 gl.FindFiles(daGlob);
691 std::vector<std::string> files = gl.GetFiles();
692 daGlob = cont->BinaryDir + "/*.gcda";
693 gl.FindFiles(daGlob);
694 std::vector<std::string>& moreFiles = gl.GetFiles();
695 files.insert(files.end(), moreFiles.begin(), moreFiles.end());
696 std::vector<std::string>::iterator it;
698 if ( files.size() == 0 )
700 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
701 " Cannot find any GCov coverage files."
702 << std::endl);
703 // No coverage files is a valid thing, so the exit code is 0
704 return 0;
707 std::string testingDir = this->CTest->GetBinaryDir() + "/Testing";
708 std::string tempDir = testingDir + "/CoverageInfo";
709 std::string currentDirectory = cmSystemTools::GetCurrentWorkingDirectory();
710 cmSystemTools::MakeDirectory(tempDir.c_str());
711 cmSystemTools::ChangeDirectory(tempDir.c_str());
713 int gcovStyle = 0;
715 std::set<std::string> missingFiles;
717 std::string actualSourceFile = "";
718 cmCTestLog(this->CTest, HANDLER_OUTPUT,
719 " Processing coverage (each . represents one file):" << std::endl);
720 cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
721 int file_count = 0;
722 // make sure output from gcov is in English!
723 cmSystemTools::PutEnv("LC_ALL=POSIX");
724 for ( it = files.begin(); it != files.end(); ++ it )
726 cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
727 std::string fileDir = cmSystemTools::GetFilenamePath(it->c_str());
728 std::string command = "\"" + gcovCommand + "\" -l -o \"" + fileDir
729 + "\" \"" + *it + "\"";
730 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, command.c_str()
731 << std::endl);
732 std::string output = "";
733 std::string errors = "";
734 int retVal = 0;
735 *cont->OFS << "* Run coverage for: " << fileDir.c_str() << std::endl;
736 *cont->OFS << " Command: " << command.c_str() << std::endl;
737 int res = this->CTest->RunCommand(command.c_str(), &output, &errors,
738 &retVal, tempDir.c_str(), 0 /*this->TimeOut*/);
740 *cont->OFS << " Output: " << output.c_str() << std::endl;
741 *cont->OFS << " Errors: " << errors.c_str() << std::endl;
742 if ( ! res )
744 cmCTestLog(this->CTest, ERROR_MESSAGE,
745 "Problem running coverage on file: " << it->c_str() << std::endl);
746 cmCTestLog(this->CTest, ERROR_MESSAGE,
747 "Command produced error: " << errors << std::endl);
748 cont->Error ++;
749 continue;
751 if ( retVal != 0 )
753 cmCTestLog(this->CTest, ERROR_MESSAGE, "Coverage command returned: "
754 << retVal << " while processing: " << it->c_str() << std::endl);
755 cmCTestLog(this->CTest, ERROR_MESSAGE,
756 "Command produced error: " << cont->Error << std::endl);
758 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
759 "--------------------------------------------------------------"
760 << std::endl
761 << output << std::endl
762 << "--------------------------------------------------------------"
763 << std::endl);
764 std::vector<cmStdString> lines;
765 std::vector<cmStdString>::iterator line;
768 // Globals for storing current source file and current gcov file;
769 cmSystemTools::Split(output.c_str(), lines);
770 for ( line = lines.begin(); line != lines.end(); ++line)
772 std::string sourceFile;
773 std::string gcovFile;
774 cmCTestLog(this->CTest, DEBUG, "Line: [" << line->c_str() << "]"
775 << std::endl);
776 if ( line->size() == 0 )
778 // Ignore empty line; probably style 2
780 else if ( st1re1.find(line->c_str()) )
782 if ( gcovStyle != 0 )
784 if ( gcovStyle != 1 )
786 cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
787 << std::endl);
788 cont->Error ++;
789 break;
791 gcovStyle = 1;
794 actualSourceFile = "";
795 sourceFile = st1re1.match(2);
797 else if ( st1re2.find(line->c_str() ) )
799 if ( gcovStyle != 0 )
801 if ( gcovStyle != 1 )
803 cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
804 << std::endl);
805 cont->Error ++;
806 break;
808 gcovStyle = 1;
811 gcovFile = st1re2.match(1);
813 else if ( st2re1.find(line->c_str() ) )
815 if ( gcovStyle != 0 )
817 if ( gcovStyle != 2 )
819 cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
820 << std::endl);
821 cont->Error ++;
822 break;
824 gcovStyle = 2;
827 actualSourceFile = "";
828 sourceFile = st2re1.match(1);
830 else if ( st2re2.find(line->c_str() ) )
832 if ( gcovStyle != 0 )
834 if ( gcovStyle != 2 )
836 cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
837 << std::endl);
838 cont->Error ++;
839 break;
841 gcovStyle = 2;
844 else if ( st2re3.find(line->c_str() ) )
846 if ( gcovStyle != 0 )
848 if ( gcovStyle != 2 )
850 cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
851 << std::endl);
852 cont->Error ++;
853 break;
855 gcovStyle = 2;
858 gcovFile = st2re3.match(2);
860 else if ( st2re4.find(line->c_str() ) )
862 if ( gcovStyle != 0 )
864 if ( gcovStyle != 2 )
866 cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
867 << std::endl);
868 cont->Error ++;
869 break;
871 gcovStyle = 2;
874 cmCTestLog(this->CTest, WARNING, "Warning: " << st2re4.match(1)
875 << " had unexpected EOF" << std::endl);
877 else if ( st2re5.find(line->c_str() ) )
879 if ( gcovStyle != 0 )
881 if ( gcovStyle != 2 )
883 cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
884 << std::endl);
885 cont->Error ++;
886 break;
888 gcovStyle = 2;
891 cmCTestLog(this->CTest, WARNING, "Warning: Cannot open file: "
892 << st2re5.match(1) << std::endl);
894 else if ( st2re6.find(line->c_str() ) )
896 if ( gcovStyle != 0 )
898 if ( gcovStyle != 2 )
900 cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
901 << std::endl);
902 cont->Error ++;
903 break;
905 gcovStyle = 2;
908 cmCTestLog(this->CTest, WARNING, "Warning: File: " << st2re6.match(1)
909 << " is newer than " << st2re6.match(2) << std::endl);
911 else
913 cmCTestLog(this->CTest, ERROR_MESSAGE,
914 "Unknown line: [" << line->c_str() << "]" << std::endl);
915 cont->Error ++;
916 //abort();
918 if ( !gcovFile.empty() && actualSourceFile.size() )
920 cmCTestCoverageHandlerContainer::SingleFileCoverageVector* vec
921 = &cont->TotalCoverage[actualSourceFile];
922 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " in file: "
923 << gcovFile << std::endl);
924 std::ifstream ifile(gcovFile.c_str());
925 if ( ! ifile )
927 cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open file: "
928 << gcovFile << std::endl);
930 else
932 long cnt = -1;
933 std::string nl;
934 while ( cmSystemTools::GetLineFromStream(ifile, nl) )
936 cnt ++;
938 //TODO: Handle gcov 3.0 non-coverage lines
940 // Skip empty lines
941 if ( !nl.size() )
943 continue;
946 // Skip unused lines
947 if ( nl.size() < 12 )
949 continue;
952 // Read the coverage count from the beginning of the gcov output
953 // line
954 std::string prefix = nl.substr(0, 12);
955 int cov = atoi(prefix.c_str());
956 // Read the line number starting at the 10th character of the gcov
957 // output line
958 std::string lineNumber = nl.substr(10, 5);
959 int lineIdx = atoi(lineNumber.c_str())-1;
960 if ( lineIdx >= 0 )
962 while ( vec->size() <=
963 static_cast<size_t>(lineIdx) )
965 vec->push_back(-1);
967 // Initially all entries are -1 (not used). If we get coverage
968 // information, increment it to 0 first.
969 if ( (*vec)[lineIdx] < 0 )
971 if ( cov > 0 || prefix.find("#") != prefix.npos )
973 (*vec)[lineIdx] = 0;
976 (*vec)[lineIdx] += cov;
980 actualSourceFile = "";
982 if ( !sourceFile.empty() && actualSourceFile.empty() )
984 gcovFile = "";
986 // Is it in the source dir?
987 if ( sourceFile.size() > cont->SourceDir.size() &&
988 (fnc(sourceFile.substr(0, cont->SourceDir.size())) ==
989 fnc(cont->SourceDir)) &&
990 sourceFile[cont->SourceDir.size()] == '/' )
992 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " produced s: "
993 << sourceFile.c_str() << std::endl);
994 *cont->OFS << " produced in source dir: " << sourceFile.c_str()
995 << std::endl;
996 actualSourceFile
997 = cmSystemTools::CollapseFullPath(sourceFile.c_str());
1000 // Binary dir?
1001 if ( sourceFile.size() > cont->BinaryDir.size() &&
1002 (fnc(sourceFile.substr(0, cont->BinaryDir.size())) ==
1003 fnc(cont->BinaryDir)) &&
1004 sourceFile[cont->BinaryDir.size()] == '/' )
1006 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " produced b: "
1007 << sourceFile.c_str() << std::endl);
1008 *cont->OFS << " produced in binary dir: " << sourceFile.c_str()
1009 << std::endl;
1010 actualSourceFile
1011 = cmSystemTools::CollapseFullPath(sourceFile.c_str());
1014 if ( actualSourceFile.empty() )
1016 if ( missingFiles.find(actualSourceFile) == missingFiles.end() )
1018 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1019 "Something went wrong" << std::endl);
1020 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1021 "Cannot find file: ["
1022 << sourceFile.c_str() << "]" << std::endl);
1023 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1024 " in source dir: ["
1025 << cont->SourceDir.c_str() << "]"
1026 << std::endl);
1027 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1028 " or binary dir: ["
1029 << cont->BinaryDir.size() << "]"
1030 << std::endl);
1031 *cont->OFS << " Something went wrong. Cannot find file: "
1032 << sourceFile.c_str()
1033 << " in source dir: " << cont->SourceDir.c_str()
1034 << " or binary dir: " << cont->BinaryDir.c_str() << std::endl;
1035 missingFiles.insert(actualSourceFile);
1040 file_count ++;
1041 if ( file_count % 50 == 0 )
1043 cmCTestLog(this->CTest, HANDLER_OUTPUT, " processed: " << file_count
1044 << " out of " << files.size() << std::endl);
1045 cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
1048 cmSystemTools::ChangeDirectory(currentDirectory.c_str());
1049 return file_count;
1052 //----------------------------------------------------------------------
1053 int cmCTestCoverageHandler::HandleTracePyCoverage(
1054 cmCTestCoverageHandlerContainer* cont)
1056 cmsys::Glob gl;
1057 gl.RecurseOn();
1058 gl.RecurseThroughSymlinksOff();
1059 std::string daGlob = cont->BinaryDir + "/*.cover";
1060 gl.FindFiles(daGlob);
1061 std::vector<std::string> files = gl.GetFiles();
1063 if ( files.size() == 0 )
1065 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1066 " Cannot find any Python Trace.py coverage files."
1067 << std::endl);
1068 // No coverage files is a valid thing, so the exit code is 0
1069 return 0;
1072 std::string testingDir = this->CTest->GetBinaryDir() + "/Testing";
1073 std::string tempDir = testingDir + "/CoverageInfo";
1074 std::string currentDirectory = cmSystemTools::GetCurrentWorkingDirectory();
1075 cmSystemTools::MakeDirectory(tempDir.c_str());
1076 cmSystemTools::ChangeDirectory(tempDir.c_str());
1078 cmSystemTools::ChangeDirectory(currentDirectory.c_str());
1080 std::vector<std::string>::iterator fileIt;
1081 int file_count = 0;
1082 for ( fileIt = files.begin(); fileIt != files.end(); ++ fileIt )
1084 std::string fileName = this->FindFile(cont, *fileIt);
1085 if ( fileName.empty() )
1087 cmCTestLog(this->CTest, ERROR_MESSAGE,
1088 "Cannot find source Python file corresponding to: "
1089 << fileIt->c_str() << std::endl);
1090 continue;
1093 std::string actualSourceFile
1094 = cmSystemTools::CollapseFullPath(fileName.c_str());
1095 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1096 " Check coverage for file: " << actualSourceFile.c_str()
1097 << std::endl);
1098 cmCTestCoverageHandlerContainer::SingleFileCoverageVector* vec
1099 = &cont->TotalCoverage[actualSourceFile];
1100 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1101 " in file: " << fileIt->c_str() << std::endl);
1102 std::ifstream ifile(fileIt->c_str());
1103 if ( ! ifile )
1105 cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open file: "
1106 << fileIt->c_str() << std::endl);
1108 else
1110 long cnt = -1;
1111 std::string nl;
1112 while ( cmSystemTools::GetLineFromStream(ifile, nl) )
1114 cnt ++;
1116 // Skip empty lines
1117 if ( !nl.size() )
1119 continue;
1122 // Skip unused lines
1123 if ( nl.size() < 12 )
1125 continue;
1128 // Read the coverage count from the beginning of the Trace.py output
1129 // line
1130 std::string prefix = nl.substr(0, 6);
1131 if ( prefix[5] != ' ' && prefix[5] != ':' )
1133 // This is a hack. We should really do something more elaborate
1134 prefix = nl.substr(0, 7);
1135 if ( prefix[6] != ' ' && prefix[6] != ':' )
1137 prefix = nl.substr(0, 8);
1138 if ( prefix[7] != ' ' && prefix[7] != ':' )
1140 cmCTestLog(this->CTest, ERROR_MESSAGE,
1141 "Currently the limit is maximum coverage of 999999"
1142 << std::endl);
1146 int cov = atoi(prefix.c_str());
1147 if ( prefix[prefix.size()-1] != ':' )
1149 // This line does not have ':' so no coverage here. That said,
1150 // Trace.py does not handle not covered lines versus comments etc.
1151 // So, this will be set to 0.
1152 cov = 0;
1154 cmCTestLog(this->CTest, DEBUG, "Prefix: " << prefix.c_str()
1155 << " cov: " << cov
1156 << std::endl);
1157 // Read the line number starting at the 10th character of the gcov
1158 // output line
1159 int lineIdx = cnt;
1160 if ( lineIdx >= 0 )
1162 while ( vec->size() <=
1163 static_cast<size_t>(lineIdx) )
1165 vec->push_back(-1);
1167 // Initially all entries are -1 (not used). If we get coverage
1168 // information, increment it to 0 first.
1169 if ( (*vec)[lineIdx] < 0 )
1171 if ( cov >= 0 )
1173 (*vec)[lineIdx] = 0;
1176 (*vec)[lineIdx] += cov;
1180 ++ file_count;
1182 cmSystemTools::ChangeDirectory(currentDirectory.c_str());
1183 return file_count;
1186 //----------------------------------------------------------------------
1187 std::string cmCTestCoverageHandler::FindFile(
1188 cmCTestCoverageHandlerContainer* cont,
1189 std::string fileName)
1191 std::string fileNameNoE
1192 = cmSystemTools::GetFilenameWithoutLastExtension(fileName);
1193 // First check in source and binary directory
1194 std::string fullName = cont->SourceDir + "/" + fileNameNoE + ".py";
1195 if ( cmSystemTools::FileExists(fullName.c_str()) )
1197 return fullName;
1199 fullName = cont->BinaryDir + "/" + fileNameNoE + ".py";
1200 if ( cmSystemTools::FileExists(fullName.c_str()) )
1202 return fullName;
1204 return "";
1207 // This is a header put on each marked up source file
1208 namespace
1210 const char* bullseyeHelp[] =
1211 {" Coverage produced by bullseye covbr tool: ",
1212 " www.bullseye.com/help/ref_covbr.html",
1213 " * An arrow --> indicates incomplete coverage.",
1214 " * An X indicates a function that was invoked, a switch label that ",
1215 " was exercised, a try-block that finished, or an exception handler ",
1216 " that was invoked.",
1217 " * A T or F indicates a boolean decision that evaluated true or false,",
1218 " respectively.",
1219 " * A t or f indicates a boolean condition within a decision if the ",
1220 " condition evaluated true or false, respectively.",
1221 " * A k indicates a constant decision or condition.",
1222 " * The slash / means this probe is excluded from summary results. ",
1226 //----------------------------------------------------------------------
1227 int cmCTestCoverageHandler::RunBullseyeCoverageBranch(
1228 cmCTestCoverageHandlerContainer* cont,
1229 std::set<cmStdString>& coveredFileNames,
1230 std::vector<std::string>& files,
1231 std::vector<std::string>& filesFullPath)
1233 if(files.size() != filesFullPath.size())
1235 cmCTestLog(this->CTest, ERROR_MESSAGE,
1236 "Files and full path files not the same size?:\n");
1237 return 0;
1239 // create the output stream for the CoverageLog-N.xml file
1240 cmGeneratedFileStream covLogFile;
1241 int logFileCount = 0;
1242 if ( !this->StartCoverageLogFile(covLogFile, logFileCount) )
1244 return -1;
1246 // for each file run covbr on that file to get the coverage
1247 // information for that file
1248 std::string outputFile;
1249 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1250 "run covbr: "
1251 << std::endl);
1253 if(!this->RunBullseyeCommand(cont, "covbr", 0, outputFile))
1255 cmCTestLog(this->CTest, ERROR_MESSAGE, "error running covbr for." << "\n");
1256 return -1;
1258 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1259 "covbr output in " << outputFile
1260 << std::endl);
1261 // open the output file
1262 std::ifstream fin(outputFile.c_str());
1263 if(!fin)
1265 cmCTestLog(this->CTest, ERROR_MESSAGE,
1266 "Cannot open coverage file: " <<
1267 outputFile.c_str() << std::endl);
1268 return 0;
1270 std::map<cmStdString, cmStdString> fileMap;
1271 std::vector<std::string>::iterator fp = filesFullPath.begin();
1272 for(std::vector<std::string>::iterator f = files.begin();
1273 f != files.end(); ++f, ++fp)
1275 fileMap[*f] = *fp;
1278 int count =0; // keep count of the number of files
1279 // Now parse each line from the bullseye cov log file
1280 std::string lineIn;
1281 bool valid = false; // are we in a valid output file
1282 int line = 0; // line of the current file
1283 cmStdString file;
1284 while(cmSystemTools::GetLineFromStream(fin, lineIn))
1286 bool startFile = false;
1287 if(lineIn.size() > 1 && lineIn[lineIn.size()-1] == ':')
1289 file = lineIn.substr(0, lineIn.size()-1);
1290 if(coveredFileNames.find(file) != coveredFileNames.end())
1292 startFile = true;
1295 if(startFile)
1297 // if we are in a valid file close it because a new one started
1298 if(valid)
1300 covLogFile << "\t\t</Report>" << std::endl
1301 << "\t</File>" << std::endl;
1303 // only allow 100 files in each log file
1304 if ( count != 0 && count % 100 == 0 )
1306 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1307 "start a new log file: "
1308 << count
1309 << std::endl);
1310 this->EndCoverageLogFile(covLogFile, logFileCount);
1311 logFileCount ++;
1312 if ( !this->StartCoverageLogFile(covLogFile, logFileCount) )
1314 return -1;
1316 count++; // move on one
1318 std::map<cmStdString, cmStdString>::iterator
1319 i = fileMap.find(file);
1320 // if the file should be covered write out the header for that file
1321 if(i != fileMap.end())
1323 // we have a new file so count it in the output
1324 count++;
1325 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1326 "Produce coverage for file: "
1327 << file.c_str() << " " << count
1328 << std::endl);
1329 // start the file output
1330 covLogFile << "\t<File Name=\""
1331 << this->CTest->MakeXMLSafe(i->first.c_str())
1332 << "\" FullPath=\"" << this->CTest->MakeXMLSafe(
1333 this->CTest->GetShortPathToFile(
1334 i->second.c_str())) << "\">" << std::endl
1335 << "\t\t<Report>" << std::endl;
1336 // write the bullseye header
1337 line =0;
1338 for(int k =0; bullseyeHelp[k] != 0; ++k)
1340 covLogFile << "\t\t<Line Number=\"" << line << "\" Count=\"-1\">"
1341 << this->CTest->MakeXMLSafe(bullseyeHelp[k])
1342 << "</Line>" << std::endl;
1343 line++;
1345 valid = true; // we are in a valid file section
1347 else
1349 // this is not a file that we want coverage for
1350 valid = false;
1353 // we are not at a start file, and we are in a valid file output the line
1354 else if(valid)
1356 covLogFile << "\t\t<Line Number=\"" << line << "\" Count=\"-1\">"
1357 << this->CTest->MakeXMLSafe(lineIn.c_str())
1358 << "</Line>" << std::endl;
1359 line++;
1362 // if we ran out of lines a valid file then close that file
1363 if(valid)
1365 covLogFile << "\t\t</Report>" << std::endl
1366 << "\t</File>" << std::endl;
1368 this->EndCoverageLogFile(covLogFile, logFileCount);
1369 return 1;
1372 //----------------------------------------------------------------------
1373 int cmCTestCoverageHandler::RunBullseyeCommand(
1374 cmCTestCoverageHandlerContainer* cont,
1375 const char* cmd,
1376 const char* arg,
1377 std::string& outputFile)
1379 std::string program = cmSystemTools::FindProgram(cmd);
1380 if(program.size() == 0)
1382 cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find :" << cmd << "\n");
1383 return 0;
1385 if(arg)
1387 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1388 "Run : " << program.c_str() << " " << arg << "\n");
1390 else
1392 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1393 "Run : " << program.c_str() << "\n");
1395 // create a process object and start it
1396 cmCTestRunProcess runCoverageSrc;
1397 runCoverageSrc.SetCommand(program.c_str());
1398 runCoverageSrc.AddArgument(arg);
1399 std::string stdoutFile = cont->BinaryDir + "/Testing/Temporary/";
1400 stdoutFile += this->GetCTestInstance()->GetCurrentTag();
1401 stdoutFile += "-";
1402 stdoutFile += cmd;
1403 std::string stderrFile = stdoutFile;
1404 stdoutFile += ".stdout";
1405 stderrFile += ".stderr";
1406 runCoverageSrc.SetStdoutFile(stdoutFile.c_str());
1407 runCoverageSrc.SetStderrFile(stderrFile.c_str());
1408 if(!runCoverageSrc.StartProcess())
1410 cmCTestLog(this->CTest, ERROR_MESSAGE, "Could not run : "
1411 << program.c_str() << " " << arg << "\n"
1412 << "kwsys process state : "
1413 << runCoverageSrc.GetProcessState());
1414 return 0;
1416 // since we set the output file names wait for it to end
1417 runCoverageSrc.WaitForExit();
1418 outputFile = stdoutFile;
1419 return 1;
1422 //----------------------------------------------------------------------
1423 int cmCTestCoverageHandler::RunBullseyeSourceSummary(
1424 cmCTestCoverageHandlerContainer* cont)
1426 // Run the covsrc command and create a temp outputfile
1427 std::string outputFile;
1428 if(!this->RunBullseyeCommand(cont, "covsrc", "-c", outputFile))
1430 cmCTestLog(this->CTest, ERROR_MESSAGE, "error running covsrc:\n");
1431 return 0;
1434 std::ostream& tmpLog = *cont->OFS;
1435 // copen the Coverage.xml file in the Testing directory
1436 cmGeneratedFileStream covSumFile;
1437 if (!this->StartResultingXML("Coverage", covSumFile))
1439 cmCTestLog(this->CTest, ERROR_MESSAGE,
1440 "Cannot open coverage summary file." << std::endl);
1441 return 0;
1443 this->CTest->StartXML(covSumFile);
1444 double elapsed_time_start = cmSystemTools::GetTime();
1445 std::string coverage_start_time = this->CTest->CurrentTime();
1446 covSumFile << "<Coverage>" << std::endl
1447 << "\t<StartDateTime>"
1448 << coverage_start_time << "</StartDateTime>"
1449 << std::endl
1450 << "\t<StartTime>"
1451 << static_cast<unsigned int>(cmSystemTools::GetTime())
1452 << "</StartTime>"
1453 << std::endl;
1454 std::string stdline;
1455 std::string errline;
1456 // expected output:
1457 // first line is:
1458 // "Source","Function Coverage","out of","%","C/D Coverage","out of","%"
1459 // after that data follows in that format
1460 std::string sourceFile;
1461 int functionsCalled = 0;
1462 int totalFunctions = 0;
1463 int percentFunction = 0;
1464 int branchCovered = 0;
1465 int totalBranches = 0;
1466 int percentBranch = 0;
1467 double total_tested = 0;
1468 double total_untested = 0;
1469 double total_functions = 0;
1470 double percent_coverage =0;
1471 double number_files = 0;
1472 std::vector<std::string> coveredFiles;
1473 std::vector<std::string> coveredFilesFullPath;
1474 // Read and parse the summary output file
1475 std::ifstream fin(outputFile.c_str());
1476 if(!fin)
1478 cmCTestLog(this->CTest, ERROR_MESSAGE,
1479 "Cannot open coverage summary file: " <<
1480 outputFile.c_str() << std::endl);
1481 return 0;
1483 std::set<cmStdString> coveredFileNames;
1484 while(cmSystemTools::GetLineFromStream(fin, stdline))
1486 // if we have a line of output from stdout
1487 if(stdline.size())
1489 // parse the comma separated output
1490 this->ParseBullsEyeCovsrcLine(stdline,
1491 sourceFile,
1492 functionsCalled,
1493 totalFunctions,
1494 percentFunction,
1495 branchCovered,
1496 totalBranches,
1497 percentBranch);
1498 // The first line is the header
1499 if(sourceFile == "Source" || sourceFile == "Total")
1501 continue;
1503 std::string file = sourceFile;
1504 coveredFileNames.insert(file);
1505 if(!cmSystemTools::FileIsFullPath(sourceFile.c_str()))
1507 // file will be relative to the binary dir
1508 file = cont->BinaryDir;
1509 file += "/";
1510 file += sourceFile;
1512 file = cmSystemTools::CollapseFullPath(file.c_str());
1513 bool shouldIDoCoverage
1514 = this->ShouldIDoCoverage(file.c_str(),
1515 cont->SourceDir.c_str(),
1516 cont->BinaryDir.c_str());
1517 if ( !shouldIDoCoverage )
1519 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1520 ".NoDartCoverage found, so skip coverage check for: "
1521 << file.c_str()
1522 << std::endl);
1523 continue;
1526 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1527 "Doing coverage for: "
1528 << file.c_str()
1529 << std::endl);
1531 coveredFiles.push_back(sourceFile);
1532 coveredFilesFullPath.push_back(file);
1534 number_files++;
1535 total_functions += totalFunctions;
1536 total_tested += functionsCalled;
1537 total_untested += (totalFunctions - functionsCalled);
1539 std::string fileName = cmSystemTools::GetFilenameName(file.c_str());
1541 float cper = percentBranch + percentFunction;
1542 if(totalBranches > 0)
1544 cper /= 2.0f;
1546 percent_coverage += cper;
1547 float cmet = percentFunction + percentBranch;
1548 if(totalBranches > 0)
1550 cmet /= 2.0f;
1552 cmet /= 100.0f;
1553 tmpLog << stdline.c_str() << "\n";
1554 tmpLog << fileName << "\n";
1555 tmpLog << "functionsCalled: " << functionsCalled/100 << "\n";
1556 tmpLog << "totalFunctions: " << totalFunctions/100 << "\n";
1557 tmpLog << "percentFunction: " << percentFunction << "\n";
1558 tmpLog << "branchCovered: " << branchCovered << "\n";
1559 tmpLog << "totalBranches: " << totalBranches << "\n";
1560 tmpLog << "percentBranch: " << percentBranch << "\n";
1561 tmpLog << "percentCoverage: " << percent_coverage << "\n";
1562 tmpLog << "coverage metric: " << cmet << "\n";
1563 covSumFile << "\t<File Name=\"" << this->CTest->MakeXMLSafe(sourceFile)
1564 << "\" FullPath=\"" << this->CTest->MakeXMLSafe(
1565 this->CTest->GetShortPathToFile(file.c_str()))
1566 << "\" Covered=\"" << (cmet>0?"true":"false") << "\">\n"
1567 << "\t\t<BranchesTested>"
1568 << branchCovered
1569 << "</BranchesTested>\n"
1570 << "\t\t<BranchesUnTested>"
1571 << totalBranches - branchCovered
1572 << "</BranchesUnTested>\n"
1573 << "\t\t<FunctionsTested>"
1574 << functionsCalled
1575 << "</FunctionsTested>\n"
1576 << "\t\t<FunctionsUnTested>"
1577 << totalFunctions - functionsCalled
1578 << "</FunctionsUnTested>\n"
1579 // Hack for conversion of function to loc assume a function
1580 // has 100 lines of code
1581 << "\t\t<LOCTested>" << functionsCalled *100
1582 << "</LOCTested>\n"
1583 << "\t\t<LOCUnTested>"
1584 << (totalFunctions - functionsCalled)*100
1585 << "</LOCUnTested>\n"
1586 << "\t\t<PercentCoverage>";
1587 covSumFile.setf(std::ios::fixed, std::ios::floatfield);
1588 covSumFile.precision(2);
1589 covSumFile << (cper) << "</PercentCoverage>\n"
1590 << "\t\t<CoverageMetric>";
1591 covSumFile.setf(std::ios::fixed, std::ios::floatfield);
1592 covSumFile.precision(2);
1593 covSumFile << (cmet) << "</CoverageMetric>\n"
1594 << "\t</File>" << std::endl;
1597 std::string end_time = this->CTest->CurrentTime();
1598 covSumFile << "\t<LOCTested>" << total_tested << "</LOCTested>\n"
1599 << "\t<LOCUntested>" << total_untested << "</LOCUntested>\n"
1600 << "\t<LOC>" << total_functions << "</LOC>\n"
1601 << "\t<PercentCoverage>";
1602 covSumFile.setf(std::ios::fixed, std::ios::floatfield);
1603 covSumFile.precision(2);
1604 covSumFile
1605 << SAFEDIV(percent_coverage,number_files)<< "</PercentCoverage>\n"
1606 << "\t<EndDateTime>" << end_time << "</EndDateTime>\n"
1607 << "\t<EndTime>" << static_cast<unsigned int>(cmSystemTools::GetTime())
1608 << "</EndTime>\n";
1609 covSumFile
1610 << "<ElapsedMinutes>" <<
1611 static_cast<int>((cmSystemTools::GetTime() - elapsed_time_start)/6)/10.0
1612 << "</ElapsedMinutes>"
1613 << "</Coverage>" << std::endl;
1614 this->CTest->EndXML(covSumFile);
1615 // Now create the coverage information for each file
1616 return this->RunBullseyeCoverageBranch(cont,
1617 coveredFileNames,
1618 coveredFiles,
1619 coveredFilesFullPath);
1622 //----------------------------------------------------------------------
1623 int cmCTestCoverageHandler::HandleBullseyeCoverage(
1624 cmCTestCoverageHandlerContainer* cont)
1626 const char* covfile = cmSystemTools::GetEnv("COVFILE");
1627 if(!covfile)
1629 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1630 " COVFILE environment variable not found, not running "
1631 " bullseye\n");
1632 return 0;
1634 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1635 " run covsrc with COVFILE=["
1636 << covfile
1637 << "]" << std::endl);
1638 if(!this->RunBullseyeSourceSummary(cont))
1640 cmCTestLog(this->CTest, ERROR_MESSAGE,
1641 "Error running bullseye summary.\n");
1642 return 0;
1644 cmCTestLog(this->CTest, DEBUG, "HandleBullseyeCoverage return 1 "
1645 << std::endl);
1646 return 1;
1649 bool cmCTestCoverageHandler::GetNextInt(std::string const& inputLine,
1650 std::string::size_type& pos,
1651 int& value)
1653 std::string::size_type start = pos;
1654 pos = inputLine.find(',', start);
1655 value = atoi(inputLine.substr(start, pos).c_str());
1656 if(pos == inputLine.npos)
1658 return true;
1660 pos++;
1661 return true;
1664 bool cmCTestCoverageHandler::ParseBullsEyeCovsrcLine(
1665 std::string const& inputLine,
1666 std::string& sourceFile,
1667 int& functionsCalled,
1668 int& totalFunctions,
1669 int& percentFunction,
1670 int& branchCovered,
1671 int& totalBranches,
1672 int& percentBranch)
1674 // find the first comma
1675 std::string::size_type pos = inputLine.find(',');
1676 if(pos == inputLine.npos)
1678 cmCTestLog(this->CTest, ERROR_MESSAGE, "Error parsing string : "
1679 << inputLine.c_str() << "\n");
1680 return false;
1682 // the source file has "" around it so extract out the file name
1683 sourceFile = inputLine.substr(1,pos-2);
1684 pos++;
1685 if(!this->GetNextInt(inputLine, pos, functionsCalled))
1687 return false;
1689 if(!this->GetNextInt(inputLine, pos, totalFunctions))
1691 return false;
1693 if(!this->GetNextInt(inputLine, pos, percentFunction))
1695 return false;
1697 if(!this->GetNextInt(inputLine, pos, branchCovered))
1699 return false;
1701 if(!this->GetNextInt(inputLine, pos, totalBranches))
1703 return false;
1705 if(!this->GetNextInt(inputLine, pos, percentBranch))
1707 return false;
1709 // should be at the end now
1710 if(pos != inputLine.npos)
1712 cmCTestLog(this->CTest, ERROR_MESSAGE, "Error parsing input : "
1713 << inputLine.c_str() << " last pos not npos = " << pos <<
1714 "\n");
1716 return true;