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