| Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 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 "Link.h" | 
 | 18 |  | 
| Ryan Mitchell | 5855de7 | 2021-02-24 14:39:13 -0800 | [diff] [blame] | 19 | #include <android-base/file.h> | 
 | 20 |  | 
 | 21 | #include "AppInfo.h" | 
| Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 22 | #include "LoadedApk.h" | 
 | 23 | #include "test/Test.h" | 
 | 24 |  | 
| Josh Hou | d3bd452 | 2022-02-14 10:24:42 +0800 | [diff] [blame] | 25 | using android::ConfigDescription; | 
| Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 26 | using testing::Eq; | 
| Ryan Mitchell | 5855de7 | 2021-02-24 14:39:13 -0800 | [diff] [blame] | 27 | using testing::HasSubstr; | 
| Ryan Mitchell | 326e35ff | 2021-04-12 07:50:42 -0700 | [diff] [blame] | 28 | using testing::IsNull; | 
| Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 29 | using testing::Ne; | 
| Ryan Mitchell | 2e9bec1 | 2021-03-22 09:31:00 -0700 | [diff] [blame] | 30 | using testing::NotNull; | 
| Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 31 |  | 
 | 32 | namespace aapt { | 
 | 33 |  | 
 | 34 | using LinkTest = CommandTestFixture; | 
 | 35 |  | 
 | 36 | TEST_F(LinkTest, RemoveRawXmlStrings) { | 
 | 37 |   StdErrDiagnostics diag; | 
 | 38 |   const std::string compiled_files_dir = GetTestPath("compiled"); | 
 | 39 |   ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)", | 
 | 40 |                           compiled_files_dir, &diag)); | 
 | 41 |  | 
 | 42 |   const std::string out_apk = GetTestPath("out.apk"); | 
 | 43 |   std::vector<std::string> link_args = { | 
 | 44 |       "--manifest", GetDefaultManifest(), | 
 | 45 |       "-o", out_apk, | 
 | 46 |   }; | 
 | 47 |  | 
 | 48 |   ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag)); | 
 | 49 |  | 
 | 50 |   // Load the binary xml tree | 
 | 51 |   android::ResXMLTree tree; | 
 | 52 |   std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag); | 
| Ryan Mitchell | 1d008d1 | 2021-03-19 14:54:17 -0700 | [diff] [blame] | 53 |   ASSERT_THAT(apk, Ne(nullptr)); | 
 | 54 |  | 
| Winson | b7be793 | 2019-01-23 11:10:52 -0800 | [diff] [blame] | 55 |   std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml"); | 
 | 56 |   ASSERT_THAT(data, Ne(nullptr)); | 
| Winson | b7be793 | 2019-01-23 11:10:52 -0800 | [diff] [blame] | 57 |   AssertLoadXml(apk.get(), data.get(), &tree); | 
| Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 58 |  | 
 | 59 |   // Check that the raw string index has not been assigned | 
 | 60 |   EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1)); | 
 | 61 | } | 
 | 62 |  | 
 | 63 | TEST_F(LinkTest, KeepRawXmlStrings) { | 
 | 64 |   StdErrDiagnostics diag; | 
 | 65 |   const std::string compiled_files_dir = GetTestPath("compiled"); | 
 | 66 |   ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)", | 
 | 67 |                           compiled_files_dir, &diag)); | 
 | 68 |  | 
 | 69 |   const std::string out_apk = GetTestPath("out.apk"); | 
 | 70 |   std::vector<std::string> link_args = { | 
 | 71 |       "--manifest", GetDefaultManifest(), | 
 | 72 |       "-o", out_apk, | 
 | 73 |       "--keep-raw-values" | 
 | 74 |   }; | 
 | 75 |  | 
 | 76 |   ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag)); | 
 | 77 |  | 
 | 78 |   // Load the binary xml tree | 
 | 79 |   android::ResXMLTree tree; | 
 | 80 |   std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag); | 
| Ryan Mitchell | 1d008d1 | 2021-03-19 14:54:17 -0700 | [diff] [blame] | 81 |   ASSERT_THAT(apk, Ne(nullptr)); | 
 | 82 |  | 
| Winson | b7be793 | 2019-01-23 11:10:52 -0800 | [diff] [blame] | 83 |   std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml"); | 
 | 84 |   ASSERT_THAT(data, Ne(nullptr)); | 
| Winson | b7be793 | 2019-01-23 11:10:52 -0800 | [diff] [blame] | 85 |   AssertLoadXml(apk.get(), data.get(), &tree); | 
| Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 86 |  | 
 | 87 |   // Check that the raw string index has been set to the correct string pool entry | 
 | 88 |   int32_t raw_index = tree.getAttributeValueStringID(0); | 
 | 89 |   ASSERT_THAT(raw_index, Ne(-1)); | 
 | 90 |   EXPECT_THAT(util::GetString(tree.getStrings(), static_cast<size_t>(raw_index)), Eq("007")); | 
 | 91 | } | 
 | 92 |  | 
