skia_test.cpp revision 197845ae157da0175bb8dd05c4fd9eb9cd935e54
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 "SkCommandLineFlags.h"
9#include "SkGraphics.h"
10#include "SkRunnable.h"
11#include "SkThreadPool.h"
12#include "SkTArray.h"
13#include "SkTemplates.h"
14#include "Test.h"
15#include "SkOSFile.h"
16
17#if SK_SUPPORT_GPU
18#include "GrContext.h"
19#endif
20
21using namespace skiatest;
22
23// need to explicitly declare this, or we get some weird infinite loop llist
24template TestRegistry* TestRegistry::gHead;
25
26class Iter {
27public:
28    Iter(Reporter* r) : fReporter(r) {
29        r->ref();
30        fReg = TestRegistry::Head();
31    }
32
33    ~Iter() {
34        fReporter->unref();
35    }
36
37    Test* next() {
38        if (fReg) {
39            TestRegistry::Factory fact = fReg->factory();
40            fReg = fReg->next();
41            Test* test = fact(NULL);
42            test->setReporter(fReporter);
43            return test;
44        }
45        return NULL;
46    }
47
48    static int Count() {
49        const TestRegistry* reg = TestRegistry::Head();
50        int count = 0;
51        while (reg) {
52            count += 1;
53            reg = reg->next();
54        }
55        return count;
56    }
57
58private:
59    Reporter* fReporter;
60    const TestRegistry* fReg;
61};
62
63static const char* result2string(Reporter::Result result) {
64    return result == Reporter::kPassed ? "passed" : "FAILED";
65}
66
67class DebugfReporter : public Reporter {
68public:
69    DebugfReporter(bool allowExtendedTest, bool allowThreaded)
70        : fNextIndex(0)
71        , fTotal(0)
72        , fAllowExtendedTest(allowExtendedTest)
73        , fAllowThreaded(allowThreaded) {
74    }
75
76    void setTotal(int total) {
77        fTotal = total;
78    }
79
80    virtual bool allowExtendedTest() const {
81        return fAllowExtendedTest;
82    }
83
84    virtual bool allowThreaded() const {
85        return fAllowThreaded;
86    }
87
88protected:
89    virtual void onStart(Test* test) {
90        const int index = sk_atomic_inc(&fNextIndex);
91        SkDebugf("[%d/%d] %s...\n", index+1, fTotal, test->getName());
92    }
93    virtual void onReport(const char desc[], Reporter::Result result) {
94        SkDebugf("\t%s: %s\n", result2string(result), desc);
95    }
96
97    virtual void onEnd(Test* test) {
98        if (!test->passed()) {
99          SkDebugf("---- FAILED\n");
100        }
101    }
102
103private:
104    int32_t fNextIndex;
105    int fTotal;
106    bool fAllowExtendedTest;
107    bool fAllowThreaded;
108};
109
110static const char* make_canonical_dir_path(const char* path, SkString* storage) {
111    if (path) {
112        // clean it up so it always has a trailing searator
113        size_t len = strlen(path);
114        if (0 == len) {
115            path = NULL;
116        } else if (SkPATH_SEPARATOR != path[len - 1]) {
117            // resize to len + 1, to make room for searator
118            storage->set(path, len + 1);
119            storage->writable_str()[len] = SkPATH_SEPARATOR;
120            path = storage->c_str();
121        }
122    }
123    return path;
124}
125
126static SkString gTmpDir;
127
128const SkString& Test::GetTmpDir() {
129    return gTmpDir;
130}
131
132static SkString gResourcePath;
133
134const SkString& Test::GetResourcePath() {
135    return gResourcePath;
136}
137
138DEFINE_string2(match, m, NULL, "substring of test name to run.");
139DEFINE_string2(tmpDir, t, NULL, "tmp directory for tests to use.");
140DEFINE_string2(resourcePath, i, NULL, "directory for test resources.");
141DEFINE_bool2(extendedTest, x, false, "run extended tests for pathOps.");
142DEFINE_bool2(threaded, z, false, "allow tests to use multiple threads internally.");
143DEFINE_bool2(verbose, v, false, "enable verbose output.");
144DEFINE_int32(threads, 8,
145             "If >0, run threadsafe tests on a threadpool with this many threads.");
146
147// Deletes self when run.
148class SkTestRunnable : public SkRunnable {
149public:
150  // Takes ownership of test.
151  SkTestRunnable(Test* test, int32_t* failCount) : fTest(test), fFailCount(failCount) {}
152
153  virtual void run() {
154      fTest->run();
155      if(!fTest->passed()) {
156          sk_atomic_inc(fFailCount);
157      }
158      SkDELETE(this);
159  }
160
161private:
162    SkAutoTDelete<Test> fTest;
163    int32_t* fFailCount;
164};
165
166int tool_main(int argc, char** argv);
167int tool_main(int argc, char** argv) {
168    SkCommandLineFlags::SetUsage("");
169    SkCommandLineFlags::Parse(argc, argv);
170
171    if (!FLAGS_tmpDir.isEmpty()) {
172        make_canonical_dir_path(FLAGS_tmpDir[0], &gTmpDir);
173    }
174    if (!FLAGS_resourcePath.isEmpty()) {
175        make_canonical_dir_path(FLAGS_resourcePath[0], &gResourcePath);
176    }
177
178#if SK_ENABLE_INST_COUNT
179    gPrintInstCount = true;
180#endif
181
182    SkGraphics::Init();
183
184    {
185        SkString header("Skia UnitTests:");
186        if (!FLAGS_match.isEmpty()) {
187            header.appendf(" --match %s", FLAGS_match[0]);
188        }
189        if (!gTmpDir.isEmpty()) {
190            header.appendf(" --tmpDir %s", gTmpDir.c_str());
191        }
192        if (!gResourcePath.isEmpty()) {
193            header.appendf(" --resourcePath %s", gResourcePath.c_str());
194        }
195#ifdef SK_DEBUG
196        header.append(" SK_DEBUG");
197#else
198        header.append(" SK_RELEASE");
199#endif
200#ifdef SK_SCALAR_IS_FIXED
201        header.append(" SK_SCALAR_IS_FIXED");
202#else
203        header.append(" SK_SCALAR_IS_FLOAT");
204#endif
205        SkDebugf("%s\n", header.c_str());
206    }
207
208    DebugfReporter reporter(FLAGS_extendedTest, FLAGS_threaded);
209    Iter iter(&reporter);
210
211    const int count = Iter::Count();
212    reporter.setTotal(count);
213    int32_t failCount = 0;
214    int skipCount = 0;
215
216    SkAutoTDelete<SkThreadPool> threadpool(SkNEW_ARGS(SkThreadPool, (FLAGS_threads)));
217    SkTArray<Test*> unsafeTests;  // Always passes ownership to an SkTestRunnable
218    for (int i = 0; i < count; i++) {
219        SkAutoTDelete<Test> test(iter.next());
220        if (!FLAGS_match.isEmpty() && !strstr(test->getName(), FLAGS_match[0])) {
221            ++skipCount;
222        } else if (!test->isThreadsafe()) {
223            unsafeTests.push_back() = test.detach();
224        } else {
225            threadpool->add(SkNEW_ARGS(SkTestRunnable, (test.detach(), &failCount)));
226        }
227    }
228
229    // Run the tests that aren't threadsafe.
230    for (int i = 0; i < unsafeTests.count(); i++) {
231        SkNEW_ARGS(SkTestRunnable, (unsafeTests[i], &failCount))->run();
232    }
233
234    // Blocks until threaded tests finish.
235    threadpool.free();
236
237    SkDebugf("Finished %d tests, %d failures, %d skipped.\n",
238             count, failCount, skipCount);
239    const int testCount = reporter.countTests();
240    if (FLAGS_verbose && testCount > 0) {
241        SkDebugf("Ran %d Internal tests.\n", testCount);
242    }
243#if SK_SUPPORT_GPU
244
245#if GR_CACHE_STATS
246    GrContext *gr = GpuTest::GetContext();
247
248    gr->printCacheStats();
249#endif
250
251#endif
252
253    SkGraphics::Term();
254    GpuTest::DestroyContexts();
255
256    return (failCount == 0) ? 0 : 1;
257}
258
259#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
260int main(int argc, char * const argv[]) {
261    return tool_main(argc, (char**) argv);
262}
263#endif
264