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 47using std::string; 48 49namespace 50{ 51 52class TestThreadState 53{ 54public: 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 75private: 76 de::Mutex m_lock; 77 78 State m_state; 79 std::string m_cmdLine; 80}; 81 82TestThreadState::TestThreadState (void) 83 : m_state(STATE_NOT_RUNNING) 84{ 85} 86 87TestThreadState::~TestThreadState (void) 88{ 89} 90 91void 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 101void 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 109void TestThreadState::testExecFinished (void) 110{ 111 de::ScopedLock stateLock(m_lock); 112 m_state = STATE_NOT_RUNNING; 113} 114 115TestThreadState::State TestThreadState::getState (void) 116{ 117 de::ScopedLock stateLock(m_lock); 118 return m_state; 119} 120 121class LocalTestProcess : public xs::TestProcess 122{ 123public: 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 139private: 140 TestThreadState& m_state; 141 string m_logFileName; 142 xs::posix::FileReader m_logReader; 143 deUint64 m_processStartTime; 144}; 145 146LocalTestProcess::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 154LocalTestProcess::~LocalTestProcess (void) 155{ 156} 157 158void 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 177void LocalTestProcess::terminate (void) 178{ 179 m_state.requestStop(); 180} 181 182void 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 196bool LocalTestProcess::isRunning (void) 197{ 198 return m_state.getState() != TestThreadState::STATE_NOT_RUNNING; 199} 200 201int 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 223class ServerThread : public de::Thread 224{ 225public: 226 ServerThread (xs::TestProcess* testProcess, int port); 227 ~ServerThread (void); 228 229 void run (void); 230 void stop (void); 231 232private: 233 xs::ExecutionServer m_server; 234 bool m_isRunning; 235}; 236 237ServerThread::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 243ServerThread::~ServerThread (void) 244{ 245 stop(); 246} 247 248void ServerThread::run (void) 249{ 250 m_isRunning = true; 251 m_server.runServer(); 252} 253 254void ServerThread::stop (void) 255{ 256 if (m_isRunning) 257 { 258 m_server.stopServer(); 259 join(); 260 m_isRunning = false; 261 } 262} 263 264string getAppBundleDir (void) 265{ 266 NSString* dataPath = [[NSBundle mainBundle] bundlePath]; 267 const char* utf8Str = [dataPath UTF8String]; 268 269 return string(utf8Str); 270} 271 272string 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 283struct tcuIOSApp_s 284{ 285public: 286 tcuIOSApp_s (void* view); 287 ~tcuIOSApp_s (void); 288 289 void iterate (void); 290 291protected: 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 308tcuIOSApp_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 322tcuIOSApp_s::~tcuIOSApp_s (void) 323{ 324 m_server.stop(); 325 destroyTestApp(); 326} 327 328void 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 345void 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 355void 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 380tcuIOSApp* 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 393void tcuIOSApp_destroy (tcuIOSApp* app) 394{ 395 delete app; 396} 397 398deBool 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