-1

I have a simple function set of converse functions (simple shift codes):

  encode, decode :: Int -> String -> String

and they test fine by some simple manual tests, but a quickCheck test reports a failure:

*** Failed! Falsifiable (after 8 tests and 4 shrinks):
"\254"
0

But running this same test by hand works fine:

   *Main> decode 0 ( encode 0 "\254")
   "c"

I'm not asking why it fails (that is something I'll chase down), but why does quickCheck fail when the (seemingly) same test by hand works?

I suspect it has to do with the character encoding, one decoding it and the other treating it as a String, but am not sure why.


This question is about how the testing works, not (yet!) the function or why it fails, but here is the code:

import Data.Char
let2int c =  ord c - ord 'a'

int2let n =  chr (ord 'a' + n)

shift :: Int -> Char -> Char
shift n c | isLower c   =  int2let ((let2int c + n) `mod` 26)
          | otherwise   =  c

encode, decode :: Int -> String -> String
encode n xs  = [shift n x | x <- xs]
decode n     = encode (-n)

Ref: Hutton, 2007

quickCheck $ (\s n-> (decode n $ encode n s) == s)
guthrie
  • 4,529
  • 4
  • 26
  • 31

1 Answers1

1

You are providing insufficient information - next time please just give the full test and the full code. There is no magic involved and no good reason for the expression in your test when applied to the output from quickCheck to give anything other than a failing result.

From your code I figured your test is decode 0 . encode 0 ~ id. This is not true, just look at the output from ghci:

*Main Test.QuickCheck> quickCheck (\x -> x == decode 0 (encode 0 x))
*** Failed! Falsifiable (after 13 tests and 4 shrinks):    
"\244"
*Main Test.QuickCheck> decode 0 (encode 0 "\244")
"r"
*Main Test.QuickCheck> "\244" == "r"
False
Thomas M. DuBuisson
  • 64,245
  • 7
  • 109
  • 166
  • Thanks, I was confused by the "\254" notation, and how it got generated. I need to create a sanitized printable strings generator for quickCheck. – guthrie Jan 06 '14 at 20:28
  • You don't need a new generator, you could just filter the inputs from the current `Arbitrary` instance: `(\x -> let y = filter isAscii x in y == decode 0 (encode 0 y))`. – Thomas M. DuBuisson Jan 06 '14 at 23:53
  • Yes, but i found that I get so many filtered blocks that it is very slow, and then can fail due to too many discarded test values. (I used a filter of (\c -> (isAlpha c) ==> ... ). From that I figured I needed a safeChar generator, and that worked well. Thanks. – guthrie Jan 07 '14 at 03:14