| Ryan Mitchell | 81dfca0e | 2019-06-07 10:20:27 -0700 | [diff] [blame] | 93 | TEST_F(LinkTest, NoCompressAssets) { | 
 | 94 |   StdErrDiagnostics diag; | 
 | 95 |   std::string content(500, 'a'); | 
 | 96 |   WriteFile(GetTestPath("assets/testtxt"), content); | 
 | 97 |   WriteFile(GetTestPath("assets/testtxt2"), content); | 
 | 98 |   WriteFile(GetTestPath("assets/test.txt"), content); | 
 | 99 |   WriteFile(GetTestPath("assets/test.hello.txt"), content); | 
 | 100 |   WriteFile(GetTestPath("assets/test.hello.xml"), content); | 
 | 101 |  | 
 | 102 |   const std::string out_apk = GetTestPath("out.apk"); | 
 | 103 |   std::vector<std::string> link_args = { | 
 | 104 |       "--manifest", GetDefaultManifest(), | 
 | 105 |       "-o", out_apk, | 
 | 106 |       "-0", ".txt", | 
 | 107 |       "-0", "txt2", | 
 | 108 |       "-0", ".hello.txt", | 
 | 109 |       "-0", "hello.xml", | 
 | 110 |       "-A", GetTestPath("assets") | 
 | 111 |   }; | 
 | 112 |  | 
 | 113 |   ASSERT_TRUE(Link(link_args, &diag)); | 
 | 114 |  | 
 | 115 |   std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag); | 
 | 116 |   ASSERT_THAT(apk, Ne(nullptr)); | 
 | 117 |   io::IFileCollection* zip = apk->GetFileCollection(); | 
 | 118 |   ASSERT_THAT(zip, Ne(nullptr)); | 
 | 119 |  | 
 | 120 |   auto file = zip->FindFile("assets/testtxt"); | 
 | 121 |   ASSERT_THAT(file, Ne(nullptr)); | 
 | 122 |   EXPECT_TRUE(file->WasCompressed()); | 
 | 123 |  | 
 | 124 |   file = zip->FindFile("assets/testtxt2"); | 
 | 125 |   ASSERT_THAT(file, Ne(nullptr)); | 
 | 126 |   EXPECT_FALSE(file->WasCompressed()); | 
 | 127 |  | 
 | 128 |   file = zip->FindFile("assets/test.txt"); | 
 | 129 |   ASSERT_THAT(file, Ne(nullptr)); | 
 | 130 |   EXPECT_FALSE(file->WasCompressed()); | 
 | 131 |  | 
 | 132 |   file = zip->FindFile("assets/test.hello.txt"); | 
 | 133 |   ASSERT_THAT(file, Ne(nullptr)); | 
 | 134 |   EXPECT_FALSE(file->WasCompressed()); | 
 | 135 |  | 
 | 136 |   file = zip->FindFile("assets/test.hello.xml"); | 
 | 137 |   ASSERT_THAT(file, Ne(nullptr)); | 
 | 138 |   EXPECT_FALSE(file->WasCompressed()); | 
 | 139 | } | 
 | 140 |  | 
 | 141 | TEST_F(LinkTest, NoCompressResources) { | 
 | 142 |   StdErrDiagnostics diag; | 
 | 143 |   std::string content(500, 'a'); | 
 | 144 |   const std::string compiled_files_dir = GetTestPath("compiled"); | 
 | 145 |   ASSERT_TRUE(CompileFile(GetTestPath("res/raw/testtxt"), content, compiled_files_dir, &diag)); | 
 | 146 |   ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test.txt"), content, compiled_files_dir, &diag)); | 
 | 147 |   ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test1.hello.txt"), content, compiled_files_dir, | 
 | 148 |               &diag)); | 
 | 149 |   ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test2.goodbye.xml"), content, compiled_files_dir, | 
 | 150 |               &diag)); | 
 | 151 |  | 
 | 152 |   const std::string out_apk = GetTestPath("out.apk"); | 
 | 153 |   std::vector<std::string> link_args = { | 
 | 154 |       "--manifest", GetDefaultManifest(), | 
 | 155 |       "-o", out_apk, | 
 | 156 |       "-0", ".txt", | 
 | 157 |       "-0", ".hello.txt", | 
 | 158 |       "-0", "goodbye.xml", | 
 | 159 |   }; | 
 | 160 |  | 
 | 161 |   ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag)); | 
 | 162 |  | 
 | 163 |   std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag); | 
 | 164 |   ASSERT_THAT(apk, Ne(nullptr)); | 
 | 165 |   io::IFileCollection* zip = apk->GetFileCollection(); | 
 | 166 |   ASSERT_THAT(zip, Ne(nullptr)); | 
 | 167 |  | 
 | 168 |   auto file = zip->FindFile("res/raw/testtxt"); | 
 | 169 |   ASSERT_THAT(file, Ne(nullptr)); | 
 | 170 |   EXPECT_TRUE(file->WasCompressed()); | 
 | 171 |  | 
 | 172 |   file = zip->FindFile("res/raw/test.txt"); | 
 | 173 |   ASSERT_THAT(file, Ne(nullptr)); | 
 | 174 |   EXPECT_FALSE(file->WasCompressed()); | 
 | 175 |  | 
 | 176 |   file = zip->FindFile("res/raw/test1.hello.hello.txt"); | 
 | 177 |   ASSERT_THAT(file, Ne(nullptr)); | 
 | 178 |   EXPECT_FALSE(file->WasCompressed()); | 
 | 179 |  | 
 | 180 |   file = zip->FindFile("res/raw/test2.goodbye.goodbye.xml"); | 
 | 181 |   ASSERT_THAT(file, Ne(nullptr)); | 
 | 182 |   EXPECT_FALSE(file->WasCompressed()); | 
 | 183 | } | 
 | 184 |  | 
| Donald Chai | 121c6e8 | 2019-06-12 12:51:57 -0700 | [diff] [blame] | 185 | TEST_F(LinkTest, OverlayStyles) { | 
 | 186 |   StdErrDiagnostics diag; | 
 | 187 |   const std::string compiled_files_dir = GetTestPath("compiled"); | 
 | 188 |   const std::string override_files_dir = GetTestPath("compiled-override"); | 
 | 189 |   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), | 
 | 190 |                           R"(<resources> | 
 | 191 |                                <style name="MyStyle"> | 
 | 192 |                                  <item name="android:textColor">#123</item> | 
 | 193 |                                </style> | 
 | 194 |                              </resources>)", | 
 | 195 |                           compiled_files_dir, &diag)); | 
 | 196 |   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values-override.xml"), | 
 | 197 |                           R"(<resources> | 
 | 198 |                                <style name="MyStyle"> | 
 | 199 |                                  <item name="android:background">#456</item> | 
 | 200 |                                </style> | 
 | 201 |                              </resources>)", | 
 | 202 |                           override_files_dir, &diag)); | 
 | 203 |  | 
 | 204 |  | 
 | 205 |   const std::string out_apk = GetTestPath("out.apk"); | 
 | 206 |   std::vector<std::string> link_args = { | 
 | 207 |       "--manifest", GetDefaultManifest(kDefaultPackageName), | 
 | 208 |       "-o", out_apk, | 
 | 209 |   }; | 
 | 210 |   const auto override_files = file::FindFiles(override_files_dir, &diag); | 
 | 211 |   for (const auto &override_file : override_files.value()) { | 
 | 212 |       link_args.push_back("-R"); | 
 | 213 |       link_args.push_back(file::BuildPath({override_files_dir, override_file})); | 
 | 214 |   } | 
 | 215 |   ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag)); | 
 | 216 |  | 
 | 217 |   std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag); | 
| Ryan Mitchell | 1d008d1 | 2021-03-19 14:54:17 -0700 | [diff] [blame] | 218 |   ASSERT_THAT(apk, Ne(nullptr)); | 
 | 219 |  | 
| Donald Chai | 121c6e8 | 2019-06-12 12:51:57 -0700 | [diff] [blame] | 220 |   const Style* actual_style = test::GetValue<Style>( | 
 | 221 |       apk->GetResourceTable(), std::string(kDefaultPackageName) + ":style/MyStyle"); | 
 | 222 |   ASSERT_NE(actual_style, nullptr); | 
 | 223 |   ASSERT_EQ(actual_style->entries.size(), 2); | 
 | 224 |   EXPECT_EQ(actual_style->entries[0].key.id, 0x01010098);  // android:textColor | 
 | 225 |   EXPECT_EQ(actual_style->entries[1].key.id, 0x010100d4);  // android:background | 
 | 226 | } | 
 | 227 |  | 
 | 228 | TEST_F(LinkTest, OverrideStylesInsteadOfOverlaying) { | 
 | 229 |   StdErrDiagnostics diag; | 
 | 230 |   const std::string compiled_files_dir = GetTestPath("compiled"); | 
 | 231 |   const std::string override_files_dir = GetTestPath("compiled-override"); | 
 | 232 |   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), | 
 | 233 |                           R"(<resources> | 
 | 234 |                                <style name="MyStyle"> | 
 | 235 |                                  <item name="android:textColor">#123</item> | 
 | 236 |                                </style> | 
 | 237 |                              </resources>)", | 
 | 238 |                           compiled_files_dir, &diag)); | 
 | 239 |   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values-override.xml"), | 
 | 240 |                           R"(<resources> | 
 | 241 |                                <style name="MyStyle"> | 
 | 242 |                                  <item name="android:background">#456</item> | 
 | 243 |                                </style> | 
 | 244 |                              </resources>)", | 
 | 245 |                           override_files_dir, &diag)); | 
 | 246 |  | 
 | 247 |  | 
 | 248 |   const std::string out_apk = GetTestPath("out.apk"); | 
 | 249 |   std::vector<std::string> link_args = { | 
 | 250 |       "--manifest", GetDefaultManifest(kDefaultPackageName), | 
 | 251 |       "--override-styles-instead-of-overlaying", | 
 | 252 |       "-o", out_apk, | 
 | 253 |   }; | 
 | 254 |   const auto override_files = file::FindFiles(override_files_dir, &diag); | 
 | 255 |   for (const auto &override_file : override_files.value()) { | 
 | 256 |       link_args.push_back("-R"); | 
 | 257 |       link_args.push_back(file::BuildPath({override_files_dir, override_file})); | 
 | 258 |   } | 
 | 259 |   ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag)); | 
 | 260 |  | 
 | 261 |   std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag); | 
