mirror of
https://github.com/johndoe6345789/SDL3CPlusPlus.git
synced 2026-04-24 13:44:58 +00:00
feat(tests): Add bgfx_gui_service_tests and integrate with CMake and dev commands
This commit is contained in:
@@ -327,6 +327,26 @@ target_link_libraries(script_engine_tests PRIVATE
|
||||
Vorbis::vorbis
|
||||
)
|
||||
add_test(NAME script_engine_tests COMMAND script_engine_tests)
|
||||
|
||||
add_executable(bgfx_gui_service_tests
|
||||
tests/test_bgfx_gui_service.cpp
|
||||
src/services/impl/bgfx_gui_service.cpp
|
||||
)
|
||||
target_include_directories(bgfx_gui_service_tests PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src")
|
||||
target_link_libraries(bgfx_gui_service_tests PRIVATE
|
||||
${SDL_TARGET}
|
||||
${SDL3CPP_RENDER_STACK_LIBS}
|
||||
${SDL3CPP_FREETYPE_LIBS}
|
||||
lunasvg::lunasvg
|
||||
)
|
||||
if(TARGET shaderc::shaderc)
|
||||
target_link_libraries(bgfx_gui_service_tests PRIVATE shaderc::shaderc)
|
||||
elseif(TARGET shaderc::shaderc_combined)
|
||||
target_link_libraries(bgfx_gui_service_tests PRIVATE shaderc::shaderc_combined)
|
||||
else()
|
||||
message(FATAL_ERROR "shaderc CMake target not found")
|
||||
endif()
|
||||
add_test(NAME bgfx_gui_service_tests COMMAND bgfx_gui_service_tests)
|
||||
endif()
|
||||
|
||||
add_executable(gxm_backend_tests
|
||||
|
||||
@@ -315,6 +315,35 @@ def build(args: argparse.Namespace) -> None:
|
||||
run_argvs([cmd], args.dry_run, env_overrides=vita_env)
|
||||
|
||||
|
||||
def tests(args: argparse.Namespace) -> None:
|
||||
"""Build (optional) and run ctest for a given build directory."""
|
||||
build_dir = _as_build_dir(args.build_dir, DEFAULT_BUILD_DIR)
|
||||
vita_env = _resolve_vita_env_for_build(build_dir)
|
||||
argvs: list[list[str]] = []
|
||||
|
||||
if args.build_first:
|
||||
build_cmd: list[str] = ["cmake", "--build", build_dir]
|
||||
if args.config:
|
||||
build_cmd.extend(["--config", args.config])
|
||||
if args.target:
|
||||
build_cmd.extend(["--target", args.target])
|
||||
build_tool_args = _strip_leading_double_dash(args.build_tool_args)
|
||||
if build_tool_args:
|
||||
build_cmd.append("--")
|
||||
build_cmd.extend(build_tool_args)
|
||||
argvs.append(build_cmd)
|
||||
|
||||
ctest_cmd: list[str] = ["ctest", "--output-on-failure", "--test-dir", build_dir]
|
||||
if args.config:
|
||||
ctest_cmd.extend(["-C", args.config])
|
||||
ctest_args = _strip_leading_double_dash(args.ctest_args)
|
||||
if ctest_args:
|
||||
ctest_cmd.extend(ctest_args)
|
||||
argvs.append(ctest_cmd)
|
||||
|
||||
run_argvs(argvs, args.dry_run, env_overrides=vita_env)
|
||||
|
||||
|
||||
def _cmd_one_liner_vcvars_then(bat: str, arch: str, then_parts: Sequence[str]) -> list[str]:
|
||||
"""
|
||||
Construct a command to call a Visual Studio environment setup batch file and
|
||||
@@ -552,7 +581,13 @@ def gui(args: argparse.Namespace) -> None:
|
||||
layout.addRow("Build Type:", self.build_type_combo)
|
||||
|
||||
self.target_combo = QComboBox()
|
||||
self.target_combo.addItems(["sdl3_app", "all"])
|
||||
self.target_combo.addItems([
|
||||
"sdl3_app",
|
||||
"all",
|
||||
"script_engine_tests",
|
||||
"gxm_backend_tests",
|
||||
"bgfx_gui_service_tests",
|
||||
])
|
||||
layout.addRow("Target:", self.target_combo)
|
||||
|
||||
buttons = QDialogButtonBox(
|
||||
@@ -1139,6 +1174,10 @@ def gui(args: argparse.Namespace) -> None:
|
||||
build_action.triggered.connect(self.run_build)
|
||||
dev_menu.addAction(build_action)
|
||||
|
||||
tests_action = QAction("Run Tests", self)
|
||||
tests_action.triggered.connect(self.run_tests)
|
||||
dev_menu.addAction(tests_action)
|
||||
|
||||
sync_action = QAction("Sync Assets", self)
|
||||
sync_action.triggered.connect(self.sync_assets)
|
||||
dev_menu.addAction(sync_action)
|
||||
@@ -1702,6 +1741,25 @@ return {{
|
||||
vita_env = _resolve_vita_env_for_build(build_dir)
|
||||
self.run_command(cmd, env_overrides=vita_env)
|
||||
|
||||
def run_tests(self):
|
||||
"""Build (optional) and run tests"""
|
||||
if self.preset != "default":
|
||||
build_dir = f"build-{self.preset.split('-')[0]}" # e.g., build-vita
|
||||
else:
|
||||
build_dir = GENERATOR_DEFAULT_DIR.get(self.generator, DEFAULT_BUILD_DIR)
|
||||
cmd = [
|
||||
sys.executable, __file__, "tests",
|
||||
"--build-dir", build_dir,
|
||||
"--config", self.build_type,
|
||||
"--target", "all"
|
||||
]
|
||||
vita_env = None
|
||||
if self.preset in VITA_PRESETS:
|
||||
vita_env = _vita_env_overrides(f"preset {self.preset}")
|
||||
else:
|
||||
vita_env = _resolve_vita_env_for_build(build_dir)
|
||||
self.run_command(cmd, env_overrides=vita_env)
|
||||
|
||||
def sync_assets(self):
|
||||
"""Sync assets into the active build directory"""
|
||||
if self.preset != "default":
|
||||
@@ -1785,6 +1843,40 @@ def main() -> int:
|
||||
),
|
||||
)
|
||||
bld.set_defaults(func=build)
|
||||
tst = subparsers.add_parser("tests", help="build (optional) and run ctest")
|
||||
tst.add_argument(
|
||||
"--build-dir", default=DEFAULT_BUILD_DIR, help="which directory to test"
|
||||
)
|
||||
tst.add_argument(
|
||||
"--config", default="Release", help="configuration for multi-config generators"
|
||||
)
|
||||
tst.add_argument(
|
||||
"--target",
|
||||
default="all",
|
||||
help="target to build before tests (use --no-build to skip)",
|
||||
)
|
||||
tst.add_argument(
|
||||
"--no-build",
|
||||
action="store_true",
|
||||
help="skip build step and only run tests",
|
||||
)
|
||||
tst.add_argument(
|
||||
"--build-tool-args",
|
||||
nargs=argparse.REMAINDER,
|
||||
help=(
|
||||
"extra args forwarded to the underlying build tool after `--` "
|
||||
"(prefix with '--' before the tool args if needed)"
|
||||
),
|
||||
)
|
||||
tst.add_argument(
|
||||
"--ctest-args",
|
||||
nargs=argparse.REMAINDER,
|
||||
help=(
|
||||
"extra arguments forwarded to ctest "
|
||||
"(prefix with '--' before ctest flags if needed)"
|
||||
),
|
||||
)
|
||||
tst.set_defaults(func=tests, build_first=True)
|
||||
msvc = subparsers.add_parser(
|
||||
"msvc-quick", help="run a VS env setup + follow-on command (README one-liner style)"
|
||||
)
|
||||
@@ -1849,6 +1941,8 @@ def main() -> int:
|
||||
)
|
||||
guip.set_defaults(func=gui)
|
||||
args = parser.parse_args()
|
||||
if hasattr(args, "no_build") and args.no_build:
|
||||
args.build_first = False
|
||||
try:
|
||||
args.func(args)
|
||||
except subprocess.CalledProcessError as exc:
|
||||
|
||||
213
tests/test_bgfx_gui_service.cpp
Normal file
213
tests/test_bgfx_gui_service.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
#define private public
|
||||
#include "services/impl/bgfx_gui_service.hpp"
|
||||
#undef private
|
||||
|
||||
#include "services/interfaces/i_config_service.hpp"
|
||||
#include "services/interfaces/i_logger.hpp"
|
||||
|
||||
#include <bgfx/bgfx.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
class TestLogger : public sdl3cpp::services::ILogger {
|
||||
public:
|
||||
void SetLevel(sdl3cpp::services::LogLevel level) override {
|
||||
level_ = level;
|
||||
}
|
||||
|
||||
sdl3cpp::services::LogLevel GetLevel() const override {
|
||||
return level_;
|
||||
}
|
||||
|
||||
void SetOutputFile(const std::string& filename) override {
|
||||
(void)filename;
|
||||
}
|
||||
|
||||
void SetMaxLinesPerFile(size_t maxLines) override {
|
||||
(void)maxLines;
|
||||
}
|
||||
|
||||
void EnableConsoleOutput(bool enable) override {
|
||||
consoleEnabled_ = enable;
|
||||
}
|
||||
|
||||
void Log(sdl3cpp::services::LogLevel level, const std::string& message) override {
|
||||
if (level < level_) {
|
||||
return;
|
||||
}
|
||||
entries_.emplace_back(level, message);
|
||||
if (consoleEnabled_) {
|
||||
std::cout << message << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void Trace(const std::string& message) override {
|
||||
Log(sdl3cpp::services::LogLevel::TRACE, message);
|
||||
}
|
||||
|
||||
void Trace(const std::string& className,
|
||||
const std::string& methodName,
|
||||
const std::string& args = "",
|
||||
const std::string& message = "") override {
|
||||
std::string formattedMessage = className + "::" + methodName;
|
||||
if (!args.empty()) {
|
||||
formattedMessage += "(" + args + ")";
|
||||
}
|
||||
if (!message.empty()) {
|
||||
formattedMessage += ": " + message;
|
||||
}
|
||||
Log(sdl3cpp::services::LogLevel::TRACE, formattedMessage);
|
||||
}
|
||||
|
||||
void Debug(const std::string& message) override {
|
||||
Log(sdl3cpp::services::LogLevel::DEBUG, message);
|
||||
}
|
||||
|
||||
void Info(const std::string& message) override {
|
||||
Log(sdl3cpp::services::LogLevel::INFO, message);
|
||||
}
|
||||
|
||||
void Warn(const std::string& message) override {
|
||||
Log(sdl3cpp::services::LogLevel::WARN, message);
|
||||
}
|
||||
|
||||
void Error(const std::string& message) override {
|
||||
Log(sdl3cpp::services::LogLevel::ERROR, message);
|
||||
}
|
||||
|
||||
void TraceFunction(const std::string& funcName) override {
|
||||
Trace("Entering " + funcName);
|
||||
}
|
||||
|
||||
void TraceVariable(const std::string& name, const std::string& value) override {
|
||||
Trace(name + " = " + value);
|
||||
}
|
||||
|
||||
void TraceVariable(const std::string& name, int value) override {
|
||||
TraceVariable(name, std::to_string(value));
|
||||
}
|
||||
|
||||
void TraceVariable(const std::string& name, size_t value) override {
|
||||
TraceVariable(name, std::to_string(value));
|
||||
}
|
||||
|
||||
void TraceVariable(const std::string& name, bool value) override {
|
||||
TraceVariable(name, std::string(value ? "true" : "false"));
|
||||
}
|
||||
|
||||
void TraceVariable(const std::string& name, float value) override {
|
||||
TraceVariable(name, std::to_string(value));
|
||||
}
|
||||
|
||||
void TraceVariable(const std::string& name, double value) override {
|
||||
TraceVariable(name, std::to_string(value));
|
||||
}
|
||||
|
||||
bool HasErrorSubstring(const std::string& fragment) const {
|
||||
for (const auto& entry : entries_) {
|
||||
if (entry.first == sdl3cpp::services::LogLevel::ERROR &&
|
||||
entry.second.find(fragment) != std::string::npos) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
sdl3cpp::services::LogLevel level_ = sdl3cpp::services::LogLevel::TRACE;
|
||||
bool consoleEnabled_ = false;
|
||||
std::vector<std::pair<sdl3cpp::services::LogLevel, std::string>> entries_;
|
||||
};
|
||||
|
||||
class StubConfigService : public sdl3cpp::services::IConfigService {
|
||||
public:
|
||||
uint32_t GetWindowWidth() const override { return 1; }
|
||||
uint32_t GetWindowHeight() const override { return 1; }
|
||||
std::filesystem::path GetScriptPath() const override { return {}; }
|
||||
bool IsLuaDebugEnabled() const override { return false; }
|
||||
std::string GetWindowTitle() const override { return ""; }
|
||||
const sdl3cpp::services::InputBindings& GetInputBindings() const override { return inputBindings_; }
|
||||
const sdl3cpp::services::MouseGrabConfig& GetMouseGrabConfig() const override { return mouseGrabConfig_; }
|
||||
const sdl3cpp::services::BgfxConfig& GetBgfxConfig() const override { return bgfxConfig_; }
|
||||
const sdl3cpp::services::MaterialXConfig& GetMaterialXConfig() const override { return materialXConfig_; }
|
||||
const std::vector<sdl3cpp::services::MaterialXMaterialConfig>& GetMaterialXMaterialConfigs() const override {
|
||||
return materialXMaterials_;
|
||||
}
|
||||
const sdl3cpp::services::GuiFontConfig& GetGuiFontConfig() const override { return guiFontConfig_; }
|
||||
const std::string& GetConfigJson() const override { return configJson_; }
|
||||
|
||||
void DisableFreeType() {
|
||||
guiFontConfig_.useFreeType = false;
|
||||
}
|
||||
|
||||
private:
|
||||
sdl3cpp::services::InputBindings inputBindings_{};
|
||||
sdl3cpp::services::MouseGrabConfig mouseGrabConfig_{};
|
||||
sdl3cpp::services::BgfxConfig bgfxConfig_{};
|
||||
sdl3cpp::services::MaterialXConfig materialXConfig_{};
|
||||
std::vector<sdl3cpp::services::MaterialXMaterialConfig> materialXMaterials_{};
|
||||
sdl3cpp::services::GuiFontConfig guiFontConfig_{};
|
||||
std::string configJson_{};
|
||||
};
|
||||
|
||||
void Assert(bool condition, const std::string& message, int& failures) {
|
||||
if (!condition) {
|
||||
std::cerr << "test failure: " << message << '\n';
|
||||
++failures;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
int failures = 0;
|
||||
|
||||
bgfx::Init init{};
|
||||
init.type = bgfx::RendererType::Noop;
|
||||
init.resolution.width = 1;
|
||||
init.resolution.height = 1;
|
||||
init.resolution.reset = BGFX_RESET_NONE;
|
||||
|
||||
if (!bgfx::init(init)) {
|
||||
std::cerr << "test failure: bgfx init failed (Noop renderer)" << '\n';
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
auto logger = std::make_shared<TestLogger>();
|
||||
auto configService = std::make_shared<StubConfigService>();
|
||||
configService->DisableFreeType();
|
||||
|
||||
sdl3cpp::services::impl::BgfxGuiService service(configService, logger);
|
||||
service.InitializeResources();
|
||||
|
||||
Assert(bgfx::isValid(service.program_), "GUI shader program should link", failures);
|
||||
Assert(bgfx::isValid(service.whiteTexture_), "white texture should be created", failures);
|
||||
|
||||
if (!bgfx::isValid(service.program_) &&
|
||||
!logger->HasErrorSubstring("bgfx::createProgram failed to link shaders")) {
|
||||
Assert(false, "missing bgfx::createProgram link failure log", failures);
|
||||
}
|
||||
|
||||
service.Shutdown();
|
||||
} catch (const std::exception& ex) {
|
||||
std::cerr << "exception during bgfx gui service tests: " << ex.what() << '\n';
|
||||
bgfx::shutdown();
|
||||
return 1;
|
||||
}
|
||||
|
||||
bgfx::shutdown();
|
||||
|
||||
if (failures == 0) {
|
||||
std::cout << "bgfx_gui_service_tests: PASSED" << '\n';
|
||||
} else {
|
||||
std::cerr << "bgfx_gui_service_tests: FAILED (" << failures << " errors)" << '\n';
|
||||
}
|
||||
|
||||
return failures;
|
||||
}
|
||||
Reference in New Issue
Block a user