Browse Source

depot_tools: Add tests for gclient_paths

Change-Id: I544aa85b040544508df8ed16310def982356fbcc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/1949417
Commit-Queue: Edward Lesmes <ehmaldonado@chromium.org>
Reviewed-by: Anthony Polito <apolito@google.com>
Edward Lemur 5 years ago
parent
commit
84b5f9a215
2 changed files with 321 additions and 58 deletions
  1. 66 58
      gclient_paths.py
  2. 255 0
      tests/gclient_paths_test.py

+ 66 - 58
gclient_paths.py

@@ -11,7 +11,9 @@
 from __future__ import print_function
 from __future__ import print_function
 
 
 import gclient_utils
 import gclient_utils
+import logging
 import os
 import os
+import subprocess2
 import sys
 import sys
 
 
 
 
@@ -25,66 +27,66 @@ def FindGclientRoot(from_dir, filename='.gclient'):
       return None
       return None
     path = split_path[0]
     path = split_path[0]
 
 
+  logging.info('Found gclient root at ' + path)
+
+  if path == real_from_dir:
+    return path
+
   # If we did not find the file in the current directory, make sure we are in a
   # If we did not find the file in the current directory, make sure we are in a
   # sub directory that is controlled by this configuration.
   # sub directory that is controlled by this configuration.
-  if path != real_from_dir:
-    entries_filename = os.path.join(path, filename + '_entries')
-    if not os.path.exists(entries_filename):
-      # If .gclient_entries does not exist, a previous call to gclient sync
-      # might have failed. In that case, we cannot verify that the .gclient
-      # is the one we want to use. In order to not to cause too much trouble,
-      # just issue a warning and return the path anyway.
-      print(
-          "%s missing, %s file in parent directory %s might not be the file "
-          "you want to use." % (entries_filename, filename, path),
-          file=sys.stderr)
+  entries_filename = os.path.join(path, filename + '_entries')
+  if not os.path.exists(entries_filename):
+    # If .gclient_entries does not exist, a previous call to gclient sync
+    # might have failed. In that case, we cannot verify that the .gclient
+    # is the one we want to use. In order to not to cause too much trouble,
+    # just issue a warning and return the path anyway.
+    print(
+        "%s missing, %s file in parent directory %s might not be the file "
+        "you want to use." % (entries_filename, filename, path),
+        file=sys.stderr)
+    return path
+
+  entries_content = gclient_utils.FileRead(entries_filename)
+  scope = {}
+  try:
+    exec(entries_content, scope)
+  except (SyntaxError, Exception) as e:
+    gclient_utils.SyntaxErrorToError(filename, e)
+
+  all_directories = scope['entries'].keys()
+  path_to_check = os.path.relpath(real_from_dir, path)
+  while path_to_check:
+    if path_to_check in all_directories:
       return path
       return path
-    scope = {}
-    try:
-      import io
-      with io.open(entries_filename, encoding='utf-8') as f:
-        exec(f.read(), scope)
-    except SyntaxError as e:
-      SyntaxErrorToError(filename, e)
-    all_directories = scope['entries'].keys()
-    path_to_check = real_from_dir[len(path)+1:]
-    while path_to_check:
-      if path_to_check in all_directories:
-        return path
-      path_to_check = os.path.dirname(path_to_check)
-    return None
+    path_to_check = os.path.dirname(path_to_check)
 
 
-  import logging
-  logging.info('Found gclient root at ' + path)
-  return path
+  return None
 
 
 
 
 def GetPrimarySolutionPath():
 def GetPrimarySolutionPath():
   """Returns the full path to the primary solution. (gclient_root + src)"""
   """Returns the full path to the primary solution. (gclient_root + src)"""
 
 
   gclient_root = FindGclientRoot(os.getcwd())
   gclient_root = FindGclientRoot(os.getcwd())
-  if not gclient_root:
-    # Some projects might not use .gclient. Try to see whether we're in a git
-    # checkout.
-    top_dir = [os.getcwd()]
-    def filter_fn(line):
-      repo_root_path = os.path.normpath(line.rstrip('\n'))
-      if os.path.exists(repo_root_path):
-        top_dir[0] = repo_root_path
-    try:
-      gclient_utils.CheckCallAndFilter(["git", "rev-parse", "--show-toplevel"],
-                                       print_stdout=False,
-                                       filter_fn=filter_fn)
-    except Exception:
-      pass
-    top_dir = top_dir[0]
-    if os.path.exists(os.path.join(top_dir, 'buildtools')):
-      return top_dir
-    return None
-
-  # Some projects' top directory is not named 'src'.
-  source_dir_name = GetGClientPrimarySolutionName(gclient_root) or 'src'
-  return os.path.join(gclient_root, source_dir_name)
+  if gclient_root:
+    # Some projects' top directory is not named 'src'.
+    source_dir_name = GetGClientPrimarySolutionName(gclient_root) or 'src'
+    return os.path.join(gclient_root, source_dir_name)
+
+  # Some projects might not use .gclient. Try to see whether we're in a git
+  # checkout that contains a 'buildtools' subdir.
+  top_dir = os.getcwd()
+  try:
+    top_dir = subprocess2.check_output(
+        ['git', 'rev-parse', '--show-toplevel'])
+    if sys.version_info.major == 3:
+      top_dir = top_dir.decode('utf-8', 'replace')
+    top_dir = os.path.normpath(top_dir.strip())
+  except subprocess2.CalledProcessError:
+    pass
+
+  if os.path.exists(os.path.join(top_dir, 'buildtools')):
+    return top_dir
+  return None
 
 
 
 
 def GetBuildtoolsPath():
 def GetBuildtoolsPath():
@@ -100,12 +102,18 @@ def GetBuildtoolsPath():
   primary_solution = GetPrimarySolutionPath()
   primary_solution = GetPrimarySolutionPath()
   if not primary_solution:
   if not primary_solution:
     return None
     return None
+
   buildtools_path = os.path.join(primary_solution, 'buildtools')
   buildtools_path = os.path.join(primary_solution, 'buildtools')
-  if not os.path.exists(buildtools_path):
-    # Buildtools may be in the gclient root.
-    gclient_root = FindGclientRoot(os.getcwd())
-    buildtools_path = os.path.join(gclient_root, 'buildtools')
-  return buildtools_path
+  if os.path.exists(buildtools_path):
+    return buildtools_path
+
+  # buildtools may be in the gclient root.
+  gclient_root = FindGclientRoot(os.getcwd())
+  buildtools_path = os.path.join(gclient_root, 'buildtools')
+  if os.path.exists(buildtools_path):
+    return buildtools_path
+
+  return None
 
 
 
 
 def GetBuildtoolsPlatformBinaryPath():
 def GetBuildtoolsPlatformBinaryPath():
@@ -121,7 +129,7 @@ def GetBuildtoolsPlatformBinaryPath():
   elif sys.platform.startswith('linux'):
   elif sys.platform.startswith('linux'):
     subdir = 'linux64'
     subdir = 'linux64'
   else:
   else:
-    raise Error('Unknown platform: ' + sys.platform)
+    raise gclient_utils.Error('Unknown platform: ' + sys.platform)
   return os.path.join(buildtools_path, subdir)
   return os.path.join(buildtools_path, subdir)
 
 
 
 
@@ -135,9 +143,9 @@ def GetExeSuffix():
 def GetGClientPrimarySolutionName(gclient_root_dir_path):
 def GetGClientPrimarySolutionName(gclient_root_dir_path):
   """Returns the name of the primary solution in the .gclient file specified."""
   """Returns the name of the primary solution in the .gclient file specified."""
   gclient_config_file = os.path.join(gclient_root_dir_path, '.gclient')
   gclient_config_file = os.path.join(gclient_root_dir_path, '.gclient')
+  gclient_config_contents = gclient_utils.FileRead(gclient_config_file)
   env = {}
   env = {}
-  exec(compile(open(gclient_config_file).read(), gclient_config_file, 'exec'),
-       env)
+  exec(gclient_config_contents, env)
   solutions = env.get('solutions', [])
   solutions = env.get('solutions', [])
   if solutions:
   if solutions:
     return solutions[0].get('name')
     return solutions[0].get('name')

+ 255 - 0
tests/gclient_paths_test.py

