1 /*
2 * Copyright (C) 2019 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 <string>
18 #include <tuple>
19
20 #include <android-base/macros.h>
21 #include <gtest/gtest.h>
22
23 #include "apex_database.h"
24
25 namespace android {
26 namespace apex {
27 namespace {
28
29 using MountedApexData = MountedApexDatabase::MountedApexData;
30
TEST(MountedApexDataTest,LinearOrder)31 TEST(MountedApexDataTest, LinearOrder) {
32 constexpr const char* kLoopName[] = {"loop1", "loop2", "loop3"};
33 constexpr const char* kPath[] = {"path1", "path2", "path3"};
34 constexpr const char* kMount[] = {"mount1", "mount2", "mount3"};
35 constexpr const char* kDm[] = {"dm1", "dm2", "dm3"};
36 constexpr const char* kHashtreeLoopName[] = {"hash-loop1", "hash-loop2",
37 "hash-loop3"};
38 // NOLINTNEXTLINE(bugprone-sizeof-expression)
39 constexpr size_t kCount = arraysize(kLoopName) * arraysize(kPath) *
40 arraysize(kMount) * arraysize(kDm);
41
42 auto index_fn = [&](size_t i) {
43 const size_t loop_index = i % arraysize(kLoopName);
44 const size_t loop_rest = i / arraysize(kLoopName);
45 const size_t path_index = loop_rest % arraysize(kPath);
46 const size_t path_rest = loop_rest / arraysize(kPath);
47 const size_t mount_index = path_rest % arraysize(kMount);
48 const size_t mount_rest = path_rest / arraysize(kMount);
49 const size_t dm_index = mount_rest % arraysize(kDm);
50 const size_t dm_rest = mount_rest / arraysize(kHashtreeLoopName);
51 const size_t hashtree_loop_index = dm_rest % arraysize(kHashtreeLoopName);
52 CHECK_EQ(dm_rest / arraysize(kHashtreeLoopName), 0u);
53 return std::make_tuple(loop_index, path_index, mount_index, dm_index,
54 hashtree_loop_index);
55 };
56
57 MountedApexData data[kCount];
58 for (size_t i = 0; i < kCount; ++i) {
59 size_t loop_idx, path_idx, mount_idx, dm_idx, hash_loop_idx;
60 std::tie(loop_idx, path_idx, mount_idx, dm_idx, hash_loop_idx) =
61 index_fn(i);
62 data[i] =
63 MountedApexData(kLoopName[loop_idx], kPath[path_idx], kMount[mount_idx],
64 kDm[dm_idx], kHashtreeLoopName[hash_loop_idx]);
65 }
66
67 for (size_t i = 0; i < kCount; ++i) {
68 size_t loop_idx_i, path_idx_i, mount_idx_i, dm_idx_i, hash_loop_idx_i;
69 std::tie(loop_idx_i, path_idx_i, mount_idx_i, dm_idx_i, hash_loop_idx_i) =
70 index_fn(i);
71 for (size_t j = i; j < kCount; ++j) {
72 size_t loop_idx_j, path_idx_j, mount_idx_j, dm_idx_j, hash_loop_idx_j;
73 std::tie(loop_idx_j, path_idx_j, mount_idx_j, dm_idx_j, hash_loop_idx_j) =
74 index_fn(j);
75 if (loop_idx_i != loop_idx_j) {
76 EXPECT_EQ(loop_idx_i < loop_idx_j, data[i] < data[j]);
77 continue;
78 }
79 if (path_idx_i != path_idx_j) {
80 EXPECT_EQ(path_idx_i < path_idx_j, data[i] < data[j]);
81 continue;
82 }
83 if (mount_idx_i != mount_idx_j) {
84 EXPECT_EQ(mount_idx_i < mount_idx_j, data[i] < data[j]);
85 continue;
86 }
87 if (dm_idx_i != dm_idx_j) {
88 EXPECT_EQ(dm_idx_i < dm_idx_j, data[i] < data[j]);
89 continue;
90 }
91 EXPECT_EQ(hash_loop_idx_i < hash_loop_idx_j, data[i] < data[j]);
92 }
93 }
94 }
95
CountPackages(const MountedApexDatabase & db)96 size_t CountPackages(const MountedApexDatabase& db) {
97 size_t ret = 0;
98 db.ForallMountedApexes([&ret](const std::string& a ATTRIBUTE_UNUSED,
99 const MountedApexData& b ATTRIBUTE_UNUSED,
100 bool c ATTRIBUTE_UNUSED) { ++ret; });
101 return ret;
102 }
103
Contains(const MountedApexDatabase & db,const std::string & package,const std::string & loop_name,const std::string & full_path,const std::string & mount_point,const std::string & device_name,const std::string & hashtree_loop_name)104 bool Contains(const MountedApexDatabase& db, const std::string& package,
105 const std::string& loop_name, const std::string& full_path,
106 const std::string& mount_point, const std::string& device_name,
107 const std::string& hashtree_loop_name) {
108 bool found = false;
109 db.ForallMountedApexes([&](const std::string& p, const MountedApexData& d,
110 bool b ATTRIBUTE_UNUSED) {
111 if (package == p && loop_name == d.loop_name && full_path == d.full_path &&
112 mount_point == d.mount_point && device_name == d.device_name &&
113 hashtree_loop_name == d.hashtree_loop_name) {
114 found = true;
115 }
116 });
117 return found;
118 }
119
ContainsPackage(const MountedApexDatabase & db,const std::string & package,const std::string & loop_name,const std::string & full_path,const std::string & dm,const std::string & hashtree_loop_name)120 bool ContainsPackage(const MountedApexDatabase& db, const std::string& package,
121 const std::string& loop_name, const std::string& full_path,
122 const std::string& dm,
123 const std::string& hashtree_loop_name) {
124 bool found = false;
125 db.ForallMountedApexes(
126 package, [&](const MountedApexData& d, bool b ATTRIBUTE_UNUSED) {
127 if (loop_name == d.loop_name && full_path == d.full_path &&
128 dm == d.device_name && hashtree_loop_name == d.hashtree_loop_name) {
129 found = true;
130 }
131 });
132 return found;
133 }
134
TEST(ApexDatabaseTest,AddRemovedMountedApex)135 TEST(ApexDatabaseTest, AddRemovedMountedApex) {
136 constexpr const char* kPackage = "package";
137 constexpr const char* kLoopName = "loop";
138 constexpr const char* kPath = "path";
139 constexpr const char* kMountPoint = "mount";
140 constexpr const char* kDeviceName = "dev";
141 constexpr const char* kHashtreeLoopName = "hash-loop";
142
143 MountedApexDatabase db;
144 ASSERT_EQ(CountPackages(db), 0u);
145
146 db.AddMountedApex(kPackage, false, kLoopName, kPath, kMountPoint, kDeviceName,
147 kHashtreeLoopName);
148 ASSERT_TRUE(Contains(db, kPackage, kLoopName, kPath, kMountPoint, kDeviceName,
149 kHashtreeLoopName));
150 ASSERT_TRUE(ContainsPackage(db, kPackage, kLoopName, kPath, kDeviceName,
151 kHashtreeLoopName));
152
153 db.RemoveMountedApex(kPackage, kPath);
154 EXPECT_FALSE(Contains(db, kPackage, kLoopName, kPath, kMountPoint,
155 kDeviceName, kHashtreeLoopName));
156 EXPECT_FALSE(ContainsPackage(db, kPackage, kLoopName, kPath, kDeviceName,
157 kHashtreeLoopName));
158 }
159
TEST(ApexDatabaseTest,MountMultiple)160 TEST(ApexDatabaseTest, MountMultiple) {
161 constexpr const char* kPackage[] = {"package", "package", "package",
162 "package"};
163 constexpr const char* kLoopName[] = {"loop", "loop2", "loop3", "loop4"};
164 constexpr const char* kPath[] = {"path", "path2", "path", "path4"};
165 constexpr const char* kMountPoint[] = {"mount", "mount2", "mount", "mount4"};
166 constexpr const char* kDeviceName[] = {"dev", "dev2", "dev3", "dev4"};
167 constexpr const char* kHashtreeLoopName[] = {"hash-loop", "hash-loop2",
168 "hash-loop3", "hash-loop4"};
169 MountedApexDatabase db;
170 ASSERT_EQ(CountPackages(db), 0u);
171
172 for (size_t i = 0; i < arraysize(kPackage); ++i) {
173 db.AddMountedApex(kPackage[i], false, kLoopName[i], kPath[i],
174 kMountPoint[i], kDeviceName[i], kHashtreeLoopName[i]);
175 }
176
177 ASSERT_EQ(CountPackages(db), 4u);
178 for (size_t i = 0; i < arraysize(kPackage); ++i) {
179 ASSERT_TRUE(Contains(db, kPackage[i], kLoopName[i], kPath[i],
180 kMountPoint[i], kDeviceName[i], kHashtreeLoopName[i]));
181 ASSERT_TRUE(ContainsPackage(db, kPackage[i], kLoopName[i], kPath[i],
182 kDeviceName[i], kHashtreeLoopName[i]));
183 }
184
185 db.RemoveMountedApex(kPackage[0], kPath[0]);
186 EXPECT_FALSE(Contains(db, kPackage[0], kLoopName[0], kPath[0], kMountPoint[0],
187 kDeviceName[0], kHashtreeLoopName[0]));
188 EXPECT_FALSE(ContainsPackage(db, kPackage[0], kLoopName[0], kPath[0],
189 kDeviceName[0], kHashtreeLoopName[0]));
190 EXPECT_TRUE(Contains(db, kPackage[1], kLoopName[1], kPath[1], kMountPoint[1],
191 kDeviceName[1], kHashtreeLoopName[1]));
192 EXPECT_TRUE(ContainsPackage(db, kPackage[1], kLoopName[1], kPath[1],
193 kDeviceName[1], kHashtreeLoopName[1]));
194 EXPECT_TRUE(Contains(db, kPackage[2], kLoopName[2], kPath[2], kMountPoint[2],
195 kDeviceName[2], kHashtreeLoopName[2]));
196 EXPECT_TRUE(ContainsPackage(db, kPackage[2], kLoopName[2], kPath[2],
197 kDeviceName[2], kHashtreeLoopName[2]));
198 EXPECT_TRUE(Contains(db, kPackage[3], kLoopName[3], kPath[3], kMountPoint[3],
199 kDeviceName[3], kHashtreeLoopName[3]));
200 EXPECT_TRUE(ContainsPackage(db, kPackage[3], kLoopName[3], kPath[3],
201 kDeviceName[3], kHashtreeLoopName[3]));
202 }
203
TEST(ApexDatabaseTest,GetLatestMountedApex)204 TEST(ApexDatabaseTest, GetLatestMountedApex) {
205 constexpr const char* kPackage = "package";
206 constexpr const char* kLoopName = "loop";
207 constexpr const char* kPath = "path";
208 constexpr const char* kMountPoint = "mount";
209 constexpr const char* kDeviceName = "dev";
210 constexpr const char* kHashtreeLoopName = "hash-loop";
211
212 MountedApexDatabase db;
213 ASSERT_EQ(CountPackages(db), 0u);
214
215 db.AddMountedApex(kPackage, true, kLoopName, kPath, kMountPoint, kDeviceName,
216 kHashtreeLoopName);
217
218 auto ret = db.GetLatestMountedApex(kPackage);
219 MountedApexData expected(kLoopName, kPath, kMountPoint, kDeviceName,
220 kHashtreeLoopName);
221 ASSERT_TRUE(ret.has_value());
222 ASSERT_EQ(ret->loop_name, std::string(kLoopName));
223 ASSERT_EQ(ret->full_path, std::string(kPath));
224 ASSERT_EQ(ret->mount_point, std::string(kMountPoint));
225 ASSERT_EQ(ret->device_name, std::string(kDeviceName));
226 ASSERT_EQ(ret->hashtree_loop_name, std::string(kHashtreeLoopName));
227 }
228
TEST(ApexDatabaseTest,GetLatestMountedApexReturnsNullopt)229 TEST(ApexDatabaseTest, GetLatestMountedApexReturnsNullopt) {
230 MountedApexDatabase db;
231 auto ret = db.GetLatestMountedApex("no-such-name");
232 ASSERT_FALSE(ret.has_value());
233 }
234
235 #pragma clang diagnostic push
236 // error: 'ReturnSentinel' was marked unused but was used
237 // [-Werror,-Wused-but-marked-unused]
238 #pragma clang diagnostic ignored "-Wused-but-marked-unused"
239
TEST(MountedApexDataTest,NoDuplicateLoopDataLoopDevices)240 TEST(MountedApexDataTest, NoDuplicateLoopDataLoopDevices) {
241 ASSERT_DEATH(
242 {
243 MountedApexDatabase db;
244 db.AddMountedApex("package", false, "loop", "path", "mount", "dm",
245 "hashtree-loop1");
246 db.AddMountedApex("package2", false, "loop", "path2", "mount2", "dm2",
247 "hashtree-loop2");
248 },
249 "Duplicate loop device: loop");
250 }
251
TEST(MountedApexDataTest,NoDuplicateLoopHashtreeLoopDevices)252 TEST(MountedApexDataTest, NoDuplicateLoopHashtreeLoopDevices) {
253 ASSERT_DEATH(
254 {
255 MountedApexDatabase db;
256 db.AddMountedApex("package", false, "loop1", "path", "mount", "dm",
257 "hashtree-loop");
258 db.AddMountedApex("package2", false, "loop2", "path2", "mount2", "dm2",
259 "hashtree-loop");
260 },
261 "Duplicate loop device: hashtree-loop");
262 }
263
TEST(MountedApexDataTest,NoDuplicateLoopHashtreeAndDataLoopDevices)264 TEST(MountedApexDataTest, NoDuplicateLoopHashtreeAndDataLoopDevices) {
265 ASSERT_DEATH(
266 {
267 MountedApexDatabase db;
268 db.AddMountedApex("package", false, "loop", "path", "mount", "dm",
269 "hashtree-loop1");
270 db.AddMountedApex("package2", false, "loop2", "path2", "mount2", "dm2",
271 "loop");
272 },
273 "Duplicate loop device: loop");
274 }
275
TEST(MountedApexDataTest,NoDuplicateDm)276 TEST(MountedApexDataTest, NoDuplicateDm) {
277 ASSERT_DEATH(
278 {
279 MountedApexDatabase db;
280 db.AddMountedApex("package", false, "loop", "path", "mount", "dm",
281 /* hashtree_loop_name= */ "");
282 db.AddMountedApex("package2", false, "loop2", "path2", "mount2", "dm",
283 /* hashtree_loop_name= */ "");
284 },
285 "Duplicate dm device: dm");
286 }
287
288 #pragma clang diagnostic pop
289
290 } // namespace
291 } // namespace apex
292 } // namespace android
293