xref: /aosp_15_r20/system/core/fastboot/task_test.cpp (revision 00c7fec1bb09f3284aad6a6f96d2f63dfc3650ad)
1 //
2 // Copyright (C) 2023 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 "task.h"
18 #include "fastboot.h"
19 #include "fastboot_driver_mock.h"
20 
21 #include <gtest/gtest.h>
22 #include <iostream>
23 #include <memory>
24 #include "android-base/strings.h"
25 #include "gmock/gmock.h"
26 
27 using android::base::Split;
28 using testing::_;
29 
30 class ParseTest : public ::testing ::Test {
31   protected:
SetUp()32     void SetUp() override {
33         fp = std::make_unique<FlashingPlan>();
34         fp->slot_override = "b";
35         fp->secondary_slot = "a";
36         fp->wants_wipe = false;
37     }
TearDown()38     void TearDown() override {}
39 
40     std::unique_ptr<FlashingPlan> fp;
41 
42   private:
43 };
44 
collectTasks(FlashingPlan * fp,const std::vector<std::string> & commands)45 static std::vector<std::unique_ptr<Task>> collectTasks(FlashingPlan* fp,
46                                                        const std::vector<std::string>& commands) {
47     std::vector<std::vector<std::string>> vec_commands;
48     for (auto& command : commands) {
49         vec_commands.emplace_back(android::base::Split(command, " "));
50     }
51     std::vector<std::unique_ptr<Task>> tasks;
52     for (auto& command : vec_commands) {
53         tasks.emplace_back(ParseFastbootInfoLine(fp, command));
54     }
55     return tasks;
56 }
57 
ParseCommand(FlashingPlan * fp,std::string command)58 std::unique_ptr<Task> ParseCommand(FlashingPlan* fp, std::string command) {
59     std::vector<std::string> vec_command = android::base::Split(command, " ");
60     return ParseFastbootInfoLine(fp, vec_command);
61 }
62 
63 // tests if tasks_a is a superset of tasks_b. Used for checking to ensure all partitions flashed
64 // from hardcoded image list is also flashed in new fastboot-info.txt
compareTaskList(std::vector<std::unique_ptr<Task>> & tasks_a,std::vector<std::unique_ptr<Task>> & tasks_b)65 static bool compareTaskList(std::vector<std::unique_ptr<Task>>& tasks_a,
66                             std::vector<std::unique_ptr<Task>>& tasks_b) {
67     std::set<std::string> list;
68     for (auto& task : tasks_a) {
69         list.insert(task->ToString());
70     }
71     for (auto& task : tasks_b) {
72         if (list.find(task->ToString()) == list.end()) {
73             std::cout << "ERROR: " << task->ToString()
74                       << " not found in task list created by fastboot-info.txt";
75             return false;
76         }
77     }
78     return true;
79 }
80 
tasksToString(std::vector<std::unique_ptr<Task>> & tasks)81 static std::string tasksToString(std::vector<std::unique_ptr<Task>>& tasks) {
82     std::string output;
83     for (auto& task : tasks) {
84         output.append(task->ToString());
85         output.append("\n");
86     }
87     return output;
88 }
89 
TEST_F(ParseTest,CorrectFlashTaskFormed)90 TEST_F(ParseTest, CorrectFlashTaskFormed) {
91     std::vector<std::string> commands = {"flash dtbo", "flash --slot-other system system_other.img",
92                                          "flash system", "flash --apply-vbmeta vbmeta"};
93 
94     std::vector<std::unique_ptr<Task>> tasks = collectTasks(fp.get(), commands);
95 
96     std::vector<std::vector<std::string>> expected_values{
97             {"dtbo", "dtbo_b", "b", "dtbo.img"},
98             {"system", "system_a", "a", "system_other.img"},
99             {"system", "system_b", "b", "system.img"},
100             {"vbmeta", "vbmeta_b", "b", "vbmeta.img"}
101 
102     };
103 
104     for (auto& task : tasks) {
105         ASSERT_TRUE(task != nullptr);
106     }
107 
108     for (size_t i = 0; i < tasks.size(); i++) {
109         auto task = tasks[i]->AsFlashTask();
110         ASSERT_TRUE(task != nullptr);
111         ASSERT_EQ(task->GetPartition(), expected_values[i][0]);
112         ASSERT_EQ(task->GetPartitionAndSlot(), expected_values[i][1]);
113         ASSERT_EQ(task->GetSlot(), expected_values[i][2]);
114         ASSERT_EQ(task->GetImageName(), expected_values[i][3]);
115     }
116 }
117 
TEST_F(ParseTest,VersionCheckCorrect)118 TEST_F(ParseTest, VersionCheckCorrect) {
119     std::vector<std::string> correct_versions = {"version 1", "version 22", "version 5",
120                                                  "version 17"};
121 
122     std::vector<std::string> bad_versions = {"version",         "version .01",    "version x1",
123                                              "version 1.0.1",   "version 1.",     "s 1.0",
124                                              "version 1.0 2.0", "version 100.00", "version 1 2"};
125 
126     for (auto& version : correct_versions) {
127         ASSERT_TRUE(CheckFastbootInfoRequirements(android::base::Split(version, " "), 26))
128                 << version;
129     }
130 
131     // returning False for failing version check
132     for (auto& version : correct_versions) {
133         ASSERT_FALSE(CheckFastbootInfoRequirements(android::base::Split(version, " "), 0))
134                 << version;
135     }
136     // returning False for bad format
137     for (auto& version : bad_versions) {
138         ASSERT_FALSE(CheckFastbootInfoRequirements(android::base::Split(version, " "), 100))
139                 << version;
140     }
141 }
142 
TEST_F(ParseTest,BadFastbootInput)143 TEST_F(ParseTest, BadFastbootInput) {
144     ASSERT_EQ(ParseCommand(fp.get(), "flash"), nullptr);
145     ASSERT_EQ(ParseCommand(fp.get(), "flash --slot-other --apply-vbmeta"), nullptr);
146     ASSERT_EQ(ParseCommand(fp.get(), "flash --apply-vbmeta"), nullptr);
147     ASSERT_EQ(ParseCommand(fp.get(), "if-wipe"), nullptr);
148     ASSERT_EQ(ParseCommand(fp.get(), "if-wipe flash"), nullptr);
149     ASSERT_EQ(ParseCommand(fp.get(), "wipe dtbo"), nullptr);
150     ASSERT_EQ(ParseCommand(fp.get(), "update-super dtbo"), nullptr);
151     ASSERT_EQ(ParseCommand(fp.get(), "flash system system.img system"), nullptr);
152     ASSERT_EQ(ParseCommand(fp.get(), "reboot bootloader fastboot"), nullptr);
153     ASSERT_EQ(ParseCommand(fp.get(),
154                            "flash --slot-other --apply-vbmeta system system_other.img system"),
155               nullptr);
156     ASSERT_EQ(ParseCommand(fp.get(), "erase"), nullptr);
157     ASSERT_EQ(ParseCommand(fp.get(), "erase dtbo dtbo"), nullptr);
158     ASSERT_EQ(ParseCommand(fp.get(), "wipe this"), nullptr);
159 }
160 
TEST_F(ParseTest,CorrectTaskFormed)161 TEST_F(ParseTest, CorrectTaskFormed) {
162     std::vector<std::string> commands = {"flash dtbo", "flash --slot-other system system_other.img",
163                                          "reboot bootloader", "update-super", "erase cache"};
164     std::vector<std::unique_ptr<Task>> tasks = collectTasks(fp.get(), commands);
165 
166     ASSERT_TRUE(tasks[0]->AsFlashTask());
167     ASSERT_TRUE(tasks[0]->AsFlashTask());
168     ASSERT_TRUE(tasks[1]->AsFlashTask());
169     ASSERT_TRUE(tasks[2]->AsRebootTask());
170     ASSERT_TRUE(tasks[3]->AsUpdateSuperTask());
171     ASSERT_TRUE(tasks[4]->AsWipeTask());
172 }
173 
TEST_F(ParseTest,CorrectDriverCalls)174 TEST_F(ParseTest, CorrectDriverCalls) {
175     fastboot::MockFastbootDriver fb;
176     fp->fb = &fb;
177 
178     EXPECT_CALL(fb, RebootTo(_, _, _)).Times(1);
179     EXPECT_CALL(fb, Reboot(_, _)).Times(1);
180     EXPECT_CALL(fb, WaitForDisconnect()).Times(2);
181 
182     std::vector<std::string> commands = {"reboot bootloader", "reboot"};
183     std::vector<std::unique_ptr<Task>> tasks = collectTasks(fp.get(), commands);
184 
185     for (auto& task : tasks) {
186         task->Run();
187     }
188 }
189 
TEST_F(ParseTest,CorrectTaskLists)190 TEST_F(ParseTest, CorrectTaskLists) {
191     if (!get_android_product_out()) {
192         GTEST_SKIP();
193     }
194 
195     fp->source.reset(new LocalImageSource);
196     fp->sparse_limit = std::numeric_limits<int64_t>::max();
197 
198     fastboot::MockFastbootDriver fb;
199     fp->fb = &fb;
200     fp->should_optimize_flash_super = false;
201 
202     ON_CALL(fb, GetVar("super-partition-name", _, _))
203             .WillByDefault(testing::Return(fastboot::BAD_ARG));
204 
205     FlashAllTool tool(fp.get());
206 
207     fp->should_use_fastboot_info = false;
208     auto hardcoded_tasks = tool.CollectTasks();
209     fp->should_use_fastboot_info = true;
210     auto fastboot_info_tasks = tool.CollectTasks();
211 
212     auto is_non_flash_task = [](const auto& task) -> bool {
213         return task->AsFlashTask() == nullptr;
214     };
215 
216     // remove non flash tasks for testing purposes
217     hardcoded_tasks.erase(
218             std::remove_if(hardcoded_tasks.begin(), hardcoded_tasks.end(), is_non_flash_task),
219             hardcoded_tasks.end());
220     fastboot_info_tasks.erase(std::remove_if(fastboot_info_tasks.begin(), fastboot_info_tasks.end(),
221                                              is_non_flash_task),
222                               fastboot_info_tasks.end());
223 
224     if (!compareTaskList(fastboot_info_tasks, hardcoded_tasks)) {
225         std::cout << "\n\n---Hardcoded Task List---\n"
226                   << tasksToString(hardcoded_tasks) << "\n---Fastboot-Info Task List---\n"
227                   << tasksToString(fastboot_info_tasks);
228     }
229 
230     ASSERT_TRUE(compareTaskList(fastboot_info_tasks, hardcoded_tasks));
231 
232     ASSERT_TRUE(fastboot_info_tasks.size() >= hardcoded_tasks.size())
233             << "size of fastboot-info task list: " << fastboot_info_tasks.size()
234             << " size of hardcoded task list: " << hardcoded_tasks.size();
235 }
TEST_F(ParseTest,IsDynamicPartitiontest)236 TEST_F(ParseTest, IsDynamicPartitiontest) {
237     if (!get_android_product_out()) {
238         GTEST_SKIP();
239     }
240 
241     fp->source.reset(new LocalImageSource);
242 
243     fastboot::MockFastbootDriver fb;
244     fp->fb = &fb;
245     fp->should_optimize_flash_super = true;
246     fp->should_use_fastboot_info = true;
247 
248     std::vector<std::pair<std::string, bool>> test_cases = {
249             {"flash boot", false},
250             {"flash init_boot", false},
251             {"flash --apply-vbmeta vbmeta", false},
252             {"flash product", true},
253             {"flash system", true},
254             {"flash --slot-other system system_other.img", true},
255     };
256     for (auto& test : test_cases) {
257         std::unique_ptr<Task> task =
258                 ParseFastbootInfoLine(fp.get(), android::base::Tokenize(test.first, " "));
259         auto flash_task = task->AsFlashTask();
260         ASSERT_FALSE(flash_task == nullptr);
261         ASSERT_EQ(FlashTask::IsDynamicPartition(fp->source.get(), flash_task), test.second);
262     }
263 }
264 
TEST_F(ParseTest,CanOptimizeTest)265 TEST_F(ParseTest, CanOptimizeTest) {
266     if (!get_android_product_out()) {
267         GTEST_SKIP();
268     }
269 
270     fp->source.reset(new LocalImageSource);
271     fp->sparse_limit = std::numeric_limits<int64_t>::max();
272 
273     fastboot::MockFastbootDriver fb;
274     fp->fb = &fb;
275     fp->should_optimize_flash_super = false;
276     fp->should_use_fastboot_info = true;
277 
278     std::vector<std::pair<std::vector<std::string>, bool>> patternmatchtest = {
279             {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
280               "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
281               "if-wipe erase userdata"},
282              true},
283             {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
284               "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
285               "if-wipe erase userdata"},
286              true},
287             {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
288               "flash product", "flash system", "flash system_ext", "flash odm",
289               "if-wipe erase userdata"},
290              false},
291             {{"flash boot", "flash init_boot", "flash vendor_boot", "update-super", "flash product",
292               "flash system", "flash system_ext", "flash odm", "if-wipe erase userdata"},
293              false},
294     };
295 
296     auto remove_if_callback = [&](const auto& task) -> bool { return !!task->AsResizeTask(); };
297 
298     for (auto& test : patternmatchtest) {
299         std::vector<std::unique_ptr<Task>> tasks = ParseFastbootInfo(fp.get(), test.first);
300         tasks.erase(std::remove_if(tasks.begin(), tasks.end(), remove_if_callback), tasks.end());
301         ASSERT_EQ(OptimizedFlashSuperTask::CanOptimize(fp->source.get(), tasks), test.second);
302     }
303 }
304 
305 // Note: this test is exclusively testing that optimized flash super pattern matches a given task
306 // list and is able to optimized based on a correct sequence of tasks
TEST_F(ParseTest,OptimizedFlashSuperPatternMatchTest)307 TEST_F(ParseTest, OptimizedFlashSuperPatternMatchTest) {
308     if (!get_android_product_out()) {
309         GTEST_SKIP();
310     }
311 
312     fp->source.reset(new LocalImageSource);
313     fp->sparse_limit = std::numeric_limits<int64_t>::max();
314 
315     fastboot::MockFastbootDriver fb;
316     fp->fb = &fb;
317     fp->should_optimize_flash_super = true;
318     fp->should_use_fastboot_info = true;
319 
320     ON_CALL(fb, GetVar("super-partition-name", _, _))
321             .WillByDefault(testing::Return(fastboot::BAD_ARG));
322 
323     ON_CALL(fb, GetVar("slot-count", _, _))
324             .WillByDefault(testing::DoAll(testing::SetArgPointee<1>("2"),
325                                           testing::Return(fastboot::SUCCESS)));
326 
327     ON_CALL(fb, GetVar("partition-size:super", _, _))
328             .WillByDefault(testing::DoAll(testing::SetArgPointee<1>("1000"),
329                                           testing::Return(fastboot::SUCCESS)));
330 
331     std::vector<std::pair<std::vector<std::string>, bool>> patternmatchtest = {
332             {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
333               "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
334               "if-wipe erase userdata"},
335              true},
336             {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
337               "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
338               "if-wipe erase userdata"},
339              true},
340             {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
341               "flash product", "flash system", "flash system_ext", "flash odm",
342               "if-wipe erase userdata"},
343              false},
344             {{"flash boot", "flash init_boot", "flash vendor_boot", "update-super", "flash product",
345               "flash system", "flash system_ext", "flash odm", "if-wipe erase userdata"},
346              false},
347     };
348 
349     for (auto& test : patternmatchtest) {
350         std::vector<std::unique_ptr<Task>> tasks = ParseFastbootInfo(fp.get(), test.first);
351         // Check to make sure we have an optimized flash super task && no more dynamic partition
352         // flashing tasks
353         auto&& IsOptimized = [](const FlashingPlan* fp,
354                                 const std::vector<std::unique_ptr<Task>>& tasks) {
355             bool contains_optimized_task = false;
356             for (auto& task : tasks) {
357                 if (task->AsOptimizedFlashSuperTask()) {
358                     contains_optimized_task = true;
359                 }
360                 if (auto flash_task = task->AsFlashTask()) {
361                     if (FlashTask::IsDynamicPartition(fp->source.get(), flash_task)) {
362                         return false;
363                     }
364                 }
365             }
366             return contains_optimized_task;
367         };
368         ASSERT_EQ(IsOptimized(fp.get(), tasks), test.second);
369     }
370 }
371