Skip to content

Commit

Permalink
test: improve test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
elhimov committed Oct 25, 2024
1 parent 1fa1a18 commit 07a24dc
Show file tree
Hide file tree
Showing 19 changed files with 794 additions and 106 deletions.
7 changes: 7 additions & 0 deletions test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,13 @@ def cartridge_app_session(request, tt_cmd, tmp_path_factory):
return cartridge_app


@pytest.fixture(scope="function")
def tt_app(tt_cmd, tmpdir_with_cfg, request):
app = utils.TtApp(tt_cmd, tmpdir_with_cfg, request.module.app_path, request.module.inst_names)
yield app
app.stop(auto_confirm="-y")


@pytest.fixture
def cartridge_app(request, cartridge_app_session):
bootstrap_vshard = True
Expand Down
1 change: 1 addition & 0 deletions test/integration/clean/multi_inst_data_app
101 changes: 101 additions & 0 deletions test/integration/clean/test_clean.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import itertools
import os
import re

import pytest

from utils import (TtApp, initial_snap, initial_xlog, log_file,
tt_app_remove_app_script, tt_app_start,
tt_app_wait_data_files)

################################################################
# multi-instance app

app_path = os.path.join(os.path.dirname(__file__), "multi_inst_data_app")
app_name = "multi_inst_data_app"
inst_names = ["router", "master", "replica", "stateboard"]


def check_multi_inst_clean(tt_app, post_start, target, **kwargs):
# Start the application.
tt_app_start(tt_app, [TtApp.app_target], post_start)
tt_app_wait_data_files(tt_app, TtApp.app_target)

# Do clean.
rc, out = tt_app.clean(target, **kwargs)
assert rc == 0
status = tt_app.status()

# Check that clean warns about application is running.
for inst_name in tt_app.inst_names:
inst_id = tt_app.inst_id(inst_name)
assert status[inst_id]["STATUS"] == "RUNNING"
msg = f"instance `{inst_name}` must be stopped"
if tt_app.inst_match_target(inst_name, target):
assert msg in out
else:
assert msg not in out

# Stop the application.
rc, out = tt_app.stop(TtApp.app_target, auto_confirm='-y')
assert rc == 0

# Do clean.
rc, out = tt_app.clean(target, **kwargs)
assert rc == 0
print(f"out:{out}")
status = tt_app.status()

# Check that clean is working.
input = kwargs.get('input')
auto_confirm = kwargs.get('auto_confirm')
is_confirms = itertools.repeat(True) if auto_confirm is not None else input.is_confirm
for inst_name, is_confirm in zip(tt_app.inst_names, is_confirms):
inst_id = tt_app.inst_id(inst_name)
if tt_app.inst_match_target(inst_name, target):
if is_confirm:
# https://github.com/tarantool/tt/issues/735
msg = r"{}...\t\[OK\]".format(inst_id)
assert len(re.findall(msg, out)) == 1
not os.path.exists(tt_app.log_path(inst_name, log_file))
not os.path.exists(tt_app.lib_path(inst_name, initial_snap))
not os.path.exists(tt_app.lib_path(inst_name, initial_xlog))
else:
msg = r"{}: cleaning...\t\[ERR\] cancelled by user".format(inst_name)
assert re.search(msg, out)
else:
assert inst_id not in out


# Test with auto-confirmation (short option).
@pytest.mark.parametrize("post_start", [
None,
# post_start_make_identical_subdir,
tt_app_remove_app_script,
])
@pytest.mark.parametrize("target", [
pytest.param(None, id="NO_TARGET"),
pytest.param(TtApp.app_target, id="APP_TARGET"),
pytest.param(TtApp.Target("master"), id="master"),
pytest.param(TtApp.Target("router"), id="router"),
])
def test_multi_inst_clean_auto_confirm_shortopt(tt_app, post_start, target):
check_multi_inst_clean(tt_app, post_start, target, auto_confirm="-f")


# Test with auto-confirmation (long option; less variations, just a few common cases).
def test_multi_inst_clean_auto_confirm_longopt(tt_app):
check_multi_inst_clean(tt_app, None, TtApp.app_target, auto_confirm="--force")


