1 /*
2 * Copyright 2011 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 <atomic>
9 #include "include/core/SkGraphics.h"
10 #include "include/core/SkTime.h"
11 #include "include/gpu/GrContext.h"
12 #include "include/private/SkTArray.h"
13 #include "include/private/SkTemplates.h"
14 #include "src/core/SkOSFile.h"
15 #include "src/core/SkTaskGroup.h"
16 #include "src/pathops/SkPathOpsDebug.h"
17 #include "tests/PathOpsDebug.h"
18 #include "tests/Test.h"
19 #include "tools/CrashHandler.h"
20 #include "tools/OverwriteLine.h"
21 #include "tools/Resources.h"
22 #include "tools/flags/CommandLineFlags.h"
23 #include "tools/gpu/GrContextFactory.h"
24
25 using namespace skiatest;
26 using namespace sk_gpu_test;
27
28 static DEFINE_bool2(dumpOp, d, false, "dump the pathOps to a file to recover mid-crash.");
29 static DEFINE_bool2(extendedTest, x, false, "run extended tests for pathOps.");
30 static DEFINE_bool2(runFail, f, false, "check for success on tests known to fail.");
31 static DEFINE_bool2(verifyOp, y, false, "compare the pathOps result against a region.");
32 static DEFINE_string2(json, J, "", "write json version of tests.");
33 static DEFINE_bool2(verbose, v, false, "enable verbose output from the test driver.");
34 static DEFINE_bool2(veryVerbose, V, false, "tell individual tests to be verbose.");
35 static DEFINE_bool(cpu, true, "master switch for running CPU-bound work.");
36 static DEFINE_bool(gpu, true, "master switch for running GPU-bound work.");
37
38 static DEFINE_string2(match, m, nullptr,
39 "[~][^]substring[$] [...] of name to run.\n"
40 "Multiple matches may be separated by spaces.\n"
41 "~ causes a matching name to always be skipped\n"
42 "^ requires the start of the name to match\n"
43 "$ requires the end of the name to match\n"
44 "^ and $ requires an exact match\n"
45 "If a name does not match any list entry,\n"
46 "it is skipped unless some list entry starts with ~");
47
48 static DEFINE_int_2(threads, j, -1,
49 "Run threadsafe tests on a threadpool with this many extra threads, "
50 "defaulting to one extra thread per core.");
51
52 #if DEBUG_COIN
53 static DEFINE_bool2(coinTest, c, false, "detect unused coincidence algorithms.");
54 #endif
55
56 // need to explicitly declare this, or we get some weird infinite loop llist
57 template TestRegistry* TestRegistry::gHead;
58 void (*gVerboseFinalize)() = nullptr;
59
60 // The threads report back to this object when they are done.
61 class Status {
62 public:
Status(int total)63 explicit Status(int total)
64 : fDone(0), fTestCount(0), fFailCount(0), fTotal(total) {}
65 // Threadsafe.
endTest(const char * testName,bool success,SkMSec elapsed,int testCount)66 void endTest(const char* testName,
67 bool success,
68 SkMSec elapsed,
69 int testCount) {
70 const int done = ++fDone;
71 fTestCount += testCount;
72 if (!success) {
73 SkDebugf("\n---- %s FAILED", testName);
74 }
75
76 SkString prefix(kSkOverwriteLine);
77 SkString time;
78 if (FLAGS_verbose) {
79 prefix.printf("\n");
80 time.printf("%5dms ", elapsed);
81 }
82 SkDebugf("%s[%3d/%3d] %s%s", prefix.c_str(), done, fTotal, time.c_str(),
83 testName);
84 }
85
reportFailure()86 void reportFailure() { fFailCount++; }
87
testCount()88 int32_t testCount() { return fTestCount; }
failCount()89 int32_t failCount() { return fFailCount; }
90
91 private:
92 std::atomic<int32_t> fDone;
93 std::atomic<int32_t> fTestCount;
94 std::atomic<int32_t> fFailCount;
95 const int fTotal;
96 };
97
98 class SkTestRunnable {
99 public:
SkTestRunnable(const Test & test,Status * status)100 SkTestRunnable(const Test& test, Status* status) : fTest(test), fStatus(status) {}
101
operator ()()102 void operator()() {
103 struct TestReporter : public skiatest::Reporter {
104 public:
105 TestReporter() : fStats(nullptr), fError(false), fTestCount(0) {}
106 void bumpTestCount() override { ++fTestCount; }
107 bool allowExtendedTest() const override { return FLAGS_extendedTest; }
108 bool verbose() const override { return FLAGS_veryVerbose; }
109 void reportFailed(const skiatest::Failure& failure) override {
110 SkDebugf("\nFAILED: %s", failure.toString().c_str());
111 fError = true;
112 }
113 void* stats() const override { return fStats; }
114 void* fStats;
115 bool fError;
116 int fTestCount;
117 } reporter;
118
119 const Timer timer;
120 fTest.proc(&reporter, GrContextOptions());
121 SkMSec elapsed = timer.elapsedMsInt();
122 if (reporter.fError) {
123 fStatus->reportFailure();
124 }
125 fStatus->endTest(fTest.name, !reporter.fError, elapsed, reporter.fTestCount);
126 }
127
128 private:
129 Test fTest;
130 Status* fStatus;
131 };
132
should_run(const char * testName,bool isGPUTest)133 static bool should_run(const char* testName, bool isGPUTest) {
134 if (CommandLineFlags::ShouldSkip(FLAGS_match, testName)) {
135 return false;
136 }
137 if (!FLAGS_cpu && !isGPUTest) {
138 return false;
139 }
140 if (!FLAGS_gpu && isGPUTest) {
141 return false;
142 }
143 return true;
144 }
145
main(int argc,char ** argv)146 int main(int argc, char** argv) {
147 CommandLineFlags::Parse(argc, argv);
148 #if DEBUG_DUMP_VERIFY
149 SkPathOpsDebug::gDumpOp = FLAGS_dumpOp;
150 SkPathOpsDebug::gVerifyOp = FLAGS_verifyOp;
151 #endif
152 SkPathOpsDebug::gRunFail = FLAGS_runFail;
153 SkPathOpsDebug::gVeryVerbose = FLAGS_veryVerbose;
154 PathOpsDebug::gOutFirst = true;
155 PathOpsDebug::gCheckForDuplicateNames = false;
156 PathOpsDebug::gOutputSVG = false;
157 if ((PathOpsDebug::gJson = !FLAGS_json.isEmpty())) {
158 PathOpsDebug::gOut = fopen(FLAGS_json[0], "wb");
159 fprintf(PathOpsDebug::gOut, "{\n");
160 FLAGS_threads = 0;
161 PathOpsDebug::gMarkJsonFlaky = false;
162 }
163 SetupCrashHandler();
164
165 SkAutoGraphics ag;
166
167 {
168 SkString header("Skia UnitTests:");
169 if (!FLAGS_match.isEmpty()) {
170 header.appendf(" --match");
171 for (int index = 0; index < FLAGS_match.count(); ++index) {
172 header.appendf(" %s", FLAGS_match[index]);
173 }
174 }
175 SkString tmpDir = skiatest::GetTmpDir();
176 if (!tmpDir.isEmpty()) {
177 header.appendf(" --tmpDir %s", tmpDir.c_str());
178 }
179 SkString resourcePath = GetResourcePath();
180 if (!resourcePath.isEmpty()) {
181 header.appendf(" --resourcePath %s", resourcePath.c_str());
182 }
183 #if DEBUG_COIN
184 if (FLAGS_coinTest) {
185 header.appendf(" -c");
186 }
187 #endif
188 if (FLAGS_dumpOp) {
189 header.appendf(" -d");
190 }
191 #ifdef SK_DEBUG
192 if (FLAGS_runFail) {
193 header.appendf(" -f");
194 }
195 #endif
196 if (FLAGS_verbose) {
197 header.appendf(" -v");
198 }
199 if (FLAGS_veryVerbose) {
200 header.appendf(" -V");
201 }
202 if (FLAGS_extendedTest) {
203 header.appendf(" -x");
204 }
205 if (FLAGS_verifyOp) {
206 header.appendf(" -y");
207 }
208 #ifdef SK_DEBUG
209 header.append(" SK_DEBUG");
210 #else
211 header.append(" SK_RELEASE");
212 #endif
213 if (FLAGS_veryVerbose) {
214 header.appendf("\n");
215 }
216 SkDebugf("%s", header.c_str());
217 }
218
219
220 // Count tests first.
221 int total = 0;
222 int toRun = 0;
223
224 for (const Test& test : TestRegistry::Range()) {
225 if (should_run(test.name, test.needsGpu)) {
226 toRun++;
227 }
228 total++;
229 }
230
231 // Now run them.
232 int skipCount = 0;
233
234 SkTaskGroup::Enabler enabled(FLAGS_threads);
235 SkTaskGroup cpuTests;
236 SkTArray<const Test*> gpuTests;
237
238 Status status(toRun);
239
240 for (const Test& test : TestRegistry::Range()) {
241 if (!should_run(test.name, test.needsGpu)) {
242 ++skipCount;
243 } else if (test.needsGpu) {
244 gpuTests.push_back(&test);
245 } else {
246 cpuTests.add(SkTestRunnable(test, &status));
247 }
248 }
249
250 // Run GPU tests on this thread.
251 for (int i = 0; i < gpuTests.count(); i++) {
252 SkTestRunnable(*gpuTests[i], &status)();
253 }
254
255 // Block until threaded tests finish.
256 cpuTests.wait();
257
258 if (FLAGS_verbose) {
259 SkDebugf(
260 "\nFinished %d tests, %d failures, %d skipped. "
261 "(%d internal tests)",
262 toRun, status.failCount(), skipCount, status.testCount());
263 if (gVerboseFinalize) {
264 (*gVerboseFinalize)();
265 }
266 }
267
268 SkDebugf("\n");
269 #if DEBUG_COIN
270 if (FLAGS_coinTest) {
271 SkPathOpsDebug::DumpCoinDict();
272 }
273 #endif
274 if (PathOpsDebug::gJson) {
275 fprintf(PathOpsDebug::gOut, "\n}\n");
276 fclose(PathOpsDebug::gOut);
277 }
278 return (status.failCount() == 0) ? 0 : 1;
279 }
280