235 lines
7.8 KiB
C++
235 lines
7.8 KiB
C++
// Copyright 2019 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "testing/perf/luci_test_result.h"
|
|
|
|
#include <utility>
|
|
|
|
#include "base/check.h"
|
|
#include "base/files/file_util.h"
|
|
#include "base/json/json_writer.h"
|
|
#include "base/strings/string_util.h"
|
|
#include "base/strings/stringprintf.h"
|
|
#include "base/values.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace perf_test {
|
|
|
|
namespace {
|
|
|
|
constexpr char kKeyFilePath[] = "filePath";
|
|
constexpr char kKeyContents[] = "contents";
|
|
constexpr char kKeyContentType[] = "contentType";
|
|
constexpr char kKeyTestResult[] = "testResult";
|
|
constexpr char kKeyTestPath[] = "testPath";
|
|
constexpr char kKeyVariant[] = "variant";
|
|
constexpr char kKeyStatus[] = "status";
|
|
constexpr char kKeyExpected[] = "expected";
|
|
constexpr char kKeyStartTime[] = "startTime";
|
|
constexpr char kKeyRunDuration[] = "runDuration";
|
|
constexpr char kKeyOutputArtifacts[] = "outputArtifacts";
|
|
constexpr char kKeyTags[] = "tags";
|
|
constexpr char kKeyKey[] = "key";
|
|
constexpr char kKeyValue[] = "value";
|
|
|
|
// Returns iso timeformat string of |time| in UTC.
|
|
std::string ToUtcIsoTime(base::Time time) {
|
|
base::Time::Exploded utc_exploded;
|
|
time.UTCExplode(&utc_exploded);
|
|
return base::StringPrintf(
|
|
"%d-%02d-%02dT%02d:%02d:%02d.%03dZ", utc_exploded.year,
|
|
utc_exploded.month, utc_exploded.day_of_month, utc_exploded.hour,
|
|
utc_exploded.minute, utc_exploded.second, utc_exploded.millisecond);
|
|
}
|
|
|
|
std::string ToString(LuciTestResult::Status status) {
|
|
using Status = LuciTestResult::Status;
|
|
switch (status) {
|
|
case Status::kUnspecified:
|
|
return "UNSPECIFIED";
|
|
case Status::kPass:
|
|
return "PASS";
|
|
case Status::kFail:
|
|
return "FAIL";
|
|
case Status::kCrash:
|
|
return "CRASH";
|
|
case Status::kAbort:
|
|
return "ABORT";
|
|
case Status::kSkip:
|
|
return "SKIP";
|
|
}
|
|
}
|
|
|
|
base::Value ToValue(const LuciTestResult::Artifact& artifact) {
|
|
// One and only one of the two optional fields must have value.
|
|
DCHECK(artifact.file_path.has_value() != artifact.contents.has_value());
|
|
|
|
base::Value::Dict dict;
|
|
|
|
if (artifact.file_path.has_value()) {
|
|
dict.Set(kKeyFilePath, artifact.file_path->AsUTF8Unsafe());
|
|
} else {
|
|
DCHECK(artifact.contents.has_value());
|
|
dict.Set(kKeyContents, artifact.contents.value());
|
|
}
|
|
|
|
dict.Set(kKeyContentType, artifact.content_type);
|
|
return base::Value(std::move(dict));
|
|
}
|
|
|
|
base::Value ToValue(const LuciTestResult& result) {
|
|
base::Value::Dict test_report;
|
|
|
|
base::Value::Dict* test_result = test_report.EnsureDict(kKeyTestResult);
|
|
test_result->Set(kKeyTestPath, result.test_path());
|
|
|
|
if (!result.extra_variant_pairs().empty()) {
|
|
base::Value::Dict* variant_dict = test_result->EnsureDict(kKeyVariant);
|
|
for (const auto& pair : result.extra_variant_pairs())
|
|
variant_dict->Set(pair.first, pair.second);
|
|
}
|
|
|
|
test_result->Set(kKeyStatus, ToString(result.status()));
|
|
test_result->Set(kKeyExpected, result.is_expected());
|
|
|
|
if (!result.start_time().is_null()) {
|
|
test_result->Set(kKeyStartTime, ToUtcIsoTime(result.start_time()));
|
|
}
|
|
if (!result.duration().is_zero()) {
|
|
test_result->Set(
|
|
kKeyRunDuration,
|
|
base::StringPrintf("%.2fs", result.duration().InSecondsF()));
|
|
}
|
|
|
|
if (!result.output_artifacts().empty()) {
|
|
base::Value::Dict* artifacts_dict =
|
|
test_result->EnsureDict(kKeyOutputArtifacts);
|
|
for (const auto& pair : result.output_artifacts())
|
|
artifacts_dict->Set(pair.first, ToValue(pair.second));
|
|
}
|
|
|
|
if (!result.tags().empty()) {
|
|
base::Value::List* tags_list = test_result->EnsureList(kKeyTags);
|
|
for (const auto& tag : result.tags()) {
|
|
base::Value::Dict tag_dict;
|
|
tag_dict.Set(kKeyKey, tag.key);
|
|
tag_dict.Set(kKeyValue, tag.value);
|
|
tags_list->Append(std::move(tag_dict));
|
|
}
|
|
}
|
|
|
|
return base::Value(std::move(test_report));
|
|
}
|
|
|
|
std::string ToJson(const LuciTestResult& result) {
|
|
std::string json;
|
|
CHECK(base::JSONWriter::Write(ToValue(result), &json));
|
|
return json;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// LuciTestResult::Artifact
|
|
|
|
LuciTestResult::Artifact::Artifact() = default;
|
|
LuciTestResult::Artifact::Artifact(const Artifact& other) = default;
|
|
LuciTestResult::Artifact::Artifact(const base::FilePath file_path,
|
|
const std::string& content_type)
|
|
: file_path(file_path), content_type(content_type) {}
|
|
LuciTestResult::Artifact::Artifact(const std::string& contents,
|
|
const std::string& content_type)
|
|
: contents(contents), content_type(content_type) {}
|
|
LuciTestResult::Artifact::~Artifact() = default;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// LuciTestResult
|
|
|
|
LuciTestResult::LuciTestResult() = default;
|
|
LuciTestResult::LuciTestResult(const LuciTestResult& other) = default;
|
|
LuciTestResult::LuciTestResult(LuciTestResult&& other) = default;
|
|
LuciTestResult::~LuciTestResult() = default;
|
|
|
|
// static
|
|
LuciTestResult LuciTestResult::CreateForGTest() {
|
|
LuciTestResult result;
|
|
|
|
const testing::TestInfo* const test_info =
|
|
testing::UnitTest::GetInstance()->current_test_info();
|
|
|
|
std::string test_case_name = test_info->name();
|
|
std::string param_index;
|
|
|
|
// If there is a "/", extract |param_index| after it and strip it from
|
|
// |test_case_name|.
|
|
auto pos = test_case_name.rfind('/');
|
|
if (pos != std::string::npos) {
|
|
param_index = test_case_name.substr(pos + 1);
|
|
test_case_name.resize(pos);
|
|
}
|
|
|
|
result.set_test_path(base::StringPrintf("%s.%s", test_info->test_suite_name(),
|
|
test_case_name.c_str()));
|
|
|
|
if (test_info->type_param())
|
|
result.AddVariant("param/instantiation", test_info->type_param());
|
|
|
|
if (!param_index.empty())
|
|
result.AddVariant("param/index", param_index);
|
|
|
|
result.set_status(test_info->result()->Passed()
|
|
? LuciTestResult::Status::kPass
|
|
: LuciTestResult::Status::kFail);
|
|
// Assumes that the expectation is test passing.
|
|
result.set_is_expected(result.status() == LuciTestResult::Status::kPass);
|
|
|
|
// Start timestamp and duration is not set before the test run finishes,
|
|
// e.g. when called from PerformanceTest::TearDownOnMainThread.
|
|
if (test_info->result()->start_timestamp()) {
|
|
result.set_start_time(base::Time::FromTimeT(
|
|
static_cast<time_t>(test_info->result()->start_timestamp() / 1000)));
|
|
result.set_duration(
|
|
base::Milliseconds(test_info->result()->elapsed_time()));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void LuciTestResult::AddVariant(const std::string& key,
|
|
const std::string& value) {
|
|
auto result = extra_variant_pairs_.insert({key, value});
|
|
DCHECK(result.second);
|
|
}
|
|
|
|
void LuciTestResult::AddOutputArtifactFile(const std::string& artifact_name,
|
|
const base::FilePath& file_path,
|
|
const std::string& content_type) {
|
|
Artifact artifact(file_path, content_type);
|
|
auto insert_result = output_artifacts_.insert(
|
|
std::make_pair(artifact_name, std::move(artifact)));
|
|
DCHECK(insert_result.second);
|
|
}
|
|
|
|
void LuciTestResult::AddOutputArtifactContents(
|
|
const std::string& artifact_name,
|
|
const std::string& contents,
|
|
const std::string& content_type) {
|
|
Artifact artifact(contents, content_type);
|
|
auto insert_result = output_artifacts_.insert(
|
|
std::make_pair(artifact_name, std::move(artifact)));
|
|
DCHECK(insert_result.second);
|
|
}
|
|
|
|
void LuciTestResult::AddTag(const std::string& key, const std::string& value) {
|
|
tags_.emplace_back(Tag{key, value});
|
|
}
|
|
|
|
void LuciTestResult::WriteToFile(const base::FilePath& result_file) const {
|
|
const std::string json = ToJson(*this);
|
|
const int json_size = json.size();
|
|
CHECK(WriteFile(result_file, json.data(), json_size) == json_size);
|
|
}
|
|
|
|
} // namespace perf_test
|