Merge #1650 Add test_memory

This commit is contained in:
Brendan Reilly
2020-09-10 13:24:05 +00:00
6 changed files with 223 additions and 0 deletions

View File

@@ -9,6 +9,7 @@ import os
import re
import six
import time
import yaml
from traceback import extract_stack
import koji
@@ -55,6 +56,13 @@ def read_staged_data(yaml_name):
return to_text_type(mmd.read())
def read_staged_data_as_yaml(yaml_name):
filename = staged_data_filename(
yaml_name if '.' in yaml_name else "{}.yaml".format(yaml_name))
with open(filename, "r") as f:
return yaml.safe_load(f)
def patch_config():
# add test builders for all resolvers
with_test_builders = dict()

View File

View File

@@ -0,0 +1,32 @@
import os
class TestConfiguration:
# TEST CONFIGURATION ('borrowed' from module_build_service.common.conf)
SECRET_KEY = os.urandom(16)
SQLALCHEMY_TRACK_MODIFICATIONS = True
HOST = "0.0.0.0"
PORT = 5000
LOG_LEVEL = "debug"
SQLALCHEMY_DATABASE_URI = os.environ.get("DATABASE_URI", "sqlite:///:memory:")
DEBUG = True
MESSAGING = "in_memory"
PDC_URL = "https://pdc.fedoraproject.org/rest_api/v1"
NET_TIMEOUT = 10
NET_RETRY_INTERVAL = 1
SCM_NET_TIMEOUT = 0.1
SCM_NET_RETRY_INTERVAL = 0.1
KOJI_CONFIG = "./conf/koji.conf"
KOJI_PROFILE = "staging"
KOJI_REPOSITORY_URL = "https://kojipkgs.stg.fedoraproject.org/repos"
SCMURLS = ["https://src.stg.fedoraproject.org/modules/"]
ALLOWED_GROUPS_TO_IMPORT_MODULE = {"mbs-import-module"}
GREENWAVE_URL = "https://greenwave.example.local/api/v1.0/"
GREENWAVE_DECISION_CONTEXT = "test_dec_context"
GREENWAVE_SUBJECT_TYPE = "some-module"
STREAM_SUFFIXES = {r"^el\d+\.\d+\.\d+\.z$": 0.1}
CELERY_TASK_ALWAYS_EAGER = True
NO_AUTH = True
YAML_SUBMIT_ALLOWED = True

View File

@@ -0,0 +1,70 @@
from __future__ import absolute_import, print_function
import koji
import mock
import signal
import threading
import types
from module_build_service import app
from module_build_service.common import conf
from module_build_service.builder import GenericBuilder
from tests.test_build.test_build import FakeModuleBuilder
from tests.test_build.test_build import main as run_scheduler
def patch_config_system_setter():
"""bypass supported builders check"""
def set_system(self, system):
self._system = system
conf._setifok_system = types.MethodType(set_system, conf)
def register_fake_builder():
patch_config_system_setter()
conf.system = FakeModuleBuilder.backend
GenericBuilder.register_backend_class(FakeModuleBuilder)
# Builder always instantly succeeds
def on_get_task_info_cb(cls, task_id):
return {"state": koji.TASK_STATES["CLOSED"]}
FakeModuleBuilder.on_get_task_info_cb = on_get_task_info_cb
class SimpleMock:
"""Dummy callable mock object - we want our memory footprint to be as small as possible"""
def __call__(self, *args, **kwargs):
return True
@mock.patch("module_build_service.scheduler.handlers.modules.handle_stream_collision_modules",
new_callable=SimpleMock)
@mock.patch("module_build_service.scheduler.handlers.modules.record_module_build_arches",
new_callable=SimpleMock)
@mock.patch("module_build_service.scheduler.greenwave.Greenwave.check_gating",
new_callable=SimpleMock)
def run_debug_instance(mock_1, mock_2, mock_3, host=None, port=None):
def handle_pdb(sig, frame):
import pdb
pdb.Pdb().set_trace(frame)
# kill -10 <PID> to start debugger
signal.signal(signal.SIGUSR1, handle_pdb)
register_fake_builder()
host = host or conf.host
port = port or conf.port
def run_app():
app.run(host=host, port=port, debug=False)
threading.Thread(target=run_app, daemon=True).start()
# run moksha hub and never stop
run_scheduler([], stop_condition=lambda msg: False)
if __name__ == '__main__':
run_debug_instance()