| Ryan Mitchell | 1d008d1 | 2021-03-19 14:54:17 -0700 | [diff] [blame] | 262 |   ASSERT_THAT(apk, Ne(nullptr)); | 
 | 263 |  | 
| Donald Chai | 121c6e8 | 2019-06-12 12:51:57 -0700 | [diff] [blame] | 264 |   const Style* actual_style = test::GetValue<Style>( | 
 | 265 |       apk->GetResourceTable(), std::string(kDefaultPackageName) + ":style/MyStyle"); | 
 | 266 |   ASSERT_NE(actual_style, nullptr); | 
 | 267 |   ASSERT_EQ(actual_style->entries.size(), 1); | 
 | 268 |   EXPECT_EQ(actual_style->entries[0].key.id, 0x010100d4);  // android:background | 
 | 269 | } | 
 | 270 |  | 
| Udam Saini | b228df3 | 2019-06-18 16:50:34 -0700 | [diff] [blame] | 271 | TEST_F(LinkTest, AppInfoWithUsesSplit) { | 
 | 272 |   StdErrDiagnostics diag; | 
 | 273 |   const std::string base_files_dir = GetTestPath("base"); | 
 | 274 |   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), | 
 | 275 |                           R"(<resources> | 
 | 276 |                                <string name="bar">bar</string> | 
 | 277 |                              </resources>)", | 
 | 278 |                           base_files_dir, &diag)); | 
 | 279 |   const std::string base_apk = GetTestPath("base.apk"); | 
 | 280 |   std::vector<std::string> link_args = { | 
 | 281 |       "--manifest", GetDefaultManifest("com.aapt2.app"), | 
 | 282 |       "-o", base_apk, | 
 | 283 |   }; | 
 | 284 |   ASSERT_TRUE(Link(link_args, base_files_dir, &diag)); | 
 | 285 |  | 
 | 286 |   const std::string feature_manifest = GetTestPath("feature_manifest.xml"); | 
 | 287 |   WriteFile(feature_manifest, android::base::StringPrintf(R"( | 
 | 288 |       <manifest xmlns:android="http://schemas.android.com/apk/res/android" | 
 | 289 |           package="com.aapt2.app" split="feature1"> | 
 | 290 |       </manifest>)")); | 
 | 291 |   const std::string feature_files_dir = GetTestPath("feature"); | 
 | 292 |   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), | 
 | 293 |                           R"(<resources> | 
 | 294 |                                <string name="foo">foo</string> | 
 | 295 |                              </resources>)", | 
 | 296 |                           feature_files_dir, &diag)); | 
 | 297 |   const std::string feature_apk = GetTestPath("feature.apk"); | 
 | 298 |   const std::string feature_package_id = "0x80"; | 
 | 299 |   link_args = { | 
 | 300 |       "--manifest", feature_manifest, | 
 | 301 |       "-I", base_apk, | 
 | 302 |       "--package-id", feature_package_id, | 
 | 303 |       "-o", feature_apk, | 
 | 304 |   }; | 
 | 305 |   ASSERT_TRUE(Link(link_args, feature_files_dir, &diag)); | 
 | 306 |  | 
 | 307 |   const std::string feature2_manifest = GetTestPath("feature2_manifest.xml"); | 
 | 308 |   WriteFile(feature2_manifest, android::base::StringPrintf(R"( | 
 | 309 |         <manifest xmlns:android="http://schemas.android.com/apk/res/android" | 
 | 310 |             package="com.aapt2.app" split="feature2"> | 
 | 311 |           <uses-split android:name="feature1"/> | 
 | 312 |         </manifest>)")); | 
 | 313 |   const std::string feature2_files_dir = GetTestPath("feature2"); | 
 | 314 |   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), | 
 | 315 |                           R"(<resources> | 
 | 316 |                                <string-array name="string_array"> | 
 | 317 |                                  <item>@string/bar</item> | 
 | 318 |                                  <item>@string/foo</item> | 
 | 319 |                                </string-array> | 
 | 320 |                              </resources>)", | 
 | 321 |                           feature2_files_dir, &diag)); | 
 | 322 |   const std::string feature2_apk = GetTestPath("feature2.apk"); | 
 | 323 |   const std::string feature2_package_id = "0x81"; | 
 | 324 |   link_args = { | 
 | 325 |       "--manifest", feature2_manifest, | 
 | 326 |       "-I", base_apk, | 
 | 327 |       "-I", feature_apk, | 
 | 328 |       "--package-id", feature2_package_id, | 
 | 329 |       "-o", feature2_apk, | 
 | 330 |   }; | 
 | 331 |   ASSERT_TRUE(Link(link_args, feature2_files_dir, &diag)); | 
 | 332 | } | 
 | 333 |  | 
