1 /* Copyright (c) 2009, 2021, Oracle and/or its affiliates.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 // First include (the generated) my_config.h, to get correct platform defines.
24 #include "my_config.h"
25 #include <gtest/gtest.h>
26
27 #include <stdarg.h>
28 #include <string>
29 #include <sstream>
30
31 using testing::TestEventListeners;
32 using testing::TestCase;
33 using testing::TestEventListener;
34 using testing::TestInfo;
35 using testing::TestPartResult;
36 using testing::UnitTest;
37
38
39 /**
40 Receives events from googletest, and outputs interesting events
41 in TAP compliant format.
42 Implementation is inspired by PrettyUnitTestResultPrinter.
43 See documentation for base class.
44 */
45 class TapEventListener : public TestEventListener
46 {
47 public:
TapEventListener()48 TapEventListener() : m_test_number(0) {}
~TapEventListener()49 virtual ~TapEventListener() {}
50
OnTestProgramStart(const UnitTest &)51 virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {}
52 virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration);
53 virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test);
OnEnvironmentsSetUpEnd(const UnitTest &)54 virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {}
55 virtual void OnTestCaseStart(const TestCase& test_case);
56 virtual void OnTestStart(const TestInfo& test_info);
57 virtual void OnTestPartResult(const TestPartResult& test_part_result);
58 virtual void OnTestEnd(const TestInfo& test_info);
OnTestCaseEnd(const TestCase &)59 virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {};
60 virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test);
OnEnvironmentsTearDownEnd(const UnitTest &)61 virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {}
62 virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration);
OnTestProgramEnd(const UnitTest &)63 virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {}
64 private:
65 int m_test_number;
66 std::string m_test_case_name;
67 };
68
69
70 /**
71 Prints argument to stdout, prefixing all lines with "# ".
72 */
tap_diagnostic_printf(const std::stringstream & str_stream)73 static void tap_diagnostic_printf(const std::stringstream &str_stream)
74 {
75 std::string message= str_stream.str();
76 size_t pos = 0;
77 while((pos = message.find("\n", pos)) != std::string::npos)
78 {
79 message.replace(pos, 1, "\n# ");
80 pos += 1;
81 }
82 printf("# %s\n", message.c_str());
83 fflush(stdout);
84 }
85
86 // Convenience wrapper function.
tap_diagnostic_printf(const std::string & txt)87 static void tap_diagnostic_printf(const std::string &txt)
88 {
89 std::stringstream str_str;
90 str_str << txt;
91 tap_diagnostic_printf(str_str);
92 }
93
94 // Convenience wrapper function.
tap_diagnostic_printf(const char * txt)95 static void tap_diagnostic_printf(const char *txt)
96 {
97 tap_diagnostic_printf(std::string(txt));
98 }
99
100
101 namespace {
102 // Helper struct to simplify output of "1 test" or "n tests".
103 struct num_tests
104 {
num_tests__anon722385a60111::num_tests105 num_tests(int num) : m_num(num) {}
106 int m_num;
107 };
108
operator <<(std::ostream & s,const num_tests & num)109 std::ostream &operator<< (std::ostream &s, const num_tests &num)
110 {
111 return s << num.m_num << (num.m_num == 1 ? " test" : " tests");
112 }
113
114 // Helper struct to simplify output of "1 test case" or "n test cases".
115 struct num_test_cases
116 {
num_test_cases__anon722385a60111::num_test_cases117 num_test_cases(int num) : m_num(num) {}
118 int m_num;
119 };
120
operator <<(std::ostream & s,const num_test_cases & num)121 std::ostream &operator<< (std::ostream &s, const num_test_cases &num)
122 {
123 return s << num.m_num << (num.m_num == 1 ? " test case" : " test cases");
124 }
125 } // namespace
126
127
128 /**
129 Converts a TestPartResult::Type enum to human-friendly string
130 representation.
131 */
test_part_result_type_tostring(TestPartResult::Type type)132 static std::string test_part_result_type_tostring(TestPartResult::Type type)
133 {
134 switch (type)
135 {
136 case TestPartResult::kSuccess:
137 return "Success";
138
139 case TestPartResult::kNonFatalFailure:
140 case TestPartResult::kFatalFailure:
141 return "Failure";
142 }
143 return "";
144 }
145
146
147 /**
148 Formats a source file path and a line number as they would appear
149 in a compiler error message.
150 */
format_file_location(const TestPartResult & test_part_result)151 static std::string format_file_location(const TestPartResult &test_part_result)
152 {
153 const char* const file= test_part_result.file_name();
154 const char* const file_name = file == NULL ? "unknown file" : file;
155 const int line= test_part_result.line_number();
156 std::stringstream str_stream;
157 str_stream << file_name << ":";
158 if (line >= 0)
159 str_stream << line << ":";
160 return str_stream.str();
161 }
162
163
164 /**
165 Formats a TestPartResult as a string.
166 */
test_part_result_tostring(const TestPartResult & test_part_result)167 static std::string test_part_result_tostring(const TestPartResult
168 &test_part_result)
169 {
170 return format_file_location(test_part_result)
171 + " "
172 + test_part_result_type_tostring(test_part_result.type())
173 + test_part_result.message();
174 }
175
176
OnTestIterationStart(const UnitTest & unit_test,int iteration)177 void TapEventListener::OnTestIterationStart(const UnitTest& unit_test,
178 int iteration)
179 {
180 std::stringstream str_stream;
181 str_stream << "Running " << num_tests(unit_test.test_to_run_count())
182 << " from " << num_test_cases(unit_test.test_case_to_run_count());
183 tap_diagnostic_printf(str_stream);
184 printf("%d..%d\n", 1, unit_test.test_to_run_count());
185 fflush(stdout);
186 }
187
188
OnEnvironmentsSetUpStart(const UnitTest & unit_test)189 void TapEventListener::OnEnvironmentsSetUpStart(const UnitTest& unit_test)
190 {
191 tap_diagnostic_printf("Global test environment set-up");
192 }
193
194
OnTestCaseStart(const TestCase & test_case)195 void TapEventListener::OnTestCaseStart(const TestCase& test_case)
196 {
197 m_test_case_name = test_case.name();
198 }
199
200
OnTestStart(const TestInfo & test_info)201 void TapEventListener::OnTestStart(const TestInfo& test_info)
202 {
203 ++m_test_number;
204 std::stringstream str_stream;
205 str_stream << "Run " << m_test_number << " "
206 << m_test_case_name << "." << test_info.name();
207 tap_diagnostic_printf(str_stream);
208 }
209
210
OnTestPartResult(const TestPartResult & test_part_result)211 void TapEventListener::OnTestPartResult(const TestPartResult& test_part_result)
212 {
213 if (test_part_result.passed())
214 return;
215 // Don't prefix error messages with #, as the tap harness will hide them!
216 fprintf(stderr, "%s\n", test_part_result_tostring(test_part_result).c_str());
217 }
218
219
OnTestEnd(const TestInfo & test_info)220 void TapEventListener::OnTestEnd(const TestInfo& test_info)
221 {
222 if (test_info.result()->Passed())
223 printf("ok %d\n", m_test_number);
224 else
225 printf("not ok %d\n", m_test_number);
226 fflush(stdout);
227 }
228
229
OnEnvironmentsTearDownStart(const UnitTest & unit_test)230 void TapEventListener::OnEnvironmentsTearDownStart(const UnitTest& unit_test)
231 {
232 tap_diagnostic_printf("Global test environment tear-down");
233 }
234
235
OnTestIterationEnd(const UnitTest & unit_test,int iteration)236 void TapEventListener::OnTestIterationEnd(const UnitTest& unit_test,
237 int iteration)
238 {
239 std::stringstream str_stream;
240 str_stream << "Ran " << num_tests(unit_test.test_to_run_count())
241 << " from " << num_test_cases(unit_test.test_case_to_run_count())
242 << "\n"
243 << "Passed " << num_tests(unit_test.successful_test_count());
244
245 if (!unit_test.Passed())
246 str_stream << "\n"
247 << "Failed " << num_tests(unit_test.failed_test_count());
248
249 const int num_disabled = unit_test.disabled_test_count();
250 if (num_disabled && !testing::GTEST_FLAG(also_run_disabled_tests))
251 str_stream << "\n"
252 << "YOU HAVE " << num_disabled << " DISABLED "
253 << (num_disabled == 1 ? "TEST" : "TESTS");
254
255 tap_diagnostic_printf(str_stream);
256 }
257
258
259 /**
260 Removes the default googletest listener (a PrettyUnitTestResultPrinter),
261 and installs our own TAP compliant pretty printer instead.
262 */
install_tap_listener()263 void install_tap_listener()
264 {
265 TestEventListeners& listeners = UnitTest::GetInstance()->listeners();
266 delete listeners.Release(listeners.default_result_printer());
267 listeners.Append(new TapEventListener);
268 }
269