Import dEQP.

Import drawElements Quality Program from an internal repository.

Bug: 17388917
Change-Id: Ic109fe4a57e31b2a816113d90fbdf51a43e7abeb
diff --git a/execserver/xsPosixTestProcess.cpp b/execserver/xsPosixTestProcess.cpp
new file mode 100644
index 0000000..5c9dcb6
--- /dev/null
+++ b/execserver/xsPosixTestProcess.cpp
@@ -0,0 +1,336 @@
+/*-------------------------------------------------------------------------
+ * 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).normalize().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