blob: 3118eb8f7731c4dc06b2f4a266e0dd4d0f73e66c [file] [log] [blame]
Ryan Mitchell479fa392019-01-02 17:15:39 -08001/*
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 "Link.h"
18
Ryan Mitchell5855de72021-02-24 14:39:13 -080019#include <android-base/file.h>
20
21#include "AppInfo.h"
Ryan Mitchell479fa392019-01-02 17:15:39 -080022#include "LoadedApk.h"
23#include "test/Test.h"
24
25using testing::Eq;
Ryan Mitchell5855de72021-02-24 14:39:13 -080026using testing::HasSubstr;
Ryan Mitchell326e35ff2021-04-12 07:50:42 -070027using testing::IsNull;
Ryan Mitchell479fa392019-01-02 17:15:39 -080028using testing::Ne;
Ryan Mitchell2e9bec12021-03-22 09:31:00 -070029using testing::NotNull;
Ryan Mitchell479fa392019-01-02 17:15:39 -080030
31namespace aapt {
32
33using LinkTest = CommandTestFixture;
34
35TEST_F(LinkTest, RemoveRawXmlStrings) {
36 StdErrDiagnostics diag;
37 const std::string compiled_files_dir = GetTestPath("compiled");
38 ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)",
39 compiled_files_dir, &diag));
40
41 const std::string out_apk = GetTestPath("out.apk");
42 std::vector<std::string> link_args = {
43 "--manifest", GetDefaultManifest(),
44 "-o", out_apk,
45 };
46
47 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
48
49 // Load the binary xml tree
50 android::ResXMLTree tree;
51 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
Ryan Mitchell1d008d12021-03-19 14:54:17 -070052 ASSERT_THAT(apk, Ne(nullptr));
53
Winsonb7be7932019-01-23 11:10:52 -080054 std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
55 ASSERT_THAT(data, Ne(nullptr));
Winsonb7be7932019-01-23 11:10:52 -080056 AssertLoadXml(apk.get(), data.get(), &tree);
Ryan Mitchell479fa392019-01-02 17:15:39 -080057
58 // Check that the raw string index has not been assigned
59 EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1));
60}
61
62TEST_F(LinkTest, KeepRawXmlStrings) {
63 StdErrDiagnostics diag;
64 const std::string compiled_files_dir = GetTestPath("compiled");
65 ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)",
66 compiled_files_dir, &diag));
67
68 const std::string out_apk = GetTestPath("out.apk");
69 std::vector<std::string> link_args = {
70 "--manifest", GetDefaultManifest(),
71 "-o", out_apk,
72 "--keep-raw-values"
73 };
74
75 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
76
77 // Load the binary xml tree
78 android::ResXMLTree tree;
79 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
Ryan Mitchell1d008d12021-03-19 14:54:17 -070080 ASSERT_THAT(apk, Ne(nullptr));
81
Winsonb7be7932019-01-23 11:10:52 -080082 std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
83 ASSERT_THAT(data, Ne(nullptr));
Winsonb7be7932019-01-23 11:10:52 -080084 AssertLoadXml(apk.get(), data.get(), &tree);
Ryan Mitchell479fa392019-01-02 17:15:39 -080085
86 // Check that the raw string index has been set to the correct string pool entry
87 int32_t raw_index = tree.getAttributeValueStringID(0);
88 ASSERT_THAT(raw_index, Ne(-1));
89 EXPECT_THAT(util::GetString(tree.getStrings(), static_cast<size_t>(raw_index)), Eq("007"));
90}
91
Ryan Mitchell81dfca02019-06-07 10:20:27 -070092TEST_F(LinkTest, NoCompressAssets) {
93 StdErrDiagnostics diag;
94 std::string content(500, 'a');
95 WriteFile(GetTestPath("assets/testtxt"), content);
96 WriteFile(GetTestPath("assets/testtxt2"), content);
97 WriteFile(GetTestPath("assets/test.txt"), content);
98 WriteFile(GetTestPath("assets/test.hello.txt"), content);
99 WriteFile(GetTestPath("assets/test.hello.xml"), content);
100
101 const std::string out_apk = GetTestPath("out.apk");
102 std::vector<std::string> link_args = {
103 "--manifest", GetDefaultManifest(),
104 "-o", out_apk,
105 "-0", ".txt",
106 "-0", "txt2",
107 "-0", ".hello.txt",
108 "-0", "hello.xml",
109 "-A", GetTestPath("assets")
110 };
111
112 ASSERT_TRUE(Link(link_args, &diag));
113
114 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
115 ASSERT_THAT(apk, Ne(nullptr));
116 io::IFileCollection* zip = apk->GetFileCollection();
117 ASSERT_THAT(zip, Ne(nullptr));
118
119 auto file = zip->FindFile("assets/testtxt");
120 ASSERT_THAT(file, Ne(nullptr));
121 EXPECT_TRUE(file->WasCompressed());
122
123 file = zip->FindFile("assets/testtxt2");
124 ASSERT_THAT(file, Ne(nullptr));
125 EXPECT_FALSE(file->WasCompressed());
126
127 file = zip->FindFile("assets/test.txt");
128 ASSERT_THAT(file, Ne(nullptr));
129 EXPECT_FALSE(file->WasCompressed());
130
131 file = zip->FindFile("assets/test.hello.txt");
132 ASSERT_THAT(file, Ne(nullptr));
133 EXPECT_FALSE(file->WasCompressed());
134
135 file = zip->FindFile("assets/test.hello.xml");
136 ASSERT_THAT(file, Ne(nullptr));
137 EXPECT_FALSE(file->WasCompressed());
138}
139
140TEST_F(LinkTest, NoCompressResources) {
141 StdErrDiagnostics diag;
142 std::string content(500, 'a');
143 const std::string compiled_files_dir = GetTestPath("compiled");
144 ASSERT_TRUE(CompileFile(GetTestPath("res/raw/testtxt"), content, compiled_files_dir, &diag));
145 ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test.txt"), content, compiled_files_dir, &diag));
146 ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test1.hello.txt"), content, compiled_files_dir,
147 &diag));
148 ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test2.goodbye.xml"), content, compiled_files_dir,
149 &diag));
150
151 const std::string out_apk = GetTestPath("out.apk");
152 std::vector<std::string> link_args = {
153 "--manifest", GetDefaultManifest(),
154 "-o", out_apk,
155 "-0", ".txt",
156 "-0", ".hello.txt",
157 "-0", "goodbye.xml",
158 };
159
160 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
161
162 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
163 ASSERT_THAT(apk, Ne(nullptr));
164 io::IFileCollection* zip = apk->GetFileCollection();
165 ASSERT_THAT(zip, Ne(nullptr));
166
167 auto file = zip->FindFile("res/raw/testtxt");
168 ASSERT_THAT(file, Ne(nullptr));
169 EXPECT_TRUE(file->WasCompressed());
170
171 file = zip->FindFile("res/raw/test.txt");
172 ASSERT_THAT(file, Ne(nullptr));
173 EXPECT_FALSE(file->WasCompressed());
174
175 file = zip->FindFile("res/raw/test1.hello.hello.txt");
176 ASSERT_THAT(file, Ne(nullptr));
177 EXPECT_FALSE(file->WasCompressed());
178
179 file = zip->FindFile("res/raw/test2.goodbye.goodbye.xml");
180 ASSERT_THAT(file, Ne(nullptr));
181 EXPECT_FALSE(file->WasCompressed());
182}
183
Donald Chai121c6e82019-06-12 12:51:57 -0700184TEST_F(LinkTest, OverlayStyles) {
185 StdErrDiagnostics diag;
186 const std::string compiled_files_dir = GetTestPath("compiled");
187 const std::string override_files_dir = GetTestPath("compiled-override");
188 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
189 R"(<resources>
190 <style name="MyStyle">
191 <item name="android:textColor">#123</item>
192 </style>
193 </resources>)",
194 compiled_files_dir, &diag));
195 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values-override.xml"),
196 R"(<resources>
197 <style name="MyStyle">
198 <item name="android:background">#456</item>
199 </style>
200 </resources>)",
201 override_files_dir, &diag));
202
203
204 const std::string out_apk = GetTestPath("out.apk");
205 std::vector<std::string> link_args = {
206 "--manifest", GetDefaultManifest(kDefaultPackageName),
207 "-o", out_apk,
208 };
209 const auto override_files = file::FindFiles(override_files_dir, &diag);
210 for (const auto &override_file : override_files.value()) {
211 link_args.push_back("-R");
212 link_args.push_back(file::BuildPath({override_files_dir, override_file}));
213 }
214 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
215
216 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
Ryan Mitchell1d008d12021-03-19 14:54:17 -0700217 ASSERT_THAT(apk, Ne(nullptr));
218
Donald Chai121c6e82019-06-12 12:51:57 -0700219 const Style* actual_style = test::GetValue<Style>(
220 apk->GetResourceTable(), std::string(kDefaultPackageName) + ":style/MyStyle");
221 ASSERT_NE(actual_style, nullptr);
222 ASSERT_EQ(actual_style->entries.size(), 2);
223 EXPECT_EQ(actual_style->entries[0].key.id, 0x01010098); // android:textColor
224 EXPECT_EQ(actual_style->entries[1].key.id, 0x010100d4); // android:background
225}
226
227TEST_F(LinkTest, OverrideStylesInsteadOfOverlaying) {
228 StdErrDiagnostics diag;
229 const std::string compiled_files_dir = GetTestPath("compiled");
230 const std::string override_files_dir = GetTestPath("compiled-override");
231 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
232 R"(<resources>
233 <style name="MyStyle">
234 <item name="android:textColor">#123</item>
235 </style>
236 </resources>)",
237 compiled_files_dir, &diag));
238 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values-override.xml"),
239 R"(<resources>
240 <style name="MyStyle">
241 <item name="android:background">#456</item>
242 </style>
243 </resources>)",
244 override_files_dir, &diag));
245
246
247 const std::string out_apk = GetTestPath("out.apk");
248 std::vector<std::string> link_args = {
249 "--manifest", GetDefaultManifest(kDefaultPackageName),
250 "--override-styles-instead-of-overlaying",
251 "-o", out_apk,
252 };
253 const auto override_files = file::FindFiles(override_files_dir, &diag);
254 for (const auto &override_file : override_files.value()) {
255 link_args.push_back("-R");
256 link_args.push_back(file::BuildPath({override_files_dir, override_file}));
257 }
258 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
259
260 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
Ryan Mitchell1d008d12021-03-19 14:54:17 -0700261 ASSERT_THAT(apk, Ne(nullptr));
262
Donald Chai121c6e82019-06-12 12:51:57 -0700263 const Style* actual_style = test::GetValue<Style>(
264 apk->GetResourceTable(), std::string(kDefaultPackageName) + ":style/MyStyle");
265 ASSERT_NE(actual_style, nullptr);
266 ASSERT_EQ(actual_style->entries.size(), 1);
267 EXPECT_EQ(actual_style->entries[0].key.id, 0x010100d4); // android:background
268}
269
Udam Sainib228df32019-06-18 16:50:34 -0700270TEST_F(LinkTest, AppInfoWithUsesSplit) {
271 StdErrDiagnostics diag;
272 const std::string base_files_dir = GetTestPath("base");
273 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
274 R"(<resources>
275 <string name="bar">bar</string>
276 </resources>)",
277 base_files_dir, &diag));
278 const std::string base_apk = GetTestPath("base.apk");
279 std::vector<std::string> link_args = {
280 "--manifest", GetDefaultManifest("com.aapt2.app"),
281 "-o", base_apk,
282 };
283 ASSERT_TRUE(Link(link_args, base_files_dir, &diag));
284
285 const std::string feature_manifest = GetTestPath("feature_manifest.xml");
286 WriteFile(feature_manifest, android::base::StringPrintf(R"(
287 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
288 package="com.aapt2.app" split="feature1">
289 </manifest>)"));
290 const std::string feature_files_dir = GetTestPath("feature");
291 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
292 R"(<resources>
293 <string name="foo">foo</string>
294 </resources>)",
295 feature_files_dir, &diag));
296 const std::string feature_apk = GetTestPath("feature.apk");
297 const std::string feature_package_id = "0x80";
298 link_args = {
299 "--manifest", feature_manifest,
300 "-I", base_apk,
301 "--package-id", feature_package_id,
302 "-o", feature_apk,
303 };
304 ASSERT_TRUE(Link(link_args, feature_files_dir, &diag));
305
306 const std::string feature2_manifest = GetTestPath("feature2_manifest.xml");
307 WriteFile(feature2_manifest, android::base::StringPrintf(R"(
308 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
309 package="com.aapt2.app" split="feature2">
310 <uses-split android:name="feature1"/>
311 </manifest>)"));
312 const std::string feature2_files_dir = GetTestPath("feature2");
313 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
314 R"(<resources>
315 <string-array name="string_array">
316 <item>@string/bar</item>
317 <item>@string/foo</item>
318 </string-array>
319 </resources>)",
320 feature2_files_dir, &diag));
321 const std::string feature2_apk = GetTestPath("feature2.apk");
322 const std::string feature2_package_id = "0x81";
323 link_args = {
324 "--manifest", feature2_manifest,
325 "-I", base_apk,
326 "-I", feature_apk,
327 "--package-id", feature2_package_id,
328 "-o", feature2_apk,
329 };
330 ASSERT_TRUE(Link(link_args, feature2_files_dir, &diag));
331}
332
Ryan Mitchell5855de72021-02-24 14:39:13 -0800333TEST_F(LinkTest, SharedLibraryAttributeRJava) {
334 StdErrDiagnostics diag;
335 const std::string lib_values =
336 R"(<resources>
337 <attr name="foo"/>
338 <public type="attr" name="foo" id="0x00010001"/>
339 <declare-styleable name="LibraryStyleable">
340 <attr name="foo" />
341 </declare-styleable>
342 </resources>)";
343
344 const std::string client_values =
345 R"(<resources>
346 <attr name="bar" />
347 <declare-styleable name="ClientStyleable">
348 <attr name="com.example.lib:foo" />
349 <attr name="bar" />
350 </declare-styleable>
351 </resources>)";
352
353 // Build a library with a public attribute
354 const std::string lib_res = GetTestPath("library-res");
355 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), lib_values, lib_res, &diag));
356
357 const std::string lib_apk = GetTestPath("library.apk");
358 const std::string lib_java = GetTestPath("library_java");
359 // clang-format off
360 auto lib_manifest = ManifestBuilder(this)
361 .SetPackageName("com.example.lib")
362 .Build();
363
364 auto lib_link_args = LinkCommandBuilder(this)
365 .SetManifestFile(lib_manifest)
366 .AddFlag("--shared-lib")
367 .AddParameter("--java", lib_java)
368 .AddCompiledResDir(lib_res, &diag)
369 .Build(lib_apk);
370 // clang-format on
371 ASSERT_TRUE(Link(lib_link_args, &diag));
372
373 const std::string lib_r_java = lib_java + "/com/example/lib/R.java";
374 std::string lib_r_contents;
375 ASSERT_TRUE(android::base::ReadFileToString(lib_r_java, &lib_r_contents));
376 EXPECT_THAT(lib_r_contents, HasSubstr(" public static int foo=0x00010001;"));
377 EXPECT_THAT(lib_r_contents, HasSubstr(" com.example.lib.R.attr.foo"));
378
379 // Build a client that uses the library attribute in a declare-styleable
380 const std::string client_res = GetTestPath("client-res");
381 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), client_values, client_res, &diag));
382
383 const std::string client_apk = GetTestPath("client.apk");
384 const std::string client_java = GetTestPath("client_java");
385 // clang-format off
386 auto client_manifest = ManifestBuilder(this)
387 .SetPackageName("com.example.client")
388 .Build();
389
390 auto client_link_args = LinkCommandBuilder(this)
391 .SetManifestFile(client_manifest)
392 .AddParameter("--java", client_java)
393 .AddParameter("-I", lib_apk)
394 .AddCompiledResDir(client_res, &diag)
395 .Build(client_apk);
396 // clang-format on
397 ASSERT_TRUE(Link(client_link_args, &diag));
398
399 const std::string client_r_java = client_java + "/com/example/client/R.java";
400 std::string client_r_contents;
401 ASSERT_TRUE(android::base::ReadFileToString(client_r_java, &client_r_contents));
402 EXPECT_THAT(client_r_contents, HasSubstr(" com.example.lib.R.attr.foo, 0x7f010000"));
403}
404
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700405TEST_F(LinkTest, StagedAndroidApi) {
406 StdErrDiagnostics diag;
407 const std::string android_values =
408 R"(<resources>
409 <public type="attr" name="finalized_res" id="0x01010001"/>
410
411 <!-- S staged attributes (support staged resources in the same type id) -->
412 <staging-public-group type="attr" first-id="0x01010050">
413 <public name="staged_s_res" />
414 </staging-public-group>
415
416 <!-- SV2 staged attributes (support staged resources in a separate type id) -->
417 <staging-public-group type="attr" first-id="0x01ff0049">
418 <public name="staged_s2_res" />
419 </staging-public-group>
420
421 <!-- T staged attributes (support staged resources in multiple separate type ids) -->
422 <staging-public-group type="attr" first-id="0x01fe0063">
423 <public name="staged_t_res" />
424 </staging-public-group>
425
426 <staging-public-group type="string" first-id="0x01fd0072">
427 <public name="staged_t_string" />
428 </staging-public-group>
429
430 <attr name="finalized_res" />
431 <attr name="staged_s_res" />
432 <attr name="staged_s2_res" />
433 <attr name="staged_t_res" />
434 <string name="staged_t_string">Hello</string>
435 </resources>)";
436
437 const std::string app_values =
438 R"(<resources xmlns:android="http://schemas.android.com/apk/res/android">
439 <attr name="bar" />
440 <declare-styleable name="ClientStyleable">
441 <attr name="android:finalized_res" />
442 <attr name="android:staged_s_res" />
443 <attr name="bar" />
444 </declare-styleable>
445 </resources>)";
446
447 const std::string android_res = GetTestPath("android-res");
448 ASSERT_TRUE(
449 CompileFile(GetTestPath("res/values/values.xml"), android_values, android_res, &diag));
450
451 const std::string android_apk = GetTestPath("android.apk");
452 const std::string android_java = GetTestPath("android_java");
453 // clang-format off
454 auto android_manifest = ManifestBuilder(this)
455 .SetPackageName("android")
456 .Build();
457
458 auto android_link_args = LinkCommandBuilder(this)
459 .SetManifestFile(android_manifest)
460 .AddParameter("--private-symbols", "com.android.internal")
461 .AddParameter("--java", android_java)
462 .AddCompiledResDir(android_res, &diag)
463 .Build(android_apk);
464 // clang-format on
465 ASSERT_TRUE(Link(android_link_args, &diag));
466
467 const std::string android_r_java = android_java + "/android/R.java";
468 std::string android_r_contents;
469 ASSERT_TRUE(android::base::ReadFileToString(android_r_java, &android_r_contents));
Ryan Mitchellca3b4f72021-03-29 14:47:02 -0700470 EXPECT_THAT(android_r_contents, HasSubstr("public static final int finalized_res=0x01010001;"));
471 EXPECT_THAT(
472 android_r_contents,
473 HasSubstr("public static final int staged_s_res; static { staged_s_res=0x01010050; }"));
474 EXPECT_THAT(
475 android_r_contents,
476 HasSubstr("public static final int staged_s2_res; static { staged_s2_res=0x01ff0049; }"));
477 EXPECT_THAT(
478 android_r_contents,
479 HasSubstr("public static final int staged_t_res; static { staged_t_res=0x01fe0063; }"));
480 EXPECT_THAT(
481 android_r_contents,
482 HasSubstr("public static final int staged_t_string; static { staged_t_string=0x01fd0072; }"));
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700483
484 // Build an app that uses the framework attribute in a declare-styleable
485 const std::string client_res = GetTestPath("app-res");
486 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), app_values, client_res, &diag));
487
488 const std::string app_apk = GetTestPath("app.apk");
489 const std::string app_java = GetTestPath("app_java");
490 // clang-format off
491 auto app_manifest = ManifestBuilder(this)
492 .SetPackageName("com.example.app")
493 .Build();
494
495 auto app_link_args = LinkCommandBuilder(this)
496 .SetManifestFile(app_manifest)
497 .AddParameter("--java", app_java)
498 .AddParameter("-I", android_apk)
499 .AddCompiledResDir(client_res, &diag)
500 .Build(app_apk);
501 // clang-format on
502 ASSERT_TRUE(Link(app_link_args, &diag));
503
504 const std::string client_r_java = app_java + "/com/example/app/R.java";
505 std::string client_r_contents;
506 ASSERT_TRUE(android::base::ReadFileToString(client_r_java, &client_r_contents));
507 EXPECT_THAT(client_r_contents, HasSubstr(" 0x01010001, android.R.attr.staged_s_res, 0x7f010000"));
508
509 // Test that the resource ids of staged and non-staged resource can be retrieved
510 android::AssetManager2 am;
511 auto android_asset = android::ApkAssets::Load(android_apk);
512 ASSERT_THAT(android_asset, NotNull());
513 ASSERT_TRUE(am.SetApkAssets({android_asset.get()}));
514
515 auto result = am.GetResourceId("android:attr/finalized_res");
516 ASSERT_TRUE(result.has_value());
517 EXPECT_THAT(*result, Eq(0x01010001));
518
519 result = am.GetResourceId("android:attr/staged_s_res");
520 ASSERT_TRUE(result.has_value());
521 EXPECT_THAT(*result, Eq(0x01010050));
522
523 result = am.GetResourceId("android:attr/staged_s2_res");
524 ASSERT_TRUE(result.has_value());
525 EXPECT_THAT(*result, Eq(0x01ff0049));
526
527 result = am.GetResourceId("android:attr/staged_t_res");
528 ASSERT_TRUE(result.has_value());
529 EXPECT_THAT(*result, Eq(0x01fe0063));
530
531 result = am.GetResourceId("android:string/staged_t_string");
532 ASSERT_TRUE(result.has_value());
533 EXPECT_THAT(*result, Eq(0x01fd0072));
534}
535
Ryan Mitchell326e35ff2021-04-12 07:50:42 -0700536TEST_F(LinkTest, MacroSubstitution) {
537 StdErrDiagnostics diag;
538 const std::string values =
539 R"(<resources xmlns:an="http://schemas.android.com/apk/res/android">
540 <macro name="is_enabled">true</macro>
541 <macro name="deep_is_enabled">@macro/is_enabled</macro>
542 <macro name="attr_ref">?is_enabled_attr</macro>
543 <macro name="raw_string">Hello World!</macro>
544 <macro name="android_ref">@an:color/primary_text_dark</macro>
545
546 <attr name="is_enabled_attr" />
547 <public type="attr" name="is_enabled_attr" id="0x7f010000"/>
548
549 <string name="is_enabled_str">@macro/is_enabled</string>
550 <bool name="is_enabled_bool">@macro/deep_is_enabled</bool>
551
552 <array name="my_array">
553 <item>@macro/is_enabled</item>
554 </array>
555
556 <style name="MyStyle">
557 <item name="android:background">@macro/attr_ref</item>
558 <item name="android:fontFamily">@macro/raw_string</item>
559 </style>
560 </resources>)";
561
562 const std::string xml_values =
563 R"(<SomeLayout xmlns:android="http://schemas.android.com/apk/res/android"
564 android:background="@macro/android_ref"
565 android:fontFamily="@macro/raw_string">
566 </SomeLayout>)";
567
568 // Build a library with a public attribute
569 const std::string lib_res = GetTestPath("test-res");
570 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), values, lib_res, &diag));
571 ASSERT_TRUE(CompileFile(GetTestPath("res/layout/layout.xml"), xml_values, lib_res, &diag));
572
573 const std::string lib_apk = GetTestPath("test.apk");
574 // clang-format off
575 auto lib_link_args = LinkCommandBuilder(this)
576 .SetManifestFile(ManifestBuilder(this).SetPackageName("com.test").Build())
577 .AddCompiledResDir(lib_res, &diag)
578 .AddFlag("--no-auto-version")
579 .Build(lib_apk);
580 // clang-format on
581 ASSERT_TRUE(Link(lib_link_args, &diag));
582
583 auto apk = LoadedApk::LoadApkFromPath(lib_apk, &diag);
584 ASSERT_THAT(apk, NotNull());
585
586 // Test that the type flags determines the value type
587 auto actual_bool =
588 test::GetValue<BinaryPrimitive>(apk->GetResourceTable(), "com.test:bool/is_enabled_bool");
589 ASSERT_THAT(actual_bool, NotNull());
590 EXPECT_EQ(android::Res_value::TYPE_INT_BOOLEAN, actual_bool->value.dataType);
591 EXPECT_EQ(0xffffffffu, actual_bool->value.data);
592
593 auto actual_str =
594 test::GetValue<String>(apk->GetResourceTable(), "com.test:string/is_enabled_str");
595 ASSERT_THAT(actual_str, NotNull());
596 EXPECT_EQ(*actual_str->value, "true");
597
598 // Test nested data structures
599 auto actual_array = test::GetValue<Array>(apk->GetResourceTable(), "com.test:array/my_array");
600 ASSERT_THAT(actual_array, NotNull());
601 EXPECT_THAT(actual_array->elements.size(), Eq(1));
602
603 auto array_el_ref = ValueCast<BinaryPrimitive>(actual_array->elements[0].get());
604 ASSERT_THAT(array_el_ref, NotNull());
605 EXPECT_THAT(array_el_ref->value.dataType, Eq(android::Res_value::TYPE_INT_BOOLEAN));
606 EXPECT_THAT(array_el_ref->value.data, Eq(0xffffffffu));
607
608 auto actual_style = test::GetValue<Style>(apk->GetResourceTable(), "com.test:style/MyStyle");
609 ASSERT_THAT(actual_style, NotNull());
610 EXPECT_THAT(actual_style->entries.size(), Eq(2));
611
612 {
613 auto style_el = ValueCast<Reference>(actual_style->entries[0].value.get());
614 ASSERT_THAT(style_el, NotNull());
615 EXPECT_THAT(style_el->reference_type, Eq(Reference::Type::kAttribute));
616 EXPECT_THAT(style_el->id, Eq(0x7f010000));
617 }
618
619 {
620 auto style_el = ValueCast<String>(actual_style->entries[1].value.get());
621 ASSERT_THAT(style_el, NotNull());
622 EXPECT_THAT(*style_el->value, Eq("Hello World!"));
623 }
624
625 // Test substitution in compiled xml files
626 auto xml = apk->LoadXml("res/layout/layout.xml", &diag);
627 ASSERT_THAT(xml, NotNull());
628
629 auto& xml_attrs = xml->root->attributes;
630 ASSERT_THAT(xml_attrs.size(), Eq(2));
631
632 auto attr_value = ValueCast<Reference>(xml_attrs[0].compiled_value.get());
633 ASSERT_THAT(attr_value, NotNull());
634 EXPECT_THAT(attr_value->reference_type, Eq(Reference::Type::kResource));
635 EXPECT_THAT(attr_value->id, Eq(0x01060001));
636
637 EXPECT_THAT(xml_attrs[1].compiled_value.get(), IsNull());
638 EXPECT_THAT(xml_attrs[1].value, Eq("Hello World!"));
639}
640
Donald Chai121c6e82019-06-12 12:51:57 -0700641} // namespace aapt