summaryrefslogtreecommitdiff
path: root/expression-test/expression_test_logger.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'expression-test/expression_test_logger.cpp')
-rw-r--r--expression-test/expression_test_logger.cpp177
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());
+ }
+}