Please consider the following piece of code:
-- Represents a parsing result of an ANSI coded string.
data Slice = Slice
{ text :: String,
color :: Color
}
newtype Color = Color
{ string :: String
}
-- A function that receives a string with ANSI esacpe codes and returns a list of slices.
categorize:: String -> [Slice]
categorize codedString = ...
Now, I wish to write a quickcheck property for the categorize function.
I have something like this in mind:
-- A quickcheck generator for ANSI coded strings.
ansiEscapeStrings :: Gen String
ansiEscapeStrings = ...
main =
verboseCheck $
forAll
ansiEscapeStrings
(\codedString -> categorize codedString == WHAT_GOES_HERE)
My question is what goes instead of WHAT_GOES_HERE?
Thanks in advance.
UPDATE:
I already wrote properties for trivial things like length and empty list.
With QuickCheck, you should identify some rules you think should hold for all possible input/output pairs. It's rather difficult to do this if your concept of input is just "an arbitrary string to pass to the function" and the output is "the slices the function returns". At this level of abstraction, the only rule you can really write is "the function should produce the slices represented by the input string" - of course, you can't test that directly, because if you had a known-good conversion from input to output you'd just use that instead.
Instead, try some thinking at a more granular level. What are some ways you think this function should behave, given certain properties of the input? Here are some I can think of.
textstrings in an output should be no larger than the size of the input.textstrings in an output should be present somewhere in the input.ncolor-change sequences, there should benslices in the output. Or is itn-1slices? How are you planning to represent "text preceding any color-change sequences"? And what about if there are two color-change sequences in a row, with no text between them? Already, thinking in terms of properties to test has us finding important edge cases in the design.These properties need varying levels of precision to test them. For (1), you don't even need QuickCheck: you can just unit-test the single, empty input. For (2) and (3), you could just pass an arbitrary string as input and compare the input and output data. But QuickCheck's arbitrary string generator likely won't produce many strings with meaningful ANSI escape sequences in them. So you will probably want to define
stringWithEscapeSequences :: Gen Stringor something like that, to ensure your test inputs are interesting.But (4) is more complicated. You could take an arbitrary string as input, scan it for escape sequences, and then compare that to the number of slices produced by
categorize. But that requires including a lot of your function's implementation into the test, and a bug in your function could easily be mirrored by a bug in your test. A less fragile approach would be to write a data type for test cases, with a generator for that type, so that you know how many slices to expect from each test case ahead of time. Something likeThere are numerous spots in that example for you to fill in based on your domain knowledge:
colorandnonEscapeCharacterare all generators, whileescapeSequenceis an ordinary function of typeColor -> String.You could design another, similar property using many of the same combinators: given a list of slices as input, you should be able to encode it as a string, and
categorizeon that string should give you back the same result you started withHere are some ideas for other properties you could test, without details on how to test them. Try to think small to come up with properties: you need something you can easily describe and verify.
sdecodes top. Choose an indexnin[0..length s], and insert an arbitrary non-escape characterxat positionninsyieldings'. Decodings'should yield a list of slices much likep, except with a single extraxat thenth position.In general, a fruitful avenue to explore is the theme in (3): Take an input and its corresponding output, perturb the input in some well-defined way, and observe that this changes the output in the expected way.