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