Issue #13964: Split os.*utime*() subsecond tests into multiple tests to help
authorVictor Stinner <victor.stinner@haypocalc.com>
Wed, 8 Feb 2012 02:36:25 +0000 (03:36 +0100)
committerVictor Stinner <victor.stinner@haypocalc.com>
Wed, 8 Feb 2012 02:36:25 +0000 (03:36 +0100)
debugging

Lib/test/test_os.py
Modules/posixmodule.c

index a591f4bfcda708876ec1a45b7a34724e5394e4de..e44174c126e8f757c6852ea5e419821e986d5333 100644 (file)
@@ -300,49 +300,86 @@ class StatAttributeTests(unittest.TestCase):
         st2 = os.stat(support.TESTFN)
         self.assertAlmostEqual(st1.st_mtime, st2.st_mtime, delta=10)
 
-    def test_utime_subsecond(self):
+    def _test_utime_subsecond(self, set_time_func):
         asec, amsec = 1, 901
         atime = asec + amsec * 1e-3
-        msec, mmsec = 5, 901
+        msec, mmsec = 2, 901
         mtime = msec + mmsec * 1e-3
         filename = self.fname
-        dirname = os.path.dirname(filename)
-        for func in ('utime', 'futimes', 'futimens', 'futimesat', 'lutimes', 'utimensat'):
-            if not hasattr(os, func):
-                continue
-            os.utime(filename, (0, 0))
-            if func == 'utime':
-                os.utime(filename, (atime, mtime))
-            elif func == 'futimes':
-                with open(filename, "wb") as f:
-                    os.futimes(f.fileno(), (atime, mtime))
-                os.utime(filename, (atime, mtime))
-            elif func == 'futimens':
-                with open(filename, "wb") as f:
-                    os.futimens(f.fileno(),
-                               (asec, amsec * 1000000),
-                               (msec, mmsec * 1000000))
-            elif func == 'lutimes':
-                os.lutimes(filename, (atime, mtime))
-            elif func == 'futimesat':
-                dirfd = os.open(dirname, os.O_RDONLY)
-                try:
-                    os.futimesat(dirfd, os.path.basename(filename),
-                                 (atime, mtime))
-                finally:
-                    os.close(dirfd)
-            else:
-                dirfd = os.open(dirname, os.O_RDONLY)
-                try:
-                    os.utimensat(dirfd, os.path.basename(filename),
-                                 (asec, amsec * 1000000),
-                                 (msec, mmsec * 1000000))
-                finally:
-                    os.close(dirfd)
-            st = os.stat(filename)
-            self.assertAlmostEqual(st.st_atime, atime, places=3)
-            self.assertAlmostEqual(st.st_mtime, mtime, places=3)
+        os.utime(filename, (0, 0))
+        set_time_func(filename, atime, mtime)
+        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)
+
+    @unittest.skipUnless(hasattr(os, 'futimes'),
+                         "os.futimes required for this test.")
+    def test_futimes_subsecond(self):
+        def set_time(filename, atime, mtime):
+            with open(filename, "wb") as f:
+                os.futimes(f.fileno(), (atime, mtime))
+        self._test_utime_subsecond(set_time)
+
+    @unittest.skipUnless(hasattr(os, 'futimens'),
+                         "os.futimens required for this test.")
+    def test_futimens_subsecond(self):
+        def set_time(filename, atime, mtime):
+            with open(filename, "wb") as f:
+                asec, ansec = divmod(atime, 1.0)
+                asec = int(asec)
+                ansec = int(ansec * 1e9)
+                msec, mnsec = divmod(mtime, 1.0)
+                msec = int(msec)
+                mnsec = int(mnsec * 1e9)
+                os.futimens(f.fileno(),
+                           (asec, ansec),
+                           (msec, mnsec))
+        self._test_utime_subsecond(set_time)
+
+    @unittest.skipUnless(hasattr(os, 'futimesat'),
+                         "os.futimesat required for this test.")
+    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.futimesat(dirfd, os.path.basename(filename),
+                             (atime, mtime))
+            finally:
+                os.close(dirfd)
+        self._test_utime_subsecond(set_time)
+
+    @unittest.skipUnless(hasattr(os, 'lutimes'),
+                         "os.lutimes required for this test.")
+    def test_lutimes_subsecond(self):
+        def set_time(filename, atime, mtime):
+            os.lutimes(filename, (atime, mtime))
+        self._test_utime_subsecond(set_time)
+
+    @unittest.skipUnless(hasattr(os, 'utimensat'),
+                         "os.utimensat required for this test.")
+    def test_utimensat_subsecond(self):
+        def set_time(filename, atime, mtime):
+            dirname = os.path.dirname(filename)
+            dirfd = os.open(dirname, os.O_RDONLY)
+            try:
+                asec, ansec = divmod(atime, 1.0)
+                asec = int(asec)
+                ansec = int(ansec * 1e9)
+                msec, mnsec = divmod(mtime, 1.0)
+                msec = int(msec)
+                mnsec = int(mnsec * 1e9)
+                os.utimensat(dirfd, os.path.basename(filename),
+                             (asec, ansec),
+                             (msec, mnsec))
+            finally:
+                os.close(dirfd)
+        self._test_utime_subsecond(set_time)
 
     # Restrict test to Win32, since there is no guarantee other
     # systems support centiseconds
