Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions Coverage/FileCoverageV2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include "FileCoverageV2.h"
#include "base64.h"

#include <cassert>
#include <format>

FileCoverageV2::FileCoverageV2(size_t nbLines) :
_nbLinesFile(nbLines)
{
_code.resize(nbLines);
}

FileCoverageV2::LineArray::value_type FileCoverageV2::encodeLine(bool isCode, const FileLineInfo& line)
{
LineArray::value_type code = 0;
if (isCode)
{
// | 15 | 14 | 13 ---- 0 |
// | is code | is partial | count |
if (line.DebugCount > 0)
{
code |= FileCoverageV2::maskIsCode;
_nbLinesCode += 1;
}
if (line.HitCount > 0)
{
if (line.DebugCount != line.HitCount)
{
code |= FileCoverageV2::maskIsPartial;
}
_nbLinesCovered += 1;
}
code |= std::min<FileCoverageV2::LineArray::value_type>(line.HitCount, FileCoverageV2::maskCount);
}
return code;
}

void FileCoverageV2::updateStats()
{
_nbLinesCovered = 0;
_nbLinesCode = 0;
for (const auto& line : _code)
{
if ((line & maskIsCode) == maskIsCode)
{
_nbLinesCode++;
if ((line & maskCount) > 0)
{
_nbLinesCovered++;
}
}
}
assert(_nbLinesCovered <= _nbLinesCode);
}

bool FileCoverageV2::merge(const FileCoverageV2& other)
{
if (_code.size() != other._code.size())
return false;

auto src = other._code.cbegin();

for (auto& line : _code)
{
const size_t count = (size_t) (line & maskCount) + (size_t) (*src & maskCount);

const bool isCode = ((line & maskIsCode) | (*src & maskIsCode)) == maskIsCode || count > 0;
const bool isPartial = (line & maskIsPartial) == maskIsPartial && (*src & maskIsPartial) == maskIsPartial;

line = (uint16_t) std::min<size_t>(count, maskCount);
line |= isCode ? maskIsCode : 0;
line |= isPartial ? maskIsPartial : 0;

++src;
}
updateStats();
return true;
}

void FileCoverageV2::writeHeader(std::ostream& ofs)
{
const std::string version("2.0");

ofs << R"(<?xml version="1.0" encoding="utf-8"?>)" << std::endl;
ofs << std::format(R"(<CppCoverage version="{0}">)", version) << std::endl;
}

void FileCoverageV2::openDirectory(std::ostream& ofs, const std::string& aDir)
{
ofs << std::format(R"( <directory path="{0}">)", aDir) << std::endl;
}

void FileCoverageV2::closeDirectory(std::ostream& ofs)
{
ofs << " </directory>" << std::endl;
}

void FileCoverageV2::writeFooter(std::ostream& ofs)
{
ofs << "</CppCoverage>" << std::endl;
}

void FileCoverageV2::write(const std::string& filepath, std::ostream& ofs) const
{
ofs << std::format(R"( <file path="{0}" md5="{1}">)", filepath, md5Code) << std::endl;
ofs << std::format(R"( <stats nbLinesInFile="{0}" nbLinesOfCode="{1}" nbLinesCovered="{2}"/>)", _nbLinesFile, _nbLinesCode, _nbLinesCovered) << std::endl;
ofs << R"( <coverage>)" << Base64::Encode(std::string(reinterpret_cast<const char*>(_code.data()), _code.size() * sizeof(LineArray::value_type))) << "</coverage>" << std::endl;
ofs << R"( </file>)" << std::endl;
}
111 changes: 10 additions & 101 deletions Coverage/FileCoverageV2.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
#pragma once

#include "base64.h"
#include "FileInfo.h"

#include <format>
#include <fstream>
#include <ostream>

