Kao nastavak prethodnog posta Tip of the day: testiranje zadataka sa HSIN-a, ovdje ću testiranje rješenja napisati u Pythonu (a ne u Bashu), i to s dodatnim funkcionalnostima kao što su automatsko preuzimanje testnih primjera, mjerenje vremena izvođenja i paralelizacija. Ipak preporučujem da najprije pročitate navedeni post radi razumijevanja načina pozivanja programa, preusmjeravanja inputa/outputa i diff-anja, što ćemo koristiti i ovdje. Osim samih skripti, ideja je i pokazati neke mogućnosti Pythona.
Želimo, za početak, zaobići ručno preuzimanje zip datoteke i ručno extractanje odgovarajućih testnih primjera iz nje. Nema problema! Evo skripte download.py, koja kao argumente prima ime zadatka i URL zip datoteke s njegovim testnim primjerima:
#!/usr/bin/python3 from io import BytesIO from zipfile import ZipFile from urllib.request import urlopen from sys import argv ime_zadatka = argv[1] url = argv[2] resp = urlopen(url) zipfile = ZipFile(BytesIO(resp.read())) for filename in zipfile.namelist(): if filename.startswith(ime_zadatka): zipfile.extract(filename, '.')
Primjer preuzimanja testnih primjera za zadatak Modulo s 1. kola HONI-ja 2006.:
./download.py modulo https://hsin.hr/honi/arhiva/2006_2007/kolo1_testpodaci.zip
Ishod će biti stvaranje datoteka modulo/modulo.in.1, modulo/modulo.out.1 i tako dalje, gledajući iz direktorija u kojem je skripta pokrenuta. Izmjenu skripte po vlastitim željama ostavljamo čitateljici za vježbu.
Ako se nalazimo unutar direktorija u kojemu je naše rješenje i testni primjeri, skripta koja testira sve primjere, usput mjereći vrijeme izvođenja za svaki primjer, izgledala bi primjerice ovako:
#!/usr/bin/python3 from os import listdir, popen from time import time from sys import argv ime_zadatka = argv[1] rjesenje = argv[2] for filename in listdir('.'): if '.in.' in filename: print('\nTestiram {}...'.format(filename)) start_time = time() popen('./{} < {} > tmp.out'.format(rjesenje, filename)).read() print('Vrijeme = {:.3f} sekundi'.format(time() - start_time)) p = popen('diff -w tmp.out {}'.format(filename.replace('.in.', '.out.'))) print(p.read() or '---------- OK ----------\n')
Dakle, za svaku *.in.* datoteku (ulazni primjer), unutar popen-a kao novi proces pokrećemo naše rješenje, preusmjeravamo taj primjer na ulaz, a naš izlaz u datoteku tmp.out koju potom uspoređujemo (diff) sa službenim *.out.* izlazom. Ako je diff prazan, ispisujemo OK. Za mjerenje izvođenja ključna je funkcija time.time() koja daje trenutačno vrijeme u sekundama. Poziv skripte za zadatak Herman izgledao bi npr. ovako:
./test.py herman herman.exe
Nedostatci? Primjeri se neće nužno izvesti prirodnim redoslijedom i možda je moguće usporedno provjeravati više primjera (na više jezgri procesora) radi bržeg testiranja. Obje stvari možemo riješiti tako da napravimo četiri procesa (multiprocessing.Pool(4)) od kojih svaki paralelno testira svoj dio testnih primjera i vraća ishod kao string (npr. “Primjer 1 – OK, 0.1 sekundi“), koje onda na kraju možemo sortirati abecedno pa će primjer 1 biti prvi.
#!/usr/bin/python3 from os import listdir, popen from time import time from sys import argv from multiprocessing import Pool ime_zadatka = argv[1] rjesenje = argv[2] def testiraj(filename): output = '\nTestiram {}...\n'.format(filename) ekstenzija = filename.split('.')[-1] start_time = time() popen('./{} < {} > tmp{}.out'.format(rjesenje, filename, ekstenzija)).read() output += 'Vrijeme = {:.3f} sekundi\n'.format(time() - start_time) p = popen('diff -w tmp{}.out {}'.format(ekstenzija, filename.replace('.in.', '.out.'))) output += p.read() or '---------- OK ----------\n' return output with Pool(4) as p: ishodi = p.map(testiraj, [filename for filename in listdir('.') if '.in.' in filename]) for i in sorted(ishodi): print(i)
I ovdje se neke stvari mogu doraditi, može se dodati timeout tj. ograničiti vrijeme izvođenja, a moguće se i pobrinuti da herman.in.10 ne dođe prije herman.in.2 iako je prije po abecedi. Sve to i mnogo više ostavljamo… znate već.
Povratni ping: Tip of the day: testiranje | Blogaritam
Povratni ping: Kategorizacija blogaritamskih objava | Blogaritam