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