blob: 9f4d21de634bd84120de43fb56440d646056ed2c [file] [log] [blame]
Jarkko Poyry3c827362014-09-02 11:48:52 +03001/*-------------------------------------------------------------------------
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
31using std::string;
32using std::vector;
33
34namespace xs
35{
36
37namespace posix
38{
39
40CaseListWriter::CaseListWriter (void)
41 : m_file (DE_NULL)
42 , m_run (false)
43{
44}
45
46CaseListWriter::~CaseListWriter (void)
47{
48}
49
50void 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
67void 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
85void 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
98PipeReader::PipeReader (ThreadedByteBuffer* dst)
99 : m_file (DE_NULL)
100 , m_buf (dst)
101{
102}
103
104PipeReader::~PipeReader (void)
105{
106}
107
108void 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
121void 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
155void 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
171PosixTestProcess::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
181PosixTestProcess::~PosixTestProcess (void)
182{
183 delete m_process;
184}
185
186void 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.
Pyry Haulos5d4caa32016-01-13 13:42:13 -0800203 string cmdLine = de::FilePath(name).isAbsolutePath() ? name : de::FilePath::join(workingDir, name).getPath();
Jarkko Poyry3c827362014-09-02 11:48:52 +0300204 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
249void 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
264void 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
298bool PosixTestProcess::isRunning (void)
299{
300 if (m_process)
301 return m_process->isRunning();
302 else
303 return false;
304}
305
306int PosixTestProcess::getExitCode (void) const
307{
308 if (m_process)
309 return m_process->getExitCode();
310 else
311 return -1;
312}
313
314int 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