1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program Execution Server
3 * ---------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief TestProcess implementation for Unix-like systems.
22 *//*--------------------------------------------------------------------*/
23
24 #include "xsPosixTestProcess.hpp"
25 #include "deFilePath.hpp"
26 #include "deClock.h"
27
28 #include <string.h>
29 #include <stdio.h>
30
31 using std::string;
32 using std::vector;
33
34 namespace xs
35 {
36
37 namespace posix
38 {
39
CaseListWriter(void)40 CaseListWriter::CaseListWriter (void)
41 : m_file (DE_NULL)
42 , m_run (false)
43 {
44 }
45
~CaseListWriter(void)46 CaseListWriter::~CaseListWriter (void)
47 {
48 }
49
start(const char * caseList,deFile * dst)50 void CaseListWriter::start (const char* caseList, deFile* dst)
51 {
52 DE_ASSERT(!isStarted());
53 m_file = dst;
54 m_run = true;
55
56 int caseListSize = (int)strlen(caseList)+1;
57 m_caseList.resize(caseListSize);
58 std::copy(caseList, caseList+caseListSize, m_caseList.begin());
59
60 // Set to non-blocking mode.
61 if (!deFile_setFlags(m_file, DE_FILE_NONBLOCKING))
62 XS_FAIL("Failed to set non-blocking mode");
63
64 de::Thread::start();
65 }
66
run(void)67 void CaseListWriter::run (void)
68 {
69 deInt64 pos = 0;
70
71 while (m_run && pos < (deInt64)m_caseList.size())
72 {
73 deInt64 numWritten = 0;
74 deFileResult result = deFile_write(m_file, &m_caseList[0] + pos, m_caseList.size()-pos, &numWritten);
75
76 if (result == DE_FILERESULT_SUCCESS)
77 pos += numWritten;
78 else if (result == DE_FILERESULT_WOULD_BLOCK)
79 deSleep(1); // Yield.
80 else
81 break; // Error.
82 }
83 }
84
stop(void)85 void CaseListWriter::stop (void)
86 {
87 if (!isStarted())
88 return; // Nothing to do.
89
90 m_run = false;
91
92 // Join thread.
93 join();
94
95 m_file = DE_NULL;
96 }
97
PipeReader(ThreadedByteBuffer * dst)98 PipeReader::PipeReader (ThreadedByteBuffer* dst)
99 : m_file (DE_NULL)
100 , m_buf (dst)
101 {
102 }
103
~PipeReader(void)104 PipeReader::~PipeReader (void)
105 {
106 }
107
start(deFile * file)108 void PipeReader::start (deFile* file)
109 {
110 DE_ASSERT(!isStarted());
111
112 // Set to non-blocking mode.
113 if (!deFile_setFlags(file, DE_FILE_NONBLOCKING))
114 XS_FAIL("Failed to set non-blocking mode");
115
116 m_file = file;
117
118 de::Thread::start();
119 }
120
run(void)121 void PipeReader::run (void)
122 {
123 std::vector<deUint8> tmpBuf (FILEREADER_TMP_BUFFER_SIZE);
124 deInt64 numRead = 0;
125
126 while (!m_buf->isCanceled())
127 {
128 deFileResult result = deFile_read(m_file, &tmpBuf[0], (deInt64)tmpBuf.size(), &numRead);
129
130 if (result == DE_FILERESULT_SUCCESS)
131 {
132 // Write to buffer.
133 try
134 {
135 m_buf->write((int)numRead, &tmpBuf[0]);
136 m_buf->flush();
137 }
138 catch (const ThreadedByteBuffer::CanceledException&)
139 {
140 // Canceled.
141 break;
142 }
143 }
144 else if (result == DE_FILERESULT_END_OF_FILE ||
145 result == DE_FILERESULT_WOULD_BLOCK)
146 {
147 // Wait for more data.
148 deSleep(FILEREADER_IDLE_SLEEP);
149 }
150 else
151 break; // Error.
152 }
153 }
154
stop(void)155 void PipeReader::stop (void)
156 {
157 if (!isStarted())
158 return; // Nothing to do.
159
160 // Buffer must be in canceled state or otherwise stopping reader might block.
161 DE_ASSERT(m_buf->isCanceled());
162
163 // Join thread.
164 join();
165
166 m_file = DE_NULL;
167 }
168
169 } // unix
170
PosixTestProcess(void)171 PosixTestProcess::PosixTestProcess (void)
172 : m_process (DE_NULL)
173 , m_processStartTime (0)
174 , m_infoBuffer (INFO_BUFFER_BLOCK_SIZE, INFO_BUFFER_NUM_BLOCKS)
175 , m_stdOutReader (&m_infoBuffer)
176 , m_stdErrReader (&m_infoBuffer)
177 , m_logReader (LOG_BUFFER_BLOCK_SIZE, LOG_BUFFER_NUM_BLOCKS)
178 {
179 }
180
~PosixTestProcess(void)181 PosixTestProcess::~PosixTestProcess (void)
182 {
183 delete m_process;
184 }
185
start(const char * name,const char * params,const char * workingDir,const char * caseList)186 void PosixTestProcess::start (const char* name, const char* params, const char* workingDir, const char* caseList)
187 {
188 bool hasCaseList = strlen(caseList) > 0;
189
190 XS_CHECK(!m_process);
191
192 de::FilePath logFilePath = de::FilePath::join(workingDir, "TestResults.qpa");
193 m_logFileName = logFilePath.getPath();
194
195 // Remove old file if such exists.
196 if (deFileExists(m_logFileName.c_str()))
197 {
198 if (!deDeleteFile(m_logFileName.c_str()) || deFileExists(m_logFileName.c_str()))
199 throw TestProcessException(string("Failed to remove '") + m_logFileName + "'");
200 }
201
202 // Construct command line.
203 string cmdLine = de::FilePath(name).isAbsolutePath() ? name : de::FilePath::join(workingDir, name).getPath();
204 cmdLine += string(" --deqp-log-filename=") + logFilePath.getBaseName();
205
206 if (hasCaseList)
207 cmdLine += " --deqp-stdin-caselist";
208
209 if (strlen(params) > 0)
210 cmdLine += string(" ") + params;
211
212 DE_ASSERT(!m_process);
213 m_process = new de::Process();
214
215 try
216 {
217 m_process->start(cmdLine.c_str(), strlen(workingDir) > 0 ? workingDir : DE_NULL);
218 }
219 catch (const de::ProcessError& e)
220 {
221 delete m_process;
222 m_process = DE_NULL;
223 throw TestProcessException(e.what());
224 }
225
226 m_processStartTime = deGetMicroseconds();
227
228 // Create stdout & stderr readers.
229 if (m_process->getStdOut())
230 m_stdOutReader.start(m_process->getStdOut());
231
232 if (m_process->getStdErr())
233 m_stdErrReader.start(m_process->getStdErr());
234
235 // Start case list writer.
236 if (hasCaseList)
237 {
238 deFile* dst = m_process->getStdIn();
239 if (dst)
240 m_caseListWriter.start(caseList, dst);
241 else
242 {
243 cleanup();
244 throw TestProcessException("Failed to write case list");
245 }
246 }
247 }
248
terminate(void)249 void PosixTestProcess::terminate (void)
250 {
251 if (m_process)
252 {
253 try
254 {
255 m_process->kill();
256 }
257 catch (const std::exception& e)
258 {
259 printf("PosixTestProcess::terminate(): Failed to kill process: %s\n", e.what());
260 }
261 }
262 }
263
cleanup(void)264 void PosixTestProcess::cleanup (void)
265 {
266 m_caseListWriter.stop();
267 m_logReader.stop();
268
269 // \note Info buffer must be canceled before stopping pipe readers.
270 m_infoBuffer.cancel();
271
272 m_stdErrReader.stop();
273 m_stdOutReader.stop();
274
275 // Reset info buffer.
276 m_infoBuffer.clear();
277
278 if (m_process)
279 {
280 try
281 {
282 if (m_process->isRunning())
283 {
284 m_process->kill();
285 m_process->waitForFinish();
286 }
287 }
288 catch (const de::ProcessError& e)
289 {
290 printf("PosixTestProcess::stop(): Failed to kill process: %s\n", e.what());
291 }
292
293 delete m_process;
294 m_process = DE_NULL;
295 }
296 }
297
isRunning(void)298 bool PosixTestProcess::isRunning (void)
299 {
300 if (m_process)
301 return m_process->isRunning();
302 else
303 return false;
304 }
305
getExitCode(void) const306 int PosixTestProcess::getExitCode (void) const
307 {
308 if (m_process)
309 return m_process->getExitCode();
310 else
311 return -1;
312 }
313
readTestLog(deUint8 * dst,int numBytes)314 int PosixTestProcess::readTestLog (deUint8* dst, int numBytes)
315 {
316 if (!m_logReader.isRunning())
317 {
318 if (deGetMicroseconds() - m_processStartTime > LOG_FILE_TIMEOUT*1000)
319 {
320 // Timeout, kill process.
321 terminate();
322 return 0; // \todo [2013-08-13 pyry] Throw exception?
323 }
324
325 if (!deFileExists(m_logFileName.c_str()))
326 return 0;
327
328 // Start reader.
329 m_logReader.start(m_logFileName.c_str());
330 }
331
332 DE_ASSERT(m_logReader.isRunning());
333 return m_logReader.read(dst, numBytes);
334 }
335
336 } // xs
337