Precedente Successivo Indice

5. Collaudi

Vista l'organizzazione del server, dapprima sono stati implementati nel server solo alcuni elementari comandi (QUOTE e MOVE, principalmente) e con questi si è cominciato a testare il funzionamento del server e del client. Buona parte dei test iniziali sono stati comunque eseguiti con un semplice telnet localhost 4444 e dando i comandi a mano. Queste semplici operazioni ci hanno permesso di controllare la corretta implementazione, verificando che ad ogni comando si otteneva la corretta risposta.

Un salto di qualità è stato lo scrivere l'applicazione java Test; questa non è altro che una vecchia versione di server adattata per funzionare come un client. Test lancia una serie di thread che non fanno altro che inviare al server, in maniera casuale, una serie di comandi o compiere una serie di operazioni, in particolare:

NAME

può essere inviato un comando NAME (che abilita un thread), e ovviamente può capitare che venga inviato due volte. Questo può generare un errore

QUOTE

un comando quote valido; di solito non genera mai errori, mal che vada viene ignorato.

MOVE

una mossa (totalmente random) viene inviata; ovviamente anche qui, solitamente, solo una mossa per partita (al meglio) sarà valida, ovvero la prima

START

il comando start; valgono i discorsi fatti per NAME

SHIT

scrive una stringa che genera un parser error

Suicidio

il thread si suicida, chiudendo le connessioni

Questa è praticamente tutta la casistica di comandi che può arrivare ad un server, e il fatto che arrivino a velocità alte, e soprattutto il forzare morti improvvise, ci è stato molto di aiuto nel scoprire insidiosi bugs e problemi di temporizzazioni. In particolare questo programmino ci ha permesso di scovare facilmente tutti gli errori dovuti a errati controlli sulla morte dei thread, tutti i problemi dovuti alle temporizzazioni e tutti gli insidiosi errori dovuti allo svuotamento del buffer post-mortem; questo errore, in particolare, ci ha fatto impazzire.

Accadeva che un client (o Test) scriveva su un socket molte cose, e poi moriva. Il server, magari eseguendo azioni in risposta al primo comando, non riusciva a scrivere nel socket (che giustamente era stato chiuso) e quindi disabilitava il thread; ma questo comunque svuotava il buffer, mandando comandi ugualmente. Il fatto che nel metodo run() di ogni ServerThread ci si fermi sulla readline() dal socket ci impediva di uscire immediatamente alla chiusura del socket e, nel caso peggiore, ma molto comune, ancora un comando da un thread morto veniva interpretato. Siamo stati costretti ad aggiungere un controllo ulteriore sul puntatore al ServerThread per avere la certezza di non considerare comandi da thread morti. Alla fine, dopo aver aggiunto condizioni qua e la, abbiamo preferito (anche per uniformità) creare un metodo che ritornasse lo stato del thread (considerando il suo numero di giocatore e il puntatore al suo thread) e su questo fare un case. Si è rivelata la scelta vincente.

Ovviamente questo ci ha garantito che il server non faccia un crash in tutte le condizioni di lavoro, ma non ci ha garantito che esegua, logicamente, le operazioni per cui l'avevamo scritto. Per questo non ci è rimasto che... giocare! ;) In particolare sono state testate le fasi di inizio e fine partita, e le condizioni critiche (morte di un giocatore a partita non iniziata e a partita iniziata).

Tutto ci è sembrato secondo specifiche, perfettamente funzionante.


Precedente Successivo Indice