depot_tools: Split gclient_smoketests.

gclient_smoketest takes the longest to run among all tests.
Split it so that presubmit takes less time overall, and is
less likely to exceed the time limit.

Change-Id: I5a4f714b98f59a2e46bae944a043ad41b6786c25
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/2121154
Reviewed-by: Josip Sokcevic <sokcevic@google.com>
Commit-Queue: Edward Lesmes <ehmaldonado@chromium.org>
changes/54/2121154/2
Edward Lesmes 5 years ago committed by LUCI CQ
parent 4c3eb70358
commit 7d1655b880

@ -0,0 +1,135 @@
#!/usr/bin/env vpython3
# Copyright (c) 2020 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.
"""Smoke tests for gclient.py.
Shell out 'gclient' and run cipd tests.
"""
import logging
import os
import sys
import unittest
import gclient_smoketest_base
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
class GClientSmokeCipd(gclient_smoketest_base.GClientSmokeBase):
def setUp(self):
super(GClientSmokeCipd, self).setUp()
self.enabled = self.FAKE_REPOS.set_up_git()
if not self.enabled:
self.skipTest('git fake repos not available')
self.env['PATH'] = (os.path.join(ROOT_DIR, 'testing_support')
+ os.pathsep + self.env['PATH'])
def testSyncCipd(self):
self.gclient(['config', self.git_base + 'repo_14', '--name', 'src'])
self.gclient(['sync'])
tree = self.mangle_git_tree(('repo_14@1', 'src'))
tree.update({
'_cipd': '\n'.join([
'$ParanoidMode CheckPresence',
'',
'@Subdir src/another_cipd_dep',
'package1 1.1-cr0',
'package2 1.13',
'',
'@Subdir src/cipd_dep',
'package0 0.1',
'',
'@Subdir src/cipd_dep_with_cipd_variable',
'package3/${platform} 1.2',
'',
'',
]),
'src/another_cipd_dep/_cipd': '\n'.join([
'package1 1.1-cr0',
'package2 1.13',
]),
'src/cipd_dep/_cipd': 'package0 0.1',
'src/cipd_dep_with_cipd_variable/_cipd': 'package3/${platform} 1.2',
})
self.assertTree(tree)
def testConvertGitToCipd(self):
self.gclient(['config', self.git_base + 'repo_13', '--name', 'src'])
# repo_13@1 has src/repo12 as a git dependency.
self.gclient(
['sync', '-v', '-v', '-v', '--revision', self.githash('repo_13', 1)])
tree = self.mangle_git_tree(('repo_13@1', 'src'),
('repo_12@1', 'src/repo12'))
self.assertTree(tree)
# repo_13@3 has src/repo12 as a cipd dependency.
self.gclient(
['sync', '-v', '-v', '-v', '--revision', self.githash('repo_13', 3),
'--delete_unversioned_trees'])
tree = self.mangle_git_tree(('repo_13@3', 'src'))
tree.update({
'_cipd': '\n'.join([
'$ParanoidMode CheckPresence',
'',
'@Subdir src/repo12',
'foo 1.3',
'',
'',
]),
'src/repo12/_cipd': 'foo 1.3',
})
self.assertTree(tree)
def testConvertCipdToGit(self):
self.gclient(['config', self.git_base + 'repo_13', '--name', 'src'])
# repo_13@3 has src/repo12 as a cipd dependency.
self.gclient(
['sync', '-v', '-v', '-v', '--revision', self.githash('repo_13', 3),
'--delete_unversioned_trees'])
tree = self.mangle_git_tree(('repo_13@3', 'src'))
tree.update({
'_cipd': '\n'.join([
'$ParanoidMode CheckPresence',
'',
'@Subdir src/repo12',
'foo 1.3',
'',
'',
]),
'src/repo12/_cipd': 'foo 1.3',
})
self.assertTree(tree)
# repo_13@1 has src/repo12 as a git dependency.
self.gclient(
['sync', '-v', '-v', '-v', '--revision', self.githash('repo_13', 1)])
tree = self.mangle_git_tree(('repo_13@1', 'src'),
('repo_12@1', 'src/repo12'))
tree.update({
'_cipd': '\n'.join([
'$ParanoidMode CheckPresence',
'',
'@Subdir src/repo12',
'foo 1.3',
'',
'',
]),
'src/repo12/_cipd': 'foo 1.3',
})
self.assertTree(tree)
if __name__ == '__main__':
if '-v' in sys.argv:
logging.basicConfig(level=logging.DEBUG)
unittest.main()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,146 @@
#!/usr/bin/env vpython3
# Copyright (c) 2020 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 logging
import os
import re
import sys
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
GCLIENT_PATH = os.path.join(ROOT_DIR, 'gclient')
sys.path.insert(0, ROOT_DIR)
import subprocess2
from testing_support import fake_repos
class GClientSmokeBase(fake_repos.FakeReposTestBase):
def setUp(self):
super(GClientSmokeBase, self).setUp()
# Make sure it doesn't try to auto update when testing!
self.env = os.environ.copy()
self.env['DEPOT_TOOLS_UPDATE'] = '0'
self.env['DEPOT_TOOLS_METRICS'] = '0'
# Suppress Python 3 warnings and other test undesirables.
self.env['GCLIENT_TEST'] = '1'
self.maxDiff = None
def gclient(self, cmd, cwd=None, error_ok=False):
if not cwd:
cwd = self.root_dir
cmd = [GCLIENT_PATH] + cmd
process = subprocess2.Popen(
cmd, cwd=cwd, env=self.env, stdout=subprocess2.PIPE,
stderr=subprocess2.PIPE, universal_newlines=True)
(stdout, stderr) = process.communicate()
logging.debug("XXX: %s\n%s\nXXX" % (' '.join(cmd), stdout))
logging.debug("YYY: %s\n%s\nYYY" % (' '.join(cmd), stderr))
if not error_ok:
self.assertEqual(0, process.returncode, stderr)
return (stdout.replace('\r\n', '\n'), stderr.replace('\r\n', '\n'),
process.returncode)
def untangle(self, stdout):
"""Separates output based on thread IDs."""
tasks = {}
remaining = []
task_id = 0
for line in stdout.splitlines(False):
m = re.match(r'^(\d)+>(.*)$', line)
if not m:
if task_id:
# Lines broken with carriage breaks don't have a thread ID, but belong
# to the last seen thread ID.
tasks.setdefault(task_id, []).append(line)
else:
remaining.append(line)
else:
self.assertEqual([], remaining)
task_id = int(m.group(1))
tasks.setdefault(task_id, []).append(m.group(2))
out = []
for key in sorted(tasks.keys()):
out.extend(tasks[key])
out.extend(remaining)
return '\n'.join(out)
def parseGclient(self, cmd, items, expected_stderr='', untangle=False):
"""Parse gclient's output to make it easier to test.
If untangle is True, tries to sort out the output from parallel checkout."""
(stdout, stderr, _) = self.gclient(cmd)
if untangle:
stdout = self.untangle(stdout)
self.checkString(expected_stderr, stderr)
return self.checkBlock(stdout, items)
def splitBlock(self, stdout):
"""Split gclient's output into logical execution blocks.
___ running 'foo' at '/bar'
(...)
___ running 'baz' at '/bar'
(...)
will result in 2 items of len((...).splitlines()) each.
"""
results = []
for line in stdout.splitlines(False):
# Intentionally skips empty lines.
if not line:
continue
if not line.startswith('__'):
if results:
results[-1].append(line)
else:
# TODO(maruel): gclient's git stdout is inconsistent.
# This should fail the test instead!!
pass
continue
match = re.match(r'^________ ([a-z]+) \'(.*)\' in \'(.*)\'$', line)
if match:
results.append([[match.group(1), match.group(2), match.group(3)]])
continue
match = re.match(r'^_____ (.*) is missing, synching instead$', line)
if match:
# Blah, it's when a dependency is deleted, we should probably not
# output this message.
results.append([line])
continue
# These two regexps are a bit too broad, they are necessary only for git
# checkouts.
if (re.match(r'_____ [^ ]+ at [^ ]+', line) or
re.match(r'_____ [^ ]+ : Attempting rebase onto [0-9a-f]+...', line)):
continue
# Fail for any unrecognized lines that start with '__'.
self.fail(line)
return results
def checkBlock(self, stdout, items):
results = self.splitBlock(stdout)
for i in range(min(len(results), len(items))):
if isinstance(items[i], (list, tuple)):
verb = items[i][0]
path = items[i][1]
else:
verb = items[i]
path = self.root_dir
self.checkString(results[i][0][0], verb, (i, results[i][0][0], verb))
if sys.platform == 'win32':
# Make path lower case since casing can change randomly.
self.checkString(
results[i][0][2].lower(),
path.lower(),
(i, results[i][0][2].lower(), path.lower()))
else:
self.checkString(results[i][0][2], path, (i, results[i][0][2], path))
self.assertEqual(len(results), len(items), (stdout, items, len(results)))
return results

