/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_hashcache.py

  • Committer: Jelmer Vernooij
  • Date: 2017-12-03 15:14:22 UTC
  • mfrom: (6829.1.1 no-branch-nick)
  • Revision ID: jelmer@jelmer.uk-20171203151422-54pwtld2ae5cx11l
Merge lp:~jelmer/brz/no-branch-nick.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# (C) 2005 Canonical Ltd
2
 
 
 
1
# Copyright (C) 2005-2011, 2016 Canonical Ltd
 
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
import os
18
 
import sha
19
 
import sys
 
18
import stat
20
19
import time
21
20
 
22
 
from bzrlib.errors import BzrError
23
 
from bzrlib.hashcache import HashCache
24
 
from bzrlib.tests import TestCaseInTempDir, TestSkipped
25
 
 
26
 
 
27
 
def sha1(t):
28
 
    return sha.new(t).hexdigest()
 
21
from .. import osutils
 
22
from ..errors import BzrError
 
23
from ..hashcache import HashCache
 
24
from . import (
 
25
    TestCaseInTempDir,
 
26
    )
 
27
from .features import (
 
28
    OsFifoFeature,
 
29
    )
 
30
 
 
31
 
 
32
sha1 = osutils.sha_string
29
33
 
30
34
 
31
35
def pause():
32
 
    if False:
33
 
        return
34
 
    if sys.platform in ('win32', 'cygwin'):
35
 
        time.sleep(3)
36
 
        return
37
 
    # allow it to stabilize
38
 
    start = int(time.time())
39
 
    while int(time.time()) == start:
40
 
        time.sleep(0.2)
41
 
 
42
 
 
43
 
class FixThisError(Exception):
44
 
    pass
45
 
    
 
36
    time.sleep(5.0)
 
37
 
46
38
 
47
39
class TestHashCache(TestCaseInTempDir):
48
 
 
49
 
    def test_hashcache(self):
50
 
        """Functional tests for hashcache"""
51
 
 
 
40
    """Test the hashcache against a real directory"""
 
41
 
 
42
    def make_hashcache(self):
52
43
        # make a dummy bzr directory just to hold the cache
53
44
        os.mkdir('.bzr')
54
 
        hc = HashCache('.', '.bzr/stat-cache')
55
 
 
56
 
        file('foo', 'wb').write('hello')
57
 
        os.mkdir('subdir')
58
 
        pause()
59
 
 
60
 
        self.assertEquals(hc.get_sha1('foo'),
61
 
                          'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d')
62
 
        self.assertEquals(hc.miss_count, 1)
63
 
        self.assertEquals(hc.hit_count, 0)
64
 
 
65
 
        # check we hit without re-reading
66
 
        self.assertEquals(hc.get_sha1('foo'),
67
 
                          'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d')
68
 
        self.assertEquals(hc.miss_count, 1)
69
 
        self.assertEquals(hc.hit_count, 1)
70
 
 
71
 
        # check again without re-reading
72
 
        self.assertEquals(hc.get_sha1('foo'),
73
 
                          'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d')
74
 
        self.assertEquals(hc.miss_count, 1)
75
 
        self.assertEquals(hc.hit_count, 2)
76
 
 
77
 
        # write new file and make sure it is seen
78
 
        file('foo', 'wb').write('goodbye')
79
 
        pause()
80
 
        self.assertEquals(hc.get_sha1('foo'),
81
 
                          '3c8ec4874488f6090a157b014ce3397ca8e06d4f')
82
 
        self.assertEquals(hc.miss_count, 2)
83
 
 
84
 
        # quickly write new file of same size and make sure it is seen
85
 
        # this may rely on detection of timestamps that are too close
86
 
        # together to be safe
87
 
        file('foo', 'wb').write('g00dbye')
88
 
        self.assertEquals(hc.get_sha1('foo'),
89
 
                          sha1('g00dbye'))
90
 
 
91
 
        file('foo2', 'wb').write('other file')
92
 
        self.assertEquals(hc.get_sha1('foo2'), sha1('other file'))
93
 
 
94
 
        os.remove('foo2')
95
 
        self.assertEquals(hc.get_sha1('foo2'), None)
96
 
 
97
 
        file('foo2', 'wb').write('new content')
98
 
        self.assertEquals(hc.get_sha1('foo2'), sha1('new content'))
99
 
 
100
 
        self.assertEquals(hc.get_sha1('subdir'), None)
101
 
 
102
 
        # pause briefly to make sure they're not treated as new uncacheable
103
 
        # files
104
 
        pause()
105
 
 
106
 
        self.assertEquals(hc.get_sha1('foo'), sha1('g00dbye'))
