1/*------------------------------------------------------------------------- 2 * drawElements Quality Program Tester Core 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 iOS App Wrapper. 22 *//*--------------------------------------------------------------------*/ 23 24#include "tcuIOSApp.h" 25#include "deClock.h" 26#include "deFilePath.hpp" 27#include "deMemory.h" 28#include "deMutex.hpp" 29#include "deThread.hpp" 30#include "tcuApp.hpp" 31#include "tcuCommandLine.hpp" 32#include "tcuIOSPlatform.hh" 33#include "tcuRenderTarget.hpp" 34#include "tcuResource.hpp" 35#include "tcuTestLog.hpp" 36#include "xsExecutionServer.hpp" 37#include "xsPosixFileReader.hpp" 38#include "xsTestProcess.hpp" 39 40#include <string> 41 42#import <Foundation/NSBundle.h> 43#import <Foundation/NSObject.h> 44#import <Foundation/NSPathUtilities.h> 45#import <Foundation/NSString.h> 46 47using std::string; 48 49namespace 50{ 51 52class TestThreadState 53{ 54 public: 55 enum State 56 { 57 STATE_NOT_RUNNING = 0, 58 STATE_RUNNING, 59 STATE_STOP_REQUESTED, 60 61 STATE_LAST 62 }; 63 64 TestThreadState(void); 65 ~TestThreadState(void); 66 67 void requestStart(const char *cmdLine); 68 void requestStop(void); 69 State getState(void); 70 71 void testExecFinished(void); 72 73 const char *getCommandLine(void) const { return m_cmdLine.c_str(); } 74 75 private: 76 de::Mutex m_lock; 77 78 State m_state; 79 std::string m_cmdLine; 80}; 81 82TestThreadState::TestThreadState(void) : m_state(STATE_NOT_RUNNING) {} 83 84TestThreadState::~TestThreadState(void) {} 85 86void TestThreadState::requestStart(const char *cmdLine) 87{ 88 de::ScopedLock stateLock(m_lock); 89 90 TCU_CHECK(m_state == STATE_NOT_RUNNING); 91 92 m_cmdLine = cmdLine; 93 m_state = STATE_RUNNING; 94} 95 96void TestThreadState::requestStop(void) 97{ 98 de::ScopedLock stateLock(m_lock); 99 100 if (m_state != STATE_NOT_RUNNING) 101 m_state = STATE_STOP_REQUESTED; 102} 103 104void TestThreadState::testExecFinished(void) 105{ 106 de::ScopedLock stateLock(m_lock); 107 m_state = STATE_NOT_RUNNING; 108} 109 110TestThreadState::State TestThreadState::getState(void) 111{ 112 de::ScopedLock stateLock(m_lock); 113 return m_state; 114} 115 116class LocalTestProcess : public xs::TestProcess 117{ 118 public: 119 LocalTestProcess(TestThreadState &state, const char *logFileName); 120 ~LocalTestProcess(void); 121 122 void start(const char *name, const char *params, const char *workingDir, 123 const char *caseList); 124 void terminate(void); 125 void cleanup(void); 126 127 bool isRunning(void); 128 int getExitCode(void) const { return 0; /* not available */ } 129 130 int readInfoLog(uint8_t *dst, int numBytes) 131 { 132 DE_UNREF(dst && numBytes); 133 return 0; /* not supported */ 134 } 135 int readTestLog(uint8_t *dst, int numBytes); 136 137 const char *getLogFileName(void) const { return m_logFileName.c_str(); } 138 139 private: 140 TestThreadState &m_state; 141 string m_logFileName; 142 xs::posix::FileReader m_logReader; 143 uint64_t m_processStartTime; 144}; 145 146LocalTestProcess::LocalTestProcess(TestThreadState &state, 147 const char *logFileName) 148 : m_state(state), m_logFileName(logFileName), 149 m_logReader(xs::LOG_BUFFER_BLOCK_SIZE, xs::LOG_BUFFER_NUM_BLOCKS), 150 m_processStartTime(0) 151{ 152} 153 154LocalTestProcess::~LocalTestProcess(void) {} 155 156void LocalTestProcess::start(const char *name, const char *params, 157 const char *workingDir, const char *caseList) 158{ 159 DE_UNREF(name && workingDir); 160 161 // Delete old log file. 162 if (deFileExists(m_logFileName.c_str())) 163 TCU_CHECK(deDeleteFile(m_logFileName.c_str())); 164 165 string cmdLine = string("deqp"); 166 if (caseList && strlen(caseList) > 0) 167 cmdLine += string(" --deqp-caselist=") + caseList; 168 169 if (params && strlen(params) > 0) 170 cmdLine += string(" ") + params; 171 172 m_state.requestStart(cmdLine.c_str()); 173 m_processStartTime = deGetMicroseconds(); 174} 175 176void LocalTestProcess::terminate(void) { m_state.requestStop(); } 177 178void LocalTestProcess::cleanup(void) 179{ 180 if (isRunning()) 181 { 182 m_state.requestStop(); 183 184 // Wait until stopped. 185 while (isRunning()) 186 deSleep(50); 187 } 188 189 m_logReader.stop(); 190} 191 192bool LocalTestProcess::isRunning(void) 193{ 194 return m_state.getState() != TestThreadState::STATE_NOT_RUNNING; 195} 196 197int LocalTestProcess::readTestLog(uint8_t *dst, int numBytes) 198{ 199 if (!m_logReader.isRunning()) 200 { 201 if (deGetMicroseconds() - m_processStartTime > 202 xs::LOG_FILE_TIMEOUT * 1000) 203 { 204 // Timeout, kill execution. 205 terminate(); 206 return 0; // \todo [2013-08-13 pyry] Throw exception? 207 } 208 209 if (!deFileExists(m_logFileName.c_str())) 210 return 0; 211 212 // Start reader. 213 m_logReader.start(m_logFileName.c_str()); 214 } 215 216 DE_ASSERT(m_logReader.isRunning()); 217 return m_logReader.read(dst, numBytes); 218} 219 220class ServerThread : public de::Thread 221{ 222 public: 223 ServerThread(xs::TestProcess *testProcess, int port); 224 ~ServerThread(void); 225 226 void run(void); 227 void stop(void); 228 229 private: 230 xs::ExecutionServer m_server; 231 bool m_isRunning; 232}; 233 234ServerThread::ServerThread(xs::TestProcess *testProcess, int port) 235 : m_server(testProcess, DE_SOCKETFAMILY_INET4, port, 236 xs::ExecutionServer::RUNMODE_FOREVER), 237 m_isRunning(false) 238{ 239} 240 241ServerThread::~ServerThread(void) { stop(); } 242 243void ServerThread::run(void) 244{ 245 m_isRunning = true; 246 m_server.runServer(); 247} 248 249void ServerThread::stop(void) 250{ 251 if (m_isRunning) 252 { 253 m_server.stopServer(); 254 join(); 255 m_isRunning = false; 256 } 257} 258 259string getAppBundleDir(void) 260{ 261 NSString *dataPath = [[NSBundle mainBundle] bundlePath]; 262 const char *utf8Str = [dataPath UTF8String]; 263 264 return string(utf8Str); 265} 266 267string getAppDocumentsDir(void) 268{ 269 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, 270 NSUserDomainMask, YES); 271 NSString *docPath = [paths objectAtIndex:0]; 272 const char *utf8Str = [docPath UTF8String]; 273 274 return string(utf8Str); 275} 276 277} // namespace 278 279struct tcuIOSApp_s 280{ 281 public: 282 tcuIOSApp_s(void *view); 283 ~tcuIOSApp_s(void); 284 285 void iterate(void); 286 287 protected: 288 void createTestApp(void); 289 void destroyTestApp(void); 290 291 TestThreadState m_state; 292 LocalTestProcess m_testProcess; 293 ServerThread m_server; 294 295 tcu::DirArchive m_archive; 296 tcu::ios::ScreenManager m_screenManager; 297 tcu::ios::Platform m_platform; 298 299 tcu::TestLog *m_log; 300 tcu::CommandLine *m_cmdLine; 301 tcu::App *m_app; 302}; 303 304tcuIOSApp_s::tcuIOSApp_s(void *view) 305 : m_testProcess(m_state, 306 de::FilePath::join(getAppDocumentsDir(), "TestResults.qpa") 307 .getPath()), 308 m_server(&m_testProcess, 50016), m_archive(getAppBundleDir().c_str()), 309 m_screenManager((tcuEAGLView *)view), m_platform(&m_screenManager), 310 m_log(DE_NULL), m_cmdLine(DE_NULL), m_app(DE_NULL) 311{ 312 // Start server. 313 m_server.start(); 314} 315 316tcuIOSApp_s::~tcuIOSApp_s(void) 317{ 318 m_server.stop(); 319 destroyTestApp(); 320} 321 322void tcuIOSApp::createTestApp(void) 323{ 324 DE_ASSERT(!m_app && !m_log && !m_cmdLine && !m_platform); 325 326 try 327 { 328 m_log = new tcu::TestLog(m_testProcess.getLogFileName()); 329 m_cmdLine = new tcu::CommandLine(m_state.getCommandLine()); 330 m_app = new tcu::App(m_platform, m_archive, *m_log, *m_cmdLine); 331 } 332 catch (const std::exception &e) 333 { 334 destroyTestApp(); 335 tcu::die("%s", e.what()); 336 } 337} 338 339void tcuIOSApp::destroyTestApp(void) 340{ 341 delete m_app; 342 delete m_cmdLine; 343 delete m_log; 344 m_app = DE_NULL; 345 m_cmdLine = DE_NULL; 346 m_log = DE_NULL; 347} 348 349void tcuIOSApp::iterate(void) 350{ 351 TestThreadState::State curState = m_state.getState(); 352 353 if (curState == TestThreadState::STATE_RUNNING) 354 { 355 if (!m_app) 356 createTestApp(); 357 358 TCU_CHECK(m_app); 359 360 if (!m_app->iterate()) 361 { 362 destroyTestApp(); 363 m_state.testExecFinished(); 364 } 365 } 366 else if (curState == TestThreadState::STATE_STOP_REQUESTED) 367 { 368 destroyTestApp(); 369 m_state.testExecFinished(); 370 } 371 // else wait until state has changed? 372} 373 374tcuIOSApp *tcuIOSApp_create(void *view) 375{ 376 try 377 { 378 return new tcuIOSApp(view); 379 } 380 catch (const std::exception &e) 381 { 382 tcu::die("FATAL ERROR: %s", e.what()); 383 return DE_NULL; 384 } 385} 386 387void tcuIOSApp_destroy(tcuIOSApp *app) { delete app; } 388 389bool tcuIOSApp_iterate(tcuIOSApp *app) 390{ 391 try 392 { 393 app->iterate(); 394 return true; 395 } 396 catch (const std::exception &e) 397 { 398 tcu::print("FATAL ERROR: %s\n", e.what()); 399 return false; 400 } 401} 402