[graphite] Set up unit testing system

Bug: skia:12466
Change-Id: I401a185d818a964327d323b9ebcd0850ec0b1c9b
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/457318
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 865a7c6..47e8d8c 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -2001,6 +2001,9 @@
     if (skia_use_gl) {
       sources += gl_tests_sources
     }
+    if (skia_enable_graphite) {
+      sources += graphite_tests_sources
+    }
     if (!skia_enable_skgpu_v1) {
       sources -= skgpu_v1_tests_sources
     }
diff --git a/dm/DMGpuTestProcs.cpp b/dm/DMGpuTestProcs.cpp
index b6e880b..ffb5f0e 100644
--- a/dm/DMGpuTestProcs.cpp
+++ b/dm/DMGpuTestProcs.cpp
@@ -9,6 +9,9 @@
 
 #include "include/gpu/GrDirectContext.h"
 
+#include "experimental/graphite/include/Context.h"
+#include "tools/graphite/ContextFactory.h"
+
 using sk_gpu_test::GrContextFactory;
 using sk_gpu_test::GLTestContext;
 using sk_gpu_test::ContextInfo;
@@ -76,4 +79,24 @@
         }
     }
 }
+
+#ifdef SK_GRAPHITE_ENABLED
+
+namespace graphite {
+
+void RunWithGraphiteTestContexts(GraphiteTestFn* test, Reporter* reporter) {
+    ContextFactory factory;
+
+    auto [_, context] = factory.getContextInfo(ContextFactory::ContextType::kMetal);
+    if (!context) {
+        return;
+    }
+
+    (*test)(reporter, context.get());
+}
+
+} // namespace graphite
+
+#endif // SK_GRAPHITE_ENABLED
+
 } // namespace skiatest
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 495a6aa..8e44a52 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -2125,11 +2125,11 @@
                           SkBitmap* dst,
                           SkWStream* dstStream,
                           SkString* log) const {
-    using ContextType = sk_graphite_test::ContextFactory::ContextType;
+    using ContextType = skiatest::graphite::ContextFactory::ContextType;
 
     SkImageInfo ii = SkImageInfo::Make(src.size(), kRGBA_8888_SkColorType, kPremul_SkAlphaType);
 
-    sk_graphite_test::ContextFactory factory;
+    skiatest::graphite::ContextFactory factory;
     auto [_, context] = factory.getContextInfo(ContextType::kMetal);
 
     sk_sp<SkSurface> surface = MakeGraphite(std::move(context), ii);
diff --git a/experimental/graphite/include/Context.h b/experimental/graphite/include/Context.h
index 719e175..905eead 100644
--- a/experimental/graphite/include/Context.h
+++ b/experimental/graphite/include/Context.h
@@ -12,6 +12,7 @@
 
 namespace skgpu {
 
+class ContextPriv;
 class Gpu;
 namespace mtl { struct BackendContext; }
 
@@ -23,14 +24,19 @@
     static sk_sp<Context> MakeMetal(const skgpu::mtl::BackendContext&);
 #endif
 
+    // Provides access to functions that aren't part of the public API.
+    ContextPriv priv();
+    const ContextPriv priv() const;  // NOLINT(readability-const-return-type)
+
 protected:
     Context(sk_sp<Gpu>);
 
 private:
+    friend class ContextPriv;
+
     sk_sp<Gpu> fGpu;
 };
 
 } // namespace skgpu
 
 #endif // skgpu_Context_DEFINED
