diff options
author | rockot@chromium.org <rockot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-08 23:32:52 +0000 |
---|---|---|
committer | rockot@chromium.org <rockot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-08 23:32:52 +0000 |
commit | 6668e5d092c93131a8ba9735a08c9a85515e121b (patch) | |
tree | a82aadb249f10b150a0cfdb45508296e6b31c4bc /extensions/common/message_bundle_unittest.cc | |
parent | a1c9554114647996f50e05ea5ee95390125ebc30 (diff) | |
download | chromium_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.cc | 434 |
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 |