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
21namespace aapt {
22
23::testing::AssertionResult VerifyIds(ResourceTable* table);
24
25TEST(IdAssignerTest, AssignIds) {
26  std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
27                                             .AddSimple("android:attr/foo")
28                                             .AddSimple("android:attr/bar")
29                                             .AddSimple("android:id/foo")
30                                             .SetPackageId("android", 0x01)
31                                             .Build();
32
33  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
34  IdAssigner assigner;
35
36  ASSERT_TRUE(assigner.Consume(context.get(), table.get()));
37  ASSERT_TRUE(VerifyIds(table.get()));
38}
39
40TEST(IdAssignerTest, AssignIdsWithReservedIds) {
41  std::unique_ptr<ResourceTable> table =
42      test::ResourceTableBuilder()
43          .AddSimple("android:id/foo", ResourceId(0x01010000))
44          .AddSimple("android:dimen/two")
45          .AddSimple("android:integer/three")
46          .AddSimple("android:string/five")
47          .AddSimple("android:attr/fun", ResourceId(0x01040000))
48          .AddSimple("android:attr/foo", ResourceId(0x01040006))
49          .AddSimple("android:attr/bar")
50          .AddSimple("android:attr/baz")
51          .AddSimple("app:id/biz")
52          .SetPackageId("android", 0x01)
53          .SetPackageId("app", 0x7f)
54          .Build();
55
56  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
57  IdAssigner assigner;
58
59  ASSERT_TRUE(assigner.Consume(context.get(), table.get()));
60  ASSERT_TRUE(VerifyIds(table.get()));
61
62  Maybe<ResourceTable::SearchResult> maybe_result;
63
64  // Expect to fill in the gaps between 0x0101XXXX and 0x0104XXXX.
65
66  maybe_result = table->FindResource(test::ParseNameOrDie("android:dimen/two"));
67  AAPT_ASSERT_TRUE(maybe_result);
68  EXPECT_EQ(make_value<uint8_t>(2), maybe_result.value().type->id);
69
70  maybe_result =
71      table->FindResource(test::ParseNameOrDie("android:integer/three"));
72  AAPT_ASSERT_TRUE(maybe_result);
73  EXPECT_EQ(make_value<uint8_t>(3), maybe_result.value().type->id);
74
75  // Expect to bypass the reserved 0x0104XXXX IDs and use the next 0x0105XXXX
76  // IDs.
77
78  maybe_result =
79      table->FindResource(test::ParseNameOrDie("android:string/five"));
80  AAPT_ASSERT_TRUE(maybe_result);
81  EXPECT_EQ(make_value<uint8_t>(5), maybe_result.value().type->id);
82
83  // Expect to fill in the gaps between 0x01040000 and 0x01040006.
84
85  maybe_result = table->FindResource(test::ParseNameOrDie("android:attr/bar"));
86  AAPT_ASSERT_TRUE(maybe_result);
87  EXPECT_EQ(make_value<uint16_t>(1), maybe_result.value().entry->id);
88
89  maybe_result = table->FindResource(test::ParseNameOrDie("android:attr/baz"));
90  AAPT_ASSERT_TRUE(maybe_result);
91  EXPECT_EQ(make_value<uint16_t>(2), maybe_result.value().entry->id);
92}
93
94TEST(IdAssignerTest, FailWhenNonUniqueIdsAssigned) {
95  std::unique_ptr<ResourceTable> table =
96      test::ResourceTableBuilder()
97          .AddSimple("android:attr/foo", ResourceId(0x01040006))
98          .AddSimple("android:attr/bar", ResourceId(0x01040006))
99          .SetPackageId("android", 0x01)
100          .SetPackageId("app", 0x7f)
101          .Build();
102
103  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
104  IdAssigner assigner;
105
106  ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
107}
108
109TEST(IdAssignerTest, AssignIdsWithIdMap) {
110  std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
111                                             .AddSimple("android:attr/foo")
112                                             .AddSimple("android:attr/bar")
113                                             .SetPackageId("android", 0x01)
114                                             .Build();
115
116  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
117  std::unordered_map<ResourceName, ResourceId> id_map = {
118      {test::ParseNameOrDie("android:attr/foo"), ResourceId(0x01010002)}};
119  IdAssigner assigner(&id_map);
120  ASSERT_TRUE(assigner.Consume(context.get(), table.get()));
121  ASSERT_TRUE(VerifyIds(table.get()));
122  Maybe<ResourceTable::SearchResult> result =
123      table->FindResource(test::ParseNameOrDie("android:attr/foo"));
124  AAPT_ASSERT_TRUE(result);
125
126  const ResourceTable::SearchResult& search_result = result.value();
127  EXPECT_EQ(make_value<uint8_t>(0x01), search_result.package->id);
128  EXPECT_EQ(make_value<uint8_t>(0x01), search_result.type->id);
129  EXPECT_EQ(make_value<uint16_t>(0x0002), search_result.entry->id);
130}
131
132::testing::AssertionResult VerifyIds(ResourceTable* table) {
133  std::set<uint8_t> package_ids;
134  for (auto& package : table->packages) {
135    if (!package->id) {
136      return ::testing::AssertionFailure() << "package " << package->name
137                                           << " has no ID";
138    }
139
140    if (!package_ids.insert(package->id.value()).second) {
141      return ::testing::AssertionFailure()
142             << "package " << package->name << " has non-unique ID " << std::hex
143             << (int)package->id.value() << std::dec;
144    }
145  }
146
147  for (auto& package : table->packages) {
148    std::set<uint8_t> type_ids;
149    for (auto& type : package->types) {
150      if (!type->id) {
151        return ::testing::AssertionFailure() << "type " << type->type
152                                             << " of package " << package->name
153                                             << " has no ID";
154      }
155
156      if (!type_ids.insert(type->id.value()).second) {
157        return ::testing::AssertionFailure()
158               << "type " << type->type << " of package " << package->name
159               << " has non-unique ID " << std::hex << (int)type->id.value()
160               << std::dec;
161      }
162    }
163
164    for (auto& type : package->types) {
165      std::set<uint16_t> entry_ids;
166      for (auto& entry : type->entries) {
167        if (!entry->id) {
168          return ::testing::AssertionFailure()
169                 << "entry " << entry->name << " of type " << type->type
170                 << " of package " << package->name << " has no ID";
171        }
172
173        if (!entry_ids.insert(entry->id.value()).second) {
174          return ::testing::AssertionFailure()
175                 << "entry " << entry->name << " of type " << type->type
176                 << " of package " << package->name << " has non-unique ID "
177                 << std::hex << (int)entry->id.value() << std::dec;
178        }
179      }
180    }
181  }
182  return ::testing::AssertionSuccess() << "all IDs are unique and assigned";
183}
184
185}  // namespace aapt
186