| Ryan Mitchell | 5855de7 | 2021-02-24 14:39:13 -0800 | [diff] [blame] | 334 | TEST_F(LinkTest, SharedLibraryAttributeRJava) { | 
 | 335 |   StdErrDiagnostics diag; | 
 | 336 |   const std::string lib_values = | 
 | 337 |       R"(<resources> | 
 | 338 |            <attr name="foo"/> | 
 | 339 |            <public type="attr" name="foo" id="0x00010001"/> | 
 | 340 |            <declare-styleable name="LibraryStyleable"> | 
 | 341 |              <attr name="foo" /> | 
 | 342 |            </declare-styleable> | 
 | 343 |          </resources>)"; | 
 | 344 |  | 
 | 345 |   const std::string client_values = | 
 | 346 |       R"(<resources> | 
 | 347 |            <attr name="bar" /> | 
 | 348 |            <declare-styleable name="ClientStyleable"> | 
 | 349 |              <attr name="com.example.lib:foo" /> | 
 | 350 |              <attr name="bar" /> | 
 | 351 |            </declare-styleable> | 
 | 352 |          </resources>)"; | 
 | 353 |  | 
 | 354 |   // Build a library with a public attribute | 
 | 355 |   const std::string lib_res = GetTestPath("library-res"); | 
 | 356 |   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), lib_values, lib_res, &diag)); | 
 | 357 |  | 
 | 358 |   const std::string lib_apk = GetTestPath("library.apk"); | 
 | 359 |   const std::string lib_java = GetTestPath("library_java"); | 
 | 360 |   // clang-format off | 
 | 361 |   auto lib_manifest = ManifestBuilder(this) | 
 | 362 |       .SetPackageName("com.example.lib") | 
 | 363 |       .Build(); | 
 | 364 |  | 
 | 365 |   auto lib_link_args = LinkCommandBuilder(this) | 
 | 366 |       .SetManifestFile(lib_manifest) | 
 | 367 |       .AddFlag("--shared-lib") | 
 | 368 |       .AddParameter("--java", lib_java) | 
 | 369 |       .AddCompiledResDir(lib_res, &diag) | 
 | 370 |       .Build(lib_apk); | 
 | 371 |   // clang-format on | 
 | 372 |   ASSERT_TRUE(Link(lib_link_args, &diag)); | 
 | 373 |  | 
 | 374 |   const std::string lib_r_java = lib_java + "/com/example/lib/R.java"; | 
 | 375 |   std::string lib_r_contents; | 
 | 376 |   ASSERT_TRUE(android::base::ReadFileToString(lib_r_java, &lib_r_contents)); | 
 | 377 |   EXPECT_THAT(lib_r_contents, HasSubstr(" public static int foo=0x00010001;")); | 
 | 378 |   EXPECT_THAT(lib_r_contents, HasSubstr(" com.example.lib.R.attr.foo")); | 
 | 379 |  | 
 | 380 |   // Build a client that uses the library attribute in a declare-styleable | 
 | 381 |   const std::string client_res = GetTestPath("client-res"); | 
 | 382 |   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), client_values, client_res, &diag)); | 
 | 383 |  | 
 | 384 |   const std::string client_apk = GetTestPath("client.apk"); | 
 | 385 |   const std::string client_java = GetTestPath("client_java"); | 
 | 386 |   // clang-format off | 
 | 387 |   auto client_manifest = ManifestBuilder(this) | 
 | 388 |       .SetPackageName("com.example.client") | 
 | 389 |       .Build(); | 
 | 390 |  | 
 | 391 |   auto client_link_args = LinkCommandBuilder(this) | 
 | 392 |       .SetManifestFile(client_manifest) | 
 | 393 |       .AddParameter("--java", client_java) | 
 | 394 |       .AddParameter("-I", lib_apk) | 
 | 395 |       .AddCompiledResDir(client_res, &diag) | 
 | 396 |       .Build(client_apk); | 
 | 397 |   // clang-format on | 
 | 398 |   ASSERT_TRUE(Link(client_link_args, &diag)); | 
 | 399 |  | 
 | 400 |   const std::string client_r_java = client_java + "/com/example/client/R.java"; | 
 | 401 |   std::string client_r_contents; | 
 | 402 |   ASSERT_TRUE(android::base::ReadFileToString(client_r_java, &client_r_contents)); | 
 | 403 |   EXPECT_THAT(client_r_contents, HasSubstr(" com.example.lib.R.attr.foo, 0x7f010000")); | 
 | 404 | } | 
 | 405 |  | 
| Ryan Mitchell | 2fedba9 | 2021-04-23 07:47:38 -0700 | [diff] [blame] | 406 | struct SourceXML { | 
 | 407 |   std::string res_file_path; | 
 | 408 |   std::string file_contents; | 
 | 409 | }; | 
 | 410 |  | 
 | 411 | static void BuildApk(const std::vector<SourceXML>& source_files, const std::string& apk_path, | 
 | 412 |                      LinkCommandBuilder&& link_args, CommandTestFixture* fixture, | 
 | 413 |                      IDiagnostics* diag) { | 
 | 414 |   TemporaryDir res_dir; | 
 | 415 |   TemporaryDir compiled_res_dir; | 
 | 416 |   for (auto& source_file : source_files) { | 
 | 417 |     ASSERT_TRUE(fixture->CompileFile(res_dir.path + source_file.res_file_path, | 
 | 418 |                                      source_file.file_contents, compiled_res_dir.path, diag)); | 
 | 419 |   } | 
 | 420 |   ASSERT_TRUE(fixture->Link( | 
 | 421 |       link_args.AddCompiledResDir(compiled_res_dir.path, diag).Build(apk_path), diag)); | 
 | 422 | } | 
 | 423 |  | 
 | 424 | static void BuildSDK(const std::vector<SourceXML>& source_files, const std::string& apk_path, | 
 | 425 |                      const std::string& java_root_path, CommandTestFixture* fixture, | 
 | 426 |                      IDiagnostics* diag) { | 
 | 427 |   auto android_manifest = ManifestBuilder(fixture).SetPackageName("android").Build(); | 
 | 428 |  | 
 | 429 |   auto android_link_args = LinkCommandBuilder(fixture) | 
 | 430 |                                .SetManifestFile(android_manifest) | 
 | 431 |                                .AddParameter("--private-symbols", "com.android.internal") | 
 | 432 |                                .AddParameter("--java", java_root_path); | 
 | 433 |  | 
 | 434 |   BuildApk(source_files, apk_path, std::move(android_link_args), fixture, diag); | 
 | 435 | } | 
 | 436 |  | 
 | 437 | static void BuildNonFinalizedSDK(const std::string& apk_path, const std::string& java_path, | 
 | 438 |                                  CommandTestFixture* fixture, IDiagnostics* diag) { | 
| Ryan Mitchell | 2e9bec1 | 2021-03-22 09:31:00 -0700 | [diff] [blame] | 439 |   const std::string android_values = | 
 | 440 |       R"(<resources> | 
 | 441 |           <public type="attr" name="finalized_res" id="0x01010001"/> | 
 | 442 |  | 
 | 443 |           <!-- S staged attributes (support staged resources in the same type id) --> | 
 | 444 |           <staging-public-group type="attr" first-id="0x01010050"> | 
 | 445 |             <public name="staged_s_res" /> | 
 | 446 |           </staging-public-group> | 
 | 447 |  | 
| Ryan Mitchell | 2fedba9 | 2021-04-23 07:47:38 -0700 | [diff] [blame] | 448 |           <staging-public-group type="string" first-id="0x01fd0080"> | 
 | 449 |             <public name="staged_s_string" /> | 
 | 450 |           </staging-public-group> | 
 | 451 |  | 
| Ryan Mitchell | 2e9bec1 | 2021-03-22 09:31:00 -0700 | [diff] [blame] | 452 |           <!-- SV2 staged attributes (support staged resources in a separate type id) --> | 
 | 453 |           <staging-public-group type="attr" first-id="0x01ff0049"> | 
 | 454 |             <public name="staged_s2_res" /> | 
 | 455 |           </staging-public-group> | 
 | 456 |  | 
 | 457 |           <!-- T staged attributes (support staged resources in multiple separate type ids) --> | 
 | 458 |           <staging-public-group type="attr" first-id="0x01fe0063"> | 
 | 459 |             <public name="staged_t_res" /> | 
 | 460 |           </staging-public-group> | 
 | 461 |  | 
| Ryan Mitchell | 2fedba9 | 2021-04-23 07:47:38 -0700 | [diff] [blame] | 462 |           <attr name="finalized_res" /> | 
 | 463 |           <attr name="staged_s_res" /> | 
 | 464 |           <attr name="staged_s2_res" /> | 
 | 465 |           <attr name="staged_t_res" /> | 
 | 466 |           <string name="staged_s_string">Hello</string> | 
 | 467 |          </resources>)"; | 
 | 468 |  | 
 | 469 |   SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = android_values}; | 
 | 470 |   BuildSDK({source_xml}, apk_path, java_path, fixture, diag); | 
 | 471 | } | 
 | 472 |  | 
 | 473 | static void BuildFinalizedSDK(const std::string& apk_path, const std::string& java_path, | 
 | 474 |                               CommandTestFixture* fixture, IDiagnostics* diag) { | 
 | 475 |   const std::string android_values = | 
 | 476 |       R"(<resources> | 
 | 477 |           <public type="attr" name="finalized_res" id="0x01010001"/> | 
 | 478 |           <public type="attr" name="staged_s_res" id="0x01010002"/> | 
 | 479 |           <public type="attr" name="staged_s2_res" id="0x01010003"/> | 
 | 480 |           <public type="string" name="staged_s_string" id="0x01020000"/> | 
 | 481 |  | 
 | 482 |           <!-- S staged attributes (support staged resources in the same type id) --> | 
 | 483 |           <staging-public-group-final type="attr" first-id="0x01010050"> | 
 | 484 |             <public name="staged_s_res" /> | 
 | 485 |           </staging-public-group-final> | 
 | 486 |  | 
 | 487 |           <staging-public-group-final type="string" first-id="0x01fd0080"> | 
 | 488 |             <public name="staged_s_string" /> | 
 | 489 |           </staging-public-group-final> | 
 | 490 |  | 
 | 491 |           <!-- SV2 staged attributes (support staged resources in a separate type id) --> | 
 | 492 |           <staging-public-group-final type="attr" first-id="0x01ff0049"> | 
 | 493 |             <public name="staged_s2_res" /> | 
 | 494 |           </staging-public-group-final> | 
 | 495 |  | 
 | 496 |           <!-- T staged attributes (support staged resources in multiple separate type ids) --> | 
 | 497 |           <staging-public-group type="attr" first-id="0x01fe0063"> | 
 | 498 |             <public name="staged_t_res" /> | 
| Ryan Mitchell | 2e9bec1 | 2021-03-22 09:31:00 -0700 | [diff] [blame] | 499 |           </staging-public-group> | 
 | 500 |  | 
 | 501 |           <attr name="finalized_res" /> | 
 | 502 |           <attr name="staged_s_res" /> | 
 | 503 |           <attr name="staged_s2_res" /> | 
 | 504 |           <attr name="staged_t_res" /> | 
| Ryan Mitchell | 2fedba9 | 2021-04-23 07:47:38 -0700 | [diff] [blame] | 505 |           <string name="staged_s_string">Hello</string> | 
| Ryan Mitchell | 2e9bec1 | 2021-03-22 09:31:00 -0700 | [diff] [blame] | 506 |          </resources>)"; | 
 | 507 |  | 
