~bzr-pqm/bzr/bzr.dev

1845.1.2 by mbp at sourcefrog
Use larger time window on hashcache to be safe with fractional times
1
# Copyright (C) 2005, 2006 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
846 by Martin Pool
- start adding refactored/simplified hash cache
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
846 by Martin Pool
- start adding refactored/simplified hash cache
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
846 by Martin Pool
- start adding refactored/simplified hash cache
13
# You should have received a copy of the GNU General Public License
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
16
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
17
import os
1534.4.51 by Robert Collins
Test the disk layout of format3 working trees.
18
import sha
1845.1.3 by Martin Pool
Improvements to hashcache testing:
19
import stat
1185.38.8 by John Arbash Meinel
[patch] testhashcache delay for win32 and cygwin
20
import sys
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
21
import time
1534.4.51 by Robert Collins
Test the disk layout of format3 working trees.
22
23
from bzrlib.errors import BzrError
24
from bzrlib.hashcache import HashCache
1845.1.3 by Martin Pool
Improvements to hashcache testing:
25
from bzrlib.tests import TestCaseInTempDir, TestSkipped, TestCase
846 by Martin Pool
- start adding refactored/simplified hash cache
26
27
28
def sha1(t):
29
    return sha.new(t).hexdigest()
30
31
32
def pause():
1845.1.2 by mbp at sourcefrog
Use larger time window on hashcache to be safe with fractional times
33
    time.sleep(5.0)
1185.33.109 by Denys Duchier
test_hashcache: catch no exceptions
34
35
1141 by Martin Pool
- rename FunctionalTest to TestCaseInTempDir
36
class TestHashCache(TestCaseInTempDir):
1845.1.3 by Martin Pool
Improvements to hashcache testing:
37
    """Test the hashcache against a real directory"""
1102 by Martin Pool
- merge test refactoring from robertc
38
1845.1.1 by mbp at sourcefrog
Refactor and improve hashcache tests
39
    def make_hashcache(self):
866 by Martin Pool
- use new path-based hashcache for WorkingTree- squash mtime/ctime to whole seconds- update and if necessary write out hashcache when WorkingTree object is created.
40
        # make a dummy bzr directory just to hold the cache
41
        os.mkdir('.bzr')
1534.4.51 by Robert Collins
Test the disk layout of format3 working trees.
42
        hc = HashCache('.', '.bzr/stat-cache')
1845.1.1 by mbp at sourcefrog
Refactor and improve hashcache tests
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')])
54
        self.assertEquals(hc.get_sha1('foo'),
55
                          'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d')
56
        self.assertEquals(hc.miss_count, 1)
57
        self.assertEquals(hc.hit_count, 0)
58
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/'])
846 by Martin Pool
- start adding refactored/simplified hash cache
82
        self.assertEquals(hc.get_sha1('subdir'), None)
848 by Martin Pool
doc
83
1845.1.1 by mbp at sourcefrog
Refactor and improve hashcache tests
84
    def test_hashcache_load(self):
85
        hc = self.make_hashcache()
86
        self.build_tree_contents([('foo', 'contents')])
861 by Martin Pool
- more hash-cache tests
87
        pause()
1845.1.1 by mbp at sourcefrog
Refactor and improve hashcache tests
88
        self.assertEquals(hc.get_sha1('foo'), sha1('contents'))
866 by Martin Pool
- use new path-based hashcache for WorkingTree- squash mtime/ctime to whole seconds- update and if necessary write out hashcache when WorkingTree object is created.
89
        hc.write()
1845.1.1 by mbp at sourcefrog
Refactor and improve hashcache tests
90
        hc = self.reopen_hashcache()
91
        self.assertEquals(hc.get_sha1('foo'), sha1('contents'))
1185.60.5 by Aaron Bentley
Reactivated hashcache tests
92
        self.assertEquals(hc.hit_count, 1)
1845.1.1 by mbp at sourcefrog
Refactor and improve hashcache tests
93
94
    def test_hammer_hashcache(self):
95
        hc = self.make_hashcache()
1845.1.2 by mbp at sourcefrog
Use larger time window on hashcache to be safe with fractional times
96
        for i in xrange(10000):
97
            self.log('start writing at %s', time.time())
1845.1.1 by mbp at sourcefrog
Refactor and improve hashcache tests
98
            f = file('foo', 'w')
99
            try:
100
                last_content = '%08x' % i
101
                f.write(last_content)
102
            finally:
103
                f.close()
1845.1.2 by mbp at sourcefrog
Use larger time window on hashcache to be safe with fractional times
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)
1845.1.1 by mbp at sourcefrog
Refactor and improve hashcache tests
109
            hc.write()
110
            hc = self.reopen_hashcache()
1185.33.106 by Denys Duchier
check that hashcache can raise BzrError
111
112
    def test_hashcache_raise(self):
113
        """check that hashcache can raise BzrError"""
1845.1.1 by mbp at sourcefrog
Refactor and improve hashcache tests
114
        hc = self.make_hashcache()
1845.1.2 by mbp at sourcefrog
Use larger time window on hashcache to be safe with fractional times
115
        if not hasattr(os, 'mkfifo'):
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')
1845.1.3 by Martin Pool
Improvements to hashcache testing:
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):
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])
153
        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)