diff --git a/tests/endtoend/http_functions/http_func/__init__.py b/tests/endtoend/http_functions/http_func/__init__.py new file mode 100644 index 000000000..6e8cb7118 --- /dev/null +++ b/tests/endtoend/http_functions/http_func/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +from datetime import datetime +# flake8: noqa +import azure.functions as func +import time + + +def main(req: func.HttpRequest) -> func.HttpResponse: + time.sleep(1) + + current_time = datetime.now().strftime("%H:%M:%S") + return func.HttpResponse(f"{current_time}") diff --git a/tests/endtoend/http_functions/http_func/function.json b/tests/endtoend/http_functions/http_func/function.json new file mode 100644 index 000000000..8c4cbe307 --- /dev/null +++ b/tests/endtoend/http_functions/http_func/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "req", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/tests/endtoend/http_functions/http_functions_stein/function_app.py b/tests/endtoend/http_functions/http_functions_stein/function_app.py index 873fce914..01fd52a09 100644 --- a/tests/endtoend/http_functions/http_functions_stein/function_app.py +++ b/tests/endtoend/http_functions/http_functions_stein/function_app.py @@ -1,7 +1,9 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. +from datetime import datetime import logging +import time import azure.functions as func @@ -32,3 +34,11 @@ def default_template(req: func.HttpRequest) -> func.HttpResponse: " personalized response.", status_code=200 ) + + +@app.route(route="http_func") +def http_func(req: func.HttpRequest) -> func.HttpResponse: + time.sleep(1) + + current_time = datetime.now().strftime("%H:%M:%S") + return func.HttpResponse(f"{current_time}") diff --git a/tests/endtoend/test_multi_worker_functions.py b/tests/endtoend/test_multi_worker_functions.py deleted file mode 100644 index 828030c88..000000000 --- a/tests/endtoend/test_multi_worker_functions.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. -import os -from threading import Thread -from unittest.mock import patch - -from tests.utils import testutils - - -class TestWorkerProcessCountStein(testutils.WebHostTestCase): - """Test the Http Trigger with setting up the python worker process count to 2. - This tests will check if worker1 indexes the function in metadata request - and worker2 indexes the function in the load request since worker2 does not - call metadata request. - """ - @classmethod - def setUpClass(cls): - os_environ = os.environ.copy() - os_environ['FUNCTIONS_WORKER_PROCESS_COUNT'] = '2' - cls._patch_environ = patch.dict('os.environ', os_environ) - cls._patch_environ.start() - super().setUpClass() - - @classmethod - def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'http_functions' / \ - 'http_functions_stein' - - def test_http_func_with_worker_process_count(self): - """Test if the default template of Http trigger in Python Function app - will return OK - """ - def http_req(): - r = self.webhost.request('GET', 'default_template') - self.assertTrue(r.ok) - - # creating 2 different threads to send HTTP request - trd1 = Thread(target=http_req, args=(0,)) - trd2 = Thread(target=http_req, args=(1,)) - trd1.start() - trd2.start() - trd1.join() - trd2.join() diff --git a/tests/endtoend/test_threadpool_thread__count_functions.py b/tests/endtoend/test_threadpool_thread__count_functions.py new file mode 100644 index 000000000..42c33eaff --- /dev/null +++ b/tests/endtoend/test_threadpool_thread__count_functions.py @@ -0,0 +1,63 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +import os +from threading import Thread +from unittest.mock import patch +from datetime import datetime +from tests.utils import testutils + + +class TestPythonThreadpoolThreadCount(testutils.WebHostTestCase): + """ Test the Http Trigger with setting up the python threadpool thread + count to 2. this test will check if both requests should be processed + at the same time. this file is more focus on testing the E2E flow + scenarios. + """ + + @classmethod + def setUpClass(cls): + os_environ = os.environ.copy() + os_environ['PYTHON_THREADPOOL_THREAD_COUNT'] = '2' + cls._patch_environ = patch.dict('os.environ', os_environ) + cls._patch_environ.start() + super().setUpClass() + + def tearDown(self): + super().tearDown() + self._patch_environ.stop() + + @classmethod + def get_script_dir(cls): + return testutils.E2E_TESTS_FOLDER / 'http_functions' + + @testutils.retryable_test(3, 5) + def test_http_func_with_thread_count(self): + response = [None, None] + + def http_req(res_num): + r = self.webhost.request('GET', 'http_func') + self.assertTrue(r.ok) + response[res_num] = datetime.strptime( + r.content.decode("utf-8"), "%H:%M:%S") + + # creating 2 different threads to send HTTP request + thread1 = Thread(target=http_req, args=(0,)) + thread2 = Thread(target=http_req, args=(1,)) + thread1.start() + thread2.start() + thread1.join() + thread2.join() + """function execution time difference between both HTTP request + should be less than 1 since both the request should be processed at + the same time because PYTHON_THREADPOOL_THREAD_COUNT is 2. + """ + time_diff_in_seconds = abs((response[0] - response[1]).total_seconds()) + self.assertTrue(time_diff_in_seconds < 1) + + +class TestPythonThreadpoolThreadCountStein(TestPythonThreadpoolThreadCount): + + @classmethod + def get_script_dir(cls): + return testutils.E2E_TESTS_FOLDER / 'http_functions' / \ + 'http_functions_stein' diff --git a/tests/endtoend/test_worker_proccess_count_functions.py b/tests/endtoend/test_worker_proccess_count_functions.py new file mode 100644 index 000000000..35e72fb48 --- /dev/null +++ b/tests/endtoend/test_worker_proccess_count_functions.py @@ -0,0 +1,64 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +import os +from threading import Thread +from unittest.mock import patch +from datetime import datetime +from tests.utils import testutils + + +class TestWorkerProcessCount(testutils.WebHostTestCase): + """Test the Http Trigger with setting up the python worker process count + to 2. this test will check if both requests should be processed at the + same time. this file is more focused on testing the E2E flow scenario for + FUNCTIONS_WORKER_PROCESS_COUNT feature. + """ + + @classmethod + def setUpClass(cls): + os_environ = os.environ.copy() + os_environ['PYTHON_THREADPOOL_THREAD_COUNT'] = '1' + os_environ['FUNCTIONS_WORKER_PROCESS_COUNT'] = '2' + cls._patch_environ = patch.dict('os.environ', os_environ) + cls._patch_environ.start() + super().setUpClass() + + def tearDown(self): + super().tearDown() + self._patch_environ.stop() + + @classmethod + def get_script_dir(cls): + return testutils.E2E_TESTS_FOLDER / 'http_functions' + + @testutils.retryable_test(3, 5) + def test_http_func_with_worker_process_count_2(self): + response = [None, None] + + def http_req(res_num): + r = self.webhost.request('GET', 'http_func') + self.assertTrue(r.ok) + response[res_num] = datetime.strptime( + r.content.decode("utf-8"), "%H:%M:%S") + + # creating 2 different threads to send HTTP request + thread1 = Thread(target=http_req, args=(0,)) + thread2 = Thread(target=http_req, args=(1,)) + thread1.start() + thread2.start() + thread1.join() + thread2.join() + '''function execution time difference between both HTTP request + should be less than 1 since both request should be processed at the + same time because FUNCTIONS_WORKER_PROCESS_COUNT is 2. + ''' + time_diff_in_seconds = abs((response[0] - response[1]).total_seconds()) + self.assertTrue(time_diff_in_seconds < 1) + + +class TestWorkerProcessCountStein(TestWorkerProcessCount): + + @classmethod + def get_script_dir(cls): + return testutils.E2E_TESTS_FOLDER / 'http_functions' /\ + 'http_functions_stein'