2015-04-10 00:30:05 -04:00
|
|
|
#include "PS2VM.h"
|
|
|
|
#include <boost/filesystem.hpp>
|
|
|
|
#include "StdStream.h"
|
2015-04-18 22:17:45 -04:00
|
|
|
#include "StdStreamUtils.h"
|
|
|
|
#include "Utils.h"
|
2015-05-05 00:12:39 -04:00
|
|
|
#include "JUnitTestReportWriter.h"
|
2015-12-21 19:45:57 -05:00
|
|
|
#include "gs/GSH_Null.h"
|
2017-05-28 22:48:36 -04:00
|
|
|
#ifdef _WIN32
|
|
|
|
#include "ui_win32/GSH_OpenGLWin32.h"
|
|
|
|
#include "ui_win32/GSH_Direct3D9.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define GS_HANDLER_NAME_NULL "null"
|
|
|
|
#define GS_HANDLER_NAME_OGL "ogl"
|
|
|
|
#define GS_HANDLER_NAME_D3D9 "d3d9"
|
|
|
|
|
|
|
|
#define DEFAULT_GS_HANDLER_NAME GS_HANDLER_NAME_NULL
|
|
|
|
|
|
|
|
static std::set<std::string> g_validGsHandlersNames =
|
|
|
|
{
|
|
|
|
GS_HANDLER_NAME_NULL,
|
|
|
|
#ifdef _WIN32
|
|
|
|
GS_HANDLER_NAME_OGL,
|
|
|
|
GS_HANDLER_NAME_D3D9,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
|
|
class CTestWindow : public Framework::Win32::CWindow, public CSingleton<CTestWindow>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
CTestWindow()
|
|
|
|
{
|
|
|
|
Create(0, Framework::Win32::CDefaultWndClass::GetName(), _T(""), WS_OVERLAPPED, Framework::Win32::CRect(0, 0, 100, 100), NULL, NULL);
|
|
|
|
SetClassPtr();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
CGSHandler::FactoryFunction GetGsHandlerFactoryFunction(const std::string& gsHandlerName)
|
|
|
|
{
|
|
|
|
if(gsHandlerName == GS_HANDLER_NAME_NULL)
|
|
|
|
{
|
|
|
|
return CGSH_Null::GetFactoryFunction();
|
|
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
|
|
else if(gsHandlerName == GS_HANDLER_NAME_OGL)
|
|
|
|
{
|
|
|
|
return CGSH_OpenGLWin32::GetFactoryFunction(&CTestWindow::GetInstance());
|
|
|
|
}
|
|
|
|
else if(gsHandlerName == GS_HANDLER_NAME_D3D9)
|
|
|
|
{
|
|
|
|
return CGSH_Direct3D9::GetFactoryFunction(&CTestWindow::GetInstance());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw std::runtime_error("Unknown GS handler name.");
|
|
|
|
}
|
|
|
|
}
|
2015-04-18 22:17:45 -04:00
|
|
|
|
|
|
|
std::vector<std::string> ReadLines(Framework::CStream& inputStream)
|
|
|
|
{
|
|
|
|
std::vector<std::string> lines;
|
|
|
|
lines.push_back(Utils::GetLine(&inputStream));
|
|
|
|
while(!inputStream.IsEOF())
|
|
|
|
{
|
|
|
|
lines.push_back(Utils::GetLine(&inputStream));
|
|
|
|
}
|
|
|
|
return lines;
|
|
|
|
}
|
|
|
|
|
2015-07-03 03:21:48 -04:00
|
|
|
TESTRESULT GetTestResult(const boost::filesystem::path& testFilePath)
|
2015-04-18 22:17:45 -04:00
|
|
|
{
|
2015-07-03 03:21:48 -04:00
|
|
|
TESTRESULT result;
|
|
|
|
result.succeeded = false;
|
2015-04-18 22:17:45 -04:00
|
|
|
try
|
|
|
|
{
|
|
|
|
auto resultFilePath = testFilePath;
|
|
|
|
resultFilePath.replace_extension(".result");
|
|
|
|
auto expectedFilePath = testFilePath;
|
|
|
|
expectedFilePath.replace_extension(".expected");
|
|
|
|
auto resultStream = Framework::CreateInputStdStream(resultFilePath.string());
|
|
|
|
auto expectedStream = Framework::CreateInputStdStream(expectedFilePath.string());
|
|
|
|
|
|
|
|
auto resultLines = ReadLines(resultStream);
|
|
|
|
auto expectedLines = ReadLines(expectedStream);
|
|
|
|
|
2015-07-03 03:21:48 -04:00
|
|
|
if(resultLines.size() != expectedLines.size()) return result;
|
2015-04-18 22:17:45 -04:00
|
|
|
|
|
|
|
for(unsigned int i = 0; i < resultLines.size(); i++)
|
|
|
|
{
|
2015-07-03 03:21:48 -04:00
|
|
|
if(resultLines[i] != expectedLines[i])
|
|
|
|
{
|
|
|
|
LINEDIFF lineDiff;
|
|
|
|
lineDiff.expected = expectedLines[i];
|
|
|
|
lineDiff.result = resultLines[i];
|
|
|
|
result.lineDiffs.push_back(lineDiff);
|
|
|
|
}
|
2015-04-18 22:17:45 -04:00
|
|
|
}
|
|
|
|
|
2015-07-03 03:21:48 -04:00
|
|
|
result.succeeded = result.lineDiffs.empty();
|
|
|
|
return result;
|
2015-04-18 22:17:45 -04:00
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
2015-07-03 03:21:48 -04:00
|
|
|
|
2015-04-18 22:17:45 -04:00
|
|
|
}
|
2015-07-03 03:21:48 -04:00
|
|
|
return result;
|
2015-04-18 22:17:45 -04:00
|
|
|
}
|
2015-04-10 00:30:05 -04:00
|
|
|
|
2017-05-28 22:48:36 -04:00
|
|
|
void ExecuteEeTest(const boost::filesystem::path& testFilePath, const std::string& gsHandlerName)
|
2015-04-10 00:30:05 -04:00
|
|
|
{
|
|
|
|
auto resultFilePath = testFilePath;
|
|
|
|
resultFilePath.replace_extension(".result");
|
|
|
|
auto resultStream = new Framework::CStdStream(resultFilePath.string().c_str(), "wb");
|
|
|
|
|
|
|
|
bool executionOver = false;
|
|
|
|
|
|
|
|
//Setup virtual machine
|
|
|
|
CPS2VM virtualMachine;
|
|
|
|
virtualMachine.Initialize();
|
|
|
|
virtualMachine.Reset();
|
2017-05-28 22:48:36 -04:00
|
|
|
virtualMachine.CreateGSHandler(GetGsHandlerFactoryFunction(gsHandlerName));
|
2015-04-10 00:30:05 -04:00
|
|
|
virtualMachine.m_ee->m_os->OnRequestExit.connect(
|
|
|
|
[&executionOver] ()
|
|
|
|
{
|
|
|
|
executionOver = true;
|
|
|
|
}
|
|
|
|
);
|
2017-11-07 08:25:10 -05:00
|
|
|
virtualMachine.m_ee->m_os->BootFromFile(testFilePath);
|
2015-07-03 22:18:29 -04:00
|
|
|
virtualMachine.m_iopOs->GetIoman()->SetFileStream(Iop::CIoman::FID_STDOUT, resultStream);
|
2015-04-10 00:30:05 -04:00
|
|
|
virtualMachine.Resume();
|
|
|
|
|
|
|
|
while(!executionOver)
|
|
|
|
{
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
}
|
|
|
|
|
|
|
|
virtualMachine.Pause();
|
2015-12-22 17:35:35 -05:00
|
|
|
virtualMachine.DestroyGSHandler();
|
2015-04-10 00:30:05 -04:00
|
|
|
virtualMachine.Destroy();
|
|
|
|
}
|
|
|
|
|
2015-07-03 22:19:12 -04:00
|
|
|
void ExecuteIopTest(const boost::filesystem::path& testFilePath)
|
|
|
|
{
|
|
|
|
//Read in the module data
|
|
|
|
std::vector<uint8> moduleData;
|
|
|
|
{
|
|
|
|
auto moduleStream = Framework::CreateInputStdStream(testFilePath.native());
|
|
|
|
auto length = moduleStream.GetLength();
|
|
|
|
moduleData.resize(length);
|
|
|
|
moduleStream.Read(moduleData.data(), length);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto resultFilePath = testFilePath;
|
|
|
|
resultFilePath.replace_extension(".result");
|
|
|
|
auto resultStream = new Framework::CStdStream(resultFilePath.string().c_str(), "wb");
|
|
|
|
|
|
|
|
bool executionOver = false;
|
|
|
|
|
|
|
|
//Setup virtual machine
|
|
|
|
CPS2VM virtualMachine;
|
|
|
|
virtualMachine.Initialize();
|
|
|
|
virtualMachine.Reset();
|
|
|
|
int32 rootModuleId = virtualMachine.m_iopOs->LoadModuleFromHost(moduleData.data());
|
|
|
|
virtualMachine.m_iopOs->OnModuleStarted.connect(
|
|
|
|
[&executionOver, &rootModuleId] (uint32 moduleId)
|
|
|
|
{
|
|
|
|
if(rootModuleId == moduleId)
|
|
|
|
{
|
|
|
|
executionOver = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
virtualMachine.m_iopOs->StartModule(rootModuleId, "", nullptr, 0);
|
|
|
|
virtualMachine.m_iopOs->GetIoman()->SetFileStream(Iop::CIoman::FID_STDOUT, resultStream);
|
|
|
|
virtualMachine.Resume();
|
|
|
|
|
|
|
|
while(!executionOver)
|
|
|
|
{
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
}
|
|
|
|
|
|
|
|
virtualMachine.Pause();
|
|
|
|
virtualMachine.Destroy();
|
|
|
|
}
|
|
|
|
|
2017-05-28 22:48:36 -04:00
|
|
|
void ScanAndExecuteTests(const boost::filesystem::path& testDirPath, const TestReportWriterPtr& testReportWriter, const std::string& gsHandlerName)
|
2015-04-10 00:30:05 -04:00
|
|
|
{
|
|
|
|
boost::filesystem::directory_iterator endIterator;
|
|
|
|
for(auto testPathIterator = boost::filesystem::directory_iterator(testDirPath);
|
|
|
|
testPathIterator != endIterator; testPathIterator++)
|
|
|
|
{
|
|
|
|
auto testPath = testPathIterator->path();
|
|
|
|
if(boost::filesystem::is_directory(testPath))
|
|
|
|
{
|
2017-05-28 22:48:36 -04:00
|
|
|
ScanAndExecuteTests(testPath, testReportWriter, gsHandlerName);
|
2015-04-10 00:30:05 -04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(testPath.extension() == ".elf")
|
|
|
|
{
|
2015-04-18 22:17:45 -04:00
|
|
|
printf("Testing '%s': ", testPath.string().c_str());
|
2017-05-28 22:48:36 -04:00
|
|
|
ExecuteEeTest(testPath, gsHandlerName);
|
2015-07-03 22:19:12 -04:00
|
|
|
auto result = GetTestResult(testPath);
|
|
|
|
printf("%s.\r\n", result.succeeded ? "SUCCEEDED" : "FAILED");
|
|
|
|
if(testReportWriter)
|
|
|
|
{
|
|
|
|
testReportWriter->ReportTestEntry(testPath.string(), result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(testPath.extension() == ".irx")
|
|
|
|
{
|
|
|
|
printf("Testing '%s': ", testPath.string().c_str());
|
|
|
|
ExecuteIopTest(testPath);
|
2015-07-03 03:21:48 -04:00
|
|
|
auto result = GetTestResult(testPath);
|
|
|
|
printf("%s.\r\n", result.succeeded ? "SUCCEEDED" : "FAILED");
|
2015-05-05 00:12:39 -04:00
|
|
|
if(testReportWriter)
|
|
|
|
{
|
2015-07-03 03:21:48 -04:00
|
|
|
testReportWriter->ReportTestEntry(testPath.string(), result);
|
2015-05-05 00:12:39 -04:00
|
|
|
}
|
2015-04-10 00:30:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, const char** argv)
|
|
|
|
{
|
|
|
|
if(argc < 2)
|
|
|
|
{
|
2017-05-28 22:48:36 -04:00
|
|
|
auto validGsHandlerNamesString =
|
|
|
|
[]()
|
|
|
|
{
|
|
|
|
std::string result;
|
|
|
|
for(auto nameIterator = g_validGsHandlersNames.begin();
|
|
|
|
nameIterator != g_validGsHandlersNames.end(); ++nameIterator)
|
|
|
|
{
|
|
|
|
if(nameIterator != g_validGsHandlersNames.begin())
|
|
|
|
{
|
|
|
|
result += "|";
|
|
|
|
}
|
|
|
|
result += *nameIterator;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}();
|
|
|
|
|
2015-05-05 00:12:39 -04:00
|
|
|
printf("Usage: AutoTest [options] testDir\r\n");
|
|
|
|
printf("Options: \r\n");
|
|
|
|
printf("\t --junitreport <path>\t Writes JUnit format report at <path>.\r\n");
|
2017-05-28 22:48:36 -04:00
|
|
|
printf("\t --gshandler <%s>\tSelects which GS handler to instantiate (default is '%s').\r\n",
|
|
|
|
validGsHandlerNamesString.c_str(), DEFAULT_GS_HANDLER_NAME);
|
2015-04-10 00:30:05 -04:00
|
|
|
return -1;
|
|
|
|
}
|
2015-05-05 00:12:39 -04:00
|
|
|
|
|
|
|
TestReportWriterPtr testReportWriter;
|
|
|
|
boost::filesystem::path autoTestRoot;
|
|
|
|
boost::filesystem::path reportPath;
|
2017-05-28 22:48:36 -04:00
|
|
|
std::string gsHandlerName = DEFAULT_GS_HANDLER_NAME;
|
|
|
|
assert(g_validGsHandlersNames.find(gsHandlerName) != std::end(g_validGsHandlersNames));
|
2015-05-05 00:12:39 -04:00
|
|
|
|
|
|
|
for(int i = 1; i < argc; i++)
|
|
|
|
{
|
|
|
|
if(!strcmp(argv[i], "--junitreport"))
|
|
|
|
{
|
|
|
|
if((i + 1) >= argc)
|
|
|
|
{
|
|
|
|
printf("Error: Path must be specified for --junitreport option.\r\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
testReportWriter = std::make_shared<CJUnitTestReportWriter>();
|
|
|
|
reportPath = boost::filesystem::path(argv[i + 1]);
|
|
|
|
i++;
|
|
|
|
}
|
2017-05-28 22:48:36 -04:00
|
|
|
else if(!strcmp(argv[i], "--gshandler"))
|
|
|
|
{
|
|
|
|
if((i + 1) >= argc)
|
|
|
|
{
|
|
|
|
printf("Error: GS handler name must be specified for --gshandler option.\r\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
gsHandlerName = argv[i + 1];
|
|
|
|
if(g_validGsHandlersNames.find(gsHandlerName) == std::end(g_validGsHandlersNames))
|
|
|
|
{
|
|
|
|
printf("Error: Invalid GS handler name '%s'.\r\n", gsHandlerName.c_str());
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
2015-05-05 00:12:39 -04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
autoTestRoot = argv[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(autoTestRoot.empty())
|
|
|
|
{
|
|
|
|
printf("Error: No test directory specified.\r\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-10-26 00:15:35 -04:00
|
|
|
try
|
|
|
|
{
|
2017-05-28 22:48:36 -04:00
|
|
|
ScanAndExecuteTests(autoTestRoot, testReportWriter, gsHandlerName);
|
2015-10-26 00:15:35 -04:00
|
|
|
}
|
|
|
|
catch(const std::exception& exception)
|
|
|
|
{
|
|
|
|
printf("Error: Failed to execute tests: %s\r\n", exception.what());
|
|
|
|
return -1;
|
|
|
|
}
|
2015-05-05 00:12:39 -04:00
|
|
|
|
|
|
|
if(testReportWriter)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
testReportWriter->Write(reportPath);
|
|
|
|
}
|
|
|
|
catch(const std::exception& exception)
|
|
|
|
{
|
|
|
|
printf("Error: Failed to write test report: %s\r\n", exception.what());
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-10 00:30:05 -04:00
|
|
|
return 0;
|
|
|
|
}
|