cmCTestCoverageHandler.cxx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  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 <cmsys/Process.h>
  17. #define SAFEDIV(x,y) (((y)!=0)?((x)/(y)):(0))
  18. //----------------------------------------------------------------------
  19. cmCTestCoverageHandler::cmCTestCoverageHandler()
  20. {
  21. m_Verbose = false;
  22. m_CTest = 0;
  23. }
  24. //----------------------------------------------------------------------
  25. //clearly it would be nice if this were broken up into a few smaller
  26. //functions and commented...
  27. int cmCTestCoverageHandler::CoverageDirectory(cmCTest *ctest_inst)
  28. {
  29. m_CTest = ctest_inst;
  30. std::cout << "Performing coverage" << std::endl;
  31. double elapsed_time_start = cmSystemTools::GetTime();
  32. cmCTest::tm_VectorOfStrings files;
  33. cmCTest::tm_VectorOfStrings cfiles;
  34. cmCTest::tm_VectorOfStrings cdirs;
  35. bool done = false;
  36. std::string::size_type cc;
  37. std::string glob;
  38. std::map<std::string, std::string> allsourcefiles;
  39. std::map<std::string, std::string> allbinaryfiles;
  40. std::string start_time = m_CTest->CurrentTime();
  41. // Find all source files.
  42. std::string sourceDirectory = m_CTest->GetDartConfiguration("SourceDirectory");
  43. if ( sourceDirectory.size() == 0 )
  44. {
  45. std::cerr << "Cannot find SourceDirectory key in the DartConfiguration.tcl" << std::endl;
  46. return 1;
  47. }
  48. std::string coverageCommand = m_CTest->GetDartConfiguration("CoverageCommand");
  49. if ( coverageCommand.size() == 0 )
  50. {
  51. std::cerr << "Coverage command not defined in DartConfiguration.tcl" << std::endl;
  52. return 1;
  53. }
  54. cdirs.push_back(sourceDirectory);
  55. while ( !done )
  56. {
  57. if ( cdirs.size() <= 0 )
  58. {
  59. break;
  60. }
  61. glob = cdirs[cdirs.size()-1] + "/*";
  62. //std::cout << "Glob: " << glob << std::endl;
  63. cdirs.pop_back();
  64. if ( cmSystemTools::SimpleGlob(glob, cfiles, 1) )
  65. {
  66. for ( cc = 0; cc < cfiles.size(); cc ++ )
  67. {
  68. allsourcefiles[cmSystemTools::GetFilenameName(cfiles[cc])] = cfiles[cc];
  69. }
  70. }
  71. if ( cmSystemTools::SimpleGlob(glob, cfiles, -1) )
  72. {
  73. for ( cc = 0; cc < cfiles.size(); cc ++ )
  74. {
  75. if ( cfiles[cc] != "." && cfiles[cc] != ".." )
  76. {
  77. cdirs.push_back(cfiles[cc]);
  78. }
  79. }
  80. }
  81. }
  82. // find all binary files
  83. cdirs.push_back(cmSystemTools::GetCurrentWorkingDirectory());
  84. while ( !done )
  85. {
  86. if ( cdirs.size() <= 0 )
  87. {
  88. break;
  89. }
  90. glob = cdirs[cdirs.size()-1] + "/*";
  91. //std::cout << "Glob: " << glob << std::endl;
  92. cdirs.pop_back();
  93. if ( cmSystemTools::SimpleGlob(glob, cfiles, 1) )
  94. {
  95. for ( cc = 0; cc < cfiles.size(); cc ++ )
  96. {
  97. allbinaryfiles[cmSystemTools::GetFilenameName(cfiles[cc])] = cfiles[cc];
  98. }
  99. }
  100. if ( cmSystemTools::SimpleGlob(glob, cfiles, -1) )
  101. {
  102. for ( cc = 0; cc < cfiles.size(); cc ++ )
  103. {
  104. if ( cfiles[cc] != "." && cfiles[cc] != ".." )
  105. {
  106. cdirs.push_back(cfiles[cc]);
  107. }
  108. }
  109. }
  110. }
  111. std::map<std::string, std::string>::iterator sit;
  112. for ( sit = allbinaryfiles.begin(); sit != allbinaryfiles.end(); sit ++ )
  113. {
  114. const std::string& fname = sit->second;
  115. //std::cout << "File: " << fname << std::endl;
  116. if ( strcmp(fname.substr(fname.size()-3, 3).c_str(), ".da") == 0 )
  117. {
  118. files.push_back(fname);
  119. }
  120. }
  121. if ( files.size() == 0 )
  122. {
  123. std::cerr << "Cannot find any coverage information files (.da)" << std::endl;
  124. return 1;
  125. }
  126. std::ofstream log;
  127. if (!m_CTest->OpenOutputFile("Temporary", "Coverage.log", log))
  128. {
  129. std::cerr << "Cannot open log file" << std::endl;
  130. return 1;
  131. }
  132. log.close();
  133. if (!m_CTest->OpenOutputFile(m_CTest->GetCurrentTag(), "Coverage.xml", log))
  134. {
  135. std::cerr << "Cannot open log file" << std::endl;
  136. return 1;
  137. }
  138. std::string opath = m_CTest->GetToplevelPath() + "/Testing/Temporary/Coverage";
  139. cmSystemTools::MakeDirectory(opath.c_str());
  140. cfiles.clear();
  141. cmCTest::tm_VectorOfStrings ncfiles;
  142. cmCTest::tm_VectorOfStrings missing_files;
  143. for ( cc = 0; cc < files.size(); cc ++ )
  144. {
  145. std::string currPath = cmSystemTools::GetFilenamePath(files[cc]);
  146. std::string command = coverageCommand + " -o \"" + currPath + "\" -l \"" + files[cc] + "\"";
  147. std::string output;
  148. int retVal = 0;
  149. if ( m_Verbose )
  150. {
  151. std::cerr << "Run gcov on " << files[cc] << " in directory: " << currPath.c_str() << std::endl;
  152. }
  153. //std::cout << " --- Run [" << command << "]" << std::endl;
  154. bool res = true;
  155. if ( !m_CTest->GetShowOnly() )
  156. {
  157. res = cmSystemTools::RunSingleCommand(command.c_str(), &output,
  158. &retVal, currPath.c_str(),
  159. m_Verbose, 0 /*m_TimeOut*/);
  160. }
  161. if ( res && retVal == 0 )
  162. {
  163. //std::cout << " - done" << std::endl;
  164. glob = currPath + "/*";
  165. if ( !cmSystemTools::SimpleGlob(glob, ncfiles, 1) )
  166. {
  167. std::cerr << "Cannot found any coverage files" << std::endl;
  168. return 1;
  169. }
  170. cfiles.insert(cfiles.end(), ncfiles.begin(), ncfiles.end());
  171. std::vector<cmStdString> gcovlines;
  172. cmSystemTools::Split(output.c_str(), gcovlines);
  173. std::vector<cmStdString>::iterator git;
  174. const char* message = "Could not open source file";
  175. for ( git = gcovlines.begin(); git != gcovlines.end(); ++git )
  176. {
  177. if ( strncmp(git->c_str(), message, strlen(message) ) == 0 )
  178. {
  179. std::cerr << "Problem: " << git->c_str() << std::endl;
  180. missing_files.push_back(git->c_str() + strlen(message));
  181. }
  182. }
  183. }
  184. else
  185. {
  186. std::cerr << "Run gcov on " << files[cc] << std::flush;
  187. std::cerr << " [" << command << "]" << std::endl;
  188. std::cerr << " - fail" << std::endl;
  189. }
  190. }
  191. files.clear();
  192. std::map<std::string, cmCTest::tm_VectorOfStrings > sourcefiles;
  193. for ( cc = 0; cc < cfiles.size(); cc ++ )
  194. {
  195. std::string& fname = cfiles[cc];
  196. // std::cout << "File: " << fname << std::endl;
  197. if ( strcmp(fname.substr(fname.size()-5, 5).c_str(), ".gcov") == 0 )
  198. {
  199. files.push_back(fname);
  200. std::string::size_type pos = fname.find(".da.");
  201. std::string::size_type pos2 = fname.find(".da##");
  202. if(pos2 != fname.npos)
  203. {
  204. pos = pos2+1;
  205. }
  206. if ( pos != fname.npos )
  207. {
  208. pos += 4;
  209. std::string::size_type epos = fname.size() - pos - strlen(".gcov");
  210. std::string nf = fname.substr(pos, epos);
  211. //std::cout << "Substring: " << nf << std::endl;
  212. if ( allsourcefiles.find(nf) != allsourcefiles.end() ||
  213. allbinaryfiles.find(nf) != allbinaryfiles.end() )
  214. {
  215. cmCTest::tm_VectorOfStrings &cvec = sourcefiles[nf];
  216. cvec.push_back(fname);
  217. }
  218. }
  219. }
  220. }
  221. // for ( cc = 0; cc < files.size(); cc ++ )
  222. // {
  223. // std::cout << "File: " << files[cc] << std::endl;
  224. // }
  225. if ( missing_files.size() > 0 )
  226. {
  227. std::cout << "---------------------------------------------------------------" << std::endl;
  228. std::cout << "The following files were missing:" << std::endl;
  229. for ( cc = 0; cc < missing_files.size(); cc ++ )
  230. {
  231. std::cout << "File: " << missing_files[cc] << std::endl;
  232. }
  233. std::cout << "---------------------------------------------------------------" << std::endl;
  234. }
  235. std::map<std::string, cmCTest::tm_VectorOfStrings >::iterator it;
  236. cmCTestCoverageHandler::tm_CoverageMap coverageresults;
  237. m_CTest->StartXML(log);
  238. log << "<Coverage>\n"
  239. << "\t<StartDateTime>" << start_time << "</StartDateTime>" << std::endl;
  240. int total_tested = 0;
  241. int total_untested = 0;
  242. for ( it = sourcefiles.begin(); it != sourcefiles.end(); it ++ )
  243. {
  244. //std::cerr << "Source file: " << it->first << std::endl;
  245. cmCTest::tm_VectorOfStrings &gfiles = it->second;
  246. for ( cc = 0; cc < gfiles.size(); cc ++ )
  247. {
  248. int do_coverage = 1;
  249. std::string coverage_dir = cmSystemTools::GetFilenamePath(gfiles[cc].c_str());
  250. std::string builDir = m_CTest->GetDartConfiguration("BuildDirectory");
  251. do
  252. {
  253. std::string coverage_file = coverage_dir + "/.NoDartCoverage";
  254. if ( cmSystemTools::FileExists(coverage_file.c_str()) )
  255. {
  256. do_coverage = 0;
  257. break;
  258. }
  259. // is there a parent directory we can check
  260. std::string::size_type pos = coverage_dir.rfind('/');
  261. // if we could not find the directory return 0
  262. if(pos == std::string::npos)
  263. {
  264. break;
  265. }
  266. coverage_dir = coverage_dir.substr(0, pos);
  267. }
  268. while (coverage_dir.size() >= builDir.size());
  269. if ( !do_coverage )
  270. {
  271. continue;
  272. }
  273. //std::cout << "\t" << gfiles[cc] << std::endl;
  274. std::ifstream ifile(gfiles[cc].c_str());
  275. if ( !ifile )
  276. {
  277. std::cerr << "Cannot open file: " << gfiles[cc].c_str() << std::endl;
  278. }
  279. ifile.seekg (0, std::ios::end);
  280. int length = ifile.tellg();
  281. ifile.seekg (0, std::ios::beg);
  282. char *buffer = new char [ length + 1 ];
  283. ifile.read(buffer, length);
  284. buffer [length] = 0;
  285. //std::cout << "Read: " << buffer << std::endl;
  286. std::vector<cmStdString> lines;
  287. cmSystemTools::Split(buffer, lines);
  288. delete [] buffer;
  289. cmCTestCoverageHandler::cmCTestCoverage& cov = coverageresults[it->first];
  290. std::vector<int>& covlines = cov.m_Lines;
  291. if ( cov.m_FullPath == "" )
  292. {
  293. covlines.insert(covlines.begin(), lines.size(), -1);
  294. if ( allsourcefiles.find(it->first) != allsourcefiles.end() )
  295. {
  296. cov.m_FullPath = allsourcefiles[it->first];
  297. }
  298. else if ( allbinaryfiles.find(it->first) != allbinaryfiles.end() )
  299. {
  300. cov.m_FullPath = allbinaryfiles[it->first];
  301. }
  302. cov.m_AbsolutePath = cov.m_FullPath;
  303. std::string src_dir = m_CTest->GetDartConfiguration("SourceDirectory");
  304. if ( src_dir[src_dir.size()-1] != '/' )
  305. {
  306. src_dir = src_dir + "/";
  307. }
  308. std::string::size_type spos = cov.m_FullPath.find(src_dir);
  309. if ( spos == 0 )
  310. {
  311. cov.m_FullPath = std::string("./") + cov.m_FullPath.substr(src_dir.size());
  312. }
  313. else
  314. {
  315. //std::cerr << "Compare -- " << cov.m_FullPath << std::endl;
  316. //std::cerr << " -- " << src_dir << std::endl;
  317. cov.m_Show = false;
  318. continue;
  319. }
  320. cov.m_Show = true;
  321. }
  322. std::string::size_type kk;
  323. // std::cerr << "number of lines " << lines.size() << "\n";
  324. for ( kk = 0; kk < lines.size(); kk ++ )
  325. {
  326. std::string& line = lines[kk];
  327. //std::cerr << line << "\n";
  328. std::string sub1 = line.substr(0, strlen(" #####"));
  329. std::string sub2 = line.substr(0, strlen(" ######"));
  330. int count = atoi(sub2.c_str());
  331. if ( sub1.compare(" #####") == 0 ||
  332. sub2.compare(" ######") == 0 )
  333. {
  334. if ( covlines[kk] == -1 )
  335. {
  336. covlines[kk] = 0;
  337. }
  338. cov.m_UnTested ++;
  339. //std::cout << "Untested - ";
  340. }
  341. else if ( count > 0 )
  342. {
  343. if ( covlines[kk] == -1 )
  344. {
  345. covlines[kk] = 0;
  346. }
  347. cov.m_Tested ++;
  348. covlines[kk] ++;
  349. //std::cout << "Tested[" << count << "] - ";
  350. }
  351. //std::cout << line << std::endl;
  352. }
  353. }
  354. }
  355. //std::cerr << "Finalizing" << std::endl;
  356. cmCTestCoverageHandler::tm_CoverageMap::iterator cit;
  357. int ccount = 0;
  358. std::ofstream cfileoutput;
  359. int cfileoutputcount = 0;
  360. char cfileoutputname[100];
  361. std::string local_start_time = m_CTest->CurrentTime();
  362. std::string local_end_time;
  363. for ( cit = coverageresults.begin(); cit != coverageresults.end(); cit ++ )
  364. {
  365. cmCTestCoverageHandler::cmCTestCoverage &cov = cit->second;
  366. if ( !cov.m_Show )
  367. {
  368. continue;
  369. }
  370. // Check if we should ignore the directory, if we find a NoDartCoverage
  371. // file in it or any of its parents
  372. int do_coverage = 1;
  373. std::string coverage_dir = cmSystemTools::GetFilenamePath(cov.m_AbsolutePath.c_str());
  374. do
  375. {
  376. std::string coverage_file = coverage_dir + "/.NoDartCoverage";
  377. if ( cmSystemTools::FileExists(coverage_file.c_str()) )
  378. {
  379. do_coverage = 0;
  380. break;
  381. }
  382. // is there a parent directory we can check
  383. std::string::size_type pos = coverage_dir.rfind('/');
  384. // if we could not find the directory return 0
  385. if(pos == std::string::npos)
  386. {
  387. break;
  388. }
  389. coverage_dir = coverage_dir.substr(0, pos);
  390. }
  391. while (coverage_dir.size() >= sourceDirectory.size());
  392. if (!do_coverage)
  393. {
  394. if ( m_Verbose )
  395. {
  396. std::cout << "Ignore file: " << cov.m_FullPath.c_str() << std::endl;
  397. }
  398. continue;
  399. }
  400. if ( ccount == 100 )
  401. {
  402. local_end_time = m_CTest->CurrentTime();
  403. cfileoutput << "\t<EndDateTime>" << local_end_time << "</EndDateTime>\n"
  404. << "</CoverageLog>" << std::endl;
  405. m_CTest->EndXML(cfileoutput);
  406. cfileoutput.close();
  407. std::cout << "Close file: " << cfileoutputname << std::endl;
  408. ccount = 0;
  409. }
  410. if ( ccount == 0 )
  411. {
  412. sprintf(cfileoutputname, "CoverageLog-%d.xml", cfileoutputcount++);
  413. std::cout << "Open file: " << cfileoutputname << std::endl;
  414. if (!m_CTest->OpenOutputFile(m_CTest->GetCurrentTag(),
  415. cfileoutputname, cfileoutput))
  416. {
  417. std::cerr << "Cannot open log file: " << cfileoutputname << std::endl;
  418. return 1;
  419. }
  420. local_start_time = m_CTest->CurrentTime();
  421. m_CTest->StartXML(cfileoutput);
  422. cfileoutput << "<CoverageLog>\n"
  423. << "\t<StartDateTime>" << local_start_time << "</StartDateTime>" << std::endl;
  424. }
  425. //std::cerr << "Final process of Source file: " << cit->first << std::endl;
  426. cov.m_UnTested = 0;
  427. cov.m_Tested = 0;
  428. for ( cc = 0; cc < cov.m_Lines.size(); cc ++ )
  429. {
  430. if ( cov.m_Lines[cc] == 0 )
  431. {
  432. cov.m_UnTested ++;
  433. }
  434. else if ( cov.m_Lines[cc] > 0 )
  435. {
  436. cov.m_Tested ++;
  437. }
  438. }
  439. std::ifstream ifile(cov.m_AbsolutePath.c_str());
  440. if ( !ifile )
  441. {
  442. std::cerr << "Cannot open file: " << cov.m_FullPath.c_str() << std::endl;
  443. }
  444. ifile.seekg (0, std::ios::end);
  445. int length = ifile.tellg();
  446. ifile.seekg (0, std::ios::beg);
  447. char *buffer = new char [ length + 1 ];
  448. ifile.read(buffer, length);
  449. buffer [length] = 0;
  450. //std::cout << "Read: " << buffer << std::endl;
  451. std::vector<cmStdString> lines;
  452. cmSystemTools::Split(buffer, lines);
  453. delete [] buffer;
  454. cfileoutput << "\t<File Name=\"" << cit->first << "\" FullPath=\""
  455. << cov.m_FullPath << "\">\n"
  456. << "\t\t<Report>" << std::endl;
  457. for ( cc = 0; cc < lines.size(); cc ++ )
  458. {
  459. cfileoutput << "\t\t<Line Number=\""
  460. << static_cast<int>(cc) << "\" Count=\""
  461. << cov.m_Lines[cc] << "\">"
  462. << cmCTest::MakeXMLSafe(lines[cc]) << "</Line>" << std::endl;
  463. }
  464. cfileoutput << "\t\t</Report>\n"
  465. << "\t</File>" << std::endl;
  466. total_tested += cov.m_Tested;
  467. total_untested += cov.m_UnTested;
  468. float cper = 0;
  469. float cmet = 0;
  470. if ( total_tested + total_untested > 0 && (cov.m_Tested + cov.m_UnTested) > 0)
  471. {
  472. cper = (100 * SAFEDIV(static_cast<float>(cov.m_Tested),
  473. static_cast<float>(cov.m_Tested + cov.m_UnTested)));
  474. cmet = ( SAFEDIV(static_cast<float>(cov.m_Tested + 10),
  475. static_cast<float>(cov.m_Tested + cov.m_UnTested + 10)));
  476. }
  477. log << "\t<File Name=\"" << cit->first << "\" FullPath=\"" << cov.m_FullPath
  478. << "\" Covered=\"" << (cmet>0?"true":"false") << "\">\n"
  479. << "\t\t<LOCTested>" << cov.m_Tested << "</LOCTested>\n"
  480. << "\t\t<LOCUnTested>" << cov.m_UnTested << "</LOCUnTested>\n"
  481. << "\t\t<PercentCoverage>";
  482. log.setf(std::ios::fixed, std::ios::floatfield);
  483. log.precision(2);
  484. log << (cper) << "</PercentCoverage>\n"
  485. << "\t\t<CoverageMetric>";
  486. log.setf(std::ios::fixed, std::ios::floatfield);
  487. log.precision(2);
  488. log << (cmet) << "</CoverageMetric>\n"
  489. << "\t</File>" << std::endl;
  490. ccount ++;
  491. }
  492. if ( ccount > 0 )
  493. {
  494. local_end_time = m_CTest->CurrentTime();
  495. cfileoutput << "\t<EndDateTime>" << local_end_time << "</EndDateTime>\n"
  496. << "</CoverageLog>" << std::endl;
  497. m_CTest->EndXML(cfileoutput);
  498. cfileoutput.close();
  499. }
  500. int total_lines = total_tested + total_untested;
  501. float percent_coverage = 100 * SAFEDIV(static_cast<float>(total_tested),
  502. static_cast<float>(total_lines));
  503. if ( total_lines == 0 )
  504. {
  505. percent_coverage = 0;
  506. }
  507. std::string end_time = m_CTest->CurrentTime();
  508. log << "\t<LOCTested>" << total_tested << "</LOCTested>\n"
  509. << "\t<LOCUntested>" << total_untested << "</LOCUntested>\n"
  510. << "\t<LOC>" << total_lines << "</LOC>\n"
  511. << "\t<PercentCoverage>";
  512. log.setf(std::ios::fixed, std::ios::floatfield);
  513. log.precision(2);
  514. log << (percent_coverage)<< "</PercentCoverage>\n"
  515. << "\t<EndDateTime>" << end_time << "</EndDateTime>\n";
  516. log << "<ElapsedMinutes>" <<
  517. static_cast<int>((cmSystemTools::GetTime() - elapsed_time_start)/6)/10.0
  518. << "</ElapsedMinutes>"
  519. << "</Coverage>" << std::endl;
  520. m_CTest->EndXML(log);
  521. std::cout << "\tCovered LOC: " << total_tested << std::endl
  522. << "\tNot covered LOC: " << total_untested << std::endl
  523. << "\tTotal LOC: " << total_lines << std::endl
  524. << "\tPercentage Coverage: ";
  525. std::cout.setf(std::ios::fixed, std::ios::floatfield);
  526. std::cout.precision(2);
  527. std::cout << (percent_coverage) << "%" << std::endl;
  528. return 1;
  529. }