##// END OF EJS Templates
filecache: use bytes wherever possible in the tests...
marmoute -
r52996:97840154 default
parent child Browse files
Show More
@@ -1,282 +1,282
1 import os
1 import os
2 import stat
2 import stat
3 import subprocess
3 import subprocess
4 import sys
4 import sys
5
5
6 if subprocess.call(
6 if subprocess.call(
7 [sys.executable, '%s/hghave' % os.environ['TESTDIR'], 'cacheable']
7 [sys.executable, '%s/hghave' % os.environ['TESTDIR'], 'cacheable']
8 ):
8 ):
9 sys.exit(80)
9 sys.exit(80)
10
10
11 print_ = print
11 print_ = print
12
12
13
13
14 def print(*args, **kwargs):
14 def print(*args, **kwargs):
15 """print() wrapper that flushes stdout buffers to avoid py3 buffer issues
15 """print() wrapper that flushes stdout buffers to avoid py3 buffer issues
16
16
17 We could also just write directly to sys.stdout.buffer the way the
17 We could also just write directly to sys.stdout.buffer the way the
18 ui object will, but this was easier for porting the test.
18 ui object will, but this was easier for porting the test.
19 """
19 """
20 print_(*args, **kwargs)
20 print_(*args, **kwargs)
21 sys.stdout.flush()
21 sys.stdout.flush()
22
22
23
23
24 from mercurial import (
24 from mercurial import (
25 extensions,
25 extensions,
26 hg,
26 hg,
27 localrepo,
27 localrepo,
28 pycompat,
28 pycompat,
29 ui as uimod,
29 ui as uimod,
30 util,
30 util,
31 vfs as vfsmod,
31 vfs as vfsmod,
32 )
32 )
33
33
34
34
35 class fakerepo:
35 class fakerepo:
36 def __init__(self):
36 def __init__(self):
37 self._filecache = {}
37 self._filecache = {}
38
38
39 class fakevfs:
39 class fakevfs:
40 def join(self, p):
40 def join(self, p):
41 return p
41 return p
42
42
43 vfs = fakevfs()
43 vfs = fakevfs()
44
44
45 def unfiltered(self):
45 def unfiltered(self):
46 return self
46 return self
47
47
48 def sjoin(self, p):
48 def sjoin(self, p):
49 return p
49 return p
50
50
51 @localrepo.repofilecache(b'x', b'y')
51 @localrepo.repofilecache(b'x', b'y')
52 def cached(self):
52 def cached(self):
53 print('creating')
53 print('creating')
54 return 'string from function'
54 return 'string from function'
55
55
56 def invalidate(self):
56 def invalidate(self):
57 for k in self._filecache:
57 for k in self._filecache:
58 try:
58 try:
59 delattr(self, pycompat.sysstr(k))
59 delattr(self, pycompat.sysstr(k))
60 except AttributeError:
60 except AttributeError:
61 pass
61 pass
62
62
63
63
64 def basic(repo):
64 def basic(repo):
65 print("* neither file exists")
65 print("* neither file exists")
66 # calls function
66 # calls function
67 repo.cached
67 repo.cached
68
68
69 repo.invalidate()
69 repo.invalidate()
70 print("* neither file still exists")
70 print("* neither file still exists")
71 # uses cache
71 # uses cache
72 repo.cached
72 repo.cached
73
73
74 # create empty file
74 # create empty file
75 f = open('x', 'w')
75 f = open('x', 'wb')
76 f.close()
76 f.close()
77 repo.invalidate()
77 repo.invalidate()
78 print("* empty file x created")
78 print("* empty file x created")
79 # should recreate the object
79 # should recreate the object
80 repo.cached
80 repo.cached
81
81
82 f = open('x', 'w')
82 f = open('x', 'wb')
83 f.write('a')
83 f.write(b'a')
84 f.close()
84 f.close()
85 repo.invalidate()
85 repo.invalidate()
86 print("* file x changed size")
86 print("* file x changed size")
87 # should recreate the object
87 # should recreate the object
88 repo.cached
88 repo.cached
89
89
90 repo.invalidate()
90 repo.invalidate()
91 print("* nothing changed with either file")
91 print("* nothing changed with either file")
92 # stats file again, reuses object
92 # stats file again, reuses object
93 repo.cached
93 repo.cached
94
94
95 # atomic replace file, size doesn't change
95 # atomic replace file, size doesn't change
96 # hopefully st_mtime doesn't change as well so this doesn't use the cache
96 # hopefully st_mtime doesn't change as well so this doesn't use the cache
97 # because of inode change
97 # because of inode change
98 f = vfsmod.vfs(b'.')(b'x', b'w', atomictemp=True)
98 f = vfsmod.vfs(b'.')(b'x', b'w', atomictemp=True)
99 f.write(b'b')
99 f.write(b'b')
100 f.close()
100 f.close()
101
101
102 repo.invalidate()
102 repo.invalidate()
103 print("* file x changed inode")
103 print("* file x changed inode")
104 repo.cached
104 repo.cached
105
105
106 # create empty file y
106 # create empty file y
107 f = open('y', 'w')
107 f = open('y', 'wb')
108 f.close()
108 f.close()
109 repo.invalidate()
109 repo.invalidate()
110 print("* empty file y created")
110 print("* empty file y created")
111 # should recreate the object
111 # should recreate the object
112 repo.cached
112 repo.cached
113
113
114 f = open('y', 'w')
114 f = open('y', 'wb')
115 f.write('A')
115 f.write(b'A')
116 f.close()
116 f.close()
117 repo.invalidate()
117 repo.invalidate()
118 print("* file y changed size")
118 print("* file y changed size")
119 # should recreate the object
119 # should recreate the object
120 repo.cached
120 repo.cached
121
121
122 f = vfsmod.vfs(b'.')(b'y', b'w', atomictemp=True)
122 f = vfsmod.vfs(b'.')(b'y', b'w', atomictemp=True)
123 f.write(b'B')
123 f.write(b'B')
124 f.close()
124 f.close()
125
125
126 repo.invalidate()
126 repo.invalidate()
127 print("* file y changed inode")
127 print("* file y changed inode")
128 repo.cached
128 repo.cached
129
129
130 f = vfsmod.vfs(b'.')(b'x', b'w', atomictemp=True)
130 f = vfsmod.vfs(b'.')(b'x', b'w', atomictemp=True)
131 f.write(b'c')
131 f.write(b'c')
132 f.close()
132 f.close()
133 f = vfsmod.vfs(b'.')(b'y', b'w', atomictemp=True)
133 f = vfsmod.vfs(b'.')(b'y', b'w', atomictemp=True)
134 f.write(b'C')
134 f.write(b'C')
135 f.close()
135 f.close()
136
136
137 repo.invalidate()
137 repo.invalidate()
138 print("* both files changed inode")
138 print("* both files changed inode")
139 repo.cached
139 repo.cached
140
140
141
141
142 def fakeuncacheable():
142 def fakeuncacheable():
143 def wrapcacheable(orig, *args, **kwargs):
143 def wrapcacheable(orig, *args, **kwargs):
144 return False
144 return False
145
145
146 def wrapinit(orig, *args, **kwargs):
146 def wrapinit(orig, *args, **kwargs):
147 pass
147 pass
148
148
149 originit = extensions.wrapfunction(util.cachestat, '__init__', wrapinit)
149 originit = extensions.wrapfunction(util.cachestat, '__init__', wrapinit)
150 origcacheable = extensions.wrapfunction(
150 origcacheable = extensions.wrapfunction(
151 util.cachestat, 'cacheable', wrapcacheable
151 util.cachestat, 'cacheable', wrapcacheable
152 )
152 )
153
153
154 for fn in ['x', 'y']:
154 for fn in [b'x', b'y']:
155 try:
155 try:
156 os.remove(fn)
156 os.remove(fn)
157 except OSError:
157 except OSError:
158 pass
158 pass
159
159
160 basic(fakerepo())
160 basic(fakerepo())
161
161
162 util.cachestat.cacheable = origcacheable
162 util.cachestat.cacheable = origcacheable
163 util.cachestat.__init__ = originit
163 util.cachestat.__init__ = originit
164
164
165
165
166 def test_filecache_synced():
166 def test_filecache_synced():
167 # test old behavior that caused filecached properties to go out of sync
167 # test old behavior that caused filecached properties to go out of sync
168 os.system('hg init && echo a >> a && hg add a && hg ci -qm.')
168 os.system('hg init && echo a >> a && hg add a && hg ci -qm.')
169 repo = hg.repository(uimod.ui.load())
169 repo = hg.repository(uimod.ui.load())
170 # first rollback clears the filecache, but changelog to stays in __dict__
170 # first rollback clears the filecache, but changelog to stays in __dict__
171 repo.rollback()
171 repo.rollback()
172 repo.commit(b'.')
172 repo.commit(b'.')
173 # second rollback comes along and touches the changelog externally
173 # second rollback comes along and touches the changelog externally
174 # (file is moved)
174 # (file is moved)
175 repo.rollback()
175 repo.rollback()
176 # but since changelog isn't under the filecache control anymore, we don't
176 # but since changelog isn't under the filecache control anymore, we don't
177 # see that it changed, and return the old changelog without reconstructing
177 # see that it changed, and return the old changelog without reconstructing
178 # it
178 # it
179 repo.commit(b'.')
179 repo.commit(b'.')
180
180
181
181
182 def setbeforeget(repo):
182 def setbeforeget(repo):
183 os.remove('x')
183 os.remove(b'x')
184 os.remove('y')
184 os.remove(b'y')
185 repo.__class__.cached.set(repo, 'string set externally')
185 repo.__class__.cached.set(repo, 'string set externally')
186 repo.invalidate()
186 repo.invalidate()
187 print("* neither file exists")
187 print("* neither file exists")
188 print(repo.cached)
188 print(repo.cached)
189 repo.invalidate()
189 repo.invalidate()
190 f = open('x', 'w')
190 f = open('x', 'wb')
191 f.write('a')
191 f.write(b'a')
192 f.close()
192 f.close()
193 print("* file x created")
193 print("* file x created")
194 print(repo.cached)
194 print(repo.cached)
195
195
196 repo.__class__.cached.set(repo, 'string 2 set externally')
196 repo.__class__.cached.set(repo, 'string 2 set externally')
197 repo.invalidate()
197 repo.invalidate()
198 print("* string set externally again")
198 print("* string set externally again")
199 print(repo.cached)
199 print(repo.cached)
200
200
201 repo.invalidate()
201 repo.invalidate()
202 f = open('y', 'w')
202 f = open('y', 'wb')
203 f.write('b')
203 f.write(b'b')
204 f.close()
204 f.close()
205 print("* file y created")
205 print("* file y created")
206 print(repo.cached)
206 print(repo.cached)
207
207
208
208
209 def antiambiguity():
209 def antiambiguity():
210 filename = 'ambigcheck'
210 filename = 'ambigcheck'
211
211
212 # try some times, because reproduction of ambiguity depends on
212 # try some times, because reproduction of ambiguity depends on
213 # "filesystem time"
213 # "filesystem time"
214 for i in range(5):
214 for i in range(5):
215 fp = open(filename, 'w')
215 fp = open(filename, 'wb')
216 fp.write('FOO')
216 fp.write(b'FOO')
217 fp.close()
217 fp.close()
218
218
219 oldstat = os.stat(filename)
219 oldstat = os.stat(filename)
220 if oldstat[stat.ST_CTIME] != oldstat[stat.ST_MTIME]:
220 if oldstat[stat.ST_CTIME] != oldstat[stat.ST_MTIME]:
221 # subsequent changing never causes ambiguity
221 # subsequent changing never causes ambiguity
222 continue
222 continue
223
223
224 repetition = 3
224 repetition = 3
225
225
226 # repeat changing via checkambigatclosing, to examine whether
226 # repeat changing via checkambigatclosing, to examine whether
227 # st_mtime is advanced multiple times as expected
227 # st_mtime is advanced multiple times as expected
228 for i in range(repetition):
228 for i in range(repetition):
229 # explicit closing
229 # explicit closing
230 fp = vfsmod.checkambigatclosing(open(filename, 'a'))
230 fp = vfsmod.checkambigatclosing(open(filename, 'ab'))
231 fp.write('FOO')
231 fp.write(b'FOO')
232 fp.close()
232 fp.close()
233
233
234 # implicit closing by "with" statement
234 # implicit closing by "with" statement
235 with vfsmod.checkambigatclosing(open(filename, 'a')) as fp:
235 with vfsmod.checkambigatclosing(open(filename, 'ab')) as fp:
236 fp.write('BAR')
236 fp.write(b'BAR')
237
237
238 newstat = os.stat(filename)
238 newstat = os.stat(filename)
239 if oldstat[stat.ST_CTIME] != newstat[stat.ST_CTIME]:
239 if oldstat[stat.ST_CTIME] != newstat[stat.ST_CTIME]:
240 # timestamp ambiguity was naturally avoided while repetition
240 # timestamp ambiguity was naturally avoided while repetition
241 continue
241 continue
242
242
243 # st_mtime should be advanced "repetition * 2" times, because
243 # st_mtime should be advanced "repetition * 2" times, because
244 # all changes occurred at same time (in sec)
244 # all changes occurred at same time (in sec)
245 expected = (oldstat[stat.ST_MTIME] + repetition * 2) & 0x7FFFFFFF
245 expected = (oldstat[stat.ST_MTIME] + repetition * 2) & 0x7FFFFFFF
246 if newstat[stat.ST_MTIME] != expected:
246 if newstat[stat.ST_MTIME] != expected:
247 print(
247 print(
248 "'newstat[stat.ST_MTIME] %s is not %s (as %s + %s * 2)"
248 "'newstat[stat.ST_MTIME] %s is not %s (as %s + %s * 2)"
249 % (
249 % (
250 newstat[stat.ST_MTIME],
250 newstat[stat.ST_MTIME],
251 expected,
251 expected,
252 oldstat[stat.ST_MTIME],
252 oldstat[stat.ST_MTIME],
253 repetition,
253 repetition,
254 )
254 )
255 )
255 )
256
256
257 # no more examination is needed regardless of result
257 # no more examination is needed regardless of result
258 break
258 break
259 else:
259 else:
260 # This platform seems too slow to examine anti-ambiguity
260 # This platform seems too slow to examine anti-ambiguity
261 # of file timestamp (or test happened to be executed at
261 # of file timestamp (or test happened to be executed at
262 # bad timing). Exit silently in this case, because running
262 # bad timing). Exit silently in this case, because running
263 # on other faster platforms can detect problems
263 # on other faster platforms can detect problems
264 pass
264 pass
265
265
266
266
267 print('basic:')
267 print('basic:')
268 print()
268 print()
269 basic(fakerepo())
269 basic(fakerepo())
270 print()
270 print()
271 print('fakeuncacheable:')
271 print('fakeuncacheable:')
272 print()
272 print()
273 fakeuncacheable()
273 fakeuncacheable()
274 test_filecache_synced()
274 test_filecache_synced()
275 print()
275 print()
276 print('setbeforeget:')
276 print('setbeforeget:')
277 print()
277 print()
278 setbeforeget(fakerepo())
278 setbeforeget(fakerepo())
279 print()
279 print()
280 print('antiambiguity:')
280 print('antiambiguity:')
281 print()
281 print()
282 antiambiguity()
282 antiambiguity()
General Comments 0
You need to be logged in to leave comments. Login now