/*
 * Copyright (C) 2012 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 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 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 "core/platform/graphics/chromium/ImageFrameGenerator.h"

#include "core/platform/SharedBuffer.h"
#include "core/platform/graphics/chromium/ImageDecodingStore.h"
#include "core/platform/graphics/chromium/test/MockImageDecoder.h"
#include "wtf/Threading.h"
#include <gtest/gtest.h>

namespace WebCore {

namespace {

// Helper methods to generate standard sizes.
SkISize fullSize() { return SkISize::Make(100, 100); }
SkISize scaledSize() { return SkISize::Make(50, 50); }

} // namespace

class ImageFrameGeneratorTest : public ::testing::Test, public MockImageDecoderClient {
public:
    virtual void SetUp() OVERRIDE
    {
        ImageDecodingStore::initializeOnce();
        m_data = SharedBuffer::create();
        m_generator = ImageFrameGenerator::create(fullSize(), m_data, true);
        useMockImageDecoderFactory();
        m_decodersDestroyed = 0;
        m_frameBufferRequestCount = 0;
        m_status = ImageFrame::FrameEmpty;
    }

    virtual void TearDown() OVERRIDE
    {
        ImageDecodingStore::shutdown();
    }

    virtual void decoderBeingDestroyed() OVERRIDE
    {
        ++m_decodersDestroyed;
    }

    virtual void frameBufferRequested() OVERRIDE
    {
        ++m_frameBufferRequestCount;
    }

    virtual ImageFrame::Status status() OVERRIDE
    {
        ImageFrame::Status currentStatus = m_status;
        m_status = m_nextFrameStatus;
        return currentStatus;
    }

    virtual size_t frameCount() OVERRIDE { return 1; }
    virtual int repetitionCount() const OVERRIDE { return cAnimationNone; }
    virtual float frameDuration() const OVERRIDE { return 0; }

protected:
    void useMockImageDecoderFactory()
    {
        m_generator->setImageDecoderFactory(MockImageDecoderFactory::create(this, fullSize()));
    }

    PassOwnPtr<ScaledImageFragment> createCompleteImage(const SkISize& size)
    {
        SkBitmap bitmap;
        bitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height());
        bitmap.allocPixels();
        return ScaledImageFragment::createComplete(size, 0, bitmap);
    }

    void addNewData()
    {
        m_data->append("g", 1);
        m_generator->setData(m_data, false);
    }

    void setFrameStatus(ImageFrame::Status status)  { m_status = m_nextFrameStatus = status; }
    void setNextFrameStatus(ImageFrame::Status status)  { m_nextFrameStatus = status; }

    SkBitmap::Allocator* allocator() const { return m_generator->allocator(); }
    void setAllocator(PassOwnPtr<SkBitmap::Allocator> allocator)
    {
        m_generator->setAllocator(allocator);
    }

    PassOwnPtr<ScaledImageFragment> decode(size_t index)
    {
        ImageDecoder* decoder = 0;
        return m_generator->decode(index, &decoder);
    }

    RefPtr<SharedBuffer> m_data;
    RefPtr<ImageFrameGenerator> m_generator;
    int m_decodersDestroyed;
    int m_frameBufferRequestCount;
    ImageFrame::Status m_status;
    ImageFrame::Status m_nextFrameStatus;
};

TEST_F(ImageFrameGeneratorTest, cacheHit)
{
    const ScaledImageFragment* fullImage = ImageDecodingStore::instance()->insertAndLockCache(
        m_generator.get(), createCompleteImage(fullSize()));
    EXPECT_EQ(fullSize(), fullImage->scaledSize());
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), fullImage);

    const ScaledImageFragment* tempImage = m_generator->decodeAndScale(fullSize());
    EXPECT_EQ(fullImage, tempImage);
    EXPECT_EQ(fullSize(), tempImage->scaledSize());
    EXPECT_TRUE(m_generator->hasAlpha(0));
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), tempImage);
    EXPECT_EQ(0, m_frameBufferRequestCount);
}

TEST_F(ImageFrameGeneratorTest, cacheMissWithScale)
{
    const ScaledImageFragment* fullImage = ImageDecodingStore::instance()->insertAndLockCache(
        m_generator.get(), createCompleteImage(fullSize()));
    EXPECT_EQ(fullSize(), fullImage->scaledSize());
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), fullImage);

    // Cache miss because of scaled size not found.
    const ScaledImageFragment* scaledImage = m_generator->decodeAndScale(scaledSize());
    EXPECT_NE(fullImage, scaledImage);
    EXPECT_EQ(scaledSize(), scaledImage->scaledSize());
    EXPECT_TRUE(m_generator->hasAlpha(0));
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), scaledImage);

    // Cache hit.
    const ScaledImageFragment* tempImage = m_generator->decodeAndScale(scaledSize());
    EXPECT_EQ(scaledImage, tempImage);
    EXPECT_EQ(scaledSize(), tempImage->scaledSize());
    EXPECT_TRUE(m_generator->hasAlpha(0));
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), tempImage);
    EXPECT_EQ(0, m_frameBufferRequestCount);
}

TEST_F(ImageFrameGeneratorTest, cacheMissWithDecodeAndScale)
{
    setFrameStatus(ImageFrame::FrameComplete);

    // Cache miss.
    const ScaledImageFragment* scaledImage = m_generator->decodeAndScale(scaledSize());
    EXPECT_EQ(1, m_frameBufferRequestCount);
    EXPECT_EQ(scaledSize(), scaledImage->scaledSize());
    EXPECT_FALSE(m_generator->hasAlpha(0));
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), scaledImage);
    EXPECT_EQ(1, m_decodersDestroyed);

    // Cache hit.
    const ScaledImageFragment* fullImage = m_generator->decodeAndScale(fullSize());
    EXPECT_NE(scaledImage, fullImage);
    EXPECT_EQ(fullSize(), fullImage->scaledSize());
    EXPECT_FALSE(m_generator->hasAlpha(0));
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), fullImage);

    // Cache hit.
    const ScaledImageFragment* tempImage = m_generator->decodeAndScale(scaledSize());
    EXPECT_EQ(scaledImage, tempImage);
    EXPECT_EQ(scaledSize(), tempImage->scaledSize());
    EXPECT_FALSE(m_generator->hasAlpha(0));
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), tempImage);
    EXPECT_EQ(1, m_frameBufferRequestCount);
}

TEST_F(ImageFrameGeneratorTest, cacheMissWithIncompleteDecode)
{
    setFrameStatus(ImageFrame::FramePartial);

    const ScaledImageFragment* tempImage= m_generator->decodeAndScale(fullSize());
    EXPECT_FALSE(tempImage->isComplete());
    EXPECT_EQ(1, m_frameBufferRequestCount);
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), tempImage);
    EXPECT_EQ(2u, ImageDecodingStore::instance()->cacheEntries());
    EXPECT_EQ(1u, ImageDecodingStore::instance()->imageCacheEntries());
    EXPECT_EQ(1u, ImageDecodingStore::instance()->decoderCacheEntries());

    addNewData();
    tempImage = m_generator->decodeAndScale(fullSize());
    EXPECT_FALSE(tempImage->isComplete());
    EXPECT_EQ(2, m_frameBufferRequestCount);
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), tempImage);
    EXPECT_EQ(3u, ImageDecodingStore::instance()->cacheEntries());
    EXPECT_EQ(2u, ImageDecodingStore::instance()->imageCacheEntries());
    EXPECT_EQ(1u, ImageDecodingStore::instance()->decoderCacheEntries());
    EXPECT_EQ(0, m_decodersDestroyed);
}

TEST_F(ImageFrameGeneratorTest, cacheMissWithIncompleteDecodeAndScale)
{
    setFrameStatus(ImageFrame::FramePartial);

    const ScaledImageFragment* tempImage= m_generator->decodeAndScale(scaledSize());
    EXPECT_FALSE(tempImage->isComplete());
    EXPECT_EQ(1, m_frameBufferRequestCount);
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), tempImage);
    EXPECT_EQ(3u, ImageDecodingStore::instance()->cacheEntries());
    EXPECT_EQ(2u, ImageDecodingStore::instance()->imageCacheEntries());
    EXPECT_EQ(1u, ImageDecodingStore::instance()->decoderCacheEntries());

    addNewData();
    tempImage = m_generator->decodeAndScale(scaledSize());
    EXPECT_FALSE(tempImage->isComplete());
    EXPECT_EQ(2, m_frameBufferRequestCount);
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), tempImage);
    EXPECT_EQ(5u, ImageDecodingStore::instance()->cacheEntries());
    EXPECT_EQ(4u, ImageDecodingStore::instance()->imageCacheEntries());
    EXPECT_EQ(1u, ImageDecodingStore::instance()->decoderCacheEntries());
    EXPECT_EQ(0, m_decodersDestroyed);
}

TEST_F(ImageFrameGeneratorTest, incompleteDecodeBecomesComplete)
{
    setFrameStatus(ImageFrame::FramePartial);

    const ScaledImageFragment* tempImage = m_generator->decodeAndScale(fullSize());
    EXPECT_FALSE(tempImage->isComplete());
    EXPECT_EQ(1, m_frameBufferRequestCount);
    EXPECT_EQ(0, m_decodersDestroyed);
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), tempImage);
    EXPECT_EQ(2u, ImageDecodingStore::instance()->cacheEntries());
    EXPECT_EQ(1u, ImageDecodingStore::instance()->imageCacheEntries());
    EXPECT_EQ(1u, ImageDecodingStore::instance()->decoderCacheEntries());

    setFrameStatus(ImageFrame::FrameComplete);
    addNewData();

    tempImage = m_generator->decodeAndScale(fullSize());
    EXPECT_TRUE(tempImage->isComplete());
    EXPECT_EQ(2, m_frameBufferRequestCount);
    EXPECT_EQ(1, m_decodersDestroyed);
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), tempImage);
    EXPECT_EQ(2u, ImageDecodingStore::instance()->cacheEntries());
    EXPECT_EQ(2u, ImageDecodingStore::instance()->imageCacheEntries());
    EXPECT_EQ(0u, ImageDecodingStore::instance()->decoderCacheEntries());

    tempImage = m_generator->decodeAndScale(fullSize());
    EXPECT_TRUE(tempImage->isComplete());
    EXPECT_EQ(2, m_frameBufferRequestCount);
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), tempImage);
}

TEST_F(ImageFrameGeneratorTest, incompleteDecodeAndScaleBecomesComplete)
{
    setFrameStatus(ImageFrame::FramePartial);

    const ScaledImageFragment* tempImage = m_generator->decodeAndScale(scaledSize());
    EXPECT_FALSE(tempImage->isComplete());
    EXPECT_EQ(1, m_frameBufferRequestCount);
    EXPECT_EQ(0, m_decodersDestroyed);
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), tempImage);
    EXPECT_EQ(3u, ImageDecodingStore::instance()->cacheEntries());
    EXPECT_EQ(2u, ImageDecodingStore::instance()->imageCacheEntries());
    EXPECT_EQ(1u, ImageDecodingStore::instance()->decoderCacheEntries());

    setFrameStatus(ImageFrame::FrameComplete);
    addNewData();

    tempImage = m_generator->decodeAndScale(scaledSize());
    EXPECT_TRUE(tempImage->isComplete());
    EXPECT_EQ(2, m_frameBufferRequestCount);
    EXPECT_EQ(1, m_decodersDestroyed);
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), tempImage);
    EXPECT_EQ(4u, ImageDecodingStore::instance()->cacheEntries());
    EXPECT_EQ(4u, ImageDecodingStore::instance()->imageCacheEntries());
    EXPECT_EQ(0u, ImageDecodingStore::instance()->decoderCacheEntries());

    tempImage = m_generator->decodeAndScale(scaledSize());
    EXPECT_TRUE(tempImage->isComplete());
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), tempImage);

    tempImage = m_generator->decodeAndScale(fullSize());
    EXPECT_TRUE(tempImage->isComplete());
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), tempImage);

    EXPECT_EQ(2, m_frameBufferRequestCount);
}

static void decodeThreadMain(void* arg)
{
    ImageFrameGenerator* generator = reinterpret_cast<ImageFrameGenerator*>(arg);
    const ScaledImageFragment* tempImage = generator->decodeAndScale(fullSize());
    ImageDecodingStore::instance()->unlockCache(generator, tempImage);
}

TEST_F(ImageFrameGeneratorTest, incompleteDecodeBecomesCompleteMultiThreaded)
{
    setFrameStatus(ImageFrame::FramePartial);

    const ScaledImageFragment* tempImage = m_generator->decodeAndScale(fullSize());
    EXPECT_FALSE(tempImage->isComplete());
    EXPECT_EQ(1, m_frameBufferRequestCount);
    EXPECT_EQ(0, m_decodersDestroyed);
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), tempImage);
    EXPECT_EQ(2u, ImageDecodingStore::instance()->cacheEntries());
    EXPECT_EQ(1u, ImageDecodingStore::instance()->imageCacheEntries());
    EXPECT_EQ(1u, ImageDecodingStore::instance()->decoderCacheEntries());

    // Frame can now be decoded completely.
    setFrameStatus(ImageFrame::FrameComplete);
    addNewData();
    ThreadIdentifier threadID = createThread(&decodeThreadMain, m_generator.get(), "DecodeThread");
    waitForThreadCompletion(threadID);

    EXPECT_EQ(2, m_frameBufferRequestCount);
    EXPECT_EQ(1, m_decodersDestroyed);
    EXPECT_EQ(2u, ImageDecodingStore::instance()->cacheEntries());
    EXPECT_EQ(2u, ImageDecodingStore::instance()->imageCacheEntries());
    EXPECT_EQ(0u, ImageDecodingStore::instance()->decoderCacheEntries());

    tempImage = m_generator->decodeAndScale(fullSize());
    EXPECT_TRUE(tempImage->isComplete());
    EXPECT_EQ(2, m_frameBufferRequestCount);
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), tempImage);
}

TEST_F(ImageFrameGeneratorTest, concurrentIncompleteDecodeAndScale)
{
    setFrameStatus(ImageFrame::FramePartial);

    const ScaledImageFragment* fullImage = m_generator->decodeAndScale(fullSize());
    const ScaledImageFragment* scaledImage = m_generator->decodeAndScale(scaledSize());
    EXPECT_FALSE(fullImage->isComplete());
    EXPECT_FALSE(scaledImage->isComplete());
    EXPECT_EQ(2, m_frameBufferRequestCount);
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), fullImage);
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), scaledImage);
    EXPECT_EQ(4u, ImageDecodingStore::instance()->cacheEntries());
    EXPECT_EQ(3u, ImageDecodingStore::instance()->imageCacheEntries());
    EXPECT_EQ(1u, ImageDecodingStore::instance()->decoderCacheEntries());
    EXPECT_EQ(0, m_decodersDestroyed);

    addNewData();
    setFrameStatus(ImageFrame::FrameComplete);
    scaledImage = m_generator->decodeAndScale(scaledSize());
    EXPECT_TRUE(scaledImage->isComplete());
    EXPECT_EQ(3, m_frameBufferRequestCount);
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), scaledImage);
    EXPECT_EQ(5u, ImageDecodingStore::instance()->cacheEntries());
    EXPECT_EQ(5u, ImageDecodingStore::instance()->imageCacheEntries());
    EXPECT_EQ(0u, ImageDecodingStore::instance()->decoderCacheEntries());
    EXPECT_EQ(1, m_decodersDestroyed);
}

TEST_F(ImageFrameGeneratorTest, incompleteBitmapCopied)
{
    setFrameStatus(ImageFrame::FramePartial);

    const ScaledImageFragment* tempImage= m_generator->decodeAndScale(fullSize());
    EXPECT_FALSE(tempImage->isComplete());
    EXPECT_EQ(1, m_frameBufferRequestCount);

    ImageDecoder* tempDecoder = 0;
    EXPECT_TRUE(ImageDecodingStore::instance()->lockDecoder(m_generator.get(), fullSize(), &tempDecoder));
    ASSERT_TRUE(tempDecoder);
    EXPECT_NE(tempDecoder->frameBufferAtIndex(0)->getSkBitmap().getPixels(), tempImage->bitmap().getPixels());
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), tempImage);
    ImageDecodingStore::instance()->unlockDecoder(m_generator.get(), tempDecoder);
}

TEST_F(ImageFrameGeneratorTest, resumeDecodeEmptyFrameTurnsComplete)
{
    m_generator = ImageFrameGenerator::create(fullSize(), m_data, false, true);
    useMockImageDecoderFactory();
    setFrameStatus(ImageFrame::FrameComplete);

    const ScaledImageFragment* tempImage = m_generator->decodeAndScale(fullSize(), 0);
    EXPECT_TRUE(tempImage->isComplete());
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), tempImage);

    setFrameStatus(ImageFrame::FrameEmpty);
    setNextFrameStatus(ImageFrame::FrameComplete);
    EXPECT_FALSE(m_generator->decodeAndScale(fullSize(), 1));
}

TEST_F(ImageFrameGeneratorTest, frameHasAlpha)
{
    setFrameStatus(ImageFrame::FramePartial);
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), m_generator->decodeAndScale(fullSize(), 1));
    EXPECT_TRUE(m_generator->hasAlpha(1));

    ImageDecoder* tempDecoder = 0;
    EXPECT_TRUE(ImageDecodingStore::instance()->lockDecoder(m_generator.get(), fullSize(), &tempDecoder));
    ASSERT_TRUE(tempDecoder);
    static_cast<MockImageDecoder*>(tempDecoder)->setFrameHasAlpha(false);
    ImageDecodingStore::instance()->unlockDecoder(m_generator.get(), tempDecoder);

    setFrameStatus(ImageFrame::FrameComplete);
    ImageDecodingStore::instance()->unlockCache(m_generator.get(), m_generator->decodeAndScale(fullSize(), 1));
    EXPECT_FALSE(m_generator->hasAlpha(1));
}

namespace {

class MockAllocator : public SkBitmap::Allocator {
public:
    // N starts from 0.
    MockAllocator(int failAtNthCall)
        : m_callCount(0)
        , m_failAtNthCall(failAtNthCall)
        , m_defaultAllocator(adoptPtr(new DiscardablePixelRefAllocator()))
    {
    }

    virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* colorTable) OVERRIDE
    {
        if (m_callCount++ == m_failAtNthCall)
            return false;
        return m_defaultAllocator->allocPixelRef(bitmap, colorTable);
    }

    int m_callCount;
    int m_failAtNthCall;
    OwnPtr<SkBitmap::Allocator> m_defaultAllocator;
};

} // namespace

TEST_F(ImageFrameGeneratorTest, decodingAllocatorFailure)
{
    // Try to emulate allocation failures at different stages. For now, the
    // first allocation is for the bitmap in ImageFrame, the second is for the
    // copy of partial bitmap. The loop will still work if the number or purpose
    // of allocations change in the future.
    for (int i = 0; ; ++i) {
        SCOPED_TRACE(testing::Message() << "Allocation failure at call " << i);
        setFrameStatus(ImageFrame::FramePartial);
        setAllocator(adoptPtr(new MockAllocator(i)));
        OwnPtr<ScaledImageFragment> image = decode(0);
        if (i >= static_cast<MockAllocator*>(allocator())->m_callCount) {
            // We have tested failures of all stages. This time all allocations
            // were successful.
            EXPECT_TRUE(image);
            break;
        }
        EXPECT_FALSE(image);
    }
}

} // namespace WebCore
