CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/headless/Headless.cpp
Views: 1401
1
// Headless version of PPSSPP, for testing using http://code.google.com/p/pspautotests/ .
2
// See headless.txt.
3
// To build on non-windows systems, just run CMake in the SDL directory, it will build both a normal ppsspp and the headless version.
4
// Example command line to run a test in the VS debugger (useful to debug failures):
5
// > --root pspautotests/tests/../ --compare --timeout=5 --graphics=software pspautotests/tests/cpu/cpu_alu/cpu_alu.prx
6
7
#include "ppsspp_config.h"
8
#include <cstdio>
9
#include <cstdlib>
10
#include <limits>
11
#if PPSSPP_PLATFORM(ANDROID)
12
#include <jni.h>
13
#endif
14
15
#include "Common/Profiler/Profiler.h"
16
#include "Common/System/NativeApp.h"
17
#include "Common/System/Request.h"
18
#include "Common/System/System.h"
19
20
#include "Common/CommonWindows.h"
21
#if PPSSPP_PLATFORM(WINDOWS)
22
#include <timeapi.h>
23
#else
24
#include <csignal>
25
#endif
26
#include "Common/CPUDetect.h"
27
#include "Common/File/VFS/VFS.h"
28
#include "Common/File/VFS/ZipFileReader.h"
29
#include "Common/File/VFS/DirectoryReader.h"
30
#include "Common/File/FileUtil.h"
31
#include "Common/GraphicsContext.h"
32
#include "Common/TimeUtil.h"
33
#include "Common/Thread/ThreadManager.h"
34
#include "Core/Config.h"
35
#include "Core/ConfigValues.h"
36
#include "Core/Core.h"
37
#include "Core/CoreTiming.h"
38
#include "Core/System.h"
39
#include "Core/WebServer.h"
40
#include "Core/HLE/sceUtility.h"
41
#include "Core/SaveState.h"
42
#include "GPU/Common/FramebufferManagerCommon.h"
43
#include "Common/Log.h"
44
#include "Common/Log/LogManager.h"
45
46
#include "Compare.h"
47
#include "HeadlessHost.h"
48
#if defined(_WIN32)
49
#include "WindowsHeadlessHost.h"
50
#elif defined(SDL)
51
#include "SDLHeadlessHost.h"
52
#endif
53
54
static HeadlessHost *g_headlessHost;
55
56
#if PPSSPP_PLATFORM(ANDROID)
57
JNIEnv *getEnv() {
58
return nullptr;
59
}
60
61
jclass findClass(const char *name) {
62
return nullptr;
63
}
64
65
bool System_AudioRecordingIsAvailable() { return false; }
66
bool System_AudioRecordingState() { return false; }
67
#endif
68
69
class PrintfLogger : public LogListener {
70
public:
71
void Log(const LogMessage &message) override {
72
switch (message.level) {
73
case LogLevel::LVERBOSE:
74
fprintf(stderr, "V %s", message.msg.c_str());
75
break;
76
case LogLevel::LDEBUG:
77
fprintf(stderr, "D %s", message.msg.c_str());
78
break;
79
case LogLevel::LINFO:
80
fprintf(stderr, "I %s", message.msg.c_str());
81
break;
82
case LogLevel::LERROR:
83
fprintf(stderr, "E %s", message.msg.c_str());
84
break;
85
case LogLevel::LWARNING:
86
fprintf(stderr, "W %s", message.msg.c_str());
87
break;
88
case LogLevel::LNOTICE:
89
default:
90
fprintf(stderr, "N %s", message.msg.c_str());
91
break;
92
}
93
}
94
};
95
96
// Temporary hacks around annoying linking errors.
97
void NativeFrame(GraphicsContext *graphicsContext) { }
98
void NativeResized() { }
99
100
std::string System_GetProperty(SystemProperty prop) { return ""; }
101
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) { return std::vector<std::string>(); }
102
int64_t System_GetPropertyInt(SystemProperty prop) {
103
if (prop == SYSPROP_SYSTEMVERSION)
104
return 31;
105
return -1;
106
}
107
float System_GetPropertyFloat(SystemProperty prop) { return -1.0f; }
108
bool System_GetPropertyBool(SystemProperty prop) {
109
switch (prop) {
110
case SYSPROP_CAN_JIT:
111
return true;
112
case SYSPROP_SKIP_UI:
113
return true;
114
default:
115
return false;
116
}
117
}
118
void System_Notify(SystemNotification notification) {}
119
void System_PostUIMessage(UIMessage message, const std::string &param) {}
120
bool System_MakeRequest(SystemRequestType type, int requestId, const std::string &param1, const std::string &param2, int64_t param3, int64_t param4) {
121
switch (type) {
122
case SystemRequestType::SEND_DEBUG_OUTPUT:
123
if (g_headlessHost) {
124
g_headlessHost->SendDebugOutput(param1);
125
return true;
126
}
127
return false;
128
case SystemRequestType::SEND_DEBUG_SCREENSHOT:
129
if (g_headlessHost) {
130
g_headlessHost->SendDebugScreenshot((const u8 *)param1.data(), (uint32_t)(param1.size() / param3), param3);
131
return true;
132
}
133
return false;
134
default:
135
return false;
136
}
137
}
138
void System_AskForPermission(SystemPermission permission) {}
139
PermissionStatus System_GetPermissionStatus(SystemPermission permission) { return PERMISSION_STATUS_GRANTED; }
140
void System_AudioGetDebugStats(char *buf, size_t bufSize) { if (buf) buf[0] = '\0'; }
141
void System_AudioClear() {}
142
void System_AudioPushSamples(const s32 *audio, int numSamples) {}
143
144
// TODO: To avoid having to define these here, these should probably be turned into system "requests".
145
bool NativeSaveSecret(std::string_view nameOfSecret, std::string_view data) { return false; }
146
std::string NativeLoadSecret(std::string_view nameOfSecret) {
147
return "";
148
}
149
150
int printUsage(const char *progname, const char *reason)
151
{
152
if (reason != NULL)
153
fprintf(stderr, "Error: %s\n\n", reason);
154
fprintf(stderr, "PPSSPP Headless\n");
155
fprintf(stderr, "This is primarily meant as a non-interactive test tool.\n\n");
156
fprintf(stderr, "Usage: %s file.elf... [options]\n\n", progname);
157
fprintf(stderr, "Options:\n");
158
fprintf(stderr, " -m, --mount umd.cso mount iso on umd1:\n");
159
fprintf(stderr, " -r, --root some/path mount path on host0: (elfs must be in here)\n");
160
fprintf(stderr, " -l, --log full log output, not just emulated printfs\n");
161
fprintf(stderr, " --debugger=PORT enable websocket debugger and break at start\n");
162
163
fprintf(stderr, " --graphics=BACKEND use a different gpu backend\n");
164
fprintf(stderr, " options: gles, software, directx9, etc.\n");
165
fprintf(stderr, " --screenshot=FILE compare against a screenshot\n");
166
fprintf(stderr, " --max-mse=NUMBER maximum allowed MSE error for screenshot\n");
167
fprintf(stderr, " --timeout=SECONDS abort test it if takes longer than SECONDS\n");
168
169
fprintf(stderr, " -v, --verbose show the full passed/failed result\n");
170
fprintf(stderr, " -i use the interpreter\n");
171
fprintf(stderr, " --ir use ir interpreter\n");
172
fprintf(stderr, " -j use jit (default)\n");
173
fprintf(stderr, " -c, --compare compare with output in file.expected\n");
174
fprintf(stderr, " --bench run multiple times and output speed\n");
175
fprintf(stderr, "\nSee headless.txt for details.\n");
176
177
return 1;
178
}
179
180
static HeadlessHost *getHost(GPUCore gpuCore) {
181
switch (gpuCore) {
182
case GPUCORE_SOFTWARE:
183
return new HeadlessHost();
184
#ifdef HEADLESSHOST_CLASS
185
default:
186
return new HEADLESSHOST_CLASS();
187
#else
188
default:
189
return new HeadlessHost();
190
#endif
191
}
192
}
193
194
struct AutoTestOptions {
195
double timeout;
196
double maxScreenshotError;
197
bool compare : 1;
198
bool verbose : 1;
199
bool bench : 1;
200
};
201
202
bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, const AutoTestOptions &opt) {
203
// Kinda ugly, trying to guesstimate the test name from filename...
204
currentTestName = GetTestName(coreParameter.fileToStart);
205
206
std::string output;
207
if (opt.compare || opt.bench)
208
coreParameter.collectDebugOutput = &output;
209
210
std::string error_string;
211
if (!PSP_InitStart(coreParameter, &error_string)) {
212
fprintf(stderr, "Failed to start '%s'. Error: %s\n", coreParameter.fileToStart.c_str(), error_string.c_str());
213
printf("TESTERROR\n");
214
TeamCityPrint("testIgnored name='%s' message='PRX/ELF missing'", currentTestName.c_str());
215
GitHubActionsPrint("error", "PRX/ELF missing for %s", currentTestName.c_str());
216
return false;
217
}
218
219
TeamCityPrint("testStarted name='%s' captureStandardOutput='true'", currentTestName.c_str());
220
221
if (opt.compare)
222
headlessHost->SetComparisonScreenshot(ExpectedScreenshotFromFilename(coreParameter.fileToStart), opt.maxScreenshotError);
223
224
while (!PSP_InitUpdate(&error_string))
225
sleep_ms(1);
226
if (!PSP_IsInited()) {
227
TeamCityPrint("testFailed name='%s' message='Startup failed'", currentTestName.c_str());
228
TeamCityPrint("testFinished name='%s'", currentTestName.c_str());
229
GitHubActionsPrint("error", "Test init failed for %s", currentTestName.c_str());
230
return false;
231
}
232
233
System_Notify(SystemNotification::BOOT_DONE);
234
235
Core_UpdateDebugStats((DebugOverlay)g_Config.iDebugOverlay == DebugOverlay::DEBUG_STATS || g_Config.bLogFrameDrops);
236
237
PSP_BeginHostFrame();
238
Draw::DrawContext *draw = coreParameter.graphicsContext ? coreParameter.graphicsContext->GetDrawContext() : nullptr;
239
if (draw)
240
draw->BeginFrame(Draw::DebugFlags::NONE);
241
242
bool passed = true;
243
double deadline = time_now_d() + opt.timeout;
244
coreState = coreParameter.startBreak ? CORE_STEPPING : CORE_RUNNING;
245
while (coreState == CORE_RUNNING || coreState == CORE_STEPPING)
246
{
247
int blockTicks = (int)usToCycles(1000000 / 10);
248
PSP_RunLoopFor(blockTicks);
249
250
// If we were rendering, this might be a nice time to do something about it.
251
if (coreState == CORE_NEXTFRAME) {
252
coreState = CORE_RUNNING;
253
headlessHost->SwapBuffers();
254
}
255
if (coreState == CORE_STEPPING && !coreParameter.startBreak) {
256
break;
257
}
258
if (time_now_d() > deadline) {
259
// Don't compare, print the output at least up to this point, and bail.
260
if (!opt.bench) {
261
printf("%s", output.c_str());
262
263
System_SendDebugOutput("TIMEOUT\n");
264
TeamCityPrint("testFailed name='%s' message='Test timeout'", currentTestName.c_str());
265
GitHubActionsPrint("error", "Test timeout for %s", currentTestName.c_str());
266
}
267
268
passed = false;
269
Core_Stop();
270
}
271
}
272
PSP_EndHostFrame();
273
274
if (draw) {
275
draw->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "Headless");
276
// Vulkan may get angry if we don't do a final present.
277
if (gpu)
278
gpu->CopyDisplayToOutput(true);
279
280
draw->EndFrame();
281
}
282
283
PSP_Shutdown();
284
285
if (!opt.bench)
286
headlessHost->FlushDebugOutput();
287
288
if (opt.compare && passed)
289
passed = CompareOutput(coreParameter.fileToStart, output, opt.verbose);
290
291
TeamCityPrint("testFinished name='%s'", currentTestName.c_str());
292
293
return passed;
294
}
295
296
std::vector<std::string> ReadFromListFile(const std::string &listFilename) {
297
std::vector<std::string> testFilenames;
298
char temp[2048]{};
299
300
if (listFilename == "-") {
301
while (scanf("%2047s", temp) == 1)
302
testFilenames.push_back(temp);
303
} else {
304
FILE *fp = File::OpenCFile(Path(listFilename), "rt");
305
if (!fp) {
306
fprintf(stderr, "Unable to open '%s' as a list file\n", listFilename.c_str());
307
return testFilenames;
308
}
309
310
while (fscanf(fp, "%2047s", temp) == 1)
311
testFilenames.push_back(temp);
312
fclose(fp);
313
}
314
315
return testFilenames;
316
}
317
318
int main(int argc, const char* argv[])
319
{
320
PROFILE_INIT();
321
TimeInit();
322
#if PPSSPP_PLATFORM(WINDOWS)
323
SetCleanExitOnAssert();
324
#else
325
// Ignore sigpipe.
326
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
327
perror("Unable to ignore SIGPIPE");
328
}
329
#endif
330
331
#if defined(_DEBUG) && defined(_MSC_VER)
332
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
333
#endif
334
335
AutoTestOptions testOptions{};
336
testOptions.timeout = std::numeric_limits<double>::infinity();
337
bool fullLog = false;
338
const char *stateToLoad = 0;
339
GPUCore gpuCore = GPUCORE_SOFTWARE;
340
CPUCore cpuCore = CPUCore::JIT;
341
int debuggerPort = -1;
342
bool newAtrac = false;
343
344
std::vector<std::string> testFilenames;
345
const char *mountIso = nullptr;
346
const char *mountRoot = nullptr;
347
const char *screenshotFilename = nullptr;
348
349
for (int i = 1; i < argc; i++)
350
{
351
if (!strcmp(argv[i], "-m") || !strcmp(argv[i], "--mount"))
352
{
353
if (++i >= argc)
354
return printUsage(argv[0], "Missing argument after -m");
355
mountIso = argv[i];
356
}
357
else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--root"))
358
{
359
if (++i >= argc)
360
return printUsage(argv[0], "Missing argument after -r");
361
mountRoot = argv[i];
362
}
363
else if (!strcmp(argv[i], "-l") || !strcmp(argv[i], "--log"))
364
fullLog = true;
365
else if (!strcmp(argv[i], "-i"))
366
cpuCore = CPUCore::INTERPRETER;
367
else if (!strcmp(argv[i], "-j"))
368
cpuCore = CPUCore::JIT;
369
else if (!strcmp(argv[i], "--jit-ir"))
370
cpuCore = CPUCore::JIT_IR;
371
else if (!strcmp(argv[i], "--ir"))
372
cpuCore = CPUCore::IR_INTERPRETER;
373
else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--compare"))
374
testOptions.compare = true;
375
else if (!strcmp(argv[i], "--bench"))
376
testOptions.bench = true;
377
else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))
378
testOptions.verbose = true;
379
else if (!strcmp(argv[i], "--new-atrac"))
380
newAtrac = true;
381
else if (!strncmp(argv[i], "--graphics=", strlen("--graphics=")) && strlen(argv[i]) > strlen("--graphics="))
382
{
383
const char *gpuName = argv[i] + strlen("--graphics=");
384
if (!strcasecmp(gpuName, "gles"))
385
gpuCore = GPUCORE_GLES;
386
// There used to be a separate "null" rendering core - just use software.
387
else if (!strcasecmp(gpuName, "software") || !strcasecmp(gpuName, "null"))
388
gpuCore = GPUCORE_SOFTWARE;
389
else if (!strcasecmp(gpuName, "directx9"))
390
gpuCore = GPUCORE_DIRECTX9;
391
else if (!strcasecmp(gpuName, "directx11"))
392
gpuCore = GPUCORE_DIRECTX11;
393
else if (!strcasecmp(gpuName, "vulkan"))
394
gpuCore = GPUCORE_VULKAN;
395
else
396
return printUsage(argv[0], "Unknown gpu backend specified after --graphics=. Allowed: software, directx9, directx11, vulkan, gles, null.");
397
}
398
// Default to GLES if no value selected.
399
else if (!strcmp(argv[i], "--graphics")) {
400
#if PPSSPP_API(ANY_GL)
401
gpuCore = GPUCORE_GLES;
402
#else
403
gpuCore = GPUCORE_DIRECTX11;
404
#endif
405
} else if (!strncmp(argv[i], "--screenshot=", strlen("--screenshot=")) && strlen(argv[i]) > strlen("--screenshot="))
406
screenshotFilename = argv[i] + strlen("--screenshot=");
407
else if (!strncmp(argv[i], "--timeout=", strlen("--timeout=")) && strlen(argv[i]) > strlen("--timeout="))
408
testOptions.timeout = strtod(argv[i] + strlen("--timeout="), nullptr);
409
else if (!strncmp(argv[i], "--max-mse=", strlen("--max-mse=")) && strlen(argv[i]) > strlen("--max-mse="))
410
testOptions.maxScreenshotError = strtod(argv[i] + strlen("--max-mse="), nullptr);
411
else if (!strncmp(argv[i], "--debugger=", strlen("--debugger=")) && strlen(argv[i]) > strlen("--debugger="))
412
debuggerPort = (int)strtoul(argv[i] + strlen("--debugger="), NULL, 10);
413
else if (!strcmp(argv[i], "--teamcity"))
414
teamCityMode = true;
415
else if (!strncmp(argv[i], "--state=", strlen("--state=")) && strlen(argv[i]) > strlen("--state="))
416
stateToLoad = argv[i] + strlen("--state=");
417
else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h"))
418
return printUsage(argv[0], NULL);
419
else
420
testFilenames.push_back(argv[i]);
421
}
422
423
if (testFilenames.size() == 1 && testFilenames[0][0] == '@')
424
testFilenames = ReadFromListFile(testFilenames[0].substr(1));
425
426
if (testFilenames.empty())
427
return printUsage(argv[0], argc <= 1 ? NULL : "No executables specified");
428
429
LogManager::Init(&g_Config.bEnableLogging);
430
LogManager *logman = LogManager::GetInstance();
431
432
PrintfLogger *printfLogger = new PrintfLogger();
433
434
for (int i = 0; i < (int)Log::NUMBER_OF_LOGS; i++) {
435
Log type = (Log)i;
436
logman->SetEnabled(type, fullLog);
437
logman->SetLogLevel(type, LogLevel::LDEBUG);
438
}
439
logman->AddListener(printfLogger);
440
441
// Needs to be after log so we don't interfere with test output.
442
g_threadManager.Init(cpu_info.num_cores, cpu_info.logical_cpu_count);
443
444
HeadlessHost *headlessHost = getHost(gpuCore);
445
g_headlessHost = headlessHost;
446
447
std::string error_string;
448
GraphicsContext *graphicsContext = nullptr;
449
bool glWorking = headlessHost->InitGraphics(&error_string, &graphicsContext, gpuCore);
450
451
CoreParameter coreParameter;
452
coreParameter.cpuCore = cpuCore;
453
coreParameter.gpuCore = glWorking ? gpuCore : GPUCORE_SOFTWARE;
454
coreParameter.graphicsContext = graphicsContext;
455
coreParameter.enableSound = false;
456
coreParameter.mountIso = mountIso ? Path(std::string(mountIso)) : Path();
457
coreParameter.mountRoot = mountRoot ? Path(std::string(mountRoot)) : Path();
458
coreParameter.startBreak = false;
459
coreParameter.headLess = true;
460
coreParameter.renderScaleFactor = 1;
461
coreParameter.renderWidth = 480;
462
coreParameter.renderHeight = 272;
463
coreParameter.pixelWidth = 480;
464
coreParameter.pixelHeight = 272;
465
coreParameter.fastForward = true;
466
467
g_Config.bEnableSound = false;
468
g_Config.bFirstRun = false;
469
g_Config.bIgnoreBadMemAccess = true;
470
// Never report from tests.
471
g_Config.sReportHost.clear();
472
g_Config.bAutoSaveSymbolMap = false;
473
g_Config.bSkipBufferEffects = false;
474
g_Config.iSkipGPUReadbackMode = (int)SkipGPUReadbackMode::NO_SKIP;
475
g_Config.bHardwareTransform = true;
476
g_Config.iAnisotropyLevel = 0; // When testing mipmapping we really don't want this.
477
g_Config.iMultiSampleLevel = 0;
478
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;
479
g_Config.iTimeFormat = PSP_SYSTEMPARAM_TIME_FORMAT_24HR;
480
g_Config.bEncryptSave = true;
481
g_Config.sNickName = "shadow";
482
g_Config.iTimeZone = 60;
483
g_Config.iDateFormat = PSP_SYSTEMPARAM_DATE_FORMAT_DDMMYYYY;
484
g_Config.iButtonPreference = PSP_SYSTEMPARAM_BUTTON_CROSS;
485
g_Config.iLockParentalLevel = 9;
486
g_Config.iInternalResolution = 1;
487
g_Config.iFastForwardMode = (int)FastForwardMode::CONTINUOUS;
488
g_Config.bEnableLogging = fullLog;
489
g_Config.bSoftwareSkinning = true;
490
g_Config.bVertexDecoderJit = true;
491
g_Config.bSoftwareRendering = coreParameter.gpuCore == GPUCORE_SOFTWARE;
492
g_Config.bSoftwareRenderingJit = true;
493
g_Config.iSplineBezierQuality = 2;
494
g_Config.bHighQualityDepth = true;
495
g_Config.bMemStickInserted = true;
496
g_Config.iMemStickSizeGB = 16;
497
g_Config.bEnableWlan = true;
498
g_Config.sMACAddress = "12:34:56:78:9A:BC";
499
g_Config.iFirmwareVersion = PSP_DEFAULT_FIRMWARE;
500
g_Config.iPSPModel = PSP_MODEL_SLIM;
501
g_Config.iGlobalVolume = VOLUME_FULL;
502
g_Config.iReverbVolume = VOLUME_FULL;
503
g_Config.internalDataDirectory.clear();
504
g_Config.bUseNewAtrac = newAtrac;
505
506
Path exePath = File::GetExeDirectory();
507
g_Config.flash0Directory = exePath / "assets/flash0";
508
509
#if PPSSPP_PLATFORM(WINDOWS)
510
// Mount a filesystem
511
g_Config.memStickDirectory = exePath / "memstick";
512
File::CreateDir(g_Config.memStickDirectory);
513
CreateSysDirectories();
514
#elif !PPSSPP_PLATFORM(ANDROID)
515
g_Config.memStickDirectory = Path(std::string(getenv("HOME"))) / ".ppsspp";
516
#endif
517
518
// Try to find the flash0 directory. Often this is from a subdirectory.
519
Path nextPath = exePath;
520
for (int i = 0; i < 5; ++i) {
521
if (File::Exists(nextPath / "assets/flash0")) {
522
g_Config.flash0Directory = nextPath / "assets/flash0";
523
#if !PPSSPP_PLATFORM(ANDROID)
524
g_VFS.Register("", new DirectoryReader(nextPath / "assets"));
525
#endif
526
break;
527
}
528
529
if (!nextPath.CanNavigateUp())
530
break;
531
nextPath = nextPath.NavigateUp();
532
}
533
534
if (screenshotFilename)
535
headlessHost->SetComparisonScreenshot(Path(std::string(screenshotFilename)), testOptions.maxScreenshotError);
536
headlessHost->SetWriteFailureScreenshot(!teamCityMode && !getenv("GITHUB_ACTIONS") && !testOptions.bench);
537
headlessHost->SetWriteDebugOutput(!testOptions.compare && !testOptions.bench);
538
539
#if PPSSPP_PLATFORM(ANDROID)
540
// For some reason the debugger installs it with this name?
541
if (File::Exists(Path("/data/app/org.ppsspp.ppsspp-2.apk"))) {
542
g_VFS.Register("", ZipFileReader::Create(Path("/data/app/org.ppsspp.ppsspp-2.apk"), "assets/"));
543
}
544
if (File::Exists(Path("/data/app/org.ppsspp.ppsspp.apk"))) {
545
g_VFS.Register("", ZipFileReader::Create(Path("/data/app/org.ppsspp.ppsspp.apk"), "assets/"));
546
}
547
#elif PPSSPP_PLATFORM(LINUX)
548
g_VFS.Register("", new DirectoryReader(Path("/usr/local/share/ppsspp/assets")));
549
g_VFS.Register("", new DirectoryReader(Path("/usr/local/share/games/ppsspp/assets")));
550
g_VFS.Register("", new DirectoryReader(Path("/usr/share/ppsspp/assets")));
551
g_VFS.Register("", new DirectoryReader(Path("/usr/share/games/ppsspp/assets")));
552
#endif
553
554
UpdateUIState(UISTATE_INGAME);
555
556
if (debuggerPort > 0) {
557
g_Config.iRemoteISOPort = debuggerPort;
558
coreParameter.startBreak = true;
559
StartWebServer(WebServerFlags::DEBUGGER);
560
}
561
562
if (stateToLoad != NULL)
563
SaveState::Load(Path(stateToLoad), -1);
564
565
std::vector<std::string> failedTests;
566
std::vector<std::string> passedTests;
567
for (size_t i = 0; i < testFilenames.size(); ++i)
568
{
569
coreParameter.fileToStart = Path(testFilenames[i]);
570
if (testOptions.compare)
571
printf("%s:\n", coreParameter.fileToStart.c_str());
572
bool passed = RunAutoTest(headlessHost, coreParameter, testOptions);
573
if (testOptions.bench) {
574
double st = time_now_d();
575
double deadline = st + testOptions.timeout;
576
double runs = 0.0;
577
for (int i = 0; i < 100; ++i) {
578
RunAutoTest(headlessHost, coreParameter, testOptions);
579
runs++;
580
581
if (time_now_d() > deadline)
582
break;
583
}
584
double et = time_now_d();
585
586
std::string testName = GetTestName(coreParameter.fileToStart);
587
printf(" %s - %f seconds average\n", testName.c_str(), (et - st) / runs);
588
}
589
if (testOptions.compare) {
590
std::string testName = GetTestName(coreParameter.fileToStart);
591
if (passed) {
592
passedTests.push_back(testName);
593
printf(" %s - passed!\n", testName.c_str());
594
}
595
else
596
failedTests.push_back(testName);
597
}
598
}
599
600
if (testOptions.compare) {
601
printf("%d tests passed, %d tests failed.\n", (int)passedTests.size(), (int)failedTests.size());
602
if (!failedTests.empty())
603
{
604
printf("Failed tests:\n");
605
for (size_t i = 0; i < failedTests.size(); ++i) {
606
printf(" %s\n", failedTests[i].c_str());
607
}
608
}
609
}
610
611
if (debuggerPort > 0) {
612
ShutdownWebServer();
613
}
614
615
headlessHost->ShutdownGraphics();
616
delete headlessHost;
617
headlessHost = nullptr;
618
g_headlessHost = nullptr;
619
620
g_VFS.Clear();
621
LogManager::Shutdown();
622
delete printfLogger;
623
624
#if PPSSPP_PLATFORM(WINDOWS)
625
timeEndPeriod(1);
626
#endif
627
628
g_threadManager.Teardown();
629
630
if (!failedTests.empty() && !teamCityMode)
631
return 1;
632
return 0;
633
}
634
635