Skip to content

Commit

Permalink
Merge pull request #16 from debsourav33/master
Browse files Browse the repository at this point in the history
Fix some Java, C_Like & Universal rules, add DO_NOT_MUTATE support for comby and reorder the rules
  • Loading branch information
agroce authored Feb 18, 2023
2 parents 5dc4cef + f1b20c2 commit e338f63
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 86 deletions.
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@
],
install_requires=[
"comby",
"python-levenshtein"
"python-levenshtein",
"tabulate"
],
url='https://github.com/agroce/universalmutator',
)
22 changes: 13 additions & 9 deletions universalmutator/comby/c_like.rules
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
#include ==> DO_NOT_MUTATE

:[expr:e] ==> /*:[expr:e]*/
:[[expr]]:[rest]:[lf~[\n]] ==> /*:[expr]:[rest]*/:[lf]
if (:[cond]) ==> if (!:[cond])
if(:[cond]) ==> if (!:[cond])
if (:[cond]) ==> if (0)
if(:[cond]) ==> if(0)
if (:[cond]) ==> if (1)
if(:[cond]) ==> if(1)
if (:[cond]) ==> if (0==1)
if(:[cond]) ==> if(0==1)
if (:[cond]) ==> if (1==1)
if(:[cond]) ==> if(1==1)
while (:[cond]) ==> while (!:[cond])
while(:[cond]) ==> while(!:[cond])
else ==>
:[[a]] && :[[b]] ==> :[[a]] && 1
:[[a]] && :[[b]] ==> 1 && :[[b]]
:[[a]] || :[[b]] ==> :[[a]] || 0
:[[a]] || :[[b]] ==> :[[a]] || 0

||:[expr] ==> || (0==1)
:[expr]|| ==> (0==1) ||
&&:[expr] ==> && (1==1)
:[expr]&& ==> (1==1) &&

// ==> SKIP_MUTATING_REST

