// Pruebas de Stack, solo por su interfaz. Este archivo se compila una vez y se enlaza con
// cada implementación. No lo cambien: sus dos implementaciones deben pasarlo tal como está.
#include <assert.h>
#include <stdio.h>

#include "stack.h"

static void test_empty(void) {
    Stack *stack = stack_new();
    int value = 77;
    assert(stack != NULL);
    assert(stack_size(stack) == 0);
    assert(!stack_pop(stack, &value));
    assert(!stack_peek(stack, &value));
    assert(value == 77);
    stack_free(stack);
}

static void test_last_in_first_out(void) {
    Stack *stack = stack_new();
    int value = 0;
    assert(stack != NULL);
    assert(stack_push(stack, 1));
    assert(stack_push(stack, 2));
    assert(stack_push(stack, 3));
    assert(stack_size(stack) == 3);
    assert(stack_peek(stack, &value) && value == 3);
    assert(stack_size(stack) == 3);
    assert(stack_pop(stack, &value) && value == 3);
    assert(stack_pop(stack, &value) && value == 2);
    assert(stack_pop(stack, &value) && value == 1);
    assert(stack_size(stack) == 0);
    assert(!stack_pop(stack, &value));
    stack_free(stack);
}

// Muchos valores: suficientes para que cualquier representación tenga que crecer.
static void test_many_values(void) {
    Stack *stack = stack_new();
    int value = 0;
    assert(stack != NULL);
    for (int i = 0; i < 1000; i++) {
        assert(stack_push(stack, i));
    }
    assert(stack_size(stack) == 1000);
    for (int i = 999; i >= 0; i--) {
        assert(stack_pop(stack, &value) && value == i);
    }
    assert(stack_size(stack) == 0);
    stack_free(stack);
}

// Sube y baja varias veces, para pasar por los mismos límites en los dos sentidos.
static void test_up_and_down(void) {
    Stack *stack = stack_new();
    int value = 0;
    assert(stack != NULL);
    for (int round = 1; round <= 20; round++) {
        for (int i = 0; i < round; i++) {
            assert(stack_push(stack, round * 100 + i));
        }
        for (int i = round - 1; i >= 0; i--) {
            assert(stack_pop(stack, &value) && value == round * 100 + i);
        }
        assert(stack_size(stack) == 0);
    }
    stack_free(stack);
}

// Liberar una pila que todavía tiene valores no debe dejar fugas.
static void test_free_with_values(void) {
    Stack *stack = stack_new();
    assert(stack != NULL);
    for (int i = 0; i < 50; i++) {
        assert(stack_push(stack, i));
    }
    stack_free(stack);
    stack_free(NULL);
}

static void test_two_at_once(void) {
    Stack *first = stack_new();
    Stack *second = stack_new();
    int value = 0;
    assert(first != NULL && second != NULL);
    assert(stack_push(first, 1));
    assert(stack_push(second, 2));
    assert(stack_pop(first, &value) && value == 1);
    assert(stack_size(second) == 1);
    stack_free(first);
    stack_free(second);
}

int main(void) {
    test_empty();
    test_last_in_first_out();
    test_many_values();
    test_up_and_down();
    test_free_with_values();
    test_two_at_once();

    printf("Todas las pruebas pasaron\n");
    return 0;
}
