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