xaizek / uncov (License: AGPLv3+) (since 2018-12-07)
Uncov(er) is a tool that collects and processes code coverage reports.
Commit 097f4deccb5f03a7f77f49c1fe41ce6ee5d2d00d

Take advantage of --stdout gcov option if present
It's significantly faster in terms of gcov run time (60% faster on Vifm
test case) and also for processing its results.
Author: xaizek
Author date (UTC): 2022-04-08 11:12
Committer name: xaizek
Committer date (UTC): 2022-04-08 11:12
Parent(s): aa075c155c07fed338b9401c15a9521eb341e23a
Signing key: 99DC5E4DB05F6BE2
Tree: 205d4524b48354f159691b2ac38558a77ef17f5d
File Lines added Lines deleted
src/GcovImporter.cpp 41 6
src/GcovImporter.hpp 18 1
tests/GcovImporter.cpp 50 3
File src/GcovImporter.cpp changed (mode: 100644) (index 86dc01c..3276d69)
... ... namespace pt = boost::property_tree;
54 54 static const char GcovJsonFormat[] = "--json-format"; static const char GcovJsonFormat[] = "--json-format";
55 55 //! `gcov` option to generate coverage in plain text format. //! `gcov` option to generate coverage in plain text format.
56 56 static const char GcovIntermediateFormat[] = "--intermediate-format"; static const char GcovIntermediateFormat[] = "--intermediate-format";
57 //! `gcov` option to dump coverage onto standard output.
58 static const char GcovStdOut[] = "--stdout";
57 59
58 60 //! First version of `gcov` which has broken `--preserve-paths` option. //! First version of `gcov` which has broken `--preserve-paths` option.
59 61 static int FirstBrokenGcovVersion = 8; static int FirstBrokenGcovVersion = 8;
 
... ... namespace {
112 114 } }
113 115
114 116 GcovInfo::GcovInfo() GcovInfo::GcovInfo()
115 : employBinning(true), jsonFormat(false), intermediateFormat(false)
117 : employBinning(true),
118 jsonFormat(false), intermediateFormat(false), stdOut(false)
116 119 { {
117 120 const std::regex optionRegex("--[-a-z]+"); const std::regex optionRegex("--[-a-z]+");
118 121 const std::regex versionRegex("gcov \\(GCC\\) (.*)"); const std::regex versionRegex("gcov \\(GCC\\) (.*)");
 
... ... GcovInfo::GcovInfo()
128 131 jsonFormat = true; jsonFormat = true;
129 132 } else if (str == GcovIntermediateFormat) { } else if (str == GcovIntermediateFormat) {
130 133 intermediateFormat = true; intermediateFormat = true;
134 } else if (str == GcovStdOut) {
135 stdOut = true;
131 136 } }
132 137
133 138 from += match.position() + match.length(); from += match.position() + match.length();
 
... ... GcovInfo::GcovInfo()
140 145 } }
141 146 } }
142 147
143 GcovInfo::GcovInfo(bool employBinning, bool jsonFormat,
144 bool intermediateFormat)
145 : employBinning(employBinning), jsonFormat(jsonFormat),
146 intermediateFormat(intermediateFormat)
148 GcovInfo::GcovInfo(bool employBinning,
149 bool jsonFormat,
150 bool intermediateFormat,
151 bool stdOut)
152 : employBinning(employBinning),
153 jsonFormat(jsonFormat),
154 intermediateFormat(intermediateFormat),
155 stdOut(stdOut)
147 156 { } { }
148 157
149 158 std::function<GcovImporter::runner_f> std::function<GcovImporter::runner_f>
 
... ... GcovImporter::getFiles() &&
255 264 void void
256 265 GcovImporter::importFiles(std::vector<fs::path> gcnoFiles) GcovImporter::importFiles(std::vector<fs::path> gcnoFiles)
257 266 { {
258 importAsFiles(std::move(gcnoFiles));
267 if (gcovInfo.hasJsonFormat() && gcovInfo.canPrintToStdOut()) {
268 importAsOutput(std::move(gcnoFiles));
269 } else {
270 importAsFiles(std::move(gcnoFiles));
271 }
272 }
273
274 void
275 GcovImporter::importAsOutput(std::vector<fs::path> gcnoFiles)
276 {
277 std::vector<std::string> cmd = {
278 "gcov", GcovJsonFormat, GcovStdOut, "--"
279 };
280
281 cmd.reserve(cmd.size() + gcnoFiles.size());
282 for (const fs::path &gcnoFile : gcnoFiles) {
283 cmd.push_back(gcnoFile.string());
284 }
285
286 const std::string output = getRunner()(std::move(cmd), "-");
287
288 for (std::string &json : split(output, '\n')) {
289 if (!json.empty()) {
290 std::istringstream iss(std::move(json));
291 parseGcovJson(iss);
292 }
293 }
259 294 } }
260 295
261 296 void void
File src/GcovImporter.hpp changed (mode: 100644) (index 865a8c1..0575e7e)
... ... public:
44 44 * @param employBinning No calling `gcov` with identically named files. * @param employBinning No calling `gcov` with identically named files.
45 45 * @param jsonFormat If JSON format is available. * @param jsonFormat If JSON format is available.
46 46 * @param intermediateFormat If plain text format is available. * @param intermediateFormat If plain text format is available.
47 * @param stdOut If dumping to stdout is available.
47 48 */ */
48 GcovInfo(bool employBinning, bool jsonFormat, bool intermediateFormat);
49 GcovInfo(bool employBinning,
50 bool jsonFormat, bool intermediateFormat, bool stdOut);
49 51
50 52 public: public:
51 53 /** /**
 
... ... public:
69 71 */ */
70 72 bool hasIntermediateFormat() const bool hasIntermediateFormat() const
71 73 { return intermediateFormat; } { return intermediateFormat; }
74 /**
75 * @brief Checks whether result can be dumped to standard output.
76 *
77 * @returns `true` if so.
78 */
79 bool canPrintToStdOut() const
80 { return jsonFormat && stdOut; }
72 81
73 82 private: private:
74 83 //! Whether `gcov` command doesn't handle identically-named files properly. //! Whether `gcov` command doesn't handle identically-named files properly.
 
