1"""Tests for distutils.command.install."""
2
3import os
4import sys
5import unittest
6import site
7
8from test.support import captured_stdout, run_unittest
9
10from distutils import sysconfig
11from distutils.command.install import install
12from distutils.command import install as install_module
13from distutils.command.build_ext import build_ext
14from distutils.command.install import INSTALL_SCHEMES
15from distutils.core import Distribution
16from distutils.errors import DistutilsOptionError
17from distutils.extension import Extension
18
19from distutils.tests import support
20from test import support as test_support
21
22
23def _make_ext_name(modname):
24    return modname + sysconfig.get_config_var('EXT_SUFFIX')
25
26
27class InstallTestCase(support.TempdirManager,
28                      support.EnvironGuard,
29                      support.LoggingSilencer,
30                      unittest.TestCase):
31
32    def test_home_installation_scheme(self):
33        # This ensure two things:
34        # - that --home generates the desired set of directory names
35        # - test --home is supported on all platforms
36        builddir = self.mkdtemp()
37        destination = os.path.join(builddir, "installation")
38
39        dist = Distribution({"name": "foopkg"})
40        # script_name need not exist, it just need to be initialized
41        dist.script_name = os.path.join(builddir, "setup.py")
42        dist.command_obj["build"] = support.DummyCommand(
43            build_base=builddir,
44            build_lib=os.path.join(builddir, "lib"),
45            )
46
47        cmd = install(dist)
48        cmd.home = destination
49        cmd.ensure_finalized()
50
51        self.assertEqual(cmd.install_base, destination)
52        self.assertEqual(cmd.install_platbase, destination)
53
54        def check_path(got, expected):
55            got = os.path.normpath(got)
56            expected = os.path.normpath(expected)
57            self.assertEqual(got, expected)
58
59        libdir = os.path.join(destination, "lib", "python")
60        check_path(cmd.install_lib, libdir)
61        check_path(cmd.install_platlib, libdir)
62        check_path(cmd.install_purelib, libdir)
63        check_path(cmd.install_headers,
64                   os.path.join(destination, "include", "python", "foopkg"))
65        check_path(cmd.install_scripts, os.path.join(destination, "bin"))
66        check_path(cmd.install_data, destination)
67
68    def test_user_site(self):
69        # test install with --user
70        # preparing the environment for the test
71        self.old_user_base = site.USER_BASE
72        self.old_user_site = site.USER_SITE
73        self.tmpdir = self.mkdtemp()
74        self.user_base = os.path.join(self.tmpdir, 'B')
75        self.user_site = os.path.join(self.tmpdir, 'S')
76        site.USER_BASE = self.user_base
77        site.USER_SITE = self.user_site
78        install_module.USER_BASE = self.user_base
79        install_module.USER_SITE = self.user_site
80
81        def _expanduser(path):
82            return self.tmpdir
83        self.old_expand = os.path.expanduser
84        os.path.expanduser = _expanduser
85
86        def cleanup():
87            site.USER_BASE = self.old_user_base
88            site.USER_SITE = self.old_user_site
89            install_module.USER_BASE = self.old_user_base
90            install_module.USER_SITE = self.old_user_site
91            os.path.expanduser = self.old_expand
92
93        self.addCleanup(cleanup)
94
95        for key in ('nt_user', 'unix_user'):
96            self.assertIn(key, INSTALL_SCHEMES)
97
98        dist = Distribution({'name': 'xx'})
99        cmd = install(dist)
100
101        # making sure the user option is there
102        options = [name for name, short, lable in
103                   cmd.user_options]
104        self.assertIn('user', options)
105
106        # setting a value
107        cmd.user = 1
108
109        # user base and site shouldn't be created yet
110        self.assertFalse(os.path.exists(self.user_base))
111        self.assertFalse(os.path.exists(self.user_site))
112
113        # let's run finalize
114        cmd.ensure_finalized()
115
116        # now they should
117        self.assertTrue(os.path.exists(self.user_base))
118        self.assertTrue(os.path.exists(self.user_site))
119
120        self.assertIn('userbase', cmd.config_vars)
121        self.assertIn('usersite', cmd.config_vars)
122
123    def test_handle_extra_path(self):
124        dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'})
125        cmd = install(dist)
126
127        # two elements
128        cmd.handle_extra_path()
129        self.assertEqual(cmd.extra_path, ['path', 'dirs'])
130        self.assertEqual(cmd.extra_dirs, 'dirs')
131        self.assertEqual(cmd.path_file, 'path')
132
133        # one element
134        cmd.extra_path = ['path']
135        cmd.handle_extra_path()
136        self.assertEqual(cmd.extra_path, ['path'])
137        self.assertEqual(cmd.extra_dirs, 'path')
138        self.assertEqual(cmd.path_file, 'path')
139
140        # none
141        dist.extra_path = cmd.extra_path = None
142        cmd.handle_extra_path()
143        self.assertEqual(cmd.extra_path, None)
144        self.assertEqual(cmd.extra_dirs, '')
145        self.assertEqual(cmd.path_file, None)
146
147        # three elements (no way !)
148        cmd.extra_path = 'path,dirs,again'
149        self.assertRaises(DistutilsOptionError, cmd.handle_extra_path)
150
151    def test_finalize_options(self):
152        dist = Distribution({'name': 'xx'})
153        cmd = install(dist)
154
155        # must supply either prefix/exec-prefix/home or
156        # install-base/install-platbase -- not both
157        cmd.prefix = 'prefix'
158        cmd.install_base = 'base'
159        self.assertRaises(DistutilsOptionError, cmd.finalize_options)
160
161        # must supply either home or prefix/exec-prefix -- not both
162        cmd.install_base = None
163        cmd.home = 'home'
164        self.assertRaises(DistutilsOptionError, cmd.finalize_options)
165
166        # can't combine user with prefix/exec_prefix/home or
167        # install_(plat)base
168        cmd.prefix = None
169        cmd.user = 'user'
170        self.assertRaises(DistutilsOptionError, cmd.finalize_options)
171
172    def test_record(self):
173        install_dir = self.mkdtemp()
174        project_dir, dist = self.create_dist(py_modules=['hello'],
175                                             scripts=['sayhi'])
176        os.chdir(project_dir)
177        self.write_file('hello.py', "def main(): print('o hai')")
178        self.write_file('sayhi', 'from hello import main; main()')
179
180        cmd = install(dist)
181        dist.command_obj['install'] = cmd
182        cmd.root = install_dir
183        cmd.record = os.path.join(project_dir, 'filelist')
184        cmd.ensure_finalized()
185        cmd.run()
186
187        f = open(cmd.record)
188        try:
189            content = f.read()
190        finally:
191            f.close()
192
193        found = [os.path.basename(line) for line in content.splitlines()]
194        expected = ['hello.py', 'hello.%s.pyc' % sys.implementation.cache_tag,
195                    'sayhi',
196                    'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]]
197        self.assertEqual(found, expected)
198
199    def test_record_extensions(self):
200        cmd = test_support.missing_compiler_executable()
201        if cmd is not None:
202            self.skipTest('The %r command is not found' % cmd)
203        install_dir = self.mkdtemp()
204        project_dir, dist = self.create_dist(ext_modules=[
205            Extension('xx', ['xxmodule.c'])])
206        os.chdir(project_dir)
207        support.copy_xxmodule_c(project_dir)
208
209        buildextcmd = build_ext(dist)
210        support.fixup_build_ext(buildextcmd)
211        buildextcmd.ensure_finalized()
212
213        cmd = install(dist)
214        dist.command_obj['install'] = cmd
215        dist.command_obj['build_ext'] = buildextcmd
216        cmd.root = install_dir
217        cmd.record = os.path.join(project_dir, 'filelist')
218        cmd.ensure_finalized()
219        cmd.run()
220
221        f = open(cmd.record)
222        try:
223            content = f.read()
224        finally:
225            f.close()
226
227        found = [os.path.basename(line) for line in content.splitlines()]
228        expected = [_make_ext_name('xx'),
229                    'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]]
230        self.assertEqual(found, expected)
231
232    def test_debug_mode(self):
233        # this covers the code called when DEBUG is set
234        old_logs_len = len(self.logs)
235        install_module.DEBUG = True
236        try:
237            with captured_stdout():
238                self.test_record()
239        finally:
240            install_module.DEBUG = False
241        self.assertGreater(len(self.logs), old_logs_len)
242
243
244def test_suite():
245    return unittest.makeSuite(InstallTestCase)
246
247if __name__ == "__main__":
248    run_unittest(test_suite())
249