index 2251a626d6b5a9ae0b2d99dece0ecd1401095506..14ef71045c45a62ae4d748f2004aa98118c1eb8e 100644 (file)
@@ -187,6 +187,9 @@ corresponding Unix manual entries for more information on calls.");
 #endif  /* ! __WATCOMC__ || __QNX__ */
 #endif /* ! __IBMC__ */
 
+
+
+
 #ifndef _MSC_VER
 
 #if defined(__sgi)&&_COMPILER_VERSION>=700
@@ -3525,53 +3528,18 @@ posix_uname(PyObject *self, PyObject *noargs)
 #endif /* HAVE_UNAME */
 
 
-/*
- * Classic POSIX utime functions supported microseconds (1m/sec).
- * Newer POSIX functions support nanoseconds (1 billion per sec).
- * posixmodule now uses the new functions where possible.
- * This improves accuracy in many situations, for example shutil.copy2().
- *
- * The implementation isn't currently sophisticated enough to handle
- * a platform where HAVE_UTIMENSAT is true but HAVE_FUTIMENS is false.
- * Specifically, posix_futimes() would break.
- *
- * Supporting such a platform wouldn't be impossible; you'd need two
- * extract_time() functions, or make its precision a parameter.
- * Since such a platform seems unlikely we haven't bothered.
- */
-#if defined(HAVE_UTIMENSAT)
-#define EXTRACT_TIME_PRECISION (1e9)
-#if !defined(HAVE_FUTIMENS)
-#error You HAVE_UTIMENSAT but not HAVE_FUTIMENS... please see accompanying comment.
-#endif
-#else
-#define EXTRACT_TIME_PRECISION (1e6)
-#endif
-
 static int
-extract_time(PyObject *t, time_t* sec, long* usec)
+extract_time(PyObject *t, time_t* sec, long* nsec)
 {
     time_t intval;
     if (PyFloat_Check(t)) {
-        double tval = PyFloat_AsDouble(t);
-        PyObject *intobj = PyNumber_Long(t);
-        if (!intobj)
-            return -1;
-#if SIZEOF_TIME_T > SIZEOF_LONG
-        intval = PyLong_AsUnsignedLongLongMask(intobj);
-#else
-        intval = PyLong_AsLong(intobj);
-#endif
-        Py_DECREF(intobj);
-        if (intval == -1 && PyErr_Occurred())
-            return -1;
-        *sec = intval;
-
-        *usec = (long)((tval - intval) * EXTRACT_TIME_PRECISION);
-        if (*usec < 0)
-            /* If rounding gave us a negative number,
-               truncate.  */
-            *usec = 0;
+        double d = PyFloat_AsDouble(t);
+        double mod;
+        *sec = (time_t)d;
+        mod = fmod(d, 1.0);
+        mod *= 1e9;
+        *nsec = (long)mod;
+        printf("%g => (%u, %li)\n", d, *sec, *nsec);
         return 0;
     }
 #if SIZEOF_TIME_T > SIZEOF_LONG
@@ -3582,7 +3550,7 @@ extract_time(PyObject *t, time_t* sec, long* usec)
     if (intval == -1 && PyErr_Occurred())
         return -1;
     *sec = intval;
-    *usec = 0;
+    *nsec = 0;
     return 0;
 }
 
@@ -3602,7 +3570,7 @@ posix_utime(PyObject *self, PyObject *args)
     const char *apath;
     HANDLE hFile;
     time_t atimesec, mtimesec;
-    long ausec, musec;
+    long ansec, mnsec;
     FILETIME atime, mtime;
     PyObject *result = NULL;
 
@@ -3655,13 +3623,13 @@ posix_utime(PyObject *self, PyObject *args)
     }
     else {
         if (extract_time(PyTuple_GET_ITEM(arg, 0),
-                         &atimesec, &ausec) == -1)
+                         &atimesec, &ansec) == -1)
             goto done;
-        time_t_to_FILE_TIME(atimesec, 1000*ausec, &atime);
+        time_t_to_FILE_TIME(atimesec, ansec, &atime);
         if (extract_time(PyTuple_GET_ITEM(arg, 1),
-                         &mtimesec, &musec) == -1)
+                         &mtimesec, &mnsec) == -1)
             goto done;
-        time_t_to_FILE_TIME(mtimesec, 1000*musec, &mtime);
+        time_t_to_FILE_TIME(mtimesec, mnsec, &mtime);
     }
     if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
         /* Avoid putting the file name into the error here,
@@ -3681,7 +3649,7 @@ done:
     PyObject *opath;
     char *path;
     time_t atime, mtime;
-    long ausec, musec;
+    long ansec, mnsec;
     int res;
     PyObject* arg = Py_None;
 
@@ -3703,12 +3671,12 @@ done:
     }
     else {
         if (extract_time(PyTuple_GET_ITEM(arg, 0),
-                         &atime, &ausec) == -1) {
+                         &atime, &ansec) == -1) {
             Py_DECREF(opath);
             return NULL;
         }
         if (extract_time(PyTuple_GET_ITEM(arg, 1),
-                         &mtime, &musec) == -1) {
+                         &mtime, &mnsec) == -1) {
             Py_DECREF(opath);
             return NULL;
         }
@@ -3718,16 +3686,16 @@ done:
 #ifdef HAVE_UTIMENSAT
         struct timespec buf[2];
         buf[0].tv_sec = atime;
-        buf[0].tv_nsec = ausec;
+        buf[0].tv_nsec = ansec;
         buf[1].tv_sec = mtime;
-        buf[1].tv_nsec = musec;
+        buf[1].tv_nsec = mnsec;
         res = utimensat(AT_FDCWD, path, buf, 0);
 #elif defined(HAVE_UTIMES)
         struct timeval buf[2];
         buf[0].tv_sec = atime;
-        buf[0].tv_usec = ausec;
+        buf[0].tv_usec = ansec / 1000;
         buf[1].tv_sec = mtime;
-        buf[1].tv_usec = musec;
+        buf[1].tv_usec = mnsec / 1000;
         res = utimes(path, buf);
 #elif defined(HAVE_UTIME_H)
         /* XXX should define struct utimbuf instead, above */
@@ -3767,7 +3735,7 @@ posix_futimes(PyObject *self, PyObject *args)
     int res, fd;
     PyObject* arg = Py_None;
     time_t atime, mtime;
-    long ausec, musec;
+    long ansec, mnsec;
 
     if (!PyArg_ParseTuple(args, "i|O:futimes", &fd, &arg))
         return NULL;
@@ -3785,11 +3753,11 @@ posix_futimes(PyObject *self, PyObject *args)
     }
     else {
         if (extract_time(PyTuple_GET_ITEM(arg, 0),
-                &atime, &ausec) == -1) {
+                &atime, &ansec) == -1) {
             return NULL;
         }
         if (extract_time(PyTuple_GET_ITEM(arg, 1),
-                &mtime, &musec) == -1) {
+                &mtime, &mnsec) == -1) {
             return NULL;
         }
         Py_BEGIN_ALLOW_THREADS
@@ -3797,16 +3765,16 @@ posix_futimes(PyObject *self, PyObject *args)
 #ifdef HAVE_FUTIMENS
         struct timespec buf[2];
         buf[0].tv_sec = atime;
-        buf[0].tv_nsec = ausec;
+        buf[0].tv_nsec = ansec;
         buf[1].tv_sec = mtime;
