packages feed

MuCheck 0.1.2.1 → 0.1.2.2

raw patch · 9 files changed

+192/−54 lines, 9 files

Files

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 }-