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