/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "utils/resources.h" #include "utils/i18n/locale.h" #include "utils/resources_generated.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace libtextclassifier3 { namespace { class ResourcesTest : public testing::Test { protected: ResourcesTest() {} std::string BuildTestResources(bool add_default_language = true) const { ResourcePoolT test_resources; // Test locales. test_resources.locale.emplace_back(new LanguageTagT); test_resources.locale.back()->language = "en"; test_resources.locale.back()->region = "US"; test_resources.locale.emplace_back(new LanguageTagT); test_resources.locale.back()->language = "en"; test_resources.locale.back()->region = "GB"; test_resources.locale.emplace_back(new LanguageTagT); test_resources.locale.back()->language = "de"; test_resources.locale.back()->region = "DE"; test_resources.locale.emplace_back(new LanguageTagT); test_resources.locale.back()->language = "fr"; test_resources.locale.back()->region = "FR"; test_resources.locale.emplace_back(new LanguageTagT); test_resources.locale.back()->language = "pt"; test_resources.locale.back()->region = "PT"; test_resources.locale.emplace_back(new LanguageTagT); test_resources.locale.back()->language = "pt"; test_resources.locale.emplace_back(new LanguageTagT); test_resources.locale.back()->language = "zh"; test_resources.locale.back()->script = "Hans"; test_resources.locale.back()->region = "CN"; test_resources.locale.emplace_back(new LanguageTagT); test_resources.locale.back()->language = "zh"; test_resources.locale.emplace_back(new LanguageTagT); test_resources.locale.back()->language = "fr"; test_resources.locale.back()->region = "CA"; test_resources.locale.emplace_back(new LanguageTagT); test_resources.locale.back()->language = "in"; if (add_default_language) { test_resources.locale.emplace_back(new LanguageTagT); // default } // Test entries. test_resources.resource_entry.emplace_back(new ResourceEntryT); test_resources.resource_entry.back()->name = /*resource_name=*/"A"; // en-US, default test_resources.resource_entry.back()->resource.emplace_back(new ResourceT); test_resources.resource_entry.back()->resource.back()->content = "localize"; test_resources.resource_entry.back()->resource.back()->locale.push_back(0); if (add_default_language) { test_resources.resource_entry.back()->resource.back()->locale.push_back( 10); } // en-GB test_resources.resource_entry.back()->resource.emplace_back(new ResourceT); test_resources.resource_entry.back()->resource.back()->content = "localise"; test_resources.resource_entry.back()->resource.back()->locale.push_back(1); // de-DE test_resources.resource_entry.back()->resource.emplace_back(new ResourceT); test_resources.resource_entry.back()->resource.back()->content = "lokalisieren"; test_resources.resource_entry.back()->resource.back()->locale.push_back(2); // fr-FR, fr-CA test_resources.resource_entry.back()->resource.emplace_back(new ResourceT); test_resources.resource_entry.back()->resource.back()->content = "localiser"; test_resources.resource_entry.back()->resource.back()->locale.push_back(3); test_resources.resource_entry.back()->resource.back()->locale.push_back(8); // pt-PT test_resources.resource_entry.back()->resource.emplace_back(new ResourceT); test_resources.resource_entry.back()->resource.back()->content = "localizar"; test_resources.resource_entry.back()->resource.back()->locale.push_back(4); // pt test_resources.resource_entry.back()->resource.emplace_back(new ResourceT); test_resources.resource_entry.back()->resource.back()->content = "concentrar"; test_resources.resource_entry.back()->resource.back()->locale.push_back(5); // zh-Hans-CN test_resources.resource_entry.back()->resource.emplace_back(new ResourceT); test_resources.resource_entry.back()->resource.back()->content = "龙"; test_resources.resource_entry.back()->resource.back()->locale.push_back(6); // zh test_resources.resource_entry.back()->resource.emplace_back(new ResourceT); test_resources.resource_entry.back()->resource.back()->content = "龍"; test_resources.resource_entry.back()->resource.back()->locale.push_back(7); // in test_resources.resource_entry.back()->resource.emplace_back(new ResourceT); test_resources.resource_entry.back()->resource.back()->content = "Apa kabar"; test_resources.resource_entry.back()->resource.back()->locale.push_back(9); flatbuffers::FlatBufferBuilder builder; builder.Finish(ResourcePool::Pack(builder, &test_resources)); return std::string( reinterpret_cast(builder.GetBufferPointer()), builder.GetSize()); } }; TEST_F(ResourcesTest, CorrectlyHandlesExactMatch) { std::string test_resources = BuildTestResources(); Resources resources( flatbuffers::GetRoot(test_resources.data())); std::string content; EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("en-US")}, /*resource_name=*/"A", &content)); EXPECT_EQ("localize", content); EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("en-GB")}, /*resource_name=*/"A", &content)); EXPECT_EQ("localise", content); EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("pt-PT")}, /*resource_name=*/"A", &content)); EXPECT_EQ("localizar", content); EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("zh-Hans-CN")}, /*resource_name=*/"A", &content)); EXPECT_EQ("龙", content); EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("zh")}, /*resource_name=*/"A", &content)); EXPECT_EQ("龍", content); EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("fr-CA")}, /*resource_name=*/"A", &content)); EXPECT_EQ("localiser", content); EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("id")}, /*resource_name=*/"A", &content)); EXPECT_EQ("Apa kabar", content); } TEST_F(ResourcesTest, CorrectlyHandlesTie) { std::string test_resources = BuildTestResources(); Resources resources( flatbuffers::GetRoot(test_resources.data())); // Uses first best match in case of a tie. std::string content; EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("en-CA")}, /*resource_name=*/"A", &content)); EXPECT_EQ("localize", content); } TEST_F(ResourcesTest, RequiresLanguageMatch) { { std::string test_resources = BuildTestResources(/*add_default_language=*/false); Resources resources( flatbuffers::GetRoot(test_resources.data())); EXPECT_FALSE(resources.GetResourceContent({Locale::FromBCP47("es-US")}, /*resource_name=*/"A", /*result=*/nullptr)); } { std::string test_resources = BuildTestResources(/*add_default_language=*/true); Resources resources( flatbuffers::GetRoot(test_resources.data())); std::string content; EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("es-US")}, /*resource_name=*/"A", /*result=*/&content)); EXPECT_EQ("localize", content); } } TEST_F(ResourcesTest, HandlesFallback) { std::string test_resources = BuildTestResources(); Resources resources( flatbuffers::GetRoot(test_resources.data())); std::string content; EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("fr-CH")}, /*resource_name=*/"A", &content)); EXPECT_EQ("localiser", content); EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("zh-Hans")}, /*resource_name=*/"A", &content)); EXPECT_EQ("龙", content); EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("zh-Hans-ZZ")}, /*resource_name=*/"A", &content)); EXPECT_EQ("龙", content); // Fallback to default, en-US. EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("ru")}, /*resource_name=*/"A", &content)); EXPECT_EQ("localize", content); } TEST_F(ResourcesTest, HandlesFallbackMultipleLocales) { std::string test_resources = BuildTestResources(); Resources resources( flatbuffers::GetRoot(test_resources.data())); std::string content; // Still use inexact match with primary locale if language matches, // even though secondary locale would match exactly. EXPECT_TRUE(resources.GetResourceContent( {Locale::FromBCP47("fr-CH"), Locale::FromBCP47("en-US")}, /*resource_name=*/"A", &content)); EXPECT_EQ("localiser", content); // Use secondary language instead of default fallback if that is an exact // language match. EXPECT_TRUE(resources.GetResourceContent( {Locale::FromBCP47("ru"), Locale::FromBCP47("de")}, /*resource_name=*/"A", &content)); EXPECT_EQ("lokalisieren", content); // Use tertiary language. EXPECT_TRUE(resources.GetResourceContent( {Locale::FromBCP47("ru"), Locale::FromBCP47("it-IT"), Locale::FromBCP47("de")}, /*resource_name=*/"A", &content)); EXPECT_EQ("lokalisieren", content); // Default fallback if no locale matches. EXPECT_TRUE(resources.GetResourceContent( {Locale::FromBCP47("ru"), Locale::FromBCP47("it-IT"), Locale::FromBCP47("es")}, /*resource_name=*/"A", &content)); EXPECT_EQ("localize", content); } TEST_F(ResourcesTest, PreferGenericCallback) { std::string test_resources = BuildTestResources(); Resources resources( flatbuffers::GetRoot(test_resources.data())); std::string content; EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("pt-BR")}, /*resource_name=*/"A", &content)); EXPECT_EQ("concentrar", content); // Falls back to pt, not pt-PT. EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("zh-Hant")}, /*resource_name=*/"A", &content)); EXPECT_EQ("龍", content); // Falls back to zh, not zh-Hans-CN. EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("zh-Hant-CN")}, /*resource_name=*/"A", &content)); EXPECT_EQ("龍", content); // Falls back to zh, not zh-Hans-CN. EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("zh-CN")}, /*resource_name=*/"A", &content)); EXPECT_EQ("龍", content); // Falls back to zh, not zh-Hans-CN. } TEST_F(ResourcesTest, PreferGenericWhenGeneric) { std::string test_resources = BuildTestResources(); Resources resources( flatbuffers::GetRoot(test_resources.data())); std::string content; EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("pt")}, /*resource_name=*/"A", &content)); // Uses pt, not pt-PT. EXPECT_EQ("concentrar", content); } } // namespace } // namespace libtextclassifier3