1 #ifdef _WIN32
2 #pragma comment(lib, "Utilities.lib")
3 #include <Windows.h>
4 #include <Shlobj.h>
5 #else
6 #include <sys/wait.h>
7 #include <stdio.h>
8 #include <execinfo.h>
9 #include <signal.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12
13 #define __stdcall
14 #endif
15
16 #include <iostream>
17 #include <thread>
18 #include <vector>
19 #include <string>
20 #include <thread>
21 #include "../Utilities/FolderUtilities.h"
22 #include "../Utilities/SimpleLock.h"
23 #include "../Utilities/Timer.h"
24 #include "../Core/MessageManager.h"
25 #include "../Core/ControlManager.h"
26 #include "../Core/EmulationSettings.h"
27
28 using namespace std;
29
30 typedef void (__stdcall *NotificationListenerCallback)(ConsoleNotificationType);
31
32 extern "C" {
33 void __stdcall InitDll();
34 void __stdcall SetFlags(uint64_t flags);
35 void __stdcall InitializeEmu(const char* homeFolder, void*, void*, bool, bool, bool);
36 void __stdcall SetControllerType(uint32_t port, ControllerType type);
37 int __stdcall RunAutomaticTest(char* filename);
38 int __stdcall RunRecordedTest(char* filename);
39 void __stdcall Run();
40 void __stdcall Stop();
41 INotificationListener* __stdcall RegisterNotificationCallback(int32_t consoleId, NotificationListenerCallback callback);
42 }
43
44 std::thread *runThread = nullptr;
45 std::atomic<int> testIndex;
46 vector<string> testFilenames;
47 vector<string> failedTests;
48 vector<int32_t> failedTestErrorCode;
49 SimpleLock lock;
50 Timer timer;
51 bool automaticTests = false;
52
RunEmu()53 void RunEmu()
54 {
55 try {
56 Run();
57 } catch(std::exception ex) {
58
59 }
60 }
61
OnNotificationReceived(ConsoleNotificationType type)62 void __stdcall OnNotificationReceived(ConsoleNotificationType type)
63 {
64 static int count = 0;
65 if(type == ConsoleNotificationType::GameLoaded) {
66 count++;
67 if(count % 2 == 0) {
68 //GameLoaded is fired twice because of how the test roms are coded, we want to start running the test on the 2nd time only
69 runThread = new std::thread(RunEmu);
70 }
71 }
72 }
73
RunTest()74 void RunTest()
75 {
76 while(true) {
77 lock.Acquire();
78 size_t index = testIndex++;
79 lock.Release();
80
81 if(index < testFilenames.size()) {
82 string filepath = testFilenames[index];
83 string filename = FolderUtilities::GetFilename(filepath, false);
84
85 string command;
86 if(automaticTests) {
87 #ifdef _WIN32
88 command = "TestHelper.exe /autotest \"" + filepath + "\"";
89 #else
90 command = "./testhelper /autotest \"" + filepath + "\"";
91 #endif
92 } else {
93 #ifdef _WIN32
94 command = "TestHelper.exe /testrom \"" + filepath + "\"";
95 #else
96 command = "./testhelper /testrom \"" + filepath + "\"";
97 #endif
98 }
99
100 lock.Acquire();
101 std::cout << std::to_string(index) << ") " << filename << std::endl;
102 lock.Release();
103
104 int failedFrames = std::system(command.c_str());
105 #ifdef __GNUC__
106 failedFrames = WEXITSTATUS(failedFrames);
107 #endif
108
109 if(failedFrames != 0) {
110 //Test failed
111 lock.Acquire();
112 failedTests.push_back(filename);
113 failedTestErrorCode.push_back(failedFrames);
114 std::cout << " **** " << std::to_string(index) << ") " << filename << " failed (" << failedFrames << ")" << std::endl;
115 lock.Release();
116 }
117 } else {
118 break;
119 }
120 }
121 }
122
123 #ifdef __GNUC__
handler(int sig)124 void handler(int sig) {
125 void *array[20];
126 size_t size = backtrace(array, 20);
127
128 std::cout << "Error: signal: " << std::to_string(sig) << std::endl;
129 backtrace_symbols_fd(array, size, STDERR_FILENO);
130 exit(1);
131 }
132 #endif
133
main(int argc,char * argv[])134 int main(int argc, char* argv[])
135 {
136 #ifdef _WIN32
137 wchar_t path[MAX_PATH];
138 SHGetFolderPath(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, path);
139 string mesenFolder = FolderUtilities::CombinePath(utf8::utf8::encode(path), "Mesen");
140 #else
141 string mesenFolder = "/home/saitoh/Mesen";
142 signal(SIGSEGV, handler);
143 #endif
144
145 if(argc >= 3 && strcmp(argv[1], "/auto") == 0) {
146 string romFolder = argv[2];
147 testFilenames = FolderUtilities::GetFilesInFolder(romFolder, { ".nes" }, true);
148 automaticTests = true;
149 } else if(argc <= 2) {
150 string testFolder;
151 if(argc == 1) {
152 testFolder = FolderUtilities::CombinePath(mesenFolder, "Tests");
153 } else {
154 testFolder = argv[1];
155 }
156 testFilenames = FolderUtilities::GetFilesInFolder(testFolder, { ".mtp" }, true);
157 automaticTests = false;
158 }
159
160 if(!testFilenames.empty()) {
161 vector<std::thread*> testThreads;
162 testIndex = 0;
163 timer.Reset();
164
165 int numberOfThreads = 16;
166 for(int i = 0; i < numberOfThreads; i++) {
167 std::thread *testThread = new std::thread(RunTest);
168 testThreads.push_back(testThread);
169 }
170
171 for(int i = 0; i < numberOfThreads; i++) {
172 testThreads[i]->join();
173 delete testThreads[i];
174 }
175
176 if(!failedTests.empty()) {
177 std::cout << std::endl << std::endl;
178 std::cout << "------------" << std::endl;
179 std::cout << "Failed tests" << std::endl;
180 std::cout << "------------" << std::endl;
181 for(int i = 0; i < (int)failedTests.size(); i++) {
182 std::cout << failedTests[i] << " (" << std::to_string(failedTestErrorCode[i]) << ")" << std::endl;
183 }
184 std::cout << std::endl << std::to_string(failedTests.size()) << " tests failed." << std::endl;
185 } else {
186 std::cout << std::endl << std::endl << "All tests passed.";
187 }
188 std::cout << std::endl << std::endl << "Elapsed time: " << (timer.GetElapsedMS() / 1000) << " seconds";
189
190 std::getchar();
191 } else if(argc == 3) {
192 char* testFilename = argv[2];
193 InitDll();
194 SetFlags(0x8000000000000000); //EmulationFlags::ConsoleMode
195 InitializeEmu(mesenFolder.c_str(), nullptr, nullptr, false, false, false);
196 RegisterNotificationCallback(0, (NotificationListenerCallback)OnNotificationReceived);
197 SetControllerType(0, ControllerType::StandardController);
198 SetControllerType(1, ControllerType::StandardController);
199
200 int result = 0;
201 if(strcmp(argv[1], "/testrom") == 0) {
202 result = RunRecordedTest(testFilename);
203 } else {
204 result = RunAutomaticTest(testFilename);
205 }
206
207 if(runThread != nullptr) {
208 runThread->join();
209 delete runThread;
210 }
211 return result;
212 }
213 return 0;
214 }
215
216