• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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