def setUp(self): try: os.unlink(UDS_PATH) except OSError: pass self.redis_server = TestProcess('redis-server', '--port', '0', '--unixsocket', UDS_PATH) self.wait_for_strings(self.redis_server.read, TIMEOUT, "Running")
def test_pid(): with TestProcess(sys.executable, HELPER, 'test_simple') as service: with dump_on_error(service.read): wait_for_strings(service.read, TIMEOUT, '/tmp/manhole-') with TestProcess('manhole-cli', str(service.proc.pid), bufsize=0, stdin=subprocess.PIPE) as client: with dump_on_error(client.read): wait_for_strings(client.read, TIMEOUT, '(ManholeConsole)', '>>>') client.proc.stdin.write(b"1234+2345\n") wait_for_strings(client.read, TIMEOUT, '3579')
def test_usr2(): with TestProcess(sys.executable, '-u', HELPER, 'test_oneshot_on_usr2') as service: with dump_on_error(service.read): wait_for_strings(service.read, TIMEOUT, 'Not patching os.fork and os.forkpty. Oneshot activation is done by signal') with TestProcess('manhole-cli', '-USR2', str(service.proc.pid), bufsize=0, stdin=subprocess.PIPE) as client: with dump_on_error(client.read): wait_for_strings(client.read, TIMEOUT, '(ManholeConsole)', '>>>') client.proc.stdin.write(b"1234+2345\n") wait_for_strings(client.read, TIMEOUT, '3579')
def test_oneshot_on_usr2_error(self): with TestProcess(sys.executable, '-u', __file__, 'daemon', 'test_oneshot_on_usr2') as proc: with self.dump_on_error(proc.read): self.wait_for_strings( proc.read, TIMEOUT, 'Not patching os.fork and os.forkpty. Oneshot activation is done by signal 12' ) self.assertRaises(AssertionError, self.wait_for_strings, proc.read, TIMEOUT, '/tmp/manhole-') proc.signal(signal.SIGUSR2) self.wait_for_strings(proc.read, TIMEOUT, '/tmp/manhole-') uds_path = re.findall("(/tmp/manhole-\d+)", proc.read())[0] self.wait_for_strings(proc.read, TIMEOUT, 'Waiting for new connection') self.assertManholeRunning( proc, uds_path, oneshot=True, extra=lambda sock: sock.send(b"raise SystemExit()\n")) proc.reset() proc.signal(signal.SIGUSR2) self.wait_for_strings(proc.read, TIMEOUT, '/tmp/manhole-') uds_path = re.findall("(/tmp/manhole-\d+)", proc.read())[0] self.wait_for_strings(proc.read, TIMEOUT, 'Waiting for new connection') self.assertManholeRunning(proc, uds_path, oneshot=True)
def test_simple_break(self): with TestProcess(sys.executable, __file__, 'daemon', 'test_simple') as proc: with self.dump_on_error(proc.read): self.wait_for_strings(proc.read, TIMEOUT, '{a1}', '{b1}', 'RemotePdb session open at ', ) host, port = re.findall("RemotePdb session open at (.+):(.+),", proc.read())[0] with closing(socket.create_connection((host, int(port)), timeout=TIMEOUT)) as conn: if PY3: fh = conn.makefile('rw', buffering=1) else: fh = conn.makefile(bufsize=0) self.wait_for_strings(proc.read, TIMEOUT, 'accepted connection from') fh.readline() self.assertEqual("-> print('{b2}')", fh.readline().strip()) fh.write('break func_a\r\n') fh.write('continue\r\n') fh.readline() fh.readline() self.assertEqual("-> print('{a2}')", fh.readline().strip()) fh.write('continue\r\n') try: fh.readline() except Exception as exc: print("fh.readline() failed:", exc) self.wait_for_strings(proc.read, TIMEOUT, 'DIED.', ) self.assertNotIn('Restoring streams', proc.read())
def test_queue_collapse(): with TestProcess(sys.executable, helper.__file__, 'queue_collapse') as proc: with dump_on_error(proc.read): wait_for_strings(proc.read, TIMEOUT, 'Queues =>') clients = [] for _ in range(5): sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.settimeout(2) sock.connect(UDS_PATH) if PY3: fh = sock.makefile("rwb", buffering=0) else: fh = sock.makefile(bufsize=0) fh.write(b"queue_collapse\n") clients.append((fh, sock)) try: t1 = time.time() for fh, _ in clients: fh.readline() delta = time.time() - t1 if delta > TIMEOUT: raise AssertionError( 'Jobs took too much time (%0.2f sec)' % delta) wait_for_strings( proc.read, TIMEOUT, 'queue_collapse OK', '%s:%s' % (pwd.getpwuid(os.getuid())[0], os.getpid())) finally: [(fh.close(), sock.close()) for fh, sock in clients]
def test_exit_with_grace(): with TestProcess(sys.executable, '-u', HELPER, 'test_simple') as proc: with dump_on_error(proc.read): wait_for_strings(proc.read, TIMEOUT, '/tmp/manhole-') uds_path = re.findall(r"(/tmp/manhole-\d+)", proc.read())[0] wait_for_strings(proc.read, TIMEOUT, 'Waiting for new connection') sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.settimeout(0.05) sock.connect(uds_path) with TestSocket(sock) as client: with dump_on_error(client.read): wait_for_strings(client.read, TIMEOUT, "ThreadID", "ProcessID", ">>>") sock.send(b"print('FOOBAR')\n") wait_for_strings(client.read, TIMEOUT, "FOOBAR") wait_for_strings(proc.read, TIMEOUT, 'UID:%s' % os.getuid()) sock.shutdown(socket.SHUT_WR) select.select([sock], [], [], 5) sock.recv(1024) try: sock.shutdown(socket.SHUT_RD) except Exception as exc: print("Failed to SHUT_RD: %s" % exc) try: sock.close() except Exception as exc: print("Failed to close socket: %s" % exc) wait_for_strings(proc.read, TIMEOUT, 'DONE.', 'Cleaned up.', 'Waiting for new connection')
def app_server(): with TestProcess("python", "app.py") as app_server: wait_for_strings(app_server.read, 10, "Running") print(app_server.read()) yield app_server print("\n>>>>Teardown app_service") app_server.close()
def test_trash_input(): with TestProcess(sys.executable, __file__, 'daemon', 'test_simple') as proc: with dump_on_error(proc.read): wait_for_strings(proc.read, TIMEOUT, '{a1}', '{b1}', 'RemotePdb session open at ') host, port = re.findall("RemotePdb session open at (.+):(.+),", proc.read())[0] with TestSocket( socket.create_connection((host, int(port)), timeout=TIMEOUT)) as client: with dump_on_error(client.read): wait_for_strings(proc.read, TIMEOUT, 'accepted connection from') wait_for_strings(client.read, TIMEOUT, "-> print('{b2}')") for i in range(100): client.fh.write(b'\r\n'.join(b'print("[%d]")' % (i * 10 + j) for j in range(10)) + b'\r\n') client.fh.write(b'continue\r\n') wait_for_strings(client.read, TIMEOUT, *['[%s]' % i for i in range(1000)]) wait_for_strings(proc.read, TIMEOUT, 'DIED.')
def test_oneshot_on_usr2_error(): with TestProcess(sys.executable, '-u', HELPER, 'test_oneshot_on_usr2') as proc: with dump_on_error(proc.read): wait_for_strings( proc.read, TIMEOUT, 'Not patching os.fork and os.forkpty. Oneshot activation is done by signal' ) pytest.raises(AssertionError, wait_for_strings, proc.read, TIMEOUT, '/tmp/manhole-') proc.signal(signal.SIGUSR2) wait_for_strings(proc.read, TIMEOUT, '/tmp/manhole-') uds_path = re.findall(r"(/tmp/manhole-\d+)", proc.read())[0] wait_for_strings(proc.read, TIMEOUT, 'Waiting for new connection') assert_manhole_running( proc, uds_path, oneshot=True, extra=lambda client: client.sock.send(b"raise SystemExit()\n")) proc.reset() proc.signal(signal.SIGUSR2) wait_for_strings(proc.read, TIMEOUT, '/tmp/manhole-') uds_path = re.findall(r"(/tmp/manhole-\d+)", proc.read())[0] wait_for_strings(proc.read, TIMEOUT, 'Waiting for new connection') assert_manhole_running(proc, uds_path, oneshot=True)
def test_sample(): with TestProcess('coverage', 'run', 'tests/nosetests.py', '--verbose', '--with-html', '--html-file=sample.html', 'tests/test_sample.py') as proc: with dump_on_error(proc.read): wait_for_strings(proc.read, TIMEOUT, 'Ran 9 tests in') output = open('sample.html').read() assert """<tr> <td>test_sample</td> <td class="failed">1</td> <td class="failed">1</td> <td>1</td> <td>1</td> <td>4</td> </tr>""" in output assert """<tr> <td>test_sample.MainTestCase</td> <td class="failed">1</td> <td>0</td> <td>0</td> <td>1</td> <td>2</td> </tr>""" in output assert """<tr> <td>test_sample.FailedSetupTestCase</td> <td>0</td> <td class="failed">1</td> <td>0</td> <td>0</td> <td>1</td> </tr>""" in output assert """<tr> <td>test_sample.SecondTestCase</td> <td>0</td> <td>0</td> <td>0</td> <td>2</td> <td>2</td> </tr>""" in output assert """<tr> <td><strong>Total</strong></td> <td class="failed">2</td> <td class="failed">2</td> <td>1</td> <td>4</td> <td>9</td> </tr>""" in output assert "<h2>test_sample.MainTestCase (1 failures, 0 errors)</h2>" in output assert '<section id="test_sample.MainTestCase:test_b">' in output assert '<h3>test_b: <strong>' in output assert '<section id="test_sample:test_b">' in output assert '<h3>test_b: <strong>' in output assert '<li><a class="success">test_a</a></li>' in output assert '<li><a class="failed" href="#test_sample.MainTestCase:test_b">test_b</a></li>' in output assert '<h2>test_sample (1 failures, 1 errors)</h2>' in output assert '<li><a class="success">test_a</a></li>' in output assert '<li><a class="failed" href="#test_sample:test_b">test_b</a></li>' in output assert "<h2>test_sample.FailedSetupTestCase (0 failures, 1 errors)</h2>" in output
def test_cli(entrypoint, tmpdir): args = ['127.0.0.1:0', '--password', 'foobar', '--storage', str(tmpdir)] with TestProcess(*entrypoint.split() + args) as process: wait_for_strings(process.read, TIMEOUT, "server at http://") print(process.read()) t = time.time() port = None while time.time() - t < TIMEOUT and port is None: for conn in psutil.Process(process.proc.pid).connections(): print(conn) if conn.status == psutil.CONN_LISTEN and conn.laddr[ 0] == '127.0.0.1': port = conn.laddr[1] break if port is None: pytest.fail("Didn't find the listen port!") session = requests.Session() resp = session.get('http://127.0.0.1:%s/' % port) csrftoken, = re.findall( 'name=[\'"]csrfmiddlewaretoken[\'"] value=[\'"](.*?)[\'"]', resp.text) resp = session.post( 'http://127.0.0.1:%s/login/?next=/redisboard/redisserver/' % port, data={ 'csrfmiddlewaretoken': csrftoken, 'username': '******', 'password': '******', }) assert '<a href="/redisboard/redisserver/1/inspect/"' in resp.text
def test_gdb(): with TestProcess('python', '-mtarget', 'manhole') as target, dump_on_error(target.read): with TestProcess('hunter-trace', '-p', str(target.proc.pid), '--gdb', 'stdlib=False') as tracer, dump_on_error(tracer.read): wait_for_strings( tracer.read, TIMEOUT, 'WARNING: Using GDB may deadlock the process or create unpredictable results!', 'Output stream active. Starting tracer', 'call => stuff()', 'line time.sleep(1)', 'return <= stuff: None', ) wait_for_strings(target.read, TIMEOUT, 'Broken pipe', 'Stopping tracer.')
def test_no_overlap(redis_server): """ This test tries to simulate contention: lots of clients trying to acquire at the same time. If there would be a bug that would allow two clients to hold the lock at the same time it would most likely regress this test. The code here mostly tries to parse out the pid of the process and the time when it got and released the lock. If there's is overlap (eg: pid1.start < pid2.start < pid1.end) then we got a very bad regression on our hands ... The subprocess being run (check helper.py) will fork bunch of processes and will try to syncronize them (using the builting sched) to try to acquire the lock at the same time. """ with TestProcess(sys.executable, HELPER, 'test_no_overlap') as proc: with dump_on_error(proc.read): name = 'lock:foobar' wait_for_strings(proc.read, 10 * TIMEOUT, 'Getting %r ...' % name) wait_for_strings(proc.read, 10 * TIMEOUT, 'Got lock for %r.' % name) wait_for_strings(proc.read, 10 * TIMEOUT, 'Releasing %r.' % name) wait_for_strings(proc.read, 10 * TIMEOUT, 'UNLOCK_SCRIPT not cached.') wait_for_strings(proc.read, 10 * TIMEOUT, 'DIED.') class Event(object): pid = start = end = '?' def __str__(self): return "Event(%s; %r => %r)" % (self.pid, self.start, self.end) events = defaultdict(Event) for line in proc.read().splitlines(): try: pid, time, junk = line.split(' ', 2) pid = int(pid) except ValueError: continue if 'Got lock for' in junk: events[pid].pid = pid events[pid].start = time if 'Releasing' in junk: events[pid].pid = pid events[pid].end = time assert len(events) == 125 # not very smart but we don't have millions of events so it's # ok - compare all the events with all the other events: for event in events.values(): for other in events.values(): if other is not event: try: if (other.start < event.start < other.end or other.start < event.end < other.end): pytest.fail('%s overlaps %s' % (event, other)) except Exception: print("[%s/%s]" % (event, other)) raise
def test_activate_on_with_oneshot_on(self): with TestProcess(sys.executable, '-u', __file__, 'daemon', 'test_activate_on_with_oneshot_on') as proc: with self.dump_on_error(proc.read): self.wait_for_strings( proc.read, TIMEOUT, "RuntimeError('You cannot do activation of the Manhole thread on the same signal that you want to do oneshot activation !')" )
def redis_server(scope='module'): try: os.unlink(UDS_PATH) except OSError: pass with TestProcess('redis-server', '--port', '0', '--unixsocket', UDS_PATH) as process: wait_for_strings(process.read, TIMEOUT, "Running") yield process
def test_activate_on_with_oneshot_on(): with TestProcess(sys.executable, '-u', HELPER, 'test_activate_on_with_oneshot_on') as proc: with dump_on_error(proc.read): wait_for_strings( proc.read, TIMEOUT, "You cannot do activation of the Manhole thread on the same signal that you want to do " "oneshot activation !")
def test_sigprocmask_negative(): with TestProcess(sys.executable, '-u', HELPER, 'test_signalfd_weirdness_negative') as proc: with dump_on_error(proc.read): wait_for_strings(proc.read, TIMEOUT, '/tmp/manhole-') uds_path = re.findall(r"(/tmp/manhole-\d+)", proc.read())[0] wait_for_strings(proc.read, TIMEOUT, 'Waiting for new connection') wait_for_strings(proc.read, TIMEOUT, 'signalled=True') assert_manhole_running(proc, uds_path)
def test_manhole(): with TestProcess('python', '-mtarget', 'manhole') as target, dump_on_error(target.read): wait_for_strings(target.read, TIMEOUT, 'Oneshot activation is done by signal') with TestProcess('hunter-trace', '-p', str(target.proc.pid), 'stdlib=False') as tracer, dump_on_error(tracer.read): wait_for_strings( tracer.read, TIMEOUT, 'Output stream active. Starting tracer', 'call => stuff()', 'line time.sleep(1)', 'return <= stuff: None', ) wait_for_strings(target.read, TIMEOUT, 'Broken pipe', 'Stopping tracer.')
def test_locmem(): with TestProcess('django-admin.py', 'runserver', '127.0.0.1:0', '--traceback', '--noreload', '--nothreading', env=dict(os.environ, UWSGI_CACHE_FALLBACK='y')) as proc: with dump_on_error(proc.read): wait_for_strings(proc.read, TIMEOUT, '127.0.0.1') port = get_ports(proc.proc.pid) url = "http://127.0.0.1:%s" % port assertions(url)
def test_locals_after_fork(): with TestProcess(sys.executable, HELPER, 'test_locals_after_fork') as proc: with dump_on_error(proc.read): wait_for_strings(proc.read, TIMEOUT, 'Fork detected') proc.reset() wait_for_strings(proc.read, TIMEOUT, '/tmp/manhole-') child_uds_path = re.findall(r"(/tmp/manhole-\d+)", proc.read())[0] wait_for_strings(proc.read, TIMEOUT, 'Waiting for new connection') check_locals(child_uds_path)
def test_custom_exit_code(): with TestProcess(sys.executable, helper.__file__, 'custom_exit_code') as proc: with dump_on_error(proc.read): wait_for_strings(proc.read, TIMEOUT, 'Queues =>') with connection(3) as fh: fh.write(b"asdf\n") line = fh.readline() json.loads(line.decode('ascii'))["exit_code"] == 123
def test_simple(): with TestProcess(sys.executable, HELPER, 'test_simple') as proc: with dump_on_error(proc.read): wait_for_strings(proc.read, TIMEOUT, '/tmp/manhole-') uds_path = re.findall(r"(/tmp/manhole-\d+)", proc.read())[0] wait_for_strings(proc.read, TIMEOUT, 'Waiting for new connection') for _ in range(20): proc.reset() assert_manhole_running(proc, uds_path)
def test_gdb_clean_exit(): with TestProcess(sys.executable, '-mtarget', 'manhole') as target, dump_on_error(target.read): with TestProcess('hunter-trace', '-p', str(target.proc.pid), 'stdlib=False', '--gdb') as tracer, dump_on_error(tracer.read): wait_for_strings( tracer.read, TIMEOUT, 'WARNING: Using GDB may deadlock the process or create unpredictable results!', 'Output stream active. Starting tracer', 'call => stuff()', 'line time.sleep(1)', 'return <= stuff: None', ) target.reset() tracer.proc.send_signal(signal.SIGINT) wait_for_strings(target.read, TIMEOUT, 'Doing stuff', 'Doing stuff', 'Doing stuff')
def test_interrupt_on_accept(): with TestProcess(sys.executable, '-u', __file__, 'daemon', 'test_interrupt_on_accept') as proc: with dump_on_error(proc.read): wait_for_strings(proc.read, TIMEOUT, '/tmp/manhole-') uds_path = re.findall(r"(/tmp/manhole-\d+)", proc.read())[0] wait_for_strings(proc.read, TIMEOUT, 'Waiting for new connection', 'Sending signal to manhole thread', 'Waiting for new connection') assert_manhole_running(proc, uds_path)
def test_simple(redis_server): with TestProcess(sys.executable, HELPER, 'test_simple') as proc: with dump_on_error(proc.read): name = 'lock:foobar' wait_for_strings(proc.read, TIMEOUT, 'Getting %r ...' % name, 'Got lock for %r.' % name, 'Releasing %r.' % name, 'UNLOCK_SCRIPT not cached.', 'DIED.', )
def test_no_block(redis_server): with Lock(StrictRedis(unix_socket_path=UDS_PATH), "foobar"): with TestProcess(sys.executable, HELPER, 'test_no_block') as proc: with dump_on_error(proc.read): name = 'lock:foobar' wait_for_strings(proc.read, TIMEOUT, 'Getting %r ...' % name, 'Failed to get %r.' % name, 'acquire=>False', 'DIED.', )
def test_timeout_acquired(conn): with TestProcess(sys.executable, HELPER, 'test_timeout') as proc: with dump_on_error(proc.read): name = 'lock:foobar' wait_for_strings( proc.read, TIMEOUT, 'Getting %r ...' % name, 'Got lock for %r.' % name, ) lock = Lock(conn, "foobar") assert lock.acquire(timeout=2)
def test_manhole_clean_exit(): with TestProcess('python', '-mtarget', 'manhole') as target, dump_on_error(target.read): wait_for_strings(target.read, TIMEOUT, 'Oneshot activation is done by signal') with TestProcess('hunter-trace', '-p', str(target.proc.pid), 'stdlib=False') as tracer, dump_on_error(tracer.read): wait_for_strings( tracer.read, TIMEOUT, 'Output stream active. Starting tracer', 'call => stuff()', 'line time.sleep(1)', 'return <= stuff: None', ) target.reset() tracer.proc.send_signal(signal.SIGINT) wait_for_strings(target.read, TIMEOUT, 'remote.deactivate()', 'Doing stuff', 'Doing stuff', 'Doing stuff')
def test_prespawned(): with TestProcess(sys.executable, helper.__file__, 'simple') as proc: with dump_on_error(proc.read): wait_for_strings(proc.read, TIMEOUT, 'Queues =>') response = client.request(helper.PATH, b"foobar") assert response.exit_code == 0 wait_for_strings(proc.read, TIMEOUT, '%s:%s' % (pwd.getpwuid(os.getuid())[0], os.getpid()), 'JOB foobar EXECUTED', 'completed. Passing back results to', 'Queues => 0 workspaces')
class RedisLockTestCase(ProcessTestCase): def setUp(self): try: os.unlink(UDS_PATH) except OSError: pass self.redis_server = TestProcess('redis-server', '--port', '0', '--unixsocket', UDS_PATH) self.wait_for_strings(self.redis_server.read, TIMEOUT, "Running") def tearDown(self): self.redis_server.close() def test_simple(self): with TestProcess(sys.executable, __file__, 'daemon', 'test_simple') as proc: with self.dump_on_error(proc.read): name = 'lock:foobar' self.wait_for_strings(proc.read, TIMEOUT, 'Getting %r ...' % name, 'Got lock for %r.' % name, 'Releasing %r.' % name, 'UNLOCK_SCRIPT not cached.', 'DIED.', ) def test_no_block(self): with Lock(StrictRedis(unix_socket_path=UDS_PATH), "foobar"): with TestProcess(sys.executable, __file__, 'daemon', 'test_no_block') as proc: with self.dump_on_error(proc.read): name = 'lock:foobar' self.wait_for_strings(proc.read, TIMEOUT, 'Getting %r ...' % name, 'Failed to get %r.' % name, 'acquire=>False', 'DIED.', ) def test_expire(self): conn = StrictRedis(unix_socket_path=UDS_PATH) with Lock(conn, "foobar", expire=TIMEOUT/4): with TestProcess(sys.executable, __file__, 'daemon', 'test_expire') as proc: with self.dump_on_error(proc.read): name = 'lock:foobar' self.wait_for_strings(proc.read, TIMEOUT, 'Getting %r ...' % name, 'Got lock for %r.' % name, 'Releasing %r.' % name, 'UNLOCK_SCRIPT not cached.', 'DIED.', ) lock = Lock(conn, "foobar") try: self.assertEqual(lock.acquire(blocking=False), True) finally: lock.release() def test_double_acquire(self): lock = Lock(StrictRedis(unix_socket_path=UDS_PATH), "foobar") with lock: self.assertRaises(RuntimeError, lock.acquire) def test_plain(self): with Lock(StrictRedis(unix_socket_path=UDS_PATH), "foobar"): time.sleep(0.01) def test_no_overlap(self): with TestProcess(sys.executable, __file__, 'daemon', 'test_no_overlap') as proc: with self.dump_on_error(proc.read): name = 'lock:foobar' self.wait_for_strings(proc.read, TIMEOUT, 'Getting %r ...' % name) self.wait_for_strings(proc.read, TIMEOUT, 'Got lock for %r.' % name) self.wait_for_strings(proc.read, TIMEOUT, 'Releasing %r.' % name) self.wait_for_strings(proc.read, TIMEOUT, 'UNLOCK_SCRIPT not cached.') self.wait_for_strings(proc.read, 10*TIMEOUT, 'DIED.') class Event(object): def __str__(self): return "Event(%s; %r => %r)" % (self.pid, self.start, self.end) events = defaultdict(Event) for line in proc.read().splitlines(): pid, time, junk = line.split(' ', 2) if 'Got lock for' in junk: events[pid].pid = pid events[pid].start = time if 'Releasing' in junk: events[pid].pid = pid events[pid].end = time self.assertEqual(len(events), 125) for event in events.values(): for other in events.values(): if other is not event: try: if other.start < event.start < other.end or \ other.start < event.end < other.end: self.fail('%s overlaps %s' % (event, other)) except: print("[%s/%s]" %(event, other)) raise