From 8fefa4a3dd8905ef46c76a7437d58a8420a5edd4 Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Mon, 13 Nov 2017 11:44:46 -0500 Subject: [PATCH] Introduce save load test Signed-off-by: Jonathan Rajotte --- lttng_ivc/settings.py | 4 + lttng_ivc/tests/tools_save-load/__init__.py | 0 .../tests/tools_save-load/test_save_load.py | 287 ++++++++++++++++++ lttng_ivc/utils/utils.py | 29 +- 4 files changed, 316 insertions(+), 4 deletions(-) create mode 100644 lttng_ivc/tests/tools_save-load/__init__.py create mode 100644 lttng_ivc/tests/tools_save-load/test_save_load.py diff --git a/lttng_ivc/settings.py b/lttng_ivc/settings.py index 55d0a1c..8a07dd2 100644 --- a/lttng_ivc/settings.py +++ b/lttng_ivc/settings.py @@ -25,4 +25,8 @@ tmp_object_prefix = "lttng-ivc-" default_babeltrace = "babeltrace-1.5" + lttng_test_procfile = "/proc/lttng-test-filter-event" + +save_ext = ".lttng" + diff --git a/lttng_ivc/tests/tools_save-load/__init__.py b/lttng_ivc/tests/tools_save-load/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lttng_ivc/tests/tools_save-load/test_save_load.py b/lttng_ivc/tests/tools_save-load/test_save_load.py new file mode 100644 index 0000000..a513934 --- /dev/null +++ b/lttng_ivc/tests/tools_save-load/test_save_load.py @@ -0,0 +1,287 @@ +import pytest +import os +import shutil +import subprocess + +import lttng_ivc.utils.ProjectFactory as ProjectFactory +import lttng_ivc.utils.utils as utils +import lttng_ivc.utils.runtime as Run +import lttng_ivc.settings as Settings + +from lttng_ivc.utils.utils import xpath_query + +""" +""" + + +test_matrix_app_contexts = [ + ("lttng-tools-2.8", "lttng-tools-2.7", False), + ("lttng-tools-2.9", "lttng-tools-2.8", True), + ("lttng-tools-2.8", "lttng-tools-2.9", True), + pytest.param("lttng-tools-2.8", "lttng-tools-2.10", True, marks=pytest.mark.xfail(reason="See https://bugs.lttng.org/issues/1136")), +] + +test_matrix_blocking_timeout = [ + ("lttng-tools-2.10", "lttng-tools-2.7", False), + ("lttng-tools-2.10", "lttng-tools-2.8", False), + ("lttng-tools-2.10", "lttng-tools-2.9", False), + ("lttng-tools-2.10", "lttng-tools-2.10", True), +] + +test_matrix_monitor_timer_interval = [ + ("lttng-tools-2.10", "lttng-tools-2.7", False), + ("lttng-tools-2.10", "lttng-tools-2.8", False), + ("lttng-tools-2.10", "lttng-tools-2.9", False), + ("lttng-tools-2.10", "lttng-tools-2.10", True), +] + +runtime_matrix_app_contexts = [] +runtime_matrix_blocking_timeout = [] +runtime_matrix_monitor_timer_interval = [] + + +if not Settings.test_only: + runtime_matrix_app_contexts = test_matrix_app_contexts + runtime_matrix_blocking_timeout = test_matrix_blocking_timeout + runtime_matrix_monitor_timer_interval = test_matrix_monitor_timer_interval +else: + for tup in test_matrix_app_contexts: + if(tup[0] in Settings.test_only or tup[1] in Settings.test_only): + runtime_matrix_app_contexts.append(tup) + for tup in test_matrix_blocking_timeout: + if(tup[0] in Settings.test_only or tup[1] in Settings.test_only): + runtime_matrix_blocking_timeout.append(tup) + for tup in test_matrix_monitor_timer_interval: + if(tup[0] in Settings.test_only or tup[1] in Settings.test_only): + runtime_matrix_monitor_timer_interval.append(tup) + + +def validate_app_context(session_name, save_file): + xpath_provider_name = '/sessions/session[name="{}"]/domains/domain[type="JUL" or type="LOG4J"]/channels/channel/contexts/context/app/provider_name'.format(session_name) + xpath_ctx_name = '/sessions/session[name="{}"]/domains/domain[type="JUL" or type="LOG4J"]/channels/channel/contexts/context/app/ctx_name'.format(session_name) + xpath_node_expected = 2 + + # Check that the file is present + assert(os.path.isfile(save_file)) + # Validate provider name + node_list = xpath_query(save_file, xpath_provider_name) + assert(len(node_list) == xpath_node_expected) + for node in node_list: + assert(node.text == "myRetriever") + + # Validate ctx_name + node_list = xpath_query(save_file, xpath_ctx_name) + assert(len(node_list) == xpath_node_expected) + for node in node_list: + assert(node.text == "intCtx") + + +@pytest.mark.parametrize("tools_save_l,tools_load_l,should_load", runtime_matrix_app_contexts) +def test_save_load_app_contexts(tmpdir, tools_save_l, tools_load_l, should_load): + + # Prepare environment + t_save = ProjectFactory.get_precook(tools_save_l) + t_load = ProjectFactory.get_precook(tools_load_l) + + t_save_runtime_path = os.path.join(str(tmpdir), "tools-save") + t_load_runtime_path = os.path.join(str(tmpdir), "tools-load") + save_load_path = os.path.join(str(tmpdir), 'save_load') + validation_path = os.path.join(str(tmpdir), 'validation') + + trace_name = "saved_trace" + trace_filename = trace_name + Settings.save_ext + trace_file_path = os.path.join(save_load_path, trace_filename) + + validation_file_path = os.path.join(validation_path, trace_filename) + + # Craft the save + with Run.get_runtime(t_save_runtime_path) as runtime: + runtime.add_project(t_save) + + # Start lttng-sessiond + sessiond = utils.sessiond_spawn(runtime) + + runtime.run("lttng create {}".format(trace_name)) + runtime.run("lttng enable-event --jul hello") + runtime.run("lttng enable-event --log4j hello") + # NOTE: For now there is a bug/quirk for which app context is applied + # everywhere. Might need to be revisited. This influence the number of + # node we expect in the saved file. + runtime.run("lttng add-context --jul --type='$app.myRetriever:intCtx'") + runtime.run("lttng save --output-path={}".format(save_load_path)) + + cp = runtime.subprocess_terminate(sessiond) + if cp.returncode != 0: + pytest.fail("Sessiond on save return code") + + validate_app_context(trace_name, trace_file_path) + + # Load the save + with Run.get_runtime(t_load_runtime_path) as runtime: + runtime.add_project(t_load) + + # Start lttng-sessiond + sessiond = utils.sessiond_spawn(runtime) + + cmd = "lttng load --input-path={} {}".format(save_load_path, trace_name) + + if not should_load: + cp, out, err = runtime.run(cmd, check_return=False) + assert(cp.returncode != 0) + assert(utils.file_contains(err, ['Session configuration file validation failed'])) + return + + runtime.run(cmd) + + # Since lttng list does not include context we need to re-save and + # validate that the contexts are presend in the newly saved file. + runtime.run("lttng save --output-path={}".format(validation_path)) + + cp = runtime.subprocess_terminate(sessiond) + if cp.returncode != 0: + pytest.fail("Sessiond on load return code") + + validate_app_context(trace_name, validation_file_path) + + +@pytest.mark.parametrize("tools_save_l,tools_load_l,should_load", runtime_matrix_blocking_timeout) +def test_save_load_blocking_timeout(tmpdir, tools_save_l, tools_load_l, should_load): + + # Prepare environment + t_save = ProjectFactory.get_precook(tools_save_l) + t_load = ProjectFactory.get_precook(tools_load_l) + + t_save_runtime_path = os.path.join(str(tmpdir), "tools-save") + t_load_runtime_path = os.path.join(str(tmpdir), "tools-load") + save_load_path = os.path.join(str(tmpdir), 'save_load') + + trace_name = "saved_trace" + channel_name = "my_channel" + trace_filename = trace_name + Settings.save_ext + trace_file_path = os.path.join(save_load_path, trace_filename) + + blocking_timeout = 1000 + + # Craft the save + with Run.get_runtime(t_save_runtime_path) as runtime: + runtime.add_project(t_save) + + # Start lttng-sessiond + sessiond = utils.sessiond_spawn(runtime) + + runtime.run("lttng create {}".format(trace_name)) + runtime.run("lttng enable-channel -u --blocking-timeout {} {}".format(blocking_timeout, channel_name)) + runtime.run("lttng save --output-path={}".format(save_load_path)) + + + cp = runtime.subprocess_terminate(sessiond) + if cp.returncode != 0: + pytest.fail("Sessiond on save return code") + + assert(os.path.isfile(trace_file_path)) + + xpath_blocking_timeout = '/sessions/session[name="{}"]/domains/domain[type="UST"]/channels/channel[name="{}"]/blocking_timeout'.format(trace_name, channel_name) + node = xpath_query(trace_file_path, xpath_blocking_timeout) + assert(len(node) == 1) + assert(node[0].text == str(blocking_timeout)) + + # Load the save + with Run.get_runtime(t_load_runtime_path) as runtime: + runtime.add_project(t_load) + + # Start lttng-sessiond + sessiond = utils.sessiond_spawn(runtime) + + if should_load: + runtime.run("lttng load --input-path={} {}".format(save_load_path, trace_name)) + else: + with pytest.raises(subprocess.CalledProcessError): + runtime.run("lttng load --input-path={} {}".format(save_load_path, trace_name)) + cp = runtime.subprocess_terminate(sessiond) + if cp.returncode != 0: + pytest.fail("Sessiond on load return code") + return + + cp, mi_out, err = runtime.run("lttng --mi xml list {}".format(trace_name)) + + cp = runtime.subprocess_terminate(sessiond) + if cp.returncode != 0: + pytest.fail("Sessiond on load return code") + + assert(os.path.isfile(mi_out)) + xpath_mi_blocking_timeout = '/command/output/sessions/session/domains/domain/channels/channel/attributes/blocking_timeout' + node = xpath_query(mi_out, xpath_mi_blocking_timeout) + assert(len(node) == 1) + assert(node[0].text == str(blocking_timeout)) + + +@pytest.mark.parametrize("tools_save_l,tools_load_l,should_load", runtime_matrix_monitor_timer_interval) +def test_save_load_timer_interval(tmpdir, tools_save_l, tools_load_l, should_load): + + # Prepare environment + t_save = ProjectFactory.get_precook(tools_save_l) + t_load = ProjectFactory.get_precook(tools_load_l) + + t_save_runtime_path = os.path.join(str(tmpdir), "tools-save") + t_load_runtime_path = os.path.join(str(tmpdir), "tools-load") + save_load_path = os.path.join(str(tmpdir), 'save_load') + + trace_name = "saved_trace" + channel_name = "my_channel" + trace_filename = trace_name + Settings.save_ext + trace_file_path = os.path.join(save_load_path, trace_filename) + + monitor_timer_interval = 1000 + + # Craft the save + with Run.get_runtime(t_save_runtime_path) as runtime: + runtime.add_project(t_save) + + # Start lttng-sessiond + sessiond = utils.sessiond_spawn(runtime) + + runtime.run("lttng create {}".format(trace_name)) + runtime.run("lttng enable-channel -u --monitor-timer={} {}".format(monitor_timer_interval, channel_name)) + runtime.run("lttng save --output-path={}".format(save_load_path)) + + assert(os.path.isfile(trace_file_path)) + + cp = runtime.subprocess_terminate(sessiond) + if cp.returncode != 0: + pytest.fail("Sessiond on save return code") + + assert(os.path.isfile(trace_file_path)) + + xpath_monitor_interval = '/sessions/session/domains/domain[type="UST"]/channels/channel/monitor_timer_interval' + node = xpath_query(trace_file_path, xpath_monitor_interval) + assert(len(node) == 1) + assert(node[0].text == str(monitor_timer_interval)) + + # Load the save + with Run.get_runtime(t_load_runtime_path) as runtime: + runtime.add_project(t_load) + + # Start lttng-sessiond + sessiond = utils.sessiond_spawn(runtime) + + if should_load: + runtime.run("lttng load --input-path={} {}".format(save_load_path, trace_name)) + else: + with pytest.raises(subprocess.CalledProcessError): + runtime.run("lttng load --input-path={} {}".format(save_load_path, trace_name)) + cp = runtime.subprocess_terminate(sessiond) + if cp.returncode != 0: + pytest.fail("Sessiond on load return code") + return + + cp, mi_out, err = runtime.run("lttng --mi xml list {}".format(trace_name)) + + cp = runtime.subprocess_terminate(sessiond) + if cp.returncode != 0: + pytest.fail("Sessiond on load return code") + + assert(os.path.isfile(mi_out)) + xpath_mi = '/command/output/sessions/session/domains/domain/channels/channel/attributes/monitor_timer_interval' + node = xpath_query(mi_out, xpath_mi) + assert(len(node) == 1) + assert(node[0].text == str(monitor_timer_interval)) diff --git a/lttng_ivc/utils/utils.py b/lttng_ivc/utils/utils.py index c174b44..37e09e1 100644 --- a/lttng_ivc/utils/utils.py +++ b/lttng_ivc/utils/utils.py @@ -4,8 +4,10 @@ import os import time import socket +from lxml import etree from contextlib import closing + def line_count(file_path): line_count = 0 with open(file_path) as f: @@ -62,9 +64,9 @@ def find_free_port(): return s.getsockname()[1] -def file_contains(stderr_file, list_of_string): - with open(stderr_file, 'r') as stderr: - for line in stderr: +def file_contains(file_path, list_of_string): + with open(file_path, 'r') as f: + for line in f: for s in list_of_string: if s in line: return True @@ -77,7 +79,26 @@ def find_dir(root, name): abs_path = None for base, dirs, files in os.walk(root): for tmp in dirs: - print(tmp) if tmp.endswith(name): abs_path = os.path.abspath(os.path.join(base, tmp)) return abs_path + + +def xpath_query(xml_file, xpath): + """ + Return a list of xml node corresponding to the xpath. The list can be of lenght + zero. + """ + with open(xml_file, 'r') as f: + tree = etree.parse(f) + root = tree.getroot() + # Remove all namespace + # https://stackoverflow.com/questions/18159221/remove-namespace-and-prefix-from-xml-in-python-using-lxml + for elem in root.getiterator(): + if not hasattr(elem.tag, 'find'): + continue + i = elem.tag.find('}') + if i >= 0: + elem.tag = elem.tag[i+1:] + + return root.xpath(xpath) -- 2.34.1