blob: a417ebb53c59630d3b20137f6ee08627966bfbd3 [file] [log] [blame]
Hal Canaryac7f23c2018-11-26 14:07:41 -05001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "tools/skqp/src/skqp.h"
Hal Canaryac7f23c2018-11-26 14:07:41 -050012
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "include/core/SkData.h"
14#include "src/core/SkOSFile.h"
15#include "tools/Resources.h"
Hal Canaryac7f23c2018-11-26 14:07:41 -050016
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};
Tyler Denniston0cfcd1c2020-03-05 10:38:17 -050033
34struct Args {
35 char *assetDir;
36 char *renderTests;
37 char *outputDir;
38};
John Stilesa6841be2020-08-06 14:11:56 -040039} // namespace
Hal Canaryac7f23c2018-11-26 14:07:41 -050040
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
Tyler Denniston0cfcd1c2020-03-05 10:38:17 -050082static 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";
Hal Canaryac7f23c2018-11-26 14:07:41 -0500101 return 2;
102 }
Tyler Denniston0cfcd1c2020-03-05 10:38:17 -0500103 StdAssetManager mgr(args.assetDir);
Hal Canaryac7f23c2018-11-26 14:07:41 -0500104 SkQP skqp;
Tyler Denniston0cfcd1c2020-03-05 10:38:17 -0500105 skqp.init(&mgr, args.renderTests, args.outputDir);
Hal Canaryac7f23c2018-11-26 14:07:41 -0500106 int ret = 0;
107
Tyler Denniston0cfcd1c2020-03-05 10:38:17 -0500108 const char* const* matchRules = &argv[4];
109 size_t matchRulesCount = (size_t)(argc - 4);
Hal Canaryac7f23c2018-11-26 14:07:41 -0500110
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}