1 /*
2 * Copyright (C) 2015 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/IdAssigner.h"
18
19 #include "test/Test.h"
20
21 namespace aapt {
22
23 struct IdAssignerTests : public ::testing::Test {
SetUpaapt::IdAssignerTests24 void SetUp() override {
25 context = test::ContextBuilder().SetCompilationPackage("android").SetPackageId(0x01).Build();
26 }
27 std::unique_ptr<IAaptContext> context;
28 };
29
30 ::testing::AssertionResult VerifyIds(ResourceTable* table);
31
TEST_F(IdAssignerTests,AssignIds)32 TEST_F(IdAssignerTests, AssignIds) {
33 auto table = test::ResourceTableBuilder()
34 .AddSimple("android:attr/foo")
35 .AddSimple("android:attr/bar")
36 .AddSimple("android:id/foo")
37 .Build();
38 IdAssigner assigner;
39
40 ASSERT_TRUE(assigner.Consume(context.get(), table.get()));
41 ASSERT_TRUE(VerifyIds(table.get()));
42 }
43
TEST_F(IdAssignerTests,AssignIdsWithReservedIds)44 TEST_F(IdAssignerTests, AssignIdsWithReservedIds) {
45 auto table = test::ResourceTableBuilder()
46 .AddSimple("android:id/foo", ResourceId(0x01010000))
47 .AddSimple("android:dimen/two")
48 .AddSimple("android:integer/three")
49 .AddSimple("android:string/five")
50 .AddSimple("android:attr/fun", ResourceId(0x01040000))
51 .AddSimple("android:attr/foo", ResourceId(0x01040006))
52 .AddSimple("android:attr/bar")
53 .AddSimple("android:attr/baz")
54 .Build();
55
56 IdAssigner assigner;
57 ASSERT_TRUE(assigner.Consume(context.get(), table.get()));
58 ASSERT_TRUE(VerifyIds(table.get()));
59
60 std::optional<ResourceTable::SearchResult> maybe_result;
61
62 // Expect to fill in the gaps between 0x0101XXXX and 0x0104XXXX.
63
64 maybe_result = table->FindResource(test::ParseNameOrDie("android:dimen/two"));
65 ASSERT_TRUE(maybe_result);
66 EXPECT_EQ(0x01020000, maybe_result.value().entry->id);
67
68 maybe_result =
69 table->FindResource(test::ParseNameOrDie("android:integer/three"));
70 ASSERT_TRUE(maybe_result);
71 EXPECT_EQ(0x01030000, maybe_result.value().entry->id);
72
73 // Expect to bypass the reserved 0x0104XXXX IDs and use the next 0x0105XXXX
74 // IDs.
75
76 maybe_result =
77 table->FindResource(test::ParseNameOrDie("android:string/five"));
78 ASSERT_TRUE(maybe_result);
79 EXPECT_EQ(0x01050000, maybe_result.value().entry->id);
80
81 // Expect to fill in the gaps between 0x01040000 and 0x01040006.
82
83 maybe_result = table->FindResource(test::ParseNameOrDie("android:attr/bar"));
84 ASSERT_TRUE(maybe_result);
85 EXPECT_EQ(0x01040001, maybe_result.value().entry->id);
86
87 maybe_result = table->FindResource(test::ParseNameOrDie("android:attr/baz"));
88 ASSERT_TRUE(maybe_result);
89 EXPECT_EQ(0x01040002, maybe_result.value().entry->id);
90 }
91
TEST_F(IdAssignerTests,FailWhenNonUniqueIdsAssigned)92 TEST_F(IdAssignerTests, FailWhenNonUniqueIdsAssigned) {
93 auto table = test::ResourceTableBuilder()
94 .AddSimple("android:attr/foo", ResourceId(0x01040006))
95 .AddSimple("android:attr/bar", ResourceId(0x01040006))
96 .Build();
97 IdAssigner assigner;
98 ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
99 }
100
TEST_F(IdAssignerTests,FailWhenNonUniqueTypeIdsAssigned)101 TEST_F(IdAssignerTests, FailWhenNonUniqueTypeIdsAssigned) {
102 auto table = test::ResourceTableBuilder()
103 .AddSimple("android:string/foo", ResourceId(0x01040000))
104 .AddSimple("android:attr/bar", ResourceId(0x01040006))
105 .Build();
106 IdAssigner assigner;
107 ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
108 }
109
TEST_F(IdAssignerTests,FailWhenTypeHasTwoNonStagedIds)110 TEST_F(IdAssignerTests, FailWhenTypeHasTwoNonStagedIds) {
111 auto table = test::ResourceTableBuilder()
112 .AddSimple("android:attr/foo", ResourceId(0x01050000))
113 .AddSimple("android:attr/bar", ResourceId(0x01040006))
114 .Build();
115 IdAssigner assigner;
116 ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
117 }
118
TEST_F(IdAssignerTests,FailWhenTypeHasTwoNonStagedIdsRegardlessOfStagedId)119 TEST_F(IdAssignerTests, FailWhenTypeHasTwoNonStagedIdsRegardlessOfStagedId) {
120 auto table =
121 test::ResourceTableBuilder()
122 .AddSimple("android:attr/foo", ResourceId(0x01050000))
123 .AddSimple("android:attr/bar", ResourceId(0x01ff0006))
124 .Add(NewResourceBuilder("android:attr/staged_baz")
125 .SetId(0x01ff0000)
126 .SetVisibility({.staged_api = true, .level = Visibility::Level::kPublic})
127 .Build())
128 .Build();
129 IdAssigner assigner;
130 ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
131 }
132
TEST_F(IdAssignerTests,FailWhenTypeHaveBothStagedAndNonStagedIds)133 TEST_F(IdAssignerTests, FailWhenTypeHaveBothStagedAndNonStagedIds) {
134 auto table =
135 test::ResourceTableBuilder()
136 .AddSimple("android:attr/foo", ResourceId(0x01010000))
137 .Add(NewResourceBuilder("android:bool/staged_baz")
138 .SetId(0x01010001)
139 .SetVisibility({.staged_api = true, .level = Visibility::Level::kPublic})
140 .Build())
141 .Build();
142 IdAssigner assigner;
143 ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
144 }
145
TEST_F(IdAssignerTests,AssignIdsWithIdMap)146 TEST_F(IdAssignerTests, AssignIdsWithIdMap) {
147 auto table = test::ResourceTableBuilder()
148 .AddSimple("android:attr/foo")
149 .AddSimple("android:attr/bar")
150 .Build();
151 std::unordered_map<ResourceName, ResourceId> id_map = {
152 {test::ParseNameOrDie("android:attr/foo"), ResourceId(0x01010002)}};
153 IdAssigner assigner(&id_map);
154 ASSERT_TRUE(assigner.Consume(context.get(), table.get()));
155 ASSERT_TRUE(VerifyIds(table.get()));
156 auto result = table->FindResource(test::ParseNameOrDie("android:attr/foo"));
157 ASSERT_TRUE(result);
158
159 const ResourceTable::SearchResult& search_result = result.value();
160 EXPECT_EQ(0x01010002, search_result.entry->id);
161 }
162
TEST_F(IdAssignerTests,UseAllEntryIds)163 TEST_F(IdAssignerTests, UseAllEntryIds) {
164 ResourceTable table;
165 const size_t max_entry_id = std::numeric_limits<uint16_t>::max();
166 for (size_t i = 0; i <= max_entry_id; i++) {
167 ASSERT_TRUE(
168 table.AddResource(NewResourceBuilder("android:attr/res" + std::to_string(i)).Build(),
169 context->GetDiagnostics()));
170 }
171 IdAssigner assigner;
172 ASSERT_TRUE(assigner.Consume(context.get(), &table));
173 }
174
TEST_F(IdAssignerTests,ExaustEntryIds)175 TEST_F(IdAssignerTests, ExaustEntryIds) {
176 ResourceTable table;
177 const size_t max_entry_id = std::numeric_limits<uint16_t>::max() + 1u;
178 for (size_t i = 0; i <= max_entry_id; i++) {
179 ASSERT_TRUE(
180 table.AddResource(NewResourceBuilder("android:attr/res" + std::to_string(i)).Build(),
181 context->GetDiagnostics()));
182 }
183 IdAssigner assigner;
184 ASSERT_FALSE(assigner.Consume(context.get(), &table));
185 }
186
TEST_F(IdAssignerTests,ExaustEntryIdsLastIdIsPublic)187 TEST_F(IdAssignerTests, ExaustEntryIdsLastIdIsPublic) {
188 ResourceTable table;
189 ASSERT_TRUE(table.AddResource(NewResourceBuilder("android:attr/res").SetId(0x0101ffff).Build(),
190 context->GetDiagnostics()));
191 const size_t max_entry_id = std::numeric_limits<uint16_t>::max();
192 for (size_t i = 0; i <= max_entry_id; i++) {
193 ASSERT_TRUE(
194 table.AddResource(NewResourceBuilder("android:attr/res" + std::to_string(i)).Build(),
195 context->GetDiagnostics()));
196 }
197 IdAssigner assigner;
198 ASSERT_FALSE(assigner.Consume(context.get(), &table));
199 }
200
VerifyIds(ResourceTable * table)201 ::testing::AssertionResult VerifyIds(ResourceTable* table) {
202 std::set<ResourceId> seen_ids;
203 for (auto& package : table->packages) {
204 for (auto& type : package->types) {
205 for (auto& entry : type->entries) {
206 if (!entry->id) {
207 return ::testing::AssertionFailure()
208 << "resource " << ResourceNameRef(package->name, type->named_type, entry->name)
209 << " has no ID";
210 }
211 if (!seen_ids.insert(entry->id.value()).second) {
212 return ::testing::AssertionFailure()
213 << "resource " << ResourceNameRef(package->name, type->named_type, entry->name)
214 << " has a non-unique ID" << std::hex << entry->id.value() << std::dec;
215 }
216 }
217 }
218 }
219
220 return ::testing::AssertionSuccess() << "all IDs are unique and assigned";
221 }
222
223 } // namespace aapt
224