You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
336 lines
6.9 KiB
336 lines
6.9 KiB
/*------------------------------------------------------------------------- |
|
* 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
|
|
|