blob: 902d16ae3df939d4283c72b01e6470c257c21645 [file] [log] [blame]
Neal Norwitze241ce82003-02-17 18:17:05 +00001"Test posix functions"
2
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003from test import support
R. David Murrayeb3615d2009-04-22 02:24:39 +00004
5# Skip these tests if there is no posix module.
6posix = support.import_module('posix')
Neal Norwitze241ce82003-02-17 18:17:05 +00007
Antoine Pitroub7572f02009-12-02 20:46:48 +00008import errno
Ronald Oussorenb6ee4f52010-07-23 13:53:51 +00009import sys
Neal Norwitze241ce82003-02-17 18:17:05 +000010import time
11import os
Christian Heimesd5e2b6f2008-03-19 21:50:51 +000012import pwd
Benjamin Petersondcf97b92008-07-02 17:30:14 +000013import shutil
Benjamin Peterson052a02b2010-08-17 01:27:09 +000014import stat
Neal Norwitze241ce82003-02-17 18:17:05 +000015import unittest
16import warnings
R. David Murraya21e4ca2009-03-31 23:16:50 +000017
Neal Norwitze241ce82003-02-17 18:17:05 +000018
19class PosixTester(unittest.TestCase):
20
21 def setUp(self):
22 # create empty file
Benjamin Petersonee8712c2008-05-20 21:35:26 +000023 fp = open(support.TESTFN, 'w+')
Neal Norwitze241ce82003-02-17 18:17:05 +000024 fp.close()
Brett Cannonc8d502e2010-03-20 21:53:28 +000025 self._warnings_manager = support.check_warnings()
26 self._warnings_manager.__enter__()
27 warnings.filterwarnings('ignore', '.* potential security risk .*',
28 RuntimeWarning)
Neal Norwitze241ce82003-02-17 18:17:05 +000029
30 def tearDown(self):
Neal Norwitzc34177c2008-08-25 01:04:16 +000031 support.unlink(support.TESTFN)
Brett Cannonc8d502e2010-03-20 21:53:28 +000032 self._warnings_manager.__exit__(None, None, None)
Neal Norwitze241ce82003-02-17 18:17:05 +000033
34 def testNoArgFunctions(self):
35 # test posix functions which take no arguments and have
36 # no side-effects which we need to cleanup (e.g., fork, wait, abort)
Guido van Rossumf0af3e32008-10-02 18:55:37 +000037 NO_ARG_FUNCTIONS = [ "ctermid", "getcwd", "getcwdb", "uname",
Guido van Rossum687b9c02007-10-25 23:18:51 +000038 "times", "getloadavg",
Neal Norwitze241ce82003-02-17 18:17:05 +000039 "getegid", "geteuid", "getgid", "getgroups",
40 "getpid", "getpgrp", "getppid", "getuid",
41 ]
Neal Norwitz71b13e82003-02-23 22:12:24 +000042
Neal Norwitze241ce82003-02-17 18:17:05 +000043 for name in NO_ARG_FUNCTIONS:
44 posix_func = getattr(posix, name, None)
45 if posix_func is not None:
46 posix_func()
Neal Norwitz2ff51a82003-02-17 22:40:31 +000047 self.assertRaises(TypeError, posix_func, 1)
Neal Norwitze241ce82003-02-17 18:17:05 +000048
Martin v. Löwis7aed61a2009-11-27 14:09:49 +000049 if hasattr(posix, 'getresuid'):
50 def test_getresuid(self):
51 user_ids = posix.getresuid()
52 self.assertEqual(len(user_ids), 3)
53 for val in user_ids:
54 self.assertGreaterEqual(val, 0)
55
56 if hasattr(posix, 'getresgid'):
57 def test_getresgid(self):
58 group_ids = posix.getresgid()
59 self.assertEqual(len(group_ids), 3)
60 for val in group_ids:
61 self.assertGreaterEqual(val, 0)
62
63 if hasattr(posix, 'setresuid'):
64 def test_setresuid(self):
65 current_user_ids = posix.getresuid()
66 self.assertIsNone(posix.setresuid(*current_user_ids))
67 # -1 means don't change that value.
68 self.assertIsNone(posix.setresuid(-1, -1, -1))
69
70 def test_setresuid_exception(self):
71 # Don't do this test if someone is silly enough to run us as root.
72 current_user_ids = posix.getresuid()
73 if 0 not in current_user_ids:
74 new_user_ids = (current_user_ids[0]+1, -1, -1)
75 self.assertRaises(OSError, posix.setresuid, *new_user_ids)
76
77 if hasattr(posix, 'setresgid'):
78 def test_setresgid(self):
79 current_group_ids = posix.getresgid()
80 self.assertIsNone(posix.setresgid(*current_group_ids))
81 # -1 means don't change that value.
82 self.assertIsNone(posix.setresgid(-1, -1, -1))
83
84 def test_setresgid_exception(self):
85 # Don't do this test if someone is silly enough to run us as root.
86 current_group_ids = posix.getresgid()
87 if 0 not in current_group_ids:
88 new_group_ids = (current_group_ids[0]+1, -1, -1)
89 self.assertRaises(OSError, posix.setresgid, *new_group_ids)
90
Antoine Pitroub7572f02009-12-02 20:46:48 +000091 @unittest.skipUnless(hasattr(posix, 'initgroups'),
92 "test needs os.initgroups()")
93 def test_initgroups(self):
94 # It takes a string and an integer; check that it raises a TypeError
95 # for other argument lists.
96 self.assertRaises(TypeError, posix.initgroups)
97 self.assertRaises(TypeError, posix.initgroups, None)
98 self.assertRaises(TypeError, posix.initgroups, 3, "foo")
99 self.assertRaises(TypeError, posix.initgroups, "foo", 3, object())
100
101 # If a non-privileged user invokes it, it should fail with OSError
102 # EPERM.
103 if os.getuid() != 0:
104 name = pwd.getpwuid(posix.getuid()).pw_name
105 try:
106 posix.initgroups(name, 13)
107 except OSError as e:
108 self.assertEquals(e.errno, errno.EPERM)
109 else:
110 self.fail("Expected OSError to be raised by initgroups")
111
Neal Norwitze241ce82003-02-17 18:17:05 +0000112 def test_statvfs(self):
113 if hasattr(posix, 'statvfs'):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000114 self.assertTrue(posix.statvfs(os.curdir))
Neal Norwitze241ce82003-02-17 18:17:05 +0000115
116 def test_fstatvfs(self):
117 if hasattr(posix, 'fstatvfs'):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000118 fp = open(support.TESTFN)
Neal Norwitze241ce82003-02-17 18:17:05 +0000119 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000120 self.assertTrue(posix.fstatvfs(fp.fileno()))
Neal Norwitze241ce82003-02-17 18:17:05 +0000121 finally:
122 fp.close()
123
124 def test_ftruncate(self):
125 if hasattr(posix, 'ftruncate'):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000126 fp = open(support.TESTFN, 'w+')
Neal Norwitze241ce82003-02-17 18:17:05 +0000127 try:
128 # we need to have some data to truncate
129 fp.write('test')
130 fp.flush()
131 posix.ftruncate(fp.fileno(), 0)
132 finally:
133 fp.close()
134
135 def test_dup(self):
136 if hasattr(posix, 'dup'):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000137 fp = open(support.TESTFN)
Neal Norwitze241ce82003-02-17 18:17:05 +0000138 try:
139 fd = posix.dup(fp.fileno())
Ezio Melottie9615932010-01-24 19:26:24 +0000140 self.assertIsInstance(fd, int)
Neal Norwitze241ce82003-02-17 18:17:05 +0000141 os.close(fd)
142 finally:
143 fp.close()
144
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000145 def test_confstr(self):
146 if hasattr(posix, 'confstr'):
147 self.assertRaises(ValueError, posix.confstr, "CS_garbage")
148 self.assertEqual(len(posix.confstr("CS_PATH")) > 0, True)
149
Neal Norwitze241ce82003-02-17 18:17:05 +0000150 def test_dup2(self):
151 if hasattr(posix, 'dup2'):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000152 fp1 = open(support.TESTFN)
153 fp2 = open(support.TESTFN)
Neal Norwitze241ce82003-02-17 18:17:05 +0000154 try:
155 posix.dup2(fp1.fileno(), fp2.fileno())
156 finally:
157 fp1.close()
158 fp2.close()
159
Skip Montanaro98470002005-06-17 01:14:49 +0000160 def test_osexlock(self):
161 if hasattr(posix, "O_EXLOCK"):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000162 fd = os.open(support.TESTFN,
Skip Montanaro98470002005-06-17 01:14:49 +0000163 os.O_WRONLY|os.O_EXLOCK|os.O_CREAT)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000164 self.assertRaises(OSError, os.open, support.TESTFN,
Skip Montanaro98470002005-06-17 01:14:49 +0000165 os.O_WRONLY|os.O_EXLOCK|os.O_NONBLOCK)
166 os.close(fd)
167
168 if hasattr(posix, "O_SHLOCK"):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000169 fd = os.open(support.TESTFN,
Skip Montanaro98470002005-06-17 01:14:49 +0000170 os.O_WRONLY|os.O_SHLOCK|os.O_CREAT)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000171 self.assertRaises(OSError, os.open, support.TESTFN,
Skip Montanaro98470002005-06-17 01:14:49 +0000172 os.O_WRONLY|os.O_EXLOCK|os.O_NONBLOCK)
173 os.close(fd)
174
175 def test_osshlock(self):
176 if hasattr(posix, "O_SHLOCK"):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000177 fd1 = os.open(support.TESTFN,
Skip Montanaro98470002005-06-17 01:14:49 +0000178 os.O_WRONLY|os.O_SHLOCK|os.O_CREAT)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000179 fd2 = os.open(support.TESTFN,
Skip Montanaro98470002005-06-17 01:14:49 +0000180 os.O_WRONLY|os.O_SHLOCK|os.O_CREAT)
181 os.close(fd2)
182 os.close(fd1)
183
184 if hasattr(posix, "O_EXLOCK"):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000185 fd = os.open(support.TESTFN,
Skip Montanaro98470002005-06-17 01:14:49 +0000186 os.O_WRONLY|os.O_SHLOCK|os.O_CREAT)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000187 self.assertRaises(OSError, os.open, support.TESTFN,
Skip Montanaro98470002005-06-17 01:14:49 +0000188 os.O_RDONLY|os.O_EXLOCK|os.O_NONBLOCK)
189 os.close(fd)
190
Neal Norwitze241ce82003-02-17 18:17:05 +0000191 def test_fstat(self):
192 if hasattr(posix, 'fstat'):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000193 fp = open(support.TESTFN)
Neal Norwitze241ce82003-02-17 18:17:05 +0000194 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000195 self.assertTrue(posix.fstat(fp.fileno()))
Neal Norwitze241ce82003-02-17 18:17:05 +0000196 finally:
197 fp.close()
198
199 def test_stat(self):
200 if hasattr(posix, 'stat'):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000201 self.assertTrue(posix.stat(support.TESTFN))
Neal Norwitze241ce82003-02-17 18:17:05 +0000202
Benjamin Peterson052a02b2010-08-17 01:27:09 +0000203 @unittest.skipUnless(hasattr(posix, 'mkfifo'), "don't have mkfifo()")
204 def test_mkfifo(self):
205 support.unlink(support.TESTFN)
206 posix.mkfifo(support.TESTFN, stat.S_IRUSR | stat.S_IWUSR)
207 self.assertTrue(stat.S_ISFIFO(posix.stat(support.TESTFN).st_mode))
208
209 @unittest.skipUnless(hasattr(posix, 'mknod') and hasattr(stat, 'S_IFIFO'),
210 "don't have mknod()/S_IFIFO")
211 def test_mknod(self):
212 # Test using mknod() to create a FIFO (the only use specified
213 # by POSIX).
214 support.unlink(support.TESTFN)
215 mode = stat.S_IFIFO | stat.S_IRUSR | stat.S_IWUSR
216 try:
217 posix.mknod(support.TESTFN, mode, 0)
218 except OSError as e:
219 # Some old systems don't allow unprivileged users to use
220 # mknod(), or only support creating device nodes.
221 self.assertIn(e.errno, (errno.EPERM, errno.EINVAL))
222 else:
223 self.assertTrue(stat.S_ISFIFO(posix.stat(support.TESTFN).st_mode))
224
Benjamin Peterson1baf4652009-12-31 03:11:23 +0000225 def _test_all_chown_common(self, chown_func, first_param):
226 """Common code for chown, fchown and lchown tests."""
227 if os.getuid() == 0:
228 try:
229 # Many linux distros have a nfsnobody user as MAX_UID-2
230 # that makes a good test case for signedness issues.
231 # http://bugs.python.org/issue1747858
232 # This part of the test only runs when run as root.
233 # Only scary people run their tests as root.
234 ent = pwd.getpwnam('nfsnobody')
235 chown_func(first_param, ent.pw_uid, ent.pw_gid)
236 except KeyError:
237 pass
238 else:
239 # non-root cannot chown to root, raises OSError
240 self.assertRaises(OSError, chown_func,
241 first_param, 0, 0)
242 # test a successful chown call
243 chown_func(first_param, os.getuid(), os.getgid())
Christian Heimesd5e2b6f2008-03-19 21:50:51 +0000244
Benjamin Peterson1baf4652009-12-31 03:11:23 +0000245 @unittest.skipUnless(hasattr(posix, 'chown'), "test needs os.chown()")
246 def test_chown(self):
247 # raise an OSError if the file does not exist
248 os.unlink(support.TESTFN)
249 self.assertRaises(OSError, posix.chown, support.TESTFN, -1, -1)
Christian Heimesd5e2b6f2008-03-19 21:50:51 +0000250
Benjamin Peterson1baf4652009-12-31 03:11:23 +0000251 # re-create the file
252 open(support.TESTFN, 'w').close()
253 self._test_all_chown_common(posix.chown, support.TESTFN)
254
255 @unittest.skipUnless(hasattr(posix, 'fchown'), "test needs os.fchown()")
256 def test_fchown(self):
257 os.unlink(support.TESTFN)
258
259 # re-create the file
260 test_file = open(support.TESTFN, 'w')
261 try:
262 fd = test_file.fileno()
263 self._test_all_chown_common(posix.fchown, fd)
264 finally:
265 test_file.close()
266
267 @unittest.skipUnless(hasattr(posix, 'lchown'), "test needs os.lchown()")
268 def test_lchown(self):
269 os.unlink(support.TESTFN)
270 # create a symlink
271 os.symlink('/tmp/dummy-symlink-target', support.TESTFN)
272 self._test_all_chown_common(posix.lchown, support.TESTFN)
Christian Heimesd5e2b6f2008-03-19 21:50:51 +0000273
Neal Norwitze241ce82003-02-17 18:17:05 +0000274 def test_chdir(self):
275 if hasattr(posix, 'chdir'):
276 posix.chdir(os.curdir)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000277 self.assertRaises(OSError, posix.chdir, support.TESTFN)
Neal Norwitze241ce82003-02-17 18:17:05 +0000278
Martin v. Löwisc9e1c7d2010-07-23 12:16:41 +0000279 def test_listdir(self):
280 if hasattr(posix, 'listdir'):
281 self.assertTrue(support.TESTFN in posix.listdir(os.curdir))
282
283 def test_listdir_default(self):
284 # When listdir is called without argument, it's the same as listdir(os.curdir)
285 if hasattr(posix, 'listdir'):
286 self.assertTrue(support.TESTFN in posix.listdir())
Neal Norwitze241ce82003-02-17 18:17:05 +0000287
288 def test_access(self):
289 if hasattr(posix, 'access'):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000290 self.assertTrue(posix.access(support.TESTFN, os.R_OK))
Neal Norwitze241ce82003-02-17 18:17:05 +0000291
292 def test_umask(self):
293 if hasattr(posix, 'umask'):
294 old_mask = posix.umask(0)
Ezio Melottie9615932010-01-24 19:26:24 +0000295 self.assertIsInstance(old_mask, int)
Neal Norwitze241ce82003-02-17 18:17:05 +0000296 posix.umask(old_mask)
297
298 def test_strerror(self):
299 if hasattr(posix, 'strerror'):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000300 self.assertTrue(posix.strerror(0))
Neal Norwitze241ce82003-02-17 18:17:05 +0000301
302 def test_pipe(self):
303 if hasattr(posix, 'pipe'):
304 reader, writer = posix.pipe()
305 os.close(reader)
306 os.close(writer)
307
Neal Norwitze241ce82003-02-17 18:17:05 +0000308 def test_utime(self):
309 if hasattr(posix, 'utime'):
310 now = time.time()
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000311 posix.utime(support.TESTFN, None)
312 self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, None))
313 self.assertRaises(TypeError, posix.utime, support.TESTFN, (now, None))
314 self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, now))
315 posix.utime(support.TESTFN, (int(now), int(now)))
316 posix.utime(support.TESTFN, (now, now))
Neal Norwitze241ce82003-02-17 18:17:05 +0000317
Thomas Wouterscf297e42007-02-23 15:07:44 +0000318 def test_chflags(self):
319 if hasattr(posix, 'chflags'):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000320 st = os.stat(support.TESTFN)
Thomas Wouterscf297e42007-02-23 15:07:44 +0000321 if hasattr(st, 'st_flags'):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000322 posix.chflags(support.TESTFN, st.st_flags)
Thomas Wouterscf297e42007-02-23 15:07:44 +0000323
324 def test_lchflags(self):
325 if hasattr(posix, 'lchflags'):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000326 st = os.stat(support.TESTFN)
Thomas Wouterscf297e42007-02-23 15:07:44 +0000327 if hasattr(st, 'st_flags'):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000328 posix.lchflags(support.TESTFN, st.st_flags)
Thomas Wouterscf297e42007-02-23 15:07:44 +0000329
Guido van Rossum98297ee2007-11-06 21:34:58 +0000330 def test_environ(self):
Victor Stinner17b490d2010-05-06 22:19:30 +0000331 if os.name == "nt":
332 item_type = str
333 else:
334 item_type = bytes
Guido van Rossum98297ee2007-11-06 21:34:58 +0000335 for k, v in posix.environ.items():
Victor Stinner17b490d2010-05-06 22:19:30 +0000336 self.assertEqual(type(k), item_type)
337 self.assertEqual(type(v), item_type)
Guido van Rossum98297ee2007-11-06 21:34:58 +0000338
Benjamin Petersondcf97b92008-07-02 17:30:14 +0000339 def test_getcwd_long_pathnames(self):
340 if hasattr(posix, 'getcwd'):
341 dirname = 'getcwd-test-directory-0123456789abcdef-01234567890abcdef'
342 curdir = os.getcwd()
343 base_path = os.path.abspath(support.TESTFN) + '.getcwd'
344
345 try:
346 os.mkdir(base_path)
347 os.chdir(base_path)
348 except:
Benjamin Petersone549ead2009-03-28 21:42:05 +0000349# Just returning nothing instead of the SkipTest exception,
Benjamin Petersondcf97b92008-07-02 17:30:14 +0000350# because the test results in Error in that case.
351# Is that ok?
Benjamin Petersone549ead2009-03-28 21:42:05 +0000352# raise unittest.SkipTest("cannot create directory for testing")
Benjamin Petersondcf97b92008-07-02 17:30:14 +0000353 return
354
355 def _create_and_do_getcwd(dirname, current_path_length = 0):
356 try:
357 os.mkdir(dirname)
358 except:
Benjamin Petersone549ead2009-03-28 21:42:05 +0000359 raise unittest.SkipTest("mkdir cannot create directory sufficiently deep for getcwd test")
Benjamin Petersondcf97b92008-07-02 17:30:14 +0000360
361 os.chdir(dirname)
362 try:
363 os.getcwd()
364 if current_path_length < 1027:
365 _create_and_do_getcwd(dirname, current_path_length + len(dirname) + 1)
366 finally:
367 os.chdir('..')
368 os.rmdir(dirname)
369
370 _create_and_do_getcwd(dirname)
371
372 finally:
Benjamin Petersondcf97b92008-07-02 17:30:14 +0000373 os.chdir(curdir)
R. David Murray414c91f2009-07-09 20:12:31 +0000374 support.rmtree(base_path)
Benjamin Petersondcf97b92008-07-02 17:30:14 +0000375
Ronald Oussorenb6ee4f52010-07-23 13:53:51 +0000376 def test_getgroups(self):
377 with os.popen('id -G') as idg:
378 groups = idg.read().strip()
379
380 if not groups:
381 raise unittest.SkipTest("need working 'id -G'")
382
Ronald Oussoren7fb6f512010-08-01 19:18:13 +0000383 # 'id -G' and 'os.getgroups()' should return the same
384 # groups, ignoring order and duplicates.
Ronald Oussorencb615e62010-07-24 14:15:19 +0000385 self.assertEqual(
Ronald Oussoren7fb6f512010-08-01 19:18:13 +0000386 set([int(x) for x in groups.split()]),
387 set(posix.getgroups()))
Ronald Oussorenb6ee4f52010-07-23 13:53:51 +0000388
389class PosixGroupsTester(unittest.TestCase):
390
391 def setUp(self):
392 if posix.getuid() != 0:
393 raise unittest.SkipTest("not enough privileges")
394 if not hasattr(posix, 'getgroups'):
395 raise unittest.SkipTest("need posix.getgroups")
396 if sys.platform == 'darwin':
397 raise unittest.SkipTest("getgroups(2) is broken on OSX")
398 self.saved_groups = posix.getgroups()
399
400 def tearDown(self):
401 if hasattr(posix, 'setgroups'):
402 posix.setgroups(self.saved_groups)
403 elif hasattr(posix, 'initgroups'):
404 name = pwd.getpwuid(posix.getuid()).pw_name
405 posix.initgroups(name, self.saved_groups[0])
406
407 @unittest.skipUnless(hasattr(posix, 'initgroups'),
408 "test needs posix.initgroups()")
409 def test_initgroups(self):
410 # find missing group
411
Antoine Pitroue5a91012010-09-04 17:32:06 +0000412 g = max(self.saved_groups) + 1
Ronald Oussorenb6ee4f52010-07-23 13:53:51 +0000413 name = pwd.getpwuid(posix.getuid()).pw_name
414 posix.initgroups(name, g)
415 self.assertIn(g, posix.getgroups())
416
417 @unittest.skipUnless(hasattr(posix, 'setgroups'),
418 "test needs posix.setgroups()")
419 def test_setgroups(self):
Antoine Pitroue5a91012010-09-04 17:32:06 +0000420 for groups in [[0], list(range(16))]:
Ronald Oussorenb6ee4f52010-07-23 13:53:51 +0000421 posix.setgroups(groups)
422 self.assertListEqual(groups, posix.getgroups())
423
Benjamin Petersondcf97b92008-07-02 17:30:14 +0000424
Neal Norwitze241ce82003-02-17 18:17:05 +0000425def test_main():
Ronald Oussorenb6ee4f52010-07-23 13:53:51 +0000426 support.run_unittest(PosixTester, PosixGroupsTester)
Neal Norwitze241ce82003-02-17 18:17:05 +0000427
428if __name__ == '__main__':
429 test_main()