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