Tip of the day: testiranje na pythonovski

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 1OK, 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ć.

Jedna misao o “Tip of the day: testiranje na pythonovski

  1. Povratni ping: Tip of the day: testiranje | Blogaritam

Komentiraj

Popunite niže tražene podatke ili kliknite na neku od ikona za prijavu:

WordPress.com Logo

Ovaj komentar pišete koristeći vaš WordPress.com račun. Odjava /  Izmijeni )

Google photo

Ovaj komentar pišete koristeći vaš Google račun. Odjava /  Izmijeni )

Twitter picture

Ovaj komentar pišete koristeći vaš Twitter račun. Odjava /  Izmijeni )

Facebook slika

Ovaj komentar pišete koristeći vaš Facebook račun. Odjava /  Izmijeni )

Spajanje na %s