Skip to content

Commit

Permalink
Merge pull request #375 from nate-thirdwave/add_codecov
Browse files Browse the repository at this point in the history
Add codecov file format emitter
  • Loading branch information
SimonKagstrom committed Mar 15, 2022
2 parents 80bcedf + afbd484 commit adc5653
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ set (${KCOV}_SRCS
source-file-cache.cc
utils.cc
writers/cobertura-writer.cc
writers/codecov-writer.cc
writers/json-writer.cc
${coveralls_SRCS}
writers/html-writer.cc
Expand Down
6 changes: 5 additions & 1 deletion src/configuration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,7 @@ class Configuration : public IConfiguration
setKey("patchelf-command", "patchelf");
setKey("coveralls-service-name", "travis-ci");
setKey("cobertura-full-paths", 0);
setKey("codecov-full-paths", 0);
}

void setKey(const std::string &key, const std::string &val)
Expand Down Expand Up @@ -664,7 +665,8 @@ class Configuration : public IConfiguration
{
if (key == "low-limit" || key == "high-limit"
|| key == "bash-use-basic-parser"
|| key == "cobertura-full-paths")
|| key == "cobertura-full-paths"
|| key == "codecov-full-paths")
{
if (!isInteger(value))
panic("Value for %s must be integer\n", key.c_str());
Expand Down Expand Up @@ -692,6 +694,8 @@ class Configuration : public IConfiguration
setKey(key, std::string(value));
else if (key == "cobertura-full-paths")
setKey(key, stoul(std::string(value)));
else if (key == "codecov-full-paths")
setKey(key, stoul(std::string(value)));
else
panic("Unknown key %s\n", key.c_str());
}
Expand Down
10 changes: 10 additions & 0 deletions src/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include "merge-parser.hh"
#include "engines/system-mode-file-format.hh"
#include "writers/codecov-writer.hh"
#include "writers/html-writer.hh"
#include "writers/json-writer.hh"
#include "writers/coveralls-writer.hh"
Expand Down Expand Up @@ -189,6 +190,8 @@ static int runMergeMode()
mergeReporter, base + "kcov-merged/sonarqube.xml");
IWriter &mergeCoverallsWriter = createCoverallsWriter(mergeParser,
mergeReporter);
IWriter &mergeCodecovWriter = createCodecovWriter(mergeParser, mergeReporter,
base + "kcov-merged/codecov.json");
(void) mkdir(fmt("%s/kcov-merged", base.c_str()).c_str(), 0755);

output.registerWriter(mergeParser);
Expand All @@ -197,6 +200,7 @@ static int runMergeMode()
output.registerWriter(mergeCoberturaWriter);
output.registerWriter(mergeSonarqubeWriter);
output.registerWriter(mergeCoverallsWriter);
output.registerWriter(mergeCodecovWriter);

output.start();
output.stop();
Expand Down Expand Up @@ -269,6 +273,8 @@ static int runKcov(IConfiguration::RunMode_t runningMode)
out);
IWriter &sonarqubeWriter = createSonarqubeWriter(*parser, reporter,
out + "/sonarqube.xml");
IWriter &codecovWriter = createCodecovWriter(*parser, reporter,
out + "/codecov.json");

IWriter &mergeHtmlWriter = createHtmlWriter(mergeParser, mergeReporter,
base, base + "/kcov-merged", conf.keyAsString("merged-name"),
Expand All @@ -279,6 +285,8 @@ static int runKcov(IConfiguration::RunMode_t runningMode)
mergeReporter, base + "kcov-merged");
IWriter &mergeSonarqubeWriter = createSonarqubeWriter(mergeParser,
mergeReporter, base + "kcov-merged/sonarqube.xml");
IWriter &mergeCodecovWriter = createCodecovWriter(mergeParser,
mergeReporter, base + "kcov-merged/codecov.json");

// Multiple binaries? Register the merged mode stuff
if (countMetadata() > 0)
Expand All @@ -289,6 +297,7 @@ static int runKcov(IConfiguration::RunMode_t runningMode)
output.registerWriter(mergeSonarqubeWriter);
output.registerWriter(
createCoverallsWriter(mergeParser, mergeReporter));
output.registerWriter(mergeCodecovWriter);
}
else
{
Expand All @@ -299,6 +308,7 @@ static int runKcov(IConfiguration::RunMode_t runningMode)
output.registerWriter(jsonWriter);
output.registerWriter(coberturaWriter);
output.registerWriter(sonarqubeWriter);
output.registerWriter(codecovWriter);
}

reporter.registerListener(mergeParser);
Expand Down
200 changes: 200 additions & 0 deletions src/writers/codecov-writer.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
namespace std
{
class type_info;
}

#include <reporter.hh>
#include <file-parser.hh>
#include <configuration.hh>
#include <writer.hh>
#include <utils.hh>

#include <string>
#include <list>
#include <vector>
#include <unordered_map>
#include <iostream>
#include <fstream>
#include <sstream>

