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 logging |
8 import os | 9 import os |
| 10 import re |
9 import shutil | 11 import shutil |
10 import subprocess | 12 import subprocess |
11 import sys | 13 import sys |
12 | 14 |
13 | 15 |
14 def addKill(): | 16 def addKill(): |
15 """Add kill() method to subprocess.Popen for python <2.6""" | 17 """Add kill() method to subprocess.Popen for python <2.6""" |
16 if getattr(subprocess.Popen, 'kill', None): | 18 if getattr(subprocess.Popen, 'kill', None): |
17 return | 19 return |
18 if sys.platform.startswith('win'): | 20 if sys.platform.startswith('win'): |
(...skipping 13 matching lines...) Expand all Loading... |
32 if os.path.exists(path): | 34 if os.path.exists(path): |
33 shutil.rmtree(path) | 35 shutil.rmtree(path) |
34 | 36 |
35 | 37 |
36 def write(path, content): | 38 def write(path, content): |
37 f = open(path, 'wb') | 39 f = open(path, 'wb') |
38 f.write(content) | 40 f.write(content) |
39 f.close() | 41 f.close() |
40 | 42 |
41 | 43 |
| 44 join = os.path.join |
| 45 |
| 46 |
| 47 def call(*args, **kwargs): |
| 48 logging.debug(args[0]) |
| 49 subprocess.call(*args, **kwargs) |
| 50 |
| 51 |
| 52 def check_call(*args, **kwargs): |
| 53 logging.debug(args[0]) |
| 54 subprocess.check_call(*args, **kwargs) |
| 55 |
| 56 |
| 57 def Popen(*args, **kwargs): |
| 58 kwargs.setdefault('stdout', subprocess.PIPE) |
| 59 kwargs.setdefault('stderr', subprocess.STDOUT) |
| 60 logging.debug(args[0]) |
| 61 return subprocess.Popen(*args, **kwargs) |
| 62 |
| 63 |
| 64 def commit_svn(repo): |
| 65 """Commits the changes and returns the new revision number.""" |
| 66 # Basic parsing. |
| 67 to_add = [] |
| 68 to_remove = [] |
| 69 for item in Popen(['svn', 'status'], |
| 70 cwd=repo).communicate()[0].splitlines(False): |
| 71 if item[0] == '?': |
| 72 to_add.append(item[8:]) |
| 73 elif item[0] == '!': |
| 74 to_remove.append(item[8:]) |
| 75 if to_add: |
| 76 check_call(['svn', 'add', '--no-auto-props', '-q'] + to_add, cwd=repo) |
| 77 if to_remove: |
| 78 check_call(['svn', 'remove', '-q'] + to_remove, cwd=repo) |
| 79 out = Popen(['svn', 'commit', repo, '-m', 'foo', '--non-interactive', |
| 80 '--no-auth-cache', '--username', 'user1', '--password', 'foo'], |
| 81 cwd=repo).communicate()[0] |
| 82 rev = re.search(r'revision (\d+).', out).group(1) |
| 83 st = Popen(['svn', 'status'], cwd=repo).communicate()[0] |
| 84 assert len(st) == 0, st |
| 85 logging.debug('At revision %s' % rev) |
| 86 return rev |
| 87 |
| 88 |
| 89 def commit_git(repo): |
| 90 """Commits the changes and returns the new hash.""" |
| 91 check_call(['git', 'add', '-A', '-f'], cwd=repo) |
| 92 out = Popen(['git', 'commit', '-m', 'foo'], cwd=repo).communicate()[0] |
| 93 rev = re.search(r'^\[.*? ([a-f\d]+)\] ', out).group(1) |
| 94 logging.debug('At revision %s' % rev) |
| 95 return rev |
| 96 |
| 97 |
42 class FakeRepos(object): | 98 class FakeRepos(object): |
| 99 """Generate both svn and git repositories to test gclient functionality. |
| 100 |
| 101 Many DEPS functionalities need to be tested: Var, File, From, deps_os, hooks, |
| 102 use_relative_paths. |
| 103 |
| 104 And types of dependencies: Relative urls, Full urls, both svn and git.""" |
| 105 |
43 def __init__(self, trial_dir, leak, local_only): | 106 def __init__(self, trial_dir, leak, local_only): |
44 self.trial_dir = trial_dir | 107 self.trial_dir = trial_dir |
45 self.repos_dir = os.path.join(self.trial_dir, 'repos') | 108 self.repos_dir = os.path.join(self.trial_dir, 'repos') |
| 109 self.git_root = join(self.repos_dir, 'git') |
| 110 self.svn_root = join(self.repos_dir, 'svn_checkout') |
46 self.leak = leak | 111 self.leak = leak |
47 self.local_only = local_only | 112 self.local_only = local_only |
48 self.svnserve = [] | 113 self.svnserve = [] |
49 self.gitdaemon = [] | 114 self.gitdaemon = [] |
50 addKill() | 115 addKill() |
51 rmtree(self.trial_dir) | 116 rmtree(self.trial_dir) |
52 os.mkdir(self.trial_dir) | 117 os.mkdir(self.trial_dir) |
53 os.mkdir(self.repos_dir) | 118 os.mkdir(self.repos_dir) |
| 119 # Format is [ None, tree, tree, ...] |
| 120 self.svn_revs = [None] |
| 121 # Format is { repo: [ (hash, tree), (hash, tree), ... ], ... } |
| 122 self.git_hashes = {} |
54 | 123 |
55 def setUp(self): | 124 def setUp(self): |
56 self.setUpSVN() | 125 self.setUpSVN() |
57 self.setUpGIT() | 126 self.setUpGIT() |
58 | 127 |
59 def tearDown(self): | 128 def tearDown(self): |
60 for i in self.svnserve: | 129 for i in self.svnserve: |
| 130 logging.debug('Killing svnserve pid %s' % i.pid) |
61 i.kill() | 131 i.kill() |
62 for i in self.gitdaemon: | 132 for i in self.gitdaemon: |
| 133 logging.debug('Killing git-daemon pid %s' % i.pid) |
63 i.kill() | 134 i.kill() |
64 if not self.leak: | 135 if not self.leak: |
| 136 logging.debug('Removing %s' % self.trial_dir) |
65 rmtree(self.trial_dir) | 137 rmtree(self.trial_dir) |
66 | 138 |
| 139 def _genTree(self, root, tree_dict): |
| 140 """For a dictionary of file contents, generate a filesystem.""" |
| 141 if not os.path.isdir(root): |
| 142 os.makedirs(root) |
| 143 for (k, v) in tree_dict.iteritems(): |
| 144 k_os = k.replace('/', os.sep) |
| 145 k_arr = k_os.split(os.sep) |
| 146 if len(k_arr) > 1: |
| 147 p = os.sep.join([root] + k_arr[:-1]) |
| 148 if not os.path.isdir(p): |
| 149 os.makedirs(p) |
| 150 if v is None: |
| 151 os.remove(join(root, k)) |
| 152 else: |
| 153 write(join(root, k), v) |
| 154 |
67 def setUpSVN(self): | 155 def setUpSVN(self): |
68 """Creates subversion repositories and start the servers.""" | 156 """Creates subversion repositories and start the servers.""" |
69 assert not self.svnserve | 157 assert not self.svnserve |
70 join = os.path.join | |
71 root = join(self.repos_dir, 'svn') | 158 root = join(self.repos_dir, 'svn') |
72 rmtree(root) | 159 rmtree(root) |
73 subprocess.check_call(['svnadmin', 'create', root]) | 160 check_call(['svnadmin', 'create', root]) |
74 write(join(root, 'conf', 'svnserve.conf'), | 161 write(join(root, 'conf', 'svnserve.conf'), |
75 '[general]\n' | 162 '[general]\n' |
76 'anon-access = read\n' | 163 'anon-access = read\n' |
77 'auth-access = write\n' | 164 'auth-access = write\n' |
78 'password-db = passwd\n') | 165 'password-db = passwd\n') |
79 write(join(root, 'conf', 'passwd'), | 166 write(join(root, 'conf', 'passwd'), |
80 '[users]\n' | 167 '[users]\n' |
81 'user1 = foo\n' | 168 'user1 = foo\n' |
82 'user2 = bar\n') | 169 'user2 = bar\n') |
83 | 170 |
84 # Repos | |
85 repo = join(self.repos_dir, 'svn_import') | |
86 rmtree(repo) | |
87 os.mkdir(repo) | |
88 os.mkdir(join(repo, 'trunk')) | |
89 os.mkdir(join(repo, 'trunk', 'src')) | |
90 write(join(repo, 'trunk', 'src', 'DEPS'), """ | |
91 # Smoke test DEPS file. | |
92 # Many DEPS functionalities need to be tested: | |
93 # Var | |
94 # File | |
95 # From | |
96 # deps_os | |
97 # hooks | |
98 # use_relative_paths | |
99 # | |
100 # Types of dependencies: | |
101 # Relative urls | |
102 # Full urls | |
103 # svn | |
104 # git | |
105 | |
106 deps = { | |
107 'src/other': 'svn://%(host)s/svn/trunk/other', | |
108 'src/third_party': '/trunk/third_party', | |
109 } | |
110 | |
111 deps_os = { | |
112 'mac': 'repo_4' | |
113 } | |
114 """ % { | |
115 'host': 'localhost', | |
116 }) | |
117 write(join(repo, 'trunk', 'src', 'origin'), "svn/trunk/src") | |
118 os.mkdir(join(repo, 'trunk', 'other')) | |
119 write(join(repo, 'trunk', 'other', 'origin'), "svn/trunk/other") | |
120 os.mkdir(join(repo, 'trunk', 'third_party')) | |
121 write(join(repo, 'trunk', 'third_party', 'origin'), "svn/trunk/third_party") | |
122 | |
123 # Start the daemon. | 171 # Start the daemon. |
124 cmd = ['svnserve', '-d', '--foreground', '-r', self.repos_dir] | 172 cmd = ['svnserve', '-d', '--foreground', '-r', self.repos_dir] |
125 if self.local_only: | 173 if self.local_only: |
126 cmd.append('--listen-host=127.0.0.1') | 174 cmd.append('--listen-host=127.0.0.1') |
127 self.svnserve.append(subprocess.Popen(cmd, cwd=root)) | 175 logging.debug(cmd) |
| 176 self.svnserve.append(Popen(cmd, cwd=root)) |
| 177 self.populateSvn() |
128 | 178 |
129 # Import the repo. | 179 def populateSvn(self): |
130 subprocess.check_call(['svn', 'import', repo, | 180 """Creates a few revisions of changes including DEPS files.""" |
131 'svn://127.0.0.1/svn', '-m', 'foo', '-q', | 181 # Repos |
132 '--no-auto-props', '--non-interactive', '--no-auth-cache', | 182 check_call(['svn', 'checkout', 'svn://127.0.0.1/svn', self.svn_root, '-q', |
133 '--username', 'user1', '--password', 'foo']) | 183 '--non-interactive', '--no-auth-cache', |
| 184 '--username', 'user1', '--password', 'foo']) |
| 185 assert os.path.isdir(join(self.svn_root, '.svn')) |
| 186 def file_system(rev, DEPS): |
| 187 fs = { |
| 188 'origin': 'svn@%(rev)d\n', |
| 189 'trunk/origin': 'svn/trunk@%(rev)d\n', |
| 190 'trunk/src/origin': 'svn/trunk/src@%(rev)d\n', |
| 191 'trunk/src/third_party/origin': 'svn/trunk/src/third_party@%(rev)d\n', |
| 192 'trunk/other/origin': 'src/trunk/other@%(rev)d\n', |
| 193 'trunk/third_party/origin': 'svn/trunk/third_party@%(rev)d\n', |
| 194 'trunk/third_party/foo/origin': 'svn/trunk/third_party/foo@%(rev)d\n', |
| 195 'trunk/third_party/prout/origin': 'svn/trunk/third_party/foo@%(rev)d\n', |
| 196 } |
| 197 for k in fs.iterkeys(): |
| 198 fs[k] = fs[k] % { 'rev': rev } |
| 199 fs['trunk/src/DEPS'] = DEPS |
| 200 return fs |
| 201 |
| 202 # Testing: |
| 203 # - dependency disapear |
| 204 # - dependency renamed |
| 205 # - versioned and unversioned reference |
| 206 # - relative and full reference |
| 207 # - deps_os |
| 208 # TODO(maruel): |
| 209 # - var |
| 210 # - hooks |
| 211 # - File |
| 212 self._commit_svn(file_system(1, """ |
| 213 deps = { |
| 214 'src/other': 'svn://%(host)s/svn/trunk/other', |
| 215 'src/third_party/fpp': '/trunk/third_party/foo', |
| 216 } |
| 217 deps_os = { |
| 218 'mac': { |
| 219 'src/third_party/prout': '/trunk/third_party/prout', |
| 220 }, |
| 221 }""" % { 'host': '127.0.0.1' })) |
| 222 |
| 223 self._commit_svn(file_system(2, """ |
| 224 deps = { |
| 225 'src/other': 'svn://%(host)s/svn/trunk/other', |
| 226 'src/third_party/foo': '/trunk/third_party/foo@1', |
| 227 } |
| 228 """ % { 'host': '127.0.0.1' })) |
134 | 229 |
135 def setUpGIT(self): | 230 def setUpGIT(self): |
136 """Creates git repositories and start the servers.""" | 231 """Creates git repositories and start the servers.""" |
137 assert not self.gitdaemon | 232 assert not self.gitdaemon |
138 join = os.path.join | 233 rmtree(self.git_root) |
139 root = join(self.repos_dir, 'git') | 234 for repo in ['repo_%d' % r for r in range(1, 5)]: |
140 rmtree(root) | 235 check_call(['git', 'init', '-q', join(self.git_root, repo)]) |
141 os.mkdir(root) | 236 self.git_hashes[repo] = [] |
142 # Repo 1 | |
143 repo = join(root, 'repo_1') | |
144 subprocess.check_call(['git', 'init', '-q', repo]) | |
145 write(join(repo, 'DEPS'), """ | |
146 # Smoke test DEPS file. | |
147 # Many DEPS functionalities need to be tested: | |
148 # Var | |
149 # File | |
150 # From | |
151 # deps_os | |
152 # hooks | |
153 # use_relative_paths | |
154 # | |
155 # Types of dependencies: | |
156 # Relative urls | |
157 # Full urls | |
158 # svn | |
159 # git | |
160 | 237 |
| 238 # Testing: |
| 239 # - dependency disapear |
| 240 # - dependency renamed |
| 241 # - versioned and unversioned reference |
| 242 # - relative and full reference |
| 243 # - deps_os |
| 244 # TODO(maruel): |
| 245 # - var |
| 246 # - hooks |
| 247 # - File |
| 248 self._commit_git('repo_1', { |
| 249 'DEPS': """ |
161 deps = { | 250 deps = { |
162 'repo2': 'git://%(host)s/git/repo_2', | 251 'src/repo2': 'git://%(host)s/git/repo_2', |
163 'repo2/repo3': '/repo_3', | 252 'src/repo2/repo3': '/repo_3', |
164 } | 253 } |
| 254 deps_os = { |
| 255 'mac': { |
| 256 'src/repo4': '/repo_4', |
| 257 }, |
| 258 }""" % { 'host': '127.0.0.1' }, |
| 259 'origin': 'git/repo_1@1\n', |
| 260 }) |
165 | 261 |
166 deps_os = { | 262 self._commit_git('repo_2', { |
167 'mac': 'repo_4' | 263 'origin': "git/repo_2@1\n" |
| 264 }) |
| 265 |
| 266 self._commit_git('repo_2', { |
| 267 'origin': "git/repo_2@2\n" |
| 268 }) |
| 269 |
| 270 self._commit_git('repo_3', { |
| 271 'origin': "git/repo_3@1\n" |
| 272 }) |
| 273 |
| 274 self._commit_git('repo_3', { |
| 275 'origin': "git/repo_3@2\n" |
| 276 }) |
| 277 |
| 278 self._commit_git('repo_4', { |
| 279 'origin': "git/repo_4@1\n" |
| 280 }) |
| 281 |
| 282 self._commit_git('repo_4', { |
| 283 'origin': "git/repo_4@2\n" |
| 284 }) |
| 285 |
| 286 self._commit_git('repo_1', { |
| 287 'DEPS': """ |
| 288 deps = { |
| 289 'src/repo2': 'git://%(host)s/git/repo_2@%(hash)s', |
| 290 'src/repo2/repo_renamed': '/repo_3', |
168 } | 291 } |
169 """ % { | 292 """ % { 'host': '127.0.0.1', 'hash': self.git_hashes['repo_2'][0][0] }, |
170 'host': 'localhost', | 293 'origin': "git/repo_1@2\n" |
171 }) | 294 }) |
172 write(join(repo, 'origin'), "git/repo_1") | |
173 subprocess.check_call(['git', 'add', '-A', '-f'], cwd=repo) | |
174 subprocess.check_call(['git', 'commit', '-q', '-m', 'foo'], cwd=repo) | |
175 | |
176 # Repo 2 | |
177 repo = join(root, 'repo_2') | |
178 subprocess.check_call(['git', 'init', '-q', repo]) | |
179 write(join(repo, 'origin'), "git/repo_2") | |
180 subprocess.check_call(['git', 'add', '-A', '-f'], cwd=repo) | |
181 subprocess.check_call(['git', 'commit', '-q', '-m', 'foo'], cwd=repo) | |
182 | |
183 # Repo 3 | |
184 repo = join(root, 'repo_3') | |
185 subprocess.check_call(['git', 'init', '-q', repo]) | |
186 write(join(repo, 'origin'), "git/repo_3") | |
187 subprocess.check_call(['git', 'add', '-A', '-f'], cwd=repo) | |
188 subprocess.check_call(['git', 'commit', '-q', '-m', 'foo'], cwd=repo) | |
189 | 295 |
190 # Start the daemon. | 296 # Start the daemon. |
191 cmd = ['git', 'daemon', '--export-all', '--base-path=' + self.repos_dir] | 297 cmd = ['git', 'daemon', '--export-all', '--base-path=' + self.repos_dir] |
192 if self.local_only: | 298 if self.local_only: |
193 cmd.append('--listen=127.0.0.1') | 299 cmd.append('--listen=127.0.0.1') |
194 self.gitdaemon.append(subprocess.Popen(cmd, cwd=self.repos_dir, | 300 logging.debug(cmd) |
195 stderr=subprocess.PIPE)) | 301 self.gitdaemon.append(Popen(cmd, cwd=self.repos_dir)) |
| 302 |
| 303 def _commit_svn(self, tree): |
| 304 self._genTree(self.svn_root, tree) |
| 305 commit_svn(self.svn_root) |
| 306 if self.svn_revs and self.svn_revs[-1]: |
| 307 new_tree = self.svn_revs[-1].copy() |
| 308 new_tree.update(tree) |
| 309 else: |
| 310 new_tree = tree.copy() |
| 311 self.svn_revs.append(new_tree) |
| 312 |
| 313 def _commit_git(self, repo, tree): |
| 314 repo_root = join(self.git_root, repo) |
| 315 self._genTree(repo_root, tree) |
| 316 hash = commit_git(repo_root) |
| 317 if self.git_hashes[repo]: |
| 318 new_tree = self.git_hashes[repo][-1][1].copy() |
| 319 new_tree.update(tree) |
| 320 else: |
| 321 new_tree = tree.copy() |
| 322 self.git_hashes[repo].append((hash, new_tree)) |
| 323 |
| 324 |
| 325 def main(argv): |
| 326 leak = '-l' in argv |
| 327 if leak: |
| 328 argv.remove('-l') |
| 329 verbose = '-v' in argv |
| 330 if verbose: |
| 331 logging.basicConfig(level=logging.DEBUG) |
| 332 argv.remove('-v') |
| 333 assert len(argv) == 1, argv |
| 334 trial_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), |
| 335 '_trial') |
| 336 print 'Using %s' % trial_dir |
| 337 fake = FakeRepos(trial_dir, leak, True) |
| 338 try: |
| 339 fake.setUp() |
| 340 print('Fake setup, press enter to quit or Ctrl-C to keep the checkouts.') |
| 341 sys.stdin.readline() |
| 342 except KeyboardInterrupt: |
| 343 fake.leak = True |
| 344 finally: |
| 345 fake.tearDown() |
| 346 return 0 |
| 347 |
196 | 348 |
197 if __name__ == '__main__': | 349 if __name__ == '__main__': |
198 fake = FakeRepos(os.path.dirname(os.path.abspath(__file__)), False) | 350 sys.exit(main(sys.argv)) |
199 try: | |
200 fake.setUp() | |
201 sys.stdin.readline() | |
202 finally: | |
203 fake.tearDown() | |
OLD | NEW |