b933a4c6ac
- New test included in check phase (as per upsteam) Signed-off-by: Jan Pokorný <jpokorny@redhat.com>
965 lines
35 KiB
Diff
965 lines
35 KiB
Diff
From d9ed1dbcd9d58cac8573b75e6db323757f39e27b Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= <jpokorny@redhat.com>
|
|
Date: Fri, 6 Oct 2017 17:17:26 +0200
|
|
Subject: [PATCH 3/6] tests: new sort of tests dubbed "functional", cover
|
|
linker vs. logging
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
These are for quick manual sanity checking, assuming the target audience
|
|
-- maintainers -- are clear on the context of use and the purpose
|
|
(perhaps with the help of static files for comparison and/or additional
|
|
checking harness, usually available through "make check", but not to be
|
|
confused with regular unit + broader tests). These test are meant to be
|
|
compiled on demand only, not during the standard building routine, for
|
|
which a trick leveraging GNUmakefile-Makefile precedence with GNU make
|
|
was devised.
|
|
|
|
The respective new tests are meant to simulate logging variants in two
|
|
different library consumption models:
|
|
a. regular: linking against system-wide library
|
|
b. developmental: consuming library from a local sub-checkout tree,
|
|
using libtool conventions and hence attaching the
|
|
library through libqb.la intermediate library
|
|
descriptor of libtool
|
|
and between up to three possibly affected logging system participants
|
|
(discrete compilation units):
|
|
1. libqb itself will emit log messages in boundary conditions or
|
|
for tracing purposes
|
|
2. client program that consumes libqb's logging API directly
|
|
3. ditto, but the client program furthermore links with a library
|
|
(referred to as "interlib") that itself exercises the logging
|
|
API (it's also linked with libqb) -- through induction, this
|
|
should cover whole class of N interlib cases
|
|
|
|
Especially the latter perspective makes for a test matrix to possibly
|
|
(hopefully) demonstrate a fix allowing to cope with the changed
|
|
behaviour of ld from binutils 2.29+ wrt. boundaries denoting symbols for
|
|
a (custom) orphan section that are no longer externally visible. Such
|
|
commit is in the pipeline...
|
|
|
|
Developmental consumption model (a.) is now also tested automatically
|
|
in Travis CI runs and as a part of %check within upstream-suggested
|
|
libqb.spec for RPM packaging, whereas the regular one (b.) serves as
|
|
a building block for new new log_test_mock.sh runner of said test matrix
|
|
-- it iterates through all the possible permutations of linker-imposed
|
|
implicit visibility of mentioned symbols between various affected
|
|
linkage sides (see 1. - 3. above) so as to demonstrate a/ the impact
|
|
of the problem (see table below), and subsequently b/ that the fix is
|
|
effective in all these situations (updated table will be provided as
|
|
well) once it lands.
|
|
|
|
Current state for this matrix, in which participants 1. - 3. map like:
|
|
1. ~ libqb(Y)
|
|
2. ~ "direct"
|
|
3. ~ libX(Y) [a.k.a. interlib]
|
|
and where "X(Y)" denotes "X linked with linker Y":
|
|
X(a) .. ld.bfd < 2.29
|
|
X(b) .. ld.bfd = 2.29 (and likely on),
|
|
goes like this:
|
|
|
|
+=========+=========+=========+=========+=========+=========+=========+
|
|
#client(x)# libqb(a) usage # libqb(b) usage #
|
|
# vvv #---------+---------+---------+---------+---------+---------+
|
|
# V # direct | libX(a) : libX(b) # direct | libX(a) : libX(b) #
|
|
+=========+=========+=========+=========+=========+=========+=========+
|
|
# x = a # OK | OK : BAD[*2] # BAD[*1] | BAD[*D] : BAD[*3] #
|
|
# x = b # BAD[*A] | BAD[*B] : BAD[*C] # BAD[*1] | BAD[*C] : BAD[*3] #
|
|
+=========+=========+=========+=========+=========+=========+=========+
|
|
|
|
[*1] client logging not working
|
|
[*2] interlib logging not working
|
|
[*3] both client and interlib logging not working
|
|
|
|
[*A] boils down to [*1], unless QB_LOG_INIT_DATA used on client side,
|
|
which fails on 'non-empty callsite section' assertion
|
|
[*B] boils down to [*1], unless QB_LOG_INIT_DATA used on interlib side,
|
|
which fails on 'non-empty callsite section' assertion
|
|
[*C] boils down to [*3], unless QB_LOG_INIT_DATA used on interlib side,
|
|
which fails on 'non-empty callsite section' assertion
|
|
[*D] boils down to [*3], unless QB_LOG_INIT_DATA used on interlib side,
|
|
which makes it boil down just to [*1] (hypothesis: mere internal
|
|
self-reference to the section's boundary symbols makes them
|
|
overcome some kind of garbage collection at the linkage stage,
|
|
so they are exposed even they wouldn't be otherwise as demonstrated
|
|
with the initial, plain case of [*3])
|
|
|
|
Note: as observed with [*D] case (libqb linked with ld.bfd < 2.29
|
|
whereas interlib and its client linked with ld.bfd = 2.29), the exact
|
|
availability of a working logging doesn't depend solely on the linkers
|
|
in question, but generally (further investigation out of scope) the
|
|
conclusion is that when 2.29+ ld.bfd is involved somewhere in the chain
|
|
of logging-related discrete compilation units, also (self-)referencing
|
|
of the section's boundary denoting symbols is a deciding factor whether
|
|
particular logging source will be honored. This may be a result of
|
|
some internal linkage garbage collection mechanisms involved.
|
|
Anyway, it is supposed that the fix to broken-by-linkage logging can be
|
|
proclaimed complete once all combinations pass barring QB_LOG_INIT_DATA
|
|
usage (incurring the mentioned active referential use of the symbols),
|
|
along with a spin using it everywhere for good measure.
|
|
|
|
Signed-off-by: Jan Pokorný <jpokorny@redhat.com>
|
|
---
|
|
configure.ac | 7 +-
|
|
libqb.spec.in | 3 +-
|
|
tests/Makefile.am | 3 +
|
|
tests/functional/GNUmakefile | 20 ++
|
|
tests/functional/Makefile.am | 23 +++
|
|
tests/functional/log.am | 49 +++++
|
|
tests/functional/log_client.c | 86 ++++++++
|
|
tests/functional/log_client.err | 2 +
|
|
tests/functional/log_external/Makefile.am | 23 +++
|
|
tests/functional/log_interlib.c | 68 +++++++
|
|
tests/functional/log_interlib_client.c | 64 ++++++
|
|
tests/functional/log_interlib_client.err | 4 +
|
|
tests/functional/log_internal/Makefile.am | 23 +++
|
|
tests/functional/log_test_client.sh | 22 +++
|
|
tests/functional/log_test_interlib_client.sh | 22 +++
|
|
tests/functional/log_test_mock.sh | 283 +++++++++++++++++++++++++++
|
|
18 files changed, 700 insertions(+), 2 deletions(-)
|
|
create mode 100644 tests/functional/GNUmakefile
|
|
create mode 100644 tests/functional/Makefile.am
|
|
create mode 100644 tests/functional/log.am
|
|
create mode 100644 tests/functional/log_client.c
|
|
create mode 100644 tests/functional/log_client.err
|
|
create mode 100644 tests/functional/log_external/Makefile.am
|
|
create mode 100644 tests/functional/log_interlib.c
|
|
create mode 100644 tests/functional/log_interlib_client.c
|
|
create mode 100644 tests/functional/log_interlib_client.err
|
|
create mode 100644 tests/functional/log_internal/Makefile.am
|
|
create mode 100755 tests/functional/log_test_client.sh
|
|
create mode 100755 tests/functional/log_test_interlib_client.sh
|
|
create mode 100755 tests/functional/log_test_mock.sh
|
|
|
|
diff --git a/configure.ac b/configure.ac
|
|
index 727b8f0..db7bc68 100644
|
|
--- a/configure.ac
|
|
+++ b/configure.ac
|
|
@@ -13,7 +13,7 @@ AC_CONFIG_SRCDIR([lib/ringbuffer.c])
|
|
AC_CONFIG_HEADERS([include/config.h include/qb/qbconfig.h])
|
|
AC_USE_SYSTEM_EXTENSIONS
|
|
|
|
-AM_INIT_AUTOMAKE([-Wno-portability dist-xz])
|
|
+AM_INIT_AUTOMAKE([-Wno-portability dist-xz subdir-objects])
|
|
dnl automake >= 1.11 offers --enable-silent-rules for suppressing the output from
|
|
dnl normal compilation. When a failure occurs, it will then display the full
|
|
dnl command line
|
|
@@ -715,6 +715,9 @@ AC_CONFIG_FILES([Makefile
|
|
lib/libqb.pc
|
|
tools/Makefile
|
|
tests/Makefile
|
|
+ tests/functional/Makefile
|
|
+ tests/functional/log_external/Makefile
|
|
+ tests/functional/log_internal/Makefile
|
|
tests/test.conf
|
|
examples/Makefile
|
|
docs/Makefile
|
|
@@ -722,6 +725,8 @@ AC_CONFIG_FILES([Makefile
|
|
docs/html.dox
|
|
docs/man.dox])
|
|
|
|
+AC_CONFIG_LINKS([tests/functional/GNUmakefile:tests/functional/GNUmakefile])
|
|
+
|
|
AC_OUTPUT
|
|
|
|
AC_MSG_RESULT([])
|
|
diff --git a/libqb.spec.in b/libqb.spec.in
|
|
index 8320982..cd7fd0f 100644
|
|
--- a/libqb.spec.in
|
|
+++ b/libqb.spec.in
|
|
@@ -31,7 +31,8 @@ make %{?_smp_mflags}
|
|
|
|
%if 0%{?with_check}
|
|
%check
|
|
-VERBOSE=1 make check
|
|
+make V=1 check \
|
|
+ && make -C tests/functional/log_internal V=1 check
|
|
%endif
|
|
|
|
%install
|
|
diff --git a/tests/Makefile.am b/tests/Makefile.am
|
|
index fe54741..df1af81 100644
|
|
--- a/tests/Makefile.am
|
|
+++ b/tests/Makefile.am
|
|
@@ -20,6 +20,9 @@
|
|
MAINTAINERCLEANFILES = Makefile.in
|
|
EXTRA_DIST =
|
|
CLEANFILES =
|
|
+
|
|
+SUBDIRS = functional
|
|
+
|
|
AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
|
|
|
|
noinst_PROGRAMS = bmc bmcpt bms rbreader rbwriter \
|
|
diff --git a/tests/functional/GNUmakefile b/tests/functional/GNUmakefile
|
|
new file mode 100644
|
|
index 0000000..1b9de81
|
|
--- /dev/null
|
|
+++ b/tests/functional/GNUmakefile
|
|
@@ -0,0 +1,20 @@
|
|
+all check:
|
|
+ # do not trigger automatically, it's for on-demand use
|
|
+ @echo "Use 'make $@' within particular subdirectories"
|
|
+install: force
|
|
+ # definitely not desired to install anything from this subtree
|
|
+distclean:
|
|
+ # following is a nasty hack to keep "make distclean" succeeding
|
|
+ # (problem mostly arises from shared object files and hence shared
|
|
+ # compiler-generated makefile includes which are swiped when
|
|
+ # processing one subdir and missing as hard error for the other)
|
|
+ @$(MAKE) -C log_external $@
|
|
+ @mkdir .deps
|
|
+ @touch .deps/log_client.Po .deps/log_interlib.Plo .deps/log_interlib_client.Po
|
|
+ @$(MAKE) -C log_internal $@
|
|
+ @$(MAKE) -f Makefile $@ SUBDIRS=
|
|
+%: force
|
|
+ @$(MAKE) -f Makefile $@
|
|
+force: ;
|
|
+
|
|
+.PHONY: check distclean force install
|
|
diff --git a/tests/functional/Makefile.am b/tests/functional/Makefile.am
|
|
new file mode 100644
|
|
index 0000000..72f551e
|
|
--- /dev/null
|
|
+++ b/tests/functional/Makefile.am
|
|
@@ -0,0 +1,23 @@
|
|
+# Copyright 2017 Red Hat, Inc.
|
|
+#
|
|
+# Authors: Jan Pokorny <jpokorny@redhat.com>
|
|
+#
|
|
+# This file is part of libqb.
|
|
+#
|
|
+# libqb is free software: you can redistribute it and/or modify
|
|
+# it under the terms of the GNU Lesser General Public License as published by
|
|
+# the Free Software Foundation, either version 2.1 of the License, or
|
|
+# (at your option) any later version.
|
|
+#
|
|
+# libqb 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 Lesser General Public License for more details.
|
|
+#
|
|
+# You should have received a copy of the GNU Lesser General Public License
|
|
+# along with libqb. If not, see <http://www.gnu.org/licenses/>.
|
|
+
|
|
+MAINTAINERCLEANFILES = Makefile.in
|
|
+EXTRA_DIST = GNUmakefile log_client.err log_interlib_client.err \
|
|
+ log_test_client.sh log_test_interlib_client.sh log_test_mock.sh
|
|
+SUBDIRS = log_external log_internal
|
|
diff --git a/tests/functional/log.am b/tests/functional/log.am
|
|
new file mode 100644
|
|
index 0000000..5fe1ae7
|
|
--- /dev/null
|
|
+++ b/tests/functional/log.am
|
|
@@ -0,0 +1,49 @@
|
|
+# Copyright 2017 Red Hat, Inc.
|
|
+#
|
|
+# Author: Jan Pokorny <jpokorny@redhat.com>
|
|
+#
|
|
+# This file is part of libqb.
|
|
+#
|
|
+# libqb is free software: you can redistribute it and/or modify
|
|
+# it under the terms of the GNU Lesser General Public License as published by
|
|
+# the Free Software Foundation, either version 2.1 of the License, or
|
|
+# (at your option) any later version.
|
|
+#
|
|
+# libqb 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 Lesser General Public License for more details.
|
|
+#
|
|
+# You should have received a copy of the GNU Lesser General Public License
|
|
+# along with libqb. If not, see <http://www.gnu.org/licenses/>.
|
|
+
|
|
+MAINTAINERCLEANFILES = Makefile.in
|
|
+CLEANFILES = log_client.err.real log_interlib_client.err.real
|
|
+
|
|
+AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
|
|
+
|
|
+noinst_PROGRAMS = log_client log_interlib_client
|
|
+# cannot use {check,noinst}_LTLIBRARIES because it leads to solely static lib
|
|
+# (this won't get installed anyway, thanks to GNUmakefile rule)
|
|
+lib_LTLIBRARIES = liblog_inter.la
|
|
+
|
|
+log_client_SOURCES = ../log_client.c
|
|
+# log_client_LDFLAGS/log_client_LDADD to be delivered by the base Makefile.am
|
|
+
|
|
+liblog_inter_la_SOURCES = ../log_interlib.c
|
|
+liblog_inter_la_LDFLAGS = -shared
|
|
+# liblog_inter_la_LIBADD to be delivered by the base Makefile.am
|
|
+
|
|
+log_interlib_client_SOURCES = ../log_interlib_client.c
|
|
+# this transitively shares link dependencies with liblog_inter.la itself
|
|
+log_interlib_client_LDADD = $(builddir)/liblog_inter.la
|
|
+
|
|
+# actual 'make check' auxiliary definitions
|
|
+TESTS = ../log_test_client.sh ../log_test_interlib_client.sh
|
|
+TEST_EXTENSIONS = .sh
|
|
+log_test.log: $(check_PROGRAMS)
|
|
+
|
|
+../log_test_client.log: log_client
|
|
+../log_test_interlib_client.log: log_interlib_client
|
|
+
|
|
+# vim: ft=automake
|
|
diff --git a/tests/functional/log_client.c b/tests/functional/log_client.c
|
|
new file mode 100644
|
|
index 0000000..aa6ebe1
|
|
--- /dev/null
|
|
+++ b/tests/functional/log_client.c
|
|
@@ -0,0 +1,86 @@
|
|
+/*
|
|
+ * Copyright 2017 Red Hat, Inc.
|
|
+ *
|
|
+ * All rights reserved.
|
|
+ *
|
|
+ * Author: Jan Pokorny <jpokorny@redhat.com>
|
|
+ *
|
|
+ * This file is part of libqb.
|
|
+ *
|
|
+ * libqb is free software: you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU Lesser General Public License as published by
|
|
+ * the Free Software Foundation, either version 2.1 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * libqb 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 Lesser General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU Lesser General Public License
|
|
+ * along with libqb. If not, see <http://www.gnu.org/licenses/>.
|
|
+ */
|
|
+#include "os_base.h"
|
|
+#include <qb/qblog.h>
|
|
+
|
|
+#ifndef NSELFCHECK
|
|
+QB_LOG_INIT_DATA(linker_contra_log);
|
|
+#endif
|
|
+
|
|
+static const char *
|
|
+my_tags_stringify(uint32_t tags)
|
|
+{
|
|
+ if (qb_bit_is_set(tags, QB_LOG_TAG_LIBQB_MSG_BIT)) {
|
|
+ return "libqb";
|
|
+ } else {
|
|
+ return "MAIN";
|
|
+ }
|
|
+}
|
|
+
|
|
+int32_t
|
|
+main(int32_t argc, char *argv[])
|
|
+{
|
|
+ int tmpfile_fd;
|
|
+ struct stat tmpfile_stat;
|
|
+ char *tmpfile_buf = strdup("linker-log-XXXXXX");
|
|
+
|
|
+ qb_log_init("linker-contra-log", LOG_USER, LOG_INFO);
|
|
+ qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
|
|
+ qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
|
|
+ QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
|
|
+ qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
|
|
+
|
|
+ qb_log_tags_stringify_fn_set(my_tags_stringify);
|
|
+ qb_log_format_set(QB_LOG_STDERR, "[%5g|%p] %f:%l:%b");
|
|
+
|
|
+#if 0
|
|
+ printf("--\n");
|
|
+ qb_log_callsites_dump();
|
|
+ printf("--\n");
|
|
+#endif
|
|
+
|
|
+ /* Casual test of "user-space" logging. */
|
|
+ qb_log(LOG_DEBUG, "hello");
|
|
+
|
|
+ /* And now of "library-space" logging, i.e., let libqb generated
|
|
+ an error message on its own behalf, first to see if it will be
|
|
+ logged at all, second if it will be distinguished properly.
|
|
+ The trigger here is as simple as trying to print non-existing
|
|
+ blackbox file. */
|
|
+ tmpfile_fd = mkstemp(tmpfile_buf);
|
|
+ if (tmpfile_fd == -1) {
|
|
+ qb_perror(LOG_ERR, "creating temporary file");
|
|
+ exit(EXIT_FAILURE);
|
|
+ }
|
|
+ unlink(tmpfile_buf);
|
|
+ close(tmpfile_fd);
|
|
+#if 0
|
|
+ if (stat(tmpfile_buf, &tmpfile_stat) == -1) {
|
|
+ qb_perror(LOG_ERR, "stat'ing nonexistent temporary file");
|
|
+ exit(EXIT_FAILURE);
|
|
+ }
|
|
+#endif
|
|
+ qb_log_blackbox_print_from_file(tmpfile_buf);
|
|
+ free(tmpfile_buf);
|
|
+ qb_log_fini();
|
|
+}
|
|
diff --git a/tests/functional/log_client.err b/tests/functional/log_client.err
|
|
new file mode 100644
|
|
index 0000000..0ae7665
|
|
--- /dev/null
|
|
+++ b/tests/functional/log_client.err
|
|
@@ -0,0 +1,2 @@
|
|
+[MAIN |debug] ../log_client.c:63:hello
|
|
+[libqb|error] log_blackbox.c:196:qb_log_blackbox_print_from_file: No such file or directory (2)
|
|
diff --git a/tests/functional/log_external/Makefile.am b/tests/functional/log_external/Makefile.am
|
|
new file mode 100644
|
|
index 0000000..36aa0af
|
|
--- /dev/null
|
|
+++ b/tests/functional/log_external/Makefile.am
|
|
@@ -0,0 +1,23 @@
|
|
+# Copyright 2017 Red Hat, Inc.
|
|
+#
|
|
+# Author: Jan Pokorny <jpokorny@redhat.com>
|
|
+#
|
|
+# This file is part of libqb.
|
|
+#
|
|
+# libqb is free software: you can redistribute it and/or modify
|
|
+# it under the terms of the GNU Lesser General Public License as published by
|
|
+# the Free Software Foundation, either version 2.1 of the License, or
|
|
+# (at your option) any later version.
|
|
+#
|
|
+# libqb 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 Lesser General Public License for more details.
|
|
+#
|
|
+# You should have received a copy of the GNU Lesser General Public License
|
|
+# along with libqb. If not, see <http://www.gnu.org/licenses/>.
|
|
+
|
|
+include ../log.am
|
|
+
|
|
+log_client_LDFLAGS = -lqb
|
|
+liblog_inter_la_LIBADD = -lqb
|
|
diff --git a/tests/functional/log_interlib.c b/tests/functional/log_interlib.c
|
|
new file mode 100644
|
|
index 0000000..498f82c
|
|
--- /dev/null
|
|
+++ b/tests/functional/log_interlib.c
|
|
@@ -0,0 +1,68 @@
|
|
+/*
|
|
+ * Copyright 2017 Red Hat, Inc.
|
|
+ *
|
|
+ * All rights reserved.
|
|
+ *
|
|
+ * Author: Jan Pokorny <jpokorny@redhat.com>
|
|
+ *
|
|
+ * This file is part of libqb.
|
|
+ *
|
|
+ * libqb is free software: you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU Lesser General Public License as published by
|
|
+ * the Free Software Foundation, either version 2.1 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * libqb 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 Lesser General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU Lesser General Public License
|
|
+ * along with libqb. If not, see <http://www.gnu.org/licenses/>.
|
|
+ */
|
|
+#include "os_base.h"
|
|
+#include <qb/qblog.h>
|
|
+
|
|
+#ifndef NSELFCHECK
|
|
+QB_LOG_INIT_DATA(linker_contra_log_lib);
|
|
+#endif
|
|
+
|
|
+void foo(void);
|
|
+
|
|
+void
|
|
+foo(void)
|
|
+{
|
|
+ int tmpfile_fd;
|
|
+ struct stat tmpfile_stat;
|
|
+ char *tmpfile_buf = strdup("linker-log-XXXXXX");
|
|
+
|
|
+#if 0
|
|
+ printf("--\n");
|
|
+ qb_log_callsites_dump();
|
|
+ printf("--\n");
|
|
+#endif
|
|
+
|
|
+ /* Casual test of "user-space" logging. */
|
|
+ qb_log(LOG_INFO, "hello");
|
|
+
|
|
+ /* And now of "library-space" logging, i.e., let libqb generated
|
|
+ an error message on its own behalf, first to see if it will be
|
|
+ logged at all, second if it will be distinguished properly.
|
|
+ The trigger here is as simple as trying to print non-existing
|
|
+ blackbox file. */
|
|
+ tmpfile_fd = mkstemp(tmpfile_buf);
|
|
+ if (tmpfile_fd == -1) {
|
|
+ qb_perror(LOG_ERR, "creating temporary file");
|
|
+ exit(EXIT_FAILURE);
|
|
+ }
|
|
+ unlink(tmpfile_buf);
|
|
+ close(tmpfile_fd);
|
|
+#if 0
|
|
+ if (stat(tmpfile_buf, &tmpfile_stat) == -1) {
|
|
+ qb_perror(LOG_ERR, "stat'ing nonexistent temporary file");
|
|
+ exit(EXIT_FAILURE);
|
|
+ }
|
|
+#endif
|
|
+ qb_log_blackbox_print_from_file(tmpfile_buf);
|
|
+ free(tmpfile_buf);
|
|
+}
|
|
diff --git a/tests/functional/log_interlib_client.c b/tests/functional/log_interlib_client.c
|
|
new file mode 100644
|
|
index 0000000..c669703
|
|
--- /dev/null
|
|
+++ b/tests/functional/log_interlib_client.c
|
|
@@ -0,0 +1,64 @@
|
|
+/*
|
|
+ * Copyright 2017 Red Hat, Inc.
|
|
+ *
|
|
+ * All rights reserved.
|
|
+ *
|
|
+ * Author: Jan Pokorny <jpokorny@redhat.com>
|
|
+ *
|
|
+ * This file is part of libqb.
|
|
+ *
|
|
+ * libqb is free software: you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU Lesser General Public License as published by
|
|
+ * the Free Software Foundation, either version 2.1 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * libqb 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 Lesser General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU Lesser General Public License
|
|
+ * along with libqb. If not, see <http://www.gnu.org/licenses/>.
|
|
+ */
|
|
+#include "os_base.h"
|
|
+#include <qb/qblog.h>
|
|
+
|
|
+#ifndef NSELFCHECK
|
|
+QB_LOG_INIT_DATA(linker_contra_log_lib_user);
|
|
+#endif
|
|
+
|
|
+void foo(void);
|
|
+
|
|
+static const char *
|
|
+my_tags_stringify(uint32_t tags)
|
|
+{
|
|
+ if (qb_bit_is_set(tags, QB_LOG_TAG_LIBQB_MSG_BIT)) {
|
|
+ return "libqb";
|
|
+ } else {
|
|
+ return "MAIN";
|
|
+ }
|
|
+}
|
|
+
|
|
+int
|
|
+main(int argc, char *argv[])
|
|
+{
|
|
+ qb_log_init("linker-contra-log", LOG_USER, LOG_INFO);
|
|
+ qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
|
|
+ qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
|
|
+ QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
|
|
+ qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
|
|
+
|
|
+ qb_log_tags_stringify_fn_set(my_tags_stringify);
|
|
+ qb_log_format_set(QB_LOG_STDERR, "[%5g|%p] %f:%l:%b");
|
|
+
|
|
+#if 0
|
|
+ printf("--\n");
|
|
+ qb_log_callsites_dump();
|
|
+ printf("--\n");
|
|
+#endif
|
|
+
|
|
+ qb_log(LOG_INFO, "BEFORE");
|
|
+ foo();
|
|
+ qb_log(LOG_INFO, "AFTER");
|
|
+ qb_log_fini();
|
|
+}
|
|
diff --git a/tests/functional/log_interlib_client.err b/tests/functional/log_interlib_client.err
|
|
new file mode 100644
|
|
index 0000000..486071a
|
|
--- /dev/null
|
|
+++ b/tests/functional/log_interlib_client.err
|
|
@@ -0,0 +1,4 @@
|
|
+[MAIN |info] ../log_interlib_client.c:60:BEFORE
|
|
+[MAIN |info] ../log_interlib.c:46:hello
|
|
+[libqb|error] log_blackbox.c:196:qb_log_blackbox_print_from_file: No such file or directory (2)
|
|
+[MAIN |info] ../log_interlib_client.c:62:AFTER
|
|
diff --git a/tests/functional/log_internal/Makefile.am b/tests/functional/log_internal/Makefile.am
|
|
new file mode 100644
|
|
index 0000000..697cee2
|
|
--- /dev/null
|
|
+++ b/tests/functional/log_internal/Makefile.am
|
|
@@ -0,0 +1,23 @@
|
|
+# Copyright 2017 Red Hat, Inc.
|
|
+#
|
|
+# Author: Jan Pokorny <jpokorny@redhat.com>
|
|
+#
|
|
+# This file is part of libqb.
|
|
+#
|
|
+# libqb is free software: you can redistribute it and/or modify
|
|
+# it under the terms of the GNU Lesser General Public License as published by
|
|
+# the Free Software Foundation, either version 2.1 of the License, or
|
|
+# (at your option) any later version.
|
|
+#
|
|
+# libqb 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 Lesser General Public License for more details.
|
|
+#
|
|
+# You should have received a copy of the GNU Lesser General Public License
|
|
+# along with libqb. If not, see <http://www.gnu.org/licenses/>.
|
|
+
|
|
+include ../log.am
|
|
+
|
|
+log_client_LDADD = $(top_builddir)/lib/libqb.la
|
|
+liblog_inter_la_LIBADD = $(top_builddir)/lib/libqb.la
|
|
diff --git a/tests/functional/log_test_client.sh b/tests/functional/log_test_client.sh
|
|
new file mode 100755
|
|
index 0000000..875871e
|
|
--- /dev/null
|
|
+++ b/tests/functional/log_test_client.sh
|
|
@@ -0,0 +1,22 @@
|
|
+#!/bin/sh
|
|
+# Copyright 2017 Red Hat, Inc.
|
|
+#
|
|
+# Author: Jan Pokorny <jpokorny@redhat.com>
|
|
+#
|
|
+# This file is part of libqb.
|
|
+#
|
|
+# libqb is free software: you can redistribute it and/or modify
|
|
+# it under the terms of the GNU Lesser General Public License as published by
|
|
+# the Free Software Foundation, either version 2.1 of the License, or
|
|
+# (at your option) any later version.
|
|
+#
|
|
+# libqb 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 Lesser General Public License for more details.
|
|
+#
|
|
+# You should have received a copy of the GNU Lesser General Public License
|
|
+# along with libqb. If not, see <http://www.gnu.org/licenses/>.
|
|
+
|
|
+./log_client >/dev/null 2>log_client.err.real
|
|
+diff -u ../log_client.err log_client.err.real
|
|
diff --git a/tests/functional/log_test_interlib_client.sh b/tests/functional/log_test_interlib_client.sh
|
|
new file mode 100755
|
|
index 0000000..59c5555
|
|
--- /dev/null
|
|
+++ b/tests/functional/log_test_interlib_client.sh
|
|
@@ -0,0 +1,22 @@
|
|
+#!/bin/sh
|
|
+# Copyright 2017 Red Hat, Inc.
|
|
+#
|
|
+# Author: Jan Pokorny <jpokorny@redhat.com>
|
|
+#
|
|
+# This file is part of libqb.
|
|
+#
|
|
+# libqb is free software: you can redistribute it and/or modify
|
|
+# it under the terms of the GNU Lesser General Public License as published by
|
|
+# the Free Software Foundation, either version 2.1 of the License, or
|
|
+# (at your option) any later version.
|
|
+#
|
|
+# libqb 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 Lesser General Public License for more details.
|
|
+#
|
|
+# You should have received a copy of the GNU Lesser General Public License
|
|
+# along with libqb. If not, see <http://www.gnu.org/licenses/>.
|
|
+
|
|
+./log_interlib_client >/dev/null 2>log_interlib_client.err.real
|
|
+diff -u ../log_interlib_client.err log_interlib_client.err.real
|
|
diff --git a/tests/functional/log_test_mock.sh b/tests/functional/log_test_mock.sh
|
|
new file mode 100755
|
|
index 0000000..cc3519d
|
|
--- /dev/null
|
|
+++ b/tests/functional/log_test_mock.sh
|
|
@@ -0,0 +1,283 @@
|
|
+#!/bin/sh
|
|
+# Copyright 2017 Red Hat, Inc.
|
|
+#
|
|
+# Author: Jan Pokorny <jpokorny@redhat.com>
|
|
+#
|
|
+# This file is part of libqb.
|
|
+#
|
|
+# libqb is free software: you can redistribute it and/or modify
|
|
+# it under the terms of the GNU Lesser General Public License as published by
|
|
+# the Free Software Foundation, either version 2.1 of the License, or
|
|
+# (at your option) any later version.
|
|
+#
|
|
+# libqb 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 Lesser General Public License for more details.
|
|
+#
|
|
+# You should have received a copy of the GNU Lesser General Public License
|
|
+# along with libqb. If not, see <http://www.gnu.org/licenses/>.
|
|
+
|
|
+# Given the source RPM for libqb, this will run through the basic test matrix
|
|
+# so as to figure out the outcomes for particular linker (pre-2.29 and 2.29+
|
|
+# differing in visibility of orphan section delimiting boundary symbols and
|
|
+# hence possibly causing harm to the logging facility of libqb) being used
|
|
+# for particular part of the composite logging system (libqb itself,
|
|
+# it's direct client / (client library + it's own client that uses logging)
|
|
+# as well). While this is tailored to Fedora, it should be possible to
|
|
+# run this testsuite wherever following is present:
|
|
+#
|
|
+# - rpm (for parsing archive name embedded in libqb.src.rpm [note that
|
|
+# rpm2cpio is part of rpm package as well] because the extracted dir
|
|
+# follows the same naming, which we need to know)
|
|
+# - mock + dependencies + fedora-27-${arch} configuration for mock
|
|
+# (or whatever other configuration if some variables below are
|
|
+# changed appropriately)
|
|
+# - koji + dependencies (but binutils packages can be precached when
|
|
+# downloaded from https://koji.fedoraproject.org/ manually)
|
|
+# - internet connection (but see the above statement for koji, and
|
|
+# possibly the full package set within the build'n'test underlying
|
|
+# container can be precached without further details on "how")
|
|
+# - commons (coreutils, findutils, sed, ...)
|
|
+#
|
|
+# The testsuite will not mangle with your host system as mock spawns
|
|
+# it's somewhat private container for be worked with under the hood.
|
|
+#
|
|
+# Note that in order not to get mad when entering the root password anytime
|
|
+# mock is invoked, you can add the user initiating the test run to the
|
|
+# 'mock' group. Be aware of the associated security risks, though:
|
|
+# https://github.com/rpm-software-management/mock/wiki#setup
|
|
+
|
|
+set -eu
|
|
+
|
|
+# change following as suitable
|
|
+
|
|
+arch=x86_64
|
|
+mock_args="-r fedora-27-${arch}"
|
|
+pkg_binutils_228=binutils-2.28-14.fc27
|
|
+pkg_binutils_229=binutils-2.29-6.fc27
|
|
+
|
|
+#
|
|
+# prettified reporters
|
|
+#
|
|
+
|
|
+do_progress () { printf "\x1b[7m%s\x1b[0m\n" "$*"; }
|
|
+do_info () { printf "\x1b[36mINFO: %s\x1b[0m\n" "$*"; }
|
|
+do_warn () { printf "\x1b[31mWARNING: %s\x1b[0m\n" "$*"; }
|
|
+do_die () { printf "\x1b[31mFATAL: %s\x1b[0m\n" "$*"; exit 1; }
|
|
+
|
|
+#
|
|
+# actual building blocks
|
|
+#
|
|
+
|
|
+# $1, ... $N: packages (and possibly related subpackages) to be downloaded
|
|
+do_download () {
|
|
+ while test $# -gt 0; do
|
|
+ if test -d "_pkgs/$1" 2>/dev/null; then
|
|
+ do_info "$1 already downloaded"
|
|
+ shift; continue
|
|
+ fi
|
|
+ mkdir -p "_pkgs/$1"
|
|
+ ( cd "_pkgs/$1" && koji download-build --arch="${arch}" "$1" )
|
|
+ shift
|
|
+ done
|
|
+}
|
|
+
|
|
+# $1, ... $N: descriptors of packages to be installed
|
|
+do_install () {
|
|
+ while test $# -gt 0; do
|
|
+ if test -d "_pkgs/$1" 2>/dev/null; then
|
|
+ do_install_inner "_pkgs/$1"/*.rpm
|
|
+ else
|
|
+ do_warn "$1 is not downloaded, hence skipped"
|
|
+ fi
|
|
+ shift
|
|
+ done
|
|
+}
|
|
+
|
|
+# $1, ... $N: concrete packages to be installed
|
|
+do_install_inner () {
|
|
+ _remove_cmd="mock ${mock_args} -- shell \"rpm --nodeps --erase"
|
|
+ _install_cmd="mock ${mock_args}"
|
|
+ while test $# -gt 0; do
|
|
+ case "$1" in
|
|
+ *.src.rpm|*-debuginfo*|*-debugsource*) ;;
|
|
+ *)
|
|
+ _pkg_name="$(basename "$1" | sed 's|\(-[0-9].*\)||')"
|
|
+ _remove_cmd="${_remove_cmd} \'${_pkg_name}\'"
|
|
+ _install_cmd="${_install_cmd} --install \"$1\"";;
|
|
+ esac
|
|
+ shift
|
|
+ done
|
|
+ eval "${_remove_cmd}\"" || : # extra quotation mark intentional
|
|
+ eval "${_install_cmd}"
|
|
+}
|
|
+
|
|
+# $1: full path of srpm to be rebuilt
|
|
+# $2: %{dist} macro for rpmbuild (distinguishing the builds)
|
|
+do_buildsrpm () {
|
|
+ _pkg_descriptor="$(basename "$1" | sed 's|\.src\.rpm$||')"
|
|
+ # need to prune due to possible duplicates caused by differing %{dist}
|
|
+ rm -f -- "_pkgs/${_pkg_descriptor}"/*
|
|
+ mock ${mock_args} -Nn --define "dist $2" --define '_without_check 1' \
|
|
+ --resultdir "_pkgs/${_pkg_descriptor}" --rebuild "$1"
|
|
+}
|
|
+
|
|
+# $1: full path srpm to be rebuilt
|
|
+# $2: extra (presumably) variable assignments for the make goal invocation
|
|
+do_compile_interlib () {
|
|
+ mock ${mock_args} --shell \
|
|
+ "find \"builddir/build/BUILD/$1/tests/functional\" \
|
|
+ \( -name log_internal -o -name '*.c' \) -prune \
|
|
+ -o -name '*liblog_inter*' \
|
|
+ -exec rm -- {} \;"
|
|
+ mock ${mock_args} --shell "( cd \"builddir/build/BUILD/$1\"; ./configure )"
|
|
+ mock ${mock_args} --shell \
|
|
+ "make -C \"builddir/build/BUILD/$1/tests/functional/log_external\" \
|
|
+ liblog_inter.la $2"
|
|
+}
|
|
+
|
|
+# $1: full path srpm to be rebuilt
|
|
+# $2: which type of client to work with (client/interclient)
|
|
+# $3: base (on-host) directory for test results
|
|
+# $4: output file to capture particular test result
|
|
+# $5: extra (presumably) variable assignments for the make goal invocation
|
|
+do_compile_and_test_client () {
|
|
+ _result=$4
|
|
+ case "$2" in
|
|
+ interclient)
|
|
+ _logfile=log_test_interlib_client
|
|
+ mock ${mock_args} --shell \
|
|
+ "find \"builddir/build/BUILD/$1/tests/functional\" \
|
|
+ \( -name log_internal -o -name '*.err' -o -name '*.c' \) -prune \
|
|
+ -o \( -name '*log_interlib_client*' -o -name \"${_logfile}.log\" \) \
|
|
+ -exec rm -- {} \;"
|
|
+ ;;
|
|
+ client|*)
|
|
+ _logfile=log_test_client
|
|
+ mock ${mock_args} --shell \
|
|
+ "find \"builddir/build/BUILD/$1/tests/functional\" \
|
|
+ \( -name log_internal -o -name '*.err' -o -name '*.c' \) -prune \
|
|
+ -o \( -name '*log_client*' -o -name \"${_logfile}.log\" \) \
|
|
+ -exec rm -- {} \;"
|
|
+ ;;
|
|
+ esac
|
|
+ mock ${mock_args} --shell "( cd \"builddir/build/BUILD/$1\"; ./configure )"
|
|
+ mock ${mock_args} --shell \
|
|
+ "make -C \"builddir/build/BUILD/$1/tests/functional/log_external\" \
|
|
+ check-TESTS \"TESTS=../${_logfile}.sh\" $5" \
|
|
+ && _result="${_result}_good" \
|
|
+ || _result="${_result}_bad"
|
|
+ mock ${mock_args} --copyout \
|
|
+ "builddir/build/BUILD/$1/tests/functional/log_external/test-suite.log" \
|
|
+ "$3/${_result}"
|
|
+}
|
|
+
|
|
+do_shell () {
|
|
+ mock ${mock_args} --shell
|
|
+}
|
|
+
|
|
+# $1, ... $N: "argv"
|
|
+do_proceed () {
|
|
+
|
|
+ _makevars=
|
|
+ case "$1" in
|
|
+ shell) shift; do_shell "$@"; return;;
|
|
+ -n) shift; _makevars='CPPFLAGS=-DNSELFCHECK';;
|
|
+ esac
|
|
+
|
|
+ test -s "$1" || do_die "Not an input file: $1"
|
|
+ _libqb_descriptor_path="$1"
|
|
+ _libqb_descriptor="$(basename "${_libqb_descriptor_path}" \
|
|
+ | sed 's|\.src\.rpm$||')"
|
|
+ _libqb_descriptor_archive="$(rpm -q --qf '[%{FILENAMES}\n]' \
|
|
+ -p "${_libqb_descriptor_path}" \
|
|
+ | sed -n '/\.tar/{s|\.tar\.[^.]*$||;p;q}')"
|
|
+
|
|
+ _resultsdir="_results/$(date '+%y%m%d_%H%M%S')_${_libqb_descriptor}"
|
|
+ test -z "${_makevars}" || _resultsdir="${_resultsdir}_sc"
|
|
+ mkdir -p "${_resultsdir}"
|
|
+ rm -f -- "${_resultsdir}/*"
|
|
+
|
|
+ _dist=
|
|
+ _outfile=
|
|
+ _outfile_client=
|
|
+ _outfile_qb=
|
|
+
|
|
+ do_download "${pkg_binutils_228}" "${pkg_binutils_229}"
|
|
+
|
|
+ for _pkg_binutils_libqb in "${pkg_binutils_228}" "${pkg_binutils_229}"; do
|
|
+
|
|
+ case "${_pkg_binutils_libqb}" in
|
|
+ ${pkg_binutils_228}) _outfile_qb="qb+"; _dist=.binutils228;;
|
|
+ ${pkg_binutils_229}) _outfile_qb="qb-"; _dist=.binutils229;;
|
|
+ *) _outfile_qb="?";;
|
|
+ esac
|
|
+
|
|
+ do_progress "installing ${_pkg_binutils_libqb} so as to build" \
|
|
+ " libqb [${_outfile_qb}]"
|
|
+ do_install "${_pkg_binutils_libqb}"
|
|
+
|
|
+ do_progress "building ${_libqb_descriptor_path} with" \
|
|
+ " ${_pkg_binutils_libqb} [${_outfile_qb}]"
|
|
+ do_buildsrpm "${_libqb_descriptor_path}" "${_dist}"
|
|
+
|
|
+ do_progress "installing ${_libqb_descriptor}-based packages" \
|
|
+ " built with ${_pkg_binutils_libqb} [${_outfile_qb}]"
|
|
+ do_install "${_libqb_descriptor}"
|
|
+
|
|
+ for _pkg_binutils_interlib in none "${pkg_binutils_228}" "${pkg_binutils_229}"; do
|
|
+
|
|
+ case "${_pkg_binutils_interlib}" in
|
|
+ none) _outfile="${_outfile_qb}";;
|
|
+ ${pkg_binutils_228}) _outfile="${_outfile_qb}_il+";;
|
|
+ ${pkg_binutils_229}) _outfile="${_outfile_qb}_il-";;
|
|
+ *) _outfile="${_outfile_qb}_?";;
|
|
+ esac
|
|
+
|
|
+ case "${_pkg_binutils_interlib}" in
|
|
+ none) ;;
|
|
+ *)
|
|
+ do_progress "installing ${_pkg_binutils_interlib}" \
|
|
+ " so as to build interlib [${_outfile}]"
|
|
+ do_install "${_pkg_binutils_interlib}"
|
|
+
|
|
+ do_progress "building interlib with ${_libqb_descriptor_archive}" \
|
|
+ " + ${_pkg_binutils_interlib} [${_outfile}]"
|
|
+ do_compile_interlib "${_libqb_descriptor_archive}" "${_makevars}"
|
|
+ ;;
|
|
+ esac
|
|
+
|
|
+ for _pkg_binutils_client in "${pkg_binutils_228}" "${pkg_binutils_229}"; do
|
|
+
|
|
+ _client=client
|
|
+ test "${_pkg_binutils_interlib}" = none || _client=interclient
|
|
+
|
|
+ case "${_pkg_binutils_client}" in
|
|
+ ${pkg_binutils_228}) _outfile_client="${_outfile}_c+";;
|
|
+ ${pkg_binutils_229}) _outfile_client="${_outfile}_c-";;
|
|
+ *) _outfile_client="${_outfile}_?";;
|
|
+ esac
|
|
+
|
|
+ do_progress "installing ${_pkg_binutils_client}" \
|
|
+ " so as to build ${_client} [${_outfile_client}]"
|
|
+ do_install "${_pkg_binutils_client}"
|
|
+ do_progress "building ${_client} with ${_libqb_descriptor_archive}" \
|
|
+ " + ${_pkg_binutils_client} [${_outfile_client}]"
|
|
+ do_compile_and_test_client "${_libqb_descriptor_archive}" \
|
|
+ "${_client}" "${_resultsdir}" \
|
|
+ "${_outfile_client}" "${_makevars}"
|
|
+ done
|
|
+ done
|
|
+ done
|
|
+}
|
|
+
|
|
+test $# -eq 0 \
|
|
+ && printf '%s\n %s\n %s\n %s\n %s\n %s\n' \
|
|
+ "usage: $0 {[-n] <libqb.src.rpm> | shell}" \
|
|
+ "- use '-n' to suppress optional self-check (\"see whole story\")" \
|
|
+ "- 'make -C ../.. srpm' (or so) can generate the requested input" \
|
|
+ " (in that case, apparently, refer to '../../libqb-X.src.rpm')" \
|
|
+ "- _pkgs dir caches (intermediate or not) packages to work with" \
|
|
+ "- results stored in '_results/<timestamp>_<input_name>[_<tag>]'" \
|
|
+ || do_proceed "$@"
|
|
--
|
|
2.14.2
|
|
|