MuCheck 0.1.2.1 → 0.1.2.2
raw patch · 9 files changed
+192/−54 lines, 9 files
Files
- MuCheck.cabal +3/−3
- src/Main.hs +1/−1
- src/MuCheck/Config.hs +28/−0
- src/MuCheck/Mutation.hs +30/−23
- src/MuCheck/Run/Common.hs +19/−0
- src/MuCheck/Run/HUnit.hs +38/−0
- src/MuCheck/Run/Hspec.hs +34/−0
- src/MuCheck/Run/QuickCheck.hs +39/−0
- src/MuCheck/StdArgs.hs +0/−27
MuCheck.cabal view
@@ -1,5 +1,5 @@ name: MuCheck-version: 0.1.2.1+version: 0.1.2.2 synopsis: Automated Mutation Testing description: This package defines a mutation analysis library for haskell programs. It does this by parsing the haskell source, and@@ -26,7 +26,7 @@ source-repository this type: git location: https://bitbucket.org/osu-testing/mucheck.git- tag: 0.1.2.1+ tag: 0.1.2.2 executable mucheck main-is: Main.hs@@ -35,7 +35,7 @@ hs-source-dirs: src library- exposed-modules: MuCheck.MuOp, MuCheck.StdArgs, MuCheck.Interpreter, MuCheck.Mutation, MuCheck.Operators, MuCheck.Utils.Syb, MuCheck.Utils.Common, MuCheck.Utils.Print+ exposed-modules: MuCheck.MuOp, MuCheck.Config, MuCheck.Interpreter, MuCheck.Mutation, MuCheck.Operators, MuCheck.Utils.Syb, MuCheck.Utils.Common, MuCheck.Utils.Print, MuCheck.Run.Common, MuCheck.Run.QuickCheck, MuCheck.Run.HUnit, MuCheck.Run.Hspec -- other-modules: build-depends: base >=4 && <5, haskell-src-exts >=1.13, syb >= 0.4.0, time >= 1.4.0.1, QuickCheck>=2.6, hint >= 0.3.1.0, mtl>=2.1.2, HUnit >= 1.0, filepath, hspec>= 2.0, hspec-core >= 2.0, random default-language: Haskell2010
src/Main.hs view
@@ -3,7 +3,7 @@ import Control.Monad (void) import MuCheck.MuOp-import MuCheck.StdArgs+import MuCheck.Config import MuCheck.Mutation import MuCheck.Operators import MuCheck.Utils.Common
+ src/MuCheck/Config.hs view
@@ -0,0 +1,28 @@+module MuCheck.Config where++import MuCheck.MuOp+import MuCheck.Operators++data GenerationMode+ = FirstOrderOnly+ | FirstAndHigherOrder+ deriving (Eq, Show)++data Config = Config { muOps :: [MuOp]+ , doMutatePatternMatches :: Rational+ , doMutateValues :: Rational+ , doNegateIfElse :: Rational+ , doNegateGuards :: Rational+ , maxNumMutants :: Int+ , genMode :: GenerationMode }+ deriving Show++defaultConfig :: Config+defaultConfig = Config {muOps = allOps+ , doMutatePatternMatches = 1.0+ , doMutateValues = 1.0+ , doNegateIfElse = 1.0+ , doNegateGuards = 1.0+ , maxNumMutants = 300+ , genMode = FirstOrderOnly }+
src/MuCheck/Mutation.hs view
@@ -19,27 +19,31 @@ import MuCheck.Utils.Syb import MuCheck.Utils.Common import MuCheck.Operators-import MuCheck.StdArgs+import MuCheck.Config --- entry point.-genMutants = genMutantsWith stdArgs+-- | The `genMutants` function is a wrapper to genMutantsWith with standard+-- configuraton+genMutants = genMutantsWith defaultConfig -genMutantsWith :: StdArgs -> String -> FilePath -> IO Int+-- | 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 Int genMutantsWith args funcname filename = liftM length $ do ast <- getASTFromFile filename- now <- round `fmap` getPOSIXTime+ g <- liftM (mkStdGen . round) getPOSIXTime let f = func funcname ast- g = mkStdGen now++ ops, swapOps, valOps, ifElseNegOps, guardedBoolNegOps :: [MuOp] ops = relevantOps f (muOps args ++ valOps ++ ifElseNegOps ++ guardedBoolNegOps) swapOps = sampleF g (doMutatePatternMatches args) $ permMatches f ++ removeOnePMatch f- valOps = sampleF g (doMutateValues args) $ selectIntOps f ifElseNegOps = sampleF g (doNegateIfElse args) $ selectIfElseBoolNegOps f guardedBoolNegOps = sampleF g (doNegateGuards args) $ selectGuardedBoolNegOps f + patternMatchMutants, ifElseNegMutants, guardedNegMutants, operatorMutants, allMutants :: [Decl] allMutants = nub $ patternMatchMutants ++ operatorMutants - patternMatchMutants, ifElseNegMutants, guardedNegMutants, operatorMutants, allMutants :: [Decl] patternMatchMutants = mutatesN swapOps f fstOrder ifElseNegMutants = mutatesN ifElseNegOps f fstOrder guardedNegMutants = mutatesN guardedBoolNegOps f fstOrder@@ -58,9 +62,9 @@ where fstOrder = 1 -- first order getASTFromFile filename = liftM parseModuleFromFile $ readFile filename --- Mutating a function's code using a bunch of mutation operators--- NOTE: In all the three mutate functions, we assume working--- with functions declaration.+-- | Mutating 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] mutates ops m = filter (/= m) $ concatMap (mutatesN ops m) [1..] @@ -69,22 +73,19 @@ mutatesN ops m 1 = concat [mutate op m | op <- ops ] mutatesN ops m c = concat [mutatesN ops m 1 | m <- mutatesN ops m (c-1)] --- given a function, generate all mutants after applying applying --- op once (op might be applied at different places). E.g.:+-- | Given a function, generate all mutants after applying applying +-- op once (op might be applied at different places).E.g.: -- op = "<" ==> ">" and there are two instances of "<" mutate :: MuOp -> Decl -> [Decl] mutate op m = once (mkMp' op) m \\ [m] -isFunction :: Name -> GenericQ Bool-isFunction (Ident n) = False `mkQ` isFunctionD n- isFunctionD :: String -> Decl -> Bool isFunctionD n (FunBind (Match _ (Ident n') _ _ _ _ : _)) = n == n' isFunctionD n (FunBind (Match _ (Symbol n') _ _ _ _ : _)) = n == n' isFunctionD n (PatBind _ (PVar (Ident n')) _ _) = n == n' isFunctionD _ _ = False --- generate all operators for permutating pattern matches in+-- | Generate all operators for permutating pattern matches in -- a function. We don't deal with permutating guards and case for now. permMatches :: Decl -> [MuOp] permMatches d@(FunBind ms) = d ==>* map FunBind (permutations ms \\ [ms])@@ -98,13 +99,18 @@ removeOneElem l = choose l (length l - 1) -- AST/module-related operations--- String is the content of the file++-- | Parse a module. Input is the content of the file parseModuleFromFile :: String -> Module parseModuleFromFile inp = fromParseResult $ parseFileContents inp getDecls :: Module -> [Decl] getDecls (Module _ _ _ _ _ _ decls) = decls +{-+isFunction :: Name -> GenericQ Bool+isFunction (Ident n) = False `mkQ` isFunctionD n+ extractStrings :: [Match] -> [String] extractStrings [] = [] extractStrings (Match _ (Symbol name) _ _ _ _:xs) = name : extractStrings xs@@ -115,12 +121,13 @@ getFuncNames (FunBind m:xs) = extractStrings m ++ getFuncNames xs getFuncNames (_:xs) = getFuncNames xs -putDecls :: Module -> [Decl] -> Module-putDecls (Module a b c d e f _) decls = Module a b c d e f decls- getName :: Module -> String getName (Module _ (ModuleName name) _ _ _ _ _) = name+-} +putDecls :: Module -> [Decl] -> Module+putDecls (Module a b c d e f _) decls = Module a b c d e f decls+ -- Define all operations on a value selectValOps :: (Data a, Eq a, Typeable b, Mutable b, Eq b) => (b -> Bool) -> [b -> b] -> a -> [MuOp] selectValOps pred fs m = concatMap (\x -> x ==>* map (\f -> f x) fs) vals@@ -139,13 +146,13 @@ where isInt (Int _) = True isInt _ = False --- negating boolean in if/else statements+-- | negating boolean in if/else statements selectIfElseBoolNegOps :: (Data a, Eq a) => a -> [MuOp] selectIfElseBoolNegOps m = selectValOps isIf [\(If e1 e2 e3) -> If (App (Var (UnQual (Ident "not"))) e1) e2 e3] m where isIf If{} = True isIf _ = False --- negating boolean in Guards+-- | negating boolean in Guards selectGuardedBoolNegOps :: (Data a, Eq a) => a -> [MuOp] selectGuardedBoolNegOps m = selectValOps' isGuardedRhs negateGuardedRhs m where isGuardedRhs GuardedRhs{} = True
+ src/MuCheck/Run/Common.hs view
@@ -0,0 +1,19 @@+module MuCheck.Run.Common where++import qualified Language.Haskell.Interpreter as I+import Data.Typeable+type InterpreterOutput a = Either I.InterpreterError (String, a)++data TSum = TSum {tsum_numMutants::Int,+ tsum_loadError::Int,+ tsum_notKilled::Int,+ tsum_killed::Int,+ tsum_others::Int,+ tsum_log::String}++-- Class/Instance declaration+type MutantFilename = String+class Typeable s => Summarizable s where+ testSummary :: [MutantFilename] -> [InterpreterOutput s] -> TSum+ isSuccess :: s -> Bool+
+ src/MuCheck/Run/HUnit.hs view
@@ -0,0 +1,38 @@+{-# LANGUAGE StandaloneDeriving, DeriveDataTypeable #-}+module MuCheck.Run.HUnit where+import qualified Test.HUnit as HUnit+import MuCheck.Run.Common+import MuCheck.Utils.Print (showA, showAS)++import Data.Typeable+import Data.List((\\))+import Data.Either (partitionEithers)++deriving instance Typeable HUnit.Counts++instance Summarizable HUnit.Counts where+ testSummary mutantFiles results = TSum {+ tsum_numMutants = r,+ tsum_loadError = le,+ tsum_notKilled = s,+ tsum_killed = fl,+ tsum_others = re + ftc,+ tsum_log = logMsg}+ where (loadingErrorCases, executedCases) = partitionEithers results+ loadingErrorFiles = mutantFiles \\ map fst executedCases+ successCases = filter (isSuccess . snd) executedCases+ failuresCases = filter ((>0) . HUnit.failures . snd) executedCases+ runningErrorCases = filter ((>0) . HUnit.errors . snd) executedCases \\ failuresCases+ failToFullyTryCases = filter ((\c -> HUnit.cases c > HUnit.tried c) . snd) executedCases+ r = length results+ le = length loadingErrorCases+ [s, fl, re, ftc] = map length [successCases, failuresCases, runningErrorCases, failToFullyTryCases]+ logMsg = showAS ["Details:",+ "Loading error files:",showA loadingErrorFiles,+ "Loading error messages:",showA loadingErrorCases,+ "Successes:", showA successCases,+ "Failures:", showA failuresCases,+ "Error while running:", showA runningErrorCases,+ "Incompletely tested (may include failures and running errors):",showA failToFullyTryCases]+ isSuccess c = (HUnit.cases c == HUnit.tried c) && HUnit.failures c == 0 && HUnit.errors c == 0+
+ src/MuCheck/Run/Hspec.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE StandaloneDeriving, DeriveDataTypeable #-}+module MuCheck.Run.Hspec where+import qualified Test.Hspec.Core.Runner as Hspec+import MuCheck.Run.Common+import MuCheck.Utils.Print (showA, showAS)++import Data.Typeable+import Data.Either (partitionEithers)+import Data.List((\\))++deriving instance Typeable Hspec.Summary++instance Summarizable Hspec.Summary where+ testSummary mutantFiles results = TSum {+ tsum_numMutants = r,+ tsum_loadError = e,+ tsum_notKilled = s,+ tsum_killed = f,+ tsum_others = 0,+ tsum_log = logMsg}+ where (errorCases, executedCases) = partitionEithers results+ r = length results+ e = length errorCases+ [successCases, failureCases] = map (\c -> filter (c . snd) executedCases) [isSuccess, isFailure]+ [s,f] = map length [successCases, failureCases]+ errorFiles = mutantFiles \\ map fst executedCases+ logMsg = showAS ["Details:",+ "Loading error files:", showA errorFiles,+ "Loading error messages:", showA errorCases,+ "Successes:", showA successCases,+ "Failure:", showA failureCases]+ isFailure = not . isSuccess+ isSuccess (Hspec.Summary { Hspec.summaryExamples = se, Hspec.summaryFailures = sf } ) = sf == 0+
+ src/MuCheck/Run/QuickCheck.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE StandaloneDeriving, DeriveDataTypeable #-}+module MuCheck.Run.QuickCheck where+import qualified Test.QuickCheck.Test as Qc+import MuCheck.Run.Common+import MuCheck.Utils.Print (showA, showAS)++import Data.Typeable+import Data.List((\\))+import Data.Either (partitionEithers)++deriving instance Typeable Qc.Result++instance Summarizable Qc.Result where+ testSummary mutantFiles results = TSum {+ tsum_numMutants = r,+ tsum_loadError = e,+ tsum_notKilled = s,+ tsum_killed = f,+ tsum_others = g,+ tsum_log = logMsg}+ where (errorCases, executedCases) = partitionEithers results+ [successCases, failureCases, gaveUpCases] = map (\c -> filter (c . snd) executedCases) [isSuccess, isFailure, isGaveUp]+ r = length results+ e = length errorCases+ [s,f,g] = map length [successCases, failureCases, gaveUpCases]+ errorFiles = mutantFiles \\ map fst executedCases+ logMsg = showAS ["Details:",+ "Loading error files:", showA errorFiles,+ "Loading error messages:", showA errorCases,+ "Successes:", showA successCases,+ "Failure:", showA failureCases,+ "Gaveups:", showA gaveUpCases]+ isFailure :: Qc.Result -> Bool+ isFailure Qc.Failure{} = True+ isFailure _ = False+ isGaveUp :: Qc.Result -> Bool+ isGaveUp Qc.GaveUp{} = True+ isGaveUp _ = False+ isSuccess = Qc.isSuccess
− src/MuCheck/StdArgs.hs
@@ -1,27 +0,0 @@-module MuCheck.StdArgs where--import MuCheck.MuOp-import MuCheck.Operators--data GenerationMode = FirstOrderOnly- | FirstAndHigherOrder- deriving (Eq, Show)--data StdArgs = StdArgs {muOps :: [MuOp]- , doMutatePatternMatches :: Rational- , doMutateValues :: Rational- , doNegateIfElse :: Rational- , doNegateGuards :: Rational- , maxNumMutants :: Int- , genMode :: GenerationMode }- deriving Show--stdArgs :: StdArgs-stdArgs = StdArgs {muOps = allOps- , doMutatePatternMatches = 1.0- , doMutateValues = 1.0- , doNegateIfElse = 1.0- , doNegateGuards = 1.0- , maxNumMutants = 300- , genMode = FirstOrderOnly }-