Adding more unit testing for SkCanvas and derived classes.

BUG=http://code.google.com/p/skia/issues/detail?id=481
REVIEW=http://codereview.appspot.com/5674077/
TEST=unit test CanvasTest



git-svn-id: http://skia.googlecode.com/svn/trunk@3228 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkStream.h b/include/core/SkStream.h
index 90d2357..67512d7 100644
--- a/include/core/SkStream.h
+++ b/include/core/SkStream.h
@@ -272,7 +272,8 @@
 public:
     SkMemoryWStream(void* buffer, size_t size);
     virtual bool write(const void* buffer, size_t size) SK_OVERRIDE;
-    
+    size_t bytesWritten() const { return fBytesWritten; }
+
 private:
     char*   fBuffer;
     size_t  fMaxLength;
diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h
index e8fa370..dfddffe 100644
--- a/src/core/SkPictureRecord.h
+++ b/src/core/SkPictureRecord.h
@@ -193,6 +193,7 @@
     void recordOffsetForRestore(SkRegion::Op op);
 
     friend class SkPicturePlayback;
+    friend class SkPictureTester; // for unit testing
 
     typedef SkCanvas INHERITED;
 };
diff --git a/tests/CanvasTest.cpp b/tests/CanvasTest.cpp
index da1fafd..0199606 100644
--- a/tests/CanvasTest.cpp
+++ b/tests/CanvasTest.cpp
@@ -1,66 +1,766 @@
 
 /*
- * Copyright 2011 Google Inc.
+ * Copyright 2012 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
-#include "Test.h"
+
+/*  Description:
+ *      This test defines a series of elementatry test steps that perform
+ *      a single or a small group of canvas API calls. Each test step is
+ *      used in several test cases that verify that different types of SkCanvas
+ *      flavors and derivatives pass it and yield consistent behavior. The
+ *      test cases analyse results that are queryable through the API. They do
+ *      not look at rendering results.
+ *
+ *  Adding test stepss:
+ *      The general pattern for creating a new test step is to write a test
+ *      function of the form:
+ *
+ *          static void MyTestStepFunction(SkCanvas* canvas, 
+ *                                         skiatest::Reporter* reporter,
+ *                                         CanvasTestStep* testStep)
+ *          {
+ *              canvas->someCanvasAPImethod();
+ *              (...)
+ *              REPORTER_ASSERT_MESSAGE(reporter, (...), \
+ *                  testStep->assertMessage());
+ *          }
+ *
+ *      The definition of the test step function should be followed by an
+ *      invocation of the TEST_STEP macro, which generates a class and
+ *      instance for the test step:
+ *
+ *          TEST_STEP(MyTestStep, MyTestStepFunction)
+ *
+ *      There are also short hand macros for defining simple test steps
+ *      in a single line of code.  A simple test step is a one that is made
+ *      of a single canvas API call.
+ *
+ *          SIMPLE_TEST_STEP(MytestStep, someCanvasAPIMethod());
+ *
+ *      There is another macro called SIMPLE_TEST_STEP_WITH_ASSERT that
+ *      works the same way as SIMPLE_TEST_STEP, and additionally verifies
+ *      that the invoked method returns a non-zero value.
+ */
 #include "SkBitmap.h"
 #include "SkCanvas.h"
+#include "SkDeferredCanvas.h"
+#include "SkDevice.h"
+#include "SkMatrix.h"
+#include "SkNWayCanvas.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkPicture.h"
+#include "SkPictureRecord.h"
+#include "SkProxyCanvas.h"
+#include "SkRect.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkStream.h"
+#include "SkTDArray.h"
+#include "Test.h"
 
