blob: d70681085d122b0ba4e6665b3259b420c4917378 [file] [log] [blame]
Yifan Hong537802d2018-08-15 13:15:42 -07001//
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 "update_engine/boot_control_android.h"
18
19#include <set>
Yifan Hongd4db07e2018-10-18 17:46:27 -070020#include <vector>
Yifan Hong537802d2018-08-15 13:15:42 -070021
Yifan Hongd4db07e2018-10-18 17:46:27 -070022#include <base/logging.h>
23#include <base/strings/string_util.h>
David Andersond63cb3c2018-10-01 14:15:00 -070024#include <fs_mgr.h>
Yifan Hong537802d2018-08-15 13:15:42 -070025#include <gmock/gmock.h>
26#include <gtest/gtest.h>
27
28#include "update_engine/mock_boot_control_hal.h"
29#include "update_engine/mock_dynamic_partition_control.h"
30
Yifan Hong537802d2018-08-15 13:15:42 -070031using android::fs_mgr::MetadataBuilder;
32using android::hardware::Void;
33using testing::_;
34using testing::AnyNumber;
35using testing::Contains;
36using testing::Eq;
37using testing::Invoke;
38using testing::Key;
39using testing::MakeMatcher;
40using testing::Matcher;
41using testing::MatcherInterface;
42using testing::MatchResultListener;
43using testing::NiceMock;
Yifan Hongd4db07e2018-10-18 17:46:27 -070044using testing::Not;
Yifan Hong537802d2018-08-15 13:15:42 -070045using testing::Return;
46
47namespace chromeos_update_engine {
48
49constexpr const uint32_t kMaxNumSlots = 2;
50constexpr const char* kSlotSuffixes[kMaxNumSlots] = {"_a", "_b"};
51constexpr const char* kFakeDevicePath = "/fake/dev/path/";
52constexpr const char* kFakeMappedPath = "/fake/mapped/path/";
53constexpr const uint32_t kFakeMetadataSize = 65536;
Yifan Hongd4db07e2018-10-18 17:46:27 -070054constexpr const char* kDefaultGroup = "foo";
55
56// "vendor"
57struct PartitionName : std::string {
58 using std::string::string;
59};
60
61// "vendor_a"
62struct PartitionNameSuffix : std::string {
63 using std::string::string;
64};
Yifan Hong537802d2018-08-15 13:15:42 -070065
66// A map describing the size of each partition.
Yifan Hongd4db07e2018-10-18 17:46:27 -070067using PartitionSizes = std::map<PartitionName, uint64_t>;
68using PartitionSuffixSizes = std::map<PartitionNameSuffix, uint64_t>;
69
70using PartitionMetadata = BootControlInterface::PartitionMetadata;
Yifan Hong537802d2018-08-15 13:15:42 -070071
72// C++ standards do not allow uint64_t (aka unsigned long) to be the parameter
73// of user-defined literal operators.
Yifan Hongd4db07e2018-10-18 17:46:27 -070074constexpr unsigned long long operator"" _MiB(unsigned long long x) { // NOLINT
Yifan Hong537802d2018-08-15 13:15:42 -070075 return x << 20;
76}
Yifan Hongd4db07e2018-10-18 17:46:27 -070077constexpr unsigned long long operator"" _GiB(unsigned long long x) { // NOLINT
Yifan Hong537802d2018-08-15 13:15:42 -070078 return x << 30;
79}
80
Yifan Hongd4db07e2018-10-18 17:46:27 -070081constexpr uint64_t kDefaultGroupSize = 5_GiB;
82// Super device size. 1 MiB for metadata.
83constexpr uint64_t kDefaultSuperSize = kDefaultGroupSize * 2 + 1_MiB;
84
Yifan Hong537802d2018-08-15 13:15:42 -070085template <typename U, typename V>
86std::ostream& operator<<(std::ostream& os, const std::map<U, V>& param) {
87 os << "{";
88 bool first = true;
89 for (const auto& pair : param) {
90 if (!first)
91 os << ", ";
92 os << pair.first << ":" << pair.second;
93 first = false;
94 }
95 return os << "}";
96}
97
Yifan Hongd4db07e2018-10-18 17:46:27 -070098template <typename T>
99std::ostream& operator<<(std::ostream& os, const std::vector<T>& param) {
100 os << "[";
101 bool first = true;
102 for (const auto& e : param) {
103 if (!first)
104 os << ", ";
105 os << e;
106 first = false;
107 }
108 return os << "]";
109}
110
111std::ostream& operator<<(std::ostream& os,
112 const PartitionMetadata::Partition& p) {
113 return os << "{" << p.name << ", " << p.size << "}";
114}
115
116std::ostream& operator<<(std::ostream& os, const PartitionMetadata::Group& g) {
117 return os << "{" << g.name << ", " << g.size << ", " << g.partitions << "}";
118}
119
120std::ostream& operator<<(std::ostream& os, const PartitionMetadata& m) {
121 return os << m.groups;
122}
123
Yifan Hong537802d2018-08-15 13:15:42 -0700124inline std::string GetDevice(const std::string& name) {
125 return kFakeDevicePath + name;
126}
Yifan Hong30eb7b52018-11-20 11:05:46 -0800127
128// TODO(elsk): fs_mgr_get_super_partition_name should be mocked.
129inline std::string GetSuperDevice(uint32_t slot) {
130 return GetDevice(fs_mgr_get_super_partition_name(slot));
Yifan Hong537802d2018-08-15 13:15:42 -0700131}
132
133struct TestParam {
134 uint32_t source;
135 uint32_t target;
136};
137std::ostream& operator<<(std::ostream& os, const TestParam& param) {
138 return os << "{source: " << param.source << ", target:" << param.target
139 << "}";
140}
141
Yifan Hongd4db07e2018-10-18 17:46:27 -0700142// To support legacy tests, auto-convert {name_a: size} map to
143// PartitionMetadata.
144PartitionMetadata toMetadata(const PartitionSuffixSizes& partition_sizes) {
145 PartitionMetadata metadata;
146 for (const char* suffix : kSlotSuffixes) {
147 metadata.groups.push_back(
148 {std::string(kDefaultGroup) + suffix, kDefaultGroupSize, {}});
149 }
150 for (const auto& pair : partition_sizes) {
151 for (size_t suffix_idx = 0; suffix_idx < kMaxNumSlots; ++suffix_idx) {
152 if (base::EndsWith(pair.first,
153 kSlotSuffixes[suffix_idx],
154 base::CompareCase::SENSITIVE)) {
155 metadata.groups[suffix_idx].partitions.push_back(
156 {pair.first, pair.second});
157 }
158 }
159 }
160 return metadata;
161}
162
163// To support legacy tests, auto-convert {name: size} map to PartitionMetadata.
164PartitionMetadata toMetadata(const PartitionSizes& partition_sizes) {
165 PartitionMetadata metadata;
166 metadata.groups.push_back(
167 {std::string{kDefaultGroup}, kDefaultGroupSize, {}});
168 for (const auto& pair : partition_sizes) {
169 metadata.groups[0].partitions.push_back({pair.first, pair.second});
170 }
171 return metadata;
172}
173
174std::unique_ptr<MetadataBuilder> NewFakeMetadata(
175 const PartitionMetadata& metadata) {
176 auto builder =
177 MetadataBuilder::New(kDefaultSuperSize, kFakeMetadataSize, kMaxNumSlots);
178 EXPECT_GE(builder->AllocatableSpace(), kDefaultGroupSize * 2);
Yifan Hong537802d2018-08-15 13:15:42 -0700179 EXPECT_NE(nullptr, builder);
180 if (builder == nullptr)
181 return nullptr;
Yifan Hongd4db07e2018-10-18 17:46:27 -0700182 for (const auto& group : metadata.groups) {
183 EXPECT_TRUE(builder->AddGroup(group.name, group.size));
184 for (const auto& partition : group.partitions) {
185 auto p = builder->AddPartition(partition.name, group.name, 0 /* attr */);
186 EXPECT_TRUE(p && builder->ResizePartition(p, partition.size));
187 }
Yifan Hong537802d2018-08-15 13:15:42 -0700188 }
189 return builder;
190}
191
192class MetadataMatcher : public MatcherInterface<MetadataBuilder*> {
193 public:
Yifan Hongd4db07e2018-10-18 17:46:27 -0700194 explicit MetadataMatcher(const PartitionSuffixSizes& partition_sizes)
195 : partition_metadata_(toMetadata(partition_sizes)) {}
196 explicit MetadataMatcher(const PartitionMetadata& partition_metadata)
197 : partition_metadata_(partition_metadata) {}
198
Yifan Hong537802d2018-08-15 13:15:42 -0700199 bool MatchAndExplain(MetadataBuilder* metadata,
200 MatchResultListener* listener) const override {
201 bool success = true;
Yifan Hongd4db07e2018-10-18 17:46:27 -0700202 for (const auto& group : partition_metadata_.groups) {
203 for (const auto& partition : group.partitions) {
204 auto p = metadata->FindPartition(partition.name);
205 if (p == nullptr) {
206 if (!success)
207 *listener << "; ";
208 *listener << "No partition " << partition.name;
209 success = false;
210 continue;
211 }
212 if (p->size() != partition.size) {
213 if (!success)
214 *listener << "; ";
215 *listener << "Partition " << partition.name << " has size "
216 << p->size() << ", expected " << partition.size;
217 success = false;
218 }
219 if (p->group_name() != group.name) {
220 if (!success)
221 *listener << "; ";
222 *listener << "Partition " << partition.name << " has group "
223 << p->group_name() << ", expected " << group.name;
224 success = false;
225 }
Yifan Hong537802d2018-08-15 13:15:42 -0700226 }
227 }
228 return success;
229 }
230
231 void DescribeTo(std::ostream* os) const override {
Yifan Hongd4db07e2018-10-18 17:46:27 -0700232 *os << "expect: " << partition_metadata_;
Yifan Hong537802d2018-08-15 13:15:42 -0700233 }
234
235 void DescribeNegationTo(std::ostream* os) const override {
Yifan Hongd4db07e2018-10-18 17:46:27 -0700236 *os << "expect not: " << partition_metadata_;
Yifan Hong537802d2018-08-15 13:15:42 -0700237 }
238
239 private:
Yifan Hongd4db07e2018-10-18 17:46:27 -0700240 PartitionMetadata partition_metadata_;
Yifan Hong537802d2018-08-15 13:15:42 -0700241};
242
243inline Matcher<MetadataBuilder*> MetadataMatches(
Yifan Hongd4db07e2018-10-18 17:46:27 -0700244 const PartitionSuffixSizes& partition_sizes) {
Yifan Hong537802d2018-08-15 13:15:42 -0700245 return MakeMatcher(new MetadataMatcher(partition_sizes));
246}
247
Yifan Hongd4db07e2018-10-18 17:46:27 -0700248inline Matcher<MetadataBuilder*> MetadataMatches(
249 const PartitionMetadata& partition_metadata) {
250 return MakeMatcher(new MetadataMatcher(partition_metadata));
251}
252
253MATCHER_P(HasGroup, group, " has group " + group) {
254 auto groups = arg->ListGroups();
255 return std::find(groups.begin(), groups.end(), group) != groups.end();
256}
257
Yifan Hong537802d2018-08-15 13:15:42 -0700258class BootControlAndroidTest : public ::testing::Test {
259 protected:
260 void SetUp() override {
261 // Fake init bootctl_
262 bootctl_.module_ = new NiceMock<MockBootControlHal>();
263 bootctl_.dynamic_control_ =
264 std::make_unique<NiceMock<MockDynamicPartitionControl>>();
265
266 ON_CALL(module(), getNumberSlots()).WillByDefault(Invoke([] {
267 return kMaxNumSlots;
268 }));
269 ON_CALL(module(), getSuffix(_, _))
270 .WillByDefault(Invoke([](auto slot, auto cb) {
271 EXPECT_LE(slot, kMaxNumSlots);
272 cb(slot < kMaxNumSlots ? kSlotSuffixes[slot] : "");
273 return Void();
274 }));
275
276 ON_CALL(dynamicControl(), IsDynamicPartitionsEnabled())
277 .WillByDefault(Return(true));
Yifan Hong6e38b352018-11-19 14:12:37 -0800278 ON_CALL(dynamicControl(), IsDynamicPartitionsRetrofit())
279 .WillByDefault(Return(false));
Yifan Hong537802d2018-08-15 13:15:42 -0700280 ON_CALL(dynamicControl(), GetDeviceDir(_))
281 .WillByDefault(Invoke([](auto path) {
282 *path = kFakeDevicePath;
283 return true;
284 }));
285 }
286
287 // Return the mocked HAL module.
288 NiceMock<MockBootControlHal>& module() {
289 return static_cast<NiceMock<MockBootControlHal>&>(*bootctl_.module_);
290 }
291
292 // Return the mocked DynamicPartitionControlInterface.
293 NiceMock<MockDynamicPartitionControl>& dynamicControl() {
294 return static_cast<NiceMock<MockDynamicPartitionControl>&>(
295 *bootctl_.dynamic_control_);
296 }
297
298 // Set the fake metadata to return when LoadMetadataBuilder is called on
299 // |slot|.
Yifan Hongd4db07e2018-10-18 17:46:27 -0700300 void SetMetadata(uint32_t slot, const PartitionSuffixSizes& sizes) {
301 SetMetadata(slot, toMetadata(sizes));
302 }
303
304 void SetMetadata(uint32_t slot, const PartitionMetadata& metadata) {
Yifan Hong6e706b12018-11-09 16:50:51 -0800305 EXPECT_CALL(dynamicControl(),
Yifan Hong30eb7b52018-11-20 11:05:46 -0800306 LoadMetadataBuilder(GetSuperDevice(slot), slot, _))
Yifan Hongd4db07e2018-10-18 17:46:27 -0700307 .Times(AnyNumber())
Yifan Hong6e706b12018-11-09 16:50:51 -0800308 .WillRepeatedly(Invoke([metadata](auto, auto, auto) {
309 return NewFakeMetadata(metadata);
310 }));
Yifan Hong537802d2018-08-15 13:15:42 -0700311 }
312
313 // Expect that MapPartitionOnDeviceMapper is called on target() metadata slot
314 // with each partition in |partitions|.
Yifan Hongaf65ef12018-10-29 11:09:06 -0700315 void ExpectMap(const std::set<std::string>& partitions,
316 bool force_writable = true) {
Yifan Hong537802d2018-08-15 13:15:42 -0700317 // Error when MapPartitionOnDeviceMapper is called on unknown arguments.
Yifan Hongaf65ef12018-10-29 11:09:06 -0700318 ON_CALL(dynamicControl(), MapPartitionOnDeviceMapper(_, _, _, _, _))
Yifan Hong537802d2018-08-15 13:15:42 -0700319 .WillByDefault(Return(false));
320
321 for (const auto& partition : partitions) {
Yifan Hong30eb7b52018-11-20 11:05:46 -0800322 EXPECT_CALL(
323 dynamicControl(),
324 MapPartitionOnDeviceMapper(
325 GetSuperDevice(target()), partition, target(), force_writable, _))
Yifan Hongaf65ef12018-10-29 11:09:06 -0700326 .WillOnce(Invoke([this](auto, auto partition, auto, auto, auto path) {
Yifan Hong537802d2018-08-15 13:15:42 -0700327 auto it = mapped_devices_.find(partition);
328 if (it != mapped_devices_.end()) {
329 *path = it->second;
330 return true;
331 }
332 mapped_devices_[partition] = *path = kFakeMappedPath + partition;
333 return true;
334 }));
335 }
336 }
337
338 // Expect that UnmapPartitionOnDeviceMapper is called on target() metadata
339 // slot with each partition in |partitions|.
340 void ExpectUnmap(const std::set<std::string>& partitions) {
341 // Error when UnmapPartitionOnDeviceMapper is called on unknown arguments.
342 ON_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(_, _))
343 .WillByDefault(Return(false));
344
345 for (const auto& partition : partitions) {
346 EXPECT_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(partition, _))
347 .WillOnce(Invoke([this](auto partition, auto) {
348 mapped_devices_.erase(partition);
349 return true;
350 }));
351 }
352 }
353
354 void ExpectRemap(const std::set<std::string>& partitions) {
355 ExpectUnmap(partitions);
356 ExpectMap(partitions);
357 }
358
359 void ExpectDevicesAreMapped(const std::set<std::string>& partitions) {
360 ASSERT_EQ(partitions.size(), mapped_devices_.size());
361 for (const auto& partition : partitions) {
362 EXPECT_THAT(mapped_devices_, Contains(Key(Eq(partition))))
363 << "Expect that " << partition << " is mapped, but it is not.";
364 }
365 }
366
Yifan Hongd4db07e2018-10-18 17:46:27 -0700367 void ExpectStoreMetadata(const PartitionSuffixSizes& partition_sizes) {
Yifan Hong549eb362018-10-19 15:11:12 -0700368 ExpectStoreMetadataMatch(MetadataMatches(partition_sizes));
369 }
370
371 virtual void ExpectStoreMetadataMatch(
372 const Matcher<MetadataBuilder*>& matcher) {
373 EXPECT_CALL(dynamicControl(),
Yifan Hong30eb7b52018-11-20 11:05:46 -0800374 StoreMetadata(GetSuperDevice(target()), matcher, target()))
Yifan Hong549eb362018-10-19 15:11:12 -0700375 .WillOnce(Return(true));
376 }
377
Yifan Hong537802d2018-08-15 13:15:42 -0700378 uint32_t source() { return slots_.source; }
379
380 uint32_t target() { return slots_.target; }
381
382 // Return partition names with suffix of source().
Yifan Hongd4db07e2018-10-18 17:46:27 -0700383 PartitionNameSuffix S(const std::string& name) {
384 return PartitionNameSuffix(name + std::string(kSlotSuffixes[source()]));
Yifan Hong537802d2018-08-15 13:15:42 -0700385 }
386
387 // Return partition names with suffix of target().
Yifan Hongd4db07e2018-10-18 17:46:27 -0700388 PartitionNameSuffix T(const std::string& name) {
389 return PartitionNameSuffix(name + std::string(kSlotSuffixes[target()]));
Yifan Hong537802d2018-08-15 13:15:42 -0700390 }
391
392 // Set source and target slots to use before testing.
393 void SetSlots(const TestParam& slots) {
394 slots_ = slots;
395
396 ON_CALL(module(), getCurrentSlot()).WillByDefault(Invoke([this] {
397 return source();
398 }));
399 // Should not store metadata to source slot.
Yifan Hong30eb7b52018-11-20 11:05:46 -0800400 EXPECT_CALL(dynamicControl(),
401 StoreMetadata(GetSuperDevice(source()), _, source()))
Yifan Hong537802d2018-08-15 13:15:42 -0700402 .Times(0);
Yifan Hongd4db07e2018-10-18 17:46:27 -0700403 // Should not load metadata from target slot.
404 EXPECT_CALL(dynamicControl(),
Yifan Hong30eb7b52018-11-20 11:05:46 -0800405 LoadMetadataBuilder(GetSuperDevice(target()), target(), _))
Yifan Hongd4db07e2018-10-18 17:46:27 -0700406 .Times(0);
407 }
408
409 bool InitPartitionMetadata(uint32_t slot, PartitionSizes partition_sizes) {
410 auto m = toMetadata(partition_sizes);
411 LOG(INFO) << m;
412 return bootctl_.InitPartitionMetadata(slot, m);
Yifan Hong537802d2018-08-15 13:15:42 -0700413 }
414
415 BootControlAndroid bootctl_; // BootControlAndroid under test.
416 TestParam slots_;
417 // mapped devices through MapPartitionOnDeviceMapper.
418 std::map<std::string, std::string> mapped_devices_;
419};
420
421class BootControlAndroidTestP
422 : public BootControlAndroidTest,
423 public ::testing::WithParamInterface<TestParam> {
424 public:
425 void SetUp() override {
426 BootControlAndroidTest::SetUp();
427 SetSlots(GetParam());
428 }
429};
430
Yifan Hong537802d2018-08-15 13:15:42 -0700431// Test resize case. Grow if target metadata contains a partition with a size
432// less than expected.
433TEST_P(BootControlAndroidTestP, NeedGrowIfSizeNotMatchWhenResizing) {
Yifan Hongd4db07e2018-10-18 17:46:27 -0700434 SetMetadata(source(),
435 {{S("system"), 2_GiB},
436 {S("vendor"), 1_GiB},
437 {T("system"), 2_GiB},
438 {T("vendor"), 1_GiB}});
Yifan Hong549eb362018-10-19 15:11:12 -0700439 ExpectStoreMetadata({{S("system"), 2_GiB},
440 {S("vendor"), 1_GiB},
441 {T("system"), 3_GiB},
442 {T("vendor"), 1_GiB}});
Yifan Hong537802d2018-08-15 13:15:42 -0700443 ExpectRemap({T("system"), T("vendor")});
444
Yifan Hongd4db07e2018-10-18 17:46:27 -0700445 EXPECT_TRUE(
446 InitPartitionMetadata(target(), {{"system", 3_GiB}, {"vendor", 1_GiB}}));
Yifan Hong537802d2018-08-15 13:15:42 -0700447 ExpectDevicesAreMapped({T("system"), T("vendor")});
448}
449
450// Test resize case. Shrink if target metadata contains a partition with a size
451// greater than expected.
452TEST_P(BootControlAndroidTestP, NeedShrinkIfSizeNotMatchWhenResizing) {
Yifan Hongd4db07e2018-10-18 17:46:27 -0700453 SetMetadata(source(),
454 {{S("system"), 2_GiB},
455 {S("vendor"), 1_GiB},
456 {T("system"), 2_GiB},
457 {T("vendor"), 1_GiB}});
Yifan Hong549eb362018-10-19 15:11:12 -0700458 ExpectStoreMetadata({{S("system"), 2_GiB},
459 {S("vendor"), 1_GiB},
460 {T("system"), 2_GiB},
461 {T("vendor"), 150_MiB}});
Yifan Hong537802d2018-08-15 13:15:42 -0700462 ExpectRemap({T("system"), T("vendor")});
463
Yifan Hongd4db07e2018-10-18 17:46:27 -0700464 EXPECT_TRUE(InitPartitionMetadata(target(),
465 {{"system", 2_GiB}, {"vendor", 150_MiB}}));
Yifan Hong537802d2018-08-15 13:15:42 -0700466 ExpectDevicesAreMapped({T("system"), T("vendor")});
467}
468
469// Test adding partitions on the first run.
470TEST_P(BootControlAndroidTestP, AddPartitionToEmptyMetadata) {
Yifan Hongd4db07e2018-10-18 17:46:27 -0700471 SetMetadata(source(), PartitionSuffixSizes{});
Yifan Hong549eb362018-10-19 15:11:12 -0700472 ExpectStoreMetadata({{T("system"), 2_GiB}, {T("vendor"), 1_GiB}});
Yifan Hong537802d2018-08-15 13:15:42 -0700473 ExpectRemap({T("system"), T("vendor")});
474
Yifan Hongd4db07e2018-10-18 17:46:27 -0700475 EXPECT_TRUE(
476 InitPartitionMetadata(target(), {{"system", 2_GiB}, {"vendor", 1_GiB}}));
Yifan Hong537802d2018-08-15 13:15:42 -0700477 ExpectDevicesAreMapped({T("system"), T("vendor")});
478}
479
480// Test subsequent add case.
481TEST_P(BootControlAndroidTestP, AddAdditionalPartition) {
482 SetMetadata(source(), {{S("system"), 2_GiB}, {T("system"), 2_GiB}});
Yifan Hong549eb362018-10-19 15:11:12 -0700483 ExpectStoreMetadata(
484 {{S("system"), 2_GiB}, {T("system"), 2_GiB}, {T("vendor"), 1_GiB}});
Yifan Hong537802d2018-08-15 13:15:42 -0700485 ExpectRemap({T("system"), T("vendor")});
486
Yifan Hongd4db07e2018-10-18 17:46:27 -0700487 EXPECT_TRUE(
488 InitPartitionMetadata(target(), {{"system", 2_GiB}, {"vendor", 1_GiB}}));
Yifan Hong537802d2018-08-15 13:15:42 -0700489 ExpectDevicesAreMapped({T("system"), T("vendor")});
490}
491
492// Test delete one partition.
493TEST_P(BootControlAndroidTestP, DeletePartition) {
Yifan Hongd4db07e2018-10-18 17:46:27 -0700494 SetMetadata(source(),
495 {{S("system"), 2_GiB},
496 {S("vendor"), 1_GiB},
497 {T("system"), 2_GiB},
498 {T("vendor"), 1_GiB}});
499 // No T("vendor")
500 ExpectStoreMetadata(
501 {{S("system"), 2_GiB}, {S("vendor"), 1_GiB}, {T("system"), 2_GiB}});
502 ExpectRemap({T("system")});
Yifan Hong537802d2018-08-15 13:15:42 -0700503
Yifan Hongd4db07e2018-10-18 17:46:27 -0700504 EXPECT_TRUE(InitPartitionMetadata(target(), {{"system", 2_GiB}}));
Yifan Hong537802d2018-08-15 13:15:42 -0700505 ExpectDevicesAreMapped({T("system")});
506}
507
508// Test delete all partitions.
509TEST_P(BootControlAndroidTestP, DeleteAll) {
Yifan Hong537802d2018-08-15 13:15:42 -0700510 SetMetadata(source(),
511 {{S("system"), 2_GiB},
512 {S("vendor"), 1_GiB},
Yifan Hongd4db07e2018-10-18 17:46:27 -0700513 {T("system"), 2_GiB},
514 {T("vendor"), 1_GiB}});
515 ExpectStoreMetadata({{S("system"), 2_GiB}, {S("vendor"), 1_GiB}});
516
517 EXPECT_TRUE(InitPartitionMetadata(target(), {}));
518 ExpectDevicesAreMapped({});
519}
520
521// Test corrupt source metadata case.
522TEST_P(BootControlAndroidTestP, CorruptedSourceMetadata) {
Yifan Hong6e706b12018-11-09 16:50:51 -0800523 EXPECT_CALL(dynamicControl(),
Yifan Hong30eb7b52018-11-20 11:05:46 -0800524 LoadMetadataBuilder(GetSuperDevice(source()), source(), _))
Yifan Hong6e706b12018-11-09 16:50:51 -0800525 .WillOnce(Invoke([](auto, auto, auto) { return nullptr; }));
Yifan Hongd4db07e2018-10-18 17:46:27 -0700526 EXPECT_FALSE(InitPartitionMetadata(target(), {{"system", 1_GiB}}))
527 << "Should not be able to continue with corrupt source metadata";
Yifan Hong537802d2018-08-15 13:15:42 -0700528}
529
530// Test that InitPartitionMetadata fail if there is not enough space on the
531// device.
532TEST_P(BootControlAndroidTestP, NotEnoughSpace) {
Yifan Hongd4db07e2018-10-18 17:46:27 -0700533 SetMetadata(source(),
534 {{S("system"), 3_GiB},
535 {S("vendor"), 2_GiB},
536 {T("system"), 0},
537 {T("vendor"), 0}});
538 EXPECT_FALSE(
539 InitPartitionMetadata(target(), {{"system", 3_GiB}, {"vendor", 3_GiB}}))
Yifan Hong537802d2018-08-15 13:15:42 -0700540 << "Should not be able to fit 11GiB data into 10GiB space";
541}
542
Yifan Hongd4db07e2018-10-18 17:46:27 -0700543TEST_P(BootControlAndroidTestP, NotEnoughSpaceForSlot) {
544 SetMetadata(source(),
545 {{S("system"), 1_GiB},
546 {S("vendor"), 1_GiB},
547 {T("system"), 0},
548 {T("vendor"), 0}});
549 EXPECT_FALSE(
550 InitPartitionMetadata(target(), {{"system", 3_GiB}, {"vendor", 3_GiB}}))
551 << "Should not be able to grow over size of super / 2";
552}
553
554INSTANTIATE_TEST_CASE_P(BootControlAndroidTest,
Yifan Hong537802d2018-08-15 13:15:42 -0700555 BootControlAndroidTestP,
556 testing::Values(TestParam{0, 1}, TestParam{1, 0}));
557
Yifan Hongd4db07e2018-10-18 17:46:27 -0700558const PartitionSuffixSizes update_sizes_0() {
559 // Initial state is 0 for "other" slot.
Yifan Hong537802d2018-08-15 13:15:42 -0700560 return {
561 {"grown_a", 2_GiB},
562 {"shrunk_a", 1_GiB},
563 {"same_a", 100_MiB},
564 {"deleted_a", 150_MiB},
Yifan Hongd4db07e2018-10-18 17:46:27 -0700565 // no added_a
566 {"grown_b", 200_MiB},
567 // simulate system_other
568 {"shrunk_b", 0},
569 {"same_b", 0},
570 {"deleted_b", 0},
571 // no added_b
572 };
573}
574
575const PartitionSuffixSizes update_sizes_1() {
576 return {
577 {"grown_a", 2_GiB},
578 {"shrunk_a", 1_GiB},
579 {"same_a", 100_MiB},
580 {"deleted_a", 150_MiB},
581 // no added_a
Yifan Hong537802d2018-08-15 13:15:42 -0700582 {"grown_b", 3_GiB},
583 {"shrunk_b", 150_MiB},
584 {"same_b", 100_MiB},
585 {"added_b", 150_MiB},
Yifan Hongd4db07e2018-10-18 17:46:27 -0700586 // no deleted_b
Yifan Hong537802d2018-08-15 13:15:42 -0700587 };
588}
589
Yifan Hongd4db07e2018-10-18 17:46:27 -0700590const PartitionSuffixSizes update_sizes_2() {
591 return {
592 {"grown_a", 4_GiB},
593 {"shrunk_a", 100_MiB},
594 {"same_a", 100_MiB},
595 {"deleted_a", 64_MiB},
596 // no added_a
597 {"grown_b", 3_GiB},
598 {"shrunk_b", 150_MiB},
599 {"same_b", 100_MiB},
600 {"added_b", 150_MiB},
601 // no deleted_b
602 };
Yifan Hong537802d2018-08-15 13:15:42 -0700603}
604
605// Test case for first update after the device is manufactured, in which
606// case the "other" slot is likely of size "0" (except system, which is
607// non-zero because of system_other partition)
608TEST_F(BootControlAndroidTest, SimulatedFirstUpdate) {
609 SetSlots({0, 1});
610
611 SetMetadata(source(), update_sizes_0());
612 SetMetadata(target(), update_sizes_0());
Yifan Hong549eb362018-10-19 15:11:12 -0700613 ExpectStoreMetadata(update_sizes_1());
Yifan Hongd4db07e2018-10-18 17:46:27 -0700614 ExpectRemap({"grown_b", "shrunk_b", "same_b", "added_b"});
Yifan Hong537802d2018-08-15 13:15:42 -0700615
Yifan Hongd4db07e2018-10-18 17:46:27 -0700616 EXPECT_TRUE(InitPartitionMetadata(target(),
617 {{"grown", 3_GiB},
618 {"shrunk", 150_MiB},
619 {"same", 100_MiB},
620 {"added", 150_MiB}}));
Yifan Hong537802d2018-08-15 13:15:42 -0700621 ExpectDevicesAreMapped({"grown_b", "shrunk_b", "same_b", "added_b"});
622}
623
624// After first update, test for the second update. In the second update, the
625// "added" partition is deleted and "deleted" partition is re-added.
626TEST_F(BootControlAndroidTest, SimulatedSecondUpdate) {
627 SetSlots({1, 0});
628
629 SetMetadata(source(), update_sizes_1());
630 SetMetadata(target(), update_sizes_0());
631
Yifan Hong549eb362018-10-19 15:11:12 -0700632 ExpectStoreMetadata(update_sizes_2());
Yifan Hongd4db07e2018-10-18 17:46:27 -0700633 ExpectRemap({"grown_a", "shrunk_a", "same_a", "deleted_a"});
Yifan Hong537802d2018-08-15 13:15:42 -0700634
Yifan Hongd4db07e2018-10-18 17:46:27 -0700635 EXPECT_TRUE(InitPartitionMetadata(target(),
636 {{"grown", 4_GiB},
637 {"shrunk", 100_MiB},
638 {"same", 100_MiB},
639 {"deleted", 64_MiB}}));
Yifan Hong537802d2018-08-15 13:15:42 -0700640 ExpectDevicesAreMapped({"grown_a", "shrunk_a", "same_a", "deleted_a"});
641}
642
643TEST_F(BootControlAndroidTest, ApplyingToCurrentSlot) {
644 SetSlots({1, 1});
Yifan Hongd4db07e2018-10-18 17:46:27 -0700645 EXPECT_FALSE(InitPartitionMetadata(target(), {}))
Yifan Hong537802d2018-08-15 13:15:42 -0700646 << "Should not be able to apply to current slot.";
647}
648
Yifan Hongd4db07e2018-10-18 17:46:27 -0700649class BootControlAndroidGroupTestP : public BootControlAndroidTestP {
650 public:
651 void SetUp() override {
652 BootControlAndroidTestP::SetUp();
653 SetMetadata(
654 source(),
655 {.groups = {SimpleGroup(S("android"), 3_GiB, S("system"), 2_GiB),
656 SimpleGroup(S("oem"), 2_GiB, S("vendor"), 1_GiB),
657 SimpleGroup(T("android"), 3_GiB, T("system"), 0),
658 SimpleGroup(T("oem"), 2_GiB, T("vendor"), 0)}});
659 }
660
661 // Return a simple group with only one partition.
662 PartitionMetadata::Group SimpleGroup(const std::string& group,
663 uint64_t group_size,
664 const std::string& partition,
665 uint64_t partition_size) {
666 return {.name = group,
667 .size = group_size,
668 .partitions = {{.name = partition, .size = partition_size}}};
669 }
670
671 void ExpectStoreMetadata(const PartitionMetadata& partition_metadata) {
672 ExpectStoreMetadataMatch(MetadataMatches(partition_metadata));
673 }
674
675 // Expect that target slot is stored with target groups.
676 void ExpectStoreMetadataMatch(
677 const Matcher<MetadataBuilder*>& matcher) override {
678 BootControlAndroidTestP::ExpectStoreMetadataMatch(AllOf(
679 MetadataMatches(PartitionMetadata{
680 .groups = {SimpleGroup(S("android"), 3_GiB, S("system"), 2_GiB),
681 SimpleGroup(S("oem"), 2_GiB, S("vendor"), 1_GiB)}}),
682 matcher));
683 }
684};
685
686// Allow to resize within group.
687TEST_P(BootControlAndroidGroupTestP, ResizeWithinGroup) {
688 ExpectStoreMetadata(PartitionMetadata{
689 .groups = {SimpleGroup(T("android"), 3_GiB, T("system"), 3_GiB),
690 SimpleGroup(T("oem"), 2_GiB, T("vendor"), 2_GiB)}});
691 ExpectRemap({T("system"), T("vendor")});
692
693 EXPECT_TRUE(bootctl_.InitPartitionMetadata(
694 target(),
695 PartitionMetadata{
696 .groups = {SimpleGroup("android", 3_GiB, "system", 3_GiB),
697 SimpleGroup("oem", 2_GiB, "vendor", 2_GiB)}}));
698 ExpectDevicesAreMapped({T("system"), T("vendor")});
699}
700
701TEST_P(BootControlAndroidGroupTestP, NotEnoughSpaceForGroup) {
702 EXPECT_FALSE(bootctl_.InitPartitionMetadata(
703 target(),
704 PartitionMetadata{
705 .groups = {SimpleGroup("android", 3_GiB, "system", 1_GiB),
706 SimpleGroup("oem", 2_GiB, "vendor", 3_GiB)}}))
707 << "Should not be able to grow over maximum size of group";
708}
709
710TEST_P(BootControlAndroidGroupTestP, GroupTooBig) {
711 EXPECT_FALSE(bootctl_.InitPartitionMetadata(
712 target(),
713 PartitionMetadata{.groups = {{.name = "android", .size = 3_GiB},
714 {.name = "oem", .size = 3_GiB}}}))
715 << "Should not be able to grow over size of super / 2";
716}
717
718TEST_P(BootControlAndroidGroupTestP, AddPartitionToGroup) {
719 ExpectStoreMetadata(PartitionMetadata{
720 .groups = {
721 {.name = T("android"),
722 .size = 3_GiB,
723 .partitions = {{.name = T("system"), .size = 2_GiB},
724 {.name = T("product_services"), .size = 1_GiB}}}}});
725 ExpectRemap({T("system"), T("vendor"), T("product_services")});
726
727 EXPECT_TRUE(bootctl_.InitPartitionMetadata(
728 target(),
729 PartitionMetadata{
730 .groups = {
731 {.name = "android",
732 .size = 3_GiB,
733 .partitions = {{.name = "system", .size = 2_GiB},
734 {.name = "product_services", .size = 1_GiB}}},
735 SimpleGroup("oem", 2_GiB, "vendor", 2_GiB)}}));
736 ExpectDevicesAreMapped({T("system"), T("vendor"), T("product_services")});
737}
738
739TEST_P(BootControlAndroidGroupTestP, RemovePartitionFromGroup) {
740 ExpectStoreMetadata(PartitionMetadata{
741 .groups = {{.name = T("android"), .size = 3_GiB, .partitions = {}}}});
742 ExpectRemap({T("vendor")});
743
744 EXPECT_TRUE(bootctl_.InitPartitionMetadata(
745 target(),
746 PartitionMetadata{
747 .groups = {{.name = "android", .size = 3_GiB, .partitions = {}},
748 SimpleGroup("oem", 2_GiB, "vendor", 2_GiB)}}));
749 ExpectDevicesAreMapped({T("vendor")});
750}
751
752TEST_P(BootControlAndroidGroupTestP, AddGroup) {
753 ExpectStoreMetadata(PartitionMetadata{
754 .groups = {
755 SimpleGroup(T("new_group"), 2_GiB, T("new_partition"), 2_GiB)}});
756 ExpectRemap({T("system"), T("vendor"), T("new_partition")});
757
758 EXPECT_TRUE(bootctl_.InitPartitionMetadata(
759 target(),
760 PartitionMetadata{
761 .groups = {
762 SimpleGroup("android", 2_GiB, "system", 2_GiB),
763 SimpleGroup("oem", 1_GiB, "vendor", 1_GiB),
764 SimpleGroup("new_group", 2_GiB, "new_partition", 2_GiB)}}));
765 ExpectDevicesAreMapped({T("system"), T("vendor"), T("new_partition")});
766}
767
768TEST_P(BootControlAndroidGroupTestP, RemoveGroup) {
769 ExpectStoreMetadataMatch(Not(HasGroup(T("oem"))));
770 ExpectRemap({T("system")});
771 EXPECT_TRUE(bootctl_.InitPartitionMetadata(
772 target(),
773 PartitionMetadata{
774 .groups = {SimpleGroup("android", 2_GiB, "system", 2_GiB)}}));
775 ExpectDevicesAreMapped({T("system")});
776}
777
778TEST_P(BootControlAndroidGroupTestP, ResizeGroup) {
779 ExpectStoreMetadata(PartitionMetadata{
780 .groups = {SimpleGroup(T("android"), 2_GiB, T("system"), 2_GiB),
781 SimpleGroup(T("oem"), 3_GiB, T("vendor"), 3_GiB)}});
782 ExpectRemap({T("system"), T("vendor")});
783
784 EXPECT_TRUE(bootctl_.InitPartitionMetadata(
785 target(),
786 PartitionMetadata{
787 .groups = {SimpleGroup("android", 2_GiB, "system", 2_GiB),
788 SimpleGroup("oem", 3_GiB, "vendor", 3_GiB)}}));
789 ExpectDevicesAreMapped({T("system"), T("vendor")});
790}
791
792INSTANTIATE_TEST_CASE_P(BootControlAndroidTest,
793 BootControlAndroidGroupTestP,
794 testing::Values(TestParam{0, 1}, TestParam{1, 0}));
795
Yifan Hong537802d2018-08-15 13:15:42 -0700796} // namespace chromeos_update_engine