[Python-checkins] cpython (3.4): Issue #15745: Rewrite os.utime() tests in test_os

victor.stinner python-checkins at python.org
Fri Jun 12 22:03:54 CEST 2015


https://hg.python.org/cpython/rev/50ec3fb126dd
changeset:   96586:50ec3fb126dd
branch:      3.4
parent:      96582:ccb0f43964e0
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Fri Jun 12 21:58:00 2015 +0200
summary:
  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

files:
  Lib/test/test_os.py |  418 ++++++++++++++++---------------
  1 files changed, 213 insertions(+), 205 deletions(-)


diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
--- 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 asynchat
+import asyncore
+import codecs
+import contextlib
+import decimal
+import errno
+import fractions
+import itertools
+import locale
+import mmap
 import os
-import errno
-import unittest
-import warnings
-import sys
-import signal
-import subprocess
-import time
-import shutil
-from test import support
-import contextlib
-import mmap
+import pickle
 import platform
 import re
+import shutil
+import signal
+import socket
+import stat
+import subprocess
+import sys
+import sysconfig
+import time
+import unittest
 import uuid
-import asyncore
-import asynchat
-import socket
-import itertools
-import stat
-import locale
-import codecs
-import decimal
-import fractions
-import pickle
-import sysconfig
+import warnings
+from test import support
 try:
     import threading
 except ImportError:
@@ -43,16 +43,6 @@
 
 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 @@
             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 @@
             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 @@
     support.run_unittest(
         FileTests,
         StatAttributeTests,
+        UtimeTests,
         EnvironTests,
         WalkTests,
         FwalkTests,

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list