blob: a6cc1f3a9565ca2d22f8844b636a69fac4d87de1 [file] [log] [blame]
/*-------------------------------------------------------------------------
* drawElements Quality Program Tester Core
* ----------------------------------------
*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief iOS App Wrapper.
*//*--------------------------------------------------------------------*/
#include "tcuIOSApp.h"
#include "tcuIOSPlatform.hh"
#include "tcuApp.hpp"
#include "tcuCommandLine.hpp"
#include "tcuRenderTarget.hpp"
#include "tcuTestLog.hpp"
#include "tcuResource.hpp"
#include "deThread.hpp"
#include "deMutex.hpp"
#include "xsExecutionServer.hpp"
#include "xsTestProcess.hpp"
#include "xsPosixFileReader.hpp"
#include "deFilePath.hpp"
#include "deClock.h"
#include "deMemory.h"
#include <string>
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>
#import <Foundation/NSBundle.h>
#import <Foundation/NSPathUtilities.h>
using std::string;
namespace
{
class TestThreadState
{
public:
enum State
{
STATE_NOT_RUNNING = 0,
STATE_RUNNING,
STATE_STOP_REQUESTED,
STATE_LAST
};
TestThreadState (void);
~TestThreadState (void);
void requestStart (const char* cmdLine);
void requestStop (void);
State getState (void);
void testExecFinished (void);
const char* getCommandLine (void) const { return m_cmdLine.c_str(); }
private:
de::Mutex m_lock;
State m_state;
std::string m_cmdLine;
};
TestThreadState::TestThreadState (void)
: m_state(STATE_NOT_RUNNING)
{
}
TestThreadState::~TestThreadState (void)
{
}
void TestThreadState::requestStart (const char* cmdLine)
{
de::ScopedLock stateLock(m_lock);
TCU_CHECK(m_state == STATE_NOT_RUNNING);
m_cmdLine = cmdLine;
m_state = STATE_RUNNING;
}
void TestThreadState::requestStop (void)
{
de::ScopedLock stateLock(m_lock);
if (m_state != STATE_NOT_RUNNING)
m_state = STATE_STOP_REQUESTED;
}
void TestThreadState::testExecFinished (void)
{
de::ScopedLock stateLock(m_lock);
m_state = STATE_NOT_RUNNING;
}
TestThreadState::State TestThreadState::getState (void)
{
de::ScopedLock stateLock(m_lock);
return m_state;
}
class LocalTestProcess : public xs::TestProcess
{
public:
LocalTestProcess (TestThreadState& state, const char* logFileName);
~LocalTestProcess (void);
void start (const char* name, const char* params, const char* workingDir, const char* caseList);
void terminate (void);
void cleanup (void);
bool isRunning (void);
int getExitCode (void) const { return 0; /* not available */ }
int readInfoLog (deUint8* dst, int numBytes) { DE_UNREF(dst && numBytes); return 0; /* not supported */ }
int readTestLog (deUint8* dst, int numBytes);
const char* getLogFileName (void) const { return m_logFileName.c_str(); }
private:
TestThreadState& m_state;
string m_logFileName;
xs::posix::FileReader m_logReader;
deUint64 m_processStartTime;
};
LocalTestProcess::LocalTestProcess (TestThreadState& state, const char* logFileName)
: m_state (state)
, m_logFileName (logFileName)
, m_logReader (xs::LOG_BUFFER_BLOCK_SIZE, xs::LOG_BUFFER_NUM_BLOCKS)
, m_processStartTime (0)
{
}
LocalTestProcess::~LocalTestProcess (void)
{
}
void LocalTestProcess::start (const char* name, const char* params, const char* workingDir, const char* caseList)
{
DE_UNREF(name && workingDir);
// Delete old log file.
if (deFileExists(m_logFileName.c_str()))
TCU_CHECK(deDeleteFile(m_logFileName.c_str()));
string cmdLine = string("deqp");
if (caseList && strlen(caseList) > 0)
cmdLine += string(" --deqp-caselist=") + caseList;
if (params && strlen(params) > 0)
cmdLine += string(" ") + params;
m_state.requestStart(cmdLine.c_str());
m_processStartTime = deGetMicroseconds();
}
void LocalTestProcess::terminate (void)
{
m_state.requestStop();
}
void LocalTestProcess::cleanup (void)
{
if (isRunning())
{
m_state.requestStop();
// Wait until stopped.
while (isRunning())
deSleep(50);
}
m_logReader.stop();
}
bool LocalTestProcess::isRunning (void)
{
return m_state.getState() != TestThreadState::STATE_NOT_RUNNING;
}
int LocalTestProcess::readTestLog (deUint8* dst, int numBytes)
{
if (!m_logReader.isRunning())
{
if (deGetMicroseconds() - m_processStartTime > xs::LOG_FILE_TIMEOUT*1000)
{
// Timeout, kill execution.
terminate();
return 0; // \todo [2013-08-13 pyry] Throw exception?
}
if (!deFileExists(m_logFileName.c_str()))
return 0;
// Start reader.
m_logReader.start(m_logFileName.c_str());
}
DE_ASSERT(m_logReader.isRunning());
return m_logReader.read(dst, numBytes);
}
class ServerThread : public de::Thread
{
public:
ServerThread (xs::TestProcess* testProcess, int port);
~ServerThread (void);
void run (void);
void stop (void);
private:
xs::ExecutionServer m_server;
bool m_isRunning;
};
ServerThread::ServerThread (xs::TestProcess* testProcess, int port)
: m_server (testProcess, DE_SOCKETFAMILY_INET4, port, xs::ExecutionServer::RUNMODE_FOREVER)
, m_isRunning (false)
{
}
ServerThread::~ServerThread (void)
{
stop();
}
void ServerThread::run (void)
{
m_isRunning = true;
m_server.runServer();
}
void ServerThread::stop (void)
{
if (m_isRunning)
{
m_server.stopServer();
join();
m_isRunning = false;
}
}
string getAppBundleDir (void)
{
NSString* dataPath = [[NSBundle mainBundle] bundlePath];
const char* utf8Str = [dataPath UTF8String];
return string(utf8Str);
}
string getAppDocumentsDir (void)
{
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* docPath = [paths objectAtIndex:0];
const char* utf8Str = [docPath UTF8String];
return string(utf8Str);
}
} // anonymous
struct tcuIOSApp_s
{
public:
tcuIOSApp_s (void* view);
~tcuIOSApp_s (void);
void iterate (void);
protected:
void createTestApp (void);
void destroyTestApp (void);
TestThreadState m_state;
LocalTestProcess m_testProcess;
ServerThread m_server;
tcu::DirArchive m_archive;
tcu::ios::ScreenManager m_screenManager;
tcu::ios::Platform m_platform;
tcu::TestLog* m_log;
tcu::CommandLine* m_cmdLine;
tcu::App* m_app;
};
tcuIOSApp_s::tcuIOSApp_s (void* view)
: m_testProcess (m_state, de::FilePath::join(getAppDocumentsDir(), "TestResults.qpa").getPath())
, m_server (&m_testProcess, 50016)
, m_archive (getAppBundleDir().c_str())
, m_screenManager ((tcuEAGLView*)view)
, m_platform (&m_screenManager)
, m_log (DE_NULL)
, m_cmdLine (DE_NULL)
, m_app (DE_NULL)
{
// Start server.
m_server.start();
}
tcuIOSApp_s::~tcuIOSApp_s (void)
{
m_server.stop();
destroyTestApp();
}
void tcuIOSApp::createTestApp (void)
{
DE_ASSERT(!m_app && !m_log && !m_cmdLine && !m_platform);
try
{
m_log = new tcu::TestLog(m_testProcess.getLogFileName());
m_cmdLine = new tcu::CommandLine(m_state.getCommandLine());
m_app = new tcu::App(m_platform, m_archive, *m_log, *m_cmdLine);
}
catch (const std::exception& e)
{
destroyTestApp();
tcu::die("%s", e.what());
}
}
void tcuIOSApp::destroyTestApp (void)
{
delete m_app;
delete m_cmdLine;
delete m_log;
m_app = DE_NULL;
m_cmdLine = DE_NULL;
m_log = DE_NULL;
}
void tcuIOSApp::iterate (void)
{
TestThreadState::State curState = m_state.getState();
if (curState == TestThreadState::STATE_RUNNING)
{
if (!m_app)
createTestApp();
TCU_CHECK(m_app);
if (!m_app->iterate())
{
destroyTestApp();
m_state.testExecFinished();
}
}
else if (curState == TestThreadState::STATE_STOP_REQUESTED)
{
destroyTestApp();
m_state.testExecFinished();
}
// else wait until state has changed?
}
tcuIOSApp* tcuIOSApp_create (void* view)
{
try
{
return new tcuIOSApp(view);
}
catch (const std::exception& e)
{
tcu::die("FATAL ERROR: %s", e.what());
return DE_NULL;
}
}
void tcuIOSApp_destroy (tcuIOSApp* app)
{
delete app;
}
deBool tcuIOSApp_iterate (tcuIOSApp* app)
{
try
{
app->iterate();
return DE_TRUE;
}
catch (const std::exception& e)
{
tcu::print("FATAL ERROR: %s\n", e.what());
return DE_FALSE;
}
}