loman: fix subcommand parsing

The conversion to argparse didn't fully mimic the optparse behavior.
Convert to subcommand support to make it work and add a unittest to
cover the basic expected API.

BUG=chromium:496565
TEST=`cros_workon-$BOARD start google-breakpad` works w/minilayout

Change-Id: I2d3879edaf4a6d2c9a199a7ac1d3d6cbc62b00a2
Reviewed-on: https://chromium-review.googlesource.com/384252
Commit-Ready: Mike Frysinger <vapier@chromium.org>
Tested-by: Mike Frysinger <vapier@chromium.org>
Reviewed-by: Ningning Xia <nxia@chromium.org>
diff --git a/scripts/loman.py b/scripts/loman.py
index af9a77f..48de07b 100644
--- a/scripts/loman.py
+++ b/scripts/loman.py
@@ -8,7 +8,6 @@
 
 import platform
 import os
-import sys
 import xml.etree.ElementTree as ElementTree
 
 from chromite.lib import commandline
@@ -147,24 +146,33 @@
 def GetParser():
   """Return a command line parser."""
   parser = commandline.ArgumentParser(description=__doc__)
-  parser.add_argument('-w', '--workon', action='store_true',
-                      default=False, help='Is this a workon package?')
-  parser.add_argument('-r', '--remote')
-  parser.add_argument('-v', '--revision',
-                      help=('Use to override the manifest defined default '
-                            'revision used for a given project.'))
-  parser.add_argument('--upgrade-minilayout', default=False,
-                      action='store_true',
-                      help=('Upgrade a minilayout checkout into a full.xml '
-                            'checkout utilizing manifest groups.'))
-  parser.add_argument('args', nargs='*')
+
+  subparsers = parser.add_subparsers(dest='command')
+
+  subparser = subparsers.add_parser(
+      'add',
+      help='Add projects to the manifest.')
+  subparser.add_argument('-w', '--workon', action='store_true',
+                         default=False, help='Is this a workon package?')
+  subparser.add_argument('-r', '--remote',
+                         help='Remote project name (for non-workon packages).')
+  subparser.add_argument('-v', '--revision',
+                         help='Use to override the manifest defined default '
+                              'revision used for a given project.')
+  subparser.add_argument('project', help='Name of project in the manifest.')
+  subparser.add_argument('path', nargs='?', help='Local path to the project.')
+
+  subparser = subparsers.add_parser(
+      'upgrade-minilayout',
+      help='Upgrade a minilayout checkout into a full.xml checkout utilizing '
+           'manifest groups.')
+
   return parser
 
 
 def main(argv):
   parser = GetParser()
   options = parser.parse_args(argv)
-  args = options.args
 
   repo_dir = git.FindRepoDir(os.getcwd())
   if not repo_dir:
@@ -183,9 +191,7 @@
   active_manifest = os.path.basename(os.readlink(options.manifest_sym_path))
   upgrade_required = active_manifest == 'minilayout.xml'
 
-  if options.upgrade_minilayout:
-    if args:
-      parser.error("--upgrade-minilayout takes no arguments.")
+  if options.command == 'upgrade-minilayout':
     if not upgrade_required:
       print("This repository checkout isn't using minilayout.xml; "
             "nothing to do")
@@ -196,26 +202,20 @@
     logging.warning(
         "Your repository checkout is using the old minilayout.xml workflow; "
         "auto-upgrading it.")
-    cros_build_lib.RunCommand(
-        [sys.argv[0], '--upgrade-minilayout'], cwd=os.getcwd(), print_cmd=False)
+    main(['upgrade-minilayout'])
 
-  if not args:
-    parser.error("No command specified.")
-  elif args[0] != 'add':
-    parser.error("Only supported subcommand is add right now.")
-  elif options.workon:
-    if len(args) != 2:
-      parser.error(
-          "Argument count is wrong for --workon; must be add <project>")
-    name, path = args[1], None
+  # For now, we only support the add command.
+  assert options.command == 'add'
+  if options.workon:
+    if options.path is not None:
+      parser.error('Adding workon projects do not set project.')
   else:
     if options.remote is None:
       parser.error('Adding non-workon projects requires a remote.')
-    elif len(args) != 3:
-      parser.error(
-          "Argument count is wrong for non-workon mode; "
-          "must be add <project> <path> --remote <remote-arg>")
-    name, path = args[1:]
+    if options.path is None:
+      parser.error('Adding non-workon projects requires a path.')
+  name = options.project
+  path = options.path
 
   revision = options.revision
   if revision is not None: