1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "config.h"
6
7#include "platform/graphics/RecordingImageBufferSurface.h"
8
9#include "platform/graphics/GraphicsContext.h"
10#include "platform/graphics/ImageBuffer.h"
11#include "platform/graphics/ImageBufferClient.h"
12#include "public/platform/Platform.h"
13#include "public/platform/WebThread.h"
14#include "third_party/skia/include/core/SkCanvas.h"
15#include "third_party/skia/include/core/SkPictureRecorder.h"
16#include "wtf/OwnPtr.h"
17#include "wtf/PassOwnPtr.h"
18#include "wtf/RefPtr.h"
19
20#include <gmock/gmock.h>
21#include <gtest/gtest.h>
22
23using namespace blink;
24using testing::Test;
25
26namespace {
27
28class FakeImageBufferClient : public ImageBufferClient, public WebThread::TaskObserver {
29public:
30    FakeImageBufferClient(ImageBuffer* imageBuffer)
31        : m_isDirty(false)
32        , m_imageBuffer(imageBuffer)
33        , m_frameCount(0)
34    { }
35
36    virtual ~FakeImageBufferClient() { }
37
38    // ImageBufferClient implementation
39    virtual void notifySurfaceInvalid() { }
40    virtual bool isDirty() { return m_isDirty; };
41    virtual void didFinalizeFrame()
42    {
43        if (m_isDirty) {
44            Platform::current()->currentThread()->removeTaskObserver(this);
45            m_isDirty = false;
46        }
47        ++m_frameCount;
48    }
49
50    // TaskObserver implementation
51    virtual void willProcessTask() OVERRIDE { ASSERT_NOT_REACHED(); }
52    virtual void didProcessTask() OVERRIDE
53    {
54        ASSERT_TRUE(m_isDirty);
55        FloatRect dirtyRect(0, 0, 1, 1);
56        m_imageBuffer->finalizeFrame(dirtyRect);
57        ASSERT_FALSE(m_isDirty);
58    }
59
60    void fakeDraw()
61    {
62        if (m_isDirty)
63            return;
64        m_isDirty = true;
65        Platform::current()->currentThread()->addTaskObserver(this);
66    }
67
68    int frameCount() { return m_frameCount; }
69
70private:
71    bool m_isDirty;
72    ImageBuffer* m_imageBuffer;
73    int m_frameCount;
74};
75
76} // unnamed namespace
77
78class RecordingImageBufferSurfaceTest : public Test {
79protected:
80    RecordingImageBufferSurfaceTest()
81    {
82        OwnPtr<RecordingImageBufferSurface> testSurface = adoptPtr(new RecordingImageBufferSurface(IntSize(10, 10)));
83        m_testSurface = testSurface.get();
84        // We create an ImageBuffer in order for the testSurface to be
85        // properly initialized with a GraphicsContext
86        m_imageBuffer = ImageBuffer::create(testSurface.release());
87        EXPECT_FALSE(!m_imageBuffer);
88        m_fakeImageBufferClient = adoptPtr(new FakeImageBufferClient(m_imageBuffer.get()));
89        m_imageBuffer->setClient(m_fakeImageBufferClient.get());
90    }
91
92public:
93    void testEmptyPicture()
94    {
95        m_testSurface->initializeCurrentFrame();
96        RefPtr<SkPicture> picture = m_testSurface->getPicture();
97        EXPECT_TRUE((bool)picture.get());
98        EXPECT_EQ(1, m_fakeImageBufferClient->frameCount());
99        expectDisplayListEnabled(true);
100    }
101
102    void testNoFallbackWithClear()
103    {
104        m_testSurface->initializeCurrentFrame();
105        m_testSurface->didClearCanvas();
106        m_testSurface->getPicture();
107        EXPECT_EQ(1, m_fakeImageBufferClient->frameCount());
108        expectDisplayListEnabled(true);
109    }
110
111    void testNonAnimatedCanvasUpdate()
112    {
113        m_testSurface->initializeCurrentFrame();
114        // acquire picture twice to simulate a static canvas: nothing drawn between updates
115        m_fakeImageBufferClient->fakeDraw();
116        m_testSurface->getPicture();
117        m_testSurface->getPicture();
118        EXPECT_EQ(2, m_fakeImageBufferClient->frameCount());
119        expectDisplayListEnabled(true);
120    }
121
122    void testAnimatedWithoutClear()
123    {
124        m_testSurface->initializeCurrentFrame();
125        m_fakeImageBufferClient->fakeDraw();
126        m_testSurface->getPicture();
127        EXPECT_EQ(1, m_fakeImageBufferClient->frameCount());
128        expectDisplayListEnabled(true); // first frame has an implicit clear
129        m_fakeImageBufferClient->fakeDraw();
130        m_testSurface->getPicture();
131        EXPECT_EQ(2, m_fakeImageBufferClient->frameCount());
132        expectDisplayListEnabled(false);
133    }
134
135    void testFrameFinalizedByTaskObserver1()
136    {
137        m_testSurface->initializeCurrentFrame();
138        expectDisplayListEnabled(true);
139        m_testSurface->getPicture();
140        EXPECT_EQ(1, m_fakeImageBufferClient->frameCount());
141        expectDisplayListEnabled(true);
142        m_fakeImageBufferClient->fakeDraw();
143        EXPECT_EQ(1, m_fakeImageBufferClient->frameCount());
144        expectDisplayListEnabled(true);
145        m_testSurface->getPicture();
146        EXPECT_EQ(2, m_fakeImageBufferClient->frameCount());
147        expectDisplayListEnabled(true);
148        m_fakeImageBufferClient->fakeDraw();
149        EXPECT_EQ(2, m_fakeImageBufferClient->frameCount());
150        expectDisplayListEnabled(true);
151        // Display list will be disabled only after exiting the runLoop
152    }
153    void testFrameFinalizedByTaskObserver2()
154    {
155        EXPECT_EQ(3, m_fakeImageBufferClient->frameCount());
156        expectDisplayListEnabled(false);
157        m_testSurface->getPicture();
158        EXPECT_EQ(4, m_fakeImageBufferClient->frameCount());
159        expectDisplayListEnabled(false);
160        m_fakeImageBufferClient->fakeDraw();
161        EXPECT_EQ(4, m_fakeImageBufferClient->frameCount());
162        expectDisplayListEnabled(false);
163    }
164
165    void testAnimatedWithClear()
166    {
167        m_testSurface->initializeCurrentFrame();
168        m_testSurface->getPicture();
169        m_testSurface->didClearCanvas();
170        m_fakeImageBufferClient->fakeDraw();
171        EXPECT_EQ(1, m_fakeImageBufferClient->frameCount());
172        m_testSurface->getPicture();
173        EXPECT_EQ(2, m_fakeImageBufferClient->frameCount());
174        expectDisplayListEnabled(true);
175        // clear after use
176        m_fakeImageBufferClient->fakeDraw();
177        m_testSurface->didClearCanvas();
178        EXPECT_EQ(2, m_fakeImageBufferClient->frameCount());
179        m_testSurface->getPicture();
180        EXPECT_EQ(3, m_fakeImageBufferClient->frameCount());
181        expectDisplayListEnabled(true);
182    }
183
184    void testClearRect()
185    {
186        m_testSurface->initializeCurrentFrame();
187        m_testSurface->getPicture();
188        m_imageBuffer->context()->clearRect(FloatRect(FloatPoint(0, 0), FloatSize(m_testSurface->size())));
189        m_fakeImageBufferClient->fakeDraw();
190        EXPECT_EQ(1, m_fakeImageBufferClient->frameCount());
191        m_testSurface->getPicture();
192        EXPECT_EQ(2, m_fakeImageBufferClient->frameCount());
193        expectDisplayListEnabled(true);
194    }
195
196    void expectDisplayListEnabled(bool displayListEnabled)
197    {
198        EXPECT_EQ(displayListEnabled, (bool)m_testSurface->m_currentFrame.get());
199        EXPECT_EQ(!displayListEnabled, (bool)m_testSurface->m_rasterCanvas.get());
200    }
201
202private:
203    RecordingImageBufferSurface* m_testSurface;
204    OwnPtr<FakeImageBufferClient> m_fakeImageBufferClient;
205    OwnPtr<ImageBuffer> m_imageBuffer;
206};
207
208namespace {
209
210// The following test helper class installs a mock platform that provides a mock WebThread
211// for the current thread. The Mock thread is capable of queuing a single non-delayed task
212// and registering a single task observer. The run loop exits immediately after running
213// the single task.
214class AutoInstallCurrentThreadPlatformMock {
215public:
216    AutoInstallCurrentThreadPlatformMock()
217    {
218        m_oldPlatform = Platform::current();
219        Platform::initialize(&m_mockPlatform);
220    }
221
222    ~AutoInstallCurrentThreadPlatformMock()
223    {
224        Platform::initialize(m_oldPlatform);
225    }
226
227private:
228    class CurrentThreadMock : public WebThread {
229    public:
230        CurrentThreadMock() : m_taskObserver(0), m_task(0) { }
231
232        virtual ~CurrentThreadMock()
233        {
234            EXPECT_EQ((Task*)0, m_task);
235        }
236
237        virtual void postTask(Task* task)
238        {
239            EXPECT_EQ((Task*)0, m_task);
240            m_task = task;
241        }
242
243        virtual void postDelayedTask(Task*, long long delayMs) OVERRIDE { ASSERT_NOT_REACHED(); };
244
245        virtual bool isCurrentThread() const OVERRIDE { return true; }
246        virtual PlatformThreadId threadId() const OVERRIDE
247        {
248            ASSERT_NOT_REACHED();
249            return 0;
250        }
251
252        virtual void addTaskObserver(TaskObserver* taskObserver) OVERRIDE
253        {
254            EXPECT_EQ((TaskObserver*)0, m_taskObserver);
255            m_taskObserver = taskObserver;
256        }
257
258        virtual void removeTaskObserver(TaskObserver* taskObserver) OVERRIDE
259        {
260            EXPECT_EQ(m_taskObserver, taskObserver);
261            m_taskObserver = 0;
262        }
263
264        virtual void enterRunLoop() OVERRIDE
265        {
266            if (m_taskObserver)
267                m_taskObserver->willProcessTask();
268            if (m_task) {
269                m_task->run();
270                delete m_task;
271                m_task = 0;
272            }
273            if (m_taskObserver)
274                m_taskObserver->didProcessTask();
275        }
276
277        virtual void exitRunLoop() OVERRIDE { ASSERT_NOT_REACHED(); }
278
279    private:
280        TaskObserver* m_taskObserver;
281        Task* m_task;
282    };
283
284    class CurrentThreadPlatformMock : public Platform {
285    public:
286        CurrentThreadPlatformMock() { }
287        virtual void cryptographicallyRandomValues(unsigned char* buffer, size_t length) { ASSERT_NOT_REACHED(); }
288        virtual WebThread* currentThread() OVERRIDE { return &m_currentThread; }
289    private:
290        CurrentThreadMock m_currentThread;
291    };
292
293    CurrentThreadPlatformMock m_mockPlatform;
294    Platform* m_oldPlatform;
295};
296
297
298#define DEFINE_TEST_TASK_WRAPPER_CLASS(TEST_METHOD)                                               \
299class TestWrapperTask_ ## TEST_METHOD : public WebThread::Task {                           \
300    public:                                                                                       \
301        TestWrapperTask_ ## TEST_METHOD(RecordingImageBufferSurfaceTest* test) : m_test(test) { } \
302        virtual void run() OVERRIDE { m_test->TEST_METHOD(); }                                    \
303    private:                                                                                      \
304        RecordingImageBufferSurfaceTest* m_test;                                                  \
305};
306
307#define CALL_TEST_TASK_WRAPPER(TEST_METHOD)                                                               \
308    {                                                                                                     \
309        AutoInstallCurrentThreadPlatformMock ctpm;                                                        \
310        Platform::current()->currentThread()->postTask(new TestWrapperTask_ ## TEST_METHOD(this)); \
311        Platform::current()->currentThread()->enterRunLoop();                                      \
312    }
313
314TEST_F(RecordingImageBufferSurfaceTest, testEmptyPicture)
315{
316    testEmptyPicture();
317}
318
319TEST_F(RecordingImageBufferSurfaceTest, testNoFallbackWithClear)
320{
321    testNoFallbackWithClear();
322}
323
324DEFINE_TEST_TASK_WRAPPER_CLASS(testNonAnimatedCanvasUpdate)
325TEST_F(RecordingImageBufferSurfaceTest, testNonAnimatedCanvasUpdate)
326{
327    CALL_TEST_TASK_WRAPPER(testNonAnimatedCanvasUpdate)
328    expectDisplayListEnabled(true);
329}
330
331DEFINE_TEST_TASK_WRAPPER_CLASS(testAnimatedWithoutClear)
332TEST_F(RecordingImageBufferSurfaceTest, testAnimatedWithoutClear)
333{
334    CALL_TEST_TASK_WRAPPER(testAnimatedWithoutClear)
335    expectDisplayListEnabled(false);
336}
337
338DEFINE_TEST_TASK_WRAPPER_CLASS(testFrameFinalizedByTaskObserver1)
339DEFINE_TEST_TASK_WRAPPER_CLASS(testFrameFinalizedByTaskObserver2)
340TEST_F(RecordingImageBufferSurfaceTest, testFrameFinalizedByTaskObserver)
341{
342    CALL_TEST_TASK_WRAPPER(testFrameFinalizedByTaskObserver1)
343    expectDisplayListEnabled(false);
344    CALL_TEST_TASK_WRAPPER(testFrameFinalizedByTaskObserver2)
345    expectDisplayListEnabled(false);
346}
347
348DEFINE_TEST_TASK_WRAPPER_CLASS(testAnimatedWithClear)
349TEST_F(RecordingImageBufferSurfaceTest, testAnimatedWithClear)
350{
351    CALL_TEST_TASK_WRAPPER(testAnimatedWithClear)
352    expectDisplayListEnabled(true);
353}
354
355DEFINE_TEST_TASK_WRAPPER_CLASS(testClearRect)
356TEST_F(RecordingImageBufferSurfaceTest, testClearRect)
357{
358    CALL_TEST_TASK_WRAPPER(testClearRect);
359    expectDisplayListEnabled(true);
360}
361
362} // namespace
363