1"""Tests for distutils._msvccompiler."""
2import sys
3import unittest
4import os
5
6from distutils.errors import DistutilsPlatformError
7from distutils.tests import support
8from test.support import run_unittest
9
10
11SKIP_MESSAGE = (None if sys.platform == "win32" else
12                "These tests are only for win32")
13
14@unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE)
15class msvccompilerTestCase(support.TempdirManager,
16                            unittest.TestCase):
17
18    def test_no_compiler(self):
19        import distutils._msvccompiler as _msvccompiler
20        # makes sure query_vcvarsall raises
21        # a DistutilsPlatformError if the compiler
22        # is not found
23        def _find_vcvarsall(plat_spec):
24            return None, None
25
26        old_find_vcvarsall = _msvccompiler._find_vcvarsall
27        _msvccompiler._find_vcvarsall = _find_vcvarsall
28        try:
29            self.assertRaises(DistutilsPlatformError,
30                              _msvccompiler._get_vc_env,
31                             'wont find this version')
32        finally:
33            _msvccompiler._find_vcvarsall = old_find_vcvarsall
34
35    def test_compiler_options(self):
36        import distutils._msvccompiler as _msvccompiler
37        # suppress path to vcruntime from _find_vcvarsall to
38        # check that /MT is added to compile options
39        old_find_vcvarsall = _msvccompiler._find_vcvarsall
40        def _find_vcvarsall(plat_spec):
41            return old_find_vcvarsall(plat_spec)[0], None
42        _msvccompiler._find_vcvarsall = _find_vcvarsall
43        try:
44            compiler = _msvccompiler.MSVCCompiler()
45            compiler.initialize()
46
47            self.assertIn('/MT', compiler.compile_options)
48            self.assertNotIn('/MD', compiler.compile_options)
49        finally:
50            _msvccompiler._find_vcvarsall = old_find_vcvarsall
51
52    def test_vcruntime_copy(self):
53        import distutils._msvccompiler as _msvccompiler
54        # force path to a known file - it doesn't matter
55        # what we copy as long as its name is not in
56        # _msvccompiler._BUNDLED_DLLS
57        old_find_vcvarsall = _msvccompiler._find_vcvarsall
58        def _find_vcvarsall(plat_spec):
59            return old_find_vcvarsall(plat_spec)[0], __file__
60        _msvccompiler._find_vcvarsall = _find_vcvarsall
61        try:
62            tempdir = self.mkdtemp()
63            compiler = _msvccompiler.MSVCCompiler()
64            compiler.initialize()
65            compiler._copy_vcruntime(tempdir)
66
67            self.assertTrue(os.path.isfile(os.path.join(
68                tempdir, os.path.basename(__file__))))
69        finally:
70            _msvccompiler._find_vcvarsall = old_find_vcvarsall
71
72    def test_vcruntime_skip_copy(self):
73        import distutils._msvccompiler as _msvccompiler
74
75        tempdir = self.mkdtemp()
76        compiler = _msvccompiler.MSVCCompiler()
77        compiler.initialize()
78        dll = compiler._vcruntime_redist
79        self.assertTrue(os.path.isfile(dll), dll or "<None>")
80
81        compiler._copy_vcruntime(tempdir)
82
83        self.assertFalse(os.path.isfile(os.path.join(
84            tempdir, os.path.basename(dll))), dll or "<None>")
85
86    def test_get_vc_env_unicode(self):
87        import distutils._msvccompiler as _msvccompiler
88
89        test_var = 'ṰḖṤṪ┅ṼẨṜ'
90        test_value = '₃⁴₅'
91
92        # Ensure we don't early exit from _get_vc_env
93        old_distutils_use_sdk = os.environ.pop('DISTUTILS_USE_SDK', None)
94        os.environ[test_var] = test_value
95        try:
96            env = _msvccompiler._get_vc_env('x86')
97            self.assertIn(test_var.lower(), env)
98            self.assertEqual(test_value, env[test_var.lower()])
99        finally:
100            os.environ.pop(test_var)
101            if old_distutils_use_sdk:
102                os.environ['DISTUTILS_USE_SDK'] = old_distutils_use_sdk
103
104    def test_get_vc2017(self):
105        import distutils._msvccompiler as _msvccompiler
106
107        # This function cannot be mocked, so pass it if we find VS 2017
108        # and mark it skipped if we do not.
109        version, path = _msvccompiler._find_vc2017()
110        if version:
111            self.assertGreaterEqual(version, 15)
112            self.assertTrue(os.path.isdir(path))
113        else:
114            raise unittest.SkipTest("VS 2017 is not installed")
115
116    def test_get_vc2015(self):
117        import distutils._msvccompiler as _msvccompiler
118
119        # This function cannot be mocked, so pass it if we find VS 2015
120        # and mark it skipped if we do not.
121        version, path = _msvccompiler._find_vc2015()
122        if version:
123            self.assertGreaterEqual(version, 14)
124            self.assertTrue(os.path.isdir(path))
125        else:
126            raise unittest.SkipTest("VS 2015 is not installed")
127
128def test_suite():
129    return unittest.makeSuite(msvccompilerTestCase)
130
131if __name__ == "__main__":
132    run_unittest(test_suite())
133