/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VtsCoverageProcessor.h"
#include <dirent.h>
#include <fcntl.h>
#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include <google/protobuf/text_format.h>
#include <test/vts/proto/VtsReportMessage.pb.h>
using namespace std;
using google::protobuf::TextFormat;
namespace android {
namespace vts {
void VtsCoverageProcessor::ParseCoverageData(const string& coverage_file,
TestReportMessage* report_msg) {
ifstream in(coverage_file, std::ios::in);
string msg_str((istreambuf_iterator<char>(in)), istreambuf_iterator<char>());
if (!TextFormat::MergeFromString(msg_str, report_msg)) {
cerr << "Can't parse a given coverage report: " << msg_str << endl;
exit(-1);
}
}
void VtsCoverageProcessor::UpdateCoverageData(
const CoverageReportMessage& ref_msg,
CoverageReportMessage* msg_to_be_updated) {
if (ref_msg.file_path() == msg_to_be_updated->file_path()) {
for (int line = 0; line < ref_msg.line_coverage_vector_size(); line++) {
if (line < msg_to_be_updated->line_coverage_vector_size()) {
if (ref_msg.line_coverage_vector(line) > 0 &&
msg_to_be_updated->line_coverage_vector(line) > 0) {
msg_to_be_updated->set_line_coverage_vector(line, 0);
msg_to_be_updated->set_covered_line_count(
msg_to_be_updated->covered_line_count() - 1);
}
} else {
cout << "Reached the end of line_coverage_vector." << endl;
break;
}
}
// sanity check.
if (msg_to_be_updated->covered_line_count() < 0) {
cerr << __func__ << ": covered_line_count should not be negative."
<< endl;
exit(-1);
}
}
}
void VtsCoverageProcessor::MergeCoverage(const string& coverage_file_dir,
const string& merged_coverage_file) {
DIR* coverage_dir = opendir(coverage_file_dir.c_str());
if (coverage_dir == 0) {
cerr << __func__ << ": " << coverage_file_dir << " does not exist." << endl;
return;
}
TestReportMessage merged_coverage_report;
struct dirent* file;
while ((file = readdir(coverage_dir)) != NULL) {
if (file->d_type == DT_REG) {
string coverage_file = coverage_file_dir;
if (coverage_file_dir.substr(coverage_file_dir.size() - 1) != "/") {
coverage_file += "/";
}
string coverage_file_base_name = file->d_name;
coverage_file += coverage_file_base_name;
TestReportMessage coverage_report;
ParseCoverageData(coverage_file, &coverage_report);
for (const auto& cov : coverage_report.coverage()) {
bool seen_cov = false;
for (int i = 0; i < merged_coverage_report.coverage_size(); i++) {
if (merged_coverage_report.coverage(i).file_path() ==
cov.file_path()) {
MergeCoverageMsg(cov, merged_coverage_report.mutable_coverage(i));
seen_cov = true;
break;
}
}
if (!seen_cov) {
*merged_coverage_report.add_coverage() = cov;
}
}
}
}
PrintCoverageSummary(merged_coverage_report);
ofstream fout;
fout.open(merged_coverage_file);
fout << merged_coverage_report.DebugString();
fout.close();
}
void VtsCoverageProcessor::MergeCoverageMsg(
const CoverageReportMessage& ref_coverage_msg,
CoverageReportMessage* merged_coverage_msg) {
// sanity check.
if (ref_coverage_msg.file_path() != merged_coverage_msg->file_path()) {
cerr << "Trying to merge coverage data for different files." << endl;
exit(-1);
}
if (ref_coverage_msg.line_coverage_vector_size() !=
merged_coverage_msg->line_coverage_vector_size()) {
cerr << "Trying to merge coverage data with different lines."
<< "ref_coverage_msg: " << ref_coverage_msg.DebugString()
<< "merged_coverage_msg: " << merged_coverage_msg->DebugString()
<< endl;
exit(-1);
}
for (int i = 0; i < ref_coverage_msg.line_coverage_vector_size(); i++) {
if (i > merged_coverage_msg->line_coverage_vector_size() - 1) {
cerr << "Reach the end of coverage vector" << endl;
break;
}
int ref_line_count = ref_coverage_msg.line_coverage_vector(i);
int merged_line_count = merged_coverage_msg->line_coverage_vector(i);
if (ref_line_count > 0) {
if (merged_line_count == 0) {
merged_coverage_msg->set_covered_line_count(
merged_coverage_msg->covered_line_count() + 1);
}
merged_coverage_msg->set_line_coverage_vector(
i, merged_line_count + ref_line_count);
}
}
}
void VtsCoverageProcessor::CompareCoverage(const string& ref_msg_file,
const string& new_msg_file) {
TestReportMessage ref_coverage_report;
TestReportMessage new_coverage_report;
ParseCoverageData(ref_msg_file, &ref_coverage_report);
ParseCoverageData(new_msg_file, &new_coverage_report);
map<string, vector<int>> new_coverage_map;
for (const auto& new_coverage : new_coverage_report.coverage()) {
bool seen_file = false;
for (const auto& ref_coverage : ref_coverage_report.coverage()) {
if (new_coverage.file_path() == ref_coverage.file_path()) {
int line = 0;
for (; line < new_coverage.line_coverage_vector_size(); line++) {
if (new_coverage.line_coverage_vector(line) > 0 &&
ref_coverage.line_coverage_vector(line) == 0) {
if (new_coverage_map.find(new_coverage.file_path()) !=
new_coverage_map.end()) {
new_coverage_map[new_coverage.file_path()].push_back(line);
} else {
new_coverage_map.insert(std::pair<string, vector<int>>(
new_coverage.file_path(), vector<int>{line}));
}
}
}
seen_file = true;
break;
}
}
if (!seen_file) {
vector<int> new_line;
for (int line = 0; line < new_coverage.line_coverage_vector_size();
line++) {
if (new_coverage.line_coverage_vector(line) > 0) {
new_line.push_back(line);
}
}
new_coverage_map.insert(
std::pair<string, vector<int>>(new_coverage.file_path(), new_line));
}
}
for (auto it = new_coverage_map.begin(); it != new_coverage_map.end(); it++) {
cout << it->first << ": " << endl;
for (int covered_line : it->second) {
cout << covered_line << endl;
}
}
}
void VtsCoverageProcessor::GetSubsetCoverage(const string& ref_msg_file,
const string& full_msg_file,
const string& result_msg_file) {
TestReportMessage ref_coverage_report;
TestReportMessage full_coverage_report;
TestReportMessage result_coverage_report;
ParseCoverageData(ref_msg_file, &ref_coverage_report);
ParseCoverageData(full_msg_file, &full_coverage_report);
for (const auto& ref_coverage : ref_coverage_report.coverage()) {
bool seen_file = false;
for (const auto& coverage : full_coverage_report.coverage()) {
if (coverage.file_path() == ref_coverage.file_path()) {
*result_coverage_report.add_coverage() = coverage;
seen_file = true;
break;
}
}
if (!seen_file) {
cout << ": missing coverage for file " << ref_coverage.file_path()
<< endl;
CoverageReportMessage* empty_coverage =
result_coverage_report.add_coverage();
*empty_coverage = ref_coverage;
for (int line = 0; line < empty_coverage->line_coverage_vector_size();
line++) {
if (empty_coverage->line_coverage_vector(line) > 0) {
empty_coverage->set_line_coverage_vector(line, 0);
}
}
empty_coverage->set_covered_line_count(0);
}
}
PrintCoverageSummary(result_coverage_report);
ofstream fout;
fout.open(result_msg_file);
fout << result_coverage_report.DebugString();
fout.close();
}
void VtsCoverageProcessor::GetCoverageSummary(const string& coverage_msg_file) {
TestReportMessage coverage_report;
ParseCoverageData(coverage_msg_file, &coverage_report);
PrintCoverageSummary(coverage_report);
}
void VtsCoverageProcessor::PrintCoverageSummary(
const TestReportMessage& coverage_report) {
long total_lines_covered = GetTotalCoverageLine(coverage_report);
long total_code_lines = GetTotalCodeLine(coverage_report);
double coverage_rate = (double)total_lines_covered / total_code_lines;
cout << "total lines covered: " << total_lines_covered << endl;
cout << "total lines: " << total_code_lines << endl;
cout << "coverage rate: " << coverage_rate << endl;
}
long VtsCoverageProcessor::GetTotalCoverageLine(
const TestReportMessage& msg) const {
long total_coverage_line = 0;
for (const auto& coverage : msg.coverage()) {
total_coverage_line += coverage.covered_line_count();
}
return total_coverage_line;
}
long VtsCoverageProcessor::GetTotalCodeLine(
const TestReportMessage& msg) const {
long total_line = 0;
for (const auto& coverage : msg.coverage()) {
total_line += coverage.total_line_count();
}
return total_line;
}
} // namespace vts
} // namespace android