##// END OF EJS Templates
more pytest cleanup
M Bussonnier -
Show More
@@ -1,507 +1,506
1 1 # encoding: utf-8
2 2 """Tests for IPython.utils.path.py"""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 import os
8 8 import shutil
9 9 import sys
10 10 import tempfile
11 11 import unittest
12 12 from contextlib import contextmanager
13 13 from importlib import reload
14 14 from os.path import abspath, join
15 15 from unittest.mock import patch
16 16
17 17 import pytest
18 18 from tempfile import TemporaryDirectory
19 19
20 20 import IPython
21 21 from IPython import paths
22 22 from IPython.testing import decorators as dec
23 23 from IPython.testing.decorators import (
24 24 onlyif_unicode_paths,
25 25 skip_if_not_win32,
26 26 skip_win32,
27 27 )
28 28 from IPython.testing.tools import make_tempfile
29 29 from IPython.utils import path
30 30
31 31 # Platform-dependent imports
32 32 try:
33 33 import winreg as wreg
34 34 except ImportError:
35 35 #Fake _winreg module on non-windows platforms
36 36 import types
37 37 wr_name = "winreg"
38 38 sys.modules[wr_name] = types.ModuleType(wr_name)
39 39 try:
40 40 import winreg as wreg
41 41 except ImportError:
42 42 import _winreg as wreg
43 43
44 44 #Add entries that needs to be stubbed by the testing code
45 45 (wreg.OpenKey, wreg.QueryValueEx,) = (None, None)
46 46
47 47 #-----------------------------------------------------------------------------
48 48 # Globals
49 49 #-----------------------------------------------------------------------------
50 50 env = os.environ
51 51 TMP_TEST_DIR = tempfile.mkdtemp()
52 52 HOME_TEST_DIR = join(TMP_TEST_DIR, "home_test_dir")
53 53 #
54 54 # Setup/teardown functions/decorators
55 55 #
56 56
57 57 def setup_module():
58 58 """Setup testenvironment for the module:
59 59
60 60 - Adds dummy home dir tree
61 61 """
62 62 # Do not mask exceptions here. In particular, catching WindowsError is a
63 63 # problem because that exception is only defined on Windows...
64 64 os.makedirs(os.path.join(HOME_TEST_DIR, 'ipython'))
65 65
66 66
67 67 def teardown_module():
68 68 """Teardown testenvironment for the module:
69 69
70 70 - Remove dummy home dir tree
71 71 """
72 72 # Note: we remove the parent test dir, which is the root of all test
73 73 # subdirs we may have created. Use shutil instead of os.removedirs, so
74 74 # that non-empty directories are all recursively removed.
75 75 shutil.rmtree(TMP_TEST_DIR)
76 76
77 77
78 def setup_environment():
79 """Setup testenvironment for some functions that are tested
80 in this module. In particular this functions stores attributes
81 and other things that we need to stub in some test functions.
82 This needs to be done on a function level and not module level because
83 each testfunction needs a pristine environment.
84 """
78 # Build decorator that uses the setup_environment/setup_environment
79 @pytest.fixture
80 def environment():
85 81 global oldstuff, platformstuff
86 oldstuff = (env.copy(), os.name, sys.platform, path.get_home_dir, IPython.__file__, os.getcwd())
82 oldstuff = (
83 env.copy(),
84 os.name,
85 sys.platform,
86 path.get_home_dir,
87 IPython.__file__,
88 os.getcwd(),
89 )
87 90
88 def teardown_environment():
89 """Restore things that were remembered by the setup_environment function
90 """
91 (oldenv, os.name, sys.platform, path.get_home_dir, IPython.__file__, old_wd) = oldstuff
91 yield
92
93 (
94 oldenv,
95 os.name,
96 sys.platform,
97 path.get_home_dir,
98 IPython.__file__,
99 old_wd,
100 ) = oldstuff
92 101 os.chdir(old_wd)
93 102 reload(path)
94 103
95 104 for key in list(env):
96 105 if key not in oldenv:
97 106 del env[key]
98 107 env.update(oldenv)
99 if hasattr(sys, 'frozen'):
100 del sys.frozen
101
102
103 # Build decorator that uses the setup_environment/setup_environment
104 @pytest.fixture
105 def environment():
106 setup_environment()
107 yield
108 teardown_environment()
108 assert not hasattr(sys, "frozen")
109 109
110 110
111 111 with_environment = pytest.mark.usefixtures("environment")
112 112
113 113
114 114 @skip_if_not_win32
115 115 @with_environment
116 116 def test_get_home_dir_1(monkeypatch):
117 117 """Testcase for py2exe logic, un-compressed lib
118 118 """
119 119 unfrozen = path.get_home_dir()
120 monkeypatch.setattr(sys, "frozen", True)
120 monkeypatch.setattr(sys, "frozen", True, raising=False)
121 121
122 122 #fake filename for IPython.__init__
123 123 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
124 124
125 125 home_dir = path.get_home_dir()
126 126 assert home_dir == unfrozen
127 127
128 128
129 129 @skip_if_not_win32
130 130 @with_environment
131 131 def test_get_home_dir_2(monkeypatch):
132 132 """Testcase for py2exe logic, compressed lib
133 133 """
134 134 unfrozen = path.get_home_dir()
135 monkeypatch.setattr(sys, "frozen", True)
135 monkeypatch.setattr(sys, "frozen", True, raising=False)
136 136 # fake filename for IPython.__init__
137 137 IPython.__file__ = abspath(
138 138 join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")
139 139 ).lower()
140 140
141 141 home_dir = path.get_home_dir(True)
142 142 assert home_dir == unfrozen
143 143
144 144
145 145 @skip_win32
146 146 @with_environment
147 147 def test_get_home_dir_3():
148 148 """get_home_dir() uses $HOME if set"""
149 149 env["HOME"] = HOME_TEST_DIR
150 150 home_dir = path.get_home_dir(True)
151 151 # get_home_dir expands symlinks
152 152 assert home_dir == os.path.realpath(env["HOME"])
153 153
154 154
155 155 @with_environment
156 156 def test_get_home_dir_4():
157 157 """get_home_dir() still works if $HOME is not set"""
158 158
159 159 if 'HOME' in env: del env['HOME']
160 160 # this should still succeed, but we don't care what the answer is
161 161 home = path.get_home_dir(False)
162 162
163 163 @skip_win32
164 164 @with_environment
165 165 def test_get_home_dir_5(monkeypatch):
166 166 """raise HomeDirError if $HOME is specified, but not a writable dir"""
167 167 env['HOME'] = abspath(HOME_TEST_DIR+'garbage')
168 168 # set os.name = posix, to prevent My Documents fallback on Windows
169 169 monkeypatch.setattr(os, "name", "posix")
170 170 pytest.raises(path.HomeDirError, path.get_home_dir, True)
171 171
172 172 # Should we stub wreg fully so we can run the test on all platforms?
173 173 @skip_if_not_win32
174 174 @with_environment
175 175 def test_get_home_dir_8(monkeypatch):
176 176 """Using registry hack for 'My Documents', os=='nt'
177 177
178 178 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
179 179 """
180 180 monkeypatch.setattr(os, "name", "nt")
181 181 # Remove from stub environment all keys that may be set
182 182 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
183 183 env.pop(key, None)
184 184
185 185 class key:
186 186 def __enter__(self):
187 187 pass
188 188 def Close(self):
189 189 pass
190 190 def __exit__(*args, **kwargs):
191 191 pass
192 192
193 193 with patch.object(wreg, 'OpenKey', return_value=key()), \
194 194 patch.object(wreg, 'QueryValueEx', return_value=[abspath(HOME_TEST_DIR)]):
195 195 home_dir = path.get_home_dir()
196 196 assert home_dir == abspath(HOME_TEST_DIR)
197 197
198 198 @with_environment
199 199 def test_get_xdg_dir_0(monkeypatch):
200 200 """test_get_xdg_dir_0, check xdg_dir"""
201 201 monkeypatch.setattr(path, "_writable_dir", lambda path: True)
202 202 monkeypatch.setattr(path, "get_home_dir", lambda: "somewhere")
203 203 monkeypatch.setattr(os, "name", "posix")
204 204 monkeypatch.setattr(sys, "platform", "linux2")
205 205 env.pop('IPYTHON_DIR', None)
206 206 env.pop('IPYTHONDIR', None)
207 207 env.pop('XDG_CONFIG_HOME', None)
208 208
209 209 assert path.get_xdg_dir() == os.path.join("somewhere", ".config")
210 210
211 211
212 212 @with_environment
213 213 def test_get_xdg_dir_1(monkeypatch):
214 214 """test_get_xdg_dir_1, check nonexistent xdg_dir"""
215 215 monkeypatch.setattr(path, "get_home_dir", lambda: HOME_TEST_DIR)
216 216 monkeypatch.setattr(os, "name", "posix")
217 217 monkeypatch.setattr(sys, "platform", "linux2")
218 218 env.pop("IPYTHON_DIR", None)
219 219 env.pop("IPYTHONDIR", None)
220 220 env.pop("XDG_CONFIG_HOME", None)
221 221 assert path.get_xdg_dir() is None
222 222
223 223 @with_environment
224 224 def test_get_xdg_dir_2(monkeypatch):
225 225 """test_get_xdg_dir_2, check xdg_dir default to ~/.config"""
226 226 monkeypatch.setattr(path, "get_home_dir", lambda: HOME_TEST_DIR)
227 227 monkeypatch.setattr(os, "name", "posix")
228 228 monkeypatch.setattr(sys, "platform", "linux2")
229 229 env.pop("IPYTHON_DIR", None)
230 230 env.pop("IPYTHONDIR", None)
231 231 env.pop("XDG_CONFIG_HOME", None)
232 232 cfgdir = os.path.join(path.get_home_dir(), ".config")
233 233 if not os.path.exists(cfgdir):
234 234 os.makedirs(cfgdir)
235 235
236 236 assert path.get_xdg_dir() == cfgdir
237 237
238 238 @with_environment
239 239 def test_get_xdg_dir_3(monkeypatch):
240 240 """test_get_xdg_dir_3, check xdg_dir not used on non-posix systems"""
241 241 monkeypatch.setattr(path, "get_home_dir", lambda: HOME_TEST_DIR)
242 242 monkeypatch.setattr(os, "name", "nt")
243 243 monkeypatch.setattr(sys, "platform", "win32")
244 244 env.pop("IPYTHON_DIR", None)
245 245 env.pop("IPYTHONDIR", None)
246 246 env.pop("XDG_CONFIG_HOME", None)
247 247 cfgdir = os.path.join(path.get_home_dir(), ".config")
248 248 os.makedirs(cfgdir, exist_ok=True)
249 249
250 250 assert path.get_xdg_dir() is None
251 251
252 252 def test_filefind():
253 253 """Various tests for filefind"""
254 254 f = tempfile.NamedTemporaryFile()
255 255 # print('fname:',f.name)
256 256 alt_dirs = paths.get_ipython_dir()
257 257 t = path.filefind(f.name, alt_dirs)
258 258 # print('found:',t)
259 259
260 260
261 261 @dec.skip_if_not_win32
262 262 def test_get_long_path_name_win32():
263 263 with TemporaryDirectory() as tmpdir:
264 264
265 265 # Make a long path. Expands the path of tmpdir prematurely as it may already have a long
266 266 # path component, so ensure we include the long form of it
267 267 long_path = os.path.join(path.get_long_path_name(tmpdir), 'this is my long path name')
268 268 os.makedirs(long_path)
269 269
270 270 # Test to see if the short path evaluates correctly.
271 271 short_path = os.path.join(tmpdir, 'THISIS~1')
272 272 evaluated_path = path.get_long_path_name(short_path)
273 273 assert evaluated_path.lower() == long_path.lower()
274 274
275 275
276 276 @dec.skip_win32
277 277 def test_get_long_path_name():
278 278 p = path.get_long_path_name("/usr/local")
279 279 assert p == "/usr/local"
280 280
281 281
282 class TestRaiseDeprecation(unittest.TestCase):
283
284 282 @dec.skip_win32 # can't create not-user-writable dir on win
285 283 @with_environment
286 def test_not_writable_ipdir(self):
284 def test_not_writable_ipdir():
287 285 tmpdir = tempfile.mkdtemp()
288 286 os.name = "posix"
289 env.pop('IPYTHON_DIR', None)
290 env.pop('IPYTHONDIR', None)
291 env.pop('XDG_CONFIG_HOME', None)
292 env['HOME'] = tmpdir
293 ipdir = os.path.join(tmpdir, '.ipython')
287 env.pop("IPYTHON_DIR", None)
288 env.pop("IPYTHONDIR", None)
289 env.pop("XDG_CONFIG_HOME", None)
290 env["HOME"] = tmpdir
291 ipdir = os.path.join(tmpdir, ".ipython")
294 292 os.mkdir(ipdir, 0o555)
295 293 try:
296 294 open(os.path.join(ipdir, "_foo_"), "w", encoding="utf-8").close()
297 295 except IOError:
298 296 pass
299 297 else:
300 298 # I can still write to an unwritable dir,
301 299 # assume I'm root and skip the test
302 300 pytest.skip("I can't create directories that I can't write to")
303 301
304 with self.assertWarnsRegex(UserWarning, 'is not a writable location'):
302 with pytest.warns(UserWarning, match="is not a writable location"):
305 303 ipdir = paths.get_ipython_dir()
306 env.pop('IPYTHON_DIR', None)
304 env.pop("IPYTHON_DIR", None)
305
307 306
308 307 @with_environment
309 308 def test_get_py_filename():
310 309 os.chdir(TMP_TEST_DIR)
311 310 with make_tempfile("foo.py"):
312 311 assert path.get_py_filename("foo.py") == "foo.py"
313 312 assert path.get_py_filename("foo") == "foo.py"
314 313 with make_tempfile("foo"):
315 314 assert path.get_py_filename("foo") == "foo"
316 315 pytest.raises(IOError, path.get_py_filename, "foo.py")
317 316 pytest.raises(IOError, path.get_py_filename, "foo")
318 317 pytest.raises(IOError, path.get_py_filename, "foo.py")
319 318 true_fn = "foo with spaces.py"
320 319 with make_tempfile(true_fn):
321 320 assert path.get_py_filename("foo with spaces") == true_fn
322 321 assert path.get_py_filename("foo with spaces.py") == true_fn
323 322 pytest.raises(IOError, path.get_py_filename, '"foo with spaces.py"')
324 323 pytest.raises(IOError, path.get_py_filename, "'foo with spaces.py'")
325 324
326 325 @onlyif_unicode_paths
327 326 def test_unicode_in_filename():
328 327 """When a file doesn't exist, the exception raised should be safe to call
329 328 str() on - i.e. in Python 2 it must only have ASCII characters.
330 329
331 330 https://github.com/ipython/ipython/issues/875
332 331 """
333 332 try:
334 333 # these calls should not throw unicode encode exceptions
335 334 path.get_py_filename('fooéè.py')
336 335 except IOError as ex:
337 336 str(ex)
338 337
339 338
340 339 class TestShellGlob(unittest.TestCase):
341 340
342 341 @classmethod
343 342 def setUpClass(cls):
344 343 cls.filenames_start_with_a = ['a0', 'a1', 'a2']
345 344 cls.filenames_end_with_b = ['0b', '1b', '2b']
346 345 cls.filenames = cls.filenames_start_with_a + cls.filenames_end_with_b
347 346 cls.tempdir = TemporaryDirectory()
348 347 td = cls.tempdir.name
349 348
350 349 with cls.in_tempdir():
351 350 # Create empty files
352 351 for fname in cls.filenames:
353 352 open(os.path.join(td, fname), "w", encoding="utf-8").close()
354 353
355 354 @classmethod
356 355 def tearDownClass(cls):
357 356 cls.tempdir.cleanup()
358 357
359 358 @classmethod
360 359 @contextmanager
361 360 def in_tempdir(cls):
362 361 save = os.getcwd()
363 362 try:
364 363 os.chdir(cls.tempdir.name)
365 364 yield
366 365 finally:
367 366 os.chdir(save)
368 367
369 368 def check_match(self, patterns, matches):
370 369 with self.in_tempdir():
371 370 # glob returns unordered list. that's why sorted is required.
372 371 assert sorted(path.shellglob(patterns)) == sorted(matches)
373 372
374 373 def common_cases(self):
375 374 return [
376 375 (['*'], self.filenames),
377 376 (['a*'], self.filenames_start_with_a),
378 377 (['*c'], ['*c']),
379 378 (['*', 'a*', '*b', '*c'], self.filenames
380 379 + self.filenames_start_with_a
381 380 + self.filenames_end_with_b
382 381 + ['*c']),
383 382 (['a[012]'], self.filenames_start_with_a),
384 383 ]
385 384
386 385 @skip_win32
387 386 def test_match_posix(self):
388 387 for (patterns, matches) in self.common_cases() + [
389 388 ([r'\*'], ['*']),
390 389 ([r'a\*', 'a*'], ['a*'] + self.filenames_start_with_a),
391 390 ([r'a\[012]'], ['a[012]']),
392 391 ]:
393 392 self.check_match(patterns, matches)
394 393
395 394 @skip_if_not_win32
396 395 def test_match_windows(self):
397 396 for (patterns, matches) in self.common_cases() + [
398 397 # In windows, backslash is interpreted as path
399 398 # separator. Therefore, you can't escape glob
400 399 # using it.
401 400 ([r'a\*', 'a*'], [r'a\*'] + self.filenames_start_with_a),
402 401 ([r'a\[012]'], [r'a\[012]']),
403 402 ]:
404 403 self.check_match(patterns, matches)
405 404
406 405
407 406 @pytest.mark.parametrize(
408 407 "globstr, unescaped_globstr",
409 408 [
410 409 (r"\*\[\!\]\?", "*[!]?"),
411 410 (r"\\*", r"\*"),
412 411 (r"\\\*", r"\*"),
413 412 (r"\\a", r"\a"),
414 413 (r"\a", r"\a"),
415 414 ],
416 415 )
417 416 def test_unescape_glob(globstr, unescaped_globstr):
418 417 assert path.unescape_glob(globstr) == unescaped_globstr
419 418
420 419
421 420 @onlyif_unicode_paths
422 421 def test_ensure_dir_exists():
423 422 with TemporaryDirectory() as td:
424 423 d = os.path.join(td, 'βˆ‚ir')
425 424 path.ensure_dir_exists(d) # create it
426 425 assert os.path.isdir(d)
427 426 path.ensure_dir_exists(d) # no-op
428 427 f = os.path.join(td, "Ζ’ile")
429 428 open(f, "w", encoding="utf-8").close() # touch
430 429 with pytest.raises(IOError):
431 430 path.ensure_dir_exists(f)
432 431
433 432 class TestLinkOrCopy(unittest.TestCase):
434 433 def setUp(self):
435 434 self.tempdir = TemporaryDirectory()
436 435 self.src = self.dst("src")
437 436 with open(self.src, "w", encoding="utf-8") as f:
438 437 f.write("Hello, world!")
439 438
440 439 def tearDown(self):
441 440 self.tempdir.cleanup()
442 441
443 442 def dst(self, *args):
444 443 return os.path.join(self.tempdir.name, *args)
445 444
446 445 def assert_inode_not_equal(self, a, b):
447 446 assert (
448 447 os.stat(a).st_ino != os.stat(b).st_ino
449 448 ), "%r and %r do reference the same indoes" % (a, b)
450 449
451 450 def assert_inode_equal(self, a, b):
452 451 assert (
453 452 os.stat(a).st_ino == os.stat(b).st_ino
454 453 ), "%r and %r do not reference the same indoes" % (a, b)
455 454
456 455 def assert_content_equal(self, a, b):
457 456 with open(a, "rb") as a_f:
458 457 with open(b, "rb") as b_f:
459 458 assert a_f.read() == b_f.read()
460 459
461 460 @skip_win32
462 461 def test_link_successful(self):
463 462 dst = self.dst("target")
464 463 path.link_or_copy(self.src, dst)
465 464 self.assert_inode_equal(self.src, dst)
466 465
467 466 @skip_win32
468 467 def test_link_into_dir(self):
469 468 dst = self.dst("some_dir")
470 469 os.mkdir(dst)
471 470 path.link_or_copy(self.src, dst)
472 471 expected_dst = self.dst("some_dir", os.path.basename(self.src))
473 472 self.assert_inode_equal(self.src, expected_dst)
474 473
475 474 @skip_win32
476 475 def test_target_exists(self):
477 476 dst = self.dst("target")
478 477 open(dst, "w", encoding="utf-8").close()
479 478 path.link_or_copy(self.src, dst)
480 479 self.assert_inode_equal(self.src, dst)
481 480
482 481 @skip_win32
483 482 def test_no_link(self):
484 483 real_link = os.link
485 484 try:
486 485 del os.link
487 486 dst = self.dst("target")
488 487 path.link_or_copy(self.src, dst)
489 488 self.assert_content_equal(self.src, dst)
490 489 self.assert_inode_not_equal(self.src, dst)
491 490 finally:
492 491 os.link = real_link
493 492
494 493 @skip_if_not_win32
495 494 def test_windows(self):
496 495 dst = self.dst("target")
497 496 path.link_or_copy(self.src, dst)
498 497 self.assert_content_equal(self.src, dst)
499 498
500 499 def test_link_twice(self):
501 500 # Linking the same file twice shouldn't leave duplicates around.
502 501 # See https://github.com/ipython/ipython/issues/6450
503 502 dst = self.dst('target')
504 503 path.link_or_copy(self.src, dst)
505 504 path.link_or_copy(self.src, dst)
506 505 self.assert_inode_equal(self.src, dst)
507 506 assert sorted(os.listdir(self.tempdir.name)) == ["src", "target"]
General Comments 0
You need to be logged in to leave comments. Login now