107
 
        self.assertEquals(hc.get_sha1('foo2'), sha1('new content'))
108
 
 
109
 
        # write out, read back in and check that we don't need to
110
 
        # re-read any files
 
45
        hc = HashCache(u'.', '.bzr/stat-cache')
 
46
        return hc
 
47
 
 
48
    def reopen_hashcache(self):
 
49
        hc = HashCache(u'.', '.bzr/stat-cache')
 
50
        hc.read()
 
51
        return hc
 
52
 
 
53
    def test_hashcache_initial_miss(self):
 
54
        """Get correct hash from an empty hashcache"""
 
55
        hc = self.make_hashcache()
 
56
        self.build_tree_contents([('foo', b'hello')])
 
57
        self.assertEqual(hc.get_sha1('foo'),
 
58
                          b'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d')
 
59
        self.assertEqual(hc.miss_count, 1)
 
60
        self.assertEqual(hc.hit_count, 0)
 
61
 
 
62
    def test_hashcache_new_file(self):
 
63
        hc = self.make_hashcache()
 
64
        self.build_tree_contents([('foo', b'goodbye')])
 
65
        # now read without pausing; it may not be possible to cache it as its
 
66
        # so new
 
67
        self.assertEqual(hc.get_sha1('foo'), sha1(b'goodbye'))
 
68
 
 
69
    def test_hashcache_nonexistent_file(self):
 
70
        hc = self.make_hashcache()
 
71
        self.assertEqual(hc.get_sha1('no-name-yet'), None)
 
72
 
 
73
    def test_hashcache_replaced_file(self):
 
74
        hc = self.make_hashcache()
 
75
        self.build_tree_contents([('foo', b'goodbye')])
 
76
        self.assertEqual(hc.get_sha1('foo'), sha1(b'goodbye'))
 
77
        os.remove('foo')
 
78
        self.assertEqual(hc.get_sha1('foo'), None)
 
79
        self.build_tree_contents([('foo', b'new content')])
 
80
        self.assertEqual(hc.get_sha1('foo'), sha1(b'new content'))
 
81
 
 
82
    def test_hashcache_not_file(self):
 
83
        hc = self.make_hashcache()
 
84
        self.build_tree(['subdir/'])
 
85
        self.assertEqual(hc.get_sha1('subdir'), None)
 
86
 
 
87
    def test_hashcache_load(self):
 
88
        hc = self.make_hashcache()
 
89
        self.build_tree_contents([('foo', b'contents')])
 
90
        pause()
 
91
        self.assertEqual(hc.get_sha1('foo'), sha1(b'contents'))
111
92
        hc.write()
112
 
        del hc
113
 
 
114
 
        hc = HashCache('.', '.bzr/stat-cache')
115
 
        hc.read()
116
 
 
117
 
        self.assertEquals(len(hc._cache), 2)
118
 
        self.assertEquals(hc.get_sha1('foo'), sha1('g00dbye'))
119
 
        self.assertEquals(hc.hit_count, 1)
120
 
        self.assertEquals(hc.miss_count, 0)
121
 
        self.assertEquals(hc.get_sha1('foo2'), sha1('new content'))
 
93
        hc = self.reopen_hashcache()
 
94
        self.assertEqual(hc.get_sha1('foo'), sha1(b'contents'))
 
95
        self.assertEqual(hc.hit_count, 1)
 
96
 
 
97
    def test_hammer_hashcache(self):
 
98
        hc = self.make_hashcache()
 
99
        for i in range(10000):
 
100
            with open('foo', 'wb') as f:
 
101
                last_content = b'%08x' % i
 
102
                f.write(last_content)
 
103
            last_sha1 = sha1(last_content)
 
104
            self.log("iteration %d: %r -> %r",
 
105
                     i, last_content, last_sha1)
 
106
            got_sha1 = hc.get_sha1('foo')
 
107
            self.assertEqual(got_sha1, last_sha1)
 
108
            hc.write()
 
109
            hc = self.reopen_hashcache()
122
110
 
123
111
    def test_hashcache_raise(self):
124
112
        """check that hashcache can raise BzrError"""
125
 
 
126
 
        os.mkdir('.bzr')
127
 
        hc = HashCache('.', '.bzr/stat-cache')
128
 
        ok = False
129
 
 
130
 
        # make a best effort to create a weird kind of file
131
 
        funcs = (getattr(os, 'mkfifo', None), getattr(os, 'mknod', None))
132
 
        for func in funcs:
133
 
            if func is None:
134
 
                continue
135
 
            try:
136
 
                func('a')
137
 
                ok = True
138
 
                break
139
 
            except FixThisError:
140
 
                pass
141
 
 
142
 
        if ok:
