-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Build and evaluate trees of predicates -- -- Build and evaluate trees of predicates. For example, you might build a -- predicate of the type Int -> Bool. You do this by assembling -- several predicates into a tree. You can then verbosely evaluate this -- tree, showing why a particular result is reached. -- -- prednote also provides modules to test several subjects against a -- given predicate, and to parse infix or RPN expressions into a tree of -- predicates. @package prednote @version 0.10.0.0 -- | Trees of predicates. -- -- Exports names which conflict with Prelude names, so you probably want -- to import this module qualified. module Data.Prednote.Pdct type Label = Text -- | A tree of predicates. data Pdct a Pdct :: Label -> (Node a) -> Pdct a data Node a -- | None of the Pdct in list may be Just False. An empty list or list with -- only Nothing is Just True. And :: [Pdct a] -> Node a -- | At least one of the Pdct in the list must be Just True. An empty list -- or list with only Nothing is Just False. Or :: [Pdct a] -> Node a -- | Just True is Just False and vice versa; Nothing remains Nothing. Not :: (Pdct a) -> Node a -- | Just True if the child is Just True; Nothing otherwise. NeverFalse :: (Pdct a) -> Node a -- | Just False if the child is Just False; Nothing otherwise. NeverTrue :: (Pdct a) -> Node a -- | An operand may return Just True or Just False to indicate success or -- failure. It may also return Nothing to indicate a discard. Operand :: (a -> Maybe Bool) -> Node a -- | Renames the top level of the Pdct. The function you pass will be -- applied to the old name. rename :: (Text -> Text) -> Pdct a -> Pdct a -- | Returns a tree that is always True. always :: Pdct a -- | Returns a tree that is always False. never :: Pdct a -- | Creates a new operand. The Pdct is Just True or Just False, never -- Nothing. operand :: Text -> (a -> Bool) -> Pdct a and :: [Pdct a] -> Pdct a or :: [Pdct a] -> Pdct a not :: Pdct a -> Pdct a -- | Turns an existing Pdct to one that never says False. If the underlying -- predicate returns Just True, the new Pdct also returns Just True. -- Otherwise, the Pdct returns Nothing. neverFalse :: Pdct a -> Pdct a -- | Turns an existing Pdct to one that never says True. If the underlying -- predicate returns Just False, the new Pdct also returns Just False. -- Otherwise, the Pdct returns Nothing. neverTrue :: Pdct a -> Pdct a -- | Forms a Pdct using and. (&&&) :: Pdct a -> Pdct a -> Pdct a -- | Forms a Pdct using or. (|||) :: Pdct a -> Pdct a -> Pdct a -- | Given a function that un-boxes values of type b, changes a Pdct from -- type a to type b. boxPdct :: (b -> a) -> Pdct a -> Pdct b -- | Given a function that un-boxes values of type b, changes a Node from -- type a to type b. boxNode :: (b -> a) -> Node a -> Node b -- | How many levels of indentation to use. Typically you will start this -- at zero. It is incremented by one for each level as functions descend -- through the tree. type Level = Int -- | The number of spaces to use for each level of indentation. type IndentAmt = Int type ShowDiscards = Bool -- | Shows a Pdct tree without evaluating it. showPdct :: IndentAmt -> Level -> Pdct a -> [Chunk] -- | Evaluates a Pdct. eval :: Pdct a -> a -> Maybe Bool -- | Verbosely evaluates a Pdct. evaluate :: IndentAmt -> ShowDiscards -> a -> Level -> Pdct a -> (Maybe Bool, [Chunk]) -- | Filters a list of items by including only the ones for which the Pdct -- returns Just True. Also, renames each top-level Pdct so that the -- textual results include a description of the item being evaluated. filter :: IndentAmt -> ShowDiscards -> Level -> (a -> Text) -> Pdct a -> [a] -> ([a], [Chunk]) -- | Build a Pdct that compares items. compareBy :: Text -> Text -> (a -> Ordering) -> Ordering -> Pdct a -- | Like compareBy but allows the comparison of items that may fail -- to return an ordering. compareByMaybe :: Text -> Text -> (a -> Maybe Ordering) -> Ordering -> Pdct a greaterBy :: Text -> Text -> (a -> Ordering) -> Pdct a lessBy :: Text -> Text -> (a -> Ordering) -> Pdct a equalBy :: Text -> Text -> (a -> Ordering) -> Pdct a greaterEqBy :: Text -> Text -> (a -> Ordering) -> Pdct a lessEqBy :: Text -> Text -> (a -> Ordering) -> Pdct a notEqBy :: Text -> Text -> (a -> Ordering) -> Pdct a -- | Overloaded version of compareBy. compare :: (Show a, Ord a) => Text -> a -> Ordering -> Pdct a greater :: (Show a, Ord a) => Text -> a -> Pdct a less :: (Show a, Ord a) => Text -> a -> Pdct a equal :: (Show a, Ord a) => Text -> a -> Pdct a greaterEq :: (Show a, Ord a) => Text -> a -> Pdct a lessEq :: (Show a, Ord a) => Text -> a -> Pdct a notEq :: (Show a, Ord a) => Text -> a -> Pdct a -- | Parses a string to find the correct comparer; returns the correct -- function to build a Pdct. parseComparer :: Text -> (Ordering -> Pdct a) -> Maybe (Pdct a) instance Show (Pdct a) -- | Postfix, or RPN, expression parsing. -- -- This module parses RPN expressions where the operands are predicates -- and the operators are one of and, or, or -- not, where and and or are binary and -- not is unary. module Data.Prednote.Expressions.RPN type Error = Text data RPNToken a TokOperand :: (Pdct a) -> RPNToken a TokOperator :: Operator -> RPNToken a data Operator OpAnd :: Operator OpOr :: Operator OpNot :: Operator pushOperand :: Pdct a -> [Pdct a] -> [Pdct a] pushOperator :: Operator -> [Pdct a] -> Exceptional Error [Pdct a] pushToken :: [Pdct a] -> RPNToken a -> Exceptional Error [Pdct a] -- | Parses an RPN expression and returns the resulting Pdct. Fails if -- there are no operands left on the stack or if there are multiple -- operands left on the stack; the stack must contain exactly one operand -- in order to succeed. parseRPN :: Foldable f => f (RPNToken a) -> Exceptional Error (Pdct a) instance Show Operator module Data.Prednote.Expressions.Infix data InfixToken a TokRPN :: (RPNToken a) -> InfixToken a TokParen :: Paren -> InfixToken a data Paren Open :: Paren Close :: Paren -- | Creates an RPN expression from an infix one. Fails only if there are -- mismatched parentheses. It is possible to create a nonsensical RPN -- expression; the RPN parser must catch this. createRPN :: Foldable f => f (InfixToken a) -> Maybe [RPNToken a] -- | Handles parsing of both infix and RPN Pdct expressions. module Data.Prednote.Expressions -- | Is this an infix or RPN expression? data ExprDesc Infix :: ExprDesc RPN :: ExprDesc type Error = Text -- | A single type for both RPN tokens and infix tokens. data Token a -- | Creates Operands from Pdct. operand :: Pdct a -> Token a -- | The And operator opAnd :: Token a -- | The Or operator opOr :: Token a -- | The Not operator opNot :: Token a -- | Open parentheses openParen :: Token a -- | Close parentheses closeParen :: Token a -- | Parses expressions. Fails if the expression is nonsensical in some way -- (for example, unbalanced parentheses, parentheses in an RPN -- expression, or multiple stack values remaining.) Works by first -- changing infix expressions to RPN ones. parseExpression :: ExprDesc -> [Token a] -> Exceptional Error (Pdct a) instance Eq ExprDesc instance Show ExprDesc -- | Helps you build a tree of tests that run against a series of items. -- This is best illustrated with an example. -- -- Let's say that you have a list of Int. You want to make sure that -- every Int in the list is odd and that every Int is greater than zero. -- You also want to make sure that at least 5 Ints in the list are -- greater than 20. -- -- Pdct from Data.Prednote.Pdct will help you, but only -- so much: a Pdct can test individual Int, but by itself it -- will not help you run a check against a whole list of Int. Of course -- you can build such a test fairly easily with any and -- all, but what if you want to view the results of the tests -- verbosely? That's where this module comes in. -- --
-- {-# LANGUAGE OverloadedStrings #-}
-- import System.Console.Rainbow
-- import Data.Prednote.TestTree
-- import Data.Prednote.Pdct
--
-- isOdd :: Pdct Int
-- isOdd = operand "is odd" odd
--
-- greaterThan0 :: Pdct Int
-- greaterThan0 = operand "greater than zero" (> 0)
--
-- greaterThan20 :: Pdct Int
-- greaterThan20 = operand "greater than 20" (> 20)
--
-- myOpts :: TestOpts Int
-- myOpts = TestOpts
-- { tIndentAmt = 2
-- , tPassVerbosity = TrueSubjects
-- , tFailVerbosity = TrueSubjects
-- , tGroupPred = const True
-- , tTestPred = const True
-- , tShowSkippedTests = True
-- , tGroupVerbosity = AllGroups
-- , tSubjects = mySubjects
-- , tStopOnFail = False
-- }
--
-- mySubjects :: [Int]
-- mySubjects = [2, 4, 6, 8, 10, 18, 19, 20, 21, 22, 24, 26]
--
-- tests :: [TestTree Int]
-- tests = [ isOdd, greaterThan0, greaterThan20 ]
--
-- main :: IO ()
-- main = do
-- let (cks, passed, failed) = runTests myOpts 0 tests
-- t <- termFromEnv
-- printChunks t cks
-- putStrLn $ "number of tests passed: " ++ show passed
-- putStrLn $ "number of tests failed: " ++ show failed
--
module Data.Prednote.TestTree
-- | The name of a test or of a group.
type Name = Text
-- | A test is a function of this type. The function must make chunks in a
-- manner that respects the applicable verbosity.
type TestFunc a = IndentAmt -> Verbosity -> Verbosity -> [a] -> Level -> (Pass, [Chunk])
-- | A tree of tests.
data TestTree a
TestTree :: Name -> (Payload a) -> TestTree a
data Payload a
Group :: [TestTree a] -> Payload a
Test :: (TestFunc a) -> Payload a
-- | Creates tests.
test :: Name -> TestFunc a -> TestTree a
-- | Passes if every subject is True.
eachSubjectMustBeTrue :: Name -> (a -> Text) -> Pdct a -> TestTree a
-- | Passes if at least n subjects are True.
nSubjectsMustBeTrue :: Name -> (a -> Text) -> Int -> Pdct a -> TestTree a
-- | Creates groups of tests.
group :: Name -> [TestTree a] -> TestTree a
-- | How verbose to be when reporting the results of tests. It would be
-- possible to have many more knobs to control this behavior; this
-- implementation is a compromise and hopefully provides enough verbosity
-- settings without being too complex.
data Verbosity
-- | Show nothing at all
Silent :: Verbosity
-- | Show only whether the test passed or failed
PassFail :: Verbosity
-- | Show subjects that are False. In addition, shows all evaluation steps
-- that led to the subject being False; however, does not show discarded
-- evaluation steps. Does not show True subjects at all.
FalseSubjects :: Verbosity
-- | Show subjects that are True. (This is cumulative, so False subjects
-- are shown too, as they would be using FalseSubjects.) Shows all
-- evaluation steps that led to the subject being True; however, does not
-- show discarded evaluation steps.
TrueSubjects :: Verbosity
-- | Shows discarded subjects. Cumulative, so also does what
-- FalseSubjects and TrueSubjects do. Also shows all
-- discarded evaluation steps for all subjects.
Discards :: Verbosity
-- | How verbose to be when showing names of groups.
data GroupVerbosity
-- | Show no group names at all. However, groups will still be indented.
NoGroups :: GroupVerbosity
-- | Show groups that are not skipped.
ActiveGroups :: GroupVerbosity
-- | Show all groups, and indicate which groups are skipped.
AllGroups :: GroupVerbosity
-- | How many levels of indentation to use. Typically you will start this
-- at zero. It is incremented by one for each level as functions descend
-- through the tree.
type Level = Int
type PassCount = Int
type FailCount = Int
-- | Runs each test in a list of tests (though each test might not run if
-- tStopOnFail is True.) Reports on how many passed and how many
-- failed. (if tStopOnFail is True, the FailCount will never
-- exceed 1.)
runTests :: TestOpts a -> Level -> [TestTree a] -> ([Chunk], PassCount, FailCount)
-- | Shows a tree, without evaluating it.
showTestTree :: IndentAmt -> Level -> TestTree a -> [Chunk]
-- | Options for running tests.
data TestOpts a
TestOpts :: Int -> Verbosity -> Verbosity -> (Name -> Bool) -> (Name -> Bool) -> Bool -> GroupVerbosity -> [a] -> Bool -> TestOpts a
-- | Indent each level by this many spaces
tIndentAmt :: TestOpts a -> Int
-- | Use this verbosity for tests that pass
tPassVerbosity :: TestOpts a -> Verbosity
-- | Use this verbosity for tests that fail
tFailVerbosity :: TestOpts a -> Verbosity
-- | Groups are run only if this predicate returns True.
tGroupPred :: TestOpts a -> Name -> Bool
-- | Tests are run only if this predicate returns True.
tTestPred :: TestOpts a -> Name -> Bool
-- | Some tests might be skipped; see tTestPred. This controls
-- whether you want to see a notification of tests that were skipped.
-- (Does not affect skipped groups; see tGroupVerbosity for that.)
tShowSkippedTests :: TestOpts a -> Bool
-- | Show group names? Even if you do not show the names of groups, tests
-- within the group will still be indented.
tGroupVerbosity :: TestOpts a -> GroupVerbosity
-- | The subjects to test
tSubjects :: TestOpts a -> [a]
-- | If True, then tests will stop running immediately after a single test
-- fails. If False, all tests are always run.
tStopOnFail :: TestOpts a -> Bool
-- | True if the tree returned a result without completely evaluating all
-- parts of the tree. This can occur if tStopOnFail is True and
-- one of the tests in the tree failed.
type ShortCircuit = Bool
type Pass = Bool
-- | Evaluates a tree. This function is the basis of runTests, which
-- is typically a bit easier to use.
evalTree :: TestOpts a -> Level -> TestTree a -> (ShortCircuit, [Either [Chunk] (Pass, [Chunk])])
instance Eq Verbosity
instance Ord Verbosity
instance Show Verbosity
instance Eq GroupVerbosity
instance Ord GroupVerbosity
instance Show GroupVerbosity