337 lines
6.9 KiB
C++
337 lines
6.9 KiB
C++
|
/*-------------------------------------------------------------------------
|
||
|
* drawElements Quality Program Execution Server
|
||
|
* ---------------------------------------------
|
||
|
*
|
||
|
* Copyright 2014 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*
|
||
|
*//*!
|
||
|
* \file
|
||
|
* \brief TestProcess implementation for Unix-like systems.
|
||
|
*//*--------------------------------------------------------------------*/
|
||
|
|
||
|
#include "xsPosixTestProcess.hpp"
|
||
|
#include "deFilePath.hpp"
|
||
|
#include "deClock.h"
|
||
|
|
||
|
#include <string.h>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
using std::string;
|
||
|
using std::vector;
|
||
|
|
||
|
namespace xs
|
||
|
{
|
||
|
|
||
|
namespace posix
|
||
|
{
|
||
|
|
||
|
CaseListWriter::CaseListWriter (void)
|
||
|
: m_file (DE_NULL)
|
||
|
, m_run (false)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
CaseListWriter::~CaseListWriter (void)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void CaseListWriter::start (const char* caseList, deFile* dst)
|
||
|
{
|
||
|
DE_ASSERT(!isStarted());
|
||
|
m_file = dst;
|
||
|
m_run = true;
|
||
|
|
||
|
int caseListSize = (int)strlen(caseList)+1;
|
||
|
m_caseList.resize(caseListSize);
|
||
|
std::copy(caseList, caseList+caseListSize, m_caseList.begin());
|
||
|
|
||
|
// Set to non-blocking mode.
|
||
|
if (!deFile_setFlags(m_file, DE_FILE_NONBLOCKING))
|
||
|
XS_FAIL("Failed to set non-blocking mode");
|
||
|
|
||
|
de::Thread::start();
|
||
|
}
|
||
|
|
||
|
void CaseListWriter::run (void)
|
||
|
{
|
||
|
deInt64 pos = 0;
|
||
|
|
||
|
while (m_run && pos < (deInt64)m_caseList.size())
|
||
|
{
|
||
|
deInt64 numWritten = 0;
|
||
|
deFileResult result = deFile_write(m_file, &m_caseList[0] + pos, m_caseList.size()-pos, &numWritten);
|
||
|
|
||
|
if (result == DE_FILERESULT_SUCCESS)
|
||
|
pos += numWritten;
|
||
|
else if (result == DE_FILERESULT_WOULD_BLOCK)
|
||
|
deSleep(1); // Yield.
|
||
|
else
|
||
|
break; // Error.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CaseListWriter::stop (void)
|
||
|
{
|
||
|
if (!isStarted())
|
||
|
return; // Nothing to do.
|
||
|
|
||
|
m_run = false;
|
||
|
|
||
|
// Join thread.
|
||
|
join();
|
||
|
|
||
|
m_file = DE_NULL;
|
||
|
}
|
||
|
|
||
|
PipeReader::PipeReader (ThreadedByteBuffer* dst)
|
||
|
: m_file (DE_NULL)
|
||
|
, m_buf (dst)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
PipeReader::~PipeReader (void)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void PipeReader::start (deFile* file)
|
||
|
{
|
||
|
DE_ASSERT(!isStarted());
|
||
|
|
||
|
// Set to non-blocking mode.
|
||
|
if (!deFile_setFlags(file, DE_FILE_NONBLOCKING))
|
||
|
XS_FAIL("Failed to set non-blocking mode");
|
||
|
|
||
|
m_file = file;
|
||
|
|
||
|
de::Thread::start();
|
||
|
}
|
||
|
|
||
|
void PipeReader::run (void)
|
||
|
{
|
||
|
std::vector<deUint8> tmpBuf (FILEREADER_TMP_BUFFER_SIZE);
|
||
|
deInt64 numRead = 0;
|
||
|
|
||
|
while (!m_buf->isCanceled())
|
||
|
{
|
||
|
deFileResult result = deFile_read(m_file, &tmpBuf[0], (deInt64)tmpBuf.size(), &numRead);
|
||
|
|
||
|
if (result == DE_FILERESULT_SUCCESS)
|
||
|
{
|
||
|
// Write to buffer.
|
||
|
try
|
||
|
{
|
||
|
m_buf->write((int)numRead, &tmpBuf[0]);
|
||
|
m_buf->flush();
|
||
|
}
|
||
|
catch (const ThreadedByteBuffer::CanceledException&)
|
||
|
{
|
||
|
// Canceled.
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else if (result == DE_FILERESULT_END_OF_FILE ||
|
||
|
result == DE_FILERESULT_WOULD_BLOCK)
|
||
|
{
|
||
|
// Wait for more data.
|
||
|
deSleep(FILEREADER_IDLE_SLEEP);
|
||
|
}
|
||
|
else
|
||
|
break; // Error.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PipeReader::stop (void)
|
||
|
{
|
||
|
if (!isStarted())
|
||
|
return; // Nothing to do.
|
||
|
|
||
|
// Buffer must be in canceled state or otherwise stopping reader might block.
|
||
|
DE_ASSERT(m_buf->isCanceled());
|
||
|
|
||
|
// Join thread.
|
||
|
join();
|
||
|
|
||
|
m_file = DE_NULL;
|
||
|
}
|
||
|
|
||
|
} // unix
|
||
|
|
||
|
PosixTestProcess::PosixTestProcess (void)
|
||
|
: m_process (DE_NULL)
|
||
|
, m_processStartTime (0)
|
||
|
, m_infoBuffer (INFO_BUFFER_BLOCK_SIZE, INFO_BUFFER_NUM_BLOCKS)
|
||
|
, m_stdOutReader (&m_infoBuffer)
|
||
|
, m_stdErrReader (&m_infoBuffer)
|
||
|
, m_logReader (LOG_BUFFER_BLOCK_SIZE, LOG_BUFFER_NUM_BLOCKS)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
PosixTestProcess::~PosixTestProcess (void)
|
||
|
{
|
||
|
delete m_process;
|
||
|
}
|
||
|
|
||
|
void PosixTestProcess::start (const char* name, const char* params, const char* workingDir, const char* caseList)
|
||
|
{
|
||
|
bool hasCaseList = strlen(caseList) > 0;
|
||
|
|
||
|
XS_CHECK(!m_process);
|
||
|
|
||
|
de::FilePath logFilePath = de::FilePath::join(workingDir, "TestResults.qpa");
|
||
|
m_logFileName = logFilePath.getPath();
|
||
|
|
||
|
// Remove old file if such exists.
|
||
|
if (deFileExists(m_logFileName.c_str()))
|
||
|
{
|
||
|
if (!deDeleteFile(m_logFileName.c_str()) || deFileExists(m_logFileName.c_str()))
|
||
|
throw TestProcessException(string("Failed to remove '") + m_logFileName + "'");
|
||
|
}
|
||
|
|
||
|
// Construct command line.
|
||
|
string cmdLine = de::FilePath(name).isAbsolutePath() ? name : de::FilePath::join(workingDir, name).getPath();
|
||
|
cmdLine += string(" --deqp-log-filename=") + logFilePath.getBaseName();
|
||
|
|
||
|
if (hasCaseList)
|
||
|
cmdLine += " --deqp-stdin-caselist";
|
||
|
|
||
|
if (strlen(params) > 0)
|
||
|
cmdLine += string(" ") + params;
|
||
|
|
||
|
DE_ASSERT(!m_process);
|
||
|
m_process = new de::Process();
|
||
|
|
||
|
try
|
||
|
{
|
||
|
m_process->start(cmdLine.c_str(), strlen(workingDir) > 0 ? workingDir : DE_NULL);
|
||
|
}
|
||
|
catch (const de::ProcessError& e)
|
||
|
{
|
||
|
delete m_process;
|
||
|
m_process = DE_NULL;
|
||
|
throw TestProcessException(e.what());
|
||
|
}
|
||
|
|
||
|
m_processStartTime = deGetMicroseconds();
|
||
|
|
||
|
// Create stdout & stderr readers.
|
||
|
if (m_process->getStdOut())
|
||
|
m_stdOutReader.start(m_process->getStdOut());
|
||
|
|
||
|
if (m_process->getStdErr())
|
||
|
m_stdErrReader.start(m_process->getStdErr());
|
||
|
|
||
|
// Start case list writer.
|
||
|
if (hasCaseList)
|
||
|
{
|
||
|
deFile* dst = m_process->getStdIn();
|
||
|
if (dst)
|
||
|
m_caseListWriter.start(caseList, dst);
|
||
|
else
|
||
|
{
|
||
|
cleanup();
|
||
|
throw TestProcessException("Failed to write case list");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PosixTestProcess::terminate (void)
|
||
|
{
|
||
|
if (m_process)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
m_process->kill();
|
||
|
}
|
||
|
catch (const std::exception& e)
|
||
|
{
|
||
|
printf("PosixTestProcess::terminate(): Failed to kill process: %s\n", e.what());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PosixTestProcess::cleanup (void)
|
||
|
{
|
||
|
m_caseListWriter.stop();
|
||
|
m_logReader.stop();
|
||
|
|
||
|
// \note Info buffer must be canceled before stopping pipe readers.
|
||
|
m_infoBuffer.cancel();
|
||
|
|
||
|
m_stdErrReader.stop();
|
||
|
m_stdOutReader.stop();
|
||
|
|
||
|
// Reset info buffer.
|
||
|
m_infoBuffer.clear();
|
||
|
|
||
|
if (m_process)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
if (m_process->isRunning())
|
||
|
{
|
||
|
m_process->kill();
|
||
|
m_process->waitForFinish();
|
||
|
}
|
||
|
}
|
||
|
catch (const de::ProcessError& e)
|
||
|
{
|
||
|
printf("PosixTestProcess::stop(): Failed to kill process: %s\n", e.what());
|
||
|
}
|
||
|
|
||
|
delete m_process;
|
||
|
m_process = DE_NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool PosixTestProcess::isRunning (void)
|
||
|
{
|
||
|
if (m_process)
|
||
|
return m_process->isRunning();
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int PosixTestProcess::getExitCode (void) const
|
||
|
{
|
||
|
if (m_process)
|
||
|
return m_process->getExitCode();
|
||
|
else
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int PosixTestProcess::readTestLog (deUint8* dst, int numBytes)
|
||
|
{
|
||
|
if (!m_logReader.isRunning())
|
||
|
{
|
||
|
if (deGetMicroseconds() - m_processStartTime > LOG_FILE_TIMEOUT*1000)
|
||
|
{
|
||
|
// Timeout, kill process.
|
||
|
terminate();
|
||
|
return 0; // \todo [2013-08-13 pyry] Throw exception?
|
||
|
}
|
||
|
|
||
|
if (!deFileExists(m_logFileName.c_str()))
|
||
|
return 0;
|
||
|
|
||
|
// Start reader.
|
||
|
m_logReader.start(m_logFileName.c_str());
|
||
|
}
|
||
|
|
||
|
DE_ASSERT(m_logReader.isRunning());
|
||
|
return m_logReader.read(dst, numBytes);
|
||
|
}
|
||
|
|
||
|
} // xs
|