1565
1599
t.put_bytes('test.kndx', '# not really a knit header\n\n')
1567
1601
self.assertRaises(KnitHeaderError, self.make_test_knit)
1604
class TestGraphIndexKnit(KnitTests):
1605
"""Tests for knits using a GraphIndex rather than a KnitIndex."""
1607
def make_g_index(self, name, ref_lists=0, nodes=[]):
1608
builder = GraphIndexBuilder(ref_lists)
1609
for node, references, value in nodes:
1610
builder.add_node(node, references, value)
1611
stream = builder.finish()
1612
trans = self.get_transport()
1613
trans.put_file(name, stream)
1614
return GraphIndex(trans, name)
1616
def two_graph_index(self, deltas=False, catch_adds=False):
1617
"""Build a two-graph index.
1619
:param deltas: If true, use underlying indices with two node-ref
1620
lists and 'parent' set to a delta-compressed against tail.
1622
# build a complex graph across several indices.
1624
index1 = self.make_g_index('1', 2, [
1625
('tip', 'N0 100', (['parent'], [], )),
1626
('tail', '', ([], []))])
1627
index2 = self.make_g_index('2', 2, [
1628
('parent', ' 100 78', (['tail', 'ghost'], ['tail'])),
1629
('separate', '', ([], []))])
1631
index1 = self.make_g_index('1', 1, [
1632
('tip', 'N0 100', (['parent'], )),
1633
('tail', '', ([], ))])
1634
index2 = self.make_g_index('2', 1, [
1635
('parent', ' 100 78', (['tail', 'ghost'], )),
1636
('separate', '', ([], ))])
1637
combined_index = CombinedGraphIndex([index1, index2])
1639
self.combined_index = combined_index
1640
self.caught_entries = []
1641
add_callback = self.catch_add
1644
return KnitGraphIndex(combined_index, deltas=deltas,
1645
add_callback=add_callback)
1647
def test_get_graph(self):
1648
index = self.two_graph_index()
1649
self.assertEqual(set([
1650
('tip', ('parent', )),
1652
('parent', ('tail', 'ghost')),
1654
]), set(index.get_graph()))
1656
def test_get_ancestry(self):
1657
# get_ancestry is defined as eliding ghosts, not erroring.
1658
index = self.two_graph_index()
1659
self.assertEqual([], index.get_ancestry([]))
1660
self.assertEqual(['separate'], index.get_ancestry(['separate']))
1661
self.assertEqual(['tail'], index.get_ancestry(['tail']))
1662
self.assertEqual(['tail', 'parent'], index.get_ancestry(['parent']))
1663
self.assertEqual(['tail', 'parent', 'tip'], index.get_ancestry(['tip']))
1664
self.assertTrue(index.get_ancestry(['tip', 'separate']) in
1665
(['tail', 'parent', 'tip', 'separate'],
1666
['separate', 'tail', 'parent', 'tip'],
1668
# and without topo_sort
1669
self.assertEqual(set(['separate']),
1670
set(index.get_ancestry(['separate'], topo_sorted=False)))
1671
self.assertEqual(set(['tail']),
1672
set(index.get_ancestry(['tail'], topo_sorted=False)))
1673
self.assertEqual(set(['tail', 'parent']),
1674
set(index.get_ancestry(['parent'], topo_sorted=False)))
1675
self.assertEqual(set(['tail', 'parent', 'tip']),
1676
set(index.get_ancestry(['tip'], topo_sorted=False)))
1677
self.assertEqual(set(['separate', 'tail', 'parent', 'tip']),
1678
set(index.get_ancestry(['tip', 'separate'])))
1679
# asking for a ghost makes it go boom.
1680
self.assertRaises(errors.RevisionNotPresent, index.get_ancestry, ['ghost'])
1682
def test_get_ancestry_with_ghosts(self):
1683
index = self.two_graph_index()
1684
self.assertEqual([], index.get_ancestry_with_ghosts([]))
1685
self.assertEqual(['separate'], index.get_ancestry_with_ghosts(['separate']))
1686
self.assertEqual(['tail'], index.get_ancestry_with_ghosts(['tail']))
1687
self.assertTrue(index.get_ancestry_with_ghosts(['parent']) in
1688
(['tail', 'ghost', 'parent'],
1689
['ghost', 'tail', 'parent'],
1691
self.assertTrue(index.get_ancestry_with_ghosts(['tip']) in
1692
(['tail', 'ghost', 'parent', 'tip'],
1693
['ghost', 'tail', 'parent', 'tip'],
1695
self.assertTrue(index.get_ancestry_with_ghosts(['tip', 'separate']) in
1696
(['tail', 'ghost', 'parent', 'tip', 'separate'],
1697
['ghost', 'tail', 'parent', 'tip', 'separate'],
1698
['separate', 'tail', 'ghost', 'parent', 'tip'],
1699
['separate', 'ghost', 'tail', 'parent', 'tip'],
1701
# asking for a ghost makes it go boom.
1702
self.assertRaises(errors.RevisionNotPresent, index.get_ancestry_with_ghosts, ['ghost'])
1704
def test_num_versions(self):
1705
index = self.two_graph_index()
1706
self.assertEqual(4, index.num_versions())
1708
def test_get_versions(self):
1709
index = self.two_graph_index()
1710
self.assertEqual(set(['tail', 'tip', 'parent', 'separate']),
1711
set(index.get_versions()))
1713
def test_has_version(self):
1714
index = self.two_graph_index()
1715
self.assertTrue(index.has_version('tail'))
1716
self.assertFalse(index.has_version('ghost'))
1718
def test_get_position(self):
1719
index = self.two_graph_index()
1720
self.assertEqual((0, 100), index.get_position('tip'))
1721
self.assertEqual((100, 78), index.get_position('parent'))
1723
def test_get_method_deltas(self):
1724
index = self.two_graph_index(deltas=True)
1725
self.assertEqual('fulltext', index.get_method('tip'))
1726
self.assertEqual('line-delta', index.get_method('parent'))
1728
def test_get_method_no_deltas(self):
1729
# check that the parent-history lookup is ignored with deltas=False.
1730
index = self.two_graph_index(deltas=False)
1731
self.assertEqual('fulltext', index.get_method('tip'))
1732
self.assertEqual('fulltext', index.get_method('parent'))
1734
def test_get_options_deltas(self):
1735
index = self.two_graph_index(deltas=True)
1736
self.assertEqual('fulltext,no-eol', index.get_options('tip'))
1737
self.assertEqual('line-delta', index.get_options('parent'))
1739
def test_get_options_no_deltas(self):
1740
# check that the parent-history lookup is ignored with deltas=False.
1741
index = self.two_graph_index(deltas=False)
1742
self.assertEqual('fulltext,no-eol', index.get_options('tip'))
1743
self.assertEqual('fulltext', index.get_options('parent'))
1745
def test_get_parents(self):
1746
# get_parents ignores ghosts
1747
index = self.two_graph_index()
1748
self.assertEqual(('tail', ), index.get_parents('parent'))
1749
# and errors on ghosts.
1750
self.assertRaises(errors.RevisionNotPresent,
1751
index.get_parents, 'ghost')
1753
def test_get_parents_with_ghosts(self):
1754
index = self.two_graph_index()
1755
self.assertEqual(('tail', 'ghost'), index.get_parents_with_ghosts('parent'))
1756
# and errors on ghosts.
1757
self.assertRaises(errors.RevisionNotPresent,
1758
index.get_parents_with_ghosts, 'ghost')
1760
def test_check_versions_present(self):
1761
# ghosts should not be considered present
1762
index = self.two_graph_index()
1763
self.assertRaises(RevisionNotPresent, index.check_versions_present,
1765
self.assertRaises(RevisionNotPresent, index.check_versions_present,
1767
index.check_versions_present(['tail', 'separate'])
1769
def catch_add(self, entries):
1770
self.caught_entries.append(entries)
1772
def test_add_no_callback_errors(self):
1773
index = self.two_graph_index()
1774
self.assertRaises(errors.ReadOnlyError, index.add_version,
1775
'new', 'fulltext,no-eol', 50, 60, ['separate'])
1777
def test_add_version_smoke(self):
1778
index = self.two_graph_index(catch_adds=True)
1779
index.add_version('new', 'fulltext,no-eol', 50, 60, ['separate'])
1780
self.assertEqual([[('new', 'N50 60', (('separate',),))]],
1781
self.caught_entries)
1783
def test_add_version_delta_not_delta_index(self):
1784
index = self.two_graph_index(catch_adds=True)
1785
self.assertRaises(errors.KnitCorrupt, index.add_version,
1786
'new', 'no-eol,line-delta', 0, 100, ['parent'])
1787
self.assertEqual([], self.caught_entries)
1789
def test_add_version_same_dup(self):
1790
index = self.two_graph_index(catch_adds=True)
1791
# options can be spelt two different ways
1792
index.add_version('tip', 'fulltext,no-eol', 0, 100, ['parent'])
1793
index.add_version('tip', 'no-eol,fulltext', 0, 100, ['parent'])
1794
# but neither should have added data.
1795
self.assertEqual([[], []], self.caught_entries)
1797
def test_add_version_different_dup(self):
1798
index = self.two_graph_index(deltas=True, catch_adds=True)
1800
self.assertRaises(errors.KnitCorrupt, index.add_version,
1801
'tip', 'no-eol,line-delta', 0, 100, ['parent'])
1802
self.assertRaises(errors.KnitCorrupt, index.add_version,
1803
'tip', 'line-delta,no-eol', 0, 100, ['parent'])
1804
self.assertRaises(errors.KnitCorrupt, index.add_version,
1805
'tip', 'fulltext', 0, 100, ['parent'])
1807
self.assertRaises(errors.KnitCorrupt, index.add_version,
1808
'tip', 'fulltext,no-eol', 50, 100, ['parent'])
1809
self.assertRaises(errors.KnitCorrupt, index.add_version,
1810
'tip', 'fulltext,no-eol', 0, 1000, ['parent'])
1812
self.assertRaises(errors.KnitCorrupt, index.add_version,
1813
'tip', 'fulltext,no-eol', 0, 100, [])
1814
self.assertEqual([], self.caught_entries)
1816
def test_add_versions_nodeltas(self):
1817
index = self.two_graph_index(catch_adds=True)
1818
index.add_versions([
1819
('new', 'fulltext,no-eol', 50, 60, ['separate']),
1820
('new2', 'fulltext', 0, 6, ['new']),
1822
self.assertEqual([('new', 'N50 60', (('separate',),)),
1823
('new2', ' 0 6', (('new',),))],
1824
sorted(self.caught_entries[0]))
1825
self.assertEqual(1, len(self.caught_entries))
1827
def test_add_versions_deltas(self):
1828
index = self.two_graph_index(deltas=True, catch_adds=True)
1829
index.add_versions([
1830
('new', 'fulltext,no-eol', 50, 60, ['separate']),
1831
('new2', 'line-delta', 0, 6, ['new']),
1833
self.assertEqual([('new', 'N50 60', (('separate',), ())),
1834
('new2', ' 0 6', (('new',), ('new',), ))],
1835
sorted(self.caught_entries[0]))
1836
self.assertEqual(1, len(self.caught_entries))
1838
def test_add_versions_delta_not_delta_index(self):
1839
index = self.two_graph_index(catch_adds=True)
1840
self.assertRaises(errors.KnitCorrupt, index.add_versions,
1841
[('new', 'no-eol,line-delta', 0, 100, ['parent'])])
1842
self.assertEqual([], self.caught_entries)
1844
def test_add_versions_same_dup(self):
1845
index = self.two_graph_index(catch_adds=True)
1846
# options can be spelt two different ways
1847
index.add_versions([('tip', 'fulltext,no-eol', 0, 100, ['parent'])])
1848
index.add_versions([('tip', 'no-eol,fulltext', 0, 100, ['parent'])])
1849
# but neither should have added data.
1850
self.assertEqual([[], []], self.caught_entries)
1852
def test_add_versions_different_dup(self):
1853
index = self.two_graph_index(deltas=True, catch_adds=True)
1855
self.assertRaises(errors.KnitCorrupt, index.add_versions,
1856
[('tip', 'no-eol,line-delta', 0, 100, ['parent'])])
1857
self.assertRaises(errors.KnitCorrupt, index.add_versions,
1858
[('tip', 'line-delta,no-eol', 0, 100, ['parent'])])
1859
self.assertRaises(errors.KnitCorrupt, index.add_versions,
1860
[('tip', 'fulltext', 0, 100, ['parent'])])
1862
self.assertRaises(errors.KnitCorrupt, index.add_versions,
1863
[('tip', 'fulltext,no-eol', 50, 100, ['parent'])])
1864
self.assertRaises(errors.KnitCorrupt, index.add_versions,
1865
[('tip', 'fulltext,no-eol', 0, 1000, ['parent'])])
1867
self.assertRaises(errors.KnitCorrupt, index.add_versions,
1868
[('tip', 'fulltext,no-eol', 0, 100, [])])
1869
# change options in the second record
1870
self.assertRaises(errors.KnitCorrupt, index.add_versions,
1871
[('tip', 'fulltext,no-eol', 0, 100, ['parent']),
1872
('tip', 'no-eol,line-delta', 0, 100, ['parent'])])
1873
self.assertEqual([], self.caught_entries)
1875
def test_iter_parents(self):
1876
index1 = self.make_g_index('1', 1, [
1878
('r0', 'N0 100', ([], )),
1880
('r1', '', (['r0'], ))])
1881
index2 = self.make_g_index('2', 1, [
1883
('r2', 'N0 100', (['r1', 'r0'], )),
1885
combined_index = CombinedGraphIndex([index1, index2])
1886
index = KnitGraphIndex(combined_index)
1888
# cases: each sample data individually:
1889
self.assertEqual(set([('r0', ())]),
1890
set(index.iter_parents(['r0'])))
1891
self.assertEqual(set([('r1', ('r0', ))]),
1892
set(index.iter_parents(['r1'])))
1893
self.assertEqual(set([('r2', ('r1', 'r0'))]),
1894
set(index.iter_parents(['r2'])))
1895
# no nodes returned for a missing node
1896
self.assertEqual(set(),
1897
set(index.iter_parents(['missing'])))
1898
# 1 node returned with missing nodes skipped
1899
self.assertEqual(set([('r1', ('r0', ))]),
1900
set(index.iter_parents(['ghost1', 'r1', 'ghost'])))
1902
self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
1903
set(index.iter_parents(['r0', 'r1'])))
1904
# 2 nodes returned, missing skipped
1905
self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
1906
set(index.iter_parents(['a', 'r0', 'b', 'r1', 'c'])))
1909
class TestNoParentsGraphIndexKnit(KnitTests):
1910
"""Tests for knits using KnitGraphIndex with no parents."""
1912
def make_g_index(self, name, ref_lists=0, nodes=[]):
1913
builder = GraphIndexBuilder(ref_lists)
1914
for node, references in nodes:
1915
builder.add_node(node, references)
1916
stream = builder.finish()
1917
trans = self.get_transport()
1918
trans.put_file(name, stream)
1919
return GraphIndex(trans, name)
1921
def test_parents_deltas_incompatible(self):
1922
index = CombinedGraphIndex([])
1923
self.assertRaises(errors.KnitError, KnitGraphIndex, index,
1924
deltas=True, parents=False)
1926
def two_graph_index(self, catch_adds=False):
1927
"""Build a two-graph index.
1929
:param deltas: If true, use underlying indices with two node-ref
1930
lists and 'parent' set to a delta-compressed against tail.
1932
# put several versions in the index.
1933
index1 = self.make_g_index('1', 0, [
1936
index2 = self.make_g_index('2', 0, [
1937
('parent', ' 100 78'),
1939
combined_index = CombinedGraphIndex([index1, index2])
1941
self.combined_index = combined_index
1942
self.caught_entries = []
1943
add_callback = self.catch_add
1946
return KnitGraphIndex(combined_index, parents=False,
1947
add_callback=add_callback)
1949
def test_get_graph(self):
1950
index = self.two_graph_index()
1951
self.assertEqual(set([
1956
]), set(index.get_graph()))
1958
def test_get_ancestry(self):
1959
# with no parents, ancestry is always just the key.
1960
index = self.two_graph_index()
1961
self.assertEqual([], index.get_ancestry([]))
1962
self.assertEqual(['separate'], index.get_ancestry(['separate']))
1963
self.assertEqual(['tail'], index.get_ancestry(['tail']))
1964
self.assertEqual(['parent'], index.get_ancestry(['parent']))
1965
self.assertEqual(['tip'], index.get_ancestry(['tip']))
1966
self.assertTrue(index.get_ancestry(['tip', 'separate']) in
1967
(['tip', 'separate'],
1968
['separate', 'tip'],
1970
# asking for a ghost makes it go boom.
1971
self.assertRaises(errors.RevisionNotPresent, index.get_ancestry, ['ghost'])
1973
def test_get_ancestry_with_ghosts(self):
1974
index = self.two_graph_index()
1975
self.assertEqual([], index.get_ancestry_with_ghosts([]))
1976
self.assertEqual(['separate'], index.get_ancestry_with_ghosts(['separate']))
1977
self.assertEqual(['tail'], index.get_ancestry_with_ghosts(['tail']))
1978
self.assertEqual(['parent'], index.get_ancestry_with_ghosts(['parent']))
1979
self.assertEqual(['tip'], index.get_ancestry_with_ghosts(['tip']))
1980
self.assertTrue(index.get_ancestry_with_ghosts(['tip', 'separate']) in
1981
(['tip', 'separate'],
1982
['separate', 'tip'],
1984
# asking for a ghost makes it go boom.
1985
self.assertRaises(errors.RevisionNotPresent, index.get_ancestry_with_ghosts, ['ghost'])
1987
def test_num_versions(self):
1988
index = self.two_graph_index()
1989
self.assertEqual(4, index.num_versions())
1991
def test_get_versions(self):
1992
index = self.two_graph_index()
1993
self.assertEqual(set(['tail', 'tip', 'parent', 'separate']),
1994
set(index.get_versions()))
1996
def test_has_version(self):
1997
index = self.two_graph_index()
1998
self.assertTrue(index.has_version('tail'))
1999
self.assertFalse(index.has_version('ghost'))
2001
def test_get_position(self):
2002
index = self.two_graph_index()
2003
self.assertEqual((0, 100), index.get_position('tip'))
2004
self.assertEqual((100, 78), index.get_position('parent'))
2006
def test_get_method(self):
2007
index = self.two_graph_index()
2008
self.assertEqual('fulltext', index.get_method('tip'))
2009
self.assertEqual('fulltext', index.get_options('parent'))
2011
def test_get_options(self):
2012
index = self.two_graph_index()
2013
self.assertEqual('fulltext,no-eol', index.get_options('tip'))
2014
self.assertEqual('fulltext', index.get_options('parent'))
2016
def test_get_parents(self):
2017
index = self.two_graph_index()
2018
self.assertEqual((), index.get_parents('parent'))
2019
# and errors on ghosts.
2020
self.assertRaises(errors.RevisionNotPresent,
2021
index.get_parents, 'ghost')
2023
def test_get_parents_with_ghosts(self):
2024
index = self.two_graph_index()
2025
self.assertEqual((), index.get_parents_with_ghosts('parent'))
2026
# and errors on ghosts.
2027
self.assertRaises(errors.RevisionNotPresent,
2028
index.get_parents_with_ghosts, 'ghost')
2030
def test_check_versions_present(self):
2031
index = self.two_graph_index()
2032
self.assertRaises(RevisionNotPresent, index.check_versions_present,
2034
self.assertRaises(RevisionNotPresent, index.check_versions_present,
2035
['tail', 'missing'])
2036
index.check_versions_present(['tail', 'separate'])
2038
def catch_add(self, entries):
2039
self.caught_entries.append(entries)
2041
def test_add_no_callback_errors(self):
2042
index = self.two_graph_index()
2043
self.assertRaises(errors.ReadOnlyError, index.add_version,
2044
'new', 'fulltext,no-eol', 50, 60, ['separate'])
2046
def test_add_version_smoke(self):
2047
index = self.two_graph_index(catch_adds=True)
2048
index.add_version('new', 'fulltext,no-eol', 50, 60, [])
2049
self.assertEqual([[('new', 'N50 60')]],
2050
self.caught_entries)
2052
def test_add_version_delta_not_delta_index(self):
2053
index = self.two_graph_index(catch_adds=True)
2054
self.assertRaises(errors.KnitCorrupt, index.add_version,
2055
'new', 'no-eol,line-delta', 0, 100, [])
2056
self.assertEqual([], self.caught_entries)
2058
def test_add_version_same_dup(self):
2059
index = self.two_graph_index(catch_adds=True)
2060
# options can be spelt two different ways
2061
index.add_version('tip', 'fulltext,no-eol', 0, 100, [])
2062
index.add_version('tip', 'no-eol,fulltext', 0, 100, [])
2063
# but neither should have added data.
2064
self.assertEqual([[], []], self.caught_entries)
2066
def test_add_version_different_dup(self):
2067
index = self.two_graph_index(catch_adds=True)
2069
self.assertRaises(errors.KnitCorrupt, index.add_version,
2070
'tip', 'no-eol,line-delta', 0, 100, [])
2071
self.assertRaises(errors.KnitCorrupt, index.add_version,
2072
'tip', 'line-delta,no-eol', 0, 100, [])
2073
self.assertRaises(errors.KnitCorrupt, index.add_version,
2074
'tip', 'fulltext', 0, 100, [])
2076
self.assertRaises(errors.KnitCorrupt, index.add_version,
2077
'tip', 'fulltext,no-eol', 50, 100, [])
2078
self.assertRaises(errors.KnitCorrupt, index.add_version,
2079
'tip', 'fulltext,no-eol', 0, 1000, [])
2081
self.assertRaises(errors.KnitCorrupt, index.add_version,
2082
'tip', 'fulltext,no-eol', 0, 100, ['parent'])
2083
self.assertEqual([], self.caught_entries)
2085
def test_add_versions(self):
2086
index = self.two_graph_index(catch_adds=True)
2087
index.add_versions([
2088
('new', 'fulltext,no-eol', 50, 60, []),
2089
('new2', 'fulltext', 0, 6, []),
2091
self.assertEqual([('new', 'N50 60'), ('new2', ' 0 6')],
2092
sorted(self.caught_entries[0]))
2093
self.assertEqual(1, len(self.caught_entries))
2095
def test_add_versions_delta_not_delta_index(self):
2096
index = self.two_graph_index(catch_adds=True)
2097
self.assertRaises(errors.KnitCorrupt, index.add_versions,
2098
[('new', 'no-eol,line-delta', 0, 100, ['parent'])])
2099
self.assertEqual([], self.caught_entries)
2101
def test_add_versions_parents_not_parents_index(self):
2102
index = self.two_graph_index(catch_adds=True)
2103
self.assertRaises(errors.KnitCorrupt, index.add_versions,
2104
[('new', 'no-eol,fulltext', 0, 100, ['parent'])])
2105
self.assertEqual([], self.caught_entries)
2107
def test_add_versions_same_dup(self):
2108
index = self.two_graph_index(catch_adds=True)
2109
# options can be spelt two different ways
2110
index.add_versions([('tip', 'fulltext,no-eol', 0, 100, [])])
2111
index.add_versions([('tip', 'no-eol,fulltext', 0, 100, [])])
2112
# but neither should have added data.
2113
self.assertEqual([[], []], self.caught_entries)
2115
def test_add_versions_different_dup(self):
2116
index = self.two_graph_index(catch_adds=True)
2118
self.assertRaises(errors.KnitCorrupt, index.add_versions,
2119
[('tip', 'no-eol,line-delta', 0, 100, [])])
2120
self.assertRaises(errors.KnitCorrupt, index.add_versions,
2121
[('tip', 'line-delta,no-eol', 0, 100, [])])
2122
self.assertRaises(errors.KnitCorrupt, index.add_versions,
2123
[('tip', 'fulltext', 0, 100, [])])
2125
self.assertRaises(errors.KnitCorrupt, index.add_versions,
2126
[('tip', 'fulltext,no-eol', 50, 100, [])])
2127
self.assertRaises(errors.KnitCorrupt, index.add_versions,
2128
[('tip', 'fulltext,no-eol', 0, 1000, [])])
2130
self.assertRaises(errors.KnitCorrupt, index.add_versions,
2131
[('tip', 'fulltext,no-eol', 0, 100, ['parent'])])
2132
# change options in the second record
2133
self.assertRaises(errors.KnitCorrupt, index.add_versions,
2134
[('tip', 'fulltext,no-eol', 0, 100, []),
2135
('tip', 'no-eol,line-delta', 0, 100, [])])
2136
self.assertEqual([], self.caught_entries)
2138
def test_iter_parents(self):
2139
index = self.two_graph_index()
2140
self.assertEqual(set([
2141
('tip', ()), ('tail', ()), ('parent', ()), ('separate', ())
2143
set(index.iter_parents(['tip', 'tail', 'ghost', 'parent', 'separate'])))
2144
self.assertEqual(set([('tip', ())]),
2145
set(index.iter_parents(['tip'])))
2146
self.assertEqual(set(),
2147
set(index.iter_parents([])))