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