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 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 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 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 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 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 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 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 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 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 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 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 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