143
 
            self.assertRaises(BzrError, hc.get_sha1, 'a')
 
113
        self.requireFeature(OsFifoFeature)
 
114
        hc = self.make_hashcache()
 
115
        os.mkfifo('a')
 
116
        # It's possible that the system supports fifos but the filesystem
 
117
        # can't.  In that case we should skip at this point.  But in fact
 
118
        # such combinations don't usually occur for the filesystem where
 
119
        # people test bzr.
 
120
        self.assertRaises(BzrError, hc.get_sha1, 'a')
 
121
 
 
122
 
 
123
class FakeHashCache(HashCache):
 
124
    """Hashcache that consults a fake clock rather than the real one.
 
125
 
 
126
    This lets us examine how old or new files would be handled, without
 
127
    actually having to wait for time to pass.
 
128
    """
 
129
    def __init__(self):
 
130
        # set root and cache file name to none to make sure we won't touch the
 
131
        # real filesystem
 
132
        HashCache.__init__(self, u'.', 'hashcache')
 
133
        self._files = {}
 
134
        # simulated clock running forward as operations happen
 
135
        self._clock = 0
 
136
 
 
137
    def put_file(self, filename, file_contents):
 
138
        abspath = './' + filename
 
139
        self._files[abspath] = (file_contents, self._clock)
 
140
 
 
141
    def _fingerprint(self, abspath, fs=None):
 
142
        entry = self._files[abspath]
 
143
        return (len(entry[0]),
 
144
                entry[1], entry[1],
 
145
                10, 20,
 
146
                stat.S_IFREG | 0o600)
 
147
 
 
148
    def _really_sha1_file(self, abspath, filters):
 
149
        if abspath in self._files:
 
150
            return sha1(self._files[abspath][0])
144
151
        else:
145
 
            raise TestSkipped('No weird file type could be created')
 
152
            return None
 
153
 
 
154
    def _cutoff_time(self):
 
155
        return self._clock - 2
 
156
 
 
157
    def pretend_to_sleep(self, secs):
 
158
        self._clock += secs
 
159
 
 
160
 
 
161
class TestHashCacheFakeFilesystem(TestCaseInTempDir):
 
162
    """Tests the hashcache using a simulated OS.
 
163
    """
 
164
 
 
165
    def make_hashcache(self):
 
166
        return FakeHashCache()
 
167
 
 
168
    def test_hashcache_miss_new_file(self):
 
169
        """A new file gives the right sha1 but misses"""
 
170
        hc = self.make_hashcache()
 
171
        hc.put_file('foo', b'hello')
 
172
        self.assertEqual(hc.get_sha1('foo'), sha1(b'hello'))
 
173
        self.assertEqual(hc.miss_count, 1)
 
174
        self.assertEqual(hc.hit_count, 0)
 
175
        # if we try again it's still too new;
 
176
        self.assertEqual(hc.get_sha1('foo'), sha1(b'hello'))
 
177
        self.assertEqual(hc.miss_count, 2)
 
178
        self.assertEqual(hc.hit_count, 0)
 
179
 
 
180
    def test_hashcache_old_file(self):
 
181
        """An old file gives the right sha1 and hits"""
 
182
        hc = self.make_hashcache()
 
183
        hc.put_file('foo', b'hello')
 
184
        hc.pretend_to_sleep(20)
 
185
        # file is new; should get the correct hash but miss
 
186
        self.assertEqual(hc.get_sha1('foo'), sha1(b'hello'))
 
187
        self.assertEqual(hc.miss_count, 1)
 
188
        self.assertEqual(hc.hit_count, 0)
 
189
        # and can now be hit
 
190
        self.assertEqual(hc.get_sha1('foo'), sha1(b'hello'))
 
191
        self.assertEqual(hc.miss_count, 1)
 
192
        self.assertEqual(hc.hit_count, 1)
 
193
        hc.pretend_to_sleep(3)
 
194
        # and again
 
195
        self.assertEqual(hc.get_sha1('foo'), sha1(b'hello'))
 
196
        self.assertEqual(hc.miss_count, 1)
 
197
        self.assertEqual(hc.hit_count, 2)
 
198
 
 
199
    def test_hashcache_invalidates(self):
 
200
        hc = self.make_hashcache()
 
201
        hc.put_file('foo', b'hello')
 
202
        hc.pretend_to_sleep(20)
 
203
        hc.get_sha1('foo')
 
204
        hc.put_file('foo', b'h1llo')
 
205
        self.assertEqual(hc.get_sha1('foo'), sha1(b'h1llo'))
 
206
        self.assertEqual(hc.miss_count, 2)
 
207
        self.assertEqual(hc.hit_count, 0)