Commit 72d2bd0c by Shahbaz Youssefi Committed by Angle LUCI CQ

Allow capturing process stdout and stderr interleaved

The test utils are enhanced to allow redirecting stderr to stdout. This is in preparation for a change that makes the test runner capture stderr together with stdout. Currently, on failure logs originating from UNIMPLEMENTED() and other such macros are not captured. Bug: angleproject:6077 Change-Id: I7a3c6c4732a59dac3ff0cc20a7835d5ed6f0f22e Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2976183Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
parent dbfc119a
...@@ -150,7 +150,7 @@ class GLMark2Benchmark : public testing::TestWithParam<GLMark2TestParams> ...@@ -150,7 +150,7 @@ class GLMark2Benchmark : public testing::TestWithParam<GLMark2TestParams>
} }
args.push_back(nullptr); args.push_back(nullptr);
ProcessHandle process(args, true, false); ProcessHandle process(args, ProcessOutputCapture::StdoutOnly);
ASSERT_TRUE(process && process->started()); ASSERT_TRUE(process && process->started());
ASSERT_TRUE(process->finish()); ASSERT_TRUE(process->finish());
......
...@@ -1433,7 +1433,7 @@ bool TestSuite::launchChildTestProcess(uint32_t batchId, ...@@ -1433,7 +1433,7 @@ bool TestSuite::launchChildTestProcess(uint32_t batchId,
} }
// Launch child process and wait for completion. // Launch child process and wait for completion.
processInfo.process = LaunchProcess(args, true, true); processInfo.process = LaunchProcess(args, ProcessOutputCapture::StdoutAndStderrSeparately);
if (!processInfo.process->started()) if (!processInfo.process->started())
{ {
......
...@@ -82,7 +82,7 @@ class TestSuiteTest : public testing::Test ...@@ -82,7 +82,7 @@ class TestSuiteTest : public testing::Test
printf("\n"); printf("\n");
} }
ProcessHandle process(args, true, true); ProcessHandle process(args, ProcessOutputCapture::StdoutAndStderrSeparately);
EXPECT_TRUE(process->started()); EXPECT_TRUE(process->started());
EXPECT_TRUE(process->finish()); EXPECT_TRUE(process->finish());
EXPECT_TRUE(process->finished()); EXPECT_TRUE(process->finished());
......
...@@ -106,16 +106,22 @@ class PosixProcess : public Process ...@@ -106,16 +106,22 @@ class PosixProcess : public Process
{ {
public: public:
PosixProcess(const std::vector<const char *> &commandLineArgs, PosixProcess(const std::vector<const char *> &commandLineArgs,
bool captureStdOut, ProcessOutputCapture captureOutput)
bool captureStdErr)
{ {
if (commandLineArgs.empty()) if (commandLineArgs.empty())
{ {
return; return;
} }
const bool captureStdout = captureOutput != ProcessOutputCapture::Nothing;
const bool captureStderr =
captureOutput == ProcessOutputCapture::StdoutAndStderrInterleaved ||
captureOutput == ProcessOutputCapture::StdoutAndStderrSeparately;
const bool pipeStderrToStdout =
captureOutput == ProcessOutputCapture::StdoutAndStderrInterleaved;
// Create pipes for stdout and stderr. // Create pipes for stdout and stderr.
if (captureStdOut) if (captureStdout)
{ {
if (pipe(mStdoutPipe.fds) != 0) if (pipe(mStdoutPipe.fds) != 0)
{ {
...@@ -128,7 +134,7 @@ class PosixProcess : public Process ...@@ -128,7 +134,7 @@ class PosixProcess : public Process
return; return;
} }
} }
if (captureStdErr) if (captureStderr && !pipeStderrToStdout)
{ {
if (pipe(mStderrPipe.fds) != 0) if (pipe(mStderrPipe.fds) != 0)
{ {
...@@ -156,7 +162,7 @@ class PosixProcess : public Process ...@@ -156,7 +162,7 @@ class PosixProcess : public Process
// Child. Execute the application. // Child. Execute the application.
// Redirect stdout and stderr to the pipe fds. // Redirect stdout and stderr to the pipe fds.
if (captureStdOut) if (captureStdout)
{ {
if (dup2(mStdoutPipe.fds[1], STDOUT_FILENO) < 0) if (dup2(mStdoutPipe.fds[1], STDOUT_FILENO) < 0)
{ {
...@@ -164,7 +170,14 @@ class PosixProcess : public Process ...@@ -164,7 +170,14 @@ class PosixProcess : public Process
} }
mStdoutPipe.closeEndPoint(1); mStdoutPipe.closeEndPoint(1);
} }
if (captureStdErr) if (pipeStderrToStdout)
{
if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
{
_exit(errno);
}
}
else if (captureStderr)
{ {
if (dup2(mStderrPipe.fds[1], STDERR_FILENO) < 0) if (dup2(mStderrPipe.fds[1], STDERR_FILENO) < 0)
{ {
...@@ -418,11 +431,9 @@ bool DeleteFile(const char *path) ...@@ -418,11 +431,9 @@ bool DeleteFile(const char *path)
return unlink(path) == 0; return unlink(path) == 0;
} }
Process *LaunchProcess(const std::vector<const char *> &args, Process *LaunchProcess(const std::vector<const char *> &args, ProcessOutputCapture captureOutput)
bool captureStdout,
bool captureStderr)
{ {
return new PosixProcess(args, captureStdout, captureStderr); return new PosixProcess(args, captureOutput);
} }
int NumberOfProcessors() int NumberOfProcessors()
......
...@@ -65,9 +65,8 @@ ProcessHandle::ProcessHandle() : mProcess(nullptr) {} ...@@ -65,9 +65,8 @@ ProcessHandle::ProcessHandle() : mProcess(nullptr) {}
ProcessHandle::ProcessHandle(Process *process) : mProcess(process) {} ProcessHandle::ProcessHandle(Process *process) : mProcess(process) {}
ProcessHandle::ProcessHandle(const std::vector<const char *> &args, ProcessHandle::ProcessHandle(const std::vector<const char *> &args,
bool captureStdout, ProcessOutputCapture captureOutput)
bool captureStderr) : mProcess(LaunchProcess(args, captureOutput))
: mProcess(LaunchProcess(args, captureStdout, captureStderr))
{} {}
ProcessHandle::~ProcessHandle() ProcessHandle::~ProcessHandle()
......
...@@ -85,12 +85,23 @@ class Process : angle::NonCopyable ...@@ -85,12 +85,23 @@ class Process : angle::NonCopyable
std::string mStderr; std::string mStderr;
}; };
enum class ProcessOutputCapture
{
Nothing,
// Capture stdout only
StdoutOnly,
// Capture stdout, and pipe stderr to stdout
StdoutAndStderrInterleaved,
// Capture stdout and stderr separately
StdoutAndStderrSeparately,
};
class ProcessHandle final : angle::NonCopyable class ProcessHandle final : angle::NonCopyable
{ {
public: public:
ProcessHandle(); ProcessHandle();
ProcessHandle(Process *process); ProcessHandle(Process *process);
ProcessHandle(const std::vector<const char *> &args, bool captureStdout, bool captureStderr); ProcessHandle(const std::vector<const char *> &args, ProcessOutputCapture captureOutput);
~ProcessHandle(); ~ProcessHandle();
ProcessHandle(ProcessHandle &&other); ProcessHandle(ProcessHandle &&other);
ProcessHandle &operator=(ProcessHandle &&rhs); ProcessHandle &operator=(ProcessHandle &&rhs);
...@@ -114,9 +125,7 @@ class ProcessHandle final : angle::NonCopyable ...@@ -114,9 +125,7 @@ class ProcessHandle final : angle::NonCopyable
// //
// On success, returns a Process pointer with started() == true. // On success, returns a Process pointer with started() == true.
// On failure, returns a Process pointer with started() == false. // On failure, returns a Process pointer with started() == false.
Process *LaunchProcess(const std::vector<const char *> &args, Process *LaunchProcess(const std::vector<const char *> &args, ProcessOutputCapture captureOutput);
bool captureStdout,
bool captureStderr);
int NumberOfProcessors(); int NumberOfProcessors();
......
...@@ -130,13 +130,16 @@ TEST(TestUtils, MAYBE_CreateAndDeleteFileInTempDir) ...@@ -130,13 +130,16 @@ TEST(TestUtils, MAYBE_CreateAndDeleteFileInTempDir)
#if defined(ANGLE_PLATFORM_ANDROID) #if defined(ANGLE_PLATFORM_ANDROID)
# define MAYBE_RunApp DISABLED_RunApp # define MAYBE_RunApp DISABLED_RunApp
# define MAYBE_RunAppAsync DISABLED_RunAppAsync # define MAYBE_RunAppAsync DISABLED_RunAppAsync
# define MAYBE_RunAppAsyncRedirectStderrToStdout DISABLED_RunAppAsyncRedirectStderrToStdout
// TODO: fuchsia support. http://anglebug.com/3161 // TODO: fuchsia support. http://anglebug.com/3161
#elif defined(ANGLE_PLATFORM_FUCHSIA) #elif defined(ANGLE_PLATFORM_FUCHSIA)
# define MAYBE_RunApp DISABLED_RunApp # define MAYBE_RunApp DISABLED_RunApp
# define MAYBE_RunAppAsync DISABLED_RunAppAsync # define MAYBE_RunAppAsync DISABLED_RunAppAsync
# define MAYBE_RunAppAsyncRedirectStderrToStdout DISABLED_RunAppAsyncRedirectStderrToStdout
#else #else
# define MAYBE_RunApp RunApp # define MAYBE_RunApp RunApp
# define MAYBE_RunAppAsync RunAppAsync # define MAYBE_RunAppAsync RunAppAsync
# define MAYBE_RunAppAsyncRedirectStderrToStdout RunAppAsyncRedirectStderrToStdout
#endif // defined(ANGLE_PLATFORM_ANDROID) #endif // defined(ANGLE_PLATFORM_ANDROID)
// Test running an external application and receiving its output // Test running an external application and receiving its output
...@@ -151,7 +154,7 @@ TEST(TestUtils, MAYBE_RunApp) ...@@ -151,7 +154,7 @@ TEST(TestUtils, MAYBE_RunApp)
// Test that the application can be executed. // Test that the application can be executed.
{ {
ProcessHandle process(args, true, true); ProcessHandle process(args, ProcessOutputCapture::StdoutAndStderrSeparately);
EXPECT_TRUE(process->started()); EXPECT_TRUE(process->started());
EXPECT_TRUE(process->finish()); EXPECT_TRUE(process->finish());
EXPECT_TRUE(process->finished()); EXPECT_TRUE(process->finished());
...@@ -167,7 +170,7 @@ TEST(TestUtils, MAYBE_RunApp) ...@@ -167,7 +170,7 @@ TEST(TestUtils, MAYBE_RunApp)
bool setEnvDone = SetEnvironmentVar(kRunAppTestEnvVarName, kRunAppTestEnvVarValue); bool setEnvDone = SetEnvironmentVar(kRunAppTestEnvVarName, kRunAppTestEnvVarValue);
EXPECT_TRUE(setEnvDone); EXPECT_TRUE(setEnvDone);
ProcessHandle process(LaunchProcess(args, true, true)); ProcessHandle process(LaunchProcess(args, ProcessOutputCapture::StdoutAndStderrSeparately));
EXPECT_TRUE(process->started()); EXPECT_TRUE(process->started());
EXPECT_TRUE(process->finish()); EXPECT_TRUE(process->finish());
...@@ -191,9 +194,9 @@ TEST(TestUtils, MAYBE_RunAppAsync) ...@@ -191,9 +194,9 @@ TEST(TestUtils, MAYBE_RunAppAsync)
std::vector<const char *> args = {executablePath.c_str(), kRunAppTestArg1, kRunAppTestArg2}; std::vector<const char *> args = {executablePath.c_str(), kRunAppTestArg1, kRunAppTestArg2};
// Test that the application can be executed. // Test that the application can be executed and the output is captured correctly.
{ {
ProcessHandle process(args, true, true); ProcessHandle process(args, ProcessOutputCapture::StdoutAndStderrSeparately);
EXPECT_TRUE(process->started()); EXPECT_TRUE(process->started());
constexpr double kTimeout = 3.0; constexpr double kTimeout = 3.0;
...@@ -213,6 +216,39 @@ TEST(TestUtils, MAYBE_RunAppAsync) ...@@ -213,6 +216,39 @@ TEST(TestUtils, MAYBE_RunAppAsync)
} }
} }
// Test running an external application and receiving its stdout and stderr output interleaved.
TEST(TestUtils, MAYBE_RunAppAsyncRedirectStderrToStdout)
{
std::string executablePath = GetExecutableDirectory();
EXPECT_NE(executablePath, "");
executablePath += "/";
executablePath += kRunAppHelperExecutable;
std::vector<const char *> args = {executablePath.c_str(), kRunAppTestArg1, kRunAppTestArg2};
// Test that the application can be executed and the output is captured correctly.
{
ProcessHandle process(args, ProcessOutputCapture::StdoutAndStderrInterleaved);
EXPECT_TRUE(process->started());
constexpr double kTimeout = 3.0;
Timer timer;
timer.start();
while (!process->finished() && timer.getElapsedTime() < kTimeout)
{
angle::Sleep(1);
}
EXPECT_TRUE(process->finished());
EXPECT_GT(process->getElapsedTimeSeconds(), 0.0);
EXPECT_EQ(std::string(kRunAppTestStdout) + kRunAppTestStderr,
NormalizeNewLines(process->getStdout()));
EXPECT_EQ("", process->getStderr());
EXPECT_EQ(EXIT_SUCCESS, process->getExitCode());
}
}
// Verify that NumberOfProcessors returns something reasonable. // Verify that NumberOfProcessors returns something reasonable.
TEST(TestUtils, NumberOfProcessors) TEST(TestUtils, NumberOfProcessors)
{ {
......
...@@ -44,6 +44,7 @@ int main(int argc, char **argv) ...@@ -44,6 +44,7 @@ int main(int argc, char **argv)
if (env == "") if (env == "")
{ {
printf("%s", kRunAppTestStdout); printf("%s", kRunAppTestStdout);
fflush(stdout);
fprintf(stderr, "%s", kRunAppTestStderr); fprintf(stderr, "%s", kRunAppTestStderr);
} }
else else
......
...@@ -134,8 +134,7 @@ class WindowsProcess : public Process ...@@ -134,8 +134,7 @@ class WindowsProcess : public Process
{ {
public: public:
WindowsProcess(const std::vector<const char *> &commandLineArgs, WindowsProcess(const std::vector<const char *> &commandLineArgs,
bool captureStdOut, ProcessOutputCapture captureOutput)
bool captureStdErr)
{ {
mProcessInfo.hProcess = INVALID_HANDLE_VALUE; mProcessInfo.hProcess = INVALID_HANDLE_VALUE;
mProcessInfo.hThread = INVALID_HANDLE_VALUE; mProcessInfo.hThread = INVALID_HANDLE_VALUE;
...@@ -162,10 +161,17 @@ class WindowsProcess : public Process ...@@ -162,10 +161,17 @@ class WindowsProcess : public Process
STARTUPINFOA startInfo = {}; STARTUPINFOA startInfo = {};
const bool captureStdout = captureOutput != ProcessOutputCapture::Nothing;
const bool captureStderr =
captureOutput == ProcessOutputCapture::StdoutAndStderrInterleaved ||
captureOutput == ProcessOutputCapture::StdoutAndStderrSeparately;
const bool pipeStderrToStdout =
captureOutput == ProcessOutputCapture::StdoutAndStderrInterleaved;
// Create pipes for stdout and stderr. // Create pipes for stdout and stderr.
startInfo.cb = sizeof(STARTUPINFOA); startInfo.cb = sizeof(STARTUPINFOA);
startInfo.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE); startInfo.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE);
if (captureStdOut) if (captureStdout)
{ {
if (!mStdoutPipe.initPipe(&securityAttribs)) if (!mStdoutPipe.initPipe(&securityAttribs))
{ {
...@@ -178,7 +184,11 @@ class WindowsProcess : public Process ...@@ -178,7 +184,11 @@ class WindowsProcess : public Process
startInfo.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); startInfo.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
} }
if (captureStdErr) if (pipeStderrToStdout)
{
startInfo.hStdError = startInfo.hStdOutput;
}
else if (captureStderr)
{ {
if (!mStderrPipe.initPipe(&securityAttribs)) if (!mStderrPipe.initPipe(&securityAttribs))
{ {
...@@ -192,7 +202,7 @@ class WindowsProcess : public Process ...@@ -192,7 +202,7 @@ class WindowsProcess : public Process
} }
#if !defined(ANGLE_ENABLE_WINDOWS_UWP) #if !defined(ANGLE_ENABLE_WINDOWS_UWP)
if (captureStdOut || captureStdErr) if (captureStdout || captureStderr)
{ {
startInfo.dwFlags |= STARTF_USESTDHANDLES; startInfo.dwFlags |= STARTF_USESTDHANDLES;
} }
...@@ -393,11 +403,9 @@ void WriteDebugMessage(const char *format, ...) ...@@ -393,11 +403,9 @@ void WriteDebugMessage(const char *format, ...)
OutputDebugStringA(buffer.data()); OutputDebugStringA(buffer.data());
} }
Process *LaunchProcess(const std::vector<const char *> &args, Process *LaunchProcess(const std::vector<const char *> &args, ProcessOutputCapture captureOutput)
bool captureStdout,
bool captureStderr)
{ {
return new WindowsProcess(args, captureStdout, captureStderr); return new WindowsProcess(args, captureOutput);
} }
bool GetTempDir(char *tempDirOut, uint32_t maxDirNameLen) bool GetTempDir(char *tempDirOut, uint32_t maxDirNameLen)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment