Home | History | Annotate | Download | only in ios
      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