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