# Test with the various inputs.
@pytest.mark.skipif(True, reason="Parsing of multiple answers from stdin is not supported.")
@pytest.mark.parametrize("input", [
TtApp.Input("y\ny\nY\nY\n", [True, True, True, True]), # confirm all
TtApp.Input("n\nn\nN\nN\n", [False, False, False, False]), # discard all
TtApp.Input("y\nn\nN\nY\n", [True, False, False, True]), # mix #1
TtApp.Input("n\ny\nY\nN\n", [False, True, True, False]), # mix #2
TtApp.Input("ny\ny\nyn\nn\nY\nN\n", [True, False, True, False]), # mix #3
])
def test_multi_inst_clean_input(tt_app, input):
check_multi_inst_clean(tt_app, tt_app_wait_data_files, TtApp.app_target, input=input)
1 change: 1 addition & 0 deletions test/integration/kill/multi_inst_app
98 changes: 98 additions & 0 deletions test/integration/kill/test_kill.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import os
import re

import pytest

from utils import TtApp, pid_file, tt_app_remove_app_script, tt_app_start

################################################################
# multi-instance app

app_path = os.path.join(os.path.dirname(__file__), "multi_inst_app")
app_name = "multi_inst_app"
inst_names = ["router", "master", "replica", "stateboard"]


def check_multi_inst_kill(tt_app, running_targets, post_start, target, **kwargs):
# Prepare the application considering the test parameters.
orig_status = tt_app_start(tt_app, running_targets, post_start)

# Do kill.
rc, out = tt_app.kill(target, **kwargs)
assert rc == 0
status = tt_app.status()
print(f"status: {status}")

# Check the discarding.
if not tt_app.is_confirm(**kwargs):
for inst_name in tt_app.inst_names:
inst_id = tt_app.inst_id(inst_name)
assert status[inst_id]["STATUS"] == orig_status[inst_id]["STATUS"]
if tt_app.inst_match_target(inst_name, target) and \
tt_app.inst_match_targets(inst_name, running_targets):
assert status[inst_id]["PID"] == orig_status[inst_id]["PID"]
return

# Check the confirmation.
for inst_name in tt_app.inst_names:
inst_id = tt_app.inst_id(inst_name)
if tt_app.inst_match_target(inst_name, target):
assert status[inst_id]["STATUS"] == "NOT RUNNING"
if tt_app.inst_match_targets(inst_name, running_targets):
orig_pid = orig_status[inst_id]["PID"]
assert f"The instance {inst_id} (PID = {orig_pid}) has been killed." in out
else:
pid_path = tt_app.run_path(inst_name, pid_file)
msg = r"failed to kill the processes:.*{}".format(pid_path)
assert re.search(msg, out)
else:
assert status[inst_id]["STATUS"] == orig_status[inst_id]["STATUS"]
assert inst_id not in out


# Test with auto-confirmation (short option).
@pytest.mark.parametrize("running_targets", [
pytest.param([], id="running:none"),
pytest.param([TtApp.app_target], id="running:all"),
pytest.param([TtApp.Target("master")], id="running:master"),
pytest.param([TtApp.Target("master"), TtApp.Target("router")], id="running:master,router"),
])
@pytest.mark.parametrize("post_start", [
None,
# post_start_make_identical_subdir,
tt_app_remove_app_script,
])
@pytest.mark.parametrize("target", [
pytest.param(None, id="NO_TARGET"),
pytest.param(TtApp.app_target, id="APP_TARGET"),
pytest.param(TtApp.Target("master"), id="master"),
pytest.param(TtApp.Target("router"), id="router"),
])
def test_multi_inst_kill_auto_confirm_shortopt(tt_app, running_targets, target, post_start):
check_multi_inst_kill(tt_app, running_targets, post_start, target, auto_confirm="-f")


# Test with auto-confirmation (long option; less variations, just a few common cases).
@pytest.mark.parametrize("running_targets", [
pytest.param([], id="running:none"),
pytest.param([TtApp.app_target], id="running:all"),
])
def test_multi_inst_kill_auto_confirm_longopt(tt_app, running_targets):
check_multi_inst_kill(tt_app, running_targets, None, TtApp.app_target, auto_confirm="--force")


