1"""Tests for distutils.file_util."""
2import unittest
3import os
4import errno
5from unittest.mock import patch
6
7from distutils.file_util import move_file, copy_file
8from distutils import log
9from distutils.tests import support
10from distutils.errors import DistutilsFileError
11from test.support import run_unittest, unlink
12
13class FileUtilTestCase(support.TempdirManager, unittest.TestCase):
14
15    def _log(self, msg, *args):
16        if len(args) > 0:
17            self._logs.append(msg % args)
18        else:
19            self._logs.append(msg)
20
21    def setUp(self):
22        super(FileUtilTestCase, self).setUp()
23        self._logs = []
24        self.old_log = log.info
25        log.info = self._log
26        tmp_dir = self.mkdtemp()
27        self.source = os.path.join(tmp_dir, 'f1')
28        self.target = os.path.join(tmp_dir, 'f2')
29        self.target_dir = os.path.join(tmp_dir, 'd1')
30
31    def tearDown(self):
32        log.info = self.old_log
33        super(FileUtilTestCase, self).tearDown()
34
35    def test_move_file_verbosity(self):
36        f = open(self.source, 'w')
37        try:
38            f.write('some content')
39        finally:
40            f.close()
41
42        move_file(self.source, self.target, verbose=0)
43        wanted = []
44        self.assertEqual(self._logs, wanted)
45
46        # back to original state
47        move_file(self.target, self.source, verbose=0)
48
49        move_file(self.source, self.target, verbose=1)
50        wanted = ['moving %s -> %s' % (self.source, self.target)]
51        self.assertEqual(self._logs, wanted)
52
53        # back to original state
54        move_file(self.target, self.source, verbose=0)
55
56        self._logs = []
57        # now the target is a dir
58        os.mkdir(self.target_dir)
59        move_file(self.source, self.target_dir, verbose=1)
60        wanted = ['moving %s -> %s' % (self.source, self.target_dir)]
61        self.assertEqual(self._logs, wanted)
62
63    def test_move_file_exception_unpacking_rename(self):
64        # see issue 22182
65        with patch("os.rename", side_effect=OSError("wrong", 1)), \
66             self.assertRaises(DistutilsFileError):
67            with open(self.source, 'w') as fobj:
68                fobj.write('spam eggs')
69            move_file(self.source, self.target, verbose=0)
70
71    def test_move_file_exception_unpacking_unlink(self):
72        # see issue 22182
73        with patch("os.rename", side_effect=OSError(errno.EXDEV, "wrong")), \
74             patch("os.unlink", side_effect=OSError("wrong", 1)), \
75             self.assertRaises(DistutilsFileError):
76            with open(self.source, 'w') as fobj:
77                fobj.write('spam eggs')
78            move_file(self.source, self.target, verbose=0)
79
80    def test_copy_file_hard_link(self):
81        with open(self.source, 'w') as f:
82            f.write('some content')
83        # Check first that copy_file() will not fall back on copying the file
84        # instead of creating the hard link.
85        try:
86            os.link(self.source, self.target)
87        except OSError as e:
88            self.skipTest('os.link: %s' % e)
89        else:
90            unlink(self.target)
91        st = os.stat(self.source)
92        copy_file(self.source, self.target, link='hard')
93        st2 = os.stat(self.source)
94        st3 = os.stat(self.target)
95        self.assertTrue(os.path.samestat(st, st2), (st, st2))
96        self.assertTrue(os.path.samestat(st2, st3), (st2, st3))
97        with open(self.source, 'r') as f:
98            self.assertEqual(f.read(), 'some content')
99
100    def test_copy_file_hard_link_failure(self):
101        # If hard linking fails, copy_file() falls back on copying file
102        # (some special filesystems don't support hard linking even under
103        #  Unix, see issue #8876).
104        with open(self.source, 'w') as f:
105            f.write('some content')
106        st = os.stat(self.source)
107        with patch("os.link", side_effect=OSError(0, "linking unsupported")):
108            copy_file(self.source, self.target, link='hard')
109        st2 = os.stat(self.source)
110        st3 = os.stat(self.target)
111        self.assertTrue(os.path.samestat(st, st2), (st, st2))
112        self.assertFalse(os.path.samestat(st2, st3), (st2, st3))
113        for fn in (self.source, self.target):
114            with open(fn, 'r') as f:
115                self.assertEqual(f.read(), 'some content')
116
117
118def test_suite():
119    return unittest.makeSuite(FileUtilTestCase)
120
121if __name__ == "__main__":
122    run_unittest(test_suite())
123