@@ -0,0 +1,255 @@
+#!/usr/bin/env vpython3
+# coding=utf-8
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import sys
+import unittest
+
+sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+import gclient_paths
+import gclient_utils
+import subprocess2
+
+if sys.version_info.major == 2:
+  from StringIO import StringIO
+  import mock
+else:
+  from io import StringIO
+  from unittest import mock
+
+
+EXCEPTION = subprocess2.CalledProcessError(
+    128, ['cmd'], 'cwd', 'stdout', 'stderr')
+
+
+class TestBase(unittest.TestCase):
+  def setUp(self):
+    super(TestBase, self).setUp()
+    self.file_tree = {}
+    self.root = 'C:\\' if sys.platform == 'win32' else '/'
+    self.cwd = self.root
+    mock.patch('gclient_utils.FileRead', self.read).start()
+    mock.patch('os.environ', {}).start()
+    mock.patch('os.getcwd', self.getcwd).start()
+    mock.patch('os.path.exists', self.exists).start()
+    mock.patch('os.path.realpath', side_effect=lambda path: path).start()
+    mock.patch('subprocess2.check_output').start()
+    mock.patch('sys.platform', '').start()
+    mock.patch('sys.stderr', StringIO()).start()
+    self.addCleanup(mock.patch.stopall)
+
+  def getcwd(self):
+    return self.cwd
+
+  def exists(self, path):
+    return path in self.file_tree
+
+  def read(self, path):
+    return self.file_tree[path]
+
+  def make_file_tree(self, file_tree):
+    self.file_tree = {
+      self.root + path: content
+      for path, content in file_tree.items()
+    }
+
+
+class FindGclientRootTest(TestBase):
+  def testFindGclientRoot(self):
+    self.make_file_tree({'.gclient': ''})
+    self.assertEqual(self.root, gclient_paths.FindGclientRoot(self.root))
+
+  def testGclientRootInParentDir(self):
+    self.make_file_tree({
+      '.gclient': '',
+      '.gclient_entries': 'entries = {"foo": "..."}',
+    })
+    self.assertEqual(
+        self.root,
+        gclient_paths.FindGclientRoot(os.path.join(self.root, 'foo', 'bar')))
+
+  def testGclientRootInParentDir_NotInGclientEntries(self):
+    self.make_file_tree({
+      '.gclient': '',
+      '.gclient_entries': 'entries = {"foo": "..."}',
+    })
+    self.assertIsNone(
+        gclient_paths.FindGclientRoot(os.path.join(self.root, 'bar', 'baz')))
+
+  def testGclientRootInParentDir_NoGclientEntriesFile(self):
+    self.make_file_tree({'.gclient': ''})
+    self.assertEqual(
+        self.root,
+        gclient_paths.FindGclientRoot(os.path.join(self.root, 'x', 'y', 'z')))
+    self.assertEqual(
+        '%s missing, .gclient file in parent directory %s might not be the '
+        'file you want to use.\n' % (
+            os.path.join(self.root, '.gclient_entries'), self.root),
+        sys.stderr.getvalue())
+
+  def testGclientRootInParentDir_ErrorWhenParsingEntries(self):
+    self.make_file_tree({'.gclient': '', '.gclient_entries': ':P'})
+    with self.assertRaises(Exception):
+      gclient_paths.FindGclientRoot(os.path.join(self.root, 'foo', 'bar'))
+
+  def testRootNotFound(self):
+    self.assertIsNone(
+        gclient_paths.FindGclientRoot(os.path.join(self.root, 'x', 'y', 'z')))
+
+
+class GetGClientPrimarySolutionNameTest(TestBase):
+  def testGetGClientPrimarySolutionName(self):
+    self.make_file_tree({'.gclient': 'solutions = [{"name": "foo"}]'})
+    self.assertEqual(
+      'foo', gclient_paths.GetGClientPrimarySolutionName(self.root))
+
+  def testNoSolutionsInGclientFile(self):
+    self.make_file_tree({'.gclient': ''})
+    self.assertIsNone(gclient_paths.GetGClientPrimarySolutionName(self.root))
+
+
+class GetPrimarySolutionPathTest(TestBase):
+  def testGetPrimarySolutionPath(self):
+    self.make_file_tree({'.gclient': 'solutions = [{"name": "foo"}]'})
+
+    self.assertEqual(
+        os.path.join(self.root, 'foo'), gclient_paths.GetPrimarySolutionPath())
+
+  def testSolutionNameDefaultsToSrc(self):
+    self.make_file_tree({'.gclient': ''})
+
+    self.assertEqual(
+        os.path.join(self.root, 'src'), gclient_paths.GetPrimarySolutionPath())
+
+  def testGclientRootNotFound_GitRootHasBuildtools(self):
+    self.make_file_tree({os.path.join('foo', 'buildtools'): ''})
+    self.cwd = os.path.join(self.root, 'foo', 'bar')
+    subprocess2.check_output.return_value = (
+        os.path.join(self.root, 'foo').replace(os.sep, '/').encode('utf-8')
+        + b'\n')
+
+    self.assertEqual(
+        os.path.join(self.root, 'foo'), gclient_paths.GetPrimarySolutionPath())
+
+  def testGclientRootNotFound_NoBuildtools(self):
+    self.cwd = os.path.join(self.root, 'foo', 'bar')
+    subprocess2.check_output.return_value = b'/foo\n'
+
+    self.assertIsNone(gclient_paths.GetPrimarySolutionPath())
+
+  def testGclientRootNotFound_NotInAGitRepo_CurrentDirHasBuildtools(self):
+    self.make_file_tree({os.path.join('foo', 'bar', 'buildtools'): ''})
+    self.cwd = os.path.join(self.root, 'foo', 'bar')
+    subprocess2.check_output.side_effect = EXCEPTION
+
+    self.assertEqual(self.cwd, gclient_paths.GetPrimarySolutionPath())
+
+  def testGclientRootNotFound_NotInAGitRepo_NoBuildtools(self):
+    self.cwd = os.path.join(self.root, 'foo')
+    subprocess2.check_output.side_effect = EXCEPTION
+
+    self.assertIsNone(gclient_paths.GetPrimarySolutionPath())
+
+
+class GetBuildtoolsPathTest(TestBase):
+  def testEnvVarOverride(self):
+    os.environ = {'CHROMIUM_BUILDTOOLS_PATH': 'foo'}
+
+    self.assertEqual('foo', gclient_paths.GetBuildtoolsPath())
+
+  def testNoSolutionsFound(self):
+    self.cwd = os.path.join(self.root, 'foo', 'bar')
+    subprocess2.check_output.side_effect = EXCEPTION
+
+    self.assertIsNone(gclient_paths.GetBuildtoolsPath())
+
+  def testBuildtoolsInSolution(self):
+    self.make_file_tree({
+      '.gclient': '',
+      os.path.join('src', 'buildtools'): '',
+    })
+    self.cwd = os.path.join(self.root, 'src', 'foo')
+
+    self.assertEqual(
+        os.path.join(self.root, 'src', 'buildtools'),
+        gclient_paths.GetBuildtoolsPath())
+
+  def testBuildtoolsInGclientRoot(self):
+    self.make_file_tree({'.gclient': '', 'buildtools': ''})
+    self.cwd = os.path.join(self.root, 'src', 'foo')
+
+    self.assertEqual(
+        os.path.join(self.root, 'buildtools'),
+        gclient_paths.GetBuildtoolsPath())
+
+  def testNoBuildtools(self):
+    self.make_file_tree({'.gclient': ''})
+    self.cwd = os.path.join(self.root, 'foo', 'bar')
+
+    self.assertIsNone(gclient_paths.GetBuildtoolsPath())
+
+
+class GetBuildtoolsPlatformBinaryPath(TestBase):
+  def testNoBuildtoolsPath(self):
+    self.make_file_tree({'.gclient': ''})
+    self.cwd = os.path.join(self.root, 'foo', 'bar')
+    self.assertIsNone(gclient_paths.GetBuildtoolsPlatformBinaryPath())
+
+  def testWin(self):
+    self.make_file_tree({'.gclient': '', 'buildtools': ''})
+    sys.platform = 'win'
+
+    self.assertEqual(
+        os.path.join(self.root, 'buildtools', 'win'),
+        gclient_paths.GetBuildtoolsPlatformBinaryPath())
+
+  def testCygwin(self):
+    self.make_file_tree({'.gclient': '', 'buildtools': ''})
+    sys.platform = 'cygwin'
+
+    self.assertEqual(
+        os.path.join(self.root, 'buildtools', 'win'),
+        gclient_paths.GetBuildtoolsPlatformBinaryPath())
+
+  def testMac(self):
+    self.make_file_tree({'.gclient': '', 'buildtools': ''})
+    sys.platform = 'darwin'
+
+    self.assertEqual(
+        os.path.join(self.root, 'buildtools', 'mac'),
+        gclient_paths.GetBuildtoolsPlatformBinaryPath())
+
+  def testLinux(self):
+    self.make_file_tree({'.gclient': '', 'buildtools': ''})
+    sys.platform = 'linux'
+
+    self.assertEqual(
+        os.path.join(self.root, 'buildtools', 'linux64'),
+        gclient_paths.GetBuildtoolsPlatformBinaryPath())
+
+  def testError(self):
+    self.make_file_tree({'.gclient': '', 'buildtools': ''})
+    sys.platform = 'foo'
+
+    with self.assertRaises(gclient_utils.Error, msg='Unknown platform: foo'):
+      gclient_paths.GetBuildtoolsPlatformBinaryPath()
+
+
+class GetExeSuffixTest(TestBase):
+  def testGetExeSuffix(self):
+    sys.platform = 'win'
+    self.assertEqual('.exe', gclient_paths.GetExeSuffix())
+
+    sys.platform = 'cygwin'
+    self.assertEqual('.exe', gclient_paths.GetExeSuffix())
+
+    sys.platform = 'foo'
+    self.assertEqual('', gclient_paths.GetExeSuffix())
+
+
+if __name__ == '__main__':
+  unittest.main()