xref: /third_party/skia/tools/skqp/src/skqp_main.cpp (revision cb93a386)
1/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include <iostream>
9#include <sys/stat.h>
10
11#include "tools/skqp/src/skqp.h"
12
13#include "include/core/SkData.h"
14#include "src/core/SkOSFile.h"
15#include "tools/Resources.h"
16
17////////////////////////////////////////////////////////////////////////////////
18
19namespace {
20class StdAssetManager : public SkQPAssetManager {
21public:
22    StdAssetManager(const char* p) : fPrefix(p) {
23        SkASSERT(!fPrefix.empty());
24        //TODO(halcanary): does this need to be changed if I run SkQP in Windows?
25        fPrefix += "/";
26    }
27    sk_sp<SkData> open(const char* path) override {
28        return SkData::MakeFromFileName((fPrefix + path).c_str());
29    }
30private:
31    std::string fPrefix;
32};
33
34struct Args {
35  char *assetDir;
36  char *renderTests;
37  char *outputDir;
38};
39}  // namespace
40
41static constexpr char kSkipUsage[] =
42    " TEST_MATCH_RULES:"
43    "    [~][^]substring[$] [...] of name to run.\n"
44    "    Multiple matches may be separated by spaces.\n"
45    "    ~ causes a matching name to always be skipped\n"
46    "    ^ requires the start of the name to match\n"
47    "    $ requires the end of the name to match\n"
48    "    ^ and $ requires an exact match\n"
49    "    If a name does not match any list entry,\n"
50    "    it is skipped unless some list entry starts with ~\n";
51
52static bool should_skip(const char* const* rules, size_t count, const char* name) {
53    size_t testLen = strlen(name);
54    bool anyExclude = count == 0;
55    for (size_t i = 0; i < count; ++i) {
56        const char* matchName = rules[i];
57        size_t matchLen = strlen(matchName);
58        bool matchExclude, matchStart, matchEnd;
59        if ((matchExclude = matchName[0] == '~')) {
60            anyExclude = true;
61            matchName++;
62            matchLen--;
63        }
64        if ((matchStart = matchName[0] == '^')) {
65            matchName++;
66            matchLen--;
67        }
68        if ((matchEnd = matchName[matchLen - 1] == '$')) {
69            matchLen--;
70        }
71        if (matchStart ? (!matchEnd || matchLen == testLen)
72                && strncmp(name, matchName, matchLen) == 0
73                : matchEnd ? matchLen <= testLen
74                && strncmp(name + testLen - matchLen, matchName, matchLen) == 0
75                : strstr(name, matchName) != nullptr) {
76            return matchExclude;
77        }
78    }
79    return !anyExclude;
80}
81
82static void parse_args(int argc, char *argv[], Args *args) {
83  if (argc < 4) {
84      std::cerr << "Usage:\n  " << argv[0]
85                << " ASSET_DIR RENDER_TESTS OUTPUT_DIR [TEST_MATCH_RULES]\n"
86                << kSkipUsage << '\n';
87      exit(1);
88  }
89  args->assetDir = argv[1];
90  args->renderTests = argv[2];
91  args->outputDir = argv[3];
92}
93
94int main(int argc, char *argv[]) {
95    Args args;
96    parse_args(argc, argv, &args);
97
98    SetResourcePath(std::string(args.assetDir + std::string("/resources")).c_str());
99    if (!sk_mkdir(args.outputDir)) {
100        std::cerr << "sk_mkdir(" << args.outputDir << ") failed.\n";
101        return 2;
102    }
103    StdAssetManager mgr(args.assetDir);
104    SkQP skqp;
105    skqp.init(&mgr, args.renderTests, args.outputDir);
106    int ret = 0;
107
108    const char* const* matchRules = &argv[4];
109    size_t matchRulesCount = (size_t)(argc - 4);
110
111    // Rendering Tests
112    std::ostream& out = std::cout;
113    for (auto backend : skqp.getSupportedBackends()) {
114        auto testPrefix = std::string(SkQP::GetBackendName(backend)) + "_";
115        for (auto gmFactory : skqp.getGMs()) {
116            auto testName = testPrefix + SkQP::GetGMName(gmFactory);
117            if (should_skip(matchRules, matchRulesCount, testName.c_str())) {
118                continue;
119            }
120            out << "Starting: " << testName << std::endl;
121            SkQP::RenderOutcome outcome;
122            std::string except;
123
124            std::tie(outcome, except) = skqp.evaluateGM(backend, gmFactory);
125            if (!except.empty()) {
126                out << "ERROR:    " << testName << " (" << except << ")\n";
127                ret = 1;
128            } else if (outcome.fMaxError != 0) {
129                out << "FAILED:   " << testName << " (" << outcome.fMaxError << ")\n";
130                ret = 1;
131            } else {
132                out << "Passed:   " << testName << "\n";
133            }
134            out.flush();
135        }
136    }
137
138    // Unit Tests
139    for (auto test : skqp.getUnitTests()) {
140        auto testName = std::string("unitTest_") +  SkQP::GetUnitTestName(test);
141        if (should_skip(matchRules, matchRulesCount, testName.c_str())) {
142            continue;
143        }
144        out << "Starting test: " << testName << std::endl;
145        std::vector<std::string> errors = skqp.executeTest(test);
146        if (!errors.empty()) {
147            out << "TEST FAILED (" << errors.size() << "): " << testName << "\n";
148            for (const std::string& error : errors) {
149                out << error << "\n";
150            }
151            ret = 1;
152        } else {
153            out << "Test passed:   " << testName << "\n";
154        }
155        out.flush();
156    }
157    skqp.makeReport();
158
159    return ret;
160}
161