1// Copyright (c) 2012 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 "chrome/browser/themes/browser_theme_pack.h"
6
7#include "base/files/scoped_temp_dir.h"
8#include "base/json/json_file_value_serializer.h"
9#include "base/json/json_reader.h"
10#include "base/message_loop/message_loop.h"
11#include "base/path_service.h"
12#include "base/values.h"
13#include "chrome/browser/themes/theme_properties.h"
14#include "chrome/common/chrome_paths.h"
15#include "content/public/test/test_browser_thread.h"
16#include "grit/theme_resources.h"
17#include "testing/gtest/include/gtest/gtest.h"
18#include "ui/gfx/color_utils.h"
19#include "ui/gfx/image/image.h"
20#include "ui/gfx/image/image_skia.h"
21#include "ui/gfx/image/image_skia_rep.h"
22
23using content::BrowserThread;
24using extensions::Extension;
25
26// Maps scale factors (enum values) to file path.
27// A similar typedef in BrowserThemePack is private.
28typedef std::map<ui::ScaleFactor, base::FilePath> TestScaleFactorToFileMap;
29
30// Maps image ids to maps of scale factors to file paths.
31// A similar typedef in BrowserThemePack is private.
32typedef std::map<int, TestScaleFactorToFileMap> TestFilePathMap;
33
34class BrowserThemePackTest : public ::testing::Test {
35 public:
36  BrowserThemePackTest()
37      : message_loop(),
38        fake_ui_thread(BrowserThread::UI, &message_loop),
39        fake_file_thread(BrowserThread::FILE, &message_loop) {
40    std::vector<ui::ScaleFactor> scale_factors;
41    scale_factors.push_back(ui::SCALE_FACTOR_100P);
42    scale_factors.push_back(ui::SCALE_FACTOR_200P);
43    scoped_set_supported_scale_factors_.reset(
44      new ui::test::ScopedSetSupportedScaleFactors(scale_factors));
45    theme_pack_ = new BrowserThemePack();
46  }
47
48  // Transformation for link underline colors.
49  SkColor BuildThirdOpacity(SkColor color_link) {
50    return SkColorSetA(color_link, SkColorGetA(color_link) / 3);
51  }
52
53  void GenerateDefaultFrameColor(std::map<int, SkColor>* colors,
54                                 int color, int tint) {
55    (*colors)[color] = HSLShift(
56        ThemeProperties::GetDefaultColor(
57            ThemeProperties::COLOR_FRAME),
58        ThemeProperties::GetDefaultTint(tint));
59  }
60
61  // Returns a mapping from each COLOR_* constant to the default value for this
62  // constant. Callers get this map, and then modify expected values and then
63  // run the resulting thing through VerifyColorMap().
64  std::map<int, SkColor> GetDefaultColorMap() {
65    std::map<int, SkColor> colors;
66    for (int i = ThemeProperties::COLOR_FRAME;
67         i <= ThemeProperties::COLOR_BUTTON_BACKGROUND; ++i) {
68      colors[i] = ThemeProperties::GetDefaultColor(i);
69    }
70
71    GenerateDefaultFrameColor(&colors, ThemeProperties::COLOR_FRAME,
72                              ThemeProperties::TINT_FRAME);
73    GenerateDefaultFrameColor(&colors,
74                              ThemeProperties::COLOR_FRAME_INACTIVE,
75                              ThemeProperties::TINT_FRAME_INACTIVE);
76    GenerateDefaultFrameColor(&colors,
77                              ThemeProperties::COLOR_FRAME_INCOGNITO,
78                              ThemeProperties::TINT_FRAME_INCOGNITO);
79    GenerateDefaultFrameColor(
80        &colors,
81        ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE,
82        ThemeProperties::TINT_FRAME_INCOGNITO_INACTIVE);
83
84    return colors;
85  }
86
87  void VerifyColorMap(const std::map<int, SkColor>& color_map) {
88    for (std::map<int, SkColor>::const_iterator it = color_map.begin();
89         it != color_map.end(); ++it) {
90      SkColor color = ThemeProperties::GetDefaultColor(it->first);
91      theme_pack_->GetColor(it->first, &color);
92      EXPECT_EQ(it->second, color) << "Color id = " << it->first;
93    }
94  }
95
96  void LoadColorJSON(const std::string& json) {
97    scoped_ptr<base::Value> value(base::JSONReader::Read(json));
98    ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
99    LoadColorDictionary(static_cast<base::DictionaryValue*>(value.get()));
100  }
101
102  void LoadColorDictionary(base::DictionaryValue* value) {
103    theme_pack_->BuildColorsFromJSON(value);
104  }
105
106  void LoadTintJSON(const std::string& json) {
107    scoped_ptr<base::Value> value(base::JSONReader::Read(json));
108    ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
109    LoadTintDictionary(static_cast<base::DictionaryValue*>(value.get()));
110  }
111
112  void LoadTintDictionary(base::DictionaryValue* value) {
113    theme_pack_->BuildTintsFromJSON(value);
114  }
115
116  void LoadDisplayPropertiesJSON(const std::string& json) {
117    scoped_ptr<base::Value> value(base::JSONReader::Read(json));
118    ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
119    LoadDisplayPropertiesDictionary(
120        static_cast<base::DictionaryValue*>(value.get()));
121  }
122
123  void LoadDisplayPropertiesDictionary(base::DictionaryValue* value) {
124    theme_pack_->BuildDisplayPropertiesFromJSON(value);
125  }
126
127  void ParseImageNamesJSON(const std::string& json,
128                           TestFilePathMap* out_file_paths) {
129    scoped_ptr<base::Value> value(base::JSONReader::Read(json));
130    ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
131    ParseImageNamesDictionary(static_cast<base::DictionaryValue*>(value.get()),
132                              out_file_paths);
133  }
134
135  void ParseImageNamesDictionary(
136      base::DictionaryValue* value,
137      TestFilePathMap* out_file_paths) {
138    theme_pack_->ParseImageNamesFromJSON(value, base::FilePath(),
139                                         out_file_paths);
140
141    // Build the source image list for HasCustomImage().
142    theme_pack_->BuildSourceImagesArray(*out_file_paths);
143  }
144
145  bool LoadRawBitmapsTo(const TestFilePathMap& out_file_paths) {
146    return theme_pack_->LoadRawBitmapsTo(out_file_paths,
147                                         &theme_pack_->images_on_ui_thread_);
148  }
149
150  // This function returns void in order to be able use ASSERT_...
151  // The BrowserThemePack is returned in |pack|.
152  void BuildFromUnpackedExtension(const base::FilePath& extension_path,
153                                  scoped_refptr<BrowserThemePack>& pack) {
154    base::FilePath manifest_path =
155        extension_path.AppendASCII("manifest.json");
156    std::string error;
157    JSONFileValueSerializer serializer(manifest_path);
158    scoped_ptr<base::DictionaryValue> valid_value(
159        static_cast<base::DictionaryValue*>(
160            serializer.Deserialize(NULL, &error)));
161    EXPECT_EQ("", error);
162    ASSERT_TRUE(valid_value.get());
163    scoped_refptr<Extension> extension(
164        Extension::Create(
165            extension_path,
166            extensions::Manifest::INVALID_LOCATION,
167            *valid_value,
168            Extension::REQUIRE_KEY,
169            &error));
170    ASSERT_TRUE(extension.get());
171    ASSERT_EQ("", error);
172    pack = BrowserThemePack::BuildFromExtension(extension.get());
173    ASSERT_TRUE(pack.get());
174  }
175
176  base::FilePath GetStarGazingPath() {
177    base::FilePath test_path;
178    if (!PathService::Get(chrome::DIR_TEST_DATA, &test_path)) {
179      NOTREACHED();
180      return test_path;
181    }
182
183    test_path = test_path.AppendASCII("profiles");
184    test_path = test_path.AppendASCII("profile_with_complex_theme");
185    test_path = test_path.AppendASCII("Default");
186    test_path = test_path.AppendASCII("Extensions");
187    test_path = test_path.AppendASCII("mblmlcbknbnfebdfjnolmcapmdofhmme");
188    test_path = test_path.AppendASCII("1.1");
189    return base::FilePath(test_path);
190  }
191
192  base::FilePath GetHiDpiThemePath() {
193    base::FilePath test_path;
194    if (!PathService::Get(chrome::DIR_TEST_DATA, &test_path)) {
195      NOTREACHED();
196      return test_path;
197    }
198    test_path = test_path.AppendASCII("extensions");
199    test_path = test_path.AppendASCII("theme_hidpi");
200    return base::FilePath(test_path);
201  }
202
203  // Verifies the data in star gazing. We do this multiple times for different
204  // BrowserThemePack objects to make sure it works in generated and mmapped
205  // mode correctly.
206  void VerifyStarGazing(BrowserThemePack* pack) {
207    // First check that values we know exist, exist.
208    SkColor color;
209    EXPECT_TRUE(pack->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT,
210                               &color));
211    EXPECT_EQ(SK_ColorBLACK, color);
212
213    EXPECT_TRUE(pack->GetColor(ThemeProperties::COLOR_NTP_BACKGROUND,
214                               &color));
215    EXPECT_EQ(SkColorSetRGB(57, 137, 194), color);
216
217    color_utils::HSL expected = { 0.6, 0.553, 0.5 };
218    color_utils::HSL actual;
219    EXPECT_TRUE(pack->GetTint(ThemeProperties::TINT_BUTTONS, &actual));
220    EXPECT_DOUBLE_EQ(expected.h, actual.h);
221    EXPECT_DOUBLE_EQ(expected.s, actual.s);
222    EXPECT_DOUBLE_EQ(expected.l, actual.l);
223
224    int val;
225    EXPECT_TRUE(pack->GetDisplayProperty(
226        ThemeProperties::NTP_BACKGROUND_ALIGNMENT, &val));
227    EXPECT_EQ(ThemeProperties::ALIGN_TOP, val);
228
229    // The stargazing theme defines the following images:
230    EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_BUTTON_BACKGROUND));
231    EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_FRAME));
232    EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_NTP_BACKGROUND));
233    EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_TAB_BACKGROUND));
234    EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_TOOLBAR));
235    EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_WINDOW_CONTROL_BACKGROUND));
236
237    // Here are a few images that we shouldn't expect because even though
238    // they're included in the theme pack, they were autogenerated and
239    // therefore shouldn't show up when calling HasCustomImage().
240    EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_FRAME_INACTIVE));
241    EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_FRAME_INCOGNITO));
242    EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_FRAME_INCOGNITO_INACTIVE));
243    EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_TAB_BACKGROUND_INCOGNITO));
244
245    // Make sure we don't have phantom data.
246    EXPECT_FALSE(pack->GetColor(ThemeProperties::COLOR_CONTROL_BACKGROUND,
247                                &color));
248    EXPECT_FALSE(pack->GetTint(ThemeProperties::TINT_FRAME, &actual));
249  }
250
251  void VerifyHiDpiTheme(BrowserThemePack* pack) {
252    // The high DPI theme defines the following images:
253    EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_FRAME));
254    EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_FRAME_INACTIVE));
255    EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_FRAME_INCOGNITO));
256    EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_FRAME_INCOGNITO_INACTIVE));
257    EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_TOOLBAR));
258
259    // The high DPI theme does not define the following images:
260    EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_TAB_BACKGROUND));
261    EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_TAB_BACKGROUND));
262    EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_TAB_BACKGROUND_INCOGNITO));
263    EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_TAB_BACKGROUND_V));
264    EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_NTP_BACKGROUND));
265    EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_FRAME_OVERLAY));
266    EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_FRAME_OVERLAY_INACTIVE));
267    EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_BUTTON_BACKGROUND));
268    EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_NTP_ATTRIBUTION));
269    EXPECT_FALSE(pack->HasCustomImage(IDR_THEME_WINDOW_CONTROL_BACKGROUND));
270
271    // Compare some known pixel colors at know locations for a theme
272    // image where two different PNG files were specified for scales 100%
273    // and 200% respectively.
274    int idr = IDR_THEME_FRAME;
275    gfx::Image image = pack->GetImageNamed(idr);
276    EXPECT_FALSE(image.IsEmpty());
277    const gfx::ImageSkia* image_skia = image.ToImageSkia();
278    ASSERT_TRUE(image_skia);
279    // Scale 100%.
280    const gfx::ImageSkiaRep& rep1 = image_skia->GetRepresentation(1.0f);
281    ASSERT_FALSE(rep1.is_null());
282    EXPECT_EQ(80, rep1.sk_bitmap().width());
283    EXPECT_EQ(80, rep1.sk_bitmap().height());
284    rep1.sk_bitmap().lockPixels();
285    EXPECT_EQ(SkColorSetRGB(255, 255, 255), rep1.sk_bitmap().getColor( 4,  4));
286    EXPECT_EQ(SkColorSetRGB(255, 255, 255), rep1.sk_bitmap().getColor( 8,  8));
287    EXPECT_EQ(SkColorSetRGB(  0, 241, 237), rep1.sk_bitmap().getColor(16, 16));
288    EXPECT_EQ(SkColorSetRGB(255, 255, 255), rep1.sk_bitmap().getColor(24, 24));
289    EXPECT_EQ(SkColorSetRGB(  0, 241, 237), rep1.sk_bitmap().getColor(32, 32));
290    rep1.sk_bitmap().unlockPixels();
291    // Scale 200%.
292    const gfx::ImageSkiaRep& rep2 = image_skia->GetRepresentation(2.0f);
293    ASSERT_FALSE(rep2.is_null());
294    EXPECT_EQ(160, rep2.sk_bitmap().width());
295    EXPECT_EQ(160, rep2.sk_bitmap().height());
296    rep2.sk_bitmap().lockPixels();
297    EXPECT_EQ(SkColorSetRGB(255, 255, 255), rep2.sk_bitmap().getColor( 4,  4));
298    EXPECT_EQ(SkColorSetRGB(223,  42,   0), rep2.sk_bitmap().getColor( 8,  8));
299    EXPECT_EQ(SkColorSetRGB(223,  42,   0), rep2.sk_bitmap().getColor(16, 16));
300    EXPECT_EQ(SkColorSetRGB(223,  42,   0), rep2.sk_bitmap().getColor(24, 24));
301    EXPECT_EQ(SkColorSetRGB(255, 255, 255), rep2.sk_bitmap().getColor(32, 32));
302    rep2.sk_bitmap().unlockPixels();
303
304    // TODO(sschmitz): I plan to remove the following (to the end of the fct)
305    // Reason: this test may be brittle. It depends on details of how we scale
306    // an 100% image to an 200% image. If there's filtering etc, this test would
307    // break. Also High DPI is new, but scaling from 100% to 200% is not new
308    // and need not be tested here. But in the interrim it is useful to verify
309    // that this image was scaled and did not appear in the input.
310
311    // Compare pixel colors and locations for a theme image that had
312    // only one PNG file specified (for scale 100%). The representation
313    // for scale of 200% was computed.
314    idr = IDR_THEME_FRAME_INCOGNITO_INACTIVE;
315    image = pack->GetImageNamed(idr);
316    EXPECT_FALSE(image.IsEmpty());
317    image_skia = image.ToImageSkia();
318    ASSERT_TRUE(image_skia);
319    // Scale 100%.
320    const gfx::ImageSkiaRep& rep3 = image_skia->GetRepresentation(1.0f);
321    ASSERT_FALSE(rep3.is_null());
322    EXPECT_EQ(80, rep3.sk_bitmap().width());
323    EXPECT_EQ(80, rep3.sk_bitmap().height());
324    rep3.sk_bitmap().lockPixels();
325    // We take samples of colors and locations along the diagonal whenever
326    // the color changes. Note these colors are slightly different from
327    // the input PNG file due to input processing.
328    std::vector<std::pair<int, SkColor> > normal;
329    int xy = 0;
330    SkColor color = rep3.sk_bitmap().getColor(xy, xy);
331    normal.push_back(std::make_pair(xy, color));
332    for (int xy = 0; xy < 40; ++xy) {
333      SkColor next_color = rep3.sk_bitmap().getColor(xy, xy);
334      if (next_color != color) {
335        color = next_color;
336        normal.push_back(std::make_pair(xy, color));
337      }
338    }
339    EXPECT_EQ(static_cast<size_t>(9), normal.size());
340    rep3.sk_bitmap().unlockPixels();
341    // Scale 200%.
342    const gfx::ImageSkiaRep& rep4 = image_skia->GetRepresentation(2.0f);
343    ASSERT_FALSE(rep4.is_null());
344    EXPECT_EQ(160, rep4.sk_bitmap().width());
345    EXPECT_EQ(160, rep4.sk_bitmap().height());
346    rep4.sk_bitmap().lockPixels();
347    // We expect the same colors and at locations scaled by 2
348    // since this bitmap was scaled by 2.
349    for (size_t i = 0; i < normal.size(); ++i) {
350      int xy = 2 * normal[i].first;
351      SkColor color = normal[i].second;
352      EXPECT_EQ(color, rep4.sk_bitmap().getColor(xy, xy));
353    }
354    rep4.sk_bitmap().unlockPixels();
355  }
356
357  base::MessageLoop message_loop;
358  content::TestBrowserThread fake_ui_thread;
359  content::TestBrowserThread fake_file_thread;
360
361  typedef scoped_ptr<ui::test::ScopedSetSupportedScaleFactors>
362      ScopedSetSupportedScaleFactors;
363  ScopedSetSupportedScaleFactors scoped_set_supported_scale_factors_;
364  scoped_refptr<BrowserThemePack> theme_pack_;
365};
366
367
368TEST_F(BrowserThemePackTest, DeriveUnderlineLinkColor) {
369  // If we specify a link color, but don't specify the underline color, the
370  // theme provider should create one.
371  std::string color_json = "{ \"ntp_link\": [128, 128, 128],"
372                           "  \"ntp_section_link\": [128, 128, 128] }";
373  LoadColorJSON(color_json);
374
375  std::map<int, SkColor> colors = GetDefaultColorMap();
376  SkColor link_color = SkColorSetRGB(128, 128, 128);
377  colors[ThemeProperties::COLOR_NTP_LINK] = link_color;
378  colors[ThemeProperties::COLOR_NTP_LINK_UNDERLINE] =
379      BuildThirdOpacity(link_color);
380  colors[ThemeProperties::COLOR_NTP_SECTION_LINK] = link_color;
381  colors[ThemeProperties::COLOR_NTP_SECTION_LINK_UNDERLINE] =
382      BuildThirdOpacity(link_color);
383
384  VerifyColorMap(colors);
385}
386
387TEST_F(BrowserThemePackTest, ProvideUnderlineLinkColor) {
388  // If we specify the underline color, it shouldn't try to generate one.
389  std::string color_json = "{ \"ntp_link\": [128, 128, 128],"
390                           "  \"ntp_link_underline\": [255, 255, 255],"
391                           "  \"ntp_section_link\": [128, 128, 128],"
392                           "  \"ntp_section_link_underline\": [255, 255, 255]"
393                           "}";
394  LoadColorJSON(color_json);
395
396  std::map<int, SkColor> colors = GetDefaultColorMap();
397  SkColor link_color = SkColorSetRGB(128, 128, 128);
398  SkColor underline_color = SkColorSetRGB(255, 255, 255);
399  colors[ThemeProperties::COLOR_NTP_LINK] = link_color;
400  colors[ThemeProperties::COLOR_NTP_LINK_UNDERLINE] = underline_color;
401  colors[ThemeProperties::COLOR_NTP_SECTION_LINK] = link_color;
402  colors[ThemeProperties::COLOR_NTP_SECTION_LINK_UNDERLINE] =
403      underline_color;
404
405  VerifyColorMap(colors);
406}
407
408TEST_F(BrowserThemePackTest, UseSectionColorAsNTPHeader) {
409  std::string color_json = "{ \"ntp_section\": [190, 190, 190] }";
410  LoadColorJSON(color_json);
411
412  std::map<int, SkColor> colors = GetDefaultColorMap();
413  SkColor ntp_color = SkColorSetRGB(190, 190, 190);
414  colors[ThemeProperties::COLOR_NTP_HEADER] = ntp_color;
415  colors[ThemeProperties::COLOR_NTP_SECTION] = ntp_color;
416  VerifyColorMap(colors);
417}
418
419TEST_F(BrowserThemePackTest, ProvideNtpHeaderColor) {
420  std::string color_json = "{ \"ntp_header\": [120, 120, 120], "
421                           "  \"ntp_section\": [190, 190, 190] }";
422  LoadColorJSON(color_json);
423
424  std::map<int, SkColor> colors = GetDefaultColorMap();
425  SkColor ntp_header = SkColorSetRGB(120, 120, 120);
426  SkColor ntp_section = SkColorSetRGB(190, 190, 190);
427  colors[ThemeProperties::COLOR_NTP_HEADER] = ntp_header;
428  colors[ThemeProperties::COLOR_NTP_SECTION] = ntp_section;
429  VerifyColorMap(colors);
430}
431
432TEST_F(BrowserThemePackTest, CanReadTints) {
433  std::string tint_json = "{ \"buttons\": [ 0.5, 0.5, 0.5 ] }";
434  LoadTintJSON(tint_json);
435
436  color_utils::HSL expected = { 0.5, 0.5, 0.5 };
437  color_utils::HSL actual = { -1, -1, -1 };
438  EXPECT_TRUE(theme_pack_->GetTint(
439      ThemeProperties::TINT_BUTTONS, &actual));
440  EXPECT_DOUBLE_EQ(expected.h, actual.h);
441  EXPECT_DOUBLE_EQ(expected.s, actual.s);
442  EXPECT_DOUBLE_EQ(expected.l, actual.l);
443}
444
445TEST_F(BrowserThemePackTest, CanReadDisplayProperties) {
446  std::string json = "{ \"ntp_background_alignment\": \"bottom\", "
447                     "  \"ntp_background_repeat\": \"repeat-x\", "
448                     "  \"ntp_logo_alternate\": 0 }";
449  LoadDisplayPropertiesJSON(json);
450
451  int out_val;
452  EXPECT_TRUE(theme_pack_->GetDisplayProperty(
453      ThemeProperties::NTP_BACKGROUND_ALIGNMENT, &out_val));
454  EXPECT_EQ(ThemeProperties::ALIGN_BOTTOM, out_val);
455
456  EXPECT_TRUE(theme_pack_->GetDisplayProperty(
457      ThemeProperties::NTP_BACKGROUND_TILING, &out_val));
458  EXPECT_EQ(ThemeProperties::REPEAT_X, out_val);
459
460  EXPECT_TRUE(theme_pack_->GetDisplayProperty(
461      ThemeProperties::NTP_LOGO_ALTERNATE, &out_val));
462  EXPECT_EQ(0, out_val);
463}
464
465TEST_F(BrowserThemePackTest, CanParsePaths) {
466  std::string path_json = "{ \"theme_button_background\": \"one\", "
467                          "  \"theme_toolbar\": \"two\" }";
468  TestFilePathMap out_file_paths;
469  ParseImageNamesJSON(path_json, &out_file_paths);
470
471  size_t expected_file_paths = 2u;
472#if defined(USE_ASH) && !defined(OS_CHROMEOS)
473  // On desktop builds with ash, additional theme paths are generated to map to
474  // the special resource ids like IDR_THEME_FRAME_DESKTOP, etc
475  expected_file_paths = 3u;
476#endif
477  EXPECT_EQ(expected_file_paths, out_file_paths.size());
478  // "12" and "5" are internal constants to BrowserThemePack and are
479  // PRS_THEME_BUTTON_BACKGROUND and PRS_THEME_TOOLBAR, but they are
480  // implementation details that shouldn't be exported.
481  // By default the scale factor is for 100%.
482  EXPECT_TRUE(base::FilePath(FILE_PATH_LITERAL("one")) ==
483              out_file_paths[12][ui::SCALE_FACTOR_100P]);
484  EXPECT_TRUE(base::FilePath(FILE_PATH_LITERAL("two")) ==
485              out_file_paths[5][ui::SCALE_FACTOR_100P]);
486}
487
488TEST_F(BrowserThemePackTest, InvalidPathNames) {
489  std::string path_json = "{ \"wrong\": [1], "
490                          "  \"theme_button_background\": \"one\", "
491                          "  \"not_a_thing\": \"blah\" }";
492  TestFilePathMap out_file_paths;
493  ParseImageNamesJSON(path_json, &out_file_paths);
494
495  // We should have only parsed one valid path out of that mess above.
496  EXPECT_EQ(1u, out_file_paths.size());
497}
498
499TEST_F(BrowserThemePackTest, InvalidColors) {
500  std::string invalid_color = "{ \"toolbar\": [\"dog\", \"cat\", [12]], "
501                              "  \"sound\": \"woof\" }";
502  LoadColorJSON(invalid_color);
503  std::map<int, SkColor> colors = GetDefaultColorMap();
504  VerifyColorMap(colors);
505}
506
507TEST_F(BrowserThemePackTest, InvalidTints) {
508  std::string invalid_tints = "{ \"buttons\": [ \"dog\", \"cat\", [\"x\"]], "
509                              "  \"invalid\": \"entry\" }";
510  LoadTintJSON(invalid_tints);
511
512  // We shouldn't have a buttons tint, as it was invalid.
513  color_utils::HSL actual = { -1, -1, -1 };
514  EXPECT_FALSE(theme_pack_->GetTint(ThemeProperties::TINT_BUTTONS,
515                                    &actual));
516}
517
518TEST_F(BrowserThemePackTest, InvalidDisplayProperties) {
519  std::string invalid_properties = "{ \"ntp_background_alignment\": [15], "
520                                   "  \"junk\": [15.3] }";
521  LoadDisplayPropertiesJSON(invalid_properties);
522
523  int out_val;
524  EXPECT_FALSE(theme_pack_->GetDisplayProperty(
525      ThemeProperties::NTP_BACKGROUND_ALIGNMENT, &out_val));
526}
527
528// These three tests should just not cause a segmentation fault.
529TEST_F(BrowserThemePackTest, NullPaths) {
530  TestFilePathMap out_file_paths;
531  ParseImageNamesDictionary(NULL, &out_file_paths);
532}
533
534TEST_F(BrowserThemePackTest, NullTints) {
535  LoadTintDictionary(NULL);
536}
537
538TEST_F(BrowserThemePackTest, NullColors) {
539  LoadColorDictionary(NULL);
540}
541
542TEST_F(BrowserThemePackTest, NullDisplayProperties) {
543  LoadDisplayPropertiesDictionary(NULL);
544}
545
546TEST_F(BrowserThemePackTest, TestHasCustomImage) {
547  // HasCustomImage should only return true for images that exist in the
548  // extension and not for autogenerated images.
549  std::string images = "{ \"theme_frame\": \"one\" }";
550  TestFilePathMap out_file_paths;
551  ParseImageNamesJSON(images, &out_file_paths);
552
553  EXPECT_TRUE(theme_pack_->HasCustomImage(IDR_THEME_FRAME));
554  EXPECT_FALSE(theme_pack_->HasCustomImage(IDR_THEME_FRAME_INCOGNITO));
555}
556
557TEST_F(BrowserThemePackTest, TestNonExistantImages) {
558  std::string images = "{ \"theme_frame\": \"does_not_exist\" }";
559  TestFilePathMap out_file_paths;
560  ParseImageNamesJSON(images, &out_file_paths);
561
562  EXPECT_FALSE(LoadRawBitmapsTo(out_file_paths));
563}
564
565// TODO(erg): This test should actually test more of the built resources from
566// the extension data, but for now, exists so valgrind can test some of the
567// tricky memory stuff that BrowserThemePack does.
568TEST_F(BrowserThemePackTest, CanBuildAndReadPack) {
569  base::ScopedTempDir dir;
570  ASSERT_TRUE(dir.CreateUniqueTempDir());
571  base::FilePath file = dir.path().AppendASCII("data.pak");
572
573  // Part 1: Build the pack from an extension.
574  {
575    base::FilePath star_gazing_path = GetStarGazingPath();
576    scoped_refptr<BrowserThemePack> pack;
577    BuildFromUnpackedExtension(star_gazing_path, pack);
578    ASSERT_TRUE(pack->WriteToDisk(file));
579    VerifyStarGazing(pack.get());
580  }
581
582  // Part 2: Try to read back the data pack that we just wrote to disk.
583  {
584    scoped_refptr<BrowserThemePack> pack =
585        BrowserThemePack::BuildFromDataPack(
586            file, "mblmlcbknbnfebdfjnolmcapmdofhmme");
587    ASSERT_TRUE(pack.get());
588    VerifyStarGazing(pack.get());
589  }
590}
591
592TEST_F(BrowserThemePackTest, HiDpiThemeTest) {
593  base::ScopedTempDir dir;
594  ASSERT_TRUE(dir.CreateUniqueTempDir());
595  base::FilePath file = dir.path().AppendASCII("theme_data.pak");
596
597  // Part 1: Build the pack from an extension.
598  {
599    base::FilePath hidpi_path = GetHiDpiThemePath();
600    scoped_refptr<BrowserThemePack> pack;
601    BuildFromUnpackedExtension(hidpi_path, pack);
602    ASSERT_TRUE(pack->WriteToDisk(file));
603    VerifyHiDpiTheme(pack.get());
604  }
605
606  // Part 2: Try to read back the data pack that we just wrote to disk.
607  {
608    scoped_refptr<BrowserThemePack> pack =
609        BrowserThemePack::BuildFromDataPack(file, "gllekhaobjnhgeag");
610    ASSERT_TRUE(pack.get());
611    VerifyHiDpiTheme(pack.get());
612  }
613}
614