Files
Tahgibu/tests/test_recovery_points.c
T
2026-10-24 11:52:16 -03:00

123 lines
5.8 KiB
C

/* Puntos de recuperación nombrados (en memoria; la persistencia se prueba en test_io). */
#include "tgb_test.h"
static tgb_context *g_ctx;
static char g_root[200];
static tgb_document *new_doc(int w, int h) {
tgb_document *d = NULL;
CHECK_OK(tgb_document_create(g_ctx, w, h, 12, &d));
return d;
}
TEST(create_list_delete) {
tgb_document *d = new_doc(16, 16);
CHECK_EQ(tgb_recovery_point_count(d), 0);
CHECK_OK(paint_px(d, 0, 0, 1, 1, RED));
uint64_t rev = tgb_document_revision(d);
uint32_t a = 0, b = 0;
CHECK_OK(tgb_recovery_point_create(d, "Base", &a));
CHECK(tgb_document_revision(d) != rev); /* hay algo nuevo que guardar */
CHECK_OK(paint_px(d, 0, 0, 2, 2, BLUE));
CHECK_OK(tgb_recovery_point_create(d, "Después del azul", &b));
CHECK_EQ(tgb_recovery_point_count(d), 2);
tgb_recovery_point_info info;
CHECK_OK(tgb_recovery_point_info_at(d, 0, &info));
CHECK(info.id == a && !strcmp(info.name, "Base") && info.compatible && info.created_unix > 1700000000);
CHECK_STATUS(tgb_recovery_point_info_at(d, 5, &info), TGB_ERR_RANGE);
CHECK_STATUS(tgb_recovery_point_create(d, "Base", NULL), TGB_ERR_EXISTS);
CHECK_STATUS(tgb_recovery_point_create(d, "", NULL), TGB_ERR_INVALID_ARG);
CHECK_STATUS(tgb_recovery_point_create(d, " ", NULL), TGB_ERR_INVALID_ARG);
CHECK_STATUS(tgb_recovery_point_create(d, NULL, NULL), TGB_ERR_INVALID_ARG);
CHECK_STATUS(tgb_recovery_point_create(d, "\xff\xfe", NULL), TGB_ERR_INVALID_ARG);
CHECK_STATUS(tgb_recovery_point_create(d, "a\nb", NULL), TGB_ERR_INVALID_ARG);
char longname[300]; memset(longname, 'x', sizeof longname - 1); longname[sizeof longname - 1] = 0;
CHECK_STATUS(tgb_recovery_point_create(d, longname, NULL), TGB_ERR_LIMIT);
CHECK_OK(tgb_recovery_point_delete(d, a));
CHECK_EQ(tgb_recovery_point_count(d), 1);
CHECK_STATUS(tgb_recovery_point_delete(d, a), TGB_ERR_NOT_FOUND);
CHECK_STATUS(tgb_recovery_point_restore(d, 9999), TGB_ERR_NOT_FOUND);
CHECK_OK(tgb_edit_begin(d, tgb_frame_id(d, 0), tgb_layer_id(d, 0), "x"));
CHECK_STATUS(tgb_recovery_point_create(d, "Durante edición", NULL), TGB_ERR_STATE);
CHECK_OK(tgb_edit_cancel(d));
tgb_document_destroy(d);
}
TEST(restore_goes_back_and_keeps_safety_point) {
tgb_document *d = new_doc(16, 16);
CHECK_OK(paint_px(d, 0, 0, 1, 1, RED));
uint32_t base = 0;
CHECK_OK(tgb_recovery_point_create(d, "Base", &base));
CHECK_OK(paint_px(d, 0, 0, 2, 2, BLUE));
CHECK_OK(tgb_frame_add(d, 1, NULL));
CHECK_OK(tgb_layer_add(d, 1, "Otra", NULL));
CHECK_EQ(tgb_frame_count(d), 2);
CHECK_OK(tgb_recovery_point_restore(d, base));
CHECK_EQ(tgb_frame_count(d), 1);
CHECK_EQ(tgb_layer_count(d), 1);
CHECK(is_px(tgb_frame_cel(d, 0, 0), 1, 1, RED));
CHECK_EQ(px_at(d, 0, 0, 2, 2).a, 0);
CHECK(!tgb_history_can_undo(d) && !tgb_history_can_redo(d)); /* el historial ordinario se vacía */
CHECK_EQ(tgb_recovery_point_count(d), 2); /* Base + punto de seguridad */
tgb_recovery_point_info info;
CHECK_OK(tgb_recovery_point_info_at(d, 1, &info));
CHECK(strstr(info.name, "Antes de restaurar") != NULL && strstr(info.name, "Base") != NULL);
/* volver al estado anterior a la restauración usando el punto automático */
CHECK_OK(tgb_recovery_point_restore(d, info.id));
CHECK_EQ(tgb_frame_count(d), 2);
CHECK_EQ(tgb_layer_count(d), 2);
CHECK(is_px(tgb_frame_cel(d, 0, 0), 2, 2, BLUE));
/* tras restaurar se sigue pudiendo editar y deshacer con normalidad */
CHECK_OK(paint_px(d, 0, 0, 9, 9, GREEN));
CHECK_OK(tgb_history_undo(d));
CHECK_EQ(px_at(d, 0, 0, 9, 9).a, 0);
/* restaurar dos veces el mismo punto: nombres de seguridad únicos */
CHECK_OK(tgb_recovery_point_restore(d, base));
CHECK_OK(tgb_recovery_point_restore(d, base));
for (size_t i = 0; i < tgb_recovery_point_count(d); i++)
for (size_t j = i + 1; j < tgb_recovery_point_count(d); j++) {
tgb_recovery_point_info a, b;
tgb_recovery_point_info_at(d, i, &a); tgb_recovery_point_info_at(d, j, &b);
CHECK(strcmp(a.name, b.name) != 0);
}
CHECK(tgb_document_is_consistent(d));
tgb_document_destroy(d);
}
TEST(points_share_memory_copy_on_write) {
tgb_document *d = new_doc(64, 64);
CHECK_OK(paint_px(d, 0, 0, 1, 1, RED));
CHECK_OK(tgb_recovery_point_create(d, "P", NULL));
CHECK_EQ(tgb_recovery_points_extra_bytes(d), 0); /* comparte la celda: coste cero */
CHECK_EQ(tgb_canvas_refs(tgb_frame_cel(d, 0, 0)), 2);
CHECK_OK(paint_px(d, 0, 0, 2, 2, BLUE)); /* editar separa la celda */
CHECK_EQ(tgb_recovery_points_extra_bytes(d), 64 * 64 * 4); /* ahora el punto retiene la versión vieja */
CHECK_OK(tgb_recovery_point_create(d, "Q", NULL)); /* otro punto de la misma versión: no duplica */
CHECK_OK(tgb_recovery_point_create(d, "R", NULL));
CHECK_EQ(tgb_recovery_points_extra_bytes(d), 64 * 64 * 4);
tgb_document_destroy(d);
}
TEST(point_limit) {
tgb_document *d = new_doc(8, 8);
char name[32];
for (int i = 0; i < TGB_MAX_RECOVERY_POINTS; i++) {
snprintf(name, sizeof name, "P%d", i);
CHECK_OK(tgb_recovery_point_create(d, name, NULL));
}
CHECK_STATUS(tgb_recovery_point_create(d, "Uno más", NULL), TGB_ERR_LIMIT); /* nunca borra puntos del usuario */
CHECK_EQ(tgb_recovery_point_count(d), TGB_MAX_RECOVERY_POINTS);
tgb_document_destroy(d);
}
int main(void) {
g_ctx = tgb_test_context(g_root, sizeof g_root);
RUN(create_list_delete);
RUN(restore_goes_back_and_keeps_safety_point);
RUN(points_share_memory_copy_on_write);
RUN(point_limit);
tgb_context_destroy(g_ctx);
tgb_test_rmtree(g_root);
TEST_SUMMARY();
}