| Ryan Mitchell | 2fedba9 | 2021-04-23 07:47:38 -0700 | [diff] [blame] | 508 |   SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = android_values}; | 
 | 509 |   BuildSDK({source_xml}, apk_path, java_path, fixture, diag); | 
 | 510 | } | 
 | 511 |  | 
 | 512 | static void BuildAppAgainstSDK(const std::string& apk_path, const std::string& java_path, | 
 | 513 |                                const std::string& sdk_path, CommandTestFixture* fixture, | 
 | 514 |                                IDiagnostics* diag) { | 
| Ryan Mitchell | 2e9bec1 | 2021-03-22 09:31:00 -0700 | [diff] [blame] | 515 |   const std::string app_values = | 
 | 516 |       R"(<resources xmlns:android="http://schemas.android.com/apk/res/android"> | 
 | 517 |            <attr name="bar" /> | 
| Ryan Mitchell | 2fedba9 | 2021-04-23 07:47:38 -0700 | [diff] [blame] | 518 |            <style name="MyStyle"> | 
 | 519 |              <item name="android:staged_s_res">@android:string/staged_s_string</item> | 
 | 520 |            </style> | 
| Ryan Mitchell | 2e9bec1 | 2021-03-22 09:31:00 -0700 | [diff] [blame] | 521 |            <declare-styleable name="ClientStyleable"> | 
 | 522 |              <attr name="android:finalized_res" /> | 
 | 523 |              <attr name="android:staged_s_res" /> | 
 | 524 |              <attr name="bar" /> | 
 | 525 |            </declare-styleable> | 
| Ryan Mitchell | 2fedba9 | 2021-04-23 07:47:38 -0700 | [diff] [blame] | 526 |            <public name="MyStyle" type="style" id="0x7f020000" /> | 
| Ryan Mitchell | 2e9bec1 | 2021-03-22 09:31:00 -0700 | [diff] [blame] | 527 |          </resources>)"; | 
 | 528 |  | 
