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