cmCTestCoverageHandler.cxx 27 KB

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