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