1 /*
2 * Copyright (C) 2016 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 "compile/PseudolocaleGenerator.h"
18
19 #include "test/Test.h"
20 #include "util/Util.h"
21
22 using ::android::ConfigDescription;
23
24 namespace aapt {
25
TEST(PseudolocaleGeneratorTest,PseudolocalizeStyledString)26 TEST(PseudolocaleGeneratorTest, PseudolocalizeStyledString) {
27 StringPool pool;
28 StyleString original_style;
29 original_style.str = "Hello world!";
30 original_style.spans = {Span{"i", 1, 10}, Span{"b", 2, 3}, Span{"b", 6, 7}};
31
32 std::unique_ptr<StyledString> new_string = PseudolocalizeStyledString(
33 util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
34 Pseudolocalizer::Method::kNone, &pool);
35
36 EXPECT_EQ(original_style.str, new_string->value->value);
37 ASSERT_EQ(original_style.spans.size(), new_string->value->spans.size());
38
39 EXPECT_EQ(std::string("i"), *new_string->value->spans[0].name);
40 EXPECT_EQ(std::u16string(u"H").size(), new_string->value->spans[0].first_char);
41 EXPECT_EQ(std::u16string(u"Hello worl").size(), new_string->value->spans[0].last_char);
42
43 EXPECT_EQ(std::string("b"), *new_string->value->spans[1].name);
44 EXPECT_EQ(std::u16string(u"He").size(), new_string->value->spans[1].first_char);
45 EXPECT_EQ(std::u16string(u"Hel").size(), new_string->value->spans[1].last_char);
46
47 EXPECT_EQ(std::string("b"), *new_string->value->spans[2].name);
48 EXPECT_EQ(std::u16string(u"Hello ").size(), new_string->value->spans[2].first_char);
49 EXPECT_EQ(std::u16string(u"Hello w").size(), new_string->value->spans[2].last_char);
50
51 original_style.spans.insert(original_style.spans.begin(), Span{"em", 0, 11u});
52
53 new_string = PseudolocalizeStyledString(
54 util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
55 Pseudolocalizer::Method::kAccent, &pool);
56
57 EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð¡ one two]"), new_string->value->value);
58 ASSERT_EQ(original_style.spans.size(), new_string->value->spans.size());
59
60 EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char);
61 EXPECT_EQ(std::u16string(u"[Ĥéļļö ŵöŕļð").size(), new_string->value->spans[0].last_char);
62
63 EXPECT_EQ(std::u16string(u"[Ĥ").size(), new_string->value->spans[1].first_char);
64 EXPECT_EQ(std::u16string(u"[Ĥéļļö ŵöŕļ").size(), new_string->value->spans[1].last_char);
65
66 EXPECT_EQ(std::u16string(u"[Ĥé").size(), new_string->value->spans[2].first_char);
67 EXPECT_EQ(std::u16string(u"[Ĥéļ").size(), new_string->value->spans[2].last_char);
68
69 EXPECT_EQ(std::u16string(u"[Ĥéļļö ").size(), new_string->value->spans[3].first_char);
70 EXPECT_EQ(std::u16string(u"[Ĥéļļö ŵ").size(), new_string->value->spans[3].last_char);
71 }
72
TEST(PseudolocaleGeneratorTest,PseudolocalizeAdjacentNestedTags)73 TEST(PseudolocaleGeneratorTest, PseudolocalizeAdjacentNestedTags) {
74 StringPool pool;
75 StyleString original_style;
76 original_style.str = "bold";
77 original_style.spans = {Span{"b", 0, 3}, Span{"i", 0, 3}};
78
79 std::unique_ptr<StyledString> new_string = PseudolocalizeStyledString(
80 util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
81 Pseudolocalizer::Method::kAccent, &pool);
82 ASSERT_NE(nullptr, new_string);
83 ASSERT_EQ(2u, new_string->value->spans.size());
84 EXPECT_EQ(std::string("[ɓöļð one]"), new_string->value->value);
85
86 EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name);
87 EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char);
88 EXPECT_EQ(std::u16string(u"[ɓöļ").size(), new_string->value->spans[0].last_char);
89
90 EXPECT_EQ(std::string("i"), *new_string->value->spans[1].name);
91 EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[1].first_char);
92 EXPECT_EQ(std::u16string(u"[ɓöļ").size(), new_string->value->spans[1].last_char);
93 }
94
TEST(PseudolocaleGeneratorTest,PseudolocalizeAdjacentTagsUnsorted)95 TEST(PseudolocaleGeneratorTest, PseudolocalizeAdjacentTagsUnsorted) {
96 StringPool pool;
97 StyleString original_style;
98 original_style.str = "bold";
99 original_style.spans = {Span{"i", 2, 3}, Span{"b", 0, 1}};
100
101 std::unique_ptr<StyledString> new_string = PseudolocalizeStyledString(
102 util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
103 Pseudolocalizer::Method::kAccent, &pool);
104 ASSERT_NE(nullptr, new_string);
105 ASSERT_EQ(2u, new_string->value->spans.size());
106 EXPECT_EQ(std::string("[ɓöļð one]"), new_string->value->value);
107
108 EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name);
109 EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char);
110 EXPECT_EQ(std::u16string(u"[ɓ").size(), new_string->value->spans[0].last_char);
111
112 EXPECT_EQ(std::string("i"), *new_string->value->spans[1].name);
113 EXPECT_EQ(std::u16string(u"[ɓö").size(), new_string->value->spans[1].first_char);
114 EXPECT_EQ(std::u16string(u"[ɓöļ").size(), new_string->value->spans[1].last_char);
115 }
116
TEST(PseudolocaleGeneratorTest,PseudolocalizeNestedAndAdjacentTags)117 TEST(PseudolocaleGeneratorTest, PseudolocalizeNestedAndAdjacentTags) {
118 StringPool pool;
119 StyleString original_style;
120 original_style.str = "This sentence is not what you think it is at all.";
121 original_style.spans = {Span{"b", 16u, 19u}, Span{"em", 29u, 47u}, Span{"i", 38u, 40u},
122 Span{"b", 44u, 47u}};
123
124 std::unique_ptr<StyledString> new_string = PseudolocalizeStyledString(
125 util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
126 Pseudolocalizer::Method::kAccent, &pool);
127 ASSERT_NE(nullptr, new_string);
128 ASSERT_EQ(4u, new_string->value->spans.size());
129 EXPECT_EQ(std::string(
130 "[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ îš åţ åļļ. one two three four five six]"),
131 new_string->value->value);
132
133 EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name);
134 EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš").size(), new_string->value->spans[0].first_char);
135 EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñö").size(), new_string->value->spans[0].last_char);
136
137 EXPECT_EQ(std::string("em"), *new_string->value->spans[1].name);
138 EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû").size(),
139 new_string->value->spans[1].first_char);
140 EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ îš åţ åļ").size(),
141 new_string->value->spans[1].last_char);
142
143 EXPECT_EQ(std::string("i"), *new_string->value->spans[2].name);
144 EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ").size(),
145 new_string->value->spans[2].first_char);
146 EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ î").size(),
147 new_string->value->spans[2].last_char);
148
149 EXPECT_EQ(std::string("b"), *new_string->value->spans[3].name);
150 EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ îš åţ").size(),
151 new_string->value->spans[3].first_char);
152 EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ îš åţ åļ").size(),
153 new_string->value->spans[3].last_char);
154 }
155
TEST(PseudolocaleGeneratorTest,PseudolocalizePartsOfString)156 TEST(PseudolocaleGeneratorTest, PseudolocalizePartsOfString) {
157 StringPool pool;
158 StyleString original_style;
159 original_style.str = "This should NOT be pseudolocalized.";
160 original_style.spans = {Span{"em", 4u, 14u}, Span{"i", 18u, 33u}};
161 std::unique_ptr<StyledString> original_string =
162 util::make_unique<StyledString>(pool.MakeRef(original_style));
163 original_string->untranslatable_sections = {UntranslatableSection{11u, 15u}};
164
165 std::unique_ptr<StyledString> new_string =
166 PseudolocalizeStyledString(original_string.get(), Pseudolocalizer::Method::kAccent, &pool);
167 ASSERT_NE(nullptr, new_string);
168 ASSERT_EQ(2u, new_string->value->spans.size());
169 EXPECT_EQ(std::string("[Ţĥîš šĥöûļð NOT ɓé þšéûðöļöçåļîžéð. one two three four]"),
170 new_string->value->value);
171
172 EXPECT_EQ(std::string("em"), *new_string->value->spans[0].name);
173 EXPECT_EQ(std::u16string(u"[Ţĥîš").size(), new_string->value->spans[0].first_char);
174 EXPECT_EQ(std::u16string(u"[Ţĥîš šĥöûļð NO").size(), new_string->value->spans[0].last_char);
175
176 EXPECT_EQ(std::string("i"), *new_string->value->spans[1].name);
177 EXPECT_EQ(std::u16string(u"[Ţĥîš šĥöûļð NOT ɓé").size(), new_string->value->spans[1].first_char);
178 EXPECT_EQ(std::u16string(u"[Ţĥîš šĥöûļð NOT ɓé þšéûðöļöçåļîžé").size(),
179 new_string->value->spans[1].last_char);
180 }
181
TEST(PseudolocaleGeneratorTest,PseudolocalizeOnlyDefaultConfigs)182 TEST(PseudolocaleGeneratorTest, PseudolocalizeOnlyDefaultConfigs) {
183 std::unique_ptr<ResourceTable> table =
184 test::ResourceTableBuilder()
185 .AddString("android:string/one", "one")
186 .AddString("android:string/two", ResourceId{},
187 test::ParseConfigOrDie("en"), "two")
188 .AddString("android:string/three", "three")
189 .AddString("android:string/three", ResourceId{},
190 test::ParseConfigOrDie("en-rXA"), "three")
191 .AddString("android:string/four", "four")
192 .Build();
193
194 String* val = test::GetValue<String>(table.get(), "android:string/four");
195 ASSERT_NE(nullptr, val);
196 val->SetTranslatable(false);
197
198 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
199 PseudolocaleGenerator generator;
200 ASSERT_TRUE(generator.Consume(context.get(), table.get()));
201
202 // Normal pseudolocalization should take place.
203 ASSERT_NE(nullptr,
204 test::GetValueForConfig<String>(table.get(), "android:string/one",
205 test::ParseConfigOrDie("en-rXA")));
206 ASSERT_NE(nullptr,
207 test::GetValueForConfig<String>(table.get(), "android:string/one",
208 test::ParseConfigOrDie("ar-rXB")));
209
210 // No default config for android:string/two, so no pseudlocales should exist.
211 ASSERT_EQ(nullptr,
212 test::GetValueForConfig<String>(table.get(), "android:string/two",
213 test::ParseConfigOrDie("en-rXA")));
214 ASSERT_EQ(nullptr,
215 test::GetValueForConfig<String>(table.get(), "android:string/two",
216 test::ParseConfigOrDie("ar-rXB")));
217
218 // Check that we didn't override manual pseudolocalization.
219 val = test::GetValueForConfig<String>(table.get(), "android:string/three",
220 test::ParseConfigOrDie("en-rXA"));
221 ASSERT_NE(nullptr, val);
222 EXPECT_EQ(std::string("three"), *val->value);
223
224 ASSERT_NE(nullptr,
225 test::GetValueForConfig<String>(table.get(), "android:string/three",
226 test::ParseConfigOrDie("ar-rXB")));
227
228 // Check that four's translateable marker was honored.
229 ASSERT_EQ(nullptr,
230 test::GetValueForConfig<String>(table.get(), "android:string/four",
231 test::ParseConfigOrDie("en-rXA")));
232 ASSERT_EQ(nullptr,
233 test::GetValueForConfig<String>(table.get(), "android:string/four",
234 test::ParseConfigOrDie("ar-rXB")));
235 }
236
TEST(PseudolocaleGeneratorTest,PluralsArePseudolocalized)237 TEST(PseudolocaleGeneratorTest, PluralsArePseudolocalized) {
238 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
239 std::unique_ptr<ResourceTable> table =
240 test::ResourceTableBuilder().SetPackageId("com.pkg", 0x7F).Build();
241 std::unique_ptr<Plural> plural = util::make_unique<Plural>();
242 plural->values = {util::make_unique<String>(table->string_pool.MakeRef("zero")),
243 util::make_unique<String>(table->string_pool.MakeRef("one"))};
244 ASSERT_TRUE(table->AddResource(test::ParseNameOrDie("com.pkg:plurals/foo"), ConfigDescription{},
245 {}, std::move(plural), context->GetDiagnostics()));
246 std::unique_ptr<Plural> expected = util::make_unique<Plural>();
247 expected->values = {util::make_unique<String>(table->string_pool.MakeRef("[žéŕö one]")),
248 util::make_unique<String>(table->string_pool.MakeRef("[öñé one]"))};
249
250 PseudolocaleGenerator generator;
251 ASSERT_TRUE(generator.Consume(context.get(), table.get()));
252
253 const auto* actual = test::GetValueForConfig<Plural>(table.get(), "com.pkg:plurals/foo",
254 test::ParseConfigOrDie("en-rXA"));
255 EXPECT_TRUE(actual->Equals(expected.get()));
256 }
257
TEST(PseudolocaleGeneratorTest,RespectUntranslateableSections)258 TEST(PseudolocaleGeneratorTest, RespectUntranslateableSections) {
259 std::unique_ptr<IAaptContext> context =
260 test::ContextBuilder().SetCompilationPackage("android").Build();
261 std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
262
263 {
264 StyleString original_style;
265 original_style.str = "Hello world!";
266 original_style.spans = {Span{"i", 1, 10}, Span{"b", 2, 3}, Span{"b", 6, 7}};
267
268 auto styled_string =
269 util::make_unique<StyledString>(table->string_pool.MakeRef(original_style));
270 styled_string->untranslatable_sections.push_back(UntranslatableSection{6u, 8u});
271 styled_string->untranslatable_sections.push_back(UntranslatableSection{8u, 11u});
272
273 auto string = util::make_unique<String>(table->string_pool.MakeRef(original_style.str));
274 string->untranslatable_sections.push_back(UntranslatableSection{6u, 11u});
275
276 ASSERT_TRUE(table->AddResource(test::ParseNameOrDie("android:string/foo"), ConfigDescription{},
277 {} /* product */, std::move(styled_string),
278 context->GetDiagnostics()));
279 ASSERT_TRUE(table->AddResource(test::ParseNameOrDie("android:string/bar"), ConfigDescription{},
280 {} /* product */, std::move(string), context->GetDiagnostics()));
281 }
282
283 PseudolocaleGenerator generator;
284 ASSERT_TRUE(generator.Consume(context.get(), table.get()));
285
286 StyledString* new_styled_string = test::GetValueForConfig<StyledString>(
287 table.get(), "android:string/foo", test::ParseConfigOrDie("en-rXA"));
288 ASSERT_NE(nullptr, new_styled_string);
289
290 // "world" should be untranslated.
291 EXPECT_NE(std::string::npos, new_styled_string->value->value.find("world"));
292
293 String* new_string = test::GetValueForConfig<String>(table.get(), "android:string/bar",
294 test::ParseConfigOrDie("en-rXA"));
295 ASSERT_NE(nullptr, new_string);
296
297 // "world" should be untranslated.
298 EXPECT_NE(std::string::npos, new_string->value->find("world"));
299 }
300
301 } // namespace aapt
302