-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Automated Mutation Testing -- -- This package defines a mutation analysis library for haskell programs. -- It does this by parsing the haskell source, and replacing a few of the -- common haskell functions and operators with other, but similar -- operators and functions, and running the testsuite to check whether -- the difference has been detected. @package MuCheck @version 0.2.1.0 -- | Common print utilities module Test.MuCheck.Utils.Print -- | simple wrapper for adding a % at the end. (./.) :: (Show a, Integral a) => a -> a -> String -- | join lines together showAS :: [String] -> String -- | make lists into lines in text. showA :: Show a => [a] -> String -- | convenience function for debug tt :: Show a => a -> a -- | Capture output and err of an IO action catchOutput :: IO a -> IO (a, String) -- | Mutation operators module Test.MuCheck.MuOp -- | MuOp constructor used to specify mutation transformation data MuOp -- | Mutation operation representing translation from one fn to another fn. class Mutable a (==>) :: Mutable a => a -> a -> MuOp -- | The function ==>* pairs up the given element with all -- elements of the second list, and applies ==> on them. (==>*) :: Mutable a => a -> [a] -> [MuOp] -- | The function *==>* pairs up all elements of first list with -- all elements of second list and applies ==> between them. (*==>*) :: Mutable a => [a] -> [a] -> [MuOp] -- | The function ~~> accepts two values, and returns a function -- that if given a value equal to first, returns second we handle x -- ~~> x separately (~~>) :: (MonadPlus m, Eq a) => a -> a -> a -> m a -- | A wrapper over mkMp mkMpMuOp :: (MonadPlus m, Typeable a) => MuOp -> a -> m a -- | The function same applies on a MuOP determining if -- transformation is between same values. same :: MuOp -> Bool instance Eq MuOp instance Mutable GuardedRhs instance Mutable Literal instance Mutable Decl instance Mutable Exp instance Mutable QOp instance Mutable QName instance Mutable Name instance Show MuOp -- | SYB functions module Test.MuCheck.Utils.Syb -- | The function relevantOps does two filters. For the first, it -- removes spurious transformations like "Int 1 ~~> Int 1". Secondly, -- it tries to apply the transformation to the given program on some -- element if it does not succeed, then we discard that transformation. relevantOps :: (Data a, Eq a) => a -> [MuOp] -> [MuOp] -- | apply a mutating function on a piece of code one at a time like -- somewhere (from so) once :: MonadPlus m => GenericM m -> GenericM m -- | Module for adapting test framekworks module Test.MuCheck.TestAdapter -- | Wrapper for interpreter output type InterpreterOutput a = Either InterpreterError (String, a) -- | Holding mutant information type Mutant = String -- | Summary of test run newtype Summary Summary :: String -> Summary -- | Interface to be implemented by a test framework class Typeable s => Summarizable s where isFailure = not . isSuccess isOther x = not (isSuccess x) && not (isFailure x) testSummary :: Summarizable s => [Mutant] -> [InterpreterOutput s] -> Summary isSuccess :: Summarizable s => s -> Bool isFailure :: Summarizable s => s -> Bool isOther :: Summarizable s => s -> Bool -- | The entry point for mucheck module Test.MuCheck.Interpreter -- | Given the list of tests suites to check, run one test suite at a time -- on all mutants. evaluateMutants :: (Summarizable a, Show a) => ([Mutant] -> [InterpreterOutput a] -> Summary) -> [Mutant] -> [String] -> FilePath -> IO () -- | Common functions used by MuCheck module Test.MuCheck.Utils.Common -- | The choose function generates subsets of a given size choose :: [a] -> Int -> [[a]] -- | The coupling function produces all possible pairings, and -- applies the given function to each coupling :: Eq a => (a -> a -> t) -> [a] -> [t] -- | The genFileNames function lazily generates filenames of mutants genFileNames :: String -> [String] -- | The replaceFst function replaces first matching element in a -- list given old and new values as a pair replaceFst :: Eq a => (a, a) -> [a] -> [a] -- | The sample function takes a random generator and chooses a -- random sample subset of given size. sample :: RandomGen g => g -> Int -> [t] -> [t] -- | Wrapper around sample providing the random seed rSample :: Int -> [t] -> IO [t] -- | The sampleF function takes a random generator, and a fraction -- and returns subset of size given by fraction sampleF :: RandomGen g => g -> Rational -> [t] -> [t] -- | The remElt function removes element at index specified from a -- list remElt :: Int -> [a] -> [a] -- | The swapElts function swaps two elements in a list given their -- indices swapElts :: Int -> Int -> [t] -> [t] -- | The genSwapped generates a list of lists where each element has -- been swapped by another genSwapped :: [t] -> [[t]] -- | Generate a random seed from the time. genRandomSeed :: IO StdGen -- | take a function of two args producing a monadic result, and apply it -- to a pair curryM :: (t1 -> t2 -> m t) -> (t1, t2) -> m t -- | Available mutation operators module Test.MuCheck.Operators -- | comparison operators [""", ""=", "/=", "=="] -- -- all available operators comparators :: [MuOp] -- | predicates ["pred", "id", "succ"] -- -- all available operators predNums :: [MuOp] -- | binary arithmetic ["+", "-", "*", "/"] -- -- all available operators binAriths :: [MuOp] -- | functions on lists ["sum", "product", "maximum", "minimum", "head", -- "last"] -- -- all available operators arithLists :: [MuOp] -- | all available operators allOps :: [MuOp] -- | Configuration module module Test.MuCheck.Config -- | The knob controlling if we want first order mutation. data GenerationMode FirstOrderOnly :: GenerationMode FirstAndHigherOrder :: GenerationMode -- | The configuration options if 1 is provided, all mutants are selected -- for that kind, and 0 ensures that no mutants are picked for that kind. -- Any fraction in between causes that many mutants to be picked randomly -- from the available pool data Config Config :: [MuOp] -> Rational -> Rational -> Rational -> Rational -> Int -> GenerationMode -> Config -- | Mutation operators on operator or function replacement muOps :: Config -> [MuOp] -- | Mutate pattern matches for functions? for example -- --
-- first [] = Nothing -- first (x:_) = Just x ---- -- is mutated to -- --
-- first (x:_) = Just x -- first [] = Nothing --doMutatePatternMatches :: Config -> Rational -- | Mutates integer values by +1 or -1 or by replacing it with 0 or 1 doMutateValues :: Config -> Rational -- | negate if conditions, that is -- --
-- if True then 1 else 0 ---- -- becomes -- --
-- if (not True) then 1 else 0 --doNegateIfElse :: Config -> Rational -- | negate guarded booleans in case statements, that is -- --
-- case v of -- True -> 1 -- otherwise -> 0 ---- -- becomes -- --
-- case v of -- (not True) -> 1 -- otherwise -> 0 --doNegateGuards :: Config -> Rational -- | Maximum number of mutants to generate. maxNumMutants :: Config -> Int -- | Generation mode, can be traditional (firstOrder) and higher order -- (higher order is experimental) genMode :: Config -> GenerationMode -- | The default configuration defaultConfig :: Config -- | Enumeration of different kinds of mutations data MuVars MutatePatternMatch :: MuVars MutateValues :: MuVars MutateNegateIfElse :: MuVars MutateNegateGuards :: MuVars -- | getSample returns the fraction in config corresponding to the enum -- passed in getSample :: MuVars -> Config -> Rational instance Eq GenerationMode instance Show GenerationMode instance Show Config -- | Mutation happens here. module Test.MuCheck.Mutation -- | The genMutants function is a wrapper to genMutantsWith with -- standard configuraton genMutants :: String -> FilePath -> IO [Mutant] -- | The genMutantsWith function takes configuration function to -- mutate, filename the function is defined in, and produces mutants in -- the same directory as the filename, and returns the number of mutants -- produced. genMutantsWith :: Config -> String -> FilePath -> IO [Mutant] -- | Wrapper around sampleF that returns correct sampling ratios according -- to configuration passed. sampler :: RandomGen g => Config -> g -> MuVars -> [t] -> [t] -- | The genMutantsForSrc takes the function name to mutate, source -- where it is defined, and a sampling function, and returns the mutated -- sources selected using sampling function. genMutantsForSrc :: Config -> String -> String -> (MuVars -> [MuOp] -> [MuOp]) -> [Mutant] -- | Replace old function definition with a new one in the AST replaceDef :: Decl -> Decl -> Module -> [Decl] -- | Fetch the function definition from module getFunc :: String -> Module -> Decl -- | Higher order mutation of a function's code using a bunch of mutation -- operators (In all the three mutate functions, we assume working with -- functions declaration.) mutates :: [MuOp] -> Decl -> [Decl] -- | First and higher order mutation. The third argument specifies whether -- it's first order or higher order mutatesN :: [MuOp] -> Decl -> Int -> [Decl] -- | Given a function, generate all mutants after applying applying op once -- (op might be applied at different places). E.g.: if the operator is -- (op = "== ">") and there are two instances of "<" in the -- AST, then it will return two AST with each replaced. mutate :: MuOp -> Decl -> [Decl] -- | is the parsed expression the function we are looking for? isFunctionD :: String -> Decl -> Bool -- | Generate all operators for permutating pattern matches in a function. -- We don't deal with permutating guards and case for now. permMatches :: Decl -> [MuOp] -- | Generates transformations that removes one pattern match from a -- function definition. removeOnePMatch :: Decl -> [MuOp] -- | Generate sub-arrays with one less element removeOneElem :: Eq t => [t] -> [[t]] -- | Returns the AST from the file getASTFromStr :: String -> Module -- | Set the declaration in a module putDecls :: Module -> [Decl] -> Module -- | For valops, unlike functions, we specify how any given literal value -- might change. So we take a predicate specifying how to recognize the -- literal value, a list of mappings specifying how the literal can -- change, and the AST, and recurse over the AST looking for literals -- that match our predicate. When we find any, we apply the given list of -- mappings to them, and produce a MuOp mapping between the original -- value and transformed value. This list of MuOp mappings are then -- returned. selectValOps :: (Typeable b, Mutable b) => (b -> Bool) -> (b -> [b]) -> Decl -> [MuOp] -- | Look for literal values in AST, and return applicable MuOp transforms. -- Unfortunately booleans are not handled here. selectLitOps :: Decl -> [MuOp] -- | Convert Boolean Literals selectBLitOps :: Decl -> [MuOp] -- | Negating boolean in if/else statements selectIfElseBoolNegOps :: Decl -> [MuOp] -- | Negating boolean in Guards selectGuardedBoolNegOps :: Decl -> [MuOp] -- | MuCheck base module module Test.MuCheck -- | Perform mutation analysis mucheck :: (Summarizable a, Show a) => ([Mutant] -> [InterpreterOutput a] -> Summary) -> String -> FilePath -> [String] -> IO ()