luci_test_result.cc 8.1 KB

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