llvm_tools: Validate chromeos checkout path

Add support for verifying chromeos checkout path so
invoking a bad path gives an error immediately.

BUG=b:187794508
TEST=unit tests

Change-Id: I889c2e83540e0ae23680b0480ff71905ac7723fe
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/4011203
Reviewed-by: Bob Haarman <inglorion@chromium.org>
Tested-by: Manoj Gupta <manojgupta@chromium.org>
Reviewed-by: Ryan Beltran <ryanbeltran@chromium.org>
Commit-Queue: Manoj Gupta <manojgupta@chromium.org>
diff --git a/llvm_tools/auto_llvm_bisection.py b/llvm_tools/auto_llvm_bisection.py
index 3640aba..c52c21b 100755
--- a/llvm_tools/auto_llvm_bisection.py
+++ b/llvm_tools/auto_llvm_bisection.py
@@ -105,6 +105,8 @@
 
     args_output = llvm_bisection.GetCommandLineArgs()
 
+    chroot.VerifyChromeOSRoot(args_output.chroot_path)
+
     if os.path.isfile(args_output.last_tested):
         print("Resuming bisection for %s" % args_output.last_tested)
     else:
diff --git a/llvm_tools/auto_llvm_bisection_unittest.py b/llvm_tools/auto_llvm_bisection_unittest.py
index c70ddee..e57637e 100755
--- a/llvm_tools/auto_llvm_bisection_unittest.py
+++ b/llvm_tools/auto_llvm_bisection_unittest.py
@@ -25,6 +25,7 @@
 class AutoLLVMBisectionTest(unittest.TestCase):
     """Unittests for auto bisection of LLVM."""
 
+    @mock.patch.object(chroot, "VerifyChromeOSRoot")
     @mock.patch.object(chroot, "VerifyOutsideChroot", return_value=True)
     @mock.patch.object(
         llvm_bisection,
@@ -53,6 +54,7 @@
         mock_sleep,
         mock_get_args,
         mock_outside_chroot,
+        mock_chromeos_root,
     ):
 
         mock_isfile.side_effect = [False, False, True, True]
@@ -90,6 +92,7 @@
         mock_traceback.assert_called_once()
         mock_sleep.assert_called_once()
 
+    @mock.patch.object(chroot, "VerifyChromeOSRoot")
     @mock.patch.object(chroot, "VerifyOutsideChroot", return_value=True)
     @mock.patch.object(time, "sleep")
     @mock.patch.object(traceback, "print_exc")
@@ -108,6 +111,7 @@
         mock_traceback,
         mock_sleep,
         mock_outside_chroot,
+        mock_chromeos_root,
     ):
 
         mock_isfile.return_value = False
@@ -123,6 +127,7 @@
 
         self.assertEqual(err.exception.code, "Unable to continue bisection.")
 
+        mock_chromeos_root.assert_called_once()
         mock_outside_chroot.assert_called_once()
         mock_get_args.assert_called_once()
         self.assertEqual(mock_isfile.call_count, 2)
@@ -130,6 +135,7 @@
         self.assertEqual(mock_traceback.call_count, 3)
         self.assertEqual(mock_sleep.call_count, 2)
 
+    @mock.patch.object(chroot, "VerifyChromeOSRoot")
     @mock.patch.object(chroot, "VerifyOutsideChroot", return_value=True)
     @mock.patch.object(
         llvm_bisection,
@@ -153,6 +159,7 @@
         mock_time,
         mock_get_args,
         mock_outside_chroot,
+        mock_chromeos_root,
     ):
 
         # Simulate behavior of `time.time()` for time passed.
diff --git a/llvm_tools/chroot.py b/llvm_tools/chroot.py
index 46464fe..2ec989c 100755
--- a/llvm_tools/chroot.py
+++ b/llvm_tools/chroot.py
@@ -9,6 +9,7 @@
 
 import collections
 import os
