Let DM run unit tests.
  - refactor GYPs and a few flags
  - make GPU tests grab a thread-local GrContextFactory when needed as we do in DM for GMs
  - add a few more UI features to make DM more like tests

I believe this makes the program 'tests' obsolete.

It should be somewhat faster to run the two sets together than running the old binaries serially:
  - serial: tests 20s (3m18s CPU), dm 21s (3m01s CPU)
  - together: 27s (6m21s CPU)

Next up is to incorporate benches.  I'm only planning there on a single-pass sanity check, so that won't obsolete the program 'bench' just yet.

Tested: out/Debug/tests && out/Debug/dm && echo ok
BUG=skia:

Committed: http://code.google.com/p/skia/source/detail?r=13586

R=reed@google.com, bsalomon@google.com, mtklein@google.com, tfarina@chromium.org

Author: mtklein@chromium.org

Review URL: https://codereview.chromium.org/178273002

git-svn-id: http://skia.googlecode.com/svn/trunk@13592 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/dm/DM.cpp b/dm/DM.cpp
index 8a4a382..ee53e9d 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -7,26 +7,29 @@
 #include "SkForceLinking.h"
 #include "SkGraphics.h"
 #include "SkString.h"
+#include "Test.h"
 #include "gm.h"
 
+#include "DMCpuTask.h"
+#include "DMGpuTask.h"
 #include "DMReporter.h"
 #include "DMTask.h"
 #include "DMTaskRunner.h"
-#include "DMCpuTask.h"
-#include "DMGpuTask.h"
+#include "DMTestTask.h"
 #include "DMWriteTask.h"
 
 #include <string.h>
 
 using skiagm::GM;
 using skiagm::GMRegistry;
+using skiatest::Test;
+using skiatest::TestRegistry;
 
-DEFINE_int32(cpuThreads, -1, "Threads for CPU work. Default NUM_CPUS.");
-DEFINE_int32(gpuThreads, 1, "Threads for GPU work.");
+DEFINE_int32(threads, -1, "Threads for CPU work. Default NUM_CPUS.");
 DEFINE_string2(expectations, r, "",
                "If a directory, compare generated images against images under this path. "
                "If a file, compare generated images against JSON expectations at this path.");
-DEFINE_string(resources, "resources", "Path to resources directory.");
+DEFINE_string2(resources, i, "resources", "Path to resources directory.");
 DEFINE_string(match, "",  "[~][^]substring[$] [...] of GM name to run.\n"
                           "Multiple matches may be separated by spaces.\n"
                           "~ causes a matching GM to always be skipped\n"
@@ -37,6 +40,10 @@
                           "it is skipped unless some list entry starts with ~");
 DEFINE_string(config, "565 8888 gpu",
         "Options: 565 8888 gpu msaa4 msaa16 gpunull gpudebug angle mesa"); // TODO(mtklein): pdf
+DEFINE_bool(leaks, false, "Print leaked instance-counted objects at exit?");
+
+DEFINE_bool(gms, true, "Run GMs?");
+DEFINE_bool(tests, true, "Run tests?");
 
 __SK_FORCE_IMAGE_DECODER_LINKING;
 
@@ -48,11 +55,11 @@
     return s;
 }
 
