1 /*
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 <algorithm>
18 #include <string>
19 
20 #include <android-base/strings.h>
21 #include <gtest/gtest.h>
22 #include <openssl/sha.h>
23 
24 #include "otautil/print_sha1.h"
25 #include "otautil/rangeset.h"
26 #include "private/commands.h"
27 
TEST(CommandsTest,ParseType)28 TEST(CommandsTest, ParseType) {
29   ASSERT_EQ(Command::Type::ZERO, Command::ParseType("zero"));
30   ASSERT_EQ(Command::Type::NEW, Command::ParseType("new"));
31   ASSERT_EQ(Command::Type::ERASE, Command::ParseType("erase"));
32   ASSERT_EQ(Command::Type::MOVE, Command::ParseType("move"));
33   ASSERT_EQ(Command::Type::BSDIFF, Command::ParseType("bsdiff"));
34   ASSERT_EQ(Command::Type::IMGDIFF, Command::ParseType("imgdiff"));
35   ASSERT_EQ(Command::Type::STASH, Command::ParseType("stash"));
36   ASSERT_EQ(Command::Type::FREE, Command::ParseType("free"));
37   ASSERT_EQ(Command::Type::COMPUTE_HASH_TREE, Command::ParseType("compute_hash_tree"));
38 }
39 
TEST(CommandsTest,ParseType_InvalidCommand)40 TEST(CommandsTest, ParseType_InvalidCommand) {
41   ASSERT_EQ(Command::Type::LAST, Command::ParseType("foo"));
42   ASSERT_EQ(Command::Type::LAST, Command::ParseType("bar"));
43 }
44 
TEST(CommandsTest,ParseTargetInfoAndSourceInfo_SourceBlocksOnly)45 TEST(CommandsTest, ParseTargetInfoAndSourceInfo_SourceBlocksOnly) {
46   const std::vector<std::string> tokens{
47     "4,569884,569904,591946,592043",
48     "117",
49     "4,566779,566799,591946,592043",
50   };
51   TargetInfo target;
52   SourceInfo source;
53   std::string err;
54   ASSERT_TRUE(Command::ParseTargetInfoAndSourceInfo(
55       tokens, "1d74d1a60332fd38cf9405f1bae67917888da6cb", &target,
56       "1d74d1a60332fd38cf9405f1bae67917888da6cb", &source, &err));
57   ASSERT_EQ(TargetInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
58                        RangeSet({ { 569884, 569904 }, { 591946, 592043 } })),
59             target);
60   ASSERT_EQ(SourceInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
61                        RangeSet({ { 566779, 566799 }, { 591946, 592043 } }), {}, {}),
62             source);
63   ASSERT_EQ(117, source.blocks());
64 }
65 
TEST(CommandsTest,ParseTargetInfoAndSourceInfo_StashesOnly)66 TEST(CommandsTest, ParseTargetInfoAndSourceInfo_StashesOnly) {
67   const std::vector<std::string> tokens{
68     "2,350729,350731",
69     "2",
70     "-",
71     "6ebcf8cf1f6be0bc49e7d4a864214251925d1d15:2,0,2",
72   };
73   TargetInfo target;
74   SourceInfo source;
75   std::string err;
76   ASSERT_TRUE(Command::ParseTargetInfoAndSourceInfo(
77       tokens, "6ebcf8cf1f6be0bc49e7d4a864214251925d1d15", &target,
78       "1c25ba04d3278d6b65a1b9f17abac78425ec8b8d", &source, &err));
79   ASSERT_EQ(
80       TargetInfo("6ebcf8cf1f6be0bc49e7d4a864214251925d1d15", RangeSet({ { 350729, 350731 } })),
81       target);
82   ASSERT_EQ(
83       SourceInfo("1c25ba04d3278d6b65a1b9f17abac78425ec8b8d", {}, {},
84                  {
85                      StashInfo("6ebcf8cf1f6be0bc49e7d4a864214251925d1d15", RangeSet({ { 0, 2 } })),
86                  }),
87       source);
88   ASSERT_EQ(2, source.blocks());
89 }
90 
TEST(CommandsTest,ParseTargetInfoAndSourceInfo_SourceBlocksAndStashes)91 TEST(CommandsTest, ParseTargetInfoAndSourceInfo_SourceBlocksAndStashes) {
92   const std::vector<std::string> tokens{
93     "4,611641,611643,636981,637075",
94     "96",
95     "4,636981,637075,770665,770666",
96     "4,0,94,95,96",
97     "9eedf00d11061549e32503cadf054ec6fbfa7a23:2,94,95",
98   };
99   TargetInfo target;
100   SourceInfo source;
101   std::string err;
102   ASSERT_TRUE(Command::ParseTargetInfoAndSourceInfo(
103       tokens, "4734d1b241eb3d0f993714aaf7d665fae43772b6", &target,
104       "a6cbdf3f416960f02189d3a814ec7e9e95c44a0d", &source, &err));
105   ASSERT_EQ(TargetInfo("4734d1b241eb3d0f993714aaf7d665fae43772b6",
106                        RangeSet({ { 611641, 611643 }, { 636981, 637075 } })),
107             target);
108   ASSERT_EQ(SourceInfo(
109                 "a6cbdf3f416960f02189d3a814ec7e9e95c44a0d",
110                 RangeSet({ { 636981, 637075 }, { 770665, 770666 } }),  // source ranges
111                 RangeSet({ { 0, 94 }, { 95, 96 } }),                   // source location
112                 {
113                     StashInfo("9eedf00d11061549e32503cadf054ec6fbfa7a23", RangeSet({ { 94, 95 } })),
114                 }),
115             source);
116   ASSERT_EQ(96, source.blocks());
117 }
118 
TEST(CommandsTest,ParseTargetInfoAndSourceInfo_InvalidInput)119 TEST(CommandsTest, ParseTargetInfoAndSourceInfo_InvalidInput) {
120   const std::vector<std::string> tokens{
121     "4,611641,611643,636981,637075",
122     "96",
123     "4,636981,637075,770665,770666",
124     "4,0,94,95,96",
125     "9eedf00d11061549e32503cadf054ec6fbfa7a23:2,94,95",
126   };
127   TargetInfo target;
128   SourceInfo source;
129   std::string err;
130 
131   // Mismatching block count.
132   {
133     std::vector<std::string> tokens_copy(tokens);
134     tokens_copy[1] = "97";
135     ASSERT_FALSE(Command::ParseTargetInfoAndSourceInfo(
136         tokens_copy, "1d74d1a60332fd38cf9405f1bae67917888da6cb", &target,
137         "1d74d1a60332fd38cf9405f1bae67917888da6cb", &source, &err));
138   }
139 
140   // Excess stashes (causing block count mismatch).
141   {
142     std::vector<std::string> tokens_copy(tokens);
143     tokens_copy.push_back("e145a2f83a33334714ac65e34969c1f115e54a6f:2,0,22");
144     ASSERT_FALSE(Command::ParseTargetInfoAndSourceInfo(
145         tokens_copy, "1d74d1a60332fd38cf9405f1bae67917888da6cb", &target,
146         "1d74d1a60332fd38cf9405f1bae67917888da6cb", &source, &err));
147   }
148 
149   // Invalid args.
150   for (size_t i = 0; i < tokens.size(); i++) {
151     TargetInfo target;
152     SourceInfo source;
153     std::string err;
154     ASSERT_FALSE(Command::ParseTargetInfoAndSourceInfo(
155         std::vector<std::string>(tokens.cbegin() + i + 1, tokens.cend()),
156         "1d74d1a60332fd38cf9405f1bae67917888da6cb", &target,
157         "1d74d1a60332fd38cf9405f1bae67917888da6cb", &source, &err));
158   }
159 }
160 
TEST(CommandsTest,Parse_EmptyInput)161 TEST(CommandsTest, Parse_EmptyInput) {
162   std::string err;
163   ASSERT_FALSE(Command::Parse("", 0, &err));
164   ASSERT_EQ("invalid type", err);
165 }
166 
TEST(CommandsTest,Parse_ABORT_Allowed)167 TEST(CommandsTest, Parse_ABORT_Allowed) {
168   Command::abort_allowed_ = true;
169 
170   const std::string input{ "abort" };
171   std::string err;
172   Command command = Command::Parse(input, 0, &err);
173   ASSERT_TRUE(command);
174 
175   ASSERT_EQ(TargetInfo(), command.target());
176   ASSERT_EQ(SourceInfo(), command.source());
177   ASSERT_EQ(StashInfo(), command.stash());
178   ASSERT_EQ(PatchInfo(), command.patch());
179 }
180 
TEST(CommandsTest,Parse_ABORT_NotAllowed)181 TEST(CommandsTest, Parse_ABORT_NotAllowed) {
182   const std::string input{ "abort" };
183   std::string err;
184   Command command = Command::Parse(input, 0, &err);
185   ASSERT_FALSE(command);
186 }
187 
TEST(CommandsTest,Parse_BSDIFF)188 TEST(CommandsTest, Parse_BSDIFF) {
189   const std::string input{
190     "bsdiff 0 148 "
191     "f201a4e04bd3860da6ad47b957ef424d58a58f8c 9d5d223b4bc5c45dbd25a799c4f1a98466731599 "
192     "4,565704,565752,566779,566799 "
193     "68 4,64525,64545,565704,565752"
194   };
195   std::string err;
196   Command command = Command::Parse(input, 1, &err);
197   ASSERT_TRUE(command);
198 
199   ASSERT_EQ(Command::Type::BSDIFF, command.type());
200   ASSERT_EQ(1, command.index());
201   ASSERT_EQ(input, command.cmdline());
202 
203   ASSERT_EQ(TargetInfo("9d5d223b4bc5c45dbd25a799c4f1a98466731599",
204                        RangeSet({ { 565704, 565752 }, { 566779, 566799 } })),
205             command.target());
206   ASSERT_EQ(SourceInfo("f201a4e04bd3860da6ad47b957ef424d58a58f8c",
207                        RangeSet({ { 64525, 64545 }, { 565704, 565752 } }), RangeSet(), {}),
208             command.source());
209   ASSERT_EQ(StashInfo(), command.stash());
210   ASSERT_EQ(PatchInfo(0, 148), command.patch());
211 }
212 
TEST(CommandsTest,Parse_ERASE)213 TEST(CommandsTest, Parse_ERASE) {
214   const std::string input{ "erase 2,5,10" };
215   std::string err;
216   Command command = Command::Parse(input, 2, &err);
217   ASSERT_TRUE(command);
218 
219   ASSERT_EQ(Command::Type::ERASE, command.type());
220   ASSERT_EQ(2, command.index());
221   ASSERT_EQ(input, command.cmdline());
222 
223   ASSERT_EQ(TargetInfo("unknown-hash", RangeSet({ { 5, 10 } })), command.target());
224   ASSERT_EQ(SourceInfo(), command.source());
225   ASSERT_EQ(StashInfo(), command.stash());
226   ASSERT_EQ(PatchInfo(), command.patch());
227 }
228 
TEST(CommandsTest,Parse_FREE)229 TEST(CommandsTest, Parse_FREE) {
230   const std::string input{ "free hash1" };
231   std::string err;
232   Command command = Command::Parse(input, 3, &err);
233   ASSERT_TRUE(command);
234 
235   ASSERT_EQ(Command::Type::FREE, command.type());
236   ASSERT_EQ(3, command.index());
237   ASSERT_EQ(input, command.cmdline());
238 
239   ASSERT_EQ(TargetInfo(), command.target());
240   ASSERT_EQ(SourceInfo(), command.source());
241   ASSERT_EQ(StashInfo("hash1", RangeSet()), command.stash());
242   ASSERT_EQ(PatchInfo(), command.patch());
243 }
244 
TEST(CommandsTest,Parse_IMGDIFF)245 TEST(CommandsTest, Parse_IMGDIFF) {
246   const std::string input{
247     "imgdiff 29629269 185 "
248     "a6b1c49aed1b57a2aab1ec3e1505b945540cd8db 51978f65035f584a8ef7afa941dacb6d5e862164 "
249     "2,90851,90852 "
250     "1 2,90851,90852"
251   };
252   std::string err;
253   Command command = Command::Parse(input, 4, &err);
254   ASSERT_TRUE(command);
255 
256   ASSERT_EQ(Command::Type::IMGDIFF, command.type());
257   ASSERT_EQ(4, command.index());
258   ASSERT_EQ(input, command.cmdline());
259 
260   ASSERT_EQ(TargetInfo("51978f65035f584a8ef7afa941dacb6d5e862164", RangeSet({ { 90851, 90852 } })),
261             command.target());
262   ASSERT_EQ(SourceInfo("a6b1c49aed1b57a2aab1ec3e1505b945540cd8db", RangeSet({ { 90851, 90852 } }),
263                        RangeSet(), {}),
264             command.source());
265   ASSERT_EQ(StashInfo(), command.stash());
266   ASSERT_EQ(PatchInfo(29629269, 185), command.patch());
267 }
268 
TEST(CommandsTest,Parse_MOVE)269 TEST(CommandsTest, Parse_MOVE) {
270   const std::string input{
271     "move 1d74d1a60332fd38cf9405f1bae67917888da6cb "
272     "4,569884,569904,591946,592043 117 4,566779,566799,591946,592043"
273   };
274   std::string err;
275   Command command = Command::Parse(input, 5, &err);
276   ASSERT_TRUE(command);
277 
278   ASSERT_EQ(Command::Type::MOVE, command.type());
279   ASSERT_EQ(5, command.index());
280   ASSERT_EQ(input, command.cmdline());
281 
282   ASSERT_EQ(TargetInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
283                        RangeSet({ { 569884, 569904 }, { 591946, 592043 } })),
284             command.target());
285   ASSERT_EQ(SourceInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
286                        RangeSet({ { 566779, 566799 }, { 591946, 592043 } }), RangeSet(), {}),
287             command.source());
288   ASSERT_EQ(StashInfo(), command.stash());
289   ASSERT_EQ(PatchInfo(), command.patch());
290 }
291 
TEST(CommandsTest,Parse_NEW)292 TEST(CommandsTest, Parse_NEW) {
293   const std::string input{ "new 4,3,5,10,12" };
294   std::string err;
295   Command command = Command::Parse(input, 6, &err);
296   ASSERT_TRUE(command);
297 
298   ASSERT_EQ(Command::Type::NEW, command.type());
299   ASSERT_EQ(6, command.index());
300   ASSERT_EQ(input, command.cmdline());
301 
302   ASSERT_EQ(TargetInfo("unknown-hash", RangeSet({ { 3, 5 }, { 10, 12 } })), command.target());
303   ASSERT_EQ(SourceInfo(), command.source());
304   ASSERT_EQ(StashInfo(), command.stash());
305   ASSERT_EQ(PatchInfo(), command.patch());
306 }
307 
TEST(CommandsTest,Parse_STASH)308 TEST(CommandsTest, Parse_STASH) {
309   const std::string input{ "stash hash1 2,5,10" };
310   std::string err;
311   Command command = Command::Parse(input, 7, &err);
312   ASSERT_TRUE(command);
313 
314   ASSERT_EQ(Command::Type::STASH, command.type());
315   ASSERT_EQ(7, command.index());
316   ASSERT_EQ(input, command.cmdline());
317 
318   ASSERT_EQ(TargetInfo(), command.target());
319   ASSERT_EQ(SourceInfo(), command.source());
320   ASSERT_EQ(StashInfo("hash1", RangeSet({ { 5, 10 } })), command.stash());
321   ASSERT_EQ(PatchInfo(), command.patch());
322 }
323 
TEST(CommandsTest,Parse_ZERO)324 TEST(CommandsTest, Parse_ZERO) {
325   const std::string input{ "zero 2,1,5" };
326   std::string err;
327   Command command = Command::Parse(input, 8, &err);
328   ASSERT_TRUE(command);
329 
330   ASSERT_EQ(Command::Type::ZERO, command.type());
331   ASSERT_EQ(8, command.index());
332   ASSERT_EQ(input, command.cmdline());
333 
334   ASSERT_EQ(TargetInfo("unknown-hash", RangeSet({ { 1, 5 } })), command.target());
335   ASSERT_EQ(SourceInfo(), command.source());
336   ASSERT_EQ(StashInfo(), command.stash());
337   ASSERT_EQ(PatchInfo(), command.patch());
338 }
339 
TEST(CommandsTest,Parse_COMPUTE_HASH_TREE)340 TEST(CommandsTest, Parse_COMPUTE_HASH_TREE) {
341   const std::string input{ "compute_hash_tree 2,0,1 2,3,4 sha1 unknown-salt unknown-root-hash" };
342   std::string err;
343   Command command = Command::Parse(input, 9, &err);
344   ASSERT_TRUE(command);
345 
346   ASSERT_EQ(Command::Type::COMPUTE_HASH_TREE, command.type());
347   ASSERT_EQ(9, command.index());
348   ASSERT_EQ(input, command.cmdline());
349 
350   HashTreeInfo expected_info(RangeSet({ { 0, 1 } }), RangeSet({ { 3, 4 } }), "sha1", "unknown-salt",
351                              "unknown-root-hash");
352   ASSERT_EQ(expected_info, command.hash_tree_info());
353   ASSERT_EQ(TargetInfo(), command.target());
354   ASSERT_EQ(SourceInfo(), command.source());
355   ASSERT_EQ(StashInfo(), command.stash());
356   ASSERT_EQ(PatchInfo(), command.patch());
357 }
358 
TEST(CommandsTest,Parse_InvalidNumberOfArgs)359 TEST(CommandsTest, Parse_InvalidNumberOfArgs) {
360   Command::abort_allowed_ = true;
361 
362   // Note that the case of having excess args in BSDIFF, IMGDIFF and MOVE is covered by
363   // ParseTargetInfoAndSourceInfo_InvalidInput.
364   std::vector<std::string> inputs{
365     "abort foo",
366     "bsdiff",
367     "compute_hash_tree, 2,0,1 2,0,1 unknown-algorithm unknown-salt",
368     "erase",
369     "erase 4,3,5,10,12 hash1",
370     "free",
371     "free id1 id2",
372     "imgdiff",
373     "move",
374     "new",
375     "new 4,3,5,10,12 hash1",
376     "stash",
377     "stash id1",
378     "stash id1 4,3,5,10,12 id2",
379     "zero",
380     "zero 4,3,5,10,12 hash2",
381   };
382   for (const auto& input : inputs) {
383     std::string err;
384     ASSERT_FALSE(Command::Parse(input, 0, &err));
385   }
386 }
387 
TEST(SourceInfoTest,Overlaps)388 TEST(SourceInfoTest, Overlaps) {
389   ASSERT_TRUE(SourceInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
390                          RangeSet({ { 7, 9 }, { 16, 20 } }), {}, {})
391                   .Overlaps(TargetInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
392                                        RangeSet({ { 7, 9 }, { 16, 20 } }))));
393 
394   ASSERT_TRUE(SourceInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
395                          RangeSet({ { 7, 9 }, { 16, 20 } }), {}, {})
396                   .Overlaps(TargetInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
397                                        RangeSet({ { 4, 7 }, { 16, 23 } }))));
398 
399   ASSERT_FALSE(SourceInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
400                           RangeSet({ { 7, 9 }, { 16, 20 } }), {}, {})
401                    .Overlaps(TargetInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
402                                         RangeSet({ { 9, 16 } }))));
403 }
404 
TEST(SourceInfoTest,Overlaps_EmptySourceOrTarget)405 TEST(SourceInfoTest, Overlaps_EmptySourceOrTarget) {
406   ASSERT_FALSE(SourceInfo().Overlaps(TargetInfo()));
407 
408   ASSERT_FALSE(SourceInfo().Overlaps(
409       TargetInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb", RangeSet({ { 7, 9 }, { 16, 20 } }))));
410 
411   ASSERT_FALSE(SourceInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
412                           RangeSet({ { 7, 9 }, { 16, 20 } }), {}, {})
413                    .Overlaps(TargetInfo()));
414 }
415 
TEST(SourceInfoTest,Overlaps_WithStashes)416 TEST(SourceInfoTest, Overlaps_WithStashes) {
417   ASSERT_FALSE(SourceInfo("a6cbdf3f416960f02189d3a814ec7e9e95c44a0d",
418                           RangeSet({ { 81, 175 }, { 265, 266 } }),  // source ranges
419                           RangeSet({ { 0, 94 }, { 95, 96 } }),      // source location
420                           { StashInfo("9eedf00d11061549e32503cadf054ec6fbfa7a23",
421                                       RangeSet({ { 94, 95 } })) })
422                    .Overlaps(TargetInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
423                                         RangeSet({ { 175, 265 } }))));
424 
425   ASSERT_TRUE(SourceInfo("a6cbdf3f416960f02189d3a814ec7e9e95c44a0d",
426                          RangeSet({ { 81, 175 }, { 265, 266 } }),  // source ranges
427                          RangeSet({ { 0, 94 }, { 95, 96 } }),      // source location
428                          { StashInfo("9eedf00d11061549e32503cadf054ec6fbfa7a23",
429                                      RangeSet({ { 94, 95 } })) })
430                   .Overlaps(TargetInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
431                                        RangeSet({ { 265, 266 } }))));
432 }
433 
434 // The block size should be specified by the caller of ReadAll (i.e. from Command instance during
435 // normal run).
436 constexpr size_t kBlockSize = 4096;
437 
TEST(SourceInfoTest,ReadAll)438 TEST(SourceInfoTest, ReadAll) {
439   // "2727756cfee3fbfe24bf5650123fd7743d7b3465" is the SHA-1 hex digest of 8192 * 'a'.
440   const SourceInfo source("2727756cfee3fbfe24bf5650123fd7743d7b3465", RangeSet({ { 0, 2 } }), {},
441                           {});
442   auto block_reader = [](const RangeSet& src, std::vector<uint8_t>* block_buffer) -> int {
443     std::fill_n(block_buffer->begin(), src.blocks() * kBlockSize, 'a');
444     return 0;
445   };
446   auto stash_reader = [](const std::string&, std::vector<uint8_t>*) -> int { return 0; };
447   std::vector<uint8_t> buffer(source.blocks() * kBlockSize);
448   ASSERT_TRUE(source.ReadAll(&buffer, kBlockSize, block_reader, stash_reader));
449   ASSERT_EQ(source.blocks() * kBlockSize, buffer.size());
450 
451   uint8_t digest[SHA_DIGEST_LENGTH];
452   SHA1(buffer.data(), buffer.size(), digest);
453   ASSERT_EQ(source.hash(), print_sha1(digest));
454 }
455 
TEST(SourceInfoTest,ReadAll_WithStashes)456 TEST(SourceInfoTest, ReadAll_WithStashes) {
457   const SourceInfo source(
458       // SHA-1 hex digest of 8192 * 'a' + 4096 * 'b'.
459       "ee3ebea26130769c10ad13604712100346d48660", RangeSet({ { 0, 2 } }), RangeSet({ { 0, 2 } }),
460       { StashInfo("1e41f7a59e80c6eb4dc043caae80d273f130bed8", RangeSet({ { 2, 3 } })) });
461   auto block_reader = [](const RangeSet& src, std::vector<uint8_t>* block_buffer) -> int {
462     std::fill_n(block_buffer->begin(), src.blocks() * kBlockSize, 'a');
463     return 0;
464   };
465   auto stash_reader = [](const std::string&, std::vector<uint8_t>* stash_buffer) -> int {
466     std::fill_n(stash_buffer->begin(), kBlockSize, 'b');
467     return 0;
468   };
469   std::vector<uint8_t> buffer(source.blocks() * kBlockSize);
470   ASSERT_TRUE(source.ReadAll(&buffer, kBlockSize, block_reader, stash_reader));
471   ASSERT_EQ(source.blocks() * kBlockSize, buffer.size());
472 
473   uint8_t digest[SHA_DIGEST_LENGTH];
474   SHA1(buffer.data(), buffer.size(), digest);
475   ASSERT_EQ(source.hash(), print_sha1(digest));
476 }
477 
TEST(SourceInfoTest,ReadAll_BufferTooSmall)478 TEST(SourceInfoTest, ReadAll_BufferTooSmall) {
479   const SourceInfo source("2727756cfee3fbfe24bf5650123fd7743d7b3465", RangeSet({ { 0, 2 } }), {},
480                           {});
481   auto block_reader = [](const RangeSet&, std::vector<uint8_t>*) -> int { return 0; };
482   auto stash_reader = [](const std::string&, std::vector<uint8_t>*) -> int { return 0; };
483   std::vector<uint8_t> buffer(source.blocks() * kBlockSize - 1);
484   ASSERT_FALSE(source.ReadAll(&buffer, kBlockSize, block_reader, stash_reader));
485 }
486 
TEST(SourceInfoTest,ReadAll_FailingReader)487 TEST(SourceInfoTest, ReadAll_FailingReader) {
488   const SourceInfo source(
489       "ee3ebea26130769c10ad13604712100346d48660", RangeSet({ { 0, 2 } }), RangeSet({ { 0, 2 } }),
490       { StashInfo("1e41f7a59e80c6eb4dc043caae80d273f130bed8", RangeSet({ { 2, 3 } })) });
491   std::vector<uint8_t> buffer(source.blocks() * kBlockSize);
492   auto failing_block_reader = [](const RangeSet&, std::vector<uint8_t>*) -> int { return -1; };
493   auto stash_reader = [](const std::string&, std::vector<uint8_t>*) -> int { return 0; };
494   ASSERT_FALSE(source.ReadAll(&buffer, kBlockSize, failing_block_reader, stash_reader));
495 
496   auto block_reader = [](const RangeSet&, std::vector<uint8_t>*) -> int { return 0; };
497   auto failing_stash_reader = [](const std::string&, std::vector<uint8_t>*) -> int { return -1; };
498   ASSERT_FALSE(source.ReadAll(&buffer, kBlockSize, block_reader, failing_stash_reader));
499 }
500 
TEST(TransferListTest,Parse)501 TEST(TransferListTest, Parse) {
502   std::vector<std::string> input_lines{
503     "4",  // version
504     "2",  // total blocks
505     "1",  // max stashed entries
506     "1",  // max stashed blocks
507     "stash 1d74d1a60332fd38cf9405f1bae67917888da6cb 2,0,1",
508     "move 1d74d1a60332fd38cf9405f1bae67917888da6cb 2,0,1 1 2,0,1",
509   };
510 
511   std::string err;
512   TransferList transfer_list = TransferList::Parse(android::base::Join(input_lines, '\n'), &err);
513   ASSERT_TRUE(static_cast<bool>(transfer_list));
514   ASSERT_EQ(4, transfer_list.version());
515   ASSERT_EQ(2, transfer_list.total_blocks());
516   ASSERT_EQ(1, transfer_list.stash_max_entries());
517   ASSERT_EQ(1, transfer_list.stash_max_blocks());
518   ASSERT_EQ(2U, transfer_list.commands().size());
519   ASSERT_EQ(Command::Type::STASH, transfer_list.commands()[0].type());
520   ASSERT_EQ(Command::Type::MOVE, transfer_list.commands()[1].type());
521 }
522 
TEST(TransferListTest,Parse_InvalidCommand)523 TEST(TransferListTest, Parse_InvalidCommand) {
524   std::vector<std::string> input_lines{
525     "4",  // version
526     "2",  // total blocks
527     "1",  // max stashed entries
528     "1",  // max stashed blocks
529     "stash 1d74d1a60332fd38cf9405f1bae67917888da6cb 2,0,1",
530     "move 1d74d1a60332fd38cf9405f1bae67917888da6cb 2,0,1 1",
531   };
532 
533   std::string err;
534   TransferList transfer_list = TransferList::Parse(android::base::Join(input_lines, '\n'), &err);
535   ASSERT_FALSE(static_cast<bool>(transfer_list));
536 }
537 
TEST(TransferListTest,Parse_ZeroTotalBlocks)538 TEST(TransferListTest, Parse_ZeroTotalBlocks) {
539   std::vector<std::string> input_lines{
540     "4",  // version
541     "0",  // total blocks
542     "0",  // max stashed entries
543     "0",  // max stashed blocks
544   };
545 
546   std::string err;
547   TransferList transfer_list = TransferList::Parse(android::base::Join(input_lines, '\n'), &err);
548   ASSERT_TRUE(static_cast<bool>(transfer_list));
549   ASSERT_EQ(4, transfer_list.version());
550   ASSERT_EQ(0, transfer_list.total_blocks());
551   ASSERT_EQ(0, transfer_list.stash_max_entries());
552   ASSERT_EQ(0, transfer_list.stash_max_blocks());
553   ASSERT_TRUE(transfer_list.commands().empty());
554 }
555