First Commit
This commit is contained in:
27
tests/ctest/CMakeLists.txt
Normal file
27
tests/ctest/CMakeLists.txt
Normal file
@@ -0,0 +1,27 @@
|
||||
enable_testing()
|
||||
add_custom_target(unittests)
|
||||
add_custom_command(TARGET unittests POST_BUILD COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure)
|
||||
|
||||
macro(add_pcsx2_test target)
|
||||
add_executable(${target} EXCLUDE_FROM_ALL ${ARGN})
|
||||
target_link_libraries(${target} PRIVATE gtest gtest_main)
|
||||
if(APPLE)
|
||||
# For some reason this doesn't get pulled in implicitly...
|
||||
target_link_libraries(${target} PRIVATE
|
||||
"-framework Foundation"
|
||||
"-framework Cocoa"
|
||||
)
|
||||
endif()
|
||||
|
||||
add_dependencies(unittests ${target})
|
||||
add_test(NAME ${target} COMMAND ${target})
|
||||
|
||||
if(MSVC AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
# For some reason, the stack refuses to grow with the latest MSVC updates.
|
||||
# Just force the commit size to 1MB for now.
|
||||
set_target_properties(${target} PROPERTIES LINK_FLAGS "/STACK:1048576,1048576")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
add_subdirectory(common)
|
||||
add_subdirectory(core)
|
||||
18
tests/ctest/common/CMakeLists.txt
Normal file
18
tests/ctest/common/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
add_pcsx2_test(common_test
|
||||
byteswap_tests.cpp
|
||||
filesystem_tests.cpp
|
||||
path_tests.cpp
|
||||
string_util_tests.cpp
|
||||
)
|
||||
|
||||
if(_M_X86)
|
||||
target_sources(common_test PRIVATE
|
||||
x86emitter/codegen_tests.cpp
|
||||
x86emitter/codegen_tests.h
|
||||
x86emitter/codegen_tests_main.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(common_test PRIVATE
|
||||
common
|
||||
)
|
||||
14
tests/ctest/common/byteswap_tests.cpp
Normal file
14
tests/ctest/common/byteswap_tests.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "common/ByteSwap.h"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(ByteSwap, ByteSwap)
|
||||
{
|
||||
ASSERT_EQ(ByteSwap(static_cast<u16>(0xabcd)), 0xcdabu);
|
||||
ASSERT_EQ(ByteSwap(static_cast<u32>(0xabcdef01)), 0x01efcdabu);
|
||||
ASSERT_EQ(ByteSwap(static_cast<u64>(0xabcdef0123456789ULL)), 0x8967452301efcdabu);
|
||||
ASSERT_EQ(ByteSwap(static_cast<s32>(0x80123456)), 0x56341280);
|
||||
}
|
||||
58
tests/ctest/common/filesystem_tests.cpp
Normal file
58
tests/ctest/common/filesystem_tests.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Path.h"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
static std::optional<std::string> create_test_directory()
|
||||
{
|
||||
for (u16 i = 0; i < UINT16_MAX; i++)
|
||||
{
|
||||
std::string path = std::string("/tmp/pcsx2_filesystem_test_") + std::to_string(i);
|
||||
if (!FileSystem::DirectoryExists(path.c_str()))
|
||||
{
|
||||
if (!FileSystem::CreateDirectoryPath(path.c_str(), false))
|
||||
break;
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
TEST(FileSystem, RecursiveDeleteDirectoryDontFollowSymbolicLinks)
|
||||
{
|
||||
// Find a suitable location to write some test files.
|
||||
std::optional<std::string> test_dir = create_test_directory();
|
||||
ASSERT_TRUE(test_dir.has_value());
|
||||
|
||||
// Create a target directory containing a file that shouldn't be deleted.
|
||||
std::string target_dir = Path::Combine(*test_dir, "target_dir");
|
||||
ASSERT_TRUE(FileSystem::CreateDirectoryPath(target_dir.c_str(), false));
|
||||
std::string file_path = Path::Combine(target_dir, "file.txt");
|
||||
ASSERT_TRUE(FileSystem::WriteStringToFile(file_path.c_str(), "Lorem ipsum!"));
|
||||
|
||||
// Create a directory containing a symlink to the target directory.
|
||||
std::string dir_to_delete = Path::Combine(*test_dir, "dir_to_delete");
|
||||
ASSERT_TRUE(FileSystem::CreateDirectoryPath(dir_to_delete.c_str(), false));
|
||||
std::string symlink_path = Path::Combine(dir_to_delete, "link");
|
||||
ASSERT_TRUE(FileSystem::CreateSymLink(symlink_path.c_str(), target_dir.c_str()));
|
||||
|
||||
// Delete the directory containing the symlink.
|
||||
ASSERT_TRUE(dir_to_delete.starts_with("/tmp/pcsx2_filesystem_test_"));
|
||||
ASSERT_TRUE(FileSystem::RecursiveDeleteDirectory(dir_to_delete.c_str()));
|
||||
|
||||
// Make sure the file in the target directory didn't get deleted.
|
||||
ASSERT_TRUE(FileSystem::FileExists(file_path.c_str()));
|
||||
|
||||
// Clean up.
|
||||
ASSERT_TRUE(FileSystem::DeleteFilePath(file_path.c_str()));
|
||||
ASSERT_TRUE(FileSystem::DeleteDirectory(target_dir.c_str()));
|
||||
ASSERT_TRUE(FileSystem::DeleteDirectory(test_dir->c_str()));
|
||||
}
|
||||
|
||||
#endif
|
||||
367
tests/ctest/common/path_tests.cpp
Normal file
367
tests/ctest/common/path_tests.cpp
Normal file
@@ -0,0 +1,367 @@
|
||||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "common/Path.h"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(Path, ToNativePath)
|
||||
{
|
||||
ASSERT_EQ(Path::ToNativePath(""), "");
|
||||
|
||||
#ifdef _WIN32
|
||||
ASSERT_EQ(Path::ToNativePath("foo"), "foo");
|
||||
ASSERT_EQ(Path::ToNativePath("foo\\"), "foo");
|
||||
ASSERT_EQ(Path::ToNativePath("foo\\\\bar"), "foo\\bar");
|
||||
ASSERT_EQ(Path::ToNativePath("foo\\bar"), "foo\\bar");
|
||||
ASSERT_EQ(Path::ToNativePath("foo\\bar\\baz"), "foo\\bar\\baz");
|
||||
ASSERT_EQ(Path::ToNativePath("foo\\bar/baz"), "foo\\bar\\baz");
|
||||
ASSERT_EQ(Path::ToNativePath("foo/bar/baz"), "foo\\bar\\baz");
|
||||
ASSERT_EQ(Path::ToNativePath("foo/🙃bar/b🙃az"), "foo\\🙃bar\\b🙃az");
|
||||
ASSERT_EQ(Path::ToNativePath("\\\\foo\\bar\\baz"), "\\\\foo\\bar\\baz");
|
||||
#else
|
||||
ASSERT_EQ(Path::ToNativePath("foo"), "foo");
|
||||
ASSERT_EQ(Path::ToNativePath("foo/"), "foo");
|
||||
ASSERT_EQ(Path::ToNativePath("foo//bar"), "foo/bar");
|
||||
ASSERT_EQ(Path::ToNativePath("foo/bar"), "foo/bar");
|
||||
ASSERT_EQ(Path::ToNativePath("foo/bar/baz"), "foo/bar/baz");
|
||||
ASSERT_EQ(Path::ToNativePath("/foo/bar/baz"), "/foo/bar/baz");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(Path, IsValidFileName)
|
||||
{
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
ASSERT_FALSE(Path::IsValidFileName("foo:bar", false));
|
||||
ASSERT_FALSE(Path::IsValidFileName("baz\\foo:bar", false));
|
||||
ASSERT_FALSE(Path::IsValidFileName("baz/foo:bar", false));
|
||||
ASSERT_FALSE(Path::IsValidFileName("baz\\foo:bar", true));
|
||||
ASSERT_FALSE(Path::IsValidFileName("baz/foo:bar", true));
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
ASSERT_TRUE(Path::IsValidFileName("baz\\foo", true));
|
||||
ASSERT_FALSE(Path::IsValidFileName("baz\\foo", false));
|
||||
ASSERT_FALSE(Path::IsValidFileName("foo.", true));
|
||||
ASSERT_FALSE(Path::IsValidFileName("foo\\.", true));
|
||||
#else
|
||||
ASSERT_FALSE(Path::IsValidFileName("foo\\*", true));
|
||||
ASSERT_FALSE(Path::IsValidFileName("foo*", true));
|
||||
#endif
|
||||
|
||||
ASSERT_TRUE(Path::IsValidFileName("baz/foo", true));
|
||||
ASSERT_FALSE(Path::IsValidFileName("baz/foo", false));
|
||||
}
|
||||
|
||||
TEST(Path, IsAbsolute)
|
||||
{
|
||||
ASSERT_FALSE(Path::IsAbsolute(""));
|
||||
ASSERT_FALSE(Path::IsAbsolute("foo"));
|
||||
ASSERT_FALSE(Path::IsAbsolute("foo/bar"));
|
||||
ASSERT_FALSE(Path::IsAbsolute("foo/b🙃ar"));
|
||||
#ifdef _WIN32
|
||||
ASSERT_TRUE(Path::IsAbsolute("C:\\foo/bar"));
|
||||
ASSERT_TRUE(Path::IsAbsolute("C://foo\\bar"));
|
||||
ASSERT_FALSE(Path::IsAbsolute("\\foo/bar"));
|
||||
ASSERT_TRUE(Path::IsAbsolute("\\\\foo\\bar\\baz"));
|
||||
#else
|
||||
ASSERT_TRUE(Path::IsAbsolute("/foo/bar"));
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(Path, Canonicalize)
|
||||
{
|
||||
ASSERT_EQ(Path::Canonicalize(""), Path::ToNativePath(""));
|
||||
ASSERT_EQ(Path::Canonicalize("foo/bar/../baz"), Path::ToNativePath("foo/baz"));
|
||||
ASSERT_EQ(Path::Canonicalize("foo/bar/./baz"), Path::ToNativePath("foo/bar/baz"));
|
||||
ASSERT_EQ(Path::Canonicalize("foo/./bar/./baz"), Path::ToNativePath("foo/bar/baz"));
|
||||
ASSERT_EQ(Path::Canonicalize("foo/bar/../baz/../foo"), Path::ToNativePath("foo/foo"));
|
||||
ASSERT_EQ(Path::Canonicalize("foo/bar/../baz/./foo"), Path::ToNativePath("foo/baz/foo"));
|
||||
ASSERT_EQ(Path::Canonicalize("./foo"), Path::ToNativePath("foo"));
|
||||
ASSERT_EQ(Path::Canonicalize("../foo"), Path::ToNativePath("../foo"));
|
||||
ASSERT_EQ(Path::Canonicalize("foo/b🙃ar/../b🙃az/./foo"), Path::ToNativePath("foo/b🙃az/foo"));
|
||||
ASSERT_EQ(Path::Canonicalize("ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱/b🙃az/../foℹ︎o"), Path::ToNativePath("ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱/foℹ︎o"));
|
||||
#ifdef _WIN32
|
||||
ASSERT_EQ(Path::Canonicalize("C:\\foo\\bar\\..\\baz\\.\\foo"), "C:\\foo\\baz\\foo");
|
||||
ASSERT_EQ(Path::Canonicalize("C:/foo\\bar\\..\\baz\\.\\foo"), "C:\\foo\\baz\\foo");
|
||||
ASSERT_EQ(Path::Canonicalize("foo\\bar\\..\\baz\\.\\foo"), "foo\\baz\\foo");
|
||||
ASSERT_EQ(Path::Canonicalize("foo\\bar/..\\baz/.\\foo"), "foo\\baz\\foo");
|
||||
ASSERT_EQ(Path::Canonicalize("\\\\foo\\bar\\baz/..\\foo"), "\\\\foo\\bar\\foo");
|
||||
#else
|
||||
ASSERT_EQ(Path::Canonicalize("/foo/bar/../baz/./foo"), "/foo/baz/foo");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(Path, Combine)
|
||||
{
|
||||
ASSERT_EQ(Path::Combine("", ""), Path::ToNativePath(""));
|
||||
ASSERT_EQ(Path::Combine("foo", "bar"), Path::ToNativePath("foo/bar"));
|
||||
ASSERT_EQ(Path::Combine("foo/bar", "baz"), Path::ToNativePath("foo/bar/baz"));
|
||||
ASSERT_EQ(Path::Combine("foo/bar", "../baz"), Path::ToNativePath("foo/bar/../baz"));
|
||||
ASSERT_EQ(Path::Combine("foo/bar/", "/baz/"), Path::ToNativePath("foo/bar/baz"));
|
||||
ASSERT_EQ(Path::Combine("foo//bar", "baz/"), Path::ToNativePath("foo/bar/baz"));
|
||||
ASSERT_EQ(Path::Combine("foo//ba🙃r", "b🙃az/"), Path::ToNativePath("foo/ba🙃r/b🙃az"));
|
||||
#ifdef _WIN32
|
||||
ASSERT_EQ(Path::Combine("C:\\foo\\bar", "baz"), "C:\\foo\\bar\\baz");
|
||||
ASSERT_EQ(Path::Combine("\\\\server\\foo\\bar", "baz"), "\\\\server\\foo\\bar\\baz");
|
||||
ASSERT_EQ(Path::Combine("foo\\bar", "baz"), "foo\\bar\\baz");
|
||||
ASSERT_EQ(Path::Combine("foo\\bar\\", "baz"), "foo\\bar\\baz");
|
||||
ASSERT_EQ(Path::Combine("foo/bar\\", "\\baz"), "foo\\bar\\baz");
|
||||
ASSERT_EQ(Path::Combine("\\\\foo\\bar", "baz"), "\\\\foo\\bar\\baz");
|
||||
#else
|
||||
ASSERT_EQ(Path::Combine("/foo/bar", "baz"), "/foo/bar/baz");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(Path, AppendDirectory)
|
||||
{
|
||||
ASSERT_EQ(Path::AppendDirectory("foo/bar", "baz"), Path::ToNativePath("foo/baz/bar"));
|
||||
ASSERT_EQ(Path::AppendDirectory("", "baz"), Path::ToNativePath("baz"));
|
||||
ASSERT_EQ(Path::AppendDirectory("", ""), Path::ToNativePath(""));
|
||||
ASSERT_EQ(Path::AppendDirectory("foo/bar", "🙃"), Path::ToNativePath("foo/🙃/bar"));
|
||||
#ifdef _WIN32
|
||||
ASSERT_EQ(Path::AppendDirectory("foo\\bar", "baz"), "foo\\baz\\bar");
|
||||
ASSERT_EQ(Path::AppendDirectory("\\\\foo\\bar", "baz"), "\\\\foo\\baz\\bar");
|
||||
#else
|
||||
ASSERT_EQ(Path::AppendDirectory("/foo/bar", "baz"), "/foo/baz/bar");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(Path, MakeRelative)
|
||||
{
|
||||
ASSERT_EQ(Path::MakeRelative("", ""), Path::ToNativePath(""));
|
||||
ASSERT_EQ(Path::MakeRelative("foo", ""), Path::ToNativePath("foo"));
|
||||
ASSERT_EQ(Path::MakeRelative("", "foo"), Path::ToNativePath(""));
|
||||
ASSERT_EQ(Path::MakeRelative("foo", "bar"), Path::ToNativePath("foo"));
|
||||
|
||||
#ifdef _WIN32
|
||||
#define A "C:\\"
|
||||
#else
|
||||
#define A "/"
|
||||
#endif
|
||||
|
||||
ASSERT_EQ(Path::MakeRelative(A "foo", A "bar"), Path::ToNativePath("../foo"));
|
||||
ASSERT_EQ(Path::MakeRelative(A "foo/bar", A "foo"), Path::ToNativePath("bar"));
|
||||
ASSERT_EQ(Path::MakeRelative(A "foo/bar", A "foo/baz"), Path::ToNativePath("../bar"));
|
||||
ASSERT_EQ(Path::MakeRelative(A "foo/b🙃ar", A "foo/b🙃az"), Path::ToNativePath("../b🙃ar"));
|
||||
ASSERT_EQ(Path::MakeRelative(A "f🙃oo/b🙃ar", A "f🙃oo/b🙃az"), Path::ToNativePath("../b🙃ar"));
|
||||
ASSERT_EQ(Path::MakeRelative(A "ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱/b🙃ar", A "ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱/b🙃az"), Path::ToNativePath("../b🙃ar"));
|
||||
|
||||
#undef A
|
||||
|
||||
#ifdef _WIN32
|
||||
ASSERT_EQ(Path::MakeRelative("\\\\foo\\bar\\baz\\foo", "\\\\foo\\bar\\baz"), "foo");
|
||||
ASSERT_EQ(Path::MakeRelative("\\\\foo\\bar\\foo", "\\\\foo\\bar\\baz"), "..\\foo");
|
||||
ASSERT_EQ(Path::MakeRelative("\\\\foo\\bar\\foo", "\\\\other\\bar\\foo"), "\\\\foo\\bar\\foo");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(Path, GetExtension)
|
||||
{
|
||||
ASSERT_EQ(Path::GetExtension("foo"), "");
|
||||
ASSERT_EQ(Path::GetExtension("foo.txt"), "txt");
|
||||
ASSERT_EQ(Path::GetExtension("foo.t🙃t"), "t🙃t");
|
||||
ASSERT_EQ(Path::GetExtension("foo."), "");
|
||||
ASSERT_EQ(Path::GetExtension("a/b/foo.txt"), "txt");
|
||||
ASSERT_EQ(Path::GetExtension("a/b/foo"), "");
|
||||
}
|
||||
|
||||
TEST(Path, GetFileName)
|
||||
{
|
||||
ASSERT_EQ(Path::GetFileName(""), "");
|
||||
ASSERT_EQ(Path::GetFileName("foo"), "foo");
|
||||
ASSERT_EQ(Path::GetFileName("foo.txt"), "foo.txt");
|
||||
ASSERT_EQ(Path::GetFileName("foo"), "foo");
|
||||
ASSERT_EQ(Path::GetFileName("foo/bar/."), ".");
|
||||
ASSERT_EQ(Path::GetFileName("foo/bar/baz"), "baz");
|
||||
ASSERT_EQ(Path::GetFileName("foo/bar/baz.txt"), "baz.txt");
|
||||
#ifdef _WIN32
|
||||
ASSERT_EQ(Path::GetFileName("foo/bar\\baz"), "baz");
|
||||
ASSERT_EQ(Path::GetFileName("foo\\bar\\baz.txt"), "baz.txt");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(Path, GetFileTitle)
|
||||
{
|
||||
ASSERT_EQ(Path::GetFileTitle(""), "");
|
||||
ASSERT_EQ(Path::GetFileTitle("foo"), "foo");
|
||||
ASSERT_EQ(Path::GetFileTitle("foo.txt"), "foo");
|
||||
ASSERT_EQ(Path::GetFileTitle("foo/bar/."), "");
|
||||
ASSERT_EQ(Path::GetFileTitle("foo/bar/baz"), "baz");
|
||||
ASSERT_EQ(Path::GetFileTitle("foo/bar/baz.txt"), "baz");
|
||||
#ifdef _WIN32
|
||||
ASSERT_EQ(Path::GetFileTitle("foo/bar\\baz"), "baz");
|
||||
ASSERT_EQ(Path::GetFileTitle("foo\\bar\\baz.txt"), "baz");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(Path, GetDirectory)
|
||||
{
|
||||
ASSERT_EQ(Path::GetDirectory(""), "");
|
||||
ASSERT_EQ(Path::GetDirectory("foo"), "");
|
||||
ASSERT_EQ(Path::GetDirectory("foo.txt"), "");
|
||||
ASSERT_EQ(Path::GetDirectory("foo/bar/."), "foo/bar");
|
||||
ASSERT_EQ(Path::GetDirectory("foo/bar/baz"), "foo/bar");
|
||||
ASSERT_EQ(Path::GetDirectory("foo/bar/baz.txt"), "foo/bar");
|
||||
#ifdef _WIN32
|
||||
ASSERT_EQ(Path::GetDirectory("foo\\bar\\baz"), "foo\\bar");
|
||||
ASSERT_EQ(Path::GetDirectory("foo\\bar/baz.txt"), "foo\\bar");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(Path, ChangeFileName)
|
||||
{
|
||||
ASSERT_EQ(Path::ChangeFileName("", ""), Path::ToNativePath(""));
|
||||
ASSERT_EQ(Path::ChangeFileName("", "bar"), Path::ToNativePath("bar"));
|
||||
ASSERT_EQ(Path::ChangeFileName("bar", ""), Path::ToNativePath(""));
|
||||
ASSERT_EQ(Path::ChangeFileName("foo/bar", ""), Path::ToNativePath("foo"));
|
||||
ASSERT_EQ(Path::ChangeFileName("foo/", "bar"), Path::ToNativePath("foo/bar"));
|
||||
ASSERT_EQ(Path::ChangeFileName("foo/bar", "baz"), Path::ToNativePath("foo/baz"));
|
||||
ASSERT_EQ(Path::ChangeFileName("foo//bar", "baz"), Path::ToNativePath("foo/baz"));
|
||||
ASSERT_EQ(Path::ChangeFileName("foo//bar.txt", "baz.txt"), Path::ToNativePath("foo/baz.txt"));
|
||||
ASSERT_EQ(Path::ChangeFileName("foo//ba🙃r.txt", "ba🙃z.txt"), Path::ToNativePath("foo/ba🙃z.txt"));
|
||||
#ifdef _WIN32
|
||||
ASSERT_EQ(Path::ChangeFileName("foo/bar", "baz"), "foo\\baz");
|
||||
ASSERT_EQ(Path::ChangeFileName("foo//bar\\foo", "baz"), "foo\\bar\\baz");
|
||||
ASSERT_EQ(Path::ChangeFileName("\\\\foo\\bar\\foo", "baz"), "\\\\foo\\bar\\baz");
|
||||
#else
|
||||
ASSERT_EQ(Path::ChangeFileName("/foo/bar", "baz"), "/foo/baz");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(Path, CreateFileURL)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
ASSERT_EQ(Path::CreateFileURL("C:\\foo\\bar"), "file:///C:/foo/bar");
|
||||
ASSERT_EQ(Path::CreateFileURL("\\\\server\\share\\file.txt"), "file://server/share/file.txt");
|
||||
#else
|
||||
ASSERT_EQ(Path::CreateFileURL("/foo/bar"), "file:///foo/bar");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if __linux__
|
||||
|
||||
static std::optional<std::string> create_test_directory()
|
||||
{
|
||||
for (u16 i = 0; i < UINT16_MAX; i++)
|
||||
{
|
||||
std::string path = std::string("/tmp/pcsx2_path_test_") + std::to_string(i);
|
||||
if (!FileSystem::DirectoryExists(path.c_str()))
|
||||
{
|
||||
if (!FileSystem::CreateDirectoryPath(path.c_str(), false))
|
||||
break;
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
TEST(Path, RealPathAbsoluteSymbolicLink)
|
||||
{
|
||||
std::optional<std::string> test_dir = create_test_directory();
|
||||
ASSERT_TRUE(test_dir.has_value());
|
||||
|
||||
// Create a file to point at.
|
||||
std::string file_path = Path::Combine(*test_dir, "file");
|
||||
ASSERT_TRUE(FileSystem::WriteStringToFile(file_path.c_str(), "Hello, world!"));
|
||||
|
||||
// Create a symbolic link that points to said file.
|
||||
std::string link_path = Path::Combine(*test_dir, "link");
|
||||
ASSERT_TRUE(FileSystem::CreateSymLink(link_path.c_str(), file_path.c_str()));
|
||||
|
||||
// Make sure the symbolic link is resolved correctly.
|
||||
ASSERT_EQ(Path::RealPath(link_path), file_path);
|
||||
|
||||
// Clean up.
|
||||
ASSERT_TRUE(FileSystem::DeleteSymbolicLink(link_path.c_str()));
|
||||
ASSERT_TRUE(FileSystem::DeleteFilePath(file_path.c_str()));
|
||||
ASSERT_TRUE(FileSystem::DeleteDirectory(test_dir->c_str()));
|
||||
}
|
||||
|
||||
TEST(Path, RealPathRelativeSymbolicLink)
|
||||
{
|
||||
std::optional<std::string> test_dir = create_test_directory();
|
||||
ASSERT_TRUE(test_dir.has_value());
|
||||
|
||||
// Create a file to point at.
|
||||
std::string file_path = Path::Combine(*test_dir, "file");
|
||||
ASSERT_TRUE(FileSystem::WriteStringToFile(file_path.c_str(), "Hello, world!"));
|
||||
|
||||
// Create a symbolic link that points to said file.
|
||||
std::string link_path = Path::Combine(*test_dir, "link");
|
||||
ASSERT_TRUE(FileSystem::CreateSymLink(link_path.c_str(), "file"));
|
||||
|
||||
// Make sure the symbolic link is resolved correctly.
|
||||
ASSERT_EQ(Path::RealPath(link_path), file_path);
|
||||
|
||||
// Clean up.
|
||||
ASSERT_TRUE(FileSystem::DeleteSymbolicLink(link_path.c_str()));
|
||||
ASSERT_TRUE(FileSystem::DeleteFilePath(file_path.c_str()));
|
||||
ASSERT_TRUE(FileSystem::DeleteDirectory(test_dir->c_str()));
|
||||
}
|
||||
|
||||
TEST(Path, RealPathDotDotSymbolicLink)
|
||||
{
|
||||
std::optional<std::string> test_dir = create_test_directory();
|
||||
ASSERT_TRUE(test_dir.has_value());
|
||||
|
||||
// Create a file to point at.
|
||||
std::string file_path = Path::Combine(*test_dir, "file");
|
||||
ASSERT_TRUE(FileSystem::WriteStringToFile(file_path.c_str(), "Hello, world!"));
|
||||
|
||||
// Create a directory to put the link in.
|
||||
std::string link_dir = Path::Combine(*test_dir, "dir");
|
||||
ASSERT_TRUE(FileSystem::CreateDirectoryPath(link_dir.c_str(), false));
|
||||
|
||||
// Create a symbolic link that points to said file.
|
||||
std::string link_path = Path::Combine(link_dir, "link");
|
||||
ASSERT_TRUE(FileSystem::CreateSymLink(link_path.c_str(), "../file"));
|
||||
|
||||
// Make sure the symbolic link is resolved correctly.
|
||||
ASSERT_EQ(Path::RealPath(link_path), file_path);
|
||||
|
||||
// Clean up.
|
||||
ASSERT_TRUE(FileSystem::DeleteSymbolicLink(link_path.c_str()));
|
||||
ASSERT_TRUE(FileSystem::DeleteDirectory(link_dir.c_str()));
|
||||
ASSERT_TRUE(FileSystem::DeleteFilePath(file_path.c_str()));
|
||||
ASSERT_TRUE(FileSystem::DeleteDirectory(test_dir->c_str()));
|
||||
}
|
||||
|
||||
TEST(Path, RealPathCircularSymbolicLink)
|
||||
{
|
||||
std::optional<std::string> test_dir = create_test_directory();
|
||||
ASSERT_TRUE(test_dir.has_value());
|
||||
|
||||
// Create a circular symbolic link.
|
||||
std::string link_path = Path::Combine(*test_dir, "link");
|
||||
ASSERT_TRUE(FileSystem::CreateSymLink(link_path.c_str(), "."));
|
||||
|
||||
// Make sure the link gets resolved correctly.
|
||||
ASSERT_EQ(Path::RealPath(link_path), *test_dir);
|
||||
ASSERT_EQ(Path::RealPath(Path::Combine(link_path, "link")), *test_dir);
|
||||
|
||||
// Clean up.
|
||||
ASSERT_TRUE(FileSystem::DeleteSymbolicLink(link_path.c_str()));
|
||||
ASSERT_TRUE(FileSystem::DeleteDirectory(test_dir->c_str()));
|
||||
}
|
||||
|
||||
TEST(Path, RealPathLoopingSymbolicLink)
|
||||
{
|
||||
std::optional<std::string> test_dir = create_test_directory();
|
||||
ASSERT_TRUE(test_dir.has_value());
|
||||
|
||||
// Create a symbolic link that points to itself.
|
||||
std::string link_path = Path::Combine(*test_dir, "link");
|
||||
ASSERT_TRUE(FileSystem::CreateSymLink(link_path.c_str(), "link"));
|
||||
|
||||
// Make sure this doesn't cause problems.
|
||||
ASSERT_EQ(Path::RealPath(link_path), link_path);
|
||||
|
||||
// Clean up.
|
||||
ASSERT_TRUE(FileSystem::DeleteSymbolicLink(link_path.c_str()));
|
||||
ASSERT_TRUE(FileSystem::DeleteDirectory(test_dir->c_str()));
|
||||
}
|
||||
|
||||
#endif
|
||||
132
tests/ctest/common/string_util_tests.cpp
Normal file
132
tests/ctest/common/string_util_tests.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include <locale>
|
||||
#include <clocale>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(StringUtil, ToChars)
|
||||
{
|
||||
ASSERT_EQ(StringUtil::ToChars(false), "false");
|
||||
ASSERT_EQ(StringUtil::ToChars(true), "true");
|
||||
ASSERT_EQ(StringUtil::ToChars(0), "0");
|
||||
ASSERT_EQ(StringUtil::ToChars(-1337), "-1337");
|
||||
ASSERT_EQ(StringUtil::ToChars(1337), "1337");
|
||||
ASSERT_EQ(StringUtil::ToChars(1337u), "1337");
|
||||
ASSERT_EQ(StringUtil::ToChars(13.37f), "13.37");
|
||||
ASSERT_EQ(StringUtil::ToChars(255, 16), "ff");
|
||||
}
|
||||
|
||||
TEST(StringUtil, FromChars)
|
||||
{
|
||||
ASSERT_EQ(StringUtil::FromChars<bool>("false").value_or(true), false);
|
||||
ASSERT_EQ(StringUtil::FromChars<bool>("true").value_or(false), true);
|
||||
ASSERT_EQ(StringUtil::FromChars<int>("0").value_or(-1), 0);
|
||||
ASSERT_EQ(StringUtil::FromChars<int>("-1337").value_or(0), -1337);
|
||||
ASSERT_EQ(StringUtil::FromChars<int>("1337").value_or(0), 1337);
|
||||
ASSERT_EQ(StringUtil::FromChars<u32>("1337").value_or(0), 1337);
|
||||
ASSERT_TRUE(std::abs(StringUtil::FromChars<float>("13.37").value_or(0.0f) - 13.37) < 0.01f);
|
||||
ASSERT_EQ(StringUtil::FromChars<int>("ff", 16).value_or(0), 255);
|
||||
}
|
||||
|
||||
TEST(StringUtil, FromCharsWithEndPtr)
|
||||
{
|
||||
using namespace std::literals;
|
||||
|
||||
std::string_view endptr;
|
||||
ASSERT_EQ(StringUtil::FromChars<u32>("123x456", 16, &endptr), std::optional<u32>(0x123));
|
||||
ASSERT_EQ(endptr, "x456"sv);
|
||||
|
||||
ASSERT_EQ(StringUtil::FromChars<u32>("0x1234", 16, &endptr), std::optional<u32>(0u));
|
||||
ASSERT_EQ(endptr, "x1234"sv);
|
||||
|
||||
ASSERT_EQ(StringUtil::FromChars<u32>("1234", 16, &endptr), std::optional<u32>(0x1234u));
|
||||
ASSERT_TRUE(endptr.empty());
|
||||
|
||||
ASSERT_EQ(StringUtil::FromChars<u32>("abcdefg", 16, &endptr), std::optional<u32>(0xabcdef));
|
||||
ASSERT_EQ(endptr, "g"sv);
|
||||
|
||||
ASSERT_EQ(StringUtil::FromChars<u32>("123abc", 10, &endptr), std::optional<u32>(123));
|
||||
ASSERT_EQ(endptr, "abc"sv);
|
||||
|
||||
ASSERT_EQ(StringUtil::FromChars<float>("1.0g", &endptr), std::optional<float>(1.0f));
|
||||
ASSERT_EQ(endptr, "g"sv);
|
||||
|
||||
ASSERT_EQ(StringUtil::FromChars<float>("2x", &endptr), std::optional<float>(2.0f));
|
||||
ASSERT_EQ(endptr, "x"sv);
|
||||
|
||||
ASSERT_EQ(StringUtil::FromChars<float>(".1p", &endptr), std::optional<float>(0.1f));
|
||||
ASSERT_EQ(endptr, "p"sv);
|
||||
|
||||
ASSERT_EQ(StringUtil::FromChars<float>("1", &endptr), std::optional<float>(1.0f));
|
||||
ASSERT_TRUE(endptr.empty());
|
||||
}
|
||||
|
||||
#if 0
|
||||
// NOTE: These tests are disabled, because they require the da_DK locale to actually be present.
|
||||
// Which probably isn't going to be the case on the CI.
|
||||
|
||||
TEST(StringUtil, ToCharsIsLocaleIndependent)
|
||||
{
|
||||
const auto old_locale = std::locale();
|
||||
std::locale::global(std::locale::classic());
|
||||
|
||||
std::string classic_result(StringUtil::ToChars(13.37f));
|
||||
|
||||
std::locale::global(std::locale("da_DK"));
|
||||
|
||||
std::string dk_result(StringUtil::ToChars(13.37f));
|
||||
|
||||
std::locale::global(old_locale);
|
||||
|
||||
ASSERT_EQ(classic_result, dk_result);
|
||||
}
|
||||
|
||||
TEST(StringUtil, FromCharsIsLocaleIndependent)
|
||||
{
|
||||
const auto old_locale = std::locale();
|
||||
std::locale::global(std::locale::classic());
|
||||
|
||||
const float classic_result = StringUtil::FromChars<float>("13.37").value_or(0.0f);
|
||||
|
||||
std::locale::global(std::locale("da_DK"));
|
||||
|
||||
const float dk_result = StringUtil::FromChars<float>("13.37").value_or(0.0f);
|
||||
|
||||
std::locale::global(old_locale);
|
||||
|
||||
ASSERT_EQ(classic_result, dk_result);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
TEST(StringUtil, Ellipsise)
|
||||
{
|
||||
ASSERT_EQ(StringUtil::Ellipsise("HelloWorld", 6, "..."), "Hel...");
|
||||
ASSERT_EQ(StringUtil::Ellipsise("HelloWorld", 7, ".."), "Hello..");
|
||||
ASSERT_EQ(StringUtil::Ellipsise("HelloWorld", 20, ".."), "HelloWorld");
|
||||
ASSERT_EQ(StringUtil::Ellipsise("", 20, "..."), "");
|
||||
ASSERT_EQ(StringUtil::Ellipsise("Hello", 10, "..."), "Hello");
|
||||
}
|
||||
|
||||
TEST(StringUtil, EllipsiseInPlace)
|
||||
{
|
||||
std::string s;
|
||||
s = "HelloWorld";
|
||||
StringUtil::EllipsiseInPlace(s, 6, "...");
|
||||
ASSERT_EQ(s, "Hel...");
|
||||
s = "HelloWorld";
|
||||
StringUtil::EllipsiseInPlace(s, 7, "..");
|
||||
ASSERT_EQ(s, "Hello..");
|
||||
s = "HelloWorld";
|
||||
StringUtil::EllipsiseInPlace(s, 20, "..");
|
||||
ASSERT_EQ(s, "HelloWorld");
|
||||
s = "";
|
||||
StringUtil::EllipsiseInPlace(s, 20, "...");
|
||||
ASSERT_EQ(s, "");
|
||||
s = "Hello";
|
||||
StringUtil::EllipsiseInPlace(s, 10, "...");
|
||||
ASSERT_EQ(s, "Hello");
|
||||
}
|
||||
32
tests/ctest/common/x86emitter/codegen_tests.cpp
Normal file
32
tests/ctest/common/x86emitter/codegen_tests.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "codegen_tests.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <common/emitter/x86emitter.h>
|
||||
#include <common/Assertions.h>
|
||||
|
||||
using namespace x86Emitter;
|
||||
|
||||
thread_local const char *currentTest;
|
||||
|
||||
void runCodegenTest(void (*exec)(void *base), const char* description, const char* expected) {
|
||||
u8 code[4096];
|
||||
memset(code, 0xcc, sizeof(code));
|
||||
char str[4096] = {0};
|
||||
|
||||
if (!expected) return;
|
||||
currentTest = description;
|
||||
xSetPtr(code);
|
||||
exec(code);
|
||||
char *strPtr = str;
|
||||
for (u8* ptr = code; ptr < xGetPtr(); ptr++) {
|
||||
sprintf(strPtr, "%02x ", *ptr);
|
||||
strPtr += 3;
|
||||
}
|
||||
if (strPtr != str) {
|
||||
// Remove final space
|
||||
*--strPtr = '\0';
|
||||
}
|
||||
EXPECT_STRCASEEQ(expected, str) << "Unexpected codegen from " << description;
|
||||
}
|
||||
8
tests/ctest/common/x86emitter/codegen_tests.h
Normal file
8
tests/ctest/common/x86emitter/codegen_tests.h
Normal file
@@ -0,0 +1,8 @@
|
||||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
|
||||
void runCodegenTest(void (*exec)(void *base), const char* description, const char* expected);
|
||||
|
||||
#define CODEGEN_TEST(command, expected) runCodegenTest([](void *base){ command; }, #command, expected)
|
||||
285
tests/ctest/common/x86emitter/codegen_tests_main.cpp
Normal file
285
tests/ctest/common/x86emitter/codegen_tests_main.cpp
Normal file
@@ -0,0 +1,285 @@
|
||||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "codegen_tests.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <common/emitter/x86emitter.h>
|
||||
#include <cstdio>
|
||||
|
||||
using namespace x86Emitter;
|
||||
|
||||
TEST(CodegenTests, MOVTest)
|
||||
{
|
||||
CODEGEN_TEST(xMOV(rax, 0), "31 c0");
|
||||
CODEGEN_TEST(xMOV(rax, rcx), "48 89 c8");
|
||||
CODEGEN_TEST(xMOV(eax, ecx), "89 c8");
|
||||
CODEGEN_TEST(xMOV(r8, 0), "45 31 c0");
|
||||
CODEGEN_TEST(xMOV(rax, r8), "4c 89 c0");
|
||||
CODEGEN_TEST(xMOV(r8, rax), "49 89 c0");
|
||||
CODEGEN_TEST(xMOV(r8, r9), "4d 89 c8");
|
||||
CODEGEN_TEST(xMOV(rax, ptr64[rcx]), "48 8b 01");
|
||||
CODEGEN_TEST(xMOV(eax, ptr32[rcx]), "8b 01");
|
||||
CODEGEN_TEST(xMOV(ptr64[rax], rcx), "48 89 08");
|
||||
CODEGEN_TEST(xMOV(ptr32[rax], ecx), "89 08");
|
||||
CODEGEN_TEST(xMOV(rax, ptr64[r8]), "49 8b 00");
|
||||
CODEGEN_TEST(xMOV(ptr64[r8], rax), "49 89 00");
|
||||
CODEGEN_TEST(xMOV(r8, ptr64[r9]), "4d 8b 01");
|
||||
CODEGEN_TEST(xMOV(ptr64[r8], r9), "4d 89 08");
|
||||
CODEGEN_TEST(xMOV(rax, ptr64[rbx*4+3+rcx]), "48 8b 44 99 03");
|
||||
CODEGEN_TEST(xMOV(ptr64[rbx*4+3+rax], rcx), "48 89 4c 98 03");
|
||||
CODEGEN_TEST(xMOV(eax, ptr32[rbx*4+3+rcx]), "8b 44 99 03");
|
||||
CODEGEN_TEST(xMOV(ptr32[rbx*4+3+rax], ecx), "89 4c 98 03");
|
||||
CODEGEN_TEST(xMOV(r8, ptr64[r10*4+3+r9]), "4f 8b 44 91 03");
|
||||
CODEGEN_TEST(xMOV(ptr64[r9*4+3+r8], r10), "4f 89 54 88 03");
|
||||
CODEGEN_TEST(xMOV(ptr64[r8], 0), "49 c7 00 00 00 00 00");
|
||||
CODEGEN_TEST(xMOV(ptr32[rax], 0), "c7 00 00 00 00 00");
|
||||
CODEGEN_TEST(xMOV(ptr32[rbx*4+3+rax], -1), "c7 44 98 03 ff ff ff ff");
|
||||
CODEGEN_TEST(xMOV(rax, 0xffffffff), "b8 ff ff ff ff");
|
||||
CODEGEN_TEST(xMOV(r8, -1), "49 c7 c0 ff ff ff ff");
|
||||
CODEGEN_TEST(xMOV64(rax, 0x1234567890), "48 b8 90 78 56 34 12 00 00 00");
|
||||
CODEGEN_TEST(xMOV64(r8, 0x1234567890), "49 b8 90 78 56 34 12 00 00 00");
|
||||
CODEGEN_TEST(xMOV(ptr32[base], 0x12), "c7 05 f6 ff ff ff 12 00 00 00");
|
||||
CODEGEN_TEST(xMOVSX(eax, dx), "0f bf c2");
|
||||
CODEGEN_TEST(xMOVSX(rax, r8d), "49 63 c0");
|
||||
CODEGEN_TEST(xMOVSX(rax, ebx), "48 63 c3");
|
||||
}
|
||||
|
||||
TEST(CodegenTests, LEATest)
|
||||
{
|
||||
CODEGEN_TEST(xLEA(rax, ptr[rcx]), "48 89 c8"); // Converted to mov rax, rcx
|
||||
CODEGEN_TEST(xLEA(eax, ptr[rcx]), "89 c8"); // Converted to mov eax, ecx
|
||||
CODEGEN_TEST(xLEA(rax, ptr[r8]), "4c 89 c0"); // Converted to mov rax, r8
|
||||
CODEGEN_TEST(xLEA(r8, ptr[r9]), "4d 89 c8"); // Converted to mov r8, r9
|
||||
CODEGEN_TEST(xLEA(rax, ptr[rbx*4+3+rcx]), "48 8d 44 99 03");
|
||||
CODEGEN_TEST(xLEA(eax, ptr32[rbx*4+3+rcx]), "8d 44 99 03");
|
||||
CODEGEN_TEST(xLEA(r8, ptr[r10*4+3+r9]), "4f 8d 44 91 03");
|
||||
CODEGEN_TEST(xLEA(r8, ptr[base]), "4c 8d 05 f9 ff ff ff");
|
||||
CODEGEN_TEST(xLoadFarAddr(r8, base), "4c 8d 05 f9 ff ff ff");
|
||||
CODEGEN_TEST(xLoadFarAddr(r8, (void*)0xff00001234567890), "49 b8 90 78 56 34 12 00 00 ff");
|
||||
CODEGEN_TEST(xLEA(rax, ptr[(void*)0x1234]), "b8 34 12 00 00"); // Converted to mov rax, 0x1234
|
||||
CODEGEN_TEST(xLoadFarAddr(rax, (void*)0x1234), "b8 34 12 00 00");
|
||||
CODEGEN_TEST(xLEA_Writeback(rbx), "48 8d 1d cd cd cd 0d");
|
||||
}
|
||||
|
||||
TEST(CodegenTests, PUSHTest)
|
||||
{
|
||||
CODEGEN_TEST(xPUSH(rax), "50");
|
||||
CODEGEN_TEST(xPUSH(r8), "41 50");
|
||||
CODEGEN_TEST(xPUSH(0x1234), "68 34 12 00 00");
|
||||
CODEGEN_TEST(xPUSH(0x12), "6a 12");
|
||||
CODEGEN_TEST(xPUSH(ptr64[rax]), "ff 30");
|
||||
CODEGEN_TEST(xPUSH(ptr64[r8]), "41 ff 30");
|
||||
CODEGEN_TEST(xPUSH(ptr64[rax*2+3+rbx]), "ff 74 43 03");
|
||||
CODEGEN_TEST(xPUSH(ptr64[rax*2+3+r8]), "41 ff 74 40 03");
|
||||
CODEGEN_TEST(xPUSH(ptr64[r9*4+3+r8]), "43 ff 74 88 03");
|
||||
CODEGEN_TEST(xPUSH(ptr64[r8*4+3+rax]), "42 ff 74 80 03");
|
||||
CODEGEN_TEST(xPUSH(ptr64[rax*8+0x1234+rbx]), "ff b4 c3 34 12 00 00");
|
||||
CODEGEN_TEST(xPUSH(ptr64[base]), "ff 35 fa ff ff ff");
|
||||
CODEGEN_TEST(xPUSH(ptr64[(void*)0x1234]), "ff 34 25 34 12 00 00");
|
||||
}
|
||||
|
||||
TEST(CodegenTests, POPTest)
|
||||
{
|
||||
CODEGEN_TEST(xPOP(rax), "58");
|
||||
CODEGEN_TEST(xPOP(r8), "41 58");
|
||||
CODEGEN_TEST(xPOP(ptr64[rax]), "8f 00");
|
||||
CODEGEN_TEST(xPOP(ptr64[r8]), "41 8f 00");
|
||||
CODEGEN_TEST(xPOP(ptr64[rax*2+3+rbx]), "8f 44 43 03");
|
||||
CODEGEN_TEST(xPOP(ptr64[rax*2+3+r8]), "41 8f 44 40 03");
|
||||
CODEGEN_TEST(xPOP(ptr64[r9*4+3+r8]), "43 8f 44 88 03");
|
||||
CODEGEN_TEST(xPOP(ptr64[r8*4+3+rax]), "42 8f 44 80 03");
|
||||
CODEGEN_TEST(xPOP(ptr64[rax*8+0x1234+rbx]), "8f 84 c3 34 12 00 00");
|
||||
CODEGEN_TEST(xPOP(ptr64[base]), "8f 05 fa ff ff ff");
|
||||
CODEGEN_TEST(xPOP(ptr64[(void*)0x1234]), "8f 04 25 34 12 00 00");
|
||||
}
|
||||
|
||||
TEST(CodegenTests, MathTest)
|
||||
{
|
||||
CODEGEN_TEST(xINC(eax), "ff c0");
|
||||
CODEGEN_TEST(xDEC(rax), "48 ff c8");
|
||||
CODEGEN_TEST(xINC(r8), "49 ff c0");
|
||||
CODEGEN_TEST(xADD(r8, r9), "4d 01 c8");
|
||||
CODEGEN_TEST(xADD(r8, 0x12), "49 83 c0 12");
|
||||
CODEGEN_TEST(xADD(rax, 0x1234), "48 05 34 12 00 00");
|
||||
CODEGEN_TEST(xADD(ptr8[base], 1), "80 05 f9 ff ff ff 01");
|
||||
CODEGEN_TEST(xADD(ptr32[base], -0x60), "83 05 f9 ff ff ff a0");
|
||||
CODEGEN_TEST(xADD(ptr32[base], 0x1234), "81 05 f6 ff ff ff 34 12 00 00");
|
||||
CODEGEN_TEST(xADD(eax, ebx), "01 d8");
|
||||
CODEGEN_TEST(xADD(eax, 0x1234), "05 34 12 00 00");
|
||||
CODEGEN_TEST(xADD(r8, ptr64[r10*4+3+r9]), "4f 03 44 91 03");
|
||||
CODEGEN_TEST(xADD(ptr64[r9*4+3+r8], r10), "4f 01 54 88 03");
|
||||
CODEGEN_TEST(xADD(eax, ptr32[rbx*4+3+rcx]), "03 44 99 03");
|
||||
CODEGEN_TEST(xADD(ptr32[rax*4+3+rbx], ecx), "01 4c 83 03");
|
||||
CODEGEN_TEST(xSUB(r8, 0x12), "49 83 e8 12");
|
||||
CODEGEN_TEST(xSUB(rax, 0x1234), "48 2d 34 12 00 00");
|
||||
CODEGEN_TEST(xSUB(eax, ptr32[rcx*4+rax]), "2b 04 88");
|
||||
CODEGEN_TEST(xMUL(ptr32[base]), "f7 2d fa ff ff ff");
|
||||
CODEGEN_TEST(xMUL(ptr32[(void*)0x1234]), "f7 2c 25 34 12 00 00");
|
||||
CODEGEN_TEST(xDIV(ecx), "f7 f9");
|
||||
}
|
||||
|
||||
TEST(CodegenTests, BitwiseTest)
|
||||
{
|
||||
CODEGEN_TEST(xSHR(r8, cl), "49 d3 e8");
|
||||
CODEGEN_TEST(xSHR(rax, cl), "48 d3 e8");
|
||||
CODEGEN_TEST(xSHR(ecx, cl), "d3 e9");
|
||||
CODEGEN_TEST(xSAR(r8, 1), "49 d1 f8");
|
||||
CODEGEN_TEST(xSAR(rax, 60), "48 c1 f8 3c");
|
||||
CODEGEN_TEST(xSAR(eax, 30), "c1 f8 1e");
|
||||
CODEGEN_TEST(xSHL(ebx, 30), "c1 e3 1e");
|
||||
CODEGEN_TEST(xSHL(ptr32[base], 4), "c1 25 f9 ff ff ff 04");
|
||||
CODEGEN_TEST(xAND(r8, r9), "4d 21 c8");
|
||||
CODEGEN_TEST(xXOR(rax, ptr64[r10]), "49 33 02");
|
||||
CODEGEN_TEST(xOR(esi, ptr32[rax+rbx]), "0b 34 18");
|
||||
CODEGEN_TEST(xNOT(r8), "49 f7 d0");
|
||||
CODEGEN_TEST(xNOT(ptr64[rax]), "48 f7 10");
|
||||
CODEGEN_TEST(xNOT(ptr32[rbx]), "f7 13");
|
||||
}
|
||||
|
||||
TEST(CodegenTests, JmpTest)
|
||||
{
|
||||
CODEGEN_TEST(xJMP(r8), "41 ff e0");
|
||||
CODEGEN_TEST(xJMP(rdi), "ff e7");
|
||||
CODEGEN_TEST(xJMP(ptr64[rax]), "ff 20");
|
||||
CODEGEN_TEST(xJA(base), "77 fe");
|
||||
CODEGEN_TEST(xJB((char*)base - 0xFFFF), "0f 82 fb ff fe ff");
|
||||
}
|
||||
|
||||
TEST(CodegenTests, SSETest)
|
||||
{
|
||||
CODEGEN_TEST(xMOVAPS(xmm0, xmm1), "0f 28 c1");
|
||||
CODEGEN_TEST(xMOVAPS(xmm8, xmm9), "45 0f 28 c1");
|
||||
CODEGEN_TEST(xMOVUPS(xmm8, ptr128[r8+r9]), "47 0f 10 04 08");
|
||||
CODEGEN_TEST(xMOVAPS(ptr128[rax+r9], xmm8), "46 0f 29 04 08");
|
||||
CODEGEN_TEST(xBLEND.PS(xmm0, xmm1, 0x55), "66 0f 3a 0c c1 55");
|
||||
CODEGEN_TEST(xBLEND.PD(xmm8, xmm9, 0xaa), "66 45 0f 3a 0d c1 aa");
|
||||
CODEGEN_TEST(xPBLEND.W(xmm0, xmm1, 0x55), "66 0f 3a 0e c1 55");
|
||||
CODEGEN_TEST(xPBLEND.VB(xmm1, xmm2), "66 0f 38 10 ca");
|
||||
CODEGEN_TEST(xEXTRACTPS(ptr32[base], xmm1, 2), "66 0f 3a 17 0d f6 ff ff ff 02");
|
||||
CODEGEN_TEST(xMOVD(eax, xmm1), "66 0f 7e c8");
|
||||
CODEGEN_TEST(xMOVD(eax, xmm10), "66 44 0f 7e d0");
|
||||
CODEGEN_TEST(xMOVD(rax, xmm1), "66 48 0f 7e c8");
|
||||
CODEGEN_TEST(xMOVD(r10, xmm1), "66 49 0f 7e ca");
|
||||
CODEGEN_TEST(xMOVD(rax, xmm10), "66 4c 0f 7e d0");
|
||||
CODEGEN_TEST(xMOVD(r10, xmm10), "66 4d 0f 7e d2");
|
||||
CODEGEN_TEST(xPINSR.B(xmm0, ebx, 1), "66 0f 3a 20 c3 01");
|
||||
CODEGEN_TEST(xPINSR.W(xmm0, ebx, 1), "66 0f c4 c3 01");
|
||||
CODEGEN_TEST(xPINSR.D(xmm0, ebx, 1), "66 0f 3a 22 c3 01");
|
||||
CODEGEN_TEST(xPINSR.Q(xmm0, rbx, 1), "66 48 0f 3a 22 c3 01");
|
||||
CODEGEN_TEST(xPEXTR.B(ebx, xmm0, 1), "66 0f 3a 14 c3 01");
|
||||
CODEGEN_TEST(xPEXTR.W(ebx, xmm0, 1), "66 0f c5 c3 01");
|
||||
CODEGEN_TEST(xPEXTR.D(ebx, xmm0, 1), "66 0f 3a 16 c3 01");
|
||||
CODEGEN_TEST(xPEXTR.Q(rbx, xmm0, 1), "66 48 0f 3a 16 c3 01");
|
||||
CODEGEN_TEST(xPEXTR.Q(ptr64[rax], xmm0, 1), "66 48 0f 3a 16 00 01");
|
||||
}
|
||||
|
||||
TEST(CodegenTests, AVXTest)
|
||||
{
|
||||
CODEGEN_TEST(xVMOVAPS(xmm0, xmm1), "c5 f8 28 c1");
|
||||
CODEGEN_TEST(xVMOVAPS(xmm0, ptr32[rdi]), "c5 f8 28 07");
|
||||
CODEGEN_TEST(xVMOVAPS(ptr32[rdi], xmm0), "c5 f8 29 07");
|
||||
CODEGEN_TEST(xVMOVUPS(xmm0, ptr32[rdi]), "c5 f8 10 07");
|
||||
CODEGEN_TEST(xVMOVUPS(ptr32[rdi], xmm0), "c5 f8 11 07");
|
||||
|
||||
CODEGEN_TEST(xVADD.PS(xmm0, xmm1, xmm2), "c5 f0 58 c2");
|
||||
CODEGEN_TEST(xVADD.PD(xmm0, xmm1, xmm2), "c5 f1 58 c2");
|
||||
CODEGEN_TEST(xVADD.SS(xmm0, xmm1, xmm2), "c5 f2 58 c2");
|
||||
CODEGEN_TEST(xVADD.SD(xmm0, xmm1, xmm2), "c5 f3 58 c2");
|
||||
CODEGEN_TEST(xVSUB.PS(xmm0, xmm1, xmm2), "c5 f0 5c c2");
|
||||
CODEGEN_TEST(xVSUB.PD(xmm0, xmm1, xmm2), "c5 f1 5c c2");
|
||||
CODEGEN_TEST(xVSUB.SS(xmm0, xmm1, xmm2), "c5 f2 5c c2");
|
||||
CODEGEN_TEST(xVSUB.SD(xmm0, xmm1, xmm2), "c5 f3 5c c2");
|
||||
CODEGEN_TEST(xVMUL.PS(xmm0, xmm1, xmm2), "c5 f0 59 c2");
|
||||
CODEGEN_TEST(xVMUL.PD(xmm0, xmm1, xmm2), "c5 f1 59 c2");
|
||||
CODEGEN_TEST(xVMUL.SS(xmm0, xmm1, xmm2), "c5 f2 59 c2");
|
||||
CODEGEN_TEST(xVMUL.SD(xmm0, xmm1, xmm2), "c5 f3 59 c2");
|
||||
CODEGEN_TEST(xVDIV.PS(xmm0, xmm1, xmm2), "c5 f0 5e c2");
|
||||
CODEGEN_TEST(xVDIV.PD(xmm0, xmm1, xmm2), "c5 f1 5e c2");
|
||||
CODEGEN_TEST(xVDIV.SS(xmm0, xmm1, xmm2), "c5 f2 5e c2");
|
||||
CODEGEN_TEST(xVDIV.SD(xmm0, xmm1, xmm2), "c5 f3 5e c2");
|
||||
|
||||
// Don't need to test all variants, since they just change the condition immediate.
|
||||
CODEGEN_TEST(xVCMP.EQ.PS(xmm0, xmm1, xmm2), "c5 f0 c2 c2 00");
|
||||
CODEGEN_TEST(xVCMP.EQ.PD(xmm0, xmm1, xmm2), "c5 f1 c2 c2 00");
|
||||
CODEGEN_TEST(xVCMP.EQ.SS(xmm0, xmm1, xmm2), "c5 f2 c2 c2 00");
|
||||
CODEGEN_TEST(xVCMP.EQ.SD(xmm0, xmm1, xmm2), "c5 f3 c2 c2 00");
|
||||
CODEGEN_TEST(xVCMP.LE.PS(xmm0, xmm1, xmm2), "c5 f0 c2 c2 02");
|
||||
CODEGEN_TEST(xVCMP.LE.PD(xmm0, xmm1, xmm2), "c5 f1 c2 c2 02");
|
||||
CODEGEN_TEST(xVCMP.LE.SS(xmm0, xmm1, xmm2), "c5 f2 c2 c2 02");
|
||||
CODEGEN_TEST(xVCMP.LE.SD(xmm0, xmm1, xmm2), "c5 f3 c2 c2 02");
|
||||
|
||||
CODEGEN_TEST(xVPCMP.EQB(xmm0, xmm1, xmm2), "c5 f1 74 c2");
|
||||
CODEGEN_TEST(xVPCMP.EQW(xmm0, xmm1, xmm2), "c5 f1 75 c2");
|
||||
CODEGEN_TEST(xVPCMP.EQD(xmm0, xmm1, xmm2), "c5 f1 76 c2");
|
||||
CODEGEN_TEST(xVPCMP.GTB(xmm0, xmm1, xmm2), "c5 f1 64 c2");
|
||||
CODEGEN_TEST(xVPCMP.GTW(xmm0, xmm1, xmm2), "c5 f1 65 c2");
|
||||
CODEGEN_TEST(xVPCMP.GTD(xmm0, xmm1, xmm2), "c5 f1 66 c2");
|
||||
|
||||
CODEGEN_TEST(xVPAND(xmm0, xmm1, xmm2), "c5 f1 db c2");
|
||||
CODEGEN_TEST(xVPANDN(xmm0, xmm1, xmm2), "c5 f1 df c2");
|
||||
CODEGEN_TEST(xVPOR(xmm0, xmm1, xmm2), "c5 f1 eb c2");
|
||||
CODEGEN_TEST(xVPXOR(xmm0, xmm1, xmm2), "c5 f1 ef c2");
|
||||
|
||||
CODEGEN_TEST(xVMOVMSKPS(eax, xmm1), "c5 f8 50 c1");
|
||||
CODEGEN_TEST(xVMOVMSKPD(eax, xmm1), "c5 f9 50 c1");
|
||||
}
|
||||
|
||||
TEST(CodegenTests, AVX256Test)
|
||||
{
|
||||
CODEGEN_TEST(xVMOVAPS(ymm0, ymm1), "c5 fc 28 c1");
|
||||
CODEGEN_TEST(xVMOVAPS(ymm0, ptr32[rdi]), "c5 fc 28 07");
|
||||
CODEGEN_TEST(xVMOVAPS(ptr32[rdi], ymm0), "c5 fc 29 07");
|
||||
CODEGEN_TEST(xVMOVUPS(ymm0, ptr32[rdi]), "c5 fc 10 07");
|
||||
CODEGEN_TEST(xVMOVUPS(ptr32[rdi], ymm0), "c5 fc 11 07");
|
||||
|
||||
CODEGEN_TEST(xVZEROUPPER(), "c5 f8 77");
|
||||
|
||||
CODEGEN_TEST(xVADD.PS(ymm0, ymm1, ymm2), "c5 f4 58 c2");
|
||||
CODEGEN_TEST(xVADD.PD(ymm0, ymm1, ymm2), "c5 f5 58 c2");
|
||||
CODEGEN_TEST(xVSUB.PS(ymm0, ymm1, ymm2), "c5 f4 5c c2");
|
||||
CODEGEN_TEST(xVSUB.PD(ymm0, ymm1, ymm2), "c5 f5 5c c2");
|
||||
CODEGEN_TEST(xVMUL.PS(ymm0, ymm1, ymm2), "c5 f4 59 c2");
|
||||
CODEGEN_TEST(xVMUL.PD(ymm0, ymm1, ymm2), "c5 f5 59 c2");
|
||||
CODEGEN_TEST(xVDIV.PS(ymm0, ymm1, ymm2), "c5 f4 5e c2");
|
||||
CODEGEN_TEST(xVDIV.PD(ymm0, ymm1, ymm2), "c5 f5 5e c2");
|
||||
|
||||
CODEGEN_TEST(xVCMP.EQ.PS(ymm0, ymm1, ymm2), "c5 f4 c2 c2 00");
|
||||
CODEGEN_TEST(xVCMP.EQ.PD(ymm0, ymm1, ymm2), "c5 f5 c2 c2 00");
|
||||
CODEGEN_TEST(xVCMP.LE.PS(ymm0, ymm1, ymm2), "c5 f4 c2 c2 02");
|
||||
CODEGEN_TEST(xVCMP.LE.PD(ymm0, ymm1, ymm2), "c5 f5 c2 c2 02");
|
||||
|
||||
CODEGEN_TEST(xVPCMP.EQB(ymm0, ymm1, ymm2), "c5 f5 74 c2");
|
||||
CODEGEN_TEST(xVPCMP.EQW(ymm0, ymm1, ymm2), "c5 f5 75 c2");
|
||||
CODEGEN_TEST(xVPCMP.EQD(ymm0, ymm1, ymm2), "c5 f5 76 c2");
|
||||
CODEGEN_TEST(xVPCMP.GTB(ymm0, ymm1, ymm2), "c5 f5 64 c2");
|
||||
CODEGEN_TEST(xVPCMP.GTW(ymm0, ymm1, ymm2), "c5 f5 65 c2");
|
||||
CODEGEN_TEST(xVPCMP.GTD(ymm0, ymm1, ymm2), "c5 f5 66 c2");
|
||||
|
||||
CODEGEN_TEST(xVPAND(ymm0, ymm1, ymm2), "c5 f5 db c2");
|
||||
CODEGEN_TEST(xVPANDN(ymm0, ymm1, ymm2), "c5 f5 df c2");
|
||||
CODEGEN_TEST(xVPOR(ymm0, ymm1, ymm2), "c5 f5 eb c2");
|
||||
CODEGEN_TEST(xVPXOR(ymm0, ymm1, ymm2), "c5 f5 ef c2");
|
||||
|
||||
CODEGEN_TEST(xVMOVMSKPS(eax, ymm1), "c5 fc 50 c1");
|
||||
CODEGEN_TEST(xVMOVMSKPD(eax, ymm1), "c5 fd 50 c1");
|
||||
}
|
||||
|
||||
TEST(CodegenTests, Extended8BitTest)
|
||||
{
|
||||
CODEGEN_TEST(xSETL(al), "0f 9c c0");
|
||||
CODEGEN_TEST(xSETL(cl), "0f 9c c1");
|
||||
CODEGEN_TEST(xSETL(dl), "0f 9c c2");
|
||||
CODEGEN_TEST(xSETL(bl), "0f 9c c3");
|
||||
CODEGEN_TEST(xSETL(spl), "40 0f 9c c4");
|
||||
CODEGEN_TEST(xSETL(bpl), "40 0f 9c c5");
|
||||
CODEGEN_TEST(xSETL(sil), "40 0f 9c c6");
|
||||
CODEGEN_TEST(xSETL(dil), "40 0f 9c c7");
|
||||
CODEGEN_TEST(xSETL(r8b), "41 0f 9c c0");
|
||||
CODEGEN_TEST(xSETL(r9b), "41 0f 9c c1");
|
||||
CODEGEN_TEST(xSETL(r10b), "41 0f 9c c2");
|
||||
CODEGEN_TEST(xSETL(r11b), "41 0f 9c c3");
|
||||
CODEGEN_TEST(xSETL(r12b), "41 0f 9c c4");
|
||||
CODEGEN_TEST(xSETL(r13b), "41 0f 9c c5");
|
||||
CODEGEN_TEST(xSETL(r14b), "41 0f 9c c6");
|
||||
CODEGEN_TEST(xSETL(r15b), "41 0f 9c c7");
|
||||
}
|
||||
75
tests/ctest/core/CMakeLists.txt
Normal file
75
tests/ctest/core/CMakeLists.txt
Normal file
@@ -0,0 +1,75 @@
|
||||
add_pcsx2_test(core_test
|
||||
StubHost.cpp
|
||||
)
|
||||
|
||||
set(multi_isa_sources
|
||||
GS/swizzle_test_main.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(core_test PUBLIC
|
||||
PCSX2_FLAGS
|
||||
PCSX2
|
||||
common
|
||||
)
|
||||
|
||||
if(DISABLE_ADVANCE_SIMD)
|
||||
if(WIN32)
|
||||
set(compile_options_avx2 /arch:AVX2)
|
||||
set(compile_options_avx /arch:AVX)
|
||||
elseif(USE_GCC)
|
||||
# GCC can't inline into multi-isa functions if we use march and mtune, but can if we use feature flags
|
||||
set(compile_options_avx2 -msse4.1 -mavx -mavx2 -mbmi -mbmi2 -mfma)
|
||||
set(compile_options_avx -msse4.1 -mavx)
|
||||
set(compile_options_sse4 -msse4.1)
|
||||
else()
|
||||
set(compile_options_avx2 -march=haswell -mtune=haswell)
|
||||
set(compile_options_avx -march=sandybridge -mtune=sandybridge)
|
||||
set(compile_options_sse4 -msse4.1 -mtune=nehalem)
|
||||
endif()
|
||||
|
||||
# This breaks when running on Apple Silicon, because even though we skip the test itself, the
|
||||
# gtest constructor still generates AVX code, and that's a global object which gets constructed
|
||||
# at binary load time. So, for now, only compile SSE4 if running on ARM64.
|
||||
if (NOT APPLE OR "${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "x86_64")
|
||||
set(isa_list "sse4" "avx" "avx2")
|
||||
else()
|
||||
set(isa_list "sse4")
|
||||
endif()
|
||||
|
||||
# ODR violation time!
|
||||
# Everything would be fine if we only defined things in cpp files, but C++ tends to like inline functions (STL anyone?)
|
||||
# Each ISA will bring with it its own copies of these inline header functions, and the linker gets to choose whichever one it wants! Not fun if the linker chooses the avx2 version and uses it with everything
|
||||
# Thankfully, most linkers don't choose at random. When presented with a bunch of .o files, most linkers seem to choose the first implementation they see, so make sure you order these from oldest to newest
|
||||
# Note: ld64 (macOS's linker) does not act the same way when presented with .a files, unless linked with `-force_load` (cmake WHOLE_ARCHIVE).
|
||||
set(is_first_isa "1")
|
||||
foreach(isa IN LISTS isa_list)
|
||||
add_library(core_test_${isa} STATIC ${multi_isa_sources})
|
||||
target_link_libraries(core_test_${isa} PRIVATE PCSX2_FLAGS gtest)
|
||||
target_compile_definitions(core_test_${isa} PRIVATE MULTI_ISA_UNSHARED_COMPILATION=isa_${isa} MULTI_ISA_IS_FIRST=${is_first_isa} ${pcsx2_defs_${isa}})
|
||||
target_compile_options(core_test_${isa} PRIVATE ${compile_options_${isa}})
|
||||
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.24)
|
||||
target_link_libraries(core_test PRIVATE $<LINK_LIBRARY:WHOLE_ARCHIVE,core_test_${isa}>)
|
||||
elseif(APPLE)
|
||||
message(FATAL_ERROR "MacOS builds with DISABLE_ADVANCE_SIMD=ON require CMake 3.24")
|
||||
else()
|
||||
target_link_libraries(core_test PRIVATE core_test_${isa})
|
||||
endif()
|
||||
set(is_first_isa "0")
|
||||
endforeach()
|
||||
else()
|
||||
target_sources(core_test PRIVATE ${multi_isa_sources})
|
||||
endif()
|
||||
|
||||
if(WIN32 AND TARGET SDL3::SDL3)
|
||||
# Copy SDL3 DLL to binary directory.
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
get_property(SDL3_DLL_PATH TARGET SDL3::SDL3 PROPERTY IMPORTED_LOCATION_DEBUG)
|
||||
else()
|
||||
get_property(SDL3_DLL_PATH TARGET SDL3::SDL3 PROPERTY IMPORTED_LOCATION_RELEASE)
|
||||
endif()
|
||||
if(SDL3_DLL_PATH)
|
||||
add_custom_command(TARGET core_test POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory "$<TARGET_FILE_DIR:core_test>"
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${SDL3_DLL_PATH}" "$<TARGET_FILE_DIR:core_test>")
|
||||
endif()
|
||||
endif()
|
||||
590
tests/ctest/core/GS/swizzle_test_main.cpp
Normal file
590
tests/ctest/core/GS/swizzle_test_main.cpp
Normal file
@@ -0,0 +1,590 @@
|
||||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "pcsx2/GS/GSBlock.h"
|
||||
#include "pcsx2/GS/GSClut.h"
|
||||
#include "pcsx2/GS/MultiISA.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cpuinfo.h"
|
||||
|
||||
#ifdef MULTI_ISA_UNSHARED_COMPILATION
|
||||
|
||||
enum class TestISA
|
||||
{
|
||||
isa_sse4,
|
||||
isa_avx,
|
||||
isa_avx2,
|
||||
isa_native,
|
||||
};
|
||||
|
||||
static bool CheckCapabilities(TestISA required_caps)
|
||||
{
|
||||
cpuinfo_initialize();
|
||||
if (required_caps == TestISA::isa_avx && !cpuinfo_has_x86_avx())
|
||||
return false;
|
||||
if (required_caps == TestISA::isa_avx2 && !cpuinfo_has_x86_avx2())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define MULTI_ISA_STRINGIZE_(x) #x
|
||||
#define MULTI_ISA_STRINGIZE(x) MULTI_ISA_STRINGIZE_(x)
|
||||
|
||||
#define MULTI_ISA_CONCAT_(a, b) a##b
|
||||
#define MULTI_ISA_CONCAT(a, b) MULTI_ISA_CONCAT_(a, b)
|
||||
|
||||
#define MULTI_ISA_TEST(group, name) TEST(MULTI_ISA_CONCAT(MULTI_ISA_CONCAT(MULTI_ISA_UNSHARED_COMPILATION, _), group), name)
|
||||
#define SKIP_IF_UNSUPPORTED() \
|
||||
if (!CheckCapabilities(TestISA::MULTI_ISA_UNSHARED_COMPILATION)) { \
|
||||
GTEST_SKIP() << "Host CPU does not support " MULTI_ISA_STRINGIZE(MULTI_ISA_UNSHARED_COMPILATION); \
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define MULTI_ISA_TEST(group, name) TEST(group, name)
|
||||
#define SKIP_IF_UNSUPPORTED()
|
||||
|
||||
#endif
|
||||
|
||||
MULTI_ISA_UNSHARED_START
|
||||
|
||||
static void swizzle(const u8* table, u8* dst, const u8* src, int bpp, bool deswizzle)
|
||||
{
|
||||
int pxbytes = bpp / 8;
|
||||
for (int i = 0; i < (256 / pxbytes); i++)
|
||||
{
|
||||
int soff = (deswizzle ? table[i] : i) * pxbytes;
|
||||
int doff = (deswizzle ? i : table[i]) * pxbytes;
|
||||
memcpy(&dst[doff], &src[soff], pxbytes);
|
||||
}
|
||||
}
|
||||
|
||||
static void swizzle4(const u16* table, u8* dst, const u8* src, bool deswizzle)
|
||||
{
|
||||
for (int i = 0; i < 512; i++)
|
||||
{
|
||||
int soff = (deswizzle ? table[i] : i);
|
||||
int doff = (deswizzle ? i : table[i]);
|
||||
int spx = src[soff >> 1] >> ((soff & 1) * 4) & 0xF;
|
||||
u8* dpx = &dst[doff >> 1];
|
||||
int dshift = (doff & 1) * 4;
|
||||
*dpx &= (0xF0 >> dshift);
|
||||
*dpx |= (spx << dshift);
|
||||
}
|
||||
}
|
||||
|
||||
static void swizzleH(const u8* table, u32* dst, const u8* src, int bpp, int shift)
|
||||
{
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
int spx;
|
||||
if (bpp == 8)
|
||||
spx = src[i];
|
||||
else
|
||||
spx = (src[i >> 1] >> ((i & 1) * 4)) & 0xF;
|
||||
spx <<= shift;
|
||||
dst[table[i]] = spx;
|
||||
}
|
||||
}
|
||||
|
||||
static void expand16(u32* dst, const u16* src, const GIFRegTEXA& texa)
|
||||
{
|
||||
for (int i = 0; i < 128; i++)
|
||||
{
|
||||
int r = (src[i] << 3) & 0x0000F8;
|
||||
int g = (src[i] << 6) & 0x00F800;
|
||||
int b = (src[i] << 9) & 0xF80000;
|
||||
dst[i] = r | g | b;
|
||||
if (src[i] & 0x8000)
|
||||
{
|
||||
dst[i] |= texa.TA1 << 24;
|
||||
}
|
||||
else if (!texa.AEM || src[i])
|
||||
{
|
||||
dst[i] |= texa.TA0 << 24;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void expand8(u32* dst, const u8* src, const u32* palette)
|
||||
{
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
dst[i] = palette[src[i]];
|
||||
}
|
||||
}
|
||||
|
||||
static void expand4(u32* dst, const u8* src, const u32* palette)
|
||||
{
|
||||
for (int i = 0; i < 512; i++)
|
||||
{
|
||||
dst[i] = palette[(src[i >> 1] >> ((i & 1) * 4)) & 0xF];
|
||||
}
|
||||
}
|
||||
|
||||
static void expand4P(u8* dst, const u8* src)
|
||||
{
|
||||
for (int i = 0; i < 512; i++)
|
||||
{
|
||||
dst[i] = (src[i >> 1] >> ((i & 1) * 4)) & 0xF;
|
||||
}
|
||||
}
|
||||
|
||||
static void expandH(u32* dst, const u32* src, const u32* palette, int shift, int mask)
|
||||
{
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
dst[i] = palette[(src[i] >> shift) & mask];
|
||||
}
|
||||
}
|
||||
|
||||
static void expandHP(u8* dst, const u32* src, int shift, int mask)
|
||||
{
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
dst[i] = (src[i] >> shift) & mask;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string image2hex(const u8* bin, int rows, int columns, int bpp)
|
||||
{
|
||||
std::string out;
|
||||
const char* hex = "0123456789ABCDEF";
|
||||
|
||||
for (int y = 0; y < rows; y++)
|
||||
{
|
||||
if (y != 0)
|
||||
out.push_back('\n');
|
||||
for (int x = 0; x < columns; x++)
|
||||
{
|
||||
if (x != 0)
|
||||
out.push_back(' ');
|
||||
if (bpp == 4)
|
||||
{
|
||||
if (x & 1)
|
||||
{
|
||||
out.push_back(hex[*bin >> 4]);
|
||||
bin++;
|
||||
}
|
||||
else
|
||||
{
|
||||
out.push_back(hex[*bin & 0xF]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int z = 0; z < (bpp / 8); z++)
|
||||
{
|
||||
out.push_back(hex[*bin >> 4]);
|
||||
out.push_back(hex[*bin & 0xF]);
|
||||
bin++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
struct TestData
|
||||
{
|
||||
alignas(64) u8 block[256];
|
||||
alignas(64) u8 output[256 * (32 / 4)];
|
||||
alignas(64) u32 clut32[256];
|
||||
alignas(64) u64 clut64[256];
|
||||
|
||||
/// Get some input data with pixel values counting up from 0
|
||||
static TestData Linear()
|
||||
{
|
||||
TestData output;
|
||||
memset(output.output, 0, sizeof(output.output));
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
output.block[i] = i;
|
||||
output.clut32[i] = i | (i << 16);
|
||||
}
|
||||
GSClut::ExpandCLUT64_T32_I8(output.clut32, output.clut64);
|
||||
return output;
|
||||
}
|
||||
|
||||
/// Get some input data with random-ish (but consistent across runs) pixel values
|
||||
static TestData Random()
|
||||
{
|
||||
srand(0);
|
||||
TestData output;
|
||||
memset(output.output, 0, sizeof(output.output));
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
output.block[i] = rand();
|
||||
output.clut32[i] = rand();
|
||||
}
|
||||
GSClut::ExpandCLUT64_T32_I8(output.clut32, output.clut64);
|
||||
return output;
|
||||
}
|
||||
|
||||
/// Move data from output back to block to run an expand
|
||||
TestData prepareExpand()
|
||||
{
|
||||
TestData output = *this;
|
||||
memcpy(output.block, output.output, sizeof(output.block));
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
static TestData swizzle(const u8* table, TestData data, int bpp, bool deswizzle)
|
||||
{
|
||||
swizzle(table, data.output, data.block, bpp, deswizzle);
|
||||
return data;
|
||||
}
|
||||
|
||||
static TestData swizzle4(const u16* table, TestData data, bool deswizzle)
|
||||
{
|
||||
swizzle4(table, data.output, data.block, deswizzle);
|
||||
return data;
|
||||
}
|
||||
|
||||
static TestData swizzleH(const u8* table, TestData data, int bpp, int shift)
|
||||
{
|
||||
swizzleH(table, reinterpret_cast<u32*>(data.output), data.block, bpp, shift);
|
||||
return data;
|
||||
}
|
||||
|
||||
static TestData expand16(TestData data, const GIFRegTEXA& texa)
|
||||
{
|
||||
expand16(reinterpret_cast<u32*>(data.output), reinterpret_cast<const u16*>(data.block), texa);
|
||||
return data;
|
||||
}
|
||||
|
||||
static TestData expand8(TestData data)
|
||||
{
|
||||
expand8(reinterpret_cast<u32*>(data.output), data.block, data.clut32);
|
||||
return data;
|
||||
}
|
||||
|
||||
static TestData expand4(TestData data)
|
||||
{
|
||||
expand4(reinterpret_cast<u32*>(data.output), data.block, data.clut32);
|
||||
return data;
|
||||
}
|
||||
|
||||
static TestData expand4P(TestData data)
|
||||
{
|
||||
expand4P(data.output, data.block);
|
||||
return data;
|
||||
}
|
||||
|
||||
static TestData expandH(TestData data, int shift, int mask)
|
||||
{
|
||||
expandH(reinterpret_cast<u32*>(data.output), reinterpret_cast<const u32*>(data.block), data.clut32, shift, mask);
|
||||
return data;
|
||||
}
|
||||
|
||||
static TestData expandHP(TestData data, int shift, int mask)
|
||||
{
|
||||
expandHP(data.output, reinterpret_cast<u32*>(data.block), shift, mask);
|
||||
return data;
|
||||
}
|
||||
|
||||
static void runTest(void (*fn)(TestData))
|
||||
{
|
||||
fn(TestData::Linear());
|
||||
fn(TestData::Random());
|
||||
}
|
||||
|
||||
static void assertEqual(const TestData& expected, const TestData& actual, const char* name, int rows, int columns, int bpp)
|
||||
{
|
||||
std::string estr = image2hex(expected.output, rows, columns, bpp);
|
||||
std::string astr = image2hex(actual.output, rows, columns, bpp);
|
||||
EXPECT_STREQ(estr.c_str(), astr.c_str()) << "Unexpected " << name;
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(ReadTest, Read32)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzle(&columnTable32[0][0], data, 32, true);
|
||||
GSBlock::ReadBlock32(data.block, data.output, 32);
|
||||
assertEqual(expected, data, "Read32", 8, 8, 32);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(WriteTest, Write32)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzle(&columnTable32[0][0], data, 32, false);
|
||||
GSBlock::WriteBlock32<32, 0xFFFFFFFF>(data.output, data.block, 32);
|
||||
assertEqual(expected, data, "Write32", 8, 8, 32);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(ReadTest, Read16)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzle(&columnTable16[0][0], data, 16, true);
|
||||
GSBlock::ReadBlock16(data.block, data.output, 32);
|
||||
assertEqual(expected, data, "Read16", 8, 16, 16);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(ReadAndExpandTest, Read16)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
GIFRegTEXA texa = {0};
|
||||
texa.TA0 = 1;
|
||||
texa.TA1 = 2;
|
||||
TestData expected = swizzle(&columnTable16[0][0], data, 16, true);
|
||||
expected = expand16(expected.prepareExpand(), texa);
|
||||
GSBlock::ReadAndExpandBlock16<false>(data.block, data.output, 64, texa);
|
||||
assertEqual(expected, data, "ReadAndExpand16", 8, 16, 32);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(ReadAndExpandTest, Read16AEM)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
// Actually test AEM
|
||||
u8 idx = data.block[0] >> 1;
|
||||
data.block[idx * 2 + 0] = 0;
|
||||
data.block[idx * 2 + 1] = 0;
|
||||
GIFRegTEXA texa = {0};
|
||||
texa.TA0 = 1;
|
||||
texa.TA1 = 2;
|
||||
texa.AEM = 1;
|
||||
TestData expected = swizzle(&columnTable16[0][0], data, 16, true);
|
||||
expected = expand16(expected.prepareExpand(), texa);
|
||||
GSBlock::ReadAndExpandBlock16<true>(data.block, data.output, 64, texa);
|
||||
assertEqual(expected, data, "ReadAndExpand16AEM", 8, 16, 32);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(WriteTest, Write16)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzle(&columnTable16[0][0], data, 16, false);
|
||||
GSBlock::WriteBlock16<32>(data.output, data.block, 32);
|
||||
assertEqual(expected, data, "Read16", 8, 16, 16);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(ReadTest, Read8)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzle(&columnTable8[0][0], data, 8, true);
|
||||
GSBlock::ReadBlock8(data.block, data.output, 16);
|
||||
assertEqual(expected, data, "Read8", 16, 16, 8);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(ReadAndExpandTest, Read8)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzle(&columnTable8[0][0], data, 8, true);
|
||||
expected = expand8(expected.prepareExpand());
|
||||
GSBlock::ReadAndExpandBlock8_32(data.block, data.output, 64, data.clut32);
|
||||
assertEqual(expected, data, "ReadAndExpand8", 16, 16, 32);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(WriteTest, Write8)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzle(&columnTable8[0][0], data, 8, false);
|
||||
GSBlock::WriteBlock8<32>(data.output, data.block, 16);
|
||||
assertEqual(expected, data, "Write8", 16, 16, 8);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(ReadTest, Read8H)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzle(&columnTable32[0][0], data, 32, true);
|
||||
expected = expandHP(expected.prepareExpand(), 24, 0xFF);
|
||||
GSBlock::ReadBlock8HP(data.block, data.output, 8);
|
||||
assertEqual(expected, data, "Read8H", 8, 8, 8);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(ReadAndExpandTest, Read8H)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzle(&columnTable32[0][0], data, 32, true);
|
||||
expected = expandH(expected.prepareExpand(), 24, 0xFF);
|
||||
GSBlock::ReadAndExpandBlock8H_32(data.block, data.output, 32, data.clut32);
|
||||
assertEqual(expected, data, "ReadAndExpand8H", 8, 8, 32);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(WriteTest, Write8H)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzleH(&columnTable32[0][0], data, 8, 24);
|
||||
GSBlock::UnpackAndWriteBlock8H(data.block, 8, data.output);
|
||||
assertEqual(expected, data, "Write8H", 8, 8, 32);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(ReadTest, Read4)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzle4(&columnTable4[0][0], data, true);
|
||||
GSBlock::ReadBlock4(data.block, data.output, 16);
|
||||
assertEqual(expected, data, "Read4", 16, 32, 4);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(ReadTest, Read4P)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzle4(&columnTable4[0][0], data, true);
|
||||
expected = expand4P(expected.prepareExpand());
|
||||
GSBlock::ReadBlock4P(data.block, data.output, 32);
|
||||
assertEqual(expected, data, "Read4P", 16, 32, 8);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(ReadAndExpandTest, Read4)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzle4(&columnTable4[0][0], data, true);
|
||||
expected = expand4(expected.prepareExpand());
|
||||
GSBlock::ReadAndExpandBlock4_32(data.block, data.output, 128, data.clut32);
|
||||
assertEqual(expected, data, "ReadAndExpand4", 16, 32, 32);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(WriteTest, Write4)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzle4(&columnTable4[0][0], data, false);
|
||||
GSBlock::WriteBlock4<32>(data.output, data.block, 16);
|
||||
assertEqual(expected, data, "Write4", 16, 16, 4);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(ReadTest, Read4HH)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzle(&columnTable32[0][0], data, 32, true);
|
||||
expected = expandHP(expected.prepareExpand(), 28, 0xF);
|
||||
GSBlock::ReadBlock4HHP(data.block, data.output, 8);
|
||||
assertEqual(expected, data, "Read4HH", 8, 8, 8);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(ReadAndExpandTest, Read4HH)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzle(&columnTable32[0][0], data, 32, true);
|
||||
expected = expandH(expected.prepareExpand(), 28, 0xF);
|
||||
GSBlock::ReadAndExpandBlock4HH_32(data.block, data.output, 32, data.clut32);
|
||||
assertEqual(expected, data, "ReadAndExpand4HH", 8, 8, 32);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(WriteTest, Write4HH)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzleH(&columnTable32[0][0], data, 4, 28);
|
||||
GSBlock::UnpackAndWriteBlock4HH(data.block, 4, data.output);
|
||||
assertEqual(expected, data, "Write4HH", 8, 8, 32);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(ReadTest, Read4HL)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzle(&columnTable32[0][0], data, 32, true);
|
||||
expected = expandHP(expected.prepareExpand(), 24, 0xF);
|
||||
GSBlock::ReadBlock4HLP(data.block, data.output, 8);
|
||||
assertEqual(expected, data, "Read4HL", 8, 8, 8);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(ReadAndExpandTest, Read4HL)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzle(&columnTable32[0][0], data, 32, true);
|
||||
expected = expandH(expected.prepareExpand(), 24, 0xF);
|
||||
GSBlock::ReadAndExpandBlock4HL_32(data.block, data.output, 32, data.clut32);
|
||||
assertEqual(expected, data, "ReadAndExpand4HL", 8, 8, 32);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_TEST(WriteTest, Write4HL)
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED();
|
||||
|
||||
runTest([](TestData data)
|
||||
{
|
||||
TestData expected = swizzleH(&columnTable32[0][0], data, 4, 24);
|
||||
GSBlock::UnpackAndWriteBlock4HL(data.block, 4, data.output);
|
||||
assertEqual(expected, data, "Write4HL", 8, 8, 32);
|
||||
});
|
||||
}
|
||||
|
||||
MULTI_ISA_UNSHARED_END
|
||||
287
tests/ctest/core/StubHost.cpp
Normal file
287
tests/ctest/core/StubHost.cpp
Normal file
@@ -0,0 +1,287 @@
|
||||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "pcsx2/Achievements.h"
|
||||
#include "pcsx2/GS.h"
|
||||
#include "pcsx2/GameList.h"
|
||||
#include "pcsx2/Host.h"
|
||||
#include "pcsx2/ImGui/FullscreenUI.h"
|
||||
#include "pcsx2/ImGui/ImGuiAnimated.h"
|
||||
#include "pcsx2/ImGui/ImGuiFullscreen.h"
|
||||
#include "pcsx2/ImGui/ImGuiManager.h"
|
||||
#include "pcsx2/Input/InputManager.h"
|
||||
#include "pcsx2/VMManager.h"
|
||||
#include "common/ProgressCallback.h"
|
||||
|
||||
void Host::CommitBaseSettingChanges()
|
||||
{
|
||||
}
|
||||
|
||||
void Host::LoadSettings(SettingsInterface& si, std::unique_lock<std::mutex>& lock)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::CheckForSettingsChanges(const Pcsx2Config& old_config)
|
||||
{
|
||||
}
|
||||
|
||||
bool Host::RequestResetSettings(bool folders, bool core, bool controllers, bool hotkeys, bool ui)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Host::SetDefaultUISettings(SettingsInterface& si)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<ProgressCallback> Host::CreateHostProgressCallback()
|
||||
{
|
||||
return ProgressCallback::CreateNullProgressCallback();
|
||||
}
|
||||
|
||||
void Host::ReportInfoAsync(const std::string_view title, const std::string_view message)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::ReportErrorAsync(const std::string_view title, const std::string_view message)
|
||||
{
|
||||
}
|
||||
|
||||
bool Host::ConfirmMessage(const std::string_view title, const std::string_view message)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void Host::OpenURL(const std::string_view url)
|
||||
{
|
||||
}
|
||||
|
||||
bool Host::InBatchMode()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Host::InNoGUIMode()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Host::CopyTextToClipboard(const std::string_view text)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Host::BeginTextInput()
|
||||
{
|
||||
}
|
||||
|
||||
void Host::EndTextInput()
|
||||
{
|
||||
}
|
||||
|
||||
std::optional<WindowInfo> Host::GetTopLevelWindowInfo()
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void Host::OnInputDeviceConnected(const std::string_view identifier, const std::string_view device_name)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnInputDeviceDisconnected(const InputBindingKey key, const std::string_view identifier)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::SetMouseMode(bool relative_mode, bool hide_cursor)
|
||||
{
|
||||
}
|
||||
|
||||
std::optional<WindowInfo> Host::AcquireRenderWindow(bool recreate_window)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void Host::ReleaseRenderWindow()
|
||||
{
|
||||
}
|
||||
|
||||
void Host::BeginPresentFrame()
|
||||
{
|
||||
}
|
||||
|
||||
void Host::RequestResizeHostDisplay(s32 width, s32 height)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnVMStarting()
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnVMStarted()
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnVMDestroyed()
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnVMPaused()
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnVMResumed()
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnGameChanged(const std::string& title, const std::string& elf_override, const std::string& disc_path,
|
||||
const std::string& disc_serial, u32 disc_crc, u32 current_crc)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnPerformanceMetricsUpdated()
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnSaveStateLoading(const std::string_view filename)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnSaveStateLoaded(const std::string_view filename, bool was_successful)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnSaveStateSaved(const std::string_view filename)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::RunOnCPUThread(std::function<void()> function, bool block /* = false */)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::RefreshGameListAsync(bool invalidate_cache)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::CancelGameListRefresh()
|
||||
{
|
||||
}
|
||||
|
||||
bool Host::IsFullscreen()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Host::SetFullscreen(bool enabled)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnCaptureStarted(const std::string& filename)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnCaptureStopped()
|
||||
{
|
||||
}
|
||||
|
||||
void Host::RequestExitApplication(bool allow_confirm)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::RequestExitBigPicture()
|
||||
{
|
||||
}
|
||||
|
||||
void Host::RequestVMShutdown(bool allow_confirm, bool allow_save_state, bool default_save_state)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::PumpMessagesOnCPUThread()
|
||||
{
|
||||
}
|
||||
|
||||
s32 Host::Internal::GetTranslatedStringImpl(
|
||||
const std::string_view context, const std::string_view msg, char* tbuf, size_t tbuf_space)
|
||||
{
|
||||
if (msg.size() > tbuf_space)
|
||||
return -1;
|
||||
else if (msg.empty())
|
||||
return 0;
|
||||
|
||||
std::memcpy(tbuf, msg.data(), msg.size());
|
||||
return static_cast<s32>(msg.size());
|
||||
}
|
||||
|
||||
std::string Host::TranslatePluralToString(const char* context, const char* msg, const char* disambiguation, int count)
|
||||
{
|
||||
TinyString count_str = TinyString::from_format("{}", count);
|
||||
|
||||
std::string ret(msg);
|
||||
for (;;)
|
||||
{
|
||||
std::string::size_type pos = ret.find("%n");
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
|
||||
ret.replace(pos, pos + 2, count_str.view());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Host::OnAchievementsLoginRequested(Achievements::LoginRequestReason reason)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnAchievementsLoginSuccess(const char* username, u32 points, u32 sc_points, u32 unread_messages)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnAchievementsRefreshed()
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnAchievementsHardcoreModeChanged(bool enabled)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnCoverDownloaderOpenRequested()
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnCreateMemoryCardOpenRequested()
|
||||
{
|
||||
}
|
||||
|
||||
bool Host::LocaleCircleConfirm()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Host::ShouldPreferHostFileSelector()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Host::OpenHostFileSelectorAsync(std::string_view title, bool select_directory, FileSelectorCallback callback,
|
||||
FileSelectorFilters filters, std::string_view initial_directory)
|
||||
{
|
||||
callback(std::string());
|
||||
}
|
||||
|
||||
std::optional<u32> InputManager::ConvertHostKeyboardStringToCode(const std::string_view str)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> InputManager::ConvertHostKeyboardCodeToString(u32 code)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const char* InputManager::ConvertHostKeyboardCodeToIcon(u32 code)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BEGIN_HOTKEY_LIST(g_host_hotkeys)
|
||||
END_HOTKEY_LIST()
|
||||
Reference in New Issue
Block a user