llvm_tools: merge update_packages_*.py files

Merge update_packages_and_run_tryjobs.py and
update_packages_and_test_cq.py as the two modules share much command
code.

BUG=chromium:1001602

TEST=local tests.

Change-Id: Iac957ea77af4257e454fec99ff13424e778ec51a
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/2148478
Reviewed-by: Manoj Gupta <manojgupta@chromium.org>
Tested-by: Jian Cai <jiancai@google.com>
diff --git a/llvm_tools/README.md b/llvm_tools/README.md
index d42462a..ce094e9 100644
--- a/llvm_tools/README.md
+++ b/llvm_tools/README.md
@@ -14,13 +14,13 @@
 **NOTE: sudo must be permissive (i.e. **`cros_sdk`** should NOT prompt for a
 password)**
 
-## `update_packages_and_run_tryjobs.py`
+## `update_packages_and_run_tests.py`
 
 ### Usage
 
-This script is used for updating a package's `LLVM_NEXT_HASH` (sys-devel/llvm,
+This script is used for updating a package's LLVM hash (sys-devel/llvm,
 sys-libs/compiler-rt, sys-libs/libcxx, sys-libs/libcxxabi, and
-sys-libs/llvm-libunwind) and then run tryjobs after updating the git hash.
+sys-libs/llvm-libunwind) and then run tests after updating the git hash.
 
 An example when this script should be run is when certain boards would like
 to be tested with the updated `LLVM_NEXT_HASH`.
@@ -28,8 +28,10 @@
 For example:
 
 ```
-$ ./update_packages_and_run_tryjobs.py \
+$ ./update_packages_and_run_tests.py \
+  --is_llvm_next \
   --llvm_version tot \
+  tryjobs \
   --options nochromesdk latest-toolchain \
   --builders kevin-release-tryjob nocturne-release-tryjob
 ```
@@ -41,25 +43,26 @@
 For help with the command line arguments of the script, run:
 
 ```
-$ ./update_packages_and_run_tryjobs.py --help
+$ ./update_packages_and_run_tests.py --help
 ```
 
-Similarly as the previous example, but for updating `LLVM_NEXT_HASH` to
-google3:
+Similarly as the previous example, but for updating `LLVM_HASH` to
+google3 and test with cq+1:
 
 ```
-$ ./update_packages_and_run_tryjobs.py \
+$ ./update_packages_and_run_tests.py \
   --llvm_version google3 \
-  --options nochromesdk latest-toolchain \
-  --builders kevin-release-tryjob nocturne-release-tryjob
+  cq
 ```
 
 Similarly as the previous example, but for updating `LLVM_NEXT_HASH` to
-the git hash of revision 367622:
+the git hash of revision 367622 and test with tryobs:
 
 ```
-$ ./update_packages_and_run_tryjobs.py \
+$ ./update_packages_and_run_tests.py \
+  --is_llvm_next \
   --llvm_version 367622 \
+  tryjobs \
   --options nochromesdk latest-toolchain \
   --builders kevin-release-tryjob nocturne-release-tryjob
 ```
diff --git a/llvm_tools/modify_a_tryjob.py b/llvm_tools/modify_a_tryjob.py
index 5d04e7f..4133725 100755
--- a/llvm_tools/modify_a_tryjob.py
+++ b/llvm_tools/modify_a_tryjob.py
@@ -17,7 +17,7 @@
 from assert_not_in_chroot import VerifyOutsideChroot
 from failure_modes import FailureModes
 from get_llvm_hash import GetLLVMHashAndVersionFromSVNOption
-from update_packages_and_run_tryjobs import RunTryJobs
+from update_packages_and_run_tests import RunTryJobs
 from update_tryjob_status import FindTryjobIndex
 from update_tryjob_status import TryjobStatus
 import update_chromeos_llvm_hash
diff --git a/llvm_tools/update_packages_and_run_tryjobs.py b/llvm_tools/update_packages_and_run_tests.py
similarity index 74%
rename from llvm_tools/update_packages_and_run_tryjobs.py
rename to llvm_tools/update_packages_and_run_tests.py
index dc9e664..ee57ea0 100755
--- a/llvm_tools/update_packages_and_run_tryjobs.py
+++ b/llvm_tools/update_packages_and_run_tests.py
@@ -38,15 +38,7 @@
 
   # Create parser and add optional command-line arguments.
   parser = argparse.ArgumentParser(
-      description=
-      'Runs a tryjob if successfully updated LLVM_NEXT_HASH of packages.')
-
-  # Add argument for the absolute path to the file that contains information on
-  # the previous tested svn version.
-  parser.add_argument(
-      '--last_tested',
-      help='the absolute path to the file that contains the last tested '
-      'arguments.')
+      description='Update an LLVM hash of packages and run tests.')
 
   # Add argument for other change lists that want to run alongside the tryjob
   # which has a change list of updating a package's git hash.
@@ -54,42 +46,29 @@
       '--extra_change_lists',
       type=int,
       nargs='+',
+      default=[],
       help='change lists that would like to be run alongside the change list '
       'of updating the packages')
 
-  # Add argument for custom options for the tryjob.
-  parser.add_argument(
-      '--options',
-      required=False,
-      nargs='+',
-      help='options to use for the tryjob testing')
-
-  # Add argument for builders for the tryjob.
-  parser.add_argument(
-      '--builders',
-      required=True,
-      nargs='+',
-      help='builders to use for the tryjob testing')
-
-  # Add argument for the description of the tryjob.
-  parser.add_argument(
-      '--description',
-      required=False,
-      nargs='+',
-      help='the description of the tryjob')
-
   # Add argument for a specific chroot path.
   parser.add_argument(
       '--chroot_path',
       default=cros_root,
       help='the path to the chroot (default: %(default)s)')
 
-  # Add argument for whether to display command contents to `stdout`.
+  # Add argument to choose between llvm and llvm-next.
   parser.add_argument(
-      '--verbose',
+      '--is_llvm_next',
       action='store_true',
-      help='display contents of a command to the terminal '
-      '(default: %(default)s)')
+      help='which llvm hash to update. Update LLVM_NEXT_HASH if specified. '
+      'Otherwise, update LLVM_HASH')
+
+  # Add argument for the absolute path to the file that contains information on
+  # the previous tested svn version.
+  parser.add_argument(
+      '--last_tested',
+      help='the absolute path to the file that contains the last tested '
+      'arguments.')
 
   # Add argument for the LLVM version to use.
   parser.add_argument(
@@ -101,8 +80,47 @@
       '(default: finds the git hash of the google3 LLVM '
       'version)')
 
