Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / chrome / common / extensions / message_bundle_unittest.cc
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/common/extensions/message_bundle.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "base/i18n/rtl.h"
11 #include "base/memory/linked_ptr.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/values.h"
16 #include "chrome/common/extensions/extension_l10n_util.h"
17 #include "extensions/common/error_utils.h"
18 #include "extensions/common/manifest_constants.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 namespace extensions {
22
23 namespace errors = manifest_errors;
24
25 class MessageBundleTest : public testing::Test {
26  protected:
27   enum BadDictionary {
28     INVALID_NAME,
29     NAME_NOT_A_TREE,
30     EMPTY_NAME_TREE,
31     MISSING_MESSAGE,
32     PLACEHOLDER_NOT_A_TREE,
33     EMPTY_PLACEHOLDER_TREE,
34     CONTENT_MISSING,
35     MESSAGE_PLACEHOLDER_DOESNT_MATCH,
36   };
37
38   // Helper method for dictionary building.
39   void SetDictionary(const std::string& name,
40                      base::DictionaryValue* subtree,
41                      base::DictionaryValue* target) {
42     target->Set(name, static_cast<base::Value*>(subtree));
43   }
44
45   void CreateContentTree(const std::string& name,
46                          const std::string& content,
47                          base::DictionaryValue* dict) {
48     base::DictionaryValue* content_tree = new base::DictionaryValue;
49     content_tree->SetString(MessageBundle::kContentKey, content);
50     SetDictionary(name, content_tree, dict);
51   }
52
53   void CreatePlaceholdersTree(base::DictionaryValue* dict) {
54     base::DictionaryValue* placeholders_tree = new base::DictionaryValue;
55     CreateContentTree("a", "A", placeholders_tree);
56     CreateContentTree("b", "B", placeholders_tree);
57     CreateContentTree("c", "C", placeholders_tree);
58     SetDictionary(MessageBundle::kPlaceholdersKey,
59                   placeholders_tree,
60                   dict);
61   }
62
63   void CreateMessageTree(const std::string& name,
64                          const std::string& message,
65                          bool create_placeholder_subtree,
66                          base::DictionaryValue* dict) {
67     base::DictionaryValue* message_tree = new base::DictionaryValue;
68     if (create_placeholder_subtree)
69       CreatePlaceholdersTree(message_tree);
70     message_tree->SetString(MessageBundle::kMessageKey, message);
71     SetDictionary(name, message_tree, dict);
72   }
73
74   // Caller owns the memory.
75   base::DictionaryValue* CreateGoodDictionary() {
76     base::DictionaryValue* dict = new base::DictionaryValue;
77     CreateMessageTree("n1", "message1 $a$ $b$", true, dict);
78     CreateMessageTree("n2", "message2 $c$", true, dict);
79     CreateMessageTree("n3", "message3", false, dict);
80     return dict;
81   }
82
83   // Caller owns the memory.
84   base::DictionaryValue* CreateBadDictionary(enum BadDictionary what_is_bad) {
85     base::DictionaryValue* dict = CreateGoodDictionary();
86     // Now remove/break things.
87     switch (what_is_bad) {
88       case INVALID_NAME:
89         CreateMessageTree("n 5", "nevermind", false, dict);
90         break;
91       case NAME_NOT_A_TREE:
92         dict->SetString("n4", "whatever");
93         break;
94       case EMPTY_NAME_TREE: {
95           base::DictionaryValue* empty_tree = new base::DictionaryValue;
96           SetDictionary("n4", empty_tree, dict);
97         }
98         break;
99       case MISSING_MESSAGE:
100         dict->Remove("n1.message", NULL);
101         break;
102       case PLACEHOLDER_NOT_A_TREE:
103         dict->SetString("n1.placeholders", "whatever");
104         break;
105       case EMPTY_PLACEHOLDER_TREE: {
106           base::DictionaryValue* empty_tree = new base::DictionaryValue;
107           SetDictionary("n1.placeholders", empty_tree, dict);
108         }
109         break;
110       case CONTENT_MISSING:
111          dict->Remove("n1.placeholders.a.content", NULL);
112         break;
113       case MESSAGE_PLACEHOLDER_DOESNT_MATCH:
114         base::DictionaryValue* value;
115         dict->Remove("n1.placeholders.a", NULL);
116         dict->GetDictionary("n1.placeholders", &value);
117         CreateContentTree("x", "X", value);
118         break;
119     }
120
121     return dict;
122   }
123
124   unsigned int ReservedMessagesCount() {
125     // Update when adding new reserved messages.
126     return 5U;
127   }
128
129   void CheckReservedMessages(MessageBundle* handler) {
130     std::string ui_locale = extension_l10n_util::CurrentLocaleOrDefault();
131     EXPECT_EQ(ui_locale,
132               handler->GetL10nMessage(MessageBundle::kUILocaleKey));
133
134     std::string text_dir = "ltr";
135     if (base::i18n::GetTextDirectionForLocale(ui_locale.c_str()) ==
136         base::i18n::RIGHT_TO_LEFT)
137       text_dir = "rtl";
138
139     EXPECT_EQ(text_dir, handler->GetL10nMessage(
140         MessageBundle::kBidiDirectionKey));
141   }
142
143   bool AppendReservedMessages(const std::string& application_locale) {
144     std::string error;
145     return handler_->AppendReservedMessagesForLocale(
146         application_locale, &error);
147   }
148
149   std::string CreateMessageBundle() {
150     std::string error;
151     handler_.reset(MessageBundle::Create(catalogs_, &error));
152
153     return error;
154   }
155
156   void ClearDictionary() {
157     handler_->dictionary_.clear();
158   }
159
160   scoped_ptr<MessageBundle> handler_;
161   std::vector<linked_ptr<base::DictionaryValue> > catalogs_;
162 };
163
164 TEST_F(MessageBundleTest, ReservedMessagesCount) {
165   ASSERT_EQ(5U, ReservedMessagesCount());
166 }
167
168 TEST_F(MessageBundleTest, InitEmptyDictionaries) {
169   CreateMessageBundle();
170   EXPECT_TRUE(handler_.get() != NULL);
171   EXPECT_EQ(0U + ReservedMessagesCount(), handler_->size());
172   CheckReservedMessages(handler_.get());
173 }
174
175 TEST_F(MessageBundleTest, InitGoodDefaultDict) {
176   catalogs_.push_back(
177       linked_ptr<base::DictionaryValue>(CreateGoodDictionary()));
178   CreateMessageBundle();
179
180   EXPECT_TRUE(handler_.get() != NULL);
181   EXPECT_EQ(3U + ReservedMessagesCount(), handler_->size());
182
183   EXPECT_EQ("message1 A B", handler_->GetL10nMessage("n1"));
184   EXPECT_EQ("message2 C", handler_->GetL10nMessage("n2"));
185   EXPECT_EQ("message3", handler_->GetL10nMessage("n3"));
186   CheckReservedMessages(handler_.get());
187 }
188
189 TEST_F(MessageBundleTest, InitAppDictConsultedFirst) {
190   catalogs_.push_back(
191       linked_ptr<base::DictionaryValue>(CreateGoodDictionary()));
192   catalogs_.push_back(
193       linked_ptr<base::DictionaryValue>(CreateGoodDictionary()));
194
195   base::DictionaryValue* app_dict = catalogs_[0].get();
196   // Flip placeholders in message of n1 tree.
197   app_dict->SetString("n1.message", "message1 $b$ $a$");
198   // Remove one message from app dict.
199   app_dict->Remove("n2", NULL);
200   // Replace n3 with N3.
201   app_dict->Remove("n3", NULL);
202   CreateMessageTree("N3", "message3_app_dict", false, app_dict);
203
204   CreateMessageBundle();
205
206   EXPECT_TRUE(handler_.get() != NULL);
207   EXPECT_EQ(3U + ReservedMessagesCount(), handler_->size());
208
209   EXPECT_EQ("message1 B A", handler_->GetL10nMessage("n1"));
210   EXPECT_EQ("message2 C", handler_->GetL10nMessage("n2"));
211   EXPECT_EQ("message3_app_dict", handler_->GetL10nMessage("n3"));
212   CheckReservedMessages(handler_.get());
213 }
214
215 TEST_F(MessageBundleTest, InitBadAppDict) {
216   catalogs_.push_back(
217       linked_ptr<base::DictionaryValue>(CreateBadDictionary(INVALID_NAME)));
218   catalogs_.push_back(
219       linked_ptr<base::DictionaryValue>(CreateGoodDictionary()));
220
221   std::string error = CreateMessageBundle();
222
223   EXPECT_TRUE(handler_.get() == NULL);
224   EXPECT_EQ("Name of a key \"n 5\" is invalid. Only ASCII [a-z], "
225             "[A-Z], [0-9] and \"_\" are allowed.", error);
226
227   catalogs_[0].reset(CreateBadDictionary(NAME_NOT_A_TREE));
228   handler_.reset(MessageBundle::Create(catalogs_, &error));
229   EXPECT_TRUE(handler_.get() == NULL);
230   EXPECT_EQ("Not a valid tree for key n4.", error);
231
232   catalogs_[0].reset(CreateBadDictionary(EMPTY_NAME_TREE));
233   handler_.reset(MessageBundle::Create(catalogs_, &error));
234   EXPECT_TRUE(handler_.get() == NULL);
235   EXPECT_EQ("There is no \"message\" element for key n4.", error);
236
237   catalogs_[0].reset(CreateBadDictionary(MISSING_MESSAGE));
238   handler_.reset(MessageBundle::Create(catalogs_, &error));
239   EXPECT_TRUE(handler_.get() == NULL);
240   EXPECT_EQ("There is no \"message\" element for key n1.", error);
241
242   catalogs_[0].reset(CreateBadDictionary(PLACEHOLDER_NOT_A_TREE));
243   handler_.reset(MessageBundle::Create(catalogs_, &error));
244   EXPECT_TRUE(handler_.get() == NULL);
245   EXPECT_EQ("Not a valid \"placeholders\" element for key n1.", error);
246
247   catalogs_[0].reset(CreateBadDictionary(EMPTY_PLACEHOLDER_TREE));
248   handler_.reset(MessageBundle::Create(catalogs_, &error));
249   EXPECT_TRUE(handler_.get() == NULL);
250   EXPECT_EQ("Variable $a$ used but not defined.", error);
251
252   catalogs_[0].reset(CreateBadDictionary(CONTENT_MISSING));
253   handler_.reset(MessageBundle::Create(catalogs_, &error));
254   EXPECT_TRUE(handler_.get() == NULL);
255   EXPECT_EQ("Invalid \"content\" element for key n1.", error);
256
257   catalogs_[0].reset(CreateBadDictionary(MESSAGE_PLACEHOLDER_DOESNT_MATCH));
258   handler_.reset(MessageBundle::Create(catalogs_, &error));
259   EXPECT_TRUE(handler_.get() == NULL);
260   EXPECT_EQ("Variable $a$ used but not defined.", error);
261 }
262
263 TEST_F(MessageBundleTest, ReservedMessagesOverrideDeveloperMessages) {
264   catalogs_.push_back(
265       linked_ptr<base::DictionaryValue>(CreateGoodDictionary()));
266
267   base::DictionaryValue* dict = catalogs_[0].get();
268   CreateMessageTree(MessageBundle::kUILocaleKey, "x", false, dict);
269
270   std::string error = CreateMessageBundle();
271
272   EXPECT_TRUE(handler_.get() == NULL);
273   std::string expected_error = ErrorUtils::FormatErrorMessage(
274       errors::kReservedMessageFound, MessageBundle::kUILocaleKey);
275   EXPECT_EQ(expected_error, error);
276 }
277
278 TEST_F(MessageBundleTest, AppendReservedMessagesForLTR) {
279   CreateMessageBundle();
280
281   ASSERT_TRUE(handler_.get() != NULL);
282   ClearDictionary();
283   ASSERT_TRUE(AppendReservedMessages("en_US"));
284
285   EXPECT_EQ("en_US",
286             handler_->GetL10nMessage(MessageBundle::kUILocaleKey));
287   EXPECT_EQ("ltr", handler_->GetL10nMessage(
288       MessageBundle::kBidiDirectionKey));
289   EXPECT_EQ("rtl", handler_->GetL10nMessage(
290       MessageBundle::kBidiReversedDirectionKey));
291   EXPECT_EQ("left", handler_->GetL10nMessage(
292       MessageBundle::kBidiStartEdgeKey));
293   EXPECT_EQ("right", handler_->GetL10nMessage(
294       MessageBundle::kBidiEndEdgeKey));
295 }
296
297 TEST_F(MessageBundleTest, AppendReservedMessagesForRTL) {
298   CreateMessageBundle();
299
300   ASSERT_TRUE(handler_.get() != NULL);
301   ClearDictionary();
302   ASSERT_TRUE(AppendReservedMessages("he"));
303
304   EXPECT_EQ("he",
305             handler_->GetL10nMessage(MessageBundle::kUILocaleKey));
306   EXPECT_EQ("rtl", handler_->GetL10nMessage(
307       MessageBundle::kBidiDirectionKey));
308   EXPECT_EQ("ltr", handler_->GetL10nMessage(
309       MessageBundle::kBidiReversedDirectionKey));
310   EXPECT_EQ("right", handler_->GetL10nMessage(
311       MessageBundle::kBidiStartEdgeKey));
312   EXPECT_EQ("left", handler_->GetL10nMessage(
313       MessageBundle::kBidiEndEdgeKey));
314 }
315
316 TEST_F(MessageBundleTest, IsValidNameCheckValidCharacters) {
317   EXPECT_TRUE(MessageBundle::IsValidName(std::string("a__BV_9")));
318   EXPECT_TRUE(MessageBundle::IsValidName(std::string("@@a__BV_9")));
319   EXPECT_FALSE(MessageBundle::IsValidName(std::string("$a__BV_9$")));
320   EXPECT_FALSE(MessageBundle::IsValidName(std::string("a-BV-9")));
321   EXPECT_FALSE(MessageBundle::IsValidName(std::string("a#BV!9")));
322   EXPECT_FALSE(MessageBundle::IsValidName(std::string("a<b")));
323 }
324
325 struct ReplaceVariables {
326   const char* original;
327   const char* result;
328   const char* error;
329   const char* begin_delimiter;
330   const char* end_delimiter;
331   bool pass;
332 };
333
334 TEST(MessageBundle, ReplaceMessagesInText) {
335   const char* kMessageBegin = MessageBundle::kMessageBegin;
336   const char* kMessageEnd = MessageBundle::kMessageEnd;
337   const char* kPlaceholderBegin = MessageBundle::kPlaceholderBegin;
338   const char* kPlaceholderEnd = MessageBundle::kPlaceholderEnd;
339
340   static ReplaceVariables test_cases[] = {
341     // Message replacement.
342     { "This is __MSG_siMPle__ message", "This is simple message",
343       "", kMessageBegin, kMessageEnd, true },
344     { "This is __MSG_", "This is __MSG_",
345       "", kMessageBegin, kMessageEnd, true },
346     { "This is __MSG__simple__ message", "This is __MSG__simple__ message",
347       "Variable __MSG__simple__ used but not defined.",
348       kMessageBegin, kMessageEnd, false },
349     { "__MSG_LoNg__", "A pretty long replacement",
350       "", kMessageBegin, kMessageEnd, true },
351     { "A __MSG_SimpLE__MSG_ a", "A simpleMSG_ a",
352       "", kMessageBegin, kMessageEnd, true },
353     { "A __MSG_simple__MSG_long__", "A simpleMSG_long__",
354       "", kMessageBegin, kMessageEnd, true },
355     { "A __MSG_simple____MSG_long__", "A simpleA pretty long replacement",
356       "", kMessageBegin, kMessageEnd, true },
357     { "__MSG_d1g1ts_are_ok__", "I are d1g1t",
358       "", kMessageBegin, kMessageEnd, true },
359     // Placeholder replacement.
360     { "This is $sImpLe$ message", "This is simple message",
361        "", kPlaceholderBegin, kPlaceholderEnd, true },
362     { "This is $", "This is $",
363        "", kPlaceholderBegin, kPlaceholderEnd, true },
364     { "This is $$sIMPle$ message", "This is $simple message",
365        "", kPlaceholderBegin, kPlaceholderEnd, true },
366     { "$LONG_V$", "A pretty long replacement",
367        "", kPlaceholderBegin, kPlaceholderEnd, true },
368     { "A $simple$$ a", "A simple$ a",
369        "", kPlaceholderBegin, kPlaceholderEnd, true },
370     { "A $simple$long_v$", "A simplelong_v$",
371        "", kPlaceholderBegin, kPlaceholderEnd, true },
372     { "A $simple$$long_v$", "A simpleA pretty long replacement",
373        "", kPlaceholderBegin, kPlaceholderEnd, true },
374     { "This is $bad name$", "This is $bad name$",
375        "", kPlaceholderBegin, kPlaceholderEnd, true },
376     { "This is $missing$", "This is $missing$",
377        "Variable $missing$ used but not defined.",
378        kPlaceholderBegin, kPlaceholderEnd, false },
379   };
380
381   MessageBundle::SubstitutionMap messages;
382   messages.insert(std::make_pair("simple", "simple"));
383   messages.insert(std::make_pair("long", "A pretty long replacement"));
384   messages.insert(std::make_pair("long_v", "A pretty long replacement"));
385   messages.insert(std::make_pair("bad name", "Doesn't matter"));
386   messages.insert(std::make_pair("d1g1ts_are_ok", "I are d1g1t"));
387
388   for (size_t i = 0; i < arraysize(test_cases); ++i) {
389     std::string text = test_cases[i].original;
390     std::string error;
391     EXPECT_EQ(test_cases[i].pass,
392               MessageBundle::ReplaceVariables(messages,
393                                               test_cases[i].begin_delimiter,
394                                               test_cases[i].end_delimiter,
395                                               &text,
396                                               &error));
397     EXPECT_EQ(test_cases[i].result, text);
398   }
399 }
400
401 ///////////////////////////////////////////////////////////////////////////////
402 //
403 // Renderer helper functions test.
404 //
405 ///////////////////////////////////////////////////////////////////////////////
406
407 TEST(GetExtensionToL10nMessagesMapTest, ReturnsTheSameObject) {
408   ExtensionToL10nMessagesMap* map1 = GetExtensionToL10nMessagesMap();
409   ASSERT_TRUE(NULL != map1);
410
411   ExtensionToL10nMessagesMap* map2 = GetExtensionToL10nMessagesMap();
412   ASSERT_EQ(map1, map2);
413 }
414
415 TEST(GetExtensionToL10nMessagesMapTest, ReturnsNullForUnknownExtensionId) {
416   const std::string extension_id("some_unique_12334212314234_id");
417   L10nMessagesMap* map = GetL10nMessagesMap(extension_id);
418   EXPECT_TRUE(NULL == map);
419 }
420
421 TEST(GetExtensionToL10nMessagesMapTest, ReturnsMapForKnownExtensionId) {
422   const std::string extension_id("some_unique_121212121212121_id");
423   // Store a map for given id.
424   L10nMessagesMap messages;
425   messages.insert(std::make_pair("message_name", "message_value"));
426   (*GetExtensionToL10nMessagesMap())[extension_id] = messages;
427
428   L10nMessagesMap* map = GetL10nMessagesMap(extension_id);
429   ASSERT_TRUE(NULL != map);
430   EXPECT_EQ(1U, map->size());
431   EXPECT_EQ("message_value", (*map)["message_name"]);
432 }
433
434 }  // namespace extensions