Run most of the tests under Python 2. (#405)

This commit is contained in:
Eric Snow 2018-05-02 18:05:13 -06:00 committed by GitHub
parent 41d362708f
commit 105032c3ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 127 additions and 46 deletions

View file

@ -42,7 +42,8 @@ ci-lint: depends lint
.PHONY: ci-test
ci-test: depends
$(PYTHON) -m tests -v --full --no-network
# For now we use --quickpy2.
$(PYTHON) -m tests -v --full --no-network --quick-py2
.PHONY: ci-coverage
ci-coverage: depends

View file

@ -1,5 +1,8 @@
from collections import namedtuple
from collections.abc import Sequence
try:
from collections.abc import Sequence
except ImportError:
from collections import Sequence
from debugger_protocol._base import Readonly
from ._common import sentinel, NOT_SET, ANY, SIMPLE_TYPES

View file

@ -1,3 +1,45 @@
from __future__ import absolute_import
# Trigger the pydevd vendoring.
import os
import os.path
import sys
import unittest
# Importing "ptvsd" here triggers the vendoring code before any vendored
# code ever gets imported.
import ptvsd # noqa
from ptvsd._vendored import list_all as vendored
TEST_ROOT = os.path.dirname(__file__) # noqa
PROJECT_ROOT = os.path.dirname(TEST_ROOT) # noqa
VENDORED_ROOTS = vendored(resolve=True) # noqa
def skip_py2(decorated=None):
if sys.version_info[0] > 2:
return decorated
msg = 'not tested under Python 2'
if decorated is None:
raise unittest.SkipTest(msg)
else:
decorator = unittest.skip(msg)
return decorator(decorated)
if sys.version_info[0] == 2:
# Hack alert!!!
class SkippingTestSuite(unittest.TestSuite):
def __init__(self, tests=()):
if tests and type(tests[0]).__name__ == 'ModuleImportFailure':
_, exc, _ = sys.exc_info()
if isinstance(exc, unittest.SkipTest):
from unittest.loader import _make_failed_load_tests
suite = _make_failed_load_tests(
tests[0]._testMethodName,
exc,
type(self),
)
tests = tuple(suite)
unittest.TestSuite.__init__(self, tests)
unittest.TestLoader.suiteClass = SkippingTestSuite

View file

@ -6,17 +6,13 @@ import subprocess
import sys
import unittest
from ptvsd._vendored import list_all as vendored
TEST_ROOT = os.path.dirname(__file__)
PROJECT_ROOT = os.path.dirname(TEST_ROOT)
VENDORED_ROOTS = vendored(resolve=True)
from . import TEST_ROOT, PROJECT_ROOT, VENDORED_ROOTS
def convert_argv(argv):
help = False
quick = False
quickpy2 = False
network = True
runtests = True
lint = False
@ -26,6 +22,9 @@ def convert_argv(argv):
if arg == '--quick':
quick = True
continue
if arg == '--quick-py2':
quickpy2 = True
continue
elif arg == '--full':
quick = False
continue
@ -76,7 +75,7 @@ def convert_argv(argv):
quickroot = os.path.join(TEST_ROOT, 'ptvsd')
if quick:
start = quickroot
elif sys.version_info[0] != 3:
elif quickpy2 and sys.version_info[0] == 2:
start = quickroot
else:
start = PROJECT_ROOT

View file

@ -1,9 +1,7 @@
import sys
import unittest
from .. import skip_py2
# The code under the debugger_protocol package isn't used
# by the debugger (it's used by schema-related tools). So we don't need
# to support Python 2.
if sys.version_info[0] == 2:
raise unittest.SkipTest('not tested under Python 2')
skip_py2()

View file

@ -1,3 +1,5 @@
import sys
class Counter(object):
"""An introspectable, dynamic alternative to itertools.count()."""
@ -23,6 +25,9 @@ class Counter(object):
self._last = self._start
return self._last
if sys.version_info[0] == 2:
next = __next__
@property
def start(self):
return self._start

View file

@ -1,3 +1,5 @@
from __future__ import absolute_import
import threading
import warnings

View file

@ -1,7 +1,8 @@
from __future__ import absolute_import
from __future__ import absolute_import, print_function
import contextlib
import json
import socket
import sys
import time
import threading
@ -34,10 +35,10 @@ class DebugSessionConnection(Closeable):
for _ in range(int(timeout * 10)):
try:
sock.connect(addr)
except OSError:
except (OSError, socket.error):
if cls.VERBOSE:
print('+', end='')
sys.stdout.flush()
sys.stdout.flush()
time.sleep(0.1)
else:
break
@ -87,7 +88,7 @@ class DebugSessionConnection(Closeable):
read = recv_as_read(self._sock)
for msg, _, _ in read_messages(read, stop=stop):
if self.VERBOSE:
print(msg)
print(repr(msg))
yield parse_message(msg)
def send(self, req):

View file

@ -1,6 +1,9 @@
from __future__ import absolute_import
import http.server
try:
from http.server import BaseHTTPRequestHandler, HTTPServer
except ImportError:
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import threading
@ -23,7 +26,7 @@ class Server:
def start(self):
if self._server is not None:
raise RuntimeError('already started')
self._server = http.server.HTTPServer(self._addr, self.handler)
self._server = HTTPServer(self._addr, self.handler)
self._thread = threading.Thread(
target=lambda: self._server.serve_forever())
self._thread.start()
@ -48,7 +51,7 @@ class Server:
def json_file_handler(data):
"""Return an HTTP handler that always serves the given JSON bytes."""
class HTTPHandler(http.server.BaseHTTPRequestHandler):
class HTTPHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-Type', b'application/json')
@ -66,7 +69,7 @@ def json_file_handler(data):
def error_handler(code, msg):
"""Return an HTTP handler that always returns the given error code."""
class HTTPHandler(http.server.BaseHTTPRequestHandler):
class HTTPHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_error(code, msg)

View file

@ -73,7 +73,11 @@ class Proc(Closeable):
def _close(self):
if self._proc is not None:
self._proc.kill()
try:
self._proc.kill()
except OSError:
# Already killed.
pass
if self.VERBOSE:
lines = self.output.decode('utf-8').splitlines()
print(' + ' + '\n + '.join(lines))

View file

@ -8,6 +8,7 @@ import warnings
from . import socket
from .counter import Counter
from .threading import acquire_with_timeout
try:
@ -259,7 +260,7 @@ class MessageDaemon(Daemon):
yield req
# Wait for the message to match.
if lock.acquire(timeout=timeout):
if acquire_with_timeout(lock, timeout=timeout):
lock.release()
else:
msg = 'timed out after {} seconds waiting for message ({})'
@ -278,7 +279,7 @@ class MessageDaemon(Daemon):
def _listen(self):
try:
with self._sock.makefile('rb') as sockfile:
with contextlib.closing(self._sock.makefile('rb')) as sockfile:
for msg in self._protocol.iter(sockfile, lambda: self._closed):
if isinstance(msg, StreamFailure):
self._failures.append(msg)

View file

@ -3,6 +3,7 @@ import time
import ptvsd.daemon
from tests.helpers import socket
from tests.helpers.threading import acquire_with_timeout
class PTVSD(ptvsd.daemon.Daemon):
@ -112,7 +113,7 @@ class BinderBase(object):
self._thread = threading.Thread(target=self._run)
self._thread.start()
# Wait for ptvsd to start up.
if self._waiter.acquire(timeout=1):
if acquire_with_timeout(self._waiter, timeout=1):
self._waiter.release()
else:
raise RuntimeError('timed out')

View file

@ -5,6 +5,7 @@ import warnings
import ptvsd._main
from tests.helpers import protocol
from tests.helpers.threading import acquire_with_timeout
from ._binder import BinderBase
@ -38,7 +39,7 @@ class Binder(BinderBase):
)
# Block until "done" debugging.
if not self._lock.acquire(timeout=3):
if not acquire_with_timeout(self._lock, timeout=3):
# This shouldn't happen since the timeout on event waiting
# is this long.
warnings.warn('timeout out waiting for "done"')

