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
26struct GrContextOptions {};
27#endif
28
29using namespace skiatest;
30using namespace sk_gpu_test;
31
32DEFINE_bool2(dumpOp, d, false, "dump the pathOps to a file to recover mid-crash.");
33DEFINE_bool2(extendedTest, x, false, "run extended tests for pathOps.");
34DEFINE_bool2(runFail, f, false, "check for success on tests known to fail.");
35DEFINE_bool2(verifyOp, y, false, "compare the pathOps result against a region.");
36
37#if DEBUG_COIN
38DEFINE_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
42template TestRegistry* TestRegistry::gHead;
43void (*gVerboseFinalize)() = nullptr;
44
45// The threads report back to this object when they are done.
46class Status {
47public:
48    explicit Status(int total)
49        : fDone(0), fTestCount(0), fFailCount(0), fTotal(total) {}
50    // Threadsafe.
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
73    void reportFailure() { sk_atomic_inc(&fFailCount); }
74
75    int32_t testCount() { return fTestCount; }
76    int32_t failCount() { return fFailCount; }
77
78private:
79    int32_t fDone;  // atomic
80    int32_t fTestCount;  // atomic
81    int32_t fFailCount;  // atomic
82    const int fTotal;
83};
84
85class SkTestRunnable {
86public:
87    SkTestRunnable(const Test& test, Status* status) : fTest(test), fStatus(status) {}
88
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
115private:
116    Test fTest;
117    Status* fStatus;
118};
119
120static 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
133int 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