// tickettest: pruebas de corrección de settickets() y getpinfo().
//
// Imprime una línea "ok" o "FAIL" por cada verificación y, al final,
// "tickettest: OK" si todas pasaron. Se ejecuta desde la shell de xv6:
//
//   $ tickettest

#include "kernel/types.h"
#include "kernel/param.h"
#include "kernel/pstat.h"
#include "user/user.h"

static struct pstat ps;
static int failures = 0;

static void
check(int cond, const char *what)
{
  if (cond) {
    printf("tickettest: %s: ok\n", what);
  } else {
    printf("tickettest: %s: FAIL\n", what);
    failures++;
  }
}

// Índice de pid en ps, o -1 si no aparece como entrada en uso.
static int
slot_of(int pid)
{
  for (int i = 0; i < NPROC; i++)
    if (ps.inuse[i] && ps.pid[i] == pid)
      return i;
  return -1;
}

// Tickets actuales de pid según getpinfo(), o -1 si algo falla.
static int
tickets_of(int pid)
{
  if (getpinfo(&ps) != 0)
    return -1;
  int i = slot_of(pid);
  return i < 0 ? -1 : ps.tickets[i];
}

int
main(int argc, char *argv[])
{
  int me = getpid();

  // 1. getpinfo funciona y el proceso actual aparece en la tabla.
  check(getpinfo(&ps) == 0, "getpinfo devuelve 0");
  int i = slot_of(me);
  check(i >= 0, "el proceso actual aparece con inuse = 1");

  // 2. Valores iniciales: 1 ticket (heredado de sh) y al menos 1 tick.
  check(i >= 0 && ps.tickets[i] == 1, "tickets por defecto = 1");
  check(i >= 0 && ps.ticks[i] > 0, "ticks del proceso actual > 0");

  // 3. Al menos init, sh y este proceso están en uso; las entradas
  //    libres no deben tener inuse = 1 con pid 0.
  int used = 0, bogus = 0;
  for (int k = 0; k < NPROC; k++) {
    if (ps.inuse[k]) {
      used++;
      if (ps.pid[k] <= 0)
        bogus++;
    }
  }
  check(used >= 3, "al menos 3 procesos en uso");
  check(bogus == 0, "toda entrada en uso tiene pid > 0");

  // 4. Argumentos inválidos de settickets.
  check(settickets(0) == -1, "settickets(0) devuelve -1");
  check(settickets(-5) == -1, "settickets(-5) devuelve -1");
  check(tickets_of(me) == 1, "un settickets fallido no cambia los tickets");

  // 5. settickets válido.
  check(settickets(5) == 0, "settickets(5) devuelve 0");
  check(tickets_of(me) == 5, "getpinfo muestra 5 tickets");

  // 6. El hijo hereda los tickets del padre.
  int pid = fork();
  if (pid < 0) {
    check(0, "fork");
  } else if (pid == 0) {
    exit(tickets_of(getpid()) == 5 ? 0 : 1);
  } else {
    int status = -1;
    wait(&status);
    check(status == 0, "el hijo hereda 5 tickets");
  }

  // 7. Punteros inválidos para getpinfo.
  check(getpinfo(0) == -1, "getpinfo(0) devuelve -1");
  check(getpinfo((struct pstat *)(sbrk(0) + 8192)) == -1,
        "getpinfo(puntero fuera del proceso) devuelve -1");
  check(getpinfo((struct pstat *)0xffffffffffff0000ULL) == -1,
        "getpinfo(puntero fuera del espacio virtual) devuelve -1");

  if (failures == 0)
    printf("tickettest: OK\n");
  else
    printf("tickettest: %d verificaciones fallaron\n", failures);
  printf("tickettest: done\n");
  exit(failures == 0 ? 0 : 1);
}
