~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_hashcache.py

  • Committer: Martin Pool
  • Date: 2006-03-21 12:26:54 UTC
  • mto: This revision was merged to the branch mainline in revision 1621.
  • Revision ID: mbp@sourcefrog.net-20060321122654-514047ed65795a17
New developer commands 'weave-list' and 'weave-join'.

Show diffs side-by-side

added added

removed removed

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