| Ryan Mitchell | 2fedba9 | 2021-04-23 07:47:38 -0700 | [diff] [blame] | 529 |   SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = app_values}; | 
| Ryan Mitchell | 2e9bec1 | 2021-03-22 09:31:00 -0700 | [diff] [blame] | 530 |  | 
| Ryan Mitchell | 2fedba9 | 2021-04-23 07:47:38 -0700 | [diff] [blame] | 531 |   auto app_manifest = ManifestBuilder(fixture).SetPackageName("com.example.app").Build(); | 
 | 532 |  | 
 | 533 |   auto app_link_args = LinkCommandBuilder(fixture) | 
 | 534 |                            .SetManifestFile(app_manifest) | 
 | 535 |                            .AddParameter("--java", java_path) | 
 | 536 |                            .AddParameter("-I", sdk_path); | 
 | 537 |  | 
 | 538 |   BuildApk({source_xml}, apk_path, std::move(app_link_args), fixture, diag); | 
 | 539 | } | 
 | 540 |  | 
 | 541 | TEST_F(LinkTest, StagedAndroidApi) { | 
 | 542 |   StdErrDiagnostics diag; | 
| Ryan Mitchell | 2e9bec1 | 2021-03-22 09:31:00 -0700 | [diff] [blame] | 543 |   const std::string android_apk = GetTestPath("android.apk"); | 
| Ryan Mitchell | 2fedba9 | 2021-04-23 07:47:38 -0700 | [diff] [blame] | 544 |   const std::string android_java = GetTestPath("android-java"); | 
 | 545 |   BuildNonFinalizedSDK(android_apk, android_java, this, &diag); | 
| Ryan Mitchell | 2e9bec1 | 2021-03-22 09:31:00 -0700 | [diff] [blame] | 546 |  | 
 | 547 |   const std::string android_r_java = android_java + "/android/R.java"; | 
 | 548 |   std::string android_r_contents; | 
 | 549 |   ASSERT_TRUE(android::base::ReadFileToString(android_r_java, &android_r_contents)); | 
| Ryan Mitchell | ca3b4f7 | 2021-03-29 14:47:02 -0700 | [diff] [blame] | 550 |   EXPECT_THAT(android_r_contents, HasSubstr("public static final int finalized_res=0x01010001;")); | 
 | 551 |   EXPECT_THAT( | 
 | 552 |       android_r_contents, | 
 | 553 |       HasSubstr("public static final int staged_s_res; static { staged_s_res=0x01010050; }")); | 
 | 554 |   EXPECT_THAT( | 
 | 555 |       android_r_contents, | 
| Ryan Mitchell | 2fedba9 | 2021-04-23 07:47:38 -0700 | [diff] [blame] | 556 |       HasSubstr("public static final int staged_s_string; static { staged_s_string=0x01fd0080; }")); | 
 | 557 |   EXPECT_THAT( | 
 | 558 |       android_r_contents, | 
| Ryan Mitchell | ca3b4f7 | 2021-03-29 14:47:02 -0700 | [diff] [blame] | 559 |       HasSubstr("public static final int staged_s2_res; static { staged_s2_res=0x01ff0049; }")); | 
 | 560 |   EXPECT_THAT( | 
 | 561 |       android_r_contents, | 
 | 562 |       HasSubstr("public static final int staged_t_res; static { staged_t_res=0x01fe0063; }")); | 
| Ryan Mitchell | 2e9bec1 | 2021-03-22 09:31:00 -0700 | [diff] [blame] | 563 |  | 
 | 564 |   const std::string app_apk = GetTestPath("app.apk"); | 
| Ryan Mitchell | 2fedba9 | 2021-04-23 07:47:38 -0700 | [diff] [blame] | 565 |   const std::string app_java = GetTestPath("app-java"); | 
 | 566 |   BuildAppAgainstSDK(app_apk, app_java, android_apk, this, &diag); | 
| Ryan Mitchell | 2e9bec1 | 2021-03-22 09:31:00 -0700 | [diff] [blame] | 567 |  | 
 | 568 |   const std::string client_r_java = app_java + "/com/example/app/R.java"; | 
 | 569 |   std::string client_r_contents; | 
 | 570 |   ASSERT_TRUE(android::base::ReadFileToString(client_r_java, &client_r_contents)); | 
 | 571 |   EXPECT_THAT(client_r_contents, HasSubstr(" 0x01010001, android.R.attr.staged_s_res, 0x7f010000")); | 
 | 572 |  | 
 | 573 |   // Test that the resource ids of staged and non-staged resource can be retrieved | 
 | 574 |   android::AssetManager2 am; | 
 | 575 |   auto android_asset = android::ApkAssets::Load(android_apk); | 
 | 576 |   ASSERT_THAT(android_asset, NotNull()); | 
 | 577 |   ASSERT_TRUE(am.SetApkAssets({android_asset.get()})); | 
 | 578 |  | 
 | 579 |   auto result = am.GetResourceId("android:attr/finalized_res"); | 
 | 580 |   ASSERT_TRUE(result.has_value()); | 
 | 581 |   EXPECT_THAT(*result, Eq(0x01010001)); | 
 | 582 |  | 
 | 583 |   result = am.GetResourceId("android:attr/staged_s_res"); | 
 | 584 |   ASSERT_TRUE(result.has_value()); | 
 | 585 |   EXPECT_THAT(*result, Eq(0x01010050)); | 
 | 586 |  | 
| Ryan Mitchell | 2fedba9 | 2021-04-23 07:47:38 -0700 | [diff] [blame] | 587 |   result = am.GetResourceId("android:string/staged_s_string"); | 
 | 588 |   ASSERT_TRUE(result.has_value()); | 
 | 589 |   EXPECT_THAT(*result, Eq(0x01fd0080)); | 
 | 590 |  | 
| Ryan Mitchell | 2e9bec1 | 2021-03-22 09:31:00 -0700 | [diff] [blame] | 591 |   result = am.GetResourceId("android:attr/staged_s2_res"); | 
 | 592 |   ASSERT_TRUE(result.has_value()); | 
 | 593 |   EXPECT_THAT(*result, Eq(0x01ff0049)); | 
 | 594 |  | 
 | 595 |   result = am.GetResourceId("android:attr/staged_t_res"); | 
 | 596 |   ASSERT_TRUE(result.has_value()); | 
 | 597 |   EXPECT_THAT(*result, Eq(0x01fe0063)); | 
| Ryan Mitchell | 2fedba9 | 2021-04-23 07:47:38 -0700 | [diff] [blame] | 598 | } | 
| Ryan Mitchell | 2e9bec1 | 2021-03-22 09:31:00 -0700 | [diff] [blame] | 599 |  | 
| Ryan Mitchell | 2fedba9 | 2021-04-23 07:47:38 -0700 | [diff] [blame] | 600 | TEST_F(LinkTest, FinalizedAndroidApi) { | 
 | 601 |   StdErrDiagnostics diag; | 
 | 602 |   const std::string android_apk = GetTestPath("android.apk"); | 
 | 603 |   const std::string android_java = GetTestPath("android-java"); | 
 | 604 |   BuildFinalizedSDK(android_apk, android_java, this, &diag); | 
 | 605 |  | 
 | 606 |   const std::string android_r_java = android_java + "/android/R.java"; | 
 | 607 |   std::string android_r_contents; | 
 | 608 |   ASSERT_TRUE(android::base::ReadFileToString(android_r_java, &android_r_contents)); | 
 | 609 |   EXPECT_THAT(android_r_contents, HasSubstr("public static final int finalized_res=0x01010001;")); | 
 | 610 |   EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s_res=0x01010002;")); | 
 | 611 |   EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s_string=0x01020000;")); | 
 | 612 |   EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s2_res=0x01010003;")); | 
 | 613 |   EXPECT_THAT( | 
 | 614 |       android_r_contents, | 
 | 615 |       HasSubstr("public static final int staged_t_res; static { staged_t_res=0x01fe0063; }")); | 
 | 616 |   ; | 
 | 617 |  | 
 | 618 |   // Build an application against the non-finalized SDK and then load it into an AssetManager with | 
 | 619 |   // the finalized SDK. | 
 | 620 |   const std::string non_finalized_android_apk = GetTestPath("non-finalized-android.apk"); | 
 | 621 |   const std::string non_finalized_android_java = GetTestPath("non-finalized-android-java"); | 
 | 622 |   BuildNonFinalizedSDK(non_finalized_android_apk, non_finalized_android_java, this, &diag); | 
 | 623 |  | 
 | 624 |   const std::string app_apk = GetTestPath("app.apk"); | 
 | 625 |   const std::string app_java = GetTestPath("app-java"); | 
 | 626 |   BuildAppAgainstSDK(app_apk, app_java, non_finalized_android_apk, this, &diag); | 
 | 627 |  | 
 | 628 |   android::AssetManager2 am; | 
 | 629 |   auto android_asset = android::ApkAssets::Load(android_apk); | 
 | 630 |   auto app_against_non_final = android::ApkAssets::Load(app_apk); | 
 | 631 |   ASSERT_THAT(android_asset, NotNull()); | 
 | 632 |   ASSERT_THAT(app_against_non_final, NotNull()); | 
 | 633 |   ASSERT_TRUE(am.SetApkAssets({android_asset.get(), app_against_non_final.get()})); | 
 | 634 |  | 
 | 635 |   auto result = am.GetResourceId("android:attr/finalized_res"); | 
| Ryan Mitchell | 2e9bec1 | 2021-03-22 09:31:00 -0700 | [diff] [blame] | 636 |   ASSERT_TRUE(result.has_value()); | 
| Ryan Mitchell | 2fedba9 | 2021-04-23 07:47:38 -0700 | [diff] [blame] | 637 |   EXPECT_THAT(*result, Eq(0x01010001)); | 
 | 638 |  | 
 | 639 |   result = am.GetResourceId("android:attr/staged_s_res"); | 
 | 640 |   ASSERT_TRUE(result.has_value()); | 
 | 641 |   EXPECT_THAT(*result, Eq(0x01010002)); | 
 | 642 |  | 
 | 643 |   result = am.GetResourceId("android:string/staged_s_string"); | 
 | 644 |   ASSERT_TRUE(result.has_value()); | 
 | 645 |   EXPECT_THAT(*result, Eq(0x01020000)); | 
 | 646 |  | 
 | 647 |   result = am.GetResourceId("android:attr/staged_s2_res"); | 
 | 648 |   ASSERT_TRUE(result.has_value()); | 
 | 649 |   EXPECT_THAT(*result, Eq(0x01010003)); | 
 | 650 |  | 
 | 651 |   { | 
 | 652 |     auto style = am.GetBag(0x7f020000); | 
 | 653 |     ASSERT_TRUE(style.has_value()); | 
 | 654 |  | 
 | 655 |     auto& entry = (*style)->entries[0]; | 
 | 656 |     EXPECT_THAT(entry.key, Eq(0x01010002)); | 
 | 657 |     EXPECT_THAT(entry.value.dataType, Eq(android::Res_value::TYPE_REFERENCE)); | 
 | 658 |     EXPECT_THAT(entry.value.data, Eq(0x01020000)); | 
 | 659 |   } | 
 | 660 |  | 
 | 661 |   // Re-compile the application against the finalized SDK and then load it into an AssetManager with | 
 | 662 |   // the finalized SDK. | 
 | 663 |   const std::string app_apk_respin = GetTestPath("app-respin.apk"); | 
 | 664 |   const std::string app_java_respin = GetTestPath("app-respin-java"); | 
 | 665 |   BuildAppAgainstSDK(app_apk_respin, app_java_respin, android_apk, this, &diag); | 
 | 666 |  | 
 | 667 |   auto app_against_final = android::ApkAssets::Load(app_apk_respin); | 
 | 668 |   ASSERT_THAT(app_against_final, NotNull()); | 
 | 669 |   ASSERT_TRUE(am.SetApkAssets({android_asset.get(), app_against_final.get()})); | 
 | 670 |  | 
 | 671 |   { | 
 | 672 |     auto style = am.GetBag(0x7f020000); | 
 | 673 |     ASSERT_TRUE(style.has_value()); | 
 | 674 |  | 
 | 675 |     auto& entry = (*style)->entries[0]; | 
 | 676 |     EXPECT_THAT(entry.key, Eq(0x01010002)); | 
 | 677 |     EXPECT_THAT(entry.value.dataType, Eq(android::Res_value::TYPE_REFERENCE)); | 
 | 678 |     EXPECT_THAT(entry.value.data, Eq(0x01020000)); | 
 | 679 |   } | 
| Ryan Mitchell | 2e9bec1 | 2021-03-22 09:31:00 -0700 | [diff] [blame] | 680 | } | 
 | 681 |  | 
