Home | History | Annotate | Download | only in compile
      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 ::testing::AssertionResult VerifyIds(ResourceTable* table);
     24 
     25 TEST(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 
     40 TEST(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 
     94 TEST(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 
    109 TEST(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