-static void kick_off_tasks(const SkTDArray<GMRegistry::Factory>& gms,
-                           const SkTArray<SkString>& configs,
-                           const DM::Expectations& expectations,
-                           DM::Reporter* reporter,
-                           DM::TaskRunner* tasks) {
+static void kick_off_gms(const SkTDArray<GMRegistry::Factory>& gms,
+                         const SkTArray<SkString>& configs,
+                         const DM::Expectations& expectations,
+                         DM::Reporter* reporter,
+                         DM::TaskRunner* tasks) {
     const SkColorType _565 = kRGB_565_SkColorType;
     const SkColorType _8888 = kPMColor_SkColorType;
     const GrContextFactory::GLContextType native = GrContextFactory::kNative_GLContextType;
@@ -93,6 +100,14 @@
 #undef START
 }
 
+static void kick_off_tests(const SkTDArray<TestRegistry::Factory>& tests,
+                           DM::Reporter* reporter,
+                           DM::TaskRunner* tasks) {
+    for (int i = 0; i < tests.count(); i++) {
+        tasks->add(SkNEW_ARGS(DM::TestTask, (reporter, tasks, tests[i])));
+    }
+}
+
 static void report_failures(const DM::Reporter& reporter) {
     SkTArray<SkString> failures;
     reporter.getFailures(&failures);
@@ -109,40 +124,57 @@
 
 int tool_main(int argc, char** argv);
 int tool_main(int argc, char** argv) {
+#if SK_ENABLE_INST_COUNT
+    gPrintInstCount = FLAGS_leaks;
+#endif
     SkGraphics::Init();
-
     SkCommandLineFlags::Parse(argc, argv);
     GM::SetResourcePath(FLAGS_resources[0]);
+    Test::SetResourcePath(FLAGS_resources[0]);
+
     SkTArray<SkString> configs;
-    for (int i = 0; i < FLAGS_config.count(); i++) {
-        SkStrSplit(FLAGS_config[i], ", ", &configs);
-    }
-
     SkTDArray<GMRegistry::Factory> gms;
-    for (const GMRegistry* reg = GMRegistry::Head(); reg != NULL; reg = reg->next()) {
-        SkAutoTDelete<GM> gmForName(reg->factory()(NULL));
-        if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, gmForName->shortName())) {
-            *gms.append() = reg->factory();
-        }
-    }
-    SkDebugf("%d GMs x %d configs\n", gms.count(), configs.count());
-
     SkAutoTDelete<DM::Expectations> expectations(SkNEW(DM::NoExpectations));
-    if (FLAGS_expectations.count() > 0) {
-        const char* path = FLAGS_expectations[0];
-        if (sk_isdir(path)) {
-            expectations.reset(SkNEW_ARGS(DM::WriteTask::Expectations, (path)));
-        } else {
-            expectations.reset(SkNEW_ARGS(DM::JsonExpectations, (path)));
+
+    if (FLAGS_gms) {
+        for (int i = 0; i < FLAGS_config.count(); i++) {
+            SkStrSplit(FLAGS_config[i], ", ", &configs);
+        }
+
+        for (const GMRegistry* reg = GMRegistry::Head(); reg != NULL; reg = reg->next()) {
+            SkAutoTDelete<GM> gmForName(reg->factory()(NULL));
+            if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, gmForName->shortName())) {
+                *gms.append() = reg->factory();
+            }
+        }
+
+        if (FLAGS_expectations.count() > 0) {
+            const char* path = FLAGS_expectations[0];
+            if (sk_isdir(path)) {
+                expectations.reset(SkNEW_ARGS(DM::WriteTask::Expectations, (path)));
+            } else {
+                expectations.reset(SkNEW_ARGS(DM::JsonExpectations, (path)));
+            }
         }
     }
 
+    SkTDArray<TestRegistry::Factory> tests;
+    if (FLAGS_tests) {
+        for (const TestRegistry* reg = TestRegistry::Head(); reg != NULL; reg = reg->next()) {
+            SkAutoTDelete<Test> testForName(reg->factory()(NULL));
+            if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, testForName->getName())) {
+                *tests.append() = reg->factory();
+            }
+        }
+    }
+
+    SkDebugf("%d GMs x %d configs, %d tests\n", gms.count(), configs.count(), tests.count());
     DM::Reporter reporter;
-    DM::TaskRunner tasks(FLAGS_cpuThreads, FLAGS_gpuThreads);
-    kick_off_tasks(gms, configs, *expectations, &reporter, &tasks);
+    DM::TaskRunner tasks(FLAGS_threads);
+    kick_off_gms(gms, configs, *expectations, &reporter, &tasks);
+    kick_off_tests(tests, &reporter, &tasks);
     tasks.wait();
 
-    reporter.updateStatusLine();
     SkDebugf("\n");
     report_failures(reporter);