mirror of
https://github.com/borgbackup/borg.git
synced 2026-10-12 02:11:36 +00:00
testsuite: run the generic timestamp tests on win32, see #8730
is_utime_fully_supported() and is_birthtime_fully_supported() probed via os.utime, which can neither set the timestamps of a symlink itself nor the birthtime on win32 - so they returned False there and all the tests they guard were skipped. Probe via platform.set_times() instead, which does the right thing on all platforms. Also: a FILETIME has a resolution of 100ns, use that for st_mtime_ns_round on win32 (rather than the 1s fallback). Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
c99d03a209
commit
8d1a36bfd1
@@ -22,6 +22,9 @@ except: # noqa
|
||||
|
||||
from ..fuse_impl import llfuse, has_any_fuse, has_llfuse, has_pyfuse3, has_mfusepy, ENOATTR # NOQA
|
||||
from .. import platform
|
||||
|
||||
# import these directly: the borg.testsuite.platform subpackage shadows the platform name above.
|
||||
from ..platform import get_birthtime_ns, set_times
|
||||
from ..platformflags import is_win32, is_darwin
|
||||
|
||||
# Does this version of llfuse support ns precision?
|
||||
@@ -47,6 +50,9 @@ else:
|
||||
if sys.platform.startswith("netbsd"):
|
||||
st_mtime_ns_round = -4 # 10us - strange: only >1 microsecond resolution here?
|
||||
|
||||
if is_win32:
|
||||
st_mtime_ns_round = -2 # 100ns - the resolution of a FILETIME, see platform.windows.set_times
|
||||
|
||||
|
||||
def same_ts_ns(ts_ns1, ts_ns2):
|
||||
"""Compare two timestamps (both in nanoseconds) to determine whether they are (roughly) equal."""
|
||||
@@ -207,16 +213,19 @@ def are_fifos_supported():
|
||||
|
||||
@functools.lru_cache
|
||||
def is_utime_fully_supported():
|
||||
# note: platform.set_times is used rather than os.utime, because os.utime can not set the
|
||||
# timestamps of a symlink itself on win32 (it raises NotImplementedError there), see #8730.
|
||||
with unopened_tempfile() as filepath:
|
||||
# Some filesystems (such as SSHFS) don't support utime on symlinks
|
||||
# Some filesystems (such as SSHFS) don't support setting the timestamps of symlinks
|
||||
if are_symlinks_supported():
|
||||
os.symlink("something", filepath)
|
||||
else:
|
||||
open(filepath, "w").close()
|
||||
try:
|
||||
os.utime(filepath, (1000, 2000), follow_symlinks=False)
|
||||
atime_ns, mtime_ns = 1000 * 10**9, 2000 * 10**9
|
||||
set_times(filepath, atime_ns=atime_ns, mtime_ns=mtime_ns, follow_symlinks=False)
|
||||
new_stats = os.stat(filepath, follow_symlinks=False)
|
||||
if new_stats.st_atime == 1000 and new_stats.st_mtime == 2000:
|
||||
if new_stats.st_atime_ns == atime_ns and new_stats.st_mtime_ns == mtime_ns:
|
||||
return True
|
||||
except OSError:
|
||||
pass
|
||||
@@ -227,20 +236,23 @@ def is_utime_fully_supported():
|
||||
|
||||
@functools.lru_cache
|
||||
def is_birthtime_fully_supported():
|
||||
if not hasattr(os.stat_result, "st_birthtime"):
|
||||
return False
|
||||
# note: platform.set_times is used rather than the "os.utime twice" trick, because that only
|
||||
# works on the BSDs and on macOS - on win32, the birthtime needs SetFileTime, see #8730.
|
||||
with unopened_tempfile() as filepath:
|
||||
# Some filesystems (such as SSHFS) don't support utime on symlinks
|
||||
# Some filesystems (such as SSHFS) don't support setting the timestamps of symlinks
|
||||
if are_symlinks_supported():
|
||||
os.symlink("something", filepath)
|
||||
else:
|
||||
open(filepath, "w").close()
|
||||
try:
|
||||
birthtime, mtime, atime = 946598400, 946684800, 946771200
|
||||
os.utime(filepath, (atime, birthtime), follow_symlinks=False)
|
||||
os.utime(filepath, (atime, mtime), follow_symlinks=False)
|
||||
birthtime_ns, mtime_ns, atime_ns = 946598400 * 10**9, 946684800 * 10**9, 946771200 * 10**9
|
||||
set_times(filepath, atime_ns=atime_ns, mtime_ns=mtime_ns, birthtime_ns=birthtime_ns, follow_symlinks=False)
|
||||
new_stats = os.stat(filepath, follow_symlinks=False)
|
||||
if new_stats.st_birthtime == birthtime and new_stats.st_mtime == mtime and new_stats.st_atime == atime:
|
||||
if (
|
||||
get_birthtime_ns(new_stats, filepath) == birthtime_ns
|
||||
and new_stats.st_mtime_ns == mtime_ns
|
||||
and new_stats.st_atime_ns == atime_ns
|
||||
):
|
||||
return True
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
@@ -252,19 +252,24 @@ def test_unix_socket(archivers, request, monkeypatch):
|
||||
def test_nobirthtime(archivers, request):
|
||||
archiver = request.getfixturevalue(archivers)
|
||||
create_test_files(archiver.input_path)
|
||||
birthtime, mtime, atime = 946598400, 946684800, 946771200
|
||||
os.utime("input/file1", (atime, birthtime))
|
||||
os.utime("input/file1", (atime, mtime))
|
||||
birthtime_ns, mtime_ns, atime_ns = 946598400 * 10**9, 946684800 * 10**9, 946771200 * 10**9
|
||||
platform.set_times("input/file1", atime_ns=atime_ns, mtime_ns=mtime_ns, birthtime_ns=birthtime_ns)
|
||||
cmd(archiver, "repo-create", RK_ENCRYPTION)
|
||||
cmd(archiver, "create", "test", "input", "--nobirthtime")
|
||||
with changedir("output"):
|
||||
cmd(archiver, "extract", "test")
|
||||
sti = os.stat("input/file1")
|
||||
sto = os.stat("output/input/file1")
|
||||
assert same_ts_ns(sti.st_birthtime * 1e9, birthtime * 1e9)
|
||||
assert same_ts_ns(sto.st_birthtime * 1e9, mtime * 1e9)
|
||||
assert same_ts_ns(platform.get_birthtime_ns(sti, "input/file1"), birthtime_ns)
|
||||
birthtime_out = platform.get_birthtime_ns(sto, "output/input/file1")
|
||||
if is_win32:
|
||||
# win32 keeps the creation time the extracted file got when it was created.
|
||||
assert not same_ts_ns(birthtime_out, birthtime_ns)
|
||||
else:
|
||||
# posix: setting an mtime older than the birthtime pulls the birthtime back to it.
|
||||
assert same_ts_ns(birthtime_out, mtime_ns)
|
||||
assert same_ts_ns(sti.st_mtime_ns, sto.st_mtime_ns)
|
||||
assert same_ts_ns(sto.st_mtime_ns, mtime * 10**9)
|
||||
assert same_ts_ns(sto.st_mtime_ns, mtime_ns)
|
||||
|
||||
|
||||
def test_create_stdin(archivers, request):
|
||||
|
||||
@@ -310,6 +310,7 @@ def test_atime(archivers, request):
|
||||
assert same_ts_ns(sto.st_atime_ns, atime * 10**9)
|
||||
|
||||
|
||||
@pytest.mark.skipif(is_win32, reason="no os.pread and no O_NOATIME on win32")
|
||||
@pytest.mark.skipif(not is_utime_fully_supported(), reason="cannot properly setup and execute test without utime")
|
||||
def test_atime_open_updates_atime(archiver):
|
||||
# simulate a platform where already the open() updates the atime (no O_NOATIME support,
|
||||
@@ -356,19 +357,20 @@ def test_atime_open_updates_atime(archiver):
|
||||
def test_birthtime(archivers, request):
|
||||
archiver = request.getfixturevalue(archivers)
|
||||
create_test_files(archiver.input_path)
|
||||
birthtime, mtime, atime = 946598400, 946684800, 946771200
|
||||
os.utime("input/file1", (atime, birthtime))
|
||||
os.utime("input/file1", (atime, mtime))
|
||||
birthtime_ns, mtime_ns, atime_ns = 946598400 * 10**9, 946684800 * 10**9, 946771200 * 10**9
|
||||
platform.set_times("input/file1", atime_ns=atime_ns, mtime_ns=mtime_ns, birthtime_ns=birthtime_ns)
|
||||
cmd(archiver, "repo-create", RK_ENCRYPTION)
|
||||
cmd(archiver, "create", "test", "input")
|
||||
with changedir("output"):
|
||||
cmd(archiver, "extract", "test")
|
||||
sti = os.stat("input/file1")
|
||||
sto = os.stat("output/input/file1")
|
||||
assert same_ts_ns(sti.st_birthtime * 1e9, sto.st_birthtime * 1e9)
|
||||
assert same_ts_ns(sto.st_birthtime * 1e9, birthtime * 1e9)
|
||||
birthtime_in = get_birthtime_ns(sti, "input/file1")
|
||||
birthtime_out = get_birthtime_ns(sto, "output/input/file1")
|
||||
assert same_ts_ns(birthtime_in, birthtime_out)
|
||||
assert same_ts_ns(birthtime_out, birthtime_ns)
|
||||
assert same_ts_ns(sti.st_mtime_ns, sto.st_mtime_ns)
|
||||
assert same_ts_ns(sto.st_mtime_ns, mtime * 10**9)
|
||||
assert same_ts_ns(sto.st_mtime_ns, mtime_ns)
|
||||
|
||||
|
||||
@pytest.mark.skipif(not is_win32, reason="Windows-only test")
|
||||
|
||||
Reference in New Issue
Block a user