• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 struct Args {
35   char *assetDir;
36   char *renderTests;
37   char *outputDir;
38 };
39 }  // namespace
40 
41 static 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 
should_skip(const char * const * rules,size_t count,const char * name)52 static 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 
parse_args(int argc,char * argv[],Args * args)82 static 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 
main(int argc,char * argv[])94 int 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