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