+from pathlib import Path
 import subprocess
 
 
@@ -30,6 +31,19 @@
     assert not InChroot(), "Script should be run outside the chroot."
 
 
+def VerifyChromeOSRoot(chromeos_root):
+    """Checks whether the path actually points to ChromiumOS checkout root.
+
+    Raises:
+      AssertionError: The path is not ChromiumOS checkout root.
+    """
+
+    subdir = "src/third_party/chromiumos-overlay"
+    path = Path(chromeos_root).expanduser() / subdir
+    msg = f"Wrong ChromeOS path. No {subdir} directory in {chromeos_root} ."
+    assert path.is_dir(), msg
+
+
 def GetChrootEbuildPaths(chromeos_root, packages):
     """Gets the chroot path(s) of the package(s).
 
diff --git a/llvm_tools/get_upstream_patch.py b/llvm_tools/get_upstream_patch.py
index 72aa16b..615d5b8 100755
--- a/llvm_tools/get_upstream_patch.py
+++ b/llvm_tools/get_upstream_patch.py
@@ -576,6 +576,7 @@
         "when --differential appears exactly once.",
     )
     args = parser.parse_args()
+    chroot.VerifyChromeOSRoot(args.chroot_path)
 
     if not (args.sha or args.differential):
         parser.error("--sha or --differential required")
diff --git a/llvm_tools/llvm_bisection.py b/llvm_tools/llvm_bisection.py
index 0b851eb..6cc93ae 100755
--- a/llvm_tools/llvm_bisection.py
+++ b/llvm_tools/llvm_bisection.py
@@ -289,7 +289,6 @@
     last_tested,
     update_packages,
     chroot_path,
-    patch_metadata_file,
     extra_change_lists,
     options,
     builder,
@@ -304,7 +303,6 @@
                 git_hash,
                 svn_revision,
                 chroot_path,
-                patch_metadata_file,
                 extra_change_lists,
                 options,
                 builder,
@@ -346,7 +344,7 @@
     """
 
     chroot.VerifyOutsideChroot()
-    patch_metadata_file = "PATCHES.json"
+    chroot.VerifyChromeOSRoot(args_output.chroot_path)
     start = args_output.start_rev
     end = args_output.end_rev
 
@@ -453,7 +451,6 @@
         args_output.last_tested,
         update_chromeos_llvm_hash.DEFAULT_PACKAGES,
         args_output.chroot_path,
-        patch_metadata_file,
         args_output.extra_change_lists,
         args_output.options,
         args_output.builder,
diff --git a/llvm_tools/llvm_bisection_unittest.py b/llvm_tools/llvm_bisection_unittest.py
index 1e86a67..a2bcc90 100755
--- a/llvm_tools/llvm_bisection_unittest.py
+++ b/llvm_tools/llvm_bisection_unittest.py
@@ -209,7 +209,6 @@
             _git_hash,
             _revision,
             _chroot_path,
-            _patch_file,
             _extra_cls,
             _options,
             _builder,
@@ -238,7 +237,6 @@
         args_output = test_helpers.ArgsOutputTest()
 
         packages = ["sys-devel/llvm"]
-        patch_file = "/abs/path/to/PATCHES.json"
 
         # Create a temporary .JSON file to simulate a status file for bisection.
         with test_helpers.CreateTemporaryJsonFile() as temp_json_file:
@@ -255,7 +253,6 @@
                     temp_json_file,
                     packages,
                     args_output.chroot_path,
-                    patch_file,
                     args_output.extra_change_lists,
                     args_output.options,
                     args_output.builders,
@@ -289,10 +286,12 @@
     @mock.patch.object(llvm_bisection, "GetCommitsBetween")
     @mock.patch.object(llvm_bisection, "GetRemainingRange")
     @mock.patch.object(llvm_bisection, "LoadStatusFile")
