• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "optimize/ResourcePathShortener.h"
18 
19 #include "ResourceTable.h"
20 #include "test/Test.h"
21 
22 using ::aapt::test::GetValue;
23 using ::testing::Not;
24 using ::testing::NotNull;
25 using ::testing::Eq;
26 
GetExtension(android::StringPiece path)27 android::StringPiece GetExtension(android::StringPiece path) {
28   auto iter = std::find(path.begin(), path.end(), '.');
29   return android::StringPiece(iter, path.end() - iter);
30 }
31 
FillTable(aapt::test::ResourceTableBuilder & builder,int start,int end)32 void FillTable(aapt::test::ResourceTableBuilder& builder, int start, int end) {
33   for (int i=start; i<end; i++) {
34     builder.AddFileReference(
35         "android:drawable/xmlfile" + std::to_string(i),
36         "res/drawable/xmlfile" + std::to_string(i) + ".xml");
37   }
38 }
39 
40 namespace aapt {
41 
TEST(ResourcePathShortenerTest,FileRefPathsChangedInResourceTable)42 TEST(ResourcePathShortenerTest, FileRefPathsChangedInResourceTable) {
43   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
44 
45   std::unique_ptr<ResourceTable> table =
46       test::ResourceTableBuilder()
47           .AddFileReference("android:drawable/xmlfile", "res/drawables/xmlfile.xml")
48           .AddFileReference("android:drawable/xmlfile2", "res/drawables/xmlfile2.xml")
49           .AddString("android:string/string", "res/should/still/be/the/same.png")
50           .Build();
51 
52   std::map<std::string, std::string> path_map;
53   ASSERT_TRUE(ResourcePathShortener(path_map).Consume(context.get(), table.get()));
54 
55   // Expect that the path map is populated
56   ASSERT_THAT(path_map.find("res/drawables/xmlfile.xml"), Not(Eq(path_map.end())));
57   ASSERT_THAT(path_map.find("res/drawables/xmlfile2.xml"), Not(Eq(path_map.end())));
58 
59   // The file paths were changed
60   EXPECT_THAT(path_map.at("res/drawables/xmlfile.xml"), Not(Eq("res/drawables/xmlfile.xml")));
61   EXPECT_THAT(path_map.at("res/drawables/xmlfile2.xml"), Not(Eq("res/drawables/xmlfile2.xml")));
62 
63   // Different file paths should remain different
64   EXPECT_THAT(path_map["res/drawables/xmlfile.xml"],
65               Not(Eq(path_map["res/drawables/xmlfile2.xml"])));
66 
67   FileReference* ref =
68       GetValue<FileReference>(table.get(), "android:drawable/xmlfile");
69   ASSERT_THAT(ref, NotNull());
70   // The map correctly points to the new location of the file
71   EXPECT_THAT(path_map["res/drawables/xmlfile.xml"], Eq(*ref->path));
72 
73   // Strings should not be affected, only file paths
74   EXPECT_THAT(
75       *GetValue<String>(table.get(), "android:string/string")->value,
76               Eq("res/should/still/be/the/same.png"));
77   EXPECT_THAT(path_map.find("res/should/still/be/the/same.png"), Eq(path_map.end()));
78 }
79 
TEST(ResourcePathShortenerTest,SkipColorFileRefPaths)80 TEST(ResourcePathShortenerTest, SkipColorFileRefPaths) {
81   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
82 
83   std::unique_ptr<ResourceTable> table =
84       test::ResourceTableBuilder()
85           .AddFileReference("android:color/colorlist", "res/color/colorlist.xml")
86           .AddFileReference("android:color/colorlist",
87                             "res/color-mdp-v21/colorlist.xml",
88                             test::ParseConfigOrDie("mdp-v21"))
89           .Build();
90 
91   std::map<std::string, std::string> path_map;
92   ASSERT_TRUE(ResourcePathShortener(path_map).Consume(context.get(), table.get()));
93 
94   // Expect that the path map to not contain the ColorStateList
95   ASSERT_THAT(path_map.find("res/color/colorlist.xml"), Eq(path_map.end()));
96   ASSERT_THAT(path_map.find("res/color-mdp-v21/colorlist.xml"), Eq(path_map.end()));
97 }
98 
TEST(ResourcePathShortenerTest,KeepExtensions)99 TEST(ResourcePathShortenerTest, KeepExtensions) {
100   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
101 
102   std::string original_xml_path = "res/drawable/xmlfile.xml";
103   std::string original_png_path = "res/drawable/pngfile.png";
104 
105   std::unique_ptr<ResourceTable> table =
106       test::ResourceTableBuilder()
107           .AddFileReference("android:color/xmlfile", original_xml_path)
108           .AddFileReference("android:color/pngfile", original_png_path)
109           .Build();
110 
111   std::map<std::string, std::string> path_map;
112   ASSERT_TRUE(ResourcePathShortener(path_map).Consume(context.get(), table.get()));
113 
114   // Expect that the path map is populated
115   ASSERT_THAT(path_map.find("res/drawable/xmlfile.xml"), Not(Eq(path_map.end())));
116   ASSERT_THAT(path_map.find("res/drawable/pngfile.png"), Not(Eq(path_map.end())));
117 
118   auto shortend_xml_path = path_map[original_xml_path];
119   auto shortend_png_path = path_map[original_png_path];
120 
121   EXPECT_THAT(GetExtension(path_map[original_xml_path]), Eq(android::StringPiece(".xml")));
122   EXPECT_THAT(GetExtension(path_map[original_png_path]), Eq(android::StringPiece(".png")));
123 }
124 
TEST(ResourcePathShortenerTest,DeterministicallyHandleCollisions)125 TEST(ResourcePathShortenerTest, DeterministicallyHandleCollisions) {
126   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
127 
128   // 4000 resources is the limit at which the hash space is expanded to 3
129   // letters to reduce collisions, we want as many collisions as possible thus
130   // N-1.
131   const auto kNumResources = 3999;
132   const auto kNumTries = 5;
133 
134   test::ResourceTableBuilder builder1;
135   FillTable(builder1, 0, kNumResources);
136   std::unique_ptr<ResourceTable> table1 = builder1.Build();
137   std::map<std::string, std::string> expected_mapping;
138   ASSERT_TRUE(ResourcePathShortener(expected_mapping).Consume(context.get(), table1.get()));
139 
140   // We are trying to ensure lack of non-determinism, it is not simple to prove
141   // a negative, thus we must try the test a few times so that the test itself
142   // is non-flaky. Basically create the pathmap 5 times from the same set of
143   // resources but a different order of addition and then ensure they are always
144   // mapped to the same short path.
145   for (int i=0; i<kNumTries; i++) {
146     test::ResourceTableBuilder builder2;
147     // This loop adds resources to the resource table in the range of
148     // [0:kNumResources).  Adding the file references in different order makes
149     // non-determinism more likely to surface. Thus we add resources
150     // [start_index:kNumResources) first then [0:start_index). We also use a
151     // different start_index each run.
152     int start_index = (kNumResources/kNumTries)*i;
153     FillTable(builder2, start_index, kNumResources);
154     FillTable(builder2, 0, start_index);
155     std::unique_ptr<ResourceTable> table2 = builder2.Build();
156 
157     std::map<std::string, std::string> actual_mapping;
158     ASSERT_TRUE(ResourcePathShortener(actual_mapping).Consume(context.get(), table2.get()));
159 
160     for (auto& item : actual_mapping) {
161       ASSERT_THAT(expected_mapping[item.first], Eq(item.second));
162     }
163   }
164 }
165 
166 }   // namespace aapt
167