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 "Linker.h"
18 #include "ResourceTable.h"
19 #include "ResourceTableResolver.h"
20 #include "ResourceValues.h"
21 #include "Util.h"
22 
23 #include <androidfw/AssetManager.h>
24 #include <gtest/gtest.h>
25 #include <string>
26 
27 namespace aapt {
28 
29 struct LinkerTest : public ::testing::Test {
SetUpaapt::LinkerTest30     virtual void SetUp() override {
31         mTable = std::make_shared<ResourceTable>();
32         mTable->setPackage(u"android");
33         mTable->setPackageId(0x01);
34         mLinker = std::make_shared<Linker>(mTable, std::make_shared<ResourceTableResolver>(
35                 mTable, std::vector<std::shared_ptr<const android::AssetManager>>()),
36                 Linker::Options{});
37 
38         // Create a few attributes for use in the tests.
39 
40         addResource(ResourceName{ {}, ResourceType::kAttr, u"integer" },
41                     util::make_unique<Attribute>(false, android::ResTable_map::TYPE_INTEGER));
42 
43         addResource(ResourceName{ {}, ResourceType::kAttr, u"string" },
44                     util::make_unique<Attribute>(false, android::ResTable_map::TYPE_STRING));
45 
46         addResource(ResourceName{ {}, ResourceType::kId, u"apple" }, util::make_unique<Id>());
47 
48         addResource(ResourceName{ {}, ResourceType::kId, u"banana" }, util::make_unique<Id>());
49 
50         std::unique_ptr<Attribute> flagAttr = util::make_unique<Attribute>(
51                 false, android::ResTable_map::TYPE_FLAGS);
52         flagAttr->symbols.push_back(Attribute::Symbol{
53                 ResourceNameRef{ u"android", ResourceType::kId, u"apple" }, 1 });
54         flagAttr->symbols.push_back(Attribute::Symbol{
55                 ResourceNameRef{ u"android", ResourceType::kId, u"banana" }, 2 });
56         addResource(ResourceName{ {}, ResourceType::kAttr, u"flags" }, std::move(flagAttr));
57     }
58 
59     /*
60      * Convenience method for adding resources with the default configuration and some
61      * bogus source line.
62      */
addResourceaapt::LinkerTest63     bool addResource(const ResourceNameRef& name, std::unique_ptr<Value> value) {
64         return mTable->addResource(name, {}, SourceLine{ "test.xml", 21 }, std::move(value));
65     }
66 
67     std::shared_ptr<ResourceTable> mTable;
68     std::shared_ptr<Linker> mLinker;
69 };
70 
TEST_F(LinkerTest,DoNotInterpretEscapedStringAsReference)71 TEST_F(LinkerTest, DoNotInterpretEscapedStringAsReference) {
72     ASSERT_TRUE(addResource(ResourceName{ u"android", ResourceType::kString, u"foo" },
73                 util::make_unique<String>(mTable->getValueStringPool().makeRef(u"?123"))));
74 
75     ASSERT_TRUE(mLinker->linkAndValidate());
76     EXPECT_TRUE(mLinker->getUnresolvedReferences().empty());
77 }
78 
TEST_F(LinkerTest,EscapeAndConvertRawString)79 TEST_F(LinkerTest, EscapeAndConvertRawString) {
80     std::unique_ptr<Style> style = util::make_unique<Style>();
81     style->entries.push_back(Style::Entry{
82             ResourceNameRef{ u"android", ResourceType::kAttr, u"integer" },
83             util::make_unique<RawString>(mTable->getValueStringPool().makeRef(u"  123"))
84     });
85     const Style* result = style.get();
86     ASSERT_TRUE(addResource(ResourceName{ u"android", ResourceType::kStyle, u"foo" },
87                 std::move(style)));
88 
89     ASSERT_TRUE(mLinker->linkAndValidate());
90     EXPECT_TRUE(mLinker->getUnresolvedReferences().empty());
91 
92     EXPECT_NE(nullptr, dynamic_cast<BinaryPrimitive*>(result->entries.front().value.get()));
93 }
94 
TEST_F(LinkerTest,FailToConvertRawString)95 TEST_F(LinkerTest, FailToConvertRawString) {
96     std::unique_ptr<Style> style = util::make_unique<Style>();
97     style->entries.push_back(Style::Entry{
98             ResourceNameRef{ u"android", ResourceType::kAttr, u"integer" },
99             util::make_unique<RawString>(mTable->getValueStringPool().makeRef(u"yo what is up?"))
100     });
101     ASSERT_TRUE(addResource(ResourceName{ u"android", ResourceType::kStyle, u"foo" },
102                 std::move(style)));
103 
104     ASSERT_FALSE(mLinker->linkAndValidate());
105 }
106 
TEST_F(LinkerTest,ConvertRawStringToString)107 TEST_F(LinkerTest, ConvertRawStringToString) {
108     std::unique_ptr<Style> style = util::make_unique<Style>();
109     style->entries.push_back(Style::Entry{
110             ResourceNameRef{ u"android", ResourceType::kAttr, u"string" },
111             util::make_unique<RawString>(
112                     mTable->getValueStringPool().makeRef(u"  \"this  is  \\u00fa\"."))
113     });
114     const Style* result = style.get();
115     ASSERT_TRUE(addResource(ResourceName{ u"android", ResourceType::kStyle, u"foo" },
116                 std::move(style)));
117 
118     ASSERT_TRUE(mLinker->linkAndValidate());
119     EXPECT_TRUE(mLinker->getUnresolvedReferences().empty());
120 
121     const String* str = dynamic_cast<const String*>(result->entries.front().value.get());
122     ASSERT_NE(nullptr, str);
123     EXPECT_EQ(*str->value, u"this  is  \u00fa.");
124 }
125 
TEST_F(LinkerTest,ConvertRawStringToFlags)126 TEST_F(LinkerTest, ConvertRawStringToFlags) {
127     std::unique_ptr<Style> style = util::make_unique<Style>();
128     style->entries.push_back(Style::Entry{
129             ResourceNameRef{ u"android", ResourceType::kAttr, u"flags" },
130             util::make_unique<RawString>(mTable->getValueStringPool().makeRef(u"banana | apple"))
131     });
132     const Style* result = style.get();
133     ASSERT_TRUE(addResource(ResourceName{ u"android", ResourceType::kStyle, u"foo" },
134                 std::move(style)));
135 
136     ASSERT_TRUE(mLinker->linkAndValidate());
137     EXPECT_TRUE(mLinker->getUnresolvedReferences().empty());
138 
139     const BinaryPrimitive* bin = dynamic_cast<const BinaryPrimitive*>(
140             result->entries.front().value.get());
141     ASSERT_NE(nullptr, bin);
142     EXPECT_EQ(bin->value.data, 1u | 2u);
143 }
144 
TEST_F(LinkerTest,AllowReferenceWithOnlyResourceIdPointingToDifferentPackage)145 TEST_F(LinkerTest, AllowReferenceWithOnlyResourceIdPointingToDifferentPackage) {
146     ASSERT_TRUE(addResource(ResourceName{ u"android", ResourceType::kInteger, u"foo" },
147                 util::make_unique<Reference>(ResourceId{ 0x02, 0x01, 0x01 })));
148 
149     ASSERT_TRUE(mLinker->linkAndValidate());
150     EXPECT_TRUE(mLinker->getUnresolvedReferences().empty());
151 }
152 
153 } // namespace aapt
154