blob: cef4300aff939d2207fee8f5ec72111c9f245bc7 [file] [log] [blame]
Fredrik Lundh5b3687d2004-10-12 15:26:28 +00001import unittest
2from test import test_support
3import subprocess
4import sys
5import signal
6import os
7import tempfile
8import time
Tim Peters3761e8d2004-10-13 04:07:12 +00009import re
Ezio Melotti8f6a2872010-02-10 21:40:33 +000010import sysconfig
Fredrik Lundh5b3687d2004-10-12 15:26:28 +000011
12mswindows = (sys.platform == "win32")
13
14#
15# Depends on the following external programs: Python
16#
17
18if mswindows:
Tim Peters3b01a702004-10-12 22:19:32 +000019 SETBINARY = ('import msvcrt; msvcrt.setmode(sys.stdout.fileno(), '
20 'os.O_BINARY);')
Fredrik Lundh5b3687d2004-10-12 15:26:28 +000021else:
22 SETBINARY = ''
23
Tim Peters3761e8d2004-10-13 04:07:12 +000024# In a debug build, stuff like "[6580 refs]" is printed to stderr at
25# shutdown time. That frustrates tests trying to check stderr produced
26# from a spawned Python process.
27def remove_stderr_debug_decorations(stderr):
Tim Peters1dbf2432004-10-14 04:16:54 +000028 return re.sub(r"\[\d+ refs\]\r?\n?$", "", stderr)
Tim Peters3761e8d2004-10-13 04:07:12 +000029
Fredrik Lundh5b3687d2004-10-12 15:26:28 +000030class ProcessTestCase(unittest.TestCase):
Neal Norwitzb15ac312006-06-29 04:10:08 +000031 def setUp(self):
Tim Peters38ff36c2006-06-30 06:18:39 +000032 # Try to minimize the number of children we have so this test
33 # doesn't crash on some buildbots (Alphas in particular).
Peter Astrand2b221ed2006-07-10 20:39:49 +000034 if hasattr(test_support, "reap_children"):
35 test_support.reap_children()
Neal Norwitzb15ac312006-06-29 04:10:08 +000036
37 def tearDown(self):
Tim Peters38ff36c2006-06-30 06:18:39 +000038 # Try to minimize the number of children we have so this test
39 # doesn't crash on some buildbots (Alphas in particular).
Peter Astrand2b221ed2006-07-10 20:39:49 +000040 if hasattr(test_support, "reap_children"):
41 test_support.reap_children()
Neal Norwitzb15ac312006-06-29 04:10:08 +000042
Fredrik Lundh5b3687d2004-10-12 15:26:28 +000043 def mkstemp(self):
44 """wrapper for mkstemp, calling mktemp if mkstemp is not available"""
45 if hasattr(tempfile, "mkstemp"):
46 return tempfile.mkstemp()
47 else:
48 fname = tempfile.mktemp()
49 return os.open(fname, os.O_RDWR|os.O_CREAT), fname
Tim Peterse718f612004-10-12 21:51:32 +000050
Fredrik Lundh5b3687d2004-10-12 15:26:28 +000051 #
52 # Generic tests
53 #
54 def test_call_seq(self):
Tim Peters7b759da2004-10-12 22:29:54 +000055 # call() function with sequence argument
Tim Peters3b01a702004-10-12 22:19:32 +000056 rc = subprocess.call([sys.executable, "-c",
57 "import sys; sys.exit(47)"])
Fredrik Lundh5b3687d2004-10-12 15:26:28 +000058 self.assertEqual(rc, 47)
59
Peter Astrand454f7672005-01-01 09:36:35 +000060 def test_check_call_zero(self):
61 # check_call() function with zero return code
62 rc = subprocess.check_call([sys.executable, "-c",
63 "import sys; sys.exit(0)"])
64 self.assertEqual(rc, 0)
65
66 def test_check_call_nonzero(self):
67 # check_call() function with non-zero return code
68 try:
69 subprocess.check_call([sys.executable, "-c",
70 "import sys; sys.exit(47)"])
71 except subprocess.CalledProcessError, e:
Peter Astrand7d1d4362006-07-14 14:04:45 +000072 self.assertEqual(e.returncode, 47)
Peter Astrand454f7672005-01-01 09:36:35 +000073 else:
74 self.fail("Expected CalledProcessError")
75
Gregory P. Smith26576802008-12-05 02:27:01 +000076 def test_check_output(self):
77 # check_output() function with zero return code
78 output = subprocess.check_output(
Gregory P. Smith97f49f42008-12-04 20:21:09 +000079 [sys.executable, "-c", "print 'BDFL'"])
Ezio Melottiaa980582010-01-23 23:04:36 +000080 self.assertIn('BDFL', output)
Gregory P. Smith97f49f42008-12-04 20:21:09 +000081
Gregory P. Smith26576802008-12-05 02:27:01 +000082 def test_check_output_nonzero(self):
Gregory P. Smith97f49f42008-12-04 20:21:09 +000083 # check_call() function with non-zero return code
84 try:
Gregory P. Smith26576802008-12-05 02:27:01 +000085 subprocess.check_output(
Gregory P. Smith97f49f42008-12-04 20:21:09 +000086 [sys.executable, "-c", "import sys; sys.exit(5)"])
87 except subprocess.CalledProcessError, e:
88 self.assertEqual(e.returncode, 5)
89 else:
90 self.fail("Expected CalledProcessError")
91
Gregory P. Smith26576802008-12-05 02:27:01 +000092 def test_check_output_stderr(self):
93 # check_output() function stderr redirected to stdout
94 output = subprocess.check_output(
Gregory P. Smith97f49f42008-12-04 20:21:09 +000095 [sys.executable, "-c", "import sys; sys.stderr.write('BDFL')"],
96 stderr=subprocess.STDOUT)
Ezio Melottiaa980582010-01-23 23:04:36 +000097 self.assertIn('BDFL', output)
Gregory P. Smith97f49f42008-12-04 20:21:09 +000098
Gregory P. Smith26576802008-12-05 02:27:01 +000099 def test_check_output_stdout_arg(self):
100 # check_output() function stderr redirected to stdout
Gregory P. Smith97f49f42008-12-04 20:21:09 +0000101 try:
Gregory P. Smith26576802008-12-05 02:27:01 +0000102 output = subprocess.check_output(
Gregory P. Smith97f49f42008-12-04 20:21:09 +0000103 [sys.executable, "-c", "print 'will not be run'"],
104 stdout=sys.stdout)
105 except ValueError, e:
Ezio Melottiaa980582010-01-23 23:04:36 +0000106 self.assertIn('stdout', e.args[0])
Gregory P. Smith97f49f42008-12-04 20:21:09 +0000107 else:
108 self.fail("Expected ValueError when stdout arg supplied.")
109
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000110 def test_call_kwargs(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000111 # call() function with keyword args
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000112 newenv = os.environ.copy()
113 newenv["FRUIT"] = "banana"
114 rc = subprocess.call([sys.executable, "-c",
115 'import sys, os;' \
116 'sys.exit(os.getenv("FRUIT")=="banana")'],
117 env=newenv)
118 self.assertEqual(rc, 1)
119
120 def test_stdin_none(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000121 # .stdin is None when not redirected
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000122 p = subprocess.Popen([sys.executable, "-c", 'print "banana"'],
123 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
124 p.wait()
125 self.assertEqual(p.stdin, None)
126
127 def test_stdout_none(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000128 # .stdout is None when not redirected
Tim Peters29b6b4f2004-10-13 03:43:40 +0000129 p = subprocess.Popen([sys.executable, "-c",
Tim Peters4052fe52004-10-13 03:29:54 +0000130 'print " this bit of output is from a '
131 'test of stdout in a different '
132 'process ..."'],
133 stdin=subprocess.PIPE, stderr=subprocess.PIPE)
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000134 p.wait()
135 self.assertEqual(p.stdout, None)
136
137 def test_stderr_none(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000138 # .stderr is None when not redirected
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000139 p = subprocess.Popen([sys.executable, "-c", 'print "banana"'],
140 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
141 p.wait()
142 self.assertEqual(p.stderr, None)
143
Ezio Melotti8f6a2872010-02-10 21:40:33 +0000144 def test_executable_with_cwd(self):
145 python_dir = os.path.dirname(os.path.realpath(sys.executable))
146 p = subprocess.Popen(["somethingyoudonthave", "-c",
147 "import sys; sys.exit(47)"],
148 executable=sys.executable, cwd=python_dir)
149 p.wait()
150 self.assertEqual(p.returncode, 47)
151
152 @unittest.skipIf(sysconfig.is_python_build(),
153 "need an installed Python. See #7774")
154 def test_executable_without_cwd(self):
155 # For a normal installation, it should work without 'cwd'
156 # argument. For test runs in the build directory, see #7774.
157 p = subprocess.Popen(["somethingyoudonthave", "-c",
158 "import sys; sys.exit(47)"],
Tim Peters3b01a702004-10-12 22:19:32 +0000159 executable=sys.executable)
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000160 p.wait()
161 self.assertEqual(p.returncode, 47)
162
163 def test_stdin_pipe(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000164 # stdin redirection
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000165 p = subprocess.Popen([sys.executable, "-c",
166 'import sys; sys.exit(sys.stdin.read() == "pear")'],
167 stdin=subprocess.PIPE)
168 p.stdin.write("pear")
169 p.stdin.close()
170 p.wait()
171 self.assertEqual(p.returncode, 1)
172
173 def test_stdin_filedes(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000174 # stdin is set to open file descriptor
Tim Peterse718f612004-10-12 21:51:32 +0000175 tf = tempfile.TemporaryFile()
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000176 d = tf.fileno()
177 os.write(d, "pear")
178 os.lseek(d, 0, 0)
179 p = subprocess.Popen([sys.executable, "-c",
180 'import sys; sys.exit(sys.stdin.read() == "pear")'],
181 stdin=d)
182 p.wait()
183 self.assertEqual(p.returncode, 1)
184
185 def test_stdin_fileobj(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000186 # stdin is set to open file object
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000187 tf = tempfile.TemporaryFile()
188 tf.write("pear")
189 tf.seek(0)
190 p = subprocess.Popen([sys.executable, "-c",
191 'import sys; sys.exit(sys.stdin.read() == "pear")'],
192 stdin=tf)
193 p.wait()
194 self.assertEqual(p.returncode, 1)
195
196 def test_stdout_pipe(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000197 # stdout redirection
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000198 p = subprocess.Popen([sys.executable, "-c",
199 'import sys; sys.stdout.write("orange")'],
200 stdout=subprocess.PIPE)
201 self.assertEqual(p.stdout.read(), "orange")
202
203 def test_stdout_filedes(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000204 # stdout is set to open file descriptor
Tim Peterse718f612004-10-12 21:51:32 +0000205 tf = tempfile.TemporaryFile()
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000206 d = tf.fileno()
207 p = subprocess.Popen([sys.executable, "-c",
208 'import sys; sys.stdout.write("orange")'],
209 stdout=d)
210 p.wait()
211 os.lseek(d, 0, 0)
212 self.assertEqual(os.read(d, 1024), "orange")
213
214 def test_stdout_fileobj(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000215 # stdout is set to open file object
Tim Peterse718f612004-10-12 21:51:32 +0000216 tf = tempfile.TemporaryFile()
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000217 p = subprocess.Popen([sys.executable, "-c",
218 'import sys; sys.stdout.write("orange")'],
219 stdout=tf)
220 p.wait()
221 tf.seek(0)
222 self.assertEqual(tf.read(), "orange")
223
224 def test_stderr_pipe(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000225 # stderr redirection
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000226 p = subprocess.Popen([sys.executable, "-c",
227 'import sys; sys.stderr.write("strawberry")'],
228 stderr=subprocess.PIPE)
Tim Peters3761e8d2004-10-13 04:07:12 +0000229 self.assertEqual(remove_stderr_debug_decorations(p.stderr.read()),
230 "strawberry")
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000231
232 def test_stderr_filedes(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000233 # stderr is set to open file descriptor
Tim Peterse718f612004-10-12 21:51:32 +0000234 tf = tempfile.TemporaryFile()
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000235 d = tf.fileno()
236 p = subprocess.Popen([sys.executable, "-c",
237 'import sys; sys.stderr.write("strawberry")'],
238 stderr=d)
239 p.wait()
240 os.lseek(d, 0, 0)
Tim Peters3761e8d2004-10-13 04:07:12 +0000241 self.assertEqual(remove_stderr_debug_decorations(os.read(d, 1024)),
242 "strawberry")
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000243
244 def test_stderr_fileobj(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000245 # stderr is set to open file object
Tim Peterse718f612004-10-12 21:51:32 +0000246 tf = tempfile.TemporaryFile()
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000247 p = subprocess.Popen([sys.executable, "-c",
248 'import sys; sys.stderr.write("strawberry")'],
249 stderr=tf)
250 p.wait()
251 tf.seek(0)
Tim Peters3761e8d2004-10-13 04:07:12 +0000252 self.assertEqual(remove_stderr_debug_decorations(tf.read()),
253 "strawberry")
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000254
255 def test_stdout_stderr_pipe(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000256 # capture stdout and stderr to the same pipe
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000257 p = subprocess.Popen([sys.executable, "-c",
258 'import sys;' \
259 'sys.stdout.write("apple");' \
260 'sys.stdout.flush();' \
261 'sys.stderr.write("orange")'],
262 stdout=subprocess.PIPE,
263 stderr=subprocess.STDOUT)
Tim Peters3761e8d2004-10-13 04:07:12 +0000264 output = p.stdout.read()
265 stripped = remove_stderr_debug_decorations(output)
266 self.assertEqual(stripped, "appleorange")
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000267
268 def test_stdout_stderr_file(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000269 # capture stdout and stderr to the same open file
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000270 tf = tempfile.TemporaryFile()
271 p = subprocess.Popen([sys.executable, "-c",
272 'import sys;' \
273 'sys.stdout.write("apple");' \
274 'sys.stdout.flush();' \
275 'sys.stderr.write("orange")'],
276 stdout=tf,
277 stderr=tf)
278 p.wait()
279 tf.seek(0)
Tim Peters3761e8d2004-10-13 04:07:12 +0000280 output = tf.read()
281 stripped = remove_stderr_debug_decorations(output)
282 self.assertEqual(stripped, "appleorange")
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000283
Gustavo Niemeyerc36bede2006-09-07 00:48:33 +0000284 def test_stdout_filedes_of_stdout(self):
285 # stdout is set to 1 (#1531862).
286 cmd = r"import sys, os; sys.exit(os.write(sys.stdout.fileno(), '.\n'))"
287 rc = subprocess.call([sys.executable, "-c", cmd], stdout=1)
288 self.assertEquals(rc, 2)
289
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000290 def test_cwd(self):
Guido van Rossume9a0e882007-12-20 17:28:10 +0000291 tmpdir = tempfile.gettempdir()
Peter Astrand195404f2004-11-12 15:51:48 +0000292 # We cannot use os.path.realpath to canonicalize the path,
293 # since it doesn't expand Tru64 {memb} strings. See bug 1063571.
294 cwd = os.getcwd()
295 os.chdir(tmpdir)
296 tmpdir = os.getcwd()
297 os.chdir(cwd)
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000298 p = subprocess.Popen([sys.executable, "-c",
299 'import sys,os;' \
300 'sys.stdout.write(os.getcwd())'],
301 stdout=subprocess.PIPE,
302 cwd=tmpdir)
Fredrik Lundh59c05592004-10-13 06:55:40 +0000303 normcase = os.path.normcase
304 self.assertEqual(normcase(p.stdout.read()), normcase(tmpdir))
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000305
306 def test_env(self):
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000307 newenv = os.environ.copy()
308 newenv["FRUIT"] = "orange"
309 p = subprocess.Popen([sys.executable, "-c",
310 'import sys,os;' \
311 'sys.stdout.write(os.getenv("FRUIT"))'],
312 stdout=subprocess.PIPE,
313 env=newenv)
314 self.assertEqual(p.stdout.read(), "orange")
315
Peter Astrandcbac93c2005-03-03 20:24:28 +0000316 def test_communicate_stdin(self):
317 p = subprocess.Popen([sys.executable, "-c",
318 'import sys; sys.exit(sys.stdin.read() == "pear")'],
319 stdin=subprocess.PIPE)
320 p.communicate("pear")
321 self.assertEqual(p.returncode, 1)
322
323 def test_communicate_stdout(self):
324 p = subprocess.Popen([sys.executable, "-c",
325 'import sys; sys.stdout.write("pineapple")'],
326 stdout=subprocess.PIPE)
327 (stdout, stderr) = p.communicate()
328 self.assertEqual(stdout, "pineapple")
329 self.assertEqual(stderr, None)
330
331 def test_communicate_stderr(self):
332 p = subprocess.Popen([sys.executable, "-c",
333 'import sys; sys.stderr.write("pineapple")'],
334 stderr=subprocess.PIPE)
335 (stdout, stderr) = p.communicate()
336 self.assertEqual(stdout, None)
Gregory P. Smith4036fd42008-05-26 20:22:14 +0000337 self.assertEqual(remove_stderr_debug_decorations(stderr), "pineapple")
Peter Astrandcbac93c2005-03-03 20:24:28 +0000338
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000339 def test_communicate(self):
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000340 p = subprocess.Popen([sys.executable, "-c",
Gregory P. Smith4036fd42008-05-26 20:22:14 +0000341 'import sys,os;'
342 'sys.stderr.write("pineapple");'
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000343 'sys.stdout.write(sys.stdin.read())'],
Tim Peters3b01a702004-10-12 22:19:32 +0000344 stdin=subprocess.PIPE,
345 stdout=subprocess.PIPE,
346 stderr=subprocess.PIPE)
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000347 (stdout, stderr) = p.communicate("banana")
348 self.assertEqual(stdout, "banana")
Tim Peters3761e8d2004-10-13 04:07:12 +0000349 self.assertEqual(remove_stderr_debug_decorations(stderr),
350 "pineapple")
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000351
Gregory P. Smith4036fd42008-05-26 20:22:14 +0000352 # This test is Linux specific for simplicity to at least have
353 # some coverage. It is not a platform specific bug.
354 if os.path.isdir('/proc/%d/fd' % os.getpid()):
355 # Test for the fd leak reported in http://bugs.python.org/issue2791.
356 def test_communicate_pipe_fd_leak(self):
357 fd_directory = '/proc/%d/fd' % os.getpid()
358 num_fds_before_popen = len(os.listdir(fd_directory))
359 p = subprocess.Popen([sys.executable, '-c', 'print()'],
360 stdout=subprocess.PIPE)
361 p.communicate()
362 num_fds_after_communicate = len(os.listdir(fd_directory))
363 del p
364 num_fds_after_destruction = len(os.listdir(fd_directory))
365 self.assertEqual(num_fds_before_popen, num_fds_after_destruction)
366 self.assertEqual(num_fds_before_popen, num_fds_after_communicate)
367
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000368 def test_communicate_returns(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000369 # communicate() should return None if no redirection is active
Tim Peters3b01a702004-10-12 22:19:32 +0000370 p = subprocess.Popen([sys.executable, "-c",
371 "import sys; sys.exit(47)"])
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000372 (stdout, stderr) = p.communicate()
373 self.assertEqual(stdout, None)
374 self.assertEqual(stderr, None)
375
376 def test_communicate_pipe_buf(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000377 # communicate() with writes larger than pipe_buf
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000378 # This test will probably deadlock rather than fail, if
Tim Peterse718f612004-10-12 21:51:32 +0000379 # communicate() does not work properly.
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000380 x, y = os.pipe()
381 if mswindows:
382 pipe_buf = 512
383 else:
384 pipe_buf = os.fpathconf(x, "PC_PIPE_BUF")
385 os.close(x)
386 os.close(y)
387 p = subprocess.Popen([sys.executable, "-c",
Tim Peterse718f612004-10-12 21:51:32 +0000388 'import sys,os;'
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000389 'sys.stdout.write(sys.stdin.read(47));' \
390 'sys.stderr.write("xyz"*%d);' \
391 'sys.stdout.write(sys.stdin.read())' % pipe_buf],
Tim Peters3b01a702004-10-12 22:19:32 +0000392 stdin=subprocess.PIPE,
393 stdout=subprocess.PIPE,
394 stderr=subprocess.PIPE)
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000395 string_to_write = "abc"*pipe_buf
396 (stdout, stderr) = p.communicate(string_to_write)
397 self.assertEqual(stdout, string_to_write)
398
399 def test_writes_before_communicate(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000400 # stdin.write before communicate()
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000401 p = subprocess.Popen([sys.executable, "-c",
402 'import sys,os;' \
403 'sys.stdout.write(sys.stdin.read())'],
Tim Peters3b01a702004-10-12 22:19:32 +0000404 stdin=subprocess.PIPE,
405 stdout=subprocess.PIPE,
406 stderr=subprocess.PIPE)
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000407 p.stdin.write("banana")
408 (stdout, stderr) = p.communicate("split")
409 self.assertEqual(stdout, "bananasplit")
Tim Peters3761e8d2004-10-13 04:07:12 +0000410 self.assertEqual(remove_stderr_debug_decorations(stderr), "")
Tim Peterse718f612004-10-12 21:51:32 +0000411
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000412 def test_universal_newlines(self):
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000413 p = subprocess.Popen([sys.executable, "-c",
Tim Peters3b01a702004-10-12 22:19:32 +0000414 'import sys,os;' + SETBINARY +
415 'sys.stdout.write("line1\\n");'
416 'sys.stdout.flush();'
417 'sys.stdout.write("line2\\r");'
418 'sys.stdout.flush();'
419 'sys.stdout.write("line3\\r\\n");'
420 'sys.stdout.flush();'
421 'sys.stdout.write("line4\\r");'
422 'sys.stdout.flush();'
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000423 'sys.stdout.write("\\nline5");'
Tim Peters3b01a702004-10-12 22:19:32 +0000424 'sys.stdout.flush();'
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000425 'sys.stdout.write("\\nline6");'],
426 stdout=subprocess.PIPE,
427 universal_newlines=1)
428 stdout = p.stdout.read()
Neal Norwitza6d01ce2006-05-02 06:23:22 +0000429 if hasattr(file, 'newlines'):
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000430 # Interpreter with universal newline support
Tim Peters3b01a702004-10-12 22:19:32 +0000431 self.assertEqual(stdout,
432 "line1\nline2\nline3\nline4\nline5\nline6")
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000433 else:
434 # Interpreter without universal newline support
Tim Peters3b01a702004-10-12 22:19:32 +0000435 self.assertEqual(stdout,
436 "line1\nline2\rline3\r\nline4\r\nline5\nline6")
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000437
438 def test_universal_newlines_communicate(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000439 # universal newlines through communicate()
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000440 p = subprocess.Popen([sys.executable, "-c",
Tim Peters3b01a702004-10-12 22:19:32 +0000441 'import sys,os;' + SETBINARY +
442 'sys.stdout.write("line1\\n");'
443 'sys.stdout.flush();'
444 'sys.stdout.write("line2\\r");'
445 'sys.stdout.flush();'
446 'sys.stdout.write("line3\\r\\n");'
447 'sys.stdout.flush();'
448 'sys.stdout.write("line4\\r");'
449 'sys.stdout.flush();'
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000450 'sys.stdout.write("\\nline5");'
Tim Peters3b01a702004-10-12 22:19:32 +0000451 'sys.stdout.flush();'
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000452 'sys.stdout.write("\\nline6");'],
453 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
454 universal_newlines=1)
455 (stdout, stderr) = p.communicate()
Neal Norwitza6d01ce2006-05-02 06:23:22 +0000456 if hasattr(file, 'newlines'):
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000457 # Interpreter with universal newline support
Tim Peters3b01a702004-10-12 22:19:32 +0000458 self.assertEqual(stdout,
459 "line1\nline2\nline3\nline4\nline5\nline6")
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000460 else:
461 # Interpreter without universal newline support
462 self.assertEqual(stdout, "line1\nline2\rline3\r\nline4\r\nline5\nline6")
463
464 def test_no_leaking(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000465 # Make sure we leak no resources
Peter Astrandd6b24302006-06-22 20:06:46 +0000466 if not hasattr(test_support, "is_resource_enabled") \
467 or test_support.is_resource_enabled("subprocess") and not mswindows:
Peter Astrandf7f1bb72005-03-03 20:47:37 +0000468 max_handles = 1026 # too much for most UNIX systems
469 else:
Tim Peterseba28be2005-03-28 01:08:02 +0000470 max_handles = 65
Fredrik Lundh9e29fc52004-10-13 07:54:54 +0000471 for i in range(max_handles):
Tim Peters3b01a702004-10-12 22:19:32 +0000472 p = subprocess.Popen([sys.executable, "-c",
473 "import sys;sys.stdout.write(sys.stdin.read())"],
474 stdin=subprocess.PIPE,
475 stdout=subprocess.PIPE,
476 stderr=subprocess.PIPE)
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000477 data = p.communicate("lime")[0]
478 self.assertEqual(data, "lime")
479
480
481 def test_list2cmdline(self):
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000482 self.assertEqual(subprocess.list2cmdline(['a b c', 'd', 'e']),
483 '"a b c" d e')
484 self.assertEqual(subprocess.list2cmdline(['ab"c', '\\', 'd']),
485 'ab\\"c \\ d')
Gregory P. Smithe047e6d2008-01-19 20:49:02 +0000486 self.assertEqual(subprocess.list2cmdline(['ab"c', ' \\', 'd']),
487 'ab\\"c " \\\\" d')
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000488 self.assertEqual(subprocess.list2cmdline(['a\\\\\\b', 'de fg', 'h']),
489 'a\\\\\\b "de fg" h')
490 self.assertEqual(subprocess.list2cmdline(['a\\"b', 'c', 'd']),
491 'a\\\\\\"b c d')
492 self.assertEqual(subprocess.list2cmdline(['a\\\\b c', 'd', 'e']),
493 '"a\\\\b c" d e')
494 self.assertEqual(subprocess.list2cmdline(['a\\\\b\\ c', 'd', 'e']),
495 '"a\\\\b\\ c" d e')
Peter Astrand10514a72007-01-13 22:35:35 +0000496 self.assertEqual(subprocess.list2cmdline(['ab', '']),
497 'ab ""')
Gregory P. Smith70eb2f92008-01-19 22:49:37 +0000498 self.assertEqual(subprocess.list2cmdline(['echo', 'foo|bar']),
499 'echo "foo|bar"')
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000500
501
502 def test_poll(self):
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000503 p = subprocess.Popen([sys.executable,
Tim Peters29b6b4f2004-10-13 03:43:40 +0000504 "-c", "import time; time.sleep(1)"])
505 count = 0
506 while p.poll() is None:
507 time.sleep(0.1)
508 count += 1
509 # We expect that the poll loop probably went around about 10 times,
510 # but, based on system scheduling we can't control, it's possible
511 # poll() never returned None. It "should be" very rare that it
512 # didn't go around at least twice.
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000513 self.assertTrue(count >= 2)
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000514 # Subsequent invocations should just return the returncode
515 self.assertEqual(p.poll(), 0)
516
517
518 def test_wait(self):
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000519 p = subprocess.Popen([sys.executable,
520 "-c", "import time; time.sleep(2)"])
521 self.assertEqual(p.wait(), 0)
522 # Subsequent invocations should just return the returncode
523 self.assertEqual(p.wait(), 0)
Tim Peterse718f612004-10-12 21:51:32 +0000524
Peter Astrand738131d2004-11-30 21:04:45 +0000525
526 def test_invalid_bufsize(self):
527 # an invalid type of the bufsize argument should raise
528 # TypeError.
529 try:
530 subprocess.Popen([sys.executable, "-c", "pass"], "orange")
531 except TypeError:
532 pass
533 else:
534 self.fail("Expected TypeError")
535
Georg Brandlf3715d22009-02-14 17:01:36 +0000536 def test_leaking_fds_on_error(self):
537 # see bug #5179: Popen leaks file descriptors to PIPEs if
538 # the child fails to execute; this will eventually exhaust
539 # the maximum number of open fds. 1024 seems a very common
540 # value for that limit, but Windows has 2048, so we loop
541 # 1024 times (each call leaked two fds).
542 for i in range(1024):
543 try:
544 subprocess.Popen(['nonexisting_i_hope'],
545 stdout=subprocess.PIPE,
546 stderr=subprocess.PIPE)
547 # Windows raises IOError
548 except (IOError, OSError), err:
549 if err.errno != 2: # ignore "no such file"
550 raise
551
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000552 #
553 # POSIX tests
554 #
555 if not mswindows:
556 def test_exceptions(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000557 # catched & re-raised exceptions
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000558 try:
559 p = subprocess.Popen([sys.executable, "-c", ""],
560 cwd="/this/path/does/not/exist")
561 except OSError, e:
562 # The attribute child_traceback should contain "os.chdir"
563 # somewhere.
564 self.assertNotEqual(e.child_traceback.find("os.chdir"), -1)
565 else:
566 self.fail("Expected OSError")
Tim Peterse718f612004-10-12 21:51:32 +0000567
Andrew M. Kuchling86e1e382006-08-01 18:16:15 +0000568 def _suppress_core_files(self):
569 """Try to prevent core files from being created.
570 Returns previous ulimit if successful, else None.
571 """
572 try:
573 import resource
574 old_limit = resource.getrlimit(resource.RLIMIT_CORE)
575 resource.setrlimit(resource.RLIMIT_CORE, (0,0))
576 return old_limit
577 except (ImportError, ValueError, resource.error):
578 return None
579
580 def _unsuppress_core_files(self, old_limit):
581 """Return core file behavior to default."""
582 if old_limit is None:
583 return
584 try:
585 import resource
586 resource.setrlimit(resource.RLIMIT_CORE, old_limit)
587 except (ImportError, ValueError, resource.error):
588 return
Tim Peters4edcba62006-08-02 03:27:46 +0000589
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000590 def test_run_abort(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000591 # returncode handles signal termination
Andrew M. Kuchling86e1e382006-08-01 18:16:15 +0000592 old_limit = self._suppress_core_files()
593 try:
594 p = subprocess.Popen([sys.executable,
595 "-c", "import os; os.abort()"])
596 finally:
597 self._unsuppress_core_files(old_limit)
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000598 p.wait()
599 self.assertEqual(-p.returncode, signal.SIGABRT)
600
601 def test_preexec(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000602 # preexec function
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000603 p = subprocess.Popen([sys.executable, "-c",
604 'import sys,os;' \
605 'sys.stdout.write(os.getenv("FRUIT"))'],
606 stdout=subprocess.PIPE,
607 preexec_fn=lambda: os.putenv("FRUIT", "apple"))
608 self.assertEqual(p.stdout.read(), "apple")
609
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000610 def test_args_string(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000611 # args is a string
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000612 f, fname = self.mkstemp()
613 os.write(f, "#!/bin/sh\n")
Gregory P. Smithaf8a6872008-05-17 07:17:34 +0000614 os.write(f, "exec '%s' -c 'import sys; sys.exit(47)'\n" %
Tim Peters3b01a702004-10-12 22:19:32 +0000615 sys.executable)
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000616 os.close(f)
617 os.chmod(fname, 0700)
618 p = subprocess.Popen(fname)
619 p.wait()
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000620 os.remove(fname)
Peter Astrand2224be62004-11-17 20:06:35 +0000621 self.assertEqual(p.returncode, 47)
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000622
623 def test_invalid_args(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000624 # invalid arguments should raise ValueError
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000625 self.assertRaises(ValueError, subprocess.call,
Tim Peters3b01a702004-10-12 22:19:32 +0000626 [sys.executable,
627 "-c", "import sys; sys.exit(47)"],
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000628 startupinfo=47)
629 self.assertRaises(ValueError, subprocess.call,
Tim Peters3b01a702004-10-12 22:19:32 +0000630 [sys.executable,
631 "-c", "import sys; sys.exit(47)"],
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000632 creationflags=47)
633
634 def test_shell_sequence(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000635 # Run command through the shell (sequence)
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000636 newenv = os.environ.copy()
637 newenv["FRUIT"] = "apple"
638 p = subprocess.Popen(["echo $FRUIT"], shell=1,
639 stdout=subprocess.PIPE,
640 env=newenv)
641 self.assertEqual(p.stdout.read().strip(), "apple")
642
643 def test_shell_string(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000644 # Run command through the shell (string)
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000645 newenv = os.environ.copy()
646 newenv["FRUIT"] = "apple"
647 p = subprocess.Popen("echo $FRUIT", shell=1,
648 stdout=subprocess.PIPE,
649 env=newenv)
650 self.assertEqual(p.stdout.read().strip(), "apple")
651
652 def test_call_string(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000653 # call() function with string argument on UNIX
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000654 f, fname = self.mkstemp()
655 os.write(f, "#!/bin/sh\n")
Gregory P. Smithaf8a6872008-05-17 07:17:34 +0000656 os.write(f, "exec '%s' -c 'import sys; sys.exit(47)'\n" %
Tim Peters3b01a702004-10-12 22:19:32 +0000657 sys.executable)
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000658 os.close(f)
659 os.chmod(fname, 0700)
660 rc = subprocess.call(fname)
Peter Astrand2224be62004-11-17 20:06:35 +0000661 os.remove(fname)
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000662 self.assertEqual(rc, 47)
663
Christian Heimesc2ca6db2008-05-06 23:42:58 +0000664 def DISABLED_test_send_signal(self):
Christian Heimese74c8f22008-04-19 02:23:57 +0000665 p = subprocess.Popen([sys.executable,
666 "-c", "input()"])
667
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000668 self.assertTrue(p.poll() is None, p.poll())
Christian Heimese74c8f22008-04-19 02:23:57 +0000669 p.send_signal(signal.SIGINT)
670 self.assertNotEqual(p.wait(), 0)
671
Christian Heimesc2ca6db2008-05-06 23:42:58 +0000672 def DISABLED_test_kill(self):
Christian Heimese74c8f22008-04-19 02:23:57 +0000673 p = subprocess.Popen([sys.executable,
674 "-c", "input()"])
675
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000676 self.assertTrue(p.poll() is None, p.poll())
Christian Heimese74c8f22008-04-19 02:23:57 +0000677 p.kill()
678 self.assertEqual(p.wait(), -signal.SIGKILL)
679
Christian Heimesc2ca6db2008-05-06 23:42:58 +0000680 def DISABLED_test_terminate(self):
Christian Heimese74c8f22008-04-19 02:23:57 +0000681 p = subprocess.Popen([sys.executable,
682 "-c", "input()"])
683
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000684 self.assertTrue(p.poll() is None, p.poll())
Christian Heimese74c8f22008-04-19 02:23:57 +0000685 p.terminate()
686 self.assertEqual(p.wait(), -signal.SIGTERM)
Tim Peterse718f612004-10-12 21:51:32 +0000687
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000688 #
689 # Windows tests
690 #
691 if mswindows:
692 def test_startupinfo(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000693 # startupinfo argument
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000694 # We uses hardcoded constants, because we do not want to
Tim Peterse718f612004-10-12 21:51:32 +0000695 # depend on win32all.
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000696 STARTF_USESHOWWINDOW = 1
697 SW_MAXIMIZE = 3
698 startupinfo = subprocess.STARTUPINFO()
699 startupinfo.dwFlags = STARTF_USESHOWWINDOW
700 startupinfo.wShowWindow = SW_MAXIMIZE
701 # Since Python is a console process, it won't be affected
702 # by wShowWindow, but the argument should be silently
703 # ignored
704 subprocess.call([sys.executable, "-c", "import sys; sys.exit(0)"],
705 startupinfo=startupinfo)
706
707 def test_creationflags(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000708 # creationflags argument
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000709 CREATE_NEW_CONSOLE = 16
Tim Peters876c4322004-10-13 03:21:35 +0000710 sys.stderr.write(" a DOS box should flash briefly ...\n")
Tim Peters3b01a702004-10-12 22:19:32 +0000711 subprocess.call(sys.executable +
Tim Peters876c4322004-10-13 03:21:35 +0000712 ' -c "import time; time.sleep(0.25)"',
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000713 creationflags=CREATE_NEW_CONSOLE)
714
715 def test_invalid_args(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000716 # invalid arguments should raise ValueError
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000717 self.assertRaises(ValueError, subprocess.call,
Tim Peters3b01a702004-10-12 22:19:32 +0000718 [sys.executable,
719 "-c", "import sys; sys.exit(47)"],
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000720 preexec_fn=lambda: 1)
721 self.assertRaises(ValueError, subprocess.call,
Tim Peters3b01a702004-10-12 22:19:32 +0000722 [sys.executable,
723 "-c", "import sys; sys.exit(47)"],
Peter Astrand81a191b2007-05-26 22:18:20 +0000724 stdout=subprocess.PIPE,
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000725 close_fds=True)
726
Peter Astrand81a191b2007-05-26 22:18:20 +0000727 def test_close_fds(self):
728 # close file descriptors
729 rc = subprocess.call([sys.executable, "-c",
730 "import sys; sys.exit(47)"],
731 close_fds=True)
732 self.assertEqual(rc, 47)
733
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000734 def test_shell_sequence(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000735 # Run command through the shell (sequence)
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000736 newenv = os.environ.copy()
737 newenv["FRUIT"] = "physalis"
738 p = subprocess.Popen(["set"], shell=1,
Tim Peterse718f612004-10-12 21:51:32 +0000739 stdout=subprocess.PIPE,
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000740 env=newenv)
741 self.assertNotEqual(p.stdout.read().find("physalis"), -1)
742
743 def test_shell_string(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000744 # Run command through the shell (string)
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000745 newenv = os.environ.copy()
746 newenv["FRUIT"] = "physalis"
747 p = subprocess.Popen("set", shell=1,
Tim Peterse718f612004-10-12 21:51:32 +0000748 stdout=subprocess.PIPE,
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000749 env=newenv)
750 self.assertNotEqual(p.stdout.read().find("physalis"), -1)
751
752 def test_call_string(self):
Tim Peters7b759da2004-10-12 22:29:54 +0000753 # call() function with string argument on Windows
Tim Peters3b01a702004-10-12 22:19:32 +0000754 rc = subprocess.call(sys.executable +
755 ' -c "import sys; sys.exit(47)"')
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000756 self.assertEqual(rc, 47)
757
Christian Heimesc2ca6db2008-05-06 23:42:58 +0000758 def DISABLED_test_send_signal(self):
Christian Heimese74c8f22008-04-19 02:23:57 +0000759 p = subprocess.Popen([sys.executable,
760 "-c", "input()"])
761
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000762 self.assertTrue(p.poll() is None, p.poll())
Christian Heimese74c8f22008-04-19 02:23:57 +0000763 p.send_signal(signal.SIGTERM)
764 self.assertNotEqual(p.wait(), 0)
765
Christian Heimesc2ca6db2008-05-06 23:42:58 +0000766 def DISABLED_test_kill(self):
Christian Heimese74c8f22008-04-19 02:23:57 +0000767 p = subprocess.Popen([sys.executable,
768 "-c", "input()"])
769
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000770 self.assertTrue(p.poll() is None, p.poll())
Christian Heimese74c8f22008-04-19 02:23:57 +0000771 p.kill()
772 self.assertNotEqual(p.wait(), 0)
773
Christian Heimesc2ca6db2008-05-06 23:42:58 +0000774 def DISABLED_test_terminate(self):
Christian Heimese74c8f22008-04-19 02:23:57 +0000775 p = subprocess.Popen([sys.executable,
776 "-c", "input()"])
777
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000778 self.assertTrue(p.poll() is None, p.poll())
Christian Heimese74c8f22008-04-19 02:23:57 +0000779 p.terminate()
780 self.assertNotEqual(p.wait(), 0)
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000781
Gregory P. Smithdd7ca242009-07-04 01:49:29 +0000782
783unit_tests = [ProcessTestCase]
784
Amaury Forgeot d'Arcce32eb72009-07-09 22:37:22 +0000785if getattr(subprocess, '_has_poll', False):
Gregory P. Smithdd7ca242009-07-04 01:49:29 +0000786 class ProcessTestCaseNoPoll(ProcessTestCase):
787 def setUp(self):
788 subprocess._has_poll = False
789 ProcessTestCase.setUp(self)
790
791 def tearDown(self):
792 subprocess._has_poll = True
793 ProcessTestCase.tearDown(self)
794
795 unit_tests.append(ProcessTestCaseNoPoll)
796
797
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000798def test_main():
Gregory P. Smithdd7ca242009-07-04 01:49:29 +0000799 test_support.run_unittest(*unit_tests)
Peter Astrand2b221ed2006-07-10 20:39:49 +0000800 if hasattr(test_support, "reap_children"):
801 test_support.reap_children()
Fredrik Lundh5b3687d2004-10-12 15:26:28 +0000802
803if __name__ == "__main__":
804 test_main()