-
diff --git a/experimental/graphite/src/ContextPriv.cpp b/experimental/graphite/src/ContextPriv.cpp
new file mode 100644
index 0000000..6395a0f
--- /dev/null
+++ b/experimental/graphite/src/ContextPriv.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "experimental/graphite/src/ContextPriv.h"
+
+#include "experimental/graphite/src/Caps.h"
+#include "experimental/graphite/src/Gpu.h"
+
+namespace skgpu {
+
+const Caps* ContextPriv::caps() {
+    return fContext->fGpu->caps();
+}
+
+} // namespace skgpu
diff --git a/experimental/graphite/src/ContextPriv.h b/experimental/graphite/src/ContextPriv.h
new file mode 100644
index 0000000..f7746f2
--- /dev/null
+++ b/experimental/graphite/src/ContextPriv.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef skgpu_ContextPriv_DEFINED
+#define skgpu_ContextPriv_DEFINED
+
+#include "experimental/graphite/include/Context.h"
+
+namespace skgpu {
+
+class Caps;
+
+/** Class that adds methods to Context that are only intended for use internal to Skia.
+    This class is purely a privileged window into Context. It should never have additional
+    data members or virtual methods. */
+class ContextPriv {
+public:
+    const Caps* caps();
+
+private:
+    friend class Context; // to construct/copy this type.
+
+    explicit ContextPriv(Context* context) : fContext(context) {}
+
+    ContextPriv(const ContextPriv&) = delete;
+    ContextPriv& operator=(const ContextPriv&) = delete;
+
+    // No taking addresses of this type.
+    const ContextPriv* operator&() const;
+    ContextPriv *operator&();
+
+    Context* fContext;
+};
+
+inline ContextPriv Context::priv() { return ContextPriv(this); }
+
+// NOLINTNEXTLINE(readability-const-return-type)
+inline const ContextPriv Context::priv() const {
+    return ContextPriv(const_cast<Context *>(this));
+}
+
+} // namespace skgpu
+
+#endif // skgpu_ContextPriv_DEFINED
diff --git a/experimental/graphite/src/geom/Shape.h b/experimental/graphite/src/geom/Shape.h
index db4cfea..18e4565 100644
--- a/experimental/graphite/src/geom/Shape.h
+++ b/experimental/graphite/src/geom/Shape.h
@@ -24,6 +24,7 @@
  * point containment, or iteration.
  */
 class Shape {
+public:
     enum class Type : uint8_t {
         kEmpty, kRect, kRRect, kPath
     };
diff --git a/gn/graphite.gni b/gn/graphite.gni
index 96d01ed..a7dfcdc 100644
--- a/gn/graphite.gni
+++ b/gn/graphite.gni
@@ -22,6 +22,8 @@
   "$_src/CommandBuffer.cpp",
   "$_src/CommandBuffer.h",
   "$_src/Context.cpp",
+  "$_src/ContextPriv.cpp",
+  "$_src/ContextPriv.h",
   "$_src/Device.cpp",
   "$_src/Device.h",
   "$_src/DrawContext.cpp",
diff --git a/gn/tests.gni b/gn/tests.gni
index 9a4ef2e..75894c8 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -325,6 +325,11 @@
   "$_tests/MtlCopySurfaceTest.mm",
 ]
 
