8d0a524a48
PathOps tests internal routines direcctly. Check to make sure that test points, lines, quads, curves, triangles, and bounds read from arrays are valid (i.e., don't contain NaN) before calling the test function. Repurpose the test flags. - make 'v' verbose test region output against path output - make 'z' single threaded (before it made it multithreaded) The latter change speeds up tests run by the buildbot by 2x to 3x. BUG= Review URL: https://codereview.chromium.org/19374003 git-svn-id: http://skia.googlecode.com/svn/trunk@10107 2bbb7eff-a529-9590-31e7-b0007b416f81
309 lines
8.8 KiB
C++
309 lines
8.8 KiB
C++
/*
|
|
* Copyright 2011 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "SkCommandLineFlags.h"
|
|
#include "SkGraphics.h"
|
|
#include "SkOSFile.h"
|
|
#include "SkRunnable.h"
|
|
#include "SkTArray.h"
|
|
#include "SkTemplates.h"
|
|
#include "SkThreadPool.h"
|
|
#include "SkTime.h"
|
|
#include "Test.h"
|
|
|
|
#if SK_SUPPORT_GPU
|
|
#include "GrContext.h"
|
|
#endif
|
|
|
|
using namespace skiatest;
|
|
|
|
// need to explicitly declare this, or we get some weird infinite loop llist
|
|
template TestRegistry* TestRegistry::gHead;
|
|
|
|
class Iter {
|
|
public:
|
|
Iter(Reporter* r) : fReporter(r) {
|
|
r->ref();
|
|
this->reset();
|
|
}
|
|
|
|
void reset() {
|
|
fReg = TestRegistry::Head();
|
|
}
|
|
|
|
~Iter() {
|
|
fReporter->unref();
|
|
}
|
|
|
|
Test* next() {
|
|
if (fReg) {
|
|
TestRegistry::Factory fact = fReg->factory();
|
|
fReg = fReg->next();
|
|
Test* test = fact(NULL);
|
|
test->setReporter(fReporter);
|
|
return test;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
private:
|
|
Reporter* fReporter;
|
|
const TestRegistry* fReg;
|
|
};
|
|
|
|
class DebugfReporter : public Reporter {
|
|
public:
|
|
DebugfReporter(bool allowExtendedTest, bool allowThreaded, bool verbose)
|
|
: fNextIndex(0)
|
|
, fPending(0)
|
|
, fTotal(0)
|
|
, fAllowExtendedTest(allowExtendedTest)
|
|
, fAllowThreaded(allowThreaded)
|
|
, fVerbose(verbose) {
|
|
}
|
|
|
|
void setTotal(int total) {
|
|
fTotal = total;
|
|
}
|
|
|
|
virtual bool allowExtendedTest() const SK_OVERRIDE {
|
|
return fAllowExtendedTest;
|
|
}
|
|
|
|
virtual bool allowThreaded() const SK_OVERRIDE {
|
|
return fAllowThreaded;
|
|
}
|
|
|
|
virtual bool verbose() const SK_OVERRIDE {
|
|
return fVerbose;
|
|
}
|
|
|
|
protected:
|
|
virtual void onStart(Test* test) {
|
|
const int index = sk_atomic_inc(&fNextIndex);
|
|
sk_atomic_inc(&fPending);
|
|
SkDebugf("[%3d/%3d] (%d) %s\n", index+1, fTotal, fPending, test->getName());
|
|
}
|
|
virtual void onReportFailed(const SkString& desc) {
|
|
SkDebugf("\tFAILED: %s\n", desc.c_str());
|
|
}
|
|
|
|
virtual void onEnd(Test* test) {
|
|
if (!test->passed()) {
|
|
SkDebugf("---- %s FAILED\n", test->getName());
|
|
}
|
|
|
|
sk_atomic_dec(&fPending);
|
|
if (fNextIndex == fTotal) {
|
|
// Just waiting on straggler tests. Shame them by printing their name and runtime.
|
|
SkDebugf(" (%d) %5.1fs %s\n",
|
|
fPending, test->elapsedMs() / 1e3, test->getName());
|
|
}
|
|
}
|
|
|
|
private:
|
|
int32_t fNextIndex;
|
|
int32_t fPending;
|
|
int fTotal;
|
|
bool fAllowExtendedTest;
|
|
bool fAllowThreaded;
|
|
bool fVerbose;
|
|
};
|
|
|
|
DEFINE_string2(match, m, NULL, "[~][^]substring[$] [...] of test name to run.\n" \
|
|
"Multiple matches may be separated by spaces.\n" \
|
|
"~ causes a matching test to always be skipped\n" \
|
|
"^ requires the start of the test to match\n" \
|
|
"$ requires the end of the test to match\n" \
|
|
"^ and $ requires an exact match\n" \
|
|
"If a test does not match any list entry,\n" \
|
|
"it is skipped unless some list entry starts with ~");
|
|
DEFINE_string2(tmpDir, t, NULL, "tmp directory for tests to use.");
|
|
DEFINE_string2(resourcePath, i, NULL, "directory for test resources.");
|
|
DEFINE_bool2(extendedTest, x, false, "run extended tests for pathOps.");
|
|
DEFINE_bool2(single, z, false, "run tests on a single thread internally.");
|
|
DEFINE_bool2(verbose, v, false, "enable verbose output.");
|
|
DEFINE_int32(threads, SkThreadPool::kThreadPerCore,
|
|
"Run threadsafe tests on a threadpool with this many threads.");
|
|
|
|
SkString Test::GetTmpDir() {
|
|
const char* tmpDir = FLAGS_tmpDir.isEmpty() ? NULL : FLAGS_tmpDir[0];
|
|
return SkString(tmpDir);
|
|
}
|
|
|
|
SkString Test::GetResourcePath() {
|
|
const char* resourcePath = FLAGS_resourcePath.isEmpty() ? NULL : FLAGS_resourcePath[0];
|
|
return SkString(resourcePath);
|
|
}
|
|
|
|
// Deletes self when run.
|
|
class SkTestRunnable : public SkRunnable {
|
|
public:
|
|
// Takes ownership of test.
|
|
SkTestRunnable(Test* test, int32_t* failCount) : fTest(test), fFailCount(failCount) {}
|
|
|
|
virtual void run() {
|
|
fTest->run();
|
|
if(!fTest->passed()) {
|
|
sk_atomic_inc(fFailCount);
|
|
}
|
|
SkDELETE(this);
|
|
}
|
|
|
|
private:
|
|
SkAutoTDelete<Test> fTest;
|
|
int32_t* fFailCount;
|
|
};
|
|
|
|
/* Takes a list of the form [~][^]match[$]
|
|
~ causes a matching test to always be skipped
|
|
^ requires the start of the test to match
|
|
$ requires the end of the test to match
|
|
^ and $ requires an exact match
|
|
If a test does not match any list entry, it is skipped unless some list entry starts with ~
|
|
*/
|
|
static bool shouldSkip(const char* testName) {
|
|
int count = FLAGS_match.count();
|
|
size_t testLen = strlen(testName);
|
|
bool anyExclude = count == 0;
|
|
for (int index = 0; index < count; ++index) {
|
|
const char* matchName = FLAGS_match[index];
|
|
size_t matchLen = strlen(matchName);
|
|
bool matchExclude, matchStart, matchEnd;
|
|
if ((matchExclude = matchName[0] == '~')) {
|
|
anyExclude = true;
|
|
matchName++;
|
|
matchLen--;
|
|
}
|
|
if ((matchStart = matchName[0] == '^')) {
|
|
matchName++;
|
|
matchLen--;
|
|
}
|
|
if ((matchEnd = matchName[matchLen - 1] == '$')) {
|
|
matchLen--;
|
|
}
|
|
if (matchStart ? (!matchEnd || matchLen == testLen)
|
|
&& strncmp(testName, matchName, matchLen) == 0
|
|
: matchEnd ? matchLen <= testLen
|
|
&& strncmp(testName + testLen - matchLen, matchName, matchLen) == 0
|
|
: strstr(testName, matchName) != 0) {
|
|
return matchExclude;
|
|
}
|
|
}
|
|
return !anyExclude;
|
|
}
|
|
|
|
int tool_main(int argc, char** argv);
|
|
int tool_main(int argc, char** argv) {
|
|
SkCommandLineFlags::SetUsage("");
|
|
SkCommandLineFlags::Parse(argc, argv);
|
|
|
|
#if SK_ENABLE_INST_COUNT
|
|
gPrintInstCount = true;
|
|
#endif
|
|
|
|
SkGraphics::Init();
|
|
|
|
{
|
|
SkString header("Skia UnitTests:");
|
|
if (!FLAGS_match.isEmpty()) {
|
|
header.appendf(" --match");
|
|
for (int index = 0; index < FLAGS_match.count(); ++index) {
|
|
header.appendf(" %s", FLAGS_match[index]);
|
|
}
|
|
}
|
|
SkString tmpDir = Test::GetTmpDir();
|
|
if (!tmpDir.isEmpty()) {
|
|
header.appendf(" --tmpDir %s", tmpDir.c_str());
|
|
}
|
|
SkString resourcePath = Test::GetResourcePath();
|
|
if (!resourcePath.isEmpty()) {
|
|
header.appendf(" --resourcePath %s", resourcePath.c_str());
|
|
}
|
|
#ifdef SK_DEBUG
|
|
header.append(" SK_DEBUG");
|
|
#else
|
|
header.append(" SK_RELEASE");
|
|
#endif
|
|
#ifdef SK_SCALAR_IS_FIXED
|
|
header.append(" SK_SCALAR_IS_FIXED");
|
|
#else
|
|
header.append(" SK_SCALAR_IS_FLOAT");
|
|
#endif
|
|
SkDebugf("%s\n", header.c_str());
|
|
}
|
|
|
|
DebugfReporter reporter(FLAGS_extendedTest, !FLAGS_single, FLAGS_verbose);
|
|
Iter iter(&reporter);
|
|
|
|
// Count tests first.
|
|
int total = 0;
|
|
int toRun = 0;
|
|
Test* test;
|
|
while ((test = iter.next()) != NULL) {
|
|
SkAutoTDelete<Test> owned(test);
|
|
if(!shouldSkip(test->getName())) {
|
|
toRun++;
|
|
}
|
|
total++;
|
|
}
|
|
reporter.setTotal(toRun);
|
|
|
|
// Now run them.
|
|
iter.reset();
|
|
int32_t failCount = 0;
|
|
int skipCount = 0;
|
|
|
|
SkAutoTDelete<SkThreadPool> threadpool(SkNEW_ARGS(SkThreadPool, (FLAGS_threads)));
|
|
SkTArray<Test*> unsafeTests; // Always passes ownership to an SkTestRunnable
|
|
for (int i = 0; i < total; i++) {
|
|
SkAutoTDelete<Test> test(iter.next());
|
|
if (shouldSkip(test->getName())) {
|
|
++skipCount;
|
|
} else if (!test->isThreadsafe()) {
|
|
unsafeTests.push_back() = test.detach();
|
|
} else {
|
|
threadpool->add(SkNEW_ARGS(SkTestRunnable, (test.detach(), &failCount)));
|
|
}
|
|
}
|
|
|
|
// Run the tests that aren't threadsafe.
|
|
for (int i = 0; i < unsafeTests.count(); i++) {
|
|
SkNEW_ARGS(SkTestRunnable, (unsafeTests[i], &failCount))->run();
|
|
}
|
|
|
|
// Blocks until threaded tests finish.
|
|
threadpool.free();
|
|
|
|
SkDebugf("Finished %d tests, %d failures, %d skipped.\n",
|
|
toRun, failCount, skipCount);
|
|
const int testCount = reporter.countTests();
|
|
if (FLAGS_verbose && testCount > 0) {
|
|
SkDebugf("Ran %d Internal tests.\n", testCount);
|
|
}
|
|
#if SK_SUPPORT_GPU
|
|
|
|
#if GR_CACHE_STATS
|
|
GrContext *gr = GpuTest::GetContext();
|
|
|
|
gr->printCacheStats();
|
|
#endif
|
|
|
|
#endif
|
|
|
|
SkGraphics::Term();
|
|
GpuTest::DestroyContexts();
|
|
|
|
return (failCount == 0) ? 0 : 1;
|
|
}
|
|
|
|
#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
|
|
int main(int argc, char * const argv[]) {
|
|
return tool_main(argc, (char**) argv);
|
|
}
|
|
#endif
|