+  # Add argument to add reviewers for the created CL.
+  parser.add_argument(
+      '--reviewers',
+      nargs='+',
+      default=[],
+      help='The reviewers for the package update changelist')
+
+  # Add argument for whether to display command contents to `stdout`.
+  parser.add_argument(
+      '--verbose',
+      action='store_true',
+      help='display contents of a command to the terminal '
+      '(default: %(default)s)')
+
+  subparsers = parser.add_subparsers(dest='subparser_name')
+  # Testing with the tryjobs.
+  tryjob_subparser = subparsers.add_parser('tryjobs')
+  tryjob_subparser.add_argument(
+      '--builders',
+      required=True,
+      nargs='+',
+      default=[],
+      help='builders to use for the tryjob testing')
+
+  # Add argument for custom options for the tryjob.
+  tryjob_subparser.add_argument(
+      '--options',
+      required=False,
+      nargs='+',
+      default=[],
+      help='options to use for the tryjob testing')
+
+  # Testing with CQ.
+  subparsers.add_parser('cq')
+
   args_output = parser.parse_args()
 
+  subparser_names = ['tryjobs', 'cq']
+  if args_output.subparser_name not in subparser_names:
+    parser.error('one of %s must be specified' % subparser_names)
+
   return args_output
 
 
@@ -134,6 +152,44 @@
   return arg_dict == last_arg_dict
 
 
+def AddReviewers(cl, reviewers, chroot_path):
+  """Add reviewers for the created CL."""
+
+  gerrit_abs_path = os.path.join(chroot_path, 'chromite/bin/gerrit')
+  for reviewer in reviewers:
+    cmd = [gerrit_abs_path, 'reviewers', str(cl), reviewer]
+
+    ExecCommandAndCaptureOutput(cmd)
+
+
+def AddTryjobLinkToCL(tryjobs, cl, chroot_path):
+  """Adds the tryjob link(s) to the CL as a comment."""
+
+  # NOTE: Invoking `cros_sdk` does not make each tryjob link appear on its own
+  # line, so invoking the `gerrit` command directly instead of using `cros_sdk`
+  # to do it for us.
+  #
+  # FIXME: Need to figure out why `cros_sdk` does not add each tryjob link as a
+  # newline.
+  gerrit_abs_path = os.path.join(chroot_path, 'chromite/bin/gerrit')
+
+  tryjob_links = ['Started the following tryjobs:']
+  tryjob_links.extend(tryjob['link'] for tryjob in tryjobs)
+
+  add_message_cmd = [
+      gerrit_abs_path, 'message',
+      str(cl), '\n'.join(tryjob_links)
+  ]
+
+  ExecCommandAndCaptureOutput(add_message_cmd)
+
+
+# Testing with tryjobs
+def GetCurrentTimeInUTC():
+  """Returns the current time via `datetime.datetime.utcnow()`."""
+  return datetime.datetime.utcnow()
+
+
 def GetTryJobCommand(change_list, extra_change_lists, options, builder):
   """Constructs the 'tryjob' command.
 
@@ -163,11 +219,6 @@
   return tryjob_cmd
 
 
-def GetCurrentTimeInUTC():
-  """Returns the current time via `datetime.datetime.utcnow()`."""
-  return datetime.datetime.utcnow()
-
-
 def RunTryJobs(cl_number, extra_change_lists, options, builders, chroot_path,
                verbose):
   """Runs a tryjob/tryjobs.
@@ -227,30 +278,33 @@
   return tryjob_results
 
 
-def AddTryjobLinkToCL(tryjobs, cl, chroot_path):
-  """Adds the tryjob link(s) to the CL via `gerrit message <CL> <message>`."""
+# Testing with CQ
+def GetCQDependString(dependent_cls):
+  """Get CQ dependency string e.g. `Cq-Depend: chromium:MM, chromium:NN`."""
 
-  # NOTE: Invoking `cros_sdk` does not make each tryjob link appear on its own
-  # line, so invoking the `gerrit` command directly instead of using `cros_sdk`
-  # to do it for us.
-  #
-  # FIXME: Need to figure out why `cros_sdk` does not add each tryjob link as a
-  # newline.
+  if not dependent_cls:
+    return None
+
+  # Cq-Depend must start a new paragraph prefixed with "Cq-Depend".
+  return '\nCq-Depend: ' + ', '.join(('chromium:%s' % i) for i in dependent_cls)
+
+
+def StartCQDryRun(cl, dependent_cls, chroot_path):
+  """Start CQ dry run for the changelist and dependencies."""
+
   gerrit_abs_path = os.path.join(chroot_path, 'chromite/bin/gerrit')
 
-  tryjob_links = ['Started the following tryjobs:']
-  tryjob_links.extend(tryjob['link'] for tryjob in tryjobs)
+  cl_list = [cl]
+  cl_list.extend(dependent_cls)
 
-  add_message_cmd = [
-      gerrit_abs_path, 'message',
-      str(cl), '\n'.join(tryjob_links)
-  ]
+  for changes in cl_list:
+    cq_dry_run_cmd = [gerrit_abs_path, 'label-cq', str(changes), '1']
 
-  ExecCommandAndCaptureOutput(add_message_cmd)
+    ExecCommandAndCaptureOutput(cq_dry_run_cmd)
 
 
 def main():
-  """Updates the packages' 'LLVM_NEXT_HASH' and submits tryjobs.
+  """Updates the packages' LLVM hash and run tests.
 
   Raises:
     AssertionError: The script was run inside the chroot.
@@ -282,40 +336,53 @@
     arg_dict = {
         'svn_version': svn_version,
         'ebuilds': chroot_file_paths,
-        'builders': args_output.builders,
         'extra_cls': args_output.extra_change_lists,
-        'tryjob_options': args_output.options
     }
+    if args_output.subparser_name == 'tryjobs':
+      arg_dict['builders'] = args_output.builders
+      arg_dict['tryjob_options'] = args_output.options
     if UnchangedSinceLastRun(args_output.last_tested, arg_dict):
       print('svn version (%d) matches the last tested svn version in %s' %
             (svn_version, args_output.last_tested))
       return
 
+  llvm_variant = update_chromeos_llvm_hash.LLVMVariant.current
+  if args_output.is_llvm_next:
+    llvm_variant = update_chromeos_llvm_hash.LLVMVariant.next
   update_chromeos_llvm_hash.verbose = args_output.verbose
+  extra_commit_msg = None
+  if args_output.subparser_name == 'cq':
+    extra_commit_msg = GetCQDependString(args_output.extra_change_lists)
 
   change_list = update_chromeos_llvm_hash.UpdatePackages(
       update_packages,
-      update_chromeos_llvm_hash.LLVMVariant.next,
+      llvm_variant,
       git_hash,
       svn_version,
       args_output.chroot_path,
       patch_metadata_file,
       FailureModes.DISABLE_PATCHES,
       svn_option,
-      extra_commit_msg=None)
+      extra_commit_msg=extra_commit_msg)
+
+  AddReviewers(change_list.cl_number, args_output.reviewers,
+               args_output.chroot_path)
 
   print('Successfully updated packages to %d' % svn_version)
   print('Gerrit URL: %s' % change_list.url)
   print('Change list number: %d' % change_list.cl_number)
 
