diff options
| -rw-r--r-- | indra/llcommon/llerror.cpp | 43 | ||||
| -rw-r--r-- | indra/test/setenv.h | 66 | ||||
| -rw-r--r-- | indra/test/test.cpp | 3 | 
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; | 
