blob: 568a6071c396a06a371252c74ee759ad40531ddc [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 "core/html/canvas/CanvasAsyncBlobCreator.h"
#include "core/html/canvas/ImageData.h"
#include "core/testing/PageTestBase.h"
#include "platform/testing/UnitTestHelpers.h"
#include "platform/wtf/Functional.h"
#include "public/platform/Platform.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkSurface.h"
namespace blink {
typedef CanvasAsyncBlobCreator::IdleTaskStatus IdleTaskStatus;
class MockCanvasAsyncBlobCreator : public CanvasAsyncBlobCreator {
public:
MockCanvasAsyncBlobCreator(scoped_refptr<StaticBitmapImage> image,
MimeType mime_type,
Document* document,
bool fail_encoder_initialization = false)
: CanvasAsyncBlobCreator(image,
mime_type,
nullptr,
0,
document,
nullptr) {
if (fail_encoder_initialization)
fail_encoder_initialization_for_test_ = true;
}
CanvasAsyncBlobCreator::IdleTaskStatus GetIdleTaskStatus() {
return idle_task_status_;
}
MOCK_METHOD0(SignalTaskSwitchInStartTimeoutEventForTesting, void());
MOCK_METHOD0(SignalTaskSwitchInCompleteTimeoutEventForTesting, void());
protected:
void CreateBlobAndReturnResult() override {}
void CreateNullAndReturnResult() override {}
void SignalAlternativeCodePathFinishedForTesting() override;
void PostDelayedTaskToCurrentThread(const base::Location&,
base::OnceClosure,
double delay_ms) override;
};
void MockCanvasAsyncBlobCreator::SignalAlternativeCodePathFinishedForTesting() {
testing::ExitRunLoop();
}
void MockCanvasAsyncBlobCreator::PostDelayedTaskToCurrentThread(
const base::Location& location,
base::OnceClosure task,
double delay_ms) {
DCHECK(IsMainThread());
Platform::Current()->MainThread()->GetWebTaskRunner()->PostTask(
location, std::move(task));
}
//==============================================================================
class MockCanvasAsyncBlobCreatorWithoutStart
: public MockCanvasAsyncBlobCreator {
public:
MockCanvasAsyncBlobCreatorWithoutStart(scoped_refptr<StaticBitmapImage> image,
Document* document)
: MockCanvasAsyncBlobCreator(image, kMimeTypePng, document) {}
protected:
void ScheduleInitiateEncoding(double) override {
// Deliberately make scheduleInitiateEncoding do nothing so that idle
// task never starts
}
};
//==============================================================================
class MockCanvasAsyncBlobCreatorWithoutComplete
: public MockCanvasAsyncBlobCreator {
public:
MockCanvasAsyncBlobCreatorWithoutComplete(
scoped_refptr<StaticBitmapImage> image,
Document* document,
bool fail_encoder_initialization = false)
: MockCanvasAsyncBlobCreator(image,
kMimeTypePng,
document,
fail_encoder_initialization) {}
protected:
void ScheduleInitiateEncoding(double quality) override {
Platform::Current()->MainThread()->GetWebTaskRunner()->PostTask(
FROM_HERE,
WTF::Bind(&MockCanvasAsyncBlobCreatorWithoutComplete::InitiateEncoding,
WrapPersistent(this), quality,
std::numeric_limits<double>::max()));
}
void IdleEncodeRows(double deadline_seconds) override {
// Deliberately make idleEncodeRows do nothing so that idle task never
// completes
}
};
//==============================================================================
class CanvasAsyncBlobCreatorTest : public PageTestBase {
public:
void PrepareMockCanvasAsyncBlobCreatorWithoutStart();
void PrepareMockCanvasAsyncBlobCreatorWithoutComplete();
void PrepareMockCanvasAsyncBlobCreatorFail();
protected:
CanvasAsyncBlobCreatorTest();
MockCanvasAsyncBlobCreator* AsyncBlobCreator() {
return async_blob_creator_.Get();
}
void TearDown() override;
private:
Persistent<MockCanvasAsyncBlobCreator> async_blob_creator_;
};
CanvasAsyncBlobCreatorTest::CanvasAsyncBlobCreatorTest() = default;
scoped_refptr<StaticBitmapImage> CreateTransparentImage(int width, int height) {
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(width, height);
if (!surface)
return nullptr;
return StaticBitmapImage::Create(surface->makeImageSnapshot());
}
void CanvasAsyncBlobCreatorTest::
PrepareMockCanvasAsyncBlobCreatorWithoutStart() {
async_blob_creator_ = new MockCanvasAsyncBlobCreatorWithoutStart(
CreateTransparentImage(20, 20), &GetDocument());
}
void CanvasAsyncBlobCreatorTest::
PrepareMockCanvasAsyncBlobCreatorWithoutComplete() {
async_blob_creator_ = new MockCanvasAsyncBlobCreatorWithoutComplete(
CreateTransparentImage(20, 20), &GetDocument());
}
void CanvasAsyncBlobCreatorTest::PrepareMockCanvasAsyncBlobCreatorFail() {
// We reuse the class MockCanvasAsyncBlobCreatorWithoutComplete because
// this test case is expected to fail at initialization step before
// completion.
async_blob_creator_ = new MockCanvasAsyncBlobCreatorWithoutComplete(
CreateTransparentImage(20, 20), &GetDocument(), true);
}
void CanvasAsyncBlobCreatorTest::TearDown() {
async_blob_creator_ = nullptr;
}
//==============================================================================
TEST_F(CanvasAsyncBlobCreatorTest,
IdleTaskNotStartedWhenStartTimeoutEventHappens) {
// This test mocks the scenario when idle task is not started when the
// StartTimeoutEvent is inspecting the idle task status.
// The whole image encoding process (including initialization) will then
// become carried out in the alternative code path instead.
PrepareMockCanvasAsyncBlobCreatorWithoutStart();
EXPECT_CALL(*(AsyncBlobCreator()),
SignalTaskSwitchInStartTimeoutEventForTesting());
AsyncBlobCreator()->ScheduleAsyncBlobCreation(true);
testing::EnterRunLoop();
::testing::Mock::VerifyAndClearExpectations(AsyncBlobCreator());
EXPECT_EQ(IdleTaskStatus::kIdleTaskSwitchedToImmediateTask,
AsyncBlobCreator()->GetIdleTaskStatus());
}
TEST_F(CanvasAsyncBlobCreatorTest,
IdleTaskNotCompletedWhenCompleteTimeoutEventHappens) {
// This test mocks the scenario when idle task is not completed when the
// CompleteTimeoutEvent is inspecting the idle task status.
// The remaining image encoding process (excluding initialization) will
// then become carried out in the alternative code path instead.
PrepareMockCanvasAsyncBlobCreatorWithoutComplete();
EXPECT_CALL(*(AsyncBlobCreator()),
SignalTaskSwitchInCompleteTimeoutEventForTesting());
AsyncBlobCreator()->ScheduleAsyncBlobCreation(true);
testing::EnterRunLoop();
::testing::Mock::VerifyAndClearExpectations(AsyncBlobCreator());
EXPECT_EQ(IdleTaskStatus::kIdleTaskSwitchedToImmediateTask,
AsyncBlobCreator()->GetIdleTaskStatus());
}
TEST_F(CanvasAsyncBlobCreatorTest, IdleTaskFailedWhenStartTimeoutEventHappens) {
// This test mocks the scenario when idle task is not failed during when
// either the StartTimeoutEvent or the CompleteTimeoutEvent is inspecting
// the idle task status.
PrepareMockCanvasAsyncBlobCreatorFail();
AsyncBlobCreator()->ScheduleAsyncBlobCreation(true);
testing::EnterRunLoop();
EXPECT_EQ(IdleTaskStatus::kIdleTaskFailed,
AsyncBlobCreator()->GetIdleTaskStatus());
}
}