1 # Copyright 2014 Altera Corporation. All Rights Reserved. 2 # Copyright 2015 John McGehee 3 # 4 # Licensed under the Apache License, Version 2.0 (the "License"); 5 # you may not use this file except in compliance with the License. 6 # You may obtain a copy of the License at 7 # 8 # http://www.apache.org/licenses/LICENSE-2.0 9 # 10 # Unless required by applicable law or agreed to in writing, software 11 # distributed under the License is distributed on an "AS IS" BASIS, 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 # See the License for the specific language governing permissions and 14 # limitations under the License. 15 16 """A base class for unit tests using the :py:class:`pyfakefs` module. 17 18 This class searches `sys.modules` for modules that import the `os`, `glob`, 19 `shutil`, and `tempfile` modules. 20 21 The `setUp()` method binds these modules to the corresponding fake 22 modules from `pyfakefs`. Further, the built in functions `file()` and 23 `open()` are bound to fake functions. 24 25 The `tearDownPyfakefs()` method returns the module bindings to their original 26 state. 27 28 It is expected that `setUp()` be invoked at the beginning of the derived 29 class' `setUp()` method, and `tearDownPyfakefs()` be invoked at the end of the 30 derived class' `tearDown()` method. 31 32 During the test, everything uses the fake file system and modules. This means 33 that even in your test, you can use familiar functions like `open()` and 34 `os.makedirs()` to manipulate the fake file system. 35 36 This also means existing unit tests that use the real file system can be 37 retrofitted to use `pyfakefs` by simply changing their base class from 38 `:py:class`unittest.TestCase` to 39 `:py:class`pyfakefs.fake_filesystem_unittest.TestCase`. 40 """ 41 42 import sys 43 import unittest 44 import doctest 45 import fake_filesystem 46 import fake_filesystem_glob 47 import fake_filesystem_shutil 48 import fake_tempfile 49 if sys.version_info < (3,): 50 import __builtin__ as builtins 51 else: 52 import builtins 53 54 import mox3.stubout 55 56 def load_doctests(loader, tests, ignore, module): 57 '''Load the doctest tests for the specified module into unittest.''' 58 _patcher = Patcher() 59 globs = _patcher.replaceGlobs(vars(module)) 60 tests.addTests(doctest.DocTestSuite(module, 61 globs=globs, 62 setUp=_patcher.setUp, 63 tearDown=_patcher.tearDown)) 64 return tests 65 66 67 class TestCase(unittest.TestCase): 68 def __init__(self, methodName='runTest'): 69 super(TestCase, self).__init__(methodName) 70 self._stubber = Patcher() 71 72 @property 73 def fs(self): 74 return self._stubber.fs 75 76 @property 77 def patches(self): 78 return self._stubber.patches 79 80 def setUpPyfakefs(self): 81 '''Bind the file-related modules to the :py:class:`pyfakefs` fake file 82 system instead of the real file system. Also bind the fake `file()` and 83 `open()` functions. 84 85 Invoke this at the beginning of the `setUp()` method in your unit test 86 class. 87 ''' 88 self._stubber.setUp() 89 self.addCleanup(self._stubber.tearDown) 90 91 92 def tearDownPyfakefs(self): 93 ''':meth:`pyfakefs.fake_filesystem_unittest.setUpPyfakefs` registers the 94 tear down procedure using :meth:`unittest.TestCase.addCleanup`. Thus this 95 method is deprecated, and remains just for backward compatibility. 96 ''' 97 pass 98 99 class Patcher(object): 100 ''' 101 Instantiate a stub creator to bind and un-bind the file-related modules to 102 the :py:mod:`pyfakefs` fake modules. 103 ''' 104 SKIPMODULES = set([None, fake_filesystem, fake_filesystem_glob, 105 fake_filesystem_shutil, fake_tempfile, sys]) 106 '''Stub nothing that is imported within these modules. 107 `sys` is included to prevent `sys.path` from being stubbed with the fake 108 `os.path`. 109 ''' 110 assert None in SKIPMODULES, "sys.modules contains 'None' values; must skip them." 111 112 SKIPNAMES = set(['os', 'glob', 'path', 'shutil', 'tempfile']) 113 114 def __init__(self): 115 # Attributes set by _findModules() 116 self._osModules = None 117 self._globModules = None 118 self._pathModules = None 119 self._shutilModules = None 120 self._tempfileModules = None 121 self._findModules() 122 assert None not in vars(self).values(), \ 123 "_findModules() missed the initialization of an instance variable" 124 125 # Attributes set by _refresh() 126 self._stubs = None 127 self.fs = None 128 self.fake_os = None 129 self.fake_glob = None 130 self.fake_path = None 131 self.fake_shutil = None 132 self.fake_tempfile_ = None 133 self.fake_open = None 134 # _isStale is set by tearDown(), reset by _refresh() 135 self._isStale = True 136 self._refresh() 137 assert None not in vars(self).values(), \ 138 "_refresh() missed the initialization of an instance variable" 139 assert self._isStale == False, "_refresh() did not reset _isStale" 140 141 def _findModules(self): 142 '''Find and cache all modules that import file system modules. 143 Later, `setUp()` will stub these with the fake file system 144 modules. 145 ''' 146 self._osModules = set() 147 self._globModules = set() 148 self._pathModules = set() 149 self._shutilModules = set() 150 self._tempfileModules = set() 151 for name, module in set(sys.modules.items()): 152 if module in self.SKIPMODULES or name in self.SKIPNAMES: 153 continue 154 if 'os' in module.__dict__: 155 self._osModules.add(module) 156 if 'glob' in module.__dict__: 157 self._globModules.add(module) 158 if 'path' in module.__dict__: 159 self._pathModules.add(module) 160 if 'shutil' in module.__dict__: 161 self._shutilModules.add(module) 162 if 'tempfile' in module.__dict__: 163 self._tempfileModules.add(module) 164 165 def _refresh(self): 166 '''Renew the fake file system and set the _isStale flag to `False`.''' 167 if self._stubs is not None: 168 self._stubs.SmartUnsetAll() 169 self._stubs = mox3.stubout.StubOutForTesting() 170 171 self.fs = fake_filesystem.FakeFilesystem() 172 self.fake_os = fake_filesystem.FakeOsModule(self.fs) 173 self.fake_glob = fake_filesystem_glob.FakeGlobModule(self.fs) 174 self.fake_path = self.fake_os.path 175 self.fake_shutil = fake_filesystem_shutil.FakeShutilModule(self.fs) 176 self.fake_tempfile_ = fake_tempfile.FakeTempfileModule(self.fs) 177 self.fake_open = fake_filesystem.FakeFileOpen(self.fs) 178 179 self._isStale = False 180 181 def setUp(self, doctester=None): 182 '''Bind the file-related modules to the :py:mod:`pyfakefs` fake 183 modules real ones. Also bind the fake `file()` and `open()` functions. 184 ''' 185 if self._isStale: 186 self._refresh() 187 188 if doctester is not None: 189 doctester.globs = self.replaceGlobs(doctester.globs) 190 191 if sys.version_info < (3,): 192 # No file() in Python3 193 self._stubs.SmartSet(builtins, 'file', self.fake_open) 194 self._stubs.SmartSet(builtins, 'open', self.fake_open) 195 196 for module in self._osModules: 197 self._stubs.SmartSet(module, 'os', self.fake_os) 198 for module in self._globModules: 199 self._stubs.SmartSet(module, 'glob', self.fake_glob) 200 for module in self._pathModules: 201 self._stubs.SmartSet(module, 'path', self.fake_path) 202 for module in self._shutilModules: 203 self._stubs.SmartSet(module, 'shutil', self.fake_shutil) 204 for module in self._tempfileModules: 205 self._stubs.SmartSet(module, 'tempfile', self.fake_tempfile_) 206 207 def replaceGlobs(self, globs_): 208 globs = globs_.copy() 209 if self._isStale: 210 self._refresh() 211 if 'os' in globs: 212 globs['os'] = fake_filesystem.FakeOsModule(self.fs) 213 if 'glob' in globs: 214 globs['glob'] = fake_filesystem_glob.FakeGlobModule(self.fs) 215 if 'path' in globs: 216 globs['path'] = fake_filesystem.FakePathModule(self.fs) 217 if 'shutil' in globs: 218 globs['shutil'] = fake_filesystem_shutil.FakeShutilModule(self.fs) 219 if 'tempfile' in globs: 220 globs['tempfile'] = fake_tempfile.FakeTempfileModule(self.fs) 221 return globs 222 223 def tearDown(self, doctester=None): 224 '''Clear the fake filesystem bindings created by `setUp()`.''' 225 self._isStale = True 226 self._stubs.SmartUnsetAll() 227