Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add e2e tests for PYTHON_THREADPOOL_THREAD_COUNT and FUNCTIONS_WORKER_PROCESS_COUNT #1136

Merged
merged 14 commits into from
Mar 15, 2023
Merged
13 changes: 13 additions & 0 deletions tests/endtoend/http_functions/http_func/__init__.py
Original file line number Diff line number Diff line change
@@ -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}")
20 changes: 20 additions & 0 deletions tests/endtoend/http_functions/http_func/function.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"scriptFile": "__init__.py",
"bindings": [
{
"authLevel": "function",
pdthummar marked this conversation as resolved.
Show resolved Hide resolved
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "$return"
}
]
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from datetime import datetime
import logging
gavin-aguiar marked this conversation as resolved.
Show resolved Hide resolved
import time

import azure.functions as func

Expand Down Expand Up @@ -32,3 +34,11 @@ def default_template(req: func.HttpRequest) -> func.HttpResponse:
" personalized response.",
status_code=200
)


@app.route(route="http_func")
pdthummar marked this conversation as resolved.
Show resolved Hide resolved
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}")
43 changes: 0 additions & 43 deletions tests/endtoend/test_multi_worker_functions.py

This file was deleted.

120 changes: 120 additions & 0 deletions tests/endtoend/test_threadpool_thread__count_functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# 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 TestPythonThreadpoolThreadCount1(testutils.WebHostTestCase):
pdthummar marked this conversation as resolved.
Show resolved Hide resolved
""" Test the Http Trigger with setting up the python threadpool thread
count to 1.this test will check if both requests should not 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'] = '1'
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_1(self):
res = [None, None]

def http_req(response_num):
r = self.webhost.request('GET', 'http_func')
self.assertTrue(r.ok)
res[response_num] = datetime.strptime(
r.content.decode("utf-8"), "%H:%M:%S")

# creating 2 different threads to send parallel HTTP request
trd1 = Thread(target=http_req, args=(0,))
trd2 = Thread(target=http_req, args=(1,))
trd1.start()
trd2.start()
trd1.join()
trd2.join()
"""function execution time difference between both HTTP request
should be greater than of equal to 1 since both the request should
not be processed at the same time because
PYTHON_THREADPOOL_THREAD_COUNT is 1.
"""
time_diff_in_seconds = abs((res[0] - res[1]).total_seconds())
self.assertTrue(time_diff_in_seconds >= 1)


class TestPythonThreadpoolThreadCount1Stein(TestPythonThreadpoolThreadCount1):

@classmethod
def get_script_dir(cls):
return testutils.E2E_TESTS_FOLDER / 'http_functions' / \
'http_functions_stein'


class TestPythonThreadpoolThreadCount2(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_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 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 TestPythonThreadpoolThreadCount2Stein(TestPythonThreadpoolThreadCount2):

@classmethod
def get_script_dir(cls):
return testutils.E2E_TESTS_FOLDER / 'http_functions' / \
'http_functions_stein'
122 changes: 122 additions & 0 deletions tests/endtoend/test_worker_proccess_count_functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# 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 TestWorkerProcessCount1(testutils.WebHostTestCase):
pdthummar marked this conversation as resolved.
Show resolved Hide resolved
"""Test the Http Trigger with setting up the python worker process count
pdthummar marked this conversation as resolved.
Show resolved Hide resolved
to 1. this test will check if both requests should not 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'] = '1'
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_1(self):
res = [None, None]

def http_req(response_num):
r = self.webhost.request('GET', 'http_func')
self.assertTrue(r.ok)
res[response_num] = datetime.strptime(
r.content.decode("utf-8"), "%H:%M:%S")

# 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()
"""function execution time difference between both HTTP request
should be greater than of equal to 1 since both the request should
not be processed at the same time because
FUNCTIONS_WORKER_PROCESS_COUNT is 1.
"""
time_diff_in_seconds = abs((res[0] - res[1]).total_seconds())
self.assertTrue(time_diff_in_seconds >= 1)


class TestWorkerProcessCount1Stein(TestWorkerProcessCount1):

@classmethod
def get_script_dir(cls):
return testutils.E2E_TESTS_FOLDER / 'http_functions' /\
'http_functions_stein'


class TestWorkerProcessCount2(testutils.WebHostTestCase):
pdthummar marked this conversation as resolved.
Show resolved Hide resolved
"""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'
pdthummar marked this conversation as resolved.
Show resolved Hide resolved
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 TestWorkerProcessCount2Stein(TestWorkerProcessCount2):

@classmethod
def get_script_dir(cls):
return testutils.E2E_TESTS_FOLDER / 'http_functions' /\
'http_functions_stein'