summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2020-04-02 13:14:31 -0400
committerNat Goodspeed <nat@lindenlab.com>2020-04-03 10:46:17 -0400
commit962ccb4f01f220850fea35e32c3d92a718d35631 (patch)
tree44e1b62b50c0677754253ab6858f38885b885d48 /indra
parentdc07509f296661cf7a4d4bceb88a1a897757de98 (diff)
DRTVWR-476: Facilitate debugging test programs with logging.
On Mac, even if you run a test program with --debug or set LOGTEST=DEBUG, it won't log to stderr if you're filtering build output or running the build in an emacs compile buffer. This is because, on Mac, a viewer launched by mouse rather than from the command line is passed a stderr stream that ultimately gets logged to the system Console. The shouldLogToStderr() function is intended to avoid spamming the Console with the (voluminous) viewer log output. It tests whether stderr isatty() and, if not, suppresses calling LLError::logToStderr(). This makes debugging test programs using log output trickier than necessary. Change shouldLogToStderr() to permit logging when either stderr isatty() or is a pipe. The original intention is preserved in that empirically, a viewer launched by mouse is passed a stderr stream identified as a character device rather than as a pipe. Also introduce SetEnv, a class that facilitates setting (e.g.) LOGTEST=DEBUG for specific test programs without setting it for all test programs in the build. Using the constructor for a static object means you can set environment variables before main() is entered, which is important because it's the main() function in test.cpp that acts on the LOGTEST and LOGFAIL environment variables. These changes make it unnecessary to retain the temporary change in test.cpp to force LOGTEST to DEBUG.
Diffstat (limited to 'indra')
-rw-r--r--indra/llcommon/llerror.cpp43
-rw-r--r--indra/test/setenv.h66
-rw-r--r--indra/test/test.cpp3
3 files changed, 96 insertions, 16 deletions
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index 1cb93d27f7..0daae96cca 100644
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -39,6 +39,7 @@
#if !LL_WINDOWS
# include <syslog.h>
# include <unistd.h>
+# include <sys/stat.h>
#endif // !LL_WINDOWS
#include <vector>
#include "string.h"
@@ -654,22 +655,38 @@ namespace LLError
namespace
{
- bool shouldLogToStderr()
- {
+ bool shouldLogToStderr()
+ {
#if LL_DARWIN
- // On Mac OS X, stderr from apps launched from the Finder goes to the
- // console log. It's generally considered bad form to spam too much
- // there.
-
- // If stderr is a tty, assume the user launched from the command line or
- // debugger and therefore wants to see stderr. Otherwise, assume we've
- // been launched from the finder and shouldn't spam stderr.
- return isatty(STDERR_FILENO);
+ // On Mac OS X, stderr from apps launched from the Finder goes to the
+ // console log. It's generally considered bad form to spam too much
+ // there. That scenario can be detected by noticing that stderr is a
+ // character device (S_IFCHR).
+
+ // If stderr is a tty or a pipe, assume the user launched from the
+ // command line or debugger and therefore wants to see stderr.
+ if (isatty(STDERR_FILENO))
+ return true;
+ // not a tty, but might still be a pipe -- check
+ struct stat st;
+ if (fstat(STDERR_FILENO, &st) < 0)
+ {
+ // capture errno right away, before engaging any other operations
+ auto errno_save = errno;
+ // this gets called during log-system setup -- can't log yet!
+ std::cerr << "shouldLogToStderr: fstat(" << STDERR_FILENO << ") failed, errno "
+ << errno_save << std::endl;
+ // if we can't tell, err on the safe side and don't write stderr
+ return false;
+ }
+
+ // fstat() worked: return true only if stderr is a pipe
+ return ((st.st_mode & S_IFMT) == S_IFIFO);
#else
- return true;
+ return true;
#endif
- }
-
+ }
+
bool stderrLogWantsTime()
{
#if LL_WINDOWS
diff --git a/indra/test/setenv.h b/indra/test/setenv.h
new file mode 100644
index 0000000000..ed2de9ccca
--- /dev/null
+++ b/indra/test/setenv.h
@@ -0,0 +1,66 @@
+/**
+ * @file setenv.h
+ * @author Nat Goodspeed
+ * @date 2020-04-01
+ * @brief Provide a way for a particular test program to alter the
+ * environment before entry to main().
+ *
+ * $LicenseInfo:firstyear=2020&license=viewerlgpl$
+ * Copyright (c) 2020, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_SETENV_H)
+#define LL_SETENV_H
+
+#include <stdlib.h> // setenv()
+
+/**
+ * Our test.cpp main program responds to environment variables LOGTEST and
+ * LOGFAIL. But if you set (e.g.) LOGTEST=DEBUG before a viewer build, @em
+ * every test program in the build emits debug log output. This can be so
+ * voluminous as to slow down the build.
+ *
+ * With an integration test program, you can specifically build (e.g.) the
+ * INTEGRATION_TEST_llstring target, and set any environment variables you
+ * want for that. But with a unit test program, since executing the program is
+ * a side effect rather than an explicit target, specifically building (e.g.)
+ * PROJECT_lllogin_TEST_lllogin only builds the executable without running it.
+ *
+ * To set an environment variable for a particular test program, declare a
+ * static instance of SetEnv in its .cpp file. SetEnv's constructor takes
+ * pairs of strings, e.g.
+ *
+ * @code
+ * static SetEnv sLOGGING("LOGTEST", "INFO");
+ * @endcode
+ *
+ * Declaring a static instance of SetEnv is important because that ensures
+ * that the environment variables are set before main() is entered, since it
+ * is main() that examines LOGTEST and LOGFAIL.
+ */
+struct SetEnv
+{
+ // degenerate constructor, terminate recursion
+ SetEnv() {}
+
+ /**
+ * SetEnv() accepts an arbitrary number of pairs of strings: variable
+ * name, value, variable name, value ... Entering the constructor sets
+ * those variables in the process environment using Posix setenv(),
+ * overriding any previous value. If static SetEnv declarations in
+ * different translation units specify overlapping sets of variable names,
+ * it is indeterminate which instance will "win."
+ */
+ template <typename VAR, typename VAL, typename... ARGS>
+ SetEnv(VAR&& var, VAL&& val, ARGS&&... rest):
+ // constructor forwarding handles the tail of the list
+ SetEnv(std::forward<ARGS>(rest)...)
+ {
+ // set just the first (variable, value) pair
+ // 1 means override previous value if any
+ setenv(std::forward<VAR>(var), std::forward<VAL>(val), 1);
+ }
+};
+
+#endif /* ! defined(LL_SETENV_H) */
diff --git a/indra/test/test.cpp b/indra/test/test.cpp
index ea54ba658e..87c4a8d8a3 100644
--- a/indra/test/test.cpp
+++ b/indra/test/test.cpp
@@ -544,9 +544,6 @@ int main(int argc, char **argv)
// LOGTEST overrides default, but can be overridden by --debug.
const char* LOGTEST = getenv("LOGTEST");
- // DELETE
- LOGTEST = "DEBUG";
-
// values used for options parsing
apr_status_t apr_err;
const char* opt_arg = NULL;