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/private/SkTArray.h"
12 #include "include/private/SkTemplates.h"
13 #include "src/core/SkOSFile.h"
14 #include "src/core/SkTaskGroup.h"
15 #include "src/pathops/SkPathOpsDebug.h"
16 #include "tests/PathOpsDebug.h"
17 #include "tests/Test.h"
18 #include "tools/CrashHandler.h"
19 #include "tools/OverwriteLine.h"
20 #include "tools/Resources.h"
21 #include "tools/flags/CommandLineFlags.h"
22 #include "tools/gpu/GrContextFactory.h"
23
24 using namespace skiatest;
25 using namespace sk_gpu_test;
26
27 static DEFINE_bool2(dumpOp, d, false, "dump the pathOps to a file to recover mid-crash.");
28 static DEFINE_bool2(extendedTest, x, false, "run extended tests for pathOps.");
29 static DEFINE_bool2(runFail, f, false, "check for success on tests known to fail.");
30 static DEFINE_bool2(verifyOp, y, false, "compare the pathOps result against a region.");
31 static DEFINE_string2(json, J, "", "write json version of tests.");
32 static DEFINE_bool2(verbose, v, false, "enable verbose output from the test driver.");
33 static DEFINE_bool2(veryVerbose, V, false, "tell individual tests to be verbose.");
34 static DEFINE_bool(cpu, true, "Run CPU-bound work?");
35 static DEFINE_bool(gpu, true, "Run GPU-bound work?");
36 static DEFINE_bool(graphite, true, "Run Graphite 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.fProc(&reporter, GrContextOptions());
121 SkMSec elapsed = timer.elapsedMsInt();
122 if (reporter.fError) {
123 fStatus->reportFailure();
124 }
125 fStatus->endTest(fTest.fName, !reporter.fError, elapsed, reporter.fTestCount);
126 }
127
128 private:
129 Test fTest;
130 Status* fStatus;
131 };
132
should_run(const char * testName,bool isGPUTest,bool isGraphiteTest)133 static bool should_run(const char* testName, bool isGPUTest, bool isGraphiteTest) {
134 if (CommandLineFlags::ShouldSkip(FLAGS_match, testName)) {
135 return false;
136 }
137 if (!FLAGS_cpu && !isGPUTest && !isGraphiteTest) {
138 return false;
139 }
140 if (!FLAGS_gpu && isGPUTest) {
141 return false;
142 }
143 if (!FLAGS_graphite && isGraphiteTest) {
144 return false;
145 }
146 return true;
147 }
148
main(int argc,char ** argv)149 int main(int argc, char** argv) {
150 CommandLineFlags::Parse(argc, argv);
151 #if DEBUG_DUMP_VERIFY
152 SkPathOpsDebug::gDumpOp = FLAGS_dumpOp;
153 SkPathOpsDebug::gVerifyOp = FLAGS_verifyOp;
154 #endif
155 SkPathOpsDebug::gRunFail = FLAGS_runFail;
156 SkPathOpsDebug::gVeryVerbose = FLAGS_veryVerbose;
157 PathOpsDebug::gOutFirst = true;
158 PathOpsDebug::gCheckForDuplicateNames = false;
159 PathOpsDebug::gOutputSVG = false;
160 if ((PathOpsDebug::gJson = !FLAGS_json.isEmpty())) {
161 PathOpsDebug::gOut = fopen(FLAGS_json[0], "wb");
162 fprintf(PathOpsDebug::gOut, "{\n");
163 FLAGS_threads = 0;
164 PathOpsDebug::gMarkJsonFlaky = false;
165 }
166 SetupCrashHandler();
167
168 SkAutoGraphics ag;
169
170 {
171 SkString header("Skia UnitTests:");
172 if (!FLAGS_match.isEmpty()) {
173 header.appendf(" --match");
174 for (int index = 0; index < FLAGS_match.count(); ++index) {
175 header.appendf(" %s", FLAGS_match[index]);
176 }
177 }
178 SkString tmpDir = skiatest::GetTmpDir();
179 if (!tmpDir.isEmpty()) {
180 header.appendf(" --tmpDir %s", tmpDir.c_str());
181 }
182 SkString resourcePath = GetResourcePath();
183 if (!resourcePath.isEmpty()) {
184 header.appendf(" --resourcePath %s", resourcePath.c_str());
185 }
186 #if DEBUG_COIN
187 if (FLAGS_coinTest) {
188 header.appendf(" -c");
189 }
190 #endif
191 if (FLAGS_dumpOp) {
192 header.appendf(" -d");
193 }
194 #ifdef SK_DEBUG
195 if (FLAGS_runFail) {
196 header.appendf(" -f");
197 }
198 #endif
199 if (FLAGS_verbose) {
200 header.appendf(" -v");
201 }
202 if (FLAGS_veryVerbose) {
203 header.appendf(" -V");
204 }
205 if (FLAGS_extendedTest) {
206 header.appendf(" -x");
207 }
208 if (FLAGS_verifyOp) {
209 header.appendf(" -y");
210 }
211 #ifdef SK_DEBUG
212 header.append(" SK_DEBUG");
213 #else
214 header.append(" SK_RELEASE");
215 #endif
216 if (FLAGS_veryVerbose) {
217 header.appendf("\n");
218 }
219 SkDebugf("%s", header.c_str());
220 }
221
222
223 // Count tests first.
224 int total = 0;
225 int toRun = 0;
226
227 for (const Test& test : TestRegistry::Range()) {
228 if (should_run(test.fName, test.fNeedsGpu, test.fNeedsGraphite)) {
229 toRun++;
230 }
231 total++;
232 }
233
234 // Now run them.
235 int skipCount = 0;
236
237 SkTaskGroup::Enabler enabled(FLAGS_threads);
238 SkTaskGroup cpuTests;
239 SkTArray<const Test*> gpuTests;
240
241 Status status(toRun);
242
243 for (const Test& test : TestRegistry::Range()) {
244 if (!should_run(test.fName, test.fNeedsGpu, test.fNeedsGraphite)) {
245 ++skipCount;
246 } else if (test.fNeedsGpu || test.fNeedsGraphite) {
247 gpuTests.push_back(&test);
248 } else {
249 cpuTests.add(SkTestRunnable(test, &status));
250 }
251 }
252
253 // Run GPU tests on this thread.
254 for (int i = 0; i < gpuTests.count(); i++) {
255 SkTestRunnable(*gpuTests[i], &status)();
256 }
257
258 // Block until threaded tests finish.
259 cpuTests.wait();
260
261 if (FLAGS_verbose) {
262 SkDebugf(
263 "\nFinished %d tests, %d failures, %d skipped. "
264 "(%d internal tests)",
265 toRun, status.failCount(), skipCount, status.testCount());
266 if (gVerboseFinalize) {
267 (*gVerboseFinalize)();
268 }
269 }
270
271 SkDebugf("\n");
272 #if DEBUG_COIN
273 if (FLAGS_coinTest) {
274 SkPathOpsDebug::DumpCoinDict();
275 }
276 #endif
277 if (PathOpsDebug::gJson) {
278 fprintf(PathOpsDebug::gOut, "\n}\n");
279 fclose(PathOpsDebug::gOut);
280 }
281 return (status.failCount() == 0) ? 0 : 1;
282 }
283