#include "writer-base.hh"

namespace kcov
{

namespace
{

// Outputs a coverage file that conforms to the Codecov Custom Coverage Format
// as described here: https://docs.codecov.com/docs/codecov-custom-coverage-format
//
// Noteably, while this file format can be consumed by Codecov, it is most
// useful for interoperating with other services that expect this format, as
// Codecov intentionally can consume multiple formats.
class CodecovWriter : public WriterBase
{
public:
CodecovWriter(IFileParser &parser, IReporter &reporter, const std::string &outFile) :
WriterBase(parser, reporter), m_outFile(outFile), m_maxPossibleHits(parser.maxPossibleHits())
{
}

void onStartup()
{
}

void onStop()
{
}

void write()
{
std::stringstream out;

setupCommonPaths();

for (FileMap_t::const_iterator it = m_files.begin(); it != m_files.end(); ++it)
{
File *file = it->second;

// Fixup file->m_codeLines etc
sumOne(file);
}
out << getHeader();

bool first_time = true;
for (FileMap_t::const_iterator it = m_files.begin(); it != m_files.end(); ++it)
{
if (!first_time) {
out << ",\n";
}
File *file = it->second;
out << writeOne(file);
first_time = false;
}
out << "\n";

out << getFooter();

std::ofstream cur(m_outFile);
cur << out.str();
}

private:
void sumOne(File *file)
{
unsigned int nExecutedLines = 0;
unsigned int nCodeLines = 0;

for (unsigned int n = 1; n < file->m_lastLineNr; n++)
{
if (!m_reporter.lineIsCode(file->m_name, n))
continue;

IReporter::LineExecutionCount cnt = m_reporter.getLineExecutionCount(file->m_name, n);

nExecutedLines += !!cnt.m_hits;
nCodeLines++;

// Update the execution count
file->m_executedLines = nExecutedLines;
file->m_codeLines = nCodeLines;
}
}

std::string writeOne(File *file)
{
unsigned int nExecutedLines = 0;
unsigned int nCodeLines = 0;
IConfiguration& conf = IConfiguration::getInstance();

// Compute filename, stripping paths
std::string filename = file->m_name;

if (conf.keyAsInt("codecov-full-paths") == 0)
{
std::string stripPath = conf.keyAsString("strip-path");
if (stripPath.size() == 0)
{
stripPath = m_commonPath;
}
size_t pos = filename.find(stripPath);
if (pos != std::string::npos && filename.size() > stripPath.size())
{
filename = filename.substr(stripPath.size() + 1);
}
}

// Produce each line score.
std::vector<std::string> lineEntries;
for (unsigned int n = 1; n < file->m_lastLineNr; n++)
{
if (m_reporter.lineIsCode(file->m_name, n))
{
IReporter::LineExecutionCount cnt = m_reporter.getLineExecutionCount(file->m_name, n);
std::string hitScore = "0";

if (m_maxPossibleHits == IFileParser::HITS_UNLIMITED || m_maxPossibleHits == IFileParser::HITS_SINGLE)
{
if (cnt.m_hits) {
hitScore = fmt("%u", cnt.m_hits);
}
}
else
{ // One or multiple for a line
hitScore = fmt("\"%u/%u\"", cnt.m_hits, cnt.m_possibleHits);
}
lineEntries.push_back(fmt(" \"%u\": %s", n, hitScore.c_str()));

nExecutedLines += !!cnt.m_hits;
nCodeLines++;
} else {
// We could emit '"linenum": null,' though that seems wasteful in storage.
}

// Update the execution count.
file->m_executedLines = nExecutedLines;
file->m_codeLines = nCodeLines;
}

std::string linesBlock;
for (std::vector<std::string>::const_iterator lineEntry = lineEntries.begin(); lineEntry != lineEntries.end(); ++lineEntry) {
if (!linesBlock.empty()) {
linesBlock += ",\n";
}
linesBlock += *lineEntry;
}
linesBlock += "\n";


std::string out =
" \"" + filename + "\": {\n" +
linesBlock +
" }";

return out;
}

std::string getHeader()
{
return "{\n"
" \"coverage\": {\n";
}

const std::string getFooter()
{
return " }\n"
"}\n";
}

std::string m_outFile;
IFileParser::PossibleHits m_maxPossibleHits;
};

} // namespace anonymous

IWriter &createCodecovWriter(IFileParser &parser, IReporter &reporter, const std::string &outFile)
{
return *new CodecovWriter(parser, reporter, outFile);
}
} // namespace kcov
13 changes: 13 additions & 0 deletions src/writers/codecov-writer.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

#include <string>

namespace kcov
{
class IFileParser;
class IReporter;
class IOutputHandler;

IWriter &createCodecovWriter(IFileParser &elf, IReporter &reporter,
const std::string &outFile);
}

0 comments on commit adc5653

Please sign in to comment.