updater: Add Command parsing codes.

The added codes are not used in the updater yet. The switch will happen
in subsequent CLs.

Test: Run recovery_unit_test and recovery_component_test on marlin.
Change-Id: I1ae8a233280f02c2171b43ef028bdccdacb39c59
diff --git a/tests/Android.mk b/tests/Android.mk
index a9e6491..2576ea0 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -27,6 +27,7 @@
     libminui \
     libotautil \
     libupdater \
+    libgtest_prod \
     libpng \
     libziparchive \
     libutils \
@@ -133,6 +134,7 @@
     libfec \
     libfec_rs \
     libfs_mgr \
+    libgtest_prod \
     liblog \
     libselinux \
     libsparse \
diff --git a/tests/unit/commands_test.cpp b/tests/unit/commands_test.cpp
index 18aa471..bbc83b9 100644
--- a/tests/unit/commands_test.cpp
+++ b/tests/unit/commands_test.cpp
@@ -18,6 +18,7 @@
 
 #include <gtest/gtest.h>
 
+#include "otautil/rangeset.h"
 #include "private/commands.h"
 
 TEST(CommandsTest, ParseType) {
@@ -35,3 +36,277 @@
   ASSERT_EQ(Command::Type::LAST, Command::ParseType("foo"));
   ASSERT_EQ(Command::Type::LAST, Command::ParseType("bar"));
 }
