blob: 09d30abed1b0b76b646a18bf508fb7cff288ae3c [file] [log] [blame]
Guido van Rossum0e548712002-08-09 16:14:33 +00001# tempfile.py unit tests.
Tim Petersc57a2852001-10-29 21:46:08 +00002
3import tempfile
Guido van Rossum0e548712002-08-09 16:14:33 +00004import os
5import sys
6import re
7import errno
8import warnings
Tim Petersc57a2852001-10-29 21:46:08 +00009
Guido van Rossum0e548712002-08-09 16:14:33 +000010import unittest
11from test import test_support
12
13if hasattr(os, 'stat'):
14 import stat
15 has_stat = 1
16else:
17 has_stat = 0
18
19has_textmode = (tempfile._text_openflags != tempfile._bin_openflags)
20
21# This is organized as one test for each chunk of code in tempfile.py,
22# in order of their appearance in the file. Testing which requires
23# threads is not done here.
24
25# Common functionality.
26class TC(unittest.TestCase):
27
28 str_check = re.compile(r"[a-zA-Z0-9_-]{6}$")
29
30 def failOnException(self, what, ei=None):
31 if ei is None:
32 ei = sys.exc_info()
33 self.fail("%s raised %s: %s" % (what, ei[0], ei[1]))
34
35 def nameCheck(self, name, dir, pre, suf):
36 (ndir, nbase) = os.path.split(name)
37 npre = nbase[:len(pre)]
38 nsuf = nbase[len(nbase)-len(suf):]
39
40 self.assertEqual(ndir, dir,
41 "file '%s' not in directory '%s'" % (name, dir))
42 self.assertEqual(npre, pre,
43 "file '%s' does not begin with '%s'" % (nbase, pre))
44 self.assertEqual(nsuf, suf,
45 "file '%s' does not end with '%s'" % (nbase, suf))
46
47 nbase = nbase[len(pre):len(nbase)-len(suf)]
48 self.assert_(self.str_check.match(nbase),
49 "random string '%s' does not match /^[a-zA-Z0-9_-]{6}$/"
50 % nbase)
51
52test_classes = []
53
54class test_exports(TC):
55 def test_exports(self):
56 """There are no surprising symbols in the tempfile module"""
57 dict = tempfile.__dict__
58
59 expected = {
60 "NamedTemporaryFile" : 1,
61 "TemporaryFile" : 1,
62 "mkstemp" : 1,
63 "mkdtemp" : 1,
64 "mktemp" : 1,
65 "TMP_MAX" : 1,
66 "gettempprefix" : 1,
67 "gettempdir" : 1,
68 "tempdir" : 1,
69 "template" : 1
70 }
71
72 unexp = []
73 for key in dict:
74 if key[0] != '_' and key not in expected:
75 unexp.append(key)
76 self.failUnless(len(unexp) == 0,
77 "unexpected keys: %s" % unexp)
78
79test_classes.append(test_exports)
80
81
82class test__once(TC):
83 """Test the internal function _once."""
84
85 def setUp(self):
86 tempfile.once_var = None
87 self.already_called = 0
88
89 def tearDown(self):
90 del tempfile.once_var
91
92 def callMeOnce(self):
93 self.failIf(self.already_called, "callMeOnce called twice")
94 self.already_called = 1
95 return 24
96
97 def do_once(self):
98 tempfile._once('once_var', self.callMeOnce)
99
100 def test_once_initializes(self):
101 """_once initializes its argument"""
102
103 self.do_once()
104
105 self.assertEqual(tempfile.once_var, 24,
106 "once_var=%d, not 24" % tempfile.once_var)
107 self.assertEqual(self.already_called, 1,
108 "already_called=%d, not 1" % self.already_called)
109
110 def test_once_means_once(self):
111 """_once calls the callback just once"""
112
113 self.do_once()
114 self.do_once()
115 self.do_once()
116 self.do_once()
117
118 def test_once_namespace_safe(self):
119 """_once does not modify anything but its argument"""
120
121 env_copy = tempfile.__dict__.copy()
122
123 self.do_once()
124
125 env = tempfile.__dict__
126
127 a = env.keys()
128 a.sort()
129 b = env_copy.keys()
130 b.sort()
131
132 self.failIf(len(a) != len(b))
133 for i in xrange(len(a)):
134 self.failIf(a[i] != b[i])
135
136 key = a[i]
137 if key != 'once_var':
138 self.failIf(env[key] != env_copy[key])
139
140test_classes.append(test__once)
141
142
143class test__RandomNameSequence(TC):
144 """Test the internal iterator object _RandomNameSequence."""
145
146 def setUp(self):
147 self.r = tempfile._RandomNameSequence()
148
149 def test_get_six_char_str(self):
150 """_RandomNameSequence returns a six-character string"""
151 s = self.r.next()
152 self.nameCheck(s, '', '', '')
153
154 def test_many(self):
155 """_RandomNameSequence returns no duplicate strings (stochastic)"""
156
157 dict = {}
158 r = self.r
159 for i in xrange(1000):
160 s = r.next()
161 self.nameCheck(s, '', '', '')
162 self.failIf(s in dict)
163 dict[s] = 1
164
165 def test_supports_iter(self):
166 """_RandomNameSequence supports the iterator protocol"""
167
168 i = 0
169 r = self.r
170 try:
171 for s in r:
172 i += 1
173 if i == 20:
174 break
175 except:
176 failOnException("iteration")
177
178test_classes.append(test__RandomNameSequence)
179
180
181class test__candidate_tempdir_list(TC):
182 """Test the internal function _candidate_tempdir_list."""
183
184 def test_nonempty_list(self):
185 """_candidate_tempdir_list returns a nonempty list of strings"""
186
187 cand = tempfile._candidate_tempdir_list()
188
189 self.failIf(len(cand) == 0)
190 for c in cand:
191 self.assert_(isinstance(c, basestring),
192 "%s is not a string" % c)
193
194 def test_wanted_dirs(self):
195 """_candidate_tempdir_list contains the expected directories"""
196
197 # Make sure the interesting environment variables are all set.
198 added = []
199 try:
200 for envname in 'TMPDIR', 'TEMP', 'TMP':
201 dirname = os.getenv(envname)
202 if not dirname:
203 os.environ[envname] = os.path.abspath(envname)
204 added.append(envname)
205
206 cand = tempfile._candidate_tempdir_list()
207
208 for envname in 'TMPDIR', 'TEMP', 'TMP':
209 dirname = os.getenv(envname)
210 if not dirname: raise ValueError
211 self.assert_(dirname in cand)
212
213 try:
214 dirname = os.getcwd()
215 except (AttributeError, os.error):
216 dirname = os.curdir
217
218 self.assert_(dirname in cand)
219
220 # Not practical to try to verify the presence of OS-specific
221 # paths in this list.
222 finally:
223 for p in added:
224 del os.environ[p]
225
226test_classes.append(test__candidate_tempdir_list)
227
228
229# We test _get_default_tempdir by testing gettempdir.
230
231
232class test__get_candidate_names(TC):
233 """Test the internal function _get_candidate_names."""
234
235 def test_retval(self):
236 """_get_candidate_names returns a _RandomNameSequence object"""
237 obj = tempfile._get_candidate_names()
238 self.assert_(isinstance(obj, tempfile._RandomNameSequence))
239
240 def test_same_thing(self):
241 """_get_candidate_names always returns the same object"""
242 a = tempfile._get_candidate_names()
243 b = tempfile._get_candidate_names()
244
245 self.assert_(a is b)
246
247test_classes.append(test__get_candidate_names)
248
249
250class test__mkstemp_inner(TC):
251 """Test the internal function _mkstemp_inner."""
252
253 class mkstemped:
254 _bflags = tempfile._bin_openflags
255 _tflags = tempfile._text_openflags
256 _close = os.close
257 _unlink = os.unlink
258
259 def __init__(self, dir, pre, suf, bin):
260 if bin: flags = self._bflags
261 else: flags = self._tflags
262
263 (self.fd, self.name) = tempfile._mkstemp_inner(dir, pre, suf, flags)
264
265 def write(self, str):
266 os.write(self.fd, str)
267
268 def __del__(self):
269 self._close(self.fd)
270 self._unlink(self.name)
Tim Petersa0d55de2002-08-09 18:01:01 +0000271
Guido van Rossum0e548712002-08-09 16:14:33 +0000272 def do_create(self, dir=None, pre="", suf="", bin=1):
273 if dir is None:
274 dir = tempfile.gettempdir()
275 try:
276 file = self.mkstemped(dir, pre, suf, bin)
277 except:
278 self.failOnException("_mkstemp_inner")
279
280 self.nameCheck(file.name, dir, pre, suf)
281 return file
282
283 def test_basic(self):
284 """_mkstemp_inner can create files"""
285 self.do_create().write("blat")
286 self.do_create(pre="a").write("blat")
287 self.do_create(suf="b").write("blat")
288 self.do_create(pre="a", suf="b").write("blat")
289 self.do_create(pre="aa", suf=".txt").write("blat")
290
291 def test_basic_many(self):
292 """_mkstemp_inner can create many files (stochastic)"""
293 extant = range(1000)
294 for i in extant:
295 extant[i] = self.do_create(pre="aa")
296
297 def test_choose_directory(self):
298 """_mkstemp_inner can create files in a user-selected directory"""
299 dir = tempfile.mkdtemp()
300 try:
301 self.do_create(dir=dir).write("blat")
302 finally:
303 os.rmdir(dir)
304
305 def test_file_mode(self):
306 """_mkstemp_inner creates files with the proper mode"""
307 if not has_stat:
308 return # ugh, can't use TestSkipped.
309
310 file = self.do_create()
311 mode = stat.S_IMODE(os.stat(file.name).st_mode)
Tim Petersca3ac7f2002-08-09 18:13:51 +0000312 expected = 0600
313 if sys.platform in ('win32',):
314 # There's no distinction among 'user', 'group' and 'world';
315 # replicate the 'user' bits.
316 user = expected >> 6
317 expected = user * (1 + 8 + 64)
318 self.assertEqual(mode, expected)
Guido van Rossum0e548712002-08-09 16:14:33 +0000319
320 def test_noinherit(self):
321 """_mkstemp_inner file handles are not inherited by child processes"""
322 # FIXME: Find a way to test this on Windows.
323 if os.name != 'posix':
324 return # ugh, can't use TestSkipped.
325
326 file = self.do_create()
327
328 # We have to exec something, so that FD_CLOEXEC will take
329 # effect. The sanest thing to try is /bin/sh; we can easily
330 # instruct it to attempt to write to the fd and report success
331 # or failure. Unfortunately, sh syntax does not permit use of
332 # fds numerically larger than 9; abandon this test if so.
333 if file.fd > 9:
334 raise test_support.TestSkipped, 'cannot test with fd %d' % file.fd
335
336 pid = os.fork()
337 if pid:
338 status = os.wait()[1]
339 self.failUnless(os.WIFEXITED(status),
340 "child process did not exit (status %d)" % status)
341
342 # We want the child to have exited _un_successfully, indicating
343 # failure to write to the closed fd.
344 self.failUnless(os.WEXITSTATUS(status) != 0,
345 "child process exited successfully")
346
347 else:
348 try:
349 # Throw away stderr.
350 nul = os.open('/dev/null', os.O_RDWR)
351 os.dup2(nul, 2)
352 os.execv('/bin/sh', ['sh', '-c', 'echo blat >&%d' % file.fd])
353 except:
354 os._exit(0)
355
356 def test_textmode(self):
357 """_mkstemp_inner can create files in text mode"""
358 if not has_textmode:
359 return # ugh, can't use TestSkipped.
360
361 self.do_create(bin=0).write("blat\n")
362 # XXX should test that the file really is a text file
363
364test_classes.append(test__mkstemp_inner)
365
366
367class test_gettempprefix(TC):
368 """Test gettempprefix()."""
369
370 def test_sane_template(self):
371 """gettempprefix returns a nonempty prefix string"""
372 p = tempfile.gettempprefix()
373
374 self.assert_(isinstance(p, basestring))
375 self.assert_(len(p) > 0)
376
377 def test_usable_template(self):
378 """gettempprefix returns a usable prefix string"""
379
380 # Create a temp directory, avoiding use of the prefix.
381 # Then attempt to create a file whose name is
382 # prefix + 'xxxxxx.xxx' in that directory.
383 p = tempfile.gettempprefix() + "xxxxxx.xxx"
384 d = tempfile.mkdtemp(prefix="")
385 try:
386 p = os.path.join(d, p)
387 try:
388 fd = os.open(p, os.O_RDWR | os.O_CREAT)
389 except:
390 self.failOnException("os.open")
391 os.close(fd)
392 os.unlink(p)
393 finally:
394 os.rmdir(d)
395
396test_classes.append(test_gettempprefix)
397
398
399class test_gettempdir(TC):
400 """Test gettempdir()."""
401
402 def test_directory_exists(self):
403 """gettempdir returns a directory which exists"""
404
405 dir = tempfile.gettempdir()
406 self.assert_(os.path.isabs(dir) or dir == os.curdir,
407 "%s is not an absolute path" % dir)
408 self.assert_(os.path.isdir(dir),
409 "%s is not a directory" % dir)
410
411 def test_directory_writable(self):
412 """gettempdir returns a directory writable by the user"""
413
414 # sneaky: just instantiate a NamedTemporaryFile, which
415 # defaults to writing into the directory returned by
416 # gettempdir.
417 try:
418 file = tempfile.NamedTemporaryFile()
419 file.write("blat")
420 file.close()
421 except:
422 self.failOnException("create file in %s" % tempfile.gettempdir())
423
424 def test_same_thing(self):
425 """gettempdir always returns the same object"""
426 a = tempfile.gettempdir()
427 b = tempfile.gettempdir()
428
429 self.assert_(a is b)
430
431test_classes.append(test_gettempdir)
432
433
434class test_mkstemp(TC):
435 """Test mkstemp()."""
436 def do_create(self, dir=None, pre="", suf="", ):
437 if dir is None:
438 dir = tempfile.gettempdir()
439 try:
440 (fd, name) = tempfile.mkstemp(dir=dir, prefix=pre, suffix=suf)
441 except:
442 self.failOnException("mkstemp")
443
444 try:
445 self.nameCheck(name, dir, pre, suf)
446 finally:
447 os.close(fd)
448 os.unlink(name)
449
450 def test_basic(self):
451 """mkstemp can create files"""
452 self.do_create()
453 self.do_create(pre="a")
454 self.do_create(suf="b")
455 self.do_create(pre="a", suf="b")
456 self.do_create(pre="aa", suf=".txt")
457
458 def test_choose_directory(self):
459 """mkstemp can create directories in a user-selected directory"""
460 dir = tempfile.mkdtemp()
461 try:
462 self.do_create(dir=dir)
463 finally:
464 os.rmdir(dir)
465
466test_classes.append(test_mkstemp)
467
468
469class test_mkdtemp(TC):
470 """Test mkdtemp()."""
471
472 def do_create(self, dir=None, pre="", suf=""):
473 if dir is None:
474 dir = tempfile.gettempdir()
475 try:
476 name = tempfile.mkdtemp(dir=dir, prefix=pre, suffix=suf)
477 except:
478 self.failOnException("mkdtemp")
479
480 try:
481 self.nameCheck(name, dir, pre, suf)
482 return name
483 except:
484 os.rmdir(name)
485 raise
486
487 def test_basic(self):
488 """mkdtemp can create directories"""
489 os.rmdir(self.do_create())
490 os.rmdir(self.do_create(pre="a"))
491 os.rmdir(self.do_create(suf="b"))
492 os.rmdir(self.do_create(pre="a", suf="b"))
493 os.rmdir(self.do_create(pre="aa", suf=".txt"))
Tim Petersa0d55de2002-08-09 18:01:01 +0000494
Guido van Rossum0e548712002-08-09 16:14:33 +0000495 def test_basic_many(self):
496 """mkdtemp can create many directories (stochastic)"""
497 extant = range(1000)
498 try:
499 for i in extant:
500 extant[i] = self.do_create(pre="aa")
501 finally:
502 for i in extant:
503 if(isinstance(i, basestring)):
504 os.rmdir(i)
505
506 def test_choose_directory(self):
507 """mkdtemp can create directories in a user-selected directory"""
508 dir = tempfile.mkdtemp()
509 try:
510 os.rmdir(self.do_create(dir=dir))
511 finally:
512 os.rmdir(dir)
513
514 def test_mode(self):
515 """mkdtemp creates directories with the proper mode"""
516 if not has_stat:
517 return # ugh, can't use TestSkipped.
518
519 dir = self.do_create()
520 try:
521 mode = stat.S_IMODE(os.stat(dir).st_mode)
Tim Petersca3ac7f2002-08-09 18:13:51 +0000522 expected = 0700
523 if sys.platform in ('win32',):
524 # There's no distinction among 'user', 'group' and 'world';
525 # replicate the 'user' bits.
526 user = expected >> 6
527 expected = user * (1 + 8 + 64)
528 self.assertEqual(mode, expected)
Guido van Rossum0e548712002-08-09 16:14:33 +0000529 finally:
530 os.rmdir(dir)
531
532test_classes.append(test_mkdtemp)
533
534
535class test_mktemp(TC):
536 """Test mktemp()."""
537
538 # For safety, all use of mktemp must occur in a private directory.
539 # We must also suppress the RuntimeWarning it generates.
540 def setUp(self):
541 self.dir = tempfile.mkdtemp()
542 warnings.filterwarnings("ignore",
543 category=RuntimeWarning,
544 message="mktemp")
545
546 def tearDown(self):
547 if self.dir:
548 os.rmdir(self.dir)
549 self.dir = None
550 # XXX This clobbers any -W options.
551 warnings.resetwarnings()
552
553 class mktemped:
554 _unlink = os.unlink
555 _bflags = tempfile._bin_openflags
556
557 def __init__(self, dir, pre, suf):
558 self.name = tempfile.mktemp(dir=dir, prefix=pre, suffix=suf)
559 # Create the file. This will raise an exception if it's
560 # mysteriously appeared in the meanwhile.
561 os.close(os.open(self.name, self._bflags, 0600))
562
563 def __del__(self):
564 self._unlink(self.name)
565
566 def do_create(self, pre="", suf=""):
567 try:
568 file = self.mktemped(self.dir, pre, suf)
569 except:
570 self.failOnException("mktemp")
571
572 self.nameCheck(file.name, self.dir, pre, suf)
573 return file
574
575 def test_basic(self):
576 """mktemp can choose usable file names"""
577 self.do_create()
578 self.do_create(pre="a")
579 self.do_create(suf="b")
580 self.do_create(pre="a", suf="b")
581 self.do_create(pre="aa", suf=".txt")
582
583 def test_many(self):
584 """mktemp can choose many usable file names (stochastic)"""
585 extant = range(1000)
586 for i in extant:
587 extant[i] = self.do_create(pre="aa")
588
589 def test_warning(self):
590 """mktemp issues a warning when used"""
591 warnings.filterwarnings("error",
592 category=RuntimeWarning,
593 message="mktemp")
594 self.assertRaises(RuntimeWarning,
595 tempfile.mktemp, (), { 'dir': self.dir })
596
597test_classes.append(test_mktemp)
598
599
600# We test _TemporaryFileWrapper by testing NamedTemporaryFile.
601
602
603class test_NamedTemporaryFile(TC):
604 """Test NamedTemporaryFile()."""
605
606 def do_create(self, dir=None, pre="", suf=""):
607 if dir is None:
608 dir = tempfile.gettempdir()
609 try:
610 file = tempfile.NamedTemporaryFile(dir=dir, prefix=pre, suffix=suf)
611 except:
612 self.failOnException("NamedTemporaryFile")
613
614 self.nameCheck(file.name, dir, pre, suf)
615 return file
616
617
618 def test_basic(self):
619 """NamedTemporaryFile can create files"""
620 self.do_create()
621 self.do_create(pre="a")
622 self.do_create(suf="b")
623 self.do_create(pre="a", suf="b")
624 self.do_create(pre="aa", suf=".txt")
625
626 def test_creates_named(self):
627 """NamedTemporaryFile creates files with names"""
628 f = tempfile.NamedTemporaryFile()
629 self.failUnless(os.path.exists(f.name),
630 "NamedTemporaryFile %s does not exist" % f.name)
631
632 def test_del_on_close(self):
633 """A NamedTemporaryFile is deleted when closed"""
634 dir = tempfile.mkdtemp()
635 try:
636 f = tempfile.NamedTemporaryFile(dir=dir)
637 f.write('blat')
638 f.close()
639 self.failIf(os.path.exists(f.name),
640 "NamedTemporaryFile %s exists after close" % f.name)
641 finally:
642 os.rmdir(dir)
643
644 def test_multiple_close(self):
645 """A NamedTemporaryFile can be closed many times without error"""
646
647 f = tempfile.NamedTemporaryFile()
648 f.write('abc\n')
649 f.close()
650 try:
651 f.close()
652 f.close()
653 except:
654 self.failOnException("close")
655
656 # How to test the mode and bufsize parameters?
657
658test_classes.append(test_NamedTemporaryFile)
659
660
661class test_TemporaryFile(TC):
662 """Test TemporaryFile()."""
663
664 def test_basic(self):
665 """TemporaryFile can create files"""
666 # No point in testing the name params - the file has no name.
667 try:
668 tempfile.TemporaryFile()
669 except:
670 self.failOnException("TemporaryFile")
671
672 def test_has_no_name(self):
673 """TemporaryFile creates files with no names (on this system)"""
674 dir = tempfile.mkdtemp()
675 f = tempfile.TemporaryFile(dir=dir)
676 f.write('blat')
677
678 # Sneaky: because this file has no name, it should not prevent
679 # us from removing the directory it was created in.
680 try:
681 os.rmdir(dir)
682 except:
683 ei = sys.exc_info()
684 # cleanup
685 f.close()
686 os.rmdir(dir)
687 self.failOnException("rmdir", ei)
688
689 def test_multiple_close(self):
690 """A TemporaryFile can be closed many times without error"""
691 f = tempfile.TemporaryFile()
692 f.write('abc\n')
693 f.close()
694 try:
695 f.close()
696 f.close()
697 except:
698 self.failOnException("close")
699
700 # How to test the mode and bufsize parameters?
701
702class dummy_test_TemporaryFile(TC):
703 def test_dummy(self):
704 """TemporaryFile and NamedTemporaryFile are the same (on this system)"""
705 pass
706
707if tempfile.NamedTemporaryFile is tempfile.TemporaryFile:
708 test_classes.append(dummy_test_TemporaryFile)
709else:
710 test_classes.append(test_TemporaryFile)
711
712def test_main():
713 suite = unittest.TestSuite()
714 for c in test_classes:
715 suite.addTest(unittest.makeSuite(c))
716 test_support.run_suite(suite)
717
718if __name__ == "__main__":
719 test_main()