struct FileCoverageV2
{
Expand All @@ -19,104 +17,15 @@ struct FileCoverageV2
size_t _nbLinesCovered = 0;
std::string md5Code;

FileCoverageV2(size_t nbLines = 0) :
_nbLinesFile(nbLines)
{
_code.resize(nbLines);
}
FileCoverageV2(size_t nbLines = 0);

LineArray::value_type encodeLine(bool isCode, const FileLineInfo& line)
{
LineArray::value_type code = 0;
if (isCode)
{
// | 15 | 14 | 13 ---- 0 |
// | is code | is partial | count |
if (line.DebugCount > 0)
{
code |= FileCoverageV2::maskIsCode;
_nbLinesCode += 1;
}
if (line.HitCount > 0)
{
if (line.DebugCount != line.HitCount)
{
code |= FileCoverageV2::maskIsPartial;
}
_nbLinesCovered += 1;
}
code |= std::min<FileCoverageV2::LineArray::value_type>(line.HitCount, FileCoverageV2::maskCount);
}
return code;
}
LineArray::value_type encodeLine(bool isCode, const FileLineInfo& line);
void updateStats();
bool merge(const FileCoverageV2& other);

void updateStats()
{
_nbLinesCovered = 0;
for (const auto& line : _code)
{
if ((line & maskIsCode) == maskIsCode)
{
if ((line & maskCount) > 0)
{
_nbLinesCovered += 1;
}
}
}
}

bool merge(const FileCoverageV2& other)
{
if (_code.size() != other._code.size())
return false;

auto src = other._code.cbegin();

for (auto& line : _code)
{
const size_t count = (size_t) (line & maskCount) + (size_t) (*src & maskCount);

const bool isCode = (line & maskIsCode) == maskIsCode;
const bool isPartial = (line & maskIsPartial) == maskIsPartial && (*src & maskIsPartial) == maskIsPartial;

line = (uint16_t) std::min<size_t>(count, maskCount);
line |= isCode ? maskIsCode : 0;
line |= isPartial ? maskIsPartial : 0;

++src;
}
updateStats();
return true;
}

static void writeHeader(std::ostream& ofs)
{
const std::string version("2.0");

ofs << R"(<?xml version="1.0" encoding="utf-8"?>)" << std::endl;
ofs << std::format(R"(<CppCoverage version="{0}">)", version) << std::endl;
}

static void openDirectory(std::ostream& ofs, const std::string& aDir)
{
ofs << std::format(R"( <directory path="{0}">)", aDir) << std::endl;
}

static void closeDirectory(std::ostream& ofs)
{
ofs << " </directory>" << std::endl;
}

static void writeFooter(std::ostream& ofs)
{
ofs << "</CppCoverage>" << std::endl;
}

void write(const std::string& filepath, std::ostream& ofs) const
{
ofs << std::format(R"( <file path="{0}" md5="{1}">)", filepath, md5Code) << std::endl;
ofs << std::format(R"( <stats nbLinesInFile="{0}" nbLinesOfCode="{1}" nbLinesCovered="{2}"/>)", _nbLinesFile, _nbLinesCode, _nbLinesCovered) << std::endl;
ofs << R"( <coverage>)" << Base64::Encode(std::string(reinterpret_cast<const char*>(_code.data()), _code.size() * sizeof(LineArray::value_type))) << "</coverage>" << std::endl;
ofs << R"( </file>)" << std::endl;
}
static void writeHeader(std::ostream& ofs);
static void openDirectory(std::ostream& ofs, const std::string& aDir);
static void closeDirectory(std::ostream& ofs);
static void writeFooter(std::ostream& ofs);
void write(const std::string& filepath, std::ostream& ofs) const;
};
3 changes: 2 additions & 1 deletion Coverage/Shared/Shared.vcxitems
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
Expand Down Expand Up @@ -45,6 +45,7 @@
<ItemGroup>
<ClCompile Include="$(MSBuildThisFileDirectory)..\Disassembler\ReachabilityAnalysis.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)..\Disassembler\X86DisassemblerDecoder.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)..\FileCoverageV2.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)..\FileInfo.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)..\FileSystem.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)..\Main.cpp" />
Expand Down
1 change: 1 addition & 0 deletions Coverage/Shared/Shared.vcxitems.filters
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<ClCompile Include="$(MSBuildThisFileDirectory)..\Main.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)..\MergeRunner.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)..\RuntimeNotifications.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)..\FileCoverageV2.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(MSBuildThisFileDirectory)..\base64.h" />
Expand Down
111 changes: 111 additions & 0 deletions Coverage/Test/FileCoverageV2Test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#include "CppUnitTest.h"
#include <SDKDDKVer.h>

#include "FileCoverageV2.h"

