1*6777b538SAndroid Build Coastguard Worker // Copyright 2019 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker #include "testing/perf/luci_test_result.h"
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker #include <utility>
8*6777b538SAndroid Build Coastguard Worker
9*6777b538SAndroid Build Coastguard Worker #include "base/check.h"
10*6777b538SAndroid Build Coastguard Worker #include "base/files/file_util.h"
11*6777b538SAndroid Build Coastguard Worker #include "base/i18n/time_formatting.h"
12*6777b538SAndroid Build Coastguard Worker #include "base/json/json_writer.h"
13*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_util.h"
14*6777b538SAndroid Build Coastguard Worker #include "base/strings/stringprintf.h"
15*6777b538SAndroid Build Coastguard Worker #include "base/values.h"
16*6777b538SAndroid Build Coastguard Worker #include "testing/gtest/include/gtest/gtest.h"
17*6777b538SAndroid Build Coastguard Worker
18*6777b538SAndroid Build Coastguard Worker namespace perf_test {
19*6777b538SAndroid Build Coastguard Worker
20*6777b538SAndroid Build Coastguard Worker namespace {
21*6777b538SAndroid Build Coastguard Worker
22*6777b538SAndroid Build Coastguard Worker constexpr char kKeyFilePath[] = "filePath";
23*6777b538SAndroid Build Coastguard Worker constexpr char kKeyContents[] = "contents";
24*6777b538SAndroid Build Coastguard Worker constexpr char kKeyContentType[] = "contentType";
25*6777b538SAndroid Build Coastguard Worker constexpr char kKeyTestResult[] = "testResult";
26*6777b538SAndroid Build Coastguard Worker constexpr char kKeyTestPath[] = "testPath";
27*6777b538SAndroid Build Coastguard Worker constexpr char kKeyVariant[] = "variant";
28*6777b538SAndroid Build Coastguard Worker constexpr char kKeyStatus[] = "status";
29*6777b538SAndroid Build Coastguard Worker constexpr char kKeyExpected[] = "expected";
30*6777b538SAndroid Build Coastguard Worker constexpr char kKeyStartTime[] = "startTime";
31*6777b538SAndroid Build Coastguard Worker constexpr char kKeyRunDuration[] = "runDuration";
32*6777b538SAndroid Build Coastguard Worker constexpr char kKeyOutputArtifacts[] = "outputArtifacts";
33*6777b538SAndroid Build Coastguard Worker constexpr char kKeyTags[] = "tags";
34*6777b538SAndroid Build Coastguard Worker constexpr char kKeyKey[] = "key";
35*6777b538SAndroid Build Coastguard Worker constexpr char kKeyValue[] = "value";
36*6777b538SAndroid Build Coastguard Worker
ToString(LuciTestResult::Status status)37*6777b538SAndroid Build Coastguard Worker std::string ToString(LuciTestResult::Status status) {
38*6777b538SAndroid Build Coastguard Worker using Status = LuciTestResult::Status;
39*6777b538SAndroid Build Coastguard Worker switch (status) {
40*6777b538SAndroid Build Coastguard Worker case Status::kUnspecified:
41*6777b538SAndroid Build Coastguard Worker return "UNSPECIFIED";
42*6777b538SAndroid Build Coastguard Worker case Status::kPass:
43*6777b538SAndroid Build Coastguard Worker return "PASS";
44*6777b538SAndroid Build Coastguard Worker case Status::kFail:
45*6777b538SAndroid Build Coastguard Worker return "FAIL";
46*6777b538SAndroid Build Coastguard Worker case Status::kCrash:
47*6777b538SAndroid Build Coastguard Worker return "CRASH";
48*6777b538SAndroid Build Coastguard Worker case Status::kAbort:
49*6777b538SAndroid Build Coastguard Worker return "ABORT";
50*6777b538SAndroid Build Coastguard Worker case Status::kSkip:
51*6777b538SAndroid Build Coastguard Worker return "SKIP";
52*6777b538SAndroid Build Coastguard Worker }
53*6777b538SAndroid Build Coastguard Worker }
54*6777b538SAndroid Build Coastguard Worker
ToValue(const LuciTestResult::Artifact & artifact)55*6777b538SAndroid Build Coastguard Worker base::Value ToValue(const LuciTestResult::Artifact& artifact) {
56*6777b538SAndroid Build Coastguard Worker // One and only one of the two optional fields must have value.
57*6777b538SAndroid Build Coastguard Worker DCHECK(artifact.file_path.has_value() != artifact.contents.has_value());
58*6777b538SAndroid Build Coastguard Worker
59*6777b538SAndroid Build Coastguard Worker base::Value::Dict dict;
60*6777b538SAndroid Build Coastguard Worker
61*6777b538SAndroid Build Coastguard Worker if (artifact.file_path.has_value()) {
62*6777b538SAndroid Build Coastguard Worker dict.Set(kKeyFilePath, artifact.file_path->AsUTF8Unsafe());
63*6777b538SAndroid Build Coastguard Worker } else {
64*6777b538SAndroid Build Coastguard Worker DCHECK(artifact.contents.has_value());
65*6777b538SAndroid Build Coastguard Worker dict.Set(kKeyContents, artifact.contents.value());
66*6777b538SAndroid Build Coastguard Worker }
67*6777b538SAndroid Build Coastguard Worker
68*6777b538SAndroid Build Coastguard Worker dict.Set(kKeyContentType, artifact.content_type);
69*6777b538SAndroid Build Coastguard Worker return base::Value(std::move(dict));
70*6777b538SAndroid Build Coastguard Worker }
71*6777b538SAndroid Build Coastguard Worker
ToValue(const LuciTestResult & result)72*6777b538SAndroid Build Coastguard Worker base::Value ToValue(const LuciTestResult& result) {
73*6777b538SAndroid Build Coastguard Worker base::Value::Dict test_report;
74*6777b538SAndroid Build Coastguard Worker
75*6777b538SAndroid Build Coastguard Worker base::Value::Dict* test_result = test_report.EnsureDict(kKeyTestResult);
76*6777b538SAndroid Build Coastguard Worker test_result->Set(kKeyTestPath, result.test_path());
77*6777b538SAndroid Build Coastguard Worker
78*6777b538SAndroid Build Coastguard Worker if (!result.extra_variant_pairs().empty()) {
79*6777b538SAndroid Build Coastguard Worker base::Value::Dict* variant_dict = test_result->EnsureDict(kKeyVariant);
80*6777b538SAndroid Build Coastguard Worker for (const auto& pair : result.extra_variant_pairs())
81*6777b538SAndroid Build Coastguard Worker variant_dict->Set(pair.first, pair.second);
82*6777b538SAndroid Build Coastguard Worker }
83*6777b538SAndroid Build Coastguard Worker
84*6777b538SAndroid Build Coastguard Worker test_result->Set(kKeyStatus, ToString(result.status()));
85*6777b538SAndroid Build Coastguard Worker test_result->Set(kKeyExpected, result.is_expected());
86*6777b538SAndroid Build Coastguard Worker
87*6777b538SAndroid Build Coastguard Worker if (!result.start_time().is_null()) {
88*6777b538SAndroid Build Coastguard Worker test_result->Set(kKeyStartTime,
89*6777b538SAndroid Build Coastguard Worker base::TimeFormatAsIso8601(result.start_time()));
90*6777b538SAndroid Build Coastguard Worker }
91*6777b538SAndroid Build Coastguard Worker if (!result.duration().is_zero()) {
92*6777b538SAndroid Build Coastguard Worker test_result->Set(
93*6777b538SAndroid Build Coastguard Worker kKeyRunDuration,
94*6777b538SAndroid Build Coastguard Worker base::StringPrintf("%.2fs", result.duration().InSecondsF()));
95*6777b538SAndroid Build Coastguard Worker }
96*6777b538SAndroid Build Coastguard Worker
97*6777b538SAndroid Build Coastguard Worker if (!result.output_artifacts().empty()) {
98*6777b538SAndroid Build Coastguard Worker base::Value::Dict* artifacts_dict =
99*6777b538SAndroid Build Coastguard Worker test_result->EnsureDict(kKeyOutputArtifacts);
100*6777b538SAndroid Build Coastguard Worker for (const auto& pair : result.output_artifacts())
101*6777b538SAndroid Build Coastguard Worker artifacts_dict->Set(pair.first, ToValue(pair.second));
102*6777b538SAndroid Build Coastguard Worker }
103*6777b538SAndroid Build Coastguard Worker
104*6777b538SAndroid Build Coastguard Worker if (!result.tags().empty()) {
105*6777b538SAndroid Build Coastguard Worker base::Value::List* tags_list = test_result->EnsureList(kKeyTags);
106*6777b538SAndroid Build Coastguard Worker for (const auto& tag : result.tags()) {
107*6777b538SAndroid Build Coastguard Worker base::Value::Dict tag_dict;
108*6777b538SAndroid Build Coastguard Worker tag_dict.Set(kKeyKey, tag.key);
109*6777b538SAndroid Build Coastguard Worker tag_dict.Set(kKeyValue, tag.value);
110*6777b538SAndroid Build Coastguard Worker tags_list->Append(std::move(tag_dict));
111*6777b538SAndroid Build Coastguard Worker }
112*6777b538SAndroid Build Coastguard Worker }
113*6777b538SAndroid Build Coastguard Worker
114*6777b538SAndroid Build Coastguard Worker return base::Value(std::move(test_report));
115*6777b538SAndroid Build Coastguard Worker }
116*6777b538SAndroid Build Coastguard Worker
ToJson(const LuciTestResult & result)117*6777b538SAndroid Build Coastguard Worker std::string ToJson(const LuciTestResult& result) {
118*6777b538SAndroid Build Coastguard Worker std::string json;
119*6777b538SAndroid Build Coastguard Worker CHECK(base::JSONWriter::Write(ToValue(result), &json));
120*6777b538SAndroid Build Coastguard Worker return json;
121*6777b538SAndroid Build Coastguard Worker }
122*6777b538SAndroid Build Coastguard Worker
123*6777b538SAndroid Build Coastguard Worker } // namespace
124*6777b538SAndroid Build Coastguard Worker
125*6777b538SAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
126*6777b538SAndroid Build Coastguard Worker // LuciTestResult::Artifact
127*6777b538SAndroid Build Coastguard Worker
128*6777b538SAndroid Build Coastguard Worker LuciTestResult::Artifact::Artifact() = default;
129*6777b538SAndroid Build Coastguard Worker LuciTestResult::Artifact::Artifact(const Artifact& other) = default;
Artifact(const base::FilePath file_path,const std::string & content_type)130*6777b538SAndroid Build Coastguard Worker LuciTestResult::Artifact::Artifact(const base::FilePath file_path,
131*6777b538SAndroid Build Coastguard Worker const std::string& content_type)
132*6777b538SAndroid Build Coastguard Worker : file_path(file_path), content_type(content_type) {}
Artifact(const std::string & contents,const std::string & content_type)133*6777b538SAndroid Build Coastguard Worker LuciTestResult::Artifact::Artifact(const std::string& contents,
134*6777b538SAndroid Build Coastguard Worker const std::string& content_type)
135*6777b538SAndroid Build Coastguard Worker : contents(contents), content_type(content_type) {}
136*6777b538SAndroid Build Coastguard Worker LuciTestResult::Artifact::~Artifact() = default;
137*6777b538SAndroid Build Coastguard Worker
138*6777b538SAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
139*6777b538SAndroid Build Coastguard Worker // LuciTestResult
140*6777b538SAndroid Build Coastguard Worker
141*6777b538SAndroid Build Coastguard Worker LuciTestResult::LuciTestResult() = default;
142*6777b538SAndroid Build Coastguard Worker LuciTestResult::LuciTestResult(const LuciTestResult& other) = default;
143*6777b538SAndroid Build Coastguard Worker LuciTestResult::LuciTestResult(LuciTestResult&& other) = default;
144*6777b538SAndroid Build Coastguard Worker LuciTestResult::~LuciTestResult() = default;
145*6777b538SAndroid Build Coastguard Worker
146*6777b538SAndroid Build Coastguard Worker // static
CreateForGTest()147*6777b538SAndroid Build Coastguard Worker LuciTestResult LuciTestResult::CreateForGTest() {
148*6777b538SAndroid Build Coastguard Worker LuciTestResult result;
149*6777b538SAndroid Build Coastguard Worker
150*6777b538SAndroid Build Coastguard Worker const testing::TestInfo* const test_info =
151*6777b538SAndroid Build Coastguard Worker testing::UnitTest::GetInstance()->current_test_info();
152*6777b538SAndroid Build Coastguard Worker
153*6777b538SAndroid Build Coastguard Worker std::string test_case_name = test_info->name();
154*6777b538SAndroid Build Coastguard Worker std::string param_index;
155*6777b538SAndroid Build Coastguard Worker
156*6777b538SAndroid Build Coastguard Worker // If there is a "/", extract |param_index| after it and strip it from
157*6777b538SAndroid Build Coastguard Worker // |test_case_name|.
158*6777b538SAndroid Build Coastguard Worker auto pos = test_case_name.rfind('/');
159*6777b538SAndroid Build Coastguard Worker if (pos != std::string::npos) {
160*6777b538SAndroid Build Coastguard Worker param_index = test_case_name.substr(pos + 1);
161*6777b538SAndroid Build Coastguard Worker test_case_name.resize(pos);
162*6777b538SAndroid Build Coastguard Worker }
163*6777b538SAndroid Build Coastguard Worker
164*6777b538SAndroid Build Coastguard Worker result.set_test_path(base::StringPrintf("%s.%s", test_info->test_suite_name(),
165*6777b538SAndroid Build Coastguard Worker test_case_name.c_str()));
166*6777b538SAndroid Build Coastguard Worker
167*6777b538SAndroid Build Coastguard Worker if (test_info->type_param())
168*6777b538SAndroid Build Coastguard Worker result.AddVariant("param/instantiation", test_info->type_param());
169*6777b538SAndroid Build Coastguard Worker
170*6777b538SAndroid Build Coastguard Worker if (!param_index.empty())
171*6777b538SAndroid Build Coastguard Worker result.AddVariant("param/index", param_index);
172*6777b538SAndroid Build Coastguard Worker
173*6777b538SAndroid Build Coastguard Worker result.set_status(test_info->result()->Passed()
174*6777b538SAndroid Build Coastguard Worker ? LuciTestResult::Status::kPass
175*6777b538SAndroid Build Coastguard Worker : LuciTestResult::Status::kFail);
176*6777b538SAndroid Build Coastguard Worker // Assumes that the expectation is test passing.
177*6777b538SAndroid Build Coastguard Worker result.set_is_expected(result.status() == LuciTestResult::Status::kPass);
178*6777b538SAndroid Build Coastguard Worker
179*6777b538SAndroid Build Coastguard Worker // Start timestamp and duration is not set before the test run finishes,
180*6777b538SAndroid Build Coastguard Worker // e.g. when called from PerformanceTest::TearDownOnMainThread.
181*6777b538SAndroid Build Coastguard Worker if (test_info->result()->start_timestamp()) {
182*6777b538SAndroid Build Coastguard Worker result.set_start_time(base::Time::FromTimeT(
183*6777b538SAndroid Build Coastguard Worker static_cast<time_t>(test_info->result()->start_timestamp() / 1000)));
184*6777b538SAndroid Build Coastguard Worker result.set_duration(
185*6777b538SAndroid Build Coastguard Worker base::Milliseconds(test_info->result()->elapsed_time()));
186*6777b538SAndroid Build Coastguard Worker }
187*6777b538SAndroid Build Coastguard Worker
188*6777b538SAndroid Build Coastguard Worker return result;
189*6777b538SAndroid Build Coastguard Worker }
190*6777b538SAndroid Build Coastguard Worker
AddVariant(const std::string & key,const std::string & value)191*6777b538SAndroid Build Coastguard Worker void LuciTestResult::AddVariant(const std::string& key,
192*6777b538SAndroid Build Coastguard Worker const std::string& value) {
193*6777b538SAndroid Build Coastguard Worker auto result = extra_variant_pairs_.insert({key, value});
194*6777b538SAndroid Build Coastguard Worker DCHECK(result.second);
195*6777b538SAndroid Build Coastguard Worker }
196*6777b538SAndroid Build Coastguard Worker
AddOutputArtifactFile(const std::string & artifact_name,const base::FilePath & file_path,const std::string & content_type)197*6777b538SAndroid Build Coastguard Worker void LuciTestResult::AddOutputArtifactFile(const std::string& artifact_name,
198*6777b538SAndroid Build Coastguard Worker const base::FilePath& file_path,
199*6777b538SAndroid Build Coastguard Worker const std::string& content_type) {
200*6777b538SAndroid Build Coastguard Worker Artifact artifact(file_path, content_type);
201*6777b538SAndroid Build Coastguard Worker auto insert_result = output_artifacts_.insert(
202*6777b538SAndroid Build Coastguard Worker std::make_pair(artifact_name, std::move(artifact)));
203*6777b538SAndroid Build Coastguard Worker DCHECK(insert_result.second);
204*6777b538SAndroid Build Coastguard Worker }
205*6777b538SAndroid Build Coastguard Worker
AddOutputArtifactContents(const std::string & artifact_name,const std::string & contents,const std::string & content_type)206*6777b538SAndroid Build Coastguard Worker void LuciTestResult::AddOutputArtifactContents(
207*6777b538SAndroid Build Coastguard Worker const std::string& artifact_name,
208*6777b538SAndroid Build Coastguard Worker const std::string& contents,
209*6777b538SAndroid Build Coastguard Worker const std::string& content_type) {
210*6777b538SAndroid Build Coastguard Worker Artifact artifact(contents, content_type);
211*6777b538SAndroid Build Coastguard Worker auto insert_result = output_artifacts_.insert(
212*6777b538SAndroid Build Coastguard Worker std::make_pair(artifact_name, std::move(artifact)));
213*6777b538SAndroid Build Coastguard Worker DCHECK(insert_result.second);
214*6777b538SAndroid Build Coastguard Worker }
215*6777b538SAndroid Build Coastguard Worker
AddTag(const std::string & key,const std::string & value)216*6777b538SAndroid Build Coastguard Worker void LuciTestResult::AddTag(const std::string& key, const std::string& value) {
217*6777b538SAndroid Build Coastguard Worker tags_.emplace_back(Tag{key, value});
218*6777b538SAndroid Build Coastguard Worker }
219*6777b538SAndroid Build Coastguard Worker
WriteToFile(const base::FilePath & result_file) const220*6777b538SAndroid Build Coastguard Worker void LuciTestResult::WriteToFile(const base::FilePath& result_file) const {
221*6777b538SAndroid Build Coastguard Worker const std::string json = ToJson(*this);
222*6777b538SAndroid Build Coastguard Worker const int json_size = json.size();
223*6777b538SAndroid Build Coastguard Worker CHECK(WriteFile(result_file, json.data(), json_size) == json_size);
224*6777b538SAndroid Build Coastguard Worker }
225*6777b538SAndroid Build Coastguard Worker
226*6777b538SAndroid Build Coastguard Worker } // namespace perf_test
227