X-Git-Url: https://git.llucax.com/software/sercom-old.git/blobdiff_plain/5174fa9bd71e4f671d9788707e106296638a3c63..219f7ebe037bf26449f4d211cdf7b486fb8b847c:/src/sc_test diff --git a/src/sc_test b/src/sc_test index 24d2369..34402ff 100755 --- a/src/sc_test +++ b/src/sc_test @@ -1,298 +1,214 @@ -#!/usr/bin/php - el código de error debe ser != 0 + # Si no el código de error esperado debe ser igual al obtenido + if caso_de_prueba.codigoRetorno != proc.returncode \ + or caso_de_prueba.codigoRetorno == 256 \ + and proc.returncode != 0: + log.debug('Código de retorno incorrecto (debía ser %d y se obtuvo %d)', + caso_de_prueba.codigoRetorno, proc.returncode) + prueba.pasada = False + prueba.observaciones = 'Código de retorno incorrecto (debía ' \ + 'ser %d y se obtuvo %d)' % (caso_de_prueba.codigoRetorno, + proc.returncode) + #TODO verificar salidas, hacer diff + log.debug('Fin de ejecución de caso de prueba (hijo: %d, ret: %d)', + proc.pid, proc.returncode) + prueba.pasada = True + log.debug('Prueba OK: %s', prueba) + return prueba + +# Conecto señales +signal.signal(signal.SIGCHLD, sigchld) +signal.signal(signal.SIGTERM, sigterm) +signal.signal(signal.SIGINT, sigterm) + +# Inicializo +conf, conn, log = sercom.init('test') +log.info('Iniciado') + +# Obtengo id de usuario con el cual ejecutar las pruebas +(uid, gid) = pwd.getpwnam(conf.get('general', 'user'))[2:4] + +# Cambio UID efectivo +os.seteuid(uid) + +# Algunas variables de configuración útiles +data_dir = conf.get('general', 'data_dir') +intervalo = float(conf.get('general', 'intervalo')) + +# Hasta que nos maten +continuar = True # Cambia con una señal +while continuar: + # Busco intento a probar + intento = Intento.getProximoAProbar(conn) + if not intento: + log.debug('No hay intento para probar') + time.sleep(intervalo) + continue + log.info('Nuevo intento a probar (%s)', intento) + # Obtengo paths + intento_dir = os.path.join(data_dir, intento.path('intentos')) + entrega_dir = os.path.join(data_dir, 'ejercicios', str(intento.entrega.ejercicioID)) + chroot_dir = os.path.join(intento_dir, 'chroot') + # Compila + if not compilar(intento, intento_dir, entrega_dir, log): + continue + # Prepara archivos + preparar(intento_dir, chroot_dir) + # Ejecución de casos de prueba + intento.inicioPruebas = datetime.datetime.now() + pruebas = [] + for caso_de_prueba in intento.entrega.ejercicio.casosDePrueba: + pruebas.append(probar(intento, caso_de_prueba, uid, gid, chroot_dir, conf, conn, log)) + intento.finPruebas = datetime.datetime.now() + #TODO make clean + #TODO Armar mail de respuesta al alumno + for prueba in pruebas: + #TODO Si es publica, veo si se hizo ok o no y voy creando mail + pass + time.sleep(intervalo) -require_once 'T/general.php'; - -define('R_ERR', 0); -define('R_OK', 1); - -$LOGLEVEL = DEBUG; - -$gconf = $CONF['general']; - -// Obtengo id de usuario con el cual ejecutar las pruebas. -if (!($usrinfo = @posix_getpwnam($gconf['user']))) { - logserr("No existe el usuario '{$gconf['user']}'"); - exit(10); // salgo con error -} -$UID = $usrinfo['uid']; -$GID = $usrinfo['gid']; -/*if (!@posix_seteuid($usrinfo['uid'])) { - logserr("No se puede cambiar el uid efectivo a '{$usrinfo['uid']}'"); - exit(11); // salgo con error -}*/ - -$intento = new T_Intento; - -// Sin cesar. -while (1) { - if (!($mail = $intento->proximo_a_probar())) { - logs('No hay intento para probar', DEBUG); - sleep($gconf['intervalo']); - continue; - } - logs('Nuevo intento a probar (' . $intento->to_line() . ')'); - - $intento_dir = "{$gconf['data_dir']}/" . $intento->path(); - $entrega_dir = "{$gconf['data_dir']}/" . $intento->base_path('entregas'); - - if (is_readable("$entrega_dir/Makefile")) { - $makefile = "$entrega_dir/Makefile"; -//XXX @copy("$entrega_dir/Makefile", "$intento_dir/Makefile") -// or logserr("Error al copiar Makefile (de '$entrega_dir/Makefile' a '$intento_dir/Makefile'"); - // Fallback - } elseif (is_readable("{$gconf['data_dir']}/Makefile")) { - $makefile = "{$gconf['data_dir']}/Makefile"; -//XXX if (!@copy("{$gconf['data_dir']}/Makefile", "$intento_dir/Makefile")) { - } else { - enviar_respuesta_error_log($mail, 'No hay un Makefile disponible', $intento); - continue; - } - - $currdir = getcwd(); - if (!@chdir($intento_dir)) { - enviar_respuesta_error_log($mail, 'Error al cambiar al directorio del tp', $intento); - continue; - } - logs("Cambio de directorio '$currdir' -> '$intento_dir'", DEBUG); - logs('Ejecutando el comando: make -f '.escapeshellarg($makefile), DEBUG); - if (exec_get_info('make -f '.escapeshellarg($makefile), $ret, $err, $out)) { - if ($ret) { - logs('Error al compilar'); - logs("Código de retorno: $ret, mensaje: $err)", DEBUG); - //XXX $intento->informar_compilacion(false); - enviar_respuesta(R_ERR, $mail, "ERROR AL COMPILAR!\n\n$err\n\nCódigo de retorno: $ret\n", $intento); - continue; - } else { - logs('Compilado OK'); - //XXX $intento->informar_compilacion(true); - // TODO mail acumulativo - // enviar_respuesta(R_OK, $mail, 'El intento fue compilado con éxito!', $intento); - } - } else { - //XXX $intento->informar_compilacion(false); - enviar_respuesta_error_log($mail, 'No se pudo ejecutar make', $intento); - continue; - } - if (!@mkdir('chroot')) { - enviar_respuesta_error_log($mail, 'Error al crear directorio para chroot', $intento); - continue; - } - if (!@rename('tp', 'chroot/tp')) { - enviar_respuesta_error_log($mail, 'Error al mover el tp al chroot', $intento); - continue; - } - if (!@copy("{$gconf['data_dir']}/redir", 'chroot/redir')) { - enviar_respuesta_error_log($mail, 'Error al copiar redireccionador al chroot', $intento); - continue; - } - if ($err = chmod_own_grp('chroot', 02770, $UID, $GID)) { - enviar_respuesta_error_log($mail, $err, $intento); - continue; - } - if ($err = chmod_own_grp('chroot/tp', 0550, $UID, $GID)) { - enviar_respuesta_error_log($mail, $err, $intento); - continue; - } - if ($err = chmod_own_grp('chroot/redir', 0550, 0, 0)) { - enviar_respuesta_error_log($mail, $err, $intento); - continue; - } - - // comienza la ejecución de casos de prueba - while ($prueba = $intento->pedir_caso_de_prueba()) { - logs('Prueba: ' . $prueba->to_line(), DEBUG); - // ejecuta con fork - $pid = pcntl_fork(); - if ($pid == -1) { - // Error al forkear - $intento->resultado_de_prueba($prueba, false, 'Error al forkear proceso'); - enviar_respuesta_error_log($mail, 'Error al forkear proceso', $intento); - continue 2; - } elseif ($pid) { - // Estamos en el padre, controlamos el tiempo. - logs("En el padre (hijo: $pid)", DEBUG); - // TODO controlar tiempo. - pcntl_waitpid($pid, $exitcode); - logs("Fin de ejecución de caso de prueba (hijo: $pid, ret: $exitcode)", DEBUG); - $stderr = false; // FIXME ver si salida de error es vacia. - if ($exitcode or $stderr) { - logs('Comando ejecutado ERROR', DEBUG); - if ($ret) { - $msg = "El programa salió con código de error $ret"; - $msgs[] = $msg; - logs($msg, DEBUG); - } - if ($stderr) { - $msg = "El programa imprimió mensajes en la salida de error: '$stderr'"; - $msgs[] = $msg; - logs($msg, DEBUG); - } - $msg = join("\n", $msgs); - $intento->resultado_de_prueba($prueba, false, $msg); - enviar_respuesta(R_ERR, $mail, $msg, $intento); - exit(0); // salgo ok del hijo - } else { // Sin errores en la ejecución. - logs('Comando ejecutado OK', DEBUG); - // TODO - $salidas = array(); - foreach ($prueba->salidas as $salida) { - // TODO hacer diffs. - if ($salida == 'stdout') { - $salidas[$salida] = $stdout; - } else { - $salidas[$salida] = file_get_contents($salida); - } - } - logs('Salidas: ' . var_export($salidas, true), DEBUG); - $intento->resultado_de_prueba($prueba, true, 'Salidas: ' . var_export($salidas, true)); - enviar_respuesta(R_ERR, $mail, 'Salidas: ' . var_export($salidas, true), $intento); - exit(0); // salgo ok del hijo - } - } else { - // Estamos en el hijo, corremos en chroot. - logs('En el hijo', DEBUG); - // Hago chroot. - if (!@chroot('chroot')) { - $intento->resultado_de_prueba($prueba, false, 'Error al hacer chroot'); - enviar_respuesta_error_log($mail, 'Error al hacer chroot', $intento); - exit(1); // salgo del hijo - } - logs('Chrooteado, cwd = '.getcwd(), DEBUG); - if (!@posix_setuid($usrinfo['uid'])) { - $intento->resultado_de_prueba($prueba, false, "Error al cambiar al uid '{$usrinfo['uid']}'"); - enviar_respuesta_error_log($mail, "Error al cambiar al uid '{$usrinfo['uid']}'", $intento); - exit(2); // salgo del hijo - } - // TODO poner stdout y stderr en archivos para hacer diff o hacer diffs en memoria - $stdout = null; - if (in_array('stdout', $prueba->salidas)) $stdout = ''; - logs('Se ejecutará: chroot chroot /tp ' . escapeshellarg($prueba->params), DEBUG); - if (!pcntl_exec('/redir', array('tp', 'stdin', 'stdout', 'stderr', '/tp', '0', $prueba->params))) { - logserr('No se pudo ejecutar el comando'); - $intento->resultado_de_prueba($prueba, false, 'No se pudo ejecutar el tp'); - enviar_respuesta_error_log($mail, 'No se pudo ejecutar el tp', $intento); - exit(4); // salgo del hijo - } - exit(100); // salgo del hijo (no deberia llegar nunca aca). - } - break; // FIXME - } - - // TODO make clean -} - - -/** - * Ejecuta un comando devolviendo el código de error y las salidas. - * - * @param cmd Comando a ejecutar. - * @param ret Código de retorno. - * @param err Salida de error (se obtiene sólo si es != null). - * @param out Salida estándar (se obtiene sólo si es != null). - * @param in Entrada a enviarle al comando (null si no se le envía nada). - * @return true si se ejecutó el comando, false si no. - */ -function exec_fds($cmd, &$ret, $in = false, $out = false, $in = false) { - $descriptors = array(); - if ($in) $descriptors[0] = get_fd_type($in, 'r'); - if ($out) $descriptors[1] = get_fd_type($out, 'w'); - if ($err) $descriptors[2] = get_fd_type($err, 'w'); -var_dump($descriptors); - $proc = proc_open($cmd, $descriptors, $pipes); - if (is_resource($proc)) { - $ret = proc_close($proc); - return true; - } - return false; -} - -/** - * Obtiene un tipo de descriptor para proc_open(). - * - * @param fd Tipo de descriptor: si es true, se usa un pipe, si es un array - * se usa el array y si es otra cosa se usa un archivo con este - * nombre. - * @param mode Modo de apertura, sólo se usa si es true o string. - * @return Array a pasar a proc_open(). - */ -function get_fd_type($fd, $mode = false) { - if ($fd === true) { - return array('pipe', $mode); - } elseif (is_array($fd)) { - return $fd; - } - return array('file', $fd, $mode); -} - -/** - * Ejecuta un comando devolviendo el código de error y las salidas. - * - * @param cmd Comando a ejecutar. - * @param ret Código de retorno. - * @param err Salida de error (se obtiene sólo si es != null). - * @param out Salida estándar (se obtiene sólo si es != null). - * @param in Entrada a enviarle al comando (null si no se le envía nada). - * @return true si se ejecutó el comando, false si no. - */ -function exec_get_info($cmd, &$ret, &$err, &$out, $in = null) { - $descriptors = array(array('pipe', 'r'), array('pipe', 'w'), array('pipe', 'w')); - $proc = proc_open($cmd, $descriptors, $pipes); - if (is_resource($proc)) { - if (is_null($in)) { - fputs($pipes[0], $in); - } - if (is_null($out)) { - $out = stream_get_contents($pipes[1]); - } - if (is_null($err)) { - $err = stream_get_contents($pipes[2]); - } - foreach (array(0,1,2) as $i) { - fclose($pipes[$i]); - } - $ret = proc_close($proc); - return true; - } - return false; -} - -function enviar_respuesta($tipo, $to, $mensaje = '', $intento = null) { - $mconf = $GLOBALS['CONF']['mail']; - $subject = '[' . $NAME . '] Prueba '; - if ($tipo == R_OK) $estado = 'OK'; - else $estado = 'ERROR'; - $subject .= $estado; - $body .= "Estado: $estado\n"; - if ($mensaje) $body .= "\n$mensaje\n"; - if ($intento) $body .= "\n" . $intento->__toString() . "\n"; - logs("Envío de mail '$subject' a '$to'\n$body\n", DEBUG); - $headers = << \ No newline at end of file