From 64e0459f93d303bef1330c0c2bc988c67c1c8582 Mon Sep 17 00:00:00 2001 From: Andy Cedilnik Date: Thu, 26 Apr 2007 23:20:12 -0400 Subject: ENH: Initial attempt to do python coverage. Hopefully will not break coverage on GCov --- Source/CTest/cmCTestCoverageHandler.cxx | 1065 ++++++++++++++++++------------- Source/CTest/cmCTestCoverageHandler.h | 7 + 2 files changed, 640 insertions(+), 432 deletions(-) diff --git a/Source/CTest/cmCTestCoverageHandler.cxx b/Source/CTest/cmCTestCoverageHandler.cxx index c3295f0..9dfef44 100644 --- a/Source/CTest/cmCTestCoverageHandler.cxx +++ b/Source/CTest/cmCTestCoverageHandler.cxx @@ -33,6 +33,22 @@ #define SAFEDIV(x,y) (((y)!=0)?((x)/(y)):(0)) //---------------------------------------------------------------------- +//********************************************************************** +class cmCTestCoverageHandlerContainer +{ +public: + int Error; + std::string SourceDir; + std::string BinaryDir; + typedef std::vector SingleFileCoverageVector; + typedef std::map TotalCoverageMap; + TotalCoverageMap TotalCoverage; + std::ostream* OFS; +}; +//********************************************************************** +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- cmCTestCoverageHandler::cmCTestCoverageHandler() { } @@ -186,12 +202,12 @@ int cmCTestCoverageHandler::ProcessHandler() return error; } + std::string coverage_start_time = this->CTest->CurrentTime(); + std::string sourceDir = this->CTest->GetCTestConfiguration("SourceDirectory"); std::string binaryDir = this->CTest->GetCTestConfiguration("BuildDirectory"); - std::string gcovCommand - = this->CTest->GetCTestConfiguration("CoverageCommand"); cmGeneratedFileStream ofs; double elapsed_time_start = cmSystemTools::GetTime(); @@ -209,191 +225,474 @@ int cmCTestCoverageHandler::ProcessHandler() std::string asfGlob = sourceDir + "/*"; std::string abfGlob = binaryDir + "/*"; - - // Style 1 - std::string st1gcovOutputRex1 - = "[0-9]+\\.[0-9]+% of [0-9]+ (source |)lines executed in file (.*)$"; - std::string st1gcovOutputRex2 = "^Creating (.*\\.gcov)\\."; - cmsys::RegularExpression st1re1(st1gcovOutputRex1.c_str()); - cmsys::RegularExpression st1re2(st1gcovOutputRex2.c_str()); - - - // Style 2 - std::string st2gcovOutputRex1 = "^File *[`'](.*)'$"; - std::string st2gcovOutputRex2 - = "Lines executed: *[0-9]+\\.[0-9]+% of [0-9]+$"; - std::string st2gcovOutputRex3 = "^(.*):creating [`'](.*\\.gcov)'"; - std::string st2gcovOutputRex4 = "^(.*):unexpected EOF *$"; - std::string st2gcovOutputRex5 = "^(.*):cannot open source file*$"; - std::string st2gcovOutputRex6 - = "^(.*):source file is newer than graph file `(.*)'$"; - cmsys::RegularExpression st2re1(st2gcovOutputRex1.c_str()); - cmsys::RegularExpression st2re2(st2gcovOutputRex2.c_str()); - cmsys::RegularExpression st2re3(st2gcovOutputRex3.c_str()); - cmsys::RegularExpression st2re4(st2gcovOutputRex4.c_str()); - cmsys::RegularExpression st2re5(st2gcovOutputRex5.c_str()); - cmsys::RegularExpression st2re6(st2gcovOutputRex6.c_str()); - cmCTestLog(this->CTest, HANDLER_OUTPUT, "Performing coverage" << std::endl); - std::string coverage_start_time = this->CTest->CurrentTime(); + cmCTestCoverageHandlerContainer cont; + cont.Error = error; + cont.SourceDir = sourceDir; + cont.BinaryDir = binaryDir; + cont.OFS = &ofs; - std::string testingDir = this->CTest->GetBinaryDir() + "/Testing"; - std::string tempDir = testingDir + "/CoverageInfo"; - std::string currentDirectory = cmSystemTools::GetCurrentWorkingDirectory(); - cmSystemTools::MakeDirectory(tempDir.c_str()); - cmSystemTools::ChangeDirectory(tempDir.c_str()); + int file_count = 0; - cmsys::Glob gl; - gl.RecurseOn(); - std::string daGlob = binaryDir + "/*.da"; - gl.FindFiles(daGlob); - std::vector files = gl.GetFiles(); - daGlob = binaryDir + "/*.gcda"; - gl.FindFiles(daGlob); - std::vector& moreFiles = gl.GetFiles(); - files.insert(files.end(), moreFiles.begin(), moreFiles.end()); - std::vector::iterator it; + file_count += this->HandleGCovCoverage(&cont); + if ( file_count < 0 ) + { + return error; + } + file_count += this->HandleTracePyCoverage(&cont); + if ( file_count < 0 ) + { + return error; + } + error = cont.Error; - if ( files.size() == 0 ) + + if ( file_count == 0 ) { cmCTestLog(this->CTest, WARNING, " Cannot find any coverage files. Ignoring Coverage request." << std::endl); - // No coverage files is a valid thing, so the exit code is 0 - cmSystemTools::ChangeDirectory(currentDirectory.c_str()); - return 0; + return error; } + cmGeneratedFileStream covSumFile; + cmGeneratedFileStream covLogFile; - this->CustomCoverageExcludeRegex.empty(); - std::vector::iterator rexIt; - for ( rexIt = this->CustomCoverageExclude.begin(); - rexIt != this->CustomCoverageExclude.end(); - ++ rexIt ) + if (!this->StartResultingXML("Coverage", covSumFile)) { - this->CustomCoverageExcludeRegex.push_back( - cmsys::RegularExpression(rexIt->c_str())); + cmCTestLog(this->CTest, ERROR_MESSAGE, + "Cannot open coverage summary file." << std::endl); + return -1; } - typedef std::vector singleFileCoverageVector; - typedef std::map totalCoverageMap; - - totalCoverageMap totalCoverage; - - int gcovStyle = 0; - - std::set missingFiles; + this->CTest->StartXML(covSumFile); + // Produce output xml files - std::string actualSourceFile = ""; + covSumFile << "" << std::endl + << "\t" << coverage_start_time << "" + << std::endl; + int logFileCount = 0; + if ( !this->StartCoverageLogFile(covLogFile, logFileCount) ) + { + return -1; + } + cmCTestCoverageHandlerContainer::TotalCoverageMap::iterator fileIterator; + int cnt = 0; + long total_tested = 0; + long total_untested = 0; + //std::string fullSourceDir = sourceDir + "/"; + //std::string fullBinaryDir = binaryDir + "/"; + cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl); cmCTestLog(this->CTest, HANDLER_OUTPUT, - " Processing coverage (each . represents one file):" << std::endl); + " Acumulating results (each . represents one file):" << std::endl); cmCTestLog(this->CTest, HANDLER_OUTPUT, " "); - int file_count = 0; - for ( it = files.begin(); it != files.end(); ++ it ) + + std::vector errorsWhileAccumulating; + + file_count = 0; + for ( fileIterator = cont.TotalCoverage.begin(); + fileIterator != cont.TotalCoverage.end(); + ++fileIterator ) { cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush); - std::string fileDir = cmSystemTools::GetFilenamePath(it->c_str()); - std::string command = "\"" + gcovCommand + "\" -l -o \"" + fileDir - + "\" \"" + *it + "\""; - cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, command.c_str() - << std::endl); - std::string output = ""; - std::string errors = ""; - int retVal = 0; - ofs << "* Run coverage for: " << fileDir.c_str() << std::endl; - ofs << " Command: " << command.c_str() << std::endl; - int res = this->CTest->RunCommand(command.c_str(), &output, &errors, - &retVal, tempDir.c_str(), 0 /*this->TimeOut*/); - - ofs << " Output: " << output.c_str() << std::endl; - ofs << " Errors: " << errors.c_str() << std::endl; - if ( ! res ) + file_count ++; + if ( file_count % 50 == 0 ) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - "Problem running coverage on file: " << it->c_str() << std::endl); - cmCTestLog(this->CTest, ERROR_MESSAGE, - "Command produced error: " << errors << std::endl); - error ++; - continue; + cmCTestLog(this->CTest, HANDLER_OUTPUT, " processed: " << file_count + << " out of " + << cont.TotalCoverage.size() << std::endl); + cmCTestLog(this->CTest, HANDLER_OUTPUT, " "); } - if ( retVal != 0 ) + if ( cnt % 100 == 0 ) { - cmCTestLog(this->CTest, ERROR_MESSAGE, "Coverage command returned: " - << retVal << " while processing: " << it->c_str() << std::endl); - cmCTestLog(this->CTest, ERROR_MESSAGE, - "Command produced error: " << error << std::endl); + this->EndCoverageLogFile(covLogFile, logFileCount); + logFileCount ++; + if ( !this->StartCoverageLogFile(covLogFile, logFileCount) ) + { + return -1; + } } + const std::string fullFileName = fileIterator->first; + const std::string fileName + = cmSystemTools::GetFilenameName(fullFileName.c_str()); + std::string fullFilePath + = cmSystemTools::GetFilenamePath(fullFileName.c_str()); cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - "--------------------------------------------------------------" - << std::endl - << output << std::endl - << "--------------------------------------------------------------" - << std::endl); - std::vector lines; - std::vector::iterator line; + "Process file: " << fullFileName << std::endl); + cmSystemTools::ConvertToUnixSlashes(fullFilePath); - // Globals for storing current source file and current gcov file; - cmSystemTools::Split(output.c_str(), lines); - for ( line = lines.begin(); line != lines.end(); ++line) + if ( !cmSystemTools::FileExists(fullFileName.c_str()) ) { - std::string sourceFile; - std::string gcovFile; - cmCTestLog(this->CTest, DEBUG, "Line: [" << line->c_str() << "]" + cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find file: " + << fullFileName.c_str() << std::endl); + continue; + } + + bool shouldIDoCoverage + = this->ShouldIDoCoverage(fullFileName.c_str(), + sourceDir.c_str(), binaryDir.c_str()); + if ( !shouldIDoCoverage ) + { + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + ".NoDartCoverage found, so skip coverage check for: " + << fullFileName.c_str() << std::endl); - if ( line->size() == 0 ) + continue; + } + + const cmCTestCoverageHandlerContainer::SingleFileCoverageVector& fcov + = fileIterator->second; + covLogFile << "\tCTest->MakeXMLSafe(fileName.c_str()) + << "\" FullPath=\"" << this->CTest->MakeXMLSafe( + this->CTest->GetShortPathToFile( + fileIterator->first.c_str())) << "\">" << std::endl + << "\t\t" << std::endl; + + std::ifstream ifs(fullFileName.c_str()); + if ( !ifs) + { + cmOStringStream ostr; + ostr << "Cannot open source file: " << fullFileName.c_str(); + errorsWhileAccumulating.push_back(ostr.str()); + error ++; + continue; + } + + int tested = 0; + int untested = 0; + + cmCTestCoverageHandlerContainer::SingleFileCoverageVector::size_type cc; + std::string line; + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + "Actually perfoming coverage for: " << fullFileName << std::endl); + for ( cc= 0; cc < fcov.size(); cc ++ ) + { + if ( !cmSystemTools::GetLineFromStream(ifs, line) && + cc != fcov.size() -1 ) { - // Ignore empty line; probably style 2 + cmOStringStream ostr; + ostr << "Problem reading source file: " << fullFileName.c_str() + << " line:" << cc; + errorsWhileAccumulating.push_back(ostr.str()); + error ++; + break; } - else if ( st1re1.find(line->c_str()) ) + covLogFile << "\t\t" + << this->CTest->MakeXMLSafe(line.c_str()) << "" << std::endl; + if ( fcov[cc] == 0 ) { - if ( gcovStyle != 0 ) - { - if ( gcovStyle != 1 ) - { - cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style" - << std::endl); - error ++; - break; - } - gcovStyle = 1; - } - - actualSourceFile = ""; - sourceFile = st1re1.match(2); + untested ++; } - else if ( st1re2.find(line->c_str() ) ) + else if ( fcov[cc] > 0 ) { - if ( gcovStyle != 0 ) - { - if ( gcovStyle != 1 ) - { - cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style" - << std::endl); - error ++; - break; - } - gcovStyle = 1; - } - - gcovFile = st1re2.match(1); + tested ++; } - else if ( st2re1.find(line->c_str() ) ) - { - if ( gcovStyle != 0 ) - { - if ( gcovStyle != 2 ) - { - cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style" - << std::endl); - error ++; - break; - } - gcovStyle = 2; - } - - actualSourceFile = ""; - sourceFile = st2re1.match(1); + } + if ( cmSystemTools::GetLineFromStream(ifs, line) ) + { + cmOStringStream ostr; + ostr << "Looks like there are more lines in the file: " << line; + errorsWhileAccumulating.push_back(ostr.str()); + } + float cper = 0; + float cmet = 0; + if ( tested + untested > 0 ) + { + cper = (100 * SAFEDIV(static_cast(tested), + static_cast(tested + untested))); + cmet = ( SAFEDIV(static_cast(tested + 10), + static_cast(tested + untested + 10))); + } + total_tested += tested; + total_untested += untested; + covLogFile << "\t\t" << std::endl + << "\t" << std::endl; + covSumFile << "\tCTest->MakeXMLSafe(fileName) + << "\" FullPath=\"" << this->CTest->MakeXMLSafe( + this->CTest->GetShortPathToFile(fullFileName.c_str())) + << "\" Covered=\"" << (cmet>0?"true":"false") << "\">\n" + << "\t\t" << tested << "\n" + << "\t\t" << untested << "\n" + << "\t\t"; + covSumFile.setf(std::ios::fixed, std::ios::floatfield); + covSumFile.precision(2); + covSumFile << (cper) << "\n" + << "\t\t"; + covSumFile.setf(std::ios::fixed, std::ios::floatfield); + covSumFile.precision(2); + covSumFile << (cmet) << "\n" + << "\t" << std::endl; + cnt ++; + } + this->EndCoverageLogFile(covLogFile, logFileCount); + + if ( errorsWhileAccumulating.size() > 0 ) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, std::endl); + cmCTestLog(this->CTest, ERROR_MESSAGE, + "Error(s) while acumulating results:" << std::endl); + std::vector::iterator erIt; + for ( erIt = errorsWhileAccumulating.begin(); + erIt != errorsWhileAccumulating.end(); + ++ erIt ) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + " " << erIt->c_str() << std::endl); + } + } + + int total_lines = total_tested + total_untested; + float percent_coverage = 100 * SAFEDIV(static_cast(total_tested), + static_cast(total_lines)); + if ( total_lines == 0 ) + { + percent_coverage = 0; + } + + std::string end_time = this->CTest->CurrentTime(); + + covSumFile << "\t" << total_tested << "\n" + << "\t" << total_untested << "\n" + << "\t" << total_lines << "\n" + << "\t"; + covSumFile.setf(std::ios::fixed, std::ios::floatfield); + covSumFile.precision(2); + covSumFile << (percent_coverage)<< "\n" + << "\t" << end_time << "\n"; + covSumFile << "" << + static_cast((cmSystemTools::GetTime() - elapsed_time_start)/6)/10.0 + << "" + << "" << std::endl; + this->CTest->EndXML(covSumFile); + + cmCTestLog(this->CTest, HANDLER_OUTPUT, "" << std::endl + << "\tCovered LOC: " + << total_tested << std::endl + << "\tNot covered LOC: " << total_untested << std::endl + << "\tTotal LOC: " << total_lines << std::endl + << "\tPercentage Coverage: " + << std::setiosflags(std::ios::fixed) + << std::setprecision(2) + << (percent_coverage) << "%" << std::endl); + + ofs << "\tCovered LOC: " << total_tested << std::endl + << "\tNot covered LOC: " << total_untested << std::endl + << "\tTotal LOC: " << total_lines << std::endl + << "\tPercentage Coverage: " + << std::setiosflags(std::ios::fixed) + << std::setprecision(2) + << (percent_coverage) << "%" << std::endl; + + + if ( error ) + { + return -1; + } + return 0; +} + +//---------------------------------------------------------------------- +void cmCTestCoverageHandler::PopulateCustomVectors(cmMakefile *mf) +{ + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + " Add coverage exclude regular expressions." << std::endl); + this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_COVERAGE_EXCLUDE", + this->CustomCoverageExclude); + std::vector::iterator it; + for ( it = this->CustomCoverageExclude.begin(); + it != this->CustomCoverageExclude.end(); + ++ it ) + { + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Add coverage exclude: " + << it->c_str() << std::endl); + } +} + +//---------------------------------------------------------------------- +int cmCTestCoverageHandler::HandleGCovCoverage( + cmCTestCoverageHandlerContainer* cont) +{ + std::string gcovCommand + = this->CTest->GetCTestConfiguration("CoverageCommand"); + + // Style 1 + std::string st1gcovOutputRex1 + = "[0-9]+\\.[0-9]+% of [0-9]+ (source |)lines executed in file (.*)$"; + std::string st1gcovOutputRex2 = "^Creating (.*\\.gcov)\\."; + cmsys::RegularExpression st1re1(st1gcovOutputRex1.c_str()); + cmsys::RegularExpression st1re2(st1gcovOutputRex2.c_str()); + + + // Style 2 + std::string st2gcovOutputRex1 = "^File *[`'](.*)'$"; + std::string st2gcovOutputRex2 + = "Lines executed: *[0-9]+\\.[0-9]+% of [0-9]+$"; + std::string st2gcovOutputRex3 = "^(.*):creating [`'](.*\\.gcov)'"; + std::string st2gcovOutputRex4 = "^(.*):unexpected EOF *$"; + std::string st2gcovOutputRex5 = "^(.*):cannot open source file*$"; + std::string st2gcovOutputRex6 + = "^(.*):source file is newer than graph file `(.*)'$"; + cmsys::RegularExpression st2re1(st2gcovOutputRex1.c_str()); + cmsys::RegularExpression st2re2(st2gcovOutputRex2.c_str()); + cmsys::RegularExpression st2re3(st2gcovOutputRex3.c_str()); + cmsys::RegularExpression st2re4(st2gcovOutputRex4.c_str()); + cmsys::RegularExpression st2re5(st2gcovOutputRex5.c_str()); + cmsys::RegularExpression st2re6(st2gcovOutputRex6.c_str()); + + + cmsys::Glob gl; + gl.RecurseOn(); + std::string daGlob = cont->BinaryDir + "/*.da"; + gl.FindFiles(daGlob); + std::vector files = gl.GetFiles(); + daGlob = cont->BinaryDir + "/*.gcda"; + gl.FindFiles(daGlob); + std::vector& moreFiles = gl.GetFiles(); + files.insert(files.end(), moreFiles.begin(), moreFiles.end()); + std::vector::iterator it; + + if ( files.size() == 0 ) + { + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + " Cannot find any GCov coverage files." + << std::endl); + // No coverage files is a valid thing, so the exit code is 0 + return 0; + } + + std::string testingDir = this->CTest->GetBinaryDir() + "/Testing"; + std::string tempDir = testingDir + "/CoverageInfo"; + std::string currentDirectory = cmSystemTools::GetCurrentWorkingDirectory(); + cmSystemTools::MakeDirectory(tempDir.c_str()); + cmSystemTools::ChangeDirectory(tempDir.c_str()); + + this->CustomCoverageExcludeRegex.empty(); + std::vector::iterator rexIt; + for ( rexIt = this->CustomCoverageExclude.begin(); + rexIt != this->CustomCoverageExclude.end(); + ++ rexIt ) + { + this->CustomCoverageExcludeRegex.push_back( + cmsys::RegularExpression(rexIt->c_str())); + } + + int gcovStyle = 0; + + std::set missingFiles; + + std::string actualSourceFile = ""; + cmCTestLog(this->CTest, HANDLER_OUTPUT, + " Processing coverage (each . represents one file):" << std::endl); + cmCTestLog(this->CTest, HANDLER_OUTPUT, " "); + int file_count = 0; + for ( it = files.begin(); it != files.end(); ++ it ) + { + cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush); + std::string fileDir = cmSystemTools::GetFilenamePath(it->c_str()); + std::string command = "\"" + gcovCommand + "\" -l -o \"" + fileDir + + "\" \"" + *it + "\""; + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, command.c_str() + << std::endl); + std::string output = ""; + std::string errors = ""; + int retVal = 0; + *cont->OFS << "* Run coverage for: " << fileDir.c_str() << std::endl; + *cont->OFS << " Command: " << command.c_str() << std::endl; + int res = this->CTest->RunCommand(command.c_str(), &output, &errors, + &retVal, tempDir.c_str(), 0 /*this->TimeOut*/); + + *cont->OFS << " Output: " << output.c_str() << std::endl; + *cont->OFS << " Errors: " << errors.c_str() << std::endl; + if ( ! res ) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "Problem running coverage on file: " << it->c_str() << std::endl); + cmCTestLog(this->CTest, ERROR_MESSAGE, + "Command produced error: " << errors << std::endl); + cont->Error ++; + continue; + } + if ( retVal != 0 ) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, "Coverage command returned: " + << retVal << " while processing: " << it->c_str() << std::endl); + cmCTestLog(this->CTest, ERROR_MESSAGE, + "Command produced error: " << cont->Error << std::endl); + } + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + "--------------------------------------------------------------" + << std::endl + << output << std::endl + << "--------------------------------------------------------------" + << std::endl); + std::vector lines; + std::vector::iterator line; + + + // Globals for storing current source file and current gcov file; + cmSystemTools::Split(output.c_str(), lines); + for ( line = lines.begin(); line != lines.end(); ++line) + { + std::string sourceFile; + std::string gcovFile; + cmCTestLog(this->CTest, DEBUG, "Line: [" << line->c_str() << "]" + << std::endl); + if ( line->size() == 0 ) + { + // Ignore empty line; probably style 2 + } + else if ( st1re1.find(line->c_str()) ) + { + if ( gcovStyle != 0 ) + { + if ( gcovStyle != 1 ) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style" + << std::endl); + cont->Error ++; + break; + } + gcovStyle = 1; + } + + actualSourceFile = ""; + sourceFile = st1re1.match(2); + } + else if ( st1re2.find(line->c_str() ) ) + { + if ( gcovStyle != 0 ) + { + if ( gcovStyle != 1 ) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style" + << std::endl); + cont->Error ++; + break; + } + gcovStyle = 1; + } + + gcovFile = st1re2.match(1); + } + else if ( st2re1.find(line->c_str() ) ) + { + if ( gcovStyle != 0 ) + { + if ( gcovStyle != 2 ) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style" + << std::endl); + cont->Error ++; + break; + } + gcovStyle = 2; + } + + actualSourceFile = ""; + sourceFile = st2re1.match(1); } else if ( st2re2.find(line->c_str() ) ) { @@ -403,7 +702,7 @@ int cmCTestCoverageHandler::ProcessHandler() { cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style" << std::endl); - error ++; + cont->Error ++; break; } gcovStyle = 2; @@ -417,7 +716,7 @@ int cmCTestCoverageHandler::ProcessHandler() { cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style" << std::endl); - error ++; + cont->Error ++; break; } gcovStyle = 2; @@ -433,7 +732,7 @@ int cmCTestCoverageHandler::ProcessHandler() { cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style" << std::endl); - error ++; + cont->Error ++; break; } gcovStyle = 2; @@ -450,7 +749,7 @@ int cmCTestCoverageHandler::ProcessHandler() { cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style" << std::endl); - error ++; + cont->Error ++; break; } gcovStyle = 2; @@ -467,7 +766,7 @@ int cmCTestCoverageHandler::ProcessHandler() { cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style" << std::endl); - error ++; + cont->Error ++; break; } gcovStyle = 2; @@ -480,12 +779,13 @@ int cmCTestCoverageHandler::ProcessHandler() { cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown line: [" << line->c_str() << "]" << std::endl); - error ++; + cont->Error ++; //abort(); } if ( !gcovFile.empty() && actualSourceFile.size() ) { - singleFileCoverageVector* vec = &totalCoverage[actualSourceFile]; + cmCTestCoverageHandlerContainer::SingleFileCoverageVector* vec + = &cont->TotalCoverage[actualSourceFile]; cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " in file: " << gcovFile << std::endl); std::ifstream ifile(gcovFile.c_str()); @@ -527,7 +827,7 @@ int cmCTestCoverageHandler::ProcessHandler() if ( lineIdx >= 0 ) { while ( vec->size() <= - static_cast(lineIdx) ) + static_cast(lineIdx) ) { vec->push_back(-1); } @@ -550,311 +850,212 @@ int cmCTestCoverageHandler::ProcessHandler() { gcovFile = ""; // Is it in the source dir? - if ( sourceFile.size() > sourceDir.size() && - sourceFile.substr(0, sourceDir.size()) == sourceDir && - sourceFile[sourceDir.size()] == '/' ) + if ( sourceFile.size() > cont->SourceDir.size() && + sourceFile.substr(0, cont->SourceDir.size()) == cont->SourceDir && + sourceFile[cont->SourceDir.size()] == '/' ) { cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " produced s: " << sourceFile.c_str() << std::endl); - ofs << " produced in source dir: " << sourceFile.c_str() + *cont->OFS << " produced in source dir: " << sourceFile.c_str() << std::endl; actualSourceFile = cmSystemTools::CollapseFullPath(sourceFile.c_str()); } // Binary dir? - if ( sourceFile.size() > binaryDir.size() && - sourceFile.substr(0, binaryDir.size()) == binaryDir && - sourceFile[binaryDir.size()] == '/' ) - { - cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " produced b: " - << sourceFile.c_str() << std::endl); - ofs << " produced in binary dir: " << sourceFile.c_str() - << std::endl; - actualSourceFile - = cmSystemTools::CollapseFullPath(sourceFile.c_str()); - } - if ( actualSourceFile.empty() ) - { - if ( missingFiles.find(actualSourceFile) == missingFiles.end() ) - { - cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - "Something went wrong" << std::endl); - cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "File: [" - << sourceFile.c_str() << "]" << std::endl); - cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "s: [" - << sourceFile.substr(0, sourceDir.size()) << "]" << std::endl); - cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "b: [" - << sourceFile.substr(0, binaryDir.size()) << "]" << std::endl); - ofs << " Something went wrong. Cannot find: " - << sourceFile.c_str() - << " in source dir: " << sourceDir.c_str() - << " or binary dir: " << binaryDir.c_str() << std::endl; - missingFiles.insert(actualSourceFile); - } - } - } - } - file_count ++; - if ( file_count % 50 == 0 ) - { - cmCTestLog(this->CTest, HANDLER_OUTPUT, " processed: " << file_count - << " out of " << files.size() << std::endl); - cmCTestLog(this->CTest, HANDLER_OUTPUT, " "); - } - } - - cmGeneratedFileStream covSumFile; - cmGeneratedFileStream covLogFile; - - if (!this->StartResultingXML("Coverage", covSumFile)) - { - cmCTestLog(this->CTest, ERROR_MESSAGE, - "Cannot open coverage summary file." << std::endl); - cmSystemTools::ChangeDirectory(currentDirectory.c_str()); - return -1; - } - - this->CTest->StartXML(covSumFile); - // Produce output xml files - - covSumFile << "" << std::endl - << "\t" << coverage_start_time << "" - << std::endl; - int logFileCount = 0; - if ( !this->StartCoverageLogFile(covLogFile, logFileCount) ) - { - cmSystemTools::ChangeDirectory(currentDirectory.c_str()); - return -1; - } - totalCoverageMap::iterator fileIterator; - int cnt = 0; - long total_tested = 0; - long total_untested = 0; - //std::string fullSourceDir = sourceDir + "/"; - //std::string fullBinaryDir = binaryDir + "/"; - cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl); - cmCTestLog(this->CTest, HANDLER_OUTPUT, - " Acumulating results (each . represents one file):" << std::endl); - cmCTestLog(this->CTest, HANDLER_OUTPUT, " "); - - std::vector errorsWhileAccumulating; - - file_count = 0; - for ( fileIterator = totalCoverage.begin(); - fileIterator != totalCoverage.end(); - ++fileIterator ) - { - cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush); + if ( sourceFile.size() > cont->BinaryDir.size() && + sourceFile.substr(0, cont->BinaryDir.size()) == cont->BinaryDir && + sourceFile[cont->BinaryDir.size()] == '/' ) + { + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " produced b: " + << sourceFile.c_str() << std::endl); + *cont->OFS << " produced in binary dir: " << sourceFile.c_str() + << std::endl; + actualSourceFile + = cmSystemTools::CollapseFullPath(sourceFile.c_str()); + } + if ( actualSourceFile.empty() ) + { + if ( missingFiles.find(actualSourceFile) == missingFiles.end() ) + { + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + "Something went wrong" << std::endl); + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "File: [" + << sourceFile.c_str() << "]" << std::endl); + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "s: [" + << sourceFile.substr(0, cont->SourceDir.size()) << "]" << std::endl); + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "b: [" + << sourceFile.substr(0, cont->BinaryDir.size()) << "]" << std::endl); + *cont->OFS << " Something went wrong. Cannot find: " + << sourceFile.c_str() + << " in source dir: " << cont->SourceDir.c_str() + << " or binary dir: " << cont->BinaryDir.c_str() << std::endl; + missingFiles.insert(actualSourceFile); + } + } + } + } file_count ++; if ( file_count % 50 == 0 ) { cmCTestLog(this->CTest, HANDLER_OUTPUT, " processed: " << file_count - << " out of " - << totalCoverage.size() << std::endl); + << " out of " << files.size() << std::endl); cmCTestLog(this->CTest, HANDLER_OUTPUT, " "); } - if ( cnt % 100 == 0 ) - { - this->EndCoverageLogFile(covLogFile, logFileCount); - logFileCount ++; - if ( !this->StartCoverageLogFile(covLogFile, logFileCount) ) - { - cmSystemTools::ChangeDirectory(currentDirectory.c_str()); - return -1; - } - } - const std::string fullFileName = fileIterator->first; - const std::string fileName - = cmSystemTools::GetFilenameName(fullFileName.c_str()); - std::string fullFilePath - = cmSystemTools::GetFilenamePath(fullFileName.c_str()); - cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Process file: " - << fullFileName << std::endl); + } + cmSystemTools::ChangeDirectory(currentDirectory.c_str()); + return file_count; +} - cmSystemTools::ConvertToUnixSlashes(fullFilePath); +//---------------------------------------------------------------------- +int cmCTestCoverageHandler::HandleTracePyCoverage( + cmCTestCoverageHandlerContainer* cont) +{ + cmsys::Glob gl; + gl.RecurseOn(); + std::string daGlob = cont->BinaryDir + "/*.cover"; + gl.FindFiles(daGlob); + std::vector files = gl.GetFiles(); - if ( !cmSystemTools::FileExists(fullFileName.c_str()) ) - { - cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find file: " - << fullFileName.c_str() << std::endl); - continue; - } + if ( files.size() == 0 ) + { + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + " Cannot find any Python Trace.py coverage files." + << std::endl); + // No coverage files is a valid thing, so the exit code is 0 + return 0; + } - bool shouldIDoCoverage - = this->ShouldIDoCoverage(fullFileName.c_str(), - sourceDir.c_str(), binaryDir.c_str()); - if ( !shouldIDoCoverage ) - { - cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - ".NoDartCoverage found, so skip coverage check for: " - << fullFileName.c_str() - << std::endl); - continue; - } + std::string testingDir = this->CTest->GetBinaryDir() + "/Testing"; + std::string tempDir = testingDir + "/CoverageInfo"; + std::string currentDirectory = cmSystemTools::GetCurrentWorkingDirectory(); + cmSystemTools::MakeDirectory(tempDir.c_str()); + cmSystemTools::ChangeDirectory(tempDir.c_str()); - const singleFileCoverageVector& fcov = fileIterator->second; - covLogFile << "\tCTest->MakeXMLSafe(fileName.c_str()) - << "\" FullPath=\"" << this->CTest->MakeXMLSafe( - this->CTest->GetShortPathToFile( - fileIterator->first.c_str())) << "\">" << std::endl - << "\t\t" << std::endl; + cmSystemTools::ChangeDirectory(currentDirectory.c_str()); - std::ifstream ifs(fullFileName.c_str()); - if ( !ifs) + std::vector::iterator fileIt; + int file_count = 0; + for ( fileIt = files.begin(); fileIt != files.end(); ++ fileIt ) + { + std::string fileName = this->FindFile(cont, *fileIt); + if ( fileName.empty() ) { - cmOStringStream ostr; - ostr << "Cannot open source file: " << fullFileName.c_str(); - errorsWhileAccumulating.push_back(ostr.str()); - error ++; + cmCTestLog(this->CTest, ERROR_MESSAGE, + "Cannot find source Python file corresponding to: " + << fileIt->c_str() << std::endl); continue; } - int tested = 0; - int untested = 0; - - singleFileCoverageVector::size_type cc; - std::string line; - for ( cc= 0; cc < fcov.size(); cc ++ ) - { - if ( !cmSystemTools::GetLineFromStream(ifs, line) && - cc != fcov.size() -1 ) - { - cmOStringStream ostr; - ostr << "Problem reading source file: " << fullFileName.c_str() - << " line:" << cc; - errorsWhileAccumulating.push_back(ostr.str()); - error ++; - break; - } - covLogFile << "\t\t" - << this->CTest->MakeXMLSafe(line.c_str()) << "" << std::endl; - if ( fcov[cc] == 0 ) - { - untested ++; - } - else if ( fcov[cc] > 0 ) - { - tested ++; - } - } - if ( cmSystemTools::GetLineFromStream(ifs, line) ) - { - cmOStringStream ostr; - ostr << "Looks like there are more lines in the file: " << line; - errorsWhileAccumulating.push_back(ostr.str()); - } - float cper = 0; - float cmet = 0; - if ( tested + untested > 0 ) + std::string actualSourceFile + = cmSystemTools::CollapseFullPath(fileName.c_str()); + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + " Check coverage for file: " << actualSourceFile.c_str() + << std::endl); + cmCTestCoverageHandlerContainer::SingleFileCoverageVector* vec + = &cont->TotalCoverage[actualSourceFile]; + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + " in file: " << fileIt->c_str() << std::endl); + std::ifstream ifile(fileIt->c_str()); + if ( ! ifile ) { - cper = (100 * SAFEDIV(static_cast(tested), - static_cast(tested + untested))); - cmet = ( SAFEDIV(static_cast(tested + 10), - static_cast(tested + untested + 10))); + cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open file: " + << fileIt->c_str() << std::endl); } - total_tested += tested; - total_untested += untested; - covLogFile << "\t\t" << std::endl - << "\t" << std::endl; - covSumFile << "\tCTest->MakeXMLSafe(fileName) - << "\" FullPath=\"" << this->CTest->MakeXMLSafe( - this->CTest->GetShortPathToFile(fullFileName.c_str())) - << "\" Covered=\"" << (cmet>0?"true":"false") << "\">\n" - << "\t\t" << tested << "\n" - << "\t\t" << untested << "\n" - << "\t\t"; - covSumFile.setf(std::ios::fixed, std::ios::floatfield); - covSumFile.precision(2); - covSumFile << (cper) << "\n" - << "\t\t"; - covSumFile.setf(std::ios::fixed, std::ios::floatfield); - covSumFile.precision(2); - covSumFile << (cmet) << "\n" - << "\t" << std::endl; - cnt ++; - } - this->EndCoverageLogFile(covLogFile, logFileCount); - - if ( errorsWhileAccumulating.size() > 0 ) - { - cmCTestLog(this->CTest, ERROR_MESSAGE, std::endl); - cmCTestLog(this->CTest, ERROR_MESSAGE, - "Error(s) while acumulating results:" << std::endl); - std::vector::iterator erIt; - for ( erIt = errorsWhileAccumulating.begin(); - erIt != errorsWhileAccumulating.end(); - ++ erIt ) + else { - cmCTestLog(this->CTest, ERROR_MESSAGE, - " " << erIt->c_str() << std::endl); - } - } - - int total_lines = total_tested + total_untested; - float percent_coverage = 100 * SAFEDIV(static_cast(total_tested), - static_cast(total_lines)); - if ( total_lines == 0 ) - { - percent_coverage = 0; - } - - std::string end_time = this->CTest->CurrentTime(); - - covSumFile << "\t" << total_tested << "\n" - << "\t" << total_untested << "\n" - << "\t" << total_lines << "\n" - << "\t"; - covSumFile.setf(std::ios::fixed, std::ios::floatfield); - covSumFile.precision(2); - covSumFile << (percent_coverage)<< "\n" - << "\t" << end_time << "\n"; - covSumFile << "" << - static_cast((cmSystemTools::GetTime() - elapsed_time_start)/6)/10.0 - << "" - << "" << std::endl; - this->CTest->EndXML(covSumFile); - - cmCTestLog(this->CTest, HANDLER_OUTPUT, "\tCovered LOC: " - << total_tested << std::endl - << "\tNot covered LOC: " << total_untested << std::endl - << "\tTotal LOC: " << total_lines << std::endl - << "\tPercentage Coverage: " - << std::setiosflags(std::ios::fixed) - << std::setprecision(2) - << (percent_coverage) << "%" << std::endl); + long cnt = -1; + std::string nl; + while ( cmSystemTools::GetLineFromStream(ifile, nl) ) + { + cnt ++; - ofs << "\tCovered LOC: " << total_tested << std::endl - << "\tNot covered LOC: " << total_untested << std::endl - << "\tTotal LOC: " << total_lines << std::endl - << "\tPercentage Coverage: " - << std::setiosflags(std::ios::fixed) - << std::setprecision(2) - << (percent_coverage) << "%" << std::endl; + // Skip empty lines + if ( !nl.size() ) + { + continue; + } - cmSystemTools::ChangeDirectory(currentDirectory.c_str()); + // Skip unused lines + if ( nl.size() < 12 ) + { + continue; + } - if ( error ) - { - return -1; + // Read the coverage count from the beginning of the Trace.py output + // line + std::string prefix = nl.substr(0, 6); + if ( prefix[5] != ' ' && prefix[5] != ':' ) + { + // This is a hack. We should really do something more elaborate + prefix = nl.substr(0, 7); + if ( prefix[6] != ' ' && prefix[6] != ':' ) + { + prefix = nl.substr(0, 8); + if ( prefix[7] != ' ' && prefix[7] != ':' ) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "Currently the limit is maximum coverage of 999999" + << std::endl); + } + } + } + int cov = atoi(prefix.c_str()); + if ( prefix[prefix.size()-1] != ':' ) + { + // This line does not have ':' so no coverage here. That said, + // Trace.py does not handle not covered lines versus comments etc. + // So, this will be set to 0. + cov = 0; + } + cmCTestLog(this->CTest, DEBUG, "Prefix: " << prefix.c_str() + << " cov: " << cov + << std::endl); + // Read the line number starting at the 10th character of the gcov + // output line + int lineIdx = cnt; + if ( lineIdx >= 0 ) + { + while ( vec->size() <= + static_cast(lineIdx) ) + { + vec->push_back(-1); + } + // Initially all entries are -1 (not used). If we get coverage + // information, increment it to 0 first. + if ( (*vec)[lineIdx] < 0 ) + { + if ( cov >= 0 ) + { + (*vec)[lineIdx] = 0; + } + } + (*vec)[lineIdx] += cov; + } + } + } + ++ file_count; } - return 0; + cmSystemTools::ChangeDirectory(currentDirectory.c_str()); + return file_count; } //---------------------------------------------------------------------- -void cmCTestCoverageHandler::PopulateCustomVectors(cmMakefile *mf) +std::string cmCTestCoverageHandler::FindFile( + cmCTestCoverageHandlerContainer* cont, + std::string fileName) { - cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - " Add coverage exclude regular expressions." << std::endl); - this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_COVERAGE_EXCLUDE", - this->CustomCoverageExclude); - std::vector::iterator it; - for ( it = this->CustomCoverageExclude.begin(); - it != this->CustomCoverageExclude.end(); - ++ it ) + std::string fileNameNoE + = cmSystemTools::GetFilenameWithoutLastExtension(fileName); + // First check in source and binary directory + std::string fullName = cont->SourceDir + "/" + fileNameNoE + ".py"; + if ( cmSystemTools::FileExists(fullName.c_str()) ) { - cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Add coverage exclude: " - << it->c_str() << std::endl); + return fullName; + } + fullName = cont->BinaryDir + "/" + fileNameNoE + ".py"; + if ( cmSystemTools::FileExists(fullName.c_str()) ) + { + return fullName; } + return ""; } diff --git a/Source/CTest/cmCTestCoverageHandler.h b/Source/CTest/cmCTestCoverageHandler.h index d2e798b..38422e0 100644 --- a/Source/CTest/cmCTestCoverageHandler.h +++ b/Source/CTest/cmCTestCoverageHandler.h @@ -25,6 +25,7 @@ #include class cmGeneratedFileStream; +class cmCTestCoverageHandlerContainer; /** \class cmCTestCoverageHandler * \brief A class that handles coverage computaiton for ctest @@ -55,6 +56,12 @@ private: bool StartCoverageLogFile(cmGeneratedFileStream& ostr, int logFileCount); void EndCoverageLogFile(cmGeneratedFileStream& ostr, int logFileCount); + int HandleGCovCoverage(cmCTestCoverageHandlerContainer* cont); + int HandleTracePyCoverage(cmCTestCoverageHandlerContainer* cont); + + std::string FindFile(cmCTestCoverageHandlerContainer* cont, + std::string fileName); + struct cmCTestCoverage { cmCTestCoverage() -- cgit v0.12