| 1 | #!/usr/bin/env python2
 | 
| 2 | """
 | 
| 3 | posix_test.py: Tests for our posix_ module subset.
 | 
| 4 | 
 | 
| 5 | NOTE: There are more tests in Python-2.7.13/Lib/test/test_posix.py.
 | 
| 6 | 
 | 
| 7 | Notes on stripping posixmodule.c:
 | 
| 8 | 
 | 
| 9 | I left in:
 | 
| 10 | 
 | 
| 11 |   - putenv, unsetenv: Isn't it simpler to use these than os.environ?  I'm not
 | 
| 12 |     sure how it works.
 | 
| 13 |   - tcgetpgrp, tcsetpgrp, setsid, getsid: is this for job control?
 | 
| 14 | 
 | 
| 15 |   - times: This is a builtin!  It's like 'time' for the shell prosecs itself.
 | 
| 16 |   - symlink - useful for writing tools?
 | 
| 17 |   - waitpid - because we're using wait
 | 
| 18 | 
 | 
| 19 |   - set*uid, etc. - for container tools?
 | 
| 20 |   - kill, killpg - would the kill builtin need these?
 | 
| 21 |   - getppid - I think for $PPID
 | 
| 22 | 
 | 
| 23 |   - mkdir, rmdir() -- might be useful for tools
 | 
| 24 | 
 | 
| 25 | Other notes:
 | 
| 26 | 
 | 
| 27 |   - The shell uses dup2 but not dup?
 | 
| 28 | """
 | 
| 29 | from __future__ import print_function
 | 
| 30 | 
 | 
| 31 | import signal
 | 
| 32 | import subprocess
 | 
| 33 | import unittest
 | 
| 34 | 
 | 
| 35 | import posix_  # module under test
 | 
| 36 | from mycpp.mylib import log
 | 
| 37 | 
 | 
| 38 | 
 | 
| 39 | # Taken from build/oil-defs/.../posix_methods.def
 | 
| 40 | 
 | 
| 41 | FUNCS = [
 | 
| 42 |     "access",
 | 
| 43 |     "chdir",
 | 
| 44 |     "getcwd",
 | 
| 45 |     "listdir",
 | 
| 46 |     "lstat",
 | 
| 47 |     "readlink",
 | 
| 48 |     "stat",
 | 
| 49 |     "umask",
 | 
| 50 |     "uname",
 | 
| 51 |     "_exit",
 | 
| 52 |     "execv",
 | 
| 53 |     "execve",
 | 
| 54 |     "fork",
 | 
| 55 |     "geteuid",
 | 
| 56 |     "getpid",
 | 
| 57 |     "getuid",
 | 
| 58 |     "wait",
 | 
| 59 |     "open",
 | 
| 60 |     "close",
 | 
| 61 |     "dup2",
 | 
| 62 |     "read",
 | 
| 63 |     "write",
 | 
| 64 |     "fdopen",
 | 
| 65 |     "isatty",
 | 
| 66 |     "pipe",
 | 
| 67 |     "strerror",
 | 
| 68 |     "WIFSIGNALED",
 | 
| 69 |     "WIFEXITED",
 | 
| 70 |     "WEXITSTATUS",
 | 
| 71 |     "WTERMSIG",
 | 
| 72 | 
 | 
| 73 |     # Additional names found by grepping
 | 
| 74 |     'X_OK',
 | 
| 75 |     'R_OK',
 | 
| 76 |     'W_OK',
 | 
| 77 | 
 | 
| 78 |     'O_APPEND',
 | 
| 79 |     'O_CREAT',
 | 
| 80 |     'O_RDONLY',
 | 
| 81 |     'O_RDWR',
 | 
| 82 |     'O_TRUNC',
 | 
| 83 |     'O_WRONLY',
 | 
| 84 | ]
 | 
| 85 | 
 | 
| 86 | class PosixTest(unittest.TestCase):
 | 
| 87 | 
 | 
| 88 |   def testFoo(self):
 | 
| 89 |     print(posix_.getcwd())
 | 
| 90 |     # Testing this because I removed a lot of #ifdef
 | 
| 91 |     entries = posix_.listdir('.')
 | 
| 92 |     self.assert_('doc' in entries)
 | 
| 93 | 
 | 
| 94 |   def testFunctionsExist(self):
 | 
| 95 |     for name in FUNCS:
 | 
| 96 |       func = getattr(posix_, name)
 | 
| 97 |       print(func)
 | 
| 98 | 
 | 
| 99 |   def testEmptyReadAndWrite(self):
 | 
| 100 |     # Regression for bug where this would hang
 | 
| 101 |     posix_.read(0, 0)
 | 