@ -0,0 +1,242 @@
#!/usr/bin/env vpython3
# Copyright (c) 2020 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.
"""Smoke tests for gclient.py.
Shell out 'gclient' and simulate the behavior of bisect bots as they transition
across DEPS changes.
"""
import logging
import os
import sys
import unittest
import gclient_smoketest_base
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, ROOT_DIR)
import scm
from testing_support import fake_repos
class SkiaDEPSTransitionSmokeTest(gclient_smoketest_base.GClientSmokeBase):
"""Simulate the behavior of bisect bots as they transition across the Skia
DEPS change."""
FAKE_REPOS_CLASS = fake_repos.FakeRepoSkiaDEPS
def setUp(self):
super(SkiaDEPSTransitionSmokeTest, self).setUp()
self.enabled = self.FAKE_REPOS.set_up_git()
if not self.enabled:
self.skipTest('git fake repos not available')
def testSkiaDEPSChangeGit(self):
# Create an initial checkout:
# - Single checkout at the root.
# - Multiple checkouts in a shared subdirectory.
self.gclient(['config', '--spec',
'solutions=['
'{"name": "src",'
' "url": ' + repr(self.git_base )+ '+ "repo_2",'
'}]'])
checkout_path = os.path.join(self.root_dir, 'src')
skia = os.path.join(checkout_path, 'third_party', 'skia')
skia_gyp = os.path.join(skia, 'gyp')
skia_include = os.path.join(skia, 'include')
skia_src = os.path.join(skia, 'src')
gyp_git_url = self.git_base + 'repo_3'
include_git_url = self.git_base + 'repo_4'
src_git_url = self.git_base + 'repo_5'
skia_git_url = self.FAKE_REPOS.git_base + 'repo_1'
pre_hash = self.githash('repo_2', 1)
post_hash = self.githash('repo_2', 2)
# Initial sync. Verify that we get the expected checkout.
res = self.gclient(['sync', '--deps', 'mac', '--revision',
'src@%s' % pre_hash])
self.assertEqual(res[2], 0, 'Initial sync failed.')
self.assertEqual(scm.GIT.Capture(['config', 'remote.origin.url'],
skia_gyp), gyp_git_url)
self.assertEqual(scm.GIT.Capture(['config', 'remote.origin.url'],
skia_include), include_git_url)
self.assertEqual(scm.GIT.Capture(['config', 'remote.origin.url'],
skia_src), src_git_url)
# Verify that the sync succeeds. Verify that we have the expected merged
# checkout.
res = self.gclient(['sync', '--deps', 'mac', '--revision',
'src@%s' % post_hash])
self.assertEqual(res[2], 0, 'DEPS change sync failed.')
self.assertEqual(scm.GIT.Capture(['config', 'remote.origin.url'],
skia), skia_git_url)
# Sync again. Verify that we still have the expected merged checkout.
res = self.gclient(['sync', '--deps', 'mac', '--revision',
'src@%s' % post_hash])
self.assertEqual(res[2], 0, 'Subsequent sync failed.')
self.assertEqual(scm.GIT.Capture(['config', 'remote.origin.url'],
skia), skia_git_url)
# Sync back to the original DEPS. Verify that we get the original structure.
res = self.gclient(['sync', '--deps', 'mac', '--revision',
'src@%s' % pre_hash])
self.assertEqual(res[2], 0, 'Reverse sync failed.')
self.assertEqual(scm.GIT.Capture(['config', 'remote.origin.url'],
skia_gyp), gyp_git_url)
self.assertEqual(scm.GIT.Capture(['config', 'remote.origin.url'],
skia_include), include_git_url)
self.assertEqual(scm.GIT.Capture(['config', 'remote.origin.url'],
skia_src), src_git_url)
# Sync again. Verify that we still have the original structure.
res = self.gclient(['sync', '--deps', 'mac', '--revision',
'src@%s' % pre_hash])
self.assertEqual(res[2], 0, 'Subsequent sync #2 failed.')
self.assertEqual(scm.GIT.Capture(['config', 'remote.origin.url'],
skia_gyp), gyp_git_url)
self.assertEqual(scm.GIT.Capture(['config', 'remote.origin.url'],
skia_include), include_git_url)
self.assertEqual(scm.GIT.Capture(['config', 'remote.origin.url'],
skia_src), src_git_url)
class BlinkDEPSTransitionSmokeTest(gclient_smoketest_base.GClientSmokeBase):
"""Simulate the behavior of bisect bots as they transition across the Blink
DEPS change."""
FAKE_REPOS_CLASS = fake_repos.FakeRepoBlinkDEPS
def setUp(self):
super(BlinkDEPSTransitionSmokeTest, self).setUp()
self.enabled = self.FAKE_REPOS.set_up_git()
if not self.enabled:
self.skipTest('git fake repos not available')
self.checkout_path = os.path.join(self.root_dir, 'src')
self.blink = os.path.join(self.checkout_path, 'third_party', 'WebKit')
self.blink_git_url = self.FAKE_REPOS.git_base + 'repo_2'
self.pre_merge_sha = self.githash('repo_1', 1)
self.post_merge_sha = self.githash('repo_1', 2)
def CheckStatusPreMergePoint(self):
self.assertEqual(scm.GIT.Capture(['config', 'remote.origin.url'],
self.blink), self.blink_git_url)
self.assertTrue(os.path.exists(join(self.blink, '.git')))
self.assertTrue(os.path.exists(join(self.blink, 'OWNERS')))
with open(join(self.blink, 'OWNERS')) as f:
owners_content = f.read()
self.assertEqual('OWNERS-pre', owners_content, 'OWNERS not updated')
self.assertTrue(os.path.exists(join(self.blink, 'Source', 'exists_always')))
self.assertTrue(os.path.exists(
join(self.blink, 'Source', 'exists_before_but_not_after')))
self.assertFalse(os.path.exists(
join(self.blink, 'Source', 'exists_after_but_not_before')))
def CheckStatusPostMergePoint(self):
# Check that the contents still exists
self.assertTrue(os.path.exists(join(self.blink, 'OWNERS')))
with open(join(self.blink, 'OWNERS')) as f:
owners_content = f.read()
self.assertEqual('OWNERS-post', owners_content, 'OWNERS not updated')
self.assertTrue(os.path.exists(join(self.blink, 'Source', 'exists_always')))
# Check that file removed between the branch point are actually deleted.
self.assertTrue(os.path.exists(
join(self.blink, 'Source', 'exists_after_but_not_before')))
self.assertFalse(os.path.exists(
join(self.blink, 'Source', 'exists_before_but_not_after')))
# But not the .git folder
self.assertFalse(os.path.exists(join(self.blink, '.git')))
@unittest.skip('flaky')
def testBlinkDEPSChangeUsingGclient(self):
"""Checks that {src,blink} repos are consistent when syncing going back and
forth using gclient sync src@revision."""
self.gclient(['config', '--spec',
'solutions=['
'{"name": "src",'
' "url": "' + self.git_base + 'repo_1",'
'}]'])
# Go back and forth two times.
for _ in range(2):
res = self.gclient(['sync', '--jobs', '1',
'--revision', 'src@%s' % self.pre_merge_sha])
self.assertEqual(res[2], 0, 'DEPS change sync failed.')
self.CheckStatusPreMergePoint()
res = self.gclient(['sync', '--jobs', '1',
'--revision', 'src@%s' % self.post_merge_sha])
self.assertEqual(res[2], 0, 'DEPS change sync failed.')
self.CheckStatusPostMergePoint()
@unittest.skip('flaky')
def testBlinkDEPSChangeUsingGit(self):
"""Like testBlinkDEPSChangeUsingGclient, but move the main project using
directly git and not gclient sync."""
self.gclient(['config', '--spec',
'solutions=['
'{"name": "src",'
' "url": "' + self.git_base + 'repo_1",'
' "managed": False,'
'}]'])
# Perform an initial sync to bootstrap the repo.
res = self.gclient(['sync', '--jobs', '1'])
self.assertEqual(res[2], 0, 'Initial gclient sync failed.')
# Go back and forth two times.
for _ in range(2):
subprocess2.check_call(['git', 'checkout', '-q', self.pre_merge_sha],
cwd=self.checkout_path)
res = self.gclient(['sync', '--jobs', '1'])
self.assertEqual(res[2], 0, 'gclient sync failed.')
self.CheckStatusPreMergePoint()
subprocess2.check_call(['git', 'checkout', '-q', self.post_merge_sha],
cwd=self.checkout_path)
res = self.gclient(['sync', '--jobs', '1'])
self.assertEqual(res[2], 0, 'DEPS change sync failed.')
self.CheckStatusPostMergePoint()
@unittest.skip('flaky')
def testBlinkLocalBranchesArePreserved(self):
"""Checks that the state of local git branches are effectively preserved
when going back and forth."""
self.gclient(['config', '--spec',
'solutions=['
'{"name": "src",'
' "url": "' + self.git_base + 'repo_1",'
'}]'])
# Initialize to pre-merge point.
self.gclient(['sync', '--revision', 'src@%s' % self.pre_merge_sha])
self.CheckStatusPreMergePoint()
# Create a branch named "foo".
subprocess2.check_call(['git', 'checkout', '-qB', 'foo'],
cwd=self.blink)
# Cross the pre-merge point.
self.gclient(['sync', '--revision', 'src@%s' % self.post_merge_sha])
self.CheckStatusPostMergePoint()
# Go backwards and check that we still have the foo branch.
self.gclient(['sync', '--revision', 'src@%s' % self.pre_merge_sha])
self.CheckStatusPreMergePoint()
subprocess2.check_call(
['git', 'show-ref', '-q', '--verify', 'refs/heads/foo'], cwd=self.blink)
if __name__ == '__main__':
if '-v' in sys.argv:
logging.basicConfig(level=logging.DEBUG)
unittest.main()
Loading…
Cancel
Save