1""" 2Tests for msvc support module. 3""" 4 5import os 6import contextlib 7import distutils.errors 8import mock 9 10import pytest 11 12from . import contexts 13 14# importing only setuptools should apply the patch 15__import__('setuptools') 16 17pytest.importorskip("distutils.msvc9compiler") 18 19 20def mock_reg(hkcu=None, hklm=None): 21 """ 22 Return a mock for distutils.msvc9compiler.Reg, patched 23 to mock out the functions that access the registry. 24 """ 25 26 _winreg = getattr(distutils.msvc9compiler, '_winreg', None) 27 winreg = getattr(distutils.msvc9compiler, 'winreg', _winreg) 28 29 hives = { 30 winreg.HKEY_CURRENT_USER: hkcu or {}, 31 winreg.HKEY_LOCAL_MACHINE: hklm or {}, 32 } 33 34 @classmethod 35 def read_keys(cls, base, key): 36 """Return list of registry keys.""" 37 hive = hives.get(base, {}) 38 return [ 39 k.rpartition('\\')[2] 40 for k in hive if k.startswith(key.lower()) 41 ] 42 43 @classmethod 44 def read_values(cls, base, key): 45 """Return dict of registry keys and values.""" 46 hive = hives.get(base, {}) 47 return dict( 48 (k.rpartition('\\')[2], hive[k]) 49 for k in hive if k.startswith(key.lower()) 50 ) 51 52 return mock.patch.multiple( 53 distutils.msvc9compiler.Reg, 54 read_keys=read_keys, read_values=read_values) 55 56 57class TestModulePatch: 58 """ 59 Ensure that importing setuptools is sufficient to replace 60 the standard find_vcvarsall function with a version that 61 recognizes the "Visual C++ for Python" package. 62 """ 63 64 key_32 = r'software\microsoft\devdiv\vcforpython\9.0\installdir' 65 key_64 = key_32.replace(r'\microsoft', r'\wow6432node\microsoft') 66 67 def test_patched(self): 68 "Test the module is actually patched" 69 mod_name = distutils.msvc9compiler.find_vcvarsall.__module__ 70 assert mod_name == "setuptools.msvc", "find_vcvarsall unpatched" 71 72 def test_no_registry_entries_means_nothing_found(self): 73 """ 74 No registry entries or environment variable should lead to an error 75 directing the user to download vcpython27. 76 """ 77 find_vcvarsall = distutils.msvc9compiler.find_vcvarsall 78 query_vcvarsall = distutils.msvc9compiler.query_vcvarsall 79 80 with contexts.environment(VS90COMNTOOLS=None): 81 with mock_reg(): 82 assert find_vcvarsall(9.0) is None 83 84 try: 85 query_vcvarsall(9.0) 86 except Exception as exc: 87 expected = distutils.errors.DistutilsPlatformError 88 assert isinstance(exc, expected) 89 assert 'aka.ms/vcpython27' in str(exc) 90 91 @pytest.yield_fixture 92 def user_preferred_setting(self): 93 """ 94 Set up environment with different install dirs for user vs. system 95 and yield the user_install_dir for the expected result. 96 """ 97 with self.mock_install_dir() as user_install_dir: 98 with self.mock_install_dir() as system_install_dir: 99 reg = mock_reg( 100 hkcu={ 101 self.key_32: user_install_dir, 102 }, 103 hklm={ 104 self.key_32: system_install_dir, 105 self.key_64: system_install_dir, 106 }, 107 ) 108 with reg: 109 yield user_install_dir 110 111 def test_prefer_current_user(self, user_preferred_setting): 112 """ 113 Ensure user's settings are preferred. 114 """ 115 result = distutils.msvc9compiler.find_vcvarsall(9.0) 116 expected = os.path.join(user_preferred_setting, 'vcvarsall.bat') 117 assert expected == result 118 119 @pytest.yield_fixture 120 def local_machine_setting(self): 121 """ 122 Set up environment with only the system environment configured. 123 """ 124 with self.mock_install_dir() as system_install_dir: 125 reg = mock_reg( 126 hklm={ 127 self.key_32: system_install_dir, 128 }, 129 ) 130 with reg: 131 yield system_install_dir 132 133 def test_local_machine_recognized(self, local_machine_setting): 134 """ 135 Ensure machine setting is honored if user settings are not present. 136 """ 137 result = distutils.msvc9compiler.find_vcvarsall(9.0) 138 expected = os.path.join(local_machine_setting, 'vcvarsall.bat') 139 assert expected == result 140 141 @pytest.yield_fixture 142 def x64_preferred_setting(self): 143 """ 144 Set up environment with 64-bit and 32-bit system settings configured 145 and yield the canonical location. 146 """ 147 with self.mock_install_dir() as x32_dir: 148 with self.mock_install_dir() as x64_dir: 149 reg = mock_reg( 150 hklm={ 151 # This *should* only exist on 32-bit machines 152 self.key_32: x32_dir, 153 # This *should* only exist on 64-bit machines 154 self.key_64: x64_dir, 155 }, 156 ) 157 with reg: 158 yield x32_dir 159 160 def test_ensure_64_bit_preferred(self, x64_preferred_setting): 161 """ 162 Ensure 64-bit system key is preferred. 163 """ 164 result = distutils.msvc9compiler.find_vcvarsall(9.0) 165 expected = os.path.join(x64_preferred_setting, 'vcvarsall.bat') 166 assert expected == result 167 168 @staticmethod 169 @contextlib.contextmanager 170 def mock_install_dir(): 171 """ 172 Make a mock install dir in a unique location so that tests can 173 distinguish which dir was detected in a given scenario. 174 """ 175 with contexts.tempdir() as result: 176 vcvarsall = os.path.join(result, 'vcvarsall.bat') 177 with open(vcvarsall, 'w'): 178 pass 179 yield result 180