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
19 namespace {
20 class StdAssetManager : public SkQPAssetManager {
21 public:
StdAssetManager(const char * p)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 }
open(const char * path)27 sk_sp<SkData> open(const char* path) override {
28 return SkData::MakeFromFileName((fPrefix + path).c_str());
29 }
30 private:
31 std::string fPrefix;
32 };
33 }
34
35 static constexpr char kSkipUsage[] =
36 " TEST_MATCH_RULES:"
37 " [~][^]substring[$] [...] of name to run.\n"
38 " Multiple matches may be separated by spaces.\n"
39 " ~ causes a matching name to always be skipped\n"
40 " ^ requires the start of the name to match\n"
41 " $ requires the end of the name to match\n"
42 " ^ and $ requires an exact match\n"
43 " If a name does not match any list entry,\n"
44 " it is skipped unless some list entry starts with ~\n";
45
should_skip(const char * const * rules,size_t count,const char * name)46 static bool should_skip(const char* const* rules, size_t count, const char* name) {
47 size_t testLen = strlen(name);
48 bool anyExclude = count == 0;
49 for (size_t i = 0; i < count; ++i) {
50 const char* matchName = rules[i];
51 size_t matchLen = strlen(matchName);
52 bool matchExclude, matchStart, matchEnd;
53 if ((matchExclude = matchName[0] == '~')) {
54 anyExclude = true;
55 matchName++;
56 matchLen--;
57 }
58 if ((matchStart = matchName[0] == '^')) {
59 matchName++;
60 matchLen--;
61 }
62 if ((matchEnd = matchName[matchLen - 1] == '$')) {
63 matchLen--;
64 }
65 if (matchStart ? (!matchEnd || matchLen == testLen)
66 && strncmp(name, matchName, matchLen) == 0
67 : matchEnd ? matchLen <= testLen
68 && strncmp(name + testLen - matchLen, matchName, matchLen) == 0
69 : strstr(name, matchName) != nullptr) {
70 return matchExclude;
71 }
72 }
73 return !anyExclude;
74 }
75
main(int argc,char ** argv)76 int main(int argc, char** argv) {
77 if (argc < 3) {
78 std::cerr << "Usage:\n " << argv[0]
79 << " ASSET_DIRECTORY_PATH SKQP_REPORT_PATH [TEST_MATCH_RULES]\n"
80 << kSkipUsage << '\n';
81 return 1;
82 }
83 SetResourcePath((std::string(argv[1]) + "/resources").c_str());
84 if (!sk_mkdir(argv[2])) {
85 std::cerr << "sk_mkdir(" << argv[2] << ") failed.\n";
86 return 2;
87 }
88 StdAssetManager mgr(argv[1]);
89 SkQP skqp;
90 skqp.init(&mgr, argv[2]);
91 int ret = 0;
92
93 const char* const* matchRules = &argv[3];
94 size_t matchRulesCount = (size_t)(argc - 3);
95
96 // Rendering Tests
97 std::ostream& out = std::cout;
98 for (auto backend : skqp.getSupportedBackends()) {
99 auto testPrefix = std::string(SkQP::GetBackendName(backend)) + "_";
100 for (auto gmFactory : skqp.getGMs()) {
101 auto testName = testPrefix + SkQP::GetGMName(gmFactory);
102 if (should_skip(matchRules, matchRulesCount, testName.c_str())) {
103 continue;
104 }
105 out << "Starting: " << testName << std::endl;
106 SkQP::RenderOutcome outcome;
107 std::string except;
108
109 std::tie(outcome, except) = skqp.evaluateGM(backend, gmFactory);
110 if (!except.empty()) {
111 out << "ERROR: " << testName << " (" << except << ")\n";
112 ret = 1;
113 } else if (outcome.fMaxError != 0) {
114 out << "FAILED: " << testName << " (" << outcome.fMaxError << ")\n";
115 ret = 1;
116 } else {
117 out << "Passed: " << testName << "\n";
118 }
119 out.flush();
120 }
121 }
122
123 // Unit Tests
124 for (auto test : skqp.getUnitTests()) {
125 auto testName = std::string("unitTest_") + SkQP::GetUnitTestName(test);
126 if (should_skip(matchRules, matchRulesCount, testName.c_str())) {
127 continue;
128 }
129 out << "Starting test: " << testName << std::endl;
130 std::vector<std::string> errors = skqp.executeTest(test);
131 if (!errors.empty()) {
132 out << "TEST FAILED (" << errors.size() << "): " << testName << "\n";
133 for (const std::string& error : errors) {
134 out << error << "\n";
135 }
136 ret = 1;
137 } else {
138 out << "Test passed: " << testName << "\n";
139 }
140 out.flush();
141 }
142 skqp.makeReport();
143
144 return ret;
145 }
146