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 datetime | 9 import datetime |
10 import errno | 10 import errno |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
145 def commit_git(repo): | 145 def commit_git(repo): |
146 """Commits the changes and returns the new hash.""" | 146 """Commits the changes and returns the new hash.""" |
147 check_call(['git', 'add', '-A', '-f'], cwd=repo) | 147 check_call(['git', 'add', '-A', '-f'], cwd=repo) |
148 check_call(['git', 'commit', '-q', '--message', 'foo'], cwd=repo) | 148 check_call(['git', 'commit', '-q', '--message', 'foo'], cwd=repo) |
149 rev = Popen(['git', 'show-ref', '--head', 'HEAD'], | 149 rev = Popen(['git', 'show-ref', '--head', 'HEAD'], |
150 cwd=repo).communicate()[0].split(' ', 1)[0] | 150 cwd=repo).communicate()[0].split(' ', 1)[0] |
151 logging.debug('At revision %s' % rev) | 151 logging.debug('At revision %s' % rev) |
152 return rev | 152 return rev |
153 | 153 |
154 | 154 |
| 155 def test_port(host, port): |
| 156 s = socket.socket() |
| 157 try: |
| 158 return s.connect_ex((host, port)) == 0 |
| 159 finally: |
| 160 s.close() |
| 161 |
| 162 |
| 163 def find_free_port(host, base_port): |
| 164 """Finds a listening port free to listen to.""" |
| 165 while base_port < (2<<16): |
| 166 if not test_port(host, base_port): |
| 167 return base_port |
| 168 base_port += 1 |
| 169 assert False, 'Having issues finding an available port' |
| 170 |
| 171 |
| 172 def wait_for_port_to_bind(host, port, process): |
| 173 sock = socket.socket() |
| 174 |
| 175 if sys.platform == 'darwin': |
| 176 # On Mac SnowLeopard, if we attempt to connect to the socket |
| 177 # immediately, it fails with EINVAL and never gets a chance to |
| 178 # connect (putting us into a hard spin and then failing). |
| 179 # Linux doesn't need this. |
| 180 time.sleep(0.1) |
| 181 |
| 182 try: |
| 183 start = datetime.datetime.utcnow() |
| 184 maxdelay = datetime.timedelta(seconds=30) |
| 185 while (datetime.datetime.utcnow() - start) < maxdelay: |
| 186 try: |
| 187 sock.connect((host, port)) |
| 188 logging.debug('%d is now bound' % port) |
| 189 return |
| 190 except EnvironmentError: |
| 191 pass |
| 192 logging.debug('%d is still not bound' % port) |
| 193 finally: |
| 194 sock.close() |
| 195 # The process failed to bind. Kill it and dump its ouput. |
| 196 process.kill() |
| 197 logging.error('%s' % process.communicate()[0]) |
| 198 assert False, '%d is still not bound' % port |
| 199 |
| 200 |
| 201 def wait_for_port_to_free(host, port): |
| 202 start = datetime.datetime.utcnow() |
| 203 maxdelay = datetime.timedelta(seconds=30) |
| 204 while (datetime.datetime.utcnow() - start) < maxdelay: |
| 205 try: |
| 206 sock = socket.socket() |
| 207 sock.connect((host, port)) |
| 208 logging.debug('%d was bound, waiting to free' % port) |
| 209 except EnvironmentError: |
| 210 logging.debug('%d now free' % port) |
| 211 return |
| 212 finally: |
| 213 sock.close() |
| 214 assert False, '%d is still bound' % port |
| 215 |
| 216 |
155 _FAKE_LOADED = False | 217 _FAKE_LOADED = False |
156 | 218 |
157 class FakeReposBase(object): | 219 class FakeReposBase(object): |
158 """Generate both svn and git repositories to test gclient functionality. | 220 """Generate both svn and git repositories to test gclient functionality. |
159 | 221 |
160 Many DEPS functionalities need to be tested: Var, File, From, deps_os, hooks, | 222 Many DEPS functionalities need to be tested: Var, File, From, deps_os, hooks, |
161 use_relative_paths. | 223 use_relative_paths. |
162 | 224 |
163 And types of dependencies: Relative urls, Full urls, both svn and git. | 225 And types of dependencies: Relative urls, Full urls, both svn and git. |
164 | 226 |
(...skipping 23 matching lines...) Expand all Loading... |
188 # For consistency with self.svn_revs, it is 1-based too. | 250 # For consistency with self.svn_revs, it is 1-based too. |
189 self.git_hashes = {} | 251 self.git_hashes = {} |
190 self.svnserve = None | 252 self.svnserve = None |
191 self.gitdaemon = None | 253 self.gitdaemon = None |
192 self.git_pid_file = None | 254 self.git_pid_file = None |
193 self.git_root = None | 255 self.git_root = None |
194 self.svn_checkout = None | 256 self.svn_checkout = None |
195 self.svn_repo = None | 257 self.svn_repo = None |
196 self.git_dirty = False | 258 self.git_dirty = False |
197 self.svn_dirty = False | 259 self.svn_dirty = False |
198 self.svn_base = 'svn://%s/svn/' % self.host | 260 self.svn_port = None |
199 self.git_base = 'git://%s/git/' % self.host | 261 self.git_port = None |
200 self.svn_port = 3690 | 262 self.svn_base = None |
201 self.git_port = 9418 | 263 self.git_base = None |
202 | 264 |
203 @property | 265 @property |
204 def root_dir(self): | 266 def root_dir(self): |
205 return self.trial.root_dir | 267 return self.trial.root_dir |
206 | 268 |
207 def set_up(self): | 269 def set_up(self): |
208 """All late initialization comes here.""" | 270 """All late initialization comes here.""" |
209 self.cleanup_dirt() | 271 self.cleanup_dirt() |
210 if not self.root_dir: | 272 if not self.root_dir: |
211 try: | 273 try: |
(...skipping 25 matching lines...) Expand all Loading... |
237 self.trial = None | 299 self.trial = None |
238 | 300 |
239 def tear_down_svn(self): | 301 def tear_down_svn(self): |
240 if self.svnserve: | 302 if self.svnserve: |
241 logging.debug('Killing svnserve pid %s' % self.svnserve.pid) | 303 logging.debug('Killing svnserve pid %s' % self.svnserve.pid) |
242 try: | 304 try: |
243 self.svnserve.kill() | 305 self.svnserve.kill() |
244 except OSError, e: | 306 except OSError, e: |
245 if e.errno != errno.ESRCH: # no such process | 307 if e.errno != errno.ESRCH: # no such process |
246 raise | 308 raise |
247 self.wait_for_port_to_free(self.svn_port) | 309 wait_for_port_to_free(self.host, self.svn_port) |
248 self.svnserve = None | 310 self.svnserve = None |
| 311 self.svn_port = None |
| 312 self.svn_base = None |
249 if not self.trial.SHOULD_LEAK: | 313 if not self.trial.SHOULD_LEAK: |
250 logging.debug('Removing %s' % self.svn_repo) | 314 logging.debug('Removing %s' % self.svn_repo) |
251 gclient_utils.rmtree(self.svn_repo) | 315 gclient_utils.rmtree(self.svn_repo) |
252 logging.debug('Removing %s' % self.svn_checkout) | 316 logging.debug('Removing %s' % self.svn_checkout) |
253 gclient_utils.rmtree(self.svn_checkout) | 317 gclient_utils.rmtree(self.svn_checkout) |
254 else: | 318 else: |
255 return False | 319 return False |
256 return True | 320 return True |
257 | 321 |
258 def tear_down_git(self): | 322 def tear_down_git(self): |
259 if self.gitdaemon: | 323 if self.gitdaemon: |
260 logging.debug('Killing git-daemon pid %s' % self.gitdaemon.pid) | 324 logging.debug('Killing git-daemon pid %s' % self.gitdaemon.pid) |
261 self.gitdaemon.kill() | 325 self.gitdaemon.kill() |
262 self.gitdaemon = None | 326 self.gitdaemon = None |
263 if self.git_pid_file: | 327 if self.git_pid_file: |
264 pid = int(self.git_pid_file.read()) | 328 pid = int(self.git_pid_file.read()) |
265 self.git_pid_file.close() | 329 self.git_pid_file.close() |
266 logging.debug('Killing git daemon pid %s' % pid) | 330 logging.debug('Killing git daemon pid %s' % pid) |
267 kill_pid(pid) | 331 kill_pid(pid) |
268 self.git_pid_file = None | 332 self.git_pid_file = None |
269 self.wait_for_port_to_free(self.git_port) | 333 wait_for_port_to_free(self.host, self.git_port) |
| 334 self.git_port = None |
| 335 self.git_base = None |
270 if not self.trial.SHOULD_LEAK: | 336 if not self.trial.SHOULD_LEAK: |
271 logging.debug('Removing %s' % self.git_root) | 337 logging.debug('Removing %s' % self.git_root) |
272 gclient_utils.rmtree(self.git_root) | 338 gclient_utils.rmtree(self.git_root) |
273 else: | 339 else: |
274 return False | 340 return False |
275 return True | 341 return True |
276 | 342 |
277 @staticmethod | 343 @staticmethod |
278 def _genTree(root, tree_dict): | 344 def _genTree(root, tree_dict): |
279 """For a dictionary of file contents, generate a filesystem.""" | 345 """For a dictionary of file contents, generate a filesystem.""" |
(...skipping 29 matching lines...) Expand all Loading... |
309 text += ''.join('%s = %s\n' % (usr, pwd) for usr, pwd in self.USERS) | 375 text += ''.join('%s = %s\n' % (usr, pwd) for usr, pwd in self.USERS) |
310 write(join(self.svn_repo, 'conf', 'passwd'), text) | 376 write(join(self.svn_repo, 'conf', 'passwd'), text) |
311 | 377 |
312 # Mac 10.6 ships with a buggy subversion build and we need this line | 378 # Mac 10.6 ships with a buggy subversion build and we need this line |
313 # to work around the bug. | 379 # to work around the bug. |
314 write(join(self.svn_repo, 'db', 'fsfs.conf'), | 380 write(join(self.svn_repo, 'db', 'fsfs.conf'), |
315 '[rep-sharing]\n' | 381 '[rep-sharing]\n' |
316 'enable-rep-sharing = false\n') | 382 'enable-rep-sharing = false\n') |
317 | 383 |
318 # Start the daemon. | 384 # Start the daemon. |
319 cmd = ['svnserve', '-d', '--foreground', '-r', self.root_dir] | 385 self.svn_port = find_free_port(self.host, 10000) |
| 386 cmd = ['svnserve', '-d', '--foreground', '-r', self.root_dir, |
| 387 '--listen-port=%d' % self.svn_port] |
320 if self.host == '127.0.0.1': | 388 if self.host == '127.0.0.1': |
321 cmd.append('--listen-host=' + self.host) | 389 cmd.append('--listen-host=' + self.host) |
322 self.check_port_is_free(self.svn_port) | 390 self.check_port_is_free(self.svn_port) |
323 self.svnserve = Popen(cmd, cwd=self.svn_repo) | 391 self.svnserve = Popen(cmd, cwd=self.svn_repo) |
324 self.wait_for_port_to_bind(self.svn_port, self.svnserve) | 392 wait_for_port_to_bind(self.host, self.svn_port, self.svnserve) |
| 393 self.svn_base = 'svn://%s:%d/svn/' % (self.host, self.svn_port) |
325 self.populateSvn() | 394 self.populateSvn() |
326 self.svn_dirty = False | 395 self.svn_dirty = False |
327 return True | 396 return True |
328 | 397 |
329 def set_up_git(self): | 398 def set_up_git(self): |
330 """Creates git repositories and start the servers.""" | 399 """Creates git repositories and start the servers.""" |
331 self.set_up() | 400 self.set_up() |
332 if self.gitdaemon: | 401 if self.gitdaemon: |
333 return True | 402 return True |
334 if sys.platform == 'win32': | 403 if sys.platform == 'win32': |
335 return False | 404 return False |
336 assert self.git_pid_file == None | 405 assert self.git_pid_file == None |
337 for repo in ['repo_%d' % r for r in range(1, self.NB_GIT_REPOS + 1)]: | 406 for repo in ['repo_%d' % r for r in range(1, self.NB_GIT_REPOS + 1)]: |
338 check_call(['git', 'init', '-q', join(self.git_root, repo)]) | 407 check_call(['git', 'init', '-q', join(self.git_root, repo)]) |
339 self.git_hashes[repo] = [None] | 408 self.git_hashes[repo] = [None] |
340 # Unlike svn, populate git before starting the server. | 409 self.git_port = find_free_port(self.host, 20000) |
341 self.populateGit() | 410 self.git_base = 'git://%s:%d/git/' % (self.host, self.git_port) |
342 # Start the daemon. | 411 # Start the daemon. |
343 self.git_pid_file = tempfile.NamedTemporaryFile() | 412 self.git_pid_file = tempfile.NamedTemporaryFile() |
344 cmd = ['git', 'daemon', | 413 cmd = ['git', 'daemon', |
345 '--export-all', | 414 '--export-all', |
346 '--reuseaddr', | 415 '--reuseaddr', |
347 '--base-path=' + self.root_dir, | 416 '--base-path=' + self.root_dir, |
348 '--pid-file=' + self.git_pid_file.name] | 417 '--pid-file=' + self.git_pid_file.name, |
| 418 '--port=%d' % self.git_port] |
349 if self.host == '127.0.0.1': | 419 if self.host == '127.0.0.1': |
350 cmd.append('--listen=' + self.host) | 420 cmd.append('--listen=' + self.host) |
351 self.check_port_is_free(self.git_port) | 421 self.check_port_is_free(self.git_port) |
352 self.gitdaemon = Popen(cmd, cwd=self.root_dir) | 422 self.gitdaemon = Popen(cmd, cwd=self.root_dir) |
353 self.wait_for_port_to_bind(self.git_port, self.gitdaemon) | 423 wait_for_port_to_bind(self.host, self.git_port, self.gitdaemon) |
| 424 self.populateGit() |
354 self.git_dirty = False | 425 self.git_dirty = False |
355 return True | 426 return True |
356 | 427 |
357 def _commit_svn(self, tree): | 428 def _commit_svn(self, tree): |
358 self._genTree(self.svn_checkout, tree) | 429 self._genTree(self.svn_checkout, tree) |
359 commit_svn(self.svn_checkout, self.USERS[0][0], self.USERS[0][1]) | 430 commit_svn(self.svn_checkout, self.USERS[0][0], self.USERS[0][1]) |
360 if self.svn_revs and self.svn_revs[-1]: | 431 if self.svn_revs and self.svn_revs[-1]: |
361 new_tree = self.svn_revs[-1].copy() | 432 new_tree = self.svn_revs[-1].copy() |
362 new_tree.update(tree) | 433 new_tree.update(tree) |
363 else: | 434 else: |
(...skipping 15 matching lines...) Expand all Loading... |
379 sock = socket.socket() | 450 sock = socket.socket() |
380 try: | 451 try: |
381 sock.connect((self.host, port)) | 452 sock.connect((self.host, port)) |
382 # It worked, throw. | 453 # It worked, throw. |
383 assert False, '%d shouldn\'t be bound' % port | 454 assert False, '%d shouldn\'t be bound' % port |
384 except EnvironmentError: | 455 except EnvironmentError: |
385 pass | 456 pass |
386 finally: | 457 finally: |
387 sock.close() | 458 sock.close() |
388 | 459 |
389 def wait_for_port_to_bind(self, port, process): | |
390 sock = socket.socket() | |
391 | |
392 if sys.platform == 'darwin': | |
393 # On Mac SnowLeopard, if we attempt to connect to the socket | |
394 # immediately, it fails with EINVAL and never gets a chance to | |
395 # connect (putting us into a hard spin and then failing). | |
396 # Linux doesn't need this. | |
397 time.sleep(0.1) | |
398 | |
399 try: | |
400 start = datetime.datetime.utcnow() | |
401 maxdelay = datetime.timedelta(seconds=30) | |
402 while (datetime.datetime.utcnow() - start) < maxdelay: | |
403 try: | |
404 sock.connect((self.host, port)) | |
405 logging.debug('%d is now bound' % port) | |
406 return | |
407 except EnvironmentError: | |
408 pass | |
409 logging.debug('%d is still not bound' % port) | |
410 finally: | |
411 sock.close() | |
412 # The process failed to bind. Kill it and dump its ouput. | |
413 process.kill() | |
414 logging.error('%s' % process.communicate()[0]) | |
415 assert False, '%d is still not bound' % port | |
416 | |
417 def wait_for_port_to_free(self, port): | |
418 start = datetime.datetime.utcnow() | |
419 maxdelay = datetime.timedelta(seconds=30) | |
420 while (datetime.datetime.utcnow() - start) < maxdelay: | |
421 try: | |
422 sock = socket.socket() | |
423 sock.connect((self.host, port)) | |
424 logging.debug('%d was bound, waiting to free' % port) | |
425 except EnvironmentError: | |
426 logging.debug('%d now free' % port) | |
427 return | |
428 finally: | |
429 sock.close() | |
430 assert False, '%d is still bound' % port | |
431 | |
432 def populateSvn(self): | 460 def populateSvn(self): |
433 raise NotImplementedError() | 461 raise NotImplementedError() |
434 | 462 |
435 def populateGit(self): | 463 def populateGit(self): |
436 raise NotImplementedError() | 464 raise NotImplementedError() |
437 | 465 |
438 | 466 |
439 class FakeRepos(FakeReposBase): | 467 class FakeRepos(FakeReposBase): |
440 """Implements populateSvn() and populateGit().""" | 468 """Implements populateSvn() and populateGit().""" |
441 NB_GIT_REPOS = 4 | 469 NB_GIT_REPOS = 4 |
(...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
747 fake.set_up_git() | 775 fake.set_up_git() |
748 print('Fake setup, press enter to quit or Ctrl-C to keep the checkouts.') | 776 print('Fake setup, press enter to quit or Ctrl-C to keep the checkouts.') |
749 sys.stdin.readline() | 777 sys.stdin.readline() |
750 except KeyboardInterrupt: | 778 except KeyboardInterrupt: |
751 trial_dir.TrialDir.SHOULD_LEAK.leak = True | 779 trial_dir.TrialDir.SHOULD_LEAK.leak = True |
752 return 0 | 780 return 0 |
753 | 781 |
754 | 782 |
755 if __name__ == '__main__': | 783 if __name__ == '__main__': |
756 sys.exit(main(sys.argv)) | 784 sys.exit(main(sys.argv)) |
OLD | NEW |