// heaptest.c — pruebas del asignador de mymalloc.c.
// Termina con código 0 si todas pasan; assert aborta en la primera que falla.
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "mymalloc.h"

#define FULL (ARENA_SIZE - 16)  // un solo trozo libre: toda la arena

static void test_split(void) {
    mm_init(1);
    void *a = mm_malloc(100);           // se redondea a 112
    assert(a != NULL && mm_offset(a) == 16);
    assert(mm_free_chunks() == 1);
    assert(mm_free_largest() == FULL - 112 - 16);
    mm_free(a);
    assert(mm_free_chunks() == 1 && mm_free_largest() == FULL);
}

static void test_no_tiny_chunks(void) {
    mm_init(1);
    // Pedir casi todo: el resto (16 bytes) no alcanza para nodo + datos.
    void *a = mm_malloc(FULL - 16);
    assert(a != NULL);
    assert(mm_free_chunks() == 0);      // se entregó el trozo completo
    assert(mm_malloc(1) == NULL);
    mm_free(a);
    assert(mm_free_largest() == FULL);
}

// Libera tres bloques vecinos en el orden dado y verifica que la arena
// vuelve a ser un solo trozo.
static void test_coalesce_order(int i, int j, int k) {
    mm_init(1);
    void *p[3];
    p[0] = mm_malloc(96);
    p[1] = mm_malloc(64);
    p[2] = mm_malloc(32);
    for (int n = 0; n < 3; n++)
        memset(p[n], 0xab, 32);         // el usuario escribe en sus bloques
    mm_free(p[i]);
    mm_free(p[j]);
    mm_free(p[k]);
    assert(mm_free_chunks() == 1);
    assert(mm_free_largest() == FULL);
}

static void test_without_coalesce(void) {
    mm_init(0);
    void *a = mm_malloc(96), *b = mm_malloc(64), *c = mm_malloc(32);
    mm_free(b);
    mm_free(a);
    mm_free(c);
    assert(mm_free_chunks() == 4);      // fragmentado: 96, 64, 32 y el resto
    assert(mm_malloc(300) == NULL);     // hay 448 bytes libres, pero partidos
}

static void test_first_fit(void) {
    mm_init(1);
    void *a = mm_malloc(32), *b = mm_malloc(32), *c = mm_malloc(32);
    (void)b;
    mm_free(a);
    mm_free(c);                          // c se fusiona con el resto
    void *d = mm_malloc(16);
    assert(d == a);                      // first fit: el hueco más bajo
}

static void test_magic(void) {
    mm_init(1);
    char *a = mm_malloc(64);
    assert(mm_valid(a));
    assert(!mm_valid(a + 8));            // no apunta al inicio de un bloque
    mm_free(a);
    assert(!mm_valid(a));                // magic ya fue pisado por next
}

int main(void) {
    test_split();
    test_no_tiny_chunks();
    int orders[6][3] = {{0,1,2},{0,2,1},{1,0,2},{1,2,0},{2,0,1},{2,1,0}};
    for (int n = 0; n < 6; n++)
        test_coalesce_order(orders[n][0], orders[n][1], orders[n][2]);
    test_without_coalesce();
    test_first_fit();
    test_magic();
    printf("heaptest: todas las pruebas pasaron\n");
    return 0;
}
