Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: tests/fake_repos.py

Issue 6625018: Add trial_dir to manage automatic trial directory creation and deletion. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: fix typo Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/trial_dir.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2010 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Generate fake repositories for testing.""" 6 """Generate fake repositories for testing."""
7 7
8 import atexit 8 import atexit
9 import datetime 9 import datetime
10 import errno 10 import errno
11 import logging 11 import logging
12 import os 12 import os
13 import pprint 13 import pprint
14 import re 14 import re
15 import socket 15 import socket
16 import stat 16 import stat
17 import subprocess 17 import subprocess
18 import sys 18 import sys
19 import tempfile 19 import tempfile
20 import time 20 import time
21 import unittest
22 21
23 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 22 # trial_dir must be first for non-system libraries.
24 23 from tests import trial_dir
25 import scm 24 import scm
26 25
27 ## Utility functions 26 ## Utility functions
28 27
29 28
30 def kill_pid(pid): 29 def kill_pid(pid):
31 """Kills a process by its process id.""" 30 """Kills a process by its process id."""
32 try: 31 try:
33 # Unable to import 'module' 32 # Unable to import 'module'
34 # pylint: disable=F0401 33 # pylint: disable=F0401
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after
223 class FakeReposBase(object): 222 class FakeReposBase(object):
224 """Generate both svn and git repositories to test gclient functionality. 223 """Generate both svn and git repositories to test gclient functionality.
225 224
226 Many DEPS functionalities need to be tested: Var, File, From, deps_os, hooks, 225 Many DEPS functionalities need to be tested: Var, File, From, deps_os, hooks,
227 use_relative_paths. 226 use_relative_paths.
228 227
229 And types of dependencies: Relative urls, Full urls, both svn and git. 228 And types of dependencies: Relative urls, Full urls, both svn and git.
230 229
231 populateSvn() and populateGit() need to be implemented by the subclass. 230 populateSvn() and populateGit() need to be implemented by the subclass.
232 """ 231 """
233
234 # When SHOULD_LEAK is set to True, temporary directories created while the
235 # tests are running aren't deleted at the end of the tests. Expect failures
236 # when running more than one test due to inter-test side-effects. Helps with
237 # debugging.
238 SHOULD_LEAK = False
239 # Override if unhappy.
240 TRIAL_DIR = None
241 # Hostname 232 # Hostname
242 NB_GIT_REPOS = 1 233 NB_GIT_REPOS = 1
243 USERS = [ 234 USERS = [
244 ('user1@example.com', 'foo'), 235 ('user1@example.com', 'foo'),
245 ('user2@example.com', 'bar'), 236 ('user2@example.com', 'bar'),
246 ] 237 ]
247 238
248 def __init__(self, trial_dir=None, leak=None, host=None): 239 def __init__(self, host=None):
249 global _FAKE_LOADED 240 global _FAKE_LOADED
250 if _FAKE_LOADED: 241 if _FAKE_LOADED:
251 raise Exception('You can only start one FakeRepos at a time.') 242 raise Exception('You can only start one FakeRepos at a time.')
252 _FAKE_LOADED = True 243 _FAKE_LOADED = True
253 # Quick hack. 244
254 if '-v' in sys.argv: 245 self.trial = trial_dir.TrialDir('repos')
255 logging.basicConfig(level=logging.DEBUG)
256 elif leak is not None:
257 self.SHOULD_LEAK = leak
258 self.host = host or '127.0.0.1' 246 self.host = host or '127.0.0.1'
259 if trial_dir:
260 self.TRIAL_DIR = trial_dir
261
262 # Format is [ None, tree, tree, ...] 247 # Format is [ None, tree, tree, ...]
263 # i.e. revisions are 1-based. 248 # i.e. revisions are 1-based.
264 self.svn_revs = [None] 249 self.svn_revs = [None]
265 # Format is { repo: [ None, (hash, tree), (hash, tree), ... ], ... } 250 # Format is { repo: [ None, (hash, tree), (hash, tree), ... ], ... }
266 # so reference looks like self.git_hashes[repo][rev][0] for hash and 251 # so reference looks like self.git_hashes[repo][rev][0] for hash and
267 # self.git_hashes[repo][rev][1] for it's tree snapshot. 252 # self.git_hashes[repo][rev][1] for it's tree snapshot.
268 # For consistency with self.svn_revs, it is 1-based too. 253 # For consistency with self.svn_revs, it is 1-based too.
269 self.git_hashes = {} 254 self.git_hashes = {}
270 self.svnserve = None 255 self.svnserve = None
271 self.gitdaemon = None 256 self.gitdaemon = None
272 self.common_init = False
273 self.repos_dir = None
274 self.git_pid_file = None 257 self.git_pid_file = None
275 self.git_root = None 258 self.git_root = None
276 self.svn_checkout = None 259 self.svn_checkout = None
277 self.svn_repo = None 260 self.svn_repo = None
278 self.git_dirty = False 261 self.git_dirty = False
279 self.svn_dirty = False 262 self.svn_dirty = False
280 self.svn_base = 'svn://%s/svn/' % self.host 263 self.svn_base = 'svn://%s/svn/' % self.host
281 self.git_base = 'git://%s/git/' % self.host 264 self.git_base = 'git://%s/git/' % self.host
282 self.svn_port = 3690 265 self.svn_port = 3690
283 self.git_port = 9418 266 self.git_port = 9418
284 267
285 def trial_dir(self): 268 @property
286 if not self.TRIAL_DIR: 269 def root_dir(self):
287 self.TRIAL_DIR = os.path.join( 270 return self.trial.root_dir
288 os.path.dirname(os.path.abspath(__file__)), '_trial')
289 return self.TRIAL_DIR
290 271
291 def set_up(self): 272 def set_up(self):
292 """All late initialization comes here. 273 """All late initialization comes here."""
293
294 Note that it deletes all trial_dir() and not only repos_dir.
295 """
296 self.cleanup_dirt() 274 self.cleanup_dirt()
297 if not self.common_init: 275 if not self.root_dir:
298 self.common_init = True 276 try:
299 self.repos_dir = os.path.join(self.trial_dir(), 'repos') 277 add_kill()
300 self.git_root = join(self.repos_dir, 'git') 278 # self.root_dir is not set before this call.
301 self.svn_checkout = join(self.repos_dir, 'svn_checkout') 279 self.trial.set_up()
302 self.svn_repo = join(self.repos_dir, 'svn') 280 self.git_root = join(self.root_dir, 'git')
303 add_kill() 281 self.svn_checkout = join(self.root_dir, 'svn_checkout')
304 rmtree(self.trial_dir()) 282 self.svn_repo = join(self.root_dir, 'svn')
305 os.makedirs(self.repos_dir) 283 finally:
306 atexit.register(self.tear_down) 284 # Registers cleanup.
285 atexit.register(self.tear_down)
307 286
308 def cleanup_dirt(self): 287 def cleanup_dirt(self):
309 """For each dirty repository, destroy it.""" 288 """For each dirty repository, destroy it."""
310 if self.svn_dirty: 289 if self.svn_dirty:
311 if not self.tear_down_svn(): 290 if not self.tear_down_svn():
312 logging.error('Using both leaking checkout and svn dirty checkout') 291 logging.error('Using both leaking checkout and svn dirty checkout')
313 if self.git_dirty: 292 if self.git_dirty:
314 if not self.tear_down_git(): 293 if not self.tear_down_git():
315 logging.error('Using both leaking checkout and git dirty checkout') 294 logging.error('Using both leaking checkout and git dirty checkout')
316 295
317 def tear_down(self): 296 def tear_down(self):
318 """Kills the servers and delete the directories.""" 297 """Kills the servers and delete the directories."""
319 self.tear_down_svn() 298 self.tear_down_svn()
320 self.tear_down_git() 299 self.tear_down_git()
321 if not self.SHOULD_LEAK: 300 # This deletes the directories.
322 logging.debug('Removing %s' % self.trial_dir()) 301 self.trial.tear_down()
323 rmtree(self.trial_dir()) 302 self.trial = None
324 303
325 def tear_down_svn(self): 304 def tear_down_svn(self):
326 if self.svnserve: 305 if self.svnserve:
327 logging.debug('Killing svnserve pid %s' % self.svnserve.pid) 306 logging.debug('Killing svnserve pid %s' % self.svnserve.pid)
328 self.svnserve.kill() 307 self.svnserve.kill()
329 self.wait_for_port_to_free(self.svn_port) 308 self.wait_for_port_to_free(self.svn_port)
330 self.svnserve = None 309 self.svnserve = None
331 if not self.SHOULD_LEAK: 310 if not self.trial.SHOULD_LEAK:
332 logging.debug('Removing %s' % self.svn_repo) 311 logging.debug('Removing %s' % self.svn_repo)
333 rmtree(self.svn_repo) 312 rmtree(self.svn_repo)
334 logging.debug('Removing %s' % self.svn_checkout) 313 logging.debug('Removing %s' % self.svn_checkout)
335 rmtree(self.svn_checkout) 314 rmtree(self.svn_checkout)
336 else: 315 else:
337 return False 316 return False
338 return True 317 return True
339 318
340 def tear_down_git(self): 319 def tear_down_git(self):
341 if self.gitdaemon: 320 if self.gitdaemon:
342 logging.debug('Killing git-daemon pid %s' % self.gitdaemon.pid) 321 logging.debug('Killing git-daemon pid %s' % self.gitdaemon.pid)
343 self.gitdaemon.kill() 322 self.gitdaemon.kill()
344 self.gitdaemon = None 323 self.gitdaemon = None
345 if self.git_pid_file: 324 if self.git_pid_file:
346 pid = int(self.git_pid_file.read()) 325 pid = int(self.git_pid_file.read())
347 self.git_pid_file.close() 326 self.git_pid_file.close()
348 logging.debug('Killing git daemon pid %s' % pid) 327 logging.debug('Killing git daemon pid %s' % pid)
349 kill_pid(pid) 328 kill_pid(pid)
350 self.git_pid_file = None 329 self.git_pid_file = None
351 self.wait_for_port_to_free(self.git_port) 330 self.wait_for_port_to_free(self.git_port)
352 if not self.SHOULD_LEAK: 331 if not self.trial.SHOULD_LEAK:
353 logging.debug('Removing %s' % self.git_root) 332 logging.debug('Removing %s' % self.git_root)
354 rmtree(self.git_root) 333 rmtree(self.git_root)
355 else: 334 else:
356 return False 335 return False
357 return True 336 return True
358 337
359 @staticmethod 338 @staticmethod
360 def _genTree(root, tree_dict): 339 def _genTree(root, tree_dict):
361 """For a dictionary of file contents, generate a filesystem.""" 340 """For a dictionary of file contents, generate a filesystem."""
362 if not os.path.isdir(root): 341 if not os.path.isdir(root):
(...skipping 22 matching lines...) Expand all
385 write(join(self.svn_repo, 'conf', 'svnserve.conf'), 364 write(join(self.svn_repo, 'conf', 'svnserve.conf'),
386 '[general]\n' 365 '[general]\n'
387 'anon-access = read\n' 366 'anon-access = read\n'
388 'auth-access = write\n' 367 'auth-access = write\n'
389 'password-db = passwd\n') 368 'password-db = passwd\n')
390 text = '[users]\n' 369 text = '[users]\n'
391 text += ''.join('%s = %s\n' % (usr, pwd) for usr, pwd in self.USERS) 370 text += ''.join('%s = %s\n' % (usr, pwd) for usr, pwd in self.USERS)
392 write(join(self.svn_repo, 'conf', 'passwd'), text) 371 write(join(self.svn_repo, 'conf', 'passwd'), text)
393 372
394 # Start the daemon. 373 # Start the daemon.
395 cmd = ['svnserve', '-d', '--foreground', '-r', self.repos_dir] 374 cmd = ['svnserve', '-d', '--foreground', '-r', self.root_dir]
396 if self.host == '127.0.0.1': 375 if self.host == '127.0.0.1':
397 cmd.append('--listen-host=' + self.host) 376 cmd.append('--listen-host=' + self.host)
398 self.check_port_is_free(self.svn_port) 377 self.check_port_is_free(self.svn_port)
399 self.svnserve = Popen(cmd, cwd=self.svn_repo) 378 self.svnserve = Popen(cmd, cwd=self.svn_repo)
400 self.wait_for_port_to_bind(self.svn_port, self.svnserve) 379 self.wait_for_port_to_bind(self.svn_port, self.svnserve)
401 self.populateSvn() 380 self.populateSvn()
402 self.svn_dirty = False 381 self.svn_dirty = False
403 return True 382 return True
404 383
405 def set_up_git(self): 384 def set_up_git(self):
406 """Creates git repositories and start the servers.""" 385 """Creates git repositories and start the servers."""
407 self.set_up() 386 self.set_up()
408 if self.gitdaemon: 387 if self.gitdaemon:
409 return True 388 return True
410 if sys.platform == 'win32': 389 if sys.platform == 'win32':
411 return False 390 return False
412 assert self.git_pid_file == None 391 assert self.git_pid_file == None
413 for repo in ['repo_%d' % r for r in range(1, self.NB_GIT_REPOS + 1)]: 392 for repo in ['repo_%d' % r for r in range(1, self.NB_GIT_REPOS + 1)]:
414 check_call(['git', 'init', '-q', join(self.git_root, repo)]) 393 check_call(['git', 'init', '-q', join(self.git_root, repo)])
415 self.git_hashes[repo] = [None] 394 self.git_hashes[repo] = [None]
416 # Unlike svn, populate git before starting the server. 395 # Unlike svn, populate git before starting the server.
417 self.populateGit() 396 self.populateGit()
418 # Start the daemon. 397 # Start the daemon.
419 self.git_pid_file = tempfile.NamedTemporaryFile() 398 self.git_pid_file = tempfile.NamedTemporaryFile()
420 cmd = ['git', 'daemon', 399 cmd = ['git', 'daemon',
421 '--export-all', 400 '--export-all',
422 '--reuseaddr', 401 '--reuseaddr',
423 '--base-path=' + self.repos_dir, 402 '--base-path=' + self.root_dir,
424 '--pid-file=' + self.git_pid_file.name] 403 '--pid-file=' + self.git_pid_file.name]
425 if self.host == '127.0.0.1': 404 if self.host == '127.0.0.1':
426 cmd.append('--listen=' + self.host) 405 cmd.append('--listen=' + self.host)
427 self.check_port_is_free(self.git_port) 406 self.check_port_is_free(self.git_port)
428 self.gitdaemon = Popen(cmd, cwd=self.repos_dir) 407 self.gitdaemon = Popen(cmd, cwd=self.root_dir)
429 self.wait_for_port_to_bind(self.git_port, self.gitdaemon) 408 self.wait_for_port_to_bind(self.git_port, self.gitdaemon)
430 self.git_dirty = False 409 self.git_dirty = False
431 return True 410 return True
432 411
433 def _commit_svn(self, tree): 412 def _commit_svn(self, tree):
434 self._genTree(self.svn_checkout, tree) 413 self._genTree(self.svn_checkout, tree)
435 commit_svn(self.svn_checkout, self.USERS[0][0], self.USERS[0][1]) 414 commit_svn(self.svn_checkout, self.USERS[0][0], self.USERS[0][1])
436 if self.svn_revs and self.svn_revs[-1]: 415 if self.svn_revs and self.svn_revs[-1]:
437 new_tree = self.svn_revs[-1].copy() 416 new_tree = self.svn_revs[-1].copy()
438 new_tree.update(tree) 417 new_tree.update(tree)
(...skipping 268 matching lines...) Expand 10 before | Expand all | Expand 10 after
707 'git_base': self.git_base, 686 'git_base': self.git_base,
708 # See self.__init__() for the format. Grab's the hash of the first 687 # See self.__init__() for the format. Grab's the hash of the first
709 # commit in repo_2. Only keep the first 7 character because of: 688 # commit in repo_2. Only keep the first 7 character because of:
710 # TODO(maruel): http://crosbug.com/3591 We need to strip the hash.. duh. 689 # TODO(maruel): http://crosbug.com/3591 We need to strip the hash.. duh.
711 'hash': self.git_hashes['repo_2'][1][0][:7] 690 'hash': self.git_hashes['repo_2'][1][0][:7]
712 }, 691 },
713 'origin': 'git/repo_1@2\n', 692 'origin': 'git/repo_1@2\n',
714 }) 693 })
715 694
716 695
717 class FakeReposTestBase(unittest.TestCase): 696 class FakeReposTestBase(trial_dir.TestCase):
718 """This is vaguely inspired by twisted.""" 697 """This is vaguely inspired by twisted."""
719
720 # Replace this in your subclass.
721 CLASS_ROOT_DIR = None
722
723 # static FakeRepos instance. Lazy loaded. 698 # static FakeRepos instance. Lazy loaded.
724 FAKE_REPOS = None 699 FAKE_REPOS = None
725 # Override if necessary. 700 # Override if necessary.
726 FAKE_REPOS_CLASS = FakeRepos 701 FAKE_REPOS_CLASS = FakeRepos
727 702
728 def __init__(self, *args, **kwargs): 703 def setUp(self):
729 unittest.TestCase.__init__(self, *args, **kwargs) 704 super(FakeReposTestBase, self).setUp()
730 if not FakeReposTestBase.FAKE_REPOS: 705 if not FakeReposTestBase.FAKE_REPOS:
731 # Lazy create the global instance. 706 # Lazy create the global instance.
732 FakeReposTestBase.FAKE_REPOS = self.FAKE_REPOS_CLASS() 707 FakeReposTestBase.FAKE_REPOS = self.FAKE_REPOS_CLASS()
733 708 # No need to call self.FAKE_REPOS.setUp(), it will be called by the child
734 def setUp(self): 709 # class.
735 unittest.TestCase.setUp(self) 710 # Do not define tearDown(), since super's version does the right thing and
736 self.FAKE_REPOS.set_up() 711 # FAKE_REPOS is kept across tests.
737
738 # Remove left overs and start fresh.
739 if not self.CLASS_ROOT_DIR:
740 self.CLASS_ROOT_DIR = join(self.FAKE_REPOS.trial_dir(), 'smoke')
741 self.root_dir = join(self.CLASS_ROOT_DIR, self.id())
742 rmtree(self.root_dir)
743 os.makedirs(self.root_dir)
744
745 def tearDown(self):
746 if not self.FAKE_REPOS.SHOULD_LEAK:
747 rmtree(self.root_dir)
748 712
749 @property 713 @property
750 def svn_base(self): 714 def svn_base(self):
751 """Shortcut.""" 715 """Shortcut."""
752 return self.FAKE_REPOS.svn_base 716 return self.FAKE_REPOS.svn_base
753 717
754 @property 718 @property
755 def git_base(self): 719 def git_base(self):
756 """Shortcut.""" 720 """Shortcut."""
757 return self.FAKE_REPOS.git_base 721 return self.FAKE_REPOS.git_base
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
817 """Sort-hand: Returns the hash for a git 'revision'.""" 781 """Sort-hand: Returns the hash for a git 'revision'."""
818 return self.FAKE_REPOS.git_hashes[repo][int(rev)][0] 782 return self.FAKE_REPOS.git_hashes[repo][int(rev)][0]
819 783
820 def gittree(self, repo, rev): 784 def gittree(self, repo, rev):
821 """Sort-hand: returns the directory tree for a git 'revision'.""" 785 """Sort-hand: returns the directory tree for a git 'revision'."""
822 return self.FAKE_REPOS.git_hashes[repo][int(rev)][1] 786 return self.FAKE_REPOS.git_hashes[repo][int(rev)][1]
823 787
824 788
825 def main(argv): 789 def main(argv):
826 fake = FakeRepos() 790 fake = FakeRepos()
827 print 'Using %s' % fake.trial_dir() 791 print 'Using %s' % fake.root_dir
828 try: 792 try:
829 fake.set_up_svn() 793 fake.set_up_svn()
830 fake.set_up_git() 794 fake.set_up_git()
831 print('Fake setup, press enter to quit or Ctrl-C to keep the checkouts.') 795 print('Fake setup, press enter to quit or Ctrl-C to keep the checkouts.')
832 sys.stdin.readline() 796 sys.stdin.readline()
833 except KeyboardInterrupt: 797 except KeyboardInterrupt:
834 fake.SHOULD_LEAK = True 798 trial_dir.TrialDir.SHOULD_LEAK.leak = True
835 return 0 799 return 0
836 800
837 801
838 if '-l' in sys.argv:
839 # See SHOULD_LEAK definition in FakeReposBase for its purpose.
840 FakeReposBase.SHOULD_LEAK = True
841 print 'Leaking!'
842 sys.argv.remove('-l')
843
844
845 if __name__ == '__main__': 802 if __name__ == '__main__':
846 sys.exit(main(sys.argv)) 803 sys.exit(main(sys.argv))
OLDNEW
« no previous file with comments | « no previous file | tests/trial_dir.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698