1 # vim: set et sw=4 sts=4 encoding=utf-8 foldmethod=marker:
3 from sercom import model as mod
5 import cStringIO as sio
8 from os import path as osp
12 log = logging.getLogger('sercom.tester')
14 #from Queue import Queue
17 class Error(StandardError): pass
19 class ExecutionFailure(Error, RuntimeError): pass
21 class RsyncError(Error, EnvironmentError): pass
23 error_interno = u'\n**Error interno al preparar la entrega.**'
25 def unzip(bytes, dst): # {{{
26 log.debug(_(u'Intentando descomprimir en %s') % dst)
29 zfile = zf.ZipFile(sio.StringIO(bytes), 'r')
30 for f in zfile.namelist():
31 if f.endswith(os.sep):
32 log.debug(_(u'Creando directorio %s') % f)
33 os.mkdir(osp.join(dst, f))
35 log.debug(_(u'Descomprimiendo archivo %s') % f)
36 file(osp.join(dst, f), 'w').write(zfile.read(f))
39 class Tester(object): #{{{
41 def __init__(self, name, path, home, queue): #{{{ y properties
49 return osp.join(self.chroot, self.home, 'build')
53 return osp.join(self.chroot, self.home, 'test')
57 return osp.join(self.path, 'chroot_' + self.name)
61 def orig_chroot(self):
62 return osp.join(self.path, 'chroot')
65 entrega_id = self.queue.get() # blocking
66 while entrega_id is not None:
67 entrega = mod.Entrega.get(entrega_id)
68 log.debug(_(u'Nueva entrega para probar en tester %s: %s')
69 % (self.name, entrega))
71 log.debug(_(u'Fin de pruebas de: %s') % entrega)
72 entrega_id = self.queue.get() # blocking
75 def test(self, entrega): #{{{
76 log.debug(_(u'Tester.test(entrega=%s)') % entrega)
77 entrega.inicio_tareas = dt.datetime.now()
80 self.setup_chroot(entrega)
81 self.ejecutar_tareas_fuente(entrega)
82 self.ejecutar_tareas_prueba(entrega)
83 self.clean_chroot(entrega)
84 except ExecutionFailure, e:
85 entrega.correcta = False
86 log.debug(_(u'Entrega incorrecta: %s') % entrega)
88 entrega.observaciones += error_interno
89 log.exception(_(u'Hubo una excepción inesperada: %s') % e)
91 entrega.observaciones += error_interno
92 log.exception(_(u'Hubo una excepción inesperada desconocida'))
94 entrega.correcta = True
95 log.debug(_(u'Entrega correcta: %s') % entrega)
97 entrega.fin_tareas = dt.datetime.now()
100 def setup_chroot(self, entrega): #{{{ y clean_chroot()
101 log.debug(_(u'Tester.setup_chroot(entrega=%s)') % entrega)
102 rsync = 'rsync --stats --itemize-changes --human-readable --archive ' \
103 '--acls --delete-during --force' # TODO config
104 orig_chroot = osp.join(self.orig_chroot, '')
105 cmd = '%s %s %s' % (rsync, orig_chroot, self.chroot)
106 log.debug(_(u'Ejecutando: %s') % cmd)
109 entrega.observaciones += error_interno
110 errstr = _(u'No se pudo hacer rsync al chroot para la prueba,' \
111 u'falló el comando: %s (con código de error %d)') % (cmd, ret)
113 raise RsyncError(errstr)
115 unzip(entrega.archivos, self.build_path)
116 except zf.BadZipfile:
117 entrega.correcta = False
118 entrega.observaciones += error_interno
119 log.error(_(u'El archivo adjunto no está en formato ZIP'))
122 entrega.observaciones += error_interno
123 log.error(_(u'Error de IO al descromprimir archivos del ZIP: %s')
127 def clean_chroot(self, entrega):
128 log.debug(_(u'Tester.clean_chroot(entrega=%s)') % entrega)
129 pass # Se limpia con el próximo rsync
132 def ejecutar_tareas_fuente(self, entrega): #{{{ y tareas_prueba
133 log.debug(_(u'Tester.ejecutar_tareas_fuente(entrega=%s)') % entrega)
134 tareas = [t for t in entrega.instancia.ejercicio.enunciado.tareas
135 if isinstance(t, mod.TareaFuente)]
137 tarea.ejecutar(self.build_path, entrega)
139 def ejecutar_tareas_prueba(self, entrega):
140 log.debug(_(u'Tester.ejecutar_tareas_prueba(entrega=%s)') % entrega)
141 for caso in entrega.instancia.ejercicio.enunciado.casos_de_prueba:
142 caso.ejecutar(self.test_path, entrega)
147 def ejecutar_caso_de_prueba(self, path, entrega): #{{{
148 log.debug(_(u'CasoDePrueba.ejecutar(path=%s, entrega=%s)')
150 tareas = [t for t in entrega.instancia.ejercicio.enunciado.tareas
151 if isinstance(t, mod.TareaPrueba)]
152 prueba = entrega.add_prueba(self)
156 tarea.ejecutar(path, prueba)
157 except ExecutionFailure, e:
158 prueba.pasada = False
159 if self.rechazar_si_falla:
160 entrega.exito = False
161 if self.terminar_si_falla:
162 raise ExecutionError(e.comando, e.tarea, prueba)
166 prueba.fin = dt.datetime.now()
167 mod.CasoDePrueba.ejecutar = ejecutar_caso_de_prueba
170 def ejecutar_tarea_fuente(self, path, entrega): #{{{
171 log.debug(_(u'TareaFuente.ejecutar(path=%s, entrega=%s)') % (path, entrega))
173 for cmd in self.comandos:
174 cmd.ejecutar(path, entrega)
175 except ExecutionFailure, e:
176 if self.rechazar_si_falla:
177 entrega.exito = False
178 if self.terminar_si_falla:
179 raise ExecutionError(e.comando, tarea)
180 mod.TareaFuente.ejecutar = ejecutar_tarea_fuente
183 def ejecutar_tarea_prueba(self, path, prueba): #{{{
184 log.debug(_(u'TareaPrueba.ejecutar(path=%s, prueba=%s)') % (path, prueba))
186 for cmd in self.comandos:
187 cmd.ejecutar(path, prueba)
188 except ExecutionFailure, e:
189 if self.rechazar_si_falla:
191 if self.terminar_si_falla:
192 raise ExecutionError(e.comando, tarea)
193 mod.TareaPrueba.ejecutar = ejecutar_tarea_prueba
196 def ejecutar_comando_fuente(self, path, entrega): #{{{
197 log.debug(_(u'ComandoFuente.ejecutar(path=%s, entrega=%s)')
199 unzip(self.archivos_entrada, path) # TODO try/except
200 comando_ejecutado = entrega.add_comando_ejecutado(self)
201 # TODO ejecutar en chroot (path)
202 comando_ejecutado.fin = dt.datetime.now()
203 # if no_anda_ejecucion: # TODO
204 # comando_ejecutado.exito = False
205 # comando_ejecutado.observaciones += 'No anduvo xxx' # TODO mas info
206 # if self.rechazar_si_falla:
207 # entrega.exito = False
208 # if self.terminar_si_falla: # TODO
209 # raise ExecutionFailure(self)
210 # XXX ESTO EN REALIDAD EN COMANDOS FUENTE NO IRIA
211 # XXX SOLO HABRÍA QUE CAPTURAR stdout/stderr
212 # XXX PODRIA TENER ARCHIVOS DE SALIDA PERO SOLO PARA MOSTRAR COMO RESULTADO
213 # for archivo in self.archivos_salida:
214 # pass # TODO hacer diff
215 # if archivos_mal: # TODO
216 # comando_ejecutado.exito = False
217 # comando_ejecutado.observaciones += 'No anduvo xxx' # TODO mas info
218 # if self.rechazar_si_falla:
219 # entrega.exito = False
220 # if self.terminar_si_falla: # TODO
221 # raise ExecutionFailure(self)
223 # comando_ejecutado.exito = True
224 # comando_ejecutado.observaciones += 'xxx OK' # TODO
225 comando_ejecutado.exito = True
226 comando_ejecutado.observaciones += 'xxx OK' # TODO
227 mod.ComandoFuente.ejecutar = ejecutar_comando_fuente
230 def ejecutar_comando_prueba(self, path, prueba): #{{{
231 log.debug(_(u'ComandoPrueba.ejecutar(path=%s, prueba=%s)')
235 unzip(prueba.caso_de_prueba.archivos_entrada, path) # TODO try/except
236 unzip(self.archivos_entrada, path) # TODO try/except
237 comando_ejecutado = prueba.add_comando_ejecutado(self)
238 # TODO ejecutar en chroot (path)
239 comando_ejecutado.fin = dt.datetime.now()
240 # if no_anda_ejecucion: # TODO
241 # comando_ejecutado.exito = False
242 # comando_ejecutado.observaciones += 'No anduvo xxx' # TODO
243 # if self.rechazar_si_falla:
244 # entrega.exito = False
245 # if self.terminar_si_falla: # TODO
246 # raise ExecutionFailure(self) # TODO info de error
247 # for archivo in self.archivos_salida:
248 # pass # TODO hacer diff
249 # if archivos_mal: # TODO
250 # comando_ejecutado.exito = False
251 # comando_ejecutado.observaciones += 'No anduvo xxx' # TODO
252 # if self.rechazar_si_falla:
253 # entrega.exito = False
254 # if self.terminar_si_falla: # TODO
255 # raise ExecutionFailure(comando=self) # TODO info de error
257 # comando_ejecutado.exito = True
258 # comando_ejecutado.observaciones += 'xxx OK' # TODO
259 comando_ejecutado.exito = True
260 comando_ejecutado.observaciones += 'xxx OK' # TODO
261 mod.ComandoPrueba.ejecutar = ejecutar_comando_prueba