From 75e53eed24a0eb0a4c4fb95b1a88fa22c462cf4a Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Thu, 26 Jul 2018 15:13:59 -0700 Subject: [PATCH] Django and Flask system test (#689) Fixes #675 #674 * Flask system tests initial * Enable route and template breakpoint tests. * Fix typo * Remove wait for thread exit * Few more tweaks * More tweaks * Try adding LC_ALL utf-8 * Add more tests * Remove flask version pinning * Rename and move flask tests to make room for django tests * Switch to using requests * Fix line number in attach * Add django to packages * Add Django tests * Tweaks to django test * Remove hardcoded port --- Makefile | 3 + tests/helpers/webhelper.py | 16 + .../test_web_frameworks/django/attach/app.py | 95 +++ .../django/attach/templates/hello.html | 10 + .../test_web_frameworks/django/launch/app.py | 88 +++ .../django/launch/templates/hello.html | 10 + .../test_web_frameworks/flask/attach/app.py | 56 ++ .../flask/attach/templates/hello.html | 10 + .../test_web_frameworks/flask/launch/app.py | 48 ++ .../flask/launch/templates/hello.html | 10 + tests/system_tests/test_web_frameworks.py | 638 ++++++++++++++++++ 11 files changed, 984 insertions(+) create mode 100644 tests/helpers/webhelper.py create mode 100644 tests/resources/system_tests/test_web_frameworks/django/attach/app.py create mode 100644 tests/resources/system_tests/test_web_frameworks/django/attach/templates/hello.html create mode 100644 tests/resources/system_tests/test_web_frameworks/django/launch/app.py create mode 100644 tests/resources/system_tests/test_web_frameworks/django/launch/templates/hello.html create mode 100644 tests/resources/system_tests/test_web_frameworks/flask/attach/app.py create mode 100644 tests/resources/system_tests/test_web_frameworks/flask/attach/templates/hello.html create mode 100644 tests/resources/system_tests/test_web_frameworks/flask/launch/app.py create mode 100644 tests/resources/system_tests/test_web_frameworks/flask/launch/templates/hello.html create mode 100644 tests/system_tests/test_web_frameworks.py diff --git a/Makefile b/Makefile index 09502b08..19ac82c1 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,9 @@ depends: $(PYTHON) -m pip install flake8_formatter_junit_xml $(PYTHON) -m pip install unittest-xml-reporting $(PYTHON) -m pip install coverage + $(PYTHON) -m pip install requests + $(PYTHON) -m pip install flask + $(PYTHON) -m pip install django .PHONY: lint lint: ## Lint the Python source code. diff --git a/tests/helpers/webhelper.py b/tests/helpers/webhelper.py new file mode 100644 index 00000000..df5028cd --- /dev/null +++ b/tests/helpers/webhelper.py @@ -0,0 +1,16 @@ +import requests + + +def get_web_string(path, obj): + r = requests.get(path) + content = r.text + if obj is not None: + obj['content'] = content + return content + + +def get_web_string_no_error(path, obj): + try: + return get_web_string(path, obj) + except Exception: + pass diff --git a/tests/resources/system_tests/test_web_frameworks/django/attach/app.py b/tests/resources/system_tests/test_web_frameworks/django/attach/app.py new file mode 100644 index 00000000..f4142abd --- /dev/null +++ b/tests/resources/system_tests/test_web_frameworks/django/attach/app.py @@ -0,0 +1,95 @@ +import os +import signal +import sys +from django.conf import settings +from django.urls import path +from django.core.management import execute_from_command_line +from django.http import HttpResponse +from django.template import loader +import ptvsd + + +ptvsd_host = os.getenv('PTVSD_HOST', 'localhost') +ptvsd_port = os.getenv('PTVSD_PORT', '9879') +ptvsd.enable_attach((ptvsd_host, ptvsd_port)) +ptvsd.wait_for_attach() + + +def sigint_handler(signal, frame): + import django.dispatch + djshutdown = django.dispatch.Signal() + djshutdown.send('system') + sys.exit(0) + + +signal.signal(signal.SIGINT, sigint_handler) + + +settings.configure( + DEBUG=True, + SECRET_KEY='B21034EB-A1A8-4DDD-90B4-C13B67BE2AE7', + ROOT_URLCONF=sys.modules[__name__], + TEMPLATES=[ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'APP_DIRS': True, + 'DIRS': [ + 'templates/' + ] + }, + ] +) + + +def home(request): + title = 'hello' + content = 'Django-Django-Test' + template = loader.get_template('hello.html') + context = { + 'title': title, + 'content': content, + } + return HttpResponse(template.render(context, request)) + + +def bad_route_handled(request): + try: + raise ArithmeticError('Hello') + except Exception: + pass + title = 'hello' + content = 'Django-Django-Test' + template = loader.get_template('hello.html') + context = { + 'title': title, + 'content': content, + } + return HttpResponse(template.render(context, request)) + + +def bad_route_unhandled(request): + raise ArithmeticError('Hello') + title = 'hello' + content = 'Django-Django-Test' + template = loader.get_template('hello.html') + context = { + 'title': title, + 'content': content, + } + return HttpResponse(template.render(context, request)) + + +def exit_app(request): + os.kill(os.getpid(), signal.SIGTERM) + return HttpResponse('Done') + + +urlpatterns = [ + path('', home, name='home'), + path('handled', bad_route_handled, name='bad_route_handled'), + path('unhandled', bad_route_unhandled, name='bad_route_unhandled'), + path('exit', exit_app, name='exit_app'), +] + +if __name__ == '__main__': + execute_from_command_line(sys.argv) diff --git a/tests/resources/system_tests/test_web_frameworks/django/attach/templates/hello.html b/tests/resources/system_tests/test_web_frameworks/django/attach/templates/hello.html new file mode 100644 index 00000000..3784dad6 --- /dev/null +++ b/tests/resources/system_tests/test_web_frameworks/django/attach/templates/hello.html @@ -0,0 +1,10 @@ + + + + + {{ title }} + + + {{ content }} + + \ No newline at end of file diff --git a/tests/resources/system_tests/test_web_frameworks/django/launch/app.py b/tests/resources/system_tests/test_web_frameworks/django/launch/app.py new file mode 100644 index 00000000..8297dd86 --- /dev/null +++ b/tests/resources/system_tests/test_web_frameworks/django/launch/app.py @@ -0,0 +1,88 @@ +import os +import signal +import sys +from django.conf import settings +from django.urls import path +from django.core.management import execute_from_command_line +from django.http import HttpResponse +from django.template import loader + + +def sigint_handler(signal, frame): + import django.dispatch + djshutdown = django.dispatch.Signal() + djshutdown.send('system') + sys.exit(0) + + +signal.signal(signal.SIGINT, sigint_handler) + + +settings.configure( + DEBUG=True, + SECRET_KEY='CD8FF4C1-7E6C-4E45-922D-C796271F2345', + ROOT_URLCONF=sys.modules[__name__], + TEMPLATES=[ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'APP_DIRS': True, + 'DIRS': [ + 'templates/' + ] + }, + ] +) + + +def home(request): + title = 'hello' + content = 'Django-Django-Test' + template = loader.get_template('hello.html') + context = { + 'title': title, + 'content': content, + } + return HttpResponse(template.render(context, request)) + + +def bad_route_handled(request): + try: + raise ArithmeticError('Hello') + except Exception: + pass + title = 'hello' + content = 'Django-Django-Test' + template = loader.get_template('hello.html') + context = { + 'title': title, + 'content': content, + } + return HttpResponse(template.render(context, request)) + + +def bad_route_unhandled(request): + raise ArithmeticError('Hello') + title = 'hello' + content = 'Django-Django-Test' + template = loader.get_template('hello.html') + context = { + 'title': title, + 'content': content, + } + return HttpResponse(template.render(context, request)) + + +def exit_app(request): + os.kill(os.getpid(), signal.SIGTERM) + return HttpResponse('Done') + + +urlpatterns = [ + path('', home, name='home'), + path('handled', bad_route_handled, name='bad_route_handled'), + path('unhandled', bad_route_unhandled, name='bad_route_unhandled'), + path('exit', exit_app, name='exit_app'), +] + +if __name__ == '__main__': + execute_from_command_line(sys.argv) diff --git a/tests/resources/system_tests/test_web_frameworks/django/launch/templates/hello.html b/tests/resources/system_tests/test_web_frameworks/django/launch/templates/hello.html new file mode 100644 index 00000000..3784dad6 --- /dev/null +++ b/tests/resources/system_tests/test_web_frameworks/django/launch/templates/hello.html @@ -0,0 +1,10 @@ + + + + + {{ title }} + + + {{ content }} + + \ No newline at end of file diff --git a/tests/resources/system_tests/test_web_frameworks/flask/attach/app.py b/tests/resources/system_tests/test_web_frameworks/flask/attach/app.py new file mode 100644 index 00000000..2ed05744 --- /dev/null +++ b/tests/resources/system_tests/test_web_frameworks/flask/attach/app.py @@ -0,0 +1,56 @@ +import os +import ptvsd +from flask import Flask +from flask import render_template + + +ptvsd_host = os.getenv('PTVSD_HOST', 'localhost') +ptvsd_port = os.getenv('PTVSD_PORT', '9879') +ptvsd.enable_attach((ptvsd_host, ptvsd_port)) +ptvsd.wait_for_attach() + + +app = Flask(__name__) + + +@app.route("/") +def home(): + content = 'Flask-Jinja-Test' + return render_template( + "hello.html", + title='Hello', + content=content + ) + + +@app.route("/handled") +def bad_route_handled(): + try: + raise ArithmeticError('Hello') + except Exception: + pass + return render_template( + "hello.html", + title='Hello', + content='Flask-Jinja-Test' + ) + + +@app.route("/unhandled") +def bad_route_unhandled(): + raise ArithmeticError('Hello') + return render_template( + "hello.html", + title='Hello', + content='Flask-Jinja-Test' + ) + + +@app.route("/exit") +def exit_app(): + from flask import request + func = request.environ.get('werkzeug.server.shutdown') + if func is None: + raise RuntimeError('No shutdown') + func() + return 'Done' diff --git a/tests/resources/system_tests/test_web_frameworks/flask/attach/templates/hello.html b/tests/resources/system_tests/test_web_frameworks/flask/attach/templates/hello.html new file mode 100644 index 00000000..3784dad6 --- /dev/null +++ b/tests/resources/system_tests/test_web_frameworks/flask/attach/templates/hello.html @@ -0,0 +1,10 @@ + + + + + {{ title }} + + + {{ content }} + + \ No newline at end of file diff --git a/tests/resources/system_tests/test_web_frameworks/flask/launch/app.py b/tests/resources/system_tests/test_web_frameworks/flask/launch/app.py new file mode 100644 index 00000000..52758964 --- /dev/null +++ b/tests/resources/system_tests/test_web_frameworks/flask/launch/app.py @@ -0,0 +1,48 @@ +from flask import Flask +from flask import render_template + + +app = Flask(__name__) + + +@app.route("/") +def home(): + content = 'Flask-Jinja-Test' + return render_template( + "hello.html", + title='Hello', + content=content + ) + + +@app.route("/handled") +def bad_route_handled(): + try: + raise ArithmeticError('Hello') + except Exception: + pass + return render_template( + "hello.html", + title='Hello', + content='Flask-Jinja-Test' + ) + + +@app.route("/unhandled") +def bad_route_unhandled(): + raise ArithmeticError('Hello') + return render_template( + "hello.html", + title='Hello', + content='Flask-Jinja-Test' + ) + + +@app.route("/exit") +def exit_app(): + from flask import request + func = request.environ.get('werkzeug.server.shutdown') + if func is None: + raise RuntimeError('No shutdown') + func() + return 'Done' diff --git a/tests/resources/system_tests/test_web_frameworks/flask/launch/templates/hello.html b/tests/resources/system_tests/test_web_frameworks/flask/launch/templates/hello.html new file mode 100644 index 00000000..3784dad6 --- /dev/null +++ b/tests/resources/system_tests/test_web_frameworks/flask/launch/templates/hello.html @@ -0,0 +1,10 @@ + + + + + {{ title }} + + + {{ content }} + + \ No newline at end of file diff --git a/tests/system_tests/test_web_frameworks.py b/tests/system_tests/test_web_frameworks.py new file mode 100644 index 00000000..afd68010 --- /dev/null +++ b/tests/system_tests/test_web_frameworks.py @@ -0,0 +1,638 @@ +import os +import os.path +import re +import threading +import unittest + +from tests.helpers.debugsession import Awaitable +from tests.helpers.resource import TestResources +from tests.helpers.webhelper import get_web_string_no_error +from . import ( + _strip_newline_output_events, lifecycle_handshake, + LifecycleTestsBase, DebugInfo, PORT +) + + +TEST_FILES = TestResources.from_module(__name__) +re_link = r"(http(s|)\:\/\/[\w\.]*\:[0-9]{4,6}(\/|))" + + +class WebFrameworkTests(LifecycleTestsBase): + def run_test_with_break_points(self, debug_info, **kwargs): + bp_filename = kwargs.pop('bp_filename') + bp_line = kwargs.pop('bp_line') + bp_name = kwargs.pop('bp_name') + bp_var_value = kwargs.pop('bp_var_value') + framework = kwargs.pop('framework', 'Django') + if (debug_info.starttype == 'attach'): + pathMappings = [] + pathMappings.append({ + 'localRoot': debug_info.cwd, + 'remoteRoot': debug_info.cwd + }) + options = { + 'debugOptions': ['RedirectOutput', framework], + 'pathMappings': pathMappings + } + else: + options = {'debugOptions': ['RedirectOutput', framework]} + + breakpoints = [{ + 'source': { + 'path': bp_filename + }, + 'breakpoints': [{ + 'line': bp_line + }] + }] + + with self.start_debugging(debug_info) as dbg: + session = dbg.session + with session.wait_for_event('stopped') as result: + (_, req_launch_attach, _, _, _, _, + ) = lifecycle_handshake(session, debug_info.starttype, + options=options, + breakpoints=breakpoints) + req_launch_attach.wait() + + # wait for flask web server start + count = 0 + path = None + while path is None and count < 10: + outevent = session.get_awaiter_for_event('output') + Awaitable.wait_all(outevent) + events = self.find_events( + session.received, 'output') + count += 1 + for e in events: + matches = re.findall(re_link, e.body['output']) + if len(matches) > 0 and len(matches[0]) > 0 and \ + len(matches[0][0].strip()) > 0: + path = matches[0][0] + break + + # connect to web server + web_result = {} + web_client_thread = threading.Thread( + target=get_web_string_no_error, + args=(path, web_result), + name='test.webClient' + ) + + web_client_thread.start() + + event = result['msg'] + tid = event.body['threadId'] + + req_stacktrace = session.send_request( + 'stackTrace', + threadId=tid, + ) + req_stacktrace.wait() + stacktrace = req_stacktrace.resp.body + + frame_id = stacktrace['stackFrames'][0]['id'] + req_scopes = session.send_request( + 'scopes', + frameId=frame_id, + ) + req_scopes.wait() + scopes = req_scopes.resp.body['scopes'] + variables_reference = scopes[0]['variablesReference'] + req_variables = session.send_request( + 'variables', + variablesReference=variables_reference, + ) + req_variables.wait() + variables = req_variables.resp.body['variables'] + + session.send_request( + 'continue', + threadId=tid, + ) + + # wait for flask rendering thread to exit + web_client_thread.join(timeout=0.1) + + # shutdown to web server + path += 'exit' if path.endswith('/') else '/exit' + web_client_thread = threading.Thread( + target=get_web_string_no_error, + args=(path, None), + name='test.webClient.shutdown' + ) + web_client_thread.start() + web_client_thread.join(timeout=1) + + received = list(_strip_newline_output_events(session.received)) + + self.assertGreaterEqual(stacktrace['totalFrames'], 1) + self.assert_is_subset(stacktrace, { + # We get Python and PTVSD frames as well. + # 'totalFrames': 2, + 'stackFrames': [{ + 'id': 1, + 'name': bp_name, + 'source': { + 'sourceReference': 0, + 'path': bp_filename + }, + 'line': bp_line, + 'column': 1, + }], + }) + variables = list(v for v in variables if v['name'] == 'content') + self.assert_is_subset(variables, [{ + 'name': 'content', + 'type': 'str', + 'value': repr(bp_var_value), + 'presentationHint': {'attributes': ['rawString']}, + 'evaluateName': 'content' + }]) + self.assertTrue(web_result['content'].find(bp_var_value) != -1) + self.assert_contains(received, [ + self.new_event( + 'stopped', + reason='breakpoint', + threadId=tid, + text=None, + description=None, + ), + self.new_event('continued', threadId=tid), + ]) + + if framework != 'Django': + # TODO: Figure out better way to shutdown Django + self.assert_contains(received, [ + self.new_event('exited', exitCode=0), + self.new_event('terminated'), + ]) + + def run_test_with_handled_exception(self, debug_info, framework, + expected_source_name): + if (debug_info.starttype == 'attach'): + pathMappings = [] + pathMappings.append({ + 'localRoot': debug_info.cwd, + 'remoteRoot': debug_info.cwd + }) + options = { + 'debugOptions': ['RedirectOutput', framework], + 'pathMappings': pathMappings + } + else: + options = {'debugOptions': ['RedirectOutput', framework]} + + excbreakpoints = [{'filters': ['raised', 'uncaught']}] + with self.start_debugging(debug_info) as dbg: + session = dbg.session + with session.wait_for_event('stopped') as result: + (_, req_launch_attach, _, _, _, _, + ) = lifecycle_handshake(session, debug_info.starttype, + options=options, + excbreakpoints=excbreakpoints) + req_launch_attach.wait() + + # wait for flask web server start + count = 0 + base_path = None + while base_path is None and count < 10: + outevent = session.get_awaiter_for_event('output') + Awaitable.wait_all(outevent) + events = self.find_events( + session.received, 'output') + count += 1 + for e in events: + matches = re.findall(re_link, e.body['output']) + if len(matches) > 0 and len(matches[0]) > 0 and \ + len(matches[0][0].strip()) > 0: + base_path = matches[0][0] + break + + # connect to web server + path = base_path + \ + 'handled' if base_path.endswith('/') else '/handled' + web_result = {} + web_client_thread = threading.Thread( + target=get_web_string_no_error, + args=(path, web_result), + name='test.webClient' + ) + + web_client_thread.start() + + event = result['msg'] + thread_id = event.body['threadId'] + + req_exc_info = dbg.session.send_request( + 'exceptionInfo', + threadId=thread_id, + ) + req_exc_info.wait() + exc_info = req_exc_info.resp.body + + self.assert_is_subset( + exc_info, { + 'exceptionId': 'ArithmeticError', + 'breakMode': 'always', + 'details': { + 'typeName': 'ArithmeticError', + 'source': expected_source_name + } + }) + + continued = dbg.session.get_awaiter_for_event('continued') + dbg.session.send_request( + 'continue', + threadId=thread_id, + ).wait() + Awaitable.wait_all(continued) + + # Shutdown webserver + path = base_path + 'exit' if base_path.endswith('/') else '/exit' + web_client_thread = threading.Thread( + target=get_web_string_no_error, + args=(path, None), + name='test.webClient.shutdown' + ) + web_client_thread.start() + web_client_thread.join(timeout=1) + + received = list(_strip_newline_output_events(dbg.session.received)) + if framework != 'Django': + # TODO: Figure out better way to shutdown Django + self.assert_contains(received, [ + self.new_event('exited', exitCode=0), + self.new_event('terminated'), + ]) + + def run_test_with_unhandled_exception(self, debug_info, framework, + expected_source_name): + if (debug_info.starttype == 'attach'): + pathMappings = [] + pathMappings.append({ + 'localRoot': debug_info.cwd, + 'remoteRoot': debug_info.cwd + }) + options = { + 'debugOptions': ['RedirectOutput', framework], + 'pathMappings': pathMappings + } + else: + options = {'debugOptions': ['RedirectOutput', framework]} + + excbreakpoints = [{'filters': ['raised', 'uncaught']}] + with self.start_debugging(debug_info) as dbg: + session = dbg.session + with session.wait_for_event('stopped') as result: + (_, req_launch_attach, _, _, _, _, + ) = lifecycle_handshake(session, debug_info.starttype, + options=options, + excbreakpoints=excbreakpoints) + req_launch_attach.wait() + + # wait for flask web server start + count = 0 + base_path = None + while base_path is None and count < 10: + outevent = session.get_awaiter_for_event('output') + Awaitable.wait_all(outevent) + events = self.find_events( + session.received, 'output') + count += 1 + for e in events: + matches = re.findall(re_link, e.body['output']) + if len(matches) > 0 and len(matches[0]) > 0 and \ + len(matches[0][0].strip()) > 0: + base_path = matches[0][0] + break + + # connect to web server + path = base_path + \ + 'unhandled' if base_path.endswith('/') else '/unhandled' + web_result = {} + web_client_thread = threading.Thread( + target=get_web_string_no_error, + args=(path, web_result), + name='test.webClient' + ) + + web_client_thread.start() + + event = result['msg'] + thread_id = event.body['threadId'] + + req_exc_info = dbg.session.send_request( + 'exceptionInfo', + threadId=thread_id, + ) + req_exc_info.wait() + exc_info = req_exc_info.resp.body + + self.assert_is_subset( + exc_info, { + 'exceptionId': 'ArithmeticError', + 'breakMode': 'always', + 'details': { + 'typeName': 'ArithmeticError', + 'source': expected_source_name + } + }) + + continued = dbg.session.get_awaiter_for_event('continued') + dbg.session.send_request( + 'continue', + threadId=thread_id, + ).wait() + Awaitable.wait_all(continued) + + # Shutdown webserver + path = base_path + 'exit' if base_path.endswith('/') else '/exit' + web_client_thread = threading.Thread( + target=get_web_string_no_error, + args=(path, None), + name='test.webClient.shutdown' + ) + web_client_thread.start() + web_client_thread.join(timeout=1) + + received = list(_strip_newline_output_events(dbg.session.received)) + if framework != 'Django': + # TODO: Figure out better way to shutdown Django + self.assert_contains(received, [ + self.new_event('exited', exitCode=0), + self.new_event('terminated'), + ]) + + +class FlaskLaunchFileTests(WebFrameworkTests): + def test_with_route_break_points(self): + filename = TEST_FILES.resolve('flask', 'launch', 'app.py') + cwd = os.path.dirname(filename) + self.run_test_with_break_points( + DebugInfo( + modulename='flask', + argv=['run', '--no-debugger', '--no-reload', '--with-threads'], + env={ + 'FLASK_APP': 'app.py', + 'FLASK_ENV': 'development', + 'FLASK_DEBUG': '0', + 'LC_ALL': 'C.UTF-8', + 'LANG': 'C.UTF-8' + }, + cwd=cwd), + framework='Jinja', + bp_filename=filename, bp_line=11, bp_name='home', + bp_var_value='Flask-Jinja-Test') + + def test_with_template_break_points(self): + filename = TEST_FILES.resolve('flask', 'launch', 'app.py') + template = TEST_FILES.resolve( + 'flask', 'launch', 'templates', 'hello.html') + cwd = os.path.dirname(filename) + self.run_test_with_break_points( + DebugInfo( + modulename='flask', + argv=['run', '--no-debugger', '--no-reload', '--with-threads'], + env={ + 'FLASK_APP': 'app.py', + 'FLASK_ENV': 'development', + 'FLASK_DEBUG': '0', + 'LC_ALL': 'C.UTF-8', + 'LANG': 'C.UTF-8' + }, + cwd=cwd), + framework='Jinja', + bp_filename=template, bp_line=8, bp_name='template', + bp_var_value='Flask-Jinja-Test') + + def test_with_handled_exceptions(self): + filename = TEST_FILES.resolve('flask', 'launch', 'app.py') + cwd = os.path.dirname(filename) + self.run_test_with_handled_exception( + DebugInfo( + modulename='flask', + argv=['run', '--no-debugger', '--no-reload', '--with-threads'], + env={ + 'FLASK_APP': 'app.py', + 'FLASK_ENV': 'development', + 'FLASK_DEBUG': '0', + 'LC_ALL': 'C.UTF-8', + 'LANG': 'C.UTF-8' + }, + cwd=cwd), 'Jinja', filename) + + def test_with_unhandled_exceptions(self): + filename = TEST_FILES.resolve('flask', 'launch', 'app.py') + cwd = os.path.dirname(filename) + self.run_test_with_unhandled_exception( + DebugInfo( + modulename='flask', + argv=['run', '--no-debugger', '--no-reload', '--with-threads'], + env={ + 'FLASK_APP': 'app.py', + 'FLASK_ENV': 'development', + 'FLASK_DEBUG': '0', + 'LC_ALL': 'C.UTF-8', + 'LANG': 'C.UTF-8' + }, + cwd=cwd), 'Jinja', filename) + + +class FlaskAttachFileTests(WebFrameworkTests): + @unittest.skip('#545') + def test_with_route_break_points(self): + filename = TEST_FILES.resolve('flask', 'attach', 'app.py') + cwd = os.path.dirname(filename) + self.run_test_with_break_points( + DebugInfo( + starttype='attach', + modulename='flask', + argv=['run', '--no-debugger', '--no-reload', '--with-threads'], + env={ + 'FLASK_APP': 'app.py', + 'FLASK_ENV': 'development', + 'FLASK_DEBUG': '0', + 'LC_ALL': 'C.UTF-8', + 'LANG': 'C.UTF-8', + 'PTVSD_HOST': 'localhost', + 'PTVSD_PORT': str(PORT), + }, + cwd=cwd), + framework='Jinja', + bp_filename=filename, bp_line=19, bp_name='home', + bp_var_value='Flask-Jinja-Test') + + @unittest.skip('#545') + def test_with_template_break_points(self): + filename = TEST_FILES.resolve('flask', 'attach', 'app.py') + template = TEST_FILES.resolve( + 'flask', 'attach', 'templates', 'hello.html') + cwd = os.path.dirname(filename) + self.run_test_with_break_points( + DebugInfo( + starttype='attach', + modulename='flask', + argv=['run', '--no-debugger', '--no-reload', '--with-threads'], + env={ + 'FLASK_APP': 'app.py', + 'FLASK_ENV': 'production', + 'LC_ALL': 'C.UTF-8', + 'LANG': 'C.UTF-8', + 'PTVSD_HOST': 'localhost', + 'PTVSD_PORT': str(PORT), + }, + cwd=cwd), + framework='Jinja', + bp_filename=template, bp_line=8, bp_name='template', + bp_var_value='Flask-Jinja-Test') + + @unittest.skip('#545') + def test_with_handled_exceptions(self): + filename = TEST_FILES.resolve('flask', 'attach', 'app.py') + cwd = os.path.dirname(filename) + self.run_test_with_handled_exception( + DebugInfo( + starttype='attach', + modulename='flask', + argv=['run', '--no-debugger', '--no-reload', '--with-threads'], + env={ + 'FLASK_APP': 'app.py', + 'FLASK_ENV': 'production', + 'LC_ALL': 'C.UTF-8', + 'LANG': 'C.UTF-8', + 'PTVSD_HOST': 'localhost', + 'PTVSD_PORT': str(PORT), + }, + cwd=cwd), 'Jinja', filename) + + @unittest.skip('#545') + def test_with_unhandled_exceptions(self): + filename = TEST_FILES.resolve('flask', 'attach', 'app.py') + cwd = os.path.dirname(filename) + self.run_test_with_unhandled_exception( + DebugInfo( + starttype='attach', + modulename='flask', + argv=['run', '--no-debugger', '--no-reload', '--with-threads'], + env={ + 'FLASK_APP': 'app.py', + 'FLASK_ENV': 'production', + 'LC_ALL': 'C.UTF-8', + 'LANG': 'C.UTF-8', + 'PTVSD_HOST': 'localhost', + 'PTVSD_PORT': str(PORT), + }, + cwd=cwd), 'Jinja', filename) + + +class DjangoLaunchFileTests(WebFrameworkTests): + def test_with_route_break_points(self): + filename = TEST_FILES.resolve('django', 'launch', 'app.py') + cwd = os.path.dirname(filename) + self.run_test_with_break_points( + DebugInfo( + filename=filename, + argv=['runserver', '--noreload', '--nothreading'], + cwd=cwd), + framework='Django', + bp_filename=filename, bp_line=40, bp_name='home', + bp_var_value='Django-Django-Test') + + def test_with_template_break_points(self): + filename = TEST_FILES.resolve('django', 'launch', 'app.py') + template = TEST_FILES.resolve( + 'django', 'launch', 'templates', 'hello.html') + cwd = os.path.dirname(filename) + self.run_test_with_break_points( + DebugInfo( + filename=filename, + argv=['runserver', '--noreload', '--nothreading'], + cwd=cwd), + framework='Django', + bp_filename=template, bp_line=8, bp_name='Django Template', + bp_var_value='Django-Django-Test') + + def test_with_handled_exceptions(self): + filename = TEST_FILES.resolve('django', 'launch', 'app.py') + cwd = os.path.dirname(filename) + self.run_test_with_handled_exception( + DebugInfo( + filename=filename, + argv=['runserver', '--noreload', '--nothreading'], + cwd=cwd), 'Django', filename) + + def test_with_unhandled_exceptions(self): + filename = TEST_FILES.resolve('django', 'launch', 'app.py') + cwd = os.path.dirname(filename) + self.run_test_with_unhandled_exception( + DebugInfo( + filename=filename, + argv=['runserver', '--noreload', '--nothreading'], + cwd=cwd), 'Django', filename) + + +class DjangoAttachFileTests(WebFrameworkTests): + @unittest.skip('#545') + def test_with_route_break_points(self): + filename = TEST_FILES.resolve('django', 'attach', 'app.py') + cwd = os.path.dirname(filename) + self.run_test_with_break_points( + DebugInfo( + filename=filename, + argv=['runserver', '--noreload', '--nothreading'], + env={ + 'PTVSD_HOST': 'localhost', + 'PTVSD_PORT': str(PORT), + }, + cwd=cwd), + framework='Django', + bp_filename=filename, bp_line=48, bp_name='home', + bp_var_value='Django-Django-Test') + + @unittest.skip('#545') + def test_with_template_break_points(self): + filename = TEST_FILES.resolve('django', 'attach', 'app.py') + template = TEST_FILES.resolve( + 'django', 'launch', 'templates', 'hello.html') + cwd = os.path.dirname(filename) + self.run_test_with_break_points( + DebugInfo( + filename=filename, + argv=['runserver', '--noreload', '--nothreading'], + env={ + 'PTVSD_HOST': 'localhost', + 'PTVSD_PORT': str(PORT), + }, + cwd=cwd), + framework='Django', + bp_filename=template, bp_line=8, bp_name='Django Template', + bp_var_value='Django-Django-Test') + + @unittest.skip('#545') + def test_with_handled_exceptions(self): + filename = TEST_FILES.resolve('django', 'attach', 'app.py') + cwd = os.path.dirname(filename) + self.run_test_with_handled_exception( + DebugInfo( + filename=filename, + argv=['runserver', '--noreload', '--nothreading'], + env={ + 'PTVSD_HOST': 'localhost', + 'PTVSD_PORT': str(PORT), + }, + cwd=cwd), 'Django', filename) + + @unittest.skip('#545') + def test_with_unhandled_exceptions(self): + filename = TEST_FILES.resolve('django', 'attach', 'app.py') + cwd = os.path.dirname(filename) + self.run_test_with_unhandled_exception( + DebugInfo( + filename=filename, + argv=['runserver', '--noreload', '--nothreading'], + env={ + 'PTVSD_HOST': 'localhost', + 'PTVSD_PORT': str(PORT), + }, + cwd=cwd), 'Django', filename)