extension_l10n_util_unittest.cc revision 8ae428e0fb7feea16d79853f29447469a93bedff
1// Copyright (c) 2010 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 "app/l10n_util.h"
6#include "base/file_path.h"
7#include "base/file_util.h"
8#include "base/linked_ptr.h"
9#include "base/path_service.h"
10#include "base/scoped_ptr.h"
11#include "base/scoped_temp_dir.h"
12#include "base/values.h"
13#include "chrome/common/chrome_paths.h"
14#include "chrome/common/extensions/extension.h"
15#include "chrome/common/extensions/extension_constants.h"
16#include "chrome/common/extensions/extension_l10n_util.h"
17#include "chrome/common/extensions/extension_message_bundle.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20namespace errors = extension_manifest_errors;
21namespace keys = extension_manifest_keys;
22
23namespace {
24
25TEST(ExtensionL10nUtil, GetValidLocalesEmptyLocaleFolder) {
26  ScopedTempDir temp;
27  ASSERT_TRUE(temp.CreateUniqueTempDir());
28
29  FilePath src_path = temp.path().Append(Extension::kLocaleFolder);
30  ASSERT_TRUE(file_util::CreateDirectory(src_path));
31
32  std::string error;
33  std::set<std::string> locales;
34  EXPECT_FALSE(extension_l10n_util::GetValidLocales(src_path,
35                                                    &locales,
36                                                    &error));
37
38  EXPECT_TRUE(locales.empty());
39}
40
41TEST(ExtensionL10nUtil, GetValidLocalesWithValidLocaleNoMessagesFile) {
42  ScopedTempDir temp;
43  ASSERT_TRUE(temp.CreateUniqueTempDir());
44
45  FilePath src_path = temp.path().Append(Extension::kLocaleFolder);
46  ASSERT_TRUE(file_util::CreateDirectory(src_path));
47  ASSERT_TRUE(file_util::CreateDirectory(src_path.AppendASCII("sr")));
48
49  std::string error;
50  std::set<std::string> locales;
51  EXPECT_FALSE(extension_l10n_util::GetValidLocales(src_path,
52                                                    &locales,
53                                                    &error));
54
55  EXPECT_TRUE(locales.empty());
56}
57
58TEST(ExtensionL10nUtil, GetValidLocalesWithUnsupportedLocale) {
59  ScopedTempDir temp;
60  ASSERT_TRUE(temp.CreateUniqueTempDir());
61
62  FilePath src_path = temp.path().Append(Extension::kLocaleFolder);
63  ASSERT_TRUE(file_util::CreateDirectory(src_path));
64  // Supported locale.
65  FilePath locale_1 = src_path.AppendASCII("sr");
66  ASSERT_TRUE(file_util::CreateDirectory(locale_1));
67  std::string data("whatever");
68  ASSERT_TRUE(file_util::WriteFile(
69      locale_1.Append(Extension::kMessagesFilename),
70      data.c_str(), data.length()));
71  // Unsupported locale.
72  ASSERT_TRUE(file_util::CreateDirectory(src_path.AppendASCII("xxx_yyy")));
73
74  std::string error;
75  std::set<std::string> locales;
76  EXPECT_TRUE(extension_l10n_util::GetValidLocales(src_path,
77                                                   &locales,
78                                                   &error));
79
80  EXPECT_FALSE(locales.empty());
81  EXPECT_TRUE(locales.find("sr") != locales.end());
82  EXPECT_FALSE(locales.find("xxx_yyy") != locales.end());
83}
84
85TEST(ExtensionL10nUtil, GetValidLocalesWithValidLocalesAndMessagesFile) {
86  FilePath install_dir;
87  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &install_dir));
88  install_dir = install_dir.AppendASCII("extensions")
89      .AppendASCII("good")
90      .AppendASCII("Extensions")
91      .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
92      .AppendASCII("1.0.0.0")
93      .Append(Extension::kLocaleFolder);
94
95  std::string error;
96  std::set<std::string> locales;
97  EXPECT_TRUE(extension_l10n_util::GetValidLocales(install_dir,
98                                                   &locales,
99                                                   &error));
100  EXPECT_EQ(3U, locales.size());
101  EXPECT_TRUE(locales.find("sr") != locales.end());
102  EXPECT_TRUE(locales.find("en") != locales.end());
103  EXPECT_TRUE(locales.find("en_US") != locales.end());
104}
105
106TEST(ExtensionL10nUtil, LoadMessageCatalogsValidFallback) {
107  FilePath install_dir;
108  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &install_dir));
109  install_dir = install_dir.AppendASCII("extensions")
110      .AppendASCII("good")
111      .AppendASCII("Extensions")
112      .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
113      .AppendASCII("1.0.0.0")
114      .Append(Extension::kLocaleFolder);
115
116  std::string error;
117  std::set<std::string> locales;
118  EXPECT_TRUE(extension_l10n_util::GetValidLocales(install_dir,
119                                                   &locales,
120                                                   &error));
121
122  scoped_ptr<ExtensionMessageBundle> bundle(
123      extension_l10n_util::LoadMessageCatalogs(
124          install_dir, "sr", "en_US", locales, &error));
125  ASSERT_FALSE(NULL == bundle.get());
126  EXPECT_TRUE(error.empty());
127  EXPECT_EQ("Color", bundle->GetL10nMessage("color"));
128  EXPECT_EQ("Not in the US or GB.", bundle->GetL10nMessage("not_in_US_or_GB"));
129}
130
131TEST(ExtensionL10nUtil, LoadMessageCatalogsMissingFiles) {
132  ScopedTempDir temp;
133  ASSERT_TRUE(temp.CreateUniqueTempDir());
134
135  FilePath src_path = temp.path().Append(Extension::kLocaleFolder);
136  ASSERT_TRUE(file_util::CreateDirectory(src_path));
137
138  std::set<std::string> valid_locales;
139  valid_locales.insert("sr");
140  valid_locales.insert("en");
141  std::string error;
142  EXPECT_TRUE(NULL == extension_l10n_util::LoadMessageCatalogs(src_path,
143                                                               "en",
144                                                               "sr",
145                                                               valid_locales,
146                                                               &error));
147  EXPECT_FALSE(error.empty());
148}
149
150TEST(ExtensionL10nUtil, LoadMessageCatalogsBadJSONFormat) {
151  ScopedTempDir temp;
152  ASSERT_TRUE(temp.CreateUniqueTempDir());
153
154  FilePath src_path = temp.path().Append(Extension::kLocaleFolder);
155  ASSERT_TRUE(file_util::CreateDirectory(src_path));
156
157  FilePath locale = src_path.AppendASCII("sr");
158  ASSERT_TRUE(file_util::CreateDirectory(locale));
159
160  std::string data = "{ \"name\":";
161  ASSERT_TRUE(
162      file_util::WriteFile(locale.Append(Extension::kMessagesFilename),
163                           data.c_str(), data.length()));
164
165  std::set<std::string> valid_locales;
166  valid_locales.insert("sr");
167  valid_locales.insert("en_US");
168  std::string error;
169  EXPECT_TRUE(NULL == extension_l10n_util::LoadMessageCatalogs(src_path,
170                                                              "en_US",
171                                                              "sr",
172                                                              valid_locales,
173                                                              &error));
174  EXPECT_EQ("Line: 1, column: 10, Syntax error.", error);
175}
176
177TEST(ExtensionL10nUtil, LoadMessageCatalogsDuplicateKeys) {
178  ScopedTempDir temp;
179  ASSERT_TRUE(temp.CreateUniqueTempDir());
180
181  FilePath src_path = temp.path().Append(Extension::kLocaleFolder);
182  ASSERT_TRUE(file_util::CreateDirectory(src_path));
183
184  FilePath locale_1 = src_path.AppendASCII("en");
185  ASSERT_TRUE(file_util::CreateDirectory(locale_1));
186
187  std::string data =
188    "{ \"name\": { \"message\": \"something\" }, "
189    "\"name\": { \"message\": \"something else\" } }";
190  ASSERT_TRUE(
191      file_util::WriteFile(locale_1.Append(Extension::kMessagesFilename),
192                           data.c_str(), data.length()));
193
194  FilePath locale_2 = src_path.AppendASCII("sr");
195  ASSERT_TRUE(file_util::CreateDirectory(locale_2));
196
197  ASSERT_TRUE(
198      file_util::WriteFile(locale_2.Append(Extension::kMessagesFilename),
199                           data.c_str(), data.length()));
200
201  std::set<std::string> valid_locales;
202  valid_locales.insert("sr");
203  valid_locales.insert("en");
204  std::string error;
205  // JSON parser hides duplicates. We are going to get only one key/value
206  // pair at the end.
207  scoped_ptr<ExtensionMessageBundle> message_bundle(
208      extension_l10n_util::LoadMessageCatalogs(src_path,
209                                               "en",
210                                               "sr",
211                                               valid_locales,
212                                               &error));
213  EXPECT_TRUE(NULL != message_bundle.get());
214  EXPECT_TRUE(error.empty());
215}
216
217TEST(ExtensionL10nUtil, GetParentLocales) {
218  std::vector<std::string> locales;
219  const std::string top_locale("sr_Cyrl_RS");
220  extension_l10n_util::GetParentLocales(top_locale, &locales);
221
222  ASSERT_EQ(3U, locales.size());
223  EXPECT_EQ("sr_Cyrl_RS", locales[0]);
224  EXPECT_EQ("sr_Cyrl", locales[1]);
225  EXPECT_EQ("sr", locales[2]);
226}
227
228// Caller owns the returned object.
229ExtensionMessageBundle* CreateManifestBundle() {
230  linked_ptr<DictionaryValue> catalog(new DictionaryValue);
231
232  DictionaryValue* name_tree = new DictionaryValue();
233  name_tree->SetString("message", "name");
234  catalog->Set("name", name_tree);
235
236  DictionaryValue* description_tree = new DictionaryValue();
237  description_tree->SetString("message", "description");
238  catalog->Set("description", description_tree);
239
240  DictionaryValue* action_title_tree = new DictionaryValue();
241  action_title_tree->SetString("message", "action title");
242  catalog->Set("title", action_title_tree);
243
244  std::vector<linked_ptr<DictionaryValue> > catalogs;
245  catalogs.push_back(catalog);
246
247  std::string error;
248  ExtensionMessageBundle* bundle =
249    ExtensionMessageBundle::Create(catalogs, &error);
250  EXPECT_TRUE(NULL != bundle);
251  EXPECT_TRUE(error.empty());
252
253  return bundle;
254}
255
256TEST(ExtensionL10nUtil, LocalizeEmptyManifest) {
257  DictionaryValue manifest;
258  std::string error;
259  scoped_ptr<ExtensionMessageBundle> messages(CreateManifestBundle());
260
261  EXPECT_FALSE(
262      extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
263  EXPECT_EQ(errors::kInvalidName, error);
264}
265
266TEST(ExtensionL10nUtil, LocalizeManifestWithoutNameMsgAndEmptyDescription) {
267  DictionaryValue manifest;
268  manifest.SetString(keys::kName, "no __MSG");
269  std::string error;
270  scoped_ptr<ExtensionMessageBundle> messages(CreateManifestBundle());
271
272  EXPECT_TRUE(
273      extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
274
275  std::string result;
276  ASSERT_TRUE(manifest.GetString(keys::kName, &result));
277  EXPECT_EQ("no __MSG", result);
278
279  EXPECT_FALSE(manifest.HasKey(keys::kDescription));
280
281  EXPECT_TRUE(error.empty());
282}
283
284TEST(ExtensionL10nUtil, LocalizeManifestWithNameMsgAndEmptyDescription) {
285  DictionaryValue manifest;
286  manifest.SetString(keys::kName, "__MSG_name__");
287  std::string error;
288  scoped_ptr<ExtensionMessageBundle> messages(CreateManifestBundle());
289
290  EXPECT_TRUE(
291      extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
292
293  std::string result;
294  ASSERT_TRUE(manifest.GetString(keys::kName, &result));
295  EXPECT_EQ("name", result);
296
297  EXPECT_FALSE(manifest.HasKey(keys::kDescription));
298
299  EXPECT_TRUE(error.empty());
300}
301
302TEST(ExtensionL10nUtil, LocalizeManifestWithBadNameMsg) {
303  DictionaryValue manifest;
304  manifest.SetString(keys::kName, "__MSG_name_is_bad__");
305  manifest.SetString(keys::kDescription, "__MSG_description__");
306  std::string error;
307  scoped_ptr<ExtensionMessageBundle> messages(CreateManifestBundle());
308
309  EXPECT_FALSE(
310      extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
311
312  std::string result;
313  ASSERT_TRUE(manifest.GetString(keys::kName, &result));
314  EXPECT_EQ("__MSG_name_is_bad__", result);
315
316  ASSERT_TRUE(manifest.GetString(keys::kDescription, &result));
317  EXPECT_EQ("__MSG_description__", result);
318
319  EXPECT_EQ("Variable __MSG_name_is_bad__ used but not defined.", error);
320}
321
322TEST(ExtensionL10nUtil, LocalizeManifestWithNameDescriptionDefaultTitleMsgs) {
323  DictionaryValue manifest;
324  manifest.SetString(keys::kName, "__MSG_name__");
325  manifest.SetString(keys::kDescription, "__MSG_description__");
326  std::string action_title(keys::kBrowserAction);
327  action_title.append(".");
328  action_title.append(keys::kPageActionDefaultTitle);
329  manifest.SetString(action_title, "__MSG_title__");
330
331  std::string error;
332  scoped_ptr<ExtensionMessageBundle> messages(CreateManifestBundle());
333
334  EXPECT_TRUE(
335      extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
336
337  std::string result;
338  ASSERT_TRUE(manifest.GetString(keys::kName, &result));
339  EXPECT_EQ("name", result);
340
341  ASSERT_TRUE(manifest.GetString(keys::kDescription, &result));
342  EXPECT_EQ("description", result);
343
344  ASSERT_TRUE(manifest.GetString(action_title, &result));
345  EXPECT_EQ("action title", result);
346
347  EXPECT_TRUE(error.empty());
348}
349
350// Try with NULL manifest.
351TEST(ExtensionL10nUtil, ShouldRelocalizeManifestWithNullManifest) {
352  ExtensionInfo info(NULL, "", FilePath(), Extension::LOAD);
353
354  EXPECT_FALSE(extension_l10n_util::ShouldRelocalizeManifest(info));
355}
356
357// Try with default and current locales missing.
358TEST(ExtensionL10nUtil, ShouldRelocalizeManifestEmptyManifest) {
359  DictionaryValue manifest;
360  ExtensionInfo info(&manifest, "", FilePath(), Extension::LOAD);
361
362  EXPECT_FALSE(extension_l10n_util::ShouldRelocalizeManifest(info));
363}
364
365// Try with missing current_locale.
366TEST(ExtensionL10nUtil, ShouldRelocalizeManifestWithDefaultLocale) {
367  DictionaryValue manifest;
368  manifest.SetString(keys::kDefaultLocale, "en_US");
369
370  ExtensionInfo info(&manifest, "", FilePath(), Extension::LOAD);
371
372  EXPECT_TRUE(extension_l10n_util::ShouldRelocalizeManifest(info));
373}
374
375// Try with missing default_locale.
376TEST(ExtensionL10nUtil, ShouldRelocalizeManifestWithCurrentLocale) {
377  DictionaryValue manifest;
378  manifest.SetString(keys::kCurrentLocale,
379                     extension_l10n_util::CurrentLocaleOrDefault());
380
381  ExtensionInfo info(&manifest, "", FilePath(), Extension::LOAD);
382
383  EXPECT_FALSE(extension_l10n_util::ShouldRelocalizeManifest(info));
384}
385
386// Try with all data present, but with same current_locale as system locale.
387TEST(ExtensionL10nUtil, ShouldRelocalizeManifestSameCurrentLocale) {
388  DictionaryValue manifest;
389  manifest.SetString(keys::kDefaultLocale, "en_US");
390  manifest.SetString(keys::kCurrentLocale,
391                     extension_l10n_util::CurrentLocaleOrDefault());
392
393  ExtensionInfo info(&manifest, "", FilePath(), Extension::LOAD);
394
395  EXPECT_FALSE(extension_l10n_util::ShouldRelocalizeManifest(info));
396}
397
398// Try with all data present, but with different current_locale.
399TEST(ExtensionL10nUtil, ShouldRelocalizeManifestDifferentCurrentLocale) {
400  DictionaryValue manifest;
401  manifest.SetString(keys::kDefaultLocale, "en_US");
402  manifest.SetString(keys::kCurrentLocale, "sr");
403
404  ExtensionInfo info(&manifest, "", FilePath(), Extension::LOAD);
405
406  EXPECT_TRUE(extension_l10n_util::ShouldRelocalizeManifest(info));
407}
408
409}  // namespace
410