• 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/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