]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/test/test_immutable.py
SegmentFetcher: use new diversity-seeking share-selection algorithm, and
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / test / test_immutable.py
1 from allmydata.test import common
2 from allmydata.interfaces import NotEnoughSharesError
3 from allmydata.util.consumer import download_to_data
4 from twisted.internet import defer
5 from twisted.trial import unittest
6 import random
7
8 class Test(common.ShareManglingMixin, common.ShouldFailMixin, unittest.TestCase):
9     def test_test_code(self):
10         # The following process of stashing the shares, running
11         # replace_shares, and asserting that the new set of shares equals the
12         # old is more to test this test code than to test the Tahoe code...
13         d = defer.succeed(None)
14         d.addCallback(self.find_all_shares)
15         stash = [None]
16         def _stash_it(res):
17             stash[0] = res
18             return res
19         d.addCallback(_stash_it)
20
21         # The following process of deleting 8 of the shares and asserting
22         # that you can't download it is more to test this test code than to
23         # test the Tahoe code...
24         def _then_delete_8(unused=None):
25             self.replace_shares(stash[0], storage_index=self.uri.get_storage_index())
26             for i in range(8):
27                 self._delete_a_share()
28         d.addCallback(_then_delete_8)
29
30         def _then_download(unused=None):
31             d2 = download_to_data(self.n)
32
33             def _after_download_callb(result):
34                 self.fail() # should have gotten an errback instead
35                 return result
36             def _after_download_errb(failure):
37                 failure.trap(NotEnoughSharesError)
38                 return None # success!
39             d2.addCallbacks(_after_download_callb, _after_download_errb)
40             return d2
41         d.addCallback(_then_download)
42
43         return d
44
45     def test_download(self):
46         """ Basic download. (This functionality is more or less already
47         tested by test code in other modules, but this module is also going
48         to test some more specific things about immutable download.)
49         """
50         d = defer.succeed(None)
51         before_download_reads = self._count_reads()
52         def _after_download(unused=None):
53             after_download_reads = self._count_reads()
54             #print before_download_reads, after_download_reads
55             self.failIf(after_download_reads-before_download_reads > 41,
56                         (after_download_reads, before_download_reads))
57         d.addCallback(self._download_and_check_plaintext)
58         d.addCallback(_after_download)
59         return d
60
61     def test_download_from_only_3_remaining_shares(self):
62         """ Test download after 7 random shares (of the 10) have been
63         removed."""
64         d = defer.succeed(None)
65         def _then_delete_7(unused=None):
66             for i in range(7):
67                 self._delete_a_share()
68         before_download_reads = self._count_reads()
69         d.addCallback(_then_delete_7)
70         def _after_download(unused=None):
71             after_download_reads = self._count_reads()
72             #print before_download_reads, after_download_reads
73             self.failIf(after_download_reads-before_download_reads > 37, (after_download_reads, before_download_reads))
74         d.addCallback(self._download_and_check_plaintext)
75         d.addCallback(_after_download)
76         return d
77
78     def test_download_from_only_3_shares_with_good_crypttext_hash(self):
79         """ Test download after 7 random shares (of the 10) have had their
80         crypttext hash tree corrupted."""
81         d = defer.succeed(None)
82         def _then_corrupt_7(unused=None):
83             shnums = range(10)
84             random.shuffle(shnums)
85             for i in shnums[:7]:
86                 self._corrupt_a_share(None, common._corrupt_offset_of_block_hashes_to_truncate_crypttext_hashes, i)
87         #before_download_reads = self._count_reads()
88         d.addCallback(_then_corrupt_7)
89         d.addCallback(self._download_and_check_plaintext)
90         return d
91
92     def test_download_abort_if_too_many_missing_shares(self):
93         """ Test that download gives up quickly when it realizes there aren't
94         enough shares out there."""
95         for i in range(8):
96             self._delete_a_share()
97         d = self.shouldFail(NotEnoughSharesError, "delete 8", None,
98                             download_to_data, self.n)
99         # the new downloader pipelines a bunch of read requests in parallel,
100         # so don't bother asserting anything about the number of reads
101         return d
102
103     def test_download_abort_if_too_many_corrupted_shares(self):
104         """Test that download gives up quickly when it realizes there aren't
105         enough uncorrupted shares out there. It should be able to tell
106         because the corruption occurs in the sharedata version number, which
107         it checks first."""
108         d = defer.succeed(None)
109         def _then_corrupt_8(unused=None):
110             shnums = range(10)
111             random.shuffle(shnums)
112             for shnum in shnums[:8]:
113                 self._corrupt_a_share(None, common._corrupt_sharedata_version_number, shnum)
114         d.addCallback(_then_corrupt_8)
115
116         before_download_reads = self._count_reads()
117         def _attempt_to_download(unused=None):
118             d2 = download_to_data(self.n)
119
120             def _callb(res):
121                 self.fail("Should have gotten an error from attempt to download, not %r" % (res,))
122             def _errb(f):
123                 self.failUnless(f.check(NotEnoughSharesError))
124             d2.addCallbacks(_callb, _errb)
125             return d2
126
127         d.addCallback(_attempt_to_download)
128
129         def _after_attempt(unused=None):
130             after_download_reads = self._count_reads()
131             #print before_download_reads, after_download_reads
132             # To pass this test, you are required to give up before reading
133             # all of the share data. Actually, we could give up sooner than
134             # 45 reads, but currently our download code does 45 reads. This
135             # test then serves as a "performance regression detector" -- if
136             # you change download code so that it takes *more* reads, then
137             # this test will fail.
138             self.failIf(after_download_reads-before_download_reads > 45,
139                         (after_download_reads, before_download_reads))
140         d.addCallback(_after_attempt)
141         return d
142
143
144 # XXX extend these tests to show bad behavior of various kinds from servers:
145 # raising exception from each remove_foo() method, for example
146
147 # XXX test disconnect DeadReferenceError from get_buckets and get_block_whatsit
148
149 # TODO: delete this whole file