-static void test_isDrawingToLayer(skiatest::Reporter* reporter) {
-    SkBitmap bm;
-    bm.setConfig(SkBitmap::kARGB_8888_Config, 256, 256);
-    bm.allocPixels();
-    
-    SkCanvas canvas(bm);
+static const int kWidth = 2;
+static const int kHeight = 2;
+// Maximum stream length for picture serialization
+static const size_t kMaxPictureBufferSize = 1024; 
 
-    REPORTER_ASSERT(reporter, !canvas.isDrawingToLayer());
-    canvas.save();
-    REPORTER_ASSERT(reporter, !canvas.isDrawingToLayer());
+// Format strings that describe the test context.  The %s token is where
+// the name of the test step is inserted.  The context is required for
+// disambiguating the error in the case of failures that are reported in
+// functions that are called multiple times in different contexts (test
+// cases and test steps).
+static const char* const kDefaultAssertMessageFormat = "%s";
+static const char* const kCanvasDrawAssertMessageFormat = 
+    "Drawing test step %s with SkCanvas";
+static const char* const kPictureDrawAssertMessageFormat = 
+    "Drawing test step %s with SkPicture";
+static const char* const kPictureSecondDrawAssertMessageFormat = 
+    "Duplicate draw of test step %s with SkPicture";
+static const char* const kPictureReDrawAssertMessageFormat = 
+    "Playing back test step %s from an SkPicture to another SkPicture";
+static const char* const kDeferredDrawAssertMessageFormat = 
+    "Drawing test step %s with SkDeferredCanvas";
+static const char* const kProxyDrawAssertMessageFormat = 
+    "Drawing test step %s with SkProxyCanvas";
+static const char* const kNWayDrawAssertMessageFormat = 
+    "Drawing test step %s with SkNWayCanvas";
+static const char* const kRoundTripAssertMessageFormat = 
+    "test step %s, SkPicture consistency after round trip";
+static const char* const kPictureRecoringAssertMessageFormat = 
+    "test step %s, SkPicture state consistency after recording";
+static const char* const kPicturePlaybackAssertMessageFormat = 
+    "test step %s, SkPicture state consistency in playback canvas";
+static const char* const kDeferredPreFlushAssertMessageFormat = 
+    "test step %s, SkDeferredCanvas state consistency before flush";
+static const char* const kDeferredPostFlushAssertMessageFormat = 
+    "test step %s, SkDeferredCanvas state consistency after flush";
+static const char* const kPictureResourceReuseMessageFormat =
+    "test step %s, SkPicture duplicate flattened object test";
+static const char* const kProxyStateAssertMessageFormat =
+    "test step %s, SkProxyCanvas state consistency";
+static const char* const kProxyIndirectStateAssertMessageFormat =
+    "test step %s, SkProxyCanvas indirect canvas state consistency";
+static const char* const kNWayStateAssertMessageFormat =
+    "test step %s, SkNWayCanvas state consistency";
+static const char* const kNWayIndirect1StateAssertMessageFormat =
+    "test step %s, SkNWayCanvas indirect canvas 1 state consistency";
+static const char* const kNWayIndirect2StateAssertMessageFormat =
+    "test step %s, SkNWayCanvas indirect canvas 2 state consistency";
+
+static void createBitmap(SkBitmap* bm, SkBitmap::Config config, SkColor color) {
+    bm->setConfig(config, kWidth, kHeight);
+    bm->allocPixels();
+    bm->eraseColor(color);
+}
+
+class CanvasTestStep;
+static SkTDArray<CanvasTestStep*>& testStepArray() {
+    static SkTDArray<CanvasTestStep*> theTests;
+    return theTests;
+}
+
+class CanvasTestStep {
+public:
+    CanvasTestStep() {
+        *testStepArray().append() = this;
+        fAssertMessageFormat = kDefaultAssertMessageFormat;
+    }
+
+    virtual void draw(SkCanvas*, skiatest::Reporter*) = 0;
+    virtual const char* name() const = 0;
+
+    const char* assertMessage() {
+        fAssertMessage.printf(fAssertMessageFormat, name());
+        return fAssertMessage.c_str();
+    }
+
+    void setAssertMessageFormat(const char* format) {
+        fAssertMessageFormat = format;
+    }
+
+private:
+    SkString fAssertMessage;
+    const char* fAssertMessageFormat;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Constants used by test steps
+
+const SkRect kTestRect = 
+    SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
+                     SkIntToScalar(2), SkIntToScalar(1));
+static SkMatrix testMatrix() {
+    SkMatrix matrix;
+    matrix.reset();
+    matrix.setScale(SkIntToScalar(2), SkIntToScalar(3));
+    return matrix;
+}
+const SkMatrix kTestMatrix = testMatrix();
+static SkPath testPath() {
+    SkPath path;
+    path.addRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
+                                  SkIntToScalar(2), SkIntToScalar(1)));
+    return path;
+}
+const SkPath kTestPath = testPath();
+static SkRegion testRegion() {
+    SkRegion region;
+    SkIRect rect = SkIRect::MakeXYWH(0, 0, 2, 1);
+    region.setRect(rect);
+    return region;
+}
+const SkIRect kTestIRect = SkIRect::MakeXYWH(0, 0, 2, 1);
+const SkRegion kTestRegion = testRegion();
+const SkColor kTestColor = 0x01020304;
+const SkPaint kTestPaint;
+const SkPoint kTestPoints[3] = {
+    {SkIntToScalar(0), SkIntToScalar(0)},
+    {SkIntToScalar(2), SkIntToScalar(1)},
+    {SkIntToScalar(0), SkIntToScalar(2)}
+};
+const size_t kTestPointCount = 3;
+static SkBitmap testBitmap() {
+    SkBitmap bitmap;
+    createBitmap(&bitmap, SkBitmap::kARGB_8888_Config, 0x05060708);
+    return bitmap;
+}
+SkBitmap kTestBitmap; // cannot be created during static init
+SkString kTestText("Hello World");
+SkPoint kTestPoint = SkPoint::Make(SkIntToScalar(0), SkIntToScalar(1));
+
+///////////////////////////////////////////////////////////////////////////////
+// Macros for defining test steps
+
+#define TEST_STEP(NAME, FUNCTION)                                       \
+class NAME##_TestStep : public CanvasTestStep{                          \
+public:                                                                 \
+    virtual void draw(SkCanvas* canvas, skiatest::Reporter* reporter) { \
+        FUNCTION (canvas, reporter, this);                              \
+    }                                                                   \
+    virtual const char* name() const {return #NAME ;}                   \
+};                                                                      \
+static NAME##_TestStep NAME##_TestStepInstance;
+
+#define SIMPLE_TEST_STEP(NAME, CALL)                              \
+static void NAME##TestStep(SkCanvas* canvas, skiatest::Reporter*, \
+    CanvasTestStep*) {                                            \
+    canvas-> CALL ;                                               \
+}                                                                 \
+TEST_STEP(NAME, NAME##TestStep )
+
+#define SIMPLE_TEST_STEP_WITH_ASSERT(NAME, CALL)                           \
+static void NAME##TestStep(SkCanvas* canvas, skiatest::Reporter* reporter, \
+    CanvasTestStep* testStep) {                                            \
+    REPORTER_ASSERT_MESSAGE(reporter, canvas-> CALL ,                      \
+        testStep->assertMessage());                                        \
+}                                                                          \
+TEST_STEP(NAME, NAME##TestStep )
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Basic test steps for most virtual methods in SkCanvas that draw or affect 
+// the state of the canvas.
+
+// The following test steps are commented-out because they currently fail
+// Issue: http://code.google.com/p/skia/issues/detail?id=506
+//SIMPLE_TEST_STEP(SaveMatrix, save(SkCanvas::kMatrix_SaveFlag));
+//SIMPLE_TEST_STEP(SaveClip, save(SkCanvas::kClip_SaveFlag));
+//SIMPLE_TEST_STEP(SaveMatrixClip, save(SkCanvas::kMatrixClip_SaveFlag));
+//SIMPLE_TEST_STEP(SaveLayer, saveLayer(NULL, NULL));
+//SIMPLE_TEST_STEP(BoundedSaveLayer, saveLayer(&kTestRect, NULL));
+//SIMPLE_TEST_STEP(PaintSaveLayer, saveLayer(NULL, &kTestPaint));
+//SIMPLE_TEST_STEP_WITH_ASSERT(Translate,
+//    translate(SkIntToScalar(1), SkIntToScalar(2)));
+//SIMPLE_TEST_STEP_WITH_ASSERT(Scale,
+//    scale(SkIntToScalar(1), SkIntToScalar(2)));
+//SIMPLE_TEST_STEP_WITH_ASSERT(Rotate, rotate(SkIntToScalar(1)));
+//SIMPLE_TEST_STEP_WITH_ASSERT(Skew,
+//    skew(SkIntToScalar(1), SkIntToScalar(2)));
+//SIMPLE_TEST_STEP_WITH_ASSERT(Concat, concat(kTestMatrix));
+//SIMPLE_TEST_STEP(SetMatrix, setMatrix(kTestMatrix));
+//SIMPLE_TEST_STEP_WITH_ASSERT(ClipRect, clipRect(kTestRect));
+//SIMPLE_TEST_STEP_WITH_ASSERT(ClipPath, clipPath(kTestPath));
+//SIMPLE_TEST_STEP_WITH_ASSERT(ClipRegion,
+//    clipRegion(kTestRegion, SkRegion::kReplace_Op));
+SIMPLE_TEST_STEP(Clear, clear(kTestColor));
+SIMPLE_TEST_STEP(DrawPaint, drawPaint(kTestPaint));
+SIMPLE_TEST_STEP(DrawPointsPoints, drawPoints(SkCanvas::kPoints_PointMode,
+    kTestPointCount, kTestPoints, kTestPaint));
+SIMPLE_TEST_STEP(DrawPointsLiness, drawPoints(SkCanvas::kLines_PointMode,
+    kTestPointCount, kTestPoints, kTestPaint));
+SIMPLE_TEST_STEP(DrawPointsPolygon, drawPoints(SkCanvas::kPolygon_PointMode,
+    kTestPointCount, kTestPoints, kTestPaint));
+SIMPLE_TEST_STEP(DrawRect, drawRect(kTestRect, kTestPaint));
+SIMPLE_TEST_STEP(DrawPath, drawPath(kTestPath, kTestPaint));
+// The following test step is commented-out because it crashes SkDeferredCanvas
+// Issue: http://code.google.com/p/skia/issues/detail?id=505
+//SIMPLE_TEST_STEP(DrawBitmap, drawBitmap(kTestBitmap, 0, 0));
+SIMPLE_TEST_STEP(DrawBitmapPaint, drawBitmap(kTestBitmap, 0, 0, &kTestPaint));
+SIMPLE_TEST_STEP(DrawBitmapRect, drawBitmapRect(kTestBitmap, NULL, kTestRect,
+    NULL));
+SIMPLE_TEST_STEP(DrawBitmapRectSrcRect, drawBitmapRect(kTestBitmap,
+    &kTestIRect, kTestRect, NULL));
+SIMPLE_TEST_STEP(DrawBitmapRectPaint, drawBitmapRect(kTestBitmap, NULL,
+    kTestRect, &kTestPaint));
+SIMPLE_TEST_STEP(DrawBitmapMatrix, drawBitmapMatrix(kTestBitmap, kTestMatrix,
+    NULL));
+SIMPLE_TEST_STEP(DrawBitmapMatrixPaint, drawBitmapMatrix(kTestBitmap,
+    kTestMatrix, &kTestPaint));
+SIMPLE_TEST_STEP(DrawBitmapNine, drawBitmapNine(kTestBitmap, kTestIRect,
+    kTestRect, NULL));
+SIMPLE_TEST_STEP(DrawBitmapNinePaint, drawBitmapNine(kTestBitmap, kTestIRect,
+    kTestRect, &kTestPaint));
+// The following test step is commented-out because it crashes SkDeferredCanvas
+// Issue: http://code.google.com/p/skia/issues/detail?id=505
+//SIMPLE_TEST_STEP(DrawSprite, drawSprite(kTestBitmap, 0, 0, NULL));
+SIMPLE_TEST_STEP(DrawSpritePaint, drawSprite(kTestBitmap, 0, 0, &kTestPaint));
+SIMPLE_TEST_STEP(DrawText, drawText(kTestText.c_str(), kTestText.size(),
+    0, 1, kTestPaint));
+SIMPLE_TEST_STEP(DrawPosText, drawPosText(kTestText.c_str(),
+    kTestText.size(), &kTestPoint, kTestPaint));
+SIMPLE_TEST_STEP(DrawTextOnPath, drawTextOnPath(kTestText.c_str(),
+    kTestText.size(), kTestPath, NULL, kTestPaint));
+SIMPLE_TEST_STEP(DrawTextOnPathMatrix, drawTextOnPath(kTestText.c_str(),
+    kTestText.size(), kTestPath, &kTestMatrix, kTestPaint));
+SIMPLE_TEST_STEP(SetExternalMatrix, setExternalMatrix(&kTestMatrix));
+SIMPLE_TEST_STEP(DrawData, drawData(kTestText.c_str(), kTestText.size()));
+
+///////////////////////////////////////////////////////////////////////////////
+// Complex test steps
+
+static void DrawVerticesShaderTestStep(SkCanvas* canvas, 
+                                       skiatest::Reporter* reporter,
+                                       CanvasTestStep* testStep) {
+    SkPoint pts[4];
+    pts[0].set(0, 0);
+    pts[1].set(SkIntToScalar(kWidth), 0);
+    pts[2].set(SkIntToScalar(kWidth), SkIntToScalar(kHeight));
+    pts[3].set(0, SkIntToScalar(kHeight));
+    SkPaint paint;
+    SkShader* shader = SkShader::CreateBitmapShader(kTestBitmap,
+        SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+    paint.setShader(shader)->unref();
+    canvas->drawVertices(SkCanvas::kTriangleFan_VertexMode, 4, pts, pts,
+                         NULL, NULL, NULL, 0, paint);
+}
+TEST_STEP(DrawVerticesShader, DrawVerticesShaderTestStep);
+
+static void DrawPictureTestStep(SkCanvas* canvas, 
+                                skiatest::Reporter* reporter,
+                                CanvasTestStep* testStep) {
+    SkPicture* testPicture = SkNEW_ARGS(SkPicture, ());
+    SkAutoUnref aup(testPicture);
+    SkCanvas* testCanvas = testPicture->beginRecording(kWidth, kHeight);
+    testCanvas->scale(SkIntToScalar(2), SkIntToScalar(1));
+    testCanvas->clipRect(kTestRect);
+    testCanvas->drawRect(kTestRect, kTestPaint);
+    canvas->drawPicture(*testPicture);
+}
+TEST_STEP(DrawPicture, DrawPictureTestStep);
+
+static void SaveRestoreTestStep(SkCanvas* canvas, 
+                                skiatest::Reporter* reporter,
+                                CanvasTestStep* testStep) {
+    REPORTER_ASSERT_MESSAGE(reporter, 1 == canvas->getSaveCount(), 
+        testStep->assertMessage());
+    size_t n = canvas->save();
+    REPORTER_ASSERT_MESSAGE(reporter, 1 == n, testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, 2 == canvas->getSaveCount(),
+        testStep->assertMessage());
+    canvas->save();
+    canvas->save();
+    REPORTER_ASSERT_MESSAGE(reporter, 4 == canvas->getSaveCount(),
+        testStep->assertMessage());
+    canvas->restoreToCount(2);
+    REPORTER_ASSERT_MESSAGE(reporter, 2 == canvas->getSaveCount(),
+        testStep->assertMessage());
+
+    // should this pin to 1, or be a no-op, or crash?
+    canvas->restoreToCount(0);
+    REPORTER_ASSERT_MESSAGE(reporter, 1 == canvas->getSaveCount(),
+        testStep->assertMessage());
+}
+TEST_STEP(SaveRestore, SaveRestoreTestStep);
+
+// The following test step is commented-out because it currently fails
+// Issue: http://code.google.com/p/skia/issues/detail?id=506
+/*
+static void DrawLayerTestStep(SkCanvas* canvas, 
+                              skiatest::Reporter* reporter,
+                              CanvasTestStep* testStep) {
+    REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(),
+        testStep->assertMessage());
+    canvas->save();
+    REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(),
+        testStep->assertMessage());
     
     const SkRect* bounds = NULL;    // null means include entire bounds
     const SkPaint* paint = NULL;
 
-    canvas.saveLayer(bounds, paint);
-    REPORTER_ASSERT(reporter, canvas.isDrawingToLayer());
-    canvas.restore();
-    REPORTER_ASSERT(reporter, !canvas.isDrawingToLayer());
+    canvas->saveLayer(bounds, paint);
+    REPORTER_ASSERT_MESSAGE(reporter, canvas->isDrawingToLayer(),
+        testStep->assertMessage());
+    canvas->restore();
+    REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(),
+        testStep->assertMessage());
 
-    canvas.saveLayer(bounds, paint);
-    canvas.saveLayer(bounds, paint);
-    REPORTER_ASSERT(reporter, canvas.isDrawingToLayer());
-    canvas.restore();
-    REPORTER_ASSERT(reporter, canvas.isDrawingToLayer());
-    canvas.restore();
+    canvas->saveLayer(bounds, paint);
+    canvas->saveLayer(bounds, paint);
+    REPORTER_ASSERT_MESSAGE(reporter, canvas->isDrawingToLayer(),
+        testStep->assertMessage());
+    canvas->restore();
+    REPORTER_ASSERT_MESSAGE(reporter, canvas->isDrawingToLayer(),
+        testStep->assertMessage());
+    canvas->restore();
     // now layer count should be 0
-    REPORTER_ASSERT(reporter, !canvas.isDrawingToLayer());
+    REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(),
+        testStep->assertMessage());
+}
+TEST_STEP(DrawLayer, DrawLayerTestStep);
+*/
+
+static void AssertCanvasStatesEqual(skiatest::Reporter* reporter,
+                                    const SkCanvas* canvas1, 
+                                    const SkCanvas* canvas2,
+                                    CanvasTestStep* testStep) {
+    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getDeviceSize() ==
+        canvas2->getDeviceSize(), testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getSaveCount() ==
+        canvas2->getSaveCount(), testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, canvas1->isDrawingToLayer() ==
+        canvas2->isDrawingToLayer(), testStep->assertMessage());
+    SkRect bounds1, bounds2;
+    REPORTER_ASSERT_MESSAGE(reporter,
+        canvas1->getClipBounds(&bounds1, SkCanvas::kAA_EdgeType) ==
+        canvas2->getClipBounds(&bounds2, SkCanvas::kAA_EdgeType),
+        testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, bounds1 == bounds2,
+        testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter,
+        canvas1->getClipBounds(&bounds1, SkCanvas::kBW_EdgeType) ==
+        canvas2->getClipBounds(&bounds2, SkCanvas::kBW_EdgeType),
+        testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, bounds1 == bounds2,
+        testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getDrawFilter() ==
+        canvas2->getDrawFilter(), testStep->assertMessage());
+    SkIRect deviceBounds1, deviceBounds2;
+    REPORTER_ASSERT_MESSAGE(reporter,
+        canvas1->getClipDeviceBounds(&deviceBounds1) ==
+        canvas2->getClipDeviceBounds(&deviceBounds2),
+        testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, deviceBounds1 == deviceBounds2,
+        testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getBounder() ==
+        canvas2->getBounder(), testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getTotalMatrix() ==
+        canvas2->getTotalMatrix(), testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getClipType() ==
+        canvas2->getClipType(), testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getTotalClip() ==
+        canvas2->getTotalClip(), testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getTotalClipStack() ==
+        canvas2->getTotalClipStack(), testStep->assertMessage());
+
+    // The following test code is commented out because the test fails when
+    // the canvas is an SkPictureRecord or SkDeferredCanvas 
+    // Issue: http://code.google.com/p/skia/issues/detail?id=498
+    // Also, creating a LayerIter on an SkProxyCanvas crashes
+    // Issue: http://code.google.com/p/skia/issues/detail?id=499
+    /*
+    SkCanvas::LayerIter layerIter1(const_cast<SkCanvas*>(canvas1), false);
+    SkCanvas::LayerIter layerIter2(const_cast<SkCanvas*>(canvas2), false);
+    while (!layerIter1.done() && !layerIter2.done()) {
+        REPORTER_ASSERT_MESSAGE(reporter, layerIter1.matrix() ==
+            layerIter2.matrix(), testStep->assertMessage());
+        REPORTER_ASSERT_MESSAGE(reporter, layerIter1.clip() ==
+            layerIter2.clip(), testStep->assertMessage());
+        REPORTER_ASSERT_MESSAGE(reporter, layerIter1.paint() ==
+            layerIter2.paint(), testStep->assertMessage());
+        REPORTER_ASSERT_MESSAGE(reporter, layerIter1.x() ==
+            layerIter2.x(), testStep->assertMessage());
+        REPORTER_ASSERT_MESSAGE(reporter, layerIter1.y() ==
+            layerIter2.y(), testStep->assertMessage());
+        layerIter1.next();
+        layerIter2.next();
+    }
+    REPORTER_ASSERT_MESSAGE(reporter, layerIter1.done(),
+        testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, layerIter2.done(),
+        testStep->assertMessage());
+    */
+}
+
+// The following class groups static functions that need to access
+// the privates members of SkPictureRecord
+class SkPictureTester {
+private:
+    static void AssertFlattenedObjectsEqual(
+        SkPictureRecord* referenceRecord,
+        SkPictureRecord* testRecord,
+        skiatest::Reporter* reporter,
+        CanvasTestStep* testStep) {
+
+        REPORTER_ASSERT_MESSAGE(reporter,
+            referenceRecord->fBitmaps.count() ==
+            testRecord->fBitmaps.count(), testStep->assertMessage());
+        for (int i = 0; i < referenceRecord->fBitmaps.count(); ++i) {
+            REPORTER_ASSERT_MESSAGE(reporter,
+                SkFlatData::Compare(referenceRecord->fBitmaps[i],
+                testRecord->fBitmaps[i]) == 0, testStep->assertMessage());
+        }
+        REPORTER_ASSERT_MESSAGE(reporter,
+            referenceRecord->fMatrices.count() ==
+            testRecord->fMatrices.count(), testStep->assertMessage());
+        for (int i = 0; i < referenceRecord->fMatrices.count(); ++i) {
+            REPORTER_ASSERT_MESSAGE(reporter,
+                SkFlatData::Compare(referenceRecord->fMatrices[i],
+                testRecord->fMatrices[i]) == 0,
+                testStep->assertMessage());
+        }
+        REPORTER_ASSERT_MESSAGE(reporter,
+            referenceRecord->fPaints.count() ==
+            testRecord->fPaints.count(), testStep->assertMessage());
+        for (int i = 0; i < referenceRecord->fPaints.count(); ++i) {
+            REPORTER_ASSERT_MESSAGE(reporter,
+                SkFlatData::Compare(referenceRecord->fPaints[i],
+                testRecord->fPaints[i]) == 0, testStep->assertMessage());
+        }
+        REPORTER_ASSERT_MESSAGE(reporter,
+            referenceRecord->fRegions.count() ==
+            testRecord->fRegions.count(), testStep->assertMessage());
+        for (int i = 0; i < referenceRecord->fRegions.count(); ++i) {
+            REPORTER_ASSERT_MESSAGE(reporter,
+                SkFlatData::Compare(referenceRecord->fRegions[i],
+                testRecord->fRegions[i]) == 0, testStep->assertMessage());
+        }
+        REPORTER_ASSERT_MESSAGE(reporter,
+            !referenceRecord->fPathHeap ==
+            !testRecord->fPathHeap,
+            testStep->assertMessage());
+        if (referenceRecord->fPathHeap) {
+            REPORTER_ASSERT_MESSAGE(reporter,
+                referenceRecord->fPathHeap->count() ==
+                testRecord->fPathHeap->count(),
+                testStep->assertMessage());
+            for (int i = 0; i < referenceRecord->fPathHeap->count(); ++i) {
+                REPORTER_ASSERT_MESSAGE(reporter,
+                    (*referenceRecord->fPathHeap)[i] ==
+                    (*testRecord->fPathHeap)[i], testStep->assertMessage());
+            }
+        }
+    
+    }
+
+public:
+
+    static void TestPictureSerializationRoundTrip(skiatest::Reporter* reporter, 
+                                                  CanvasTestStep* testStep) {
+        testStep->setAssertMessageFormat(kPictureDrawAssertMessageFormat);
+        SkPicture referencePicture;
+        testStep->draw(referencePicture.beginRecording(kWidth, kHeight),
+            reporter);
+        SkPicture initialPicture;
+        testStep->draw(initialPicture.beginRecording(kWidth, kHeight),
+            reporter);
+        testStep->setAssertMessageFormat(kPictureReDrawAssertMessageFormat);
+        SkPicture roundTripPicture;
+        initialPicture.draw(roundTripPicture.beginRecording(kWidth, kHeight));
+
+        SkPictureRecord* referenceRecord = static_cast<SkPictureRecord*>(
+            referencePicture.getRecordingCanvas());
+        SkPictureRecord* roundTripRecord = static_cast<SkPictureRecord*>(
+            roundTripPicture.getRecordingCanvas());
+
+        testStep->setAssertMessageFormat(kPictureReDrawAssertMessageFormat);
+
+        // Verify that deserialization-serialization round trip conserves all
+        // data by comparing referenceRecord to roundTripRecord
+        REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fBitmapIndex ==
+            roundTripRecord->fBitmapIndex, testStep->assertMessage());
+        REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fMatrixIndex ==
+            roundTripRecord->fMatrixIndex, testStep->assertMessage());
+        REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fPaintIndex ==
+            roundTripRecord->fPaintIndex, testStep->assertMessage());
+        REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fRegionIndex ==
+            roundTripRecord->fRegionIndex, testStep->assertMessage());
+        char referenceBuffer[kMaxPictureBufferSize];
+        SkMemoryWStream referenceStream(referenceBuffer,
+            kMaxPictureBufferSize);
+        referenceRecord->fWriter.writeToStream(&referenceStream);
+        char roundTripBuffer[kMaxPictureBufferSize];
+        SkMemoryWStream roundTripStream(roundTripBuffer,
+            kMaxPictureBufferSize);
+        roundTripRecord->fWriter.writeToStream(&roundTripStream);
+        REPORTER_ASSERT_MESSAGE(reporter,
+            roundTripStream.bytesWritten() == referenceStream.bytesWritten(),
+            testStep->assertMessage());
+        REPORTER_ASSERT_MESSAGE(reporter, 0 == memcmp(referenceBuffer,
+            roundTripBuffer, roundTripStream.bytesWritten()),
+            testStep->assertMessage());
+        REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fRecordFlags ==
+            roundTripRecord->fRecordFlags, testStep->assertMessage());
+        REPORTER_ASSERT_MESSAGE(reporter,
+            referenceRecord->fRestoreOffsetStack ==
+            roundTripRecord->fRestoreOffsetStack,
+            testStep->assertMessage());
+        AssertFlattenedObjectsEqual(referenceRecord, roundTripRecord,
+            reporter, testStep);
+        AssertCanvasStatesEqual(reporter, referenceRecord, roundTripRecord,
+            testStep);
+    }
+
+    static void TestPictureFlattenedObjectReuse(skiatest::Reporter* reporter, 
+                                         CanvasTestStep* testStep) {
+        // Verify that when a test step is executed twice, no extra resources
+        // are flattened during the second execution
+        testStep->setAssertMessageFormat(kPictureDrawAssertMessageFormat);
+        SkPicture referencePicture;
+        SkCanvas* referenceCanvas = referencePicture.beginRecording(kWidth,
+            kHeight);
+        testStep->draw(referenceCanvas, reporter);
+        SkPicture testPicture;
+        SkCanvas* testCanvas = referencePicture.beginRecording(kWidth,
+            kHeight);
+        testStep->draw(referencePicture.beginRecording(kWidth, kHeight),
+            reporter);
+        testStep->setAssertMessageFormat(kPictureSecondDrawAssertMessageFormat);
+        testStep->draw(referencePicture.beginRecording(kWidth, kHeight),
+            reporter);
+
+        SkPictureRecord* referenceRecord = static_cast<SkPictureRecord*>(
+            referenceCanvas);
+        SkPictureRecord* testRecord = static_cast<SkPictureRecord*>(
+            testCanvas);
+        testStep->setAssertMessageFormat(kPictureResourceReuseMessageFormat);
+        AssertFlattenedObjectsEqual(referenceRecord, testRecord,
+            reporter, testStep);    
+    }
+};
+
+static void TestPictureStateConsistency(skiatest::Reporter* reporter, 
+                                        CanvasTestStep* testStep,
+                                        const SkCanvas& referenceCanvas) {
+    // Verify that the recording canvas's state is consistent
+    // with that of a regular canvas
+    SkPicture testPicture;
+    SkCanvas* pictureCanvas = testPicture.beginRecording(kWidth, kHeight);
+    testStep->setAssertMessageFormat(kPictureDrawAssertMessageFormat);
+    testStep->draw(pictureCanvas, reporter);
+    testStep->setAssertMessageFormat(kPictureRecoringAssertMessageFormat);
+    AssertCanvasStatesEqual(reporter, pictureCanvas, &referenceCanvas,
+        testStep);
+
+    SkBitmap playbackStore;
+    createBitmap(&playbackStore, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
+    SkDevice playbackDevice(playbackStore);
+    SkCanvas playbackCanvas(&playbackDevice);
+    testPicture.draw(&playbackCanvas);
+    testStep->setAssertMessageFormat(kPicturePlaybackAssertMessageFormat);
+    AssertCanvasStatesEqual(reporter, &playbackCanvas, &referenceCanvas,
+        testStep);
+
+    // The following test code is commented out because SkPicture is not
+    // currently expected to preserve state when restarting recording.
+    /*
+    SkCanvas* pictureCanvas = testPicture.beginRecording(kWidth, kHeight);
+    testStep->setAssertMessageFormat(kPictureResumeAssertMessageFormat);
+    AssertCanvasStatesEqual(reporter, pictureCanvas, &referenceCanvas,
+        testStep);
+    */
+}
+
+static void TestDeferredCanvasStateConsistency(
+    skiatest::Reporter* reporter,
+    CanvasTestStep* testStep,
+    const SkCanvas& referenceCanvas) {
+
+    SkBitmap deferredStore;
+    createBitmap(&deferredStore, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
+    SkDevice deferredDevice(deferredStore);
+    SkDeferredCanvas deferredCanvas(&deferredDevice);
+    testStep->setAssertMessageFormat(kDeferredDrawAssertMessageFormat);
+    testStep->draw(&deferredCanvas, reporter);
+    testStep->setAssertMessageFormat(kDeferredPreFlushAssertMessageFormat);
+    AssertCanvasStatesEqual(reporter, &deferredCanvas, &referenceCanvas,
+        testStep);
+
+    // Verified that deferred canvas state is not affected by flushing
+    // pending draw operations
+
+    // The following test code is commented out because it currently fails.
+    // Issue: http://code.google.com/p/skia/issues/detail?id=496
+    /*
+    deferredCanvas.flush();
+    testStep->setAssertMessageFormat(kDeferredPostFlushAssertMessageFormat);
+    AssertCanvasStatesEqual(reporter, &deferredCanvas, &referenceCanvas,
+        testStep);
+    */
+}
+
+static void TestProxyCanvasStateConsistency(
+    skiatest::Reporter* reporter,
+    CanvasTestStep* testStep,
+    const SkCanvas& referenceCanvas) {
+
+    SkBitmap indirectStore;
+    createBitmap(&indirectStore, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
+    SkDevice indirectDevice(indirectStore);
+    SkCanvas indirectCanvas(&indirectDevice);
+    SkProxyCanvas proxyCanvas(&indirectCanvas);
+    testStep->setAssertMessageFormat(kProxyDrawAssertMessageFormat);
+    testStep->draw(&proxyCanvas, reporter);
+    // Verify that the SkProxyCanvas reports consitent state
+    testStep->setAssertMessageFormat(kProxyStateAssertMessageFormat);
+    AssertCanvasStatesEqual(reporter, &proxyCanvas, &referenceCanvas,
+        testStep);
+    // Verify that the indirect canvas reports consitent state
+    testStep->setAssertMessageFormat(kProxyIndirectStateAssertMessageFormat);
+    AssertCanvasStatesEqual(reporter, &indirectCanvas, &referenceCanvas,
+        testStep);
+}
+
+static void TestNWayCanvasStateConsistency(
+    skiatest::Reporter* reporter,
+    CanvasTestStep* testStep,
+    const SkCanvas& referenceCanvas) {
+
+    SkBitmap indirectStore1;
+    createBitmap(&indirectStore1, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
+    SkDevice indirectDevice1(indirectStore1);
+    SkCanvas indirectCanvas1(&indirectDevice1);
+
+    SkBitmap indirectStore2;
+    createBitmap(&indirectStore2, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
+    SkDevice indirectDevice2(indirectStore2);
+    SkCanvas indirectCanvas2(&indirectDevice2);
+
+    SkNWayCanvas nWayCanvas;
+    nWayCanvas.addCanvas(&indirectCanvas1);
+    nWayCanvas.addCanvas(&indirectCanvas2);
+
+    testStep->setAssertMessageFormat(kNWayDrawAssertMessageFormat);
+    testStep->draw(&nWayCanvas, reporter);
+    // Verify that the SkProxyCanvas reports consitent state
+    testStep->setAssertMessageFormat(kNWayStateAssertMessageFormat);
+    AssertCanvasStatesEqual(reporter, &nWayCanvas, &referenceCanvas,
+        testStep);
+    // Verify that the indirect canvases report consitent state
+    testStep->setAssertMessageFormat(kNWayIndirect1StateAssertMessageFormat);
+    AssertCanvasStatesEqual(reporter, &indirectCanvas1, &referenceCanvas,
+        testStep);
+    testStep->setAssertMessageFormat(kNWayIndirect2StateAssertMessageFormat);
+    AssertCanvasStatesEqual(reporter, &indirectCanvas2, &referenceCanvas,
+        testStep);
+}
+
+/*
+ * This sub-test verifies that the test step passes when executed
+ * with SkCanvas and with classes derrived from SkCanvas. It also verifies
+ * that the all canvas derivatives report the same state as an SkCanvas
+ * after having executed the test step.
+ */
+static void TestOverrideStateConsistency(skiatest::Reporter* reporter, 
+                                         CanvasTestStep* testStep) {
+    SkBitmap referenceStore;
+    createBitmap(&referenceStore, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
+    SkDevice referenceDevice(referenceStore);
+    SkCanvas referenceCanvas(&referenceDevice);
+    testStep->setAssertMessageFormat(kCanvasDrawAssertMessageFormat);
+    testStep->draw(&referenceCanvas, reporter);
+
+    TestPictureStateConsistency(reporter, testStep, referenceCanvas);
+    TestDeferredCanvasStateConsistency(reporter, testStep, referenceCanvas);
+
+    // The following test code is commented out because SkProxyCanvas is
+    // missing a lot of virtual overrides on get* methods, which are used
+    // to verify canvas state.
+    // Issue: http://code.google.com/p/skia/issues/detail?id=500
+    
+    //TestProxyCanvasStateConsistency(reporter, testStep, referenceCanvas);
+
+    // The following test code is commented out because SkNWayCanvas does not
+    // report correct clipping and device bounds information
+    // Issue: http://code.google.com/p/skia/issues/detail?id=501
+    
+    //TestNWayCanvasStateConsistency(reporter, testStep, referenceCanvas);
 }
 
 static void TestCanvas(skiatest::Reporter* reporter) {
-    SkBitmap bm;
-    bm.setConfig(SkBitmap::kARGB_8888_Config, 256, 256);
-    bm.allocPixels();
+    // Init global here because bitmap pixels cannot be alocated during
+    // static initialization
+    kTestBitmap = testBitmap();
 
-    SkCanvas canvas(bm);
-    int n;
-
-    REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount());
-    n = canvas.save();
-    REPORTER_ASSERT(reporter, 1 == n);
-    REPORTER_ASSERT(reporter, 2 == canvas.getSaveCount());
-    canvas.save();
-    canvas.save();
-    REPORTER_ASSERT(reporter, 4 == canvas.getSaveCount());
-    canvas.restoreToCount(2);
-    REPORTER_ASSERT(reporter, 2 == canvas.getSaveCount());
-
-    // should this pin to 1, or be a no-op, or crash?
-    canvas.restoreToCount(0);
-    REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount());
-
-    test_isDrawingToLayer(reporter);
+    for (int testStep = 0; testStep < testStepArray().count(); testStep++) {
+        TestOverrideStateConsistency(reporter, testStepArray()[testStep]);
+        SkPictureTester::TestPictureSerializationRoundTrip(reporter, 
+            testStepArray()[testStep]);
+        SkPictureTester::TestPictureFlattenedObjectReuse(reporter,
+            testStepArray()[testStep]);
+    }
 }
 
 #include "TestClassDef.h"
diff --git a/tests/Test.h b/tests/Test.h
index 8728040..4ca1971 100644
--- a/tests/Test.h
+++ b/tests/Test.h
@@ -115,10 +115,19 @@
     do {                                                                \
         if (!(cond)) {                                                  \
             SkString desc;                                              \
-            desc.printf("%s:%d: %s", __FILE__, __LINE__, #cond);      \
+            desc.printf("%s:%d: %s", __FILE__, __LINE__, #cond);        \
             r->reportFailed(desc);                                      \
         }                                                               \
     } while(0)
 
+#define REPORTER_ASSERT_MESSAGE(r, cond, message)                            \
+    do {                                                                     \
+        if (!(cond)) {                                                       \
+            SkString desc;                                                   \
+            desc.printf("%s %s:%d: %s", message, __FILE__, __LINE__, #cond); \
+            r->reportFailed(desc);                                           \
+        }                                                                    \
+    } while(0)
+
 
 #endif