blob: 3c094c6e79cf713a0830422c8dbb4db54be8fa0e [file] [log] [blame]
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Author: skerner@google.com (Sam Kerner)
#include "net/instaweb/spriter/image_library_interface.h"
#include "net/instaweb/spriter/mock_image_library_interface.h"
#include "net/instaweb/spriter/public/image_spriter.h"
#include "net/instaweb/spriter/public/image_spriter.pb.h"
#include "pagespeed/kernel/base/gmock.h"
#include "pagespeed/kernel/base/gtest.h"
#include "pagespeed/kernel/base/scoped_ptr.h"
namespace net_instaweb {
namespace spriter {
namespace {
// GMock names.
using ::testing::Return;
using ::testing::_;
using ::testing::StrictMock;
using ::testing::DoAll;
using ::testing::SetArgumentPointee;
// Constants used in several tests.
const int kSpriteId = 12345;
const ImageLibraryInterface::FilePath kInBasePath("/input/path");
const ImageLibraryInterface::FilePath kOutBasePath("/output/path");
const ImageLibraryInterface::FilePath kCombinedImagePath("subdir/out.png");
const ImageLibraryInterface::FilePath kPngA("path/to/a.png");
const ImageLibraryInterface::FilePath kPngB("b.png");
// Set up a protobuf of spriting settings. Tests use this to get
// a reasonable default.
void SetupCommonOptions(SpriterInput* spriter_input,
ImageFormat format) {
spriter_input->set_id(kSpriteId);
SpriteOptions* options = spriter_input->mutable_options();
options->set_placement_method(VERTICAL_STRIP);
options->set_input_base_path(kInBasePath);
options->set_output_base_path(kOutBasePath);
options->set_output_image_path(kCombinedImagePath);
options->set_output_format(format);
}
// This class acts as a delegate to ImageLibraryInterface,
// asserting if any failure occurs.
class FailOnImageLibError : public ImageLibraryInterface::Delegate {
public:
// Implement ImageLibInterface::Delegate:
virtual void OnError(const GoogleString& error) const {
ASSERT_TRUE(0) << "Unexpected error: " << error;
}
};
// Test that ImageSpriter produces an empty image when asked to sprite
// zero images.
TEST(SpriterTest, ZeroImages) {
SpriterInput spriter_input;
SetupCommonOptions(&spriter_input, PNG);
// Build mock ojects:
FailOnImageLibError no_failures_allowed;
scoped_ptr<StrictMock<MockImageLibraryInterface::MockCanvas> >
mock_canvas(new StrictMock<MockImageLibraryInterface::MockCanvas>);
testing::StrictMock<MockImageLibraryInterface>
mock_image_lib(kInBasePath, kOutBasePath, &no_failures_allowed);
// No images: Combined image will be 0x0.
EXPECT_CALL(mock_image_lib, CreateCanvas(0, 0))
.WillOnce(Return(mock_canvas.get()));
EXPECT_CALL(*mock_canvas, WriteToFile(kCombinedImagePath, PNG))
.WillOnce(Return(true));
// spriter.Sprite() will free the canvas it gets. In the test,
// the canvas is mock object |mock_canvas|. Disown the pointer.
EXPECT_FALSE(NULL == mock_canvas.release());
ImageSpriter spriter(&mock_image_lib);
scoped_ptr<SpriterResult> sprite_result(spriter.Sprite(spriter_input));
ASSERT_TRUE(sprite_result.get());
EXPECT_EQ(kSpriteId, sprite_result->id());
EXPECT_EQ(kOutBasePath, sprite_result->output_base_path());
EXPECT_EQ(0, sprite_result->image_position_size());
}
// Sprite a single image. Test that ImageSpriter produces an image
// that is the same size as the input.
TEST(SpriterTest, OneImage) {
SpriterInput spriter_input;
SetupCommonOptions(&spriter_input, JPEG);
spriter_input.add_input_image_set()->set_path(kPngA);
FailOnImageLibError no_failures_allowed;
scoped_ptr<StrictMock<MockImageLibraryInterface::MockCanvas> >
mock_canvas(new StrictMock<MockImageLibraryInterface::MockCanvas>);
scoped_ptr<StrictMock<MockImageLibraryInterface::MockImage> >
mock_image_a(new StrictMock<MockImageLibraryInterface::MockImage>);
testing::StrictMock<MockImageLibraryInterface>
mock_image_lib(kInBasePath, kOutBasePath, &no_failures_allowed);
EXPECT_CALL(mock_image_lib, ReadFromFile(kPngA))
.WillOnce(Return(mock_image_a.get()));
// Image #1: 10x11.
EXPECT_CALL(*mock_image_a, GetDimensions(_, _))
.WillOnce(DoAll(SetArgumentPointee<0>(10),
SetArgumentPointee<1>(11),
Return(true)));
EXPECT_CALL(*mock_canvas, DrawImage(mock_image_a.get(), 0, 0))
.WillOnce(Return(true));
// Canvas should be 10x11
EXPECT_CALL(mock_image_lib, CreateCanvas(10, 11))
.WillOnce(Return(mock_canvas.get()));
EXPECT_CALL(*mock_canvas, WriteToFile(kCombinedImagePath, JPEG))
.WillOnce(Return(true));
// spriter.Sprite() will free these.
EXPECT_FALSE(NULL == mock_canvas.release());
EXPECT_FALSE(NULL == mock_image_a.release());
ImageSpriter spriter(&mock_image_lib);
scoped_ptr<SpriterResult> sprite_result(spriter.Sprite(spriter_input));
ASSERT_TRUE(sprite_result.get());
EXPECT_EQ(kSpriteId, sprite_result->id());
EXPECT_EQ(kOutBasePath, sprite_result->output_base_path());
EXPECT_EQ(1, sprite_result->image_position_size());
}
// Sprite two images. Test that ImageSpriter produces an image
// that is the right overall size, and places the two images in
// the expected locations within the combined image.
TEST(SpriterTest, TwoImages) {
SpriterInput spriter_input;
SetupCommonOptions(&spriter_input, PNG);
// Add two mock images.
spriter_input.add_input_image_set()->set_path(kPngA);
spriter_input.add_input_image_set()->set_path(kPngB);
FailOnImageLibError no_failures_allowed;
scoped_ptr<StrictMock<MockImageLibraryInterface::MockCanvas> >
mock_canvas(new StrictMock<MockImageLibraryInterface::MockCanvas>);
scoped_ptr<StrictMock<MockImageLibraryInterface::MockImage> >
mock_image_a(new StrictMock<MockImageLibraryInterface::MockImage>);
scoped_ptr<StrictMock<MockImageLibraryInterface::MockImage> >
mock_image_b(new StrictMock<MockImageLibraryInterface::MockImage>);
testing::StrictMock<MockImageLibraryInterface>
mock_image_lib(kInBasePath, kOutBasePath, &no_failures_allowed);
EXPECT_CALL(mock_image_lib, ReadFromFile(kPngA))
.WillOnce(Return(mock_image_a.get()));
EXPECT_CALL(*mock_image_a, GetDimensions(_, _))
.WillOnce(DoAll(SetArgumentPointee<0>(10),
SetArgumentPointee<1>(11),
Return(true)));
EXPECT_CALL(mock_image_lib, ReadFromFile(kPngB))
.WillOnce(Return(mock_image_b.get()));
EXPECT_CALL(*mock_image_b, GetDimensions(_, _))
.WillOnce(DoAll(SetArgumentPointee<0>(20),
SetArgumentPointee<1>(21),
Return(true)));
int expected_width = 20; // max(10, 20)
int expected_hieght = 32; // 11 + 21
EXPECT_CALL(mock_image_lib, CreateCanvas(expected_width, expected_hieght))
.WillOnce(Return(mock_canvas.get()));
// Draw the first image at the origin.
EXPECT_CALL(*mock_canvas, DrawImage(mock_image_a.get(), 0, 0))
.WillOnce(Return(true));
// Draw the second image just below the first.
EXPECT_CALL(*mock_canvas, DrawImage(mock_image_b.get(), 0, 11))
.WillOnce(Return(true));
EXPECT_CALL(*mock_canvas, WriteToFile(kCombinedImagePath, PNG))
.WillOnce(Return(true));
// spriter.Sprite() will free these.
EXPECT_FALSE(NULL == mock_canvas.release());
EXPECT_FALSE(NULL == mock_image_a.release());
EXPECT_FALSE(NULL == mock_image_b.release());
ImageSpriter spriter(&mock_image_lib);
scoped_ptr<SpriterResult> sprite_result(spriter.Sprite(spriter_input));
ASSERT_TRUE(sprite_result.get());
EXPECT_EQ(kSpriteId, sprite_result->id());
EXPECT_EQ(kOutBasePath, sprite_result->output_base_path());
EXPECT_EQ(2, sprite_result->image_position_size());
EXPECT_EQ(kCombinedImagePath, sprite_result->output_image_path());
}
} // namesapce
} // namespace spriter
} // namespace net_instaweb