test-filecache.py
282 lines
| 6.8 KiB
| text/x-python
|
PythonLexer
/ tests / test-filecache.py
Robert Stanca
|
r28741 | import os | ||
Augie Fackler
|
r36799 | import stat | ||
Robert Stanca
|
r28741 | import subprocess | ||
import sys | ||||
Idan Kamara
|
r14928 | |||
Augie Fackler
|
r43346 | if subprocess.call( | ||
Mathias De Mare
|
r46438 | [sys.executable, '%s/hghave' % os.environ['TESTDIR'], 'cacheable'] | ||
Augie Fackler
|
r43346 | ): | ||
Idan Kamara
|
r14928 | sys.exit(80) | ||
Augie Fackler
|
r37917 | print_ = print | ||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r37917 | def print(*args, **kwargs): | ||
"""print() wrapper that flushes stdout buffers to avoid py3 buffer issues | ||||
We could also just write directly to sys.stdout.buffer the way the | ||||
ui object will, but this was easier for porting the test. | ||||
""" | ||||
print_(*args, **kwargs) | ||||
sys.stdout.flush() | ||||
Augie Fackler
|
r43346 | |||
Yuya Nishihara
|
r28802 | from mercurial import ( | ||
extensions, | ||||
hg, | ||||
Pierre-Yves David
|
r31284 | localrepo, | ||
Pulkit Goyal
|
r36300 | pycompat, | ||
Yuya Nishihara
|
r28803 | ui as uimod, | ||
Yuya Nishihara
|
r28802 | util, | ||
Pierre-Yves David
|
r31251 | vfs as vfsmod, | ||
Yuya Nishihara
|
r28802 | ) | ||
Idan Kamara
|
r14928 | |||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r49801 | class fakerepo: | ||
Idan Kamara
|
r14928 | def __init__(self): | ||
self._filecache = {} | ||||
Gregory Szorc
|
r49801 | class fakevfs: | ||
Pierre-Yves David
|
r31284 | def join(self, p): | ||
return p | ||||
vfs = fakevfs() | ||||
def unfiltered(self): | ||||
return self | ||||
Idan Kamara
|
r14928 | |||
def sjoin(self, p): | ||||
return p | ||||
r52994 | @localrepo.repofilecache(b'x', b'y') | |||
Idan Kamara
|
r14928 | def cached(self): | ||
Robert Stanca
|
r28742 | print('creating') | ||
Siddharth Agarwal
|
r20040 | return 'string from function' | ||
Idan Kamara
|
r14928 | |||
def invalidate(self): | ||||
for k in self._filecache: | ||||
try: | ||||
Augie Fackler
|
r37917 | delattr(self, pycompat.sysstr(k)) | ||
Idan Kamara
|
r14928 | except AttributeError: | ||
pass | ||||
Augie Fackler
|
r43346 | |||
Idan Kamara
|
r14928 | def basic(repo): | ||
Robert Stanca
|
r28742 | print("* neither file exists") | ||
Siddharth Agarwal
|
r20041 | # calls function | ||
Idan Kamara
|
r14928 | repo.cached | ||
repo.invalidate() | ||||
Robert Stanca
|
r28742 | print("* neither file still exists") | ||
Siddharth Agarwal
|
r20041 | # uses cache | ||
Idan Kamara
|
r14928 | repo.cached | ||
# create empty file | ||||
r52996 | f = open('x', 'wb') | |||
Idan Kamara
|
r14928 | f.close() | ||
repo.invalidate() | ||||
Robert Stanca
|
r28742 | print("* empty file x created") | ||
Idan Kamara
|
r14928 | # should recreate the object | ||
repo.cached | ||||
r52996 | f = open('x', 'wb') | |||
f.write(b'a') | ||||
Idan Kamara
|
r14928 | f.close() | ||
repo.invalidate() | ||||
Robert Stanca
|
r28742 | print("* file x changed size") | ||
Idan Kamara
|
r14928 | # should recreate the object | ||
repo.cached | ||||
repo.invalidate() | ||||
Robert Stanca
|
r28742 | print("* nothing changed with either file") | ||
Siddharth Agarwal
|
r20041 | # stats file again, reuses object | ||
Idan Kamara
|
r14928 | repo.cached | ||
# atomic replace file, size doesn't change | ||||
# hopefully st_mtime doesn't change as well so this doesn't use the cache | ||||
# because of inode change | ||||
Augie Fackler
|
r37917 | f = vfsmod.vfs(b'.')(b'x', b'w', atomictemp=True) | ||
f.write(b'b') | ||||
Greg Ward
|
r15057 | f.close() | ||
Idan Kamara
|
r14928 | |||
repo.invalidate() | ||||
Robert Stanca
|
r28742 | print("* file x changed inode") | ||
Idan Kamara
|
r14928 | repo.cached | ||
Siddharth Agarwal
|
r20045 | # create empty file y | ||
r52996 | f = open('y', 'wb') | |||
Siddharth Agarwal
|
r20045 | f.close() | ||
repo.invalidate() | ||||
Robert Stanca
|
r28742 | print("* empty file y created") | ||
Siddharth Agarwal
|
r20045 | # should recreate the object | ||
repo.cached | ||||
r52996 | f = open('y', 'wb') | |||
f.write(b'A') | ||||
Siddharth Agarwal
|
r20045 | f.close() | ||
repo.invalidate() | ||||
Robert Stanca
|
r28742 | print("* file y changed size") | ||
Siddharth Agarwal
|
r20045 | # should recreate the object | ||
repo.cached | ||||
Augie Fackler
|
r37917 | f = vfsmod.vfs(b'.')(b'y', b'w', atomictemp=True) | ||
f.write(b'B') | ||||
Siddharth Agarwal
|
r20045 | f.close() | ||
repo.invalidate() | ||||
Robert Stanca
|
r28742 | print("* file y changed inode") | ||
Siddharth Agarwal
|
r20045 | repo.cached | ||
Augie Fackler
|
r37917 | f = vfsmod.vfs(b'.')(b'x', b'w', atomictemp=True) | ||
f.write(b'c') | ||||
Siddharth Agarwal
|
r20045 | f.close() | ||
Augie Fackler
|
r37917 | f = vfsmod.vfs(b'.')(b'y', b'w', atomictemp=True) | ||
f.write(b'C') | ||||
Siddharth Agarwal
|
r20045 | f.close() | ||
repo.invalidate() | ||||
Robert Stanca
|
r28742 | print("* both files changed inode") | ||
Siddharth Agarwal
|
r20045 | repo.cached | ||
Augie Fackler
|
r43346 | |||
Idan Kamara
|
r14928 | def fakeuncacheable(): | ||
def wrapcacheable(orig, *args, **kwargs): | ||||
return False | ||||
def wrapinit(orig, *args, **kwargs): | ||||
pass | ||||
originit = extensions.wrapfunction(util.cachestat, '__init__', wrapinit) | ||||
Augie Fackler
|
r43346 | origcacheable = extensions.wrapfunction( | ||
util.cachestat, 'cacheable', wrapcacheable | ||||
) | ||||
Idan Kamara
|
r14928 | |||
r52996 | for fn in [b'x', b'y']: | |||
Siddharth Agarwal
|
r20045 | try: | ||
os.remove(fn) | ||||
except OSError: | ||||
pass | ||||
Idan Kamara
|
r14928 | |||
basic(fakerepo()) | ||||
util.cachestat.cacheable = origcacheable | ||||
util.cachestat.__init__ = originit | ||||
Augie Fackler
|
r43346 | |||
Idan Kamara
|
r18313 | def test_filecache_synced(): | ||
timeless@mozdev.org
|
r26098 | # test old behavior that caused filecached properties to go out of sync | ||
r50883 | os.system('hg init && echo a >> a && hg add a && hg ci -qm.') | |||
Yuya Nishihara
|
r30559 | repo = hg.repository(uimod.ui.load()) | ||
Idan Kamara
|
r18313 | # first rollback clears the filecache, but changelog to stays in __dict__ | ||
repo.rollback() | ||||
Augie Fackler
|
r37917 | repo.commit(b'.') | ||
Idan Kamara
|
r18313 | # second rollback comes along and touches the changelog externally | ||
# (file is moved) | ||||
repo.rollback() | ||||
# but since changelog isn't under the filecache control anymore, we don't | ||||
# see that it changed, and return the old changelog without reconstructing | ||||
# it | ||||
Augie Fackler
|
r37917 | repo.commit(b'.') | ||
Idan Kamara
|
r18313 | |||
Augie Fackler
|
r43346 | |||
Idan Kamara
|
r18316 | def setbeforeget(repo): | ||
r52996 | os.remove(b'x') | |||
os.remove(b'y') | ||||
Yuya Nishihara
|
r40454 | repo.__class__.cached.set(repo, 'string set externally') | ||
Idan Kamara
|
r18316 | repo.invalidate() | ||
Robert Stanca
|
r28742 | print("* neither file exists") | ||
print(repo.cached) | ||||
Idan Kamara
|
r18316 | repo.invalidate() | ||
r52996 | f = open('x', 'wb') | |||
f.write(b'a') | ||||
Idan Kamara
|
r18316 | f.close() | ||
Robert Stanca
|
r28742 | print("* file x created") | ||
print(repo.cached) | ||||
Idan Kamara
|
r18316 | |||
Yuya Nishihara
|
r40454 | repo.__class__.cached.set(repo, 'string 2 set externally') | ||
Siddharth Agarwal
|
r20045 | repo.invalidate() | ||
Robert Stanca
|
r28742 | print("* string set externally again") | ||
print(repo.cached) | ||||
Siddharth Agarwal
|
r20045 | |||
repo.invalidate() | ||||
r52996 | f = open('y', 'wb') | |||
f.write(b'b') | ||||
Siddharth Agarwal
|
r20045 | f.close() | ||
Robert Stanca
|
r28742 | print("* file y created") | ||
print(repo.cached) | ||||
Siddharth Agarwal
|
r20045 | |||
Augie Fackler
|
r43346 | |||
FUJIWARA Katsunori
|
r29995 | def antiambiguity(): | ||
filename = 'ambigcheck' | ||||
# try some times, because reproduction of ambiguity depends on | ||||
# "filesystem time" | ||||
Manuel Jacob
|
r50180 | for i in range(5): | ||
r52996 | fp = open(filename, 'wb') | |||
fp.write(b'FOO') | ||||
FUJIWARA Katsunori
|
r29995 | fp.close() | ||
oldstat = os.stat(filename) | ||||
Augie Fackler
|
r36799 | if oldstat[stat.ST_CTIME] != oldstat[stat.ST_MTIME]: | ||
FUJIWARA Katsunori
|
r29995 | # subsequent changing never causes ambiguity | ||
continue | ||||
repetition = 3 | ||||
# repeat changing via checkambigatclosing, to examine whether | ||||
Mads Kiilerich
|
r30332 | # st_mtime is advanced multiple times as expected | ||
Manuel Jacob
|
r50180 | for i in range(repetition): | ||
FUJIWARA Katsunori
|
r29995 | # explicit closing | ||
r52996 | fp = vfsmod.checkambigatclosing(open(filename, 'ab')) | |||
fp.write(b'FOO') | ||||
FUJIWARA Katsunori
|
r29995 | fp.close() | ||
# implicit closing by "with" statement | ||||
r52996 | with vfsmod.checkambigatclosing(open(filename, 'ab')) as fp: | |||
fp.write(b'BAR') | ||||
FUJIWARA Katsunori
|
r29995 | |||
newstat = os.stat(filename) | ||||
Augie Fackler
|
r36799 | if oldstat[stat.ST_CTIME] != newstat[stat.ST_CTIME]: | ||
FUJIWARA Katsunori
|
r29995 | # timestamp ambiguity was naturally avoided while repetition | ||
continue | ||||
# st_mtime should be advanced "repetition * 2" times, because | ||||
Mads Kiilerich
|
r30332 | # all changes occurred at same time (in sec) | ||
Augie Fackler
|
r43346 | expected = (oldstat[stat.ST_MTIME] + repetition * 2) & 0x7FFFFFFF | ||
Augie Fackler
|
r36799 | if newstat[stat.ST_MTIME] != expected: | ||
Augie Fackler
|
r43346 | print( | ||
"'newstat[stat.ST_MTIME] %s is not %s (as %s + %s * 2)" | ||||
% ( | ||||
newstat[stat.ST_MTIME], | ||||
expected, | ||||
oldstat[stat.ST_MTIME], | ||||
repetition, | ||||
) | ||||
) | ||||
FUJIWARA Katsunori
|
r29995 | |||
# no more examination is needed regardless of result | ||||
break | ||||
else: | ||||
# This platform seems too slow to examine anti-ambiguity | ||||
# of file timestamp (or test happened to be executed at | ||||
# bad timing). Exit silently in this case, because running | ||||
# on other faster platforms can detect problems | ||||
pass | ||||
Augie Fackler
|
r43346 | |||
Robert Stanca
|
r28742 | print('basic:') | ||
print() | ||||
Idan Kamara
|
r14928 | basic(fakerepo()) | ||
Robert Stanca
|
r28742 | print() | ||
print('fakeuncacheable:') | ||||
print() | ||||
Idan Kamara
|
r14928 | fakeuncacheable() | ||
Idan Kamara
|
r18313 | test_filecache_synced() | ||
Robert Stanca
|
r28742 | print() | ||
print('setbeforeget:') | ||||
print() | ||||
Idan Kamara
|
r18316 | setbeforeget(fakerepo()) | ||
FUJIWARA Katsunori
|
r29995 | print() | ||
print('antiambiguity:') | ||||
print() | ||||
antiambiguity() | ||||