+    @mock.patch.object(chroot, "VerifyChromeOSRoot")
     @mock.patch.object(chroot, "VerifyOutsideChroot", return_value=True)
     def testMainPassed(
         self,
         mock_outside_chroot,
+        mock_chromeos_root,
         mock_load_status_file,
         mock_get_range,
         mock_get_revision_and_hash_list,
@@ -337,6 +336,8 @@
             llvm_bisection.BisectionExitStatus.BISECTION_COMPLETE.value,
         )
 
+        mock_chromeos_root.assert_called_once()
+
         mock_outside_chroot.assert_called_once()
 
         mock_load_status_file.assert_called_once()
@@ -362,9 +363,10 @@
         )
 
     @mock.patch.object(llvm_bisection, "LoadStatusFile")
+    @mock.patch.object(chroot, "VerifyChromeOSRoot")
     @mock.patch.object(chroot, "VerifyOutsideChroot", return_value=True)
     def testMainFailedWithInvalidRange(
-        self, mock_outside_chroot, mock_load_status_file
+        self, mock_chromeos_root, mock_outside_chroot, mock_load_status_file
     ):
 
         start = 500
@@ -394,6 +396,8 @@
 
         self.assertEqual(str(err.exception), error_message)
 
+        mock_chromeos_root.assert_called_once()
+
         mock_outside_chroot.assert_called_once()
 
         mock_load_status_file.assert_called_once()
@@ -401,9 +405,11 @@
     @mock.patch.object(llvm_bisection, "GetCommitsBetween")
     @mock.patch.object(llvm_bisection, "GetRemainingRange")
     @mock.patch.object(llvm_bisection, "LoadStatusFile")
