Add facilities to test CLI and plugin regressions with expectation files
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Tue, 11 Jun 2019 23:08:46 +0000 (19:08 -0400)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Mon, 17 Jun 2019 20:56:59 +0000 (16:56 -0400)
This patch adds `tests/utils/diff.sh.in` (which becomes
`tests/utils/diff.sh`) which contains shell functions to use the CLI and
a `sink.text.details` component to easily test the CLI itself and
plugins for regressions with expectation files.

There are three functions in `tests/utils/diff.sh.in`:

bt_diff_cli():
    Compares the output of the CLI with specific arguments to the
    content of a file.

bt_diff_details_ctf_single():
    Compares the output of the CLI using an `src.ctf.fs` component to
    open a specific CTF trace and a `sink.text.details` component to the
    content of a file.

bt_diff_details_ctf_gen_single():
    Like bt_diff_details_ctf_single(), but runs a given CTF trace
    generating program instead of using an existing CTF trace.

When there's any difference, the functions write the full diff to the
standard error. This makes it possible to look at what's wrong in the
CI.

`tests/plugins/ctf` is moved to `tests/plugins/src.ctf.fs` to indicate
that we're testing this specific component class. The specific query
tests are moved to `tests/plugins/src.ctf.fs/query`.

`tests/plugins/src.ctf.fs/diff/test_diff.in` uses `tests/utils/diff.sh`
to show how to use bt_diff_details_ctf_single() and
bt_diff_details_ctf_gen_single().

The functions test_ctf_single() and test_ctf_gen_single() use resp.
bt_diff_details_ctf_single() and bt_diff_details_ctf_gen_single() to
compare the given CTF traces using a `sink.text.details` component to
expectation files ending with `.expect` in the same directory.

In both test_ctf_single() and test_ctf_gen_single(), we make the
`sink.text.details` component hide the trace and stream names because
`src.ctf.fs` puts absolute paths in them. For test_ctf_gen_single(), we
also make the `sink.text.details` component hide UUIDs because CTF
writer creates random ones by default.

More specifically, this line:

    test_ctf_gen_single simple

means:

1. Run the program `gen-trace-simple` in the same directory to generate
   a CTF trace.

2. Get the output of:

       babeltrace2 /path/to/trace -c sink.text.details \
                   -p with-uuid=no,with-trace-name=no,with-stream-name=no

3. Compare the content of 2. to the file `trace-simple.expect` in the
   same directory.

This line:

    test_ctf_single smalltrace

means:

1. Get the output of:

       babeltrace2 "$BTDIR/tests/ctf-traces/succeed/smalltrace" \
                   -c sink.text.details \
                   -p with-trace-name=no,with-stream-name=no

2. Compare the content of 1. to the file `trace-smalltrace.expect` in
   the same directory.

Eventually, we can move the test_ctf_gen_single() and test_ctf_single()
functions outside the `src.ctf.fs` tests to make them available to test
other plugins and parts of the project. They are placed there for the
moment because only the `src.ctf.fs` tests need them.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Change-Id: I53660111c23117926e3ee114b5d0cd060fff87d3
Reviewed-on: https://review.lttng.org/c/babeltrace/+/1414
Tested-by: jenkins
15 files changed:
.gitignore
configure.ac
tests/Makefile.am
tests/plugins/Makefile.am
tests/plugins/ctf/test_ctf_plugin.in [deleted file]
tests/plugins/ctf/test_query_trace_info.py [deleted file]
tests/plugins/src.ctf.fs/Makefile.am [new file with mode: 0644]
tests/plugins/src.ctf.fs/query/test_query.in [new file with mode: 0644]
tests/plugins/src.ctf.fs/query/test_query_trace_info.py [new file with mode: 0644]
tests/plugins/src.ctf.fs/succeed/Makefile.am [new file with mode: 0644]
tests/plugins/src.ctf.fs/succeed/gen-trace-simple.c [new file with mode: 0644]
tests/plugins/src.ctf.fs/succeed/test_succeed.in [new file with mode: 0644]
tests/plugins/src.ctf.fs/succeed/trace-simple.expect [new file with mode: 0644]
tests/plugins/src.ctf.fs/succeed/trace-smalltrace.expect [new file with mode: 0644]
tests/utils/diff.sh.in [new file with mode: 0644]

index 3e318243822e7d7a0372a02cf710b5adfe4122ce..ef202c5ed91f65eb9490aaf3f84c685783bc6886 100644 (file)
 /tests/plugins/test_dwarf_powerpc64le-linux-gnu
 /tests/plugins/test_dwarf_x86_64-linux-gnu
 /tests/plugins/test_dwarf
-/tests/plugins/ctf/test_ctf_plugin
+/tests/plugins/src.ctf.fs/query/test_query
+/tests/plugins/src.ctf.fs/succeed/test_succeed
+/tests/plugins/src.ctf.fs/succeed/gen-trace-*
 /tests/utils/common.sh
+/tests/utils/diff.sh
 *.o
 *.a
 *.la
