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 "GrContext.h" 10 #include "GrContextFactory.h" 11 #include "OverwriteLine.h" 12 #include "PathOpsDebug.h" 13 #include "Resources.h" 14 #include "SkCommonFlags.h" 15 #include "SkGraphics.h" 16 #include "SkOSFile.h" 17 #include "SkPathOpsDebug.h" 18 #include "SkTArray.h" 19 #include "SkTaskGroup.h" 20 #include "SkTemplates.h" 21 #include "SkTime.h" 22 #include "Test.h" 23 #include <atomic> 24 25 using namespace skiatest; 26 using namespace sk_gpu_test; 27 28 DEFINE_bool2(dumpOp, d, false, "dump the pathOps to a file to recover mid-crash."); 29 DEFINE_bool2(extendedTest, x, false, "run extended tests for pathOps."); 30 DEFINE_bool2(runFail, f, false, "check for success on tests known to fail."); 31 DEFINE_bool2(verifyOp, y, false, "compare the pathOps result against a region."); 32 DEFINE_string2(json, J, "", "write json version of tests."); 33 34 #if DEBUG_COIN 35 DEFINE_bool2(coinTest, c, false, "detect unused coincidence algorithms."); 36 #endif 37 38 // need to explicitly declare this, or we get some weird infinite loop llist 39 template TestRegistry* TestRegistry::gHead; 40 void (*gVerboseFinalize)() = nullptr; 41 42 // The threads report back to this object when they are done. 43 class Status { 44 public: 45 explicit Status(int total) 46 : fDone(0), fTestCount(0), fFailCount(0), fTotal(total) {} 47 // Threadsafe. 48 void endTest(const char* testName, 49 bool success, 50 SkMSec elapsed, 51 int testCount) { 52 const int done = ++fDone; 53 fTestCount += testCount; 54 if (!success) { 55 SkDebugf("\n---- %s FAILED", testName); 56 } 57 58 SkString prefix(kSkOverwriteLine); 59 SkString time; 60 if (FLAGS_verbose) { 61 prefix.printf("\n"); 62 time.printf("%5dms ", elapsed); 63 } 64 SkDebugf("%s[%3d/%3d] %s%s", prefix.c_str(), done, fTotal, time.c_str(), 65 testName); 66 } 67 68 void reportFailure() { fFailCount++; } 69 70 int32_t testCount() { return fTestCount; } 71 int32_t failCount() { return fFailCount; } 72 73 private: 74 std::atomic<int32_t> fDone; 75 std::atomic<int32_t> fTestCount; 76 std::atomic<int32_t> fFailCount; 77 const int fTotal; 78 }; 79 80 class SkTestRunnable { 81 public: 82 SkTestRunnable(const Test& test, Status* status) : fTest(test), fStatus(status) {} 83 84 void operator()() { 85 struct TestReporter : public skiatest::Reporter { 86 public: 87 TestReporter() : fStats(nullptr), fError(false), fTestCount(0) {} 88 void bumpTestCount() override { ++fTestCount; } 89 bool allowExtendedTest() const override { return FLAGS_extendedTest; } 90 bool verbose() const override { return FLAGS_veryVerbose; } 91 void reportFailed(const skiatest::Failure& failure) override { 92 SkDebugf("\nFAILED: %s", failure.toString().c_str()); 93 fError = true; 94 } 95 void* stats() const override { return fStats; } 96 void* fStats; 97 bool fError; 98 int fTestCount; 99 } reporter; 100 101 const Timer timer; 102 fTest.proc(&reporter, GrContextOptions()); 103 SkMSec elapsed = timer.elapsedMsInt(); 104 if (reporter.fError) { 105 fStatus->reportFailure(); 106 } 107 fStatus->endTest(fTest.name, !reporter.fError, elapsed, reporter.fTestCount); 108 } 109 110 private: 111 Test fTest; 112 Status* fStatus; 113 }; 114 115 static bool should_run(const char* testName, bool isGPUTest) { 116 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, testName)) { 117 return false; 118 } 119 if (!FLAGS_cpu && !isGPUTest) { 120 return false; 121 } 122 if (!FLAGS_gpu && isGPUTest) { 123 return false; 124 } 125 return true; 126 } 127 128 int main(int argc, char** argv) { 129 SkCommandLineFlags::Parse(argc, argv); 130 #if DEBUG_DUMP_VERIFY 131 SkPathOpsDebug::gDumpOp = FLAGS_dumpOp; 132 SkPathOpsDebug::gVerifyOp = FLAGS_verifyOp; 133 #endif 134 SkPathOpsDebug::gRunFail = FLAGS_runFail; 135 SkPathOpsDebug::gVeryVerbose = FLAGS_veryVerbose; 136 PathOpsDebug::gOutFirst = true; 137 PathOpsDebug::gCheckForDuplicateNames = false; 138 PathOpsDebug::gOutputSVG = false; 139 if ((PathOpsDebug::gJson = !FLAGS_json.isEmpty())) { 140 PathOpsDebug::gOut = fopen(FLAGS_json[0], "wb"); 141 fprintf(PathOpsDebug::gOut, "{\n"); 142 FLAGS_threads = 0; 143 PathOpsDebug::gMarkJsonFlaky = false; 144 } 145 SetupCrashHandler(); 146 147 SkAutoGraphics ag; 148 149 { 150 SkString header("Skia UnitTests:"); 151 if (!FLAGS_match.isEmpty()) { 152 header.appendf(" --match"); 153 for (int index = 0; index < FLAGS_match.count(); ++index) { 154 header.appendf(" %s", FLAGS_match[index]); 155 } 156 } 157 SkString tmpDir = skiatest::GetTmpDir(); 158 if (!tmpDir.isEmpty()) { 159 header.appendf(" --tmpDir %s", tmpDir.c_str()); 160 } 161 SkString resourcePath = GetResourcePath(); 162 if (!resourcePath.isEmpty()) { 163 header.appendf(" --resourcePath %s", resourcePath.c_str()); 164 } 165 #if DEBUG_COIN 166 if (FLAGS_coinTest) { 167 header.appendf(" -c"); 168 } 169 #endif 170 if (FLAGS_dumpOp) { 171 header.appendf(" -d"); 172 } 173 #ifdef SK_DEBUG 174 if (FLAGS_runFail) { 175 header.appendf(" -f"); 176 } 177 #endif 178 if (FLAGS_verbose) { 179 header.appendf(" -v"); 180 } 181 if (FLAGS_veryVerbose) { 182 header.appendf(" -V"); 183 } 184 if (FLAGS_extendedTest) { 185 header.appendf(" -x"); 186 } 187 if (FLAGS_verifyOp) { 188 header.appendf(" -y"); 189 } 190 #ifdef SK_DEBUG 191 header.append(" SK_DEBUG"); 192 #else 193 header.append(" SK_RELEASE"); 194 #endif 195 if (FLAGS_veryVerbose) { 196 header.appendf("\n"); 197 } 198 SkDebugf("%s", header.c_str()); 199 } 200 201 202 // Count tests first. 203 int total = 0; 204 int toRun = 0; 205 206 for (const Test& test : TestRegistry::Range()) { 207 if (should_run(test.name, test.needsGpu)) { 208 toRun++; 209 } 210 total++; 211 } 212 213 // Now run them. 214 int skipCount = 0; 215 216 SkTaskGroup::Enabler enabled(FLAGS_threads); 217 SkTaskGroup cpuTests; 218 SkTArray<const Test*> gpuTests; 219 220 Status status(toRun); 221 222 for (const Test& test : TestRegistry::Range()) { 223 if (!should_run(test.name, test.needsGpu)) { 224 ++skipCount; 225 } else if (test.needsGpu) { 226 gpuTests.push_back(&test); 227 } else { 228 cpuTests.add(SkTestRunnable(test, &status)); 229 } 230 } 231 232 // Run GPU tests on this thread. 233 for (int i = 0; i < gpuTests.count(); i++) { 234 SkTestRunnable(*gpuTests[i], &status)(); 235 } 236 237 // Block until threaded tests finish. 238 cpuTests.wait(); 239 240 if (FLAGS_verbose) { 241 SkDebugf( 242 "\nFinished %d tests, %d failures, %d skipped. " 243 "(%d internal tests)", 244 toRun, status.failCount(), skipCount, status.testCount()); 245 if (gVerboseFinalize) { 246 (*gVerboseFinalize)(); 247 } 248 } 249 250 SkDebugf("\n"); 251 #if DEBUG_COIN 252 if (FLAGS_coinTest) { 253 SkPathOpsDebug::DumpCoinDict(); 254 } 255 #endif 256 if (PathOpsDebug::gJson) { 257 fprintf(PathOpsDebug::gOut, "\n}\n"); 258 fclose(PathOpsDebug::gOut); 259 } 260 return (status.failCount() == 0) ? 0 : 1; 261 } 262