+
+TEST(CommandsTest, ParseTargetInfoAndSourceInfo_SourceBlocksOnly) {
+  const std::vector<std::string> tokens{
+    "4,569884,569904,591946,592043",
+    "117",
+    "4,566779,566799,591946,592043",
+  };
+  TargetInfo target;
+  SourceInfo source;
+  std::string err;
+  ASSERT_TRUE(Command::ParseTargetInfoAndSourceInfo(
+      tokens, "1d74d1a60332fd38cf9405f1bae67917888da6cb", &target,
+      "1d74d1a60332fd38cf9405f1bae67917888da6cb", &source, &err));
+  ASSERT_EQ(TargetInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
+                       RangeSet({ { 569884, 569904 }, { 591946, 592043 } })),
+            target);
+  ASSERT_EQ(SourceInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
+                       RangeSet({ { 566779, 566799 }, { 591946, 592043 } }), {}, {}),
+            source);
+  ASSERT_EQ(117, source.blocks());
+}
+
+TEST(CommandsTest, ParseTargetInfoAndSourceInfo_StashesOnly) {
+  const std::vector<std::string> tokens{
+    "2,350729,350731",
+    "2",
+    "-",
+    "6ebcf8cf1f6be0bc49e7d4a864214251925d1d15:2,0,2",
+  };
+  TargetInfo target;
+  SourceInfo source;
+  std::string err;
+  ASSERT_TRUE(Command::ParseTargetInfoAndSourceInfo(
+      tokens, "6ebcf8cf1f6be0bc49e7d4a864214251925d1d15", &target,
+      "1c25ba04d3278d6b65a1b9f17abac78425ec8b8d", &source, &err));
+  ASSERT_EQ(
+      TargetInfo("6ebcf8cf1f6be0bc49e7d4a864214251925d1d15", RangeSet({ { 350729, 350731 } })),
+      target);
+  ASSERT_EQ(
+      SourceInfo("1c25ba04d3278d6b65a1b9f17abac78425ec8b8d", {}, {},
+                 {
+                     StashInfo("6ebcf8cf1f6be0bc49e7d4a864214251925d1d15", RangeSet({ { 0, 2 } })),
+                 }),
+      source);
+  ASSERT_EQ(2, source.blocks());
+}
+
+TEST(CommandsTest, ParseTargetInfoAndSourceInfo_SourceBlocksAndStashes) {
+  const std::vector<std::string> tokens{
+    "4,611641,611643,636981,637075",
+    "96",
+    "4,636981,637075,770665,770666",
+    "4,0,94,95,96",
+    "9eedf00d11061549e32503cadf054ec6fbfa7a23:2,94,95",
+  };
+  TargetInfo target;
+  SourceInfo source;
+  std::string err;
+  ASSERT_TRUE(Command::ParseTargetInfoAndSourceInfo(
+      tokens, "4734d1b241eb3d0f993714aaf7d665fae43772b6", &target,
+      "a6cbdf3f416960f02189d3a814ec7e9e95c44a0d", &source, &err));
+  ASSERT_EQ(TargetInfo("4734d1b241eb3d0f993714aaf7d665fae43772b6",
+                       RangeSet({ { 611641, 611643 }, { 636981, 637075 } })),
+            target);
+  ASSERT_EQ(SourceInfo(
+                "a6cbdf3f416960f02189d3a814ec7e9e95c44a0d",
+                RangeSet({ { 636981, 637075 }, { 770665, 770666 } }),  // source ranges
+                RangeSet({ { 0, 94 }, { 95, 96 } }),                   // source location
+                {
+                    StashInfo("9eedf00d11061549e32503cadf054ec6fbfa7a23", RangeSet({ { 94, 95 } })),
+                }),
+            source);
+  ASSERT_EQ(96, source.blocks());
+}
+
+TEST(CommandsTest, ParseTargetInfoAndSourceInfo_InvalidInput) {
+  const std::vector<std::string> tokens{
+    "4,611641,611643,636981,637075",
+    "96",
+    "4,636981,637075,770665,770666",
+    "4,0,94,95,96",
+    "9eedf00d11061549e32503cadf054ec6fbfa7a23:2,94,95",
+  };
+  TargetInfo target;
+  SourceInfo source;
+  std::string err;
+
+  // Mismatching block count.
+  {
+    std::vector<std::string> tokens_copy(tokens);
+    tokens_copy[1] = "97";
+    ASSERT_FALSE(Command::ParseTargetInfoAndSourceInfo(
+        tokens_copy, "1d74d1a60332fd38cf9405f1bae67917888da6cb", &target,
+        "1d74d1a60332fd38cf9405f1bae67917888da6cb", &source, &err));
+  }
+
+  // Excess stashes (causing block count mismatch).
+  {
+    std::vector<std::string> tokens_copy(tokens);
+    tokens_copy.push_back("e145a2f83a33334714ac65e34969c1f115e54a6f:2,0,22");
+    ASSERT_FALSE(Command::ParseTargetInfoAndSourceInfo(
+        tokens_copy, "1d74d1a60332fd38cf9405f1bae67917888da6cb", &target,
+        "1d74d1a60332fd38cf9405f1bae67917888da6cb", &source, &err));
+  }
+
+  // Invalid args.
+  for (size_t i = 0; i < tokens.size(); i++) {
+    TargetInfo target;
+    SourceInfo source;
+    std::string err;
+    ASSERT_FALSE(Command::ParseTargetInfoAndSourceInfo(
+        std::vector<std::string>(tokens.cbegin() + i + 1, tokens.cend()),
+        "1d74d1a60332fd38cf9405f1bae67917888da6cb", &target,
+        "1d74d1a60332fd38cf9405f1bae67917888da6cb", &source, &err));
+  }
+}
+
+TEST(CommandsTest, Parse_EmptyInput) {
+  std::string err;
+  ASSERT_FALSE(Command::Parse("", 0, &err));
+  ASSERT_EQ("invalid type", err);
+}
+
+TEST(CommandsTest, Parse_BSDIFF) {
+  const std::string input{
+    "bsdiff 0 148 "
+    "f201a4e04bd3860da6ad47b957ef424d58a58f8c 9d5d223b4bc5c45dbd25a799c4f1a98466731599 "
+    "4,565704,565752,566779,566799 "
+    "68 4,64525,64545,565704,565752"
+  };
+  std::string err;
+  Command command = Command::Parse(input, 1, &err);
+  ASSERT_TRUE(command);
+
+  ASSERT_EQ(Command::Type::BSDIFF, command.type());
+  ASSERT_EQ(1, command.index());
+  ASSERT_EQ(input, command.cmdline());
+
+  ASSERT_EQ(TargetInfo("9d5d223b4bc5c45dbd25a799c4f1a98466731599",
+                       RangeSet({ { 565704, 565752 }, { 566779, 566799 } })),
+            command.target());
+  ASSERT_EQ(SourceInfo("f201a4e04bd3860da6ad47b957ef424d58a58f8c",
+                       RangeSet({ { 64525, 64545 }, { 565704, 565752 } }), RangeSet(), {}),
+            command.source());
+  ASSERT_EQ(StashInfo(), command.stash());
+  ASSERT_EQ(PatchInfo(0, 148), command.patch());
+}
+
+TEST(CommandsTest, Parse_ERASE) {
+  const std::string input{ "erase 2,5,10" };
+  std::string err;
+  Command command = Command::Parse(input, 2, &err);
+  ASSERT_TRUE(command);
+
+  ASSERT_EQ(Command::Type::ERASE, command.type());
+  ASSERT_EQ(2, command.index());
+  ASSERT_EQ(input, command.cmdline());
+
+  ASSERT_EQ(TargetInfo("unknown-hash", RangeSet({ { 5, 10 } })), command.target());
+  ASSERT_EQ(SourceInfo(), command.source());
+  ASSERT_EQ(StashInfo(), command.stash());
+  ASSERT_EQ(PatchInfo(), command.patch());
+}
+
+TEST(CommandsTest, Parse_FREE) {
+  const std::string input{ "free hash1" };
+  std::string err;
+  Command command = Command::Parse(input, 3, &err);
+  ASSERT_TRUE(command);
+
+  ASSERT_EQ(Command::Type::FREE, command.type());
+  ASSERT_EQ(3, command.index());
+  ASSERT_EQ(input, command.cmdline());
+
+  ASSERT_EQ(TargetInfo(), command.target());
+  ASSERT_EQ(SourceInfo(), command.source());
+  ASSERT_EQ(StashInfo("hash1", RangeSet()), command.stash());
+  ASSERT_EQ(PatchInfo(), command.patch());
+}
+
+TEST(CommandsTest, Parse_IMGDIFF) {
+  const std::string input{
+    "imgdiff 29629269 185 "
+    "a6b1c49aed1b57a2aab1ec3e1505b945540cd8db 51978f65035f584a8ef7afa941dacb6d5e862164 "
+    "2,90851,90852 "
+    "1 2,90851,90852"
+  };
+  std::string err;
+  Command command = Command::Parse(input, 4, &err);
+  ASSERT_TRUE(command);
+
+  ASSERT_EQ(Command::Type::IMGDIFF, command.type());
+  ASSERT_EQ(4, command.index());
+  ASSERT_EQ(input, command.cmdline());
+
+  ASSERT_EQ(TargetInfo("51978f65035f584a8ef7afa941dacb6d5e862164", RangeSet({ { 90851, 90852 } })),
+            command.target());
+  ASSERT_EQ(SourceInfo("a6b1c49aed1b57a2aab1ec3e1505b945540cd8db", RangeSet({ { 90851, 90852 } }),
+                       RangeSet(), {}),
+            command.source());
+  ASSERT_EQ(StashInfo(), command.stash());
+  ASSERT_EQ(PatchInfo(29629269, 185), command.patch());
+}
+
+TEST(CommandsTest, Parse_MOVE) {
+  const std::string input{
+    "move 1d74d1a60332fd38cf9405f1bae67917888da6cb "
+    "4,569884,569904,591946,592043 117 4,566779,566799,591946,592043"
+  };
+  std::string err;
+  Command command = Command::Parse(input, 5, &err);
+  ASSERT_TRUE(command);
+
+  ASSERT_EQ(Command::Type::MOVE, command.type());
+  ASSERT_EQ(5, command.index());
+  ASSERT_EQ(input, command.cmdline());
+
+  ASSERT_EQ(TargetInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
+                       RangeSet({ { 569884, 569904 }, { 591946, 592043 } })),
+            command.target());
+  ASSERT_EQ(SourceInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
+                       RangeSet({ { 566779, 566799 }, { 591946, 592043 } }), RangeSet(), {}),
+            command.source());
+  ASSERT_EQ(StashInfo(), command.stash());
+  ASSERT_EQ(PatchInfo(), command.patch());
+}
+
+TEST(CommandsTest, Parse_NEW) {
+  const std::string input{ "new 4,3,5,10,12" };
+  std::string err;
+  Command command = Command::Parse(input, 6, &err);
+  ASSERT_TRUE(command);
+
+  ASSERT_EQ(Command::Type::NEW, command.type());
+  ASSERT_EQ(6, command.index());
+  ASSERT_EQ(input, command.cmdline());
+
+  ASSERT_EQ(TargetInfo("unknown-hash", RangeSet({ { 3, 5 }, { 10, 12 } })), command.target());
+  ASSERT_EQ(SourceInfo(), command.source());
+  ASSERT_EQ(StashInfo(), command.stash());
+  ASSERT_EQ(PatchInfo(), command.patch());
+}
+
+TEST(CommandsTest, Parse_STASH) {
+  const std::string input{ "stash hash1 2,5,10" };
+  std::string err;
+  Command command = Command::Parse(input, 7, &err);
+  ASSERT_TRUE(command);
+
+  ASSERT_EQ(Command::Type::STASH, command.type());
+  ASSERT_EQ(7, command.index());
+  ASSERT_EQ(input, command.cmdline());
+
+  ASSERT_EQ(TargetInfo(), command.target());
+  ASSERT_EQ(SourceInfo(), command.source());
+  ASSERT_EQ(StashInfo("hash1", RangeSet({ { 5, 10 } })), command.stash());
+  ASSERT_EQ(PatchInfo(), command.patch());
+}
+
+TEST(CommandsTest, Parse_ZERO) {
+  const std::string input{ "zero 2,1,5" };
+  std::string err;
+  Command command = Command::Parse(input, 8, &err);
+  ASSERT_TRUE(command);
+
+  ASSERT_EQ(Command::Type::ZERO, command.type());
+  ASSERT_EQ(8, command.index());
+  ASSERT_EQ(input, command.cmdline());
+
+  ASSERT_EQ(TargetInfo("unknown-hash", RangeSet({ { 1, 5 } })), command.target());
+  ASSERT_EQ(SourceInfo(), command.source());
+  ASSERT_EQ(StashInfo(), command.stash());
+  ASSERT_EQ(PatchInfo(), command.patch());
+}