def testRunHooks(self):
    if not self.enabled:
      return
    self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
    self.gclient(['sync', '--deps', 'mac'])
    tree = self.mangle_git_tree(('repo_1@2', 'src'),
                                ('repo_2@1', 'src/repo2'),
                                ('repo_3@2', 'src/repo2/repo_renamed'))
    tree['src/git_hooked1'] = 'git_hooked1'
    tree['src/git_hooked2'] = 'git_hooked2'
    self.assertTree(tree)

    os.remove(join(self.root_dir, 'src', 'git_hooked1'))
    os.remove(join(self.root_dir, 'src', 'git_hooked2'))
    # runhooks runs all hooks even if not matching by design.
    out = self.parseGclient(['runhooks', '--deps', 'mac'],
                            ['running', 'running'])
    self.assertEquals(1, len(out[0]))
    self.assertEquals(1, len(out[1]))
    tree = self.mangle_git_tree(('repo_1@2', 'src'),
                                ('repo_2@1', 'src/repo2'),
                                ('repo_3@2', 'src/repo2/repo_renamed'))
    tree['src/git_hooked1'] = 'git_hooked1'
    tree['src/git_hooked2'] = 'git_hooked2'
    self.assertTree(tree)
 def testRest(self):
   if not self.enabled:
     return
   self.gclient(['sync'])
   # TODO(maruel): This is incorrect, it should run on ./ too.
   self.parseGclient(
       ['cleanup', '--deps', 'mac', '--verbose', '--jobs', '1'],
       [('running', join(self.root_dir, 'foo', 'bar'))])
   self.parseGclient(
       ['diff', '--deps', 'mac', '--verbose', '--jobs', '1'],
       [('running', join(self.root_dir, 'foo', 'bar'))])
 def testDifferentTopLevelDirectory(self):
   # Check that even if the .gclient file does not mention the directory src
   # itself, but it is included via dependencies, the .gclient file is used.
   self.gclient(['config', self.svn_base + 'trunk/src.DEPS'])
   deps = join(self.root_dir, 'src.DEPS')
   os.mkdir(deps)
   write(join(deps, 'DEPS'),
       'deps = { "src": "%strunk/src" }' % (self.svn_base))
   src = join(self.root_dir, 'src')
   os.mkdir(src)
   res = self.gclient(['status', '--jobs', '1'], src)
   self.checkBlock(res[0], [('running', deps), ('running', src)])
 def testInitialCheckoutFailed(self):
   # Check that gclient can be executed from an arbitrary sub directory if the
   # initial checkout has failed.
   if not self.enabled:
     return
   self.gclient(['config', self.svn_base + 'trunk/src/'])
   self.gclient(['sync'])
   # Cripple the checkout.
   os.remove(join(self.root_dir, '.gclient_entries'))
   src = join(self.root_dir, 'src')
   res = self.gclient(['sync', '--jobs', '1'], src)
   self.checkBlock(res[0],
                   ['running', 'running', 'running'])
 def testCorrectDirectory(self):
   # Check that when we're in the subdirectory src, the .gclient configuration
   # is used.
   if not self.enabled:
     return
   self.gclient(['config', self.svn_base + 'trunk/src/'])
   self.gclient(['sync'])
   src = join(self.root_dir, 'src')
   res = self.gclient(['status', '--jobs', '1'], src)
   self.checkBlock(res[0], [('running', src)])
  def testSyncJobs(self):
    if not self.enabled:
      return
    # TODO(maruel): safesync.
    self.gclient(['config', self.svn_base + 'trunk/src/'])
    # Test unversioned checkout.
    self.parseGclient(
        ['sync', '--deps', 'mac', '--jobs', '8'],
        ['running', 'running',
        # This is due to the way svn update is called for a
        # single file when File() is used in a DEPS file.
        ('running', os.path.join(self.root_dir, 'src', 'file', 'other')),
        'running', 'running', 'running', 'running'],
        untangle=True)
    tree = self.mangle_svn_tree(
        ('trunk/src@2', 'src'),
        ('trunk/third_party/foo@1', 'src/third_party/foo'),
        ('trunk/other@2', 'src/other'))
    tree['src/file/other/DEPS'] = (
        self.FAKE_REPOS.svn_revs[2]['trunk/other/DEPS'])
    tree['src/svn_hooked1'] = 'svn_hooked1'
    self.assertTree(tree)

    # Manually remove svn_hooked1 before synching to make sure it's not
    # recreated.
    os.remove(join(self.root_dir, 'src', 'svn_hooked1'))

    # Test incremental versioned sync: sync backward.
    self.parseGclient(
        ['sync', '--revision', 'src@1', '--deps', 'mac',
          '--delete_unversioned_trees', '--jobs', '8'],
        ['running', 'running', 'running', 'running', 'deleting'],
        untangle=True)
    tree = self.mangle_svn_tree(
        ('trunk/src@1', 'src'),
        ('trunk/third_party/foo@2', 'src/third_party/fpp'),
        ('trunk/other@1', 'src/other'),
        ('trunk/third_party/foo@2', 'src/third_party/prout'))
    tree['src/file/other/DEPS'] = (
        self.FAKE_REPOS.svn_revs[2]['trunk/other/DEPS'])
    self.assertTree(tree)
    # Test incremental sync: delete-unversioned_trees isn't there.
    self.parseGclient(['sync', '--deps', 'mac', '--jobs', '8'],
                      ['running', 'running', 'running', 'running', 'running'],
                      untangle=True)
    tree = self.mangle_svn_tree(
        ('trunk/src@2', 'src'),
        ('trunk/third_party/foo@2', 'src/third_party/fpp'),
        ('trunk/third_party/foo@1', 'src/third_party/foo'),
        ('trunk/other@2', 'src/other'),
        ('trunk/third_party/foo@2', 'src/third_party/prout'))
    tree['src/file/other/DEPS'] = (
        self.FAKE_REPOS.svn_revs[2]['trunk/other/DEPS'])
    tree['src/svn_hooked1'] = 'svn_hooked1'
    self.assertTree(tree)
 def testWrongDirectory(self):
   # Check that we're not using a .gclient configuration which only talks
   # about a subdirectory src when we're in a different subdirectory src-other.
   if not self.enabled:
     return
   self.gclient(['config', self.svn_base + 'trunk/src/'])
   self.gclient(['sync'])
   other_src = join(self.root_dir, 'src-other')
   os.mkdir(other_src)
   res = ('', 'Error: client not configured; see \'gclient config\'\n', 1)
   self.check(res, self.gclient(['status'], other_src))
  def testRevertAndStatus(self):
    """TODO(maruel): Remove this line once this test is fixed."""
    if not self.enabled:
      return
    self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
    # Tested in testSync.
    self.gclient(['sync', '--deps', 'mac'])
    write(join(self.root_dir, 'src', 'repo2', 'hi'), 'Hey!')

    out = self.parseGclient(['status', '--deps', 'mac'], [])
    # TODO(maruel): http://crosbug.com/3584 It should output the unversioned
    # files.
    self.assertEquals(0, len(out))

    # Revert implies --force implies running hooks without looking at pattern
    # matching.
    results = self.gclient(['revert', '--deps', 'mac'])
    out = results[0].splitlines(False)
    # TODO(maruel): http://crosbug.com/3583 It just runs the hooks right now.
    self.assertEquals(13, len(out))
    self.checkString('', results[1])
    self.assertEquals(0, results[2])
    tree = self.mangle_git_tree(('repo_1@2', 'src'),
                                ('repo_2@1', 'src/repo2'),
                                ('repo_3@2', 'src/repo2/repo_renamed'))
    # TODO(maruel): http://crosbug.com/3583 This file should have been removed.
    tree[join('src', 'repo2', 'hi')] = 'Hey!'
    tree['src/git_hooked1'] = 'git_hooked1'
    tree['src/git_hooked2'] = 'git_hooked2'
    self.assertTree(tree)

    results = self.gclient(['status', '--deps', 'mac'])
    out = results[0].splitlines(False)
    # TODO(maruel): http://crosbug.com/3584 It should output the unversioned
    # files.
    self.assertEquals(0, len(out))
  def testSyncJobs(self):
    if not self.enabled:
      return
    # TODO(maruel): safesync.
    self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
    # Test unversioned checkout.
    self.parseGclient(['sync', '--deps', 'mac', '--jobs', '8'],
                      ['running', 'running', 'running', 'running', 'running'],
                      untangle=True)
    # TODO(maruel): http://crosbug.com/3582 hooks run even if not matching, must
    # add sync parsing to get the list of updated files.
    tree = self.mangle_git_tree(('repo_1@2', 'src'),
                                ('repo_2@1', 'src/repo2'),
                                ('repo_3@2', 'src/repo2/repo_renamed'))
    tree['src/git_hooked1'] = 'git_hooked1'
    tree['src/git_hooked2'] = 'git_hooked2'
    self.assertTree(tree)

    # Manually remove git_hooked1 before synching to make sure it's not
    # recreated.
    os.remove(join(self.root_dir, 'src', 'git_hooked1'))

    # Test incremental versioned sync: sync backward.
    self.parseGclient(
        ['sync', '--revision', 'src@' + self.githash('repo_1', 1),
          '--deps', 'mac', '--delete_unversioned_trees', '--jobs', '8'],
        ['running', 'running', 'deleting'],
        untangle=True)
    tree = self.mangle_git_tree(('repo_1@1', 'src'),
                                ('repo_2@2', 'src/repo2'),
                                ('repo_3@1', 'src/repo2/repo3'),
                                ('repo_4@2', 'src/repo4'))
    tree['src/git_hooked2'] = 'git_hooked2'
    self.assertTree(tree)
    # Test incremental sync: delete-unversioned_trees isn't there.
    self.parseGclient(['sync', '--deps', 'mac', '--jobs', '8'],
        ['running', 'running', 'running'], untangle=True)
    tree = self.mangle_git_tree(('repo_1@2', 'src'),
                                ('repo_2@1', 'src/repo2'),
                                ('repo_3@1', 'src/repo2/repo3'),
                                ('repo_3@2', 'src/repo2/repo_renamed'),
                                ('repo_4@2', 'src/repo4'))
    tree['src/git_hooked1'] = 'git_hooked1'
    tree['src/git_hooked2'] = 'git_hooked2'
    self.assertTree(tree)
  def testRevertAndStatus(self):
    if not self.enabled:
      return
    self.gclient(['config', self.svn_base + 'trunk/src/'])
    # Tested in testSync.
    self.gclient(['sync', '--deps', 'mac'])
    write(join(self.root_dir, 'src', 'other', 'hi'), 'Hey!')

    out = self.parseGclient(['status', '--deps', 'mac', '--jobs', '1'],
                            [['running', join(self.root_dir, 'src')],
                             ['running', join(self.root_dir, 'src', 'other')]])
    out = self.svnBlockCleanup(out)
    self.checkString('file', out[0][1])
    self.checkString('other', out[0][2])
    self.checkString('svn_hooked1', out[0][3])
    self.checkString(join('third_party', 'foo'), out[0][4])
    self.checkString('hi', out[1][1])
    self.assertEquals(5, len(out[0]))
    self.assertEquals(2, len(out[1]))

    # Revert implies --force implies running hooks without looking at pattern
    # matching.
    results = self.gclient(['revert', '--deps', 'mac', '--jobs', '1'])
    out = self.splitBlock(results[0])
    # src, src/other is missing, src/other, src/third_party/foo is missing,
    # src/third_party/foo, 2 svn hooks, 3 related to File().
    self.assertEquals(10, len(out))
    self.checkString('', results[1])
    self.assertEquals(0, results[2])
    tree = self.mangle_svn_tree(
        ('trunk/src@2', 'src'),
        ('trunk/third_party/foo@1', 'src/third_party/foo'),
        ('trunk/other@2', 'src/other'))
    tree['src/file/other/DEPS'] = (
        self.FAKE_REPOS.svn_revs[2]['trunk/other/DEPS'])
    tree['src/svn_hooked1'] = 'svn_hooked1'
    tree['src/svn_hooked2'] = 'svn_hooked2'
    self.assertTree(tree)

    out = self.parseGclient(['status', '--deps', 'mac', '--jobs', '1'],
                            [['running', join(self.root_dir, 'src')]])
    out = self.svnBlockCleanup(out)
    self.checkString('file', out[0][1])
    self.checkString('other', out[0][2])
    self.checkString('svn_hooked1', out[0][3])
    self.checkString('svn_hooked2', out[0][4])
    self.checkString(join('third_party', 'foo'), out[0][5])
    self.assertEquals(6, len(out[0]))
    self.assertEquals(1, len(out))
  def testSyncTransitive(self):
    # TODO(maruel): safesync.
    if not self.enabled:
      return
    self.gclient(['config', self.svn_base + 'trunk/src/'])

    # Make sure we can populate a new repository with --transitive.
    self.parseGclient(
        ['sync', '--transitive', '--revision', 'src@1', '--deps', 'mac',
          '--jobs', '1'],
        ['running', 'running', 'running', 'running'])
    tree = self.mangle_svn_tree(
        ('trunk/src@1', 'src'),
        ('trunk/third_party/foo@1', 'src/third_party/fpp'),
        ('trunk/other@1', 'src/other'),
        ('trunk/third_party/foo@1', 'src/third_party/prout'))

    # Get up to date, so we can test synching back.
    self.gclient(['sync', '--deps', 'mac', '--jobs', '1'])

    # Manually remove svn_hooked1 before synching to make sure it's not
    # recreated.
    os.remove(join(self.root_dir, 'src', 'svn_hooked1'))

    self.parseGclient(
        ['sync', '--transitive', '--revision', 'src@1', '--deps', 'mac',
          '--delete_unversioned_trees', '--jobs', '1'],
        ['running', 'running', 'running', 'running', 'deleting'])
    tree = self.mangle_svn_tree(
        ('trunk/src@1', 'src'),
        ('trunk/third_party/foo@1', 'src/third_party/fpp'),
        ('trunk/other@1', 'src/other'),
        ('trunk/third_party/foo@1', 'src/third_party/prout'))
    tree['src/file/other/DEPS'] = (
        self.FAKE_REPOS.svn_revs[2]['trunk/other/DEPS'])
    self.assertTree(tree)
  def testRevertAndStatusDepsOs(self):
    if not self.enabled:
      return
    self.gclient(['config', self.svn_base + 'trunk/src/'])
    # Tested in testSync.
    self.gclient(['sync', '--deps', 'mac', '--revision', 'src@1'])
    write(join(self.root_dir, 'src', 'other', 'hi'), 'Hey!')

    # Without --verbose, gclient won't output the directories without
    # modification.
    out = self.parseGclient(['status', '--deps', 'mac', '--jobs', '1'],
                            [['running', join(self.root_dir, 'src')],
                             ['running', join(self.root_dir, 'src', 'other')]])
    out = self.svnBlockCleanup(out)
    self.checkString('other', out[0][1])
    self.checkString(join('third_party', 'fpp'), out[0][2])
    self.checkString(join('third_party', 'prout'), out[0][3])
    self.checkString('hi', out[1][1])
    self.assertEquals(4, len(out[0]))
    self.assertEquals(2, len(out[1]))

    # So verify it works with --verbose.
    out = self.parseGclient(
        ['status', '--deps', 'mac', '--verbose', '--jobs', '1'],
        [['running', join(self.root_dir, 'src')],
          ['running', join(self.root_dir, 'src', 'third_party', 'fpp')],
          ['running', join(self.root_dir, 'src', 'other')],
          ['running', join(self.root_dir, 'src', 'third_party', 'prout')]])
    out = self.svnBlockCleanup(out)
    self.checkString('other', out[0][1])
    self.checkString(join('third_party', 'fpp'), out[0][2])
    self.checkString(join('third_party', 'prout'), out[0][3])
    self.checkString('hi', out[2][1])
    self.assertEquals(4, len(out[0]))
    self.assertEquals(1, len(out[1]))
    self.assertEquals(2, len(out[2]))
    self.assertEquals(1, len(out[3]))
    self.assertEquals(4, len(out))

    # Revert implies --force implies running hooks without looking at pattern
    # matching.
    # TODO(maruel): In general, gclient revert output is wrong. It should output
    # the file list after some ___ running 'svn status'
    results = self.gclient(['revert', '--deps', 'mac', '--jobs', '1'])
    out = self.splitBlock(results[0])
    self.assertEquals(7, len(out))
    self.checkString('', results[1])
    self.assertEquals(0, results[2])
    tree = self.mangle_svn_tree(
        ('trunk/src@1', 'src'),
        ('trunk/third_party/foo@2', 'src/third_party/fpp'),
        ('trunk/other@1', 'src/other'),
        ('trunk/third_party/prout@2', 'src/third_party/prout'))
    self.assertTree(tree)

    out = self.parseGclient(['status', '--deps', 'mac', '--jobs', '1'],
                            [['running', join(self.root_dir, 'src')]])
    out = self.svnBlockCleanup(out)
    self.checkString('other', out[0][1])
    self.checkString(join('third_party', 'fpp'), out[0][2])
    self.checkString(join('third_party', 'prout'), out[0][3])
    self.assertEquals(4, len(out[0]))
  def testConfig(self):
    p = join(self.root_dir, '.gclient')
    def test(cmd, expected):
      if os.path.exists(p):
        os.remove(p)
      results = self.gclient(cmd)
      self.check(('', '', 0), results)
      self.checkString(expected, open(p, 'rU').read())

    test(['config', self.svn_base + 'trunk/src/'],
         ('solutions = [\n'
          '  { "name"        : "src",\n'
          '    "url"         : "%strunk/src",\n'
          '    "deps_file"   : "DEPS",\n'
          '    "managed"     : True,\n'
          '    "custom_deps" : {\n'
          '    },\n'
          '    "safesync_url": "",\n'
          '  },\n'
          ']\n') % self.svn_base)

    test(['config', self.git_base + 'repo_1', '--name', 'src'],
         ('solutions = [\n'
          '  { "name"        : "src",\n'
          '    "url"         : "%srepo_1",\n'
          '    "deps_file"   : "DEPS",\n'
          '    "managed"     : True,\n'
          '    "custom_deps" : {\n'
          '    },\n'
          '    "safesync_url": "",\n'
          '  },\n'
          ']\n') % self.git_base)

    test(['config', 'foo', 'faa'],
         'solutions = [\n'
         '  { "name"        : "foo",\n'
         '    "url"         : "foo",\n'
         '    "deps_file"   : "DEPS",\n'
          '    "managed"     : True,\n'
         '    "custom_deps" : {\n'
         '    },\n'
         '    "safesync_url": "faa",\n'
         '  },\n'
         ']\n')

    test(['config', 'foo', '--deps', 'blah'],
         'solutions = [\n'
         '  { "name"        : "foo",\n'
         '    "url"         : "foo",\n'
         '    "deps_file"   : "blah",\n'
         '    "managed"     : True,\n'
         '    "custom_deps" : {\n'
         '    },\n'
          '    "safesync_url": "",\n'
         '  },\n'
         ']\n')

    test(['config', '--spec', '["blah blah"]'], '["blah blah"]')

    os.remove(p)
    results = self.gclient(['config', 'foo', 'faa', 'fuu'])
    err = ('Usage: gclient.py config [options] [url] [safesync url]\n\n'
           'gclient.py: error: Inconsistent arguments. Use either --spec or one'
           ' or 2 args\n')
    self.check(('', err, 2), results)
    self.assertFalse(os.path.exists(join(self.root_dir, '.gclient')))