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 "Convert.h"
18
19 #include "LoadedApk.h"
20 #include "test/Common.h"
21 #include "test/Test.h"
22 #include "ziparchive/zip_archive.h"
23
24 using testing::AnyOfArray;
25 using testing::Eq;
26 using testing::Ne;
27 using testing::Not;
28 using testing::SizeIs;
29
30 namespace aapt {
31 using namespace aapt::test;
32
33 using ConvertTest = CommandTestFixture;
34
TEST_F(ConvertTest,RemoveRawXmlStrings)35 TEST_F(ConvertTest, 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 "--keep-raw-values",
46 "--proto-format"
47 };
48
49 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
50
51 const std::string out_convert_apk = GetTestPath("out_convert.apk");
52 std::vector<android::StringPiece> convert_args = {
53 "-o", out_convert_apk,
54 "--output-format", "binary",
55 out_apk,
56 };
57 ASSERT_THAT(ConvertCommand().Execute(convert_args, &std::cerr), Eq(0));
58
59 // Load the binary xml tree
60 android::ResXMLTree tree;
61 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_convert_apk, &diag);
62
63 std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
64 ASSERT_THAT(data, Ne(nullptr));
65
66 AssertLoadXml(apk.get(), data.get(), &tree);
67
68 // Check that the raw string index has not been assigned
69 EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1));
70 }
71
TEST_F(ConvertTest,KeepRawXmlStrings)72 TEST_F(ConvertTest, KeepRawXmlStrings) {
73 StdErrDiagnostics diag;
74 const std::string compiled_files_dir = GetTestPath("compiled");
75 ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)",
76 compiled_files_dir, &diag));
77
78 const std::string out_apk = GetTestPath("out.apk");
79 std::vector<std::string> link_args = {
80 "--manifest", GetDefaultManifest(),
81 "-o", out_apk,
82 "--keep-raw-values",
83 "--proto-format"
84 };
85
86 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
87
88 const std::string out_convert_apk = GetTestPath("out_convert.apk");
89 std::vector<android::StringPiece> convert_args = {
90 "-o", out_convert_apk,
91 "--output-format", "binary",
92 "--keep-raw-values",
93 out_apk,
94 };
95 ASSERT_THAT(ConvertCommand().Execute(convert_args, &std::cerr), Eq(0));
96
97 // Load the binary xml tree
98 android::ResXMLTree tree;
99 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_convert_apk, &diag);
100
101 std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
102 ASSERT_THAT(data, Ne(nullptr));
103
104 AssertLoadXml(apk.get(), data.get(), &tree);
105
106 // Check that the raw string index has been set to the correct string pool entry
107 int32_t raw_index = tree.getAttributeValueStringID(0);
108 ASSERT_THAT(raw_index, Ne(-1));
109 EXPECT_THAT(android::util::GetString(tree.getStrings(), static_cast<size_t>(raw_index)),
110 Eq("007"));
111 }
112
TEST_F(ConvertTest,DuplicateEntriesWrittenOnce)113 TEST_F(ConvertTest, DuplicateEntriesWrittenOnce) {
114 StdErrDiagnostics diag;
115 const std::string apk_path =
116 file::BuildPath({android::base::GetExecutableDirectory(),
117 "integration-tests", "ConvertTest", "duplicate_entries.apk"});
118
119 const std::string out_convert_apk = GetTestPath("out_convert.apk");
120 std::vector<android::StringPiece> convert_args = {
121 "-o", out_convert_apk,
122 "--output-format", "proto",
123 apk_path
124 };
125 ASSERT_THAT(ConvertCommand().Execute(convert_args, &std::cerr), Eq(0));
126
127 ZipArchiveHandle handle;
128 ASSERT_THAT(OpenArchive(out_convert_apk.c_str(), &handle), Eq(0));
129
130 void* cookie = nullptr;
131
132 int32_t result = StartIteration(handle, &cookie, "res/theme/10", "");
133
134 // If this is -5, that means we've found a duplicate entry and this test has failed
135 EXPECT_THAT(result, Eq(0));
136
137 // But if read succeeds, verify only one res/theme/10 entry
138 int count = 0;
139
140 // Can't pass nullptrs into Next()
141 std::string zip_name;
142 ZipEntry zip_data;
143
144 while ((result = Next(cookie, &zip_data, &zip_name)) == 0) {
145 count++;
146 }
147
148 EndIteration(cookie);
149
150 EXPECT_THAT(count, Eq(1));
151 }
152
TEST_F(ConvertTest,ConvertWithResourceNameCollapsing)153 TEST_F(ConvertTest, ConvertWithResourceNameCollapsing) {
154 StdErrDiagnostics diag;
155 const std::string compiled_files_dir = GetTestPath("compiled");
156 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
157 R"(<resources>
158 <string name="first">string</string>
159 <string name="second">string</string>
160 <string name="third">another string</string>
161
162 <bool name="bool1">true</bool>
163 <bool name="bool2">true</bool>
164 <bool name="bool3">true</bool>
165
166 <integer name="int1">10</integer>
167 <integer name="int2">10</integer>
168 </resources>)",
169 compiled_files_dir, &diag));
170 std::string resource_config_path = GetTestPath("resource-config");
171 WriteFile(resource_config_path, "integer/int1#no_collapse\ninteger/int2#no_collapse");
172
173 const std::string proto_apk = GetTestPath("proto.apk");
174 std::vector<std::string> link_args = {
175 "--proto-format", "--manifest", GetDefaultManifest(kDefaultPackageName), "-o", proto_apk,
176 };
177 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
178
179 const std::string binary_apk = GetTestPath("binary.apk");
180 std::vector<android::StringPiece> convert_args = {"-o",
181 binary_apk,
182 "--output-format",
183 "binary",
184 "--collapse-resource-names",
185 "--deduplicate-entry-values",
186 "--resources-config-path",
187 resource_config_path,
188 proto_apk};
189 ASSERT_THAT(ConvertCommand().Execute(convert_args, &std::cerr), Eq(0));
190
191 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(binary_apk, &diag);
192 for (const auto& package : apk->GetResourceTable()->packages) {
193 for (const auto& type : package->types) {
194 switch (type->named_type.type) {
195 case ResourceType::kBool:
196 EXPECT_THAT(type->entries, SizeIs(3));
197 for (const auto& entry : type->entries) {
198 auto value = ValueCast<BinaryPrimitive>(entry->FindValue({})->value.get())->value;
199 EXPECT_THAT(value.data, Eq(0xffffffffu));
200 }
201 break;
202 case ResourceType::kString:
203 EXPECT_THAT(type->entries, SizeIs(3));
204 for (const auto& entry : type->entries) {
205 auto value = ValueCast<String>(entry->FindValue({})->value.get())->value;
206 EXPECT_THAT(entry->name, Not(AnyOfArray({"first", "second", "third"})));
207 EXPECT_THAT(*value, AnyOfArray({"string", "another string"}));
208 }
209 break;
210 case ResourceType::kInteger:
211 EXPECT_THAT(type->entries, SizeIs(2));
212 for (const auto& entry : type->entries) {
213 auto value = ValueCast<BinaryPrimitive>(entry->FindValue({})->value.get())->value;
214 EXPECT_THAT(entry->name, AnyOfArray({"int1", "int2"}));
215 EXPECT_THAT(value.data, Eq(10));
216 }
217 break;
218 default:
219 break;
220 }
221 }
222 }
223 }
224
225 } // namespace aapt
226