cmCTestCoverageHandler.cxx 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849
  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. //----------------------------------------------------------------------
  26. cmCTestCoverageHandler::cmCTestCoverageHandler()
  27. {
  28. }
  29. //----------------------------------------------------------------------
  30. void cmCTestCoverageHandler::Initialize()
  31. {
  32. this->Superclass::Initialize();
  33. this->CustomCoverageExclude.empty();
  34. }
  35. //----------------------------------------------------------------------
  36. bool cmCTestCoverageHandler::StartCoverageLogFile(
  37. cmGeneratedFileStream& covLogFile, int logFileCount)
  38. {
  39. char covLogFilename[1024];
  40. sprintf(covLogFilename, "CoverageLog-%d", logFileCount);
  41. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Open file: "
  42. << covLogFilename << std::endl);
  43. if (!this->StartResultingXML(covLogFilename, covLogFile) )
  44. {
  45. cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open log file: "
  46. << covLogFilename << std::endl);
  47. return false;
  48. }
  49. std::string local_start_time = this->CTest->CurrentTime();
  50. this->CTest->StartXML(covLogFile);
  51. covLogFile << "<CoverageLog>" << std::endl
  52. << "\t<StartDateTime>" << local_start_time << "</StartDateTime>"
  53. << std::endl;
  54. return true;
  55. }
  56. //----------------------------------------------------------------------
  57. void cmCTestCoverageHandler::EndCoverageLogFile(cmGeneratedFileStream& ostr,
  58. int logFileCount)
  59. {
  60. std::string local_end_time = this->CTest->CurrentTime();
  61. ostr << "\t<EndDateTime>" << local_end_time << "</EndDateTime>" << std::endl
  62. << "</CoverageLog>" << std::endl;
  63. this->CTest->EndXML(ostr);
  64. char covLogFilename[1024];
  65. sprintf(covLogFilename, "CoverageLog-%d.xml", logFileCount);
  66. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Close file: "
  67. << covLogFilename << std::endl);
  68. ostr.Close();
  69. }
  70. //----------------------------------------------------------------------
  71. bool cmCTestCoverageHandler::ShouldIDoCoverage(const char* file,
  72. const char* srcDir,
  73. const char* binDir)
  74. {
  75. std::vector<cmsys::RegularExpression>::iterator sit;
  76. for ( sit = this->CustomCoverageExcludeRegex.begin();
  77. sit != this->CustomCoverageExcludeRegex.end(); ++ sit )
  78. {
  79. if ( sit->find(file) )
  80. {
  81. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " File " << file
  82. << " is excluded in CTestCustom.ctest" << std::endl;);
  83. return false;
  84. }
  85. }
  86. std::string fSrcDir = cmSystemTools::CollapseFullPath(srcDir);
  87. std::string fBinDir = cmSystemTools::CollapseFullPath(binDir);
  88. std::string fFile = cmSystemTools::CollapseFullPath(file);
  89. bool sourceSubDir = cmSystemTools::IsSubDirectory(fFile.c_str(),
  90. fSrcDir.c_str());
  91. bool buildSubDir = cmSystemTools::IsSubDirectory(fFile.c_str(),
  92. fBinDir.c_str());
  93. // Always check parent directory of the file.
  94. std::string fileDir = cmSystemTools::GetFilenamePath(fFile.c_str());
  95. std::string checkDir;
  96. // We also need to check the binary/source directory pair.
  97. if ( sourceSubDir && buildSubDir )
  98. {
  99. if ( fSrcDir.size() > fBinDir.size() )
  100. {
  101. checkDir = fSrcDir;
  102. }
  103. else
  104. {
  105. checkDir = fBinDir;
  106. }
  107. }
  108. else if ( sourceSubDir )
  109. {
  110. checkDir = fSrcDir;
  111. }
  112. else if ( buildSubDir )
  113. {
  114. checkDir = fBinDir;
  115. }
  116. std::string ndc
  117. = cmSystemTools::FileExistsInParentDirectories(".NoDartCoverage",
  118. fFile.c_str(), checkDir.c_str());
  119. if ( ndc.size() )
  120. {
  121. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Found: " << ndc.c_str()
  122. << " so skip coverage of " << file << std::endl);
  123. return false;
  124. }
  125. // By now checkDir should be set to parent directory of the file.
  126. // Get the relative path to the file an apply it to the opposite directory.
  127. // If it is the same as fileDir, then ignore, otherwise check.
  128. std::string relPath = cmSystemTools::RelativePath(checkDir.c_str(),
  129. fFile.c_str());
  130. if ( checkDir == fSrcDir )
  131. {
  132. checkDir = fBinDir;
  133. }
  134. else
  135. {
  136. checkDir = fSrcDir;
  137. }
  138. fFile = checkDir + "/" + relPath;
  139. fFile = cmSystemTools::GetFilenamePath(fFile.c_str());
  140. if ( fileDir == fFile )
  141. {
  142. // This is in-source build, so we trust the previous check.
  143. return true;
  144. }
  145. ndc = cmSystemTools::FileExistsInParentDirectories(".NoDartCoverage",
  146. fFile.c_str(), checkDir.c_str());
  147. if ( ndc.size() )
  148. {
  149. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Found: " << ndc.c_str()
  150. << " so skip coverage of: " << file << std::endl);
  151. return false;
  152. }
  153. // Ok, nothing in source tree, nothing in binary tree
  154. return true;
  155. }
  156. //----------------------------------------------------------------------
  157. //clearly it would be nice if this were broken up into a few smaller
  158. //functions and commented...
  159. int cmCTestCoverageHandler::ProcessHandler()
  160. {
  161. int error = 0;
  162. std::string sourceDir
  163. = this->CTest->GetCTestConfiguration("SourceDirectory");
  164. std::string binaryDir
  165. = this->CTest->GetCTestConfiguration("BuildDirectory");
  166. std::string gcovCommand
  167. = this->CTest->GetCTestConfiguration("CoverageCommand");
  168. cmGeneratedFileStream ofs;
  169. double elapsed_time_start = cmSystemTools::GetTime();
  170. if ( !this->StartLogFile("Coverage", ofs) )
  171. {
  172. cmCTestLog(this->CTest, ERROR_MESSAGE,
  173. "Cannot create LastCoverage.log file" << std::endl);
  174. }
  175. ofs << "Performing coverage: " << elapsed_time_start << std::endl;
  176. cmSystemTools::ConvertToUnixSlashes(sourceDir);
  177. cmSystemTools::ConvertToUnixSlashes(binaryDir);
  178. std::string asfGlob = sourceDir + "/*";
  179. std::string abfGlob = binaryDir + "/*";
  180. // Style 1
  181. std::string st1gcovOutputRex1
  182. = "[0-9]+\\.[0-9]+% of [0-9]+ (source |)lines executed in file (.*)$";
  183. std::string st1gcovOutputRex2 = "^Creating (.*\\.gcov)\\.";
  184. cmsys::RegularExpression st1re1(st1gcovOutputRex1.c_str());
  185. cmsys::RegularExpression st1re2(st1gcovOutputRex2.c_str());
  186. // Style 2
  187. std::string st2gcovOutputRex1 = "^File *[`'](.*)'$";
  188. std::string st2gcovOutputRex2
  189. = "Lines executed: *[0-9]+\\.[0-9]+% of [0-9]+$";
  190. std::string st2gcovOutputRex3 = "^(.*):creating [`'](.*\\.gcov)'";
  191. std::string st2gcovOutputRex4 = "^(.*):unexpected EOF *$";
  192. std::string st2gcovOutputRex5 = "^(.*):cannot open source file*$";
  193. std::string st2gcovOutputRex6
  194. = "^(.*):source file is newer than graph file `(.*)'$";
  195. cmsys::RegularExpression st2re1(st2gcovOutputRex1.c_str());
  196. cmsys::RegularExpression st2re2(st2gcovOutputRex2.c_str());
  197. cmsys::RegularExpression st2re3(st2gcovOutputRex3.c_str());
  198. cmsys::RegularExpression st2re4(st2gcovOutputRex4.c_str());
  199. cmsys::RegularExpression st2re5(st2gcovOutputRex5.c_str());
  200. cmsys::RegularExpression st2re6(st2gcovOutputRex6.c_str());
  201. cmCTestLog(this->CTest, HANDLER_OUTPUT, "Performing coverage" << std::endl);
  202. std::string coverage_start_time = this->CTest->CurrentTime();
  203. std::string testingDir = this->CTest->GetBinaryDir() + "/Testing";
  204. std::string tempDir = testingDir + "/CoverageInfo";
  205. std::string currentDirectory = cmSystemTools::GetCurrentWorkingDirectory();
  206. cmSystemTools::MakeDirectory(tempDir.c_str());
  207. cmSystemTools::ChangeDirectory(tempDir.c_str());
  208. cmsys::Glob gl;
  209. gl.RecurseOn();
  210. std::string daGlob = binaryDir + "/*.da";
  211. gl.FindFiles(daGlob);
  212. std::vector<std::string> files = gl.GetFiles();
  213. daGlob = binaryDir + "/*.gcda";
  214. gl.FindFiles(daGlob);
  215. std::vector<std::string>& moreFiles = gl.GetFiles();
  216. files.insert(files.end(), moreFiles.begin(), moreFiles.end());
  217. std::vector<std::string>::iterator it;
  218. if ( files.size() == 0 )
  219. {
  220. cmCTestLog(this->CTest, ERROR_MESSAGE, " Cannot find any coverage files."
  221. << std::endl);
  222. // No coverage files is a valid thing, so the exit code is 0
  223. return 0;
  224. }
  225. this->CustomCoverageExcludeRegex.empty();
  226. std::vector<cmStdString>::iterator rexIt;
  227. for ( rexIt = this->CustomCoverageExclude.begin();
  228. rexIt != this->CustomCoverageExclude.end();
  229. ++ rexIt )
  230. {
  231. this->CustomCoverageExcludeRegex.push_back(
  232. cmsys::RegularExpression(rexIt->c_str()));
  233. }
  234. typedef std::vector<int> singleFileCoverageVector;
  235. typedef std::map<std::string, singleFileCoverageVector> totalCoverageMap;
  236. totalCoverageMap totalCoverage;
  237. int gcovStyle = 0;
  238. std::set<std::string> missingFiles;
  239. std::string actualSourceFile = "";
  240. cmCTestLog(this->CTest, HANDLER_OUTPUT,
  241. " Processing coverage (each . represents one file):" << std::endl);
  242. cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
  243. int file_count = 0;
  244. for ( it = files.begin(); it != files.end(); ++ it )
  245. {
  246. cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
  247. std::string fileDir = cmSystemTools::GetFilenamePath(it->c_str());
  248. std::string command = "\"" + gcovCommand + "\" -l -o \"" + fileDir
  249. + "\" \"" + *it + "\"";
  250. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, command.c_str()
  251. << std::endl);
  252. std::string output = "";
  253. std::string errors = "";
  254. int retVal = 0;
  255. ofs << "* Run coverage for: " << fileDir.c_str() << std::endl;
  256. ofs << " Command: " << command.c_str() << std::endl;
  257. int res = this->CTest->RunCommand(command.c_str(), &output, &errors,
  258. &retVal, tempDir.c_str(), 0 /*this->TimeOut*/);
  259. ofs << " Output: " << output.c_str() << std::endl;
  260. ofs << " Errors: " << errors.c_str() << std::endl;
  261. if ( ! res )
  262. {
  263. cmCTestLog(this->CTest, ERROR_MESSAGE,
  264. "Problem running coverage on file: " << it->c_str() << std::endl);
  265. cmCTestLog(this->CTest, ERROR_MESSAGE,
  266. "Command produced error: " << errors << std::endl);
  267. error ++;
  268. continue;
  269. }
  270. if ( retVal != 0 )
  271. {
  272. cmCTestLog(this->CTest, ERROR_MESSAGE, "Coverage command returned: "
  273. << retVal << " while processing: " << it->c_str() << std::endl);
  274. cmCTestLog(this->CTest, ERROR_MESSAGE,
  275. "Command produced error: " << error << std::endl);
  276. }
  277. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  278. "--------------------------------------------------------------"
  279. << std::endl
  280. << output << std::endl
  281. << "--------------------------------------------------------------"
  282. << std::endl);
  283. std::vector<cmStdString> lines;
  284. std::vector<cmStdString>::iterator line;
  285. // Globals for storing current source file and current gcov file;
  286. cmSystemTools::Split(output.c_str(), lines);
  287. for ( line = lines.begin(); line != lines.end(); ++line)
  288. {
  289. std::string sourceFile;
  290. std::string gcovFile;
  291. cmCTestLog(this->CTest, DEBUG, "Line: [" << line->c_str() << "]"
  292. << std::endl);
  293. if ( line->size() == 0 )
  294. {
  295. // Ignore empty line; probably style 2
  296. }
  297. else if ( st1re1.find(line->c_str()) )
  298. {
  299. if ( gcovStyle != 0 )
  300. {
  301. if ( gcovStyle != 1 )
  302. {
  303. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  304. << std::endl);
  305. error ++;
  306. break;
  307. }
  308. gcovStyle = 1;
  309. }
  310. actualSourceFile = "";
  311. sourceFile = st1re1.match(2);
  312. }
  313. else if ( st1re2.find(line->c_str() ) )
  314. {
  315. if ( gcovStyle != 0 )
  316. {
  317. if ( gcovStyle != 1 )
  318. {
  319. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  320. << std::endl);
  321. error ++;
  322. break;
  323. }
  324. gcovStyle = 1;
  325. }
  326. gcovFile = st1re2.match(1);
  327. }
  328. else if ( st2re1.find(line->c_str() ) )
  329. {
  330. if ( gcovStyle != 0 )
  331. {
  332. if ( gcovStyle != 2 )
  333. {
  334. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  335. << std::endl);
  336. error ++;
  337. break;
  338. }
  339. gcovStyle = 2;
  340. }
  341. actualSourceFile = "";
  342. sourceFile = st2re1.match(1);
  343. }
  344. else if ( st2re2.find(line->c_str() ) )
  345. {
  346. if ( gcovStyle != 0 )
  347. {
  348. if ( gcovStyle != 2 )
  349. {
  350. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  351. << std::endl);
  352. error ++;
  353. break;
  354. }
  355. gcovStyle = 2;
  356. }
  357. }
  358. else if ( st2re3.find(line->c_str() ) )
  359. {
  360. if ( gcovStyle != 0 )
  361. {
  362. if ( gcovStyle != 2 )
  363. {
  364. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  365. << std::endl);
  366. error ++;
  367. break;
  368. }
  369. gcovStyle = 2;
  370. }
  371. gcovFile = st2re3.match(2);
  372. }
  373. else if ( st2re4.find(line->c_str() ) )
  374. {
  375. if ( gcovStyle != 0 )
  376. {
  377. if ( gcovStyle != 2 )
  378. {
  379. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  380. << std::endl);
  381. error ++;
  382. break;
  383. }
  384. gcovStyle = 2;
  385. }
  386. cmCTestLog(this->CTest, WARNING, "Warning: " << st2re4.match(1)
  387. << " had unexpected EOF" << std::endl);
  388. }
  389. else if ( st2re5.find(line->c_str() ) )
  390. {
  391. if ( gcovStyle != 0 )
  392. {
  393. if ( gcovStyle != 2 )
  394. {
  395. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  396. << std::endl);
  397. error ++;
  398. break;
  399. }
  400. gcovStyle = 2;
  401. }
  402. cmCTestLog(this->CTest, WARNING, "Warning: Cannot open file: "
  403. << st2re5.match(1) << std::endl);
  404. }
  405. else if ( st2re6.find(line->c_str() ) )
  406. {
  407. if ( gcovStyle != 0 )
  408. {
  409. if ( gcovStyle != 2 )
  410. {
  411. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  412. << std::endl);
  413. error ++;
  414. break;
  415. }
  416. gcovStyle = 2;
  417. }
  418. cmCTestLog(this->CTest, WARNING, "Warning: File: " << st2re6.match(1)
  419. << " is newer than " << st2re6.match(2) << std::endl);
  420. }
  421. else
  422. {
  423. cmCTestLog(this->CTest, ERROR_MESSAGE,
  424. "Unknown line: [" << line->c_str() << "]" << std::endl);
  425. error ++;
  426. //abort();
  427. }
  428. if ( !gcovFile.empty() && actualSourceFile.size() )
  429. {
  430. singleFileCoverageVector* vec = &totalCoverage[actualSourceFile];
  431. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " in file: "
  432. << gcovFile << std::endl);
  433. std::ifstream ifile(gcovFile.c_str());
  434. if ( ! ifile )
  435. {
  436. cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open file: "
  437. << gcovFile << std::endl);
  438. }
  439. else
  440. {
  441. long cnt = -1;
  442. std::string nl;
  443. while ( cmSystemTools::GetLineFromStream(ifile, nl) )
  444. {
  445. cnt ++;
  446. //TODO: Handle gcov 3.0 non-coverage lines
  447. // Skip empty lines
  448. if ( !nl.size() )
  449. {
  450. continue;
  451. }
  452. // Skip unused lines
  453. if ( nl.size() < 12 )
  454. {
  455. continue;
  456. }
  457. // Read the coverage count from the beginning of the gcov output
  458. // line
  459. std::string prefix = nl.substr(0, 12);
  460. int cov = atoi(prefix.c_str());
  461. // Read the line number starting at the 10th character of the gcov
  462. // output line
  463. std::string lineNumber = nl.substr(10, 5);
  464. int lineIdx = atoi(lineNumber.c_str())-1;
  465. if ( lineIdx >= 0 )
  466. {
  467. while ( vec->size() <=
  468. static_cast<singleFileCoverageVector::size_type>(lineIdx) )
  469. {
  470. vec->push_back(-1);
  471. }
  472. // Initially all entries are -1 (not used). If we get coverage
  473. // information, increment it to 0 first.
  474. if ( (*vec)[lineIdx] < 0 )
  475. {
  476. if ( cov > 0 || prefix.find("#") != prefix.npos )
  477. {
  478. (*vec)[lineIdx] = 0;
  479. }
  480. }
  481. (*vec)[lineIdx] += cov;
  482. }
  483. }
  484. }
  485. actualSourceFile = "";
  486. }
  487. if ( !sourceFile.empty() && actualSourceFile.empty() )
  488. {
  489. gcovFile = "";
  490. // Is it in the source dir?
  491. if ( sourceFile.size() > sourceDir.size() &&
  492. sourceFile.substr(0, sourceDir.size()) == sourceDir &&
  493. sourceFile[sourceDir.size()] == '/' )
  494. {
  495. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " produced s: "
  496. << sourceFile.c_str() << std::endl);
  497. ofs << " produced in source dir: " << sourceFile.c_str()
  498. << std::endl;
  499. actualSourceFile
  500. = cmSystemTools::CollapseFullPath(sourceFile.c_str());
  501. }
  502. // Binary dir?
  503. if ( sourceFile.size() > binaryDir.size() &&
  504. sourceFile.substr(0, binaryDir.size()) == binaryDir &&
  505. sourceFile[binaryDir.size()] == '/' )
  506. {
  507. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " produced b: "
  508. << sourceFile.c_str() << std::endl);
  509. ofs << " produced in binary dir: " << sourceFile.c_str()
  510. << std::endl;
  511. actualSourceFile
  512. = cmSystemTools::CollapseFullPath(sourceFile.c_str());
  513. }
  514. if ( actualSourceFile.empty() )
  515. {
  516. if ( missingFiles.find(actualSourceFile) == missingFiles.end() )
  517. {
  518. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  519. "Something went wrong" << std::endl);
  520. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "File: ["
  521. << sourceFile.c_str() << "]" << std::endl);
  522. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "s: ["
  523. << sourceFile.substr(0, sourceDir.size()) << "]" << std::endl);
  524. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "b: ["
  525. << sourceFile.substr(0, binaryDir.size()) << "]" << std::endl);
  526. ofs << " Something went wrong. Cannot find: "
  527. << sourceFile.c_str()
  528. << " in source dir: " << sourceDir.c_str()
  529. << " or binary dir: " << binaryDir.c_str() << std::endl;
  530. missingFiles.insert(actualSourceFile);
  531. }
  532. }
  533. }
  534. }
  535. file_count ++;
  536. if ( file_count % 50 == 0 )
  537. {
  538. cmCTestLog(this->CTest, HANDLER_OUTPUT, " processed: " << file_count
  539. << " out of " << files.size() << std::endl);
  540. cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
  541. }
  542. }
  543. cmGeneratedFileStream covSumFile;
  544. cmGeneratedFileStream covLogFile;
  545. if (!this->StartResultingXML("Coverage", covSumFile))
  546. {
  547. cmCTestLog(this->CTest, ERROR_MESSAGE,
  548. "Cannot open coverage summary file." << std::endl);
  549. return -1;
  550. }
  551. this->CTest->StartXML(covSumFile);
  552. // Produce output xml files
  553. covSumFile << "<Coverage>" << std::endl
  554. << "\t<StartDateTime>" << coverage_start_time << "</StartDateTime>"
  555. << std::endl;
  556. int logFileCount = 0;
  557. if ( !this->StartCoverageLogFile(covLogFile, logFileCount) )
  558. {
  559. return -1;
  560. }
  561. totalCoverageMap::iterator fileIterator;
  562. int cnt = 0;
  563. long total_tested = 0;
  564. long total_untested = 0;
  565. //std::string fullSourceDir = sourceDir + "/";
  566. //std::string fullBinaryDir = binaryDir + "/";
  567. cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl);
  568. cmCTestLog(this->CTest, HANDLER_OUTPUT,
  569. " Acumulating results (each . represents one file):" << std::endl);
  570. cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
  571. std::vector<std::string> errorsWhileAccumulating;
  572. file_count = 0;
  573. for ( fileIterator = totalCoverage.begin();
  574. fileIterator != totalCoverage.end();
  575. ++fileIterator )
  576. {
  577. cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
  578. file_count ++;
  579. if ( file_count % 50 == 0 )
  580. {
  581. cmCTestLog(this->CTest, HANDLER_OUTPUT, " processed: " << file_count
  582. << " out of "
  583. << totalCoverage.size() << std::endl);
  584. cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
  585. }
  586. if ( cnt % 100 == 0 )
  587. {
  588. this->EndCoverageLogFile(covLogFile, logFileCount);
  589. logFileCount ++;
  590. if ( !this->StartCoverageLogFile(covLogFile, logFileCount) )
  591. {
  592. return -1;
  593. }
  594. }
  595. const std::string fullFileName = fileIterator->first;
  596. const std::string fileName
  597. = cmSystemTools::GetFilenameName(fullFileName.c_str());
  598. std::string fullFilePath
  599. = cmSystemTools::GetFilenamePath(fullFileName.c_str());
  600. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Process file: "
  601. << fullFileName << std::endl);
  602. cmSystemTools::ConvertToUnixSlashes(fullFilePath);
  603. if ( !cmSystemTools::FileExists(fullFileName.c_str()) )
  604. {
  605. cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find file: "
  606. << fullFileName.c_str() << std::endl);
  607. continue;
  608. }
  609. bool shouldIDoCoverage
  610. = this->ShouldIDoCoverage(fullFileName.c_str(),
  611. sourceDir.c_str(), binaryDir.c_str());
  612. if ( !shouldIDoCoverage )
  613. {
  614. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  615. ".NoDartCoverage found, so skip coverage check for: "
  616. << fullFileName.c_str()
  617. << std::endl);
  618. continue;
  619. }
  620. const singleFileCoverageVector& fcov = fileIterator->second;
  621. covLogFile << "\t<File Name=\""
  622. << this->CTest->MakeXMLSafe(fileName.c_str())
  623. << "\" FullPath=\"" << this->CTest->MakeXMLSafe(
  624. this->CTest->GetShortPathToFile(
  625. fileIterator->first.c_str())) << "\">" << std::endl
  626. << "\t\t<Report>" << std::endl;
  627. std::ifstream ifs(fullFileName.c_str());
  628. if ( !ifs)
  629. {
  630. cmOStringStream ostr;
  631. ostr << "Cannot open source file: " << fullFileName.c_str();
  632. errorsWhileAccumulating.push_back(ostr.str());
  633. error ++;
  634. continue;
  635. }
  636. int tested = 0;
  637. int untested = 0;
  638. singleFileCoverageVector::size_type cc;
  639. std::string line;
  640. for ( cc= 0; cc < fcov.size(); cc ++ )
  641. {
  642. if ( !cmSystemTools::GetLineFromStream(ifs, line) )
  643. {
  644. cmOStringStream ostr;
  645. ostr << "Problem reading source file: " << fullFileName.c_str()
  646. << " line:" << cc;
  647. errorsWhileAccumulating.push_back(ostr.str());
  648. error ++;
  649. break;
  650. }
  651. covLogFile << "\t\t<Line Number=\"" << cc << "\" Count=\"" << fcov[cc]
  652. << "\">"
  653. << this->CTest->MakeXMLSafe(line.c_str()) << "</Line>" << std::endl;
  654. if ( fcov[cc] == 0 )
  655. {
  656. untested ++;
  657. }
  658. else if ( fcov[cc] > 0 )
  659. {
  660. tested ++;
  661. }
  662. }
  663. if ( cmSystemTools::GetLineFromStream(ifs, line) )
  664. {
  665. cmOStringStream ostr;
  666. ostr << "Looks like there are more lines in the file: " << line;
  667. errorsWhileAccumulating.push_back(ostr.str());
  668. }
  669. float cper = 0;
  670. float cmet = 0;
  671. if ( tested + untested > 0 )
  672. {
  673. cper = (100 * SAFEDIV(static_cast<float>(tested),
  674. static_cast<float>(tested + untested)));
  675. cmet = ( SAFEDIV(static_cast<float>(tested + 10),
  676. static_cast<float>(tested + untested + 10)));
  677. }
  678. total_tested += tested;
  679. total_untested += untested;
  680. covLogFile << "\t\t</Report>" << std::endl
  681. << "\t</File>" << std::endl;
  682. covSumFile << "\t<File Name=\"" << this->CTest->MakeXMLSafe(fileName)
  683. << "\" FullPath=\"" << this->CTest->MakeXMLSafe(
  684. this->CTest->GetShortPathToFile(fullFileName.c_str()))
  685. << "\" Covered=\"" << (cmet>0?"true":"false") << "\">\n"
  686. << "\t\t<LOCTested>" << tested << "</LOCTested>\n"
  687. << "\t\t<LOCUnTested>" << untested << "</LOCUnTested>\n"
  688. << "\t\t<PercentCoverage>";
  689. covSumFile.setf(std::ios::fixed, std::ios::floatfield);
  690. covSumFile.precision(2);
  691. covSumFile << (cper) << "</PercentCoverage>\n"
  692. << "\t\t<CoverageMetric>";
  693. covSumFile.setf(std::ios::fixed, std::ios::floatfield);
  694. covSumFile.precision(2);
  695. covSumFile << (cmet) << "</CoverageMetric>\n"
  696. << "\t</File>" << std::endl;
  697. cnt ++;
  698. }
  699. this->EndCoverageLogFile(covLogFile, logFileCount);
  700. if ( errorsWhileAccumulating.size() > 0 )
  701. {
  702. cmCTestLog(this->CTest, ERROR_MESSAGE, std::endl);
  703. cmCTestLog(this->CTest, ERROR_MESSAGE,
  704. "Error(s) while acumulating results:" << std::endl);
  705. std::vector<std::string>::iterator erIt;
  706. for ( erIt = errorsWhileAccumulating.begin();
  707. erIt != errorsWhileAccumulating.end();
  708. ++ erIt )
  709. {
  710. cmCTestLog(this->CTest, ERROR_MESSAGE,
  711. " " << erIt->c_str() << std::endl);
  712. }
  713. }
  714. int total_lines = total_tested + total_untested;
  715. float percent_coverage = 100 * SAFEDIV(static_cast<float>(total_tested),
  716. static_cast<float>(total_lines));
  717. if ( total_lines == 0 )
  718. {
  719. percent_coverage = 0;
  720. }
  721. std::string end_time = this->CTest->CurrentTime();
  722. covSumFile << "\t<LOCTested>" << total_tested << "</LOCTested>\n"
  723. << "\t<LOCUntested>" << total_untested << "</LOCUntested>\n"
  724. << "\t<LOC>" << total_lines << "</LOC>\n"
  725. << "\t<PercentCoverage>";
  726. covSumFile.setf(std::ios::fixed, std::ios::floatfield);
  727. covSumFile.precision(2);
  728. covSumFile << (percent_coverage)<< "</PercentCoverage>\n"
  729. << "\t<EndDateTime>" << end_time << "</EndDateTime>\n";
  730. covSumFile << "<ElapsedMinutes>" <<
  731. static_cast<int>((cmSystemTools::GetTime() - elapsed_time_start)/6)/10.0
  732. << "</ElapsedMinutes>"
  733. << "</Coverage>" << std::endl;
  734. this->CTest->EndXML(covSumFile);
  735. cmCTestLog(this->CTest, HANDLER_OUTPUT, "\tCovered LOC: "
  736. << total_tested << std::endl
  737. << "\tNot covered LOC: " << total_untested << std::endl
  738. << "\tTotal LOC: " << total_lines << std::endl
  739. << "\tPercentage Coverage: "
  740. << std::setiosflags(std::ios::fixed)
  741. << std::setprecision(2)
  742. << (percent_coverage) << "%" << std::endl);
  743. ofs << "\tCovered LOC: " << total_tested << std::endl
  744. << "\tNot covered LOC: " << total_untested << std::endl
  745. << "\tTotal LOC: " << total_lines << std::endl
  746. << "\tPercentage Coverage: "
  747. << std::setiosflags(std::ios::fixed)
  748. << std::setprecision(2)
  749. << (percent_coverage) << "%" << std::endl;
  750. cmSystemTools::ChangeDirectory(currentDirectory.c_str());
  751. if ( error )
  752. {
  753. return -1;
  754. }
  755. return 0;
  756. }
  757. //----------------------------------------------------------------------
  758. void cmCTestCoverageHandler::PopulateCustomVectors(cmMakefile *mf)
  759. {
  760. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  761. " Add coverage exclude regular expressions." << std::endl);
  762. this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_COVERAGE_EXCLUDE",
  763. this->CustomCoverageExclude);
  764. std::vector<cmStdString>::iterator it;
  765. for ( it = this->CustomCoverageExclude.begin();
  766. it != this->CustomCoverageExclude.end();
  767. ++ it )
  768. {
  769. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Add coverage exclude: "
  770. << it->c_str() << std::endl);
  771. }
  772. }