/*------------------------------------------------------------------------- * 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 Test Driver. *//*--------------------------------------------------------------------*/ #include "xsTestDriver.hpp" #include "deClock.h" #include <string> #include <vector> #include <cstdio> using std::string; using std::vector; #if 0 # define DBG_PRINT(X) printf X #else # define DBG_PRINT(X) #endif namespace xs { TestDriver::TestDriver (xs::TestProcess* testProcess) : m_state (STATE_NOT_STARTED) , m_lastExitCode (0) , m_process (testProcess) , m_lastProcessDataTime (0) , m_dataMsgTmpBuf (SEND_RECV_TMP_BUFFER_SIZE) { } TestDriver::~TestDriver (void) { reset(); } void TestDriver::reset (void) { m_process->cleanup(); m_state = STATE_NOT_STARTED; } void TestDriver::startProcess (const char* name, const char* params, const char* workingDir, const char* caseList) { try { m_process->start(name, params, workingDir, caseList); m_state = STATE_PROCESS_STARTED; } catch (const TestProcessException& e) { printf("Failed to launch test process: %s\n", e.what()); m_state = STATE_PROCESS_LAUNCH_FAILED; m_lastLaunchFailure = e.what(); } } void TestDriver::stopProcess (void) { m_process->terminate(); } bool TestDriver::poll (ByteBuffer& messageBuffer) { switch (m_state) { case STATE_NOT_STARTED: return false; // Nothing to report. case STATE_PROCESS_LAUNCH_FAILED: DBG_PRINT((" STATE_PROCESS_LAUNCH_FAILED\n")); if (writeMessage(messageBuffer, ProcessLaunchFailedMessage(m_lastLaunchFailure.c_str()))) { m_state = STATE_NOT_STARTED; m_lastLaunchFailure = ""; return true; } else return false; case STATE_PROCESS_STARTED: DBG_PRINT((" STATE_PROCESS_STARTED\n")); if (writeMessage(messageBuffer, ProcessStartedMessage())) { m_state = STATE_PROCESS_RUNNING; return true; } else return false; case STATE_PROCESS_RUNNING: { DBG_PRINT((" STATE_PROCESS_RUNNING\n")); bool gotProcessData = false; // Poll log file and info buffer. gotProcessData = pollLogFile(messageBuffer) || gotProcessData; gotProcessData = pollInfo(messageBuffer) || gotProcessData; if (gotProcessData) return true; // Got IO. if (!m_process->isRunning()) { // Process died. m_state = STATE_READING_DATA; m_lastExitCode = m_process->getExitCode(); m_lastProcessDataTime = deGetMicroseconds(); return true; // Got state change. } return false; // Nothing to report. } case STATE_READING_DATA: { DBG_PRINT((" STATE_READING_DATA\n")); bool gotProcessData = false; // Poll log file and info buffer. gotProcessData = pollLogFile(messageBuffer) || gotProcessData; gotProcessData = pollInfo(messageBuffer) || gotProcessData; if (gotProcessData) { // Got data. m_lastProcessDataTime = deGetMicroseconds(); return true; } else if (deGetMicroseconds() - m_lastProcessDataTime > READ_DATA_TIMEOUT*1000) { // Read timeout occurred. m_state = STATE_PROCESS_FINISHED; return true; // State change. } else return false; // Still waiting for data. } case STATE_PROCESS_FINISHED: DBG_PRINT((" STATE_PROCESS_FINISHED\n")); if (writeMessage(messageBuffer, ProcessFinishedMessage(m_lastExitCode))) { // Signal TestProcess to clean up any remaining resources. m_process->cleanup(); m_state = STATE_NOT_STARTED; m_lastExitCode = 0; return true; } else return false; default: DE_ASSERT(DE_FALSE); return false; } } bool TestDriver::pollLogFile (ByteBuffer& messageBuffer) { return pollBuffer(messageBuffer, MESSAGETYPE_PROCESS_LOG_DATA); } bool TestDriver::pollInfo (ByteBuffer& messageBuffer) { return pollBuffer(messageBuffer, MESSAGETYPE_INFO); } bool TestDriver::pollBuffer (ByteBuffer& messageBuffer, MessageType msgType) { const int minBytesAvailable = MESSAGE_HEADER_SIZE + MIN_MSG_PAYLOAD_SIZE; if (messageBuffer.getNumFree() < minBytesAvailable) return false; // Not enough space in message buffer. const int maxMsgSize = de::min((int)m_dataMsgTmpBuf.size(), messageBuffer.getNumFree()); int numRead = 0; int msgSize = MESSAGE_HEADER_SIZE+1; // One byte is reserved for terminating 0. // Fill in data \note Last byte is reserved for 0. numRead = msgType == MESSAGETYPE_PROCESS_LOG_DATA ? m_process->readTestLog(&m_dataMsgTmpBuf[MESSAGE_HEADER_SIZE], maxMsgSize-MESSAGE_HEADER_SIZE-1) : m_process->readInfoLog(&m_dataMsgTmpBuf[MESSAGE_HEADER_SIZE], maxMsgSize-MESSAGE_HEADER_SIZE-1); if (numRead <= 0) return false; // Didn't get any data. msgSize += numRead; // Terminate with 0. m_dataMsgTmpBuf[msgSize-1] = 0; // Write header. Message::writeHeader(msgType, msgSize, &m_dataMsgTmpBuf[0], MESSAGE_HEADER_SIZE); // Write to messagebuffer. messageBuffer.pushFront(&m_dataMsgTmpBuf[0], msgSize); DBG_PRINT((" wrote %d bytes of %s data\n", msgSize, msgType == MESSAGETYPE_INFO ? "info" : "log")); return true; } bool TestDriver::writeMessage (ByteBuffer& messageBuffer, const Message& message) { vector<deUint8> buf; message.write(buf); if (messageBuffer.getNumFree() < (int)buf.size()) return false; messageBuffer.pushFront(&buf[0], (int)buf.size()); return true; } } // xs