-  tryjob_results = RunTryJobs(change_list.cl_number,
-                              args_output.extra_change_lists,
-                              args_output.options, args_output.builders,
-                              args_output.chroot_path, args_output.verbose)
-
-  print('Tryjobs:')
-  for tryjob in tryjob_results:
-    print(tryjob)
+  if args_output.subparser_name == 'tryjobs':
+    tryjob_results = RunTryJobs(change_list.cl_number,
+                                args_output.extra_change_lists,
+                                args_output.options, args_output.builders,
+                                args_output.chroot_path, args_output.verbose)
+    print('Tryjobs:')
+    for tryjob in tryjob_results:
+      print(tryjob)
+  else:
+    StartCQDryRun(change_list.cl_number, args_output.extra_change_lists,
+                  args_output.chroot_path)
 
   # If --last_tested is specified, record the arguments used
   if args_output.last_tested:
diff --git a/llvm_tools/update_packages_and_run_tests_unittest.py b/llvm_tools/update_packages_and_run_tests_unittest.py
new file mode 100755
index 0000000..721b995
--- /dev/null
+++ b/llvm_tools/update_packages_and_run_tests_unittest.py
@@ -0,0 +1,415 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Unittests for running tests after updating packages."""
+
+from __future__ import print_function
+
+import json
+import unittest
+import unittest.mock as mock
+
+from test_helpers import ArgsOutputTest
+from test_helpers import CreateTemporaryFile
+from update_chromeos_llvm_hash import CommitContents
+import update_chromeos_llvm_hash
+import update_packages_and_run_tests
+
+
+# Testing with tryjobs.
+class UpdatePackagesAndRunTryjobsTest(unittest.TestCase):
+  """Unittests when running tryjobs after updating packages."""
+
+  def testNoLastTestedFile(self):
+    self.assertEqual(
+        update_packages_and_run_tests.UnchangedSinceLastRun(None, {}), False)
+
+  def testEmptyLastTestedFile(self):
+    with CreateTemporaryFile() as temp_file:
+      self.assertEqual(
+          update_packages_and_run_tests.UnchangedSinceLastRun(temp_file, {}),
+          False)
+
+  def testLastTestedFileDoesNotExist(self):
+    # Simulate 'open()' on a lasted tested file that does not exist.
+    mock.mock_open(read_data='')
+
+    self.assertEqual(
+        update_packages_and_run_tests.UnchangedSinceLastRun(
+            '/some/file/that/does/not/exist.txt', {}), False)
+
+  def testMatchedLastTestedFile(self):
+    with CreateTemporaryFile() as last_tested_file:
+      arg_dict = {
+          'svn_version':
+              1234,
+          'ebuilds': [
+              '/path/to/package1-r2.ebuild',
+              '/path/to/package2/package2-r3.ebuild'
+          ],
+          'builders': [
+              'kevin-llvm-next-toolchain-tryjob',
+              'eve-llvm-next-toolchain-tryjob'
+          ],
+          'extra_cls': [10, 1],
+          'tryjob_options': ['latest-toolchain', 'hwtest']
+      }
+
+      with open(last_tested_file, 'w') as f:
+        f.write(json.dumps(arg_dict, indent=2))
+
+      self.assertEqual(
+          update_packages_and_run_tests.UnchangedSinceLastRun(
+              last_tested_file, arg_dict), True)
+
+  def testGetTryJobCommandWithNoExtraInformation(self):
+    test_change_list = 1234
+
+    test_builder = 'nocturne'
+
+    expected_tryjob_cmd_list = [
+        'cros', 'tryjob', '--yes', '--json', '-g',
+        '%d' % test_change_list, test_builder
+    ]
+
+    self.assertEqual(
+        update_packages_and_run_tests.GetTryJobCommand(test_change_list, None,
+                                                       None, test_builder),
+        expected_tryjob_cmd_list)
+
+  def testGetTryJobCommandWithExtraInformation(self):
+    test_change_list = 4321
+    test_extra_cls = [1000, 10]
+    test_options = ['report_error', 'delete_tryjob']
+    test_builder = 'kevin'
+
+    expected_tryjob_cmd_list = [
+        'cros',
+        'tryjob',
+        '--yes',
+        '--json',
+        '-g',
+        '%d' % test_change_list,
+        '-g',
+        '%d' % test_extra_cls[0],
+        '-g',
+        '%d' % test_extra_cls[1],
+        test_builder,
+        '--%s' % test_options[0],
+        '--%s' % test_options[1],
+    ]
+
+    self.assertEqual(
+        update_packages_and_run_tests.GetTryJobCommand(
+            test_change_list, test_extra_cls, test_options, test_builder),
+        expected_tryjob_cmd_list)
+
+  @mock.patch.object(
+      update_packages_and_run_tests,
+      'GetCurrentTimeInUTC',
+      return_value='2019-09-09')
+  @mock.patch.object(update_packages_and_run_tests, 'AddTryjobLinkToCL')
+  @mock.patch.object(update_packages_and_run_tests, 'ChrootRunCommand')
+  def testSuccessfullySubmittedTryJob(
+      self, mock_chroot_cmd, mock_add_tryjob_link_to_cl, mock_launch_time):
+
+    expected_tryjob_cmd_list = [
+        'cros', 'tryjob', '--yes', '--json', '-g',
+        '%d' % 900, '-g',
+        '%d' % 1200, 'builder1', '--some_option'
+    ]
+
+    buildbucket_id = '1234'
+    url = 'https://some_tryjob_url.com'
+
+    tryjob_launch_contents = [{'buildbucket_id': buildbucket_id, 'url': url}]
+
+    mock_chroot_cmd.return_value = json.dumps(tryjob_launch_contents)
+
+    extra_cls = [1200]
+    tryjob_options = ['some_option']
+    builder_list = ['builder1']
+    chroot_path = '/some/path/to/chroot'
+    cl_to_launch_tryjob = 900
+    verbose = False
+
+    tryjob_results_list = update_packages_and_run_tests.RunTryJobs(
+        cl_to_launch_tryjob, extra_cls, tryjob_options, builder_list,
+        chroot_path, verbose)
+
+    expected_tryjob_dict = {
+        'launch_time': mock_launch_time.return_value,
+        'link': url,
+        'buildbucket_id': int(buildbucket_id),
+        'extra_cls': extra_cls,
+        'options': tryjob_options,
+        'builder': builder_list
+    }
+
+    self.assertEqual(tryjob_results_list, [expected_tryjob_dict])
+
+    mock_chroot_cmd.assert_called_once_with(
+        chroot_path, expected_tryjob_cmd_list, verbose=False)
+
+    mock_add_tryjob_link_to_cl.assert_called_once()
+
+  @mock.patch.object(
+      update_packages_and_run_tests,
+      'ExecCommandAndCaptureOutput',
+      return_value=None)
+  def testSuccessfullyAddedTryjobLinkToCL(self, mock_exec_cmd):
+    chroot_path = '/abs/path/to/chroot'
+
+    test_cl_number = 1000
+
+    tryjob_result = [{'link': 'https://some_tryjob_link.com'}]
+
+    update_packages_and_run_tests.AddTryjobLinkToCL(tryjob_result,
+                                                    test_cl_number, chroot_path)
+
+    expected_gerrit_message = [
+        '%s/chromite/bin/gerrit' % chroot_path, 'message',
+        str(test_cl_number),
+        'Started the following tryjobs:\n%s' % tryjob_result[0]['link']
+    ]
+
+    mock_exec_cmd.assert_called_once_with(expected_gerrit_message)
+
+  @mock.patch.object(update_packages_and_run_tests, 'RunTryJobs')
+  @mock.patch.object(update_chromeos_llvm_hash, 'UpdatePackages')
+  @mock.patch.object(update_packages_and_run_tests, 'GetCommandLineArgs')
+  @mock.patch.object(update_packages_and_run_tests,
+                     'GetLLVMHashAndVersionFromSVNOption')
+  @mock.patch.object(
+      update_packages_and_run_tests, 'VerifyOutsideChroot', return_value=True)
+  @mock.patch.object(update_chromeos_llvm_hash, 'GetChrootBuildPaths')
+  def testUpdatedLastTestedFileWithNewTestedRevision(
+      self, mock_get_chroot_build_paths, mock_outside_chroot,
+      mock_get_hash_and_version, mock_get_commandline_args,
+      mock_update_packages, mock_run_tryjobs):
+
+    # Create a temporary file to simulate the last tested file that contains a
+    # revision.
+    with CreateTemporaryFile() as last_tested_file:
+      builders = [
+          'kevin-llvm-next-toolchain-tryjob', 'eve-llvm-next-toolchain-tryjob'
+      ]
+      extra_cls = [10, 1]
+      tryjob_options = ['latest-toolchain', 'hwtest']
+      ebuilds = [
+          '/path/to/package1/package1-r2.ebuild',
+          '/path/to/package2/package2-r3.ebuild'
+      ]
+
+      arg_dict = {
+          'svn_version': 100,
+          'ebuilds': ebuilds,
+          'builders': builders,
+          'extra_cls': extra_cls,
+          'tryjob_options': tryjob_options
+      }
+      # Parepared last tested file
+      with open(last_tested_file, 'w') as f:
+        json.dump(arg_dict, f, indent=2)
+
+      # Call with a changed LLVM svn version
+      args_output = ArgsOutputTest()
+      args_output.is_llvm_next = True
+      args_output.extra_change_lists = extra_cls
+      args_output.last_tested = last_tested_file
+      args_output.reviewers = []
+
+      args_output.subparser_name = 'tryjobs'
+      args_output.builders = builders
+      args_output.options = tryjob_options
+
+      mock_get_commandline_args.return_value = args_output
+
+      mock_get_chroot_build_paths.return_value = ebuilds
+
+      mock_get_hash_and_version.return_value = ('a123testhash2', 200)
+
+      mock_update_packages.return_value = CommitContents(
+          url='https://some_cl_url.com', cl_number=12345)
+
+      mock_run_tryjobs.return_value = [{
+          'link': 'https://some_tryjob_url.com',
+          'buildbucket_id': 1234
+      }]
+
+      update_packages_and_run_tests.main()
+
+      # Verify that the lasted tested file has been updated to the new LLVM
+      # revision.
+      with open(last_tested_file) as f:
+        arg_dict = json.load(f)
+
+        self.assertEqual(arg_dict['svn_version'], 200)
+
+    mock_outside_chroot.assert_called_once()
+
+    mock_get_commandline_args.assert_called_once()
+
+    mock_get_hash_and_version.assert_called_once()
+
+    mock_run_tryjobs.assert_called_once()
+
+    mock_update_packages.assert_called_once()
+
+
+class UpdatePackagesAndRunTestCQTest(unittest.TestCase):
+  """Unittests for CQ dry run after updating packages."""
+
+  def testGetCQDependString(self):
+    test_no_changelists = []
+    test_single_changelist = [1234]
+    test_multiple_changelists = [1234, 5678]
+
+    self.assertEqual(
+        update_packages_and_run_tests.GetCQDependString(test_no_changelists),
+        None)
+
+    self.assertEqual(
+        update_packages_and_run_tests.GetCQDependString(test_single_changelist),
+        '\nCq-Depend: chromium:1234')
+
+    self.assertEqual(
+        update_packages_and_run_tests.GetCQDependString(
+            test_multiple_changelists),
+        '\nCq-Depend: chromium:1234, chromium:5678')
+
+  # Mock ExecCommandAndCaptureOutput for the gerrit command execution.
+  @mock.patch.object(
+      update_packages_and_run_tests,
+      'ExecCommandAndCaptureOutput',
+      return_value=None)
+  def testStartCQDryRunNoDeps(self, mock_exec_cmd):
+    chroot_path = '/abs/path/to/chroot'
+    test_cl_number = 1000
+
+    # test with no deps cls.
+    extra_cls = []
+    update_packages_and_run_tests.StartCQDryRun(test_cl_number, extra_cls,
+                                                chroot_path)
+
+    expected_gerrit_message = [
+        '%s/chromite/bin/gerrit' % chroot_path, 'label-cq',
+        str(test_cl_number), '1'
+    ]
+
+    mock_exec_cmd.assert_called_once_with(expected_gerrit_message)
+
+  # Mock ExecCommandAndCaptureOutput for the gerrit command execution.
+  @mock.patch.object(
+      update_packages_and_run_tests,
+      'ExecCommandAndCaptureOutput',
+      return_value=None)
+  # test with a single deps cl.
+  def testStartCQDryRunSingleDep(self, mock_exec_cmd):
+    chroot_path = '/abs/path/to/chroot'
+    test_cl_number = 1000
+
+    extra_cls = [2000]
+    update_packages_and_run_tests.StartCQDryRun(test_cl_number, extra_cls,
+                                                chroot_path)
+
+    expected_gerrit_cmd_1 = [
+        '%s/chromite/bin/gerrit' % chroot_path, 'label-cq',
+        str(test_cl_number), '1'
+    ]
+    expected_gerrit_cmd_2 = [
+        '%s/chromite/bin/gerrit' % chroot_path, 'label-cq',
+        str(2000), '1'
+    ]
+
+    self.assertEqual(mock_exec_cmd.call_count, 2)
+    self.assertEqual(mock_exec_cmd.call_args_list[0],
+                     mock.call(expected_gerrit_cmd_1))
+    self.assertEqual(mock_exec_cmd.call_args_list[1],
+                     mock.call(expected_gerrit_cmd_2))
+
+  # Mock ExecCommandAndCaptureOutput for the gerrit command execution.
+  @mock.patch.object(
+      update_packages_and_run_tests,
+      'ExecCommandAndCaptureOutput',
+      return_value=None)
+  def testStartCQDryRunMultipleDep(self, mock_exec_cmd):
+    chroot_path = '/abs/path/to/chroot'
+    test_cl_number = 1000
+
+    # test with multiple deps cls.
+    extra_cls = [3000, 4000]
+    update_packages_and_run_tests.StartCQDryRun(test_cl_number, extra_cls,
+                                                chroot_path)
+
+    expected_gerrit_cmd_1 = [
+        '%s/chromite/bin/gerrit' % chroot_path, 'label-cq',
+        str(test_cl_number), '1'
+    ]
+    expected_gerrit_cmd_2 = [
+        '%s/chromite/bin/gerrit' % chroot_path, 'label-cq',
+        str(3000), '1'
+    ]
+    expected_gerrit_cmd_3 = [
+        '%s/chromite/bin/gerrit' % chroot_path, 'label-cq',
+        str(4000), '1'
+    ]
+
+    self.assertEqual(mock_exec_cmd.call_count, 3)
+    self.assertEqual(mock_exec_cmd.call_args_list[0],
+                     mock.call(expected_gerrit_cmd_1))
+    self.assertEqual(mock_exec_cmd.call_args_list[1],
+                     mock.call(expected_gerrit_cmd_2))
+    self.assertEqual(mock_exec_cmd.call_args_list[2],
+                     mock.call(expected_gerrit_cmd_3))
+
+  # Mock ExecCommandAndCaptureOutput for the gerrit command execution.
+  @mock.patch.object(
+      update_packages_and_run_tests,
+      'ExecCommandAndCaptureOutput',
+      return_value=None)
+  # test with no reviewers.
+  def testAddReviewersNone(self, mock_exec_cmd):
+    chroot_path = '/abs/path/to/chroot'
+    reviewers = []
+    test_cl_number = 1000
+
+    update_packages_and_run_tests.AddReviewers(test_cl_number, reviewers,
+                                               chroot_path)
+    self.assertTrue(mock_exec_cmd.not_called)
+
+  # Mock ExecCommandAndCaptureOutput for the gerrit command execution.
+  @mock.patch.object(
+      update_packages_and_run_tests,
+      'ExecCommandAndCaptureOutput',
+      return_value=None)
+  # test with multiple reviewers.
+  def testAddReviewersMultiple(self, mock_exec_cmd):
+    chroot_path = '/abs/path/to/chroot'
+    reviewers = ['none1@chromium.org', 'none2@chromium.org']
+    test_cl_number = 1000
+
+    update_packages_and_run_tests.AddReviewers(test_cl_number, reviewers,
+                                               chroot_path)
+
+    expected_gerrit_cmd_1 = [
+        '%s/chromite/bin/gerrit' % chroot_path, 'reviewers',
+        str(test_cl_number), 'none1@chromium.org'
+    ]
+    expected_gerrit_cmd_2 = [
+        '%s/chromite/bin/gerrit' % chroot_path, 'reviewers',
+        str(test_cl_number), 'none2@chromium.org'
+    ]
+
+    self.assertEqual(mock_exec_cmd.call_count, 2)
+    self.assertEqual(mock_exec_cmd.call_args_list[0],
+                     mock.call(expected_gerrit_cmd_1))
+    self.assertEqual(mock_exec_cmd.call_args_list[1],
+                     mock.call(expected_gerrit_cmd_2))
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/llvm_tools/update_packages_and_run_tryjobs_unittest.py b/llvm_tools/update_packages_and_run_tryjobs_unittest.py
deleted file mode 100755
index 7352f4c..0000000
--- a/llvm_tools/update_packages_and_run_tryjobs_unittest.py
+++ /dev/null
@@ -1,261 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-# Copyright 2019 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Unittests for running tryjobs after updating packages."""
-
-from __future__ import print_function
-
-import json
-import unittest
-import unittest.mock as mock
-
-from test_helpers import ArgsOutputTest
-from test_helpers import CreateTemporaryFile
-from update_chromeos_llvm_hash import CommitContents
-import update_chromeos_llvm_hash
-import update_packages_and_run_tryjobs
-
-
-class UpdatePackagesAndRunTryjobsTest(unittest.TestCase):
-  """Unittests when running tryjobs after updating packages."""
-
-  def testNoLastTestedFile(self):
-    self.assertEqual(
-        update_packages_and_run_tryjobs.UnchangedSinceLastRun(None, {}), False)
-
-  def testEmptyLastTestedFile(self):
-    with CreateTemporaryFile() as temp_file:
-      self.assertEqual(
-          update_packages_and_run_tryjobs.UnchangedSinceLastRun(temp_file, {}),
-          False)
-
-  def testLastTestedFileDoesNotExist(self):
-    # Simulate 'open()' on a lasted tested file that does not exist.
-    mock.mock_open(read_data='')
-
-    self.assertEqual(
-        update_packages_and_run_tryjobs.UnchangedSinceLastRun(
-            '/some/file/that/does/not/exist.txt', {}), False)
-
-  def testMatchedLastTestedFile(self):
-    with CreateTemporaryFile() as last_tested_file:
-      arg_dict = {
-          'svn_version':
-              1234,
-          'ebuilds': [
-              '/path/to/package1-r2.ebuild',
-              '/path/to/package2/package2-r3.ebuild'
-          ],
-          'builders': [
-              'kevin-llvm-next-toolchain-tryjob',
-              'eve-llvm-next-toolchain-tryjob'
-          ],
-          'extra_cls': [10, 1],
-          'tryjob_options': ['latest-toolchain', 'hwtest']
-      }
-
-      with open(last_tested_file, 'w') as f:
-        f.write(json.dumps(arg_dict, indent=2))
-
-      self.assertEqual(
-          update_packages_and_run_tryjobs.UnchangedSinceLastRun(
-              last_tested_file, arg_dict), True)
-
-  def testGetTryJobCommandWithNoExtraInformation(self):
-    test_change_list = 1234
-
-    test_builder = 'nocturne'
-
-    expected_tryjob_cmd_list = [
-        'cros', 'tryjob', '--yes', '--json', '-g',
-        '%d' % test_change_list, test_builder
-    ]
-
-    self.assertEqual(
-        update_packages_and_run_tryjobs.GetTryJobCommand(
-            test_change_list, None, None, test_builder),
-        expected_tryjob_cmd_list)
-
-  def testGetTryJobCommandWithExtraInformation(self):
-    test_change_list = 4321
-    test_extra_cls = [1000, 10]
-    test_options = ['report_error', 'delete_tryjob']
-    test_builder = 'kevin'
-
-    expected_tryjob_cmd_list = [
-        'cros',
-        'tryjob',
-        '--yes',
-        '--json',
-        '-g',
-        '%d' % test_change_list,
-        '-g',
-        '%d' % test_extra_cls[0],
-        '-g',
-        '%d' % test_extra_cls[1],
-        test_builder,
-        '--%s' % test_options[0],
-        '--%s' % test_options[1],
-    ]
-
-    self.assertEqual(
-        update_packages_and_run_tryjobs.GetTryJobCommand(
-            test_change_list, test_extra_cls, test_options, test_builder),
-        expected_tryjob_cmd_list)
-
-  @mock.patch.object(
-      update_packages_and_run_tryjobs,
-      'GetCurrentTimeInUTC',
-      return_value='2019-09-09')
-  @mock.patch.object(update_packages_and_run_tryjobs, 'AddTryjobLinkToCL')
-  @mock.patch.object(update_packages_and_run_tryjobs, 'ChrootRunCommand')
-  def testSuccessfullySubmittedTryJob(
-      self, mock_chroot_cmd, mock_add_tryjob_link_to_cl, mock_launch_time):
-
-    expected_tryjob_cmd_list = [
-        'cros', 'tryjob', '--yes', '--json', '-g',
-        '%d' % 900, '-g',
-        '%d' % 1200, 'builder1', '--some_option'
-    ]
-
-    buildbucket_id = '1234'
-    url = 'https://some_tryjob_url.com'
-
-    tryjob_launch_contents = [{'buildbucket_id': buildbucket_id, 'url': url}]
-
-    mock_chroot_cmd.return_value = json.dumps(tryjob_launch_contents)
-
-    extra_cls = [1200]
-    tryjob_options = ['some_option']
-    builder_list = ['builder1']
-    chroot_path = '/some/path/to/chroot'
-    cl_to_launch_tryjob = 900
-    verbose = False
-
-    tryjob_results_list = update_packages_and_run_tryjobs.RunTryJobs(
-        cl_to_launch_tryjob, extra_cls, tryjob_options, builder_list,
-        chroot_path, verbose)
-
-    expected_tryjob_dict = {
-        'launch_time': '2019-09-09',
-        'link': url,
-        'buildbucket_id': int(buildbucket_id),
-        'extra_cls': extra_cls,
-        'options': tryjob_options,
-        'builder': builder_list
-    }
-
-    self.assertEqual(tryjob_results_list, [expected_tryjob_dict])
-
-    mock_chroot_cmd.assert_called_once_with(
-        chroot_path, expected_tryjob_cmd_list, verbose=False)
-
-    mock_add_tryjob_link_to_cl.assert_called_once()
-
-    mock_launch_time.assert_called_once()
-
-  @mock.patch.object(
-      update_packages_and_run_tryjobs,
-      'ExecCommandAndCaptureOutput',
-      return_value=None)
-  def testSuccessfullyAddedTryjobLinkToCL(self, mock_exec_cmd):
-    chroot_path = '/abs/path/to/chroot'
-
-    test_cl_number = 1000
-
-    tryjob_result = [{'link': 'https://some_tryjob_link.com'}]
-
-    update_packages_and_run_tryjobs.AddTryjobLinkToCL(
-        tryjob_result, test_cl_number, chroot_path)
-
-    expected_gerrit_message = [
-        '%s/chromite/bin/gerrit' % chroot_path, 'message',
-        str(test_cl_number),
-        'Started the following tryjobs:\n%s' % tryjob_result[0]['link']
-    ]
-
-    mock_exec_cmd.assert_called_once_with(expected_gerrit_message)
-
-  @mock.patch.object(update_packages_and_run_tryjobs, 'RunTryJobs')
-  @mock.patch.object(update_chromeos_llvm_hash, 'UpdatePackages')
-  @mock.patch.object(update_packages_and_run_tryjobs, 'GetCommandLineArgs')
-  @mock.patch.object(update_packages_and_run_tryjobs,
-                     'GetLLVMHashAndVersionFromSVNOption')
-  @mock.patch.object(
-      update_packages_and_run_tryjobs, 'VerifyOutsideChroot', return_value=True)
-  @mock.patch.object(update_chromeos_llvm_hash, 'GetChrootBuildPaths')
-  def testUpdatedLastTestedFileWithNewTestedRevision(
-      self, mock_get_chroot_build_paths, mock_outside_chroot,
-      mock_get_hash_and_version, mock_get_commandline_args,
-      mock_update_packages, mock_run_tryjobs):
-
-    # Create a temporary file to simulate the last tested file that contains a
-    # revision.
-    with CreateTemporaryFile() as last_tested_file:
-      builders = [
-          'kevin-llvm-next-toolchain-tryjob', 'eve-llvm-next-toolchain-tryjob'
-      ]
-      extra_cls = [10, 1]
-      tryjob_options = ['latest-toolchain', 'hwtest']
-      ebuilds = [
-          '/path/to/package1/package1-r2.ebuild',
-          '/path/to/package2/package2-r3.ebuild'
-      ]
-
-      arg_dict = {
-          'svn_version': 100,
-          'ebuilds': ebuilds,
-          'builders': builders,
-          'extra_cls': extra_cls,
-          'tryjob_options': tryjob_options
-      }
-      # Parepared last tested file
-      with open(last_tested_file, 'w') as f:
-        json.dump(arg_dict, f, indent=2)
-
-      # Call with a changed LLVM svn version
-      args_output = ArgsOutputTest()
-      args_output.builders = builders
-      args_output.extra_change_lists = extra_cls
-      args_output.options = tryjob_options
-      args_output.last_tested = last_tested_file
-
-      mock_get_commandline_args.return_value = args_output
-
-      mock_get_chroot_build_paths.return_value = ebuilds
-
-      mock_get_hash_and_version.return_value = ('a123testhash2', 200)
-
-      mock_update_packages.return_value = CommitContents(
-          url='https://some_cl_url.com', cl_number=12345)
-
-      mock_run_tryjobs.return_value = [{
-          'link': 'https://some_tryjob_url.com',
-          'buildbucket_id': 1234
-      }]
-
-      update_packages_and_run_tryjobs.main()
-
-      # Verify that the lasted tested file has been updated to the new LLVM
-      # revision.
-      with open(last_tested_file) as f:
-        arg_dict = json.load(f)
-
-        self.assertEqual(arg_dict['svn_version'], 200)
-
-    mock_outside_chroot.assert_called_once()
-
-    mock_get_commandline_args.assert_called_once()
-
-    mock_get_hash_and_version.assert_called_once()
-
-    mock_run_tryjobs.assert_called_once()
-
-    mock_update_packages.assert_called_once()
-
-
-if __name__ == '__main__':
-  unittest.main()
diff --git a/llvm_tools/update_packages_and_test_cq.py b/llvm_tools/update_packages_and_test_cq.py
deleted file mode 100755
index 15246e4..0000000
--- a/llvm_tools/update_packages_and_test_cq.py
+++ /dev/null
@@ -1,226 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Runs CQ dry run after updating the packages."""
-
-from __future__ import print_function
-
-import argparse
-import datetime
-import json
-import os
-
-from assert_not_in_chroot import VerifyOutsideChroot
-from failure_modes import FailureModes
-from get_llvm_hash import GetLLVMHashAndVersionFromSVNOption
-from get_llvm_hash import is_svn_option
-from subprocess_helpers import ChrootRunCommand
-from subprocess_helpers import ExecCommandAndCaptureOutput
-import update_chromeos_llvm_hash
-
-
-def GetCommandLineArgs():
-  """Parses the command line for the command line arguments.
-
-  Returns:
-    The log level to use when retrieving the LLVM hash or google3 LLVM version,
-    the chroot path to use for executing chroot commands,
-    a list of a package or packages to update their LLVM next hash,
-    and the LLVM version to use when retrieving the LLVM hash.
-  """
-
-  # Default path to the chroot if a path is not specified.
-  cros_root = os.path.expanduser('~')
-  cros_root = os.path.join(cros_root, 'chromiumos')
-
-  # Create parser and add optional command-line arguments.
-  parser = argparse.ArgumentParser(
-      description=
-      'Runs a tryjob if successfully updated LLVM_NEXT_HASH of packages.')
-
-  # Add argument for the absolute path to the file that contains information on
-  # the previous tested svn version.
-  parser.add_argument(
-      '--last_tested',
-      help='the absolute path to the file that contains the last tested '
-      'svn version')
-
-  # Add argument for other change lists that want to run alongside the tryjob
-  # which has a change list of updating a package's git hash.
-  parser.add_argument(
-      '--extra_change_lists',
-      type=int,
-      default=[],
-      nargs='+',
-      help='change lists that would like to be run alongside the change list '
-      'of updating the packages')
-
-  # Add argument for a specific chroot path.
-  parser.add_argument(
-      '--chroot_path',
-      default=cros_root,
-      help='the path to the chroot (default: %(default)s)')
-
-  # Add argument to choose between llvm and llvm-next.
-  parser.add_argument(
-      '--is_llvm_next',
-      action='store_true',
-      help='which llvm hash to update. Update LLVM_NEXT_HASH if specified. '
-      'Otherwise, update LLVM_HASH')
-
-  # Add argument to add reviewers for the created CL.
-  parser.add_argument(
-      '--reviewers',
-      nargs='+',
-      default=[],
-      help='The reviewers for the package update changelist')
-
-  # Add argument for whether to display command contents to `stdout`.
-  parser.add_argument(
-      '--verbose',
-      action='store_true',
-      help='display contents of a command to the terminal '
-      '(default: %(default)s)')
-
-  # Add argument for the LLVM version to use.
-  parser.add_argument(
-      '--llvm_version',
-      type=is_svn_option,
-      required=True,
-      help='which git hash of LLVM to find '
-      '{google3, ToT, <svn_version>} '
-      '(default: finds the git hash of the google3 LLVM '
-      'version)')
-
-  args_output = parser.parse_args()
-
-  return args_output
-
-
-def GetLastTestedSVNVersion(last_tested_file):
-  """Gets the lasted tested svn version from the file.
-
-  Args:
-    last_tested_file: The absolute path to the file that contains the last
-    tested svn version.
-
-  Returns:
-    The last tested svn version or 'None' if the file did not have a last tested
-    svn version (the file exists, but failed to convert the contents to an
-    integer) or the file does not exist.
-  """
-
-  if not last_tested_file:
-    return None
-
-  last_svn_version = None
-
-  # Get the last tested svn version if the file exists.
-  try:
-    with open(last_tested_file) as file_obj:
-      # For now, the first line contains the last tested svn version.
-      return int(file_obj.read().rstrip())
-
-  except (IOError, ValueError):
-    pass
-
-  return last_svn_version
-
-
-def GetCQDependString(dependent_cls):
-  """Get CQ dependency string e.g. `Cq-Depend: chromium:MM, chromium:NN`."""
-
-  if not dependent_cls:
-    return None
-
-  # Cq-Depend must start a new paragraph prefixed with "Cq-Depend".
-  return '\nCq-Depend: ' + ', '.join(('chromium:%s' % i) for i in dependent_cls)
-
-
-def AddReviewers(cl, reviewers, chroot_path):
-  """Add reviewers for the created CL."""
-
-  gerrit_abs_path = os.path.join(chroot_path, 'chromite/bin/gerrit')
-  for reviewer in reviewers:
-    cmd = [gerrit_abs_path, 'reviewers', str(cl), reviewer]
-
-    ExecCommandAndCaptureOutput(cmd)
-
-
-def StartCQDryRun(cl, dependent_cls, chroot_path):
-  """Start CQ dry run for the changelist and dependencies."""
-
-  gerrit_abs_path = os.path.join(chroot_path, 'chromite/bin/gerrit')
-
-  cl_list = [cl]
-  cl_list.extend(dependent_cls)
-
-  for changes in cl_list:
-    cq_dry_run_cmd = [gerrit_abs_path, 'label-cq', str(changes), '1']
-
-    ExecCommandAndCaptureOutput(cq_dry_run_cmd)
-
-
-def main():
-  """Updates the packages' 'LLVM_NEXT_HASH' and submits tryjobs.
-
-  Raises:
-    AssertionError: The script was run inside the chroot.
-  """
-
-  VerifyOutsideChroot()
-
-  args_output = GetCommandLineArgs()
-
-  last_svn_version = GetLastTestedSVNVersion(args_output.last_tested)
-
-  update_packages = [
-      'sys-devel/llvm', 'sys-libs/compiler-rt', 'sys-libs/libcxx',
-      'sys-libs/libcxxabi', 'sys-libs/llvm-libunwind'
-  ]
-
-  patch_metadata_file = 'PATCHES.json'
-
-  svn_option = args_output.llvm_version
-
-  git_hash, svn_version = GetLLVMHashAndVersionFromSVNOption(svn_option)
-
-  # There is no need to run tryjobs when the SVN version matches the last tested
-  # SVN version.
-  if last_svn_version == svn_version:
-    print('svn version (%d) matches the last tested svn version (%d) in %s' %
-          (svn_version, last_svn_version, args_output.last_tested))
-    return
-
-  llvm_variant = update_chromeos_llvm_hash.LLVMVariant.current
-  if args_output.is_llvm_next:
-    llvm_variant = update_chromeos_llvm_hash.LLVMVariant.next
-  update_chromeos_llvm_hash.verbose = args_output.verbose
-  extra_commit_msg = GetCQDependString(args_output.extra_change_lists)
-
-  change_list = update_chromeos_llvm_hash.UpdatePackages(
-      update_packages, llvm_variant, git_hash, svn_version,
-      args_output.chroot_path, patch_metadata_file,
-      FailureModes.DISABLE_PATCHES, svn_option, extra_commit_msg)
-
-  print('Successfully updated packages to %d' % svn_version)
-  print('Gerrit URL: %s' % change_list.url)
-  print('Change list number: %d' % change_list.cl_number)
-
-  AddReviewers(change_list.cl_number, args_output.reviewers,
-               args_output.chroot_path)
-  StartCQDryRun(change_list.cl_number, args_output.extra_change_lists,
-                args_output.chroot_path)
-
-  # Updated the packages and submitted tryjobs successfully, so the file will
-  # contain 'svn_version' which will now become the last tested svn version.
-  if args_output.last_tested:
-    with open(args_output.last_tested, 'w', encoding='utf-8') as file_obj:
-      file_obj.write(str(svn_version))
-
-
-if __name__ == '__main__':
-  main()
diff --git a/llvm_tools/update_packages_and_test_cq_unittest.py b/llvm_tools/update_packages_and_test_cq_unittest.py
deleted file mode 100755
index acf17c6..0000000
--- a/llvm_tools/update_packages_and_test_cq_unittest.py
+++ /dev/null
@@ -1,174 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Unittests for running tryjobs after updating packages."""
-
-from __future__ import print_function
-
-import json
-import unittest
-import unittest.mock as mock
-
-from test_helpers import ArgsOutputTest
-from test_helpers import CreateTemporaryFile
-from update_chromeos_llvm_hash import CommitContents
-import update_chromeos_llvm_hash
-import update_packages_and_test_cq
-
-
-class UpdatePackagesAndRunTestCQTest(unittest.TestCase):
-  """Unittests for CQ dry run after updating packages."""
-
-  def testGetCQDependString(self):
-    test_no_changelists = []
-    test_single_changelist = [1234]
-    test_multiple_changelists = [1234, 5678]
-
-    self.assertEqual(
-        update_packages_and_test_cq.GetCQDependString(test_no_changelists),
-        None)
-
-    self.assertEqual(
-        update_packages_and_test_cq.GetCQDependString(test_single_changelist),
-        '\nCq-Depend: chromium:1234')
-
-    self.assertEqual(
-        update_packages_and_test_cq.GetCQDependString(
-            test_multiple_changelists),
-        '\nCq-Depend: chromium:1234, chromium:5678')
-
-  # Mock ExecCommandAndCaptureOutput for the gerrit command execution.
-  @mock.patch.object(
-      update_packages_and_test_cq,
-      'ExecCommandAndCaptureOutput',
-      return_value=None)
-  def testStartCQDryRunNoDeps(self, mock_exec_cmd):
-    chroot_path = '/abs/path/to/chroot'
-    test_cl_number = 1000
-
-    # test with no deps cls.
-    extra_cls = []
-    update_packages_and_test_cq.StartCQDryRun(test_cl_number, extra_cls,
-                                              chroot_path)
-
-    expected_gerrit_message = [
-        '%s/chromite/bin/gerrit' % chroot_path, 'label-cq',
-        str(test_cl_number), '1'
-    ]
-
-    mock_exec_cmd.assert_called_once_with(expected_gerrit_message)
-
-  # Mock ExecCommandAndCaptureOutput for the gerrit command execution.
-  @mock.patch.object(
-      update_packages_and_test_cq,
-      'ExecCommandAndCaptureOutput',
-      return_value=None)
-  # test with a single deps cl.
-  def testStartCQDryRunSingleDep(self, mock_exec_cmd):
-    chroot_path = '/abs/path/to/chroot'
-    test_cl_number = 1000
-
-    extra_cls = [2000]
-    update_packages_and_test_cq.StartCQDryRun(test_cl_number, extra_cls,
-                                              chroot_path)
-
-    expected_gerrit_cmd_1 = [
-        '%s/chromite/bin/gerrit' % chroot_path, 'label-cq',
-        str(test_cl_number), '1'
-    ]
-    expected_gerrit_cmd_2 = [
-        '%s/chromite/bin/gerrit' % chroot_path, 'label-cq',
-        str(2000), '1'
-    ]
-
-    self.assertEqual(mock_exec_cmd.call_count, 2)
-    self.assertEqual(mock_exec_cmd.call_args_list[0],
-                     mock.call(expected_gerrit_cmd_1))
-    self.assertEqual(mock_exec_cmd.call_args_list[1],
-                     mock.call(expected_gerrit_cmd_2))
-
-  # Mock ExecCommandAndCaptureOutput for the gerrit command execution.
-  @mock.patch.object(
-      update_packages_and_test_cq,
-      'ExecCommandAndCaptureOutput',
-      return_value=None)
-  def testStartCQDryRunMultipleDep(self, mock_exec_cmd):
-    chroot_path = '/abs/path/to/chroot'
-    test_cl_number = 1000
-
-    # test with multiple deps cls.
-    extra_cls = [3000, 4000]
-    update_packages_and_test_cq.StartCQDryRun(test_cl_number, extra_cls,
-                                              chroot_path)
-
-    expected_gerrit_cmd_1 = [
-        '%s/chromite/bin/gerrit' % chroot_path, 'label-cq',
-        str(test_cl_number), '1'
-    ]
-    expected_gerrit_cmd_2 = [
-        '%s/chromite/bin/gerrit' % chroot_path, 'label-cq',
-        str(3000), '1'
-    ]
-    expected_gerrit_cmd_3 = [
-        '%s/chromite/bin/gerrit' % chroot_path, 'label-cq',
-        str(4000), '1'
-    ]
-
-    self.assertEqual(mock_exec_cmd.call_count, 3)
-    self.assertEqual(mock_exec_cmd.call_args_list[0],
-                     mock.call(expected_gerrit_cmd_1))
-    self.assertEqual(mock_exec_cmd.call_args_list[1],
-                     mock.call(expected_gerrit_cmd_2))
-    self.assertEqual(mock_exec_cmd.call_args_list[2],
-                     mock.call(expected_gerrit_cmd_3))
-
-  # Mock ExecCommandAndCaptureOutput for the gerrit command execution.
-  @mock.patch.object(
-      update_packages_and_test_cq,
-      'ExecCommandAndCaptureOutput',
-      return_value=None)
-  # test with no reviewers.
-  def testAddReviewersNone(self, mock_exec_cmd):
-    chroot_path = '/abs/path/to/chroot'
-    reviewers = []
-    test_cl_number = 1000
-
-    update_packages_and_test_cq.AddReviewers(test_cl_number, reviewers,
-                                             chroot_path)
-    self.assertTrue(mock_exec_cmd.not_called)
-
-  # Mock ExecCommandAndCaptureOutput for the gerrit command execution.
-  @mock.patch.object(
-      update_packages_and_test_cq,
-      'ExecCommandAndCaptureOutput',
-      return_value=None)
-  # test with multiple reviewers.
-  def testAddReviewersMultiple(self, mock_exec_cmd):
-    chroot_path = '/abs/path/to/chroot'
-    reviewers = ['none1@chromium.org', 'none2@chromium.org']
-    test_cl_number = 1000
-
-    update_packages_and_test_cq.AddReviewers(test_cl_number, reviewers,
-                                             chroot_path)
-
-    expected_gerrit_cmd_1 = [
-        '%s/chromite/bin/gerrit' % chroot_path, 'reviewers',
-        str(test_cl_number), 'none1@chromium.org'
-    ]
-    expected_gerrit_cmd_2 = [
-        '%s/chromite/bin/gerrit' % chroot_path, 'reviewers',
-        str(test_cl_number), 'none2@chromium.org'
-    ]
-
-    self.assertEqual(mock_exec_cmd.call_count, 2)
-    self.assertEqual(mock_exec_cmd.call_args_list[0],
-                     mock.call(expected_gerrit_cmd_1))
-    self.assertEqual(mock_exec_cmd.call_args_list[1],
-                     mock.call(expected_gerrit_cmd_2))
-
-
-if __name__ == '__main__':
-  unittest.main()