... ... private:
77 86 bool jsonFormat; bool jsonFormat;
78 87 //! Whether plain text intermediate format is supported. //! Whether plain text intermediate format is supported.
79 88 bool intermediateFormat; bool intermediateFormat;
89 //! Whether result can be dumpted to stdout.
90 bool stdOut;
80 91 }; };
81 92
82 93 /** /**
 
... ... private:
133 144 * @param gcnoFiles Absolute paths to `*.gcno` files. * @param gcnoFiles Absolute paths to `*.gcno` files.
134 145 */ */
135 146 void importFiles(std::vector<boost::filesystem::path> gcnoFiles); void importFiles(std::vector<boost::filesystem::path> gcnoFiles);
147 /**
148 * @brief Calls `gcov` to print output to stdout and processes it.
149 *
150 * @param gcnoFiles Absolute paths to `*.gcno` files.
151 */
152 void importAsOutput(std::vector<boost::filesystem::path> gcnoFiles);
136 153 /** /**
137 154 * @brief Calls `gcov` to generate output files and processes them. * @brief Calls `gcov` to generate output files and processes them.
138 155 * *
File tests/GcovImporter.cpp changed (mode: 100644) (index e134e09..e1fa923)
... ... namespace fs = boost::filesystem;
34 34 TEST_CASE("Need support for at least one intermediate format", "[GcovImporter]") TEST_CASE("Need support for at least one intermediate format", "[GcovImporter]")
35 35 { {
36 36 GcovInfo gcovInfo(/*employBinning=*/false, /*jsonFormat=*/false, GcovInfo gcovInfo(/*employBinning=*/false, /*jsonFormat=*/false,
37 /*intermediateFormat=*/false);
37 /*intermediateFormat=*/false, /*stdOut=*/false);
38 38 REQUIRE_THROWS_AS(GcovImporter("", "", {}, "", gcovInfo), REQUIRE_THROWS_AS(GcovImporter("", "", {}, "", gcovInfo),
39 39 const std::runtime_error &); const std::runtime_error &);
40 40 } }
 
... ... TEST_CASE("Plain text format parsed and binning is performed", "[GcovImporter]")
73 73 }; };
74 74
75 75 GcovInfo gcovInfo(/*employBinning=*/true, /*jsonFormat=*/false, GcovInfo gcovInfo(/*employBinning=*/true, /*jsonFormat=*/false,
76 /*intermediateFormat=*/true);
76 /*intermediateFormat=*/true, /*stdOut=*/false);
77 77 std::vector<File> files = std::vector<File> files =
78 78 GcovImporter(tempDirPath, tempDirPath, {}, tempDirPath, GcovImporter(tempDirPath, tempDirPath, {}, tempDirPath,
79 79 gcovInfo).getFiles(); gcovInfo).getFiles();
 
... ... TEST_CASE("JSON format is parsed", "[GcovImporter]")
119 119 }; };
120 120
121 121 GcovInfo gcovInfo(/*employBinning=*/false, /*jsonFormat=*/true, GcovInfo gcovInfo(/*employBinning=*/false, /*jsonFormat=*/true,
122 /*intermediateFormat=*/true);
122 /*intermediateFormat=*/true, /*stdOut=*/false);
123 std::vector<File> files =
124 GcovImporter(tempDirPath, tempDirPath, {}, tempDirPath,
125 gcovInfo).getFiles();
126
127 REQUIRE(files.size() == 1);
128 CHECK(files[0].getCoveredCount() == 1);
129 CHECK(files[0].getMissedCount() == 1);
130 }
131
132 TEST_CASE("JSON on stdout is parsed", "[GcovImporter]")
133 {
134 TempDir tempDir("gcovimporter");
135 std::string tempDirPath = tempDir;
136
137 std::ofstream{tempDirPath + "/file.gcno"} << "\n";
138
139 auto runner = [&tempDirPath](std::vector<std::string> &&/*cmd*/,
140 const std::string &from) {
141 CHECK(from == "-");
142
143 std::string json = R"({
144 "current_working_directory": ")" + tempDirPath + R"(",
145 "files": [
146 {
147 "file": "file.gcno",
148 "lines": [
149 { "line_number": 1, "count": 1 },
150 { "line_number": 2, "count": 0 }
151 ]
152 },
153 {
154 "file": "/usr/include/whatever.h",
155 "lines": [ { "line_number": 1, "count": 1 } ]
156 }
157 ]
158 })";
159 removeChars(json, '\n');
160
161 return json;
162 };
163 auto prevRunner = GcovImporter::setRunner(runner);
164 BOOST_SCOPE_EXIT_ALL(prevRunner) {
165 GcovImporter::setRunner(prevRunner);
166 };
167
168 GcovInfo gcovInfo(/*employBinning=*/false, /*jsonFormat=*/true,
169 /*intermediateFormat=*/true, /*stdOut=*/true);
123 170 std::vector<File> files = std::vector<File> files =
124 171 GcovImporter(tempDirPath, tempDirPath, {}, tempDirPath, GcovImporter(tempDirPath, tempDirPath, {}, tempDirPath,
125 172 gcovInfo).getFiles(); gcovInfo).getFiles();
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/uncov

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/uncov

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master