View file

@ -1,4 +1,5 @@
from collections import namedtuple
import sys
try:
from urllib.parse import quote, unquote
except ImportError:
@ -11,6 +12,10 @@ from tests.helpers.protocol import StreamFailure
# TODO: Everything here belongs in a proper pydevd package.
if sys.version_info[0] > 2:
basestring = str
def parse_message(msg):
"""Return a message object for the given "msg" data."""
if type(msg) is bytes:
@ -112,7 +117,7 @@ class Message(namedtuple('Message', 'cmdid seq payload')):
"""Return the de-serialized payload."""
if isinstance(payload, bytes):
payload = payload.decode('utf-8')
if isinstance(payload, str):
if isinstance(payload, basestring):
text = unquote(payload)
return cls._parse_payload_text(text)
elif hasattr(payload, 'as_text'):

View file

@ -8,11 +8,12 @@ import warnings
if sys.version_info < (3,):
def acquire_with_timeout(lock, timeout):
segments = int(timeout * 10) + 1
for _ in range(segments):
if lock.acquire(False):
return True
for _ in range(int(timeout * 10)):
time.sleep(0.1)
if lock.acquire(False):
return True
time.sleep(0.1)
else:
return False
else:

View file

@ -1,5 +1,6 @@
from collections import namedtuple
import json
import sys
from debugger_protocol.messages import wireformat
from tests.helpers.protocol import StreamFailure
@ -7,6 +8,10 @@ from tests.helpers.protocol import StreamFailure
# TODO: Use more of the code from debugger_protocol.
if sys.version_info[0] > 2:
unicode = str
class ProtocolMessageError(Exception): pass # noqa
class MalformedMessageError(ProtocolMessageError): pass # noqa
class IncompleteMessageError(MalformedMessageError): pass # noqa
@ -15,7 +20,7 @@ class UnsupportedMessageTypeError(ProtocolMessageError): pass # noqa
def parse_message(msg):
"""Return a message object for the given "msg" data."""
if type(msg) is str:
if type(msg) is str or type(msg) is unicode:
data = json.loads(msg)
elif isinstance(msg, bytes):
data = json.loads(msg.decode('utf-8'))

