- add sources.
[platform/framework/web/crosswalk.git] / src / ui / gfx / icon_util_unittest.cc
1 // Copyright (c) 2011 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 "base/file_util.h"
6 #include "base/files/scoped_temp_dir.h"
7 #include "base/memory/scoped_ptr.h"
8 #include "base/path_service.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10 #include "third_party/skia/include/core/SkBitmap.h"
11 #include "ui/gfx/gfx_paths.h"
12 #include "ui/gfx/icon_util.h"
13 #include "ui/gfx/image/image.h"
14 #include "ui/gfx/image/image_family.h"
15 #include "ui/gfx/size.h"
16 #include "ui/test/ui_unittests_resource.h"
17
18 namespace {
19
20 static const char kSmallIconName[] = "icon_util/16_X_16_icon.ico";
21 static const char kLargeIconName[] = "icon_util/128_X_128_icon.ico";
22 static const char kTempIconFilename[] = "temp_test_icon.ico";
23
24 }  // namespace
25
26 class IconUtilTest : public testing::Test {
27  public:
28   virtual void SetUp() OVERRIDE {
29     PathService::Get(gfx::DIR_TEST_DATA, &test_data_directory_);
30     temp_directory_.CreateUniqueTempDir();
31   }
32
33   static const int kSmallIconWidth = 16;
34   static const int kSmallIconHeight = 16;
35   static const int kLargeIconWidth = 128;
36   static const int kLargeIconHeight = 128;
37
38   // Given a file name for an .ico file and an image dimensions, this
39   // function loads the icon and returns an HICON handle.
40   HICON LoadIconFromFile(const base::FilePath& filename,
41                          int width, int height) {
42     HICON icon = static_cast<HICON>(LoadImage(NULL,
43                                     filename.value().c_str(),
44                                     IMAGE_ICON,
45                                     width,
46                                     height,
47                                     LR_LOADTRANSPARENT | LR_LOADFROMFILE));
48     return icon;
49   }
50
51   SkBitmap CreateBlackSkBitmap(int width, int height) {
52     SkBitmap bitmap;
53     bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
54     bitmap.allocPixels();
55     // Setting the pixels to black.
56     memset(bitmap.getPixels(), 0, width * height * 4);
57     return bitmap;
58   }
59
60   // Loads an .ico file from |icon_filename| and asserts that it contains all of
61   // the expected icon sizes up to and including |max_icon_size|, and no other
62   // icons. If |max_icon_size| >= 256, this tests for a 256x256 PNG icon entry.
63   void CheckAllIconSizes(const base::FilePath& icon_filename,
64                          int max_icon_size);
65
66  protected:
67   // The root directory for test files. This should be treated as read-only.
68   base::FilePath test_data_directory_;
69
70   // Directory for creating files by this test.
71   base::ScopedTempDir temp_directory_;
72 };
73
74 void IconUtilTest::CheckAllIconSizes(const base::FilePath& icon_filename,
75                                      int max_icon_size) {
76   ASSERT_TRUE(base::PathExists(icon_filename));
77
78   // Determine how many icons to expect, based on |max_icon_size|.
79   int expected_num_icons = 0;
80   for (size_t i = 0; i < IconUtil::kNumIconDimensions; ++i) {
81     if (IconUtil::kIconDimensions[i] > max_icon_size)
82       break;
83     ++expected_num_icons;
84   }
85
86   // First, use the Windows API to load the icon, a basic validity test.
87   HICON icon = LoadIconFromFile(icon_filename, kSmallIconWidth,
88                                 kSmallIconHeight);
89   EXPECT_NE(static_cast<HICON>(NULL), icon);
90   if (icon != NULL)
91     ::DestroyIcon(icon);
92
93   // Read the file completely into memory.
94   std::string icon_data;
95   ASSERT_TRUE(base::ReadFileToString(icon_filename, &icon_data));
96   ASSERT_GE(icon_data.length(), sizeof(IconUtil::ICONDIR));
97
98   // Ensure that it has exactly the expected number and sizes of icons, in the
99   // expected order. This matches each entry of the loaded file's icon directory
100   // with the corresponding element of kIconDimensions.
101   // Also extracts the 256x256 entry as png_entry.
102   const IconUtil::ICONDIR* icon_dir =
103       reinterpret_cast<const IconUtil::ICONDIR*>(icon_data.data());
104   EXPECT_EQ(expected_num_icons, icon_dir->idCount);
105   ASSERT_GE(IconUtil::kNumIconDimensions, icon_dir->idCount);
106   ASSERT_GE(icon_data.length(),
107             sizeof(IconUtil::ICONDIR) +
108                 icon_dir->idCount * sizeof(IconUtil::ICONDIRENTRY));
109   const IconUtil::ICONDIRENTRY* png_entry = NULL;
110   for (size_t i = 0; i < icon_dir->idCount; ++i) {
111     const IconUtil::ICONDIRENTRY* entry = &icon_dir->idEntries[i];
112     // Mod 256 because as a special case in ICONDIRENTRY, the value 0 represents
113     // a width or height of 256.
114     int expected_size = IconUtil::kIconDimensions[i] % 256;
115     EXPECT_EQ(expected_size, static_cast<int>(entry->bWidth));
116     EXPECT_EQ(expected_size, static_cast<int>(entry->bHeight));
117     if (entry->bWidth == 0 && entry->bHeight == 0) {
118       EXPECT_EQ(NULL, png_entry);
119       png_entry = entry;
120     }
121   }
122
123   if (max_icon_size >= 256) {
124     ASSERT_TRUE(png_entry);
125
126     // Convert the PNG entry data back to a SkBitmap to ensure it's valid.
127     ASSERT_GE(icon_data.length(),
128               png_entry->dwImageOffset + png_entry->dwBytesInRes);
129     const unsigned char* png_bytes = reinterpret_cast<const unsigned char*>(
130         icon_data.data() + png_entry->dwImageOffset);
131     gfx::Image image = gfx::Image::CreateFrom1xPNGBytes(
132         png_bytes, png_entry->dwBytesInRes);
133     SkBitmap bitmap = image.AsBitmap();
134     EXPECT_EQ(256, bitmap.width());
135     EXPECT_EQ(256, bitmap.height());
136   }
137 }
138
139 // The following test case makes sure IconUtil::SkBitmapFromHICON fails
140 // gracefully when called with invalid input parameters.
141 TEST_F(IconUtilTest, TestIconToBitmapInvalidParameters) {
142   base::FilePath icon_filename =
143       test_data_directory_.AppendASCII(kSmallIconName);
144   gfx::Size icon_size(kSmallIconWidth, kSmallIconHeight);
145   HICON icon = LoadIconFromFile(icon_filename,
146                                 icon_size.width(),
147                                 icon_size.height());
148   ASSERT_TRUE(icon != NULL);
149
150   // Invalid size parameter.
151   gfx::Size invalid_icon_size(kSmallIconHeight, 0);
152   EXPECT_EQ(IconUtil::CreateSkBitmapFromHICON(icon, invalid_icon_size),
153             static_cast<SkBitmap*>(NULL));
154
155   // Invalid icon.
156   EXPECT_EQ(IconUtil::CreateSkBitmapFromHICON(NULL, icon_size),
157             static_cast<SkBitmap*>(NULL));
158
159   // The following code should succeed.
160   scoped_ptr<SkBitmap> bitmap;
161   bitmap.reset(IconUtil::CreateSkBitmapFromHICON(icon, icon_size));
162   EXPECT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
163   ::DestroyIcon(icon);
164 }
165
166 // The following test case makes sure IconUtil::CreateHICONFromSkBitmap fails
167 // gracefully when called with invalid input parameters.
168 TEST_F(IconUtilTest, TestBitmapToIconInvalidParameters) {
169   HICON icon = NULL;
170   scoped_ptr<SkBitmap> bitmap;
171
172   // Wrong bitmap format.
173   bitmap.reset(new SkBitmap);
174   ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
175   bitmap->setConfig(SkBitmap::kA8_Config, kSmallIconWidth, kSmallIconHeight);
176   icon = IconUtil::CreateHICONFromSkBitmap(*bitmap);
177   EXPECT_EQ(icon, static_cast<HICON>(NULL));
178
179   // Invalid bitmap size.
180   bitmap.reset(new SkBitmap);
181   ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
182   bitmap->setConfig(SkBitmap::kARGB_8888_Config, 0, 0);
183   icon = IconUtil::CreateHICONFromSkBitmap(*bitmap);
184   EXPECT_EQ(icon, static_cast<HICON>(NULL));
185
186   // Valid bitmap configuration but no pixels allocated.
187   bitmap.reset(new SkBitmap);
188   ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
189   bitmap->setConfig(SkBitmap::kARGB_8888_Config,
190                     kSmallIconWidth,
191                     kSmallIconHeight);
192   icon = IconUtil::CreateHICONFromSkBitmap(*bitmap);
193   EXPECT_TRUE(icon == NULL);
194 }
195
196 // The following test case makes sure IconUtil::CreateIconFileFromImageFamily
197 // fails gracefully when called with invalid input parameters.
198 TEST_F(IconUtilTest, TestCreateIconFileInvalidParameters) {
199   scoped_ptr<SkBitmap> bitmap;
200   gfx::ImageFamily image_family;
201   base::FilePath valid_icon_filename = temp_directory_.path().AppendASCII(
202       kTempIconFilename);
203   base::FilePath invalid_icon_filename = temp_directory_.path().AppendASCII(
204       "<>?.ico");
205
206   // Wrong bitmap format.
207   bitmap.reset(new SkBitmap);
208   ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
209   bitmap->setConfig(SkBitmap::kA8_Config, kSmallIconWidth, kSmallIconHeight);
210   // Must allocate pixels or else ImageSkia will ignore the bitmap and just
211   // return an empty image.
212   bitmap->allocPixels();
213   memset(bitmap->getPixels(), 0, bitmap->width() * bitmap->height());
214   image_family.Add(gfx::Image::CreateFrom1xBitmap(*bitmap));
215   EXPECT_FALSE(IconUtil::CreateIconFileFromImageFamily(image_family,
216                                                        valid_icon_filename));
217   EXPECT_FALSE(base::PathExists(valid_icon_filename));
218
219   // Invalid bitmap size.
220   image_family.clear();
221   bitmap.reset(new SkBitmap);
222   ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
223   bitmap->setConfig(SkBitmap::kARGB_8888_Config, 0, 0);
224   bitmap->allocPixels();
225   image_family.Add(gfx::Image::CreateFrom1xBitmap(*bitmap));
226   EXPECT_FALSE(IconUtil::CreateIconFileFromImageFamily(image_family,
227                                                        valid_icon_filename));
228   EXPECT_FALSE(base::PathExists(valid_icon_filename));
229
230   // Bitmap with no allocated pixels.
231   image_family.clear();
232   bitmap.reset(new SkBitmap);
233   ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
234   bitmap->setConfig(SkBitmap::kARGB_8888_Config,
235                     kSmallIconWidth,
236                     kSmallIconHeight);
237   image_family.Add(gfx::Image::CreateFrom1xBitmap(*bitmap));
238   EXPECT_FALSE(IconUtil::CreateIconFileFromImageFamily(image_family,
239                                                        valid_icon_filename));
240   EXPECT_FALSE(base::PathExists(valid_icon_filename));
241
242   // Invalid file name.
243   image_family.clear();
244   bitmap->allocPixels();
245   // Setting the pixels to black.
246   memset(bitmap->getPixels(), 0, bitmap->width() * bitmap->height() * 4);
247   image_family.Add(gfx::Image::CreateFrom1xBitmap(*bitmap));
248   EXPECT_FALSE(IconUtil::CreateIconFileFromImageFamily(image_family,
249                                                        invalid_icon_filename));
250   EXPECT_FALSE(base::PathExists(invalid_icon_filename));
251 }
252
253 // This test case makes sure IconUtil::CreateIconFileFromImageFamily fails if
254 // the image family is empty or invalid.
255 TEST_F(IconUtilTest, TestCreateIconFileEmptyImageFamily) {
256   base::FilePath icon_filename = temp_directory_.path().AppendASCII(
257       kTempIconFilename);
258
259   // Empty image family.
260   EXPECT_FALSE(IconUtil::CreateIconFileFromImageFamily(gfx::ImageFamily(),
261                                                        icon_filename));
262   EXPECT_FALSE(base::PathExists(icon_filename));
263
264   // Image family with only an empty image.
265   gfx::ImageFamily image_family;
266   image_family.Add(gfx::Image());
267   EXPECT_FALSE(IconUtil::CreateIconFileFromImageFamily(image_family,
268                                                        icon_filename));
269   EXPECT_FALSE(base::PathExists(icon_filename));
270 }
271
272 // This test case makes sure that when we load an icon from disk and convert
273 // the HICON into a bitmap, the bitmap has the expected format and dimensions.
274 TEST_F(IconUtilTest, TestCreateSkBitmapFromHICON) {
275   scoped_ptr<SkBitmap> bitmap;
276   base::FilePath small_icon_filename = test_data_directory_.AppendASCII(
277       kSmallIconName);
278   gfx::Size small_icon_size(kSmallIconWidth, kSmallIconHeight);
279   HICON small_icon = LoadIconFromFile(small_icon_filename,
280                                       small_icon_size.width(),
281                                       small_icon_size.height());
282   ASSERT_NE(small_icon, static_cast<HICON>(NULL));
283   bitmap.reset(IconUtil::CreateSkBitmapFromHICON(small_icon, small_icon_size));
284   ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
285   EXPECT_EQ(bitmap->width(), small_icon_size.width());
286   EXPECT_EQ(bitmap->height(), small_icon_size.height());
287   EXPECT_EQ(bitmap->config(), SkBitmap::kARGB_8888_Config);
288   ::DestroyIcon(small_icon);
289
290   base::FilePath large_icon_filename = test_data_directory_.AppendASCII(
291       kLargeIconName);
292   gfx::Size large_icon_size(kLargeIconWidth, kLargeIconHeight);
293   HICON large_icon = LoadIconFromFile(large_icon_filename,
294                                       large_icon_size.width(),
295                                       large_icon_size.height());
296   ASSERT_NE(large_icon, static_cast<HICON>(NULL));
297   bitmap.reset(IconUtil::CreateSkBitmapFromHICON(large_icon, large_icon_size));
298   ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
299   EXPECT_EQ(bitmap->width(), large_icon_size.width());
300   EXPECT_EQ(bitmap->height(), large_icon_size.height());
301   EXPECT_EQ(bitmap->config(), SkBitmap::kARGB_8888_Config);
302   ::DestroyIcon(large_icon);
303 }
304
305 // This test case makes sure that when an HICON is created from an SkBitmap,
306 // the returned handle is valid and refers to an icon with the expected
307 // dimensions color depth etc.
308 TEST_F(IconUtilTest, TestBasicCreateHICONFromSkBitmap) {
309   SkBitmap bitmap = CreateBlackSkBitmap(kSmallIconWidth, kSmallIconHeight);
310   HICON icon = IconUtil::CreateHICONFromSkBitmap(bitmap);
311   EXPECT_NE(icon, static_cast<HICON>(NULL));
312   ICONINFO icon_info;
313   ASSERT_TRUE(::GetIconInfo(icon, &icon_info));
314   EXPECT_TRUE(icon_info.fIcon);
315
316   // Now that have the icon information, we should obtain the specification of
317   // the icon's bitmap and make sure it matches the specification of the
318   // SkBitmap we started with.
319   //
320   // The bitmap handle contained in the icon information is a handle to a
321   // compatible bitmap so we need to call ::GetDIBits() in order to retrieve
322   // the bitmap's header information.
323   BITMAPINFO bitmap_info;
324   ::ZeroMemory(&bitmap_info, sizeof(BITMAPINFO));
325   bitmap_info.bmiHeader.biSize = sizeof(BITMAPINFO);
326   HDC hdc = ::GetDC(NULL);
327   int result = ::GetDIBits(hdc,
328                            icon_info.hbmColor,
329                            0,
330                            kSmallIconWidth,
331                            NULL,
332                            &bitmap_info,
333                            DIB_RGB_COLORS);
334   ASSERT_GT(result, 0);
335   EXPECT_EQ(bitmap_info.bmiHeader.biWidth, kSmallIconWidth);
336   EXPECT_EQ(bitmap_info.bmiHeader.biHeight, kSmallIconHeight);
337   EXPECT_EQ(bitmap_info.bmiHeader.biPlanes, 1);
338   EXPECT_EQ(bitmap_info.bmiHeader.biBitCount, 32);
339   ::ReleaseDC(NULL, hdc);
340   ::DestroyIcon(icon);
341 }
342
343 // This test case makes sure that CreateIconFileFromImageFamily creates a
344 // valid .ico file given an ImageFamily, and appropriately creates all icon
345 // sizes from the given input.
346 TEST_F(IconUtilTest, TestCreateIconFileFromImageFamily) {
347   gfx::ImageFamily image_family;
348   base::FilePath icon_filename =
349       temp_directory_.path().AppendASCII(kTempIconFilename);
350
351   // Test with only a 16x16 icon. Should only scale up to 48x48.
352   image_family.Add(gfx::Image::CreateFrom1xBitmap(
353       CreateBlackSkBitmap(kSmallIconWidth, kSmallIconHeight)));
354   ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family,
355                                                       icon_filename));
356   CheckAllIconSizes(icon_filename, 48);
357
358   // Test with a 48x48 icon. Should only scale down.
359   image_family.Add(gfx::Image::CreateFrom1xBitmap(CreateBlackSkBitmap(48, 48)));
360   ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family,
361                                                       icon_filename));
362   CheckAllIconSizes(icon_filename, 48);
363
364   // Test with a 64x64 icon. Should scale up to 256x256.
365   image_family.Add(gfx::Image::CreateFrom1xBitmap(CreateBlackSkBitmap(64, 64)));
366   ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family,
367                                                       icon_filename));
368   CheckAllIconSizes(icon_filename, 256);
369
370   // Test with a 256x256 icon. Should include the 256x256 in the output.
371   image_family.Add(gfx::Image::CreateFrom1xBitmap(
372       CreateBlackSkBitmap(256, 256)));
373   ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family,
374                                                       icon_filename));
375   CheckAllIconSizes(icon_filename, 256);
376
377   // Test with a 49x49 icon. Should scale up to 256x256, but exclude the
378   // original 49x49 representation from the output.
379   image_family.clear();
380   image_family.Add(gfx::Image::CreateFrom1xBitmap(CreateBlackSkBitmap(49, 49)));
381   ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family,
382                                                       icon_filename));
383   CheckAllIconSizes(icon_filename, 256);
384
385   // Test with a non-square 16x32 icon. Should scale up to 48, but exclude the
386   // original 16x32 representation from the output.
387   image_family.clear();
388   image_family.Add(gfx::Image::CreateFrom1xBitmap(CreateBlackSkBitmap(16, 32)));
389   ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family,
390                                                       icon_filename));
391   CheckAllIconSizes(icon_filename, 48);
392
393   // Test with a non-square 32x49 icon. Should scale up to 256, but exclude the
394   // original 32x49 representation from the output.
395   image_family.Add(gfx::Image::CreateFrom1xBitmap(CreateBlackSkBitmap(32, 49)));
396   ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family,
397                                                       icon_filename));
398   CheckAllIconSizes(icon_filename, 256);
399
400   // Test with an empty and non-empty image.
401   // The empty image should be ignored.
402   image_family.clear();
403   image_family.Add(gfx::Image());
404   image_family.Add(gfx::Image::CreateFrom1xBitmap(CreateBlackSkBitmap(16, 16)));
405   ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family,
406                                                        icon_filename));
407   CheckAllIconSizes(icon_filename, 48);
408 }
409
410 TEST_F(IconUtilTest, TestCreateSkBitmapFromIconResource48x48) {
411   HMODULE module = GetModuleHandle(NULL);
412   scoped_ptr<SkBitmap> bitmap(
413       IconUtil::CreateSkBitmapFromIconResource(module, IDR_MAINFRAME, 48));
414   ASSERT_TRUE(bitmap.get());
415   EXPECT_EQ(48, bitmap->width());
416   EXPECT_EQ(48, bitmap->height());
417 }
418
419 TEST_F(IconUtilTest, TestCreateSkBitmapFromIconResource256x256) {
420   HMODULE module = GetModuleHandle(NULL);
421   scoped_ptr<SkBitmap> bitmap(
422       IconUtil::CreateSkBitmapFromIconResource(module, IDR_MAINFRAME, 256));
423   ASSERT_TRUE(bitmap.get());
424   EXPECT_EQ(256, bitmap->width());
425   EXPECT_EQ(256, bitmap->height());
426 }
427
428 // This tests that kNumIconDimensionsUpToMediumSize has the correct value.
429 TEST_F(IconUtilTest, TestNumIconDimensionsUpToMediumSize) {
430   ASSERT_LE(IconUtil::kNumIconDimensionsUpToMediumSize,
431             IconUtil::kNumIconDimensions);
432   EXPECT_EQ(IconUtil::kMediumIconSize,
433             IconUtil::kIconDimensions[
434                 IconUtil::kNumIconDimensionsUpToMediumSize - 1]);
435 }