cmCTestCoverageHandler.cxx 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718
  1. /*=========================================================================
  2. Program: CMake - Cross-Platform Makefile Generator
  3. Module: $RCSfile$
  4. Language: C++
  5. Date: $Date$
  6. Version: $Revision$
  7. Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
  8. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
  9. This software is distributed WITHOUT ANY WARRANTY; without even
  10. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  11. PURPOSE. See the above copyright notices for more information.
  12. =========================================================================*/
  13. #include "cmCTestCoverageHandler.h"
  14. #include "cmCTest.h"
  15. #include "cmake.h"
  16. #include "cmSystemTools.h"
  17. #include "cmGeneratedFileStream.h"
  18. #include <cmsys/Process.h>
  19. #include <cmsys/RegularExpression.hxx>
  20. #include <cmsys/Glob.hxx>
  21. #include <stdlib.h>
  22. #include <math.h>
  23. #include <float.h>
  24. #define SAFEDIV(x,y) (((y)!=0)?((x)/(y)):(0))
  25. class cmCTestRunProcess
  26. {
  27. public:
  28. cmCTestRunProcess()
  29. {
  30. this->Process = cmsysProcess_New();
  31. this->PipeState = -1;
  32. this->TimeOut = -1;
  33. }
  34. ~cmCTestRunProcess()
  35. {
  36. if(!(this->PipeState == -1)
  37. && !(this->PipeState == cmsysProcess_Pipe_None )
  38. && !(this->PipeState == cmsysProcess_Pipe_Timeout))
  39. {
  40. this->WaitForExit();
  41. }
  42. cmsysProcess_Delete(this->Process);
  43. }
  44. void SetCommand(const char* command)
  45. {
  46. this->CommandLineStrings.clear();
  47. this->CommandLineStrings.push_back(command);;
  48. }
  49. void AddArgument(const char* arg)
  50. {
  51. if(arg)
  52. {
  53. this->CommandLineStrings.push_back(arg);
  54. }
  55. }
  56. void SetWorkingDirectory(const char* dir)
  57. {
  58. this->WorkingDirectory = dir;
  59. }
  60. void SetTimeout(double t)
  61. {
  62. this->TimeOut = t;
  63. }
  64. bool StartProcess()
  65. {
  66. std::vector<const char*> args;
  67. for(std::vector<std::string>::iterator i =
  68. this->CommandLineStrings.begin();
  69. i != this->CommandLineStrings.end(); ++i)
  70. {
  71. args.push_back(i->c_str());
  72. }
  73. args.push_back(0); // null terminate
  74. cmsysProcess_SetCommand(this->Process, &*args.begin());
  75. if(this->WorkingDirectory.size())
  76. {
  77. cmsysProcess_SetWorkingDirectory(this->Process,
  78. this->WorkingDirectory.c_str());
  79. }
  80. cmsysProcess_SetOption(this->Process,
  81. cmsysProcess_Option_HideWindow, 1);
  82. if(this->TimeOut != -1)
  83. {
  84. cmsysProcess_SetTimeout(this->Process, this->TimeOut);
  85. }
  86. cmsysProcess_Execute(this->Process);
  87. this->PipeState = cmsysProcess_GetState(this->Process);
  88. // if the process is running or exited return true
  89. if(this->PipeState == cmsysProcess_State_Executing
  90. || this->PipeState == cmsysProcess_State_Exited)
  91. {
  92. return true;
  93. }
  94. return false;
  95. }
  96. void SetStdoutFile(const char* fname)
  97. {
  98. cmsysProcess_SetPipeFile(this->Process, cmsysProcess_Pipe_STDOUT, fname);
  99. }
  100. void SetStderrFile(const char* fname)
  101. {
  102. cmsysProcess_SetPipeFile(this->Process, cmsysProcess_Pipe_STDERR, fname);
  103. }
  104. int WaitForExit(double* timeout =0)
  105. {
  106. this->PipeState = cmsysProcess_WaitForExit(this->Process,
  107. timeout);
  108. return this->PipeState;
  109. }
  110. int GetProcessState() { return this->PipeState;}
  111. private:
  112. int PipeState;
  113. cmsysProcess* Process;
  114. std::vector<std::string> CommandLineStrings;
  115. std::string WorkingDirectory;
  116. double TimeOut;
  117. };
  118. //----------------------------------------------------------------------
  119. //**********************************************************************
  120. class cmCTestCoverageHandlerContainer
  121. {
  122. public:
  123. int Error;
  124. std::string SourceDir;
  125. std::string BinaryDir;
  126. typedef std::vector<int> SingleFileCoverageVector;
  127. typedef std::map<std::string, SingleFileCoverageVector> TotalCoverageMap;
  128. TotalCoverageMap TotalCoverage;
  129. std::ostream* OFS;
  130. };
  131. //**********************************************************************
  132. //----------------------------------------------------------------------
  133. //----------------------------------------------------------------------
  134. cmCTestCoverageHandler::cmCTestCoverageHandler()
  135. {
  136. }
  137. //----------------------------------------------------------------------
  138. void cmCTestCoverageHandler::Initialize()
  139. {
  140. this->Superclass::Initialize();
  141. this->CustomCoverageExclude.empty();
  142. }
  143. //----------------------------------------------------------------------
  144. bool cmCTestCoverageHandler::StartCoverageLogFile(
  145. cmGeneratedFileStream& covLogFile, int logFileCount)
  146. {
  147. char covLogFilename[1024];
  148. sprintf(covLogFilename, "CoverageLog-%d", logFileCount);
  149. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Open file: "
  150. << covLogFilename << std::endl);
  151. if(!this->StartResultingXML(cmCTest::PartCoverage,
  152. covLogFilename, covLogFile))
  153. {
  154. cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open log file: "
  155. << covLogFilename << std::endl);
  156. return false;
  157. }
  158. std::string local_start_time = this->CTest->CurrentTime();
  159. this->CTest->StartXML(covLogFile, this->AppendXML);
  160. covLogFile << "<CoverageLog>" << std::endl
  161. << "\t<StartDateTime>" << local_start_time << "</StartDateTime>"
  162. << "\t<StartTime>"
  163. << static_cast<unsigned int>(cmSystemTools::GetTime())
  164. << "</StartTime>"
  165. << std::endl;
  166. return true;
  167. }
  168. //----------------------------------------------------------------------
  169. void cmCTestCoverageHandler::EndCoverageLogFile(cmGeneratedFileStream& ostr,
  170. int logFileCount)
  171. {
  172. std::string local_end_time = this->CTest->CurrentTime();
  173. ostr << "\t<EndDateTime>" << local_end_time << "</EndDateTime>" << std::endl
  174. << "\t<EndTime>" <<
  175. static_cast<unsigned int>(cmSystemTools::GetTime())
  176. << "</EndTime>" << std::endl
  177. << "</CoverageLog>" << std::endl;
  178. this->CTest->EndXML(ostr);
  179. char covLogFilename[1024];
  180. sprintf(covLogFilename, "CoverageLog-%d.xml", logFileCount);
  181. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Close file: "
  182. << covLogFilename << std::endl);
  183. ostr.Close();
  184. }
  185. //----------------------------------------------------------------------
  186. bool cmCTestCoverageHandler::ShouldIDoCoverage(const char* file,
  187. const char* srcDir,
  188. const char* binDir)
  189. {
  190. std::vector<cmsys::RegularExpression>::iterator sit;
  191. for ( sit = this->CustomCoverageExcludeRegex.begin();
  192. sit != this->CustomCoverageExcludeRegex.end(); ++ sit )
  193. {
  194. if ( sit->find(file) )
  195. {
  196. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " File " << file
  197. << " is excluded in CTestCustom.ctest" << std::endl;);
  198. return false;
  199. }
  200. }
  201. std::string fSrcDir = cmSystemTools::CollapseFullPath(srcDir);
  202. std::string fBinDir = cmSystemTools::CollapseFullPath(binDir);
  203. std::string fFile = cmSystemTools::CollapseFullPath(file);
  204. bool sourceSubDir = cmSystemTools::IsSubDirectory(fFile.c_str(),
  205. fSrcDir.c_str());
  206. bool buildSubDir = cmSystemTools::IsSubDirectory(fFile.c_str(),
  207. fBinDir.c_str());
  208. // Always check parent directory of the file.
  209. std::string fileDir = cmSystemTools::GetFilenamePath(fFile.c_str());
  210. std::string checkDir;
  211. // We also need to check the binary/source directory pair.
  212. if ( sourceSubDir && buildSubDir )
  213. {
  214. if ( fSrcDir.size() > fBinDir.size() )
  215. {
  216. checkDir = fSrcDir;
  217. }
  218. else
  219. {
  220. checkDir = fBinDir;
  221. }
  222. }
  223. else if ( sourceSubDir )
  224. {
  225. checkDir = fSrcDir;
  226. }
  227. else if ( buildSubDir )
  228. {
  229. checkDir = fBinDir;
  230. }
  231. std::string ndc
  232. = cmSystemTools::FileExistsInParentDirectories(".NoDartCoverage",
  233. fFile.c_str(), checkDir.c_str());
  234. if ( ndc.size() )
  235. {
  236. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Found: " << ndc.c_str()
  237. << " so skip coverage of " << file << std::endl);
  238. return false;
  239. }
  240. // By now checkDir should be set to parent directory of the file.
  241. // Get the relative path to the file an apply it to the opposite directory.
  242. // If it is the same as fileDir, then ignore, otherwise check.
  243. std::string relPath;
  244. if(checkDir.size() )
  245. {
  246. relPath = cmSystemTools::RelativePath(checkDir.c_str(),
  247. fFile.c_str());
  248. }
  249. else
  250. {
  251. relPath = fFile;
  252. }
  253. if ( checkDir == fSrcDir )
  254. {
  255. checkDir = fBinDir;
  256. }
  257. else
  258. {
  259. checkDir = fSrcDir;
  260. }
  261. fFile = checkDir + "/" + relPath;
  262. fFile = cmSystemTools::GetFilenamePath(fFile.c_str());
  263. if ( fileDir == fFile )
  264. {
  265. // This is in-source build, so we trust the previous check.
  266. return true;
  267. }
  268. ndc = cmSystemTools::FileExistsInParentDirectories(".NoDartCoverage",
  269. fFile.c_str(), checkDir.c_str());
  270. if ( ndc.size() )
  271. {
  272. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Found: " << ndc.c_str()
  273. << " so skip coverage of: " << file << std::endl);
  274. return false;
  275. }
  276. // Ok, nothing in source tree, nothing in binary tree
  277. return true;
  278. }
  279. //----------------------------------------------------------------------
  280. //clearly it would be nice if this were broken up into a few smaller
  281. //functions and commented...
  282. int cmCTestCoverageHandler::ProcessHandler()
  283. {
  284. int error = 0;
  285. // do we have time for this
  286. if (this->CTest->GetRemainingTimeAllowed() < 120)
  287. {
  288. return error;
  289. }
  290. std::string coverage_start_time = this->CTest->CurrentTime();
  291. unsigned int coverage_start_time_time = static_cast<unsigned int>(
  292. cmSystemTools::GetTime());
  293. std::string sourceDir
  294. = this->CTest->GetCTestConfiguration("SourceDirectory");
  295. std::string binaryDir
  296. = this->CTest->GetCTestConfiguration("BuildDirectory");
  297. cmGeneratedFileStream ofs;
  298. double elapsed_time_start = cmSystemTools::GetTime();
  299. if ( !this->StartLogFile("Coverage", ofs) )
  300. {
  301. cmCTestLog(this->CTest, ERROR_MESSAGE,
  302. "Cannot create LastCoverage.log file" << std::endl);
  303. }
  304. ofs << "Performing coverage: " << elapsed_time_start << std::endl;
  305. cmSystemTools::ConvertToUnixSlashes(sourceDir);
  306. cmSystemTools::ConvertToUnixSlashes(binaryDir);
  307. std::string asfGlob = sourceDir + "/*";
  308. std::string abfGlob = binaryDir + "/*";
  309. cmCTestLog(this->CTest, HANDLER_OUTPUT, "Performing coverage" << std::endl);
  310. cmCTestCoverageHandlerContainer cont;
  311. cont.Error = error;
  312. cont.SourceDir = sourceDir;
  313. cont.BinaryDir = binaryDir;
  314. cont.OFS = &ofs;
  315. // setup the regex exclude stuff
  316. this->CustomCoverageExcludeRegex.empty();
  317. std::vector<cmStdString>::iterator rexIt;
  318. for ( rexIt = this->CustomCoverageExclude.begin();
  319. rexIt != this->CustomCoverageExclude.end();
  320. ++ rexIt )
  321. {
  322. this->CustomCoverageExcludeRegex.push_back(
  323. cmsys::RegularExpression(rexIt->c_str()));
  324. }
  325. if(this->HandleBullseyeCoverage(&cont))
  326. {
  327. return cont.Error;
  328. }
  329. int file_count = 0;
  330. file_count += this->HandleGCovCoverage(&cont);
  331. if ( file_count < 0 )
  332. {
  333. return error;
  334. }
  335. file_count += this->HandleTracePyCoverage(&cont);
  336. if ( file_count < 0 )
  337. {
  338. return error;
  339. }
  340. error = cont.Error;
  341. if ( file_count == 0 )
  342. {
  343. cmCTestLog(this->CTest, WARNING,
  344. " Cannot find any coverage files. Ignoring Coverage request."
  345. << std::endl);
  346. return error;
  347. }
  348. cmGeneratedFileStream covSumFile;
  349. cmGeneratedFileStream covLogFile;
  350. if(!this->StartResultingXML(cmCTest::PartCoverage, "Coverage", covSumFile))
  351. {
  352. cmCTestLog(this->CTest, ERROR_MESSAGE,
  353. "Cannot open coverage summary file." << std::endl);
  354. return -1;
  355. }
  356. this->CTest->StartXML(covSumFile, this->AppendXML);
  357. // Produce output xml files
  358. covSumFile << "<Coverage>" << std::endl
  359. << "\t<StartDateTime>" << coverage_start_time << "</StartDateTime>"
  360. << std::endl
  361. << "\t<StartTime>" << coverage_start_time_time << "</StartTime>"
  362. << std::endl;
  363. int logFileCount = 0;
  364. if ( !this->StartCoverageLogFile(covLogFile, logFileCount) )
  365. {
  366. return -1;
  367. }
  368. cmCTestCoverageHandlerContainer::TotalCoverageMap::iterator fileIterator;
  369. int cnt = 0;
  370. long total_tested = 0;
  371. long total_untested = 0;
  372. //std::string fullSourceDir = sourceDir + "/";
  373. //std::string fullBinaryDir = binaryDir + "/";
  374. cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl);
  375. cmCTestLog(this->CTest, HANDLER_OUTPUT,
  376. " Acumulating results (each . represents one file):" << std::endl);
  377. cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
  378. std::vector<std::string> errorsWhileAccumulating;
  379. file_count = 0;
  380. for ( fileIterator = cont.TotalCoverage.begin();
  381. fileIterator != cont.TotalCoverage.end();
  382. ++fileIterator )
  383. {
  384. cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
  385. file_count ++;
  386. if ( file_count % 50 == 0 )
  387. {
  388. cmCTestLog(this->CTest, HANDLER_OUTPUT, " processed: " << file_count
  389. << " out of "
  390. << cont.TotalCoverage.size() << std::endl);
  391. cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
  392. }
  393. if ( cnt % 100 == 0 )
  394. {
  395. this->EndCoverageLogFile(covLogFile, logFileCount);
  396. logFileCount ++;
  397. if ( !this->StartCoverageLogFile(covLogFile, logFileCount) )
  398. {
  399. return -1;
  400. }
  401. }
  402. const std::string fullFileName = fileIterator->first;
  403. const std::string fileName
  404. = cmSystemTools::GetFilenameName(fullFileName.c_str());
  405. std::string fullFilePath
  406. = cmSystemTools::GetFilenamePath(fullFileName.c_str());
  407. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  408. "Process file: " << fullFileName << std::endl);
  409. cmSystemTools::ConvertToUnixSlashes(fullFilePath);
  410. if ( !cmSystemTools::FileExists(fullFileName.c_str()) )
  411. {
  412. cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find file: "
  413. << fullFileName.c_str() << std::endl);
  414. continue;
  415. }
  416. bool shouldIDoCoverage
  417. = this->ShouldIDoCoverage(fullFileName.c_str(),
  418. sourceDir.c_str(), binaryDir.c_str());
  419. if ( !shouldIDoCoverage )
  420. {
  421. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  422. ".NoDartCoverage found, so skip coverage check for: "
  423. << fullFileName.c_str()
  424. << std::endl);
  425. continue;
  426. }
  427. const cmCTestCoverageHandlerContainer::SingleFileCoverageVector& fcov
  428. = fileIterator->second;
  429. covLogFile << "\t<File Name=\""
  430. << this->CTest->MakeXMLSafe(fileName.c_str())
  431. << "\" FullPath=\"" << this->CTest->MakeXMLSafe(
  432. this->CTest->GetShortPathToFile(
  433. fileIterator->first.c_str())) << "\">" << std::endl
  434. << "\t\t<Report>" << std::endl;
  435. std::ifstream ifs(fullFileName.c_str());
  436. if ( !ifs)
  437. {
  438. cmOStringStream ostr;
  439. ostr << "Cannot open source file: " << fullFileName.c_str();
  440. errorsWhileAccumulating.push_back(ostr.str());
  441. error ++;
  442. continue;
  443. }
  444. int tested = 0;
  445. int untested = 0;
  446. cmCTestCoverageHandlerContainer::SingleFileCoverageVector::size_type cc;
  447. std::string line;
  448. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  449. "Actually perfoming coverage for: " << fullFileName << std::endl);
  450. for ( cc= 0; cc < fcov.size(); cc ++ )
  451. {
  452. if ( !cmSystemTools::GetLineFromStream(ifs, line) &&
  453. cc != fcov.size() -1 )
  454. {
  455. cmOStringStream ostr;
  456. ostr << "Problem reading source file: " << fullFileName.c_str()
  457. << " line:" << cc;
  458. errorsWhileAccumulating.push_back(ostr.str());
  459. error ++;
  460. break;
  461. }
  462. covLogFile << "\t\t<Line Number=\"" << cc << "\" Count=\"" << fcov[cc]
  463. << "\">"
  464. << this->CTest->MakeXMLSafe(line.c_str()) << "</Line>" << std::endl;
  465. if ( fcov[cc] == 0 )
  466. {
  467. untested ++;
  468. }
  469. else if ( fcov[cc] > 0 )
  470. {
  471. tested ++;
  472. }
  473. }
  474. if ( cmSystemTools::GetLineFromStream(ifs, line) )
  475. {
  476. cmOStringStream ostr;
  477. ostr << "Looks like there are more lines in the file: " << line;
  478. errorsWhileAccumulating.push_back(ostr.str());
  479. }
  480. float cper = 0;
  481. float cmet = 0;
  482. if ( tested + untested > 0 )
  483. {
  484. cper = (100 * SAFEDIV(static_cast<float>(tested),
  485. static_cast<float>(tested + untested)));
  486. cmet = ( SAFEDIV(static_cast<float>(tested + 10),
  487. static_cast<float>(tested + untested + 10)));
  488. }
  489. total_tested += tested;
  490. total_untested += untested;
  491. covLogFile << "\t\t</Report>" << std::endl
  492. << "\t</File>" << std::endl;
  493. covSumFile << "\t<File Name=\"" << this->CTest->MakeXMLSafe(fileName)
  494. << "\" FullPath=\"" << this->CTest->MakeXMLSafe(
  495. this->CTest->GetShortPathToFile(fullFileName.c_str()))
  496. << "\" Covered=\"" << (tested > 0 ? "true":"false") << "\">\n"
  497. << "\t\t<LOCTested>" << tested << "</LOCTested>\n"
  498. << "\t\t<LOCUnTested>" << untested << "</LOCUnTested>\n"
  499. << "\t\t<PercentCoverage>";
  500. covSumFile.setf(std::ios::fixed, std::ios::floatfield);
  501. covSumFile.precision(2);
  502. covSumFile << (cper) << "</PercentCoverage>\n"
  503. << "\t\t<CoverageMetric>";
  504. covSumFile.setf(std::ios::fixed, std::ios::floatfield);
  505. covSumFile.precision(2);
  506. covSumFile << (cmet) << "</CoverageMetric>\n"
  507. << "\t</File>" << std::endl;
  508. cnt ++;
  509. }
  510. this->EndCoverageLogFile(covLogFile, logFileCount);
  511. if ( errorsWhileAccumulating.size() > 0 )
  512. {
  513. cmCTestLog(this->CTest, ERROR_MESSAGE, std::endl);
  514. cmCTestLog(this->CTest, ERROR_MESSAGE,
  515. "Error(s) while acumulating results:" << std::endl);
  516. std::vector<std::string>::iterator erIt;
  517. for ( erIt = errorsWhileAccumulating.begin();
  518. erIt != errorsWhileAccumulating.end();
  519. ++ erIt )
  520. {
  521. cmCTestLog(this->CTest, ERROR_MESSAGE,
  522. " " << erIt->c_str() << std::endl);
  523. }
  524. }
  525. int total_lines = total_tested + total_untested;
  526. float percent_coverage = 100 * SAFEDIV(static_cast<float>(total_tested),
  527. static_cast<float>(total_lines));
  528. if ( total_lines == 0 )
  529. {
  530. percent_coverage = 0;
  531. }
  532. std::string end_time = this->CTest->CurrentTime();
  533. covSumFile << "\t<LOCTested>" << total_tested << "</LOCTested>\n"
  534. << "\t<LOCUntested>" << total_untested << "</LOCUntested>\n"
  535. << "\t<LOC>" << total_lines << "</LOC>\n"
  536. << "\t<PercentCoverage>";
  537. covSumFile.setf(std::ios::fixed, std::ios::floatfield);
  538. covSumFile.precision(2);
  539. covSumFile << (percent_coverage)<< "</PercentCoverage>\n"
  540. << "\t<EndDateTime>" << end_time << "</EndDateTime>\n"
  541. << "\t<EndTime>" <<
  542. static_cast<unsigned int>(cmSystemTools::GetTime())
  543. << "</EndTime>\n";
  544. covSumFile << "<ElapsedMinutes>" <<
  545. static_cast<int>((cmSystemTools::GetTime() - elapsed_time_start)/6)/10.0
  546. << "</ElapsedMinutes>"
  547. << "</Coverage>" << std::endl;
  548. this->CTest->EndXML(covSumFile);
  549. cmCTestLog(this->CTest, HANDLER_OUTPUT, "" << std::endl
  550. << "\tCovered LOC: "
  551. << total_tested << std::endl
  552. << "\tNot covered LOC: " << total_untested << std::endl
  553. << "\tTotal LOC: " << total_lines << std::endl
  554. << "\tPercentage Coverage: "
  555. << std::setiosflags(std::ios::fixed)
  556. << std::setprecision(2)
  557. << (percent_coverage) << "%" << std::endl);
  558. ofs << "\tCovered LOC: " << total_tested << std::endl
  559. << "\tNot covered LOC: " << total_untested << std::endl
  560. << "\tTotal LOC: " << total_lines << std::endl
  561. << "\tPercentage Coverage: "
  562. << std::setiosflags(std::ios::fixed)
  563. << std::setprecision(2)
  564. << (percent_coverage) << "%" << std::endl;
  565. if ( error )
  566. {
  567. return -1;
  568. }
  569. return 0;
  570. }
  571. //----------------------------------------------------------------------
  572. void cmCTestCoverageHandler::PopulateCustomVectors(cmMakefile *mf)
  573. {
  574. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  575. " Add coverage exclude regular expressions." << std::endl);
  576. this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_COVERAGE_EXCLUDE",
  577. this->CustomCoverageExclude);
  578. std::vector<cmStdString>::iterator it;
  579. for ( it = this->CustomCoverageExclude.begin();
  580. it != this->CustomCoverageExclude.end();
  581. ++ it )
  582. {
  583. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Add coverage exclude: "
  584. << it->c_str() << std::endl);
  585. }
  586. }
  587. //----------------------------------------------------------------------
  588. // Fix for issue #4971 where the case of the drive letter component of
  589. // the filenames might be different when analyzing gcov output.
  590. //
  591. // Compare file names: fnc(fn1) == fnc(fn2) // fnc == file name compare
  592. //
  593. #ifdef _WIN32
  594. #define fnc(s) cmSystemTools::LowerCase(s)
  595. #else
  596. #define fnc(s) s
  597. #endif
  598. //----------------------------------------------------------------------
  599. int cmCTestCoverageHandler::HandleGCovCoverage(
  600. cmCTestCoverageHandlerContainer* cont)
  601. {
  602. std::string gcovCommand
  603. = this->CTest->GetCTestConfiguration("CoverageCommand");
  604. // Style 1
  605. std::string st1gcovOutputRex1
  606. = "[0-9]+\\.[0-9]+% of [0-9]+ (source |)lines executed in file (.*)$";
  607. std::string st1gcovOutputRex2 = "^Creating (.*\\.gcov)\\.";
  608. cmsys::RegularExpression st1re1(st1gcovOutputRex1.c_str());
  609. cmsys::RegularExpression st1re2(st1gcovOutputRex2.c_str());
  610. // Style 2
  611. std::string st2gcovOutputRex1 = "^File *[`'](.*)'$";
  612. std::string st2gcovOutputRex2
  613. = "Lines executed: *[0-9]+\\.[0-9]+% of [0-9]+$";
  614. std::string st2gcovOutputRex3 = "^(.*):creating [`'](.*\\.gcov)'";
  615. std::string st2gcovOutputRex4 = "^(.*):unexpected EOF *$";
  616. std::string st2gcovOutputRex5 = "^(.*):cannot open source file*$";
  617. std::string st2gcovOutputRex6
  618. = "^(.*):source file is newer than graph file `(.*)'$";
  619. cmsys::RegularExpression st2re1(st2gcovOutputRex1.c_str());
  620. cmsys::RegularExpression st2re2(st2gcovOutputRex2.c_str());
  621. cmsys::RegularExpression st2re3(st2gcovOutputRex3.c_str());
  622. cmsys::RegularExpression st2re4(st2gcovOutputRex4.c_str());
  623. cmsys::RegularExpression st2re5(st2gcovOutputRex5.c_str());
  624. cmsys::RegularExpression st2re6(st2gcovOutputRex6.c_str());
  625. cmsys::Glob gl;
  626. gl.RecurseOn();
  627. gl.RecurseThroughSymlinksOff();
  628. std::string daGlob = cont->BinaryDir + "/*.da";
  629. gl.FindFiles(daGlob);
  630. std::vector<std::string> files = gl.GetFiles();
  631. daGlob = cont->BinaryDir + "/*.gcda";
  632. gl.FindFiles(daGlob);
  633. std::vector<std::string>& moreFiles = gl.GetFiles();
  634. files.insert(files.end(), moreFiles.begin(), moreFiles.end());
  635. std::vector<std::string>::iterator it;
  636. if ( files.size() == 0 )
  637. {
  638. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  639. " Cannot find any GCov coverage files."
  640. << std::endl);
  641. // No coverage files is a valid thing, so the exit code is 0
  642. return 0;
  643. }
  644. std::string testingDir = this->CTest->GetBinaryDir() + "/Testing";
  645. std::string tempDir = testingDir + "/CoverageInfo";
  646. std::string currentDirectory = cmSystemTools::GetCurrentWorkingDirectory();
  647. cmSystemTools::MakeDirectory(tempDir.c_str());
  648. cmSystemTools::ChangeDirectory(tempDir.c_str());
  649. int gcovStyle = 0;
  650. std::set<std::string> missingFiles;
  651. std::string actualSourceFile = "";
  652. cmCTestLog(this->CTest, HANDLER_OUTPUT,
  653. " Processing coverage (each . represents one file):" << std::endl);
  654. cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
  655. int file_count = 0;
  656. // make sure output from gcov is in English!
  657. cmSystemTools::PutEnv("LC_ALL=POSIX");
  658. for ( it = files.begin(); it != files.end(); ++ it )
  659. {
  660. cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
  661. std::string fileDir = cmSystemTools::GetFilenamePath(it->c_str());
  662. std::string command = "\"" + gcovCommand + "\" -l -o \"" + fileDir
  663. + "\" \"" + *it + "\"";
  664. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, command.c_str()
  665. << std::endl);
  666. std::string output = "";
  667. std::string errors = "";
  668. int retVal = 0;
  669. *cont->OFS << "* Run coverage for: " << fileDir.c_str() << std::endl;
  670. *cont->OFS << " Command: " << command.c_str() << std::endl;
  671. int res = this->CTest->RunCommand(command.c_str(), &output, &errors,
  672. &retVal, tempDir.c_str(), 0 /*this->TimeOut*/);
  673. *cont->OFS << " Output: " << output.c_str() << std::endl;
  674. *cont->OFS << " Errors: " << errors.c_str() << std::endl;
  675. if ( ! res )
  676. {
  677. cmCTestLog(this->CTest, ERROR_MESSAGE,
  678. "Problem running coverage on file: " << it->c_str() << std::endl);
  679. cmCTestLog(this->CTest, ERROR_MESSAGE,
  680. "Command produced error: " << errors << std::endl);
  681. cont->Error ++;
  682. continue;
  683. }
  684. if ( retVal != 0 )
  685. {
  686. cmCTestLog(this->CTest, ERROR_MESSAGE, "Coverage command returned: "
  687. << retVal << " while processing: " << it->c_str() << std::endl);
  688. cmCTestLog(this->CTest, ERROR_MESSAGE,
  689. "Command produced error: " << cont->Error << std::endl);
  690. }
  691. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  692. "--------------------------------------------------------------"
  693. << std::endl
  694. << output << std::endl
  695. << "--------------------------------------------------------------"
  696. << std::endl);
  697. std::vector<cmStdString> lines;
  698. std::vector<cmStdString>::iterator line;
  699. // Globals for storing current source file and current gcov file;
  700. cmSystemTools::Split(output.c_str(), lines);
  701. for ( line = lines.begin(); line != lines.end(); ++line)
  702. {
  703. std::string sourceFile;
  704. std::string gcovFile;
  705. cmCTestLog(this->CTest, DEBUG, "Line: [" << line->c_str() << "]"
  706. << std::endl);
  707. if ( line->size() == 0 )
  708. {
  709. // Ignore empty line; probably style 2
  710. }
  711. else if ( st1re1.find(line->c_str()) )
  712. {
  713. if ( gcovStyle != 0 )
  714. {
  715. if ( gcovStyle != 1 )
  716. {
  717. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  718. << std::endl);
  719. cont->Error ++;
  720. break;
  721. }
  722. gcovStyle = 1;
  723. }
  724. actualSourceFile = "";
  725. sourceFile = st1re1.match(2);
  726. }
  727. else if ( st1re2.find(line->c_str() ) )
  728. {
  729. if ( gcovStyle != 0 )
  730. {
  731. if ( gcovStyle != 1 )
  732. {
  733. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  734. << std::endl);
  735. cont->Error ++;
  736. break;
  737. }
  738. gcovStyle = 1;
  739. }
  740. gcovFile = st1re2.match(1);
  741. }
  742. else if ( st2re1.find(line->c_str() ) )
  743. {
  744. if ( gcovStyle != 0 )
  745. {
  746. if ( gcovStyle != 2 )
  747. {
  748. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  749. << std::endl);
  750. cont->Error ++;
  751. break;
  752. }
  753. gcovStyle = 2;
  754. }
  755. actualSourceFile = "";
  756. sourceFile = st2re1.match(1);
  757. }
  758. else if ( st2re2.find(line->c_str() ) )
  759. {
  760. if ( gcovStyle != 0 )
  761. {
  762. if ( gcovStyle != 2 )
  763. {
  764. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  765. << std::endl);
  766. cont->Error ++;
  767. break;
  768. }
  769. gcovStyle = 2;
  770. }
  771. }
  772. else if ( st2re3.find(line->c_str() ) )
  773. {
  774. if ( gcovStyle != 0 )
  775. {
  776. if ( gcovStyle != 2 )
  777. {
  778. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  779. << std::endl);
  780. cont->Error ++;
  781. break;
  782. }
  783. gcovStyle = 2;
  784. }
  785. gcovFile = st2re3.match(2);
  786. }
  787. else if ( st2re4.find(line->c_str() ) )
  788. {
  789. if ( gcovStyle != 0 )
  790. {
  791. if ( gcovStyle != 2 )
  792. {
  793. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  794. << std::endl);
  795. cont->Error ++;
  796. break;
  797. }
  798. gcovStyle = 2;
  799. }
  800. cmCTestLog(this->CTest, WARNING, "Warning: " << st2re4.match(1)
  801. << " had unexpected EOF" << std::endl);
  802. }
  803. else if ( st2re5.find(line->c_str() ) )
  804. {
  805. if ( gcovStyle != 0 )
  806. {
  807. if ( gcovStyle != 2 )
  808. {
  809. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  810. << std::endl);
  811. cont->Error ++;
  812. break;
  813. }
  814. gcovStyle = 2;
  815. }
  816. cmCTestLog(this->CTest, WARNING, "Warning: Cannot open file: "
  817. << st2re5.match(1) << std::endl);
  818. }
  819. else if ( st2re6.find(line->c_str() ) )
  820. {
  821. if ( gcovStyle != 0 )
  822. {
  823. if ( gcovStyle != 2 )
  824. {
  825. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  826. << std::endl);
  827. cont->Error ++;
  828. break;
  829. }
  830. gcovStyle = 2;
  831. }
  832. cmCTestLog(this->CTest, WARNING, "Warning: File: " << st2re6.match(1)
  833. << " is newer than " << st2re6.match(2) << std::endl);
  834. }
  835. else
  836. {
  837. cmCTestLog(this->CTest, ERROR_MESSAGE,
  838. "Unknown line: [" << line->c_str() << "]" << std::endl);
  839. cont->Error ++;
  840. //abort();
  841. }
  842. if ( !gcovFile.empty() && actualSourceFile.size() )
  843. {
  844. cmCTestCoverageHandlerContainer::SingleFileCoverageVector* vec
  845. = &cont->TotalCoverage[actualSourceFile];
  846. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " in file: "
  847. << gcovFile << std::endl);
  848. std::ifstream ifile(gcovFile.c_str());
  849. if ( ! ifile )
  850. {
  851. cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open file: "
  852. << gcovFile << std::endl);
  853. }
  854. else
  855. {
  856. long cnt = -1;
  857. std::string nl;
  858. while ( cmSystemTools::GetLineFromStream(ifile, nl) )
  859. {
  860. cnt ++;
  861. //TODO: Handle gcov 3.0 non-coverage lines
  862. // Skip empty lines
  863. if ( !nl.size() )
  864. {
  865. continue;
  866. }
  867. // Skip unused lines
  868. if ( nl.size() < 12 )
  869. {
  870. continue;
  871. }
  872. // Read the coverage count from the beginning of the gcov output
  873. // line
  874. std::string prefix = nl.substr(0, 12);
  875. int cov = atoi(prefix.c_str());
  876. // Read the line number starting at the 10th character of the gcov
  877. // output line
  878. std::string lineNumber = nl.substr(10, 5);
  879. int lineIdx = atoi(lineNumber.c_str())-1;
  880. if ( lineIdx >= 0 )
  881. {
  882. while ( vec->size() <=
  883. static_cast<size_t>(lineIdx) )
  884. {
  885. vec->push_back(-1);
  886. }
  887. // Initially all entries are -1 (not used). If we get coverage
  888. // information, increment it to 0 first.
  889. if ( (*vec)[lineIdx] < 0 )
  890. {
  891. if ( cov > 0 || prefix.find("#") != prefix.npos )
  892. {
  893. (*vec)[lineIdx] = 0;
  894. }
  895. }
  896. (*vec)[lineIdx] += cov;
  897. }
  898. }
  899. }
  900. actualSourceFile = "";
  901. }
  902. if ( !sourceFile.empty() && actualSourceFile.empty() )
  903. {
  904. gcovFile = "";
  905. // Is it in the source dir?
  906. if ( sourceFile.size() > cont->SourceDir.size() &&
  907. (fnc(sourceFile.substr(0, cont->SourceDir.size())) ==
  908. fnc(cont->SourceDir)) &&
  909. sourceFile[cont->SourceDir.size()] == '/' )
  910. {
  911. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " produced s: "
  912. << sourceFile.c_str() << std::endl);
  913. *cont->OFS << " produced in source dir: " << sourceFile.c_str()
  914. << std::endl;
  915. actualSourceFile
  916. = cmSystemTools::CollapseFullPath(sourceFile.c_str());
  917. }
  918. // Binary dir?
  919. if ( sourceFile.size() > cont->BinaryDir.size() &&
  920. (fnc(sourceFile.substr(0, cont->BinaryDir.size())) ==
  921. fnc(cont->BinaryDir)) &&
  922. sourceFile[cont->BinaryDir.size()] == '/' )
  923. {
  924. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " produced b: "
  925. << sourceFile.c_str() << std::endl);
  926. *cont->OFS << " produced in binary dir: " << sourceFile.c_str()
  927. << std::endl;
  928. actualSourceFile
  929. = cmSystemTools::CollapseFullPath(sourceFile.c_str());
  930. }
  931. if ( actualSourceFile.empty() )
  932. {
  933. if ( missingFiles.find(actualSourceFile) == missingFiles.end() )
  934. {
  935. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  936. "Something went wrong" << std::endl);
  937. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  938. "Cannot find file: ["
  939. << sourceFile.c_str() << "]" << std::endl);
  940. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  941. " in source dir: ["
  942. << cont->SourceDir.c_str() << "]"
  943. << std::endl);
  944. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  945. " or binary dir: ["
  946. << cont->BinaryDir.size() << "]"
  947. << std::endl);
  948. *cont->OFS << " Something went wrong. Cannot find file: "
  949. << sourceFile.c_str()
  950. << " in source dir: " << cont->SourceDir.c_str()
  951. << " or binary dir: " << cont->BinaryDir.c_str() << std::endl;
  952. missingFiles.insert(actualSourceFile);
  953. }
  954. }
  955. }
  956. }
  957. file_count ++;
  958. if ( file_count % 50 == 0 )
  959. {
  960. cmCTestLog(this->CTest, HANDLER_OUTPUT, " processed: " << file_count
  961. << " out of " << files.size() << std::endl);
  962. cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
  963. }
  964. }
  965. cmSystemTools::ChangeDirectory(currentDirectory.c_str());
  966. return file_count;
  967. }
  968. //----------------------------------------------------------------------
  969. int cmCTestCoverageHandler::HandleTracePyCoverage(
  970. cmCTestCoverageHandlerContainer* cont)
  971. {
  972. cmsys::Glob gl;
  973. gl.RecurseOn();
  974. gl.RecurseThroughSymlinksOff();
  975. std::string daGlob = cont->BinaryDir + "/*.cover";
  976. gl.FindFiles(daGlob);
  977. std::vector<std::string> files = gl.GetFiles();
  978. if ( files.size() == 0 )
  979. {
  980. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  981. " Cannot find any Python Trace.py coverage files."
  982. << std::endl);
  983. // No coverage files is a valid thing, so the exit code is 0
  984. return 0;
  985. }
  986. std::string testingDir = this->CTest->GetBinaryDir() + "/Testing";
  987. std::string tempDir = testingDir + "/CoverageInfo";
  988. std::string currentDirectory = cmSystemTools::GetCurrentWorkingDirectory();
  989. cmSystemTools::MakeDirectory(tempDir.c_str());
  990. cmSystemTools::ChangeDirectory(tempDir.c_str());
  991. cmSystemTools::ChangeDirectory(currentDirectory.c_str());
  992. std::vector<std::string>::iterator fileIt;
  993. int file_count = 0;
  994. for ( fileIt = files.begin(); fileIt != files.end(); ++ fileIt )
  995. {
  996. std::string fileName = this->FindFile(cont, *fileIt);
  997. if ( fileName.empty() )
  998. {
  999. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1000. "Cannot find source Python file corresponding to: "
  1001. << fileIt->c_str() << std::endl);
  1002. continue;
  1003. }
  1004. std::string actualSourceFile
  1005. = cmSystemTools::CollapseFullPath(fileName.c_str());
  1006. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1007. " Check coverage for file: " << actualSourceFile.c_str()
  1008. << std::endl);
  1009. cmCTestCoverageHandlerContainer::SingleFileCoverageVector* vec
  1010. = &cont->TotalCoverage[actualSourceFile];
  1011. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1012. " in file: " << fileIt->c_str() << std::endl);
  1013. std::ifstream ifile(fileIt->c_str());
  1014. if ( ! ifile )
  1015. {
  1016. cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open file: "
  1017. << fileIt->c_str() << std::endl);
  1018. }
  1019. else
  1020. {
  1021. long cnt = -1;
  1022. std::string nl;
  1023. while ( cmSystemTools::GetLineFromStream(ifile, nl) )
  1024. {
  1025. cnt ++;
  1026. // Skip empty lines
  1027. if ( !nl.size() )
  1028. {
  1029. continue;
  1030. }
  1031. // Skip unused lines
  1032. if ( nl.size() < 12 )
  1033. {
  1034. continue;
  1035. }
  1036. // Read the coverage count from the beginning of the Trace.py output
  1037. // line
  1038. std::string prefix = nl.substr(0, 6);
  1039. if ( prefix[5] != ' ' && prefix[5] != ':' )
  1040. {
  1041. // This is a hack. We should really do something more elaborate
  1042. prefix = nl.substr(0, 7);
  1043. if ( prefix[6] != ' ' && prefix[6] != ':' )
  1044. {
  1045. prefix = nl.substr(0, 8);
  1046. if ( prefix[7] != ' ' && prefix[7] != ':' )
  1047. {
  1048. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1049. "Currently the limit is maximum coverage of 999999"
  1050. << std::endl);
  1051. }
  1052. }
  1053. }
  1054. int cov = atoi(prefix.c_str());
  1055. if ( prefix[prefix.size()-1] != ':' )
  1056. {
  1057. // This line does not have ':' so no coverage here. That said,
  1058. // Trace.py does not handle not covered lines versus comments etc.
  1059. // So, this will be set to 0.
  1060. cov = 0;
  1061. }
  1062. cmCTestLog(this->CTest, DEBUG, "Prefix: " << prefix.c_str()
  1063. << " cov: " << cov
  1064. << std::endl);
  1065. // Read the line number starting at the 10th character of the gcov
  1066. // output line
  1067. int lineIdx = cnt;
  1068. if ( lineIdx >= 0 )
  1069. {
  1070. while ( vec->size() <=
  1071. static_cast<size_t>(lineIdx) )
  1072. {
  1073. vec->push_back(-1);
  1074. }
  1075. // Initially all entries are -1 (not used). If we get coverage
  1076. // information, increment it to 0 first.
  1077. if ( (*vec)[lineIdx] < 0 )
  1078. {
  1079. if ( cov >= 0 )
  1080. {
  1081. (*vec)[lineIdx] = 0;
  1082. }
  1083. }
  1084. (*vec)[lineIdx] += cov;
  1085. }
  1086. }
  1087. }
  1088. ++ file_count;
  1089. }
  1090. cmSystemTools::ChangeDirectory(currentDirectory.c_str());
  1091. return file_count;
  1092. }
  1093. //----------------------------------------------------------------------
  1094. std::string cmCTestCoverageHandler::FindFile(
  1095. cmCTestCoverageHandlerContainer* cont,
  1096. std::string fileName)
  1097. {
  1098. std::string fileNameNoE
  1099. = cmSystemTools::GetFilenameWithoutLastExtension(fileName);
  1100. // First check in source and binary directory
  1101. std::string fullName = cont->SourceDir + "/" + fileNameNoE + ".py";
  1102. if ( cmSystemTools::FileExists(fullName.c_str()) )
  1103. {
  1104. return fullName;
  1105. }
  1106. fullName = cont->BinaryDir + "/" + fileNameNoE + ".py";
  1107. if ( cmSystemTools::FileExists(fullName.c_str()) )
  1108. {
  1109. return fullName;
  1110. }
  1111. return "";
  1112. }
  1113. // This is a header put on each marked up source file
  1114. namespace
  1115. {
  1116. const char* bullseyeHelp[] =
  1117. {" Coverage produced by bullseye covbr tool: ",
  1118. " www.bullseye.com/help/ref_covbr.html",
  1119. " * An arrow --> indicates incomplete coverage.",
  1120. " * An X indicates a function that was invoked, a switch label that ",
  1121. " was exercised, a try-block that finished, or an exception handler ",
  1122. " that was invoked.",
  1123. " * A T or F indicates a boolean decision that evaluated true or false,",
  1124. " respectively.",
  1125. " * A t or f indicates a boolean condition within a decision if the ",
  1126. " condition evaluated true or false, respectively.",
  1127. " * A k indicates a constant decision or condition.",
  1128. " * The slash / means this probe is excluded from summary results. ",
  1129. 0};
  1130. }
  1131. //----------------------------------------------------------------------
  1132. int cmCTestCoverageHandler::RunBullseyeCoverageBranch(
  1133. cmCTestCoverageHandlerContainer* cont,
  1134. std::set<cmStdString>& coveredFileNames,
  1135. std::vector<std::string>& files,
  1136. std::vector<std::string>& filesFullPath)
  1137. {
  1138. if(files.size() != filesFullPath.size())
  1139. {
  1140. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1141. "Files and full path files not the same size?:\n");
  1142. return 0;
  1143. }
  1144. // create the output stream for the CoverageLog-N.xml file
  1145. cmGeneratedFileStream covLogFile;
  1146. int logFileCount = 0;
  1147. if ( !this->StartCoverageLogFile(covLogFile, logFileCount) )
  1148. {
  1149. return -1;
  1150. }
  1151. // for each file run covbr on that file to get the coverage
  1152. // information for that file
  1153. std::string outputFile;
  1154. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1155. "run covbr: "
  1156. << std::endl);
  1157. if(!this->RunBullseyeCommand(cont, "covbr", 0, outputFile))
  1158. {
  1159. cmCTestLog(this->CTest, ERROR_MESSAGE, "error running covbr for." << "\n");
  1160. return -1;
  1161. }
  1162. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1163. "covbr output in " << outputFile
  1164. << std::endl);
  1165. // open the output file
  1166. std::ifstream fin(outputFile.c_str());
  1167. if(!fin)
  1168. {
  1169. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1170. "Cannot open coverage file: " <<
  1171. outputFile.c_str() << std::endl);
  1172. return 0;
  1173. }
  1174. std::map<cmStdString, cmStdString> fileMap;
  1175. std::vector<std::string>::iterator fp = filesFullPath.begin();
  1176. for(std::vector<std::string>::iterator f = files.begin();
  1177. f != files.end(); ++f, ++fp)
  1178. {
  1179. fileMap[*f] = *fp;
  1180. }
  1181. int count =0; // keep count of the number of files
  1182. // Now parse each line from the bullseye cov log file
  1183. std::string lineIn;
  1184. bool valid = false; // are we in a valid output file
  1185. int line = 0; // line of the current file
  1186. cmStdString file;
  1187. while(cmSystemTools::GetLineFromStream(fin, lineIn))
  1188. {
  1189. bool startFile = false;
  1190. if(lineIn.size() > 1 && lineIn[lineIn.size()-1] == ':')
  1191. {
  1192. file = lineIn.substr(0, lineIn.size()-1);
  1193. if(coveredFileNames.find(file) != coveredFileNames.end())
  1194. {
  1195. startFile = true;
  1196. }
  1197. }
  1198. if(startFile)
  1199. {
  1200. // if we are in a valid file close it because a new one started
  1201. if(valid)
  1202. {
  1203. covLogFile << "\t\t</Report>" << std::endl
  1204. << "\t</File>" << std::endl;
  1205. }
  1206. // only allow 100 files in each log file
  1207. if ( count != 0 && count % 100 == 0 )
  1208. {
  1209. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1210. "start a new log file: "
  1211. << count
  1212. << std::endl);
  1213. this->EndCoverageLogFile(covLogFile, logFileCount);
  1214. logFileCount ++;
  1215. if ( !this->StartCoverageLogFile(covLogFile, logFileCount) )
  1216. {
  1217. return -1;
  1218. }
  1219. count++; // move on one
  1220. }
  1221. std::map<cmStdString, cmStdString>::iterator
  1222. i = fileMap.find(file);
  1223. // if the file should be covered write out the header for that file
  1224. if(i != fileMap.end())
  1225. {
  1226. // we have a new file so count it in the output
  1227. count++;
  1228. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1229. "Produce coverage for file: "
  1230. << file.c_str() << " " << count
  1231. << std::endl);
  1232. // start the file output
  1233. covLogFile << "\t<File Name=\""
  1234. << this->CTest->MakeXMLSafe(i->first.c_str())
  1235. << "\" FullPath=\"" << this->CTest->MakeXMLSafe(
  1236. this->CTest->GetShortPathToFile(
  1237. i->second.c_str())) << "\">" << std::endl
  1238. << "\t\t<Report>" << std::endl;
  1239. // write the bullseye header
  1240. line =0;
  1241. for(int k =0; bullseyeHelp[k] != 0; ++k)
  1242. {
  1243. covLogFile << "\t\t<Line Number=\"" << line << "\" Count=\"-1\">"
  1244. << this->CTest->MakeXMLSafe(bullseyeHelp[k])
  1245. << "</Line>" << std::endl;
  1246. line++;
  1247. }
  1248. valid = true; // we are in a valid file section
  1249. }
  1250. else
  1251. {
  1252. // this is not a file that we want coverage for
  1253. valid = false;
  1254. }
  1255. }
  1256. // we are not at a start file, and we are in a valid file output the line
  1257. else if(valid)
  1258. {
  1259. covLogFile << "\t\t<Line Number=\"" << line << "\" Count=\"-1\">"
  1260. << this->CTest->MakeXMLSafe(lineIn.c_str())
  1261. << "</Line>" << std::endl;
  1262. line++;
  1263. }
  1264. }
  1265. // if we ran out of lines a valid file then close that file
  1266. if(valid)
  1267. {
  1268. covLogFile << "\t\t</Report>" << std::endl
  1269. << "\t</File>" << std::endl;
  1270. }
  1271. this->EndCoverageLogFile(covLogFile, logFileCount);
  1272. return 1;
  1273. }
  1274. //----------------------------------------------------------------------
  1275. int cmCTestCoverageHandler::RunBullseyeCommand(
  1276. cmCTestCoverageHandlerContainer* cont,
  1277. const char* cmd,
  1278. const char* arg,
  1279. std::string& outputFile)
  1280. {
  1281. std::string program = cmSystemTools::FindProgram(cmd);
  1282. if(program.size() == 0)
  1283. {
  1284. cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find :" << cmd << "\n");
  1285. return 0;
  1286. }
  1287. if(arg)
  1288. {
  1289. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1290. "Run : " << program.c_str() << " " << arg << "\n");
  1291. }
  1292. else
  1293. {
  1294. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1295. "Run : " << program.c_str() << "\n");
  1296. }
  1297. // create a process object and start it
  1298. cmCTestRunProcess runCoverageSrc;
  1299. runCoverageSrc.SetCommand(program.c_str());
  1300. runCoverageSrc.AddArgument(arg);
  1301. std::string stdoutFile = cont->BinaryDir + "/Testing/Temporary/";
  1302. stdoutFile += this->GetCTestInstance()->GetCurrentTag();
  1303. stdoutFile += "-";
  1304. stdoutFile += cmd;
  1305. std::string stderrFile = stdoutFile;
  1306. stdoutFile += ".stdout";
  1307. stderrFile += ".stderr";
  1308. runCoverageSrc.SetStdoutFile(stdoutFile.c_str());
  1309. runCoverageSrc.SetStderrFile(stderrFile.c_str());
  1310. if(!runCoverageSrc.StartProcess())
  1311. {
  1312. cmCTestLog(this->CTest, ERROR_MESSAGE, "Could not run : "
  1313. << program.c_str() << " " << arg << "\n"
  1314. << "kwsys process state : "
  1315. << runCoverageSrc.GetProcessState());
  1316. return 0;
  1317. }
  1318. // since we set the output file names wait for it to end
  1319. runCoverageSrc.WaitForExit();
  1320. outputFile = stdoutFile;
  1321. return 1;
  1322. }
  1323. //----------------------------------------------------------------------
  1324. int cmCTestCoverageHandler::RunBullseyeSourceSummary(
  1325. cmCTestCoverageHandlerContainer* cont)
  1326. {
  1327. // Run the covsrc command and create a temp outputfile
  1328. std::string outputFile;
  1329. if(!this->RunBullseyeCommand(cont, "covsrc", "-c", outputFile))
  1330. {
  1331. cmCTestLog(this->CTest, ERROR_MESSAGE, "error running covsrc:\n");
  1332. return 0;
  1333. }
  1334. std::ostream& tmpLog = *cont->OFS;
  1335. // copen the Coverage.xml file in the Testing directory
  1336. cmGeneratedFileStream covSumFile;
  1337. if(!this->StartResultingXML(cmCTest::PartCoverage, "Coverage", covSumFile))
  1338. {
  1339. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1340. "Cannot open coverage summary file." << std::endl);
  1341. return 0;
  1342. }
  1343. this->CTest->StartXML(covSumFile, this->AppendXML);
  1344. double elapsed_time_start = cmSystemTools::GetTime();
  1345. std::string coverage_start_time = this->CTest->CurrentTime();
  1346. covSumFile << "<Coverage>" << std::endl
  1347. << "\t<StartDateTime>"
  1348. << coverage_start_time << "</StartDateTime>"
  1349. << std::endl
  1350. << "\t<StartTime>"
  1351. << static_cast<unsigned int>(cmSystemTools::GetTime())
  1352. << "</StartTime>"
  1353. << std::endl;
  1354. std::string stdline;
  1355. std::string errline;
  1356. // expected output:
  1357. // first line is:
  1358. // "Source","Function Coverage","out of","%","C/D Coverage","out of","%"
  1359. // after that data follows in that format
  1360. std::string sourceFile;
  1361. int functionsCalled = 0;
  1362. int totalFunctions = 0;
  1363. int percentFunction = 0;
  1364. int branchCovered = 0;
  1365. int totalBranches = 0;
  1366. int percentBranch = 0;
  1367. double total_tested = 0;
  1368. double total_untested = 0;
  1369. double total_functions = 0;
  1370. double percent_coverage =0;
  1371. double number_files = 0;
  1372. std::vector<std::string> coveredFiles;
  1373. std::vector<std::string> coveredFilesFullPath;
  1374. // Read and parse the summary output file
  1375. std::ifstream fin(outputFile.c_str());
  1376. if(!fin)
  1377. {
  1378. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1379. "Cannot open coverage summary file: " <<
  1380. outputFile.c_str() << std::endl);
  1381. return 0;
  1382. }
  1383. std::set<cmStdString> coveredFileNames;
  1384. while(cmSystemTools::GetLineFromStream(fin, stdline))
  1385. {
  1386. // if we have a line of output from stdout
  1387. if(stdline.size())
  1388. {
  1389. // parse the comma separated output
  1390. this->ParseBullsEyeCovsrcLine(stdline,
  1391. sourceFile,
  1392. functionsCalled,
  1393. totalFunctions,
  1394. percentFunction,
  1395. branchCovered,
  1396. totalBranches,
  1397. percentBranch);
  1398. // The first line is the header
  1399. if(sourceFile == "Source" || sourceFile == "Total")
  1400. {
  1401. continue;
  1402. }
  1403. std::string file = sourceFile;
  1404. coveredFileNames.insert(file);
  1405. if(!cmSystemTools::FileIsFullPath(sourceFile.c_str()))
  1406. {
  1407. // file will be relative to the binary dir
  1408. file = cont->BinaryDir;
  1409. file += "/";
  1410. file += sourceFile;
  1411. }
  1412. file = cmSystemTools::CollapseFullPath(file.c_str());
  1413. bool shouldIDoCoverage
  1414. = this->ShouldIDoCoverage(file.c_str(),
  1415. cont->SourceDir.c_str(),
  1416. cont->BinaryDir.c_str());
  1417. if ( !shouldIDoCoverage )
  1418. {
  1419. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1420. ".NoDartCoverage found, so skip coverage check for: "
  1421. << file.c_str()
  1422. << std::endl);
  1423. continue;
  1424. }
  1425. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1426. "Doing coverage for: "
  1427. << file.c_str()
  1428. << std::endl);
  1429. coveredFiles.push_back(sourceFile);
  1430. coveredFilesFullPath.push_back(file);
  1431. number_files++;
  1432. total_functions += totalFunctions;
  1433. total_tested += functionsCalled;
  1434. total_untested += (totalFunctions - functionsCalled);
  1435. std::string fileName = cmSystemTools::GetFilenameName(file.c_str());
  1436. float cper = percentBranch + percentFunction;
  1437. if(totalBranches > 0)
  1438. {
  1439. cper /= 2.0f;
  1440. }
  1441. percent_coverage += cper;
  1442. float cmet = percentFunction + percentBranch;
  1443. if(totalBranches > 0)
  1444. {
  1445. cmet /= 2.0f;
  1446. }
  1447. cmet /= 100.0f;
  1448. tmpLog << stdline.c_str() << "\n";
  1449. tmpLog << fileName << "\n";
  1450. tmpLog << "functionsCalled: " << functionsCalled/100 << "\n";
  1451. tmpLog << "totalFunctions: " << totalFunctions/100 << "\n";
  1452. tmpLog << "percentFunction: " << percentFunction << "\n";
  1453. tmpLog << "branchCovered: " << branchCovered << "\n";
  1454. tmpLog << "totalBranches: " << totalBranches << "\n";
  1455. tmpLog << "percentBranch: " << percentBranch << "\n";
  1456. tmpLog << "percentCoverage: " << percent_coverage << "\n";
  1457. tmpLog << "coverage metric: " << cmet << "\n";
  1458. covSumFile << "\t<File Name=\"" << this->CTest->MakeXMLSafe(sourceFile)
  1459. << "\" FullPath=\"" << this->CTest->MakeXMLSafe(
  1460. this->CTest->GetShortPathToFile(file.c_str()))
  1461. << "\" Covered=\"" << (cmet>0?"true":"false") << "\">\n"
  1462. << "\t\t<BranchesTested>"
  1463. << branchCovered
  1464. << "</BranchesTested>\n"
  1465. << "\t\t<BranchesUnTested>"
  1466. << totalBranches - branchCovered
  1467. << "</BranchesUnTested>\n"
  1468. << "\t\t<FunctionsTested>"
  1469. << functionsCalled
  1470. << "</FunctionsTested>\n"
  1471. << "\t\t<FunctionsUnTested>"
  1472. << totalFunctions - functionsCalled
  1473. << "</FunctionsUnTested>\n"
  1474. // Hack for conversion of function to loc assume a function
  1475. // has 100 lines of code
  1476. << "\t\t<LOCTested>" << functionsCalled *100
  1477. << "</LOCTested>\n"
  1478. << "\t\t<LOCUnTested>"
  1479. << (totalFunctions - functionsCalled)*100
  1480. << "</LOCUnTested>\n"
  1481. << "\t\t<PercentCoverage>";
  1482. covSumFile.setf(std::ios::fixed, std::ios::floatfield);
  1483. covSumFile.precision(2);
  1484. covSumFile << (cper) << "</PercentCoverage>\n"
  1485. << "\t\t<CoverageMetric>";
  1486. covSumFile.setf(std::ios::fixed, std::ios::floatfield);
  1487. covSumFile.precision(2);
  1488. covSumFile << (cmet) << "</CoverageMetric>\n"
  1489. << "\t</File>" << std::endl;
  1490. }
  1491. }
  1492. std::string end_time = this->CTest->CurrentTime();
  1493. covSumFile << "\t<LOCTested>" << total_tested << "</LOCTested>\n"
  1494. << "\t<LOCUntested>" << total_untested << "</LOCUntested>\n"
  1495. << "\t<LOC>" << total_functions << "</LOC>\n"
  1496. << "\t<PercentCoverage>";
  1497. covSumFile.setf(std::ios::fixed, std::ios::floatfield);
  1498. covSumFile.precision(2);
  1499. covSumFile
  1500. << SAFEDIV(percent_coverage,number_files)<< "</PercentCoverage>\n"
  1501. << "\t<EndDateTime>" << end_time << "</EndDateTime>\n"
  1502. << "\t<EndTime>" << static_cast<unsigned int>(cmSystemTools::GetTime())
  1503. << "</EndTime>\n";
  1504. covSumFile
  1505. << "<ElapsedMinutes>" <<
  1506. static_cast<int>((cmSystemTools::GetTime() - elapsed_time_start)/6)/10.0
  1507. << "</ElapsedMinutes>"
  1508. << "</Coverage>" << std::endl;
  1509. this->CTest->EndXML(covSumFile);
  1510. // Now create the coverage information for each file
  1511. return this->RunBullseyeCoverageBranch(cont,
  1512. coveredFileNames,
  1513. coveredFiles,
  1514. coveredFilesFullPath);
  1515. }
  1516. //----------------------------------------------------------------------
  1517. int cmCTestCoverageHandler::HandleBullseyeCoverage(
  1518. cmCTestCoverageHandlerContainer* cont)
  1519. {
  1520. const char* covfile = cmSystemTools::GetEnv("COVFILE");
  1521. if(!covfile)
  1522. {
  1523. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1524. " COVFILE environment variable not found, not running "
  1525. " bullseye\n");
  1526. return 0;
  1527. }
  1528. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1529. " run covsrc with COVFILE=["
  1530. << covfile
  1531. << "]" << std::endl);
  1532. if(!this->RunBullseyeSourceSummary(cont))
  1533. {
  1534. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1535. "Error running bullseye summary.\n");
  1536. return 0;
  1537. }
  1538. cmCTestLog(this->CTest, DEBUG, "HandleBullseyeCoverage return 1 "
  1539. << std::endl);
  1540. return 1;
  1541. }
  1542. bool cmCTestCoverageHandler::GetNextInt(std::string const& inputLine,
  1543. std::string::size_type& pos,
  1544. int& value)
  1545. {
  1546. std::string::size_type start = pos;
  1547. pos = inputLine.find(',', start);
  1548. value = atoi(inputLine.substr(start, pos).c_str());
  1549. if(pos == inputLine.npos)
  1550. {
  1551. return true;
  1552. }
  1553. pos++;
  1554. return true;
  1555. }
  1556. bool cmCTestCoverageHandler::ParseBullsEyeCovsrcLine(
  1557. std::string const& inputLine,
  1558. std::string& sourceFile,
  1559. int& functionsCalled,
  1560. int& totalFunctions,
  1561. int& percentFunction,
  1562. int& branchCovered,
  1563. int& totalBranches,
  1564. int& percentBranch)
  1565. {
  1566. // find the first comma
  1567. std::string::size_type pos = inputLine.find(',');
  1568. if(pos == inputLine.npos)
  1569. {
  1570. cmCTestLog(this->CTest, ERROR_MESSAGE, "Error parsing string : "
  1571. << inputLine.c_str() << "\n");
  1572. return false;
  1573. }
  1574. // the source file has "" around it so extract out the file name
  1575. sourceFile = inputLine.substr(1,pos-2);
  1576. pos++;
  1577. if(!this->GetNextInt(inputLine, pos, functionsCalled))
  1578. {
  1579. return false;
  1580. }
  1581. if(!this->GetNextInt(inputLine, pos, totalFunctions))
  1582. {
  1583. return false;
  1584. }
  1585. if(!this->GetNextInt(inputLine, pos, percentFunction))
  1586. {
  1587. return false;
  1588. }
  1589. if(!this->GetNextInt(inputLine, pos, branchCovered))
  1590. {
  1591. return false;
  1592. }
  1593. if(!this->GetNextInt(inputLine, pos, totalBranches))
  1594. {
  1595. return false;
  1596. }
  1597. if(!this->GetNextInt(inputLine, pos, percentBranch))
  1598. {
  1599. return false;
  1600. }
  1601. // should be at the end now
  1602. if(pos != inputLine.npos)
  1603. {
  1604. cmCTestLog(this->CTest, ERROR_MESSAGE, "Error parsing input : "
  1605. << inputLine.c_str() << " last pos not npos = " << pos <<
  1606. "\n");
  1607. }
  1608. return true;
  1609. }