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

--no-reload option to run tests #471

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions framework/deproxy_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,25 +195,44 @@ def receive_request(self, request):


class StaticDeproxyServer(BaseDeproxyServer):
__response: bytes

def __init__(self, *args, **kwargs):
self.response = kwargs["response"]
self.set_response(kwargs.get("response", b""))
self.__default_response = self.__response
kwargs.pop("response", None)
BaseDeproxyServer.__init__(self, *args, **kwargs)
self.last_request = None
self.requests = []

def run_start(self):
self.requests = []
self.clear_stats()
BaseDeproxyServer.run_start(self)

def set_response(self, response):
self.response = response
@property
def response(self) -> str:
return self.__response.decode(errors="ignore")

@response.setter
def response(self, response: str or bytes) -> None:
self.set_response(response)

def set_response(self, response: str or bytes) -> None:
if isinstance(response, str):
self.__response = response.encode()
elif isinstance(response, bytes):
self.__response = response

def receive_request(self, request):
self.requests.append(request)
self.last_request = request
return self.response, False

def clear_stats(self):
self.set_response(self.__default_response)
self.last_request = None
self.requests = []


def deproxy_srv_factory(server, name, tester):
port = server["port"]
Expand Down
140 changes: 102 additions & 38 deletions framework/tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
import framework.deproxy_manager as deproxy_manager
import framework.external_client as external_client
import framework.wrk_client as wrk_client
import run_config
from framework.templates import fill_template, populate_properties
from helpers import control, dmesg, remote, sysnet, tf_cfg
from helpers import control, dmesg, remote, stateful, sysnet, tf_cfg

__author__ = "Tempesta Technologies, Inc."
__copyright__ = "Copyright (C) 2018-2023 Tempesta Technologies, Inc."
Expand Down Expand Up @@ -56,6 +57,8 @@ class TempestaTest(unittest.TestCase):
3) several test functions.
function name should start with 'test'

no_reload - Tempesta and backends run once for test class.

