blob: 1a3f664718c90de673ffeb11102965021c32f685 [file] [log] [blame]
Yifan Hongc049f932019-07-23 15:06:05 -07001//
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 "update_engine/dynamic_partition_control_android.h"
18
19#include <set>
20#include <vector>
21
22#include <base/logging.h>
23#include <base/strings/string_util.h>
24#include <gmock/gmock.h>
25#include <gtest/gtest.h>
26
27#include "update_engine/dynamic_partition_test_utils.h"
28#include "update_engine/mock_dynamic_partition_control.h"
29
30using std::string;
31using testing::_;
32using testing::AnyNumber;
33using testing::Invoke;
34using testing::NiceMock;
35using testing::Not;
36using testing::Return;
37
38namespace chromeos_update_engine {
39
40class DynamicPartitionControlAndroidTest : public ::testing::Test {
41 public:
42 void SetUp() override {
43 module_ = std::make_unique<NiceMock<MockDynamicPartitionControlAndroid>>();
44
45 ON_CALL(dynamicControl(), GetDynamicPartitionsFeatureFlag())
46 .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
Yifan Hong413d5722019-07-23 14:21:09 -070047 ON_CALL(dynamicControl(), GetVirtualAbFeatureFlag())
48 .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::NONE)));
Yifan Hongc049f932019-07-23 15:06:05 -070049
50 ON_CALL(dynamicControl(), GetDeviceDir(_))
51 .WillByDefault(Invoke([](auto path) {
52 *path = kFakeDevicePath;
53 return true;
54 }));
Yifan Hong700d7c12019-07-23 20:49:16 -070055
56 ON_CALL(dynamicControl(), GetSuperPartitionName(_))
57 .WillByDefault(Return(kFakeSuper));
Yifan Hongc049f932019-07-23 15:06:05 -070058 }
59
60 // Return the mocked DynamicPartitionControlInterface.
61 NiceMock<MockDynamicPartitionControlAndroid>& dynamicControl() {
62 return static_cast<NiceMock<MockDynamicPartitionControlAndroid>&>(*module_);
63 }
64
Yifan Hong700d7c12019-07-23 20:49:16 -070065 std::string GetSuperDevice(uint32_t slot) {
66 return GetDevice(dynamicControl().GetSuperPartitionName(slot));
67 }
68
Yifan Hongc049f932019-07-23 15:06:05 -070069 uint32_t source() { return slots_.source; }
70 uint32_t target() { return slots_.target; }
71
72 // Return partition names with suffix of source().
73 std::string S(const std::string& name) {
74 return name + kSlotSuffixes[source()];
75 }
76
77 // Return partition names with suffix of target().
78 std::string T(const std::string& name) {
79 return name + kSlotSuffixes[target()];
80 }
81
82 // Set the fake metadata to return when LoadMetadataBuilder is called on
83 // |slot|.
84 void SetMetadata(uint32_t slot, const PartitionSuffixSizes& sizes) {
85 EXPECT_CALL(dynamicControl(),
86 LoadMetadataBuilder(GetSuperDevice(slot), slot, _))
87 .Times(AnyNumber())
88 .WillRepeatedly(Invoke([sizes](auto, auto, auto) {
89 return NewFakeMetadata(PartitionSuffixSizesToMetadata(sizes));
90 }));
91 }
92
93 void ExpectStoreMetadata(const PartitionSuffixSizes& partition_sizes) {
94 EXPECT_CALL(dynamicControl(),
95 StoreMetadata(GetSuperDevice(target()),
96 MetadataMatches(partition_sizes),
97 target()))
98 .WillOnce(Return(true));
99 }
100
101 // Expect that UnmapPartitionOnDeviceMapper is called on target() metadata
102 // slot with each partition in |partitions|.
103 void ExpectUnmap(const std::set<std::string>& partitions) {
104 // Error when UnmapPartitionOnDeviceMapper is called on unknown arguments.
105 ON_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(_))
106 .WillByDefault(Return(false));
107
108 for (const auto& partition : partitions) {
109 EXPECT_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(partition))
110 .WillOnce(Return(true));
111 }
112 }
113 bool PreparePartitionsForUpdate(const PartitionSizes& partition_sizes) {
114 return dynamicControl().PreparePartitionsForUpdate(
115 source(), target(), PartitionSizesToMetadata(partition_sizes));
116 }
117 void SetSlots(const TestParam& slots) { slots_ = slots; }
118
119 struct Listener : public ::testing::MatchResultListener {
120 explicit Listener(std::ostream* os) : MatchResultListener(os) {}
121 };
122
123 testing::AssertionResult UpdatePartitionMetadata(
124 const PartitionSuffixSizes& source_metadata,
125 const PartitionSizes& update_metadata,
126 const PartitionSuffixSizes& expected) {
127 return UpdatePartitionMetadata(
128 PartitionSuffixSizesToMetadata(source_metadata),
129 PartitionSizesToMetadata(update_metadata),
130 PartitionSuffixSizesToMetadata(expected));
131 }
132 testing::AssertionResult UpdatePartitionMetadata(
133 const PartitionMetadata& source_metadata,
134 const PartitionMetadata& update_metadata,
135 const PartitionMetadata& expected) {
136 return UpdatePartitionMetadata(
137 source_metadata, update_metadata, MetadataMatches(expected));
138 }
139 testing::AssertionResult UpdatePartitionMetadata(
140 const PartitionMetadata& source_metadata,
141 const PartitionMetadata& update_metadata,
142 const Matcher<MetadataBuilder*>& matcher) {
143 auto super_metadata = NewFakeMetadata(source_metadata);
144 if (!module_->UpdatePartitionMetadata(
145 super_metadata.get(), target(), update_metadata)) {
146 return testing::AssertionFailure()
147 << "UpdatePartitionMetadataInternal failed";
148 }
149 std::stringstream ss;
150 Listener listener(&ss);
151 if (matcher.MatchAndExplain(super_metadata.get(), &listener)) {
152 return testing::AssertionSuccess() << ss.str();
153 } else {
154 return testing::AssertionFailure() << ss.str();
155 }
156 }
157
158 std::unique_ptr<DynamicPartitionControlAndroid> module_;
159 TestParam slots_;
160};
161
162class DynamicPartitionControlAndroidTestP
163 : public DynamicPartitionControlAndroidTest,
164 public ::testing::WithParamInterface<TestParam> {
165 public:
166 void SetUp() override {
167 DynamicPartitionControlAndroidTest::SetUp();
168 SetSlots(GetParam());
169 }
170};
171
172// Test resize case. Grow if target metadata contains a partition with a size
173// less than expected.
174TEST_P(DynamicPartitionControlAndroidTestP,
175 NeedGrowIfSizeNotMatchWhenResizing) {
176 PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
177 {S("vendor"), 1_GiB},
178 {T("system"), 2_GiB},
179 {T("vendor"), 1_GiB}};
180 PartitionSuffixSizes expected{{S("system"), 2_GiB},
181 {S("vendor"), 1_GiB},
182 {T("system"), 3_GiB},
183 {T("vendor"), 1_GiB}};
184 PartitionSizes update_metadata{{"system", 3_GiB}, {"vendor", 1_GiB}};
185 EXPECT_TRUE(
186 UpdatePartitionMetadata(source_metadata, update_metadata, expected));
187}
188
189// Test resize case. Shrink if target metadata contains a partition with a size
190// greater than expected.
191TEST_P(DynamicPartitionControlAndroidTestP,
192 NeedShrinkIfSizeNotMatchWhenResizing) {
193 PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
194 {S("vendor"), 1_GiB},
195 {T("system"), 2_GiB},
196 {T("vendor"), 1_GiB}};
197 PartitionSuffixSizes expected{{S("system"), 2_GiB},
198 {S("vendor"), 1_GiB},
199 {T("system"), 2_GiB},
200 {T("vendor"), 150_MiB}};
201 PartitionSizes update_metadata{{"system", 2_GiB}, {"vendor", 150_MiB}};
202 EXPECT_TRUE(
203 UpdatePartitionMetadata(source_metadata, update_metadata, expected));
204}
205
206// Test adding partitions on the first run.
207TEST_P(DynamicPartitionControlAndroidTestP, AddPartitionToEmptyMetadata) {
208 PartitionSuffixSizes source_metadata{};
209 PartitionSuffixSizes expected{{T("system"), 2_GiB}, {T("vendor"), 1_GiB}};
210 PartitionSizes update_metadata{{"system", 2_GiB}, {"vendor", 1_GiB}};
211 EXPECT_TRUE(
212 UpdatePartitionMetadata(source_metadata, update_metadata, expected));
213}
214
215// Test subsequent add case.
216TEST_P(DynamicPartitionControlAndroidTestP, AddAdditionalPartition) {
217 PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
218 {T("system"), 2_GiB}};
219 PartitionSuffixSizes expected{
220 {S("system"), 2_GiB}, {T("system"), 2_GiB}, {T("vendor"), 1_GiB}};
221 PartitionSizes update_metadata{{"system", 2_GiB}, {"vendor", 1_GiB}};
222 EXPECT_TRUE(
223 UpdatePartitionMetadata(source_metadata, update_metadata, expected));
224}
225
226// Test delete one partition.
227TEST_P(DynamicPartitionControlAndroidTestP, DeletePartition) {
228 PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
229 {S("vendor"), 1_GiB},
230 {T("system"), 2_GiB},
231 {T("vendor"), 1_GiB}};
232 // No T("vendor")
233 PartitionSuffixSizes expected{
234 {S("system"), 2_GiB}, {S("vendor"), 1_GiB}, {T("system"), 2_GiB}};
235 PartitionSizes update_metadata{{"system", 2_GiB}};
236 EXPECT_TRUE(
237 UpdatePartitionMetadata(source_metadata, update_metadata, expected));
238}
239
240// Test delete all partitions.
241TEST_P(DynamicPartitionControlAndroidTestP, DeleteAll) {
242 PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
243 {S("vendor"), 1_GiB},
244 {T("system"), 2_GiB},
245 {T("vendor"), 1_GiB}};
246 PartitionSuffixSizes expected{{S("system"), 2_GiB}, {S("vendor"), 1_GiB}};
247 PartitionSizes update_metadata{};
248 EXPECT_TRUE(
249 UpdatePartitionMetadata(source_metadata, update_metadata, expected));
250}
251
252// Test corrupt source metadata case.
253TEST_P(DynamicPartitionControlAndroidTestP, CorruptedSourceMetadata) {
254 EXPECT_CALL(dynamicControl(),
255 LoadMetadataBuilder(GetSuperDevice(source()), source(), _))
256 .WillOnce(Invoke([](auto, auto, auto) { return nullptr; }));
257 ExpectUnmap({T("system")});
258
259 EXPECT_FALSE(PreparePartitionsForUpdate({{"system", 1_GiB}}))
260 << "Should not be able to continue with corrupt source metadata";
261}
262
263// Test that UpdatePartitionMetadata fails if there is not enough space on the
264// device.
265TEST_P(DynamicPartitionControlAndroidTestP, NotEnoughSpace) {
266 PartitionSuffixSizes source_metadata{{S("system"), 3_GiB},
267 {S("vendor"), 2_GiB},
268 {T("system"), 0},
269 {T("vendor"), 0}};
270 PartitionSizes update_metadata{{"system", 3_GiB}, {"vendor", 3_GiB}};
271
272 EXPECT_FALSE(UpdatePartitionMetadata(source_metadata, update_metadata, {}))
273 << "Should not be able to fit 11GiB data into 10GiB space";
274}
275
276TEST_P(DynamicPartitionControlAndroidTestP, NotEnoughSpaceForSlot) {
277 PartitionSuffixSizes source_metadata{{S("system"), 1_GiB},
278 {S("vendor"), 1_GiB},
279 {T("system"), 0},
280 {T("vendor"), 0}};
281 PartitionSizes update_metadata{{"system", 3_GiB}, {"vendor", 3_GiB}};
282 EXPECT_FALSE(UpdatePartitionMetadata(source_metadata, update_metadata, {}))
283 << "Should not be able to grow over size of super / 2";
284}
285
286INSTANTIATE_TEST_CASE_P(DynamicPartitionControlAndroidTest,
287 DynamicPartitionControlAndroidTestP,
288 testing::Values(TestParam{0, 1}, TestParam{1, 0}));
289
290class DynamicPartitionControlAndroidGroupTestP
291 : public DynamicPartitionControlAndroidTestP {
292 public:
293 PartitionMetadata source_metadata;
294 void SetUp() override {
295 DynamicPartitionControlAndroidTestP::SetUp();
296 source_metadata = {
297 .groups = {SimpleGroup(S("android"), 3_GiB, S("system"), 2_GiB),
298 SimpleGroup(S("oem"), 2_GiB, S("vendor"), 1_GiB),
299 SimpleGroup(T("android"), 3_GiB, T("system"), 0),
300 SimpleGroup(T("oem"), 2_GiB, T("vendor"), 0)}};
301 }
302
303 // Return a simple group with only one partition.
304 PartitionMetadata::Group SimpleGroup(const string& group,
305 uint64_t group_size,
306 const string& partition,
307 uint64_t partition_size) {
308 return {.name = group,
309 .size = group_size,
310 .partitions = {{.name = partition, .size = partition_size}}};
311 }
312};
313
314// Allow to resize within group.
315TEST_P(DynamicPartitionControlAndroidGroupTestP, ResizeWithinGroup) {
316 PartitionMetadata expected{
317 .groups = {SimpleGroup(T("android"), 3_GiB, T("system"), 3_GiB),
318 SimpleGroup(T("oem"), 2_GiB, T("vendor"), 2_GiB)}};
319
320 PartitionMetadata update_metadata{
321 .groups = {SimpleGroup("android", 3_GiB, "system", 3_GiB),
322 SimpleGroup("oem", 2_GiB, "vendor", 2_GiB)}};
323
324 EXPECT_TRUE(
325 UpdatePartitionMetadata(source_metadata, update_metadata, expected));
326}
327
328TEST_P(DynamicPartitionControlAndroidGroupTestP, NotEnoughSpaceForGroup) {
329 PartitionMetadata update_metadata{
330 .groups = {SimpleGroup("android", 3_GiB, "system", 1_GiB),
331 SimpleGroup("oem", 2_GiB, "vendor", 3_GiB)}};
332 EXPECT_FALSE(UpdatePartitionMetadata(source_metadata, update_metadata, {}))
333 << "Should not be able to grow over maximum size of group";
334}
335
336TEST_P(DynamicPartitionControlAndroidGroupTestP, GroupTooBig) {
337 PartitionMetadata update_metadata{
338 .groups = {{.name = "android", .size = 3_GiB},
339 {.name = "oem", .size = 3_GiB}}};
340 EXPECT_FALSE(UpdatePartitionMetadata(source_metadata, update_metadata, {}))
341 << "Should not be able to grow over size of super / 2";
342}
343
344TEST_P(DynamicPartitionControlAndroidGroupTestP, AddPartitionToGroup) {
345 PartitionMetadata expected{
346 .groups = {{.name = T("android"),
347 .size = 3_GiB,
348 .partitions = {{.name = T("system"), .size = 2_GiB},
349 {.name = T("system_ext"), .size = 1_GiB}}}}};
350 PartitionMetadata update_metadata{
351 .groups = {{.name = "android",
352 .size = 3_GiB,
353 .partitions = {{.name = "system", .size = 2_GiB},
354 {.name = "system_ext", .size = 1_GiB}}},
355 SimpleGroup("oem", 2_GiB, "vendor", 2_GiB)}};
356 EXPECT_TRUE(
357 UpdatePartitionMetadata(source_metadata, update_metadata, expected));
358}
359
360TEST_P(DynamicPartitionControlAndroidGroupTestP, RemovePartitionFromGroup) {
361 PartitionMetadata expected{
362 .groups = {{.name = T("android"), .size = 3_GiB, .partitions = {}}}};
363 PartitionMetadata update_metadata{
364 .groups = {{.name = "android", .size = 3_GiB, .partitions = {}},
365 SimpleGroup("oem", 2_GiB, "vendor", 2_GiB)}};
366 EXPECT_TRUE(
367 UpdatePartitionMetadata(source_metadata, update_metadata, expected));
368}
369
370TEST_P(DynamicPartitionControlAndroidGroupTestP, AddGroup) {
371 PartitionMetadata expected{
372 .groups = {
373 SimpleGroup(T("new_group"), 2_GiB, T("new_partition"), 2_GiB)}};
374 PartitionMetadata update_metadata{
375 .groups = {SimpleGroup("android", 2_GiB, "system", 2_GiB),
376 SimpleGroup("oem", 1_GiB, "vendor", 1_GiB),
377 SimpleGroup("new_group", 2_GiB, "new_partition", 2_GiB)}};
378 EXPECT_TRUE(
379 UpdatePartitionMetadata(source_metadata, update_metadata, expected));
380}
381
382TEST_P(DynamicPartitionControlAndroidGroupTestP, RemoveGroup) {
383 PartitionMetadata update_metadata{
384 .groups = {SimpleGroup("android", 2_GiB, "system", 2_GiB)}};
385
386 EXPECT_TRUE(UpdatePartitionMetadata(
387 source_metadata, update_metadata, Not(HasGroup(T("oem")))));
388}
389
390TEST_P(DynamicPartitionControlAndroidGroupTestP, ResizeGroup) {
391 PartitionMetadata expected{
392 .groups = {SimpleGroup(T("android"), 2_GiB, T("system"), 2_GiB),
393 SimpleGroup(T("oem"), 3_GiB, T("vendor"), 3_GiB)}};
394 PartitionMetadata update_metadata{
395 .groups = {SimpleGroup("android", 2_GiB, "system", 2_GiB),
396 SimpleGroup("oem", 3_GiB, "vendor", 3_GiB)}};
397 EXPECT_TRUE(
398 UpdatePartitionMetadata(source_metadata, update_metadata, expected));
399}
400
401INSTANTIATE_TEST_CASE_P(DynamicPartitionControlAndroidTest,
402 DynamicPartitionControlAndroidGroupTestP,
403 testing::Values(TestParam{0, 1}, TestParam{1, 0}));
404
405const PartitionSuffixSizes update_sizes_0() {
406 // Initial state is 0 for "other" slot.
407 return {
408 {"grown_a", 2_GiB},
409 {"shrunk_a", 1_GiB},
410 {"same_a", 100_MiB},
411 {"deleted_a", 150_MiB},
412 // no added_a
413 {"grown_b", 200_MiB},
414 // simulate system_other
415 {"shrunk_b", 0},
416 {"same_b", 0},
417 {"deleted_b", 0},
418 // no added_b
419 };
420}
421
422const PartitionSuffixSizes update_sizes_1() {
423 return {
424 {"grown_a", 2_GiB},
425 {"shrunk_a", 1_GiB},
426 {"same_a", 100_MiB},
427 {"deleted_a", 150_MiB},
428 // no added_a
429 {"grown_b", 3_GiB},
430 {"shrunk_b", 150_MiB},
431 {"same_b", 100_MiB},
432 {"added_b", 150_MiB},
433 // no deleted_b
434 };
435}
436
437const PartitionSuffixSizes update_sizes_2() {
438 return {
439 {"grown_a", 4_GiB},
440 {"shrunk_a", 100_MiB},
441 {"same_a", 100_MiB},
442 {"deleted_a", 64_MiB},
443 // no added_a
444 {"grown_b", 3_GiB},
445 {"shrunk_b", 150_MiB},
446 {"same_b", 100_MiB},
447 {"added_b", 150_MiB},
448 // no deleted_b
449 };
450}
451
452// Test case for first update after the device is manufactured, in which
453// case the "other" slot is likely of size "0" (except system, which is
454// non-zero because of system_other partition)
455TEST_F(DynamicPartitionControlAndroidTest, SimulatedFirstUpdate) {
456 SetSlots({0, 1});
457
458 SetMetadata(source(), update_sizes_0());
459 SetMetadata(target(), update_sizes_0());
460 ExpectStoreMetadata(update_sizes_1());
461 ExpectUnmap({"grown_b", "shrunk_b", "same_b", "added_b"});
462
463 EXPECT_TRUE(PreparePartitionsForUpdate({{"grown", 3_GiB},
464 {"shrunk", 150_MiB},
465 {"same", 100_MiB},
466 {"added", 150_MiB}}));
467}
468
469// After first update, test for the second update. In the second update, the
470// "added" partition is deleted and "deleted" partition is re-added.
471TEST_F(DynamicPartitionControlAndroidTest, SimulatedSecondUpdate) {
472 SetSlots({1, 0});
473
474 SetMetadata(source(), update_sizes_1());
475 SetMetadata(target(), update_sizes_0());
476
477 ExpectStoreMetadata(update_sizes_2());
478 ExpectUnmap({"grown_a", "shrunk_a", "same_a", "deleted_a"});
479
480 EXPECT_TRUE(PreparePartitionsForUpdate({{"grown", 4_GiB},
481 {"shrunk", 100_MiB},
482 {"same", 100_MiB},
483 {"deleted", 64_MiB}}));
484}
485
486} // namespace chromeos_update_engine