Home | History | Annotate | Download | only in unit
      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 
     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 
     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 
     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 
     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 
     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 
    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 
    161 TEST(CommandsTest, Parse_EmptyInput) {
    162   std::string err;
    163   ASSERT_FALSE(Command::Parse("", 0, &err));
    164   ASSERT_EQ("invalid type", err);
    165 }
    166 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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