Verbose documentation is placed in README.md
"""

Expand All @@ -69,18 +72,25 @@ class TempestaTest(unittest.TestCase):
"backends": [],
}

def __init_subclass__(cls, base=False, **kwargs):
def __init_subclass__(cls, base=False, no_reload=False, **kwargs):
super().__init_subclass__(**kwargs)
cls._base = base
cls._no_reload = no_reload
cls.deproxy_manager = deproxy_manager.DeproxyManager()

if cls._no_reload or run_config.NO_RELOAD:
cls.__servers = {}
cls.__tempesta = None

def __init__(self, *args, **kwargs):
unittest.TestCase.__init__(self, *args, **kwargs)
self.__servers = {}
self.__clients = {}
self.__tcpdump: subprocess.Popen = None
self.__ips = []
self.__tempesta = None
self.deproxy_manager = deproxy_manager.DeproxyManager()

if not self._no_reload or not run_config.NO_RELOAD:
self.__servers = {}
self.__tempesta = None

def __create_client_deproxy(self, client, ssl, bind_addr):
addr = fill_template(client["addr"], client)
Expand Down Expand Up @@ -154,7 +164,8 @@ def __create_client(self, client):
elif client["type"] == "external":
self.__clients[cid] = self.__create_client_external(client)

def __create_backend(self, server):
@staticmethod
def __create_backend(tester, server):
srv = None
checks = []
sid = server["id"]
Expand All @@ -172,14 +183,15 @@ def __create_backend(self, server):
tf_cfg.dbg(1, "Unsupported backend %s" % stype)
tf_cfg.dbg(1, "Supported backends: %s" % backend_defs)
raise e
srv = factory(server, sid, self)
srv = factory(server, sid, tester)
srv.port_checks = checks
self.__servers[sid] = srv
tester.__servers[sid] = srv

def __create_servers(self):
for server in self.backends:
@staticmethod
def __create_servers(tester):
for server in tester.backends:
# Copy description to keep it clean between several tests.
self.__create_backend(server.copy())
tester.__create_backend(tester, server.copy())

def get_server(self, sid):
"""Return client with specified id"""
Expand Down Expand Up @@ -218,36 +230,47 @@ def get_tempesta(self):
"""Return Tempesta instance"""
return self.__tempesta

def __create_tempesta(self):
desc = self.tempesta.copy()
@staticmethod
def __create_tempesta(tester):
desc = tester.tempesta.copy()
populate_properties(desc)
custom_cert = False
if "custom_cert" in desc:
custom_cert = self.tempesta["custom_cert"]
custom_cert = tester.tempesta["custom_cert"]
config = ""
if "config" in desc:
config = desc["config"]
if "type" in desc:
factory = tempesta_defs[desc["type"]]
self.__tempesta = factory(desc)
tester.__tempesta = factory(desc)
else:
self.__tempesta = default_tempesta_factory(desc)
self.__tempesta.config.set_defconfig(fill_template(config, desc), custom_cert)
tester.__tempesta = default_tempesta_factory(desc)
tester.__tempesta.config.set_defconfig(fill_template(config, desc), custom_cert)

def start_all_servers(self):
for sid in self.__servers:
srv = self.__servers[sid]
self.__start_all_servers(self)

@staticmethod
def __start_all_servers(tester):
for sid in tester.__servers:
srv = tester.__servers[sid]
srv.start()
if not srv.is_running():
raise Exception("Can not start server %s" % sid)

def start_tempesta(self):
self.__start_tempesta(self)

@staticmethod
def __start_tempesta(tester):
"""Start Tempesta and wait until the initialization process finish."""
if tester.__tempesta.state == stateful.STATE_STARTED:
return None
# "modules are started" string is only logged in debug builds while
# "Tempesta FW is ready" is logged at all levels.
with dmesg.wait_for_msg("[tempesta fw] Tempesta FW is ready", 1, True):
self.__tempesta.start()
if not self.__tempesta.is_running():
tester.__tempesta.start()
if not tester.__tempesta.is_running():
raise Exception("Can not start Tempesta")

def start_all_clients(self):
Expand All @@ -257,38 +280,55 @@ def start_all_clients(self):
if not client.is_running():
raise Exception("Can not start client %s" % cid)

@classmethod
def setUpClass(cls) -> None:
tf_cfg.dbg(3, "\tsetUpClass - start.")
if not remote.wait_available():
raise Exception("Tempesta node is unavaliable")

if run_config.NO_RELOAD and cls._no_reload:
cls.__create_servers(cls)
cls.__create_tempesta(cls)
cls.__start_all_servers(cls)
cls.__start_tempesta(cls)
cls.deproxy_manager.start()
assert cls.__wait_all_connections(cls), "Tempesta did not connect to servers."

tf_cfg.dbg(3, "\tsetUpClass - complete.")

def setUp(self):
# `unittest.TestLoader.discover` returns initialized objects, we can't
# raise `SkipTest` inside of `TempestaTest.__init__` because we are unable
# to interfere `unittest` code and catch that exception inside of it.
# Please, make sure to put the following check in your code if you override `setUp`.
tf_cfg.dbg(3, "\tsetUp - start.")
if self._base:
self.skipTest("This is an abstract class")

tf_cfg.dbg(3, "\tInit test case...")
if not remote.wait_available():
raise Exception("Tempesta node is unavaliable")
self.oops = dmesg.DmesgFinder()
self.oops_ignore = []
self.__create_servers()
self.__create_tempesta()
self.__create_clients()

if not self._no_reload or not run_config.NO_RELOAD:
self.__create_servers(self)
self.__create_tempesta(self)

self.__run_tcpdump()

tf_cfg.dbg(3, "\tsetUp - complete.")

def tearDown(self):
tf_cfg.dbg(3, "\tTeardown")
tf_cfg.dbg(3, "\ttearDown - start.")
if not run_config.NO_RELOAD or not self._no_reload:
self.__stop_tempesta_servers_deproxy_manager(self)
else:
for sid in self.__servers:
server = self.__servers[sid]
server.clear_stats()

for cid in self.__clients:
client = self.__clients[cid]
client.stop()
self.__tempesta.stop()
for sid in self.__servers:
server = self.__servers[sid]
server.stop()
self.deproxy_manager.stop()
try:
deproxy_manager.finish_all_deproxy()
except:
print("Unknown exception in stopping deproxy")

tf_cfg.dbg(3, "Removing interfaces")
interface = tf_cfg.cfg.get("Server", "aliases_interface")
Expand Down Expand Up @@ -319,6 +359,26 @@ def tearDown(self):
self.oops_ignore = []
self.__stop_tcpdump()

tf_cfg.dbg(3, "\ttearDown - complete.")

@classmethod
def tearDownClass(cls) -> None:
tf_cfg.dbg(3, "\ttearDownClass - start.")

if run_config.NO_RELOAD and cls._no_reload:
cls.__stop_tempesta_servers_deproxy_manager(cls)

tf_cfg.dbg(3, "\ttearDownClass - complete.")

@staticmethod
def __stop_tempesta_servers_deproxy_manager(tester):
tester.__tempesta.stop()
for sid in tester.__servers:
server = tester.__servers[sid]
server.stop()
tester.deproxy_manager.stop()
deproxy_manager.finish_all_deproxy()

def wait_while_busy(self, *items):
if items is None:
return
Expand All @@ -331,8 +391,12 @@ def wait_while_busy(self, *items):

# Should replace all duplicated instances of wait_all_connections
def wait_all_connections(self, tmt=1):
for sid in self.__servers:
srv = self.__servers[sid]
return self.__wait_all_connections(self, tmt)

@staticmethod
def __wait_all_connections(tester, tmt=1):
for sid in tester.__servers:
srv = tester.__servers[sid]
if not srv.wait_for_connections(timeout=tmt):
return False
return True
Expand Down
26 changes: 11 additions & 15 deletions http2_general/test_h2_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@

from h2.errors import ErrorCodes
from h2.exceptions import StreamClosedError
from hpack import HeaderTuple

from framework import deproxy_client, tester
from helpers import checks_for_tests as checks
from http2_general.helpers import H2Base
from framework import deproxy_client
from helpers.networker import NetWorker
from hpack import HeaderTuple
from http2_general.helpers import H2Base


class TestH2Frame(H2Base):
class TestH2Frame(H2Base, no_reload=True):
def test_data_framing(self):
"""Send many 1 byte frames in request."""
self.start_all_services()
Expand Down Expand Up @@ -297,13 +296,6 @@ def __assert_test(self, client, request_body: str, request_number: int):
self.assertTrue(client.wait_for_response(timeout=5))
self.assertEqual(client.last_response.status, "200")
self.assertEqual(len(server.requests), request_number)
checks.check_tempesta_request_and_response_stats(
tempesta=self.get_tempesta(),
cl_msg_received=request_number,
cl_msg_forwarded=request_number,
srv_msg_received=request_number,
srv_msg_forwarded=request_number,
)
error_msg = "Malformed request from Tempesta."
self.assertEqual(server.last_request.method, self.post_request[3][1], error_msg)
self.assertEqual(server.last_request.headers["host"], self.post_request[0][1], error_msg)
Expand All @@ -327,7 +319,9 @@ def setup_tests(self):
DEFAULT_MTU = 1500


class TestH2FrameEnabledDisabledTsoGroGso(TestH2FrameEnabledDisabledTsoGroGsoBase, NetWorker):
class TestH2FrameEnabledDisabledTsoGroGso(
TestH2FrameEnabledDisabledTsoGroGsoBase, NetWorker, no_reload=True
):
def test_headers_frame_with_continuation(self):
client, server = self.setup_tests()
self.run_test_tso_gro_gso_disabled(
Expand Down Expand Up @@ -405,7 +399,7 @@ def _test_headers_data_frames(self, client, server, header_len, body_len):


class TestH2FrameEnabledDisabledTsoGroGsoStickyCookie(
TestH2FrameEnabledDisabledTsoGroGsoBase, NetWorker
TestH2FrameEnabledDisabledTsoGroGsoBase, NetWorker, no_reload=True
):
tempesta = {
"config": """
Expand Down Expand Up @@ -476,7 +470,9 @@ def _test_headers_frame_for_local_resp_sticky_cookie(
self.post_request.pop()


class TestH2FrameEnabledDisabledTsoGroGsoCache(TestH2FrameEnabledDisabledTsoGroGsoBase, NetWorker):
class TestH2FrameEnabledDisabledTsoGroGsoCache(
TestH2FrameEnabledDisabledTsoGroGsoBase, NetWorker, no_reload=True
):
tempesta = {
"config": """
listen 443 proto=h2;
Expand Down
Loading