add testing and plan milestones

This commit is contained in:
2026-01-16 19:50:59 +01:00
parent 25b0603913
commit 2e097e4e54
8 changed files with 2540 additions and 28 deletions

View File

@@ -84,35 +84,132 @@ bool VisualCapture::CaptureScreenshot(const std::string& path) {
return true;
}
// Helper struct for loaded PNG data
struct PNGImage {
int width = 0;
int height = 0;
std::vector<uint8_t> pixels; // RGBA format
bool valid = false;
};
// Load a PNG file into memory
static PNGImage LoadPNG(const std::string& path) {
PNGImage image;
FILE* fp = fopen(path.c_str(), "rb");
if (!fp) return image;
// Check PNG signature
uint8_t header[8];
if (fread(header, 1, 8, fp) != 8 || png_sig_cmp(header, 0, 8)) {
fclose(fp);
return image;
}
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (!png) {
fclose(fp);
return image;
}
png_infop info = png_create_info_struct(png);
if (!info) {
png_destroy_read_struct(&png, nullptr, nullptr);
fclose(fp);
return image;
}
if (setjmp(png_jmpbuf(png))) {
png_destroy_read_struct(&png, &info, nullptr);
fclose(fp);
return image;
}
png_init_io(png, fp);
png_set_sig_bytes(png, 8);
png_read_info(png, info);
image.width = png_get_image_width(png, info);
image.height = png_get_image_height(png, info);
int color_type = png_get_color_type(png, info);
int bit_depth = png_get_bit_depth(png, info);
// Convert to RGBA if necessary
if (bit_depth == 16)
png_set_strip_16(png);
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png);
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand_gray_1_2_4_to_8(png);
if (png_get_valid(png, info, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(png);
if (color_type == PNG_COLOR_TYPE_RGB ||
color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_PALETTE)
png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png);
png_read_update_info(png, info);
// Allocate memory and read image
image.pixels.resize(image.width * image.height * 4);
std::vector<png_bytep> row_pointers(image.height);
for (int y = 0; y < image.height; y++) {
row_pointers[y] = image.pixels.data() + y * image.width * 4;
}
png_read_image(png, row_pointers.data());
png_read_end(png, nullptr);
png_destroy_read_struct(&png, &info, nullptr);
fclose(fp);
image.valid = true;
return image;
}
// Compare two pixels with tolerance
static bool PixelsMatch(const uint8_t* p1, const uint8_t* p2, int tolerance = 2) {
for (int c = 0; c < 4; c++) {
if (std::abs(static_cast<int>(p1[c]) - static_cast<int>(p2[c])) > tolerance) {
return false;
}
}
return true;
}
float VisualCapture::CompareImages(const std::string& path1, const std::string& path2) {
// Load both images and compare pixel by pixel
// For simplicity, just check if files exist for now
// A full implementation would load PNGs and compute difference
FILE* fp1 = fopen(path1.c_str(), "rb");
FILE* fp2 = fopen(path2.c_str(), "rb");
if (!fp1 || !fp2) {
if (fp1) fclose(fp1);
if (fp2) fclose(fp2);
return 1.0f; // Can't compare, assume different
// Load both images
PNGImage img1 = LoadPNG(path1);
PNGImage img2 = LoadPNG(path2);
// Check if both loaded successfully
if (!img1.valid || !img2.valid) {
return 1.0f; // Can't compare, assume completely different
}
// Read and compare file sizes as quick check
fseek(fp1, 0, SEEK_END);
fseek(fp2, 0, SEEK_END);
long size1 = ftell(fp1);
long size2 = ftell(fp2);
fclose(fp1);
fclose(fp2);
// If sizes differ significantly, images are different
if (std::abs(size1 - size2) > 1000) {
return 0.5f; // Moderately different
// Check dimensions match
if (img1.width != img2.width || img1.height != img2.height) {
return 1.0f; // Different dimensions = completely different
}
return 0.0f; // Assume similar if sizes match
// Count differing pixels
int total_pixels = img1.width * img1.height;
int diff_pixels = 0;
for (int i = 0; i < total_pixels; i++) {
const uint8_t* p1 = img1.pixels.data() + i * 4;
const uint8_t* p2 = img2.pixels.data() + i * 4;
if (!PixelsMatch(p1, p2)) {
diff_pixels++;
}
}
// Return difference ratio (0.0 = identical, 1.0 = completely different)
return static_cast<float>(diff_pixels) / static_cast<float>(total_pixels);
}
} // namespace mosis::testing