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