2 - Testing & Debugging
2 - Testing & Debugging
Laboratorio di Informatica
i bug
Bug = errore sintattico o semantico presente
in un programma
Debug = processo di riconoscimento e
rimozione dei bug
Attenzione:
Esempio
Questo programma tenta di leggere una
sequenza di caratteri da un file e li memorizza
in un array fino a quando viene letta una
newline o si raggiunge la dimensione massima
MAX>0
int i = 0;
char s[MAX];
while ((s[i] = fgetc(file)) != '\n' && i < MAX-1)
i++;
s[--i] = '\0';
9
Esempio
Quali sono le condizioni limite?
1. L'input vuoto (\n)
2. MAX == 1
3. L'input ha una lunghezza pari a MAX
4. L'input ha una lunghezza maggiore di MAX
5. L'input non contiene una newline (se
possibile)
10
Esempio
Riscrivendo il codice usando uno stile pi
leggibile, si eviterebbe l'errore del codice
precedente
int i = 0;
char s[MAX];
int stop = 0;
while (!stop && i < MAX) {
s[i] = fgetc(file);
stop = (s[i] == '\n');
i++;
}
s[--i] = '\0';
11
Esercizio
/**
* Restituisce la media aritmetica di un array.
*
* @param a double di len_a elementi; len_a>0
* @return media aritmetica degli elementi dell'array
*/
double avg(double a[], int len_a) {
int i;
double sum = 0.0;
for (i=0; i < len_a; i++) {
sum += a[i];
}
return sum / len_a;
}
12
Programmazione difensiva
Aggiunta di codice per casi impossibili
if (age < 0 || age > MAX_AGE) {
range = "???";
} else if (age <= 18) {
range = "Teenager"
} ...
13
14
Testing sistematico
Verifica incrementale
Verifica bottom-up
15
Verifica incrementale
Test di pari passo con l'implementazione
Test di unit elementari
16
Verifica bottom-up
Testare prima:
5 combinazioni possibili
17
Testare un compilatore
18
Verificare la copertura
dei test
I test applicati devono garantire che ogni
istruzione del programma sia eseguita
almeno una volta
Rami then-else
Tutti i case di una switch
Esecuzione dei cicli
Classi di equivalenza
19
CUnit
introduzione
Unit test
Cos?
A cosa serve?
Unit software?
Come si fa?
21
CUnit
Framework di unit test per il linguaggio C
Home page: http://cunit.sourceforge.net/index.html
Libreria che va inclusa in ogni progetto
Eclipse CDT che intende avvalersene
Guida di installazione disponibile qui:
http://collab.di.uniba.it/fabio/guide/
23
24
26
Procedure e Funzioni in
C
un breve ripasso
28
Procedure e funzioni
Sono istruzioni non primitive per risolvere
parti specifiche di un problema: i
sottoprogrammi (o metodi)
Sono realizzate mediante la definizione di
unit di programma (sottoprogrammi)
distinte dal programma principale (main)
Rappresentano nuove istruzioni/operatori
che agiscono sui dati utilizzati dal
programma
Sono definite a partire da una sequenza di
istruzioni primitive e altre procedure/funzioni
29
Sottoprogrammi: funzioni e
procedure
Tutti i linguaggi di alto livello offrono la
possibilit di utilizzare funzioni e procedure
mediante:
Astrazione
Riusabilit
Modularit (strutturazione)
Leggibilit
30
Funzioni e procedure:
definizione
Nella fase di definizione di una funzione o
procedura si stabilisce:
Un identificatore del sottoprogramma
Esempio di funzione e
procedura in C
Funzione
Procedura
// prototipo
int sum(int a, int b);
// prototipo
void print(int a);
// dichiarazione
int sum(int a, int b) {
int c = a + b;
return c;
}
// dichiarazione
void print(int a) {
printf("%d", a);
}
// chiamata
int main(void) {
int op1, op2, result;
// chiamata
int main(void) {
int value;
print(value);
}
32
Funzioni e procedure:
differenze
Funzione
Operatore non
primitivo
Permette di definire
nuovi operatori
complessi da
affiancare a quelli
primitivi
Restituisce un valore
di ritorno (mediante
return)
Procedura
Istruzione non
primitiva
E attivabile in un
qualunque punto del
programma in cui
pu comparire
unistruzione
Non restituisce un
valore di ritorno
(mediante return)
33
Funzioni e procedure:
parametri
I parametri costituiscono il mezzo di
comunicazione tra unit chiamante e unit
chiamata
Si differenziano in
Parametri formali e
parametri attuali
Parametri formali
Sono quelli specificati
nella definizione del
sottoprogramma
Sono in numero
prefissato e a ognuno
di essi viene
associato un tipo
Le istruzioni del
corpo del
sottoprogramma
utilizzano i parametri
formali
Parametri attuali
Sono i valori
effettivamente forniti
dallunit chiamante
al sottoprogramma
allatto della
invocazione
Per valore
Per indirizzo (o riferimento) lo vedremo pi avanti
. Di ritorno a CUnit .
38
#include <stdio.h>
#include <stdlib.h>
#include CUnit/Basic.h
39 39
Significato
CU_ASSERT(int espressione)
CU_TEST(int espressione)
CU_ASSERT_TRUE(valore)
CU_ASSERT_FALSE(valore)
CU_ASSERT_EQUAL(reale, atteso)
CU_ASSERT_NOT_EQUAL(reale, atteso)
CU_ASSERT_STRING_EQUAL(reale, atteso)
CU_ASSERT_STRING_NOT_EQUAL(reale,
atteso)
se n = 0
n! =
void test_factorial(void) {
n(n-1)!
se n > 1
// fallisce
CU_ASSERT_EQUAL(factorial(4), 12 );
CU_ASSERT(factorial(3) == 6);
CU_TEST(factorial(6) == 720);
}
Il Test Registry
Raccoglie tutte le test suite
Quando si esegue un Test Registry si
eseguono tutte le suite al suo interno e, di
conseguenza, tutti i test method allinterno
delle suite
Inizializzazione del
Test Registry
Test Suite
Una test suite definita da:
49
Inizializzazione e pulizia
delle suite
Le test suite devono essere inizializzate e
ripulite prima e dopo luso
Perch?
50
Inizializzazione e pulizia
// Alloca tutte le risorse necessarie allesecuzione
// dei test
int init_suite_default(void) {
return 0; // tutto ok
}
// dealloca tutte le risorse allocate allinizializzazione
int clean_suite_default(void) {
return 0; // tutto ok
}
51
Registrazione ed
esecuzione dei test
La procedura CU_basic_run_tests esegue tutte
le suite del registry e mostra i risultati
Pulire il registry
Pulizia dopo aver eseguito tutti i test nel
registro
Il
Esercitazione 1
CUnit
Link allesercitazione
http://goo.gl/VYfhsN
Debugging
59
60
61
62
Backward reasoning
Quando si scopre un bug, occorre pensare
al contrario
63
Pattern familiari
Riconoscere variazioni rispetto a modelli
(pattern) di codice familiari
int n;
scanf("%d", n);
int n;
scanf("%d", &n);
64
Sviluppo incrementale
Testare le procedure man mano che
vengono sviluppate
65
66
68
Rendere riproducibile un
bug
Individuare tutte le condizioni che portano
alla manifestazione di un bug
69
Divide et impera
Individuare le condizioni minimali che
rendono manifesto un bug
70
Ricerca di regolarit
Alcuni bug si presentano con regolarit, ma
non sempre
In questo caso, occorre capire il modello
(pattern) che genera la regolarit
Stampe ausiliarie
Per seguire lesecuzione di un programma
pu essere utile introdurre stampe ausiliarie
72
Altre tecniche
Visualizzazioni grafiche
Test statistici
Strumenti di analisi di testo
grep
diff
...
73
Debugger
Un debugger guarda dentro il programma
durante lesecuzione
Tracing del programma
Visualizzazione del contenuto delle variabili
Valutazione dinamica di espressioni
Breakpoint, anche condizionali
Stack trace
Debug
Release
Ottimizzata
75
76
Esecuzione passo-passo
Il debugger consente di eseguire il
programma una istruzione alla volta
Esecuzione passo-passo
Step into
Esegue listruzione
corrente, e procede
allistruzione
successiva che sar
effettivamente eseguita
Step over
Esegue listruzione
corrente, trattando le
routine come istruzioni
primitive.
continua lesecuzione
Step return fino al termine della
procedura.
78
Step Into
79
Step over
80
Step return
81
Informazioni di debug:
variabili
Le variabili visibili
nellambito
dellistruzione
corrente sono
visualizzate
Le variabili che
cambiano valore
sono evidenziate
82
Informazioni di debug:
espressioni
Unespressione un
pezzo ben formato
di codice (snippet)
che pu essere
valutato per
produrre un risultato
83
Informazioni di debug:
stack trace
Visualizza la pila
delle chiamate
Si pu selezionare
un elemento della
pila per conoscerne
lo stato corrente
84
Breakpoint
Un breakpoint interrompe il flusso di
esecuzione su una linea selezionata
85
Terminate
Breakpoint condizionali
I breakpoint possono interrompere
lesecuzione solo quando una condizione
diventa vera
87