Dio 1 – Niti i trka podataka
Dio 2 – Zastoj i inverzija prioriteta
Dio 3 – Nestabilni testovi
Praksa – Niti, zastoji i nestabilni testovi
Materijale priredio Erkan Islamović
erkan.islamovic@eubd.edu.ba · erkan.islamovic@gmail.com
Petnaest sedmica: svaka ima tri časa teorije i tri časa praktične nastave. Danas: poglavlje 12 (Sedmica 12, označeno). Isti primjer kroz knjigu: DeliveryApp, narudžba N-1001.
| Sed. | Poglavlje | Teorija (3 časa) | Praksa (3 časa) |
|---|---|---|---|
| 1 | Zašto testiramo | Uvodne priče; greška, defekt, otkaz; sedam principa; DeliveryApp | Tabela očekivanih cijena rukom |
| 2 | Proces i nivoi | Test proces; nivoi i V-model; vrste; statičko testiranje | Recenzija zahtjeva DeliveryApp |
| 3 | Manuelno testiranje | Test slučaj; izvještaj o defektu; istraživačko testiranje | Test slučajevi za web stranicu |
| 4 | Automatizovano testiranje | Šta automatizovati; piramida testova; prvi test | Prvi pytest |
| 5 | Jedinično testiranje | AAA; fixture, parametri, izuzeci; TDD | Testovi za dostavu i popust |
| 6 | Bijela kutija | Graf toka; pokrivenost; ciklomatska složenost | coverage.py do 100 % grana |
| 7 | Tok podataka | Definicija i upotreba; anomalije; statička analiza, mutacije | ruff, mypy, ručna mutacija |
| 8 | Crna kutija | Ekvivalencija i granice; tabela odlučivanja; stanja | Lov na pet grešaka |
| Sed. | Poglavlje | Teorija (3 časa) | Praksa (3 časa) |
|---|---|---|---|
| 9 | Model i integracija | Model; strategije integracije; API | TestClient za API |
| 10 | Sistemsko testiranje | Sistemsko; performanse i sigurnost; prihvatno i BDD | Locust, Gherkin |
| 11 | OO testiranje | Stanje objekta; nasljeđivanje; dvojnici | Narudzba, unittest.mock |
| 12 | Paralelne aplikacije | Trka podataka; zastoj; nestabilni testovi | Niti, 1000 ponavljanja |
| 13 | Web aplikacije | Web i DOM; Playwright; Page Object, pristupačnost | Playwright za DeliveryApp |
| 14 | Upravljanje testiranjem | Test plan i rizik; metrike; CI/CD | GitHub Actions |
| 15 | Alati, AI, projekat | Pregled alata; AI u testiranju; ISTQB | Odbrana projekta |
Četvrtog jula 1997. NASA-in Mars Pathfinder spustio se na Mars, odskakujući po površini u velikim vazdušnim jastucima, i počeo slati prve slike i podatke. Nekoliko dana kasnije desilo se nešto čudno: računar letjelice se sam od sebe ponovo pokretao, i pri tome gubio dio prikupljenih podataka. Na Zemlji, u laboratoriji JPL, inženjeri su imali kopiju letjelice i pokretali isti softver satima, dok nisu uhvatili trenutak u kojem se greška javlja. Uzrok je bio problem poznat iz udžbenika, ali rijedak u praksi: inverzija prioriteta. Zadatak niskog prioriteta (prikupljanje meteoroloških podataka) zaključao je zajednički resurs. Zadatak visokog prioriteta (upravljanje unutrašnjom sabirnicom podataka) čekao je da se resurs oslobodi. A između njih se uglavio zadatak srednjeg prioriteta (komunikacija), koji je dugo radio i nije puštao niski zadatak da završi i oslobodi bravu. Visoki zadatak je čekao predugo, nadzorni mehanizam je zaključio da nešto ne valja i ponovo pokrenuo računar.
Rješenje je bilo već ugrađeno u operativni sistem, samo nije bilo uključeno: nasljeđivanje prioriteta. Inženjeri su promjenu poslali sa Zemlje, na Mars, i letjelica je radila dalje. Šest godina kasnije, 14. avgusta 2003., sličan, ali skuplji problem: u kontrolnom centru jedne elektroprivrede u Ohaju trka podataka u alarmnom softveru tiho je zaustavila alarme. Operateri više od sat vremena nisu vidjeli upozorenja da dalekovodi ispadaju jedan za drugim, a kvarovi su se proširili na veliki dio sjeveroistoka SAD-a i Ontarija. Bez struje je ostalo oko 50 miliona ljudi. Obje priče imaju isti zaključak: greške u programima koji rade više stvari istovremeno javljaju se rijetko, nepredvidivo i baš onda kad je najgore. Ovo poglavlje uči kako ih testirati.
U poglavlju 11 vidjeli smo da se stanje objekta mora slagati sa stvarnošću i da je Therac-25 pao pri brzom unosu: dva događaja koja su se desila „u isto vrijeme”. Sada se time bavimo direktno. Prvo učimo šta su niti i kako nastaje trka podataka, na brojaču narudžbi DeliveryApp. Zatim zastoj i inverziju prioriteta. Na kraju nestabilne testove: testove koji ponekad prolaze, a ponekad padaju, i tehnike kojima se paralelne greške ipak mogu uhvatiti.
Nakon poglavlja možete:
objasniti šta su niti i zašto dijeljeni podaci prave probleme; pokazati na primjeru kako nastaje trka podataka; spriječiti trku bravom i provjeriti ispravku testom; objasniti zašto običan test trku obično ne vidi; napisati test koji pokreće više niti i provjerava rezultat; prepoznati zastoj i napisati test koji ga otkriva bez da visi; objasniti inverziju prioriteta i nasljeđivanje prioriteta; nabrojati uzroke nestabilnih testova i načine kako se s njima postupa.
| Čas | Sadržaj | Na času |
|---|---|---|
| Teorija 1 | 12.1–12.3 Niti i dijeljeni podaci; trka podataka; brava | Dva kupca, isti broj narudžbe |
| Teorija 2 | 12.4–12.6 Zastoj; inverzija prioriteta; kako se testira paralelni kod | Kuhinja i dostava čekaju jedna drugu |
| Teorija 3 | 12.7–12.9 Nestabilni testovi; uzroci; šta raditi s njima | Test koji pada svaki deseti put |
Petak je, 19 sati. Stotine kupaca istovremeno naručuju na DeliveryApp. Server ne može čekati da prvi kupac završi pa tek onda primiti drugog: on istovremeno obrađuje mnogo zahtjeva, svaki u svojoj niti. Slika iz života: kuhinja restorana. Jedan kuhar je jedna nit. Više kuhara radi istovremeno, brže, ali dijele isti frižider, istu tablu s narudžbama i isti šporet. Dok kuhari ne diraju iste stvari, sve je u redu. Problem nastaje kad dvojica u istom trenutku posegnu za posljednjim jajetom.
Za testera, paralelni programi imaju tri neugodne osobine:
print da vidite šta se dešava, promijeni se brzina i greška nestane (zato se zovu „Heisenbug”, po fizičaru Heisenbergu i njegovom principu neodređenosti).Svaka narudžba dobija broj: N-1001, N-1002, … Brojač u primjeri/paralelno/brojac.py:
class Brojac:
"""Brojač BEZ brave: čitanje i upis nisu jedan nedjeljiv korak."""
def __init__(self, pocetak=1000):
self.zadnji = pocetak
def sljedeci(self):
procitano = self.zadnji # 1. pročitaj
time.sleep(0) # (ovdje sistem može prebaciti na drugu nit)
self.zadnji = procitano + 1 # 2. upiši
return f"N-{self.zadnji}"Metoda ima dva koraka: pročitaj zadnji broj, pa upiši sljedeći. U jednoj niti to je savršeno ispravno. Ali pogledajte šta se može desiti s dvije niti (slika 12.1):