| Ryan Mitchell | 326e35ff | 2021-04-12 07:50:42 -0700 | [diff] [blame] | 682 | TEST_F(LinkTest, MacroSubstitution) { | 
 | 683 |   StdErrDiagnostics diag; | 
 | 684 |   const std::string values = | 
 | 685 |       R"(<resources xmlns:an="http://schemas.android.com/apk/res/android"> | 
 | 686 |            <macro name="is_enabled">true</macro> | 
 | 687 |            <macro name="deep_is_enabled">@macro/is_enabled</macro> | 
 | 688 |            <macro name="attr_ref">?is_enabled_attr</macro> | 
 | 689 |            <macro name="raw_string">Hello World!</macro> | 
 | 690 |            <macro name="android_ref">@an:color/primary_text_dark</macro> | 
 | 691 |  | 
 | 692 |            <attr name="is_enabled_attr" /> | 
 | 693 |            <public type="attr" name="is_enabled_attr" id="0x7f010000"/> | 
 | 694 |  | 
 | 695 |            <string name="is_enabled_str">@macro/is_enabled</string> | 
 | 696 |            <bool name="is_enabled_bool">@macro/deep_is_enabled</bool> | 
 | 697 |  | 
 | 698 |            <array name="my_array"> | 
 | 699 |              <item>@macro/is_enabled</item> | 
 | 700 |            </array> | 
 | 701 |  | 
 | 702 |            <style name="MyStyle"> | 
 | 703 |               <item name="android:background">@macro/attr_ref</item> | 
 | 704 |               <item name="android:fontFamily">@macro/raw_string</item> | 
 | 705 |            </style> | 
 | 706 |          </resources>)"; | 
 | 707 |  | 
 | 708 |   const std::string xml_values = | 
 | 709 |       R"(<SomeLayout xmlns:android="http://schemas.android.com/apk/res/android" | 
 | 710 |                      android:background="@macro/android_ref" | 
 | 711 |                      android:fontFamily="@macro/raw_string"> | 
 | 712 |          </SomeLayout>)"; | 
 | 713 |  | 
 | 714 |   // Build a library with a public attribute | 
 | 715 |   const std::string lib_res = GetTestPath("test-res"); | 
 | 716 |   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), values, lib_res, &diag)); | 
 | 717 |   ASSERT_TRUE(CompileFile(GetTestPath("res/layout/layout.xml"), xml_values, lib_res, &diag)); | 
 | 718 |  | 
 | 719 |   const std::string lib_apk = GetTestPath("test.apk"); | 
 | 720 |   // clang-format off | 
 | 721 |   auto lib_link_args = LinkCommandBuilder(this) | 
 | 722 |       .SetManifestFile(ManifestBuilder(this).SetPackageName("com.test").Build()) | 
 | 723 |       .AddCompiledResDir(lib_res, &diag) | 
 | 724 |       .AddFlag("--no-auto-version") | 
 | 725 |       .Build(lib_apk); | 
 | 726 |   // clang-format on | 
 | 727 |   ASSERT_TRUE(Link(lib_link_args, &diag)); | 
 | 728 |  | 
 | 729 |   auto apk = LoadedApk::LoadApkFromPath(lib_apk, &diag); | 
 | 730 |   ASSERT_THAT(apk, NotNull()); | 
 | 731 |  | 
 | 732 |   // Test that the type flags determines the value type | 
 | 733 |   auto actual_bool = | 
 | 734 |       test::GetValue<BinaryPrimitive>(apk->GetResourceTable(), "com.test:bool/is_enabled_bool"); | 
 | 735 |   ASSERT_THAT(actual_bool, NotNull()); | 
 | 736 |   EXPECT_EQ(android::Res_value::TYPE_INT_BOOLEAN, actual_bool->value.dataType); | 
 | 737 |   EXPECT_EQ(0xffffffffu, actual_bool->value.data); | 
 | 738 |  | 
 | 739 |   auto actual_str = | 
 | 740 |       test::GetValue<String>(apk->GetResourceTable(), "com.test:string/is_enabled_str"); | 
 | 741 |   ASSERT_THAT(actual_str, NotNull()); | 
 | 742 |   EXPECT_EQ(*actual_str->value, "true"); | 
 | 743 |  | 
 | 744 |   // Test nested data structures | 
 | 745 |   auto actual_array = test::GetValue<Array>(apk->GetResourceTable(), "com.test:array/my_array"); | 
 | 746 |   ASSERT_THAT(actual_array, NotNull()); | 
 | 747 |   EXPECT_THAT(actual_array->elements.size(), Eq(1)); | 
 | 748 |  | 
 | 749 |   auto array_el_ref = ValueCast<BinaryPrimitive>(actual_array->elements[0].get()); | 
 | 750 |   ASSERT_THAT(array_el_ref, NotNull()); | 
 | 751 |   EXPECT_THAT(array_el_ref->value.dataType, Eq(android::Res_value::TYPE_INT_BOOLEAN)); | 
 | 752 |   EXPECT_THAT(array_el_ref->value.data, Eq(0xffffffffu)); | 
 | 753 |  | 
 | 754 |   auto actual_style = test::GetValue<Style>(apk->GetResourceTable(), "com.test:style/MyStyle"); | 
 | 755 |   ASSERT_THAT(actual_style, NotNull()); | 
 | 756 |   EXPECT_THAT(actual_style->entries.size(), Eq(2)); | 
 | 757 |  | 
 | 758 |   { | 
 | 759 |     auto style_el = ValueCast<Reference>(actual_style->entries[0].value.get()); | 
 | 760 |     ASSERT_THAT(style_el, NotNull()); | 
 | 761 |     EXPECT_THAT(style_el->reference_type, Eq(Reference::Type::kAttribute)); | 
 | 762 |     EXPECT_THAT(style_el->id, Eq(0x7f010000)); | 
 | 763 |   } | 
 | 764 |  | 
 | 765 |   { | 
 | 766 |     auto style_el = ValueCast<String>(actual_style->entries[1].value.get()); | 
 | 767 |     ASSERT_THAT(style_el, NotNull()); | 
 | 768 |     EXPECT_THAT(*style_el->value, Eq("Hello World!")); | 
 | 769 |   } | 
 | 770 |  | 
 | 771 |   // Test substitution in compiled xml files | 
 | 772 |   auto xml = apk->LoadXml("res/layout/layout.xml", &diag); | 
 | 773 |   ASSERT_THAT(xml, NotNull()); | 
 | 774 |  | 
 | 775 |   auto& xml_attrs = xml->root->attributes; | 
 | 776 |   ASSERT_THAT(xml_attrs.size(), Eq(2)); | 
 | 777 |  | 
 | 778 |   auto attr_value = ValueCast<Reference>(xml_attrs[0].compiled_value.get()); | 
 | 779 |   ASSERT_THAT(attr_value, NotNull()); | 
 | 780 |   EXPECT_THAT(attr_value->reference_type, Eq(Reference::Type::kResource)); | 
 | 781 |   EXPECT_THAT(attr_value->id, Eq(0x01060001)); | 
 | 782 |  | 
 | 783 |   EXPECT_THAT(xml_attrs[1].compiled_value.get(), IsNull()); | 
 | 784 |   EXPECT_THAT(xml_attrs[1].value, Eq("Hello World!")); | 
 | 785 | } | 
 | 786 |  | 