View File

@@ -0,0 +1,103 @@
import json
import os
import psutil
import pytest
import requests
import time
from subprocess import Popen
from tests import read_staged_data_as_yaml
base_dir = os.path.dirname(__file__)
yaml = read_staged_data_as_yaml("testmodule.yaml")
LOCAL_MBS_URL = "http://localhost:5000/module-build-service/1/module-builds/"
def submit_yaml_build(module_name):
"""Submit module build with custom name to get a unique NVR each time."""
yaml["data"]["name"] = module_name
data = {"modulemd": str(yaml), "module_name": "testmodule"}
r = requests.post(LOCAL_MBS_URL, data=json.dumps(data))
if r.status_code > 300:
pytest.fail(str(r.json()))
return r.json()["id"]
def wait_for_module_build(build_id, timeout=60, interval=5):
"""Wait for module build to be ready
:param int build_id: build definition (either id or Build object)
:param float timeout: timeout in seconds
:param float interval: scan interval in seconds
"""
start = time.time()
while (time.time() - start) < timeout:
state = requests.get(LOCAL_MBS_URL + str(build_id)).json()["state_name"]
if state == "ready":
return
time.sleep(interval)
pytest.skip("Wait for build timed out after {}s".format(timeout))
@pytest.fixture()
def run_debug_mbs_instance():
"""Starts a 'debug' MBS instance:
* mbs-frontend (no auth, yaml import enabled)
* moksha hub (in memory messaging)
* tests.test_build.test_build.FakeModuleBuilder as builder backend (always succeeds)
Optionally:
Set MBS_TEST_INSTANCE_PID env variable to run test against your own running instance.
If you intend to run a standalone instance, make sure you have these env vars set:
* MODULE_BUILD_SERVICE_DEVELOPER_ENV=0
* MBS_CONFIG_SECTION=TestConfiguration
* MBS_CONFIG_FILE=tests/test_memory/mbs_configuration.py
* DATABASE_URI=postgresql+psycopg2://postgres:@127.0.0.1/mbstest
...then 'python tests/test_memory/mbs_debug.py' (and 'kill -10 <PID>' to start debugger)
"""
process = None
try:
running_instance_pid = int(os.environ.get("MBS_TEST_INSTANCE_PID"))
yield running_instance_pid
except TypeError or ValueError:
mbs_config_file_path = os.path.join(base_dir, "mbs_configuration.py")
env = {
"MBS_CONFIG_SECTION": "TestConfiguration",
"MBS_CONFIG_FILE": mbs_config_file_path,
}
# Pass the preset database configuration (if present)
if os.environ.get("DATABASE_URI"):
env["DATABASE_URI"] = os.environ.get("DATABASE_URI")
mbs_exec_script = os.path.join(base_dir, "mbs_debug.py")
process = Popen(["python", mbs_exec_script], stdin=None, stdout=None, env=env)
time.sleep(5) # wait a couple of secs for MBS to start
yield process.pid
if process:
process.terminate()
@pytest.mark.parametrize("num_builds", [20])
def test_submit_build(require_platform_and_default_arch, run_debug_mbs_instance, num_builds):
pid = run_debug_mbs_instance
process = psutil.Process(pid)
def get_rss(): # resident set size in MB
return process.memory_info().rss / 1000000
consumed_memory = []
for i in range(num_builds):
build_id = submit_yaml_build("test-module-{}".format(i))
# wait for the build to finish, so that the build logger is flushed/closed
wait_for_module_build(build_id, interval=0.5, timeout=10)
consumed_memory.append(get_rss())
print("Memory [MB]: {}".format(consumed_memory))
if (consumed_memory[-1] - consumed_memory[0]) > 0.1:
pytest.fail("Memory is leaking, [MB]: {}".format(consumed_memory))

10
tox.ini
View File

@@ -23,6 +23,7 @@ deps = -r{toxinidir}/test-requirements.txt
commands =
py.test -v \
--ignore tests/integration \
--ignore tests/test_memory \
--cov module_build_service \
--cov-report html \
--cov-report term \
@@ -91,3 +92,12 @@ commands =
--html=report.html \
--self-contained-html \
{posargs:tests/integration}
[testenv:memory]
basepython = python3
deps = {[testenv]deps}
passenv =
MBS_TEST_INSTANCE_PID
DATABASE_URI
commands =
py.test -rA {posargs:tests/test_memory --show-capture=no}