Tänä päivänä ohjelmistojen laatuun kiinnitetään yhä enemmän huomiota. Asiakkaat ja käyttäjät vaativat ohjelmilta enemmän ja ovat laatutietoisempia kuin ennen. Kiristynyt kilpailu ja esimerkiksi ISO 9000 -sarjan laatujärjestelmien tulo ohjelmistomarkkinoille ovat osaltaan pakottaneet lisäämään panostusta myös ohjelmistotestaukseen. Muita syitä testauksen tarpeen kasvuun, ja sitä myötä testauksen automatisoinnin kasvuun, ovat olleet vaikeammin testattavien graafisten, tapahtumakeskeisten käyttöympäristöjen (Windows, X Window System, jne.) sovellusten yleistyminen, sovellusten tuottaminen yhä useampiin ympäristöihin sekä sovellusten siirtyminen ja hajautuminen yhä enemmän verkkoihin.
Toisaalta ohjelmistotestausta tukee se, että viimeisten arvioiden mukaan noin 40% kaikista ohjelmistoprojekteista epäonnistuu täydellisesti. Esimerkikisi Yhdysvalloissa menetettiin epäonnistuneissa sovelluskehitysprojekteissa vuonna 1995 yli 350 miljardia markkaa. Suomessa vastaavan luvun arvellaan olevan noin kaksi miljardia [TIE96]. Kaikkia projekteja ei ohjelmistotestauskaan kuitenkaan voi pelastaa. Useimmiten epäonnistumisien pääsyinä pidetään tavoitteen huonoa määrittelyä sekä puuttellista projektin hallintaa. Jos ohjelma jo alunperin määritellään huonosti tai jopa virheellisesti, todennäköisyys sille, että ohjelmaa koskaan tullaan käyttämään, on hyvin pieni.
Aina ohjelmistojen laadusta puhuttaessa, esille tulee termi SQA (Software quality assurance). SQA:n tarkoitus on nimensä mukaisesti varmistaa kehitettävän ohjelmiston laatua. SQA kattaa koko ohjelmistoprosessin aina määrittelystä tuotteen julkaisemiseen, käsittäen kaiken siihen liittyvän toiminnan kuten analyysit, dokumentoinnit, koodauksen, testausmenetelmät ja niin edelleen. Ohjelmistotestaus on vain yksi, mutta tärkeä osa SQA:ta.
Ohjelman laadulle asetetut vaatimukset vaihtelevat usein laajastikin ohjelman käyttötarkoituksen mukaan. Onhan selvää, että tapauksissa joissa ohjelmalta edellytetään esimerkiksi äärimmäistä turvallisuutta ja toimivuutta (eli ohjelma ei saa toimia "väärällä tavalla"), kuten esimerkiksi ydinvoimaloissa, lentokoneissa jne., ovat myös laatuvaatimukset ja testauksen tarve huomattavasti korkeammat kuin "tavallisissa" ohjelmissa.
Tyypillisiä hyvälaatuiselle ohjelmalle asetettuja vaatimuksia ovat mm.
Testauksen laatua mitataan usein sillä, miten kattavasti testaus on suoritettu. Kaikista pienimpienkin ohjelmien täydellinen testaus on kuitenkin käytännössä mahdotonta. Otetaan esimerkiksi seuraava lyhyt ohjelman pätkä.
i=0; do { switch (a) { case 1: .... case 2: .... case 3: .... } i++; } while (i < 20);
Koska edellä esitetyn esimerkin mukaan täydellinen ohjelman läpikäynti tai testaus on usein mahdotonta (tai ainakin järjetöntä), olisi siis keskityttävä mahdollisimman kattavan, mutta järkevän, testauksen toteutukseen.
Riittävän kattavan testiaineiston valinta on aina hankalaa ja usein yhtä luovaa kuin itse ohjelman suunnittelu. Lisäksi testiaineiston valintaan liittyy yksi puhtaasti psykologinen ongelma. Yhden henkilön projekteissa tai projekteissa, joissa esim. puutteellisten resurssien takia on vähän työvoimaa, testaus suoritetaan usein ohjelmoijan tai ohjelmoijien toimesta. Itse tuottamansa koodin testaajalla on hyvin usein taipumus, ainakin alitajuisesti, valita ja johtaa testitapaukset liikaa "koodin mukaan" tai jopa vältellen mahdollisia virheenaiheuttajia. Tällöin testitapaukset harvemmin palvelevat varsinaista tarkoitustaan ja testaus on kaikkea muuta kuin kattava.
1.3
Testauksen tarkoitus ja tavoitteet
SQA:n kannalta testauksen tarkoitus on siis varmistaa ja parantaa kehitettävän ohjelmiston laatua. Toisaalta testauksen tarkoituksena on yksinkertaisesti ohjelmassa esiintyvien virheiden etsiminen ja korjaaminen. Mitä virheellä sitten tarkoitetaan? Yleisesti ottaen virheeksi voidaan käsittää mikä tahansa tila tai tapahtuma, joka poikkeaa odotetusta. Esimerkiksi ohjelmassa voi tapahtua jotain, mitä sen määrittelyn mukaan ei olisi pitänyt tapahtua. Toisaalta itse määrittely voi olla virheellinen, jolloin virhe tapahtuu, vaikka ohjelma toimisikin määritellysti.
Mitä myöhäisemmässä vaiheessa ohjelmistoprojektia virhe löydetään, sitä enemmän työtä sen korjaus teettää ja sitä kalliimmaksi se tulee. Yleisesti on esitetty, että jos virheen havaitseminen ja korjaaminen ohjelman suunnitteluvaiheessa tulee maksamaan yhden rahayksikön, tulee havaitseminen juuri ennen testausta maksamaan noin 6, testauksen aikana noin 15 ja ohjelman julkaisun jälkeen 60-100 rahayksikköä. Joissakin tapauksissa julkaisun jälkeen havaittu virhe voi itsessään aiheuttaa vielä paljon suurempia kustannuksia.
Koska ohjelmaa ei kuitenkaan voida testata täydellisesti, ei koskaan
myöskään voida olla täysin varmoja ohjelman
virheettömyydestä. Tästä syystä onkin haastavampaa ja
järkevämpää asettaa testauksen tavoitteeksi, ohjelman
virheettömyyden todistamisen sijaan, mahdollisimman monen virheen
löytäminen ja korjaaminen. Käytännössä
tämä edellyttää esimerkiksi testiaineiston valintaa siten,
että testitapauksilla olisi mahdollisimman suuri todennäköisyys
paljastaa mahdolliset virheet. Jotta virheet löydettäisiin
mahdollisimman varhaisessa ohjelmistoprojektin vaiheessa, tulisi testausta
tehostaa erityisesti matalampien tasojen testauksessa (ks. luku 2.2).
2.
Testausprosessi
Lukuunottamatta joitakin pieniä ja yksinkertaisia ohjelmia, testaus tulisi käsittää kokonaisena prosessina ohjelmistokehityksen aikana. Testausprosessin suhdetta ohjelmistoprosessiin kuvataan usein seuraavanlaisella V-mallilla.
Kuva 2.1. Testausprosessi osana ohjelmistokehitystä.
Kuten mitä tahansa prosessia, testausprosessia on helpompi hallita, kun se on hyvin suunniteltu ja dokumentoitu. Suunnittelu säästää aikaa ja antaa selkeän kuvan suoritettavasta prosessista. Prosessin läpi kestävä dokumentointi ja raportointi puolestaan usein helpottaa projektissa mukana olevien ihmisten kommunikointia ja nopeuttaa vaiheista toisiin siirtymistä.
Testausprosessista ja sen vaiheista (ks. luku 2.2.) laaditaan omat testisuunnitelmansa. Testisuunnitelma pitää sisällään mm. lähestymistavan, resurssit, aikataulun, testattavat kohteet ja testitapaukset (usein omina suunnitelmina). Testaussuunnitelmat johdetaan kuvan 2.1. V-mallin mukaisesti. Pääsuunnitelma (master test plan) johdetaan yleensä ohjelman vaatimuksista (kuten alfa-testaussuunnitelmakin).
Testauksen raportointiin liittyy testauksesta laadittavien dokumenttien lisäksi tärkeänä osana ns. testilokit (test log). Testiloki sisältää kronologisen raportin suoritettujen testien yksityiskohdista. Useimmat testausohjelmat tuottavat lokit automaattisesti nauhoittaen tapahtumat omiin lokitiedostoihin.
Testausprosessin suunnittelu ja dokumentointi noudattaa pitkälle samoja periaatteita kuin yleensä ohjelmistoprosessin suunnittelu ja dokumentointi. Tarkempaa tietoa löytyy mm. ohjelmistoprosessia laajemmin käsittelevästä kirjallisuudesta [SOM95],[PRE92].
Testausprosessi voidaan jakaa pienempiin ja paremmin hallittaviin osiin / vaiheisiin, joita kutsutaan myös tasoiksi (testing levels).
Kuva 2.2. Testauksen tasot (ks. kuva 2.1).
Jos testattava kohde ei läpäise jotakin testivaihetta, se palautetaan tasolle, jolla virhe on tapahtunut tai jolla virheen uskotaan olevan.. Tämän jälkeen virheet ja ristiriidat tarvittaessa paikannetaan, korjataan ja kohde testataan uudelleen. Usein puhutaan regressiotestauksesta, jolla tarkoitetaan, että kaikki virhettä edeltäneet testit suoritetaan uudelleen varmentaen, etteivät tehdyt korjaukset ja muutokset ole synnyttäneet uusia virheitä alemmille tasoille.
Moduulitestaus (yksikkötestaus, unit testing) käsittää pienimpien ohjelmayksiköiden, esimerkiksi funktioiden, ja niistä muodostuvien pienten kokoelmien eli moduulien, testauksen. Moduulitestauksen tavoitteena on löytää selvien virheiden lisäksi ristiriitoja yksiköiden ja moduulien määrittelyjen ja toiminnan välillä sekä korjata ne.
Joissakin kirjallisuuslähteissä mm. [SOM95] yksikkö- ja moduulitestaustasot on esitetty omina kokonaisuuksinaan. Tällöin nämä yhdessä muodostavat ns. komponenttitestausvaiheen. Tässä moduulitestaus käsitetään omana kokonaisuutena ja ensimmäisen tason testausvaiheena.
Integraatiotestauksella tarkoitettaan vaihetta, jossa pienemmät osat kootaan yhteen ja testataan, kunnes koko systeemi on saatu kasaan. Integraatiotestauksen tarkoituksena on varmistaa, että kasatut osat toimivat määritellysti keskenään, sekä löytää ja korjata mahdollisimman paljon yhteensoveltumattomia moduuleita.
Systeemitestauksen päätavoite on tutkia täyttääkö valmis systeemi (esim. ohjelmisto) sen määrittelyn asettamat vaatimukset. Toisaalta tavoitteena voidaan taas pitää sitä, että löydetään ja korjataan mahdollisimman paljon ristiriitoja systeemin ja sen määrittelyn väliltä. Joskus voi olla lisäksi tarpeellista testata esimerkiksi valmiin ohjelman suorituskykyä, rasituskestävyyttä, turvallisuutta ja toimivuutta verkossa.
Alfa-testauksessa tutkitaan täyttääkö esimerkiksi tilaustyönä tehty ohjelmisto asiakkaan asettamat vaatimukset. Tätä vaihetta jatketaan, kunnes systeemin kehittäjä ja asiakas hyväksyvät tuotteen ominaisuudet.
Beta-testausta käytetään usein ennen kaupallisen tuotteen julkaisua. Tässä ohjelmistoa jaetaan tai toimitetaan potentiaalisille asiakkaille (tai joskus kaikille halukkaille), jotka sitten käyttävät ohjelmistoa ja raportoivat mahdollisista virheistä tai parannusehdotuksista tuotteen kehittäjälle. Beta-testauksen jälkeen (esimerkiksi joitakin kuukausia myöhemmin) ohjelmaa mahdollisesti muokataan ja julkaistaan uusi beta-versio tai lopullinen kaupallinen versio.
2.3
Virheenjäljitysprosessi (Debugg
ing process)
Virheenjäljitykseksi (debugging) kutsutaan prosessia, jossa testitapahtumien paljastamia virheitä ja ongelmia yritetään paikantaa ja korjata. Debuggauksen tavoitteena on siis löytää havaittujen ongelmien ja virheiden aiheuttajat sekä korjata ne.
Virheen tai ongelman aiheuttajan paikantaminen ei aina ole helppoa. Mitä korkeammalla testaustasolla ollaan, sitä vaikeampaa todellisen aiheuttajan löytäminen käytännössä on. Tyypillinen esimerkki on tapaus, jossa ongelmasta saatetaan päästä eroon tilapäisesti, kun löydetäänkin (ja korjataan) jokin toinen virhe kuin alkuperäinen ongelman aiheuttaja. Toisaalta ongelma ei välttämättä johdu mistään selkeästä virheestä, vaan se voi aiheutua esimerkiksi tapahtuneesta pyöristysepätarkkuudesta.
Toinen ongelma, myös korkeammilla testaustasoilla, on se, että monimutkaisen virheen korjauksen seurauksena onkin aiheutettu uusi virhe. Käytännössä on osoittautunut, että hyvin suuren, lähellä kriittistä kokoaan olevan, ja monimutkaisen ohjelman korjaus aiheuttaa helposti useampia uusia virheitä.
Debuggauksen lähestymistapoina käytettään usein
backtracking- tai cause elimination-metodeja.
Backtracking-metodin mukaan koodia lähdetään
käymään virheen esiintymiskohdasta taaksepäin, kunnes virhe
löydetään. Cause elimination-metodin mukaan virheen aiheuttajaa
jäljitetään sulkemalla pois "varmat tapaukset" (joiden ei uskota
aiheuttaneen virhettä). Tätä jatketaan, kunnes oletetaan,
että virhe sisältyy jäljelle jäänneeseen osaan.
Tämän jälkeen virheen aiheuttajaa yritetään
eristää mm. testaamalla jäljelle jäännyttä osaa
sopivilla testitapauksilla.
3.
Staattinen analyysi
Staattisessa analyysissä etsitään virheitä ja puuttellisuuksia tutkittavasta kohteesta ilman ohjelmakoodin suorittamista. Analysointi voidaan suorittaa joko käsin tai automaattisesti. Seuraavassa on lueteltu esimerkkejä siitä, minkä tyyppisiä virheitä staattisessa analyysissä voidaan paljastaa:
Staattiseen analyysiin liittyy myös joukko ns. ohjelmamittoja (software metrics), joilla kuvataan mm. ohjelman pituutta, kokoa ja vaikeusastetta. Näiden mittojen perusteella voidaan arvioida esimerkiksi ohjelman kompleksisuutta. Ohjelman kompleksisuuden määrittäminen voi olla hyödyllistä, sillä monimutkainen koodi on jo itsessään riskialtis virheille ja monimutkaisuuden kasvaessa myös ohjelman testattavuus vaikeutuu. Lisäksi monimutkainen koodi vaikeuttaa ohjelman ylläpitoa ja päivitystä.
Tässä esitellyt ohjelmamitat eivät sinällään anna mitään konkreettista tai varmaa tietoa siitä, että analysoitava ohjelma olisi virheellinen. Ohjelmamittojen avulla voidaan kuitenkin ennustaa, missä mahdollisia ongelmia voi esiintyä.
3.1.1
Halsteadin ohjelmamittoja
Seuraavassa on lueteltu paljon käytettyjä Maurice H. Halsteadin määrittelemiä ohjelmaa kuvaavia mittoja (tarkempaa tietoa [HAL77]).
n1 ohjelmassa esiintyvien eri operaattoreiden lukumäärä,
n2 ohjelmassa esiintyvien eri operandien lukumäärä,
N1 ohjelmassa esiintyvien operaattoreiden kokonaislukumäärä sekä
N2 ohjelmassa esiintyvien operandien kokonaislukumäärä
Ohjelman koko (program volume),
Ohjelman vaikeusaste / virhealttius (difficulty level / error propeness),
Ohjelman taso (program level),
Halstead on havainnut, että ohjelman ymmärtämiseen kuluvan ajan (time to understand a program),
Virhe-ennuste (bug prediction),
McCaben "syklomaattinen" luku (cyclomatic number) on ohjelmamitta, joka kertoo ohjelman loogisesta kompleksisuudesta. Esimerkiksi funktioille luku lasketaan usein seuraavanlaisesti (laskemiseen on monia muitakin vaihtoehtoja, joissa kuitenkin päädytään samaan lopputulokseen):
Käytännössä on havaittu että funktion tapauksessa
v(G):n tulisi olla pienempi kuin 15. Tätä suuremman
arvon omaavia funktioita on usein vaikea hahmottaa ja testata.
3.2
Koodin tarkastelu ja läpikäynti (Inspections and
walkthroughs)
Ohjelmakoodin tarkastelu "käsin" on havaittu varsin tehokkaaksi testausmenetelmäksi varsinkin, kun tiedetään, mitä etsiä. Useimmiten tällä tavoin voidaan paljastaa tyypillisiä ja tunnettuja virheitä, joita automaattisesti ei välttämättä havaita. Suuremmissa projekteissa koodin tarkastelu tapahtuu ryhmätyönä. Jokainen testaaja tutkii ja läpikäy (esimerkiksi edeltäkäsin sovittujen testiaineistojen kanssa) koodin ensin itsekseen. Tämän jälkeen pidetään palaveri, jossa koodista sitten keskustellaan yhdessä. Koodin analysoinnissa ei keskitytä pelkästään tyypillisiin virheisiin, vaan usein kiinnitetään huomiota mm. ohjelmointityyliin.
Kokemusten perusteella on havaittu, että nämä metodit ovat
tehokkaampia kuin perinteisemmät pöytätestausmenetelmät,
joissa usein itse ohjelmoija kävi koodia läpi.
Käytännössä näillä metodeilla voidaan
löytää 30 - 70 % loogisista suunnittelu- ja
koodausvirheistä.
4.
Dynaaminen analyysi
Dynaamisessa analyysissä keskitytään ohjelman ja koodin käyttäytymiseen suorituksen aikana. Testaus edellyttää siis jonkinlaista toimivaa ohjelmaa tai ympäristön, missä esimerkiksi yksittäisiä funktioita voidaan suorittaa. Tällä tavoin saadaan testattua ominaisuuksia, mitkä staattisessa analyysissä olisivat mahdottomia tai ainakin vaikeita testata.
Seuraavassa on esitelty muutamia yleisimmin käytettyjä dynaamisen analyysin testausstrategioita. Valittava testausstrategia riippuu hyvin pitkälle testattavasta kohteesta, sen määrittelystä sekä testausvaiheesta. Nämä strategiat eivät myöskään ole toisiaan poissulkevia, vaan niitä voidaan käyttää ja usein käytetään rinnakkain täydentämässä toisiaan.
Black box-testaus perustuu testattavan systeemin (esim. ohjelma) tai komponentin (esim. funktio) input-output käyttäytymiseen. Black box-testauksessa ei välitetä testattavan kohteen rakenteesta tai sisällöstä, vaan tutkittavana ovat kohteen tulosteet (output) erilaisilla syötearvoilla (input). Testaajalle kohde on siis jokin tuntematon "musta laatikko", black box. Testattavan kohteen oikeellisuutta tarkastellaan vertaamalla saatuja tulosteita haluttuihin tai odotettuihin tulosteisiin. Testitapaukset johdetaan aina kohteen määrittelyn perusteella.
Kuva 4.1. Black box testaus.
4.1.1
Testitapausten jako ekvivalenssiluokkiin (Equivalence partitioning)
Jotta testitapausten lukumäärä saataisiin mahdollisimman pieneksi, mutta silti mahdollisimman kattaviksi, on yksi mahdollisuus jakaa syötejoukko ns. ekvivalenssiluokkiin. Testattavan kohteen määrittelyn perusteella voidaan nähdä, mitkä syötteet vaikuttavat kohteeseen samalla tavalla (ja tuottavat samanlaisia tulosteita). Nämä syötteet muodostavat oman ekvivalenssiluokkansa. Ekvivalenssiluokat voidaan vielä jakaa "oikeisiin" (valid) sekä "vääriin" (invalid). Tämän jälkeen syötteet eli testitapaukset tulisi valita kattavasti sekä oikeista että vääristä ekvivalenssiluokista. Esimerkkinä oikeista ja vääristä ekvivalenssiluokista voidaan ajatella ohjelmaa, joka kysyy: "Oletko varma (K/E) ?". Tällöin oikeat ekvivalenssiluokat muodostuisivat kirjaimista K ja k, sekä kirjaimista E ja e. Väärän ekvivalenssiluokan puolestaan muodostaisivat kaikki muut mahdolliset syötteet.
4.1.2
Reuna-arvoanalyysi (Boundary value analysis, BVA)
Reuna-arvoanalyysissä testitapauksiksi valitaan nimensä mukaan rajoilla (reunoilla) olevia (äärimmäisiä) arvoja. Nämä arvot voivat olla esimerkiksi mahdollisimman suuria tai pieniä, pitkiä tai lyhyitä ja niin edelleen. Reuna-arvoanalyysi perustuu olettamukseen, että jos testattava kohde epäonnistuu joillakin rajojen arvoilla, se yleensä epäonnistuu myös arvoilla, jotka kuuluvat rajojen sisään. Tällä tavoin saadaan taas testitapausten lukumäärää pienennettyä.
4.1.3
Virheen arvaus (Error guessing)
Testaajalla on usein jo kokemusperäistä tietoa erityyppisistä ongelmia aiheuttavista syötteistä, jotka eivät sisälly esimerkiksi edellämainittujen metodien antamiin tai ehdottamiin syötteisiin. Esimerkiksi 0 on syöte, joka ei välttämättä kuulu BVA:n rajoihin, mutta on aiheuttanut ja aiheuttaa virheellisiä käsittelyjä useissa ohjelmissa. Virheen arvaus-metodi perustuukin siihen, että listataan mahdolliset mieleentulevat testitapaukset, jolla testattava kohde saadaan epäonnistumaan. Tämän jälkeen suoritetaan testiajot näillä syötteillä. Tämä ei itsessään ole kovinkaan kattava black box-testausmetodi, mutta se on hyvä lisä muille metodeille.
4.1.4
Sattumanvarainen testaus (Random testing)
Yksi tapa (joskin usein huono sellainen) on valita syötejoukosta umpimähkään tietty määrä syötteitä ja suorittaa testiajot näillä. Tällä tavoin, varsinkin valitun syötejoukon ollessa suhteellisen pienen, ei voida saada varmuutta siitä, että kohde tuli edes kohtuullisesti testattua.
Päinvastoin kuin black box-testauksessa, jossa testitapaukset johdettiin kohteen määrittelystä, white box-testauksessa testitapaukset johdetaan kohteen, esimerkiksi koko ohjelman sisäisestä rakenteesta ja logiikasta. Tavoitteena on valita testitapaukset siten, että kaikki kohteen (esimerkiksi ohjelman tai funktion) haarat ja ohjelmapolut tulisi käytyä läpi.
Kuva 4.2. White box-testaus.
Seuraavassa on lueteltu muutamia white box-metodeja, joiden kriteerien mukaan testiaineisto eli syötteet voitaisiin valita.
Lauseiden kattavuus-metodin mukaan testitapausten tulisi kattaa (suorittaa ainakin kerran) kaikki ohjelman lauseet. Käytännössä tämä on heikko kriteeri, sillä testitapauksia tulisi usein aivan liian suuri määrä.
Haarojen kattavuus-metodin mukaan kaikki ohjelman haarojen vaihtoehdot tulisi pystyä suorittamaan valituilla testitapauksilla. Haaralla (branch) tarkoitetaan tässä ohjelman kohtaa, jossa on kaksi tai useampia vaihtoehtoisesti suoritettavia lauseita (if-else, switch-case jne.).
Päätösten kattavuus (Decision coverage) -metodin mukaan testitapaukset tulisi valita siten, että kaikki ehdon sisältävät lauseet (esim. if-lause) saisivat kaikki mahdolliset tulokset (esim. TRUE ja FALSE). Tämä on itsessään melko heikko kriteeri, sillä useasti ehdolliset lauseet sisältävät tai peittävät toisia ehdollisia lauseita ja näin ollen kaikki mahdolliset kombinaatiot eivät tule välttämättä testatuiksi. Ehtokombinaatioiden kattavuus (Multiple-condition coverage) -metodi paikkaakin tämän puutteen, sillä sen mukaan testitapausten tulisi kattaa kaikki mahdolliset tuloskombinaatiot. Käytännössä kaikkien tuloskombinaatioiden läpikäynti on kuitenkin usein mahdotonta (ks. luku 1.2).
Top-down-strategiassa testaus aloitetaan ohjelman korkeimman tason moduuleista ja vasta sitten edetään alempien tasojen moduulien ja komponenttien testaukseen. Jotta testattavaa moduulia alempien tasojen moduuleja voitaisiin simuloida, tarvitaan ns. "tyhmiä" moduuleita tai moduulin pätkiä (module stubs, stubs). Nämä stub-moduulit ovat hyvin yksinkertaisia, eivätkä sisällä mitään monimutkaisempaa toiminnallisuutta. Kun testaus etenee alemmalle tasolle, stub-moduulit korvataan oikeilla moduuleilla.
Kuva 4.3. Top-down-testaus.
Top-down-testauksen etuja ovat varhaisessa vaiheessa saatava alustava, toimiva ohjelma (jo psykologisestikin kannustavaa suunnittelussa mukana oleville) sekä kahden mahdollisesti erillisen testivaiheen, integraatio- ja systeemitestauksen, yhdistyminen.
Suurin haittatekijä on "ylimääräisten" stub-moduulien luominen, joka vie aikaa. Lisäksi testiaineiston valinta voi tuottaa vaikeuksia (esim. monimutkaisten moduulien stub-versioissa, jotka eivät välttämättä kuvaa täysin oikean moduulin käytöstä) ennenkuin oikeat moduulit on lisätty.
Bottom-up-strategiassa testaus aloitetaan alimman tason moduuleista (moduuleista, jotka eivät käytä tai kutsu muita moduuleja). Testaus etenee ylemmille tasoille, kunnes koko ohjelma on testattu. Kuten top-down-testauksessa jouduttiin luomaan stub-moduuleita, bottom-up testauksessa joudutaan luomaan ns. testiajureita (test drivers), joilla alemman tason komponentteja voidaan suorittaa ja testata. Käytännössä useamman testiajurin sijaan, luodaan yleensä testauksen mahdollistava ympäristö, joka toimii kaikkien moduulien yhteisenä testiajurina (ks. luku 5.2).
Kuva 4.4. Bottom-up-testaus.
Bottom-up-strategian etuna on testiaineiston kohtalaisen helppo valinta. Myös siinä tapauksessa, että kriittisimmät tai virhealtteimmat moduulit sijoittuvat alemmille tasoille, on bottom-up testaus hyvä valinta.
Suurin haitta bottom-up-testauksessa on se, että mitään valmista
ohjelmaa ei ole olemassa ennenkuin viimeinenkin ylimmän tason moduuli on
testattu. Testausta ei myöskään voida aloittaa ennen kuin
alimmankin tason moduulit on suunniteltu ja määritelty.
5.
Ohjelmistotestauksen työkaluja
Kuten ohjelmistotestaus edellä, ohjelmistotestauksessa käytettävät työkalutkin jaetaan usein karkeasti sen mukaan, suoritetaanko testattavaa kohdetta testauksen aikana vai ei. Testauksessa käytetyt automatisoidut apuvälineet luokitellaan usein siis staattisen analyysin ja dynaamisen analyysin ja testauksen työkaluihin. Kolmannen luokan muodostaa testauksen hallinnan työkalut.
5.1
Staattisen analyysin työkalut
Staattisen analyysin työkalujen tarkoitus on löytää virheitä ohjelman koodista ilman minkäänlaista ohjelman suoritusta. Triviaali esimerkki staattisen analyysin apuvälineestä on kääntäjä, joka koodin kääntämisen yhteydessä ilmoittaa useista koodista havaituista virheistä.
Staattisia analysoijia käytetään yleisimmin ohjelmamittojen laskemiseen ja ohjelman rakenteen tarkempaan analysointiin. Kuten jo edellä (ks. luku 3.1) mainittiin, ohjelmamitoista voidaan päätellä ja ennustaa koodin mahdollisia ongelmakohtia sekä analysoida ohjelman kompleksisuutta.
Esimerkkeinä staattisen analyysin työkaluista mainittakoon CMT++ (ks. luku 6.3), Testwell Oy; QA C/C++/Fortran, Programming Research LTD; PC-Metric, SET Laboratories, Inc.
5.2
Dynaamisen analyysin / testauksen työkalut
Jotta suoritusaikainen testaus ja sen analysointi olisi mahdollista, tarvitaan ohjelman pohjalle jonkinlainen ympäristö, joka mahdollistaa tulosten havainnoinnin. Tällainen ympäristö muodostetaan yleensä testiajureiden (test drivers) avulla. Testiajuri voi olla joko itsenäinen ohjelma tai testattavaan ohjelmaan yhdistetty (itsenäinen) osa.
Usein testiajureiden avulla voidaan myös nauhoittaa erityyppistä informaatiota testausta suoritettaessa. Nauhoitukset suoritetaan yleensä erillisiin lokitiedostoihin, joista haluttua informaatiota sitten voidaan tarkastella. Yhä useammin, varsinkin graafisten käyttöliittymien tapauksessa, testiajureilla saadaan myös tietoa testattavan ohjelman vaikutuksesta ympäristöönsä, esimerkiksi tietoa ohjelman muistin ja resurssien käytöstä.
Itse testaus suoritetaan yleensä joko käsin (koko ajan syöttäen testitapahtumia) tai luodulla testiskriptillä (test script), joka suorittaa tai syöttää testattavalle ohjelmalle siinä luetellut testitapahtumat. Testiskriptien etuna on yhtäläisen testauksen nopeus ja helppous toistettaessa jo aikaisemmin suoritettuja testejä esimerkiksi regressiotestauksessa.
Seuraavassa on lueteltu joitakin tyypillisiä, tiettyyn tehtävään erikoistuneita dynaamisen testauksen työkaluja. Simulaattoreilla (simulators, test beds) tarkoitetaan työkaluja, joilla simuloidaan (väliaikainen) suoritusympäristö esimerkiksi ohjelman osille ja funktioille, joita sinällään ei voida suorittaa. Esimerkkinä CTB (ks. luku 6.1), Teswell Oy.
Testauksen kattavuutta ja testausta analysoivat (test coverage analysis tools) työkalut antavat tietoa testauksen laadusta (kattavuudesta). Niiden avulla pidetään kirjaa jo testatuista ja testaamattomista ohjelman osista. Esimerkkeinä CTC++ (ks. luku 6.2), Testwell Oy; QC/Coverage, CenterLine Software; C-Cover, Bullseye Testing Technology; PureCoverage, Pure Atria.
Suorituskykyä ja rasitussietoisuutta analysoivilla (performance & load / stress testing tools) työkaluilla voidaan tarkastella esimerkiksi ohjelman tehokkuutta ja simuloida useiden käyttäjien ympäristöjä. Esimerkkeinä SQA LoadTest, SQA, Inc.; PurePerformix, Pure Atria.
Dynaamisen analyysin työkaluihin luetaan kuuluvaksi usein myös testiaineistogeneraattorit (test data generators). Näiden työkalujen tarkoituksena on siis automatisoida testiaineiston luontia. Testitapahtumat johdetaan usein suoraan ohjelman koodista saadun informaation perusteella tai käyttämällä hyväksi valmiita tietokantoja. Esimerkkeinä PiSCES Test Case Generator, Reliable Software Technologies; STW/Advisor, Software Research; TESTBytes, InfoStructures, Inc.
5.3
Testauksen hallinnan työkalut
Testauksen hallinnan työkalut ovat kehitetty automatisoimaan testauksen suunnittelua, analysointia, dokumentointia ja raportointia. Markkinoilla on tarjolla ohjelmia ja ohjelmistoja, joilla luodaan oma ympäristökokonaisuus testauksen hallintaan. Tyypilliset hallintaympäristöt helpottavat testauksen organisointia, analysoivat testituloksia, tekevät yhteenvetoraportteja, lähettävät yhteenvetoja prosessissa mukanaoleville. Esimerkkeinä mainittakoon SQA Manager, Software Quality Automation Inc.; STEPMaster, Software Quality Engineering; TestDirector, Mercury Interactive; T-Plan, Software Quality Assurance Ltd.; QA Plan, Direct Technology; PureTestExpert, Pure Atria.
5.4
Testausohjelmistot ja -järjestelmät
Ohjelmistotestaukseen on kasvavassa määrin tarjolla suurempia testausohjelmistokokonaisuuksia. Testausjärjestelmät kattavat usein lähes kaiken toiminnan aina suunnittelusta projektin loppuun saakka. Suurempien testausjärjestelmien etuna on mm. eri testaus- ja prosessin vaiheiden saumattomuus, prosessin hallinnan helppous, dokumentoinnin yhtenäisyys ja useimmin monen käyttäjän (verkko)tuki.
Suurin haittatekijä suuremmissa testausjärjestelmissä on ainakin tällä hetkellä niiden hinta (hintahaitari tosin on hyvin laaja). Halvimmillaan useamman käyttäjän lisenssien hinnat pyörivät ominaisuuksista (ohjelmista) riippuen kymmenentuhannen ja sadantuhannen markan välissä. Toisaalta kalleimmillaan yhden käyttäjän lisenssi saattaa maksaa lähes satatuhatta markkaa.
Tämän hetken tunnetuin ja kehutuin testausjärjestelmä
Windows-sovelluksille (3.x, 95 ja NT) on Software Quality Automation Inc.:in
(SQA Inc.) SQA Suite 5.0. SQA Suite tukee useimpia olio-orientointuneita
ohjelmointiympäristöjä sekä verkkokäyttöä.
Paketti sisältää testiajurin SQA Robotin, testauksen
hallintaohjelman SQA Managerin sekä rasitustestausohjelman SQA LoadTestin.
Lisäksi esimerkkeinä testausjärjestelmistä voidaan mainita
Direct Technologyn Automator QACenter (Windows-versiot) sekä AZOR
Inc.:in Ferret (mm. Windows, DOS, OS/2, VMS, Nextstep, Unix).
Tässä luvussa lyhyesti esiteltävät työkalut CTB, CTC++ ja CMT++ ovat tamperelaisen Testwell Oy:n testausvälineitä C- ja C++-kielisille ohjelmille. CodeGuard (16/32) on puolestaan Borland C++ 5.01:n mukana toimitettava testausapuväline.
CTB on moduulitestaustyökalu C-kielelle MS-DOSiin (saatavilla myös ainakin muutamiin UNIX-ympäristöihin). Koska C-kielistä moduulia (esimerkiksi yksittäinen funktio) ei itsessään voida suorittaa (eli se ei ole dynaamisesti testattava) on moduuleille luotava jonkinlainen ympäristö, missä testaus tulee mahdolliseksi. CTB:llä voidaan generoida tällainen moduulitestaus-ympäristö testattavalle koodille. Tätä ympäristöä voidaan kutsutaan nimellä test bed. Käytännössä voitaisiin tietenkin kirjoittaa moduuleille oma testiohjelmansa (toimisi siis testiympäristönä) ja debuggerin avulla käydä ohjelmaa esimerkiksi rivi riviltä läpi. CTB kuitenkin tekee tämän ympäristön testaajan puolesta ja antaa käyttöön lisäksi ominaisuuksia, joiden rakentaminen veisi aikaa itse testaukselta.
Parhaiten CTB tukee bottom-up black box-strategiaa. Tukea löytyy myös stub-moduuleille, joten top-down-strategian käyttö on mahdollista. Test bedin sisällä voidaan kutsua funktioita ja tutkia niiden palauttamia arvoja. Myös "omien" muuttujien (test bed variables) varaus on mahdollista test bedissä. Muutujia voidaan käyttää esimerkiksi funktioiden palautusten tutkimiseen ja vertailuun. Lisäksi test bed-ympäristössä voidaan ajaa valmiita testiskriptejä (test scripts, ks. luku 6.1.4).
CTB-generaattorilla luodaan testattavista moduuleista (C-kielinen) testiajuri (test driver). Testiajuri puolestaan käännetään C-kääntäjällä CTB-kirjaston ja lopun moduulien tarvitseman C-koodin kanssa varsinaiseksi test bed-ohjelmaksi.
Kuva 6.1. CTB-generaattorin toiminta.
Moduulien testausympäristöä kutsutaan siis nimellä test bed. CTB:ssä test bed on suoritettava ohjelma, jonka sisään testattavat moduulit on liitetty. Test bediä voidaan käyttää testaukseen interaktiivisesti komentotulkin tapaan tai/sekä valmiita testiskriptejä suorittaen. Test bed sisältää joukon käskyjä, joiden avulla voidaan mm. tarkastella yksittäisiä funktioita ja muuttujia (test bed variables) sekä suorittaa testattavia moduuleita (funktioita).
6.1.4
Testiskriptit (test scripts)
Testiskriptit koostuvat joukosta komentoja, joita test bedissä halutaan suorittaa. Testiskriptit muodostavat siis eräänlaisia ohjelmia tai ohjelman pätkiä joiden avulla testattavien kohteiden input-output käyttäytymistä voidaan analysoida. Kuten jo edellä mainittiin, testiskriptit nopeuttavat testien uudelleen suoritusta esimerkiksi regressiotestauksessa sekä helpottavat testauksen dokumentointia.
6.2
CTC++, Test Coverage Analyzer for C/C++
CTC++ on suoritusaikaisen testauksen kattavuutta analysoiva työkalu C- ja C++-kielisille ohjelmille DOS- ja Windows-ympäristöissä. CTC++:lla saadaan siis tietoa siitä, mitä ohjelman tai funktion osia ohjelman suorituksen aikana on jo käyty tai on käymättä läpi. Lisäksi CTC++:lla on mahdollista paikantaa ohjelman "pullonkauloja" eli esimerkiksi funktioiden ylisuuria suoritusaikoja.
6.2.1
CTC++:n osat ja ominaisuuksia
CTC++ koostuu CTC++-esikääntäjästä, itse testattavasta ohjelmasta sekä CTCPost-jälkikäsittelijästä. Esikääntäjä muokkaa testattavan C/C++-koodin ohjelman kääntämistä ja linkittämistä varten (ohjelman käyttäminen vaatii siis lisäksi C/C++-kääntäjän). CTCPost-ohjelmalla saadaan luotua raportit tuloksista. Sekä CTC++-esikääntäjää että CTCPostia voidaan käyttää joko suoraan komentorivikäskyillä tai interaktiivisesti ohjelman tarjotessa vaihtoehtoja.
Testattavat kooditiedostot muokataan esikääntäjän avulla. CTC++-esikääntäjä muokkaa ja osittain generoi tiedostot, joista monitoriohjelma käännetään. Tuloksena on siis monitoriohjelma, joka toiminnaltaan vastaa "alkuperäistä" testattavaa ohjelmaa. Kuva 6.2 hahmottelee CTC++-esikääntäjän käyttöä.
Kuva 6.2. CTC++-esikääntäjän käyttö.
Kuten kuvasta 6.2 havaittiin, testattava ohjelma käännetään siis muokatusta (testattavasta) koodista, mahdollisesta testauksen ulkopuolelle jätettävästä koodista sekä valmiista CTC++:n mukana tulevasta (eri muistimalleille + Windowsille omat) kirjastotiedostosta. Testattavassa ohjelmassa voi olla osia tai funktioita, joita ei raportoida, eli monitori ei välttämättä käsitä koko testattavaa ohjelmaa. Suoritettaessa testiajoja monitori pitää kirjaa mm. läpikäydyistä funktioista ja funktioiden palautuksista erilliseen datatiedostoon (kuva 6.3). Datatiedosto myös päivittyy aina, kun ohjelmaa ajetaan uudelleen.
Kuva 6.3. Monitorin toiminta testattavan ohjelman suorituksen aikana.
6.2.4
CTCPost
-jälkikäsittelijä
CTCPost-ohjelmalla voidaan tuottaa raportteja monitoriohjelman testiajoista. CTCPost kokoaa monitorin data- ja esikääntäjän symbolitiedostojen (.sym) tiedot taulukoiksi esimerkiksi funktioittain.
Kuva 6.4. CTCPost:n toiminta.
Käyttäjällä on mahdollisuus tuottaa mm. profiili- (profile), yhteenveto- (summary) ja "ajankäyttö"- (timing) raportit testiajon tai -ajojen aikana kerätyistä tiedoista. Profiiliraportti sisältää yksityiskohtaiset tiedot kaikista testattavan ohjelman funktioista ja siitä mitä funktion lauseita on jo käyty läpi. C++-ohjelmille saadaan lisäksi jokaiselle ohjelman luokalle Interface coverage-arvo (ks. luku 6.2.5). Yhteenvetoraportti sisältää tiivistelmän mm. funktioiden ja ohjelman TER-arvoista (ks. luku 6.2.5). Ajankäyttöraportista käy ilmi funktioiden kuluttamat ajat ohjelman suorituksen aikana.
Tuotettujen raporttien avulla käyttäjän on helppo nähdä, mitä funktioita tai funktiotasolla jopa mitä lauseita ei olla saavutettu suoritetuilla testitapauksilla. Ohjelma laskee kaikille funktioille ja koko ohjelmalle TER-arvon (Test Effectiveness Ratio). TER on prosenttiluku, joka kuvaa funktion testauksen kattavuutta. Se siis kuvaa sitä, kuinka suuri osa funktiosta on jo CTC++:n mittauskyvyn mukaan testattu tai käyty läpi. Mitä suurempi TER-arvo funktiolla tai ohjelmalla on, sitä kattavammin se on tullut käsiteltyä. C++-ohjelmien luokille ohjelmalla on mahdollista laskea nk. Interface coverage-arvo. Tämä arvo kuvastaa sitä, miten monta prosenttia luokalle kuuluvista ja periytyvistä funktioista on käyty läpi testiajon tai -ajojen aikana. Ajankäyttöraportit puolestaan antavat funktiokohtaista tietoa siitä, miten paljon aikaa on missäkin käytetty. Mitatut arvot ilmoitetaan oletuksena millisekunteina.
Testattavana esimerkkinä on käytetty CTC++:n mukana tulevaa prime.c -esimerkkiohjelmaa (liite 1). Lähdekoodi siis muokataan ensin CTC++-esikääntäjällä, jonka antamat tiedostot (tässä tiedosto) käännetään ja linkitetään muiden tarvittavien tiedostojen kanssa valmiiksi ohjelmaksi (tässä mon.exe).
Seuraavassa valmiilla testiohjelmalla (monitorilla) mon.exe suoritetut testitapaukset:
6.3
CMT++, Complexity Measures Tool for C/C++
CMT++ on nimensä mukaisesti C- ja C++-koodin kompleksisuuden mittaustyökalu MS-DOSiin (saatavilla myös muutamiin UNIX-ympäristöihin). Mittaustulokset perustuvat jo edellä esiteltyihin Halsteadin ja McCaben ohjelmamittoihin sekä ohjelman omiin lukuihin ohjelmarivien mitoista.
Ohjelmaa voidaan käyttää joko suoraan komentorivikäskyillä tai interaktiivisesti ohjelman antamien vaihtoehtojen kautta. Käyttäjä voi valita joko lyhyen tiivistelmän tai tarkemmin eritellyn raportin analysoitavien tiedostojen koodista. Tarkempi erittely tulostaa kaikki laskettaviin tuloksiin tarvittavat muuttujat, esimerkiksi operaattorit, operandit ja niiden lukumäärät. Lyhyemmässä raportissa ohjelma kokoaa mittaustulokset taulukkoon, josta käy ilmi ne koodin kohdat, esimerkiksi funktiossa, joissa ilmeni rajojen ylityksiä. Nämä mittausrajat on mahdollista määritellä itse ohjelman INI-tiedostoon. Muutamia oletuksena olevia mittausvälejä on lueteltu luvussa 6.3.3.
Ohjelmalla voidaan laskea C- ja C++ -tiedostoista mm. seuraavia tuloksia:
Seuraavat hyväksyttävät mittausvälit perustuvat ohjelman mukana toimitettavaan manuaaliin.
Funktion pituuden tulisi olla 4 - 40 ohjelmariviä. Pidemmät funktiot voidaan yleensä jakaa pienempiin, joka parantaa luettavuutta. Yhden tiedoston pituuden tulisi olla alle 400 ohjelmariviä. Pitempien ymmärtäminen kokonaisuutena voi olla vaikeaa. Funktion v(G) tulisi olla alle 15. Yli 15 ohjelmapolkua sisältäviä funktioita on vaikea ymmärtää ja testata. Arvioitujen virheiden (B) lukumäärä tiedostossa tulisi olla alle 2
Kuten aiemmin ohjelmamittojen kohdalla todettiin, niin tulokset ovat luonnollisesti vain suuntaa antavia. Ne eivät siis anna mitään varmaa tietoa siitä, että ohjelma sisältää tai ei sisällä virheitä.
Testattavana esimerkkinä on käytetty liitteen 1 prime.c -ohjelmaa. Kyseisestä tiedostosta saatu tiivistetty raportti on esitetty liitteessä 3.
CodeGuard 16/32 on Borland C++ 5.01 mukana toimitettava ohjelman suoritusaikaista käyttäytymistä analysoiva työkalu. CodeGuardin avulla voidaan havainnoida joitakin virheitä, joita kääntäjä ei ole havainnut. CodeGuard tukee Borlandin kääntäjillä (joissa CodeGuard -tuki) luotuja 16 ja 32-bittisiä Windows- ja EasyWin-sovelluksia.
CodeGuardin avulla voidaan paljastaa lähinnä muistin ja funktioiden käyttöön liittyviä virheitä. CodeGuard raportoi esimerkiksi muistin varauksiin, vapauttamisiin, viittauksiin ja kahvoihin liittyvistä väärinkäytöksistä sekä funktioiden palautuksiin liittyvistä virheistä ja ongelmista. CodeGuardin havaitessa virheen, se ilmoittaa (haluttaessa) virheestä omassa viesti-ikkunassa, sekä tulostaa havaitut väärinkäytökset omaan lokitiedostoon (.cgl). Virheiden raportointiin voi itse vaikuttaa CodeGuardin konfigurointiohjelman (CodeGuard Configuration Utily) avulla.
CodeGuard saadaan käyttöön halutulle sovellukselle kääntämällä tarkoitukseen soveltuva CodeGuard -kirjastotiedosto ko. sovelluksen kanssa. Yksinkertaisimmin tämä tapahtuu valitsemalla TargetExpertissä CodeGuard-optio päälle ennen kääntämistä. Tämän jälkeen CodeGuard toimii automaattisesti ohjelmaa suoritettaessa.
Liitteessä 4 on lyhyt esimerkki siitä, miten CodeGuard raportoi
havaitsemistaan virheistä.
Borland International, Inc., CodeGuard (16/32) avustustiedosto (cg5.hlp), 1996
[HAL77] Maurice H. Halstead, Elements of Software Science, 1977
H&P Software Engineering Center Oy, Swengineering 3 / 1996
Richard A. DeMillo, W. Michael McCracken, R. J. Martin & John F. Passafiume, Software Testing and Evaluation, 1987
Petri Kuusela, Module Testing and Protocol Testing in Software Quality Assurance, http://www.to.icl.fi/~kuusela/mt
[PRE92] Roger S. Pressman, Software Engineering, A Practitioner's Approach, 3rd edition, 1992
SoFine, Suomalaisen ohjelmistoviennin kehitysohjelma, osa 8: Ohjelmistotestaus, http://www.sofine.hut.fi/vienti/ohjelmistovienti/osa8.html
[SOM95] Ian Sommerville, Software Engineering, 5th edition, 1995
ST Labs, Testing Tools Supplier List, http://www.stlabs.com/MARICK/faqs/tools.htm
Testwell Oy, CTB-, CTC++- ja CMT++ -manuaalit, 1995
[TIE96] Tietoviikko, "Sovellusprojekteissa tyritään miljardeja",
numero 33, 1996
#include <stdio.h> /* C.f. CTC++ 4.0 manual Tutorial Example */ #include <stdlib.h> int prime(unsigned u) { unsigned divisor; if (u == 1) return 0; if (u == 2) return 1; if (u % 2 == 0) return 0; for (divisor = 3; divisor < u / 2; divisor += 2) { if (u % divisor == 0) return 0; } return 1; } int main (void) { unsigned prime_candidate; printf("Enter a number: "); scanf("%u", &prime_candidate); if (prime(prime_candidate)) printf("%u is a prime.\n", prime_candidate); else printf("%u is not a prime.\n", prime_candidate); return 0; }
**************************************************************************** * CTC++ System, Version 4.0 * * Test Coverage Analyzer for C/C++ * * * * EXECUTION PROFILE LISTING * * * * Copyright (c) 1993-1995 Testwell Oy * * Base Technology Copyright (c) 1988-1992 ICL Personal Systems Oy * **************************************************************************** Monitor name : MON Monitor generated at : Tue Nov 05 12:43:08 1996 Listing produced at : Tue Nov 05 12:45:24 1996 MONITORED SOURCE FILE : prime.c INSTRUMENTATION MODE : function-decision START/ END/ TRUE FALSE LINE DESCRIPTION ============================================================================ 4 0 5 FUNCTION prime() 1 3 8 if (u == 1 ) 1 9 return 0 0 3 - 10 if (u == 2 ) 0 - 11 return 1 1 2 12 if (u % 2 == 0 ) 1 13 return 0 99 2 14 for (;divisor < u / 2 ;) 0 99 - 17 if (u % divisor == 0 ) 0 - 18 return 0 2 20 return 1 ***TER 75 % (12/16) of FUNCTION prime() ---------------------------------------------------------------------------- 4 0 24 FUNCTION main() 2 2 29 if (prime ( prime_candidate ) ) 32 else 4 35 return 0 ***TER 100 % (4/4) of FUNCTION main() ---------------------------------------------------------------------------- ***TER 80 % (16/20) of SOURCE FILE prime.c ---------------------------------------------------------------------------- SUMMARY ======= Number of monitored source files : 1 Number of source lines : 38 Number of measurement points : 16 TER : 80%
**************************************************************************** * CMT++ System, Version 1.6 * * Complexity Measures Tool for C/C++ * * * * COMPLEXITY MEASURES REPORT * * * * Copyright (c) 1993-1995 Testwell Oy * * Base Technology Copyright (c) 1988-1992 ICL Personal Systems Oy * **************************************************************************** This report was produced at Sun Nov 03 18:40:55 1996 Measured source file: prime.c Measured object v(G) LOCpro LOCcom V B T ============================================================================ prime() 6 17 0 259.15 0.10 00:04:28 main() 2 12 0 171.30 0.03 00:00:55 prime.c 7 31 1- 523.19 0.16 00:09:59 ============================================================================ SUMMARY: Measure Alarmed Measured % Limits ============================================================================ Cyclomatic number of function : 0 2 0 1 15 Cyclomatic number of file : 0 1 0 1 100 Executable lines in function : 0 2 0 4 40 Executable lines in file : 0 1 0 4 400 Comment ratio of file (%) : 1 1 100 30 75 Volume of function : 0 2 0 20 1000 Volume of file : 0 1 0 100 8000 Delivered bugs in file : 0 1 0 0 2 ============================================================================ Total : 1 11 9
// cg_esim.c // Järjetön esimerkkiohjelma CG:n virheraportointia varten #include<stdlib.h> int main(void) { int *a,*b; // varataan a:lle muistia ja vapautetaan samantien a=(int *) malloc(sizeof(int)*10); free(a); // varataan b:lle muistia b=(int *) malloc(sizeof(int)*2); // käytetään a:ta vaikka se on jo vapautettu if (a[9]) b[0]=0; // jätetään b vapauttamatta return 0; }
Error 00001. 0x100630 (Thread 0xFFF9C569): Access in freed memory: Attempt to access 4 byte(s) at 0x00B322A4+36. | cg_esim.c line 9: | free(a); | b=(int *) malloc(sizeof(int)*2); |> if (a[9]) b[0]=0; | return 0; | } Call Tree: 0x004010B0(=CG_ESIM.EXE:0x01:0000B0) cg_esim.c#9 0x00406C11(=CG_ESIM.EXE:0x01:005C11) The memory block (0x00B322A4) [size: 40 bytes] was allocated with malloc | cg_esim.c line 6: | { | int *a,*b; |> a=(int *) malloc(sizeof(int)*10); | free(a); | b=(int *) malloc(sizeof(int)*2); Call Tree: 0x00401094(=CG_ESIM.EXE:0x01:000094) cg_esim.c#6 0x00406C11(=CG_ESIM.EXE:0x01:005C11) The memory block (0x00B322A4) was freed with free | cg_esim.c line 7: | int *a,*b; | a=(int *) malloc(sizeof(int)*10); |> free(a); | b=(int *) malloc(sizeof(int)*2); | if (a[9]) b[0]=0; Call Tree: 0x0040109D(=CG_ESIM.EXE:0x01:00009D) cg_esim.c#7 0x00406C11(=CG_ESIM.EXE:0x01:005C11) ------------------------------------------ Error 00002. 0x300010 (Thread 0xFFF9C569): Resource leak: The memory block (0xB322D0) was never freed The memory block (0x00B322D0) [size: 8 bytes] was allocated with malloc | cg_esim.c line 8: | a=(int *) malloc(sizeof(int)*10); | free(a); |> b=(int *) malloc(sizeof(int)*2); | if (a[9]) b[0]=0; | return 0; Call Tree: 0x004010A5(=CG_ESIM.EXE:0x01:0000A5) cg_esim.c#8 0x00406C11(=CG_ESIM.EXE:0x01:005C11)