424
424
def __init__(self, parents_provider):
426
426
self._real_parents_provider = parents_provider
427
get_cached = getattr(parents_provider, 'get_cached_parent_map', None)
428
if get_cached is not None:
429
# Only expose the underlying 'get_cached_parent_map' function if
430
# the wrapped provider has it.
431
self.get_cached_parent_map = self._get_cached_parent_map
433
428
def get_parent_map(self, nodes):
434
429
self.calls.extend(nodes)
435
430
return self._real_parents_provider.get_parent_map(nodes)
437
def _get_cached_parent_map(self, nodes):
438
self.calls.append(('cached', sorted(nodes)))
439
return self._real_parents_provider.get_cached_parent_map(nodes)
442
class SharedInstrumentedParentsProvider(object):
444
def __init__(self, parents_provider, calls, info):
447
self._real_parents_provider = parents_provider
448
get_cached = getattr(parents_provider, 'get_cached_parent_map', None)
449
if get_cached is not None:
450
# Only expose the underlying 'get_cached_parent_map' function if
451
# the wrapped provider has it.
452
self.get_cached_parent_map = self._get_cached_parent_map
454
def get_parent_map(self, nodes):
455
self.calls.append((self.info, sorted(nodes)))
456
return self._real_parents_provider.get_parent_map(nodes)
458
def _get_cached_parent_map(self, nodes):
459
self.calls.append((self.info, 'cached', sorted(nodes)))
460
return self._real_parents_provider.get_cached_parent_map(nodes)
463
433
class TestGraphBase(tests.TestCase):
703
673
self.assertEqual((set(['e']), set(['f', 'g'])),
704
674
graph.find_difference('e', 'f'))
677
def test_stacked_parents_provider(self):
678
parents1 = _mod_graph.DictParentsProvider({'rev2': ['rev3']})
679
parents2 = _mod_graph.DictParentsProvider({'rev1': ['rev4']})
680
stacked = _mod_graph.StackedParentsProvider([parents1, parents2])
681
self.assertEqual({'rev1':['rev4'], 'rev2':['rev3']},
682
stacked.get_parent_map(['rev1', 'rev2']))
683
self.assertEqual({'rev2':['rev3'], 'rev1':['rev4']},
684
stacked.get_parent_map(['rev2', 'rev1']))
685
self.assertEqual({'rev2':['rev3']},
686
stacked.get_parent_map(['rev2', 'rev2']))
687
self.assertEqual({'rev1':['rev4']},
688
stacked.get_parent_map(['rev1', 'rev1']))
690
def test_stacked_parents_provider_overlapping(self):
691
# rev2 is availible in both providers.
695
parents1 = _mod_graph.DictParentsProvider({'rev2': ['rev1']})
696
parents2 = _mod_graph.DictParentsProvider({'rev2': ['rev1']})
697
stacked = _mod_graph.StackedParentsProvider([parents1, parents2])
698
self.assertEqual({'rev2': ['rev1']},
699
stacked.get_parent_map(['rev2']))
706
701
def test_iter_topo_order(self):
707
702
graph = self.make_graph(ancestry_1)
708
703
args = ['rev2a', 'rev3', 'rev1']
1231
1229
self.assertSeenAndResult(expected, search, search.next)
1232
1230
self.assertRaises(StopIteration, search.next)
1233
1231
self.assertEqual(set(['head', 'ghost', NULL_REVISION]), search.seen)
1234
state = search.get_state()
1236
(set(['ghost', 'head']), set(['ghost']),
1237
set(['head', NULL_REVISION])),
1232
result = search.get_result()
1233
self.assertEqual(('search', set(['ghost', 'head']), set(['ghost']), 2),
1234
result.get_recipe())
1235
self.assertEqual(set(['head', NULL_REVISION]), result.get_keys())
1239
1236
# using next_with_ghosts:
1240
1237
search = graph._make_breadth_first_searcher(['head'])
1241
1238
self.assertSeenAndResult(expected, search, search.next_with_ghosts)
1242
1239
self.assertRaises(StopIteration, search.next)
1243
1240
self.assertEqual(set(['head', 'ghost', NULL_REVISION]), search.seen)
1244
state = search.get_state()
1246
(set(['ghost', 'head']), set(['ghost']),
1247
set(['head', NULL_REVISION])),
1241
result = search.get_result()
1242
self.assertEqual(('search', set(['ghost', 'head']), set(['ghost']), 2),
1243
result.get_recipe())
1244
self.assertEqual(set(['head', NULL_REVISION]), result.get_keys())
1251
1247
class TestFindUniqueAncestors(TestGraphBase):
1676
1656
for n in graph_thunk.merge_sort('C')])
1679
class TestStackedParentsProvider(tests.TestCase):
1682
super(TestStackedParentsProvider, self).setUp()
1685
def get_shared_provider(self, info, ancestry, has_cached):
1686
pp = _mod_graph.DictParentsProvider(ancestry)
1688
pp.get_cached_parent_map = pp.get_parent_map
1689
return SharedInstrumentedParentsProvider(pp, self.calls, info)
1691
def test_stacked_parents_provider(self):
1692
parents1 = _mod_graph.DictParentsProvider({'rev2': ['rev3']})
1693
parents2 = _mod_graph.DictParentsProvider({'rev1': ['rev4']})
1694
stacked = _mod_graph.StackedParentsProvider([parents1, parents2])
1695
self.assertEqual({'rev1':['rev4'], 'rev2':['rev3']},
1696
stacked.get_parent_map(['rev1', 'rev2']))
1697
self.assertEqual({'rev2':['rev3'], 'rev1':['rev4']},
1698
stacked.get_parent_map(['rev2', 'rev1']))
1699
self.assertEqual({'rev2':['rev3']},
1700
stacked.get_parent_map(['rev2', 'rev2']))
1701
self.assertEqual({'rev1':['rev4']},
1702
stacked.get_parent_map(['rev1', 'rev1']))
1704
def test_stacked_parents_provider_overlapping(self):
1705
# rev2 is availible in both providers.
1709
parents1 = _mod_graph.DictParentsProvider({'rev2': ['rev1']})
1710
parents2 = _mod_graph.DictParentsProvider({'rev2': ['rev1']})
1711
stacked = _mod_graph.StackedParentsProvider([parents1, parents2])
1712
self.assertEqual({'rev2': ['rev1']},
1713
stacked.get_parent_map(['rev2']))
1715
def test_handles_no_get_cached_parent_map(self):
1716
# this shows that we both handle when a provider doesn't implement
1717
# get_cached_parent_map
1718
pp1 = self.get_shared_provider('pp1', {'rev2': ('rev1',)},
1720
pp2 = self.get_shared_provider('pp2', {'rev2': ('rev1',)},
1722
stacked = _mod_graph.StackedParentsProvider([pp1, pp2])
1723
self.assertEqual({'rev2': ('rev1',)}, stacked.get_parent_map(['rev2']))
1724
# No call on 'pp1' because it doesn't provide get_cached_parent_map
1725
self.assertEqual([('pp2', 'cached', ['rev2'])], self.calls)
1727
def test_query_order(self):
1728
# We should call get_cached_parent_map on all providers before we call
1729
# get_parent_map. Further, we should track what entries we have found,
1730
# and not re-try them.
1731
pp1 = self.get_shared_provider('pp1', {'a': ()}, has_cached=True)
1732
pp2 = self.get_shared_provider('pp2', {'c': ('b',)}, has_cached=False)
1733
pp3 = self.get_shared_provider('pp3', {'b': ('a',)}, has_cached=True)
1734
stacked = _mod_graph.StackedParentsProvider([pp1, pp2, pp3])
1735
self.assertEqual({'a': (), 'b': ('a',), 'c': ('b',)},
1736
stacked.get_parent_map(['a', 'b', 'c', 'd']))
1737
self.assertEqual([('pp1', 'cached', ['a', 'b', 'c', 'd']),
1738
# No call to pp2, because it doesn't have cached
1739
('pp3', 'cached', ['b', 'c', 'd']),
1740
('pp1', ['c', 'd']),
1741
('pp2', ['c', 'd']),
1659
class TestPendingAncestryResultGetKeys(TestCaseWithMemoryTransport):
1660
"""Tests for bzrlib.graph.PendingAncestryResult."""
1662
def test_get_keys(self):
1663
builder = self.make_branch_builder('b')
1664
builder.start_series()
1665
builder.build_snapshot('rev-1', None, [
1666
('add', ('', 'root-id', 'directory', ''))])
1667
builder.build_snapshot('rev-2', ['rev-1'], [])
1668
builder.finish_series()
1669
repo = builder.get_branch().repository
1671
self.addCleanup(repo.unlock)
1672
result = _mod_graph.PendingAncestryResult(['rev-2'], repo)
1673
self.assertEqual(set(['rev-1', 'rev-2']), set(result.get_keys()))
1675
def test_get_keys_excludes_ghosts(self):
1676
builder = self.make_branch_builder('b')
1677
builder.start_series()
1678
builder.build_snapshot('rev-1', None, [
1679
('add', ('', 'root-id', 'directory', ''))])
1680
builder.build_snapshot('rev-2', ['rev-1', 'ghost'], [])
1681
builder.finish_series()
1682
repo = builder.get_branch().repository
1684
self.addCleanup(repo.unlock)
1685
result = _mod_graph.PendingAncestryResult(['rev-2'], repo)
1686
self.assertEqual(sorted(['rev-1', 'rev-2']), sorted(result.get_keys()))
1688
def test_get_keys_excludes_null(self):
1689
# Make a 'graph' with an iter_ancestry that returns NULL_REVISION
1690
# somewhere other than the last element, which can happen in real
1692
class StubGraph(object):
1693
def iter_ancestry(self, keys):
1694
return [(NULL_REVISION, ()), ('foo', (NULL_REVISION,))]
1695
result = _mod_graph.PendingAncestryResult(['rev-3'], None)
1696
result_keys = result._get_keys(StubGraph())
1697
# Only the non-null keys from the ancestry appear.
1698
self.assertEqual(set(['foo']), set(result_keys))
1701
class TestPendingAncestryResultRefine(TestGraphBase):
1703
def test_refine(self):
1704
# Used when pulling from a stacked repository, so test some revisions
1705
# being satisfied from the stacking branch.
1706
g = self.make_graph(
1707
{"tip":["mid"], "mid":["base"], "tag":["base"],
1708
"base":[NULL_REVISION], NULL_REVISION:[]})
1709
result = _mod_graph.PendingAncestryResult(['tip', 'tag'], None)
1710
result = result.refine(set(['tip']), set(['mid']))
1711
self.assertEqual(set(['mid', 'tag']), result.heads)
1712
result = result.refine(set(['mid', 'tag', 'base']),
1713
set([NULL_REVISION]))
1714
self.assertEqual(set([NULL_REVISION]), result.heads)
1715
self.assertTrue(result.is_empty())
1718
class TestSearchResultRefine(TestGraphBase):
1720
def test_refine(self):
1721
# Used when pulling from a stacked repository, so test some revisions
1722
# being satisfied from the stacking branch.
1723
g = self.make_graph(
1724
{"tip":["mid"], "mid":["base"], "tag":["base"],
1725
"base":[NULL_REVISION], NULL_REVISION:[]})
1726
result = _mod_graph.SearchResult(set(['tip', 'tag']),
1727
set([NULL_REVISION]), 4, set(['tip', 'mid', 'tag', 'base']))
1728
result = result.refine(set(['tip']), set(['mid']))
1729
recipe = result.get_recipe()
1730
# We should be starting from tag (original head) and mid (seen ref)
1731
self.assertEqual(set(['mid', 'tag']), recipe[1])
1732
# We should be stopping at NULL (original stop) and tip (seen head)
1733
self.assertEqual(set([NULL_REVISION, 'tip']), recipe[2])
1734
self.assertEqual(3, recipe[3])
1735
result = result.refine(set(['mid', 'tag', 'base']),
1736
set([NULL_REVISION]))
1737
recipe = result.get_recipe()
1738
# We should be starting from nothing (NULL was known as a cut point)
1739
self.assertEqual(set([]), recipe[1])
1740
# We should be stopping at NULL (original stop) and tip (seen head) and
1741
# tag (seen head) and mid(seen mid-point head). We could come back and
1742
# define this as not including mid, for minimal results, but it is
1743
# still 'correct' to include mid, and simpler/easier.
1744
self.assertEqual(set([NULL_REVISION, 'tip', 'tag', 'mid']), recipe[2])
1745
self.assertEqual(0, recipe[3])
1746
self.assertTrue(result.is_empty())