From ed30eb8d59e3bc1d26808d3414a1fbc5f8bb23d8 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Genevi=C3=A8ve=20Bastien?= Date: Wed, 26 Feb 2020 15:49:57 -0500 Subject: [PATCH] Tests: sink.text.pretty: Add unit tests for enum fields printing MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Adds a Python component class who creates an event class with an enum field that contains the enumeration described by the test caller. The script iterator has a single event with a value also sent in parameter. The tests themselves are run from a bash script, for signed/unsigned values. This commit also renames the existing `test_pretty` test file to `test_pretty_python` to control all future Python tests for the `sink.text.pretty` component class. Change-Id: I3f631224dd3bdf21dbb2ef2d233c9f2dc8da43fa Signed-off-by: Geneviève Bastien Reviewed-on: https://review.lttng.org/c/babeltrace/+/3149 CI-Build: Francis Deslauriers Tested-by: jenkins Reviewed-by: Philippe Proulx --- configure.ac | 1 + tests/Makefile.am | 8 +- .../sink.text.pretty/bt_plugin_pretty_test.py | 83 +++++++++ tests/plugins/Makefile.am | 3 +- tests/plugins/sink.text.pretty/Makefile.am | 3 + tests/plugins/sink.text.pretty/test_enum | 160 ++++++++++++++++++ .../{test_pretty => test_pretty_python} | 0 tests/utils/python/split_sort_compare.py | 24 +++ 8 files changed, 278 insertions(+), 4 deletions(-) create mode 100644 tests/data/plugins/sink.text.pretty/bt_plugin_pretty_test.py create mode 100644 tests/plugins/sink.text.pretty/Makefile.am create mode 100755 tests/plugins/sink.text.pretty/test_enum rename tests/plugins/sink.text.pretty/{test_pretty => test_pretty_python} (100%) create mode 100644 tests/utils/python/split_sort_compare.py diff --git a/configure.ac b/configure.ac index 63eef1bc..34eb2f15 100644 --- a/configure.ac +++ b/configure.ac @@ -817,6 +817,7 @@ AC_CONFIG_FILES([ tests/plugins/flt.utils.muxer/Makefile tests/plugins/flt.utils.muxer/succeed/Makefile tests/plugins/flt.utils.trimmer/Makefile + tests/plugins/sink.text.pretty/Makefile tests/utils/Makefile tests/utils/tap/Makefile ]) diff --git a/tests/Makefile.am b/tests/Makefile.am index bd7ae03d..f1bc2e7f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -55,8 +55,9 @@ dist_check_SCRIPTS = \ cli/test_trace_read \ cli/test_trimmer \ plugins/sink.text.details/succeed/test_succeed \ - plugins/sink.text.pretty/test_pretty \ + plugins/sink.text.pretty/test_enum \ plugins/sink.text.pretty/test_pretty.py \ + plugins/sink.text.pretty/test_pretty_python \ plugins/src.ctf.lttng-live/test_live \ python-plugin-provider/bt_plugin_test_python_plugin_provider.py \ python-plugin-provider/test_python_plugin_provider \ @@ -135,7 +136,8 @@ TESTS_CLI += \ cli/test_exit_status TESTS_PLUGINS += plugins/flt.utils.trimmer/test_trimming \ - plugins/flt.utils.muxer/succeed/test_succeed + plugins/flt.utils.muxer/succeed/test_succeed \ + plugins/sink.text.pretty/test_enum endif endif @@ -151,7 +153,7 @@ TESTS_PYTHON_PLUGIN_PROVIDER = if ENABLE_PYTHON_PLUGINS TESTS_PYTHON_PLUGIN_PROVIDER += python-plugin-provider/test_python_plugin_provider -TESTS_PLUGINS += plugins/sink.text.pretty/test_pretty +TESTS_PLUGINS += plugins/sink.text.pretty/test_pretty_python if ENABLE_DEBUG_INFO TESTS_PLUGINS += \ plugins/flt.lttng-utils.debug-info/test_succeed diff --git a/tests/data/plugins/sink.text.pretty/bt_plugin_pretty_test.py b/tests/data/plugins/sink.text.pretty/bt_plugin_pretty_test.py new file mode 100644 index 00000000..c3a66f74 --- /dev/null +++ b/tests/data/plugins/sink.text.pretty/bt_plugin_pretty_test.py @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (C) 2020 Geneviève Bastien + +import bt2 + + +class TheIteratorOfProblems(bt2._UserMessageIterator): + def __init__(self, config, port): + tc, sc, ec1, params = port.user_data + trace = tc() + stream = trace.create_stream(sc) + event_value = params['value'] + self._msgs = [] + + self._msgs.append(self._create_stream_beginning_message(stream)) + + ev_msg1 = self._create_event_message(ec1, stream) + ev_msg1.event.payload_field["enum_field"] = event_value + + self._msgs.append(ev_msg1) + + self._msgs.append(self._create_stream_end_message(stream)) + + self._at = 0 + config.can_seek_forward = True + + def _user_seek_beginning(self): + self._at = 0 + + def __next__(self): + if self._at < len(self._msgs): + msg = self._msgs[self._at] + self._at += 1 + return msg + else: + raise StopIteration + + +@bt2.plugin_component_class +class TheSourceOfProblems( + bt2._UserSourceComponent, message_iterator_class=TheIteratorOfProblems +): + def __init__(self, config, params, obj): + tc = self._create_trace_class() + + enum_values_str = params['enum-values'] + + sc = tc.create_stream_class() + + # Create the enumeration field with the values in parameter + if params['enum-signed']: + enumfc = tc.create_signed_enumeration_field_class() + else: + enumfc = tc.create_unsigned_enumeration_field_class() + + groups = str(enum_values_str).split(' ') + mappings = {} + range_set_type = ( + bt2.SignedIntegerRangeSet + if params['enum-signed'] + else bt2.UnsignedIntegerRangeSet + ) + for group in groups: + label, low, high = group.split(',') + + if label not in mappings.keys(): + mappings[label] = range_set_type() + + mappings[label].add((int(low), int(high))) + + for x, y in mappings.items(): + enumfc.add_mapping(x, y) + + # Create the struct field to contain the enum field class + struct_fc = tc.create_structure_field_class() + struct_fc.append_member('enum_field', enumfc) + + # Create an event class on this stream with the struct field + ec1 = sc.create_event_class(name='with_enum', payload_field_class=struct_fc) + self._add_output_port('out', (tc, sc, ec1, params)) + + +bt2.register_plugin(__name__, 'test-pretty') diff --git a/tests/plugins/Makefile.am b/tests/plugins/Makefile.am index d0f711a8..2cf409ef 100644 --- a/tests/plugins/Makefile.am +++ b/tests/plugins/Makefile.am @@ -5,4 +5,5 @@ SUBDIRS = \ src.ctf.fs \ flt.lttng-utils.debug-info \ flt.utils.muxer \ - flt.utils.trimmer + flt.utils.trimmer \ + sink.text.pretty diff --git a/tests/plugins/sink.text.pretty/Makefile.am b/tests/plugins/sink.text.pretty/Makefile.am new file mode 100644 index 00000000..2eb912ff --- /dev/null +++ b/tests/plugins/sink.text.pretty/Makefile.am @@ -0,0 +1,3 @@ +dist_check_SCRIPTS = \ + test_pretty_python \ + test_enum diff --git a/tests/plugins/sink.text.pretty/test_enum b/tests/plugins/sink.text.pretty/test_enum new file mode 100755 index 00000000..c00e6ac7 --- /dev/null +++ b/tests/plugins/sink.text.pretty/test_enum @@ -0,0 +1,160 @@ +#!/bin/bash +# +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2020 Geneviève Bastien +# +# This file tests pretty printing in details some event classes that are +# not all covered by the main babeltrace tests with traces. +SH_TAP=1 + +if [ "x${BT_TESTS_SRCDIR:-}" != "x" ]; then + UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh" +else + UTILSSH="$(dirname "$0")/../../utils/utils.sh" +fi + +# shellcheck source=../../utils/utils.sh +source "$UTILSSH" + +data_dir="$BT_TESTS_DATADIR/plugins/sink.text.pretty" +temp_stdout_expected_file=$(mktemp -t test_pretty_expected_stdout.XXXXXX) +temp_stderr_expected="/dev/null" + +plan_tests 14 + +function compare_enum_sorted +{ + local expected_file="$1" + local actual_file="$2" + + # The order in which enum labels are printed by a `sink.text.pretty` + # component directly depends on the order in which mappings were added + # to the enum field class in the source component. This order should + # not be relied on when testing. Relying on it caused problems with + # Python component classes because different versions of Python sort + # data structures differently (e.g. dictionaries are insertion sorted + # since Python 3.7). + + run_python_bt2 python3 "${BT_TESTS_SRCDIR}/utils/python/split_sort_compare.py" \ + "$(cat $expected_file)" "$(cat $actual_file)" +} + +function run_test +{ + local test_name=$1 + local expected_to_fail="$2" + local value="$3" + local expected_stdout_file="$4" + local test_text= + local actual_stdout_file + local actual_stderr_file + local ret=0 + local local_args=( + "--plugin-path" "$data_dir" + "-c" "src.test-pretty.TheSourceOfProblems" + "-p" "enum-values=\"$enum_values\"" + "-p" "value=$value" + "-p" "enum-signed=$enum_signed" + "-c" "sink.text.pretty" + ) + + actual_stdout_file="$(mktemp -t actual_pretty_stdout.XXXXXX)" + actual_stderr_file="$(mktemp -t actual_pretty_stderr.XXXXXX)" + + bt_cli "$actual_stdout_file" "$actual_stderr_file" "${local_args[@]}" + + compare_enum_sorted "$expected_stdout_file" "$actual_stdout_file" + ret_stdout=$? + + bt_diff /dev/null "$actual_stderr_file" + ret_stderr=$? + + if ((ret_stdout != 0 || ret_stderr != 0)); then + ret=1 + fi + + rm -f "$actual_stdout_file" "$actual_stderr_file" + + if (($expected_to_fail)); then + isnt $ret 0 "$test_name signed=$enum_signed with value=$value doesn't match as expected" + else + ok $ret "$test_name signed=$enum_signed with value=$value matches" + fi + +} + +function test_normal_enum { + test_name="Normal enum" + enum_signed="$1" + enum_values="single,1,1 single2,2,2 single3,4,4 range,4,8 range2,15,20" + + # Hit a single value + cat <<- 'END' > "$temp_stdout_expected_file" + with_enum: { enum_field = ( "single" : container = 1 ) } + END + run_test "$test_name" 0 1 "$temp_stdout_expected_file" + + # Hit a single range + cat <<- 'END' > "$temp_stdout_expected_file" + with_enum: { enum_field = ( "range" : container = 7 ) } + END + run_test "$test_name" 0 7 "$temp_stdout_expected_file" + + # Unknown + cat <<- 'END' > "$temp_stdout_expected_file" + with_enum: { enum_field = ( : container = 21 ) } + END + run_test "$test_name" 0 21 "$temp_stdout_expected_file" + + # Unknown but with bits with a value, but range larger than 1 element + cat <<- 'END' > "$temp_stdout_expected_file" + with_enum: { enum_field = ( : container = 12 ) } + END + run_test "$test_name" 0 12 "$temp_stdout_expected_file" + + # Unknown value of 0 + cat <<- 'END' > "$temp_stdout_expected_file" + with_enum: { enum_field = ( : container = 0 ) } + END + run_test "$test_name" 0 0 "$temp_stdout_expected_file" +} + +function test_normal_enum_negative { + test_name="Normal enum with negative value" + enum_signed="true" + enum_values="zero,0,0 single,1,1 single2,2,2 single3,4,4 range,4,8 negative,-1,-1 rangeNegative,-6,-2" + + # Hit a single negative value + cat <<- 'END' > "$temp_stdout_expected_file" + with_enum: { enum_field = ( "negative" : container = -1 ) } + END + run_test "$test_name" 0 -1 "$temp_stdout_expected_file" + + # Hit a single negative range + cat <<- 'END' > "$temp_stdout_expected_file" + with_enum: { enum_field = ( "rangeNegative" : container = -6 ) } + END + run_test "$test_name" 0 -6 "$temp_stdout_expected_file" + + # Unknown + cat <<- 'END' > "$temp_stdout_expected_file" + with_enum: { enum_field = ( : container = -7 ) } + END + run_test "$test_name" 0 -7 "$temp_stdout_expected_file" + + # value of 0 + cat <<- 'END' > "$temp_stdout_expected_file" + with_enum: { enum_field = ( "zero" : container = 0 ) } + END + run_test "$test_name" 0 0 "$temp_stdout_expected_file" +} + +# Enumerations tests +test_normal_enum "false" +test_normal_enum "true" +test_normal_enum_negative + +# Do not `rm` $temp_stderr_expected because it's set to `/dev/null` right now +# and that would print an error. +rm -f "$temp_stdout_expected_file" diff --git a/tests/plugins/sink.text.pretty/test_pretty b/tests/plugins/sink.text.pretty/test_pretty_python similarity index 100% rename from tests/plugins/sink.text.pretty/test_pretty rename to tests/plugins/sink.text.pretty/test_pretty_python diff --git a/tests/utils/python/split_sort_compare.py b/tests/utils/python/split_sort_compare.py new file mode 100644 index 00000000..3b4f83d5 --- /dev/null +++ b/tests/utils/python/split_sort_compare.py @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2020 Francis Deslauriers + +import sys +import re + + +def main(): + expected = sys.argv[1] + actual = sys.argv[2] + sorted_expected = ''.join(sorted(re.findall(r'\w+|\W+', expected.strip()))) + sorted_actual = ''.join(sorted(re.findall(r'\w+|\W+', actual.strip()))) + + if sorted_expected == sorted_actual: + status = 0 + else: + status = 1 + + sys.exit(status) + + +if __name__ == '__main__': + main() -- 2.34.1