2012-11-01 16:19:01 +01:00
|
|
|
// Headless version of PPSSPP, for testing using http://code.google.com/p/pspautotests/ .
|
|
|
|
// See headless.txt.
|
|
|
|
// To build on non-windows systems, just run CMake in the SDL directory, it will build both a normal ppsspp and the headless version.
|
2022-11-23 08:52:46 +01:00
|
|
|
// Example command line to run a test in the VS debugger (useful to debug failures):
|
|
|
|
// > --root pspautotests/tests/../ --compare --timeout=5 --graphics=software pspautotests/tests/cpu/cpu_alu/cpu_alu.prx
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2021-01-10 12:16:02 -08:00
|
|
|
#include "ppsspp_config.h"
|
2013-09-16 08:36:10 -07:00
|
|
|
#include <cstdio>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <limits>
|
2021-01-10 12:16:02 -08:00
|
|
|
#if PPSSPP_PLATFORM(ANDROID)
|
2020-04-05 15:23:13 -07:00
|
|
|
#include <jni.h>
|
|
|
|
#endif
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2020-10-04 10:04:01 +02:00
|
|
|
#include "Common/Profiler/Profiler.h"
|
2020-10-04 10:30:18 +02:00
|
|
|
#include "Common/System/NativeApp.h"
|
2023-03-25 17:34:29 -07:00
|
|
|
#include "Common/System/Request.h"
|
2020-10-04 10:30:18 +02:00
|
|
|
#include "Common/System/System.h"
|
2020-08-15 20:53:08 +02:00
|
|
|
|
2022-01-30 08:23:14 -08:00
|
|
|
#include "Common/CommonWindows.h"
|
|
|
|
#if PPSSPP_PLATFORM(WINDOWS)
|
|
|
|
#include <timeapi.h>
|
2022-04-02 16:07:51 -07:00
|
|
|
#else
|
|
|
|
#include <csignal>
|
2022-01-30 08:23:14 -08:00
|
|
|
#endif
|
2021-06-13 10:16:53 +02:00
|
|
|
#include "Common/CPUDetect.h"
|
2020-10-04 09:29:36 +02:00
|
|
|
#include "Common/File/VFS/VFS.h"
|
2023-03-06 15:30:39 +01:00
|
|
|
#include "Common/File/VFS/ZipFileReader.h"
|
|
|
|
#include "Common/File/VFS/DirectoryReader.h"
|
2020-10-04 20:48:47 +02:00
|
|
|
#include "Common/File/FileUtil.h"
|
2017-04-20 21:10:59 -07:00
|
|
|
#include "Common/GraphicsContext.h"
|
2020-08-15 20:53:08 +02:00
|
|
|
#include "Common/TimeUtil.h"
|
2021-06-13 10:16:53 +02:00
|
|
|
#include "Common/Thread/ThreadManager.h"
|
2013-02-18 23:25:06 +01:00
|
|
|
#include "Core/Config.h"
|
2018-06-16 18:42:31 -07:00
|
|
|
#include "Core/ConfigValues.h"
|
2013-02-18 23:25:06 +01:00
|
|
|
#include "Core/Core.h"
|
|
|
|
#include "Core/CoreTiming.h"
|
|
|
|
#include "Core/System.h"
|
2021-01-16 17:41:42 -08:00
|
|
|
#include "Core/WebServer.h"
|
2013-04-10 22:17:43 -07:00
|
|
|
#include "Core/HLE/sceUtility.h"
|
2014-03-25 00:46:21 -07:00
|
|
|
#include "Core/SaveState.h"
|
2020-08-03 23:17:22 +02:00
|
|
|
#include "GPU/Common/FramebufferManagerCommon.h"
|
2012-11-01 16:19:01 +01:00
|
|
|
#include "Log.h"
|
|
|
|
#include "LogManager.h"
|
|
|
|
|
2013-02-08 00:22:37 -08:00
|
|
|
#include "Compare.h"
|
2012-12-29 19:36:03 -08:00
|
|
|
#include "StubHost.h"
|
2017-06-18 12:15:59 -07:00
|
|
|
#if defined(_WIN32)
|
2012-12-29 19:36:03 -08:00
|
|
|
#include "WindowsHeadlessHost.h"
|
2017-06-18 12:15:59 -07:00
|
|
|
#elif defined(SDL)
|
|
|
|
#include "SDLHeadlessHost.h"
|
2012-12-29 19:36:03 -08:00
|
|
|
#endif
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2023-03-25 17:47:25 -07:00
|
|
|
static HeadlessHost *g_headlessHost;
|
|
|
|
|
2021-01-10 12:16:02 -08:00
|
|
|
#if PPSSPP_PLATFORM(ANDROID)
|
2020-04-05 15:23:13 -07:00
|
|
|
JNIEnv *getEnv() {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
jclass findClass(const char *name) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2021-01-10 12:16:02 -08:00
|
|
|
|
2023-03-21 10:42:23 +01:00
|
|
|
bool System_AudioRecordingIsAvailable() { return false; }
|
|
|
|
bool System_AudioRecordingState() { return false; }
|
2020-04-05 15:23:13 -07:00
|
|
|
#endif
|
|
|
|
|
2017-03-18 10:47:10 +01:00
|
|
|
class PrintfLogger : public LogListener {
|
2012-11-09 13:40:09 +01:00
|
|
|
public:
|
2022-12-10 20:32:12 -08:00
|
|
|
void Log(const LogMessage &message) override {
|
2017-03-18 10:47:10 +01:00
|
|
|
switch (message.level) {
|
2013-03-10 22:31:47 -07:00
|
|
|
case LogTypes::LVERBOSE:
|
2017-03-18 10:47:10 +01:00
|
|
|
fprintf(stderr, "V %s", message.msg.c_str());
|
2013-03-10 22:31:47 -07:00
|
|
|
break;
|
2012-11-09 13:40:09 +01:00
|
|
|
case LogTypes::LDEBUG:
|
2017-03-18 10:47:10 +01:00
|
|
|
fprintf(stderr, "D %s", message.msg.c_str());
|
2012-11-09 13:40:09 +01:00
|
|
|
break;
|
|
|
|
case LogTypes::LINFO:
|
2017-03-18 10:47:10 +01:00
|
|
|
fprintf(stderr, "I %s", message.msg.c_str());
|
2012-11-09 13:40:09 +01:00
|
|
|
break;
|
|
|
|
case LogTypes::LERROR:
|
2017-03-18 10:47:10 +01:00
|
|
|
fprintf(stderr, "E %s", message.msg.c_str());
|
2012-11-09 13:40:09 +01:00
|
|
|
break;
|
|
|
|
case LogTypes::LWARNING:
|
2017-03-18 10:47:10 +01:00
|
|
|
fprintf(stderr, "W %s", message.msg.c_str());
|
2012-11-09 13:40:09 +01:00
|
|
|
break;
|
|
|
|
case LogTypes::LNOTICE:
|
|
|
|
default:
|
2017-03-18 10:47:10 +01:00
|
|
|
fprintf(stderr, "N %s", message.msg.c_str());
|
2012-11-09 13:40:09 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-08-17 16:07:14 +02:00
|
|
|
// Temporary hacks around annoying linking errors.
|
2017-03-14 22:01:18 -07:00
|
|
|
void NativeUpdate() { }
|
2015-12-31 16:59:40 +01:00
|
|
|
void NativeRender(GraphicsContext *graphicsContext) { }
|
2014-02-10 15:14:45 +01:00
|
|
|
void NativeResized() { }
|
2013-03-29 22:39:37 -07:00
|
|
|
|
2013-09-04 12:07:42 +02:00
|
|
|
std::string System_GetProperty(SystemProperty prop) { return ""; }
|
2021-01-06 16:37:04 +01:00
|
|
|
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) { return std::vector<std::string>(); }
|
2022-09-03 21:05:58 -07:00
|
|
|
int System_GetPropertyInt(SystemProperty prop) {
|
|
|
|
if (prop == SYSPROP_SYSTEMVERSION)
|
|
|
|
return 31;
|
|
|
|
return -1;
|
|
|
|
}
|
2021-01-06 16:37:04 +01:00
|
|
|
float System_GetPropertyFloat(SystemProperty prop) { return -1.0f; }
|
2023-03-06 15:30:39 +01:00
|
|
|
bool System_GetPropertyBool(SystemProperty prop) {
|
2021-02-21 22:02:11 +01:00
|
|
|
switch (prop) {
|
|
|
|
case SYSPROP_CAN_JIT:
|
|
|
|
return true;
|
2023-03-21 11:42:55 +01:00
|
|
|
case SYSPROP_SKIP_UI:
|
|
|
|
return true;
|
2021-02-21 22:02:11 +01:00
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2023-03-21 11:10:09 +01:00
|
|
|
void System_Notify(SystemNotification notification) {}
|
2023-03-24 21:39:02 +01:00
|
|
|
void System_PostUIMessage(const std::string &message, const std::string ¶m) {}
|
2023-03-25 10:29:18 +01:00
|
|
|
void System_NotifyUserMessage(const std::string &message, float duration, u32 color, const char *id) {}
|
2023-03-25 17:34:29 -07:00
|
|
|
bool System_MakeRequest(SystemRequestType type, int requestId, const std::string ¶m1, const std::string ¶m2, int param3) {
|
|
|
|
switch (type) {
|
|
|
|
case SystemRequestType::SEND_DEBUG_OUTPUT:
|
2023-03-25 17:47:25 -07:00
|
|
|
if (g_headlessHost) {
|
|
|
|
g_headlessHost->SendDebugOutput(param1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2023-03-25 17:34:29 -07:00
|
|
|
case SystemRequestType::SEND_DEBUG_SCREENSHOT:
|
2023-03-25 17:47:25 -07:00
|
|
|
if (g_headlessHost) {
|
|
|
|
g_headlessHost->SendDebugScreenshot((const u8 *)param1.data(), (uint32_t)(param1.size() / param3), param3);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2023-03-25 17:34:29 -07:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2020-03-08 18:59:17 -07:00
|
|
|
void System_InputBoxGetString(const std::string &title, const std::string &defaultValue, std::function<void(bool, const std::string &)> cb) { cb(false, ""); }
|
2015-12-17 22:41:50 +01:00
|
|
|
void System_AskForPermission(SystemPermission permission) {}
|
|
|
|
PermissionStatus System_GetPermissionStatus(SystemPermission permission) { return PERMISSION_STATUS_GRANTED; }
|
2023-03-24 17:19:57 +01:00
|
|
|
void System_AudioGetDebugStats(char *buf, size_t bufSize) { if (buf) buf[0] = '\0'; }
|
|
|
|
void System_AudioClear() {}
|
|
|
|
void System_AudioPushSamples(const s32 *audio, int numSamples) {}
|
2013-09-04 12:07:42 +02:00
|
|
|
|
2014-04-19 12:52:43 -07:00
|
|
|
int printUsage(const char *progname, const char *reason)
|
2012-11-01 16:19:01 +01:00
|
|
|
{
|
2012-11-24 21:45:32 -08:00
|
|
|
if (reason != NULL)
|
|
|
|
fprintf(stderr, "Error: %s\n\n", reason);
|
2012-11-01 16:19:01 +01:00
|
|
|
fprintf(stderr, "PPSSPP Headless\n");
|
2012-12-05 23:47:09 -08:00
|
|
|
fprintf(stderr, "This is primarily meant as a non-interactive test tool.\n\n");
|
2013-09-16 23:58:42 -07:00
|
|
|
fprintf(stderr, "Usage: %s file.elf... [options]\n\n", progname);
|
2012-11-24 21:45:32 -08:00
|
|
|
fprintf(stderr, "Options:\n");
|
2014-04-19 12:52:43 -07:00
|
|
|
fprintf(stderr, " -m, --mount umd.cso mount iso on umd1:\n");
|
|
|
|
fprintf(stderr, " -r, --root some/path mount path on host0: (elfs must be in here)\n");
|
2012-11-24 21:45:32 -08:00
|
|
|
fprintf(stderr, " -l, --log full log output, not just emulated printfs\n");
|
2022-09-18 06:40:15 -07:00
|
|
|
fprintf(stderr, " --debugger=PORT enable websocket debugger and break at start\n");
|
2012-12-29 21:01:13 -08:00
|
|
|
|
2022-09-17 10:05:11 -07:00
|
|
|
fprintf(stderr, " --graphics=BACKEND use a different gpu backend\n");
|
|
|
|
fprintf(stderr, " options: gles, software, directx9, etc.\n");
|
|
|
|
fprintf(stderr, " --screenshot=FILE compare against a screenshot\n");
|
2022-09-17 11:24:15 -07:00
|
|
|
fprintf(stderr, " --max-mse=NUMBER maximum allowed MSE error for screenshot\n");
|
2013-09-16 08:36:10 -07:00
|
|
|
fprintf(stderr, " --timeout=SECONDS abort test it if takes longer than SECONDS\n");
|
2012-12-29 21:01:13 -08:00
|
|
|
|
2013-09-17 00:15:19 -07:00
|
|
|
fprintf(stderr, " -v, --verbose show the full passed/failed result\n");
|
2013-02-09 01:14:39 -08:00
|
|
|
fprintf(stderr, " -i use the interpreter\n");
|
2016-05-14 19:21:52 -07:00
|
|
|
fprintf(stderr, " --ir use ir interpreter\n");
|
2013-02-09 01:14:39 -08:00
|
|
|
fprintf(stderr, " -j use jit (default)\n");
|
2012-11-24 21:45:32 -08:00
|
|
|
fprintf(stderr, " -c, --compare compare with output in file.expected\n");
|
2022-09-17 12:30:16 -07:00
|
|
|
fprintf(stderr, " --bench run multiple times and output speed\n");
|
2012-11-24 21:45:32 -08:00
|
|
|
fprintf(stderr, "\nSee headless.txt for details.\n");
|
2014-04-19 12:52:43 -07:00
|
|
|
|
|
|
|
return 1;
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
|
2014-08-30 09:57:56 -07:00
|
|
|
static HeadlessHost *getHost(GPUCore gpuCore) {
|
|
|
|
switch (gpuCore) {
|
2021-01-02 09:27:17 -08:00
|
|
|
case GPUCORE_SOFTWARE:
|
2013-09-16 09:42:30 +02:00
|
|
|
return new HeadlessHost();
|
2014-08-30 09:57:56 -07:00
|
|
|
#ifdef HEADLESSHOST_CLASS
|
2013-09-16 09:42:30 +02:00
|
|
|
default:
|
|
|
|
return new HEADLESSHOST_CLASS();
|
2014-08-30 09:57:56 -07:00
|
|
|
#else
|
|
|
|
default:
|
|
|
|
return new HeadlessHost();
|
|
|
|
#endif
|
2013-09-16 09:42:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-17 10:05:11 -07:00
|
|
|
struct AutoTestOptions {
|
|
|
|
double timeout;
|
2022-09-17 11:24:15 -07:00
|
|
|
double maxScreenshotError;
|
2022-09-17 10:05:11 -07:00
|
|
|
bool compare : 1;
|
|
|
|
bool verbose : 1;
|
2022-09-17 12:30:16 -07:00
|
|
|
bool bench : 1;
|
2022-09-17 10:05:11 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, const AutoTestOptions &opt) {
|
2020-12-30 14:10:03 -08:00
|
|
|
// Kinda ugly, trying to guesstimate the test name from filename...
|
2021-05-09 18:38:48 +02:00
|
|
|
currentTestName = GetTestName(coreParameter.fileToStart);
|
2013-09-16 23:49:41 -07:00
|
|
|
|
|
|
|
std::string output;
|
2022-09-17 12:30:16 -07:00
|
|
|
if (opt.compare || opt.bench)
|
2013-09-16 23:49:41 -07:00
|
|
|
coreParameter.collectEmuLog = &output;
|
|
|
|
|
|
|
|
std::string error_string;
|
2022-01-30 08:04:35 -08:00
|
|
|
if (!PSP_InitStart(coreParameter, &error_string)) {
|
2021-05-06 01:31:38 +02:00
|
|
|
fprintf(stderr, "Failed to start '%s'. Error: %s\n", coreParameter.fileToStart.c_str(), error_string.c_str());
|
2013-09-16 23:49:41 -07:00
|
|
|
printf("TESTERROR\n");
|
2020-12-30 14:10:03 -08:00
|
|
|
TeamCityPrint("testIgnored name='%s' message='PRX/ELF missing'", currentTestName.c_str());
|
|
|
|
GitHubActionsPrint("error", "PRX/ELF missing for %s", currentTestName.c_str());
|
2013-09-18 00:17:30 -07:00
|
|
|
return false;
|
2013-09-16 23:49:41 -07:00
|
|
|
}
|
|
|
|
|
2020-12-30 14:10:03 -08:00
|
|
|
TeamCityPrint("testStarted name='%s' captureStandardOutput='true'", currentTestName.c_str());
|
2013-09-16 23:49:41 -07:00
|
|
|
|
2022-09-17 10:05:11 -07:00
|
|
|
if (opt.compare)
|
2022-09-17 11:24:15 -07:00
|
|
|
headlessHost->SetComparisonScreenshot(ExpectedScreenshotFromFilename(coreParameter.fileToStart), opt.maxScreenshotError);
|
2013-09-16 23:49:41 -07:00
|
|
|
|
2022-01-30 08:04:35 -08:00
|
|
|
while (!PSP_InitUpdate(&error_string))
|
|
|
|
sleep_ms(1);
|
|
|
|
if (!PSP_IsInited()) {
|
|
|
|
TeamCityPrint("testFailed name='%s' message='Startup failed'", currentTestName.c_str());
|
|
|
|
TeamCityPrint("testFinished name='%s'", currentTestName.c_str());
|
2022-09-17 12:30:16 -07:00
|
|
|
GitHubActionsPrint("error", "Test init failed for %s", currentTestName.c_str());
|
2022-01-30 08:04:35 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-03-21 11:10:09 +01:00
|
|
|
System_Notify(SystemNotification::BOOT_DONE);
|
2013-09-16 23:49:41 -07:00
|
|
|
|
2017-11-08 11:57:53 +01:00
|
|
|
Core_UpdateDebugStats(g_Config.bShowDebugStats || g_Config.bLogFrameDrops);
|
|
|
|
|
2017-04-22 18:17:03 -07:00
|
|
|
PSP_BeginHostFrame();
|
2022-10-18 21:34:59 -07:00
|
|
|
Draw::DrawContext *draw = coreParameter.graphicsContext ? coreParameter.graphicsContext->GetDrawContext() : nullptr;
|
|
|
|
if (draw)
|
|
|
|
draw->BeginFrame();
|
2017-04-22 18:17:03 -07:00
|
|
|
|
2022-09-17 12:30:16 -07:00
|
|
|
bool passed = true;
|
|
|
|
double deadline = time_now_d() + opt.timeout;
|
2021-01-16 17:41:42 -08:00
|
|
|
coreState = coreParameter.startBreak ? CORE_STEPPING : CORE_RUNNING;
|
|
|
|
while (coreState == CORE_RUNNING || coreState == CORE_STEPPING)
|
2013-09-16 23:49:41 -07:00
|
|
|
{
|
2022-01-30 15:15:38 -08:00
|
|
|
int blockTicks = (int)usToCycles(1000000 / 10);
|
2013-09-16 23:49:41 -07:00
|
|
|
PSP_RunLoopFor(blockTicks);
|
|
|
|
|
|
|
|
// If we were rendering, this might be a nice time to do something about it.
|
|
|
|
if (coreState == CORE_NEXTFRAME) {
|
|
|
|
coreState = CORE_RUNNING;
|
|
|
|
headlessHost->SwapBuffers();
|
|
|
|
}
|
2021-01-16 17:41:42 -08:00
|
|
|
if (coreState == CORE_STEPPING && !coreParameter.startBreak) {
|
|
|
|
break;
|
|
|
|
}
|
2013-09-16 23:49:41 -07:00
|
|
|
if (time_now_d() > deadline) {
|
|
|
|
// Don't compare, print the output at least up to this point, and bail.
|
2022-09-17 12:30:16 -07:00
|
|
|
if (!opt.bench) {
|
|
|
|
printf("%s", output.c_str());
|
|
|
|
|
2023-03-25 17:34:29 -07:00
|
|
|
System_SendDebugOutput("TIMEOUT\n");
|
2022-09-17 12:30:16 -07:00
|
|
|
TeamCityPrint("testFailed name='%s' message='Test timeout'", currentTestName.c_str());
|
|
|
|
GitHubActionsPrint("error", "Test timeout for %s", currentTestName.c_str());
|
|
|
|
}
|
2013-09-16 23:49:41 -07:00
|
|
|
|
2022-09-17 12:30:16 -07:00
|
|
|
passed = false;
|
2013-09-16 23:49:41 -07:00
|
|
|
Core_Stop();
|
|
|
|
}
|
|
|
|
}
|
2017-11-15 15:35:15 +01:00
|
|
|
PSP_EndHostFrame();
|
2013-09-16 23:49:41 -07:00
|
|
|
|
2022-10-18 21:34:59 -07:00
|
|
|
if (draw) {
|
|
|
|
draw->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "Headless");
|
|
|
|
// Vulkan may get angry if we don't do a final present.
|
|
|
|
if (gpu)
|
|
|
|
gpu->CopyDisplayToOutput(true);
|
|
|
|
|
|
|
|
draw->EndFrame();
|
|
|
|
}
|
2017-04-22 18:17:03 -07:00
|
|
|
|
2013-09-16 23:49:41 -07:00
|
|
|
PSP_Shutdown();
|
|
|
|
|
2022-09-17 12:30:16 -07:00
|
|
|
if (!opt.bench)
|
|
|
|
headlessHost->FlushDebugOutput();
|
2013-09-16 23:49:41 -07:00
|
|
|
|
2022-09-17 10:05:11 -07:00
|
|
|
if (opt.compare && passed)
|
|
|
|
passed = CompareOutput(coreParameter.fileToStart, output, opt.verbose);
|
2013-09-16 23:49:41 -07:00
|
|
|
|
2020-12-30 14:10:03 -08:00
|
|
|
TeamCityPrint("testFinished name='%s'", currentTestName.c_str());
|
2013-09-16 23:49:41 -07:00
|
|
|
|
|
|
|
return passed;
|
|
|
|
}
|
|
|
|
|
2023-03-12 13:34:10 -07:00
|
|
|
std::vector<std::string> ReadFromListFile(const std::string &listFilename) {
|
|
|
|
std::vector<std::string> testFilenames;
|
|
|
|
char temp[2048]{};
|
|
|
|
|
|
|
|
if (listFilename == "-") {
|
|
|
|
while (scanf("%2047s", temp) == 1)
|
|
|
|
testFilenames.push_back(temp);
|
|
|
|
} else {
|
|
|
|
FILE *fp = File::OpenCFile(Path(listFilename), "rt");
|
|
|
|
if (!fp) {
|
|
|
|
fprintf(stderr, "Unable to open '%s' as a list file\n", listFilename.c_str());
|
|
|
|
return testFilenames;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (fscanf(fp, "%2047s", temp) == 1)
|
|
|
|
testFilenames.push_back(temp);
|
|
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return testFilenames;
|
|
|
|
}
|
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
int main(int argc, const char* argv[])
|
|
|
|
{
|
2015-05-30 12:53:57 -07:00
|
|
|
PROFILE_INIT();
|
2022-01-30 08:23:14 -08:00
|
|
|
#if PPSSPP_PLATFORM(WINDOWS)
|
|
|
|
timeBeginPeriod(1);
|
2022-04-02 16:07:51 -07:00
|
|
|
#else
|
|
|
|
// Ignore sigpipe.
|
|
|
|
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
|
|
|
|
perror("Unable to ignore SIGPIPE");
|
|
|
|
}
|
2022-01-30 08:23:14 -08:00
|
|
|
#endif
|
2015-05-30 12:53:57 -07:00
|
|
|
|
2017-06-18 12:15:59 -07:00
|
|
|
#if defined(_DEBUG) && defined(_MSC_VER)
|
2017-04-29 12:02:07 -07:00
|
|
|
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
|
|
|
#endif
|
|
|
|
|
2022-09-17 10:05:11 -07:00
|
|
|
AutoTestOptions testOptions{};
|
|
|
|
testOptions.timeout = std::numeric_limits<double>::infinity();
|
2012-11-01 16:19:01 +01:00
|
|
|
bool fullLog = false;
|
2014-03-25 00:46:21 -07:00
|
|
|
const char *stateToLoad = 0;
|
2021-01-02 09:27:17 -08:00
|
|
|
GPUCore gpuCore = GPUCORE_SOFTWARE;
|
2017-03-02 12:36:54 +01:00
|
|
|
CPUCore cpuCore = CPUCore::JIT;
|
2021-01-16 17:41:42 -08:00
|
|
|
int debuggerPort = -1;
|
2020-01-06 01:04:07 +08:00
|
|
|
|
2013-09-16 23:58:42 -07:00
|
|
|
std::vector<std::string> testFilenames;
|
2021-05-09 17:24:04 -07:00
|
|
|
const char *mountIso = nullptr;
|
|
|
|
const char *mountRoot = nullptr;
|
|
|
|
const char *screenshotFilename = nullptr;
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2012-11-24 21:45:32 -08:00
|
|
|
for (int i = 1; i < argc; i++)
|
2012-11-01 16:19:01 +01:00
|
|
|
{
|
2014-04-19 12:52:43 -07:00
|
|
|
if (!strcmp(argv[i], "-m") || !strcmp(argv[i], "--mount"))
|
2012-11-01 16:19:01 +01:00
|
|
|
{
|
2014-04-19 12:52:43 -07:00
|
|
|
if (++i >= argc)
|
|
|
|
return printUsage(argv[0], "Missing argument after -m");
|
2012-11-01 16:19:01 +01:00
|
|
|
mountIso = argv[i];
|
|
|
|
}
|
2014-04-19 12:52:43 -07:00
|
|
|
else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--root"))
|
|
|
|
{
|
|
|
|
if (++i >= argc)
|
|
|
|
return printUsage(argv[0], "Missing argument after -r");
|
|
|
|
mountRoot = argv[i];
|
|
|
|
}
|
2012-11-24 21:45:32 -08:00
|
|
|
else if (!strcmp(argv[i], "-l") || !strcmp(argv[i], "--log"))
|
2012-11-01 16:19:01 +01:00
|
|
|
fullLog = true;
|
2013-02-09 01:14:39 -08:00
|
|
|
else if (!strcmp(argv[i], "-i"))
|
2017-03-02 12:36:54 +01:00
|
|
|
cpuCore = CPUCore::INTERPRETER;
|
2012-11-01 16:19:01 +01:00
|
|
|
else if (!strcmp(argv[i], "-j"))
|
2017-03-02 12:36:54 +01:00
|
|
|
cpuCore = CPUCore::JIT;
|
2016-05-14 19:21:52 -07:00
|
|
|
else if (!strcmp(argv[i], "--ir"))
|
2017-03-02 12:36:54 +01:00
|
|
|
cpuCore = CPUCore::IR_JIT;
|
2012-11-24 21:45:32 -08:00
|
|
|
else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--compare"))
|
2022-09-17 10:05:11 -07:00
|
|
|
testOptions.compare = true;
|
2022-09-17 12:30:16 -07:00
|
|
|
else if (!strcmp(argv[i], "--bench"))
|
|
|
|
testOptions.bench = true;
|
2013-09-17 00:15:19 -07:00
|
|
|
else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))
|
2022-09-17 10:05:11 -07:00
|
|
|
testOptions.verbose = true;
|
2013-09-11 21:40:25 -07:00
|
|
|
else if (!strncmp(argv[i], "--graphics=", strlen("--graphics=")) && strlen(argv[i]) > strlen("--graphics="))
|
|
|
|
{
|
|
|
|
const char *gpuName = argv[i] + strlen("--graphics=");
|
|
|
|
if (!strcasecmp(gpuName, "gles"))
|
2016-04-10 10:21:48 +02:00
|
|
|
gpuCore = GPUCORE_GLES;
|
2021-01-02 09:36:13 -08:00
|
|
|
// There used to be a separate "null" rendering core - just use software.
|
|
|
|
else if (!strcasecmp(gpuName, "software") || !strcasecmp(gpuName, "null"))
|
2016-04-10 10:21:48 +02:00
|
|
|
gpuCore = GPUCORE_SOFTWARE;
|
2013-09-11 21:40:25 -07:00
|
|
|
else if (!strcasecmp(gpuName, "directx9"))
|
2016-04-10 10:21:48 +02:00
|
|
|
gpuCore = GPUCORE_DIRECTX9;
|
2017-04-22 18:17:03 -07:00
|
|
|
else if (!strcasecmp(gpuName, "directx11"))
|
|
|
|
gpuCore = GPUCORE_DIRECTX11;
|
2016-03-13 11:23:46 -07:00
|
|
|
else if (!strcasecmp(gpuName, "vulkan"))
|
2016-04-10 10:21:48 +02:00
|
|
|
gpuCore = GPUCORE_VULKAN;
|
2013-09-11 21:40:25 -07:00
|
|
|
else
|
2017-11-15 15:35:15 +01:00
|
|
|
return printUsage(argv[0], "Unknown gpu backend specified after --graphics=. Allowed: software, directx9, directx11, vulkan, gles, null.");
|
2013-09-11 21:40:25 -07:00
|
|
|
}
|
|
|
|
// Default to GLES if no value selected.
|
2019-05-10 23:25:57 +02:00
|
|
|
else if (!strcmp(argv[i], "--graphics")) {
|
|
|
|
#if PPSSPP_API(ANY_GL)
|
2016-04-10 10:21:48 +02:00
|
|
|
gpuCore = GPUCORE_GLES;
|
2019-05-10 23:25:57 +02:00
|
|
|
#else
|
|
|
|
gpuCore = GPUCORE_DIRECTX11;
|
|
|
|
#endif
|
2019-05-11 06:41:45 +02:00
|
|
|
} else if (!strncmp(argv[i], "--screenshot=", strlen("--screenshot=")) && strlen(argv[i]) > strlen("--screenshot="))
|
2013-01-13 16:35:34 -08:00
|
|
|
screenshotFilename = argv[i] + strlen("--screenshot=");
|
2013-09-16 08:36:10 -07:00
|
|
|
else if (!strncmp(argv[i], "--timeout=", strlen("--timeout=")) && strlen(argv[i]) > strlen("--timeout="))
|
2022-09-17 10:05:11 -07:00
|
|
|
testOptions.timeout = strtod(argv[i] + strlen("--timeout="), nullptr);
|
2022-09-17 11:24:15 -07:00
|
|
|
else if (!strncmp(argv[i], "--max-mse=", strlen("--max-mse=")) && strlen(argv[i]) > strlen("--max-mse="))
|
|
|
|
testOptions.maxScreenshotError = strtod(argv[i] + strlen("--max-mse="), nullptr);
|
2021-01-16 17:41:42 -08:00
|
|
|
else if (!strncmp(argv[i], "--debugger=", strlen("--debugger=")) && strlen(argv[i]) > strlen("--debugger="))
|
|
|
|
debuggerPort = (int)strtoul(argv[i] + strlen("--debugger="), NULL, 10);
|
2013-09-16 08:15:59 -07:00
|
|
|
else if (!strcmp(argv[i], "--teamcity"))
|
|
|
|
teamCityMode = true;
|
2014-03-25 00:46:21 -07:00
|
|
|
else if (!strncmp(argv[i], "--state=", strlen("--state=")) && strlen(argv[i]) > strlen("--state="))
|
|
|
|
stateToLoad = argv[i] + strlen("--state=");
|
2013-09-16 23:58:42 -07:00
|
|
|
else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h"))
|
2014-04-19 12:52:43 -07:00
|
|
|
return printUsage(argv[0], NULL);
|
2013-09-16 23:58:42 -07:00
|
|
|
else
|
|
|
|
testFilenames.push_back(argv[i]);
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
|
2023-03-12 13:34:10 -07:00
|
|
|
if (testFilenames.size() == 1 && testFilenames[0][0] == '@')
|
|
|
|
testFilenames = ReadFromListFile(testFilenames[0].substr(1));
|
2013-09-18 00:17:30 -07:00
|
|
|
|
2013-09-16 23:58:42 -07:00
|
|
|
if (testFilenames.empty())
|
2014-04-19 12:52:43 -07:00
|
|
|
return printUsage(argv[0], argc <= 1 ? NULL : "No executables specified");
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2020-08-15 18:40:50 +02:00
|
|
|
LogManager::Init(&g_Config.bEnableLogging);
|
2012-11-01 16:19:01 +01:00
|
|
|
LogManager *logman = LogManager::GetInstance();
|
2020-01-06 01:04:07 +08:00
|
|
|
|
2012-11-09 13:40:09 +01:00
|
|
|
PrintfLogger *printfLogger = new PrintfLogger();
|
|
|
|
|
2015-12-31 16:59:40 +01:00
|
|
|
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) {
|
2012-11-01 16:19:01 +01:00
|
|
|
LogTypes::LOG_TYPE type = (LogTypes::LOG_TYPE)i;
|
2017-03-18 10:47:10 +01:00
|
|
|
logman->SetEnabled(type, fullLog);
|
2012-11-01 16:19:01 +01:00
|
|
|
logman->SetLogLevel(type, LogTypes::LDEBUG);
|
|
|
|
}
|
2017-03-17 17:57:32 +01:00
|
|
|
logman->AddListener(printfLogger);
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2021-08-07 17:55:04 -07:00
|
|
|
// Needs to be after log so we don't interfere with test output.
|
|
|
|
g_threadManager.Init(cpu_info.num_cores, cpu_info.logical_cpu_count);
|
|
|
|
|
2020-09-08 15:52:39 -07:00
|
|
|
HeadlessHost *headlessHost = getHost(gpuCore);
|
2023-03-25 17:47:25 -07:00
|
|
|
g_headlessHost = headlessHost;
|
2020-09-08 15:52:39 -07:00
|
|
|
|
|
|
|
std::string error_string;
|
|
|
|
GraphicsContext *graphicsContext = nullptr;
|
2023-03-22 22:51:00 -07:00
|
|
|
bool glWorking = headlessHost->InitGraphics(&error_string, &graphicsContext, gpuCore);
|
2020-09-08 15:52:39 -07:00
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
CoreParameter coreParameter;
|
2016-05-08 01:43:27 +02:00
|
|
|
coreParameter.cpuCore = cpuCore;
|
2021-01-02 09:27:17 -08:00
|
|
|
coreParameter.gpuCore = glWorking ? gpuCore : GPUCORE_SOFTWARE;
|
2016-01-03 11:05:36 -08:00
|
|
|
coreParameter.graphicsContext = graphicsContext;
|
2012-11-01 16:19:01 +01:00
|
|
|
coreParameter.enableSound = false;
|
2021-05-06 01:31:38 +02:00
|
|
|
coreParameter.mountIso = mountIso ? Path(std::string(mountIso)) : Path();
|
|
|
|
coreParameter.mountRoot = mountRoot ? Path(std::string(mountRoot)) : Path();
|
2018-06-23 10:14:36 -07:00
|
|
|
coreParameter.startBreak = false;
|
2022-09-17 10:05:11 -07:00
|
|
|
coreParameter.printfEmuLog = !testOptions.compare;
|
2013-04-10 22:17:43 -07:00
|
|
|
coreParameter.headLess = true;
|
2020-11-05 11:21:00 +01:00
|
|
|
coreParameter.renderScaleFactor = 1;
|
2013-04-10 22:17:43 -07:00
|
|
|
coreParameter.renderWidth = 480;
|
|
|
|
coreParameter.renderHeight = 272;
|
|
|
|
coreParameter.pixelWidth = 480;
|
|
|
|
coreParameter.pixelHeight = 272;
|
2021-08-17 16:48:47 +02:00
|
|
|
coreParameter.fastForward = true;
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2012-11-17 18:08:10 +01:00
|
|
|
g_Config.bEnableSound = false;
|
2012-11-05 13:36:12 +01:00
|
|
|
g_Config.bFirstRun = false;
|
|
|
|
g_Config.bIgnoreBadMemAccess = true;
|
2013-03-09 12:53:53 -08:00
|
|
|
// Never report from tests.
|
2022-09-30 12:26:30 +03:00
|
|
|
g_Config.sReportHost.clear();
|
2013-04-10 22:17:43 -07:00
|
|
|
g_Config.bAutoSaveSymbolMap = false;
|
2022-11-06 19:20:10 +01:00
|
|
|
g_Config.bSkipBufferEffects = false;
|
2022-11-06 19:30:17 +01:00
|
|
|
g_Config.bSkipGPUReadbacks = false;
|
2013-04-10 22:17:43 -07:00
|
|
|
g_Config.bHardwareTransform = true;
|
2017-11-15 18:14:04 +01:00
|
|
|
g_Config.iAnisotropyLevel = 0; // When testing mipmapping we really don't want this.
|
2022-11-28 20:45:06 +01:00
|
|
|
g_Config.iMultiSampleLevel = 0;
|
2021-09-14 23:43:29 -07:00
|
|
|
g_Config.bVertexCache = false;
|
2013-09-16 18:08:09 -04:00
|
|
|
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;
|
2013-06-19 13:08:29 +08:00
|
|
|
g_Config.iTimeFormat = PSP_SYSTEMPARAM_TIME_FORMAT_24HR;
|
2013-04-10 22:17:43 -07:00
|
|
|
g_Config.bEncryptSave = true;
|
2013-04-20 09:30:46 -07:00
|
|
|
g_Config.sNickName = "shadow";
|
|
|
|
g_Config.iTimeZone = 60;
|
|
|
|
g_Config.iDateFormat = PSP_SYSTEMPARAM_DATE_FORMAT_DDMMYYYY;
|
2013-06-19 13:08:29 +08:00
|
|
|
g_Config.iButtonPreference = PSP_SYSTEMPARAM_BUTTON_CROSS;
|
2013-04-20 09:30:46 -07:00
|
|
|
g_Config.iLockParentalLevel = 9;
|
2013-09-11 21:40:25 -07:00
|
|
|
g_Config.iInternalResolution = 1;
|
2021-08-17 16:48:47 +02:00
|
|
|
g_Config.iFastForwardMode = (int)FastForwardMode::CONTINUOUS;
|
2013-11-17 10:15:13 -08:00
|
|
|
g_Config.bEnableLogging = fullLog;
|
2018-04-10 12:22:02 +02:00
|
|
|
g_Config.bSoftwareSkinning = true;
|
2014-03-25 00:46:21 -07:00
|
|
|
g_Config.bVertexDecoderJit = true;
|
2022-08-21 15:52:18 -07:00
|
|
|
g_Config.bSoftwareRendering = coreParameter.gpuCore == GPUCORE_SOFTWARE;
|
2021-11-28 15:54:48 -08:00
|
|
|
g_Config.bSoftwareRenderingJit = true;
|
2016-04-09 21:10:41 -07:00
|
|
|
g_Config.iSplineBezierQuality = 2;
|
2017-12-26 16:19:11 -08:00
|
|
|
g_Config.bHighQualityDepth = true;
|
2018-05-28 17:24:40 -07:00
|
|
|
g_Config.bMemStickInserted = true;
|
2020-10-19 21:43:00 -07:00
|
|
|
g_Config.iMemStickSizeGB = 16;
|
2019-08-18 11:53:39 -07:00
|
|
|
g_Config.bEnableWlan = true;
|
2019-08-18 12:34:31 -07:00
|
|
|
g_Config.sMACAddress = "12:34:56:78:9A:BC";
|
2020-10-19 21:43:00 -07:00
|
|
|
g_Config.iFirmwareVersion = PSP_DEFAULT_FIRMWARE;
|
|
|
|
g_Config.iPSPModel = PSP_MODEL_SLIM;
|
2021-08-09 20:47:24 -07:00
|
|
|
g_Config.iGlobalVolume = VOLUME_FULL;
|
|
|
|
g_Config.iReverbVolume = VOLUME_FULL;
|
2012-11-05 13:36:12 +01:00
|
|
|
|
2022-01-20 22:42:04 -08:00
|
|
|
#if PPSSPP_PLATFORM(WINDOWS)
|
2021-05-15 09:32:41 -07:00
|
|
|
g_Config.internalDataDirectory.clear();
|
2013-10-24 21:29:48 -07:00
|
|
|
InitSysDirectories();
|
2013-10-25 11:45:41 +02:00
|
|
|
#endif
|
|
|
|
|
2023-03-12 16:46:51 -07:00
|
|
|
Path executablePath = File::GetExeDirectory();
|
2022-01-20 22:42:04 -08:00
|
|
|
#if !PPSSPP_PLATFORM(ANDROID) && !PPSSPP_PLATFORM(WINDOWS)
|
2021-05-09 15:25:12 +02:00
|
|
|
g_Config.memStickDirectory = Path(std::string(getenv("HOME"))) / ".ppsspp";
|
2023-03-12 16:46:51 -07:00
|
|
|
g_Config.flash0Directory = executablePath / "assets/flash0";
|
2013-01-21 01:50:42 +01:00
|
|
|
#endif
|
|
|
|
|
2013-11-17 10:15:49 -08:00
|
|
|
// Try to find the flash0 directory. Often this is from a subdirectory.
|
2023-03-12 16:46:51 -07:00
|
|
|
Path nextPath = executablePath;
|
|
|
|
for (int i = 0; i < 5; ++i) {
|
|
|
|
if (File::Exists(nextPath / "assets/flash0")) {
|
|
|
|
g_Config.flash0Directory = nextPath / "assets/flash0";
|
|
|
|
#if !PPSSPP_PLATFORM(ANDROID)
|
|
|
|
g_VFS.Register("", new DirectoryReader(nextPath / "assets"));
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!nextPath.CanNavigateUp())
|
|
|
|
break;
|
|
|
|
nextPath = nextPath.NavigateUp();
|
2013-11-17 10:15:49 -08:00
|
|
|
}
|
|
|
|
|
2022-09-17 11:24:15 -07:00
|
|
|
if (screenshotFilename)
|
|
|
|
headlessHost->SetComparisonScreenshot(Path(std::string(screenshotFilename)), testOptions.maxScreenshotError);
|
2022-09-17 12:30:16 -07:00
|
|
|
headlessHost->SetWriteFailureScreenshot(!teamCityMode && !getenv("GITHUB_ACTIONS") && !testOptions.bench);
|
2013-01-13 16:35:34 -08:00
|
|
|
|
2022-01-20 22:42:04 -08:00
|
|
|
#if PPSSPP_PLATFORM(ANDROID)
|
2014-03-25 00:49:03 -07:00
|
|
|
// For some reason the debugger installs it with this name?
|
2021-05-09 15:02:46 +02:00
|
|
|
if (File::Exists(Path("/data/app/org.ppsspp.ppsspp-2.apk"))) {
|
2023-03-07 20:22:28 +01:00
|
|
|
g_VFS.Register("", ZipFileReader::Create(Path("/data/app/org.ppsspp.ppsspp-2.apk"), "assets/"));
|
2014-03-25 00:49:03 -07:00
|
|
|
}
|
2021-05-09 15:02:46 +02:00
|
|
|
if (File::Exists(Path("/data/app/org.ppsspp.ppsspp.apk"))) {
|
2023-03-07 20:22:28 +01:00
|
|
|
g_VFS.Register("", ZipFileReader::Create(Path("/data/app/org.ppsspp.ppsspp.apk"), "assets/"));
|
2014-03-25 00:49:03 -07:00
|
|
|
}
|
2023-03-12 16:46:51 -07:00
|
|
|
#elif PPSSPP_PLATFORM(LINUX)
|
|
|
|
g_VFS.Register("", new DirectoryReader(Path("/usr/local/share/ppsspp/assets")));
|
|
|
|
g_VFS.Register("", new DirectoryReader(Path("/usr/local/share/games/ppsspp/assets")));
|
|
|
|
g_VFS.Register("", new DirectoryReader(Path("/usr/share/ppsspp/assets")));
|
|
|
|
g_VFS.Register("", new DirectoryReader(Path("/usr/share/games/ppsspp/assets")));
|
2014-03-25 00:49:03 -07:00
|
|
|
#endif
|
|
|
|
|
2021-01-16 17:41:42 -08:00
|
|
|
UpdateUIState(UISTATE_INGAME);
|
|
|
|
|
|
|
|
if (debuggerPort > 0) {
|
|
|
|
g_Config.iRemoteISOPort = debuggerPort;
|
|
|
|
coreParameter.startBreak = true;
|
|
|
|
StartWebServer(WebServerFlags::DEBUGGER);
|
|
|
|
}
|
|
|
|
|
2014-03-25 00:46:21 -07:00
|
|
|
if (stateToLoad != NULL)
|
2021-05-06 01:31:38 +02:00
|
|
|
SaveState::Load(Path(stateToLoad), -1);
|
2014-03-25 00:46:21 -07:00
|
|
|
|
2013-09-17 00:02:53 -07:00
|
|
|
std::vector<std::string> failedTests;
|
|
|
|
std::vector<std::string> passedTests;
|
2013-09-16 23:58:42 -07:00
|
|
|
for (size_t i = 0; i < testFilenames.size(); ++i)
|
|
|
|
{
|
2021-05-06 01:31:38 +02:00
|
|
|
coreParameter.fileToStart = Path(testFilenames[i]);
|
2022-09-17 10:05:11 -07:00
|
|
|
if (testOptions.compare)
|
2013-09-16 23:58:42 -07:00
|
|
|
printf("%s:\n", coreParameter.fileToStart.c_str());
|
2022-09-17 10:05:11 -07:00
|
|
|
bool passed = RunAutoTest(headlessHost, coreParameter, testOptions);
|
2022-09-17 12:30:16 -07:00
|
|
|
if (testOptions.bench) {
|
|
|
|
double st = time_now_d();
|
|
|
|
double deadline = st + testOptions.timeout;
|
|
|
|
double runs = 0.0;
|
|
|
|
for (int i = 0; i < 100; ++i) {
|
|
|
|
RunAutoTest(headlessHost, coreParameter, testOptions);
|
|
|
|
runs++;
|
|
|
|
|
|
|
|
if (time_now_d() > deadline)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
double et = time_now_d();
|
|
|
|
|
|
|
|
std::string testName = GetTestName(coreParameter.fileToStart);
|
|
|
|
printf(" %s - %f seconds average\n", testName.c_str(), (et - st) / runs);
|
|
|
|
}
|
2022-09-17 10:05:11 -07:00
|
|
|
if (testOptions.compare) {
|
2021-05-09 18:38:48 +02:00
|
|
|
std::string testName = GetTestName(coreParameter.fileToStart);
|
2022-09-17 12:30:16 -07:00
|
|
|
if (passed) {
|
2013-09-17 00:02:53 -07:00
|
|
|
passedTests.push_back(testName);
|
|
|
|
printf(" %s - passed!\n", testName.c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
failedTests.push_back(testName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-17 10:05:11 -07:00
|
|
|
if (testOptions.compare) {
|
2013-09-17 00:02:53 -07:00
|
|
|
printf("%d tests passed, %d tests failed.\n", (int)passedTests.size(), (int)failedTests.size());
|
|
|
|
if (!failedTests.empty())
|
|
|
|
{
|
|
|
|
printf("Failed tests:\n");
|
2013-09-19 00:29:19 -07:00
|
|
|
for (size_t i = 0; i < failedTests.size(); ++i) {
|
2013-09-17 08:12:39 -07:00
|
|
|
printf(" %s\n", failedTests[i].c_str());
|
2013-09-17 00:02:53 -07:00
|
|
|
}
|
|
|
|
}
|
2013-09-16 23:58:42 -07:00
|
|
|
}
|
|
|
|
|
2021-01-16 17:41:42 -08:00
|
|
|
if (debuggerPort > 0) {
|
|
|
|
ShutdownWebServer();
|
|
|
|
}
|
|
|
|
|
2023-03-22 22:51:00 -07:00
|
|
|
headlessHost->ShutdownGraphics();
|
2023-03-25 17:47:25 -07:00
|
|
|
delete headlessHost;
|
2017-04-29 12:02:07 -07:00
|
|
|
headlessHost = nullptr;
|
2023-03-25 17:47:25 -07:00
|
|
|
g_headlessHost = nullptr;
|
2017-04-29 12:02:07 -07:00
|
|
|
|
2023-03-06 14:23:56 +01:00
|
|
|
g_VFS.Clear();
|
2017-04-29 12:02:07 -07:00
|
|
|
LogManager::Shutdown();
|
|
|
|
delete printfLogger;
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2022-01-30 08:23:14 -08:00
|
|
|
#if PPSSPP_PLATFORM(WINDOWS)
|
|
|
|
timeEndPeriod(1);
|
|
|
|
#endif
|
|
|
|
|
2022-08-07 12:10:17 -07:00
|
|
|
g_threadManager.Teardown();
|
|
|
|
|
2022-01-20 20:53:38 -08:00
|
|
|
if (!failedTests.empty() && !teamCityMode)
|
|
|
|
return 1;
|
2012-11-01 16:19:01 +01:00
|
|
|
return 0;
|
|
|
|
}
|