blob: b661763fb94764db740752297f683be2de317988 [file] [log] [blame]
* 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 <atomic>
#include "include/core/SkGraphics.h"
#include "include/core/SkTime.h"
#include "include/gpu/GrContext.h"
#include "include/private/SkTArray.h"
#include "include/private/SkTemplates.h"
#include "src/core/SkOSFile.h"
#include "src/core/SkTaskGroup.h"
#include "src/pathops/SkPathOpsDebug.h"
#include "tests/PathOpsDebug.h"
#include "tests/Test.h"
#include "tools/CrashHandler.h"
#include "tools/OverwriteLine.h"
#include "tools/Resources.h"
#include "tools/flags/CommandLineFlags.h"
#include "tools/gpu/GrContextFactory.h"
using namespace skiatest;
using namespace sk_gpu_test;
static DEFINE_bool2(dumpOp, d, false, "dump the pathOps to a file to recover mid-crash.");
static DEFINE_bool2(extendedTest, x, false, "run extended tests for pathOps.");
static DEFINE_bool2(runFail, f, false, "check for success on tests known to fail.");
static DEFINE_bool2(verifyOp, y, false, "compare the pathOps result against a region.");
static DEFINE_string2(json, J, "", "write json version of tests.");
static DEFINE_bool2(verbose, v, false, "enable verbose output from the test driver.");
static DEFINE_bool2(veryVerbose, V, false, "tell individual tests to be verbose.");
static DEFINE_bool(cpu, true, "master switch for running CPU-bound work.");
static DEFINE_bool(gpu, true, "master switch for running GPU-bound work.");
static DEFINE_string2(match, m, nullptr,
"[~][^]substring[$] [...] of name to run.\n"
"Multiple matches may be separated by spaces.\n"
"~ causes a matching name to always be skipped\n"
"^ requires the start of the name to match\n"
"$ requires the end of the name to match\n"
"^ and $ requires an exact match\n"
"If a name does not match any list entry,\n"
"it is skipped unless some list entry starts with ~");
static DEFINE_int_2(threads, j, -1,
"Run threadsafe tests on a threadpool with this many extra threads, "
"defaulting to one extra thread per core.");
static DEFINE_bool2(coinTest, c, false, "detect unused coincidence algorithms.");
// need to explicitly declare this, or we get some weird infinite loop llist
template TestRegistry* TestRegistry::gHead;
void (*gVerboseFinalize)() = nullptr;
// The threads report back to this object when they are done.
class Status {
explicit Status(int total)
: fDone(0), fTestCount(0), fFailCount(0), fTotal(total) {}
// Threadsafe.
void endTest(const char* testName,
bool success,
SkMSec elapsed,
int testCount) {
const int done = ++fDone;
fTestCount += testCount;
if (!success) {
SkDebugf("\n---- %s FAILED", testName);
SkString prefix(kSkOverwriteLine);
SkString time;
if (FLAGS_verbose) {
time.printf("%5dms ", elapsed);
SkDebugf("%s[%3d/%3d] %s%s", prefix.c_str(), done, fTotal, time.c_str(),
void reportFailure() { fFailCount++; }
int32_t testCount() { return fTestCount; }
int32_t failCount() { return fFailCount; }
std::atomic<int32_t> fDone;
std::atomic<int32_t> fTestCount;
std::atomic<int32_t> fFailCount;
const int fTotal;
class SkTestRunnable {
SkTestRunnable(const Test& test, Status* status) : fTest(test), fStatus(status) {}
void operator()() {
struct TestReporter : public skiatest::Reporter {
TestReporter() : fStats(nullptr), fError(false), fTestCount(0) {}
void bumpTestCount() override { ++fTestCount; }
bool allowExtendedTest() const override { return FLAGS_extendedTest; }
bool verbose() const override { return FLAGS_veryVerbose; }
void reportFailed(const skiatest::Failure& failure) override {
SkDebugf("\nFAILED: %s", failure.toString().c_str());
fError = true;
void* stats() const override { return fStats; }
void* fStats;
bool fError;
int fTestCount;
} reporter;
const Timer timer;
fTest.proc(&reporter, GrContextOptions());
SkMSec elapsed = timer.elapsedMsInt();
if (reporter.fError) {
fStatus->endTest(, !reporter.fError, elapsed, reporter.fTestCount);
Test fTest;
Status* fStatus;
static bool should_run(const char* testName, bool isGPUTest) {
if (CommandLineFlags::ShouldSkip(FLAGS_match, testName)) {
return false;
if (!FLAGS_cpu && !isGPUTest) {
return false;
if (!FLAGS_gpu && isGPUTest) {
return false;
return true;
int main(int argc, char** argv) {
CommandLineFlags::Parse(argc, argv);
SkPathOpsDebug::gDumpOp = FLAGS_dumpOp;
SkPathOpsDebug::gVerifyOp = FLAGS_verifyOp;
SkPathOpsDebug::gRunFail = FLAGS_runFail;
SkPathOpsDebug::gVeryVerbose = FLAGS_veryVerbose;
PathOpsDebug::gOutFirst = true;
PathOpsDebug::gCheckForDuplicateNames = false;
PathOpsDebug::gOutputSVG = false;
if ((PathOpsDebug::gJson = !FLAGS_json.isEmpty())) {
PathOpsDebug::gOut = fopen(FLAGS_json[0], "wb");
fprintf(PathOpsDebug::gOut, "{\n");
FLAGS_threads = 0;
PathOpsDebug::gMarkJsonFlaky = false;
SkAutoGraphics ag;
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 = skiatest::GetTmpDir();
if (!tmpDir.isEmpty()) {
header.appendf(" --tmpDir %s", tmpDir.c_str());
SkString resourcePath = GetResourcePath();
if (!resourcePath.isEmpty()) {
header.appendf(" --resourcePath %s", resourcePath.c_str());
if (FLAGS_coinTest) {
header.appendf(" -c");
if (FLAGS_dumpOp) {
header.appendf(" -d");
#ifdef SK_DEBUG
if (FLAGS_runFail) {
header.appendf(" -f");
if (FLAGS_verbose) {
header.appendf(" -v");
if (FLAGS_veryVerbose) {
header.appendf(" -V");
if (FLAGS_extendedTest) {
header.appendf(" -x");
if (FLAGS_verifyOp) {
header.appendf(" -y");
#ifdef SK_DEBUG
header.append(" SK_DEBUG");
header.append(" SK_RELEASE");
if (FLAGS_veryVerbose) {
SkDebugf("%s", header.c_str());
// Count tests first.
int total = 0;
int toRun = 0;
for (const Test& test : TestRegistry::Range()) {
if (should_run(, test.needsGpu)) {
// Now run them.
int skipCount = 0;
SkTaskGroup::Enabler enabled(FLAGS_threads);
SkTaskGroup cpuTests;
SkTArray<const Test*> gpuTests;
Status status(toRun);
for (const Test& test : TestRegistry::Range()) {
if (!should_run(, test.needsGpu)) {
} else if (test.needsGpu) {
} else {
cpuTests.add(SkTestRunnable(test, &status));
// Run GPU tests on this thread.
for (int i = 0; i < gpuTests.count(); i++) {
SkTestRunnable(*gpuTests[i], &status)();
// Block until threaded tests finish.
if (FLAGS_verbose) {
"\nFinished %d tests, %d failures, %d skipped. "
"(%d internal tests)",
toRun, status.failCount(), skipCount, status.testCount());
if (gVerboseFinalize) {
if (FLAGS_coinTest) {
if (PathOpsDebug::gJson) {
fprintf(PathOpsDebug::gOut, "\n}\n");
return (status.failCount() == 0) ? 0 : 1;