2012-10-19 11 views
7

Mi piacerebbe scrivere un test per un parser Parsec. Ecco l'esempio di parser e dati di struttura:Haskell - Test Parsec con l'aiuto di QuickCheck

data Event = Event { keyEvent :: String } 
    deriving Show 

parseKey :: Parser Event 
parseKey = do 
      char '<' 
      k <- many1 (letter <|> digit <|> oneOf "_") 
      char '>' 
      return $ Event k 

so che ho bisogno di controllare la proprietà parse = parse . pretty . parse. Ma come dovrei generare il corretto e casi di test errati? In generale, come devo generare casi di test per BNF? So di instance Arbitrary, ma non molto aiuto da questo.

Sarei grato se fornisci un esempio ben commentato di generatore per questo semplice parser.

+2

Per i casi di test corretto si verificava 'id = parse. bello? essere abbastanza buono? (Inoltre, 'oneOf" _ "== char '_'') – huon

+0

Penso che sarebbe abbastanza, sì! (Inoltre, la prima versione era molto più strana). – m0nhawk

+2

@dbaupp: Ho pensato a questo e no, considera un semplice esempio quando ci sono 'spazi 'o altri caratteri ignorabili nel parser, quindi, il risultato prodotto sarebbe diverso. E un ulteriore "parse" aiuterebbe. – m0nhawk

risposta

12

Il test dei parser non è del tutto banale. (A seconda della complessità della cosa, come con tutti i test.)

Una cosa che puoi fare è scrivere un'istanza Arbitrary che costruisce espressioni tutti validi (o quello che è si sta cercando di analizzare), e quindi controllare che se si stampa la cosa e poi si analizza la stringa risultante, si ottiene esattamente ciò che è stato avviato.

ci sono un paio di problemi con quello:

  • Se la risposta è sbagliata, ciò che è rotto? Il parser o la stampante?

  • Se la cosa che stai analizzando è abbastanza complicata da avere parentesi opzionali e cose, è necessario controllare che funzioni sia con che senza le parentesi opzionali. La tua bella stampante lo farà solo in un modo o nell'altro, di solito.

  • Ciò non controlla che l'input non valido venga rifiutato (e non analizzato come qualcosa di strano). Ad esempio, ho scritto un sacco di parser Parsec che ignorano silenziosamente un errore di sintassi se si verifica allo fine dell'input.

In generale, l'unico veramente approfondita modo che conosco di testare un parser è quello di scrivere solo un sacco di test manuali a mano. (E ogni volta che trovi qualcosa che analizza male, aggiungi un altro test case per questo). Questo è essenzialmente ciò che GHC fa con la loro tuta, per esempio.

Ovviamente, dipende da quanto è complesso il parser e da quanta certezza si desidera ... Se si sta solo analizzando JSON, è probabile che lo si verifichi abbastanza facilmente. Se stai analizzando qualcosa come Markdown ... il mio Dio abbi pietà della tua anima!

+0

Sto scrivendo il parser di [XCompose] (http://www.unix.com/man-page/all/5/XCompose/), se interessato [qui] (https://github.com/m0nhawk/ XComposeChecker) sono le fonti. Quindi, creare QuickCheck per questo è logico? Oppure i test scritti a mano sarebbero migliori? E quale biblioteca può essere proposta per il secondo caso? – m0nhawk

+0

Ovviamente puoi _tare_ di queste cose. Utilizzare QuickCheck per verificare che il round-trip da AST a stringa su AST funzioni e scrivere alcuni test manuali per verificare i casi limite.Sembra una grammatica abbastanza piccola, quindi non dovrebbe essere troppo difficile da testare ... – MathematicalOrchid