Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 errno | |
| 9 import logging | 10 import logging |
| 10 import os | 11 import os |
| 11 import pprint | 12 import pprint |
| 12 import re | 13 import re |
| 13 import shutil | 14 import stat |
| 14 import subprocess | 15 import subprocess |
| 15 import sys | 16 import sys |
| 17 import time | |
| 16 import unittest | 18 import unittest |
| 17 | 19 |
| 18 | 20 |
| 19 ## Utility functions | 21 ## Utility functions |
| 20 | 22 |
| 21 | 23 |
| 22 def addKill(): | 24 def addKill(): |
| 23 """Add kill() method to subprocess.Popen for python <2.6""" | 25 """Add kill() method to subprocess.Popen for python <2.6""" |
| 24 if getattr(subprocess.Popen, 'kill', None): | 26 if getattr(subprocess.Popen, 'kill', None): |
| 25 return | 27 return |
| 26 if sys.platform.startswith('win'): | 28 if sys.platform.startswith('win'): |
| 27 def kill_win(process): | 29 def kill_win(process): |
| 28 import win32process | 30 import win32process |
| 29 return win32process.TerminateProcess(process._handle, -1) | 31 return win32process.TerminateProcess(process._handle, -1) |
| 30 subprocess.kill = kill_win | 32 subprocess.kill = kill_win |
| 31 else: | 33 else: |
| 32 def kill_nix(process): | 34 def kill_nix(process): |
| 33 import signal | 35 import signal |
| 34 return os.kill(process.pid, signal.SIGKILL) | 36 return os.kill(process.pid, signal.SIGKILL) |
| 35 subprocess.kill = kill_nix | 37 subprocess.kill = kill_nix |
| 36 | 38 |
| 37 | 39 |
| 38 def rmtree(path): | 40 def rmtree(*path): |
| 39 """Delete a directory.""" | 41 """Recursively removes a directory, even if it's marked read-only. |
| 40 if os.path.exists(path): | 42 |
| 41 shutil.rmtree(path) | 43 Remove the directory located at *path, if it exists. |
| 44 | |
| 45 shutil.rmtree() doesn't work on Windows if any of the files or directories | |
| 46 are read-only, which svn repositories and some .svn files are. We need to | |
| 47 be able to force the files to be writable (i.e., deletable) as we traverse | |
| 48 the tree. | |
| 49 | |
| 50 Even with all this, Windows still sometimes fails to delete a file, citing | |
| 51 a permission error (maybe something to do with antivirus scans or disk | |
| 52 indexing). The best suggestion any of the user forums had was to wait a | |
| 53 bit and try again, so we do that too. It's hand-waving, but sometimes it | |
| 54 works. :/ | |
| 55 """ | |
| 56 file_path = os.path.join(*path) | |
| 57 if not os.path.exists(file_path): | |
| 58 return | |
| 59 | |
| 60 def RemoveWithRetry_win(rmfunc, path): | |
| 61 os.chmod(path, stat.S_IWRITE) | |
| 62 if win32_api_avail: | |
| 63 win32api.SetFileAttributes(path, win32con.FILE_ATTRIBUTE_NORMAL) | |
| 64 try: | |
| 65 return rmfunc(path) | |
| 66 except EnvironmentError, e: | |
| 67 if e.errno != errno.EACCES: | |
| 68 raise | |
| 69 print 'Failed to delete %s: trying again' % repr(path) | |
| 70 time.sleep(0.1) | |
| 71 return rmfunc(path) | |
| 72 | |
| 73 def RemoveWithRetry_non_win(rmfunc, path): | |
| 74 if os.path.islink(path): | |
| 75 return os.remove(path) | |
| 76 else: | |
| 77 return rmfunc(path) | |
| 78 | |
| 79 win32_api_avail = False | |
| 80 remove_with_retry = None | |
| 81 if sys.platform.startswith('win'): | |
|
bradn
2010/06/01 01:12:04
Pretty sure this is always win32, but anyways. Did
M-A Ruel
2010/06/01 01:26:27
You are right, it's just a copy-paste from scripts
| |
| 82 # Some people don't have the APIs installed. In that case we'll do without. | |
| 83 try: | |
| 84 win32api = __import__('win32api') | |
| 85 win32con = __import__('win32con') | |
| 86 win32_api_avail = True | |
| 87 except ImportError: | |
| 88 pass | |
| 89 remove_with_retry = RemoveWithRetry_win | |
| 90 else: | |
| 91 remove_with_retry = RemoveWithRetry_non_win | |
| 92 | |
| 93 for root, dirs, files in os.walk(file_path, topdown=False): | |
| 94 # For POSIX: making the directory writable guarantees removability. | |
| 95 # Windows will ignore the non-read-only bits in the chmod value. | |
| 96 os.chmod(root, 0770) | |
| 97 for name in files: | |
| 98 remove_with_retry(os.remove, os.path.join(root, name)) | |
| 99 for name in dirs: | |
| 100 remove_with_retry(os.rmdir, os.path.join(root, name)) | |
| 101 | |
| 102 remove_with_retry(os.rmdir, file_path) | |
| 42 | 103 |
| 43 | 104 |
| 44 def write(path, content): | 105 def write(path, content): |
| 45 f = open(path, 'wb') | 106 f = open(path, 'wb') |
| 46 f.write(content) | 107 f.write(content) |
| 47 f.close() | 108 f.close() |
| 48 | 109 |
| 49 | 110 |
| 50 join = os.path.join | 111 join = os.path.join |
| 51 | 112 |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 62 return subprocess.Popen(*args, **kwargs) | 123 return subprocess.Popen(*args, **kwargs) |
| 63 | 124 |
| 64 | 125 |
| 65 def read_tree(tree_root): | 126 def read_tree(tree_root): |
| 66 """Returns a dict of all the files in a tree. Defaults to self.root_dir.""" | 127 """Returns a dict of all the files in a tree. Defaults to self.root_dir.""" |
| 67 tree = {} | 128 tree = {} |
| 68 for root, dirs, files in os.walk(tree_root): | 129 for root, dirs, files in os.walk(tree_root): |
| 69 for d in filter(lambda x: x.startswith('.'), dirs): | 130 for d in filter(lambda x: x.startswith('.'), dirs): |
| 70 dirs.remove(d) | 131 dirs.remove(d) |
| 71 for f in [join(root, f) for f in files if not f.startswith('.')]: | 132 for f in [join(root, f) for f in files if not f.startswith('.')]: |
| 72 tree[f[len(tree_root) + 1:]] = open(join(root, f), 'rb').read() | 133 filepath = f[len(tree_root) + 1:].replace(os.sep, '/') |
| 134 assert len(filepath), f | |
| 135 tree[filepath] = open(join(root, f), 'rU').read() | |
| 73 return tree | 136 return tree |
| 74 | 137 |
| 75 | 138 |
| 76 def dict_diff(dict1, dict2): | 139 def dict_diff(dict1, dict2): |
| 77 diff = {} | 140 diff = {} |
| 78 for k, v in dict1.iteritems(): | 141 for k, v in dict1.iteritems(): |
| 79 if k not in dict2: | 142 if k not in dict2: |
| 80 diff[k] = v | 143 diff[k] = v |
| 81 elif v != dict2[k]: | 144 elif v != dict2[k]: |
| 82 diff[k] = (v, dict2[k]) | 145 diff[k] = (v, dict2[k]) |
| 83 for k, v in dict2.iteritems(): | 146 for k, v in dict2.iteritems(): |
| 84 if k not in dict1: | 147 if k not in dict1: |
| 85 diff[k] = v | 148 diff[k] = v |
| 86 return diff | 149 return diff |
| 87 | 150 |
| 88 | 151 |
| 89 def mangle_svn_tree(*args): | 152 def mangle_svn_tree(*args): |
| 90 result = {} | 153 result = {} |
| 91 for old_root, new_root, tree in args: | 154 for old_root, new_root, tree in args: |
| 92 for k, v in tree.iteritems(): | 155 for k, v in tree.iteritems(): |
| 93 if not k.startswith(old_root): | 156 if not k.startswith(old_root): |
| 94 continue | 157 continue |
| 95 result[join(new_root, k[len(old_root) + 1:])] = v | 158 result[join(new_root, k[len(old_root) + 1:]).replace(os.sep, '/')] = v |
| 96 return result | 159 return result |
| 97 | 160 |
| 98 | 161 |
| 99 def mangle_git_tree(*args): | 162 def mangle_git_tree(*args): |
| 100 result = {} | 163 result = {} |
| 101 for new_root, tree in args: | 164 for new_root, tree in args: |
| 102 for k, v in tree.iteritems(): | 165 for k, v in tree.iteritems(): |
| 103 result[join(new_root, k)] = v | 166 result[join(new_root, k)] = v |
| 104 return result | 167 return result |
| 105 | 168 |
| (...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 323 'pattern': 'nonexistent', | 386 'pattern': 'nonexistent', |
| 324 'action': ['python', '-c', | 387 'action': ['python', '-c', |
| 325 'open(\\'src/svn_hooked2\\', \\'w\\').write(\\'svn_hooked2\\')'], | 388 'open(\\'src/svn_hooked2\\', \\'w\\').write(\\'svn_hooked2\\')'], |
| 326 }, | 389 }, |
| 327 ] | 390 ] |
| 328 """ % { 'host': self.HOST })) | 391 """ % { 'host': self.HOST })) |
| 329 | 392 |
| 330 def setUpGIT(self): | 393 def setUpGIT(self): |
| 331 """Creates git repositories and start the servers.""" | 394 """Creates git repositories and start the servers.""" |
| 332 if self.gitdaemon: | 395 if self.gitdaemon: |
| 333 return | 396 return True |
| 334 self.setUp() | 397 self.setUp() |
| 398 if sys.platform == 'win32': | |
| 399 return False | |
| 335 for repo in ['repo_%d' % r for r in range(1, 5)]: | 400 for repo in ['repo_%d' % r for r in range(1, 5)]: |
| 336 check_call(['git', 'init', '-q', join(self.git_root, repo)]) | 401 check_call(['git', 'init', '-q', join(self.git_root, repo)]) |
| 337 self.git_hashes[repo] = [] | 402 self.git_hashes[repo] = [] |
| 338 | 403 |
| 339 # Testing: | 404 # Testing: |
| 340 # - dependency disapear | 405 # - dependency disapear |
| 341 # - dependency renamed | 406 # - dependency renamed |
| 342 # - versioned and unversioned reference | 407 # - versioned and unversioned reference |
| 343 # - relative and full reference | 408 # - relative and full reference |
| 344 # - deps_os | 409 # - deps_os |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 417 }, | 482 }, |
| 418 'origin': "git/repo_1@2\n" | 483 'origin': "git/repo_1@2\n" |
| 419 }) | 484 }) |
| 420 | 485 |
| 421 # Start the daemon. | 486 # Start the daemon. |
| 422 cmd = ['git', 'daemon', '--export-all', '--base-path=' + self.repos_dir] | 487 cmd = ['git', 'daemon', '--export-all', '--base-path=' + self.repos_dir] |
| 423 if self.HOST == '127.0.0.1': | 488 if self.HOST == '127.0.0.1': |
| 424 cmd.append('--listen=127.0.0.1') | 489 cmd.append('--listen=127.0.0.1') |
| 425 logging.debug(cmd) | 490 logging.debug(cmd) |
| 426 self.gitdaemon = Popen(cmd, cwd=self.repos_dir) | 491 self.gitdaemon = Popen(cmd, cwd=self.repos_dir) |
| 492 return True | |
| 427 | 493 |
| 428 def _commit_svn(self, tree): | 494 def _commit_svn(self, tree): |
| 429 self._genTree(self.svn_root, tree) | 495 self._genTree(self.svn_root, tree) |
| 430 commit_svn(self.svn_root) | 496 commit_svn(self.svn_root) |
| 431 if self.svn_revs and self.svn_revs[-1]: | 497 if self.svn_revs and self.svn_revs[-1]: |
| 432 new_tree = self.svn_revs[-1].copy() | 498 new_tree = self.svn_revs[-1].copy() |
| 433 new_tree.update(tree) | 499 new_tree.update(tree) |
| 434 else: | 500 else: |
| 435 new_tree = tree.copy() | 501 new_tree = tree.copy() |
| 436 self.svn_revs.append(new_tree) | 502 self.svn_revs.append(new_tree) |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 511 fake.setUp() | 577 fake.setUp() |
| 512 print('Fake setup, press enter to quit or Ctrl-C to keep the checkouts.') | 578 print('Fake setup, press enter to quit or Ctrl-C to keep the checkouts.') |
| 513 sys.stdin.readline() | 579 sys.stdin.readline() |
| 514 except KeyboardInterrupt: | 580 except KeyboardInterrupt: |
| 515 fake.SHOULD_LEAK = True | 581 fake.SHOULD_LEAK = True |
| 516 return 0 | 582 return 0 |
| 517 | 583 |
| 518 | 584 |
| 519 if __name__ == '__main__': | 585 if __name__ == '__main__': |
| 520 sys.exit(main(sys.argv)) | 586 sys.exit(main(sys.argv)) |
| OLD | NEW |