Dva kupca dobiju isti broj narudžbe. Restoran pripremi jednu, dostavljač odnese pogrešnu, a jedna uplata nema svoju narudžbu. To je trka podataka: rezultat zavisi od toga koja nit „pobijedi” u trci.
Red time.sleep(0) ne čeka ništa, ali kaže sistemu „sad možeš prebaciti na drugu nit”. Ubacili smo ga da trka postane česta, da je vidimo na času. Funkcija podijeli_brojeve pokrene 8 niti i svaka traži 200 brojeva, ukupno 1600. Pet pokretanja:
dobijeno brojeva: 1600 1600 1600 1600 1600
različitih: 385 323 395 364 344Od 1600 narudžbi samo oko 350 ima jedinstven broj.
A bez reda time.sleep(0), s običnim self.zadnji += 1? Izmjerili smo 200 pokretanja: nijedan dupli broj. Znači li to da je kod ispravan? Ne. Operacija += nad atributom nije atomska: Python je izvršava u nekoliko koraka, i sistem može prebaciti nit između njih. U našem mjerenju se to nije desilo, ali na drugom računaru, drugoj verziji Pythona, pod većim opterećenjem, ili jednom u milion narudžbi, desiće se. Ovo je najvažnija pouka poglavlja: paralelna greška koju test ne vidi i dalje postoji.
Pogledajte primjeri/deliveryapp/api.py, funkciju nova_narudzba: BROJAC[0] += 1, pa broj = f"N-{BROJAC[0]}". FastAPI obične (def) funkcije izvršava u više niti istovremeno. Ima li ovdje trke? – Ima: dvije niti mogu povećati brojač, pa obje pročitati istu, veću vrijednost, i dobiti isti broj. API iz poglavlja 9 ima istu skrivenu grešku kao naš brojač bez brave.
time.sleep(0) trka je česta: od 1600 brojeva samo oko 350 je jedinstveno.Rješenje je da „pročitaj i upiši” postane kritična sekcija: dio koda u kojem smije biti samo jedna nit. Za to služi brava (threading.Lock). Nit koja uđe u blok with self.brava: zaključa bravu; druga nit koja pokuša ući čeka dok prva ne izađe.
class BrojacSaBravom(Brojac):
"""Brojač S bravom: samo jedna nit u isto vrijeme radi „pročitaj i upiši”."""
def __init__(self, pocetak=1000):
super().__init__(pocetak)
self.brava = threading.Lock()
def sljedeci(self):
with self.brava:
return super().sljedeci()Slika iz života: jedan ključ od toaleta na benzinskoj pumpi. Ko ima ključ, unutra je; ostali čekaju, ma koliko se žurili.
Kako to testirati? Običan test s jednom niti je beskoristan: i brojač bez brave prolazi.
def test_jedna_nit_bez_brave_radi():
"""U jednoj niti i brojač bez brave je ispravan: zato običan test grešku ne vidi."""
b = Brojac()
assert [b.sljedeci() for _ in range(3)] == ["N-1001", "N-1002", "N-1003"]Test paralelnog koda mora napraviti paralelnost: pokrenuti mnogo niti, pustiti ih da se „sudaraju”, i provjeriti svojstvo koje mora važiti bez obzira na redoslijed. Za brojač: svi brojevi su različiti, ima ih tačno 1600, a najveći je N-2600.
@pytest.mark.parametrize("ponavljanje", range(20))
def test_sa_bravom_svi_brojevi_razliciti(ponavljanje):
brojevi = podijeli_brojeve(BrojacSaBravom(), niti=8, po_niti=200)
assert len(brojevi) == 1600
assert len(set(brojevi)) == 1600 # nijedan broj nije dat dvaput
assert max(brojevi, key=lambda b: int(b[2:])) == "N-2600"Tri tehnike u jednom testu:
range(20)), jer jedno prolaženje paralelnog testa ne dokazuje mnogo.Za brojač bez brave postoji isti test, označen sa xfail („očekujemo da padne”), da u izvještaju ostane vidljivo da je ta verzija pogrešna:
pytest -q -rx test_brojac.py
XFAIL test_brojac.py::test_bez_brave_svi_brojevi_razliciti - trka podataka: brojač bez brave daje iste brojeve
22 passed, 1 xfailed in 0.79s„Python ima GIL (globalnu bravu interpretera), pa trke u Pythonu nema.” GIL samo osigurava da jedna nit u jednom trenutku izvršava Python instrukciju. Operacija kao self.zadnji += 1 ima više instrukcija, i sistem može prebaciti nit između njih. Trka u Pythonu postoji, samo je rjeđa, a to je, za testera, još gore.
xfail čuva u izvještaju test koji namjerno pokazuje poznatu grešku.Odgovori: tipka R ili klik na zamućeno polje.
Brava rješava trku, ali uvodi novu opasnost. Zamislite dvije brave, kuhinja i dostava. Nit A (restoran) zaključa kuhinju, pa traži dostavu. Nit B (dostavljač) zaključa dostavu, pa traži kuhinju. Svaka drži ono što onoj drugoj treba, i obje čekaju zauvijek. To je zastoj (deadlock). Slika iz života: dva auta na uskom mostu, jedan sa svake strane; nijedan ne želi da se vrati.
Za zastoj moraju važiti četiri uslova zajedno (tzv. Coffmanovi uslovi): resurs može držati samo jedna nit; nit drži jedan resurs dok čeka drugi; resurs se ne može silom oduzeti; i niti čekaju u krug. Najčešći lijek je ukloniti krug: sve niti zaključavaju brave uvijek istim redom (npr. uvijek prvo kuhinja, pa dostava).
Kako testirati zastoj, a da test ne visi zauvijek? Svako čekanje u testu mora imati vremensko ograničenje. Test iz test_brojac.py namjerno pravi zastoj i provjerava da ga je otkrio:
def test_zastoj_otkriven_vremenskim_ogranicenjem():
kuhinja, dostava = threading.Lock(), threading.Lock()
obje_drze_prvu, obje_pokusale = threading.Barrier(2), threading.Barrier(2)
uspjeh = []
def nit(prva, druga):
with prva:
obje_drze_prvu.wait() # obje niti sada drže po jednu bravu
dobila = druga.acquire(timeout=0.5) # čeka drugu bravu najviše pola sekunde
uspjeh.append(dobila)
obje_pokusale.wait() # prvu bravu pušta tek kad su obje pokušale
if dobila:
druga.release()
a = threading.Thread(target=nit, args=(kuhinja, dostava))
b = threading.Thread(target=nit, args=(dostava, kuhinja))
a.start(); b.start(); a.join(); b.join()
assert uspjeh == [False, False] # nijedna nit nije dobila drugu bravu: zastojDva detalja su važna. Barijera (threading.Barrier(2)) tjera obje niti da sačekaju jedna drugu na istom mjestu, pa se zastoj dešava svaki put, a ne slučajno: test je ponovljiv. A acquire(timeout=0.5) umjesto običnog acquire() znači da test u najgorem slučaju traje pola sekunde, a ne beskonačno.
U stvarnim projektima isto važi za cijelu svitu: alat kao pytest-timeout prekida svaki test koji traje predugo i prijavi ga kao pad, umjesto da CI čeka satima.
Na Marsu se desila treća vrsta problema. U sistemima stvarnog vremena (letjelice, automobili, medicinski uređaji) zadaci imaju prioritete: važniji zadatak prekida manje važan. To je dobro, ali u kombinaciji s bravom stvara zamku (slika 12.2):