View file

@ -477,17 +477,18 @@ class VSCFixture(FixtureBase):
except AttributeError:
return None
def send_request(self, command, args=None, handle_response=None):
def send_request(self, cmd, args=None, handle_response=None, timeout=1):
kwargs = dict(args or {}, handler=handle_response)
with self._wait_for_response(command, **kwargs) as req:
with self._wait_for_response(cmd, timeout=timeout, **kwargs) as req:
self.fake.send_request(req)
return req
@contextlib.contextmanager
def _wait_for_response(self, command, *args, **kwargs):
handler = kwargs.pop('handler', None)
handle = kwargs.pop('handler', None)
timeout = kwargs.pop('timeout', 1)
req = self.msgs.new_request(command, *args, **kwargs)
with self.fake.wait_for_response(req, handler=handler):
with self.fake.wait_for_response(req, handler=handle, timeout=timeout):
yield req
if self._hidden:
self.msgs.next_response()
@ -623,8 +624,9 @@ class HighlevelFixture(object):
thread = self._pydevd.threads.add(name)
self._default_threads[name] = thread
def send_request(self, command, args=None, handle_response=None):
return self._vsc.send_request(command, args, handle_response)
def send_request(self, command, args=None, handle_response=None, **kwargs):
return self._vsc.send_request(command, args, handle_response,
**kwargs)
@contextlib.contextmanager
def wait_for_event(self, event, *args, **kwargs):

View file

@ -9,6 +9,13 @@ from tests.helpers.vsc import parse_message, VSCMessages
from tests.helpers.workspace import Workspace, PathEntry
def _strip_pydevd_output(out):
# TODO: Leave relevant lines from before the marker?
pre, sep, out = out.partition(
'pydev debugger: starting' + os.linesep + os.linesep)
return out if sep else pre
def lifecycle_handshake(session, command='launch', options=None):
with session.wait_for_event('initialized'):
req_initialize = session.send_request(
@ -92,8 +99,8 @@ class CLITests(TestsBase, unittest.TestCase):
session.send_request('disconnect')
out = adapter.output
self.assertEqual(out.decode('utf-8'),
"[{!r}, '--eggs']\n".format(filename))
self.assertEqual(out.decode('utf-8').strip().splitlines()[-1],
u"[{!r}, '--eggs']".format(filename))
def test_run_to_completion(self):
filename = self.pathentry.write_module('spam', """
@ -199,7 +206,7 @@ class LifecycleTests(TestsBase, unittest.TestCase):
timeout=3.0,
)
wait_for_started()
out = adapter.output
out = adapter.output.decode('utf-8')
self.assert_received(session.received, [
# TODO: Use self.new_event()...
@ -216,7 +223,8 @@ class LifecycleTests(TestsBase, unittest.TestCase):
},
},
])
self.assertEqual(out, b'')
out = _strip_pydevd_output(out)
self.assertEqual(out, '')
def test_launch_ptvsd_client(self):
argv = []

View file

@ -7,6 +7,8 @@ import tempfile
from textwrap import dedent
import unittest
from tests import skip_py2
skip_py2() # noqa
from tests.helpers import http
from debugger_protocol.schema.__main__ import handle_check

View file

@ -3,13 +3,10 @@ import os.path
import unittest
import sys
from . import TEST_ROOT, PROJECT_ROOT
from .__main__ import convert_argv
TEST_ROOT = os.path.dirname(__file__)
PROJECT_ROOT = os.path.dirname(TEST_ROOT)
class ConvertArgsTests(unittest.TestCase):
def test_no_args(self):