blob: b7ab1bf70e1867d034a799c9f848999ce668c390 [file] [log] [blame]
// Copyright 2016 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 "platform/graphics/OffscreenCanvasFrameDispatcherImpl.h"
#include "platform/wtf/PtrUtil.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkSurface.h"
using ::testing::_;
using ::testing::Mock;
namespace blink {
class MockOffscreenCanvasFrameDispatcherImpl
: public OffscreenCanvasFrameDispatcherImpl {
public:
MockOffscreenCanvasFrameDispatcherImpl()
: OffscreenCanvasFrameDispatcherImpl(nullptr, 0, 0, 0, 10, 10) {}
MOCK_METHOD2(PostImageToPlaceholder,
void(RefPtr<StaticBitmapImage>, unsigned resource_id));
};
class OffscreenCanvasFrameDispatcherImplTest : public ::testing::Test {
public:
void DispatchOneFrame();
OffscreenCanvasResourceProvider* GetResourceProvider() {
return dispatcher_->offscreen_canvas_resource_provider_.get();
}
unsigned GetNumUnreclaimedFramesPosted() {
return dispatcher_->num_unreclaimed_frames_posted_;
}
StaticBitmapImage* GetLatestUnpostedImage() {
return dispatcher_->latest_unposted_image_.get();
}
unsigned GetLatestUnpostedResourceId() {
return dispatcher_->latest_unposted_resource_id_;
}
protected:
OffscreenCanvasFrameDispatcherImplTest() {
dispatcher_ = WTF::WrapUnique(new MockOffscreenCanvasFrameDispatcherImpl());
}
MockOffscreenCanvasFrameDispatcherImpl* Dispatcher() {
return dispatcher_.get();
}
private:
RefPtr<StaticBitmapImage> PrepareStaticBitmapImage();
std::unique_ptr<MockOffscreenCanvasFrameDispatcherImpl> dispatcher_;
};
void OffscreenCanvasFrameDispatcherImplTest::DispatchOneFrame() {
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(10, 10);
dispatcher_->DispatchFrame(
StaticBitmapImage::Create(surface->makeImageSnapshot()), 0.0,
SkIRect::MakeEmpty(), false);
}
TEST_F(OffscreenCanvasFrameDispatcherImplTest, PlaceholderRunsNormally) {
/* We allow OffscreenCanvas to post up to 3 frames without hearing a response
* from placeholder. */
// Post first frame
unsigned post_resource_id = 1u;
EXPECT_CALL(*(Dispatcher()), PostImageToPlaceholder(_, post_resource_id));
DispatchOneFrame();
EXPECT_EQ(1u, GetNumUnreclaimedFramesPosted());
EXPECT_EQ(1u, GetResourceProvider()->GetNextResourceId());
Mock::VerifyAndClearExpectations(Dispatcher());
// Post second frame
post_resource_id++;
EXPECT_CALL(*(Dispatcher()), PostImageToPlaceholder(_, post_resource_id));
DispatchOneFrame();
EXPECT_EQ(2u, GetNumUnreclaimedFramesPosted());
EXPECT_EQ(2u, GetResourceProvider()->GetNextResourceId());
Mock::VerifyAndClearExpectations(Dispatcher());
// Post third frame
post_resource_id++;
EXPECT_CALL(*(Dispatcher()), PostImageToPlaceholder(_, post_resource_id));
DispatchOneFrame();
EXPECT_EQ(3u, GetNumUnreclaimedFramesPosted());
EXPECT_EQ(3u, GetResourceProvider()->GetNextResourceId());
EXPECT_EQ(nullptr, GetLatestUnpostedImage());
Mock::VerifyAndClearExpectations(Dispatcher());
/* We mock the behavior of placeholder on main thread here, by reclaiming
* the resources in order. */
// Reclaim first frame
unsigned reclaim_resource_id = 1u;
Dispatcher()->ReclaimResource(reclaim_resource_id);
EXPECT_EQ(2u, GetNumUnreclaimedFramesPosted());
// Reclaim second frame
reclaim_resource_id++;
Dispatcher()->ReclaimResource(reclaim_resource_id);
EXPECT_EQ(1u, GetNumUnreclaimedFramesPosted());
// Reclaim third frame
reclaim_resource_id++;
Dispatcher()->ReclaimResource(reclaim_resource_id);
EXPECT_EQ(0u, GetNumUnreclaimedFramesPosted());
}
TEST_F(OffscreenCanvasFrameDispatcherImplTest, PlaceholderBeingBlocked) {
/* When main thread is blocked, attempting to post more than 3 frames will
* result in only 3 PostImageToPlaceholder. The latest unposted image will
* be saved. */
EXPECT_CALL(*(Dispatcher()), PostImageToPlaceholder(_, _)).Times(3);
// Attempt to post 4 times
DispatchOneFrame();
DispatchOneFrame();
DispatchOneFrame();
DispatchOneFrame();
unsigned post_resource_id = 4u;
EXPECT_EQ(3u, GetNumUnreclaimedFramesPosted());
EXPECT_EQ(post_resource_id, GetResourceProvider()->GetNextResourceId());
EXPECT_TRUE(GetLatestUnpostedImage());
EXPECT_EQ(post_resource_id, GetLatestUnpostedResourceId());
// Attempt to post the 5th time. The latest unposted image will be replaced.
post_resource_id++;
DispatchOneFrame();
EXPECT_EQ(3u, GetNumUnreclaimedFramesPosted());
EXPECT_EQ(post_resource_id, GetResourceProvider()->GetNextResourceId());
EXPECT_TRUE(GetLatestUnpostedImage());
EXPECT_EQ(post_resource_id, GetLatestUnpostedResourceId());
Mock::VerifyAndClearExpectations(Dispatcher());
/* When main thread becomes unblocked, the first reclaim called by placeholder
* will trigger OffscreenCanvasFrameDispatcher to post the last saved image.
* Resource reclaim happens in the same order as frame posting. */
unsigned reclaim_resource_id = 1u;
EXPECT_CALL(*(Dispatcher()), PostImageToPlaceholder(_, post_resource_id));
Dispatcher()->ReclaimResource(reclaim_resource_id);
// Reclaim 1 frame and post 1 frame, so numPostImagesUnresponded remains as 3
EXPECT_EQ(3u, GetNumUnreclaimedFramesPosted());
// Not generating new resource Id
EXPECT_EQ(post_resource_id, GetResourceProvider()->GetNextResourceId());
EXPECT_FALSE(GetLatestUnpostedImage());
EXPECT_EQ(0u, GetLatestUnpostedResourceId());
Mock::VerifyAndClearExpectations(Dispatcher());
reclaim_resource_id++;
Dispatcher()->ReclaimResource(reclaim_resource_id);
EXPECT_EQ(2u, GetNumUnreclaimedFramesPosted());
}
} // namespace blink