Created
November 1, 2018 12:39
-
-
Save udaken/f2ac828f2b97d374fb686294ef720eb5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #define _CRT_SECURE_NO_WARNINGS 1 | |
| #include <stdio.h> | |
| #include <fstream> | |
| #include <iostream> | |
| #include "gtest/gtest.h" | |
| #pragma comment(lib, "gtestd.lib") | |
| namespace { | |
| using namespace ::testing; | |
| static std::string FormatTimeInMillisAsDuration(TimeInMillis ms) { | |
| ::std::stringstream ss; | |
| ss << (static_cast<double>(ms) * 1e-3) << "s"; | |
| return ss.str(); | |
| } | |
| static std::string FormatTimeInMillsAsTime(TimeInMillis ms) { | |
| time_t t = ms / 1000; | |
| struct tm* ltm = localtime(&t); | |
| if (ltm) { | |
| char buf[64] = {}; | |
| // YYYY-MM-DDThh:mm:ss | |
| std::strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", ltm); | |
| return buf; | |
| } | |
| else | |
| return ""; | |
| } | |
| static const char* TestPartResultTypeToString(TestPartResult::Type type) { | |
| switch (type) { | |
| case TestPartResult::kSkip: | |
| return "Skipped"; | |
| case TestPartResult::kSuccess: | |
| return "Success"; | |
| case TestPartResult::kNonFatalFailure: | |
| return "NonFatalFailure"; | |
| case TestPartResult::kFatalFailure: | |
| return "FatalFailure"; | |
| default: | |
| return "Unknown result type"; | |
| } | |
| } | |
| class MarkdownUnitTestResultPrinter : public EmptyTestEventListener { | |
| public: | |
| explicit MarkdownUnitTestResultPrinter(const char* output_file) | |
| : output_file_(output_file) { | |
| if (output_file_.empty()) { | |
| GTEST_LOG_(FATAL) << "Markdown output file may not be null"; | |
| } | |
| } | |
| virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration) { | |
| std::ofstream out(output_file_.c_str()); | |
| std::stringstream stream; | |
| PrintMdUnitTest(&stream, unit_test); | |
| out << stream.str(); | |
| } | |
| private: | |
| static std::string EscapeMd(const std::string& str) { | |
| Message m; | |
| for (size_t i = 0; i < str.size(); ++i) { | |
| const char ch = str[i]; | |
| switch (ch) { | |
| case '\\': | |
| case '`': | |
| case '*': | |
| case '_': | |
| case '{': | |
| case '}': | |
| case '[': | |
| case ']': | |
| case '(': | |
| case ')': | |
| case '#': | |
| case '+': | |
| case '-': | |
| case '!': | |
| case '.': | |
| case '<': | |
| case '>': | |
| m << '\\' << ch; | |
| break; | |
| case '\n': | |
| m << "<br>"; | |
| break; | |
| default: | |
| m << ch; | |
| break; | |
| } | |
| } | |
| return m.GetString(); | |
| } | |
| static void OutputMdTableHeader(std::ostream* stream, | |
| const std::string& name = "Measure of", | |
| const std::string& value = "Value") { | |
| *stream << "| " << name << " | " << value << " |\n"; | |
| *stream << "| --- | --- |\n"; | |
| } | |
| static void OutputMdTableRow(std::ostream* stream, const std::string& name, | |
| const std::string& value) { | |
| *stream << "| " << name << " | " << EscapeMd(value) << " |\n"; | |
| } | |
| static void OutputMdTableRow(std::ostream* stream, const std::string& name, | |
| int value) { | |
| OutputMdTableRow(stream, name, internal::StreamableToString(value)); | |
| } | |
| static void OutputMdTestInfo(::std::ostream* stream, | |
| const TestInfo& test_info) { | |
| const TestResult& result = *test_info.result(); | |
| *stream << "### Test: " << EscapeMd(test_info.name()) << "\n"; | |
| OutputMdTableHeader(stream); | |
| if (test_info.value_param() != nullptr) { | |
| OutputMdTableRow(stream, "value_param", test_info.value_param()); | |
| } | |
| if (test_info.type_param() != nullptr) { | |
| OutputMdTableRow(stream, "type_param", test_info.type_param()); | |
| } | |
| if (GTEST_FLAG(list_tests)) { | |
| OutputMdTableRow(stream, "file", test_info.file()); | |
| OutputMdTableRow(stream, "line", test_info.line()); | |
| return; | |
| } | |
| OutputMdTableRow(stream, "status", | |
| test_info.should_run() ? "RUN" : "NOTRUN"); | |
| OutputMdTableRow(stream, "time", | |
| FormatTimeInMillisAsDuration(result.elapsed_time())); | |
| *stream << "\n"; | |
| *stream << TestPropertiesAsMd(result); | |
| if (result.total_part_count() > 0) { | |
| *stream << "| \\# | type | location | message |\n" | |
| << "| ----: | :---- | :---- | :---- |\n"; | |
| for (int i = 0; i < result.total_part_count(); ++i) { | |
| const TestPartResult& part = result.GetTestPartResult(i); | |
| const std::string location = | |
| internal::FormatCompilerIndependentFileLocation(part.file_name(), | |
| part.line_number()); | |
| *stream << "| " << internal::StreamableToString(i + 1) | |
| << " | " << TestPartResultTypeToString(part.type()) | |
| << " | `" << location << "` " | |
| << " | " << EscapeMd(part.message()) << " |\n"; | |
| } | |
| *stream << "\n"; | |
| } | |
| } | |
| static void PrintMdTestCase(::std::ostream* stream, | |
| const TestCase& test_case) { | |
| *stream << "## Test Case: " << EscapeMd(test_case.name()) << "\n"; | |
| OutputMdTableHeader(stream); | |
| OutputMdTableRow(stream, "tests", test_case.reportable_test_count()); | |
| if (!GTEST_FLAG(list_tests)) { | |
| OutputMdTableRow(stream, "failures", test_case.failed_test_count()); | |
| OutputMdTableRow(stream, "disabled", | |
| test_case.reportable_disabled_test_count()); | |
| OutputMdTableRow(stream, "errors", 0); | |
| OutputMdTableRow(stream, "time", | |
| FormatTimeInMillisAsDuration(test_case.elapsed_time())); | |
| *stream << "\n"; | |
| if (test_case.ad_hoc_test_result().test_property_count() > 0) { | |
| *stream << "--- \n"; | |
| OutputMdTableHeader(stream); | |
| *stream << TestPropertiesAsMd(test_case.ad_hoc_test_result()) << "\n"; | |
| } | |
| } | |
| for (int i = 0; i < test_case.total_test_count(); ++i) { | |
| if (test_case.GetTestInfo(i)->is_reportable()) { | |
| OutputMdTestInfo(stream, *test_case.GetTestInfo(i)); | |
| } | |
| } | |
| *stream << "\n"; | |
| } | |
| static void PrintMdUnitTest(::std::ostream* stream, | |
| const UnitTest& unit_test) { | |
| *stream << "# Summary\n"; | |
| OutputMdTableHeader(stream); | |
| OutputMdTableRow(stream, "tests", unit_test.reportable_test_count()); | |
| OutputMdTableRow(stream, "failures", unit_test.failed_test_count()); | |
| OutputMdTableRow(stream, "disabled", | |
| unit_test.reportable_disabled_test_count()); | |
| if (GTEST_FLAG(shuffle)) { | |
| OutputMdTableRow(stream, "random_seed", unit_test.random_seed()); | |
| } | |
| OutputMdTableRow(stream, "timestamp", | |
| FormatTimeInMillsAsTime(unit_test.start_timestamp())); | |
| OutputMdTableRow(stream, "time", | |
| FormatTimeInMillisAsDuration(unit_test.elapsed_time())); | |
| *stream << "\n"; | |
| if (unit_test.ad_hoc_test_result().test_property_count() > 0) { | |
| OutputMdTableHeader(stream, "Propertiy Key", "Value"); | |
| *stream << TestPropertiesAsMd(unit_test.ad_hoc_test_result()) << "\n"; | |
| } | |
| for (int i = 0; i < unit_test.total_test_case_count(); ++i) { | |
| if (unit_test.GetTestCase(i)->reportable_test_count() > 0) { | |
| PrintMdTestCase(stream, *unit_test.GetTestCase(i)); | |
| } | |
| } | |
| } | |
| static std::string TestPropertiesAsMd(const testing::TestResult& result) { | |
| Message attributes; | |
| for (int i = 0; i < result.test_property_count(); ++i) { | |
| const TestProperty& property = result.GetTestProperty(i); | |
| attributes << "| " << property.key() << " | " | |
| << EscapeMd(property.value()) << " |\n"; | |
| } | |
| return attributes.GetString(); | |
| } | |
| const std::string output_file_; | |
| }; | |
| } | |
| int main(int argc, char** argv) { | |
| printf("Running main() from %s\n", __FILE__); | |
| InitGoogleTest(&argc, argv); | |
| UnitTest& unit_test = *UnitTest::GetInstance(); | |
| bool markdown_output = true; | |
| if (markdown_output) { | |
| TestEventListeners& listeners = unit_test.listeners(); | |
| listeners.Append(new MarkdownUnitTestResultPrinter("test_report.md")); | |
| } | |
| int ret_val = RUN_ALL_TESTS(); | |
| return ret_val; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment