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 "apex_database.h"
18
19 #include <android-base/macros.h>
20 #include <android-base/result-gmock.h>
21 #include <gmock/gmock.h>
22 #include <gtest/gtest.h>
23
24 #include <string>
25 #include <tuple>
26
27 using android::base::Error;
28 using android::base::Result;
29 using android::base::testing::HasError;
30 using android::base::testing::Ok;
31 using android::base::testing::WithMessage;
32
33 namespace android {
34 namespace apex {
35 namespace {
36
37 using MountedApexData = MountedApexDatabase::MountedApexData;
38
CountPackages(const MountedApexDatabase & db)39 size_t CountPackages(const MountedApexDatabase& db) {
40 size_t ret = 0;
41 db.ForallMountedApexes([&ret](const std::string& a ATTRIBUTE_UNUSED,
42 const MountedApexData& b ATTRIBUTE_UNUSED,
43 bool c ATTRIBUTE_UNUSED) { ++ret; });
44 return ret;
45 }
46
Contains(const MountedApexDatabase & db,const std::string & package,const MountedApexData & data)47 bool Contains(const MountedApexDatabase& db, const std::string& package,
48 const MountedApexData& data) {
49 bool found = false;
50 db.ForallMountedApexes([&](const std::string& p, const MountedApexData& d,
51 bool b ATTRIBUTE_UNUSED) {
52 if (package == p && data == d) {
53 found = true;
54 }
55 });
56 return found;
57 }
58
ContainsPackage(const MountedApexDatabase & db,const std::string & package,const MountedApexData & data)59 bool ContainsPackage(const MountedApexDatabase& db, const std::string& package,
60 const MountedApexData& data) {
61 bool found = false;
62 db.ForallMountedApexes(
63 package, [&](const MountedApexData& d, bool b ATTRIBUTE_UNUSED) {
64 if (data == d) {
65 found = true;
66 }
67 });
68 return found;
69 }
70
TEST(ApexDatabaseTest,AddRemovedMountedApex)71 TEST(ApexDatabaseTest, AddRemovedMountedApex) {
72 constexpr const char* kPackage = "package";
73 constexpr const char* kLoopName = "loop";
74 constexpr const char* kPath = "path";
75 constexpr const char* kMountPoint = "mount";
76 constexpr const char* kDeviceName = "dev";
77
78 MountedApexDatabase db;
79 ASSERT_EQ(CountPackages(db), 0u);
80
81 MountedApexData data(0, kLoopName, kPath, kMountPoint, kDeviceName);
82 db.AddMountedApex(kPackage, data);
83 ASSERT_TRUE(Contains(db, kPackage, data));
84 ASSERT_TRUE(ContainsPackage(db, kPackage, data));
85
86 db.RemoveMountedApex(kPackage, kPath);
87 EXPECT_FALSE(Contains(db, kPackage, data));
88 EXPECT_FALSE(ContainsPackage(db, kPackage, data));
89 }
90
TEST(ApexDatabaseTest,MountMultiple)91 TEST(ApexDatabaseTest, MountMultiple) {
92 constexpr const char* kPackage[] = {"package", "package", "package",
93 "package"};
94 constexpr const char* kLoopName[] = {"loop", "loop2", "loop3", "loop4"};
95 constexpr const char* kPath[] = {"path", "path2", "path", "path4"};
96 constexpr const char* kMountPoint[] = {"mount", "mount2", "mount", "mount4"};
97 constexpr const char* kDeviceName[] = {"dev", "dev2", "dev3", "dev4"};
98
99 MountedApexDatabase db;
100 ASSERT_EQ(CountPackages(db), 0u);
101
102 MountedApexData data[arraysize(kPackage)];
103 for (size_t i = 0; i < arraysize(kPackage); ++i) {
104 data[i] = MountedApexData(0, kLoopName[i], kPath[i], kMountPoint[i],
105 kDeviceName[i]);
106 db.AddMountedApex(kPackage[i], data[i]);
107 }
108
109 ASSERT_EQ(CountPackages(db), 4u);
110 for (size_t i = 0; i < arraysize(kPackage); ++i) {
111 ASSERT_TRUE(Contains(db, kPackage[i], data[i]));
112 ASSERT_TRUE(ContainsPackage(db, kPackage[i], data[i]));
113 }
114
115 db.RemoveMountedApex(kPackage[0], kPath[0]);
116 EXPECT_FALSE(Contains(db, kPackage[0], data[0]));
117 EXPECT_FALSE(ContainsPackage(db, kPackage[0], data[0]));
118 EXPECT_TRUE(Contains(db, kPackage[1], data[1]));
119 EXPECT_TRUE(ContainsPackage(db, kPackage[1], data[1]));
120 EXPECT_TRUE(Contains(db, kPackage[2], data[2]));
121 EXPECT_TRUE(ContainsPackage(db, kPackage[2], data[2]));
122 EXPECT_TRUE(Contains(db, kPackage[3], data[3]));
123 EXPECT_TRUE(ContainsPackage(db, kPackage[3], data[3]));
124 }
125
TEST(ApexDatabaseTest,DoIfLatest)126 TEST(ApexDatabaseTest, DoIfLatest) {
127 // Check by passing error-returning handler
128 // When handler is triggered, DoIfLatest() returns the expected error.
129 auto returnError = []() -> Result<void> { return Error() << "expected"; };
130
131 MountedApexDatabase db;
132
133 // With apex: [{version=0,path=path}]
134 db.AddMountedApex("package", 0, "loop", "path", "mount", "dev");
135 ASSERT_THAT(db.DoIfLatest("package", "path", returnError),
136 HasError(WithMessage("expected")));
137
138 // With apexes: [{version=0,path=path}, {version=5,path=path5}]
139 db.AddMountedApex("package", 5, "loop5", "path5", "mount5", "dev5");
140 ASSERT_THAT(db.DoIfLatest("package", "path", returnError), Ok());
141 ASSERT_THAT(db.DoIfLatest("package", "path5", returnError),
142 HasError(WithMessage("expected")));
143 }
144
TEST(ApexDatabaseTest,GetLatestMountedApex)145 TEST(ApexDatabaseTest, GetLatestMountedApex) {
146 constexpr const char* kPackage = "package";
147 constexpr const char* kLoopName = "loop";
148 constexpr const char* kPath = "path";
149 constexpr const char* kMountPoint = "mount";
150 constexpr const char* kDeviceName = "dev";
151
152 MountedApexDatabase db;
153 ASSERT_EQ(CountPackages(db), 0u);
154
155 MountedApexData data(0, kLoopName, kPath, kMountPoint, kDeviceName);
156 db.AddMountedApex(kPackage, data);
157
158 auto ret = db.GetLatestMountedApex(kPackage);
159 ASSERT_TRUE(ret.has_value());
160 ASSERT_EQ(ret.value(), data);
161 }
162
TEST(ApexDatabaseTest,GetLatestMountedApexReturnsNullopt)163 TEST(ApexDatabaseTest, GetLatestMountedApexReturnsNullopt) {
164 MountedApexDatabase db;
165 auto ret = db.GetLatestMountedApex("no-such-name");
166 ASSERT_FALSE(ret.has_value());
167 }
168
169 #pragma clang diagnostic push
170 // error: 'ReturnSentinel' was marked unused but was used
171 // [-Werror,-Wused-but-marked-unused]
172 #pragma clang diagnostic ignored "-Wused-but-marked-unused"
173
TEST(MountedApexDataTest,NoDuplicateLoopDataLoopDevices)174 TEST(MountedApexDataTest, NoDuplicateLoopDataLoopDevices) {
175 ASSERT_DEATH(
176 {
177 MountedApexDatabase db;
178 db.AddMountedApex("package", 0, "loop", "path", "mount", "dm");
179 db.AddMountedApex("package2", 0, "loop", "path2", "mount2", "dm2");
180 },
181 "Duplicate loop device: loop");
182 }
183
TEST(MountedApexDataTest,NoDuplicateDm)184 TEST(MountedApexDataTest, NoDuplicateDm) {
185 ASSERT_DEATH(
186 {
187 MountedApexDatabase db;
188 db.AddMountedApex("package", 0, "loop", "path", "mount", "dm");
189 db.AddMountedApex("package2", 0, "loop2", "path2", "mount2", "dm");
190 },
191 "Duplicate dm device: dm");
192 }
193
194 #pragma clang diagnostic pop
195
196 } // namespace
197 } // namespace apex
198 } // namespace android
199