text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "base/basictypes.h"
  #include "base/bind.h"
  #include "base/compiler_specific.h"
  #include "base/file_util.h"
  #include "base/files/file_path.h"
  #include "base/memory/scoped_ptr.h"
  #include "base/message_loop/message_loop.h"
  #include "base/path_service.h"
  #include "base/strings/string_util.h"
  #include "base/strings/stringprintf.h"
  #include "cc/layers/delegated_frame_provider.h"
  #include "cc/layers/delegated_frame_resource_collection.h"
  #include "cc/layers/layer.h"
  #include "cc/output/delegated_frame_data.h"
  #include "cc/test/pixel_test_utils.h"
  #include "testing/gtest/include/gtest/gtest.h"
  #include "ui/compositor/compositor_observer.h"
  #include "ui/compositor/layer.h"
  #include "ui/compositor/layer_animation_sequence.h"
  #include "ui/compositor/layer_animator.h"
  #include "ui/compositor/test/context_factories_for_test.h"
  #include "ui/compositor/test/test_compositor_host.h"
  #include "ui/compositor/test/test_layers.h"
  #include "ui/gfx/canvas.h"
  #include "ui/gfx/codec/png_codec.h"
  #include "ui/gfx/gfx_paths.h"
  #include "ui/gfx/skia_util.h"
  
  using cc::MatchesPNGFile;
  
  namespace ui {
  
  namespace {
  
  // There are three test classes in here that configure the Compositor and
  // Layer's slightly differently:
  // - LayerWithNullDelegateTest uses NullLayerDelegate as the LayerDelegate. This
  //   is typically the base class you want to use.
  // - LayerWithDelegateTest uses LayerDelegate on the delegates.
  // - LayerWithRealCompositorTest when a real compositor is required for testing.
  //    - Slow because they bring up a window and run the real compositor. This
  //      is typically not what you want.
  
  class ColoredLayer : public Layer, public LayerDelegate {
   public:
    explicit ColoredLayer(SkColor color)
        : Layer(LAYER_TEXTURED),
          color_(color) {
      set_delegate(this);
    }
  
    virtual ~ColoredLayer() { }
  
    // Overridden from LayerDelegate:
    virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE {
      canvas->DrawColor(color_);
    }
  
    virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {
    }
  
    virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE {
      return base::Closure();
    }
  
   private:
    SkColor color_;
  };
  
  class LayerWithRealCompositorTest : public testing::Test {
   public:
    LayerWithRealCompositorTest() {
      if (PathService::Get(gfx::DIR_TEST_DATA, &test_data_directory_)) {
        test_data_directory_ = test_data_directory_.AppendASCII("compositor");
      } else {
        LOG(ERROR) << "Could not open test data directory.";
      }
    }
    virtual ~LayerWithRealCompositorTest() {}
  
    // Overridden from testing::Test:
    virtual void SetUp() OVERRIDE {
      bool allow_test_contexts = false;
      InitializeContextFactoryForTests(allow_test_contexts);
      Compositor::Initialize();
  
      const gfx::Rect host_bounds(10, 10, 500, 500);
      window_.reset(TestCompositorHost::Create(host_bounds));
      window_->Show();
    }
  
    virtual void TearDown() OVERRIDE {
      window_.reset();
      TerminateContextFactoryForTests();
      Compositor::Terminate();
    }
  
    Compositor* GetCompositor() {
      return window_->GetCompositor();
    }
  
    Layer* CreateLayer(LayerType type) {
      return new Layer(type);
    }
  
    Layer* CreateColorLayer(SkColor color, const gfx::Rect& bounds) {
      Layer* layer = new ColoredLayer(color);
      layer->SetBounds(bounds);
      return layer;
    }
  
    Layer* CreateNoTextureLayer(const gfx::Rect& bounds) {
      Layer* layer = CreateLayer(LAYER_NOT_DRAWN);
      layer->SetBounds(bounds);
      return layer;
    }
  
    void DrawTree(Layer* root) {
      GetCompositor()->SetRootLayer(root);
      GetCompositor()->ScheduleDraw();
      WaitForDraw();
    }
  
    bool ReadPixels(SkBitmap* bitmap) {
      return GetCompositor()->ReadPixels(bitmap,
                                         gfx::Rect(GetCompositor()->size()));
    }
  
    void WaitForDraw() {
      ui::DrawWaiterForTest::Wait(GetCompositor());
    }
  
    void WaitForCommit() {
      ui::DrawWaiterForTest::WaitForCommit(GetCompositor());
    }
  
    // Invalidates the entire contents of the layer.
    void SchedulePaintForLayer(Layer* layer) {
      layer->SchedulePaint(
          gfx::Rect(0, 0, layer->bounds().width(), layer->bounds().height()));
    }
  
    const base::FilePath& test_data_directory() const {
      return test_data_directory_;
    }
  
   private:
    scoped_ptr<TestCompositorHost> window_;
  
    // The root directory for test files.
    base::FilePath test_data_directory_;
  
    DISALLOW_COPY_AND_ASSIGN(LayerWithRealCompositorTest);
  };
  
  // LayerDelegate that paints colors to the layer.
  class TestLayerDelegate : public LayerDelegate {
   public:
    explicit TestLayerDelegate() { reset(); }
    virtual ~TestLayerDelegate() {}
  
    void AddColor(SkColor color) {
      colors_.push_back(color);
    }
  
    const gfx::Size& paint_size() const { return paint_size_; }
    int color_index() const { return color_index_; }
  
    std::string ToScaleString() const {
      return base::StringPrintf("%.1f %.1f", scale_x_, scale_y_);
    }
  
    float device_scale_factor() const {
      return device_scale_factor_;
    }
  
    // Overridden from LayerDelegate:
    virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE {
      SkIRect clip_bounds;
      canvas->sk_canvas()->getClipDeviceBounds(&clip_bounds);
      paint_size_ = gfx::Size(clip_bounds.width(), clip_bounds.height());
      canvas->FillRect(gfx::Rect(paint_size_), colors_[color_index_]);
      color_index_ = (color_index_ + 1) % static_cast<int>(colors_.size());
      const SkMatrix& matrix = canvas->sk_canvas()->getTotalMatrix();
      scale_x_ = matrix.getScaleX();
      scale_y_ = matrix.getScaleY();
    }
  
    virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {
      device_scale_factor_ = device_scale_factor;
    }
  
    virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE {
      return base::Closure();
    }
  
    void reset() {
      color_index_ = 0;
      paint_size_.SetSize(0, 0);
      scale_x_ = scale_y_ = 0.0f;
      device_scale_factor_ = 0.0f;
    }
  
   private:
    std::vector<SkColor> colors_;
    int color_index_;
    gfx::Size paint_size_;
    float scale_x_;
    float scale_y_;
    float device_scale_factor_;
  
    DISALLOW_COPY_AND_ASSIGN(TestLayerDelegate);
  };
  
  // LayerDelegate that verifies that a layer was asked to update its canvas.
  class DrawTreeLayerDelegate : public LayerDelegate {
   public:
    DrawTreeLayerDelegate() : painted_(false) {}
    virtual ~DrawTreeLayerDelegate() {}
  
    void Reset() {
      painted_ = false;
    }
  
    bool painted() const { return painted_; }
  
   private:
    // Overridden from LayerDelegate:
    virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE {
      painted_ = true;
    }
    virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {
    }
    virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE {
      return base::Closure();
    }
  
    bool painted_;
  
    DISALLOW_COPY_AND_ASSIGN(DrawTreeLayerDelegate);
  };
  
  // The simplest possible layer delegate. Does nothing.
  class NullLayerDelegate : public LayerDelegate {
   public:
    NullLayerDelegate() {}
    virtual ~NullLayerDelegate() {}
  
   private:
    // Overridden from LayerDelegate:
    virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE {
    }
    virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {
    }
    virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE {
      return base::Closure();
    }
  
    DISALLOW_COPY_AND_ASSIGN(NullLayerDelegate);
  };
  
  // Remembers if it has been notified.
  class TestCompositorObserver : public CompositorObserver {
   public:
    TestCompositorObserver()
        : committed_(false), started_(false), ended_(false), aborted_(false) {}
  
    bool committed() const { return committed_; }
    bool notified() const { return started_ && ended_; }
    bool aborted() const { return aborted_; }
  
    void Reset() {
      committed_ = false;
      started_ = false;
      ended_ = false;
      aborted_ = false;
    }
  
   private:
    virtual void OnCompositingDidCommit(Compositor* compositor) OVERRIDE {
      committed_ = true;
    }
  
    virtual void OnCompositingStarted(Compositor* compositor,
                                      base::TimeTicks start_time) OVERRIDE {
      started_ = true;
    }
  
    virtual void OnCompositingEnded(Compositor* compositor) OVERRIDE {
      ended_ = true;
    }
  
    virtual void OnCompositingAborted(Compositor* compositor) OVERRIDE {
      aborted_ = true;
    }
  
    virtual void OnCompositingLockStateChanged(Compositor* compositor) OVERRIDE {
    }
  
    virtual void OnUpdateVSyncParameters(Compositor* compositor,
                                         base::TimeTicks timebase,
                                         base::TimeDelta interval) OVERRIDE {
    }
  
    bool committed_;
    bool started_;
    bool ended_;
    bool aborted_;
  
    DISALLOW_COPY_AND_ASSIGN(TestCompositorObserver);
  };
  
  }  // namespace
  
  TEST_F(LayerWithRealCompositorTest, Draw) {
    scoped_ptr<Layer> layer(CreateColorLayer(SK_ColorRED,
                                             gfx::Rect(20, 20, 50, 50)));
    DrawTree(layer.get());
  }
  
  // Create this hierarchy:
  // L1 - red
  // +-- L2 - blue
  // |   +-- L3 - yellow
  // +-- L4 - magenta
  //
  TEST_F(LayerWithRealCompositorTest, Hierarchy) {
    scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorRED,
                                          gfx::Rect(20, 20, 400, 400)));
    scoped_ptr<Layer> l2(CreateColorLayer(SK_ColorBLUE,
                                          gfx::Rect(10, 10, 350, 350)));
    scoped_ptr<Layer> l3(CreateColorLayer(SK_ColorYELLOW,
                                          gfx::Rect(5, 5, 25, 25)));
    scoped_ptr<Layer> l4(CreateColorLayer(SK_ColorMAGENTA,
                                          gfx::Rect(300, 300, 100, 100)));
  
    l1->Add(l2.get());
    l1->Add(l4.get());
    l2->Add(l3.get());
  
    DrawTree(l1.get());
  }
  
  class LayerWithDelegateTest : public testing::Test {
   public:
    LayerWithDelegateTest() {}
    virtual ~LayerWithDelegateTest() {}
  
    // Overridden from testing::Test:
    virtual void SetUp() OVERRIDE {
      bool allow_test_contexts = true;
      InitializeContextFactoryForTests(allow_test_contexts);
      Compositor::Initialize();
      compositor_.reset(new Compositor(gfx::kNullAcceleratedWidget));
      compositor_->SetScaleAndSize(1.0f, gfx::Size(1000, 1000));
    }
  
    virtual void TearDown() OVERRIDE {
      compositor_.reset();
      TerminateContextFactoryForTests();
      Compositor::Terminate();
    }
  
    Compositor* compositor() { return compositor_.get(); }
  
    virtual Layer* CreateLayer(LayerType type) {
      return new Layer(type);
    }
  
    Layer* CreateColorLayer(SkColor color, const gfx::Rect& bounds) {
      Layer* layer = new ColoredLayer(color);
      layer->SetBounds(bounds);
      return layer;
    }
  
    virtual Layer* CreateNoTextureLayer(const gfx::Rect& bounds) {
      Layer* layer = CreateLayer(LAYER_NOT_DRAWN);
      layer->SetBounds(bounds);
      return layer;
    }
  
    void DrawTree(Layer* root) {
      compositor()->SetRootLayer(root);
      Draw();
    }
  
    // Invalidates the entire contents of the layer.
    void SchedulePaintForLayer(Layer* layer) {
      layer->SchedulePaint(
          gfx::Rect(0, 0, layer->bounds().width(), layer->bounds().height()));
    }
  
    // Invokes DrawTree on the compositor.
    void Draw() {
      compositor()->ScheduleDraw();
      WaitForDraw();
    }
  
    void WaitForDraw() {
      DrawWaiterForTest::Wait(compositor());
    }
  
    void WaitForCommit() {
      DrawWaiterForTest::WaitForCommit(compositor());
    }
  
   private:
    scoped_ptr<Compositor> compositor_;
  
    DISALLOW_COPY_AND_ASSIGN(LayerWithDelegateTest);
  };
  
  // L1
  //  +-- L2
  TEST_F(LayerWithDelegateTest, ConvertPointToLayer_Simple) {
    scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorRED,
                                          gfx::Rect(20, 20, 400, 400)));
    scoped_ptr<Layer> l2(CreateColorLayer(SK_ColorBLUE,
                                          gfx::Rect(10, 10, 350, 350)));
    l1->Add(l2.get());
    DrawTree(l1.get());
  
    gfx::Point point1_in_l2_coords(5, 5);
    Layer::ConvertPointToLayer(l2.get(), l1.get(), &point1_in_l2_coords);
    gfx::Point point1_in_l1_coords(15, 15);
    EXPECT_EQ(point1_in_l1_coords, point1_in_l2_coords);
  
    gfx::Point point2_in_l1_coords(5, 5);
    Layer::ConvertPointToLayer(l1.get(), l2.get(), &point2_in_l1_coords);
    gfx::Point point2_in_l2_coords(-5, -5);
    EXPECT_EQ(point2_in_l2_coords, point2_in_l1_coords);
  }
  
  // L1
  //  +-- L2
  //       +-- L3
  TEST_F(LayerWithDelegateTest, ConvertPointToLayer_Medium) {
    scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorRED,
                                          gfx::Rect(20, 20, 400, 400)));
    scoped_ptr<Layer> l2(CreateColorLayer(SK_ColorBLUE,
                                          gfx::Rect(10, 10, 350, 350)));
    scoped_ptr<Layer> l3(CreateColorLayer(SK_ColorYELLOW,
                                          gfx::Rect(10, 10, 100, 100)));
    l1->Add(l2.get());
    l2->Add(l3.get());
    DrawTree(l1.get());
  
    gfx::Point point1_in_l3_coords(5, 5);
    Layer::ConvertPointToLayer(l3.get(), l1.get(), &point1_in_l3_coords);
    gfx::Point point1_in_l1_coords(25, 25);
    EXPECT_EQ(point1_in_l1_coords, point1_in_l3_coords);
  
    gfx::Point point2_in_l1_coords(5, 5);
    Layer::ConvertPointToLayer(l1.get(), l3.get(), &point2_in_l1_coords);
    gfx::Point point2_in_l3_coords(-15, -15);
    EXPECT_EQ(point2_in_l3_coords, point2_in_l1_coords);
  }
  
  TEST_F(LayerWithRealCompositorTest, Delegate) {
    scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorBLACK,
                                          gfx::Rect(20, 20, 400, 400)));
    GetCompositor()->SetRootLayer(l1.get());
    WaitForDraw();
  
    TestLayerDelegate delegate;
    l1->set_delegate(&delegate);
    delegate.AddColor(SK_ColorWHITE);
    delegate.AddColor(SK_ColorYELLOW);
    delegate.AddColor(SK_ColorGREEN);
  
    l1->SchedulePaint(gfx::Rect(0, 0, 400, 400));
    WaitForDraw();
  
    EXPECT_EQ(delegate.color_index(), 1);
    EXPECT_EQ(delegate.paint_size(), l1->bounds().size());
  
    l1->SchedulePaint(gfx::Rect(10, 10, 200, 200));
    WaitForDraw();
    EXPECT_EQ(delegate.color_index(), 2);
    EXPECT_EQ(delegate.paint_size(), gfx::Size(200, 200));
  
    l1->SchedulePaint(gfx::Rect(5, 5, 50, 50));
    WaitForDraw();
    EXPECT_EQ(delegate.color_index(), 0);
    EXPECT_EQ(delegate.paint_size(), gfx::Size(50, 50));
  }
  
  TEST_F(LayerWithRealCompositorTest, DrawTree) {
    scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorRED,
                                          gfx::Rect(20, 20, 400, 400)));
    scoped_ptr<Layer> l2(CreateColorLayer(SK_ColorBLUE,
                                          gfx::Rect(10, 10, 350, 350)));
    scoped_ptr<Layer> l3(CreateColorLayer(SK_ColorYELLOW,
                                          gfx::Rect(10, 10, 100, 100)));
    l1->Add(l2.get());
    l2->Add(l3.get());
  
    GetCompositor()->SetRootLayer(l1.get());
    WaitForDraw();
  
    DrawTreeLayerDelegate d1;
    l1->set_delegate(&d1);
    DrawTreeLayerDelegate d2;
    l2->set_delegate(&d2);
    DrawTreeLayerDelegate d3;
    l3->set_delegate(&d3);
  
    l2->SchedulePaint(gfx::Rect(5, 5, 5, 5));
    WaitForDraw();
    EXPECT_FALSE(d1.painted());
    EXPECT_TRUE(d2.painted());
    EXPECT_FALSE(d3.painted());
  }
  
  // Tests no-texture Layers.
  // Create this hierarchy:
  // L1 - red
  // +-- L2 - NO TEXTURE
  // |   +-- L3 - yellow
  // +-- L4 - magenta
  //
  TEST_F(LayerWithRealCompositorTest, HierarchyNoTexture) {
    scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorRED,
                                          gfx::Rect(20, 20, 400, 400)));
    scoped_ptr<Layer> l2(CreateNoTextureLayer(gfx::Rect(10, 10, 350, 350)));
    scoped_ptr<Layer> l3(CreateColorLayer(SK_ColorYELLOW,
                                          gfx::Rect(5, 5, 25, 25)));
    scoped_ptr<Layer> l4(CreateColorLayer(SK_ColorMAGENTA,
                                          gfx::Rect(300, 300, 100, 100)));
  
    l1->Add(l2.get());
    l1->Add(l4.get());
    l2->Add(l3.get());
  
    GetCompositor()->SetRootLayer(l1.get());
    WaitForDraw();
  
    DrawTreeLayerDelegate d2;
    l2->set_delegate(&d2);
    DrawTreeLayerDelegate d3;
    l3->set_delegate(&d3);
  
    l2->SchedulePaint(gfx::Rect(5, 5, 5, 5));
    l3->SchedulePaint(gfx::Rect(5, 5, 5, 5));
    WaitForDraw();
  
    // |d2| should not have received a paint notification since it has no texture.
    EXPECT_FALSE(d2.painted());
    // |d3| should have received a paint notification.
    EXPECT_TRUE(d3.painted());
  }
  
  class LayerWithNullDelegateTest : public LayerWithDelegateTest {
   public:
    LayerWithNullDelegateTest() {}
    virtual ~LayerWithNullDelegateTest() {}
  
    virtual void SetUp() OVERRIDE {
      LayerWithDelegateTest::SetUp();
      default_layer_delegate_.reset(new NullLayerDelegate());
    }
  
    virtual Layer* CreateLayer(LayerType type) OVERRIDE {
      Layer* layer = new Layer(type);
      layer->set_delegate(default_layer_delegate_.get());
      return layer;
    }
  
    Layer* CreateTextureRootLayer(const gfx::Rect& bounds) {
      Layer* layer = CreateTextureLayer(bounds);
      compositor()->SetRootLayer(layer);
      return layer;
    }
  
    Layer* CreateTextureLayer(const gfx::Rect& bounds) {
      Layer* layer = CreateLayer(LAYER_TEXTURED);
      layer->SetBounds(bounds);
      return layer;
    }
  
    virtual Layer* CreateNoTextureLayer(const gfx::Rect& bounds) OVERRIDE {
      Layer* layer = CreateLayer(LAYER_NOT_DRAWN);
      layer->SetBounds(bounds);
      return layer;
    }
  
   private:
    scoped_ptr<NullLayerDelegate> default_layer_delegate_;
  
    DISALLOW_COPY_AND_ASSIGN(LayerWithNullDelegateTest);
  };
  
  class FakeTexture : public Texture {
   public:
    FakeTexture(bool flipped, const gfx::Size& size, float device_scale_factor)
        : Texture(flipped, size, device_scale_factor) {}
  
    virtual unsigned int PrepareTexture() OVERRIDE { return 0; }
    virtual blink::WebGraphicsContext3D* HostContext3D() OVERRIDE {
      return NULL;
    }
  
   protected:
    virtual ~FakeTexture() {}
  };
  
  TEST_F(LayerWithNullDelegateTest, SwitchLayerPreservesCCLayerState) {
    scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorRED,
                                          gfx::Rect(20, 20, 400, 400)));
    l1->SetFillsBoundsOpaquely(true);
    l1->SetForceRenderSurface(true);
    l1->SetVisible(false);
  
    EXPECT_EQ(gfx::PointF().ToString(),
              l1->cc_layer()->anchor_point().ToString());
    EXPECT_TRUE(l1->cc_layer()->DrawsContent());
    EXPECT_TRUE(l1->cc_layer()->contents_opaque());
    EXPECT_TRUE(l1->cc_layer()->force_render_surface());
    EXPECT_TRUE(l1->cc_layer()->hide_layer_and_subtree());
  
    cc::Layer* before_layer = l1->cc_layer();
  
    scoped_refptr<Texture> texture =
        new FakeTexture(false, gfx::Size(10, 10), 1.f);
    l1->SetExternalTexture(texture.get());
  
    EXPECT_NE(before_layer, l1->cc_layer());
  
    EXPECT_EQ(gfx::PointF().ToString(),
              l1->cc_layer()->anchor_point().ToString());
    EXPECT_TRUE(l1->cc_layer()->DrawsContent());
    EXPECT_TRUE(l1->cc_layer()->contents_opaque());
    EXPECT_TRUE(l1->cc_layer()->force_render_surface());
    EXPECT_TRUE(l1->cc_layer()->hide_layer_and_subtree());
  }
  
  // Various visibile/drawn assertions.
  TEST_F(LayerWithNullDelegateTest, Visibility) {
    scoped_ptr<Layer> l1(new Layer(LAYER_TEXTURED));
    scoped_ptr<Layer> l2(new Layer(LAYER_TEXTURED));
    scoped_ptr<Layer> l3(new Layer(LAYER_TEXTURED));
    l1->Add(l2.get());
    l2->Add(l3.get());
  
    NullLayerDelegate delegate;
    l1->set_delegate(&delegate);
    l2->set_delegate(&delegate);
    l3->set_delegate(&delegate);
  
    // Layers should initially be drawn.
    EXPECT_TRUE(l1->IsDrawn());
    EXPECT_TRUE(l2->IsDrawn());
    EXPECT_TRUE(l3->IsDrawn());
    EXPECT_FALSE(l1->cc_layer()->hide_layer_and_subtree());
    EXPECT_FALSE(l2->cc_layer()->hide_layer_and_subtree());
    EXPECT_FALSE(l3->cc_layer()->hide_layer_and_subtree());
  
    compositor()->SetRootLayer(l1.get());
  
    Draw();
  
    l1->SetVisible(false);
    EXPECT_FALSE(l1->IsDrawn());
    EXPECT_FALSE(l2->IsDrawn());
    EXPECT_FALSE(l3->IsDrawn());
    EXPECT_TRUE(l1->cc_layer()->hide_layer_and_subtree());
    EXPECT_FALSE(l2->cc_layer()->hide_layer_and_subtree());
    EXPECT_FALSE(l3->cc_layer()->hide_layer_and_subtree());
  
    l3->SetVisible(false);
    EXPECT_FALSE(l1->IsDrawn());
    EXPECT_FALSE(l2->IsDrawn());
    EXPECT_FALSE(l3->IsDrawn());
    EXPECT_TRUE(l1->cc_layer()->hide_layer_and_subtree());
    EXPECT_FALSE(l2->cc_layer()->hide_layer_and_subtree());
    EXPECT_TRUE(l3->cc_layer()->hide_layer_and_subtree());
  
    l1->SetVisible(true);
    EXPECT_TRUE(l1->IsDrawn());
    EXPECT_TRUE(l2->IsDrawn());
    EXPECT_FALSE(l3->IsDrawn());
    EXPECT_FALSE(l1->cc_layer()->hide_layer_and_subtree());
    EXPECT_FALSE(l2->cc_layer()->hide_layer_and_subtree());
    EXPECT_TRUE(l3->cc_layer()->hide_layer_and_subtree());
  }
  
  // Checks that stacking-related methods behave as advertised.
  TEST_F(LayerWithNullDelegateTest, Stacking) {
    scoped_ptr<Layer> root(new Layer(LAYER_NOT_DRAWN));
    scoped_ptr<Layer> l1(new Layer(LAYER_TEXTURED));
    scoped_ptr<Layer> l2(new Layer(LAYER_TEXTURED));
    scoped_ptr<Layer> l3(new Layer(LAYER_TEXTURED));
    l1->set_name("1");
    l2->set_name("2");
    l3->set_name("3");
    root->Add(l3.get());
    root->Add(l2.get());
    root->Add(l1.get());
  
    // Layers' children are stored in bottom-to-top order.
    EXPECT_EQ("3 2 1", test::ChildLayerNamesAsString(*root.get()));
  
    root->StackAtTop(l3.get());
    EXPECT_EQ("2 1 3", test::ChildLayerNamesAsString(*root.get()));
  
    root->StackAtTop(l1.get());
    EXPECT_EQ("2 3 1", test::ChildLayerNamesAsString(*root.get()));
  
    root->StackAtTop(l1.get());
    EXPECT_EQ("2 3 1", test::ChildLayerNamesAsString(*root.get()));
  
    root->StackAbove(l2.get(), l3.get());
    EXPECT_EQ("3 2 1", test::ChildLayerNamesAsString(*root.get()));
  
    root->StackAbove(l1.get(), l3.get());
    EXPECT_EQ("3 1 2", test::ChildLayerNamesAsString(*root.get()));
  
    root->StackAbove(l2.get(), l1.get());
    EXPECT_EQ("3 1 2", test::ChildLayerNamesAsString(*root.get()));
  
    root->StackAtBottom(l2.get());
    EXPECT_EQ("2 3 1", test::ChildLayerNamesAsString(*root.get()));
  
    root->StackAtBottom(l3.get());
    EXPECT_EQ("3 2 1", test::ChildLayerNamesAsString(*root.get()));
  
    root->StackAtBottom(l3.get());
    EXPECT_EQ("3 2 1", test::ChildLayerNamesAsString(*root.get()));
  
    root->StackBelow(l2.get(), l3.get());
    EXPECT_EQ("2 3 1", test::ChildLayerNamesAsString(*root.get()));
  
    root->StackBelow(l1.get(), l3.get());
    EXPECT_EQ("2 1 3", test::ChildLayerNamesAsString(*root.get()));
  
    root->StackBelow(l3.get(), l2.get());
    EXPECT_EQ("3 2 1", test::ChildLayerNamesAsString(*root.get()));
  
    root->StackBelow(l3.get(), l2.get());
    EXPECT_EQ("3 2 1", test::ChildLayerNamesAsString(*root.get()));
  
    root->StackBelow(l3.get(), l1.get());
    EXPECT_EQ("2 3 1", test::ChildLayerNamesAsString(*root.get()));
  }
  
  // Verifies SetBounds triggers the appropriate painting/drawing.
  TEST_F(LayerWithNullDelegateTest, SetBoundsSchedulesPaint) {
    scoped_ptr<Layer> l1(CreateTextureLayer(gfx::Rect(0, 0, 200, 200)));
    compositor()->SetRootLayer(l1.get());
  
    Draw();
  
    l1->SetBounds(gfx::Rect(5, 5, 200, 200));
  
    // The CompositorDelegate (us) should have been told to draw for a move.
    WaitForDraw();
  
    l1->SetBounds(gfx::Rect(5, 5, 100, 100));
  
    // The CompositorDelegate (us) should have been told to draw for a resize.
    WaitForDraw();
  }
  
  // Checks that pixels are actually drawn to the screen with a read back.
  TEST_F(LayerWithRealCompositorTest, DrawPixels) {
    gfx::Size viewport_size = GetCompositor()->size();
  
    // The window should be some non-trivial size but may not be exactly
    // 500x500 on all platforms/bots.
    EXPECT_GE(viewport_size.width(), 200);
    EXPECT_GE(viewport_size.height(), 200);
  
    int blue_height = 10;
  
    scoped_ptr<Layer> layer(
        CreateColorLayer(SK_ColorRED, gfx::Rect(viewport_size)));
    scoped_ptr<Layer> layer2(
        CreateColorLayer(SK_ColorBLUE,
                         gfx::Rect(0, 0, viewport_size.width(), blue_height)));
  
    layer->Add(layer2.get());
  
    DrawTree(layer.get());
  
    SkBitmap bitmap;
    ASSERT_TRUE(GetCompositor()->ReadPixels(&bitmap, gfx::Rect(viewport_size)));
    ASSERT_FALSE(bitmap.empty());
  
    SkAutoLockPixels lock(bitmap);
    for (int x = 0; x < viewport_size.width(); x++) {
      for (int y = 0; y < viewport_size.height(); y++) {
        SkColor actual_color = bitmap.getColor(x, y);
        SkColor expected_color = y < blue_height ? SK_ColorBLUE : SK_ColorRED;
        EXPECT_EQ(expected_color, actual_color)
            << "Pixel error at x=" << x << " y=" << y << "; "
            << "actual RGBA=("
            << SkColorGetR(actual_color) << ","
            << SkColorGetG(actual_color) << ","
            << SkColorGetB(actual_color) << ","
            << SkColorGetA(actual_color) << "); "
            << "expected RGBA=("
            << SkColorGetR(expected_color) << ","
            << SkColorGetG(expected_color) << ","
            << SkColorGetB(expected_color) << ","
            << SkColorGetA(expected_color) << ")";
      }
    }
  }
  
  // Checks the logic around Compositor::SetRootLayer and Layer::SetCompositor.
  TEST_F(LayerWithRealCompositorTest, SetRootLayer) {
    Compositor* compositor = GetCompositor();
    scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorRED,
                                          gfx::Rect(20, 20, 400, 400)));
    scoped_ptr<Layer> l2(CreateColorLayer(SK_ColorBLUE,
                                          gfx::Rect(10, 10, 350, 350)));
  
    EXPECT_EQ(NULL, l1->GetCompositor());
    EXPECT_EQ(NULL, l2->GetCompositor());
  
    compositor->SetRootLayer(l1.get());
    EXPECT_EQ(compositor, l1->GetCompositor());
  
    l1->Add(l2.get());
    EXPECT_EQ(compositor, l2->GetCompositor());
  
    l1->Remove(l2.get());
    EXPECT_EQ(NULL, l2->GetCompositor());
  
    l1->Add(l2.get());
    EXPECT_EQ(compositor, l2->GetCompositor());
  
    compositor->SetRootLayer(NULL);
    EXPECT_EQ(NULL, l1->GetCompositor());
    EXPECT_EQ(NULL, l2->GetCompositor());
  }
  
  // Checks that compositor observers are notified when:
  // - DrawTree is called,
  // - After ScheduleDraw is called, or
  // - Whenever SetBounds, SetOpacity or SetTransform are called.
  // TODO(vollick): could be reorganized into compositor_unittest.cc
  TEST_F(LayerWithRealCompositorTest, CompositorObservers) {
    scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorRED,
                                          gfx::Rect(20, 20, 400, 400)));
    scoped_ptr<Layer> l2(CreateColorLayer(SK_ColorBLUE,
                                          gfx::Rect(10, 10, 350, 350)));
    l1->Add(l2.get());
    TestCompositorObserver observer;
    GetCompositor()->AddObserver(&observer);
  
    // Explicitly called DrawTree should cause the observers to be notified.
    // NOTE: this call to DrawTree sets l1 to be the compositor's root layer.
    DrawTree(l1.get());
    EXPECT_TRUE(observer.notified());
  
    // ScheduleDraw without any visible change should cause a commit.
    observer.Reset();
    l1->ScheduleDraw();
    WaitForCommit();
    EXPECT_TRUE(observer.committed());
  
    // Moving, but not resizing, a layer should alert the observers.
    observer.Reset();
    l2->SetBounds(gfx::Rect(0, 0, 350, 350));
    WaitForDraw();
    EXPECT_TRUE(observer.notified());
  
    // So should resizing a layer.
    observer.Reset();
    l2->SetBounds(gfx::Rect(0, 0, 400, 400));
    WaitForDraw();
    EXPECT_TRUE(observer.notified());
  
    // Opacity changes should alert the observers.
    observer.Reset();
    l2->SetOpacity(0.5f);
    WaitForDraw();
    EXPECT_TRUE(observer.notified());
  
    // So should setting the opacity back.
    observer.Reset();
    l2->SetOpacity(1.0f);
    WaitForDraw();
    EXPECT_TRUE(observer.notified());
  
    // Setting the transform of a layer should alert the observers.
    observer.Reset();
    gfx::Transform transform;
    transform.Translate(200.0, 200.0);
    transform.Rotate(90.0);
    transform.Translate(-200.0, -200.0);
    l2->SetTransform(transform);
    WaitForDraw();
    EXPECT_TRUE(observer.notified());
  
    // A change resulting in an aborted swap buffer should alert the observer
    // and also signal an abort.
    observer.Reset();
    l2->SetOpacity(0.1f);
    GetCompositor()->DidAbortSwapBuffers();
    WaitForDraw();
    EXPECT_TRUE(observer.notified());
    EXPECT_TRUE(observer.aborted());
  
    GetCompositor()->RemoveObserver(&observer);
  
    // Opacity changes should no longer alert the removed observer.
    observer.Reset();
    l2->SetOpacity(0.5f);
    WaitForDraw();
  
    EXPECT_FALSE(observer.notified());
  }
  
  // Checks that modifying the hierarchy correctly affects final composite.
  TEST_F(LayerWithRealCompositorTest, ModifyHierarchy) {
    GetCompositor()->SetScaleAndSize(1.0f, gfx::Size(50, 50));
  
    // l0
    //  +-l11
    //  | +-l21
    //  +-l12
    scoped_ptr<Layer> l0(CreateColorLayer(SK_ColorRED,
                                          gfx::Rect(0, 0, 50, 50)));
    scoped_ptr<Layer> l11(CreateColorLayer(SK_ColorGREEN,
                                           gfx::Rect(0, 0, 25, 25)));
    scoped_ptr<Layer> l21(CreateColorLayer(SK_ColorMAGENTA,
                                           gfx::Rect(0, 0, 15, 15)));
    scoped_ptr<Layer> l12(CreateColorLayer(SK_ColorBLUE,
                                           gfx::Rect(10, 10, 25, 25)));
  
    base::FilePath ref_img1 =
        test_data_directory().AppendASCII("ModifyHierarchy1.png");
    base::FilePath ref_img2 =
        test_data_directory().AppendASCII("ModifyHierarchy2.png");
    SkBitmap bitmap;
  
    l0->Add(l11.get());
    l11->Add(l21.get());
    l0->Add(l12.get());
    DrawTree(l0.get());
    ASSERT_TRUE(ReadPixels(&bitmap));
    ASSERT_FALSE(bitmap.empty());
    // WritePNGFile(bitmap, ref_img1);
    EXPECT_TRUE(MatchesPNGFile(bitmap, ref_img1, cc::ExactPixelComparator(true)));
  
    l0->StackAtTop(l11.get());
    DrawTree(l0.get());
    ASSERT_TRUE(ReadPixels(&bitmap));
    ASSERT_FALSE(bitmap.empty());
    // WritePNGFile(bitmap, ref_img2);
    EXPECT_TRUE(MatchesPNGFile(bitmap, ref_img2, cc::ExactPixelComparator(true)));
  
    // should restore to original configuration
    l0->StackAbove(l12.get(), l11.get());
    DrawTree(l0.get());
    ASSERT_TRUE(ReadPixels(&bitmap));
    ASSERT_FALSE(bitmap.empty());
    EXPECT_TRUE(MatchesPNGFile(bitmap, ref_img1, cc::ExactPixelComparator(true)));
  
    // l11 back to front
    l0->StackAtTop(l11.get());
    DrawTree(l0.get());
    ASSERT_TRUE(ReadPixels(&bitmap));
    ASSERT_FALSE(bitmap.empty());
    EXPECT_TRUE(MatchesPNGFile(bitmap, ref_img2, cc::ExactPixelComparator(true)));
  
    // should restore to original configuration
    l0->StackAbove(l12.get(), l11.get());
    DrawTree(l0.get());
    ASSERT_TRUE(ReadPixels(&bitmap));
    ASSERT_FALSE(bitmap.empty());
    EXPECT_TRUE(MatchesPNGFile(bitmap, ref_img1, cc::ExactPixelComparator(true)));
  
    // l11 back to front
    l0->StackAbove(l11.get(), l12.get());
    DrawTree(l0.get());
    ASSERT_TRUE(ReadPixels(&bitmap));
    ASSERT_FALSE(bitmap.empty());
    EXPECT_TRUE(MatchesPNGFile(bitmap, ref_img2, cc::ExactPixelComparator(true)));
  }
  
  // Opacity is rendered correctly.
  // Checks that modifying the hierarchy correctly affects final composite.
  TEST_F(LayerWithRealCompositorTest, Opacity) {
    GetCompositor()->SetScaleAndSize(1.0f, gfx::Size(50, 50));
  
    // l0
    //  +-l11
    scoped_ptr<Layer> l0(CreateColorLayer(SK_ColorRED,
                                          gfx::Rect(0, 0, 50, 50)));
    scoped_ptr<Layer> l11(CreateColorLayer(SK_ColorGREEN,
                                           gfx::Rect(0, 0, 25, 25)));
  
    base::FilePath ref_img = test_data_directory().AppendASCII("Opacity.png");
  
    l11->SetOpacity(0.75);
    l0->Add(l11.get());
    DrawTree(l0.get());
    SkBitmap bitmap;
    ASSERT_TRUE(ReadPixels(&bitmap));
    ASSERT_FALSE(bitmap.empty());
    // WritePNGFile(bitmap, ref_img);
    EXPECT_TRUE(MatchesPNGFile(bitmap, ref_img, cc::ExactPixelComparator(true)));
  }
  
  namespace {
  
  class SchedulePaintLayerDelegate : public LayerDelegate {
   public:
    SchedulePaintLayerDelegate() : paint_count_(0), layer_(NULL) {}
  
    virtual ~SchedulePaintLayerDelegate() {}
  
    void set_layer(Layer* layer) {
      layer_ = layer;
      layer_->set_delegate(this);
    }
  
    void SetSchedulePaintRect(const gfx::Rect& rect) {
      schedule_paint_rect_ = rect;
    }
  
    int GetPaintCountAndClear() {
      int value = paint_count_;
      paint_count_ = 0;
      return value;
    }
  
    const gfx::RectF& last_clip_rect() const { return last_clip_rect_; }
  
   private:
    // Overridden from LayerDelegate:
    virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE {
      paint_count_++;
      if (!schedule_paint_rect_.IsEmpty()) {
        layer_->SchedulePaint(schedule_paint_rect_);
        schedule_paint_rect_ = gfx::Rect();
      }
      SkRect sk_clip_rect;
      if (canvas->sk_canvas()->getClipBounds(&sk_clip_rect))
        last_clip_rect_ = gfx::SkRectToRectF(sk_clip_rect);
    }
  
    virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {
    }
  
    virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE {
      return base::Closure();
    }
  
    int paint_count_;
    Layer* layer_;
    gfx::Rect schedule_paint_rect_;
    gfx::RectF last_clip_rect_;
  
    DISALLOW_COPY_AND_ASSIGN(SchedulePaintLayerDelegate);
  };
  
  }  // namespace
  
  // Verifies that if SchedulePaint is invoked during painting the layer is still
  // marked dirty.
  TEST_F(LayerWithDelegateTest, SchedulePaintFromOnPaintLayer) {
    scoped_ptr<Layer> root(CreateColorLayer(SK_ColorRED,
                                            gfx::Rect(0, 0, 500, 500)));
    SchedulePaintLayerDelegate child_delegate;
    scoped_ptr<Layer> child(CreateColorLayer(SK_ColorBLUE,
                                             gfx::Rect(0, 0, 200, 200)));
    child_delegate.set_layer(child.get());
  
    root->Add(child.get());
  
    SchedulePaintForLayer(root.get());
    DrawTree(root.get());
    child->SchedulePaint(gfx::Rect(0, 0, 20, 20));
    EXPECT_EQ(1, child_delegate.GetPaintCountAndClear());
  
    // Set a rect so that when OnPaintLayer() is invoked SchedulePaint is invoked
    // again.
    child_delegate.SetSchedulePaintRect(gfx::Rect(10, 10, 30, 30));
    WaitForCommit();
    EXPECT_EQ(1, child_delegate.GetPaintCountAndClear());
  
    // Because SchedulePaint() was invoked from OnPaintLayer() |child| should
    // still need to be painted.
    WaitForCommit();
    EXPECT_EQ(1, child_delegate.GetPaintCountAndClear());
    EXPECT_TRUE(child_delegate.last_clip_rect().Contains(
                    gfx::Rect(10, 10, 30, 30)));
  }
  
  TEST_F(LayerWithRealCompositorTest, ScaleUpDown) {
    scoped_ptr<Layer> root(CreateColorLayer(SK_ColorWHITE,
                                            gfx::Rect(10, 20, 200, 220)));
    TestLayerDelegate root_delegate;
    root_delegate.AddColor(SK_ColorWHITE);
    root->set_delegate(&root_delegate);
  
    scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorWHITE,
                                          gfx::Rect(10, 20, 140, 180)));
    TestLayerDelegate l1_delegate;
    l1_delegate.AddColor(SK_ColorWHITE);
    l1->set_delegate(&l1_delegate);
  
    GetCompositor()->SetScaleAndSize(1.0f, gfx::Size(500, 500));
    GetCompositor()->SetRootLayer(root.get());
    root->Add(l1.get());
    WaitForDraw();
  
    EXPECT_EQ("10,20 200x220", root->bounds().ToString());
    EXPECT_EQ("10,20 140x180", l1->bounds().ToString());
    gfx::Size size_in_pixel = root->cc_layer()->bounds();
    EXPECT_EQ("200x220", size_in_pixel.ToString());
    size_in_pixel = l1->cc_layer()->bounds();
    EXPECT_EQ("140x180", size_in_pixel.ToString());
    // No scale change, so no scale notification.
    EXPECT_EQ(0.0f, root_delegate.device_scale_factor());
    EXPECT_EQ(0.0f, l1_delegate.device_scale_factor());
  
    EXPECT_EQ("200x220", root_delegate.paint_size().ToString());
    EXPECT_EQ("140x180", l1_delegate.paint_size().ToString());
  
    // Scale up to 2.0. Changing scale doesn't change the bounds in DIP.
    GetCompositor()->SetScaleAndSize(2.0f, gfx::Size(500, 500));
    EXPECT_EQ("10,20 200x220", root->bounds().ToString());
    EXPECT_EQ("10,20 140x180", l1->bounds().ToString());
    // Pixel size must have been scaled up.
    size_in_pixel = root->cc_layer()->bounds();
    EXPECT_EQ("400x440", size_in_pixel.ToString());
    size_in_pixel = l1->cc_layer()->bounds();
    EXPECT_EQ("280x360", size_in_pixel.ToString());
    // New scale factor must have been notified.
    EXPECT_EQ(2.0f, root_delegate.device_scale_factor());
    EXPECT_EQ(2.0f, l1_delegate.device_scale_factor());
  
    // Canvas size must have been scaled down up.
    WaitForDraw();
    EXPECT_EQ("400x440", root_delegate.paint_size().ToString());
    EXPECT_EQ("2.0 2.0", root_delegate.ToScaleString());
    EXPECT_EQ("280x360", l1_delegate.paint_size().ToString());
    EXPECT_EQ("2.0 2.0", l1_delegate.ToScaleString());
  
    // Scale down back to 1.0f.
    GetCompositor()->SetScaleAndSize(1.0f, gfx::Size(500, 500));
    EXPECT_EQ("10,20 200x220", root->bounds().ToString());
    EXPECT_EQ("10,20 140x180", l1->bounds().ToString());
    // Pixel size must have been scaled down.
    size_in_pixel = root->cc_layer()->bounds();
    EXPECT_EQ("200x220", size_in_pixel.ToString());
    size_in_pixel = l1->cc_layer()->bounds();
    EXPECT_EQ("140x180", size_in_pixel.ToString());
    // New scale factor must have been notified.
    EXPECT_EQ(1.0f, root_delegate.device_scale_factor());
    EXPECT_EQ(1.0f, l1_delegate.device_scale_factor());
  
    // Canvas size must have been scaled down too.
    WaitForDraw();
    EXPECT_EQ("200x220", root_delegate.paint_size().ToString());
    EXPECT_EQ("1.0 1.0", root_delegate.ToScaleString());
    EXPECT_EQ("140x180", l1_delegate.paint_size().ToString());
    EXPECT_EQ("1.0 1.0", l1_delegate.ToScaleString());
  
    root_delegate.reset();
    l1_delegate.reset();
    // Just changing the size shouldn't notify the scale change nor
    // trigger repaint.
    GetCompositor()->SetScaleAndSize(1.0f, gfx::Size(1000, 1000));
    // No scale change, so no scale notification.
    EXPECT_EQ(0.0f, root_delegate.device_scale_factor());
    EXPECT_EQ(0.0f, l1_delegate.device_scale_factor());
    WaitForDraw();
    EXPECT_EQ("0x0", root_delegate.paint_size().ToString());
    EXPECT_EQ("0.0 0.0", root_delegate.ToScaleString());
    EXPECT_EQ("0x0", l1_delegate.paint_size().ToString());
    EXPECT_EQ("0.0 0.0", l1_delegate.ToScaleString());
  }
  
  TEST_F(LayerWithRealCompositorTest, ScaleReparent) {
    scoped_ptr<Layer> root(CreateColorLayer(SK_ColorWHITE,
                                            gfx::Rect(10, 20, 200, 220)));
    scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorWHITE,
                                          gfx::Rect(10, 20, 140, 180)));
    TestLayerDelegate l1_delegate;
    l1_delegate.AddColor(SK_ColorWHITE);
    l1->set_delegate(&l1_delegate);
  
    GetCompositor()->SetScaleAndSize(1.0f, gfx::Size(500, 500));
    GetCompositor()->SetRootLayer(root.get());
    WaitForDraw();
  
    root->Add(l1.get());
    EXPECT_EQ("10,20 140x180", l1->bounds().ToString());
    gfx::Size size_in_pixel = l1->cc_layer()->bounds();
    EXPECT_EQ("140x180", size_in_pixel.ToString());
    EXPECT_EQ(0.0f, l1_delegate.device_scale_factor());
  
    WaitForDraw();
    EXPECT_EQ("140x180", l1_delegate.paint_size().ToString());
    EXPECT_EQ("1.0 1.0", l1_delegate.ToScaleString());
  
    // Remove l1 from root and change the scale.
    root->Remove(l1.get());
    EXPECT_EQ(NULL, l1->parent());
    EXPECT_EQ(NULL, l1->GetCompositor());
    GetCompositor()->SetScaleAndSize(2.0f, gfx::Size(500, 500));
    // Sanity check on root and l1.
    EXPECT_EQ("10,20 200x220", root->bounds().ToString());
    size_in_pixel = l1->cc_layer()->bounds();
    EXPECT_EQ("140x180", size_in_pixel.ToString());
  
  
    root->Add(l1.get());
    EXPECT_EQ("10,20 140x180", l1->bounds().ToString());
    size_in_pixel = l1->cc_layer()->bounds();
    EXPECT_EQ("280x360", size_in_pixel.ToString());
    EXPECT_EQ(2.0f, l1_delegate.device_scale_factor());
    WaitForDraw();
    EXPECT_EQ("280x360", l1_delegate.paint_size().ToString());
    EXPECT_EQ("2.0 2.0", l1_delegate.ToScaleString());
  }
  
  // Tests layer::set_scale_content(false).
  TEST_F(LayerWithRealCompositorTest, NoScaleCanvas) {
    scoped_ptr<Layer> root(CreateColorLayer(SK_ColorWHITE,
                                            gfx::Rect(10, 20, 200, 220)));
    scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorWHITE,
                                          gfx::Rect(10, 20, 140, 180)));
    l1->set_scale_content(false);
    root->Add(l1.get());
    TestLayerDelegate l1_delegate;
    l1_delegate.AddColor(SK_ColorWHITE);
    l1->set_delegate(&l1_delegate);
  
    GetCompositor()->SetScaleAndSize(2.0f, gfx::Size(500, 500));
    GetCompositor()->SetRootLayer(root.get());
    // Scale factor change is notified regardless of scale_content flag.
    EXPECT_EQ(2.0f, l1_delegate.device_scale_factor());
  
    WaitForDraw();
    EXPECT_EQ("280x360", l1_delegate.paint_size().ToString());
    EXPECT_EQ("1.0 1.0", l1_delegate.ToScaleString());
  }
  
  // Verifies that when changing bounds on a layer that is invisible, and then
  // made visible, the right thing happens:
  // - if just a move, then no painting should happen.
  // - if a resize, the layer should be repainted.
  TEST_F(LayerWithDelegateTest, SetBoundsWhenInvisible) {
    scoped_ptr<Layer> root(CreateNoTextureLayer(gfx::Rect(0, 0, 1000, 1000)));
  
    scoped_ptr<Layer> child(CreateLayer(LAYER_TEXTURED));
    child->SetBounds(gfx::Rect(0, 0, 500, 500));
    DrawTreeLayerDelegate delegate;
    child->set_delegate(&delegate);
    root->Add(child.get());
  
    // Paint once for initial damage.
    child->SetVisible(true);
    DrawTree(root.get());
  
    // Reset into invisible state.
    child->SetVisible(false);
    DrawTree(root.get());
    delegate.Reset();
  
    // Move layer.
    child->SetBounds(gfx::Rect(200, 200, 500, 500));
    child->SetVisible(true);
    DrawTree(root.get());
    EXPECT_FALSE(delegate.painted());
  
    // Reset into invisible state.
    child->SetVisible(false);
    DrawTree(root.get());
    delegate.Reset();
  
    // Resize layer.
    child->SetBounds(gfx::Rect(200, 200, 400, 400));
    child->SetVisible(true);
    DrawTree(root.get());
    EXPECT_TRUE(delegate.painted());
  }
  
  static scoped_ptr<cc::DelegatedFrameData> MakeFrameData(gfx::Size size) {
    scoped_ptr<cc::DelegatedFrameData> frame_data(new cc::DelegatedFrameData);
    scoped_ptr<cc::RenderPass> render_pass(cc::RenderPass::Create());
    render_pass->SetNew(cc::RenderPass::Id(1, 1),
                        gfx::Rect(size),
                        gfx::RectF(),
                        gfx::Transform());
    frame_data->render_pass_list.push_back(render_pass.Pass());
    return frame_data.Pass();
  }
  
  TEST_F(LayerWithDelegateTest, DelegatedLayer) {
    scoped_ptr<Layer> root(CreateNoTextureLayer(gfx::Rect(0, 0, 1000, 1000)));
  
    scoped_ptr<Layer> child(CreateLayer(LAYER_TEXTURED));
  
    child->SetBounds(gfx::Rect(0, 0, 10, 10));
    child->SetVisible(true);
    root->Add(child.get());
    DrawTree(root.get());
  
    scoped_refptr<cc::DelegatedFrameResourceCollection> resource_collection =
        new cc::DelegatedFrameResourceCollection;
    scoped_refptr<cc::DelegatedFrameProvider> frame_provider;
  
    // Content matches layer size.
    frame_provider = new cc::DelegatedFrameProvider(
        resource_collection.get(), MakeFrameData(gfx::Size(10, 10)));
    child->SetShowDelegatedContent(frame_provider, gfx::Size(10, 10));
    EXPECT_EQ(child->cc_layer()->bounds().ToString(),
              gfx::Size(10, 10).ToString());
  
    // Content larger than layer.
    child->SetBounds(gfx::Rect(0, 0, 5, 5));
    EXPECT_EQ(child->cc_layer()->bounds().ToString(),
              gfx::Size(5, 5).ToString());
  
    // Content smaller than layer.
    child->SetBounds(gfx::Rect(0, 0, 10, 10));
    frame_provider = new cc::DelegatedFrameProvider(
        resource_collection.get(), MakeFrameData(gfx::Size(5, 5)));
    child->SetShowDelegatedContent(frame_provider, gfx::Size(5, 5));
    EXPECT_EQ(child->cc_layer()->bounds().ToString(), gfx::Size(5, 5).ToString());
  
    // Hi-DPI content on low-DPI layer.
    frame_provider = new cc::DelegatedFrameProvider(
        resource_collection.get(), MakeFrameData(gfx::Size(20, 20)));
    child->SetShowDelegatedContent(frame_provider, gfx::Size(10, 10));
    EXPECT_EQ(child->cc_layer()->bounds().ToString(),
              gfx::Size(10, 10).ToString());
  
    // Hi-DPI content on hi-DPI layer.
    compositor()->SetScaleAndSize(2.f, gfx::Size(1000, 1000));
    EXPECT_EQ(child->cc_layer()->bounds().ToString(),
              gfx::Size(20, 20).ToString());
  
    // Low-DPI content on hi-DPI layer.
    frame_provider = new cc::DelegatedFrameProvider(
        resource_collection.get(), MakeFrameData(gfx::Size(10, 10)));
    child->SetShowDelegatedContent(frame_provider, gfx::Size(10, 10));
    EXPECT_EQ(child->cc_layer()->bounds().ToString(),
              gfx::Size(20, 20).ToString());
  }
  
  TEST_F(LayerWithDelegateTest, ExternalContent) {
    scoped_ptr<Layer> root(CreateNoTextureLayer(gfx::Rect(0, 0, 1000, 1000)));
    scoped_ptr<Layer> child(CreateLayer(LAYER_TEXTURED));
  
    child->SetBounds(gfx::Rect(0, 0, 10, 10));
    child->SetVisible(true);
    root->Add(child.get());
  
    // The layer is already showing painted content, so the cc layer won't change.
    scoped_refptr<cc::Layer> before = child->cc_layer();
    child->SetShowPaintedContent();
    EXPECT_TRUE(child->cc_layer());
    EXPECT_EQ(before, child->cc_layer());
  
    scoped_refptr<cc::DelegatedFrameResourceCollection> resource_collection =
        new cc::DelegatedFrameResourceCollection;
    scoped_refptr<cc::DelegatedFrameProvider> frame_provider =
        new cc::DelegatedFrameProvider(resource_collection.get(),
                                       MakeFrameData(gfx::Size(10, 10)));
  
    // Showing delegated content changes the underlying cc layer.
    before = child->cc_layer();
    child->SetShowDelegatedContent(frame_provider, gfx::Size(10, 10));
    EXPECT_TRUE(child->cc_layer());
    EXPECT_NE(before, child->cc_layer());
  
    // Changing to painted content should change the underlying cc layer.
    before = child->cc_layer();
    child->SetShowPaintedContent();
    EXPECT_TRUE(child->cc_layer());
    EXPECT_NE(before, child->cc_layer());
  }
  
  // Tests Layer::AddThreadedAnimation and Layer::RemoveThreadedAnimation.
  TEST_F(LayerWithRealCompositorTest, AddRemoveThreadedAnimations) {
    scoped_ptr<Layer> root(CreateLayer(LAYER_TEXTURED));
    scoped_ptr<Layer> l1(CreateLayer(LAYER_TEXTURED));
    scoped_ptr<Layer> l2(CreateLayer(LAYER_TEXTURED));
  
    l1->SetAnimator(LayerAnimator::CreateImplicitAnimator());
    l2->SetAnimator(LayerAnimator::CreateImplicitAnimator());
  
    EXPECT_FALSE(l1->HasPendingThreadedAnimations());
  
    // Trigger a threaded animation.
    l1->SetOpacity(0.5f);
  
    EXPECT_TRUE(l1->HasPendingThreadedAnimations());
  
    // Ensure we can remove a pending threaded animation.
    l1->GetAnimator()->StopAnimating();
  
    EXPECT_FALSE(l1->HasPendingThreadedAnimations());
  
    // Trigger another threaded animation.
    l1->SetOpacity(0.2f);
  
    EXPECT_TRUE(l1->HasPendingThreadedAnimations());
  
    root->Add(l1.get());
    GetCompositor()->SetRootLayer(root.get());
  
    // Now that l1 is part of a tree, it should have dispatched the pending
    // animation.
    EXPECT_FALSE(l1->HasPendingThreadedAnimations());
  
    // Ensure that l1 no longer holds on to animations.
    l1->SetOpacity(0.1f);
    EXPECT_FALSE(l1->HasPendingThreadedAnimations());
  
    // Ensure that adding a layer to an existing tree causes its pending
    // animations to get dispatched.
    l2->SetOpacity(0.5f);
    EXPECT_TRUE(l2->HasPendingThreadedAnimations());
  
    l1->Add(l2.get());
    EXPECT_FALSE(l2->HasPendingThreadedAnimations());
  }
  
  // Tests that in-progress threaded animations complete when a Layer's
  // cc::Layer changes.
  TEST_F(LayerWithRealCompositorTest, SwitchCCLayerAnimations) {
    scoped_ptr<Layer> root(CreateLayer(LAYER_TEXTURED));
    scoped_ptr<Layer> l1(CreateLayer(LAYER_TEXTURED));
    GetCompositor()->SetRootLayer(root.get());
    root->Add(l1.get());
  
    l1->SetAnimator(LayerAnimator::CreateImplicitAnimator());
  
    EXPECT_FLOAT_EQ(l1->opacity(), 1.0f);
  
    // Trigger a threaded animation.
    l1->SetOpacity(0.5f);
  
    // Change l1's cc::Layer.
    l1->SwitchCCLayerForTest();
  
    // Ensure that the opacity animation completed.
    EXPECT_FLOAT_EQ(l1->opacity(), 0.5f);
  }
  
  }  // namespace ui
  