// Pruebas de grades.c. Se corren con make test-grades.
//
// Esta prueba usa las funciones de grades.c sin correr su main. Las tres líneas de
// abajo son una receta por ahora: en la semana 6 veremos cómo se separa bien un
// programa en varios archivos.
#define main grades_main
#include "grades.c"
#undef main

// Cada check escribe un aviso si falla y devuelve cuántas fallas hubo: 0 o 1.
int check_int(const char what[], int actual, int expected) {
    if (actual != expected) {
        printf("FALLA %s: se esperaba %d y salió %d\n", what, expected, actual);
        return 1;
    }
    return 0;
}

int check_double(const char what[], double actual, double expected) {
    double difference = actual - expected;
    if (difference < -0.001 || difference > 0.001) {
        printf("FALLA %s: se esperaba %.3f y salió %.3f\n", what, expected, actual);
        return 1;
    }
    return 0;
}

int test_remove_newline(void) {
    int failed = 0;
    char with_newline[] = "Ana\n";
    failed += check_int("remove_newline: la encuentra", remove_newline(with_newline), 1);
    failed += check_int("remove_newline: la quita", with_newline[3], '\0');
    char without_newline[] = "Ana";
    failed += check_int("remove_newline: no hay", remove_newline(without_newline), 0);
    return failed;
}

// Qué líneas son válidas: justo dos campos y cuatro notas entre 0 y 20.
int test_parse_student(void) {
    int failed = 0;
    struct student list[1];
    failed += check_int("parse: línea correcta", parse_student("A Ana 14 9 17 12", list, 0), 1);
    failed += check_int("parse: guarda la sección", list[0].section, 'A');
    failed += check_int("parse: guarda la última nota", list[0].grades[3], 12);
    failed += check_int("parse: falta una nota", parse_student("A Ana 14 9 17", list, 0), 0);
    failed += check_int("parse: sobra una nota", parse_student("A Ana 14 9 17 12 5", list, 0), 0);
    failed += check_int("parse: nota con letras", parse_student("A Ana 14 x 17 12", list, 0), 0);
    failed += check_int("parse: sección en minúscula", parse_student("a Ana 14 9 17 12", list, 0), 0);
    failed += check_int("parse: primera nota mayor que 20", parse_student("A Ana 21 9 17 12", list, 0), 0);
    failed += check_int("parse: última nota mayor que 20", parse_student("A Ana 14 9 17 21", list, 0), 0);
    failed += check_int("parse: última nota negativa", parse_student("A Ana 14 9 17 -1", list, 0), 0);
    failed += check_int("parse: 0 y 20 valen", parse_student("A Ana 0 20 0 20", list, 0), 1);
    return failed;
}

// casos.txt tiene 6 líneas y solo 2 son válidas: la primera y la última.
// Los avisos de líneas mal formadas que salen aquí son parte de la prueba.
int test_read_students(void) {
    int failed = 0;
    struct student list[MAX_STUDENTS];
    failed += check_int("read_students: cuántos guarda", read_students("casos.txt", list, MAX_STUDENTS), 2);
    failed += check_int("read_students: el primero", list[0].section, 'A');
    failed += check_int("read_students: el segundo", list[1].section, 'F');
    return failed;
}

int test_average(void) {
    int failed = 0;
    struct student ana = {'A', "Ana", {14, 9, 17, 12}};
    struct student eva = {'A', "Eva", {14, 9, 17, 13}};
    struct student zero = {'A', "Cero", {0, 0, 0, 0}};
    failed += check_int("total_points", total_points(ana), 52);
    failed += check_double("average: con decimales", average(eva), 13.25);
    failed += check_double("average: exacto", average(ana), 13.0);
    failed += check_double("average: todo 0", average(zero), 0.0);
    return failed;
}

// Se aprueba con promedio 11 o más: el borde cuenta.
int test_is_passed(void) {
    int failed = 0;
    struct student exact = {'A', "Justo", {11, 11, 11, 11}};
    struct student below = {'A', "Casi", {11, 11, 11, 10}};
    failed += check_int("is_passed: justo 11", is_passed(exact), 1);
    failed += check_int("is_passed: 10.75", is_passed(below), 0);
    return failed;
}

int test_list_functions(void) {
    int failed = 0;
    struct student list[] = {
        {'A', "Ana", {13, 13, 13, 14}},
        {'B', "Bruno", {14, 14, 14, 13}},
        {'A', "Carla", {5, 5, 5, 5}},
    };
    failed += check_int("count_passed", count_passed(list, 3), 2);
    failed += check_int("count_passed: nadie", count_passed(list, 0), 0);
    failed += check_int("best_index: Ana 13.25, Bruno 13.75", best_index(list, 3), 1);
    failed += check_int("best_index: sin estudiantes", best_index(list, 0), -1);
    failed += check_double("class_average", class_average(list, 3), (13.25 + 13.75 + 5.0) / 3);
    failed += check_double("class_average: sin estudiantes", class_average(list, 0), 0.0);
    return failed;
}

// Sesión 2: agreguen aquí las pruebas de average_without_lowest.

int main(void) {
    int failed = 0;
    failed += test_remove_newline();
    failed += test_parse_student();
    failed += test_read_students();
    failed += test_average();
    failed += test_is_passed();
    failed += test_list_functions();
    if (failed == 0) {
        printf("Todas las pruebas pasan\n");
        return 0;
    }
    printf("Comprobaciones que fallaron: %d\n", failed);
    return 1;
}