+    @mock.patch.object(chroot, "VerifyChromeOSRoot")
     @mock.patch.object(chroot, "VerifyOutsideChroot", return_value=True)
     def testMainFailedWithPendingBuilds(
         self,
+        mock_chromeos_root,
         mock_outside_chroot,
         mock_load_status_file,
         mock_get_range,
@@ -451,6 +457,8 @@
 
         self.assertEqual(str(err.exception), error_message)
 
+        mock_chromeos_root.assert_called_once()
+
         mock_outside_chroot.assert_called_once()
 
         mock_load_status_file.assert_called_once()
@@ -462,10 +470,12 @@
     @mock.patch.object(llvm_bisection, "GetCommitsBetween")
     @mock.patch.object(llvm_bisection, "GetRemainingRange")
     @mock.patch.object(llvm_bisection, "LoadStatusFile")
+    @mock.patch.object(chroot, "VerifyChromeOSRoot")
     @mock.patch.object(chroot, "VerifyOutsideChroot", return_value=True)
     def testMainFailedWithDuplicateBuilds(
         self,
         mock_outside_chroot,
+        mock_chromeos_root,
         mock_load_status_file,
         mock_get_range,
         mock_get_revision_and_hash_list,
@@ -508,6 +518,8 @@
         error_message = 'Revision %d exists already in "jobs"' % rev
         self.assertEqual(str(err.exception), error_message)
 
+        mock_chromeos_root.assert_called_once()
+
         mock_outside_chroot.assert_called_once()
 
         mock_load_status_file.assert_called_once()
@@ -523,10 +535,12 @@
     @mock.patch.object(llvm_bisection, "GetCommitsBetween")
     @mock.patch.object(llvm_bisection, "GetRemainingRange")
     @mock.patch.object(llvm_bisection, "LoadStatusFile")
+    @mock.patch.object(chroot, "VerifyChromeOSRoot")
     @mock.patch.object(chroot, "VerifyOutsideChroot", return_value=True)
     def testMainFailedToAbandonCL(
         self,
         mock_outside_chroot,
+        mock_chromeos_root,
         mock_load_status_file,
         mock_get_range,
         mock_get_revision_and_hash_list,
@@ -574,6 +588,8 @@
 
         self.assertEqual(err.exception.output, error_message)
 
+        mock_chromeos_root.assert_called_once()
+
         mock_outside_chroot.assert_called_once()
 
         mock_load_status_file.assert_called_once()
diff --git a/llvm_tools/modify_a_tryjob.py b/llvm_tools/modify_a_tryjob.py
index 03de606..7b29214 100755
--- a/llvm_tools/modify_a_tryjob.py
+++ b/llvm_tools/modify_a_tryjob.py
@@ -134,7 +134,6 @@
     git_hash,
     svn_version,
     chroot_path,
-    patch_metadata_file,
     svn_option,
 ):
     """Updates the packages' LLVM_NEXT."""
@@ -196,7 +195,6 @@
     git_hash,
     revision,
     chroot_path,
-    patch_metadata_file,
     extra_cls,
     options,
     builder,
@@ -212,7 +210,6 @@
         git_hash,
         revision,
         chroot_path,
-        patch_metadata_file,
         svn_option,
     )
 
@@ -322,8 +319,6 @@
         # bisection.
         elif bisect_contents["start"] < revision < bisect_contents["end"]:
 
-            patch_metadata_file = "PATCHES.json"
-
             (
                 git_hash,
                 revision,
@@ -334,7 +329,6 @@
                 git_hash,
                 revision,
                 chroot_path,
-                patch_metadata_file,
                 extra_cls,
                 options,
                 builder,
@@ -365,6 +359,8 @@
 
     args_output = GetCommandLineArgs()
 
+    chroot.VerifyChromeOSRoot(args_output.chroot_path)
+
     PerformTryjobModification(
         args_output.revision,
         ModifyTryjob(args_output.modify_tryjob),
diff --git a/llvm_tools/update_chromeos_llvm_hash.py b/llvm_tools/update_chromeos_llvm_hash.py
index 75c6ce6..cf7ae47 100755
--- a/llvm_tools/update_chromeos_llvm_hash.py
+++ b/llvm_tools/update_chromeos_llvm_hash.py
@@ -764,6 +764,8 @@
 
     args_output = GetCommandLineArgs()
 
+    chroot.VerifyChromeOSRoot(args_output.chroot_path)
+
     llvm_variant = LLVMVariant.current
     if args_output.is_llvm_next:
         llvm_variant = LLVMVariant.next
@@ -773,7 +775,6 @@
     git_hash, svn_version = get_llvm_hash.GetLLVMHashAndVersionFromSVNOption(
         git_hash_source
     )
-
     # Filter out empty strings. For example "".split{",") returns [""].
     packages = set(p for p in args_output.update_packages.split(",") if p)
     manifest_packages = set(
diff --git a/llvm_tools/update_chromeos_llvm_hash_unittest.py b/llvm_tools/update_chromeos_llvm_hash_unittest.py
index b758538..f7f6247 100755
--- a/llvm_tools/update_chromeos_llvm_hash_unittest.py
+++ b/llvm_tools/update_chromeos_llvm_hash_unittest.py
@@ -1017,11 +1017,16 @@
 
         mock_delete_repo.assert_called_once_with(path_to_package_dir, branch)
 
+    @mock.patch.object(chroot, "VerifyChromeOSRoot")
     @mock.patch.object(chroot, "VerifyOutsideChroot")
     @mock.patch.object(get_llvm_hash, "GetLLVMHashAndVersionFromSVNOption")
     @mock.patch.object(update_chromeos_llvm_hash, "UpdatePackages")
     def testMainDefaults(
-        self, mock_update_packages, mock_gethash, mock_outside_chroot
+        self,
+        mock_update_packages,
+        mock_gethash,
+        mock_outside_chroot,
+        mock_chromeos_root,
     ):
         git_hash = "1234abcd"
         svn_version = 5678
@@ -1053,12 +1058,18 @@
             extra_commit_msg=None,
         )
         mock_outside_chroot.assert_called()
+        mock_chromeos_root.assert_called()
 
+    @mock.patch.object(chroot, "VerifyChromeOSRoot")
     @mock.patch.object(chroot, "VerifyOutsideChroot")
     @mock.patch.object(get_llvm_hash, "GetLLVMHashAndVersionFromSVNOption")
     @mock.patch.object(update_chromeos_llvm_hash, "UpdatePackages")
     def testMainLlvmNext(
-        self, mock_update_packages, mock_gethash, mock_outside_chroot
+        self,
+        mock_update_packages,
+        mock_gethash,
+        mock_outside_chroot,
+        mock_chromeos_root,
     ):
         git_hash = "1234abcd"
         svn_version = 5678
@@ -1089,12 +1100,18 @@
             extra_commit_msg=None,
         )
         mock_outside_chroot.assert_called()
+        mock_chromeos_root.assert_called()
 
+    @mock.patch.object(chroot, "VerifyChromeOSRoot")
     @mock.patch.object(chroot, "VerifyOutsideChroot")
     @mock.patch.object(get_llvm_hash, "GetLLVMHashAndVersionFromSVNOption")
     @mock.patch.object(update_chromeos_llvm_hash, "UpdatePackages")
     def testMainAllArgs(
-        self, mock_update_packages, mock_gethash, mock_outside_chroot
+        self,
+        mock_update_packages,
+        mock_gethash,
+        mock_outside_chroot,
+        mock_chromeos_root,
     ):
         packages_to_update = "test-packages/package1,test-libs/lib1"
         manifest_packages = "test-libs/lib1,test-libs/lib2"
@@ -1140,6 +1157,7 @@
             extra_commit_msg=None,
         )
         mock_outside_chroot.assert_called()
+        mock_chromeos_root.assert_called()
 
     @mock.patch.object(subprocess, "check_output", return_value=None)
     @mock.patch.object(get_llvm_hash, "GetLLVMMajorVersion")
diff --git a/llvm_tools/update_packages_and_run_tests.py b/llvm_tools/update_packages_and_run_tests.py
index dc14b6d..43c8390 100755
--- a/llvm_tools/update_packages_and_run_tests.py
+++ b/llvm_tools/update_packages_and_run_tests.py
@@ -422,6 +422,8 @@
 
     args_output = GetCommandLineArgs()
 
+    chroot.VerifyChromeOSRoot(args_output.chroot_path)
+
     svn_option = args_output.llvm_version
 
     git_hash, svn_version = get_llvm_hash.GetLLVMHashAndVersionFromSVNOption(
diff --git a/llvm_tools/update_packages_and_run_tests_unittest.py b/llvm_tools/update_packages_and_run_tests_unittest.py
index fc65749..e62ed04 100755
--- a/llvm_tools/update_packages_and_run_tests_unittest.py
+++ b/llvm_tools/update_packages_and_run_tests_unittest.py
@@ -263,12 +263,14 @@
     @mock.patch.object(update_chromeos_llvm_hash, "UpdatePackages")
     @mock.patch.object(update_packages_and_run_tests, "GetCommandLineArgs")
     @mock.patch.object(get_llvm_hash, "GetLLVMHashAndVersionFromSVNOption")
+    @mock.patch.object(chroot, "VerifyChromeOSRoot")
     @mock.patch.object(chroot, "VerifyOutsideChroot", return_value=True)
     @mock.patch.object(chroot, "GetChrootEbuildPaths")
     def testUpdatedLastTestedFileWithNewTestedRevision(
         self,
         mock_get_chroot_build_paths,
         mock_outside_chroot,
+        mock_chromeos_root,
         mock_get_hash_and_version,
         mock_get_commandline_args,
         mock_update_packages,
@@ -336,6 +338,8 @@
 
         mock_outside_chroot.assert_called_once()
 
+        mock_chromeos_root.assert_called_once()
+
         mock_get_commandline_args.assert_called_once()
 
         mock_get_hash_and_version.assert_called_once()