summaryrefslogtreecommitdiff
path: root/src/yuzu_tester/yuzu.cpp
diff options
context:
space:
mode:
authorGravatar bunnei2019-06-21 11:42:38 -0400
committerGravatar GitHub2019-06-21 11:42:38 -0400
commit100ed88e15e9ece96506795aa8a079a55cdbad57 (patch)
tree9b961f52134c9a7f8bee354828b8bf6e5dc9205b /src/yuzu_tester/yuzu.cpp
parentMerge pull request #2596 from FernandoS27/revert-2590 (diff)
parentyuzutest: Add minor comments (diff)
downloadyuzu-100ed88e15e9ece96506795aa8a079a55cdbad57.tar.gz
yuzu-100ed88e15e9ece96506795aa8a079a55cdbad57.tar.xz
yuzu-100ed88e15e9ece96506795aa8a079a55cdbad57.zip
Merge pull request #2291 from DarkLordZach/homebrew-testing
yuzu_tester: Add and implement testing utility for homebrew
Diffstat (limited to 'src/yuzu_tester/yuzu.cpp')
-rw-r--r--src/yuzu_tester/yuzu.cpp267
1 files changed, 267 insertions, 0 deletions
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp
new file mode 100644
index 000000000..b589c3de3
--- /dev/null
+++ b/src/yuzu_tester/yuzu.cpp
@@ -0,0 +1,267 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <iostream>
6#include <memory>
7#include <string>
8#include <thread>
9
10#include <fmt/ostream.h>
11
12#include "common/common_paths.h"
13#include "common/detached_tasks.h"
14#include "common/file_util.h"
15#include "common/logging/backend.h"
16#include "common/logging/filter.h"
17#include "common/logging/log.h"
18#include "common/microprofile.h"
19#include "common/scm_rev.h"
20#include "common/scope_exit.h"
21#include "common/string_util.h"
22#include "common/telemetry.h"
23#include "core/core.h"
24#include "core/crypto/key_manager.h"
25#include "core/file_sys/vfs_real.h"
26#include "core/hle/service/filesystem/filesystem.h"
27#include "core/loader/loader.h"
28#include "core/settings.h"
29#include "core/telemetry_session.h"
30#include "video_core/renderer_base.h"
31#include "yuzu_tester/config.h"
32#include "yuzu_tester/emu_window/emu_window_sdl2_hide.h"
33#include "yuzu_tester/service/yuzutest.h"
34
35#ifdef _WIN32
36// windows.h needs to be included before shellapi.h
37#include <windows.h>
38
39#include <shellapi.h>
40#endif
41
42#undef _UNICODE
43#include <getopt.h>
44#ifndef _MSC_VER
45#include <unistd.h>
46#endif
47
48#ifdef _WIN32
49extern "C" {
50// tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable
51// graphics
52__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
53__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
54}
55#endif
56
57static void PrintHelp(const char* argv0) {
58 std::cout << "Usage: " << argv0
59 << " [options] <filename>\n"
60 "-h, --help Display this help and exit\n"
61 "-v, --version Output version information and exit\n"
62 "-d, --datastring Pass following string as data to test service command #2\n"
63 "-l, --log Log to console in addition to file (will log to file only "
64 "by default)\n";
65}
66
67static void PrintVersion() {
68 std::cout << "yuzu [Test Utility] " << Common::g_scm_branch << " " << Common::g_scm_desc
69 << std::endl;
70}
71
72static void InitializeLogging(bool console) {
73 Log::Filter log_filter(Log::Level::Debug);
74 log_filter.ParseFilterString(Settings::values.log_filter);
75 Log::SetGlobalFilter(log_filter);
76
77 if (console)
78 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
79
80 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
81 FileUtil::CreateFullPath(log_dir);
82 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
83#ifdef _WIN32
84 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
85#endif
86}
87
88/// Application entry point
89int main(int argc, char** argv) {
90 Common::DetachedTasks detached_tasks;
91 Config config;
92
93 int option_index = 0;
94
95 char* endarg;
96#ifdef _WIN32
97 int argc_w;
98 auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
99
100 if (argv_w == nullptr) {
101 std::cout << "Failed to get command line arguments" << std::endl;
102 return -1;
103 }
104#endif
105 std::string filepath;
106
107 static struct option long_options[] = {
108 {"help", no_argument, 0, 'h'},
109 {"version", no_argument, 0, 'v'},
110 {"datastring", optional_argument, 0, 'd'},
111 {"log", no_argument, 0, 'l'},
112 {0, 0, 0, 0},
113 };
114
115 bool console_log = false;
116 std::string datastring;
117
118 while (optind < argc) {
119 int arg = getopt_long(argc, argv, "hvdl::", long_options, &option_index);
120 if (arg != -1) {
121 switch (static_cast<char>(arg)) {
122 case 'h':
123 PrintHelp(argv[0]);
124 return 0;
125 case 'v':
126 PrintVersion();
127 return 0;
128 case 'd':
129 datastring = argv[optind];
130 ++optind;
131 break;
132 case 'l':
133 console_log = true;
134 break;
135 }
136 } else {
137#ifdef _WIN32
138 filepath = Common::UTF16ToUTF8(argv_w[optind]);
139#else
140 filepath = argv[optind];
141#endif
142 optind++;
143 }
144 }
145
146 InitializeLogging(console_log);
147
148#ifdef _WIN32
149 LocalFree(argv_w);
150#endif
151
152 MicroProfileOnThreadCreate("EmuThread");
153 SCOPE_EXIT({ MicroProfileShutdown(); });
154
155 if (filepath.empty()) {
156 LOG_CRITICAL(Frontend, "Failed to load application: No application specified");
157 std::cout << "Failed to load application: No application specified" << std::endl;
158 PrintHelp(argv[0]);
159 return -1;
160 }
161
162 Settings::values.use_gdbstub = false;
163 Settings::Apply();
164
165 std::unique_ptr<EmuWindow_SDL2_Hide> emu_window{std::make_unique<EmuWindow_SDL2_Hide>()};
166
167 if (!Settings::values.use_multi_core) {
168 // Single core mode must acquire OpenGL context for entire emulation session
169 emu_window->MakeCurrent();
170 }
171
172 bool finished = false;
173 int return_value = 0;
174 const auto callback = [&finished,
175 &return_value](std::vector<Service::Yuzu::TestResult> results) {
176 finished = true;
177 return_value = 0;
178
179 // Find the minimum length needed to fully enclose all test names (and the header field) in
180 // the fmt::format column by first finding the maximum size of any test name and comparing
181 // that to 9, the string length of 'Test Name'
182 const auto needed_length_name =
183 std::max<u64>(std::max_element(results.begin(), results.end(),
184 [](const auto& lhs, const auto& rhs) {
185 return lhs.name.size() < rhs.name.size();
186 })
187 ->name.size(),
188 9ull);
189
190 std::size_t passed = 0;
191 std::size_t failed = 0;
192
193 std::cout << fmt::format("Result [Res Code] | {:<{}} | Extra Data", "Test Name",
194 needed_length_name)
195 << std::endl;
196
197 for (const auto& res : results) {
198 const auto main_res = res.code == 0 ? "PASSED" : "FAILED";
199 if (res.code == 0)
200 ++passed;
201 else
202 ++failed;
203 std::cout << fmt::format("{} [{:08X}] | {:<{}} | {}", main_res, res.code, res.name,
204 needed_length_name, res.data)
205 << std::endl;
206 }
207
208 std::cout << std::endl
209 << fmt::format("{:4d} Passed | {:4d} Failed | {:4d} Total | {:2.2f} Passed Ratio",
210 passed, failed, passed + failed,
211 static_cast<float>(passed) / (passed + failed))
212 << std::endl
213 << (failed == 0 ? "PASSED" : "FAILED") << std::endl;
214
215 if (failed > 0)
216 return_value = -1;
217 };
218
219 Core::System& system{Core::System::GetInstance()};
220 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
221 Service::FileSystem::CreateFactories(*system.GetFilesystem());
222
223 SCOPE_EXIT({ system.Shutdown(); });
224
225 const Core::System::ResultStatus load_result{system.Load(*emu_window, filepath)};
226
227 switch (load_result) {
228 case Core::System::ResultStatus::ErrorGetLoader:
229 LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", filepath.c_str());
230 return -1;
231 case Core::System::ResultStatus::ErrorLoader:
232 LOG_CRITICAL(Frontend, "Failed to load ROM!");
233 return -1;
234 case Core::System::ResultStatus::ErrorNotInitialized:
235 LOG_CRITICAL(Frontend, "CPUCore not initialized");
236 return -1;
237 case Core::System::ResultStatus::ErrorVideoCore:
238 LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!");
239 return -1;
240 case Core::System::ResultStatus::Success:
241 break; // Expected case
242 default:
243 if (static_cast<u32>(load_result) >
244 static_cast<u32>(Core::System::ResultStatus::ErrorLoader)) {
245 const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader);
246 const u16 error_id = static_cast<u16>(load_result) - loader_id;
247 LOG_CRITICAL(Frontend,
248 "While attempting to load the ROM requested, an error occured. Please "
249 "refer to the yuzu wiki for more information or the yuzu discord for "
250 "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
251 loader_id, error_id, static_cast<Loader::ResultStatus>(error_id));
252 }
253 }
254
255 Service::Yuzu::InstallInterfaces(system.ServiceManager(), datastring, callback);
256
257 system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDLHideTester");
258
259 system.Renderer().Rasterizer().LoadDiskResources();
260
261 while (!finished) {
262 system.RunLoop();
263 }
264
265 detached_tasks.WaitForAllTasks();
266 return return_value;
267}