/*
 * Copyright (C) 2011 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"

#include "cc/CCScheduler.h"

#include "CCSchedulerTestCommon.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <wtf/OwnPtr.h>

using namespace WTF;
using namespace WebCore;
using namespace WebKitTests;

namespace {

class FakeCCSchedulerClient : public CCSchedulerClient {
public:
    FakeCCSchedulerClient() { reset(); }
    void reset()
    {
        m_actions.clear();
        m_hasMoreResourceUpdates = false;
    }

    void setHasMoreResourceUpdates(bool b) { m_hasMoreResourceUpdates = b; }

    int numActions() const { return static_cast<int>(m_actions.size()); }
    const char* action(int i) const { return m_actions[i]; }

    virtual bool hasMoreResourceUpdates() const { return m_hasMoreResourceUpdates; }
    virtual void scheduledActionBeginFrame() { m_actions.push_back("scheduledActionBeginFrame"); }
    virtual void scheduledActionDrawAndSwap() { m_actions.push_back("scheduledActionDrawAndSwap"); }
    virtual void scheduledActionUpdateMoreResources() { m_actions.push_back("scheduledActionUpdateMoreResources"); }
    virtual void scheduledActionCommit() { m_actions.push_back("scheduledActionCommit"); }

protected:
    bool m_hasMoreResourceUpdates;
    std::vector<const char*> m_actions;
};

TEST(CCSchedulerTest, RequestCommit)
{
    FakeCCSchedulerClient client;
    RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource());
    OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource)));
    scheduler->setVisible(true);

    // SetNeedsCommit should begin the frame.
    scheduler->setNeedsCommit();
    EXPECT_EQ(1, client.numActions());
    EXPECT_STREQ("scheduledActionBeginFrame", client.action(0));
    client.reset();

    // Since, hasMoreResourceUpdates is set to false,
    // beginFrameComplete should updateMoreResources, then
    // commit
    scheduler->beginFrameComplete();
    EXPECT_EQ(2, client.numActions());
    EXPECT_STREQ("scheduledActionUpdateMoreResources", client.action(0));
    EXPECT_STREQ("scheduledActionCommit", client.action(1));
    client.reset();

    // Tick should draw.
    timeSource->tick();
    EXPECT_EQ(1, client.numActions());
    EXPECT_STREQ("scheduledActionDrawAndSwap", client.action(0));
    client.reset();

    // Tick should do nothing.
    timeSource->tick();
    EXPECT_EQ(0, client.numActions());
}

TEST(CCSchedulerTest, RequestCommitAfterBeginFrame)
{
    FakeCCSchedulerClient client;
    RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource());
    OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource)));
    scheduler->setVisible(true);

    // SetNedsCommit should begin the frame.
    scheduler->setNeedsCommit();
    EXPECT_EQ(1, client.numActions());
    EXPECT_STREQ("scheduledActionBeginFrame", client.action(0));
    client.reset();

    // Now setNeedsCommit again. Calling here means we need a second frame.
    scheduler->setNeedsCommit();

    // Since, hasMoreResourceUpdates is set to false, and another commit is
    // needed, beginFrameComplete should updateMoreResources, then commit, then
    // begin another frame.
    scheduler->beginFrameComplete();
    EXPECT_EQ(2, client.numActions());
    EXPECT_STREQ("scheduledActionUpdateMoreResources", client.action(0));
    EXPECT_STREQ("scheduledActionCommit", client.action(1));
    client.reset();

    // Tick should draw but then begin another frame.
    timeSource->tick();
    EXPECT_EQ(2, client.numActions());
    EXPECT_STREQ("scheduledActionDrawAndSwap", client.action(0));
    EXPECT_STREQ("scheduledActionBeginFrame", client.action(1));
    client.reset();
}

TEST(CCSchedulerTest, RequestRedraw)
{
}

TEST(CCSchedulerTest, RequestRedrawWithTwoFramesPending)
{
}

}
