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

Side by Side Diff: tests/fake_repos.py

Issue 8508015: Create a new depot_tools_testing_lib to move utility modules there (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Renamed to testing_support Created 9 years, 1 month 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 | « tests/checkout_test.py ('k') | tests/filesystem_mock.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Generate fake repositories for testing."""
7
8 import atexit
9 import datetime
10 import errno
11 import logging
12 import os
13 import pprint
14 import re
15 import socket
16 import sys
17 import tempfile
18 import time
19
20 # trial_dir must be first for non-system libraries.
21 from tests import trial_dir
22 import gclient_utils
23 import scm
24 import subprocess2
25
26
27 def write(path, content):
28 f = open(path, 'wb')
29 f.write(content)
30 f.close()
31
32
33 join = os.path.join
34
35
36 def read_tree(tree_root):
37 """Returns a dict of all the files in a tree. Defaults to self.root_dir."""
38 tree = {}
39 for root, dirs, files in os.walk(tree_root):
40 for d in filter(lambda x: x.startswith('.'), dirs):
41 dirs.remove(d)
42 for f in [join(root, f) for f in files if not f.startswith('.')]:
43 filepath = f[len(tree_root) + 1:].replace(os.sep, '/')
44 assert len(filepath), f
45 tree[filepath] = open(join(root, f), 'rU').read()
46 return tree
47
48
49 def dict_diff(dict1, dict2):
50 diff = {}
51 for k, v in dict1.iteritems():
52 if k not in dict2:
53 diff[k] = v
54 elif v != dict2[k]:
55 diff[k] = (v, dict2[k])
56 for k, v in dict2.iteritems():
57 if k not in dict1:
58 diff[k] = v
59 return diff
60
61
62 def commit_svn(repo, usr, pwd):
63 """Commits the changes and returns the new revision number."""
64 to_add = []
65 to_remove = []
66 for status, filepath in scm.SVN.CaptureStatus(repo):
67 if status[0] == '?':
68 to_add.append(filepath)
69 elif status[0] == '!':
70 to_remove.append(filepath)
71 if to_add:
72 subprocess2.check_output(
73 ['svn', 'add', '--no-auto-props', '-q'] + to_add, cwd=repo)
74 if to_remove:
75 subprocess2.check_output(['svn', 'remove', '-q'] + to_remove, cwd=repo)
76
77 out = subprocess2.check_output(
78 ['svn', 'commit', repo, '-m', 'foo', '--non-interactive',
79 '--no-auth-cache',
80 '--username', usr, '--password', pwd],
81 cwd=repo)
82 match = re.search(r'(\d+)', out)
83 if not match:
84 raise Exception('Commit failed', out)
85 rev = match.group(1)
86 status = subprocess2.check_output(['svn', 'status'], cwd=repo)
87 assert len(status) == 0, status
88 logging.debug('At revision %s' % rev)
89 return rev
90
91
92 def commit_git(repo):
93 """Commits the changes and returns the new hash."""
94 subprocess2.check_call(['git', 'add', '-A', '-f'], cwd=repo)
95 subprocess2.check_call(['git', 'commit', '-q', '--message', 'foo'], cwd=repo)
96 rev = subprocess2.check_output(
97 ['git', 'show-ref', '--head', 'HEAD'], cwd=repo).split(' ', 1)[0]
98 logging.debug('At revision %s' % rev)
99 return rev
100
101
102 def test_port(host, port):
103 s = socket.socket()
104 try:
105 return s.connect_ex((host, port)) == 0
106 finally:
107 s.close()
108
109
110 def find_free_port(host, base_port):
111 """Finds a listening port free to listen to."""
112 while base_port < (2<<16):
113 if not test_port(host, base_port):
114 return base_port
115 base_port += 1
116 assert False, 'Having issues finding an available port'
117
118
119 def wait_for_port_to_bind(host, port, process):
120 sock = socket.socket()
121
122 if sys.platform == 'darwin':
123 # On Mac SnowLeopard, if we attempt to connect to the socket
124 # immediately, it fails with EINVAL and never gets a chance to
125 # connect (putting us into a hard spin and then failing).
126 # Linux doesn't need this.
127 time.sleep(0.1)
128
129 try:
130 start = datetime.datetime.utcnow()
131 maxdelay = datetime.timedelta(seconds=30)
132 while (datetime.datetime.utcnow() - start) < maxdelay:
133 try:
134 sock.connect((host, port))
135 logging.debug('%d is now bound' % port)
136 return
137 except (socket.error, EnvironmentError):
138 pass
139 logging.debug('%d is still not bound' % port)
140 finally:
141 sock.close()
142 # The process failed to bind. Kill it and dump its ouput.
143 process.kill()
144 logging.error('%s' % process.communicate()[0])
145 assert False, '%d is still not bound' % port
146
147
148 def wait_for_port_to_free(host, port):
149 start = datetime.datetime.utcnow()
150 maxdelay = datetime.timedelta(seconds=30)
151 while (datetime.datetime.utcnow() - start) < maxdelay:
152 try:
153 sock = socket.socket()
154 sock.connect((host, port))
155 logging.debug('%d was bound, waiting to free' % port)
156 except (socket.error, EnvironmentError):
157 logging.debug('%d now free' % port)
158 return
159 finally:
160 sock.close()
161 assert False, '%d is still bound' % port
162
163
164 _FAKE_LOADED = False
165
166 class FakeReposBase(object):
167 """Generate both svn and git repositories to test gclient functionality.
168
169 Many DEPS functionalities need to be tested: Var, File, From, deps_os, hooks,
170 use_relative_paths.
171
172 And types of dependencies: Relative urls, Full urls, both svn and git.
173
174 populateSvn() and populateGit() need to be implemented by the subclass.
175 """
176 # Hostname
177 NB_GIT_REPOS = 1
178 USERS = [
179 ('user1@example.com', 'foo'),
180 ('user2@example.com', 'bar'),
181 ]
182
183 def __init__(self, host=None):
184 global _FAKE_LOADED
185 if _FAKE_LOADED:
186 raise Exception('You can only start one FakeRepos at a time.')
187 _FAKE_LOADED = True
188
189 self.trial = trial_dir.TrialDir('repos')
190 self.host = host or '127.0.0.1'
191 # Format is [ None, tree, tree, ...]
192 # i.e. revisions are 1-based.
193 self.svn_revs = [None]
194 # Format is { repo: [ None, (hash, tree), (hash, tree), ... ], ... }
195 # so reference looks like self.git_hashes[repo][rev][0] for hash and
196 # self.git_hashes[repo][rev][1] for it's tree snapshot.
197 # For consistency with self.svn_revs, it is 1-based too.
198 self.git_hashes = {}
199 self.svnserve = None
200 self.gitdaemon = None
201 self.git_pid_file = None
202 self.git_root = None
203 self.svn_checkout = None
204 self.svn_repo = None
205 self.git_dirty = False
206 self.svn_dirty = False
207 self.svn_port = None
208 self.git_port = None
209 self.svn_base = None
210 self.git_base = None
211
212 @property
213 def root_dir(self):
214 return self.trial.root_dir
215
216 def set_up(self):
217 """All late initialization comes here."""
218 self.cleanup_dirt()
219 if not self.root_dir:
220 try:
221 # self.root_dir is not set before this call.
222 self.trial.set_up()
223 self.git_root = join(self.root_dir, 'git')
224 self.svn_checkout = join(self.root_dir, 'svn_checkout')
225 self.svn_repo = join(self.root_dir, 'svn')
226 finally:
227 # Registers cleanup.
228 atexit.register(self.tear_down)
229
230 def cleanup_dirt(self):
231 """For each dirty repository, destroy it."""
232 if self.svn_dirty:
233 if not self.tear_down_svn():
234 logging.error('Using both leaking checkout and svn dirty checkout')
235 if self.git_dirty:
236 if not self.tear_down_git():
237 logging.error('Using both leaking checkout and git dirty checkout')
238
239 def tear_down(self):
240 """Kills the servers and delete the directories."""
241 self.tear_down_svn()
242 self.tear_down_git()
243 # This deletes the directories.
244 self.trial.tear_down()
245 self.trial = None
246
247 def tear_down_svn(self):
248 if self.svnserve:
249 logging.debug('Killing svnserve pid %s' % self.svnserve.pid)
250 try:
251 self.svnserve.kill()
252 except OSError, e:
253 if e.errno != errno.ESRCH: # no such process
254 raise
255 wait_for_port_to_free(self.host, self.svn_port)
256 self.svnserve = None
257 self.svn_port = None
258 self.svn_base = None
259 if not self.trial.SHOULD_LEAK:
260 logging.debug('Removing %s' % self.svn_repo)
261 gclient_utils.rmtree(self.svn_repo)
262 logging.debug('Removing %s' % self.svn_checkout)
263 gclient_utils.rmtree(self.svn_checkout)
264 else:
265 return False
266 return True
267
268 def tear_down_git(self):
269 if self.gitdaemon:
270 logging.debug('Killing git-daemon pid %s' % self.gitdaemon.pid)
271 self.gitdaemon.kill()
272 self.gitdaemon = None
273 if self.git_pid_file:
274 pid = int(self.git_pid_file.read())
275 self.git_pid_file.close()
276 logging.debug('Killing git daemon pid %s' % pid)
277 subprocess2.kill_pid(pid)
278 self.git_pid_file = None
279 wait_for_port_to_free(self.host, self.git_port)
280 self.git_port = None
281 self.git_base = None
282 if not self.trial.SHOULD_LEAK:
283 logging.debug('Removing %s' % self.git_root)
284 gclient_utils.rmtree(self.git_root)
285 else:
286 return False
287 return True
288
289 @staticmethod
290 def _genTree(root, tree_dict):
291 """For a dictionary of file contents, generate a filesystem."""
292 if not os.path.isdir(root):
293 os.makedirs(root)
294 for (k, v) in tree_dict.iteritems():
295 k_os = k.replace('/', os.sep)
296 k_arr = k_os.split(os.sep)
297 if len(k_arr) > 1:
298 p = os.sep.join([root] + k_arr[:-1])
299 if not os.path.isdir(p):
300 os.makedirs(p)
301 if v is None:
302 os.remove(join(root, k))
303 else:
304 write(join(root, k), v)
305
306 def set_up_svn(self):
307 """Creates subversion repositories and start the servers."""
308 self.set_up()
309 if self.svnserve:
310 return True
311 try:
312 subprocess2.check_call(['svnadmin', 'create', self.svn_repo])
313 except (OSError, subprocess2.CalledProcessError):
314 return False
315 write(join(self.svn_repo, 'conf', 'svnserve.conf'),
316 '[general]\n'
317 'anon-access = read\n'
318 'auth-access = write\n'
319 'password-db = passwd\n')
320 text = '[users]\n'
321 text += ''.join('%s = %s\n' % (usr, pwd) for usr, pwd in self.USERS)
322 write(join(self.svn_repo, 'conf', 'passwd'), text)
323
324 # Mac 10.6 ships with a buggy subversion build and we need this line
325 # to work around the bug.
326 write(join(self.svn_repo, 'db', 'fsfs.conf'),
327 '[rep-sharing]\n'
328 'enable-rep-sharing = false\n')
329
330 # Start the daemon.
331 self.svn_port = find_free_port(self.host, 10000)
332 logging.debug('Using port %d' % self.svn_port)
333 cmd = ['svnserve', '-d', '--foreground', '-r', self.root_dir,
334 '--listen-port=%d' % self.svn_port]
335 if self.host == '127.0.0.1':
336 cmd.append('--listen-host=' + self.host)
337 self.check_port_is_free(self.svn_port)
338 self.svnserve = subprocess2.Popen(
339 cmd,
340 cwd=self.svn_repo,
341 stdout=subprocess2.PIPE,
342 stderr=subprocess2.PIPE)
343 wait_for_port_to_bind(self.host, self.svn_port, self.svnserve)
344 self.svn_base = 'svn://%s:%d/svn/' % (self.host, self.svn_port)
345 self.populateSvn()
346 self.svn_dirty = False
347 return True
348
349 def set_up_git(self):
350 """Creates git repositories and start the servers."""
351 self.set_up()
352 if self.gitdaemon:
353 return True
354 assert self.git_pid_file == None
355 try:
356 subprocess2.check_output(['git', '--version'])
357 except (OSError, subprocess2.CalledProcessError):
358 return False
359 for repo in ['repo_%d' % r for r in range(1, self.NB_GIT_REPOS + 1)]:
360 subprocess2.check_call(['git', 'init', '-q', join(self.git_root, repo)])
361 self.git_hashes[repo] = [None]
362 self.git_port = find_free_port(self.host, 20000)
363 self.git_base = 'git://%s:%d/git/' % (self.host, self.git_port)
364 # Start the daemon.
365 self.git_pid_file = tempfile.NamedTemporaryFile()
366 cmd = ['git', 'daemon',
367 '--export-all',
368 '--reuseaddr',
369 '--base-path=' + self.root_dir,
370 '--pid-file=' + self.git_pid_file.name,
371 '--port=%d' % self.git_port]
372 if self.host == '127.0.0.1':
373 cmd.append('--listen=' + self.host)
374 self.check_port_is_free(self.git_port)
375 self.gitdaemon = subprocess2.Popen(
376 cmd,
377 cwd=self.root_dir,
378 stdout=subprocess2.PIPE,
379 stderr=subprocess2.PIPE)
380 wait_for_port_to_bind(self.host, self.git_port, self.gitdaemon)
381 self.populateGit()
382 self.git_dirty = False
383 return True
384
385 def _commit_svn(self, tree):
386 self._genTree(self.svn_checkout, tree)
387 commit_svn(self.svn_checkout, self.USERS[0][0], self.USERS[0][1])
388 if self.svn_revs and self.svn_revs[-1]:
389 new_tree = self.svn_revs[-1].copy()
390 new_tree.update(tree)
391 else:
392 new_tree = tree.copy()
393 self.svn_revs.append(new_tree)
394
395 def _commit_git(self, repo, tree):
396 repo_root = join(self.git_root, repo)
397 self._genTree(repo_root, tree)
398 commit_hash = commit_git(repo_root)
399 if self.git_hashes[repo][-1]:
400 new_tree = self.git_hashes[repo][-1][1].copy()
401 new_tree.update(tree)
402 else:
403 new_tree = tree.copy()
404 self.git_hashes[repo].append((commit_hash, new_tree))
405
406 def check_port_is_free(self, port):
407 sock = socket.socket()
408 try:
409 sock.connect((self.host, port))
410 # It worked, throw.
411 assert False, '%d shouldn\'t be bound' % port
412 except (socket.error, EnvironmentError):
413 pass
414 finally:
415 sock.close()
416
417 def populateSvn(self):
418 raise NotImplementedError()
419
420 def populateGit(self):
421 raise NotImplementedError()
422
423
424 class FakeRepos(FakeReposBase):
425 """Implements populateSvn() and populateGit()."""
426 NB_GIT_REPOS = 4
427
428 def populateSvn(self):
429 """Creates a few revisions of changes including DEPS files."""
430 # Repos
431 subprocess2.check_call(
432 ['svn', 'checkout', self.svn_base, self.svn_checkout,
433 '-q', '--non-interactive', '--no-auth-cache',
434 '--username', self.USERS[0][0], '--password', self.USERS[0][1]])
435 assert os.path.isdir(join(self.svn_checkout, '.svn'))
436 def file_system(rev, DEPS, DEPS_ALT=None):
437 fs = {
438 'origin': 'svn@%(rev)d\n',
439 'trunk/origin': 'svn/trunk@%(rev)d\n',
440 'trunk/src/origin': 'svn/trunk/src@%(rev)d\n',
441 'trunk/src/third_party/origin': 'svn/trunk/src/third_party@%(rev)d\n',
442 'trunk/other/origin': 'src/trunk/other@%(rev)d\n',
443 'trunk/third_party/origin': 'svn/trunk/third_party@%(rev)d\n',
444 'trunk/third_party/foo/origin': 'svn/trunk/third_party/foo@%(rev)d\n',
445 'trunk/third_party/prout/origin': 'svn/trunk/third_party/foo@%(rev)d\n',
446 }
447 for k in fs.iterkeys():
448 fs[k] = fs[k] % { 'rev': rev }
449 fs['trunk/src/DEPS'] = DEPS
450 if DEPS_ALT:
451 fs['trunk/src/DEPS.alt'] = DEPS_ALT
452 return fs
453
454 # Testing:
455 # - dependency disapear
456 # - dependency renamed
457 # - versioned and unversioned reference
458 # - relative and full reference
459 # - deps_os
460 # - var
461 # - hooks
462 # - From
463 # - File
464 # TODO(maruel):
465 # - $matching_files
466 # - use_relative_paths
467 DEPS = """
468 vars = {
469 'DummyVariable': 'third_party',
470 }
471 deps = {
472 'src/other': '%(svn_base)strunk/other@1',
473 'src/third_party/fpp': '/trunk/' + Var('DummyVariable') + '/foo',
474 }
475 deps_os = {
476 'mac': {
477 'src/third_party/prout': '/trunk/third_party/prout',
478 },
479 }""" % { 'svn_base': self.svn_base }
480
481 DEPS_ALT = """
482 deps = {
483 'src/other2': '%(svn_base)strunk/other@2'
484 }
485 """ % { 'svn_base': self.svn_base }
486
487 fs = file_system(1, DEPS, DEPS_ALT)
488 self._commit_svn(fs)
489
490 fs = file_system(2, """
491 deps = {
492 'src/other': '%(svn_base)strunk/other',
493 # Load another DEPS and load a dependency from it. That's an example of
494 # WebKit's chromium checkout flow. Verify it works out of order.
495 'src/third_party/foo': From('src/file/other', 'foo/bar'),
496 'src/file/other': File('%(svn_base)strunk/other/DEPS'),
497 }
498 # I think this is wrong to have the hooks run from the base of the gclient
499 # checkout. It's maybe a bit too late to change that behavior.
500 hooks = [
501 {
502 'pattern': '.',
503 'action': ['python', '-c',
504 'open(\\'src/svn_hooked1\\', \\'w\\').write(\\'svn_hooked1\\')'],
505 },
506 {
507 # Should not be run.
508 'pattern': 'nonexistent',
509 'action': ['python', '-c',
510 'open(\\'src/svn_hooked2\\', \\'w\\').write(\\'svn_hooked2\\')'],
511 },
512 ]
513 """ % { 'svn_base': self.svn_base })
514 fs['trunk/other/DEPS'] = """
515 deps = {
516 'foo/bar': '/trunk/third_party/foo@1',
517 # Only the requested deps should be processed.
518 'invalid': '/does_not_exist',
519 }
520 """
521 # WebKit abuses this.
522 fs['trunk/webkit/.gclient'] = """
523 solutions = [
524 {
525 'name': './',
526 'url': None,
527 },
528 ]
529 """
530 fs['trunk/webkit/DEPS'] = """
531 deps = {
532 'foo/bar': '%(svn_base)strunk/third_party/foo@1'
533 }
534
535 hooks = [
536 {
537 'pattern': '.*',
538 'action': ['echo', 'foo'],
539 },
540 ]
541 """ % { 'svn_base': self.svn_base }
542 self._commit_svn(fs)
543
544 def populateGit(self):
545 # Testing:
546 # - dependency disapear
547 # - dependency renamed
548 # - versioned and unversioned reference
549 # - relative and full reference
550 # - deps_os
551 # - var
552 # - hooks
553 # - From
554 # TODO(maruel):
555 # - File: File is hard to test here because it's SVN-only. It's
556 # implementation should probably be replaced to use urllib instead.
557 # - $matching_files
558 # - use_relative_paths
559 self._commit_git('repo_3', {
560 'origin': 'git/repo_3@1\n',
561 })
562
563 self._commit_git('repo_3', {
564 'origin': 'git/repo_3@2\n',
565 })
566
567 self._commit_git('repo_1', {
568 'DEPS': """
569 vars = {
570 'DummyVariable': 'repo',
571 }
572 deps = {
573 'src/repo2': '%(git_base)srepo_2',
574 'src/repo2/repo3': '/' + Var('DummyVariable') + '_3@%(hash3)s',
575 }
576 deps_os = {
577 'mac': {
578 'src/repo4': '/repo_4',
579 },
580 }""" % {
581 'git_base': self.git_base,
582 # See self.__init__() for the format. Grab's the hash of the first
583 # commit in repo_2. Only keep the first 7 character because of:
584 # TODO(maruel): http://crosbug.com/3591 We need to strip the hash..
585 # duh.
586 'hash3': self.git_hashes['repo_3'][1][0][:7]
587 },
588 'origin': 'git/repo_1@1\n',
589 })
590
591 self._commit_git('repo_2', {
592 'origin': 'git/repo_2@1\n',
593 'DEPS': """
594 deps = {
595 'foo/bar': '/repo_3',
596 }
597 """,
598 })
599
600 self._commit_git('repo_2', {
601 'origin': 'git/repo_2@2\n',
602 })
603
604 self._commit_git('repo_4', {
605 'origin': 'git/repo_4@1\n',
606 })
607
608 self._commit_git('repo_4', {
609 'origin': 'git/repo_4@2\n',
610 })
611
612 self._commit_git('repo_1', {
613 'DEPS': """
614 deps = {
615 'src/repo2': '%(git_base)srepo_2@%(hash)s',
616 #'src/repo2/repo_renamed': '/repo_3',
617 'src/repo2/repo_renamed': From('src/repo2', 'foo/bar'),
618 }
619 # I think this is wrong to have the hooks run from the base of the gclient
620 # checkout. It's maybe a bit too late to change that behavior.
621 hooks = [
622 {
623 'pattern': '.',
624 'action': ['python', '-c',
625 'open(\\'src/git_hooked1\\', \\'w\\').write(\\'git_hooked1\\')'],
626 },
627 {
628 # Should not be run.
629 'pattern': 'nonexistent',
630 'action': ['python', '-c',
631 'open(\\'src/git_hooked2\\', \\'w\\').write(\\'git_hooked2\\')'],
632 },
633 ]
634 """ % {
635 'git_base': self.git_base,
636 # See self.__init__() for the format. Grab's the hash of the first
637 # commit in repo_2. Only keep the first 7 character because of:
638 # TODO(maruel): http://crosbug.com/3591 We need to strip the hash.. duh.
639 'hash': self.git_hashes['repo_2'][1][0][:7]
640 },
641 'origin': 'git/repo_1@2\n',
642 })
643
644
645 class FakeReposTestBase(trial_dir.TestCase):
646 """This is vaguely inspired by twisted."""
647 # static FakeRepos instance. Lazy loaded.
648 FAKE_REPOS = None
649 # Override if necessary.
650 FAKE_REPOS_CLASS = FakeRepos
651
652 def setUp(self):
653 super(FakeReposTestBase, self).setUp()
654 if not FakeReposTestBase.FAKE_REPOS:
655 # Lazy create the global instance.
656 FakeReposTestBase.FAKE_REPOS = self.FAKE_REPOS_CLASS()
657 # No need to call self.FAKE_REPOS.setUp(), it will be called by the child
658 # class.
659 # Do not define tearDown(), since super's version does the right thing and
660 # FAKE_REPOS is kept across tests.
661
662 @property
663 def svn_base(self):
664 """Shortcut."""
665 return self.FAKE_REPOS.svn_base
666
667 @property
668 def git_base(self):
669 """Shortcut."""
670 return self.FAKE_REPOS.git_base
671
672 def checkString(self, expected, result, msg=None):
673 """Prints the diffs to ease debugging."""
674 if expected != result:
675 # Strip the begining
676 while expected and result and expected[0] == result[0]:
677 expected = expected[1:]
678 result = result[1:]
679 # The exception trace makes it hard to read so dump it too.
680 if '\n' in result:
681 print result
682 self.assertEquals(expected, result, msg)
683
684 def check(self, expected, results):
685 """Checks stdout, stderr, returncode."""
686 self.checkString(expected[0], results[0])
687 self.checkString(expected[1], results[1])
688 self.assertEquals(expected[2], results[2])
689
690 def assertTree(self, tree, tree_root=None):
691 """Diff the checkout tree with a dict."""
692 if not tree_root:
693 tree_root = self.root_dir
694 actual = read_tree(tree_root)
695 diff = dict_diff(tree, actual)
696 if diff:
697 logging.debug('Actual %s\n%s' % (tree_root, pprint.pformat(actual)))
698 logging.debug('Expected\n%s' % pprint.pformat(tree))
699 logging.debug('Diff\n%s' % pprint.pformat(diff))
700 self.assertEquals(diff, [])
701
702 def mangle_svn_tree(self, *args):
703 """Creates a 'virtual directory snapshot' to compare with the actual result
704 on disk."""
705 result = {}
706 for item, new_root in args:
707 old_root, rev = item.split('@', 1)
708 tree = self.FAKE_REPOS.svn_revs[int(rev)]
709 for k, v in tree.iteritems():
710 if not k.startswith(old_root):
711 continue
712 item = k[len(old_root) + 1:]
713 if item.startswith('.'):
714 continue
715 result[join(new_root, item).replace(os.sep, '/')] = v
716 return result
717
718 def mangle_git_tree(self, *args):
719 """Creates a 'virtual directory snapshot' to compare with the actual result
720 on disk."""
721 result = {}
722 for item, new_root in args:
723 repo, rev = item.split('@', 1)
724 tree = self.gittree(repo, rev)
725 for k, v in tree.iteritems():
726 result[join(new_root, k)] = v
727 return result
728
729 def githash(self, repo, rev):
730 """Sort-hand: Returns the hash for a git 'revision'."""
731 return self.FAKE_REPOS.git_hashes[repo][int(rev)][0]
732
733 def gittree(self, repo, rev):
734 """Sort-hand: returns the directory tree for a git 'revision'."""
735 return self.FAKE_REPOS.git_hashes[repo][int(rev)][1]
736
737
738 def main(argv):
739 fake = FakeRepos()
740 print 'Using %s' % fake.root_dir
741 try:
742 fake.set_up_svn()
743 fake.set_up_git()
744 print('Fake setup, press enter to quit or Ctrl-C to keep the checkouts.')
745 sys.stdin.readline()
746 except KeyboardInterrupt:
747 trial_dir.TrialDir.SHOULD_LEAK.leak = True
748 return 0
749
750
751 if __name__ == '__main__':
752 sys.exit(main(sys.argv))
OLDNEW
« no previous file with comments | « tests/checkout_test.py ('k') | tests/filesystem_mock.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698