1
# Copyright (C) 2005-2009, 2011 Canonical Ltd
1
# (C) 2005 Canonical Ltd
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.
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.
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
19
from bzrlib.selftest import TestCaseInTempDir
21
from bzrlib import osutils
22
from bzrlib.errors import BzrError
23
from bzrlib.hashcache import HashCache
24
from bzrlib.tests import (
31
return osutils.sha(t).hexdigest()
25
return sha.new(t).hexdigest()
34
# allow it to stabilize
35
start = int(time.time())
36
while int(time.time()) == start:
38
40
class TestHashCache(TestCaseInTempDir):
39
"""Test the hashcache against a real directory"""
41
def make_hashcache(self):
42
def test_hashcache(self):
43
"""Functional tests for hashcache"""
44
from bzrlib.hashcache import HashCache
42
47
# make a dummy bzr directory just to hold the cache
44
hc = HashCache('.', '.bzr/stat-cache')
47
def reopen_hashcache(self):
48
hc = HashCache('.', '.bzr/stat-cache')
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')])
51
file('foo', 'wb').write('hello')
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)
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
66
self.assertEquals(hc.get_sha1('foo'), sha1('goodbye'))
68
def test_hashcache_nonexistent_file(self):
69
hc = self.make_hashcache()
70
self.assertEquals(hc.get_sha1('no-name-yet'), None)
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'))
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'))
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)
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)
72
# write new file and make sure it is seen
73
file('foo', 'wb').write('goodbye')
75
self.assertEquals(hc.get_sha1('foo'),
76
'3c8ec4874488f6090a157b014ce3397ca8e06d4f')
77
##self.assertEquals(hc.miss_count, 2)
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
82
file('foo', 'wb').write('g00dbye')
83
self.assertEquals(hc.get_sha1('foo'),
86
file('foo2', 'wb').write('other file')
87
self.assertEquals(hc.get_sha1('foo2'), sha1('other file'))
90
self.assertEquals(hc.get_sha1('foo2'), None)
92
file('foo2', 'wb').write('new content')
93
self.assertEquals(hc.get_sha1('foo2'), sha1('new content'))
84
95
self.assertEquals(hc.get_sha1('subdir'), None)
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
90
self.assertEquals(hc.get_sha1('foo'), sha1('contents'))
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)
107
# write out, read back in and check that we don't need to
92
hc = self.reopen_hashcache()
93
self.assertEquals(hc.get_sha1('foo'), sha1('contents'))
94
self.assertEquals(hc.hit_count, 1)
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())
102
last_content = '%08x' % i
103
f.write(last_content)
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)
112
hc = self.reopen_hashcache()
114
def test_hashcache_raise(self):
115
"""check that hashcache can raise BzrError"""
116
self.requireFeature(OsFifoFeature)
117
hc = self.make_hashcache()
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
123
self.assertRaises(BzrError, hc.get_sha1, 'a')
126
class FakeHashCache(HashCache):
127
"""Hashcache that consults a fake clock rather than the real one.
129
This lets us examine how old or new files would be handled, without
130
actually having to wait for time to pass.
133
# set root and cache file name to none to make sure we won't touch the
135
HashCache.__init__(self, '.', 'hashcache')
137
# simulated clock running forward as operations happen
140
def put_file(self, filename, file_contents):
141
abspath = './' + filename
142
self._files[abspath] = (file_contents, self._clock)
144
def _fingerprint(self, abspath, fs=None):
145
entry = self._files[abspath]
146
return (len(entry[0]),
151
def _really_sha1_file(self, abspath, filters):
152
if abspath in self._files:
153
return sha1(self._files[abspath][0])
157
def _cutoff_time(self):
158
return self._clock - 2
160
def pretend_to_sleep(self, secs):
164
class TestHashCacheFakeFilesystem(TestCaseInTempDir):
165
"""Tests the hashcache using a simulated OS.
168
def make_hashcache(self):
169
return FakeHashCache()
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)
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)
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)
198
self.assertEquals(hc.get_sha1('foo'), sha1('hello'))
199
self.assertEquals(hc.miss_count, 1)
200
self.assertEquals(hc.hit_count, 2)
202
def test_hashcache_invalidates(self):
203
hc = self.make_hashcache()
204
hc.put_file('foo', 'hello')
205
hc.pretend_to_sleep(20)
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)
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'))