index dfbe00c6ac67774beed024ec7c189311e44865da..d9d41eea57c4cd57aacff0799c0241f9e1954f07 100644 (file)
@@ -783,7 +783,10 @@ AC_CONFIG_FILES([
        tests/lib/test-plugin-plugins/Makefile
        tests/Makefile
        tests/plugins/Makefile
+       tests/plugins/src.ctf.fs/Makefile
+       tests/plugins/src.ctf.fs/succeed/Makefile
        tests/utils/common.sh
+       tests/utils/diff.sh
        tests/utils/Makefile
        tests/utils/tap/Makefile
 ])
@@ -798,7 +801,8 @@ AC_CONFIG_FILES([tests/cli/test_trimmer], [chmod +x tests/cli/test_trimmer])
 AC_CONFIG_FILES([tests/ctf-writer/test_ctf_writer], [chmod +x tests/ctf-writer/test_ctf_writer])
 AC_CONFIG_FILES([tests/lib/test_plugin_complete], [chmod +x tests/lib/test_plugin_complete])
 AC_CONFIG_FILES([tests/lib/trace-ir/test_trace_ir], [chmod +x tests/lib/trace-ir/test_trace_ir])
-AC_CONFIG_FILES([tests/plugins/ctf/test_ctf_plugin], [chmod +x tests/plugins/ctf/test_ctf_plugin])
+AC_CONFIG_FILES([tests/plugins/src.ctf.fs/query/test_query], [chmod +x tests/plugins/src.ctf.fs/query/test_query])
+AC_CONFIG_FILES([tests/plugins/src.ctf.fs/succeed/test_succeed], [chmod +x tests/plugins/src.ctf.fs/succeed/test_succeed])
 AC_CONFIG_FILES([tests/plugins/test_utils_muxer_complete], [chmod +x tests/plugins/test_utils_muxer_complete])
 AC_CONFIG_FILES([tests/plugins/test_lttng_utils_debug_info], [chmod +x tests/plugins/test_lttng_utils_debug_info])
 AC_CONFIG_FILES([tests/plugins/test_dwarf_i386-linux-gnu], [chmod +x tests/plugins/test_dwarf_i386-linux-gnu])
index 2f5ed60e88222f276a8817b14445ad794e7f541d..842a3858e15211b8125597a4c30fe19227799697 100644 (file)
@@ -63,11 +63,12 @@ if ENABLE_PYTHON_BINDINGS
 TESTS_LIB += lib/trace-ir/test_trace_ir
 endif
 
-TESTS_PLUGINS =
+TESTS_PLUGINS = \
+       plugins/src.ctf.fs/succeed/test_succeed
 
 if !ENABLE_BUILT_IN_PLUGINS
 if ENABLE_PYTHON_BINDINGS
-TESTS_PLUGINS += plugins/ctf/test_ctf_plugin
+TESTS_PLUGINS += plugins/src.ctf.fs/query/test_query
 
 if ENABLE_DEBUG_INFO
 TESTS_PLUGINS += plugins/test_lttng_utils_debug_info
index 015f775fbf82736a7ea106d74d4c97e21edacccd..e748d4fe4f31981e3aedaf13e0555c06e6961779 100644 (file)
@@ -1,10 +1,10 @@
+SUBDIRS = src.ctf.fs
 AM_CPPFLAGS += -I$(top_srcdir)/tests/utils -I$(top_srcdir)/src/plugins
 
 LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la
 
 dist_check_SCRIPTS = \
-       test_lttng_utils_debug_info.py\
-       ctf/test_query_trace_info.py
+       test_lttng_utils_debug_info.py
 
 noinst_PROGRAMS =
 
