Extract SIGPIPE ignoring code to a common place.
[chromium-blink-merge.git] / chrome / common / extensions / message_bundle_unittest.cc
blob1d313a1cb41145de9d387d2d92bd99781959576e
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.
5 #include "chrome/common/extensions/message_bundle.h"
7 #include <string>
8 #include <vector>
10 #include "base/i18n/rtl.h"
11 #include "base/memory/linked_ptr.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/string_util.h"
14 #include "base/utf_string_conversions.h"
15 #include "base/values.h"
16 #include "chrome/common/extensions/extension_manifest_constants.h"
17 #include "chrome/common/extensions/extension_error_utils.h"
18 #include "chrome/common/extensions/extension_l10n_util.h"
19 #include "testing/gtest/include/gtest/gtest.h"
21 namespace errors = extension_manifest_errors;
23 namespace extensions {
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,
38 // Helper method for dictionary building.
39 void SetDictionary(const std::string& name,
40 DictionaryValue* subtree,
41 DictionaryValue* target) {
42 target->Set(name, static_cast<Value*>(subtree));
45 void CreateContentTree(const std::string& name,
46 const std::string& content,
47 DictionaryValue* dict) {
48 DictionaryValue* content_tree = new DictionaryValue;
49 content_tree->SetString(MessageBundle::kContentKey, content);
50 SetDictionary(name, content_tree, dict);
53 void CreatePlaceholdersTree(DictionaryValue* dict) {
54 DictionaryValue* placeholders_tree = new 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);
63 void CreateMessageTree(const std::string& name,
64 const std::string& message,
65 bool create_placeholder_subtree,
66 DictionaryValue* dict) {
67 DictionaryValue* message_tree = new DictionaryValue;
68 if (create_placeholder_subtree)
69 CreatePlaceholdersTree(message_tree);
70 message_tree->SetString(MessageBundle::kMessageKey, message);
71 SetDictionary(name, message_tree, dict);
74 // Caller owns the memory.
75 DictionaryValue* CreateGoodDictionary() {
76 DictionaryValue* dict = new 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;
83 // Caller owns the memory.
84 DictionaryValue* CreateBadDictionary(enum BadDictionary what_is_bad) {
85 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 DictionaryValue* empty_tree = new DictionaryValue;
96 SetDictionary("n4", empty_tree, dict);
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 DictionaryValue* empty_tree = new DictionaryValue;
107 SetDictionary("n1.placeholders", empty_tree, dict);
109 break;
110 case CONTENT_MISSING:
111 dict->Remove("n1.placeholders.a.content", NULL);
112 break;
113 case MESSAGE_PLACEHOLDER_DOESNT_MATCH:
114 DictionaryValue* value;
115 dict->Remove("n1.placeholders.a", NULL);
116 dict->GetDictionary("n1.placeholders", &value);
117 CreateContentTree("x", "X", value);
118 break;
121 return dict;
124 unsigned int ReservedMessagesCount() {
125 // Update when adding new reserved messages.
126 return 5U;
129 void CheckReservedMessages(MessageBundle* handler) {
130 std::string ui_locale = extension_l10n_util::CurrentLocaleOrDefault();
131 EXPECT_EQ(ui_locale,
132 handler->GetL10nMessage(MessageBundle::kUILocaleKey));
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";
139 EXPECT_EQ(text_dir, handler->GetL10nMessage(
140 MessageBundle::kBidiDirectionKey));
143 bool AppendReservedMessages(const std::string& application_locale) {
144 std::string error;
145 return handler_->AppendReservedMessagesForLocale(
146 application_locale, &error);
149 std::string CreateMessageBundle() {
150 std::string error;
151 handler_.reset(MessageBundle::Create(catalogs_, &error));
153 return error;
156 void ClearDictionary() {
157 handler_->dictionary_.clear();
160 scoped_ptr<MessageBundle> handler_;
161 std::vector<linked_ptr<DictionaryValue> > catalogs_;
164 TEST_F(MessageBundleTest, ReservedMessagesCount) {
165 ASSERT_EQ(5U, ReservedMessagesCount());
168 TEST_F(MessageBundleTest, InitEmptyDictionaries) {
169 CreateMessageBundle();
170 EXPECT_TRUE(handler_.get() != NULL);
171 EXPECT_EQ(0U + ReservedMessagesCount(), handler_->size());
172 CheckReservedMessages(handler_.get());
175 TEST_F(MessageBundleTest, InitGoodDefaultDict) {
176 catalogs_.push_back(linked_ptr<DictionaryValue>(CreateGoodDictionary()));
177 CreateMessageBundle();
179 EXPECT_TRUE(handler_.get() != NULL);
180 EXPECT_EQ(3U + ReservedMessagesCount(), handler_->size());
182 EXPECT_EQ("message1 A B", handler_->GetL10nMessage("n1"));
183 EXPECT_EQ("message2 C", handler_->GetL10nMessage("n2"));
184 EXPECT_EQ("message3", handler_->GetL10nMessage("n3"));
185 CheckReservedMessages(handler_.get());
188 TEST_F(MessageBundleTest, InitAppDictConsultedFirst) {
189 catalogs_.push_back(linked_ptr<DictionaryValue>(CreateGoodDictionary()));
190 catalogs_.push_back(linked_ptr<DictionaryValue>(CreateGoodDictionary()));
192 DictionaryValue* app_dict = catalogs_[0].get();
193 // Flip placeholders in message of n1 tree.
194 app_dict->SetString("n1.message", "message1 $b$ $a$");
195 // Remove one message from app dict.
196 app_dict->Remove("n2", NULL);
197 // Replace n3 with N3.
198 app_dict->Remove("n3", NULL);
199 CreateMessageTree("N3", "message3_app_dict", false, app_dict);
201 CreateMessageBundle();
203 EXPECT_TRUE(handler_.get() != NULL);
204 EXPECT_EQ(3U + ReservedMessagesCount(), handler_->size());
206 EXPECT_EQ("message1 B A", handler_->GetL10nMessage("n1"));
207 EXPECT_EQ("message2 C", handler_->GetL10nMessage("n2"));
208 EXPECT_EQ("message3_app_dict", handler_->GetL10nMessage("n3"));
209 CheckReservedMessages(handler_.get());
212 TEST_F(MessageBundleTest, InitBadAppDict) {
213 catalogs_.push_back(
214 linked_ptr<DictionaryValue>(CreateBadDictionary(INVALID_NAME)));
215 catalogs_.push_back(linked_ptr<DictionaryValue>(CreateGoodDictionary()));
217 std::string error = CreateMessageBundle();
219 EXPECT_TRUE(handler_.get() == NULL);
220 EXPECT_EQ("Name of a key \"n 5\" is invalid. Only ASCII [a-z], "
221 "[A-Z], [0-9] and \"_\" are allowed.", error);
223 catalogs_[0].reset(CreateBadDictionary(NAME_NOT_A_TREE));
224 handler_.reset(MessageBundle::Create(catalogs_, &error));
225 EXPECT_TRUE(handler_.get() == NULL);
226 EXPECT_EQ("Not a valid tree for key n4.", error);
228 catalogs_[0].reset(CreateBadDictionary(EMPTY_NAME_TREE));
229 handler_.reset(MessageBundle::Create(catalogs_, &error));
230 EXPECT_TRUE(handler_.get() == NULL);
231 EXPECT_EQ("There is no \"message\" element for key n4.", error);
233 catalogs_[0].reset(CreateBadDictionary(MISSING_MESSAGE));
234 handler_.reset(MessageBundle::Create(catalogs_, &error));
235 EXPECT_TRUE(handler_.get() == NULL);
236 EXPECT_EQ("There is no \"message\" element for key n1.", error);
238 catalogs_[0].reset(CreateBadDictionary(PLACEHOLDER_NOT_A_TREE));
239 handler_.reset(MessageBundle::Create(catalogs_, &error));
240 EXPECT_TRUE(handler_.get() == NULL);
241 EXPECT_EQ("Not a valid \"placeholders\" element for key n1.", error);
243 catalogs_[0].reset(CreateBadDictionary(EMPTY_PLACEHOLDER_TREE));
244 handler_.reset(MessageBundle::Create(catalogs_, &error));
245 EXPECT_TRUE(handler_.get() == NULL);
246 EXPECT_EQ("Variable $a$ used but not defined.", error);
248 catalogs_[0].reset(CreateBadDictionary(CONTENT_MISSING));
249 handler_.reset(MessageBundle::Create(catalogs_, &error));
250 EXPECT_TRUE(handler_.get() == NULL);
251 EXPECT_EQ("Invalid \"content\" element for key n1.", error);
253 catalogs_[0].reset(CreateBadDictionary(MESSAGE_PLACEHOLDER_DOESNT_MATCH));
254 handler_.reset(MessageBundle::Create(catalogs_, &error));
255 EXPECT_TRUE(handler_.get() == NULL);
256 EXPECT_EQ("Variable $a$ used but not defined.", error);
259 TEST_F(MessageBundleTest, ReservedMessagesOverrideDeveloperMessages) {
260 catalogs_.push_back(linked_ptr<DictionaryValue>(CreateGoodDictionary()));
262 DictionaryValue* dict = catalogs_[0].get();
263 CreateMessageTree(MessageBundle::kUILocaleKey, "x", false, dict);
265 std::string error = CreateMessageBundle();
267 EXPECT_TRUE(handler_.get() == NULL);
268 std::string expected_error = ExtensionErrorUtils::FormatErrorMessage(
269 errors::kReservedMessageFound, MessageBundle::kUILocaleKey);
270 EXPECT_EQ(expected_error, error);
273 TEST_F(MessageBundleTest, AppendReservedMessagesForLTR) {
274 CreateMessageBundle();
276 ASSERT_TRUE(handler_.get() != NULL);
277 ClearDictionary();
278 ASSERT_TRUE(AppendReservedMessages("en_US"));
280 EXPECT_EQ("en_US",
281 handler_->GetL10nMessage(MessageBundle::kUILocaleKey));
282 EXPECT_EQ("ltr", handler_->GetL10nMessage(
283 MessageBundle::kBidiDirectionKey));
284 EXPECT_EQ("rtl", handler_->GetL10nMessage(
285 MessageBundle::kBidiReversedDirectionKey));
286 EXPECT_EQ("left", handler_->GetL10nMessage(
287 MessageBundle::kBidiStartEdgeKey));
288 EXPECT_EQ("right", handler_->GetL10nMessage(
289 MessageBundle::kBidiEndEdgeKey));
292 TEST_F(MessageBundleTest, AppendReservedMessagesForRTL) {
293 CreateMessageBundle();
295 ASSERT_TRUE(handler_.get() != NULL);
296 ClearDictionary();
297 ASSERT_TRUE(AppendReservedMessages("he"));
299 EXPECT_EQ("he",
300 handler_->GetL10nMessage(MessageBundle::kUILocaleKey));
301 EXPECT_EQ("rtl", handler_->GetL10nMessage(
302 MessageBundle::kBidiDirectionKey));
303 EXPECT_EQ("ltr", handler_->GetL10nMessage(
304 MessageBundle::kBidiReversedDirectionKey));
305 EXPECT_EQ("right", handler_->GetL10nMessage(
306 MessageBundle::kBidiStartEdgeKey));
307 EXPECT_EQ("left", handler_->GetL10nMessage(
308 MessageBundle::kBidiEndEdgeKey));
311 TEST_F(MessageBundleTest, IsValidNameCheckValidCharacters) {
312 EXPECT_TRUE(MessageBundle::IsValidName(std::string("a__BV_9")));
313 EXPECT_TRUE(MessageBundle::IsValidName(std::string("@@a__BV_9")));
314 EXPECT_FALSE(MessageBundle::IsValidName(std::string("$a__BV_9$")));
315 EXPECT_FALSE(MessageBundle::IsValidName(std::string("a-BV-9")));
316 EXPECT_FALSE(MessageBundle::IsValidName(std::string("a#BV!9")));
317 EXPECT_FALSE(MessageBundle::IsValidName(std::string("a<b")));
320 struct ReplaceVariables {
321 const char* original;
322 const char* result;
323 const char* error;
324 const char* begin_delimiter;
325 const char* end_delimiter;
326 bool pass;
329 TEST(MessageBundle, ReplaceMessagesInText) {
330 const char* kMessageBegin = MessageBundle::kMessageBegin;
331 const char* kMessageEnd = MessageBundle::kMessageEnd;
332 const char* kPlaceholderBegin = MessageBundle::kPlaceholderBegin;
333 const char* kPlaceholderEnd = MessageBundle::kPlaceholderEnd;
335 static ReplaceVariables test_cases[] = {
336 // Message replacement.
337 { "This is __MSG_siMPle__ message", "This is simple message",
338 "", kMessageBegin, kMessageEnd, true },
339 { "This is __MSG_", "This is __MSG_",
340 "", kMessageBegin, kMessageEnd, true },
341 { "This is __MSG__simple__ message", "This is __MSG__simple__ message",
342 "Variable __MSG__simple__ used but not defined.",
343 kMessageBegin, kMessageEnd, false },
344 { "__MSG_LoNg__", "A pretty long replacement",
345 "", kMessageBegin, kMessageEnd, true },
346 { "A __MSG_SimpLE__MSG_ a", "A simpleMSG_ a",
347 "", kMessageBegin, kMessageEnd, true },
348 { "A __MSG_simple__MSG_long__", "A simpleMSG_long__",
349 "", kMessageBegin, kMessageEnd, true },
350 { "A __MSG_simple____MSG_long__", "A simpleA pretty long replacement",
351 "", kMessageBegin, kMessageEnd, true },
352 { "__MSG_d1g1ts_are_ok__", "I are d1g1t",
353 "", kMessageBegin, kMessageEnd, true },
354 // Placeholder replacement.
355 { "This is $sImpLe$ message", "This is simple message",
356 "", kPlaceholderBegin, kPlaceholderEnd, true },
357 { "This is $", "This is $",
358 "", kPlaceholderBegin, kPlaceholderEnd, true },
359 { "This is $$sIMPle$ message", "This is $simple message",
360 "", kPlaceholderBegin, kPlaceholderEnd, true },
361 { "$LONG_V$", "A pretty long replacement",
362 "", kPlaceholderBegin, kPlaceholderEnd, true },
363 { "A $simple$$ a", "A simple$ a",
364 "", kPlaceholderBegin, kPlaceholderEnd, true },
365 { "A $simple$long_v$", "A simplelong_v$",
366 "", kPlaceholderBegin, kPlaceholderEnd, true },
367 { "A $simple$$long_v$", "A simpleA pretty long replacement",
368 "", kPlaceholderBegin, kPlaceholderEnd, true },
369 { "This is $bad name$", "This is $bad name$",
370 "", kPlaceholderBegin, kPlaceholderEnd, true },
371 { "This is $missing$", "This is $missing$",
372 "Variable $missing$ used but not defined.",
373 kPlaceholderBegin, kPlaceholderEnd, false },
376 MessageBundle::SubstitutionMap messages;
377 messages.insert(std::make_pair("simple", "simple"));
378 messages.insert(std::make_pair("long", "A pretty long replacement"));
379 messages.insert(std::make_pair("long_v", "A pretty long replacement"));
380 messages.insert(std::make_pair("bad name", "Doesn't matter"));
381 messages.insert(std::make_pair("d1g1ts_are_ok", "I are d1g1t"));
383 for (size_t i = 0; i < arraysize(test_cases); ++i) {
384 std::string text = test_cases[i].original;
385 std::string error;
386 EXPECT_EQ(test_cases[i].pass,
387 MessageBundle::ReplaceVariables(messages,
388 test_cases[i].begin_delimiter,
389 test_cases[i].end_delimiter,
390 &text,
391 &error));
392 EXPECT_EQ(test_cases[i].result, text);
396 ///////////////////////////////////////////////////////////////////////////////
398 // Renderer helper functions test.
400 ///////////////////////////////////////////////////////////////////////////////
402 TEST(GetExtensionToL10nMessagesMapTest, ReturnsTheSameObject) {
403 ExtensionToL10nMessagesMap* map1 = GetExtensionToL10nMessagesMap();
404 ASSERT_TRUE(NULL != map1);
406 ExtensionToL10nMessagesMap* map2 = GetExtensionToL10nMessagesMap();
407 ASSERT_EQ(map1, map2);
410 TEST(GetExtensionToL10nMessagesMapTest, ReturnsNullForUnknownExtensionId) {
411 const std::string extension_id("some_unique_12334212314234_id");
412 L10nMessagesMap* map = GetL10nMessagesMap(extension_id);
413 EXPECT_TRUE(NULL == map);
416 TEST(GetExtensionToL10nMessagesMapTest, ReturnsMapForKnownExtensionId) {
417 const std::string extension_id("some_unique_121212121212121_id");
418 // Store a map for given id.
419 L10nMessagesMap messages;
420 messages.insert(std::make_pair("message_name", "message_value"));
421 (*GetExtensionToL10nMessagesMap())[extension_id] = messages;
423 L10nMessagesMap* map = GetL10nMessagesMap(extension_id);
424 ASSERT_TRUE(NULL != map);
425 EXPECT_EQ(1U, map->size());
426 EXPECT_EQ("message_value", (*map)["message_name"]);
429 } // namespace extensions