1 | """ |
---|
2 | Ported to Python 3. |
---|
3 | """ |
---|
4 | |
---|
5 | from ..common import AsyncTestCase |
---|
6 | from testtools.matchers import Equals, HasLength |
---|
7 | from allmydata.monitor import Monitor |
---|
8 | from allmydata.mutable.common import MODE_CHECK, MODE_READ |
---|
9 | from .util import PublishMixin, CheckerMixin |
---|
10 | |
---|
11 | |
---|
12 | class MultipleVersions(AsyncTestCase, PublishMixin, CheckerMixin): |
---|
13 | |
---|
14 | def setUp(self): |
---|
15 | super(MultipleVersions, self).setUp() |
---|
16 | return self.publish_multiple() |
---|
17 | |
---|
18 | def test_multiple_versions(self): |
---|
19 | # if we see a mix of versions in the grid, download_best_version |
---|
20 | # should get the latest one |
---|
21 | self._set_versions(dict([(i,2) for i in (0,2,4,6,8)])) |
---|
22 | d = self._fn.download_best_version() |
---|
23 | d.addCallback(lambda res: self.assertThat(res, Equals(self.CONTENTS[4]))) |
---|
24 | # and the checker should report problems |
---|
25 | d.addCallback(lambda res: self._fn.check(Monitor())) |
---|
26 | d.addCallback(self.check_bad, "test_multiple_versions") |
---|
27 | |
---|
28 | # but if everything is at version 2, that's what we should download |
---|
29 | d.addCallback(lambda res: |
---|
30 | self._set_versions(dict([(i,2) for i in range(10)]))) |
---|
31 | d.addCallback(lambda res: self._fn.download_best_version()) |
---|
32 | d.addCallback(lambda res: self.assertThat(res, Equals(self.CONTENTS[2]))) |
---|
33 | # if exactly one share is at version 3, we should still get v2 |
---|
34 | d.addCallback(lambda res: |
---|
35 | self._set_versions({0:3})) |
---|
36 | d.addCallback(lambda res: self._fn.download_best_version()) |
---|
37 | d.addCallback(lambda res: self.assertThat(res, Equals(self.CONTENTS[2]))) |
---|
38 | # but the servermap should see the unrecoverable version. This |
---|
39 | # depends upon the single newer share being queried early. |
---|
40 | d.addCallback(lambda res: self._fn.get_servermap(MODE_READ)) |
---|
41 | def _check_smap(smap): |
---|
42 | self.assertThat(smap.unrecoverable_versions(), HasLength(1)) |
---|
43 | newer = smap.unrecoverable_newer_versions() |
---|
44 | self.assertThat(newer, HasLength(1)) |
---|
45 | verinfo, health = list(newer.items())[0] |
---|
46 | self.assertThat(verinfo[0], Equals(4)) |
---|
47 | self.assertThat(health, Equals((1,3))) |
---|
48 | self.assertThat(smap.needs_merge(), Equals(False)) |
---|
49 | d.addCallback(_check_smap) |
---|
50 | # if we have a mix of two parallel versions (s4a and s4b), we could |
---|
51 | # recover either |
---|
52 | d.addCallback(lambda res: |
---|
53 | self._set_versions({0:3,2:3,4:3,6:3,8:3, |
---|
54 | 1:4,3:4,5:4,7:4,9:4})) |
---|
55 | d.addCallback(lambda res: self._fn.get_servermap(MODE_READ)) |
---|
56 | def _check_smap_mixed(smap): |
---|
57 | self.assertThat(smap.unrecoverable_versions(), HasLength(0)) |
---|
58 | newer = smap.unrecoverable_newer_versions() |
---|
59 | self.assertThat(newer, HasLength(0)) |
---|
60 | self.assertTrue(smap.needs_merge()) |
---|
61 | d.addCallback(_check_smap_mixed) |
---|
62 | d.addCallback(lambda res: self._fn.download_best_version()) |
---|
63 | d.addCallback(lambda res: self.assertTrue(res == self.CONTENTS[3] or |
---|
64 | res == self.CONTENTS[4])) |
---|
65 | return d |
---|
66 | |
---|
67 | def test_replace(self): |
---|
68 | # if we see a mix of versions in the grid, we should be able to |
---|
69 | # replace them all with a newer version |
---|
70 | |
---|
71 | # if exactly one share is at version 3, we should download (and |
---|
72 | # replace) v2, and the result should be v4. Note that the index we |
---|
73 | # give to _set_versions is different than the sequence number. |
---|
74 | target = dict([(i,2) for i in range(10)]) # seqnum3 |
---|
75 | target[0] = 3 # seqnum4 |
---|
76 | self._set_versions(target) |
---|
77 | |
---|
78 | def _modify(oldversion, servermap, first_time): |
---|
79 | return oldversion + b" modified" |
---|
80 | d = self._fn.modify(_modify) |
---|
81 | d.addCallback(lambda res: self._fn.download_best_version()) |
---|
82 | expected = self.CONTENTS[2] + b" modified" |
---|
83 | d.addCallback(lambda res: self.assertThat(res, Equals(expected))) |
---|
84 | # and the servermap should indicate that the outlier was replaced too |
---|
85 | d.addCallback(lambda res: self._fn.get_servermap(MODE_CHECK)) |
---|
86 | def _check_smap(smap): |
---|
87 | self.assertThat(smap.highest_seqnum(), Equals(5)) |
---|
88 | self.assertThat(smap.unrecoverable_versions(), HasLength(0)) |
---|
89 | self.assertThat(smap.recoverable_versions(), HasLength(1)) |
---|
90 | d.addCallback(_check_smap) |
---|
91 | return d |
---|