diff --git a/tests/plugins/ctf/test_ctf_plugin.in b/tests/plugins/ctf/test_ctf_plugin.in
deleted file mode 100644 (file)
index 3916445..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; only version 2
-# of the License.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-
-NO_SH_TAP=1
-. "@abs_top_builddir@/tests/utils/common.sh"
-
-TESTRUNNER_PY="$BT_SRC_PATH/tests/utils/python/testrunner.py"
-THIS_DIR="$BT_SRC_PATH/tests/plugins/ctf"
-
-exec "$BT_BUILD_PATH/tests/utils/test_python_bt2_env" python3 "$TESTRUNNER_PY" "$THIS_DIR"
diff --git a/tests/plugins/ctf/test_query_trace_info.py b/tests/plugins/ctf/test_query_trace_info.py
deleted file mode 100644 (file)
index a026a68..0000000
+++ /dev/null
@@ -1,203 +0,0 @@
-# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; only version 2
-# of the License.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-
-import unittest
-import bt2
-import os
-import re
-
-
-test_ctf_traces_path = os.environ['TEST_CTF_TRACES_PATH']
-
-
-# Key to sort streams in a predictable order.
-def sort_predictably(stream):
-    if 'range-ns' in stream:
-        return stream['range-ns']['begin']
-    else:
-        return stream['paths'][0]
-
-
-class QueryTraceInfoClockOffsetTestCase(unittest.TestCase):
-
-    def setUp(self):
-        ctf = bt2.find_plugin('ctf')
-        self._fs = ctf.source_component_classes['fs']
-
-        self._paths = [os.path.join(test_ctf_traces_path, 'intersection', '3eventsintersect')]
-        self._executor = bt2.QueryExecutor()
-
-    def _check(self, trace, offset):
-        self.assertEqual(trace['range-ns']['begin'], 13515309000000000 + offset)
-        self.assertEqual(trace['range-ns']['end'], 13515309000000120 + offset)
-        self.assertEqual(trace['intersection-range-ns']['begin'], 13515309000000070 + offset)
-        self.assertEqual(trace['intersection-range-ns']['end'], 13515309000000100 + offset)
-
-        streams = sorted(trace['streams'], key=sort_predictably)
-        self.assertEqual(streams[0]['range-ns']['begin'], 13515309000000000 + offset)
-        self.assertEqual(streams[0]['range-ns']['end'], 13515309000000100 + offset)
-        self.assertEqual(streams[1]['range-ns']['begin'], 13515309000000070 + offset)
-        self.assertEqual(streams[1]['range-ns']['end'], 13515309000000120 + offset)
-
-    # Test various cominations of the clock-class-offset-s and
-    # clock-class-offset-ns parameters to trace-info queries.
-
-    # Without clock class offset
-
-    def test_no_clock_class_offset(self):
-        res = self._executor.query(self._fs, 'trace-info', {
-            'paths': self._paths,
-        })
-        trace = res[0]
-        self._check(trace, 0)
-
-    # With clock-class-offset-s
-
-    def test_clock_class_offset_s(self):
-        res = self._executor.query(self._fs, 'trace-info', {
-            'paths': self._paths,
-            'clock-class-offset-s': 2,
-        })
-        trace = res[0]
-        self._check(trace, 2000000000)
-
-    # With clock-class-offset-ns
-
-    def test_clock_class_offset_ns(self):
-        res = self._executor.query(self._fs, 'trace-info', {
-            'paths': self._paths,
-            'clock-class-offset-ns': 2,
-        })
-        trace = res[0]
-        self._check(trace, 2)
-
-    # With both, negative
-
-    def test_clock_class_offset_both(self):
-        res = self._executor.query(self._fs, 'trace-info', {
-            'paths': self._paths,
-            'clock-class-offset-s': -2,
-            'clock-class-offset-ns': -2,
-        })
-        trace = res[0]
-        self._check(trace, -2000000002)
-
-    def test_clock_class_offset_s_wrong_type(self):
-        with self.assertRaises(bt2.InvalidQueryParams):
-            self._executor.query(self._fs, 'trace-info', {
-                'paths': self._paths,
-                'clock-class-offset-s': "2",
-            })
-
-    def test_clock_class_offset_s_wrong_type_none(self):
-        with self.assertRaises(bt2.InvalidQueryParams):
-            self._executor.query(self._fs, 'trace-info', {
-                'paths': self._paths,
-                'clock-class-offset-s': None,
-            })
-
-    def test_clock_class_offset_ns_wrong_type(self):
-        with self.assertRaises(bt2.InvalidQueryParams):
-            self._executor.query(self._fs, 'trace-info', {
-                'paths': self._paths,
-                'clock-class-offset-ns': "2",
-            })
-
-    def test_clock_class_offset_ns_wrong_type_none(self):
-        with self.assertRaises(bt2.InvalidQueryParams):
-            self._executor.query(self._fs, 'trace-info', {
-                'paths': self._paths,
-                'clock-class-offset-ns': None,
-            })
-
-
-class QueryTraceInfoPortNameTestCase(unittest.TestCase):
-    def setUp(self):
-        ctf = bt2.find_plugin("ctf")
-        self._fs = ctf.source_component_classes["fs"]
-
-        self._executor = bt2.QueryExecutor()
-
-    def test_trace_uuid_stream_class_id_no_stream_id(self):
-        res = self._executor.query(
-            self._fs,
-            "trace-info",
-            {
-                "paths": [
-                    os.path.join(
-                        test_ctf_traces_path, "intersection", "3eventsintersect"
-                    )
-                ]
-            },
-        )
-        self.assertEqual(len(res), 1)
-        trace = res[0]
-        streams = sorted(trace["streams"], key=sort_predictably)
-        self.assertEqual(len(streams), 2)
-        self.assertRegexpMatches(
-            str(streams[0]["port-name"]),
-            r"^7afe8fbe-79b8-4f6a-bbc7-d0c782e7ddaf \| 0 \| .*/tests/ctf-traces/intersection/3eventsintersect/test_stream_0$",
-        )
-        self.assertRegexpMatches(
-            str(streams[1]["port-name"]),
-            r"^7afe8fbe-79b8-4f6a-bbc7-d0c782e7ddaf \| 0 \| .*/tests/ctf-traces/intersection/3eventsintersect/test_stream_1$",
-        )
-
-    def test_trace_uuid_no_stream_class_id_no_stream_id(self):
-        res = self._executor.query(
-            self._fs,
-            "trace-info",
-            {"paths": [os.path.join(test_ctf_traces_path, "succeed", "succeed1")]},
-        )
-        self.assertEqual(len(res), 1)
-        trace = res[0]
-        streams = sorted(trace["streams"], key=sort_predictably)
-        self.assertEqual(len(streams), 1)
-        self.assertRegexpMatches(
-            str(streams[0]["port-name"]),
-            r"^2a6422d0-6cee-11e0-8c08-cb07d7b3a564 \| .*/tests/ctf-traces/succeed/succeed1/dummystream$",
-        )
-
-
-class QueryTraceInfoRangeTestCase(unittest.TestCase):
-    def setUp(self):
-        ctf = bt2.find_plugin("ctf")
-        self._fs = ctf.source_component_classes["fs"]
-
-        self._executor = bt2.QueryExecutor()
-
-    def test_trace_no_range(self):
-        # This trace has no `timestamp_begin` and `timestamp_end` in its packet
-        # context. The `trace-info` query should omit the `range-ns` fields in
-        # the `trace` and `stream` data structures.
-
-        res = self._executor.query(
-            self._fs,
-            "trace-info",
-            {"paths": [os.path.join(test_ctf_traces_path, "succeed", "succeed1")]},
-        )
-
-        self.assertEqual(len(res), 1)
-        trace = res[0]
-        streams = trace["streams"]
-        self.assertEqual(len(streams), 1)
-
-        self.assertRaises(KeyError, lambda: trace['range-ns'])
-        self.assertRaises(KeyError, lambda: streams[0]['range-ns'])
-
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/tests/plugins/src.ctf.fs/Makefile.am b/tests/plugins/src.ctf.fs/Makefile.am
new file mode 100644 (file)
index 0000000..4082add
--- /dev/null
@@ -0,0 +1,3 @@
+SUBDIRS = succeed
+dist_check_SCRIPTS = \
+       query/test_query_trace_info.py
diff --git a/tests/plugins/src.ctf.fs/query/test_query.in b/tests/plugins/src.ctf.fs/query/test_query.in
new file mode 100644 (file)
index 0000000..9c5d984
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; only version 2
+# of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+NO_SH_TAP=1
+. "@abs_top_builddir@/tests/utils/common.sh"
+
+TESTRUNNER_PY="$BT_SRC_PATH/tests/utils/python/testrunner.py"
+THIS_DIR="$BT_SRC_PATH/tests/plugins/src.ctf.fs/query"
+
+exec "$BT_BUILD_PATH/tests/utils/test_python_bt2_env" python3 "$TESTRUNNER_PY" "$THIS_DIR"
diff --git a/tests/plugins/src.ctf.fs/query/test_query_trace_info.py b/tests/plugins/src.ctf.fs/query/test_query_trace_info.py
new file mode 100644 (file)
index 0000000..a026a68
--- /dev/null
@@ -0,0 +1,203 @@
+# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; only version 2
+# of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+import unittest
+import bt2
+import os
+import re
+
+
+test_ctf_traces_path = os.environ['TEST_CTF_TRACES_PATH']
+
+
+# Key to sort streams in a predictable order.
+def sort_predictably(stream):
+    if 'range-ns' in stream:
+        return stream['range-ns']['begin']
+    else:
+        return stream['paths'][0]
+
+
+class QueryTraceInfoClockOffsetTestCase(unittest.TestCase):
+
+    def setUp(self):
+        ctf = bt2.find_plugin('ctf')
+        self._fs = ctf.source_component_classes['fs']
+
+        self._paths = [os.path.join(test_ctf_traces_path, 'intersection', '3eventsintersect')]
+        self._executor = bt2.QueryExecutor()
+
+    def _check(self, trace, offset):
+        self.assertEqual(trace['range-ns']['begin'], 13515309000000000 + offset)
+        self.assertEqual(trace['range-ns']['end'], 13515309000000120 + offset)
+        self.assertEqual(trace['intersection-range-ns']['begin'], 13515309000000070 + offset)
+        self.assertEqual(trace['intersection-range-ns']['end'], 13515309000000100 + offset)
+
+        streams = sorted(trace['streams'], key=sort_predictably)
+        self.assertEqual(streams[0]['range-ns']['begin'], 13515309000000000 + offset)
+        self.assertEqual(streams[0]['range-ns']['end'], 13515309000000100 + offset)
+        self.assertEqual(streams[1]['range-ns']['begin'], 13515309000000070 + offset)
+        self.assertEqual(streams[1]['range-ns']['end'], 13515309000000120 + offset)
+
+    # Test various cominations of the clock-class-offset-s and
+    # clock-class-offset-ns parameters to trace-info queries.
+
+    # Without clock class offset
+
+    def test_no_clock_class_offset(self):
+        res = self._executor.query(self._fs, 'trace-info', {
+            'paths': self._paths,
+        })
+        trace = res[0]
+        self._check(trace, 0)
+
+    # With clock-class-offset-s
+
+    def test_clock_class_offset_s(self):
+        res = self._executor.query(self._fs, 'trace-info', {
+            'paths': self._paths,
+            'clock-class-offset-s': 2,
+        })
+        trace = res[0]
+        self._check(trace, 2000000000)
+
+    # With clock-class-offset-ns
+
+    def test_clock_class_offset_ns(self):
+        res = self._executor.query(self._fs, 'trace-info', {
+            'paths': self._paths,
+            'clock-class-offset-ns': 2,
+        })
+        trace = res[0]
+        self._check(trace, 2)
+
+    # With both, negative
+
+    def test_clock_class_offset_both(self):
+        res = self._executor.query(self._fs, 'trace-info', {
+            'paths': self._paths,
+            'clock-class-offset-s': -2,
+            'clock-class-offset-ns': -2,
+        })
+        trace = res[0]
+        self._check(trace, -2000000002)
+
+    def test_clock_class_offset_s_wrong_type(self):
+        with self.assertRaises(bt2.InvalidQueryParams):
+            self._executor.query(self._fs, 'trace-info', {
+                'paths': self._paths,
+                'clock-class-offset-s': "2",
+            })
+
+    def test_clock_class_offset_s_wrong_type_none(self):
+        with self.assertRaises(bt2.InvalidQueryParams):
+            self._executor.query(self._fs, 'trace-info', {
+                'paths': self._paths,
+                'clock-class-offset-s': None,
+            })
+
+    def test_clock_class_offset_ns_wrong_type(self):
+        with self.assertRaises(bt2.InvalidQueryParams):
+            self._executor.query(self._fs, 'trace-info', {
+                'paths': self._paths,
+                'clock-class-offset-ns': "2",
+            })
+
+    def test_clock_class_offset_ns_wrong_type_none(self):
+        with self.assertRaises(bt2.InvalidQueryParams):
+            self._executor.query(self._fs, 'trace-info', {
+                'paths': self._paths,
+                'clock-class-offset-ns': None,
+            })
+
+
+class QueryTraceInfoPortNameTestCase(unittest.TestCase):
+    def setUp(self):
+        ctf = bt2.find_plugin("ctf")
+        self._fs = ctf.source_component_classes["fs"]
+
+        self._executor = bt2.QueryExecutor()
+
+    def test_trace_uuid_stream_class_id_no_stream_id(self):
+        res = self._executor.query(
+            self._fs,
+            "trace-info",
+            {
+                "paths": [
+                    os.path.join(
+                        test_ctf_traces_path, "intersection", "3eventsintersect"
+                    )
+                ]
+            },
+        )
+        self.assertEqual(len(res), 1)
+        trace = res[0]
+        streams = sorted(trace["streams"], key=sort_predictably)
+        self.assertEqual(len(streams), 2)
+        self.assertRegexpMatches(
+            str(streams[0]["port-name"]),
+            r"^7afe8fbe-79b8-4f6a-bbc7-d0c782e7ddaf \| 0 \| .*/tests/ctf-traces/intersection/3eventsintersect/test_stream_0$",
+        )
+        self.assertRegexpMatches(
+            str(streams[1]["port-name"]),
+            r"^7afe8fbe-79b8-4f6a-bbc7-d0c782e7ddaf \| 0 \| .*/tests/ctf-traces/intersection/3eventsintersect/test_stream_1$",
+        )
+
+    def test_trace_uuid_no_stream_class_id_no_stream_id(self):
+        res = self._executor.query(
+            self._fs,
+            "trace-info",
+            {"paths": [os.path.join(test_ctf_traces_path, "succeed", "succeed1")]},
+        )
+        self.assertEqual(len(res), 1)
+        trace = res[0]
+        streams = sorted(trace["streams"], key=sort_predictably)
+        self.assertEqual(len(streams), 1)
+        self.assertRegexpMatches(
+            str(streams[0]["port-name"]),
+            r"^2a6422d0-6cee-11e0-8c08-cb07d7b3a564 \| .*/tests/ctf-traces/succeed/succeed1/dummystream$",
+        )
+
+
+class QueryTraceInfoRangeTestCase(unittest.TestCase):
+    def setUp(self):
+        ctf = bt2.find_plugin("ctf")
+        self._fs = ctf.source_component_classes["fs"]
+
+        self._executor = bt2.QueryExecutor()
+
+    def test_trace_no_range(self):
+        # This trace has no `timestamp_begin` and `timestamp_end` in its packet
+        # context. The `trace-info` query should omit the `range-ns` fields in
+        # the `trace` and `stream` data structures.
+
+        res = self._executor.query(
+            self._fs,
+            "trace-info",
+            {"paths": [os.path.join(test_ctf_traces_path, "succeed", "succeed1")]},
+        )
+
+        self.assertEqual(len(res), 1)
+        trace = res[0]
+        streams = trace["streams"]
+        self.assertEqual(len(streams), 1)
+
+        self.assertRaises(KeyError, lambda: trace['range-ns'])
+        self.assertRaises(KeyError, lambda: streams[0]['range-ns'])
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/plugins/src.ctf.fs/succeed/Makefile.am b/tests/plugins/src.ctf.fs/succeed/Makefile.am
new file mode 100644 (file)
index 0000000..4a562cc
--- /dev/null
@@ -0,0 +1,15 @@
+dist_check_SCRIPTS = test_succeed
+
+# Expectation files
+EXTRA_DIST = \
+       trace-smalltrace.expect \
+       trace-simple.expect
+
+# CTF trace generators
+GEN_TRACE_LDADD = $(top_builddir)/src/ctf-writer/libbabeltrace2-ctf-writer.la
+
+gen_trace_simple_SOURCES = gen-trace-simple.c
+gen_trace_simple_LDADD = $(GEN_TRACE_LDADD)
+
+noinst_PROGRAMS = \
+       gen-trace-simple
diff --git a/tests/plugins/src.ctf.fs/succeed/gen-trace-simple.c b/tests/plugins/src.ctf.fs/succeed/gen-trace-simple.c
new file mode 100644 (file)
index 0000000..bcfddae
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <babeltrace2/ctf-writer/writer.h>
+#include <babeltrace2/ctf-writer/clock.h>
+#include <babeltrace2/ctf-writer/clock-class.h>
+#include <babeltrace2/ctf-writer/stream.h>
+#include <babeltrace2/ctf-writer/event.h>
+#include <babeltrace2/ctf-writer/event-types.h>
+#include <babeltrace2/ctf-writer/event-fields.h>
+#include <babeltrace2/ctf-writer/stream-class.h>
+#include <babeltrace2/ctf-writer/trace.h>
+
+#include "common/assert.h"
+
+struct config {
+       struct bt_ctf_writer *writer;
+       struct bt_ctf_trace *trace;
+       struct bt_ctf_clock *clock;
+       struct bt_ctf_stream_class *sc;
+       struct bt_ctf_stream *stream;
+       struct bt_ctf_event_class *ec;
+};
+
+static
+void fini_config(struct config *cfg)
+{
+       bt_ctf_object_put_ref(cfg->stream);
+       bt_ctf_object_put_ref(cfg->sc);
+       bt_ctf_object_put_ref(cfg->ec);
+       bt_ctf_object_put_ref(cfg->clock);
+       bt_ctf_object_put_ref(cfg->trace);
+       bt_ctf_object_put_ref(cfg->writer);
+}
+
+static
+void configure_writer(struct config *cfg, const char *path)
+{
+       struct bt_ctf_field_type *ft;
+       int ret;
+
+       cfg->writer = bt_ctf_writer_create(path);
+       BT_ASSERT(cfg->writer);
+       cfg->trace = bt_ctf_writer_get_trace(cfg->writer);
+       BT_ASSERT(cfg->trace);
+       cfg->clock = bt_ctf_clock_create("default");
+       BT_ASSERT(cfg->clock);
+       ret = bt_ctf_writer_add_clock(cfg->writer, cfg->clock);
+       BT_ASSERT(ret == 0);
+       ret = bt_ctf_writer_set_byte_order(cfg->writer,
+               BT_CTF_BYTE_ORDER_BIG_ENDIAN);
+       BT_ASSERT(ret == 0);
+       cfg->sc = bt_ctf_stream_class_create("hello");
+       BT_ASSERT(cfg->sc);
+       ret = bt_ctf_stream_class_set_clock(cfg->sc, cfg->clock);
+       BT_ASSERT(ret == 0);
+       cfg->ec = bt_ctf_event_class_create("ev");
+       BT_ASSERT(cfg->ec);
+       ft = bt_ctf_field_type_integer_create(8);
+       BT_ASSERT(ft);
+       ret = bt_ctf_field_type_integer_set_is_signed(ft, BT_TRUE);
+       BT_ASSERT(ret == 0);
+       ret = bt_ctf_event_class_add_field(cfg->ec, ft, "first");
+       BT_ASSERT(ret == 0);
+       bt_ctf_object_put_ref(ft);
+       ft = bt_ctf_field_type_string_create();
+       BT_ASSERT(ft);
+       ret = bt_ctf_event_class_add_field(cfg->ec, ft, "second");
+       BT_ASSERT(ret == 0);
+       bt_ctf_object_put_ref(ft);
+       ret = bt_ctf_stream_class_add_event_class(cfg->sc, cfg->ec);
+       BT_ASSERT(ret == 0);
+       cfg->stream = bt_ctf_writer_create_stream(cfg->writer, cfg->sc);
+       BT_ASSERT(cfg->stream);
+}
+
+static
+void write_stream(struct config *cfg)
+{
+       struct bt_ctf_event *ev;
+       struct bt_ctf_field *field;
+       uint64_t i;
+       int ret;
+
+       for (i = 0; i < 25; i++) {
+               ev = bt_ctf_event_create(cfg->ec);
+               BT_ASSERT(ev);
+               field = bt_ctf_event_get_payload(ev, "first");
+               BT_ASSERT(field);
+               ret = bt_ctf_field_integer_signed_set_value(field, -23 + i);
+               BT_ASSERT(ret == 0);
+               bt_ctf_object_put_ref(field);
+               field = bt_ctf_event_get_payload(ev, "second");
+               BT_ASSERT(field);
+               ret = bt_ctf_field_string_set_value(field, "saluuuut");
+               BT_ASSERT(ret == 0);
+               bt_ctf_object_put_ref(field);
+               ret = bt_ctf_clock_set_time(cfg->clock, 3600 + i * 5000);
+               BT_ASSERT(ret == 0);
+               ret = bt_ctf_stream_append_event(cfg->stream, ev);
+               BT_ASSERT(ret == 0);
+               bt_ctf_object_put_ref(ev);
+       }
+
+       ret = bt_ctf_stream_flush(cfg->stream);
+       BT_ASSERT(ret == 0);
+}
+
+int main(int argc, char **argv)
+{
+       struct config cfg = {0};
+
+       BT_ASSERT(argc >= 2);
+       configure_writer(&cfg, argv[1]);
+       write_stream(&cfg);
+       fini_config(&cfg);
+       return 0;
+}
diff --git a/tests/plugins/src.ctf.fs/succeed/test_succeed.in b/tests/plugins/src.ctf.fs/succeed/test_succeed.in
new file mode 100644 (file)
index 0000000..74db85a
--- /dev/null
@@ -0,0 +1,55 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 Philippe Proulx <pproulx@efficios.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; only version 2
+# of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+# This test validates that a `src.ctf.fs` component successfully reads
+# specific CTF traces and creates the expected messages.
+#
+# Such CTF traces to open either exist (in `tests/ctf-traces/succeed`)
+# or are generated by this test using local trace generators.
+
+. "@abs_top_builddir@/tests/utils/diff.sh"
+
+this_dir_relative="tests/plugins/src.ctf.fs/succeed"
+this_dir_src="$BT_SRC_PATH/$this_dir_relative"
+this_dir_build="$BT_BUILD_PATH/$this_dir_relative"
+succeed_trace_dir="$BT_CTF_TRACES/succeed"
+
+test_ctf_common_details_args="-p with-trace-name=no,with-stream-name=no"
+
+test_ctf_gen_single() {
+       name="$1"
+
+       diag "Generating trace '$name'"
+       bt_diff_details_ctf_gen_single "$this_dir_build/gen-trace-$name" \
+               "$this_dir_src/trace-$name.expect" \
+               "$test_ctf_common_details_args -p with-uuid=no"
+       ok $? "Generated trace '$name' gives the expected output"
+}
+
+test_ctf_single() {
+       name="$1"
+
+       bt_diff_details_ctf_single "$succeed_trace_dir/$name" \
+               "$this_dir_src/trace-$name.expect" "$test_ctf_common_details_args"
+       ok $? "Trace '$name' gives the expected output"
+}
+
+plan_tests 2
+
+test_ctf_gen_single simple
+test_ctf_single smalltrace
diff --git a/tests/plugins/src.ctf.fs/succeed/trace-simple.expect b/tests/plugins/src.ctf.fs/succeed/trace-simple.expect
new file mode 100644 (file)
index 0000000..90218c2
--- /dev/null
@@ -0,0 +1,218 @@
+Trace class:
+  Stream class (ID 0):
+    Packets have beginning default clock snapshot: Yes
+    Packets have end default clock snapshot: Yes
+    Supports discarded events: Yes
+    Discarded events have default clock snapshots: Yes
+    Supports discarded packets: No
+    Discarded packets have default clock snapshots: No
+    Default clock class:
+      Name: default
+      Frequency (Hz): 1,000,000,000
+      Precision (cycles): 1
+      Offset (s): 0
+      Offset (cycles): 0
+      Origin is Unix epoch: No
+    Event class `ev` (ID 0):
+      Payload field class: Structure (2 members):
+        first: Signed integer (8-bit, Base 10)
+        second: String
+
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+[Unknown]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream activity beginning
+
+[0 cycles, 0 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Packet beginning:
+
+[3600 cycles, 3600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -23
+    second: saluuuut
+
+[8600 cycles, 8600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -22
+    second: saluuuut
+
+[13,600 cycles, 13,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -21
+    second: saluuuut
+
+[18,600 cycles, 18,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -20
+    second: saluuuut
+
+[23,600 cycles, 23,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -19
+    second: saluuuut
+
+[28,600 cycles, 28,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -18
+    second: saluuuut
+
+[33,600 cycles, 33,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -17
+    second: saluuuut
+
+[38,600 cycles, 38,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -16
+    second: saluuuut
+
+[43,600 cycles, 43,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -15
+    second: saluuuut
+
+[48,600 cycles, 48,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -14
+    second: saluuuut
+
+[53,600 cycles, 53,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -13
+    second: saluuuut
+
+[58,600 cycles, 58,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -12
+    second: saluuuut
+
+[63,600 cycles, 63,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -11
+    second: saluuuut
+
+[68,600 cycles, 68,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -10
+    second: saluuuut
+
+[73,600 cycles, 73,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -9
+    second: saluuuut
+
+[78,600 cycles, 78,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -8
+    second: saluuuut
+
+[83,600 cycles, 83,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -7
+    second: saluuuut
+
+[88,600 cycles, 88,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -6
+    second: saluuuut
+
+[93,600 cycles, 93,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -5
+    second: saluuuut
+
+[98,600 cycles, 98,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -4
+    second: saluuuut
+
+[103,600 cycles, 103,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -3
+    second: saluuuut
+
+[108,600 cycles, 108,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -2
+    second: saluuuut
+
+[113,600 cycles, 113,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: -1
+    second: saluuuut
+
+[118,600 cycles, 118,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: 0
+    second: saluuuut
+
+[123,600 cycles, 123,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `ev` (Class ID 0):
+  Payload:
+    first: 1
+    second: saluuuut
+
+[123,600 cycles, 123,600 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Packet end
+
+[Unknown]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream activity end
+
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
diff --git a/tests/plugins/src.ctf.fs/succeed/trace-smalltrace.expect b/tests/plugins/src.ctf.fs/succeed/trace-smalltrace.expect
new file mode 100644 (file)
index 0000000..5140490
--- /dev/null
@@ -0,0 +1,45 @@
+Trace class:
+  UUID: 2a6422d0-6cee-11e0-8c08-cb07d7b3a564
+  Stream class (ID 0):
+    Packets have beginning default clock snapshot: No
+    Packets have end default clock snapshot: No
+    Supports discarded events: No
+    Discarded events have default clock snapshots: No
+    Supports discarded packets: No
+    Discarded packets have default clock snapshots: No
+    Event class `string` (ID 0):
+      Payload field class: Structure (1 member):
+        str: String
+
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Trace:
+    Class UUID: 2a6422d0-6cee-11e0-8c08-cb07d7b3a564
+    Stream (ID 0, Class ID 0)
+
+[Unknown]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream activity beginning
+
+{Trace 0, Stream class ID 0, Stream ID 0}
+Packet beginning:
+
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `string` (Class ID 0):
+  Payload:
+    str: This is a test trace
+
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `string` (Class ID 0):
+  Payload:
+    str: with only two small events.
+
+{Trace 0, Stream class ID 0, Stream ID 0}
+Packet end
+
+[Unknown]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream activity end
+
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
diff --git a/tests/utils/diff.sh.in b/tests/utils/diff.sh.in
new file mode 100644 (file)
index 0000000..57f88e1
--- /dev/null
@@ -0,0 +1,101 @@
+#!/bin/sh
+
+# Copyright (C) 2019 - Philippe Proulx <pproulx@efficios.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; only version 2
+# of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+. "@abs_top_builddir@/tests/utils/common.sh"
+
+# Checks the difference between:
+#
+# 1. What the CLI outputs when given the arguments "$1" (passed to
+#    `xargs`, so they can include quoted arguments).
+# 2. The file with path "$2".
+#
+# Returns 0 if there's no difference, and 1 if there is, also printing
+# said difference to the standard error.
+bt_diff_cli() {
+       args="$1"
+       expected_file="$2"
+       temp_output_file="$(mktemp)"
+       temp_diff="$(mktemp)"
+
+       # Run the CLI to get a detailed file
+       echo "$args" | xargs "$BT_BIN" 2>/dev/null >"$temp_output_file"
+
+       # Compare output with expected output
+       if ! diff "$temp_output_file" "$expected_file" 2>/dev/null >"$temp_diff"; then
+               echo "ERROR: for '$args': actual and expected outputs differ:" >&2
+               cat "$temp_diff" >&2
+               rm -rf "$temp_output_file" "$temp_diff"
+               return 1
+       fi
+
+       rm -f "$temp_output_file" "$temp_diff"
+}
+
+# Checks the difference between:
+#
+# 1. What the CLI outputs when given the arguments:
+#
+#        "$1" -c sink.text.details $3
+#
+# 2. The file with path "$2".
+#
+# Parameter 3 is optional.
+#
+# Returns 0 if there's no difference, and 1 if there is, also printing
+# said difference to the standard error.
+bt_diff_details_ctf_single() {
+       trace_dir="$1"
+       expected_file="$2"
+       extra_details_args=""
+
+       if [ $# -ge 3 ]; then
+               extra_details_args="$3"
+       fi
+
+       # Compare using the CLI with `sink.text.details`
+       bt_diff_cli "\"$trace_dir\" -c sink.text.details $extra_details_args" "$expected_file"
+}
+
+# Calls bt_diff_details_ctf_single(), except that "$1" is the path to a
+# program which generates the CTF trace to compare to. The program "$1"
+# receives the path to a temporary, empty directory where to write the
+# CTF trace as its first argument.
+bt_diff_details_ctf_gen_single() {
+       ctf_gen_prog_path="$1"
+       expected_file="$2"
+       extra_details_args=""
+
+       if [ $# -ge 3 ]; then
+               extra_details_args="$3"
+       fi
+
+       temp_trace_dir="$(mktemp -d)"
+
+       # Run the CTF trace generator program to get a CTF trace
+       if ! "$ctf_gen_prog_path" "$temp_trace_dir" 2>/dev/null; then
+               echo "ERROR: \"$ctf_gen_prog_path\" \"$temp_trace_dir\" failed" >&2
+               rm -rf "$temp_trace_dir"
+               return 1
+       fi
+
+       # Compare using the CLI with `sink.text.details`
+       bt_diff_details_ctf_single "$temp_trace_dir" "$expected_file" "$extra_details_args"
+       ret=$?
+       rm -rf "$temp_trace_dir"
+       return $ret
+}
This page took 0.039168 seconds and 4 git commands to generate.