blob: d96aae7f7e76bf5ec0d64d1bae20b258c994e629 [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)
271
272 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)
312 self.assertEqual(mode, 0600)
313
314 def test_noinherit(self):
315 """_mkstemp_inner file handles are not inherited by child processes"""
316 # FIXME: Find a way to test this on Windows.
317 if os.name != 'posix':
318 return # ugh, can't use TestSkipped.
319
320 file = self.do_create()
321
322 # We have to exec something, so that FD_CLOEXEC will take
323 # effect. The sanest thing to try is /bin/sh; we can easily
324 # instruct it to attempt to write to the fd and report success
325 # or failure. Unfortunately, sh syntax does not permit use of
326 # fds numerically larger than 9; abandon this test if so.
327 if file.fd > 9:
328 raise test_support.TestSkipped, 'cannot test with fd %d' % file.fd
329
330 pid = os.fork()
331 if pid:
332 status = os.wait()[1]
333 self.failUnless(os.WIFEXITED(status),
334 "child process did not exit (status %d)" % status)
335
336 # We want the child to have exited _un_successfully, indicating
337 # failure to write to the closed fd.
338 self.failUnless(os.WEXITSTATUS(status) != 0,
339 "child process exited successfully")
340
341 else:
342 try:
343 # Throw away stderr.
344 nul = os.open('/dev/null', os.O_RDWR)
345 os.dup2(nul, 2)
346 os.execv('/bin/sh', ['sh', '-c', 'echo blat >&%d' % file.fd])
347 except:
348 os._exit(0)
349
350 def test_textmode(self):
351 """_mkstemp_inner can create files in text mode"""
352 if not has_textmode:
353 return # ugh, can't use TestSkipped.
354
355 self.do_create(bin=0).write("blat\n")
356 # XXX should test that the file really is a text file
357
358test_classes.append(test__mkstemp_inner)
359
360
361class test_gettempprefix(TC):
362 """Test gettempprefix()."""
363
364 def test_sane_template(self):
365 """gettempprefix returns a nonempty prefix string"""
366 p = tempfile.gettempprefix()
367
368 self.assert_(isinstance(p, basestring))
369 self.assert_(len(p) > 0)
370
371 def test_usable_template(self):
372 """gettempprefix returns a usable prefix string"""
373
374 # Create a temp directory, avoiding use of the prefix.
375 # Then attempt to create a file whose name is
376 # prefix + 'xxxxxx.xxx' in that directory.
377 p = tempfile.gettempprefix() + "xxxxxx.xxx"
378 d = tempfile.mkdtemp(prefix="")
379 try:
380 p = os.path.join(d, p)
381 try:
382 fd = os.open(p, os.O_RDWR | os.O_CREAT)
383 except:
384 self.failOnException("os.open")
385 os.close(fd)
386 os.unlink(p)
387 finally:
388 os.rmdir(d)
389
390test_classes.append(test_gettempprefix)
391
392
393class test_gettempdir(TC):
394 """Test gettempdir()."""
395
396 def test_directory_exists(self):
397 """gettempdir returns a directory which exists"""
398
399 dir = tempfile.gettempdir()
400 self.assert_(os.path.isabs(dir) or dir == os.curdir,
401 "%s is not an absolute path" % dir)
402 self.assert_(os.path.isdir(dir),
403 "%s is not a directory" % dir)
404
405 def test_directory_writable(self):
406 """gettempdir returns a directory writable by the user"""
407
408 # sneaky: just instantiate a NamedTemporaryFile, which
409 # defaults to writing into the directory returned by
410 # gettempdir.
411 try:
412 file = tempfile.NamedTemporaryFile()
413 file.write("blat")
414 file.close()
415 except:
416 self.failOnException("create file in %s" % tempfile.gettempdir())
417
418 def test_same_thing(self):
419 """gettempdir always returns the same object"""
420 a = tempfile.gettempdir()
421 b = tempfile.gettempdir()
422
423 self.assert_(a is b)
424
425test_classes.append(test_gettempdir)
426
427
428class test_mkstemp(TC):
429 """Test mkstemp()."""
430 def do_create(self, dir=None, pre="", suf="", ):
431 if dir is None:
432 dir = tempfile.gettempdir()
433 try:
434 (fd, name) = tempfile.mkstemp(dir=dir, prefix=pre, suffix=suf)
435 except:
436 self.failOnException("mkstemp")
437
438 try:
439 self.nameCheck(name, dir, pre, suf)
440 finally:
441 os.close(fd)
442 os.unlink(name)
443
444 def test_basic(self):
445 """mkstemp can create files"""
446 self.do_create()
447 self.do_create(pre="a")
448 self.do_create(suf="b")
449 self.do_create(pre="a", suf="b")
450 self.do_create(pre="aa", suf=".txt")
451
452 def test_choose_directory(self):
453 """mkstemp can create directories in a user-selected directory"""
454 dir = tempfile.mkdtemp()
455 try:
456 self.do_create(dir=dir)
457 finally:
458 os.rmdir(dir)
459
460test_classes.append(test_mkstemp)
461
462
463class test_mkdtemp(TC):
464 """Test mkdtemp()."""
465
466 def do_create(self, dir=None, pre="", suf=""):
467 if dir is None:
468 dir = tempfile.gettempdir()
469 try:
470 name = tempfile.mkdtemp(dir=dir, prefix=pre, suffix=suf)
471 except:
472 self.failOnException("mkdtemp")
473
474 try:
475 self.nameCheck(name, dir, pre, suf)
476 return name
477 except:
478 os.rmdir(name)
479 raise
480
481 def test_basic(self):
482 """mkdtemp can create directories"""
483 os.rmdir(self.do_create())
484 os.rmdir(self.do_create(pre="a"))
485 os.rmdir(self.do_create(suf="b"))
486 os.rmdir(self.do_create(pre="a", suf="b"))
487 os.rmdir(self.do_create(pre="aa", suf=".txt"))
488
489 def test_basic_many(self):
490 """mkdtemp can create many directories (stochastic)"""
491 extant = range(1000)
492 try:
493 for i in extant:
494 extant[i] = self.do_create(pre="aa")
495 finally:
496 for i in extant:
497 if(isinstance(i, basestring)):
498 os.rmdir(i)
499
500 def test_choose_directory(self):
501 """mkdtemp can create directories in a user-selected directory"""
502 dir = tempfile.mkdtemp()
503 try:
504 os.rmdir(self.do_create(dir=dir))
505 finally:
506 os.rmdir(dir)
507
508 def test_mode(self):
509 """mkdtemp creates directories with the proper mode"""
510 if not has_stat:
511 return # ugh, can't use TestSkipped.
512
513 dir = self.do_create()
514 try:
515 mode = stat.S_IMODE(os.stat(dir).st_mode)
516 self.assertEqual(mode, 0700)
517 finally:
518 os.rmdir(dir)
519
520test_classes.append(test_mkdtemp)
521
522
523class test_mktemp(TC):
524 """Test mktemp()."""
525
526 # For safety, all use of mktemp must occur in a private directory.
527 # We must also suppress the RuntimeWarning it generates.
528 def setUp(self):
529 self.dir = tempfile.mkdtemp()
530 warnings.filterwarnings("ignore",
531 category=RuntimeWarning,
532 message="mktemp")
533
534 def tearDown(self):
535 if self.dir:
536 os.rmdir(self.dir)
537 self.dir = None
538 # XXX This clobbers any -W options.
539 warnings.resetwarnings()
540
541 class mktemped:
542 _unlink = os.unlink
543 _bflags = tempfile._bin_openflags
544
545 def __init__(self, dir, pre, suf):
546 self.name = tempfile.mktemp(dir=dir, prefix=pre, suffix=suf)
547 # Create the file. This will raise an exception if it's
548 # mysteriously appeared in the meanwhile.
549 os.close(os.open(self.name, self._bflags, 0600))
550
551 def __del__(self):
552 self._unlink(self.name)
553
554 def do_create(self, pre="", suf=""):
555 try:
556 file = self.mktemped(self.dir, pre, suf)
557 except:
558 self.failOnException("mktemp")
559
560 self.nameCheck(file.name, self.dir, pre, suf)
561 return file
562
563 def test_basic(self):
564 """mktemp can choose usable file names"""
565 self.do_create()
566 self.do_create(pre="a")
567 self.do_create(suf="b")
568 self.do_create(pre="a", suf="b")
569 self.do_create(pre="aa", suf=".txt")
570
571 def test_many(self):
572 """mktemp can choose many usable file names (stochastic)"""
573 extant = range(1000)
574 for i in extant:
575 extant[i] = self.do_create(pre="aa")
576
577 def test_warning(self):
578 """mktemp issues a warning when used"""
579 warnings.filterwarnings("error",
580 category=RuntimeWarning,
581 message="mktemp")
582 self.assertRaises(RuntimeWarning,
583 tempfile.mktemp, (), { 'dir': self.dir })
584
585test_classes.append(test_mktemp)
586
587
588# We test _TemporaryFileWrapper by testing NamedTemporaryFile.
589
590
591class test_NamedTemporaryFile(TC):
592 """Test NamedTemporaryFile()."""
593
594 def do_create(self, dir=None, pre="", suf=""):
595 if dir is None:
596 dir = tempfile.gettempdir()
597 try:
598 file = tempfile.NamedTemporaryFile(dir=dir, prefix=pre, suffix=suf)
599 except:
600 self.failOnException("NamedTemporaryFile")
601
602 self.nameCheck(file.name, dir, pre, suf)
603 return file
604
605
606 def test_basic(self):
607 """NamedTemporaryFile can create files"""
608 self.do_create()
609 self.do_create(pre="a")
610 self.do_create(suf="b")
611 self.do_create(pre="a", suf="b")
612 self.do_create(pre="aa", suf=".txt")
613
614 def test_creates_named(self):
615 """NamedTemporaryFile creates files with names"""
616 f = tempfile.NamedTemporaryFile()
617 self.failUnless(os.path.exists(f.name),
618 "NamedTemporaryFile %s does not exist" % f.name)
619
620 def test_del_on_close(self):
621 """A NamedTemporaryFile is deleted when closed"""
622 dir = tempfile.mkdtemp()
623 try:
624 f = tempfile.NamedTemporaryFile(dir=dir)
625 f.write('blat')
626 f.close()
627 self.failIf(os.path.exists(f.name),
628 "NamedTemporaryFile %s exists after close" % f.name)
629 finally:
630 os.rmdir(dir)
631
632 def test_multiple_close(self):
633 """A NamedTemporaryFile can be closed many times without error"""
634
635 f = tempfile.NamedTemporaryFile()
636 f.write('abc\n')
637 f.close()
638 try:
639 f.close()
640 f.close()
641 except:
642 self.failOnException("close")
643
644 # How to test the mode and bufsize parameters?
645
646test_classes.append(test_NamedTemporaryFile)
647
648
649class test_TemporaryFile(TC):
650 """Test TemporaryFile()."""
651
652 def test_basic(self):
653 """TemporaryFile can create files"""
654 # No point in testing the name params - the file has no name.
655 try:
656 tempfile.TemporaryFile()
657 except:
658 self.failOnException("TemporaryFile")
659
660 def test_has_no_name(self):
661 """TemporaryFile creates files with no names (on this system)"""
662 dir = tempfile.mkdtemp()
663 f = tempfile.TemporaryFile(dir=dir)
664 f.write('blat')
665
666 # Sneaky: because this file has no name, it should not prevent
667 # us from removing the directory it was created in.
668 try:
669 os.rmdir(dir)
670 except:
671 ei = sys.exc_info()
672 # cleanup
673 f.close()
674 os.rmdir(dir)
675 self.failOnException("rmdir", ei)
676
677 def test_multiple_close(self):
678 """A TemporaryFile can be closed many times without error"""
679 f = tempfile.TemporaryFile()
680 f.write('abc\n')
681 f.close()
682 try:
683 f.close()
684 f.close()
685 except:
686 self.failOnException("close")
687
688 # How to test the mode and bufsize parameters?
689
690class dummy_test_TemporaryFile(TC):
691 def test_dummy(self):
692 """TemporaryFile and NamedTemporaryFile are the same (on this system)"""
693 pass
694
695if tempfile.NamedTemporaryFile is tempfile.TemporaryFile:
696 test_classes.append(dummy_test_TemporaryFile)
697else:
698 test_classes.append(test_TemporaryFile)
699
700def test_main():
701 suite = unittest.TestSuite()
702 for c in test_classes:
703 suite.addTest(unittest.makeSuite(c))
704 test_support.run_suite(suite)
705
706if __name__ == "__main__":
707 test_main()