From 83bea09ad234efd0873c2185dbe8b68130fbf1cc Mon Sep 17 00:00:00 2001 From: "maruel@chromium.org" Date: Tue, 18 May 2010 22:51:31 +0000 Subject: [PATCH] Add basic gclient smoke tests. The unit tests are unbearable and the next change forces me to trash them. Thus adding smoke tests before. The current tests aren't involved enough yet but are a good base for further testing. I plan to only test the most important functionalities, the ones used in the continuous build and try server. TEST=new smoke test BUG=23328 Review URL: http://codereview.chromium.org/2092012 git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@47580 0039d316-1c4b-4281-b951-d872f2087c98 --- .gitignore | 1 + tests/fake_repos.py | 203 +++++++++++++++++++++++++++++++++++++ tests/gclient_smoketest.py | 135 ++++++++++++++++++++++++ 3 files changed, 339 insertions(+) create mode 100755 tests/fake_repos.py create mode 100755 tests/gclient_smoketest.py diff --git a/.gitignore b/.gitignore index bb054a9ef..0e56ccd8d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.pyc /git-cl-repo /tests/pymox +/tests/_trial /python /python.bat /python_bin diff --git a/tests/fake_repos.py b/tests/fake_repos.py new file mode 100755 index 000000000..307a3b790 --- /dev/null +++ b/tests/fake_repos.py @@ -0,0 +1,203 @@ +#!/usr/bin/python +# Copyright (c) 2010 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. + +"""Generate fake repositories for testing.""" + +import os +import shutil +import subprocess +import sys + + +def addKill(): + """Add kill() method to subprocess.Popen for python <2.6""" + if getattr(subprocess.Popen, 'kill', None): + return + if sys.platform.startswith('win'): + def kill_win(process): + import win32process + return win32process.TerminateProcess(process._handle, -1) + subprocess.kill = kill_win + else: + def kill_nix(process): + import signal + return os.kill(process.pid, signal.SIGKILL) + subprocess.kill = kill_nix + + +def rmtree(path): + """Delete a directory.""" + if os.path.exists(path): + shutil.rmtree(path) + + +def write(path, content): + f = open(path, 'wb') + f.write(content) + f.close() + + +class FakeRepos(object): + def __init__(self, trial_dir, leak, local_only): + self.trial_dir = trial_dir + self.repos_dir = os.path.join(self.trial_dir, 'repos') + self.leak = leak + self.local_only = local_only + self.svnserve = [] + self.gitdaemon = [] + addKill() + rmtree(self.trial_dir) + os.mkdir(self.trial_dir) + os.mkdir(self.repos_dir) + + def setUp(self): + self.setUpSVN() + self.setUpGIT() + + def tearDown(self): + for i in self.svnserve: + i.kill() + for i in self.gitdaemon: + i.kill() + if not self.leak: + rmtree(self.trial_dir) + + def setUpSVN(self): + """Creates subversion repositories and start the servers.""" + assert not self.svnserve + join = os.path.join + root = join(self.repos_dir, 'svn') + rmtree(root) + subprocess.check_call(['svnadmin', 'create', root]) + write(join(root, 'conf', 'svnserve.conf'), + '[general]\n' + 'anon-access = read\n' + 'auth-access = write\n' + 'password-db = passwd\n') + write(join(root, 'conf', 'passwd'), + '[users]\n' + 'user1 = foo\n' + 'user2 = bar\n') + + # Repos + repo = join(self.repos_dir, 'svn_import') + rmtree(repo) + os.mkdir(repo) + os.mkdir(join(repo, 'trunk')) + os.mkdir(join(repo, 'trunk', 'src')) + write(join(repo, 'trunk', 'src', 'DEPS'), """ +# Smoke test DEPS file. +# Many DEPS functionalities need to be tested: +# Var +# File +# From +# deps_os +# hooks +# use_relative_paths +# +# Types of dependencies: +# Relative urls +# Full urls +# svn +# git + +deps = { + 'src/other': 'svn://%(host)s/svn/trunk/other', + 'src/third_party': '/trunk/third_party', +} + +deps_os = { + 'mac': 'repo_4' +} +""" % { + 'host': 'localhost', +}) + write(join(repo, 'trunk', 'src', 'origin'), "svn/trunk/src") + os.mkdir(join(repo, 'trunk', 'other')) + write(join(repo, 'trunk', 'other', 'origin'), "svn/trunk/other") + os.mkdir(join(repo, 'trunk', 'third_party')) + write(join(repo, 'trunk', 'third_party', 'origin'), "svn/trunk/third_party") + + # Start the daemon. + cmd = ['svnserve', '-d', '--foreground', '-r', self.repos_dir] + if self.local_only: + cmd.append('--listen-host=127.0.0.1') + self.svnserve.append(subprocess.Popen(cmd, cwd=root)) + + # Import the repo. + subprocess.check_call(['svn', 'import', repo, + 'svn://127.0.0.1/svn', '-m', 'foo', '-q', + '--no-auto-props', '--non-interactive', '--no-auth-cache', + '--username', 'user1', '--password', 'foo']) + + def setUpGIT(self): + """Creates git repositories and start the servers.""" + assert not self.gitdaemon + join = os.path.join + root = join(self.repos_dir, 'git') + rmtree(root) + os.mkdir(root) + # Repo 1 + repo = join(root, 'repo_1') + subprocess.check_call(['git', 'init', '-q', repo]) + write(join(repo, 'DEPS'), """ +# Smoke test DEPS file. +# Many DEPS functionalities need to be tested: +# Var +# File +# From +# deps_os +# hooks +# use_relative_paths +# +# Types of dependencies: +# Relative urls +# Full urls +# svn +# git + +deps = { + 'repo2': 'git://%(host)s/git/repo_2', + 'repo2/repo3': '/repo_3', +} + +deps_os = { + 'mac': 'repo_4' +} +""" % { + 'host': 'localhost', +}) + write(join(repo, 'origin'), "git/repo_1") + subprocess.check_call(['git', 'add', '-A', '-f'], cwd=repo) + subprocess.check_call(['git', 'commit', '-q', '-m', 'foo'], cwd=repo) + + # Repo 2 + repo = join(root, 'repo_2') + subprocess.check_call(['git', 'init', '-q', repo]) + write(join(repo, 'origin'), "git/repo_2") + subprocess.check_call(['git', 'add', '-A', '-f'], cwd=repo) + subprocess.check_call(['git', 'commit', '-q', '-m', 'foo'], cwd=repo) + + # Repo 3 + repo = join(root, 'repo_3') + subprocess.check_call(['git', 'init', '-q', repo]) + write(join(repo, 'origin'), "git/repo_3") + subprocess.check_call(['git', 'add', '-A', '-f'], cwd=repo) + subprocess.check_call(['git', 'commit', '-q', '-m', 'foo'], cwd=repo) + + # Start the daemon. + cmd = ['git', 'daemon', '--export-all', '--base-path=' + self.repos_dir] + if self.local_only: + cmd.append('--listen=127.0.0.1') + self.gitdaemon.append(subprocess.Popen(cmd, cwd=self.repos_dir, + stderr=subprocess.PIPE)) + +if __name__ == '__main__': + fake = FakeRepos(os.path.dirname(os.path.abspath(__file__)), False) + try: + fake.setUp() + sys.stdin.readline() + finally: + fake.tearDown() diff --git a/tests/gclient_smoketest.py b/tests/gclient_smoketest.py new file mode 100755 index 000000000..9d04dc6d8 --- /dev/null +++ b/tests/gclient_smoketest.py @@ -0,0 +1,135 @@ +#!/usr/bin/python +# Copyright (c) 2010 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 basic conformance tests. + +This test assumes GClientSmokeBase.URL_BASE is valid. +""" + +import os +import shutil +import subprocess +import sys +import unittest + +from fake_repos import rmtree, FakeRepos + +SHOULD_LEAK = False +UNITTEST_DIR = os.path.abspath(os.path.dirname(__file__)) +GCLIENT_PATH = os.path.join(os.path.dirname(UNITTEST_DIR), 'gclient') +# all tests outputs goes there. +TRIAL_DIR = os.path.join(UNITTEST_DIR, '_trial') +# In case you want to use another machine to create the fake repos, e.g. +# not on Windows. +HOST = '127.0.0.1' + + +class GClientSmokeBase(unittest.TestCase): + # This subversion repository contains a test repository. + ROOT_DIR = os.path.join(TRIAL_DIR, 'smoke') + + def setUp(self): + # Vaguely inspired by twisted. + # Make sure it doesn't try to auto update when testing! + self.env = os.environ.copy() + self.env['DEPOT_TOOLS_UPDATE'] = '0' + # Remove left overs + self.root_dir = os.path.join(self.ROOT_DIR, self.id()) + rmtree(self.root_dir) + if not os.path.exists(self.ROOT_DIR): + os.mkdir(self.ROOT_DIR) + os.mkdir(self.root_dir) + self.svn_base = 'svn://%s/svn/' % HOST + self.git_base = 'git://%s/git/' % HOST + + def tearDown(self): + if not SHOULD_LEAK: + rmtree(self.root_dir) + + def gclient(self, cmd, cwd=None): + if not cwd: + cwd = self.root_dir + process = subprocess.Popen([GCLIENT_PATH] + cmd, cwd=cwd, env=self.env, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + shell=sys.platform.startswith('win')) + (stdout, stderr) = process.communicate() + return (stdout, stderr, process.returncode) + + def check(self, expected, results): + def checkString(expected, result): + if expected != result: + while expected and result and expected[0] == result[0]: + expected = expected[1:] + result = result[1:] + self.assertEquals(expected, result) + checkString(expected[0], results[0]) + checkString(expected[1], results[1]) + self.assertEquals(expected[2], results[2]) + + +class GClientSmoke(GClientSmokeBase): + def testCommands(self): + """This test is to make sure no new command was added.""" + result = self.gclient(['help']) + self.assertEquals(3189, len(result[0])) + self.assertEquals(0, len(result[1])) + self.assertEquals(0, result[2]) + + def testNotConfigured(self): + res = ("", "Error: client not configured; see 'gclient config'\n", 1) + self.check(res, self.gclient(['cleanup'])) + self.check(res, self.gclient(['diff'])) + self.check(res, self.gclient(['export', 'foo'])) + self.check(res, self.gclient(['pack'])) + self.check(res, self.gclient(['revert'])) + self.check(res, self.gclient(['revinfo'])) + self.check(res, self.gclient(['runhooks'])) + self.check(res, self.gclient(['status'])) + self.check(res, self.gclient(['sync'])) + self.check(res, self.gclient(['update'])) + + +class GClientSmokeSync(GClientSmokeBase): + """sync is the most important command. Hence test it more.""" + def testSyncSvn(self): + """Test pure gclient svn checkout, example of Chromium checkout""" + self.gclient(['config', self.svn_base + 'trunk/src/']) + results = self.gclient(['sync']) + self.assertEquals(0, results[2]) + results = self.gclient(['sync', '--revision', 'a@32']) + self.assertEquals(0, results[2]) + + def testSyncGit(self): + """Test pure gclient git checkout, example of Chromium OS checkout""" + self.gclient(['config', self.git_base + 'repo_1']) + results = self.gclient(['sync']) + print results[0] + print results[1] + self.assertEquals(0, results[2]) + + +class GClientSmokeRevert(GClientSmokeBase): + """revert is the second most important command. Hence test it more.""" + def setUp(self): + GClientSmokeBase.setUp(self) + self.gclient(['config', self.URL_BASE]) + + +class GClientSmokeRevInfo(GClientSmokeBase): + """revert is the second most important command. Hence test it more.""" + def setUp(self): + GClientSmokeBase.setUp(self) + self.gclient(['config', self.URL_BASE]) + + +if __name__ == '__main__': + fake = FakeRepos(TRIAL_DIR, SHOULD_LEAK, True) + try: + fake.setUp() + unittest.main() + finally: + fake.tearDown()