diff options
Diffstat (limited to 'expression-test/expression_test_logger.cpp')
-rw-r--r-- | expression-test/expression_test_logger.cpp | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/expression-test/expression_test_logger.cpp b/expression-test/expression_test_logger.cpp new file mode 100644 index 0000000000..9127a6d7ae --- /dev/null +++ b/expression-test/expression_test_logger.cpp @@ -0,0 +1,177 @@ +#include "expression_test_logger.hpp" +#include "expression_test_runner.hpp" +#include "filesystem.hpp" + +#include <mbgl/util/io.hpp> +#include <mbgl/util/string.hpp> + +#include <sstream> + +using namespace mbgl; +using namespace std::literals; + +namespace { + +const char* resultsStyle = R"HTML( +<style> + body { font: 18px/1.2 -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif; padding: 10px; } + h1 { font-size: 32px; margin-bottom: 0; } + button { vertical-align: middle; } + h2 { font-size: 24px; font-weight: normal; margin: 10px 0 10px; line-height: 1; } + .test { border-bottom: 1px dotted #bbb; padding-bottom: 5px; } + .tests { border-top: 1px dotted #bbb; margin-top: 10px; } + .test p, .test pre { margin: 0 0 10px; } + .test pre { font-size: 14px; } + .label { color: white; font-size: 18px; padding: 2px 6px 3px; border-radius: 3px; margin-right: 3px; vertical-align: bottom; display: inline-block; } + .hide { display: none; } + .test.failed > h2 > span { background: red; } + .test.passed > h2 > span { background: green; } + .test.ignored > h2 > span { background: grey; } + .test.errored > h2 > span { background: #ff006f; } + .test.ignored.passed > h2 > span { background: #E8A408; } +</style> +)HTML"; + +const char* resultsHeaderButtons = R"HTML( + <button id='toggle-sequence'>Toggle test sequence</button> + <button id='toggle-passed'>Toggle passed tests</button> + <button id='toggle-ignored'>Toggle ignored tests</button> +</h1> +)HTML"; + +const char* resultsScript = R"HTML( +<script> +document.getElementById('toggle-passed').addEventListener('click', function (e) { + for (const row of document.querySelectorAll('.test.passed')) { + row.classList.toggle('hide'); + } +}); +document.getElementById('toggle-ignored').addEventListener('click', function (e) { + for (const row of document.querySelectorAll('.test.ignored')) { + row.classList.toggle('hide'); + } +}); +document.getElementById('toggle-sequence').addEventListener('click', function (e) { + document.getElementById('test-sequence').classList.toggle('hide'); +}); +</script> +)HTML"; + +std::string createResultItem(const TestRunOutput& result, const std::string& status, bool shouldHide) { + std::ostringstream html; + html << "<div class=\"test " << status << (shouldHide ? " hide" : "") << "\">\n"; + html << R"(<h2><span class="label">)" << status << "</span> " << result.id << "</h2>\n"; + + html << "<p><pre>"s << result.expression << "</pre></p>\n"s; + if (result.passed) { + html << "<strong>Serialized:</strong><p><pre>"s << result.serialized << "</pre></p>\n"s; + } else { + html << "<p><strong>Difference:</strong><pre>" << result.text << "</pre></p>\n"; + } + html << "</div>\n"; + + return html.str(); +} + +std::string createResultPage(const TestStats& stats, bool shuffle, uint32_t seed) { + const std::size_t unsuccessfulCount = stats.errored.size() + stats.failed.size(); + const bool unsuccessful = unsuccessfulCount > 0; + std::ostringstream resultsPage; + + // Style + resultsPage << resultsStyle; + + // Header with buttons + if (unsuccessful) { + resultsPage << R"HTML(<h1 style="color: red;">)HTML"; + resultsPage << util::toString(unsuccessfulCount) << " tests failed."; + } else { + resultsPage << R"HTML(<h1 style="color: green;">)HTML"; + resultsPage << "All tests passed!"; + } + + resultsPage << resultsHeaderButtons; + + // Test sequence + { + resultsPage << "<div id='test-sequence' class='hide'>\n"; + + // Failed tests + if (unsuccessful) { + resultsPage << "<p><strong>Failed tests:</strong>"; + for (const auto& failed : stats.failed) { + resultsPage << failed.id << " "; + } + resultsPage << "<p><strong>Errored tests:</strong>"; + for (const auto& errored : stats.errored) { + resultsPage << errored.id << " "; + } + resultsPage << "</p>\n"; + } + + // Test sequence + resultsPage << "<p><strong>Test sequence: </strong>"; + for (const auto& id : stats.ids) { + resultsPage << id << " "; + } + resultsPage << "</p>\n"; + + // Shuffle + if (shuffle) { + resultsPage << "<p><strong>Shuffle seed</strong>: " << util::toString(seed) << "</p>\n"; + } + + resultsPage << "</div>\n"; + } + + // Script + resultsPage << resultsScript; + + // Tests + resultsPage << "<div class=\"tests\">\n"; + const auto appendResult = [&] (const auto& results, const std::string& status, bool hide = false) { + for (const auto& result : results) { + resultsPage << createResultItem(result, status, hide); + } + }; + + appendResult(stats.passed, "passed"s, unsuccessful); + appendResult(stats.failed, "failed"s); + appendResult(stats.errored, "errored"s); + appendResult(stats.ignorePassed, "ignored passed"s, unsuccessful); + appendResult(stats.ignoreFailed, "ignored"s, true); + resultsPage << "</div>\n"; + + return resultsPage.str(); +} + +} // namespace + +void printStats(const TestStats& stats) { + const std::size_t count = stats.testCount(); + if (std::size_t passedTests = stats.passed.size()) { + printf(ANSI_COLOR_GREEN "%zu passed (%.1lf%%)" ANSI_COLOR_RESET "\n", passedTests, 100.0 * passedTests / count); + } + if (std::size_t ignorePassedTests = stats.ignorePassed.size()) { + printf(ANSI_COLOR_YELLOW "%zu passed but were ignored (%.1lf%%)" ANSI_COLOR_RESET "\n", ignorePassedTests, 100.0 * ignorePassedTests / count); + } + if (std::size_t ignoreFailedTests = stats.ignoreFailed.size()) { + printf(ANSI_COLOR_LIGHT_GRAY "%zu ignored (%.1lf%%)" ANSI_COLOR_RESET "\n", ignoreFailedTests, 100.0 * ignoreFailedTests / count); + } + if (std::size_t failedTests = stats.failed.size()) { + printf(ANSI_COLOR_RED "%zu failed (%.1lf%%)" ANSI_COLOR_RESET "\n", failedTests, 100.0 * failedTests / count); + } + if (std::size_t erroredTests = stats.errored.size()) { + printf(ANSI_COLOR_RED "%zu errored (%.1lf%%)" ANSI_COLOR_RESET "\n", erroredTests, 100.0 * erroredTests / count); + } +} + +void writeHTMLResults(const TestStats& stats, const std::string& rootPath, bool shuffle, uint32_t seed) { + filesystem::path path = filesystem::path(rootPath) / "index.html"s; + try { + util::write_file(path.string(), createResultPage(stats, shuffle, seed)); + printf("Results at: %s\n", path.string().c_str()); + } catch (std::exception&) { + printf(ANSI_COLOR_RED "* ERROR can't write result page %s" ANSI_COLOR_RESET "\n", path.string().c_str()); + } +} |