summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2015-06-12 19:58:00 (GMT)
committerVictor Stinner <victor.stinner@gmail.com>2015-06-12 19:58:00 (GMT)
commite12e7aa3fdd98e924dbc2179c54065fff44efd55 (patch)
treed167d4581978fee164a2757de889ab3f2ba4a079
parentf5d4523844cad49074a562bad4a7095ed955640d (diff)
downloadcpython-e12e7aa3fdd98e924dbc2179c54065fff44efd55.zip
cpython-e12e7aa3fdd98e924dbc2179c54065fff44efd55.tar.gz
cpython-e12e7aa3fdd98e924dbc2179c54065fff44efd55.tar.bz2
Issue #15745: Rewrite os.utime() tests in test_os
* Don't use the timestamp of an existing file anymore, only use fixed timestamp * Enhance the code checking the resolution of the filesystem timestamps. * Check timestamps with a resolution of 1 microsecond instead of 1 millisecond * When os.utime() uses the current system clock, tolerate a delta of 20 ms. Before some os.utime() tolerated a different of 10 seconds. * Merge duplicated tests and simplify the code
-rw-r--r--Lib/test/test_os.py412
1 files changed, 210 insertions, 202 deletions
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index 31f2cc3..0d49b99 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -2,32 +2,32 @@
# does add tests for a few functions which have been determined to be more
# portable than they had been thought to be.
-import os
-import errno
-import unittest
-import warnings
-import sys
-import signal
-import subprocess
-import time
-import shutil
-from test import support
+import asynchat
+import asyncore
+import codecs
import contextlib
+import decimal
+import errno
+import fractions
+import itertools
+import locale
import mmap
+import os
+import pickle
import platform
import re
-import uuid
-import asyncore
-import asynchat
+import shutil
+import signal
import socket
-import itertools
import stat
-import locale
-import codecs
-import decimal
-import fractions
-import pickle
+import subprocess
+import sys
import sysconfig
+import time
+import unittest
+import uuid
+import warnings
+from test import support
try:
import threading
except ImportError:
@@ -43,16 +43,6 @@ except ImportError:
from test.script_helper import assert_python_ok
-with warnings.catch_warnings():
- warnings.simplefilter("ignore", DeprecationWarning)
- os.stat_float_times(True)
-st = os.stat(__file__)
-stat_supports_subsecond = (
- # check if float and int timestamps are different
- (st.st_atime != st[7])
- or (st.st_mtime != st[8])
- or (st.st_ctime != st[9]))
-
# Detect whether we're on a Linux system that uses the (now outdated
# and unmaintained) linuxthreads threading library. There's an issue
# when combining linuxthreads with a failed execv call: see
@@ -336,179 +326,6 @@ class StatAttributeTests(unittest.TestCase):
unpickled = pickle.loads(p)
self.assertEqual(result, unpickled)
- def test_utime_dir(self):
- delta = 1000000
- st = os.stat(support.TESTFN)
- # round to int, because some systems may support sub-second
- # time stamps in stat, but not in utime.
- os.utime(support.TESTFN, (st.st_atime, int(st.st_mtime-delta)))
- st2 = os.stat(support.TESTFN)
- self.assertEqual(st2.st_mtime, int(st.st_mtime-delta))
-
- def _test_utime(self, filename, attr, utime, delta):
- # Issue #13327 removed the requirement to pass None as the
- # second argument. Check that the previous methods of passing
- # a time tuple or None work in addition to no argument.
- st0 = os.stat(filename)
- # Doesn't set anything new, but sets the time tuple way
- utime(filename, (attr(st0, "st_atime"), attr(st0, "st_mtime")))
- # Setting the time to the time you just read, then reading again,
- # should always return exactly the same times.
- st1 = os.stat(filename)
- self.assertEqual(attr(st0, "st_mtime"), attr(st1, "st_mtime"))
- self.assertEqual(attr(st0, "st_atime"), attr(st1, "st_atime"))
- # Set to the current time in the old explicit way.
- os.utime(filename, None)
- st2 = os.stat(support.TESTFN)
- # Set to the current time in the new way
- os.utime(filename)
- st3 = os.stat(filename)
- self.assertAlmostEqual(attr(st2, "st_mtime"), attr(st3, "st_mtime"), delta=delta)
-
- def test_utime(self):
- def utime(file, times):
- return os.utime(file, times)
- self._test_utime(self.fname, getattr, utime, 10)
- self._test_utime(support.TESTFN, getattr, utime, 10)
-
-
- def _test_utime_ns(self, set_times_ns, test_dir=True):
- def getattr_ns(o, attr):
- return getattr(o, attr + "_ns")
- ten_s = 10 * 1000 * 1000 * 1000
- self._test_utime(self.fname, getattr_ns, set_times_ns, ten_s)
- if test_dir:
- self._test_utime(support.TESTFN, getattr_ns, set_times_ns, ten_s)
-
- def test_utime_ns(self):
- def utime_ns(file, times):
- return os.utime(file, ns=times)
- self._test_utime_ns(utime_ns)
-
- requires_utime_dir_fd = unittest.skipUnless(
- os.utime in os.supports_dir_fd,
- "dir_fd support for utime required for this test.")
- requires_utime_fd = unittest.skipUnless(
- os.utime in os.supports_fd,
- "fd support for utime required for this test.")
- requires_utime_nofollow_symlinks = unittest.skipUnless(
- os.utime in os.supports_follow_symlinks,
- "follow_symlinks support for utime required for this test.")
-
- @requires_utime_nofollow_symlinks
- def test_lutimes_ns(self):
- def lutimes_ns(file, times):
- return os.utime(file, ns=times, follow_symlinks=False)
- self._test_utime_ns(lutimes_ns)
-
- @requires_utime_fd
- def test_futimes_ns(self):
- def futimes_ns(file, times):
- with open(file, "wb") as f:
- os.utime(f.fileno(), ns=times)
- self._test_utime_ns(futimes_ns, test_dir=False)
-
- def _utime_invalid_arguments(self, name, arg):
- with self.assertRaises(ValueError):
- getattr(os, name)(arg, (5, 5), ns=(5, 5))
-
- def test_utime_invalid_arguments(self):
- self._utime_invalid_arguments('utime', self.fname)
-
-
- @unittest.skipUnless(stat_supports_subsecond,
- "os.stat() doesn't has a subsecond resolution")
- def _test_utime_subsecond(self, set_time_func):
- asec, amsec = 1, 901
- atime = asec + amsec * 1e-3
- msec, mmsec = 2, 901
- mtime = msec + mmsec * 1e-3
- filename = self.fname
- os.utime(filename, (0, 0))
- set_time_func(filename, atime, mtime)
- with warnings.catch_warnings():
- warnings.simplefilter("ignore", DeprecationWarning)
- os.stat_float_times(True)
- st = os.stat(filename)
- self.assertAlmostEqual(st.st_atime, atime, places=3)
- self.assertAlmostEqual(st.st_mtime, mtime, places=3)
-
- def test_utime_subsecond(self):
- def set_time(filename, atime, mtime):
- os.utime(filename, (atime, mtime))
- self._test_utime_subsecond(set_time)
-
- @requires_utime_fd
- def test_futimes_subsecond(self):
- def set_time(filename, atime, mtime):
- with open(filename, "wb") as f:
- os.utime(f.fileno(), times=(atime, mtime))
- self._test_utime_subsecond(set_time)
-
- @requires_utime_fd
- def test_futimens_subsecond(self):
- def set_time(filename, atime, mtime):
- with open(filename, "wb") as f:
- os.utime(f.fileno(), times=(atime, mtime))
- self._test_utime_subsecond(set_time)
-
- @requires_utime_dir_fd
- def test_futimesat_subsecond(self):
- def set_time(filename, atime, mtime):
- dirname = os.path.dirname(filename)
- dirfd = os.open(dirname, os.O_RDONLY)
- try:
- os.utime(os.path.basename(filename), dir_fd=dirfd,
- times=(atime, mtime))
- finally:
- os.close(dirfd)
- self._test_utime_subsecond(set_time)
-
- @requires_utime_nofollow_symlinks
- def test_lutimes_subsecond(self):
- def set_time(filename, atime, mtime):
- os.utime(filename, (atime, mtime), follow_symlinks=False)
- self._test_utime_subsecond(set_time)
-
- @requires_utime_dir_fd
- def test_utimensat_subsecond(self):
- def set_time(filename, atime, mtime):
- dirname = os.path.dirname(filename)
- dirfd = os.open(dirname, os.O_RDONLY)
- try:
- os.utime(os.path.basename(filename), dir_fd=dirfd,
- times=(atime, mtime))
- finally:
- os.close(dirfd)
- self._test_utime_subsecond(set_time)
-
- # Restrict tests to Win32, since there is no guarantee other
- # systems support centiseconds
- def get_file_system(path):
- if sys.platform == 'win32':
- root = os.path.splitdrive(os.path.abspath(path))[0] + '\\'
- import ctypes
- kernel32 = ctypes.windll.kernel32
- buf = ctypes.create_unicode_buffer("", 100)
- if kernel32.GetVolumeInformationW(root, None, 0, None, None, None, buf, len(buf)):
- return buf.value
-
- @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests")
- @unittest.skipUnless(get_file_system(support.TESTFN) == "NTFS",
- "requires NTFS")
- def test_1565150(self):
- t1 = 1159195039.25
- os.utime(self.fname, (t1, t1))
- self.assertEqual(os.stat(self.fname).st_mtime, t1)
-
- @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests")
- @unittest.skipUnless(get_file_system(support.TESTFN) == "NTFS",
- "requires NTFS")
- def test_large_time(self):
- t1 = 5000000000 # some day in 2128
- os.utime(self.fname, (t1, t1))
- self.assertEqual(os.stat(self.fname).st_mtime, t1)
-
@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests")
def test_1686475(self):
# Verify that an open file can be stat'ed
@@ -533,6 +350,196 @@ class StatAttributeTests(unittest.TestCase):
os.stat(r)
self.assertEqual(ctx.exception.errno, errno.EBADF)
+
+class UtimeTests(unittest.TestCase):
+ def setUp(self):
+ self.dirname = support.TESTFN
+ self.fname = os.path.join(self.dirname, "f1")
+
+ self.addCleanup(support.rmtree, self.dirname)
+ os.mkdir(self.dirname)
+ with open(self.fname, 'wb') as fp:
+ fp.write(b"ABC")
+
+ # ensure that st_atime and st_mtime are float
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore", DeprecationWarning)
+
+ old_state = os.stat_float_times(-1)
+ self.addCleanup(os.stat_float_times, old_state)
+
+ os.stat_float_times(True)
+
+ def support_subsecond(self, filename):
+ # Heuristic to check if the filesystem supports timestamp with
+ # subsecond resolution: check if float and int timestamps are different
+ st = os.stat(filename)
+ return ((st.st_atime != st[7])
+ or (st.st_mtime != st[8])
+ or (st.st_ctime != st[9]))
+
+ def _test_utime(self, set_time, filename=None):
+ if not filename:
+ filename = self.fname
+
+ support_subsecond = self.support_subsecond(filename)
+ if support_subsecond:
+ # Timestamp with a resolution of 1 microsecond (10^-6).
+ #
+ # The resolution of the C internal function used by os.utime()
+ # depends on the platform: 1 sec, 1 us, 1 ns. Writing a portable
+ # test with a resolution of 1 ns requires more work:
+ # see the issue #15745.
+ atime_ns = 1002003000 # 1.002003 seconds
+ mtime_ns = 4005006000 # 4.005006 seconds
+ else:
+ # use a resolution of 1 second
+ atime_ns = 5 * 10**9
+ mtime_ns = 8 * 10**9
+
+ set_time(filename, (atime_ns, mtime_ns))
+ st = os.stat(filename)
+
+ if support_subsecond:
+ self.assertAlmostEqual(st.st_atime, atime_ns * 1e-9, delta=1e-6)
+ self.assertAlmostEqual(st.st_mtime, mtime_ns * 1e-9, delta=1e-6)
+ else:
+ self.assertEqual(st.st_atime, atime_ns * 1e-9)
+ self.assertEqual(st.st_mtime, mtime_ns * 1e-9)
+ self.assertEqual(st.st_atime_ns, atime_ns)
+ self.assertEqual(st.st_mtime_ns, mtime_ns)
+
+ def test_utime(self):
+ def set_time(filename, ns):
+ # test the ns keyword parameter
+ os.utime(filename, ns=ns)
+ self._test_utime(set_time)
+
+ @staticmethod
+ def ns_to_sec(ns):
+ # Convert a number of nanosecond (int) to a number of seconds (float).
+ # Round towards infinity by adding 0.5 nanosecond to avoid rounding
+ # issue, os.utime() rounds towards minus infinity.
+ return (ns * 1e-9) + 0.5e-9
+
+ def test_utime_by_indexed(self):
+ # pass times as floating point seconds as the second indexed parameter
+ def set_time(filename, ns):
+ atime_ns, mtime_ns = ns
+ atime = self.ns_to_sec(atime_ns)
+ mtime = self.ns_to_sec(mtime_ns)
+ # test utimensat(timespec), utimes(timeval), utime(utimbuf)
+ # or utime(time_t)
+ os.utime(filename, (atime, mtime))
+ self._test_utime(set_time)
+
+ def test_utime_by_times(self):
+ def set_time(filename, ns):
+ atime_ns, mtime_ns = ns
+ atime = self.ns_to_sec(atime_ns)
+ mtime = self.ns_to_sec(mtime_ns)
+ # test the times keyword parameter
+ os.utime(filename, times=(atime, mtime))
+ self._test_utime(set_time)
+
+ @unittest.skipUnless(os.utime in os.supports_follow_symlinks,
+ "follow_symlinks support for utime required "
+ "for this test.")
+ def test_utime_nofollow_symlinks(self):
+ def set_time(filename, ns):
+ # use follow_symlinks=False to test utimensat(timespec)
+ # or lutimes(timeval)
+ os.utime(filename, ns=ns, follow_symlinks=False)
+ self._test_utime(set_time)
+
+ @unittest.skipUnless(os.utime in os.supports_fd,
+ "fd support for utime required for this test.")
+ def test_utime_fd(self):
+ def set_time(filename, ns):
+ with open(filename, 'wb') as fp:
+ # use a file descriptor to test futimens(timespec)
+ # or futimes(timeval)
+ os.utime(fp.fileno(), ns=ns)
+ self._test_utime(set_time)
+
+ @unittest.skipUnless(os.utime in os.supports_dir_fd,
+ "dir_fd support for utime required for this test.")
+ def test_utime_dir_fd(self):
+ def set_time(filename, ns):
+ dirname, name = os.path.split(filename)
+ dirfd = os.open(dirname, os.O_RDONLY)
+ try:
+ # pass dir_fd to test utimensat(timespec) or futimesat(timeval)
+ os.utime(name, dir_fd=dirfd, ns=ns)
+ finally:
+ os.close(dirfd)
+ self._test_utime(set_time)
+
+ def test_utime_directory(self):
+ def set_time(filename, ns):
+ # test calling os.utime() on a directory
+ os.utime(filename, ns=ns)
+ self._test_utime(set_time, filename=self.dirname)
+
+ def _test_utime_current(self, set_time):
+ # Get the system clock
+ current = time.time()
+
+ # Call os.utime() to set the timestamp to the current system clock
+ set_time(self.fname)
+
+ if not self.support_subsecond(self.fname):
+ delta = 1.0
+ else:
+ # On Windows, the usual resolution of time.time() is 15.6 ms
+ delta = 0.020
+ st = os.stat(self.fname)
+ msg = ("st_time=%r, current=%r, dt=%r"
+ % (st.st_mtime, current, st.st_mtime - current))
+ self.assertAlmostEqual(st.st_mtime, current,
+ delta=delta, msg=msg)
+
+ def test_utime_current(self):
+ def set_time(filename):
+ # Set to the current time in the new way
+ os.utime(self.fname)
+ self._test_utime_current(set_time)
+
+ def test_utime_current_old(self):
+ def set_time(filename):
+ # Set to the current time in the old explicit way.
+ os.utime(self.fname, None)
+ self._test_utime_current(set_time)
+
+ def get_file_system(self, path):
+ if sys.platform == 'win32':
+ root = os.path.splitdrive(os.path.abspath(path))[0] + '\\'
+ import ctypes
+ kernel32 = ctypes.windll.kernel32
+ buf = ctypes.create_unicode_buffer("", 100)
+ ok = kernel32.GetVolumeInformationW(root, None, 0,
+ None, None, None,
+ buf, len(buf))
+ if ok:
+ return buf.value
+ # return None if the filesystem is unknown
+
+ def test_large_time(self):
+ # Many filesystems are limited to the year 2038. At least, the test
+ # pass with NTFS filesystem.
+ if self.get_file_system(self.dirname) != "NTFS":
+ self.skipTest("requires NTFS")
+
+ large = 5000000000 # some day in 2128
+ os.utime(self.fname, (large, large))
+ self.assertEqual(os.stat(self.fname).st_mtime, large)
+
+ def test_utime_invalid_arguments(self):
+ # seconds and nanoseconds parameters are mutually exclusive
+ with self.assertRaises(ValueError):
+ os.utime(self.fname, (5, 5), ns=(5, 5))
+
+
from test import mapping_tests
class EnvironTests(mapping_tests.BasicTestMappingProtocol):
@@ -2561,6 +2568,7 @@ def test_main():
support.run_unittest(
FileTests,
StatAttributeTests,
+ UtimeTests,
EnvironTests,
WalkTests,
FwalkTests,