20 changes: 11 additions & 9 deletions universalmutator/comby/java.rules
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
synchronized ==>
:[s~[ \t]*]import ==> DO_NOT_MUTATE
:[s~[ \t]*]@:[annotation] ==> DO_NOT_MUTATE
:[s~[ \t]*]\/*:[comment~.+] ==> DO_NOT_MUTATE
:[s~[ \t]*]\/\/:[comment~.+] ==> DO_NOT_MUTATE
:[s~[ \t]*]*:[comment~.+] ==> DO_NOT_MUTATE


:[[a]] && :[[b]] ==> :[[a]] && true
:[[a]] && :[[b]] ==> true && :[[b]]
:[[a]] || :[[b]] ==> :[[a]] || false
:[[a]] || :[[b]] ==> :[[a]] || false
synchronized ==>

:[spaces~[ \t]*]@:[annotation] ==> DO_NOT_MUTATE
:[spaces~[ \t]*]/*:[comment~.+] ==> DO_NOT_MUTATE
:[spaces~[ \t]*]//:[comment~.+] ==> DO_NOT_MUTATE
:[spaces~[ \t]*]*:[comment~.+] ==> DO_NOT_MUTATE
:[a]&&:[b] ==> :[a] && true
:[a]&&:[b] ==> true && :[b]
:[a]||:[b] ==> :[a] || false
:[a]||:[b] ==> false || :[b]
41 changes: 23 additions & 18 deletions universalmutator/comby/python.rules
Original file line number Diff line number Diff line change
@@ -1,42 +1,47 @@
print(:[1], :[2], :[3]) ==> print(:[3], :[2], :[1])
:[[v]] = :[[fn]](:[x]) ==> :[[v]] = :[[fn]]((:[x] - 1))
:[s~[ \t]*]import ==> DO_NOT_MUTATE

if :[cond] ==> if not :[cond]
while :[cond] ==> while not :[cond]

:[body]continue ==> :[body] break
:[body]break ==> :[body] continue

print(:[1], :[2], :[3]) ==> print(:[3], :[2], :[1])
:[[v]] = :[[fn]](:[x]) ==> :[[v]] = :[[fn]]((:[x] - 1))

:[[a]] and :[[b]] ==> :[[a]] or :[[b]]
:[[a]] or :[[b]] ==> :[[a]] and :[[b]]
:[[a]] and :[[b]] ==> :[[a]] or True
:[[a]] or :[[b]] ==> :[[a]] or False
:[[a]] and :[[b]] ==> True and :[[b]]
:[[a]] or :[[b]] ==> False or :[[b]]
:[[a]] and :[[b]] ==> :[[a]] or True
:[[a]] and :[[b]] ==> True or :[[b]]

not :[[a]] ==> :[[a]]
//:[[a]] ==> /:[[a]]
/:[[a]] ==> //:[[a]]

return :[expr]:[newline~[\n]] ==> return None:[newline]
:[ spaces]:[expr]:[newline~[\n]] ==> :[spaces]pass:[newline]
return :[expr]:[lf~[\n]] ==> return None:[lf]
:[ s]:[expr]:[lf~[\n]] ==> :[s]pass:[lf]

[:[expr]] ==> []
{:[expr]} ==> {}
//:[[a]] ==> /:[[a]]
/:[[a]] ==> //:[[a]]

True ==> False

@:[spaces~[ \t]*]:[annotation] :[body] ==> :[body]

[:[expr]] ==> []
[:[first],:[rest]] ==> [:[first]]
[:[first],:[rest]] ==> [:[rest]]
,:[s~[ \t]*]:[[x]]:[bra~[\]]] ==> ]

{:[expr]} ==> {}
{:[first],:[rest]} ==> {:[first]}
{:[first],:[rest]} ==> {:[rest]}
,:[spaces~[ \t]*]:[[x]]:[bra~[\]]] ==> ]
,:[spaces~[ \t]*]:[[x]]:[bra~[\}]] ==> }
,:[s~[ \t]*]:[[x]]:[bra~[\}]] ==> }

':[str]' ==> ''
,:[item], ==> ,

,:[spaces~[ \t]*]:[[item1]],:[spaces~[ \t]*]:[[item2]] ==> ,:[[item2]],:[[item1]]
,:[s~[ \t]*]:[[item1]],:[s~[ \t]*]:[[item2]] ==> ,:[[item2]],:[[item1]]
:[par~[(]]:[~[ \t]*]:[[item1]],:[~[ \t]*]:[[item2]] ==> :[par]:[[item2]],:[[item1]]
:[bra~[\[]]:[~[ \t]*]:[[item1]],:[~[ \t]*]:[[item2]] ==> :[bra]:[[item2]],:[[item1]]
:[bra~[\[]]:[~[ \t]*]:[[item1]],:[~[ \t]*]:[[item2]] ==> :[bra]:[[item2]],:[[item1]]

,:[item], ==> ,
':[str]' ==> ''

@:[s~[ \t]*]:[annotation] :[body] ==> :[body]
27 changes: 14 additions & 13 deletions universalmutator/comby/universal.rules
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
DO_NOT_MUTATE ==> DO_NOT_MUTATE

:[a]+:[b] ==> :[a]-:[b]
:[a]+:[b] ==> :[a]*:[b]
:[a]+:[b] ==> :[a]/:[b]
Expand Down Expand Up @@ -64,32 +66,31 @@
:[a]++ ==> :[a]--

-:[a] ==> :[a]
!:[a] ==> :[a]

":[str]" ==> ""
:[a~\D]:[number~\d+]:[b~\D] ==> :[a]0:[b]
:[a~\D]:[number~\d+]:[b~\D] ==> :[a]1:[b]
:[a~\D]:[number~\d+]:[b~\D] ==> :[a]-1:[b]
:[a~\D]:[number~\d+]:[b~\D] ==> :[a](:[number]+1):[b]
:[a~\D]:[number~\d+]:[b~\D] ==> :[a](:[number]-1):[b]

:[a]&&:[b] ==> :[a]||:[b]
:[a]||:[b] ==> :[a]&&:[b]
!:[a] ==> :[a]

:[a]&:[b] ==> :[a]|:[b]
:[a]|:[b] ==> :[a]&:[b]

while :[cond:e] ==> if :[cond:e]
:[s~[ \t]*]:[[a]]:[b]:[lf~[\n]] ==> :[s]:[a]:[b]:[lf]:[s]break;:[lf]
:[s~[ \t]*]:[[a]]:[b]:[lf~[\n]] ==> :[s]:[a]:[b]:[lf]:[s]continue;:[lf]

":[str]" ==> ""

while ==> if

,:[[v1]],:[[v2]] ==> ,:[[v2]],:[[v1]]
(:[[v1]],:[[v2]]) ==> (:[[v2]],:[[v1]])
[:[[v1]],:[[v2]]] ==> [:[[v2]],:[[v1]]]

:[a~\D]:[number~\d+]:[b~\D] ==> :[a]0:[b]
:[a~\D]:[number~\d+]:[b~\D] ==> :[a]1:[b]
:[a~\D]:[number~\d+]:[b~\D] ==> :[a]-1:[b]

:[a~\D]:[number~\d+]:[b~\D] ==> :[a](:[number]+1):[b]
:[a~\D]:[number~\d+]:[b~\D] ==> :[a](:[number]-1):[b]

:[spaces~[ \t]*]:[a]:[newline~[\n]] ==> :[spaces]:[a]:[newline]:[spaces]break;:[newline]
:[spaces~[ \t]*]:[a]:[newline~[\n]] ==> :[spaces]:[a]:[newline]:[spaces]continue;:[newline]

min ==> max
max ==> min
begin ==> end
Expand Down
54 changes: 36 additions & 18 deletions universalmutator/genmutants.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from __future__ import print_function
from tabulate import tabulate

import os
import random
Expand Down Expand Up @@ -179,12 +180,6 @@ def main():
doSwaps = True
args.remove("--swap")

cmd = None
try:
cmdpos = args.index("--cmd")
except ValueError:
cmdpos = -1

tstl = False
if "--tstl" in args:
tstl = True
Expand All @@ -200,6 +195,12 @@ def main():
printStat = True
args.remove("--printStat")

cmd = None
try:
cmdpos = args.index("--cmd")
except ValueError:
cmdpos = -1

if cmdpos != -1:
cmd = args[cmdpos + 1]
args.remove("--cmd")
Expand Down Expand Up @@ -500,14 +501,17 @@ def main():
print(len(validMutants), "VALID MUTANTS")
print(len(invalidMutants), "INVALID MUTANTS")
print(len(redundantMutants), "REDUNDANT MUTANTS")
print(f"Valid Percentage: {len(validMutants) * 100.0/(len(validMutants)+len(invalidMutants)+len(redundantMutants))}%")

(rules, ignoreRules, skipRules) = mutator.parseRules(["universal.rules","python.rules"], comby= comby)
totalMutants = len(validMutants) + len(invalidMutants) + len(redundantMutants)
valid_rate = 0 if totalMutants == 0 else (len(validMutants) * 100.0)/totalMutants
print(f"Valid Percentage: {valid_rate}%")

(rules, ignoreRules, skipRules) = mutator.parseRules(rules, comby= comby)

if printStat:
source = sourceJoined if comby else None
printMutantsStat((validMutants, invalidMutants, redundantMutants), source)
printRulesStat(rules, validMutants)
printRulesStat(rules, validMutants, invalidMutants)

if dumbHandler:
print()
Expand Down Expand Up @@ -542,24 +546,38 @@ def dumpToFile(fileName, mutants):
dumpToFile('invalid_mutants.txt', invalidMutants)
dumpToFile('redundant_mutants.txt', redundantMutants)

def printRulesStat(rules, validMutants):
cnt = {}
def printRulesStat(rules, validMutants, invalidMutants):
valid_cnt = {}
invalid_cnt = {}

for mutant in validMutants:
lhs, rhs = mutant[-1]
if (lhs,rhs) not in cnt:
cnt[(lhs,rhs)] = 0
cnt[(lhs,rhs)] += 1
if (lhs,rhs) not in valid_cnt:
valid_cnt[(lhs,rhs)] = 0
valid_cnt[(lhs,rhs)] += 1

for mutant in invalidMutants:
lhs, rhs = mutant[-1]
if (lhs,rhs) not in invalid_cnt:
invalid_cnt[(lhs,rhs)] = 0
invalid_cnt[(lhs,rhs)] += 1

fis = open("rules_count.txt", "w")
i = 0
table = []
table.append(["#","Rule","No. of Valids", "No. of Invalids"])

for ((lhs, rhs), ruleUsed) in rules:
if (lhs,rhs) not in cnt:
cnt[(lhs,rhs)] = 0
if (lhs,rhs) not in valid_cnt:
valid_cnt[(lhs,rhs)] = 0
if (lhs,rhs) not in invalid_cnt:
invalid_cnt[(lhs,rhs)] = 0
i += 1
fis.write(f"{i}. {lhs} --> {rhs} == {cnt[(lhs,rhs)]}\n")
sys.stdout.flush()

table.append([f'{i}',f'{lhs} ==> {rhs}', f'{valid_cnt[(lhs,rhs)]}', f'{invalid_cnt[(lhs,rhs)]}'])

fis.write(tabulate(table,tablefmt="grid"))
sys.stdout.flush()
fis.close()

if __name__ == '__main__':
Expand Down
27 changes: 22 additions & 5 deletions universalmutator/mutator.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from __future__ import print_function

import re
import pkg_resources
import random
Expand Down Expand Up @@ -80,15 +79,25 @@ def parseRules(ruleFiles, comby=False):
return (rules, ignoreRules, skipRules)



def mutants_comby(source, ruleFiles=["universal.rules"], mutateTestCode=False, mutateBoth=False,
ignorePatterns=None, ignoreStringOnly=False, fuzzing=False, language=".generic"):
comby = Comby()
print("MUTATING WITH RULES (COMBY):", ", ".join(ruleFiles))
(rules, ignoreRules, skipRules) = parseRules(ruleFiles, True)
for p in ignorePatterns:
for lhs in ignorePatterns:
ignoreRules.append(lhs)
source = ''.join(source)
mutants = []

# Lines that match with DO_NOT_MUTATE and other ignore rules will be skipped
ignoreLines = set()
for lhs in ignoreRules:
for match in comby.matches(source, lhs, language=language):
lineRange = (match.location.start.line, match.location.stop.line)
for line in range(lineRange[0],lineRange[1]+1):
ignoreLines.add(line)

# Instead of line-by-line x rule-by-rule iterate rule-by-rule x match-by-match.
for ((lhs, rhs), ruleUsed) in rules:
try:
Expand All @@ -99,7 +108,16 @@ def mutants_comby(source, ruleFiles=["universal.rules"], mutateTestCode=False, m
mutant = comby.substitute(rhs, environment)
substitutionRange = (match.location.start.offset, match.location.stop.offset)
lineRange = (match.location.start.line, match.location.stop.line)
mutants.append((substitutionRange, mutant, ruleUsed, lineRange))

# Check if the match contains an ignoreLine
skipMutant = False
for line in range(lineRange[0],lineRange[1]+1):
if line in ignoreLines:
skipMutant = True
break

if not skipMutant:
mutants.append((substitutionRange, mutant, ruleUsed, lineRange, (lhs,rhs)))
except JSONDecodeError as e:
continue
except Exception as e:
Expand All @@ -109,7 +127,6 @@ def mutants_comby(source, ruleFiles=["universal.rules"], mutateTestCode=False, m

def mutants(source, ruleFiles=["universal.rules"], mutateTestCode=False, mutateBoth=False,
ignorePatterns=None, ignoreStringOnly=False, fuzzing=False):

print("MUTATING WITH RULES:", ", ".join(ruleFiles))

(rules, ignoreRules, skipRules) = parseRules(ruleFiles)
Expand Down Expand Up @@ -214,7 +231,7 @@ def mutants(source, ruleFiles=["universal.rules"], mutateTestCode=False, mutateB
skipDueToString = True
stringSkipped += 1
if (mutant != l) and ((lineno, mutant) not in produced) and (not skipDueToString):
mutants.append((lineno, mutant, ruleUsed))
mutants.append((lineno, mutant, ruleUsed, (lhs,rhs)))
produced[(lineno, mutant)] = True
p = lhs.search(l, pos)
if abandon:
Expand Down
24 changes: 12 additions & 12 deletions universalmutator/static/c_like.rules
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
(^\s*)(\S+[^{}]+.*)\n ==> \1/*\2*/\n
if (\(.*\)) ==> if (!\1)
if(\(.*\)) ==> if (!\1)
if (\(.*\)) ==> if (0)
if(\(.*\)) ==> if(0)
if (\(.*\)) ==> if (1)
if(\(.*\)) ==> if(1)
if (\(.*\)) ==> if (0==1)
if(\(.*\)) ==> if(0==1)
if (\(.*\)) ==> if (1==1)
if(\(.*\)) ==> if(1==1)
while (\(.*\)) ==> while (!\1)
while(\(.*\)) ==> while(!\1)
else ==>
\|\|.*\) ==> || 0
\|\|.*\s ==> || 0
\(.*\|\| ==> 0 ||
\s.*\|\| ==> 0 ||
&&.*\) ==> && 1
&&.*\s ==> && 1
\(.*&& ==> 1 &&
\s.*&& ==> 1 &&
\|\|.*\) ==> || (0==1))
\|\|.*\s ==> || (0==1)
\(.*\|\| ==> ((0==1) ||
\s.*\|\| ==> (0==1) ||
&&.*\) ==> && (1==1))
&&.*\s ==> && (1==1)
\(.*&& ==> ((1==1) &&
\s.*&& ==> (1==1) &&
// ==> SKIP_MUTATING_REST
1 change: 1 addition & 0 deletions universalmutator/static/java.rules
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
(^\s*import) ==> DO_NOT_MUTATE
(^\s*(@.+)) ==> DO_NOT_MUTATE
(^\s*(\/\*.+)) ==> DO_NOT_MUTATE
(^\s*(\/\/.+)) ==> DO_NOT_MUTATE
Expand Down
Loading

0 comments on commit e338f63

Please sign in to comment.