# Test with the various inputs.
@pytest.mark.parametrize("running_targets", [
pytest.param([], id="running:none"),
pytest.param([TtApp.app_target], id="running:all"),
])
@pytest.mark.parametrize("input", [
TtApp.Input("y\n", True), # confirm (lowercase)
TtApp.Input("Y\n", True), # confirm (uppercase)
TtApp.Input("nn\nny\ny\n", True), # confirm preceded with the wrong answers
TtApp.Input("n\n", False), # discard (lowercase)
TtApp.Input("N\n", False), # discard (uppercase)
TtApp.Input("yy\nyn\nn\n", False), # discard preceded with the wrong answers
])
def test_multi_inst_kill_input(tt_app, running_targets, input):
check_multi_inst_kill(tt_app, running_targets, None, TtApp.app_target, input=input)
1 change: 1 addition & 0 deletions test/integration/logrotate/multi_inst_app
77 changes: 77 additions & 0 deletions test/integration/logrotate/test_logrotate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import os

import pytest

from utils import (TtApp, log_file, tt_app_remove_app_script, tt_app_start,
tt_app_wait_log_files)

################################################################
# multi-instance app

app_path = os.path.join(os.path.dirname(__file__), "multi_inst_app")
app_name = "multi_inst_app"
inst_names = ["router", "master", "replica", "stateboard"]


def check_multi_inst_logrotate(tt_app, running_targets, post_start, target):
# Prepare the application considering the test parameters.
orig_status = tt_app_start(tt_app, running_targets, post_start)

# Rename log files.
for inst_name in tt_app.inst_names:
inst_id = tt_app.inst_id(inst_name)
if tt_app.inst_match_target(inst_name, target):
if orig_status[inst_id]["STATUS"] == "RUNNING":
log_path = tt_app.log_path(inst_name, log_file)
os.rename(log_path, log_path + '0')

# Do logrotate.
rc, out = tt_app.logrotate(target)

# If any of the requested instances is not running return failure.
for inst_name in tt_app.inst_names:
if tt_app.inst_match_target(inst_name, target) and \
not tt_app.inst_match_targets(inst_name, running_targets):
assert rc != 0
assert "NOT RUNNING" in out
return

# Wait for the files to be re-created.
assert rc == 0
status = tt_app.status()
log_paths = []
for inst_name in tt_app.inst_names:
inst_id = tt_app.inst_id(inst_name)
log_path = tt_app.log_path(inst_name, log_file)
if tt_app.inst_match_target(inst_name, target):
assert status[inst_id]["STATUS"] == "RUNNING"
assert status[inst_id]["PID"] == orig_status[inst_id]["PID"]
pid = status[inst_id]["PID"]
assert f"{inst_id}: logs has been rotated. PID: {pid}" in out
log_paths.append(log_path)
tt_app_wait_log_files(tt_app, target)
for log_path in log_paths:
with open(log_path) as f:
assert "reopened" in f.read()


# Test various scenarios.
@pytest.mark.parametrize("running_targets", [
pytest.param([], id="running:none"),
pytest.param([TtApp.app_target], id="running:all"),
pytest.param([TtApp.Target("master")], id="running:master"),
pytest.param([TtApp.Target("master"), TtApp.Target("router")], id="running:master,router"),
])
@pytest.mark.parametrize("post_start", [
None,
# post_start_make_identical_subdir,
tt_app_remove_app_script,
])
@pytest.mark.parametrize("target", [
pytest.param(None, id="NO_TARGET"),
pytest.param(TtApp.app_target, id="APP_TARGET"),
pytest.param(TtApp.Target("master"), id="master"),
pytest.param(TtApp.Target("router"), id="router"),
])
def test_multi_inst_logrotate(tt_app, running_targets, post_start, target):
check_multi_inst_logrotate(tt_app, running_targets, post_start, target)
1 change: 1 addition & 0 deletions test/integration/restart/multi_inst_app
Loading

0 comments on commit 07a24dc

Please sign in to comment.