| Josh Hou | d3bd452 | 2022-02-14 10:24:42 +0800 | [diff] [blame] | 787 | TEST_F(LinkTest, ParseLocaleConfig) { | 
 | 788 |   StdErrDiagnostics diag; | 
 | 789 |   const std::string xml_values = | 
 | 790 |       R"(<locale-config xmlns:android="http://schemas.android.com/apk/res/android"> | 
 | 791 |             <locale android:name="pt"/> | 
 | 792 |             <locale android:name="chr"/> | 
 | 793 |             <locale android:name="chr-US"/> | 
 | 794 |             <locale android:name="zh-Hant"/> | 
 | 795 |             <locale android:name="es-419"/> | 
 | 796 |             <locale android:name="en-US"/> | 
 | 797 |             <locale android:name="zh-Hans-SG"/> | 
 | 798 |         </locale-config>)"; | 
 | 799 |  | 
 | 800 |   const std::string res = GetTestPath("test-res"); | 
 | 801 |   ASSERT_TRUE(CompileFile(GetTestPath("res/xml/locale_config.xml"), xml_values, res, &diag)); | 
 | 802 |  | 
 | 803 |   const std::string out_apk = GetTestPath("out.apk"); | 
 | 804 |   auto link_args = LinkCommandBuilder(this) | 
 | 805 |                        .SetManifestFile(ManifestBuilder(this).SetPackageName("com.test").Build()) | 
 | 806 |                        .AddCompiledResDir(res, &diag) | 
 | 807 |                        .AddFlag("--no-auto-version") | 
 | 808 |                        .Build(out_apk); | 
 | 809 |   ASSERT_TRUE(Link(link_args, &diag)); | 
 | 810 |  | 
 | 811 |   std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag); | 
 | 812 |   ASSERT_THAT(apk, Ne(nullptr)); | 
 | 813 |  | 
 | 814 |   auto xml = apk->LoadXml("res/xml/locale_config.xml", &diag); | 
 | 815 |   ASSERT_THAT(xml, NotNull()); | 
 | 816 |   EXPECT_THAT(xml->root->name, Eq("locale-config")); | 
 | 817 |   ASSERT_THAT(xml->root->children.size(), Eq(7)); | 
 | 818 |   for (auto& node : xml->root->children) { | 
 | 819 |     const xml::Element* child_el = xml::NodeCast<xml::Element>(node.get()); | 
 | 820 |     ASSERT_THAT(child_el, NotNull()); | 
 | 821 |     EXPECT_THAT(child_el->name, Eq("locale")); | 
 | 822 |  | 
 | 823 |     auto& xml_attrs = child_el->attributes; | 
 | 824 |     for (auto& attr : xml_attrs) { | 
 | 825 |       std::string locale = "b+"; | 
 | 826 |       locale += attr.value; | 
 | 827 |       std::replace(locale.begin(), locale.end(), '-', '+'); | 
 | 828 |       ConfigDescription config; | 
 | 829 |       ASSERT_TRUE(ConfigDescription::Parse(locale, &config)); | 
 | 830 |     } | 
 | 831 |   } | 
 | 832 | } | 
 | 833 |  | 
| Donald Chai | 121c6e8 | 2019-06-12 12:51:57 -0700 | [diff] [blame] | 834 | }  // namespace aapt |