#ifndef NOMINMAX
# define NOMINMAX
# include <Windows.h>
#endif

#pragma warning(disable: 4091)
#include <DbgHelp.h>
#pragma warning(default: 4091)

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace Microsoft
{
namespace VisualStudio
{
namespace CppUnitTestFramework
{
template<> static std::wstring ToString<FileCoverageV2::LineArray>(const class FileCoverageV2::LineArray& t) { return L"FileCoverageV2::LineArray"; }
}
}
}

namespace TestNativeV2
{
TEST_CLASS(FileCoverage)
{
static constexpr auto max = FileCoverageV2::maskCount;
static constexpr auto c = FileCoverageV2::maskIsCode;
static constexpr auto p = FileCoverageV2::maskIsPartial;
public:

TEST_METHOD(WriteTest)
{
FileCoverageV2 coverage(9);
coverage.md5Code = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
coverage._code = { 0, 0, c, c, c | p | 1, c | 10000, c | 3000, 0, c | p | 1 };
coverage.updateStats();

Assert::AreEqual(9u, coverage._nbLinesFile);
Assert::AreEqual(6u, coverage._nbLinesCode);
Assert::AreEqual(4u, coverage._nbLinesCovered);

static constexpr char EXPECT_STREAM[] =
" <file path=\"filename\" md5=\"0123456789ABCDEFGHIJKLMNOPQRSTUV\">\n"
" <stats nbLinesInFile=\"9\" nbLinesOfCode=\"6\" nbLinesCovered=\"4\"/>\n"
" <coverage>AAAAAACAAIABwBCnuIsAAAHA</coverage>\n"
" </file>\n";

std::stringstream ss;
coverage.write("filename", ss);
Assert::AreEqual(EXPECT_STREAM, ss.str().c_str());
}

TEST_METHOD(MergeTest)
{
FileCoverageV2 coverage(9);
coverage.md5Code = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
coverage._code = { 0, 0, c, c, c | p | 1, c | 10000, c | 3000, 0, c };
coverage.updateStats();

FileCoverageV2 other(9);
other.md5Code = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
other._code = { 10, c | p | 20, c, c, c | p | 4, c | p | 50, c | 3000, c | 8, c | 2 };
other.updateStats();

auto result = coverage.merge(other);
Assert::IsTrue(result);

static constexpr char EXPECT_STREAM[] =
" <file path=\"filename\" md5=\"0123456789ABCDEFGHIJKLMNOPQRSTUV\">\n"
" <stats nbLinesInFile=\"9\" nbLinesOfCode=\"9\" nbLinesCovered=\"7\"/>\n"
" <coverage>CoAUgACAAIAFwEKncJcIgAKA</coverage>\n"
" </file>\n";

std::stringstream ss;
coverage.write("filename", ss);
Assert::AreEqual(EXPECT_STREAM, ss.str().c_str());
}

TEST_METHOD(MergeDifferentLinesCountTest)
{
FileCoverageV2 coverage(3);
coverage.md5Code = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
coverage._code = { 0, 0, c };
coverage.updateStats();

FileCoverageV2 other(9);
other.md5Code = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
other._code = { 10, c | p | 20, c, c, c | p | 4, c | p | 50, c | 3000, c | 8, c | 2 };
other.updateStats();

auto result = coverage.merge(other);
Assert::IsFalse(result);

static constexpr char EXPECT_STREAM[] =
" <file path=\"filename\" md5=\"0123456789ABCDEFGHIJKLMNOPQRSTUV\">\n"
" <stats nbLinesInFile=\"3\" nbLinesOfCode=\"1\" nbLinesCovered=\"0\"/>\n"
" <coverage>AAAAAACA</coverage>\n"
" </file>\n";

std::stringstream ss;
coverage.write("filename", ss);
Assert::AreEqual(EXPECT_STREAM, ss.str().c_str());
}
};
}
1 change: 1 addition & 0 deletions Coverage/Test/Test.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="FileCallbackInfoTest.cpp" />
<ClCompile Include="FileCoverageV2Test.cpp" />
<ClCompile Include="FileInfoTest.cpp" />
<ClCompile Include="md5Test.cpp" />
<ClCompile Include="MergeRunnerV1Test.cpp" />
Expand Down