extension_l10n_util_unittest.cc revision 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7
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  DictionaryValue* omnibox_keyword_tree = new DictionaryValue();
245  omnibox_keyword_tree->SetString("message", "omnibox keyword");
246  catalog->Set("omnibox_keyword", omnibox_keyword_tree);
247
248  std::vector<linked_ptr<DictionaryValue> > catalogs;
249  catalogs.push_back(catalog);
250
251  std::string error;
252  ExtensionMessageBundle* bundle =
253      ExtensionMessageBundle::Create(catalogs, &error);
254  EXPECT_TRUE(bundle);
255  EXPECT_TRUE(error.empty());
256
257  return bundle;
258}
259
260TEST(ExtensionL10nUtil, LocalizeEmptyManifest) {
261  DictionaryValue manifest;
262  std::string error;
263  scoped_ptr<ExtensionMessageBundle> messages(CreateManifestBundle());
264
265  EXPECT_FALSE(
266      extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
267  EXPECT_EQ(errors::kInvalidName, error);
268}
269
270TEST(ExtensionL10nUtil, LocalizeManifestWithoutNameMsgAndEmptyDescription) {
271  DictionaryValue manifest;
272  manifest.SetString(keys::kName, "no __MSG");
273  std::string error;
274  scoped_ptr<ExtensionMessageBundle> messages(CreateManifestBundle());
275
276  EXPECT_TRUE(
277      extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
278
279  std::string result;
280  ASSERT_TRUE(manifest.GetString(keys::kName, &result));
281  EXPECT_EQ("no __MSG", result);
282
283  EXPECT_FALSE(manifest.HasKey(keys::kDescription));
284
285  EXPECT_TRUE(error.empty());
286}
287
288TEST(ExtensionL10nUtil, LocalizeManifestWithNameMsgAndEmptyDescription) {
289  DictionaryValue manifest;
290  manifest.SetString(keys::kName, "__MSG_name__");
291  std::string error;
292  scoped_ptr<ExtensionMessageBundle> messages(CreateManifestBundle());
293
294  EXPECT_TRUE(
295      extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
296
297  std::string result;
298  ASSERT_TRUE(manifest.GetString(keys::kName, &result));
299  EXPECT_EQ("name", result);
300
301  EXPECT_FALSE(manifest.HasKey(keys::kDescription));
302
303  EXPECT_TRUE(error.empty());
304}
305
306TEST(ExtensionL10nUtil, LocalizeManifestWithBadNameMsg) {
307  DictionaryValue manifest;
308  manifest.SetString(keys::kName, "__MSG_name_is_bad__");
309  manifest.SetString(keys::kDescription, "__MSG_description__");
310  std::string error;
311  scoped_ptr<ExtensionMessageBundle> messages(CreateManifestBundle());
312
313  EXPECT_FALSE(
314      extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
315
316  std::string result;
317  ASSERT_TRUE(manifest.GetString(keys::kName, &result));
318  EXPECT_EQ("__MSG_name_is_bad__", result);
319
320  ASSERT_TRUE(manifest.GetString(keys::kDescription, &result));
321  EXPECT_EQ("__MSG_description__", result);
322
323  EXPECT_EQ("Variable __MSG_name_is_bad__ used but not defined.", error);
324}
325
326TEST(ExtensionL10nUtil, LocalizeManifestWithNameDescriptionDefaultTitleMsgs) {
327  DictionaryValue manifest;
328  manifest.SetString(keys::kName, "__MSG_name__");
329  manifest.SetString(keys::kDescription, "__MSG_description__");
330  std::string action_title(keys::kBrowserAction);
331  action_title.append(".");
332  action_title.append(keys::kPageActionDefaultTitle);
333  manifest.SetString(action_title, "__MSG_title__");
334
335  std::string error;
336  scoped_ptr<ExtensionMessageBundle> messages(CreateManifestBundle());
337
338  EXPECT_TRUE(
339      extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
340
341  std::string result;
342  ASSERT_TRUE(manifest.GetString(keys::kName, &result));
343  EXPECT_EQ("name", result);
344
345  ASSERT_TRUE(manifest.GetString(keys::kDescription, &result));
346  EXPECT_EQ("description", result);
347
348  ASSERT_TRUE(manifest.GetString(action_title, &result));
349  EXPECT_EQ("action title", result);
350
351  EXPECT_TRUE(error.empty());
352}
353
354TEST(ExtensionL10nUtil, LocalizeManifestWithNameDescriptionOmniboxMsgs) {
355  DictionaryValue manifest;
356  manifest.SetString(keys::kName, "__MSG_name__");
357  manifest.SetString(keys::kDescription, "__MSG_description__");
358  manifest.SetString(keys::kOmniboxKeyword, "__MSG_omnibox_keyword__");
359
360  std::string error;
361  scoped_ptr<ExtensionMessageBundle> messages(CreateManifestBundle());
362
363  EXPECT_TRUE(
364      extension_l10n_util::LocalizeManifest(*messages, &manifest, &error));
365
366  std::string result;
367  ASSERT_TRUE(manifest.GetString(keys::kName, &result));
368  EXPECT_EQ("name", result);
369
370  ASSERT_TRUE(manifest.GetString(keys::kDescription, &result));
371  EXPECT_EQ("description", result);
372
373  ASSERT_TRUE(manifest.GetString(keys::kOmniboxKeyword, &result));
374  EXPECT_EQ("omnibox keyword", result);
375
376  EXPECT_TRUE(error.empty());
377}
378
379// Try with NULL manifest.
380TEST(ExtensionL10nUtil, ShouldRelocalizeManifestWithNullManifest) {
381  ExtensionInfo info(NULL, "", FilePath(), Extension::LOAD);
382
383  EXPECT_FALSE(extension_l10n_util::ShouldRelocalizeManifest(info));
384}
385
386// Try with default and current locales missing.
387TEST(ExtensionL10nUtil, ShouldRelocalizeManifestEmptyManifest) {
388  DictionaryValue manifest;
389  ExtensionInfo info(&manifest, "", FilePath(), Extension::LOAD);
390
391  EXPECT_FALSE(extension_l10n_util::ShouldRelocalizeManifest(info));
392}
393
394// Try with missing current_locale.
395TEST(ExtensionL10nUtil, ShouldRelocalizeManifestWithDefaultLocale) {
396  DictionaryValue manifest;
397  manifest.SetString(keys::kDefaultLocale, "en_US");
398
399  ExtensionInfo info(&manifest, "", FilePath(), Extension::LOAD);
400
401  EXPECT_TRUE(extension_l10n_util::ShouldRelocalizeManifest(info));
402}
403
404// Try with missing default_locale.
405TEST(ExtensionL10nUtil, ShouldRelocalizeManifestWithCurrentLocale) {
406  DictionaryValue manifest;
407  manifest.SetString(keys::kCurrentLocale,
408                     extension_l10n_util::CurrentLocaleOrDefault());
409
410  ExtensionInfo info(&manifest, "", FilePath(), Extension::LOAD);
411
412  EXPECT_FALSE(extension_l10n_util::ShouldRelocalizeManifest(info));
413}
414
415// Try with all data present, but with same current_locale as system locale.
416TEST(ExtensionL10nUtil, ShouldRelocalizeManifestSameCurrentLocale) {
417  DictionaryValue manifest;
418  manifest.SetString(keys::kDefaultLocale, "en_US");
419  manifest.SetString(keys::kCurrentLocale,
420                     extension_l10n_util::CurrentLocaleOrDefault());
421
422  ExtensionInfo info(&manifest, "", FilePath(), Extension::LOAD);
423
424  EXPECT_FALSE(extension_l10n_util::ShouldRelocalizeManifest(info));
425}
426
427// Try with all data present, but with different current_locale.
428TEST(ExtensionL10nUtil, ShouldRelocalizeManifestDifferentCurrentLocale) {
429  DictionaryValue manifest;
430  manifest.SetString(keys::kDefaultLocale, "en_US");
431  manifest.SetString(keys::kCurrentLocale, "sr");
432
433  ExtensionInfo info(&manifest, "", FilePath(), Extension::LOAD);
434
435  EXPECT_TRUE(extension_l10n_util::ShouldRelocalizeManifest(info));
436}
437
438}  // namespace
439