-        buf[1].tv_nsec = musec;
+        buf[1].tv_nsec = mnsec;
         res = futimens(fd, buf);
 #else
         struct timeval buf[2];
         buf[0].tv_sec = atime;
-        buf[0].tv_usec = ausec;
+        buf[0].tv_usec = ansec / 1000;
         buf[1].tv_sec = mtime;
-        buf[1].tv_usec = musec;
+        buf[1].tv_usec = mnsec / 1000;
         res = futimes(fd, buf);
 #endif
         }
@@ -3831,7 +3799,7 @@ posix_lutimes(PyObject *self, PyObject *args)
     const char *path;
     int res;
     time_t atime, mtime;
-    long ausec, musec;
+    long ansec, mnsec;
 
     if (!PyArg_ParseTuple(args, "O&|O:lutimes",
             PyUnicode_FSConverter, &opath, &arg))
@@ -3851,12 +3819,12 @@ posix_lutimes(PyObject *self, PyObject *args)
     }
     else {
         if (extract_time(PyTuple_GET_ITEM(arg, 0),
-                &atime, &ausec) == -1) {
+                &atime, &ansec) == -1) {
             Py_DECREF(opath);
             return NULL;
         }
         if (extract_time(PyTuple_GET_ITEM(arg, 1),
-                &mtime, &musec) == -1) {
+                &mtime, &mnsec) == -1) {
             Py_DECREF(opath);
             return NULL;
         }
@@ -3865,16 +3833,16 @@ posix_lutimes(PyObject *self, PyObject *args)
 #ifdef HAVE_UTIMENSAT
         struct timespec buf[2];
         buf[0].tv_sec = atime;
-        buf[0].tv_nsec = ausec;
+        buf[0].tv_nsec = ansec;
         buf[1].tv_sec = mtime;
-        buf[1].tv_nsec = musec;
+        buf[1].tv_nsec = mnsec;
         res = utimensat(AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW);
 #else
         struct timeval buf[2];
         buf[0].tv_sec = atime;
-        buf[0].tv_usec = ausec;
+        buf[0].tv_usec = ansec / 1000;
         buf[1].tv_sec = mtime;
-        buf[1].tv_usec = musec;
+        buf[1].tv_usec = mnsec / 1000;
         res = lutimes(path, buf);
 #endif
         }
@@ -9712,7 +9680,7 @@ posix_futimesat(PyObject *self, PyObject *args)
     int res, dirfd;
     PyObject* arg = Py_None;
     time_t atime, mtime;
-    long ausec, musec;
+    long ansec, mnsec;
 
     if (!PyArg_ParseTuple(args, "iO&|O:futimesat",
             &dirfd, PyUnicode_FSConverter, &opath, &arg))
@@ -9732,12 +9700,12 @@ posix_futimesat(PyObject *self, PyObject *args)
     }
     else {
         if (extract_time(PyTuple_GET_ITEM(arg, 0),
-                         &atime, &ausec) == -1) {
+                         &atime, &ansec) == -1) {
             Py_DECREF(opath);
             return NULL;
         }
         if (extract_time(PyTuple_GET_ITEM(arg, 1),
-                         &mtime, &musec) == -1) {
+                         &mtime, &mnsec) == -1) {
             Py_DECREF(opath);
             return NULL;
         }
@@ -9747,16 +9715,16 @@ posix_futimesat(PyObject *self, PyObject *args)
 #ifdef HAVE_UTIMENSAT
         struct timespec buf[2];
         buf[0].tv_sec = atime;
-        buf[0].tv_nsec = ausec;
+        buf[0].tv_nsec = ansec;
         buf[1].tv_sec = mtime;
-        buf[1].tv_nsec = musec;
+        buf[1].tv_nsec = mnsec;
         res = utimensat(dirfd, path, buf, 0);
 #else
         struct timeval buf[2];
         buf[0].tv_sec = atime;
-        buf[0].tv_usec = ausec;
+        buf[0].tv_usec = ansec / 1000;
         buf[1].tv_sec = mtime;
-        buf[1].tv_usec = musec;
+        buf[1].tv_usec = mnsec / 1000;
         res = futimesat(dirfd, path, buf);
 #endif
         }