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