~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/testhashcache.py

Refactored the export code to make it easier to add new export formats.

Show diffs side-by-side

added added

removed removed

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