mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
Run most of the tests under Python 2. (#405)
This commit is contained in:
parent
41d362708f
commit
105032c3ae
20 changed files with 127 additions and 46 deletions
3
Makefile
3
Makefile
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
import threading
|
||||
import warnings
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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"')
|
||||
|
|
|
|||
|
|
@ -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'):
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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'))
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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 = []
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue