1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "utils/resources.h"
18
19 #include "utils/i18n/locale.h"
20 #include "utils/resources_generated.h"
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
23
24 namespace libtextclassifier3 {
25 namespace {
26
27 class ResourcesTest : public testing::Test {
28 protected:
ResourcesTest()29 ResourcesTest() {}
30
BuildTestResources(bool add_default_language=true) const31 std::string BuildTestResources(bool add_default_language = true) const {
32 ResourcePoolT test_resources;
33
34 // Test locales.
35 test_resources.locale.emplace_back(new LanguageTagT);
36 test_resources.locale.back()->language = "en";
37 test_resources.locale.back()->region = "US";
38 test_resources.locale.emplace_back(new LanguageTagT);
39 test_resources.locale.back()->language = "en";
40 test_resources.locale.back()->region = "GB";
41 test_resources.locale.emplace_back(new LanguageTagT);
42 test_resources.locale.back()->language = "de";
43 test_resources.locale.back()->region = "DE";
44 test_resources.locale.emplace_back(new LanguageTagT);
45 test_resources.locale.back()->language = "fr";
46 test_resources.locale.back()->region = "FR";
47 test_resources.locale.emplace_back(new LanguageTagT);
48 test_resources.locale.back()->language = "pt";
49 test_resources.locale.back()->region = "PT";
50 test_resources.locale.emplace_back(new LanguageTagT);
51 test_resources.locale.back()->language = "pt";
52 test_resources.locale.emplace_back(new LanguageTagT);
53 test_resources.locale.back()->language = "zh";
54 test_resources.locale.back()->script = "Hans";
55 test_resources.locale.back()->region = "CN";
56 test_resources.locale.emplace_back(new LanguageTagT);
57 test_resources.locale.back()->language = "zh";
58 test_resources.locale.emplace_back(new LanguageTagT);
59 test_resources.locale.back()->language = "fr";
60 test_resources.locale.back()->region = "CA";
61 if (add_default_language) {
62 test_resources.locale.emplace_back(new LanguageTagT); // default
63 }
64
65 // Test entries.
66 test_resources.resource_entry.emplace_back(new ResourceEntryT);
67 test_resources.resource_entry.back()->name = /*resource_name=*/"A";
68
69 // en-US, default
70 test_resources.resource_entry.back()->resource.emplace_back(new ResourceT);
71 test_resources.resource_entry.back()->resource.back()->content = "localize";
72 test_resources.resource_entry.back()->resource.back()->locale.push_back(0);
73 if (add_default_language) {
74 test_resources.resource_entry.back()->resource.back()->locale.push_back(
75 9);
76 }
77
78 // en-GB
79 test_resources.resource_entry.back()->resource.emplace_back(new ResourceT);
80 test_resources.resource_entry.back()->resource.back()->content = "localise";
81 test_resources.resource_entry.back()->resource.back()->locale.push_back(1);
82
83 // de-DE
84 test_resources.resource_entry.back()->resource.emplace_back(new ResourceT);
85 test_resources.resource_entry.back()->resource.back()->content =
86 "lokalisieren";
87 test_resources.resource_entry.back()->resource.back()->locale.push_back(2);
88
89 // fr-FR, fr-CA
90 test_resources.resource_entry.back()->resource.emplace_back(new ResourceT);
91 test_resources.resource_entry.back()->resource.back()->content =
92 "localiser";
93 test_resources.resource_entry.back()->resource.back()->locale.push_back(3);
94 test_resources.resource_entry.back()->resource.back()->locale.push_back(8);
95
96 // pt-PT
97 test_resources.resource_entry.back()->resource.emplace_back(new ResourceT);
98 test_resources.resource_entry.back()->resource.back()->content =
99 "localizar";
100 test_resources.resource_entry.back()->resource.back()->locale.push_back(4);
101
102 // pt
103 test_resources.resource_entry.back()->resource.emplace_back(new ResourceT);
104 test_resources.resource_entry.back()->resource.back()->content =
105 "concentrar";
106 test_resources.resource_entry.back()->resource.back()->locale.push_back(5);
107
108 // zh-Hans-CN
109 test_resources.resource_entry.back()->resource.emplace_back(new ResourceT);
110 test_resources.resource_entry.back()->resource.back()->content = "龙";
111 test_resources.resource_entry.back()->resource.back()->locale.push_back(6);
112
113 // zh
114 test_resources.resource_entry.back()->resource.emplace_back(new ResourceT);
115 test_resources.resource_entry.back()->resource.back()->content = "龍";
116 test_resources.resource_entry.back()->resource.back()->locale.push_back(7);
117
118 flatbuffers::FlatBufferBuilder builder;
119 builder.Finish(ResourcePool::Pack(builder, &test_resources));
120
121 return std::string(
122 reinterpret_cast<const char*>(builder.GetBufferPointer()),
123 builder.GetSize());
124 }
125 };
126
TEST_F(ResourcesTest,CorrectlyHandlesExactMatch)127 TEST_F(ResourcesTest, CorrectlyHandlesExactMatch) {
128 std::string test_resources = BuildTestResources();
129 Resources resources(
130 flatbuffers::GetRoot<ResourcePool>(test_resources.data()));
131 std::string content;
132 EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("en-US")},
133 /*resource_name=*/"A", &content));
134 EXPECT_EQ("localize", content);
135 EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("en-GB")},
136 /*resource_name=*/"A", &content));
137 EXPECT_EQ("localise", content);
138 EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("pt-PT")},
139 /*resource_name=*/"A", &content));
140 EXPECT_EQ("localizar", content);
141 EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("zh-Hans-CN")},
142 /*resource_name=*/"A", &content));
143 EXPECT_EQ("龙", content);
144 EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("zh")},
145 /*resource_name=*/"A", &content));
146 EXPECT_EQ("龍", content);
147 EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("fr-CA")},
148 /*resource_name=*/"A", &content));
149 EXPECT_EQ("localiser", content);
150 }
151
TEST_F(ResourcesTest,CorrectlyHandlesTie)152 TEST_F(ResourcesTest, CorrectlyHandlesTie) {
153 std::string test_resources = BuildTestResources();
154 Resources resources(
155 flatbuffers::GetRoot<ResourcePool>(test_resources.data()));
156 // Uses first best match in case of a tie.
157 std::string content;
158 EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("en-CA")},
159 /*resource_name=*/"A", &content));
160 EXPECT_EQ("localize", content);
161 }
162
TEST_F(ResourcesTest,RequiresLanguageMatch)163 TEST_F(ResourcesTest, RequiresLanguageMatch) {
164 {
165 std::string test_resources =
166 BuildTestResources(/*add_default_language=*/false);
167 Resources resources(
168 flatbuffers::GetRoot<ResourcePool>(test_resources.data()));
169 EXPECT_FALSE(resources.GetResourceContent({Locale::FromBCP47("es-US")},
170 /*resource_name=*/"A",
171 /*result=*/nullptr));
172 }
173 {
174 std::string test_resources =
175 BuildTestResources(/*add_default_language=*/true);
176 Resources resources(
177 flatbuffers::GetRoot<ResourcePool>(test_resources.data()));
178 std::string content;
179 EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("es-US")},
180 /*resource_name=*/"A",
181 /*result=*/&content));
182 EXPECT_EQ("localize", content);
183 }
184 }
185
TEST_F(ResourcesTest,HandlesFallback)186 TEST_F(ResourcesTest, HandlesFallback) {
187 std::string test_resources = BuildTestResources();
188 Resources resources(
189 flatbuffers::GetRoot<ResourcePool>(test_resources.data()));
190 std::string content;
191 EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("fr-CH")},
192 /*resource_name=*/"A", &content));
193 EXPECT_EQ("localiser", content);
194 EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("zh-Hans")},
195 /*resource_name=*/"A", &content));
196 EXPECT_EQ("龙", content);
197 EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("zh-Hans-ZZ")},
198 /*resource_name=*/"A", &content));
199 EXPECT_EQ("龙", content);
200
201 // Fallback to default, en-US.
202 EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("ru")},
203 /*resource_name=*/"A", &content));
204 EXPECT_EQ("localize", content);
205 }
206
TEST_F(ResourcesTest,HandlesFallbackMultipleLocales)207 TEST_F(ResourcesTest, HandlesFallbackMultipleLocales) {
208 std::string test_resources = BuildTestResources();
209 Resources resources(
210 flatbuffers::GetRoot<ResourcePool>(test_resources.data()));
211 std::string content;
212
213 // Still use inexact match with primary locale if language matches,
214 // even though secondary locale would match exactly.
215 EXPECT_TRUE(resources.GetResourceContent(
216 {Locale::FromBCP47("fr-CH"), Locale::FromBCP47("en-US")},
217 /*resource_name=*/"A", &content));
218 EXPECT_EQ("localiser", content);
219
220 // Use secondary language instead of default fallback if that is an exact
221 // language match.
222 EXPECT_TRUE(resources.GetResourceContent(
223 {Locale::FromBCP47("ru"), Locale::FromBCP47("de")},
224 /*resource_name=*/"A", &content));
225 EXPECT_EQ("lokalisieren", content);
226
227 // Use tertiary language.
228 EXPECT_TRUE(resources.GetResourceContent(
229 {Locale::FromBCP47("ru"), Locale::FromBCP47("it-IT"),
230 Locale::FromBCP47("de")},
231 /*resource_name=*/"A", &content));
232 EXPECT_EQ("lokalisieren", content);
233
234 // Default fallback if no locale matches.
235 EXPECT_TRUE(resources.GetResourceContent(
236 {Locale::FromBCP47("ru"), Locale::FromBCP47("it-IT"),
237 Locale::FromBCP47("es")},
238 /*resource_name=*/"A", &content));
239 EXPECT_EQ("localize", content);
240 }
241
TEST_F(ResourcesTest,PreferGenericCallback)242 TEST_F(ResourcesTest, PreferGenericCallback) {
243 std::string test_resources = BuildTestResources();
244 Resources resources(
245 flatbuffers::GetRoot<ResourcePool>(test_resources.data()));
246 std::string content;
247 EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("pt-BR")},
248 /*resource_name=*/"A", &content));
249 EXPECT_EQ("concentrar", content); // Falls back to pt, not pt-PT.
250 EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("zh-Hant")},
251 /*resource_name=*/"A", &content));
252 EXPECT_EQ("龍", content); // Falls back to zh, not zh-Hans-CN.
253 EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("zh-Hant-CN")},
254 /*resource_name=*/"A", &content));
255 EXPECT_EQ("龍", content); // Falls back to zh, not zh-Hans-CN.
256 EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("zh-CN")},
257 /*resource_name=*/"A", &content));
258 EXPECT_EQ("龍", content); // Falls back to zh, not zh-Hans-CN.
259 }
260
TEST_F(ResourcesTest,PreferGenericWhenGeneric)261 TEST_F(ResourcesTest, PreferGenericWhenGeneric) {
262 std::string test_resources = BuildTestResources();
263 Resources resources(
264 flatbuffers::GetRoot<ResourcePool>(test_resources.data()));
265 std::string content;
266 EXPECT_TRUE(resources.GetResourceContent({Locale::FromBCP47("pt")},
267 /*resource_name=*/"A", &content));
268
269 // Uses pt, not pt-PT.
270 EXPECT_EQ("concentrar", content);
271 }
272
273 } // namespace
274 } // namespace libtextclassifier3
275