• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 Google LLC.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "include/igt_test_helper.h"
18 
19 #include <android-base/logging.h>
20 #include <gtest/gtest.h>
21 
22 #include <array>
23 #include <cstdio>
24 #include <cstdlib>
25 #include <memory>
26 #include <optional>
27 #include <sstream>
28 
29 namespace igt {
30 namespace {
31 enum class TestResult { kPass, kFail, kSkip, kUnknown };
32 
runCommand(const std::string & cmd)33 std::optional<std::string> runCommand(const std::string &cmd) {
34   // Gtest runs from root /, so the command should start from there.
35   std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"),
36                                                 pclose);
37   if (!pipe) {
38     ADD_FAILURE() << "popen() failed! Could not find or run the binary.";
39     return std::nullopt;
40   }
41 
42   std::array<char, 128> buffer;
43   std::string result;
44   while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
45     result += buffer.data();
46   }
47   return result;
48 }
49 
getSubtestTestResultFromLog(const std::string & log,const std::string & subTestName)50 TestResult getSubtestTestResultFromLog(const std::string &log,
51                                        const std::string &subTestName) {
52   if (log.find("Subtest " + subTestName + ": FAIL") != std::string::npos) {
53     return TestResult::kFail;
54   } else if (log.find("Subtest " + subTestName + ": SKIP") !=
55              std::string::npos) {
56     return TestResult::kSkip;
57   } else if (log.find("Subtest " + subTestName + ": SUCCESS") !=
58              std::string::npos) {
59     return TestResult::kPass;
60   } else {
61     return TestResult::kUnknown;
62   }
63 }
64 
getTestResultFromLog(std::string & log)65 TestResult getTestResultFromLog(std::string &log) {
66   std::for_each(log.begin(), log.end(), [](char &c) { c = ::tolower(c); });
67 
68   if (log.find("fail") != std::string::npos) {
69     return TestResult::kFail;
70   } else if (log.find("skip") != std::string::npos) {
71     return TestResult::kSkip;
72   } else if (log.find("success") != std::string::npos) {
73     return TestResult::kPass;
74   } else {
75     return TestResult::kUnknown;
76   }
77 }
78 
generateFailureLog(const std::string & log,const std::string_view & desc,const std::string_view & rationale)79 std::string generateFailureLog(const std::string &log,
80                                const std::string_view &desc,
81                                const std::string_view &rationale) {
82 
83   std::stringstream failureMessage;
84   failureMessage << log << std::endl;
85   failureMessage << "**What the test is doing**: " << desc << std::endl;
86   failureMessage << "**Why the test should be fixed**: " << rationale
87                  << std::endl;
88 
89   return failureMessage.str();
90 }
91 
presentTestResult(TestResult result,const std::string & log,const std::string_view & desc,const std::string_view & rationale)92 void presentTestResult(TestResult result, const std::string &log,
93                        const std::string_view &desc,
94                        const std::string_view &rationale) {
95   switch (result) {
96   case TestResult::kPass:
97     SUCCEED();
98     break;
99   case TestResult::kFail:
100     ADD_FAILURE() << generateFailureLog(log, desc, rationale);
101     break;
102   case TestResult::kSkip:
103     GTEST_SKIP() << log;
104     break;
105   case TestResult::kUnknown:
106     ADD_FAILURE() << "Could not determine test result.\n" << log;
107     break;
108   default:
109     ADD_FAILURE() << log;
110     break;
111   }
112 }
113 } // namespace
114 
115 // static
generateGTestName(const::testing::TestParamInfo<IgtSubtestParams> & info)116 std::string IgtTestHelper::generateGTestName(
117     const ::testing::TestParamInfo<IgtSubtestParams> &info) {
118   std::string dashedName(info.param.name);
119 
120   // Many subtest names include %s and %d which are not valid GTest names.
121   size_t pos = dashedName.find("%s");
122   while (pos != std::string::npos) {
123     dashedName.erase(pos, 2);
124     pos = dashedName.find("%s", pos);
125   }
126   pos = dashedName.find("%d");
127   while (pos != std::string::npos) {
128     dashedName.erase(pos, 2);
129     pos = dashedName.find("%d", pos);
130   }
131 
132   // convert test-name to PascalCase
133   std::stringstream ss(dashedName);
134   std::string word;
135   std::string pascalCaseName;
136   while (std::getline(ss, word, '-')) {
137     if (!word.empty()) {
138       // Capitalize the first letter
139       word[0] = std::toupper(word[0]);
140       pascalCaseName += word;
141     }
142   }
143 
144   return pascalCaseName;
145 }
146 
runSubTest(const IgtSubtestParams & subtest)147 void IgtTestHelper::runSubTest(const IgtSubtestParams &subtest) {
148   CHECK(test_name_.size());
149   std::optional<std::string> log =
150       runCommand(test_name_ + " --run-subtest " + subtest.name);
151   if (!log.has_value())
152     return;
153 
154   TestResult result = getSubtestTestResultFromLog(log.value(), subtest.name);
155   presentTestResult(result, log.value(), subtest.desc, subtest.rationale);
156 }
157 
runTest(const std::string & desc,const std::string & rationale)158 void IgtTestHelper::runTest(const std::string &desc,
159                             const std::string &rationale) {
160   CHECK(test_name_.size());
161 
162   std::optional<std::string> log = runCommand(test_name_);
163   if (!log.has_value())
164     return;
165 
166   TestResult result = getTestResultFromLog(log.value());
167   presentTestResult(result, log.value(), desc, rationale);
168 }
169 
170 } // namespace igt
171