summaryrefslogtreecommitdiffstats
path: root/extensions/common/message_bundle_unittest.cc
diff options
context:
space:
mode:
authorrockot@chromium.org <rockot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-08 23:32:52 +0000
committerrockot@chromium.org <rockot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-08 23:32:52 +0000
commit6668e5d092c93131a8ba9735a08c9a85515e121b (patch)
treea82aadb249f10b150a0cfdb45508296e6b31c4bc /extensions/common/message_bundle_unittest.cc
parenta1c9554114647996f50e05ea5ee95390125ebc30 (diff)
downloadchromium_src-6668e5d092c93131a8ba9735a08c9a85515e121b.zip
chromium_src-6668e5d092c93131a8ba9735a08c9a85515e121b.tar.gz
chromium_src-6668e5d092c93131a8ba9735a08c9a85515e121b.tar.bz2
Move core extensions l10n code to //extensions
There are three important changes in this CL, all interdependent enough that it seemed best to tackle them at the same time. 1. message_bundle moves to //extensions. (mechanical) 2. extension_l10n_util moves to //extensions. (mechanical) 3. MessageBundle-related functionality from //chrome/c/e/extension_file_util has been moved into //extensions/common/file_util to support the move of extension_l10n_util. BUG=359836 TBR=sky@chromium.org for chrome/utility, browser_process_impl TBR=kalman@chromium.org for tabs API TBR=zork@chromium.org for //c/b/chromeos/input_method TBR=satorux@chromium.org for //c/b/chromeos/e/file_manager Review URL: https://codereview.chromium.org/228073005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@262552 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'extensions/common/message_bundle_unittest.cc')
-rw-r--r--extensions/common/message_bundle_unittest.cc434
1 files changed, 434 insertions, 0 deletions
diff --git a/extensions/common/message_bundle_unittest.cc b/extensions/common/message_bundle_unittest.cc
new file mode 100644
index 0000000..3f332fc
--- /dev/null
+++ b/extensions/common/message_bundle_unittest.cc
@@ -0,0 +1,434 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/common/message_bundle.h"
+
+#include <string>
+#include <vector>
+
+#include "base/i18n/rtl.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "extensions/common/error_utils.h"
+#include "extensions/common/extension_l10n_util.h"
+#include "extensions/common/manifest_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+namespace errors = manifest_errors;
+
+class MessageBundleTest : public testing::Test {
+ protected:
+ enum BadDictionary {
+ INVALID_NAME,
+ NAME_NOT_A_TREE,
+ EMPTY_NAME_TREE,
+ MISSING_MESSAGE,
+ PLACEHOLDER_NOT_A_TREE,
+ EMPTY_PLACEHOLDER_TREE,
+ CONTENT_MISSING,
+ MESSAGE_PLACEHOLDER_DOESNT_MATCH,
+ };
+
+ // Helper method for dictionary building.
+ void SetDictionary(const std::string& name,
+ base::DictionaryValue* subtree,
+ base::DictionaryValue* target) {
+ target->Set(name, static_cast<base::Value*>(subtree));
+ }
+
+ void CreateContentTree(const std::string& name,
+ const std::string& content,
+ base::DictionaryValue* dict) {
+ base::DictionaryValue* content_tree = new base::DictionaryValue;
+ content_tree->SetString(MessageBundle::kContentKey, content);
+ SetDictionary(name, content_tree, dict);
+ }
+
+ void CreatePlaceholdersTree(base::DictionaryValue* dict) {
+ base::DictionaryValue* placeholders_tree = new base::DictionaryValue;
+ CreateContentTree("a", "A", placeholders_tree);
+ CreateContentTree("b", "B", placeholders_tree);
+ CreateContentTree("c", "C", placeholders_tree);
+ SetDictionary(MessageBundle::kPlaceholdersKey,
+ placeholders_tree,
+ dict);
+ }
+
+ void CreateMessageTree(const std::string& name,
+ const std::string& message,
+ bool create_placeholder_subtree,
+ base::DictionaryValue* dict) {
+ base::DictionaryValue* message_tree = new base::DictionaryValue;
+ if (create_placeholder_subtree)
+ CreatePlaceholdersTree(message_tree);
+ message_tree->SetString(MessageBundle::kMessageKey, message);
+ SetDictionary(name, message_tree, dict);
+ }
+
+ // Caller owns the memory.
+ base::DictionaryValue* CreateGoodDictionary() {
+ base::DictionaryValue* dict = new base::DictionaryValue;
+ CreateMessageTree("n1", "message1 $a$ $b$", true, dict);
+ CreateMessageTree("n2", "message2 $c$", true, dict);
+ CreateMessageTree("n3", "message3", false, dict);
+ return dict;
+ }
+
+ // Caller owns the memory.
+ base::DictionaryValue* CreateBadDictionary(enum BadDictionary what_is_bad) {
+ base::DictionaryValue* dict = CreateGoodDictionary();
+ // Now remove/break things.
+ switch (what_is_bad) {
+ case INVALID_NAME:
+ CreateMessageTree("n 5", "nevermind", false, dict);
+ break;
+ case NAME_NOT_A_TREE:
+ dict->SetString("n4", "whatever");
+ break;
+ case EMPTY_NAME_TREE: {
+ base::DictionaryValue* empty_tree = new base::DictionaryValue;
+ SetDictionary("n4", empty_tree, dict);
+ }
+ break;
+ case MISSING_MESSAGE:
+ dict->Remove("n1.message", NULL);
+ break;
+ case PLACEHOLDER_NOT_A_TREE:
+ dict->SetString("n1.placeholders", "whatever");
+ break;
+ case EMPTY_PLACEHOLDER_TREE: {
+ base::DictionaryValue* empty_tree = new base::DictionaryValue;
+ SetDictionary("n1.placeholders", empty_tree, dict);
+ }
+ break;
+ case CONTENT_MISSING:
+ dict->Remove("n1.placeholders.a.content", NULL);
+ break;
+ case MESSAGE_PLACEHOLDER_DOESNT_MATCH:
+ base::DictionaryValue* value;
+ dict->Remove("n1.placeholders.a", NULL);
+ dict->GetDictionary("n1.placeholders", &value);
+ CreateContentTree("x", "X", value);
+ break;
+ }
+
+ return dict;
+ }
+
+ unsigned int ReservedMessagesCount() {
+ // Update when adding new reserved messages.
+ return 5U;
+ }
+
+ void CheckReservedMessages(MessageBundle* handler) {
+ std::string ui_locale = extension_l10n_util::CurrentLocaleOrDefault();
+ EXPECT_EQ(ui_locale,
+ handler->GetL10nMessage(MessageBundle::kUILocaleKey));
+
+ std::string text_dir = "ltr";
+ if (base::i18n::GetTextDirectionForLocale(ui_locale.c_str()) ==
+ base::i18n::RIGHT_TO_LEFT)
+ text_dir = "rtl";
+
+ EXPECT_EQ(text_dir, handler->GetL10nMessage(
+ MessageBundle::kBidiDirectionKey));
+ }
+
+ bool AppendReservedMessages(const std::string& application_locale) {
+ std::string error;
+ return handler_->AppendReservedMessagesForLocale(
+ application_locale, &error);
+ }
+
+ std::string CreateMessageBundle() {
+ std::string error;
+ handler_.reset(MessageBundle::Create(catalogs_, &error));
+
+ return error;
+ }
+
+ void ClearDictionary() {
+ handler_->dictionary_.clear();
+ }
+
+ scoped_ptr<MessageBundle> handler_;
+ std::vector<linked_ptr<base::DictionaryValue> > catalogs_;
+};
+
+TEST_F(MessageBundleTest, ReservedMessagesCount) {
+ ASSERT_EQ(5U, ReservedMessagesCount());
+}
+
+TEST_F(MessageBundleTest, InitEmptyDictionaries) {
+ CreateMessageBundle();
+ EXPECT_TRUE(handler_.get() != NULL);
+ EXPECT_EQ(0U + ReservedMessagesCount(), handler_->size());
+ CheckReservedMessages(handler_.get());
+}
+
+TEST_F(MessageBundleTest, InitGoodDefaultDict) {
+ catalogs_.push_back(
+ linked_ptr<base::DictionaryValue>(CreateGoodDictionary()));
+ CreateMessageBundle();
+
+ EXPECT_TRUE(handler_.get() != NULL);
+ EXPECT_EQ(3U + ReservedMessagesCount(), handler_->size());
+
+ EXPECT_EQ("message1 A B", handler_->GetL10nMessage("n1"));
+ EXPECT_EQ("message2 C", handler_->GetL10nMessage("n2"));
+ EXPECT_EQ("message3", handler_->GetL10nMessage("n3"));
+ CheckReservedMessages(handler_.get());
+}
+
+TEST_F(MessageBundleTest, InitAppDictConsultedFirst) {
+ catalogs_.push_back(
+ linked_ptr<base::DictionaryValue>(CreateGoodDictionary()));
+ catalogs_.push_back(
+ linked_ptr<base::DictionaryValue>(CreateGoodDictionary()));
+
+ base::DictionaryValue* app_dict = catalogs_[0].get();
+ // Flip placeholders in message of n1 tree.
+ app_dict->SetString("n1.message", "message1 $b$ $a$");
+ // Remove one message from app dict.
+ app_dict->Remove("n2", NULL);
+ // Replace n3 with N3.
+ app_dict->Remove("n3", NULL);
+ CreateMessageTree("N3", "message3_app_dict", false, app_dict);
+
+ CreateMessageBundle();
+
+ EXPECT_TRUE(handler_.get() != NULL);
+ EXPECT_EQ(3U + ReservedMessagesCount(), handler_->size());
+
+ EXPECT_EQ("message1 B A", handler_->GetL10nMessage("n1"));
+ EXPECT_EQ("message2 C", handler_->GetL10nMessage("n2"));
+ EXPECT_EQ("message3_app_dict", handler_->GetL10nMessage("n3"));
+ CheckReservedMessages(handler_.get());
+}
+
+TEST_F(MessageBundleTest, InitBadAppDict) {
+ catalogs_.push_back(
+ linked_ptr<base::DictionaryValue>(CreateBadDictionary(INVALID_NAME)));
+ catalogs_.push_back(
+ linked_ptr<base::DictionaryValue>(CreateGoodDictionary()));
+
+ std::string error = CreateMessageBundle();
+
+ EXPECT_TRUE(handler_.get() == NULL);
+ EXPECT_EQ("Name of a key \"n 5\" is invalid. Only ASCII [a-z], "
+ "[A-Z], [0-9] and \"_\" are allowed.", error);
+
+ catalogs_[0].reset(CreateBadDictionary(NAME_NOT_A_TREE));
+ handler_.reset(MessageBundle::Create(catalogs_, &error));
+ EXPECT_TRUE(handler_.get() == NULL);
+ EXPECT_EQ("Not a valid tree for key n4.", error);
+
+ catalogs_[0].reset(CreateBadDictionary(EMPTY_NAME_TREE));
+ handler_.reset(MessageBundle::Create(catalogs_, &error));
+ EXPECT_TRUE(handler_.get() == NULL);
+ EXPECT_EQ("There is no \"message\" element for key n4.", error);
+
+ catalogs_[0].reset(CreateBadDictionary(MISSING_MESSAGE));
+ handler_.reset(MessageBundle::Create(catalogs_, &error));
+ EXPECT_TRUE(handler_.get() == NULL);
+ EXPECT_EQ("There is no \"message\" element for key n1.", error);
+
+ catalogs_[0].reset(CreateBadDictionary(PLACEHOLDER_NOT_A_TREE));
+ handler_.reset(MessageBundle::Create(catalogs_, &error));
+ EXPECT_TRUE(handler_.get() == NULL);
+ EXPECT_EQ("Not a valid \"placeholders\" element for key n1.", error);
+
+ catalogs_[0].reset(CreateBadDictionary(EMPTY_PLACEHOLDER_TREE));
+ handler_.reset(MessageBundle::Create(catalogs_, &error));
+ EXPECT_TRUE(handler_.get() == NULL);
+ EXPECT_EQ("Variable $a$ used but not defined.", error);
+
+ catalogs_[0].reset(CreateBadDictionary(CONTENT_MISSING));
+ handler_.reset(MessageBundle::Create(catalogs_, &error));
+ EXPECT_TRUE(handler_.get() == NULL);
+ EXPECT_EQ("Invalid \"content\" element for key n1.", error);
+
+ catalogs_[0].reset(CreateBadDictionary(MESSAGE_PLACEHOLDER_DOESNT_MATCH));
+ handler_.reset(MessageBundle::Create(catalogs_, &error));
+ EXPECT_TRUE(handler_.get() == NULL);
+ EXPECT_EQ("Variable $a$ used but not defined.", error);
+}
+
+TEST_F(MessageBundleTest, ReservedMessagesOverrideDeveloperMessages) {
+ catalogs_.push_back(
+ linked_ptr<base::DictionaryValue>(CreateGoodDictionary()));
+
+ base::DictionaryValue* dict = catalogs_[0].get();
+ CreateMessageTree(MessageBundle::kUILocaleKey, "x", false, dict);
+
+ std::string error = CreateMessageBundle();
+
+ EXPECT_TRUE(handler_.get() == NULL);
+ std::string expected_error = ErrorUtils::FormatErrorMessage(
+ errors::kReservedMessageFound, MessageBundle::kUILocaleKey);
+ EXPECT_EQ(expected_error, error);
+}
+
+TEST_F(MessageBundleTest, AppendReservedMessagesForLTR) {
+ CreateMessageBundle();
+
+ ASSERT_TRUE(handler_.get() != NULL);
+ ClearDictionary();
+ ASSERT_TRUE(AppendReservedMessages("en_US"));
+
+ EXPECT_EQ("en_US",
+ handler_->GetL10nMessage(MessageBundle::kUILocaleKey));
+ EXPECT_EQ("ltr", handler_->GetL10nMessage(
+ MessageBundle::kBidiDirectionKey));
+ EXPECT_EQ("rtl", handler_->GetL10nMessage(
+ MessageBundle::kBidiReversedDirectionKey));
+ EXPECT_EQ("left", handler_->GetL10nMessage(
+ MessageBundle::kBidiStartEdgeKey));
+ EXPECT_EQ("right", handler_->GetL10nMessage(
+ MessageBundle::kBidiEndEdgeKey));
+}
+
+TEST_F(MessageBundleTest, AppendReservedMessagesForRTL) {
+ CreateMessageBundle();
+
+ ASSERT_TRUE(handler_.get() != NULL);
+ ClearDictionary();
+ ASSERT_TRUE(AppendReservedMessages("he"));
+
+ EXPECT_EQ("he",
+ handler_->GetL10nMessage(MessageBundle::kUILocaleKey));
+ EXPECT_EQ("rtl", handler_->GetL10nMessage(
+ MessageBundle::kBidiDirectionKey));
+ EXPECT_EQ("ltr", handler_->GetL10nMessage(
+ MessageBundle::kBidiReversedDirectionKey));
+ EXPECT_EQ("right", handler_->GetL10nMessage(
+ MessageBundle::kBidiStartEdgeKey));
+ EXPECT_EQ("left", handler_->GetL10nMessage(
+ MessageBundle::kBidiEndEdgeKey));
+}
+
+TEST_F(MessageBundleTest, IsValidNameCheckValidCharacters) {
+ EXPECT_TRUE(MessageBundle::IsValidName(std::string("a__BV_9")));
+ EXPECT_TRUE(MessageBundle::IsValidName(std::string("@@a__BV_9")));
+ EXPECT_FALSE(MessageBundle::IsValidName(std::string("$a__BV_9$")));
+ EXPECT_FALSE(MessageBundle::IsValidName(std::string("a-BV-9")));
+ EXPECT_FALSE(MessageBundle::IsValidName(std::string("a#BV!9")));
+ EXPECT_FALSE(MessageBundle::IsValidName(std::string("a<b")));
+}
+
+struct ReplaceVariables {
+ const char* original;
+ const char* result;
+ const char* error;
+ const char* begin_delimiter;
+ const char* end_delimiter;
+ bool pass;
+};
+
+TEST(MessageBundle, ReplaceMessagesInText) {
+ const char* kMessageBegin = MessageBundle::kMessageBegin;
+ const char* kMessageEnd = MessageBundle::kMessageEnd;
+ const char* kPlaceholderBegin = MessageBundle::kPlaceholderBegin;
+ const char* kPlaceholderEnd = MessageBundle::kPlaceholderEnd;
+
+ static ReplaceVariables test_cases[] = {
+ // Message replacement.
+ { "This is __MSG_siMPle__ message", "This is simple message",
+ "", kMessageBegin, kMessageEnd, true },
+ { "This is __MSG_", "This is __MSG_",
+ "", kMessageBegin, kMessageEnd, true },
+ { "This is __MSG__simple__ message", "This is __MSG__simple__ message",
+ "Variable __MSG__simple__ used but not defined.",
+ kMessageBegin, kMessageEnd, false },
+ { "__MSG_LoNg__", "A pretty long replacement",
+ "", kMessageBegin, kMessageEnd, true },
+ { "A __MSG_SimpLE__MSG_ a", "A simpleMSG_ a",
+ "", kMessageBegin, kMessageEnd, true },
+ { "A __MSG_simple__MSG_long__", "A simpleMSG_long__",
+ "", kMessageBegin, kMessageEnd, true },
+ { "A __MSG_simple____MSG_long__", "A simpleA pretty long replacement",
+ "", kMessageBegin, kMessageEnd, true },
+ { "__MSG_d1g1ts_are_ok__", "I are d1g1t",
+ "", kMessageBegin, kMessageEnd, true },
+ // Placeholder replacement.
+ { "This is $sImpLe$ message", "This is simple message",
+ "", kPlaceholderBegin, kPlaceholderEnd, true },
+ { "This is $", "This is $",
+ "", kPlaceholderBegin, kPlaceholderEnd, true },
+ { "This is $$sIMPle$ message", "This is $simple message",
+ "", kPlaceholderBegin, kPlaceholderEnd, true },
+ { "$LONG_V$", "A pretty long replacement",
+ "", kPlaceholderBegin, kPlaceholderEnd, true },
+ { "A $simple$$ a", "A simple$ a",
+ "", kPlaceholderBegin, kPlaceholderEnd, true },
+ { "A $simple$long_v$", "A simplelong_v$",
+ "", kPlaceholderBegin, kPlaceholderEnd, true },
+ { "A $simple$$long_v$", "A simpleA pretty long replacement",
+ "", kPlaceholderBegin, kPlaceholderEnd, true },
+ { "This is $bad name$", "This is $bad name$",
+ "", kPlaceholderBegin, kPlaceholderEnd, true },
+ { "This is $missing$", "This is $missing$",
+ "Variable $missing$ used but not defined.",
+ kPlaceholderBegin, kPlaceholderEnd, false },
+ };
+
+ MessageBundle::SubstitutionMap messages;
+ messages.insert(std::make_pair("simple", "simple"));
+ messages.insert(std::make_pair("long", "A pretty long replacement"));
+ messages.insert(std::make_pair("long_v", "A pretty long replacement"));
+ messages.insert(std::make_pair("bad name", "Doesn't matter"));
+ messages.insert(std::make_pair("d1g1ts_are_ok", "I are d1g1t"));
+
+ for (size_t i = 0; i < arraysize(test_cases); ++i) {
+ std::string text = test_cases[i].original;
+ std::string error;
+ EXPECT_EQ(test_cases[i].pass,
+ MessageBundle::ReplaceVariables(messages,
+ test_cases[i].begin_delimiter,
+ test_cases[i].end_delimiter,
+ &text,
+ &error));
+ EXPECT_EQ(test_cases[i].result, text);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Renderer helper functions test.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+TEST(GetExtensionToL10nMessagesMapTest, ReturnsTheSameObject) {
+ ExtensionToL10nMessagesMap* map1 = GetExtensionToL10nMessagesMap();
+ ASSERT_TRUE(NULL != map1);
+
+ ExtensionToL10nMessagesMap* map2 = GetExtensionToL10nMessagesMap();
+ ASSERT_EQ(map1, map2);
+}
+
+TEST(GetExtensionToL10nMessagesMapTest, ReturnsNullForUnknownExtensionId) {
+ const std::string extension_id("some_unique_12334212314234_id");
+ L10nMessagesMap* map = GetL10nMessagesMap(extension_id);
+ EXPECT_TRUE(NULL == map);
+}
+
+TEST(GetExtensionToL10nMessagesMapTest, ReturnsMapForKnownExtensionId) {
+ const std::string extension_id("some_unique_121212121212121_id");
+ // Store a map for given id.
+ L10nMessagesMap messages;
+ messages.insert(std::make_pair("message_name", "message_value"));
+ (*GetExtensionToL10nMessagesMap())[extension_id] = messages;
+
+ L10nMessagesMap* map = GetL10nMessagesMap(extension_id);
+ ASSERT_TRUE(NULL != map);
+ EXPECT_EQ(1U, map->size());
+ EXPECT_EQ("message_value", (*map)["message_name"]);
+}
+
+} // namespace extensions