Rješenje je nasljeđivanje prioriteta: dok niski zadatak drži bravu na koju čeka visoki, on privremeno dobija visoki prioritet. Srednji ga više ne može prekinuti, niski brzo završi, pusti bravu, i visoki nastavlja. Operativni sistem Pathfindera to je znao raditi, ali opcija nije bila uključena za tu bravu. Inženjeri su je uključili porukom poslanom na Mars.
Zašto testovi na Zemlji nisu našli grešku prije lansiranja? Prema kasnijem pismu vođe softverskog tima, ponovna pokretanja su se tokom testiranja javila, ali rijetko; nisu se mogla ponoviti ni objasniti, a prednost je dobio posao koji je tada izgledao važniji, softver za slijetanje. Na Marsu, s više podataka i drugačijim rasporedom poslova, javljala su se češće. Pouka za testera: rijetka, neobjašnjena greška u paralelnom sistemu nije šum, nego trag.
Nestanak struje 2003. ima sličnu pouku. Istražna komisija utvrdila je da je trka podataka u alarmnom softveru zaustavila obradu alarma, a sistem to nije javio: operateri su gledali ekran koji je izgledao normalno. Najopasnija paralelna greška nije ona koja ruši program, nego ona koja ga tiho zaustavi.
Paralelne greške se ne mogu „isključiti” testom kao što se može provjeriti cijena N-1001. Ali šansa da ih nađemo može se jako povećati. Tehnike, od najjednostavnije:
| Tehnika | Kako | U DeliveryApp |
|---|---|---|
| stres s nitima | mnogo niti, mnogo operacija istovremeno | 8 niti × 200 brojeva |
| ponavljanje | isti test stotine ili hiljade puta | range(20); dodatak pytest-repeat (--count=1000) |
| provjera svojstva | provjerava se ono što mora važiti uvijek, ne tačan redoslijed | svi brojevi različiti |
| ubrzavanje prebacivanja | ubaciti tačke u kojima sistem prebacuje niti | time.sleep(0) između čitanja i upisa |
| barijere | niti se sinhronizuju na tačno određenom mjestu, pa je scenarij ponovljiv | test zastoja |
| vremenska ograničenja | svako čekanje ima rok | acquire(timeout=0.5), pytest-timeout |
| statička analiza i recenzija | tražiti dijeljene podatke bez brave čitanjem koda | BROJAC[0] += 1 u api.py |
| test opterećenja | stotine pravih korisnika istovremeno | Locust iz poglavlja 10, s provjerom jedinstvenih brojeva |
Najvažnija rečenica: jedan prolaz paralelnog testa ne dokazuje skoro ništa, a jedan pad dokazuje da greška postoji. Zato se paralelni testovi ponavljaju, a svaki njihov pad se istražuje, nikad ne ignoriše.
Postoji i dizajnerski savjet koji štedi najviše posla: što manje dijeljenih podataka. Brojač narudžbi u pravom sistemu obično se prepušta bazi podataka (npr. automatski rastući ključ ili sekvenca), koja je napravljena da radi s mnogo istovremenih korisnika i testirana decenijama. Najbolja trka je ona koju ne moramo testirati jer nema dijeljenog podatka.
Odgovori: tipka R ili klik na zamućeno polje.
Nestabilan test (flaky test) nad istim kodom nekad prolazi, a nekad pada. Paralelne greške su jedan uzrok, ali ne jedini. Nestabilni testovi su veliki problem u industriji: veliki timovi javno izvještavaju da značajan dio padova u njihovom CI-ju nisu prave greške, nego nestabilni testovi. Kad tim navikne da „crveno ponekad znači ništa”, prestane vjerovati testovima, i prava greška prođe, kao poruka „MALFUNCTION 54” kod Therac-25 koju su operateri navikli preskakati.
Najčešći uzroci, s primjerima iz DeliveryApp:
| Uzrok | Primjer | Lijek |
|---|---|---|
| paralelnost | brojač bez brave | brava; test s nitima koji se ponavlja |
| vrijeme | test radnog vremena prolazi samo prije 23 h | patch trenutnog vremena (poglavlje 11) |
| redoslijed testova | test očekuje broj N-1001, ali neki raniji test je već napravio narudžbu | svaki test sam pravi svoje podatke; fixture |
| dijeljeno stanje | globalni rječnik NARUDZBE u api.py puni se iz testa u test | izolacija: novo stanje za svaki test |
| čekanje „na oko” | time.sleep(1) pa provjera; na sporom računaru 1 s nije dovoljno | čekati uslov, ne vrijeme (poglavlje 13) |
| slučajnost | test koristi random bez sjemena | fiksno sjeme (poglavlje 9) |
| spoljni servisi i mreža | test zove pravi servis mapa koji ponekad kasni | dvojnik u jediničnim testovima; ponovni pokušaj u sistemskim |
| decimalni brojevi i zaokruživanje | poređenje 0.1 + 0.2 == 0.3 | centi (poglavlje 5) |
Primjer nestabilnog testa zbog redoslijeda:
def test_prva_narudzba_ima_broj_1001():
r = klijent.post("/v1/narudzbe", json=N1001)
assert r.json()["broj"] == "N-1001" # tačno samo ako je ovo prva narudžba u cijeloj sesijiPokrenut sam, prolazi. Pokrenut s ostalim testovima API-ja, pada, jer je BROJAC već povećan. Ispravno: provjeriti svojstvo (broj počinje sa „N-” i različit je od prethodnog), ili da svaki test dobija novu, praznu aplikaciju.
Nestabilan test se teško hvata, jer kad ga pogledate, prolazi. Alati pomažu:
pytest --count=200 tests/test_api.py (dodatak pytest-repeat) pokrene svaki test 200 puta; test koji padne makar jednom je nestabilan;-p randomly:SJEME ga ponavlja, -p no:randomly isključuje); test koji pada samo u nekom redoslijedu zavisi od drugog testa;pytest -n 4) pokreće testove u četiri procesa istovremeno; otkriva testove koji dijele fajlove, portove ili bazu;Kad se nestabilan test nađe, ponovite ga lokalno, mnogo puta, s istim uslovima (isto sjeme, isti redoslijed), dok ne nađete uzrok. Zapišite pad kao defekt, s tačnim uslovima, kao u poglavlju 3.
Tri pogrešna odgovora, koja se u praksi često čuju:
time.sleep(5).” Test postaje spor i i dalje nestabilan na sporijem računaru.Dobar postupak:
@pytest.mark.skip(reason="nestabilan, defekt D-123") ili posebna oznaka) i zapiše kao defekt s vlasnikom i rokom. Tako ne blokira tim, a ne nestaje iz vida.patch umjesto pravog vremena, čekanje uslova umjesto sleep.„Nestabilan test je problem testa, a ne aplikacije.” Ponekad jeste. Ali test koji ponekad pada zbog trke u kodu ispravno prijavljuje pravu grešku, koja će se u produkciji desiti mnogo češće, jer ima mnogo više korisnika. Brojač bez brave „ponekad” daje isti broj; u petak u 19 h to „ponekad” postaje „svakih par minuta”.
sleep, slučajnost bez sjemena, mreža.Odgovori: tipka R ili klik na zamućeno polje.
Cilj je izazvati i spriječiti trku podataka, napraviti i otkriti zastoj, i pronaći nestabilne testove u DeliveryApp. Radi se u parovima. Prije početka: pip install pytest-repeat pytest-randomly pytest-timeout.
primjeri/paralelno pokrenite pytest -v -rx test_brojac.py. Zapišite rezultate.podijeli_brojeve(Brojac()) pet puta i zapišite broj jedinstvenih brojeva. Zatim pet puta sa BrojacSaBravom().brojac.py obrišite red time.sleep(0). Pokrenite test bez brave 200 puta (pytest --count=200 -k bez_brave). Koliko puta je pao? Objasnite zašto to ne znači da je kod ispravan.sleep). Da li se trka sada pojavljuje?TestClient napravi 50 narudžbi (POST /v1/narudzbe). Provjerite da su svi brojevi različiti. Ponovite 20 puta.api.py u svojoj kopiji bravom oko povećanja i čitanja brojača. Ponovite test.test_zastoj_otkriven_vremenskim_ogranicenjem. Zatim u kopiji promijenite redoslijed brava u drugoj niti tako da obje zaključavaju kuhinja pa dostava. Šta se desi s testom? Ispravite tvrdnju u testu tako da provjerava da zastoja nema.pytest --timeout=5. Šta ispisuje pytest-timeout?primjeri/deliveryapp pokrenite pytest tests tri puta (pytest-randomly svaki put miješa redoslijed i na vrhu ispiše sjeme), i pytest --count=20 tests/test_api.py. Da li neki test pada samo ponekad?test_prva_narudzba_ima_broj_1001 iz 12.7 u svoju kopiju test_api.py. Pokrenite ga sam, pa sa ostalim testovima, pa nekoliko puta s nasumičnim redoslijedom. Zapišite rezultate i prepravite test tako da provjerava svojstvo.patch (koristi pravo trenutno vrijeme). U koje doba dana pada? Objasnite zašto je nestabilan i popravite ga.Rezultat koji se predaje: fajl rezultati-12.md s mjerenjima brojača sa i bez brave, sa i bez time.sleep(0); test i izvještaj o defektu za brojač u API-ju; izmijenjen test zastoja; spisak nestabilnih testova koje ste našli, s uzrokom i popravkom.
Rješenja su sakrivena – tipka R ili klik na okvir.
Dvije niti istovremeno izvršavaju stanje_racuna = stanje_racuna - iznos za račun od 100 € (svaka plaća 30 €). Koja su sve moguća konačna stanja i koja je ispravna?
Ispravno je 40 € (100 − 30 − 30). Ako obje niti pročitaju 100 prije nego što ijedna upiše, obje upišu 70, pa je konačno stanje 70 €: 30 € je „nestalo” iz evidencije. Moguća stanja su 40 € i 70 €. Lijek: brava oko čitanja i upisa, ili atomska operacija u bazi.
Funkcija istovremeno (8 niti) dodaje stavke u korpu. Koje svojstvo test treba provjeriti?
Da je broj stavki u korpi jednak zbiru svih dodatih, i da je ukupni iznos jednak zbiru cijena svih dodatih stavki (invarijanta korpe iz poglavlja 11), bez obzira na redoslijed. Ne provjerava se redoslijed stavki, jer on nije zadan.
Nit „restoran” zaključava bravu narudžbe pa bravu dostavljača; nit „dostavljač” zaključava bravu dostavljača pa bravu narudžbe. Kako spriječiti zastoj?
Dogovoriti isti redoslijed za sve niti, npr. uvijek prvo brava narudžbe, pa dostavljača. Tada nema čekanja u krug. Druga mogućnost je zaključavanje s vremenskim ograničenjem i ponovnim pokušajem, ali isti redoslijed je jednostavniji i sigurniji.
Test provjerava da se nakon klika na „Naruči” poruka „Narudžba primljena” pojavi poslije time.sleep(2). Na računaru programera uvijek prolazi, u CI-ju pada otprilike svaki deseti put. Uzrok i lijek?
Uzrok je čekanje „na oko”: na sporijem CI serveru odgovor ponekad stigne poslije 2 s. Lijek nije veći sleep, nego čekanje uslova: provjeravati svakih 0,1 s da li se poruka pojavila, do najviše npr. 10 s. Playwright to radi sam (poglavlje 13).
Zašto je nasljeđivanje prioriteta riješilo problem, a ne bi ga riješilo npr. povećanje prioriteta zadatka za meteo podatke na stalno visok?
Nasljeđivanje daje niskom zadatku visok prioritet samo dok drži bravu na koju čeka visoki zadatak; poslije se vraća na nizak i ne smeta drugim poslovima. Stalno visok prioritet za meteo zadatak bi ga stavio ispred komunikacije i drugih važnih poslova uvijek, pa bi se pomjerio problem: neki drugi važan zadatak bi kasnio. Nasljeđivanje rješava tačno uzrok (čekanje na bravu), a ne mijenja cijeli raspored.
time.sleep(0) u 200 pokretanja nije pao, a kod ipak nije ispravan?test_sa_bravom_svi_brojevi_razliciti?