Enhance testing framework and UI integration for YAZE

- Added a comprehensive testing framework with support for unit, integration, and UI tests, improving overall test coverage and reliability.
- Integrated ImGui Test Engine for UI testing, allowing for real-time feedback and visualization of test results.
- Updated CMake configuration to conditionally include testing components based on build options, enhancing flexibility for developers.
- Introduced a new command in the CLI for running asset loading tests on ROMs, providing a straightforward way to validate functionality.
- Enhanced error handling and resource management during testing, ensuring stability and clarity in test execution.
- Improved user interface with a dedicated test dashboard for monitoring test progress and results, enhancing developer experience.
This commit is contained in:
scawful
2025-09-25 13:26:56 -04:00
parent 77ceb0256b
commit 41adb1b70e
21 changed files with 1406 additions and 25 deletions

View File

@@ -18,7 +18,21 @@ Arena::Arena() {
}
Arena::~Arena() {
// Safely clear all resources with proper error checking
for (auto& [key, texture] : textures_) {
// Don't rely on unique_ptr deleter during shutdown - manually manage
if (texture && key) {
[[maybe_unused]] auto* released = texture.release(); // Release ownership to prevent double deletion
}
}
textures_.clear();
for (auto& [key, surface] : surfaces_) {
// Don't rely on unique_ptr deleter during shutdown - manually manage
if (surface && key) {
[[maybe_unused]] auto* released = surface.release(); // Release ownership to prevent double deletion
}
}
surfaces_.clear();
}
@@ -56,6 +70,16 @@ void Arena::FreeTexture(SDL_Texture* texture) {
}
}
void Arena::Shutdown() {
// Clear all resources safely - let the unique_ptr deleters handle the cleanup
// while SDL context is still available
// Just clear the containers - the unique_ptr destructors will handle SDL cleanup
// This avoids double-free issues from manual destruction
textures_.clear();
surfaces_.clear();
}
void Arena::UpdateTexture(SDL_Texture* texture, SDL_Surface* surface) {
if (!texture || !surface) {
SDL_Log("Invalid texture or surface passed to UpdateTexture");
@@ -104,6 +128,7 @@ SDL_Surface* Arena::AllocateSurface(int width, int height, int depth,
return surface;
}
void Arena::FreeSurface(SDL_Surface* surface) {
if (!surface) return;

View File

@@ -19,12 +19,20 @@ class Arena {
~Arena();
// Resource management
SDL_Texture* AllocateTexture(SDL_Renderer* renderer, int width, int height);
void FreeTexture(SDL_Texture* texture);
void UpdateTexture(SDL_Texture* texture, SDL_Surface* surface);
SDL_Surface* AllocateSurface(int width, int height, int depth, int format);
void FreeSurface(SDL_Surface* surface);
// Explicit cleanup method for controlled shutdown
void Shutdown();
// Resource tracking for debugging
size_t GetTextureCount() const { return textures_.size(); }
size_t GetSurfaceCount() const { return surfaces_.size(); }
std::array<gfx::Bitmap, 223>& gfx_sheets() { return gfx_sheets_; }
auto gfx_sheet(int i) { return gfx_sheets_[i]; }

View File

@@ -228,8 +228,9 @@ Bitmap::Bitmap(const Bitmap& other)
if (active_ && !data_.empty()) {
surface_ = Arena::Get().AllocateSurface(width_, height_, depth_,
GetSnesPixelFormat(BitmapFormat::kIndexed));
if (surface_) {
surface_->pixels = pixel_data_;
if (surface_ && surface_->pixels) {
memcpy(surface_->pixels, pixel_data_,
std::min(data_.size(), static_cast<size_t>(surface_->h * surface_->pitch)));
}
}
}
@@ -345,14 +346,24 @@ void Bitmap::Create(int width, int height, int depth, int format,
active_ = false;
return;
}
surface_->pixels = pixel_data_;
// Copy our data into the surface's pixel buffer instead of pointing to external data
if (surface_->pixels && data_.size() > 0) {
memcpy(surface_->pixels, pixel_data_,
std::min(data_.size(), static_cast<size_t>(surface_->h * surface_->pitch)));
}
active_ = true;
}
void Bitmap::Reformat(int format) {
surface_ = Arena::Get().AllocateSurface(width_, height_, depth_,
GetSnesPixelFormat(format));
surface_->pixels = pixel_data_;
// Copy our data into the surface's pixel buffer
if (surface_ && surface_->pixels && data_.size() > 0) {
memcpy(surface_->pixels, pixel_data_,
std::min(data_.size(), static_cast<size_t>(surface_->h * surface_->pitch)));
}
active_ = true;
SetPalette(palette_);
}
@@ -362,7 +373,13 @@ void Bitmap::UpdateTexture(SDL_Renderer *renderer) {
CreateTexture(renderer);
return;
}
memcpy(surface_->pixels, data_.data(), data_.size());
// Ensure surface pixels are synchronized with our data
if (surface_ && surface_->pixels && data_.size() > 0) {
memcpy(surface_->pixels, data_.data(),
std::min(data_.size(), static_cast<size_t>(surface_->h * surface_->pitch)));
}
Arena::Get().UpdateTexture(texture_, surface_);
}

View File

@@ -42,9 +42,9 @@ class SnesColor {
explicit SnesColor(const ImVec4 val) : rgb_(val) {
snes_color color;
color.red = val.x / kColorByteMax;
color.green = val.y / kColorByteMax;
color.blue = val.z / kColorByteMax;
color.red = static_cast<uint16_t>(val.x * kColorByteMax);
color.green = static_cast<uint16_t>(val.y * kColorByteMax);
color.blue = static_cast<uint16_t>(val.z * kColorByteMax);
snes_ = ConvertRgbToSnes(color);
}