xref: /aosp_15_r20/external/deqp/framework/platform/ios/tcuIOSApp.mm (revision 35238bce31c2a825756842865a792f8cf7f89930)
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