1"""Tests for distutils._msvccompiler.""" 2import sys 3import unittest 4import os 5import threading 6 7from distutils.errors import DistutilsPlatformError 8from distutils.tests import support 9from test.support import run_unittest 10 11 12SKIP_MESSAGE = (None if sys.platform == "win32" else 13 "These tests are only for win32") 14 15@unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE) 16class msvccompilerTestCase(support.TempdirManager, 17 unittest.TestCase): 18 19 def test_no_compiler(self): 20 import distutils._msvccompiler as _msvccompiler 21 # makes sure query_vcvarsall raises 22 # a DistutilsPlatformError if the compiler 23 # is not found 24 def _find_vcvarsall(plat_spec): 25 return None, None 26 27 old_find_vcvarsall = _msvccompiler._find_vcvarsall 28 _msvccompiler._find_vcvarsall = _find_vcvarsall 29 try: 30 self.assertRaises(DistutilsPlatformError, 31 _msvccompiler._get_vc_env, 32 'wont find this version') 33 finally: 34 _msvccompiler._find_vcvarsall = old_find_vcvarsall 35 36 def test_get_vc_env_unicode(self): 37 import distutils._msvccompiler as _msvccompiler 38 39 test_var = 'ṰḖṤṪ┅ṼẨṜ' 40 test_value = '₃⁴₅' 41 42 # Ensure we don't early exit from _get_vc_env 43 old_distutils_use_sdk = os.environ.pop('DISTUTILS_USE_SDK', None) 44 os.environ[test_var] = test_value 45 try: 46 env = _msvccompiler._get_vc_env('x86') 47 self.assertIn(test_var.lower(), env) 48 self.assertEqual(test_value, env[test_var.lower()]) 49 finally: 50 os.environ.pop(test_var) 51 if old_distutils_use_sdk: 52 os.environ['DISTUTILS_USE_SDK'] = old_distutils_use_sdk 53 54 def test_get_vc2017(self): 55 import distutils._msvccompiler as _msvccompiler 56 57 # This function cannot be mocked, so pass it if we find VS 2017 58 # and mark it skipped if we do not. 59 version, path = _msvccompiler._find_vc2017() 60 if version: 61 self.assertGreaterEqual(version, 15) 62 self.assertTrue(os.path.isdir(path)) 63 else: 64 raise unittest.SkipTest("VS 2017 is not installed") 65 66 def test_get_vc2015(self): 67 import distutils._msvccompiler as _msvccompiler 68 69 # This function cannot be mocked, so pass it if we find VS 2015 70 # and mark it skipped if we do not. 71 version, path = _msvccompiler._find_vc2015() 72 if version: 73 self.assertGreaterEqual(version, 14) 74 self.assertTrue(os.path.isdir(path)) 75 else: 76 raise unittest.SkipTest("VS 2015 is not installed") 77 78 79class CheckThread(threading.Thread): 80 exc_info = None 81 82 def run(self): 83 try: 84 super().run() 85 except Exception: 86 self.exc_info = sys.exc_info() 87 88 def __bool__(self): 89 return not self.exc_info 90 91 92class TestSpawn(unittest.TestCase): 93 def test_concurrent_safe(self): 94 """ 95 Concurrent calls to spawn should have consistent results. 96 """ 97 import distutils._msvccompiler as _msvccompiler 98 compiler = _msvccompiler.MSVCCompiler() 99 compiler._paths = "expected" 100 inner_cmd = 'import os; assert os.environ["PATH"] == "expected"' 101 command = [sys.executable, '-c', inner_cmd] 102 103 threads = [ 104 CheckThread(target=compiler.spawn, args=[command]) 105 for n in range(100) 106 ] 107 for thread in threads: 108 thread.start() 109 for thread in threads: 110 thread.join() 111 assert all(threads) 112 113 def test_concurrent_safe_fallback(self): 114 """ 115 If CCompiler.spawn has been monkey-patched without support 116 for an env, it should still execute. 117 """ 118 import distutils._msvccompiler as _msvccompiler 119 from distutils import ccompiler 120 compiler = _msvccompiler.MSVCCompiler() 121 compiler._paths = "expected" 122 123 def CCompiler_spawn(self, cmd): 124 "A spawn without an env argument." 125 assert os.environ["PATH"] == "expected" 126 127 with unittest.mock.patch.object( 128 ccompiler.CCompiler, 'spawn', CCompiler_spawn): 129 compiler.spawn(["n/a"]) 130 131 assert os.environ.get("PATH") != "expected" 132 133 134def test_suite(): 135 return unittest.TestLoader().loadTestsFromTestCase(msvccompilerTestCase) 136 137if __name__ == "__main__": 138 run_unittest(test_suite()) 139