+graphite_tests_sources = [
+  "$_tests/graphite/CapsTest.cpp",
+  "$_tests/graphite/ShapeTest.cpp",
+]
+
 pathops_tests_sources = [
   "$_tests/PathOpsAngleIdeas.cpp",
   "$_tests/PathOpsAngleTest.cpp",
diff --git a/tests/Test.h b/tests/Test.h
index f02b751..7aedde9 100644
--- a/tests/Test.h
+++ b/tests/Test.h
@@ -13,6 +13,8 @@
 #include "tools/Registry.h"
 #include "tools/gpu/GrContextFactory.h"
 
+namespace skgpu { class Context; }
+
 namespace skiatest {
 
 SkString GetTmpDir();
@@ -124,6 +126,14 @@
 void RunWithGPUTestContexts(GrContextTestFn*, GrContextTypeFilterFn*, Reporter*,
                             const GrContextOptions&);
 
+namespace graphite {
+
+typedef void GraphiteTestFn(Reporter*, skgpu::Context*);
+
+void RunWithGraphiteTestContexts(GraphiteTestFn*, Reporter*);
+
+} // namespace graphite
+
 /** Timer provides wall-clock duration since its creation. */
 class Timer {
 public:
@@ -178,13 +188,33 @@
     skiatest::TestRegistry name##TestRegistry(skiatest::Test(#name, false, test_##name)); \
     void test_##name(skiatest::Reporter* reporter, const GrContextOptions&)
 
+#define DEF_GRAPHITE_TEST(name, reporter)                                             \
+    static void test_##name(skiatest::Reporter*);                                     \
+    static void test_graphite_##name(skiatest::Reporter* reporter,                    \
+                                     const GrContextOptions& /*unused*/) {            \
+        test_##name(reporter);                                                        \
+    }                                                                                 \
+    skiatest::TestRegistry name##TestRegistry(                                        \
+            skiatest::Test(#name, true, test_graphite_##name));                       \
+    void test_##name(skiatest::Reporter* reporter)
+
+#define DEF_GRAPHITE_TEST_FOR_CONTEXTS(name, reporter, graphite_context)              \
+    static void test_##name(skiatest::Reporter*, skgpu::Context*);                    \
+    static void test_graphite_contexts_##name(skiatest::Reporter* _reporter,           \
+                                              const GrContextOptions& /*unused*/) {   \
+        skiatest::graphite::RunWithGraphiteTestContexts(test_##name, _reporter);       \
+    }                                                                                 \
+    skiatest::TestRegistry name##TestRegistry(                                        \
+            skiatest::Test(#name, true, test_graphite_contexts_##name));              \
+    void test_##name(skiatest::Reporter* reporter, skgpu::Context* graphite_context)
+
 #define DEF_GPUTEST(name, reporter, options)                                             \
     static void test_##name(skiatest::Reporter*, const GrContextOptions&);               \
     skiatest::TestRegistry name##TestRegistry(skiatest::Test(#name, true, test_##name)); \
     void test_##name(skiatest::Reporter* reporter, const GrContextOptions& options)
 
 #define DEF_GPUTEST_FOR_CONTEXTS(name, context_filter, reporter, context_info, options_filter)  \
-    static void test_##name(skiatest::Reporter*, const sk_gpu_test::ContextInfo& context_info); \
+    static void test_##name(skiatest::Reporter*, const sk_gpu_test::ContextInfo&);              \
     static void test_gpu_contexts_##name(skiatest::Reporter* reporter,                          \
                                          const GrContextOptions& options) {                     \
         skiatest::RunWithGPUTestContexts(test_##name, context_filter, reporter, options);       \
diff --git a/tests/graphite/CapsTest.cpp b/tests/graphite/CapsTest.cpp
new file mode 100644
index 0000000..ff13f6f
--- /dev/null
+++ b/tests/graphite/CapsTest.cpp
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "tests/Test.h"
+
+#include "experimental/graphite/include/Context.h"
+#include "experimental/graphite/src/ContextPriv.h"
+
+DEF_GRAPHITE_TEST_FOR_CONTEXTS(CapsTest, reporter, context) {
+    // TODO: Jim takes this over
+    auto caps = context->priv().caps();
+    REPORTER_ASSERT(reporter, caps);
+}
diff --git a/tests/graphite/ShapeTest.cpp b/tests/graphite/ShapeTest.cpp
new file mode 100644
index 0000000..09b5a05
--- /dev/null
+++ b/tests/graphite/ShapeTest.cpp
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "tests/Test.h"
+
+#include "experimental/graphite/src/geom/Shape.h"
+
+DEF_GRAPHITE_TEST(ShapeTest, reporter) {
+    // TODO: Michael takes this over
+    skgpu::geom::Shape s;
+    REPORTER_ASSERT(reporter, s.type() == skgpu::geom::Shape::Type::kEmpty);
+}
diff --git a/tools/graphite/ContextFactory.cpp b/tools/graphite/ContextFactory.cpp
index 0bc2c54..a1e7fb3 100644
--- a/tools/graphite/ContextFactory.cpp
+++ b/tools/graphite/ContextFactory.cpp
@@ -13,7 +13,7 @@
 #include "tools/graphite/mtl/GraphiteMtlTestContext.h"
 #endif
 
-namespace sk_graphite_test {
+namespace skiatest::graphite {
 
  std::tuple<GraphiteTestContext*, sk_sp<skgpu::Context>> ContextFactory::getContextInfo(
         ContextType type) {
@@ -51,4 +51,4 @@
     return { fContexts.back().testContext(), fContexts.back().refContext() };
 }
 
-} // namespace sk_graphite_test
+} // namespace skiatest::graphite
diff --git a/tools/graphite/ContextFactory.h b/tools/graphite/ContextFactory.h
index 46f0121..4acdd36 100644
--- a/tools/graphite/ContextFactory.h
+++ b/tools/graphite/ContextFactory.h
@@ -5,8 +5,8 @@
  * found in the LICENSE file.
  */
 
-#ifndef sk_graphite_test_ContextFactory_DEFINED
-#define sk_graphite_test_ContextFactory_DEFINED
+#ifndef skiatest_graphite_ContextFactory_DEFINED
+#define skiatest_graphite_ContextFactory_DEFINED
 
 #include <vector>
 #include "experimental/graphite/include/GraphiteTypes.h"
@@ -17,7 +17,7 @@
     class Context;
 };
 
-namespace sk_graphite_test {
+namespace skiatest::graphite {
 
 class ContextFactory {
 public:
@@ -71,6 +71,6 @@
     std::vector<ContextInfo> fContexts;
 };
 
-} // namespace sk_graphite_test
+} // namespace skiatest::graphite
 
-#endif // sk_graphite_test_ContextFactory_DEFINED
+#endif // skiatest_graphite_ContextFactory_DEFINED
diff --git a/tools/graphite/GraphiteTestContext.cpp b/tools/graphite/GraphiteTestContext.cpp
index 44100c3..8b76482 100644
--- a/tools/graphite/GraphiteTestContext.cpp
+++ b/tools/graphite/GraphiteTestContext.cpp
@@ -7,10 +7,10 @@
 
 #include "tools/graphite/GraphiteTestContext.h"
 
-namespace sk_graphite_test {
+namespace skiatest::graphite {
 
 GraphiteTestContext::GraphiteTestContext() {}
 
 GraphiteTestContext::~GraphiteTestContext() {}
 
-}  // namespace sk_graphite_test
+}  // namespace skiatest::graphite
diff --git a/tools/graphite/GraphiteTestContext.h b/tools/graphite/GraphiteTestContext.h
index 0d97fb7..642945a 100644
--- a/tools/graphite/GraphiteTestContext.h
+++ b/tools/graphite/GraphiteTestContext.h
@@ -5,15 +5,15 @@
  * found in the LICENSE file.
  */
 
-#ifndef sk_graphite_test_GraphiteTestContext_DEFINED
-#define sk_graphite_test_GraphiteTestContext_DEFINED
+#ifndef skiatest_graphite_GraphiteTestContext_DEFINED
+#define skiatest_graphite_GraphiteTestContext_DEFINED
 
 #include "experimental/graphite/include/GraphiteTypes.h"
 #include "include/core/SkRefCnt.h"
 
 namespace skgpu { class Context; }
 
-namespace sk_graphite_test {
+namespace skiatest::graphite {
 
 /**
  * An offscreen 3D context. This class is intended for Skia's internal testing needs and not
@@ -35,6 +35,6 @@
 };
 
 
-}  // namespace sk_graphite_test
+}  // namespace skiatest::graphite
 
-#endif // sk_graphite_test_GraphiteTestContext_DEFINED
+#endif // skiatest_graphite_GraphiteTestContext_DEFINED
diff --git a/tools/graphite/mtl/GraphiteMtlTestContext.h b/tools/graphite/mtl/GraphiteMtlTestContext.h
index ea06ad9..5acd2dc 100644
--- a/tools/graphite/mtl/GraphiteMtlTestContext.h
+++ b/tools/graphite/mtl/GraphiteMtlTestContext.h
@@ -5,8 +5,8 @@
  * found in the LICENSE file.
  */
 
-#ifndef skgpu_MtlTestContext_DEFINED
-#define skgpu_MtlTestContext_DEFINED
+#ifndef skiatest_graphite_MtlTestContext_DEFINED
+#define skiatest_graphite_MtlTestContext_DEFINED
 
 #include "tools/graphite/GraphiteTestContext.h"
 
@@ -14,7 +14,7 @@
 
 #include "experimental/graphite/include/mtl/MtlBackendContext.h"
 
-namespace sk_graphite_test::mtl {
+namespace skiatest::graphite::mtl {
 
 class TestContext : public GraphiteTestContext {
 public:
@@ -36,8 +36,8 @@
     skgpu::mtl::BackendContext fMtl;
 };
 
-}  // namespace sk_graphite_test::mtl
+}  // namespace skiatest::graphite::mtl
 
 #endif // SK_METAL
 
-#endif // skgpu_MtlTestContext_DEFINED
+#endif // skiatest_graphite_MtlTestContext_DEFINED
diff --git a/tools/graphite/mtl/MtlTestContext.mm b/tools/graphite/mtl/MtlTestContext.mm
index ea8f263..7085f28 100644
--- a/tools/graphite/mtl/MtlTestContext.mm
+++ b/tools/graphite/mtl/MtlTestContext.mm
@@ -14,7 +14,7 @@
 
 #import <Metal/Metal.h>
 
-namespace sk_graphite_test::mtl {
+namespace skiatest::graphite::mtl {
 
 std::unique_ptr<GraphiteTestContext> TestContext::Make() {
     sk_cfp<id<MTLDevice>> device;
@@ -51,6 +51,6 @@
     return skgpu::Context::MakeMetal(fMtl);
 }
 
-}  // namespace sk_graphite_test::mtl
+}  // namespace skiatest::graphite::mtl
 
 #endif // SK_METAL