text: // Copyright 2011 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 "cc/scheduler/scheduler.h"
  
  #include <string>
  #include <vector>
  
  #include "base/logging.h"
  #include "cc/test/scheduler_test_common.h"
  #include "testing/gmock/include/gmock/gmock.h"
  #include "testing/gtest/include/gtest/gtest.h"
  
  #define EXPECT_ACTION(action, client, action_index, expected_num_actions) \
    EXPECT_EQ(expected_num_actions, client.num_actions_());                 \
    ASSERT_LT(action_index, client.num_actions_());                         \
    do {                                                                    \
      EXPECT_STREQ(action, client.Action(action_index));                    \
      for (int i = expected_num_actions; i < client.num_actions_(); ++i)    \
        ADD_FAILURE() << "Unexpected action: " << client.Action(i) <<       \
            " with state:\n" << client.StateForAction(action_index);        \
    } while (false)
  
  #define EXPECT_SINGLE_ACTION(action, client) \
    EXPECT_ACTION(action, client, 0, 1)
  
  namespace cc {
  namespace {
  
  class FakeSchedulerClient : public SchedulerClient {
   public:
    FakeSchedulerClient()
    : needs_begin_frame_(false) {
      Reset();
    }
  
    void Reset() {
      actions_.clear();
      states_.clear();
      draw_will_happen_ = true;
      swap_will_happen_if_draw_happens_ = true;
      num_draws_ = 0;
    }
  
    Scheduler* CreateScheduler(const SchedulerSettings& settings) {
      scheduler_ = Scheduler::Create(this, settings);
      return scheduler_.get();
    }
  
    bool needs_begin_frame() { return needs_begin_frame_; }
    int num_draws() const { return num_draws_; }
    int num_actions_() const { return static_cast<int>(actions_.size()); }
    const char* Action(int i) const { return actions_[i]; }
    std::string StateForAction(int i) const { return states_[i]; }
  
    bool HasAction(const char* action) const {
      for (size_t i = 0; i < actions_.size(); i++)
        if (!strcmp(actions_[i], action))
          return true;
      return false;
    }
  
    void SetDrawWillHappen(bool draw_will_happen) {
      draw_will_happen_ = draw_will_happen;
    }
    void SetSwapWillHappenIfDrawHappens(bool swap_will_happen_if_draw_happens) {
      swap_will_happen_if_draw_happens_ = swap_will_happen_if_draw_happens;
    }
  
    // Scheduler Implementation.
    virtual void SetNeedsBeginFrameOnImplThread(bool enable) OVERRIDE {
      actions_.push_back("SetNeedsBeginFrameOnImplThread");
      states_.push_back(scheduler_->StateAsStringForTesting());
      needs_begin_frame_ = enable;
    }
    virtual void ScheduledActionSendBeginFrameToMainThread() OVERRIDE {
      actions_.push_back("ScheduledActionSendBeginFrameToMainThread");
      states_.push_back(scheduler_->StateAsStringForTesting());
    }
    virtual ScheduledActionDrawAndSwapResult
    ScheduledActionDrawAndSwapIfPossible() OVERRIDE {
      actions_.push_back("ScheduledActionDrawAndSwapIfPossible");
      states_.push_back(scheduler_->StateAsStringForTesting());
      num_draws_++;
      return ScheduledActionDrawAndSwapResult(draw_will_happen_,
                                              draw_will_happen_ &&
                                              swap_will_happen_if_draw_happens_);
    }
    virtual ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapForced()
        OVERRIDE {
      actions_.push_back("ScheduledActionDrawAndSwapForced");
      states_.push_back(scheduler_->StateAsStringForTesting());
      return ScheduledActionDrawAndSwapResult(true,
                                              swap_will_happen_if_draw_happens_);
    }
    virtual void ScheduledActionCommit() OVERRIDE {
      actions_.push_back("ScheduledActionCommit");
      states_.push_back(scheduler_->StateAsStringForTesting());
    }
    virtual void ScheduledActionUpdateVisibleTiles() OVERRIDE {
      actions_.push_back("ScheduledActionUpdateVisibleTiles");
      states_.push_back(scheduler_->StateAsStringForTesting());
    }
    virtual void ScheduledActionActivatePendingTreeIfNeeded() OVERRIDE {
      actions_.push_back("ScheduledActionActivatePendingTreeIfNeeded");
      states_.push_back(scheduler_->StateAsStringForTesting());
    }
    virtual void ScheduledActionBeginOutputSurfaceCreation() OVERRIDE {
      actions_.push_back("ScheduledActionBeginOutputSurfaceCreation");
      states_.push_back(scheduler_->StateAsStringForTesting());
    }
    virtual void ScheduledActionAcquireLayerTexturesForMainThread() OVERRIDE {
      actions_.push_back("ScheduledActionAcquireLayerTexturesForMainThread");
      states_.push_back(scheduler_->StateAsStringForTesting());
    }
    virtual void DidAnticipatedDrawTimeChange(base::TimeTicks) OVERRIDE {}
    virtual base::TimeDelta DrawDurationEstimate() OVERRIDE {
      return base::TimeDelta();
    }
    virtual base::TimeDelta BeginFrameToCommitDurationEstimate() OVERRIDE {
      return base::TimeDelta();
    }
    virtual base::TimeDelta CommitToActivateDurationEstimate() OVERRIDE {
      return base::TimeDelta();
    }
  
   protected:
    bool needs_begin_frame_;
    bool draw_will_happen_;
    bool swap_will_happen_if_draw_happens_;
    int num_draws_;
    std::vector<const char*> actions_;
    std::vector<std::string> states_;
    scoped_ptr<Scheduler> scheduler_;
  };
  
  TEST(SchedulerTest, InitializeOutputSurfaceDoesNotBeginFrame) {
    FakeSchedulerClient client;
    SchedulerSettings default_scheduler_settings;
    Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
    scheduler->SetCanStart();
    scheduler->SetVisible(true);
    scheduler->SetCanDraw(true);
  
    EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
    client.Reset();
    scheduler->DidCreateAndInitializeOutputSurface();
    EXPECT_EQ(0, client.num_actions_());
  }
  
  TEST(SchedulerTest, RequestCommit) {
    FakeSchedulerClient client;
    SchedulerSettings default_scheduler_settings;
    Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
    scheduler->SetCanStart();
    scheduler->SetVisible(true);
    scheduler->SetCanDraw(true);
  
    EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
    client.Reset();
    scheduler->DidCreateAndInitializeOutputSurface();
  
    // SetNeedsCommit should begin the frame.
    scheduler->SetNeedsCommit();
    EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
    EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
    EXPECT_TRUE(client.needs_begin_frame());
    client.Reset();
  
    // FinishCommit should commit
    scheduler->FinishCommit();
    EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
    EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
    EXPECT_TRUE(client.needs_begin_frame());
    client.Reset();
  
    // BeginFrame should draw.
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
    EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
    EXPECT_FALSE(client.needs_begin_frame());
    client.Reset();
  }
  
  TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread) {
    FakeSchedulerClient client;
    SchedulerSettings default_scheduler_settings;
    Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
    scheduler->SetCanStart();
    scheduler->SetVisible(true);
    scheduler->SetCanDraw(true);
  
    EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
    client.Reset();
    scheduler->DidCreateAndInitializeOutputSurface();
  
    // SetNedsCommit should begin the frame.
    scheduler->SetNeedsCommit();
    EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
    EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
    client.Reset();
  
    // Now SetNeedsCommit again. Calling here means we need a second frame.
    scheduler->SetNeedsCommit();
    EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 0, 1);
    client.Reset();
  
    // Since another commit is needed, FinishCommit should commit,
    // then begin another frame.
    scheduler->FinishCommit();
    EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
    EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
    client.Reset();
  
    // Tick should draw but then begin another frame.
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_TRUE(client.needs_begin_frame());
    EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
    EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 2);
    client.Reset();
  
    // Go back to quiescent state and verify we no longer request BeginFrames.
    scheduler->FinishCommit();
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_FALSE(client.needs_begin_frame());
  }
  
  TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw) {
    FakeSchedulerClient client;
    SchedulerSettings default_scheduler_settings;
    Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
    scheduler->SetCanStart();
    scheduler->SetVisible(true);
    scheduler->SetCanDraw(true);
    EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
  
    client.Reset();
    scheduler->DidCreateAndInitializeOutputSurface();
    scheduler->SetNeedsRedraw();
    EXPECT_TRUE(scheduler->RedrawPending());
    EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
    EXPECT_TRUE(client.needs_begin_frame());
  
    client.Reset();
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
    EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
    EXPECT_FALSE(scheduler->RedrawPending());
    EXPECT_FALSE(client.needs_begin_frame());
  
    client.Reset();
    scheduler->SetMainThreadNeedsLayerTextures();
    EXPECT_SINGLE_ACTION("ScheduledActionAcquireLayerTexturesForMainThread",
                         client);
  
    // We should request a BeginFrame in anticipation of a draw.
    client.Reset();
    scheduler->SetNeedsRedraw();
    EXPECT_TRUE(scheduler->RedrawPending());
    EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
    EXPECT_TRUE(client.needs_begin_frame());
  
    // No draw happens since the textures are acquired by the main thread.
    client.Reset();
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
    EXPECT_TRUE(scheduler->RedrawPending());
    EXPECT_TRUE(client.needs_begin_frame());
  
    scheduler->SetNeedsCommit();
    EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 0, 2);
    EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 2);
    EXPECT_TRUE(client.needs_begin_frame());
  
    // Commit will release the texture.
    client.Reset();
    scheduler->FinishCommit();
    EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
    EXPECT_TRUE(scheduler->RedrawPending());
    EXPECT_TRUE(client.needs_begin_frame());
  
    // Now we can draw again after the commit happens.
    client.Reset();
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
    EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
    EXPECT_FALSE(scheduler->RedrawPending());
    EXPECT_FALSE(client.needs_begin_frame());
    client.Reset();
  }
  
  TEST(SchedulerTest, TextureAcquisitionCollision) {
    FakeSchedulerClient client;
    SchedulerSettings default_scheduler_settings;
    Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
    scheduler->SetCanStart();
    scheduler->SetVisible(true);
    scheduler->SetCanDraw(true);
  
    EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
    client.Reset();
    scheduler->DidCreateAndInitializeOutputSurface();
  
    scheduler->SetNeedsCommit();
    scheduler->SetMainThreadNeedsLayerTextures();
    EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 4);
    EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 4);
    EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread",
                  client,
                  2,
                  4);
    EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 3, 4);
    client.Reset();
  
    // Although the compositor cannot draw because textures are locked by main
    // thread, we continue requesting SetNeedsBeginFrame in anticipation of the
    // unlock.
    EXPECT_TRUE(client.needs_begin_frame());
  
    // Trigger the commit
    scheduler->FinishCommit();
    EXPECT_TRUE(client.needs_begin_frame());
    client.Reset();
  
    // Between commit and draw, texture acquisition for main thread delayed,
    // and main thread blocks.
    scheduler->SetMainThreadNeedsLayerTextures();
    EXPECT_EQ(0, client.num_actions_());
    client.Reset();
  
    // No implicit commit is expected.
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 3);
    EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread",
                  client,
                  1,
                  3);
    EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 2, 3);
    client.Reset();
  
    // Compositor not scheduled to draw because textures are locked by main
    // thread.
    EXPECT_FALSE(client.needs_begin_frame());
  
    // Needs an explicit commit from the main thread.
    scheduler->SetNeedsCommit();
    EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
    EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
    client.Reset();
  
    // Trigger the commit
    scheduler->FinishCommit();
    EXPECT_TRUE(client.needs_begin_frame());
  }
  
  TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition) {
    FakeSchedulerClient client;
    SchedulerSettings default_scheduler_settings;
    Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
    scheduler->SetCanStart();
    scheduler->SetVisible(true);
    scheduler->SetCanDraw(true);
  
    EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
    client.Reset();
    scheduler->DidCreateAndInitializeOutputSurface();
  
    scheduler->SetNeedsCommit();
    scheduler->FinishCommit();
    scheduler->SetMainThreadNeedsLayerTextures();
    scheduler->SetNeedsCommit();
    client.Reset();
    // Verify that pending texture acquisition fires when visibility
    // is lost in order to avoid a deadlock.
    scheduler->SetVisible(false);
    EXPECT_SINGLE_ACTION("ScheduledActionAcquireLayerTexturesForMainThread",
                         client);
    client.Reset();
  
    // Already sent a begin frame on this current frame, so wait.
    scheduler->SetVisible(true);
    EXPECT_EQ(0, client.num_actions_());
    client.Reset();
  
    // Regaining visibility with textures acquired by main thread while
    // compositor is waiting for first draw should result in a request
    // for a new frame in order to escape a deadlock.
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
    EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
  }
  
  class SchedulerClientThatsetNeedsDrawInsideDraw : public FakeSchedulerClient {
   public:
    virtual void ScheduledActionSendBeginFrameToMainThread() OVERRIDE {}
    virtual ScheduledActionDrawAndSwapResult
    ScheduledActionDrawAndSwapIfPossible() OVERRIDE {
      // Only SetNeedsRedraw the first time this is called
      if (!num_draws_)
        scheduler_->SetNeedsRedraw();
      return FakeSchedulerClient::ScheduledActionDrawAndSwapIfPossible();
    }
  
    virtual ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapForced()
        OVERRIDE {
      NOTREACHED();
      return ScheduledActionDrawAndSwapResult(true, true);
    }
  
    virtual void ScheduledActionCommit() OVERRIDE {}
    virtual void ScheduledActionBeginOutputSurfaceCreation() OVERRIDE {}
    virtual void DidAnticipatedDrawTimeChange(base::TimeTicks) OVERRIDE {}
  };
  
  // Tests for two different situations:
  // 1. the scheduler dropping SetNeedsRedraw requests that happen inside
  //    a ScheduledActionDrawAndSwap
  // 2. the scheduler drawing twice inside a single tick
  TEST(SchedulerTest, RequestRedrawInsideDraw) {
    SchedulerClientThatsetNeedsDrawInsideDraw client;
    SchedulerSettings default_scheduler_settings;
    Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
    scheduler->SetCanStart();
    scheduler->SetVisible(true);
    scheduler->SetCanDraw(true);
    scheduler->DidCreateAndInitializeOutputSurface();
  
    scheduler->SetNeedsRedraw();
    EXPECT_TRUE(scheduler->RedrawPending());
    EXPECT_TRUE(client.needs_begin_frame());
    EXPECT_EQ(0, client.num_draws());
  
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_EQ(1, client.num_draws());
    EXPECT_TRUE(scheduler->RedrawPending());
    EXPECT_TRUE(client.needs_begin_frame());
  
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_EQ(2, client.num_draws());
    EXPECT_FALSE(scheduler->RedrawPending());
    EXPECT_FALSE(client.needs_begin_frame());
  }
  
  // Test that requesting redraw inside a failed draw doesn't lose the request.
  TEST(SchedulerTest, RequestRedrawInsideFailedDraw) {
    SchedulerClientThatsetNeedsDrawInsideDraw client;
    SchedulerSettings default_scheduler_settings;
    Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
    scheduler->SetCanStart();
    scheduler->SetVisible(true);
    scheduler->SetCanDraw(true);
    scheduler->DidCreateAndInitializeOutputSurface();
  
    client.SetDrawWillHappen(false);
  
    scheduler->SetNeedsRedraw();
    EXPECT_TRUE(scheduler->RedrawPending());
    EXPECT_TRUE(client.needs_begin_frame());
    EXPECT_EQ(0, client.num_draws());
  
    // Fail the draw.
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_EQ(1, client.num_draws());
  
    // We have a commit pending and the draw failed, and we didn't lose the redraw
    // request.
    EXPECT_TRUE(scheduler->CommitPending());
    EXPECT_TRUE(scheduler->RedrawPending());
    EXPECT_TRUE(client.needs_begin_frame());
  
    // Fail the draw again.
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_EQ(2, client.num_draws());
    EXPECT_TRUE(scheduler->CommitPending());
    EXPECT_TRUE(scheduler->RedrawPending());
    EXPECT_TRUE(client.needs_begin_frame());
  
    // Draw successfully.
    client.SetDrawWillHappen(true);
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_EQ(3, client.num_draws());
    EXPECT_TRUE(scheduler->CommitPending());
    EXPECT_FALSE(scheduler->RedrawPending());
    EXPECT_TRUE(client.needs_begin_frame());
  }
  
  class SchedulerClientThatsetNeedsCommitInsideDraw : public FakeSchedulerClient {
   public:
    virtual void ScheduledActionSendBeginFrameToMainThread() OVERRIDE {}
    virtual ScheduledActionDrawAndSwapResult
    ScheduledActionDrawAndSwapIfPossible() OVERRIDE {
      // Only SetNeedsCommit the first time this is called
      if (!num_draws_)
        scheduler_->SetNeedsCommit();
      return FakeSchedulerClient::ScheduledActionDrawAndSwapIfPossible();
    }
  
    virtual ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapForced()
        OVERRIDE {
      NOTREACHED();
      return ScheduledActionDrawAndSwapResult(true, true);
    }
  
    virtual void ScheduledActionCommit() OVERRIDE {}
    virtual void ScheduledActionBeginOutputSurfaceCreation() OVERRIDE {}
    virtual void DidAnticipatedDrawTimeChange(base::TimeTicks) OVERRIDE {}
  };
  
  // Tests for the scheduler infinite-looping on SetNeedsCommit requests that
  // happen inside a ScheduledActionDrawAndSwap
  TEST(SchedulerTest, RequestCommitInsideDraw) {
    SchedulerClientThatsetNeedsCommitInsideDraw client;
    SchedulerSettings default_scheduler_settings;
    Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
    scheduler->SetCanStart();
    scheduler->SetVisible(true);
    scheduler->SetCanDraw(true);
    scheduler->DidCreateAndInitializeOutputSurface();
  
    scheduler->SetNeedsRedraw();
    EXPECT_TRUE(scheduler->RedrawPending());
    EXPECT_EQ(0, client.num_draws());
    EXPECT_TRUE(client.needs_begin_frame());
  
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_EQ(1, client.num_draws());
    EXPECT_TRUE(scheduler->CommitPending());
    EXPECT_TRUE(client.needs_begin_frame());
    scheduler->FinishCommit();
  
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_EQ(2, client.num_draws());;
    EXPECT_FALSE(scheduler->RedrawPending());
    EXPECT_FALSE(scheduler->CommitPending());
    EXPECT_FALSE(client.needs_begin_frame());
  }
  
  // Tests that when a draw fails then the pending commit should not be dropped.
  TEST(SchedulerTest, RequestCommitInsideFailedDraw) {
    SchedulerClientThatsetNeedsDrawInsideDraw client;
    SchedulerSettings default_scheduler_settings;
    Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
    scheduler->SetCanStart();
    scheduler->SetVisible(true);
    scheduler->SetCanDraw(true);
    scheduler->DidCreateAndInitializeOutputSurface();
  
    client.SetDrawWillHappen(false);
  
    scheduler->SetNeedsRedraw();
    EXPECT_TRUE(scheduler->RedrawPending());
    EXPECT_TRUE(client.needs_begin_frame());
    EXPECT_EQ(0, client.num_draws());
  
    // Fail the draw.
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_EQ(1, client.num_draws());
  
    // We have a commit pending and the draw failed, and we didn't lose the commit
    // request.
    EXPECT_TRUE(scheduler->CommitPending());
    EXPECT_TRUE(scheduler->RedrawPending());
    EXPECT_TRUE(client.needs_begin_frame());
  
    // Fail the draw again.
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_EQ(2, client.num_draws());
    EXPECT_TRUE(scheduler->CommitPending());
    EXPECT_TRUE(scheduler->RedrawPending());
    EXPECT_TRUE(client.needs_begin_frame());
  
    // Draw successfully.
    client.SetDrawWillHappen(true);
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_EQ(3, client.num_draws());
    EXPECT_TRUE(scheduler->CommitPending());
    EXPECT_FALSE(scheduler->RedrawPending());
    EXPECT_TRUE(client.needs_begin_frame());
  }
  
  TEST(SchedulerTest, NoSwapWhenDrawFails) {
    SchedulerClientThatsetNeedsCommitInsideDraw client;
    SchedulerSettings default_scheduler_settings;
    Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
    scheduler->SetCanStart();
    scheduler->SetVisible(true);
    scheduler->SetCanDraw(true);
    scheduler->DidCreateAndInitializeOutputSurface();
  
    scheduler->SetNeedsRedraw();
    EXPECT_TRUE(scheduler->RedrawPending());
    EXPECT_TRUE(client.needs_begin_frame());
    EXPECT_EQ(0, client.num_draws());
  
    // Draw successfully, this starts a new frame.
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_EQ(1, client.num_draws());
  
    scheduler->SetNeedsRedraw();
    EXPECT_TRUE(scheduler->RedrawPending());
    EXPECT_TRUE(client.needs_begin_frame());
  
    // Fail to draw, this should not start a frame.
    client.SetDrawWillHappen(false);
    scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
    EXPECT_EQ(2, client.num_draws());
  }
  
  TEST(SchedulerTest, NoSwapWhenSwapFailsDuringForcedCommit) {
    FakeSchedulerClient client;
    SchedulerSettings default_scheduler_settings;
    Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
  
    // Tell the client that it will fail to swap.
    client.SetDrawWillHappen(true);
    client.SetSwapWillHappenIfDrawHappens(false);
  
    // Get the compositor to do a ScheduledActionDrawAndSwapForced.
    scheduler->SetCanDraw(true);
    scheduler->SetNeedsRedraw();
    scheduler->SetNeedsForcedRedraw();
    EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapForced"));
  }
  
  }  // namespace
  }  // namespace cc
  