SkQP: refatctor C++ bits.

  * C++ code moved into tools/skqp/src/.
  * State held with single SkQP class.
  * gmkb functions moved to skqp_model.{h,cpp}
  * model no longer knows about report format.
  * skqp_main and skqp_lib no longer have globals
  * jni code has fewer globals.
  * skqp_main no longer uses googletest.
  * AssetMng returns SkData, not a SkStream.
  * Add jitter tool.
  * dump GPU information into grdump.txt
  * JUnit puts report in directory with timestamp.
  * Document SkQP Render Test Algorithm.
  * GPU driver correctness workarounds always off
  * cut_release tool for assembling models
  * make_rendertests_list.py to help cut_release
  * make_gmkb.go emits a list of models

CQ_INCLUDE_TRYBOTS=skia.primary:Build-Debian9-Clang-x86-devrel-Android_SKQP

Change-Id: I7d4f0c24592b1f64be0088578a3f1a0bc366dd4d
Reviewed-on: https://skia-review.googlesource.com/c/110420
Reviewed-by: Hal Canary <halcanary@google.com>
Commit-Queue: Hal Canary <halcanary@google.com>
diff --git a/tools/skqp/src/skqp_main.cpp b/tools/skqp/src/skqp_main.cpp
new file mode 100644
index 0000000..2737a9a
--- /dev/null
+++ b/tools/skqp/src/skqp_main.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <iostream>
+#include <sys/stat.h>
+
+#include "skqp.h"
+
+#include "Resources.h"
+#include "SkData.h"
+#include "SkOSFile.h"
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace {
+class StdAssetManager : public SkQPAssetManager {
+public:
+    StdAssetManager(const char* p) : fPrefix(p) {
+        SkASSERT(!fPrefix.empty());
+        //TODO(halcanary): does this need to be changed if I run SkQP in Windows?
+        fPrefix += "/";
+    }
+    sk_sp<SkData> open(const char* path) override {
+        return SkData::MakeFromFileName((fPrefix + path).c_str());
+    }
+private:
+    std::string fPrefix;
+};
+}
+
+static constexpr char kSkipUsage[] =
+    " TEST_MATCH_RULES:"
+    "    [~][^]substring[$] [...] of name to run.\n"
+    "    Multiple matches may be separated by spaces.\n"
+    "    ~ causes a matching name to always be skipped\n"
+    "    ^ requires the start of the name to match\n"
+    "    $ requires the end of the name to match\n"
+    "    ^ and $ requires an exact match\n"
+    "    If a name does not match any list entry,\n"
+    "    it is skipped unless some list entry starts with ~\n";
+
+static bool should_skip(const char* const* rules, size_t count, const char* name) {
+    size_t testLen = strlen(name);
+    bool anyExclude = count == 0;
+    for (size_t i = 0; i < count; ++i) {
+        const char* matchName = rules[i];
+        size_t matchLen = strlen(matchName);
+        bool matchExclude, matchStart, matchEnd;
+        if ((matchExclude = matchName[0] == '~')) {
+            anyExclude = true;
+            matchName++;
+            matchLen--;
+        }
+        if ((matchStart = matchName[0] == '^')) {
+            matchName++;
+            matchLen--;
+        }
+        if ((matchEnd = matchName[matchLen - 1] == '$')) {
+            matchLen--;
+        }
+        if (matchStart ? (!matchEnd || matchLen == testLen)
+                && strncmp(name, matchName, matchLen) == 0
+                : matchEnd ? matchLen <= testLen
+                && strncmp(name + testLen - matchLen, matchName, matchLen) == 0
+                : strstr(name, matchName) != nullptr) {
+            return matchExclude;
+        }
+    }
+    return !anyExclude;
+}
+
+int main(int argc, char** argv) {
+    if (argc < 3) {
+        std::cerr << "Usage:\n  " << argv[0]
+                  << " ASSET_DIRECTORY_PATH SKQP_REPORT_PATH [TEST_MATCH_RULES]\n"
+                  << kSkipUsage << '\n';
+        return 1;
+    }
+    SetResourcePath((std::string(argv[1]) + "/resources").c_str());
+    if (!sk_mkdir(argv[2])) {
+        std::cerr << "sk_mkdir(" << argv[2] << ") failed.\n";
+        return 2;
+    }
+    StdAssetManager mgr(argv[1]);
+    SkQP skqp;
+    skqp.init(&mgr, argv[2]);
+    int ret = 0;
+
+    const char* const* matchRules = &argv[3];
+    size_t matchRulesCount = (size_t)(argc - 3);
+
+    // Rendering Tests
+    std::ostream& out = std::cout;
+    for (auto backend : skqp.getSupportedBackends()) {
+        auto testPrefix = std::string(SkQP::GetBackendName(backend)) + "_";
+        for (auto gmFactory : skqp.getGMs()) {
+            auto testName = testPrefix + SkQP::GetGMName(gmFactory);
+            if (should_skip(matchRules, matchRulesCount, testName.c_str())) {
+                continue;
+            }
+            out << "Starting: " << testName << std::endl;
+            SkQP::RenderOutcome outcome;
+            std::string except;
+
+            std::tie(outcome, except) = skqp.evaluateGM(backend, gmFactory);
+            if (!except.empty()) {
+                out << "ERROR:    " << testName << " (" << except << ")\n";
+                ret = 1;
+            } else if (outcome.fMaxError != 0) {
+                out << "FAILED:   " << testName << " (" << outcome.fMaxError << ")\n";
+                ret = 1;
+            } else {
+                out << "Passed:   " << testName << "\n";
+            }
+            out.flush();
+        }
+    }
+
+    // Unit Tests
+    for (auto test : skqp.getUnitTests()) {
+        auto testName = std::string("unitTest_") +  SkQP::GetUnitTestName(test);
+        if (should_skip(matchRules, matchRulesCount, testName.c_str())) {
+            continue;
+        }
+        out << "Starting test: " << testName << std::endl;
+        std::vector<std::string> errors = skqp.executeTest(test);
+        if (!errors.empty()) {
+            out << "TEST FAILED (" << errors.size() << "): " << testName << "\n";
+            for (const std::string& error : errors) {
+                out << error << "\n";
+            }
+            ret = 1;
+        } else {
+            out << "Test passed:   " << testName << "\n";
+        }
+        out.flush();
+    }
+    skqp.makeReport();
+
+    return ret;
+}