skia_test.cpp revision 261c66668269588a26757a0bfe28a3a3eac07665
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 "SkOSFile.h"
11#include "SkTArray.h"
12#include "SkTemplates.h"
13#include "SkThreadPool.h"
14#include "SkTime.h"
15#include "Test.h"
16#include "OverwriteLine.h"
17
18#if SK_SUPPORT_GPU
19#include "GrContext.h"
20#endif
21
22using namespace skiatest;
23
24DEFINE_string2(match, m, NULL, "[~][^]substring[$] [...] of test name to run.\n" \
25                               "Multiple matches may be separated by spaces.\n" \
26                               "~ causes a matching test to always be skipped\n" \
27                               "^ requires the start of the test to match\n" \
28                               "$ requires the end of the test to match\n" \
29                               "^ and $ requires an exact match\n" \
30                               "If a test does not match any list entry,\n" \
31                               "it is skipped unless some list entry starts with ~");
32DEFINE_string2(tmpDir, t, NULL, "tmp directory for tests to use.");
33DEFINE_string2(resourcePath, i, "resources", "directory for test resources.");
34DEFINE_bool2(extendedTest, x, false, "run extended tests for pathOps.");
35DEFINE_bool2(single, z, false, "run tests on a single thread internally.");
36DEFINE_bool2(verbose, v, false, "enable verbose output.");
37DEFINE_int32(threads, SkThreadPool::kThreadPerCore,
38             "Run threadsafe tests on a threadpool with this many threads.");
39
40// need to explicitly declare this, or we get some weird infinite loop llist
41template TestRegistry* TestRegistry::gHead;
42
43class Iter {
44public:
45    Iter() { this->reset(); }
46    void reset() { fReg = TestRegistry::Head(); }
47
48    Test* next(Reporter* r) {
49        if (fReg) {
50            TestRegistry::Factory fact = fReg->factory();
51            fReg = fReg->next();
52            Test* test = fact(NULL);
53            test->setReporter(r);
54            return test;
55        }
56        return NULL;
57    }
58
59private:
60    const TestRegistry* fReg;
61};
62
63class DebugfReporter : public Reporter {
64public:
65    explicit DebugfReporter(int total) : fDone(0), fTotal(total) {}
66
67    virtual bool allowExtendedTest() const SK_OVERRIDE { return FLAGS_extendedTest; }
68    virtual bool allowThreaded()     const SK_OVERRIDE { return !FLAGS_single; }
69    virtual bool verbose()           const SK_OVERRIDE { return FLAGS_verbose; }
70
71protected:
72    virtual void onReportFailed(const SkString& desc) SK_OVERRIDE {
73        SkDebugf("\nFAILED: %s", desc.c_str());
74    }
75
76    virtual void onEnd(Test* test) SK_OVERRIDE {
77        const int done = 1 + sk_atomic_inc(&fDone);
78
79        if (!test->passed()) {
80            SkDebugf("\n---- %s FAILED", test->getName());
81        }
82
83        SkString prefix(kSkOverwriteLine);
84        SkString time;
85        if (FLAGS_verbose) {
86            prefix.printf("\n");
87            time.printf("%5dms ", test->elapsedMs());
88        }
89        SkDebugf("%s[%3d/%3d] %s%s", prefix.c_str(), done, fTotal, time.c_str(), test->getName());
90    }
91
92private:
93    int32_t fDone;  // atomic
94    const int fTotal;
95};
96
97SkString Test::GetTmpDir() {
98    const char* tmpDir = FLAGS_tmpDir.isEmpty() ? NULL : FLAGS_tmpDir[0];
99    return SkString(tmpDir);
100}
101
102SkString Test::GetResourcePath() {
103    const char* resourcePath = FLAGS_resourcePath.isEmpty() ? NULL : FLAGS_resourcePath[0];
104    return SkString(resourcePath);
105}
106
107// Deletes self when run.
108class SkTestRunnable : public SkRunnable {
109public:
110  // Takes ownership of test.
111  SkTestRunnable(Test* test, int32_t* failCount) : fTest(test), fFailCount(failCount) {}
112
113  virtual void run() {
114      fTest->run();
115      if(!fTest->passed()) {
116          sk_atomic_inc(fFailCount);
117      }
118      SkDELETE(this);
119  }
120
121private:
122    SkAutoTDelete<Test> fTest;
123    int32_t* fFailCount;
124};
125
126int tool_main(int argc, char** argv);
127int tool_main(int argc, char** argv) {
128    SkCommandLineFlags::SetUsage("");
129    SkCommandLineFlags::Parse(argc, argv);
130
131#if SK_ENABLE_INST_COUNT
132    gPrintInstCount = true;
133#endif
134
135    SkGraphics::Init();
136
137    {
138        SkString header("Skia UnitTests:");
139        if (!FLAGS_match.isEmpty()) {
140            header.appendf(" --match");
141            for (int index = 0; index < FLAGS_match.count(); ++index) {
142                header.appendf(" %s", FLAGS_match[index]);
143            }
144        }
145        SkString tmpDir = Test::GetTmpDir();
146        if (!tmpDir.isEmpty()) {
147            header.appendf(" --tmpDir %s", tmpDir.c_str());
148        }
149        SkString resourcePath = Test::GetResourcePath();
150        if (!resourcePath.isEmpty()) {
151            header.appendf(" --resourcePath %s", resourcePath.c_str());
152        }
153#ifdef SK_DEBUG
154        header.append(" SK_DEBUG");
155#else
156        header.append(" SK_RELEASE");
157#endif
158        header.appendf(" skia_arch_width=%d", (int)sizeof(void*) * 8);
159        SkDebugf(header.c_str());
160    }
161
162
163    // Count tests first.
164    int total = 0;
165    int toRun = 0;
166    Test* test;
167
168    Iter iter;
169    while ((test = iter.next(NULL/*reporter not needed*/)) != NULL) {
170        SkAutoTDelete<Test> owned(test);
171
172        if(!SkCommandLineFlags::ShouldSkip(FLAGS_match, test->getName())) {
173            toRun++;
174        }
175        total++;
176    }
177
178    // Now run them.
179    iter.reset();
180    int32_t failCount = 0;
181    int skipCount = 0;
182
183    SkThreadPool threadpool(FLAGS_threads);
184    SkTArray<Test*> unsafeTests;  // Always passes ownership to an SkTestRunnable
185
186    DebugfReporter reporter(toRun);
187    for (int i = 0; i < total; i++) {
188        SkAutoTDelete<Test> test(iter.next(&reporter));
189        if (SkCommandLineFlags::ShouldSkip(FLAGS_match, test->getName())) {
190            ++skipCount;
191        } else if (!test->isThreadsafe()) {
192            unsafeTests.push_back() = test.detach();
193        } else {
194            threadpool.add(SkNEW_ARGS(SkTestRunnable, (test.detach(), &failCount)));
195        }
196    }
197
198    // Run the tests that aren't threadsafe.
199    for (int i = 0; i < unsafeTests.count(); i++) {
200        SkNEW_ARGS(SkTestRunnable, (unsafeTests[i], &failCount))->run();
201    }
202
203    // Block until threaded tests finish.
204    threadpool.wait();
205
206    if (FLAGS_verbose) {
207        SkDebugf("\nFinished %d tests, %d failures, %d skipped. (%d internal tests)",
208                 toRun, failCount, skipCount, reporter.countTests());
209    }
210    SkGraphics::Term();
211    GpuTest::DestroyContexts();
212
213    SkDebugf("\n");
214    return (failCount == 0) ? 0 : 1;
215}
216
217#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
218int main(int argc, char * const argv[]) {
219    return tool_main(argc, (char**) argv);
220}
221#endif
222