From ed79125c652d7ea02cb79a84c1292fdc19932a94 Mon Sep 17 00:00:00 2001 From: Sergey Shepelev Date: Thu, 5 Jan 2017 05:08:38 +0300 Subject: [PATCH] tests cleanup, CI with Python 3.6 --- .coveragerc | 3 +- .gitignore | 1 + .travis.yml | 57 ++++++++++++++++----------- codecov.yml | 4 ++ tests/fork_test.py | 52 ------------------------- tests/greenio_test.py | 2 +- tests/hub_test.py | 65 +++++++++++++++---------------- tests/hub_test_fork.py | 25 ------------ tests/isolated/hub_fork.py | 33 ++++++++++++++++ tests/isolated/hub_fork_simple.py | 58 +++++++++++++++++++++++++++ tests/test__refcount.py | 65 ++++++++++++++----------------- tests/test__socket_errors.py | 2 +- tests/tpool_test.py | 15 ++++--- tox.ini | 8 ++-- 14 files changed, 209 insertions(+), 181 deletions(-) delete mode 100644 tests/fork_test.py delete mode 100644 tests/hub_test_fork.py create mode 100644 tests/isolated/hub_fork.py create mode 100644 tests/isolated/hub_fork_simple.py diff --git a/.coveragerc b/.coveragerc index bd611b7..0c429f8 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,7 +1,8 @@ [run] branch = True source = eventlet -concurrency = eventlet +# concurrency=eventlet gives 0% report on CPython and start error on pypy +#concurrency = eventlet omit = eventlet/support/dns/* eventlet/support/six.py diff --git a/.gitignore b/.gitignore index 157f792..f420228 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.pyc .* build/ +.coverage coverage.xml dist/ doc/changelog.rst diff --git a/.travis.yml b/.travis.yml index 15f5459..1fd9456 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,31 +1,42 @@ sudo: false language: python -python: 3.5 -env: - matrix: - - TOX_ENV=pep8 - - TOX_ENV=py26-epolls - - TOX_ENV=py26-poll - - TOX_ENV=py26-selects - - TOX_ENV=py27-epolls - - TOX_ENV=py27-poll - - TOX_ENV=py27-selects - - TOX_ENV=py33-epolls - - TOX_ENV=py33-poll - - TOX_ENV=py33-selects - - TOX_ENV=py34-epolls - - TOX_ENV=py34-poll - - TOX_ENV=py34-selects - - TOX_ENV=py35-epolls - - TOX_ENV=py35-poll - - TOX_ENV=py35-selects - - TOX_ENV=pypy-epolls - - TOX_ENV=pypy-poll - - TOX_ENV=pypy-selects matrix: fast_finish: true + include: + - {python: 3.5, env: TOX_ENV=pep8} + + - {python: 2.6, env: TOX_ENV=py26-epolls} + - {python: 2.6, env: TOX_ENV=py26-poll} + - {python: 2.6, env: TOX_ENV=py26-selects} + + - {python: 2.7, env: TOX_ENV=py27-epolls} + - {python: 2.7, env: TOX_ENV=py27-poll} + - {python: 2.7, env: TOX_ENV=py27-selects} + + - {python: 3.3, env: TOX_ENV=py33-epolls} + - {python: 3.3, env: TOX_ENV=py33-poll} + - {python: 3.3, env: TOX_ENV=py33-selects} + + - {python: 3.4, env: TOX_ENV=py34-epolls} + - {python: 3.4, env: TOX_ENV=py34-poll} + - {python: 3.4, env: TOX_ENV=py34-selects} + + - {python: 3.5, env: TOX_ENV=py35-epolls} + - {python: 3.5, env: TOX_ENV=py35-poll} + - {python: 3.5, env: TOX_ENV=py35-selects} + + - {python: 3.6-dev, env: TOX_ENV=py36-epolls} + - {python: 3.6-dev, env: TOX_ENV=py36-poll} + - {python: 3.6-dev, env: TOX_ENV=py36-selects} + + - {python: pypy, env: TOX_ENV=pypy-epolls} + - {python: pypy, env: TOX_ENV=pypy-poll} + - {python: pypy, env: TOX_ENV=pypy-selects} allow_failures: + - env: TOX_ENV=py26-epolls + - env: TOX_ENV=py26-poll + - env: TOX_ENV=py26-selects - env: TOX_ENV=pypy-epolls - env: TOX_ENV=pypy-poll - env: TOX_ENV=pypy-selects @@ -51,7 +62,7 @@ before_script: - "export PATH=/usr/lib/ccache:$PATH" script: - tox -v -v -e $TOX_ENV - - codecov + - codecov --flags=$(echo $TOX_ENV |tr -d '-.') after_failure: - for X in .tox/$TOX_ENV/log/*; do echo "$X\n"; cat "$X"; echo "\n\n"; done - echo "pip.log\n"; cat $HOME/.pip/pip.log diff --git a/codecov.yml b/codecov.yml index 676557d..6e9350e 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,2 +1,6 @@ codecov: token: 2a926756-1923-42a1-89c7-97925ea0e17a + +coverage: + precision: 0 + round: down diff --git a/tests/fork_test.py b/tests/fork_test.py deleted file mode 100644 index f15883f..0000000 --- a/tests/fork_test.py +++ /dev/null @@ -1,52 +0,0 @@ -from tests.patcher_test import ProcessBase - - -class ForkTest(ProcessBase): - def test_simple(self): - newmod = ''' -import eventlet -import os -import sys -import signal -from eventlet.support import bytes_to_str, six -mydir = %r -signal_file = os.path.join(mydir, "output.txt") -pid = os.fork() -if (pid != 0): - eventlet.Timeout(10) - try: - port = None - while True: - try: - contents = open(signal_file, "rb").read() - port = int(contents.split()[0]) - break - except (IOError, IndexError, ValueError, TypeError): - eventlet.sleep(0.1) - eventlet.connect(('127.0.0.1', port)) - while True: - try: - contents = open(signal_file, "rb").read() - result = contents.split()[1] - break - except (IOError, IndexError): - eventlet.sleep(0.1) - print('result {0}'.format(bytes_to_str(result))) - finally: - os.kill(pid, signal.SIGTERM) -else: - try: - s = eventlet.listen(('', 0)) - fd = open(signal_file, "wb") - fd.write(six.b(str(s.getsockname()[1]))) - fd.write(b"\\n") - fd.flush() - s.accept() - fd.write(b"done") - fd.flush() - finally: - fd.close() -''' - self.write_to_tempfile("newmod", newmod % self.tempdir) - output, lines = self.launch_subprocess('newmod.py') - self.assertEqual(lines[0], "result done", output) diff --git a/tests/greenio_test.py b/tests/greenio_test.py index 619ccd0..6d01857 100644 --- a/tests/greenio_test.py +++ b/tests/greenio_test.py @@ -443,7 +443,7 @@ class TestGreenSocket(tests.LimitedTestCase): wrap_rfile = client.makefile() wrap_rfile.read(1) self.fail() - except eventlet.TimeoutError: + except eventlet.Timeout: pass result = evt.wait() diff --git a/tests/hub_test.py b/tests/hub_test.py index fc0ce6f..65ba0a9 100644 --- a/tests/hub_test.py +++ b/tests/hub_test.py @@ -1,14 +1,12 @@ from __future__ import with_statement import sys +import time import tests -from tests import LimitedTestCase, main, skip_with_pyevent, skip_if_no_itimer, skip_unless +from tests import skip_with_pyevent, skip_if_no_itimer, skip_unless from tests.patcher_test import ProcessBase -import time import eventlet from eventlet import hubs -from eventlet.event import Event -from eventlet.semaphore import Semaphore from eventlet.support import greenlets, six @@ -19,7 +17,7 @@ def noop(): pass -class TestTimerCleanup(LimitedTestCase): +class TestTimerCleanup(tests.LimitedTestCase): TEST_TIMEOUT = 2 @skip_with_pyevent @@ -85,7 +83,7 @@ class TestTimerCleanup(LimitedTestCase): eventlet.sleep() -class TestScheduleCall(LimitedTestCase): +class TestScheduleCall(tests.LimitedTestCase): def test_local(self): lst = [1] @@ -111,7 +109,7 @@ class TestScheduleCall(LimitedTestCase): self.assertEqual(lst, [1, 2, 3]) -class TestDebug(LimitedTestCase): +class TestDebug(tests.LimitedTestCase): def test_debug_listeners(self): hubs.get_hub().set_debug_listeners(True) @@ -122,7 +120,7 @@ class TestDebug(LimitedTestCase): hubs.get_hub().set_timer_exceptions(False) -class TestExceptionInMainloop(LimitedTestCase): +class TestExceptionInMainloop(tests.LimitedTestCase): def test_sleep(self): # even if there was an error in the mainloop, the hub should continue @@ -149,13 +147,13 @@ class TestExceptionInMainloop(LimitedTestCase): delay, DELAY) -class TestExceptionInGreenthread(LimitedTestCase): +class TestExceptionInGreenthread(tests.LimitedTestCase): @skip_unless(greenlets.preserves_excinfo) def test_exceptionpreservation(self): # events for controlling execution order - gt1event = Event() - gt2event = Event() + gt1event = eventlet.Event() + gt2event = eventlet.Event() def test_gt1(): try: @@ -196,7 +194,7 @@ class TestExceptionInGreenthread(LimitedTestCase): hubs.get_hub().switch() # semaphores for controlling execution order - sem = Semaphore() + sem = eventlet.Semaphore() sem.acquire() g = eventlet.spawn(test_gt, sem) try: @@ -206,7 +204,7 @@ class TestExceptionInGreenthread(LimitedTestCase): g.kill() -class TestHubSelection(LimitedTestCase): +class TestHubSelection(tests.LimitedTestCase): def test_explicit_hub(self): oldhub = hubs.get_hub() @@ -217,7 +215,7 @@ class TestHubSelection(LimitedTestCase): hubs._threadlocal.hub = oldhub -class TestHubBlockingDetector(LimitedTestCase): +class TestHubBlockingDetector(tests.LimitedTestCase): TEST_TIMEOUT = 10 @skip_with_pyevent @@ -245,7 +243,7 @@ class TestHubBlockingDetector(LimitedTestCase): debug.hub_blocking_detection(False) -class TestSuspend(LimitedTestCase): +class TestSuspend(tests.LimitedTestCase): TEST_TIMEOUT = 4 longMessage = True maxDiff = None @@ -283,25 +281,30 @@ except eventlet.Timeout: shutil.rmtree(self.tempdir) -class TestBadFilenos(LimitedTestCase): +def test_repeated_select_bad_fd(): + from eventlet.green import select - @skip_with_pyevent - def test_repeated_selects(self): - from eventlet.green import select - self.assertRaises(ValueError, select.select, [-1], [], []) - self.assertRaises(ValueError, select.select, [-1], [], []) + def once(): + try: + select.select([-1], [], []) + assert False, 'Expected ValueError' + except ValueError: + pass + + once() + once() -class TestFork(LimitedTestCase): - - @skip_with_pyevent - def test_fork(self): - output = tests.run_python('tests/hub_test_fork.py') - lines = output.splitlines() - self.assertEqual(lines, [b"accept blocked", b"child died ok"], output) +@skip_with_pyevent +def test_fork(): + tests.run_isolated('hub_fork.py') -class TestDeadRunLoop(LimitedTestCase): +def test_fork_simple(): + tests.run_isolated('hub_fork_simple.py') + + +class TestDeadRunLoop(tests.LimitedTestCase): TEST_TIMEOUT = 2 class CustomException(Exception): @@ -397,7 +400,3 @@ print('ok') self.write_to_tempfile('newmod', module_source) output, _ = self.launch_subprocess('newmod.py') self.assertEqual(output, 'kqueue tried\nok\n') - - -if __name__ == '__main__': - main() diff --git a/tests/hub_test_fork.py b/tests/hub_test_fork.py deleted file mode 100644 index f886a96..0000000 --- a/tests/hub_test_fork.py +++ /dev/null @@ -1,25 +0,0 @@ -# no standard tests in this file, ignore -__test__ = False - -if __name__ == '__main__': - import os - import eventlet - server = eventlet.listen(('localhost', 12345)) - t = eventlet.Timeout(0.01) - try: - new_sock, address = server.accept() - except eventlet.Timeout as t: - pass - - pid = os.fork() - if not pid: - t = eventlet.Timeout(0.1) - try: - new_sock, address = server.accept() - except eventlet.Timeout as t: - print("accept blocked") - else: - kpid, status = os.wait() - assert kpid == pid - assert status == 0 - print("child died ok") diff --git a/tests/isolated/hub_fork.py b/tests/isolated/hub_fork.py new file mode 100644 index 0000000..1872942 --- /dev/null +++ b/tests/isolated/hub_fork.py @@ -0,0 +1,33 @@ +# verify eventlet.listen() accepts in forked children +__test__ = False + +if __name__ == '__main__': + import os + import sys + import eventlet + + server = eventlet.listen(('127.0.0.1', 0)) + result = eventlet.with_timeout(0.01, server.accept, timeout_value=True) + assert result is True, 'Expected timeout' + + pid = os.fork() + if pid < 0: + print('fork error') + sys.exit(1) + elif pid == 0: + with eventlet.Timeout(1): + sock, _ = server.accept() + sock.sendall('ok {0}'.format(os.getpid()).encode()) + sock.close() + sys.exit(0) + elif pid > 0: + with eventlet.Timeout(1): + sock = eventlet.connect(server.getsockname()) + data = sock.recv(20).decode() + assert data.startswith('ok ') + spid = int(data[3:].strip()) + assert spid == pid + kpid, status = os.wait() + assert kpid == pid + assert status == 0 + print('pass') diff --git a/tests/isolated/hub_fork_simple.py b/tests/isolated/hub_fork_simple.py new file mode 100644 index 0000000..96389de --- /dev/null +++ b/tests/isolated/hub_fork_simple.py @@ -0,0 +1,58 @@ +import os +import signal +import sys +import tempfile +__test__ = False + + +def parent(signal_path, pid): + eventlet.Timeout(5) + port = None + while True: + try: + contents = open(signal_path, 'rb').read() + port = int(contents.strip()) + break + except Exception: + eventlet.sleep(0.1) + eventlet.connect(('127.0.0.1', port)) + while True: + try: + contents = open(signal_path, 'rb').read() + result = contents.split()[1] + break + except Exception: + eventlet.sleep(0.1) + assert result == b'done', repr(result) + print('pass') + + +def child(signal_path): + eventlet.Timeout(5) + s = eventlet.listen(('127.0.0.1', 0)) + with open(signal_path, 'wb') as f: + f.write(str(s.getsockname()[1]).encode() + b'\n') + f.flush() + s.accept() + f.write(b'done\n') + f.flush() + + +if __name__ == '__main__': + import eventlet + + with tempfile.NamedTemporaryFile() as signal_file: + signal_path = signal_file.name + + pid = os.fork() + if pid < 0: + sys.stderr.write('fork error\n') + sys.exit(1) + elif pid == 0: + child(signal_path) + sys.exit(0) + elif pid > 0: + try: + parent(signal_path, pid) + except Exception: + os.kill(pid, signal.SIGTERM) diff --git a/tests/test__refcount.py b/tests/test__refcount.py index 6ccfed7..1090a1f 100644 --- a/tests/test__refcount.py +++ b/tests/test__refcount.py @@ -2,78 +2,71 @@ are not leaked by the hub. """ import gc -from pprint import pformat +import pprint +import sys import weakref -from eventlet.support import clear_sys_exc_info +import eventlet from eventlet.green import socket -from eventlet.green.thread import start_new_thread -from eventlet.green.time import sleep + SOCKET_TIMEOUT = 0.1 -def init_server(): - s = socket.socket() - s.settimeout(SOCKET_TIMEOUT) - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - s.bind(('localhost', 0)) - s.listen(5) - return s, s.getsockname()[1] - - def handle_request(s, raise_on_timeout): try: conn, address = s.accept() except socket.timeout: + print('handle_request: server accept timeout') if raise_on_timeout: raise else: return - # print('handle_request - accepted') + print('handle_request: accepted') res = conn.recv(100) assert res == b'hello', repr(res) - # print('handle_request - recvd %r' % res) - res = conn.send(b'bye') - # print('handle_request - sent %r' % res) - # print('handle_request - conn refcount: %s' % sys.getrefcount(conn)) - # conn.close() + # print('handle_request: recvd %r' % res) + res = conn.sendall(b'bye') + # print('handle_request: sent %r' % res) + # print('handle_request: conn refcount: %s' % sys.getrefcount(conn)) -def make_request(port): +def make_request(addr): # print('make_request') - s = socket.socket() - s.connect(('localhost', port)) + s = eventlet.connect(addr) # print('make_request - connected') - res = s.send(b'hello') + res = s.sendall(b'hello') # print('make_request - sent %s' % res) res = s.recv(100) assert res == b'bye', repr(res) # print('make_request - recvd %r' % res) - # s.close() def run_interaction(run_client): - s, port = init_server() - start_new_thread(handle_request, (s, run_client)) + s = eventlet.listen(('127.0.0.1', 0)) + s.settimeout(SOCKET_TIMEOUT) + addr = s.getsockname() + print('run_interaction: addr:', addr) + eventlet.spawn(handle_request, s, run_client) if run_client: - start_new_thread(make_request, (port,)) - sleep(0.1 + SOCKET_TIMEOUT) - # print(sys.getrefcount(s.fd)) - # s.close() + eventlet.spawn(make_request, addr) + eventlet.sleep(0.1 + SOCKET_TIMEOUT) + print('run_interaction: refcount(s.fd)', sys.getrefcount(s.fd)) return weakref.ref(s.fd) def run_and_check(run_client): w = run_interaction(run_client=run_client) - clear_sys_exc_info() + # clear_sys_exc_info() gc.collect() - if w(): - print(pformat(gc.get_referrers(w()))) - for x in gc.get_referrers(w()): - print(pformat(x)) + fd = w() + print('run_and_check: weakref fd:', fd) + if fd: + print(pprint.pformat(gc.get_referrers(fd))) + for x in gc.get_referrers(fd): + print(pprint.pformat(x)) for y in gc.get_referrers(x): - print('- {0}'.format(pformat(y))) + print('- {0}'.format(pprint.pformat(y))) raise AssertionError('server should be dead by now') diff --git a/tests/test__socket_errors.py b/tests/test__socket_errors.py index c33b227..8cb1f87 100644 --- a/tests/test__socket_errors.py +++ b/tests/test__socket_errors.py @@ -56,7 +56,7 @@ class TestSocketErrors(unittest.TestCase): def test_create_connection_refused(): errno = None try: - socket.create_connection(('127.0.0.1', 0)) + socket.create_connection(('127.0.0.1', 1)) except socket.error as ex: errno = ex.errno assert errno in [111, 61, 10061], 'Expected socket.error ECONNREFUSED, got {0}'.format(errno) diff --git a/tests/tpool_test.py b/tests/tpool_test.py index d44ff6b..78e3437 100644 --- a/tests/tpool_test.py +++ b/tests/tpool_test.py @@ -20,7 +20,7 @@ import re import time import eventlet -from eventlet import tpool, debug, event +from eventlet import tpool from eventlet.support import six import tests @@ -218,10 +218,13 @@ class TestTpool(tests.LimitedTestCase): @tests.skip_with_pyevent def test_timeout(self): - import time - eventlet.Timeout(0.1, eventlet.TimeoutError()) - self.assertRaises(eventlet.TimeoutError, - tpool.execute, time.sleep, 0.3) + blocking = eventlet.patcher.original('time') + eventlet.Timeout(0.1, eventlet.Timeout()) + try: + tpool.execute(blocking.sleep, 0.3) + assert False, 'Expected Timeout' + except eventlet.Timeout: + pass @tests.skip_with_pyevent def test_killall(self): @@ -230,7 +233,7 @@ class TestTpool(tests.LimitedTestCase): @tests.skip_with_pyevent def test_killall_remaining_results(self): - semaphore = event.Event() + semaphore = eventlet.Event() def native_fun(): time.sleep(.5) diff --git a/tox.ini b/tox.ini index 7e9daec..f6dfd66 100644 --- a/tox.ini +++ b/tox.ini @@ -14,9 +14,9 @@ show-source = 1 statistics = 1 [tox] -minversion=1.8 +minversion=2.5 envlist = - pep8, py{26,27,33,34,35,py}-{selects,poll,epolls} + pep8, py{26,27,33,34,35,36,py}-{selects,poll,epolls} [testenv:pep8] basepython = python2.7 @@ -34,12 +34,14 @@ setenv = selects: EVENTLET_HUB = selects poll: EVENTLET_HUB = poll epolls: EVENTLET_HUB = epolls + tox_cover_args = --with-coverage --cover-erase --cover-package=eventlet basepython = py26: python2.6 py27: python2.7 py33: python3.3 py34: python3.4 py35: python3.5 + py36: python3.6 pypy: pypy deps = coverage==4.3.1 @@ -52,5 +54,5 @@ deps = {selects,poll,epolls}: psycopg2cffi-compat==1.1 {selects,poll,epolls}: pyzmq==13.1.0 commands = - nosetests --verbose --with-coverage --cover-erase --cover-package=eventlet {posargs:tests/} + nosetests --verbose {env:tox_cover_args} {posargs:tests/} coverage xml -i