| 102 |     posix_.write(1, '')
 | 
| 103 | 
 | 
| 104 |   def testRead(self):
 | 
| 105 |     if posix_.environ.get('EINTR_TEST'):
 | 
| 106 |       # Now we can do kill -TERM PID can get EINTR.
 | 
| 107 |       # Or Ctrl-C for KeyboardInterrupt
 | 
| 108 | 
 | 
| 109 |       signal.signal(signal.SIGTERM, _Handler)
 | 
| 110 |       log('Hanging on read in pid %d', posix_.getpid())
 | 
| 111 |       posix_.read(0, 1)
 | 
| 112 | 
 | 
| 113 |   def testWait(self):
 | 
| 114 |     if posix_.environ.get('EINTR_TEST'):
 | 
| 115 |       # Now we can do kill -TERM PID can get EINTR.
 | 
| 116 |       signal.signal(signal.SIGTERM, _Handler)
 | 
| 117 | 
 | 
| 118 |       p = subprocess.Popen(['sleep', '5'])
 | 
| 119 |       log('started sleep pid %d', p.pid)
 | 
| 120 | 
 | 
| 121 |       log('Hanging on wait in pid %d', posix_.getpid())
 | 
| 122 |       posix_.wait()
 | 
| 123 | 
 | 
| 124 |   def testWaitpid(self):
 | 
| 125 |     if posix_.environ.get('EINTR_TEST'):
 | 
| 126 |       # Now we can do kill -TERM PID can get EINTR.
 | 
| 127 |       signal.signal(signal.SIGTERM, _Handler)
 | 
| 128 | 
 | 
| 129 |       p = subprocess.Popen(['sleep', '5'])
 | 
| 130 |       log('started sleep pid %d', p.pid)
 | 
| 131 | 
 | 
| 132 |       log('Hanging on waitpid in pid %d', posix_.getpid())
 | 
| 133 |       posix_.waitpid(-1, 0)
 | 
| 134 | 
 | 
| 135 |   def testWrite(self):
 | 
| 136 |     if posix_.environ.get('EINTR_TEST'):
 | 
| 137 | 
 | 
| 138 |       signal.signal(signal.SIGTERM, _Handler)
 | 
| 139 |       r, w = posix_.pipe()
 | 
| 140 |       log('Hanging on write in pid %d', posix_.getpid())
 | 
| 141 | 
 | 
| 142 |       # 1 byte bigger than pipe size
 | 
| 143 |       n = posix_.write(w, 'x'*65537)
 | 
| 144 |       log('1: Wrote %d bytes', n)
 | 
| 145 | 
 | 
| 146 |       # write returns early when a signal interrupts it, and we read at least
 | 
| 147 |       # one byte!  We do NOT get EINTR>
 | 
| 148 | 
 | 
| 149 |       # On the second try, it didn't write anything, and we get EINTR!
 | 
| 150 | 
 | 
| 151 |       log('Second try (pid %d)', posix_.getpid())
 | 
| 152 |       n = posix_.write(w, 'x'*65537)
 | 
| 153 |       log('2: Wrote %d bytes', n)
 | 
| 154 | 
 | 
| 155 |   def testPrint(self):
 | 
| 156 |     # Conclusion: print CAN raise IOError with EINTR.
 | 
| 157 | 
 | 
| 158 |     if posix_.environ.get('EINTR_TEST'):
 | 
| 159 | 
 | 
| 160 |       signal.signal(signal.SIGTERM, _Handler)
 | 
| 161 |       r, w = posix_.pipe()
 | 
| 162 |       log('Hanging on write in pid %d', posix_.getpid())
 | 
| 163 |       f = posix_.fdopen(w, 'w')
 | 
| 164 | 
 | 
| 165 |       # 1 byte bigger than pipe size
 | 
| 166 |       print('x'*65537, file=f)
 | 
| 167 |       log('1: done')
 | 
| 168 | 
 | 
| 169 |       # write returns early when a signal interrupts it, and we read at least
 | 
| 170 |       # one byte!  We do NOT get EINTR>
 | 
| 171 | 
 | 
| 172 |       # On the second try, it didn't write anything, and we get EINTR!
 | 
| 173 | 
 | 
| 174 |       log('Second try (pid %d)', posix_.getpid())
 | 
| 175 |       print('x'*65537, file=f)
 | 
| 176 |       log('2: done')
 | 
| 177 | 
 | 
| 178 | 
 | 
| 179 | def _Handler(x, y):
 | 
| 180 |   log('Got signal %s %s', x, y)
 | 
| 181 | 
 | 
| 182 | 
 | 
| 183 | if __name__ == '__main__':
 | 
| 184 |   unittest.main()
 |