Skip to content

Commit

Permalink
Fix line numbers of mutants and remove obsolete parentheses in tests
Browse files Browse the repository at this point in the history
  • Loading branch information
phihos committed Nov 11, 2018
1 parent 18d0361 commit ac9162a
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 26 deletions.
11 changes: 11 additions & 0 deletions mutpy/operators/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ def fix_node_internals(self, old_node, new_node):
if not hasattr(new_node, 'parent'):
new_node.children = old_node.children
new_node.parent = old_node.parent
if not hasattr(new_node, 'lineno') and hasattr(old_node, 'lineno'):
new_node.lineno = old_node.lineno
if hasattr(old_node, 'marker'):
new_node.marker = old_node.marker

Expand All @@ -130,6 +132,15 @@ def getattrs_like(ob, attr_like):
pattern = re.compile(attr_like + "($|(_\w+)+$)")
return [getattr(ob, attr) for attr in dir(ob) if pattern.match(attr)]

def set_lineno(self, node, lineno):
for n in ast.walk(node):
if hasattr(n, 'lineno'):
n.lineno = lineno

def shift_lines(self, nodes, shift_by=1):
for node in nodes:
ast.increment_lineno(node, shift_by)

@classmethod
def name(cls):
return ''.join([c for c in cls.__name__ if str.isupper(c)])
Expand Down
8 changes: 6 additions & 2 deletions mutpy/operators/decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ def mutate_FunctionDef(self, node):
decorator_name = decorator.id
if decorator_name == self.get_decorator_name():
raise MutationResign()

decorator = ast.Name(id=self.get_decorator_name(), ctx=ast.Load())
if node.decorator_list:
lineno = node.decorator_list[-1].lineno
else:
lineno = node.lineno
decorator = ast.Name(id=self.get_decorator_name(), ctx=ast.Load(), lineno=lineno)
self.shift_lines(node.body, 1)
node.decorator_list.append(decorator)
return node

Expand Down
6 changes: 4 additions & 2 deletions mutpy/operators/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ class ExceptionHandlerDeletion(MutationOperator):
def mutate_ExceptHandler(self, node):
if node.body and isinstance(node.body[0], ast.Raise):
raise MutationResign()
return ast.ExceptHandler(type=node.type, name=node.name, body=[ast.Raise()])
return ast.ExceptHandler(type=node.type, name=node.name, lineno=node.lineno,
body=[ast.Raise(lineno=node.body[0].lineno)])


class ExceptionSwallowing(MutationOperator):
def mutate_ExceptHandler(self, node):
if len(node.body) == 1 and isinstance(node.body[0], ast.Pass):
raise MutationResign()
return ast.ExceptHandler(type=node.type, name=node.name, body=[ast.Pass()])
return ast.ExceptHandler(type=node.type, name=node.name, lineno=node.lineno,
body=[ast.Pass(lineno=node.body[0].lineno)])

@classmethod
def name(cls):
Expand Down
9 changes: 8 additions & 1 deletion mutpy/operators/inheritance.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,12 @@ def mutate_FunctionDef(self, node):
super_call = node.body[index]
del node.body[index]
if index == 0:
self.set_lineno(super_call, node.body[-1].lineno)
self.shift_lines(node.body, -1)
node.body.append(super_call)
else:
self.set_lineno(super_call, node.body[0].lineno)
self.shift_lines(node.body, 1)
node.body.insert(0, super_call)
return node

Expand Down Expand Up @@ -130,7 +134,7 @@ def mutate_FunctionDef(self, node):
index, _ = self.get_super_call(node)
if index is None:
raise MutationResign()
node.body[index] = ast.Pass()
node.body[index] = ast.Pass(lineno=node.body[index].lineno)
return node


Expand All @@ -148,8 +152,10 @@ def mutate_FunctionDef(self, node):
if index is not None:
raise MutationResign()
node.body.insert(0, self.create_super_call(node))
self.shift_lines(node.body[1:], 1)
return node

@copy_node
def create_super_call(self, node):
super_call = utils.create_ast('super().{}()'.format(node.name)).body[0]
for arg in node.args.args[1:-len(node.args.defaults) or None]:
Expand All @@ -162,6 +168,7 @@ def create_super_call(self, node):
self.add_vararg_to_super_call(super_call, node.args.vararg)
if node.args.kwarg:
self.add_kwarg_to_super_call(super_call, node.args.kwarg)
self.set_lineno(super_call, node.body[0].lineno)
return super_call

@staticmethod
Expand Down
4 changes: 2 additions & 2 deletions mutpy/operators/loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

class OneIterationLoop(MutationOperator):
def one_iteration(self, node):
node.body.append(ast.Break())
node.body.append(ast.Break(lineno=node.body[-1].lineno + 1))
return node

@copy_node
Expand Down Expand Up @@ -33,7 +33,7 @@ def mutate_For(self, node):

class ZeroIterationLoop(MutationOperator):
def zero_iteration(self, node):
node.body = [ast.Break()]
node.body = [ast.Break(lineno=node.body[0].lineno)]
return node

@copy_node
Expand Down
4 changes: 2 additions & 2 deletions mutpy/test/test_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,10 +320,10 @@ def test_second_order_mutation_with_same_node_as_target(self):
self.assertEqual('a', codegen.to_source(mutant))
self.assertEqual(len(mutations), 1)
elif number == 1:
self.assertEqual('(+a)', codegen.to_source(mutant))
self.assertEqual('+a', codegen.to_source(mutant))
self.assertEqual(len(mutations), 1)
self.assertEqual(number, 1)
self.assertEqual(codegen.to_source(target_ast), '(-a)')
self.assertEqual(codegen.to_source(target_ast), '-a')

def test_second_order_mutation_with_multiple_visitors(self):
mutator = controller.HighOrderMutator(
Expand Down
34 changes: 17 additions & 17 deletions mutpy/test/test_operators.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import unittest
import ast
from mutpy import operators, codegen, coverage, utils
import unittest

from mutpy import operators, codegen, coverage, utils

EOL = '\n'
INDENT = ' ' * 4
PASS = 'pass'


class MutationOperatorTest(unittest.TestCase):

class PassIdOperator(operators.MutationOperator):

def mutate_Pass(self, node):
Expand All @@ -20,7 +19,6 @@ def setUp(self):
self.target_ast = utils.create_ast(PASS)

def test_generate_all_mutations_if_always_sampler(self):

class AlwaysSampler:

def is_mutation_time(self):
Expand All @@ -31,7 +29,6 @@ def is_mutation_time(self):
self.assertEqual(len(mutations), 1)

def test_no_mutations_if_never_sampler(self):

class NeverSampler:

def is_mutation_time(self):
Expand Down Expand Up @@ -67,8 +64,11 @@ def assert_mutation(self, original, mutants, lines=None, operator=None, with_cov
self.assertIn(mutant_code, mutants, msg)
mutants.remove(mutant_code)
self.assert_location(mutatnt)
if not lines is None:
self.assert_mutation_lineo(mutation.node.lineno, lines)
if lines is not None:
if not hasattr(mutation.node, 'lineno'):
self.assert_mutation_lineo(mutation.node.parent.lineno, lines)
else:
self.assert_mutation_lineo(mutation.node.lineno, lines)

self.assertListEqual(mutants, [], 'did not generate all mutants')

Expand All @@ -94,7 +94,7 @@ def setUpClass(cls):
cls.op = operators.ConstantReplacement()

def test_numbers_increment(self):
self.assert_mutation('2 + 3 - 99', ['3 + 3 - 99', '2 + 4 - 99', '2 + 3 - 100'])
self.assert_mutation('2 + 3 - 99', ['(3 + 3) - 99', '(2 + 4) - 99', '(2 + 3) - 100'])

def test_string_replacement(self):
self.assert_mutation(
Expand Down Expand Up @@ -149,7 +149,7 @@ def setUpClass(cls):
cls.op = operators.ArithmeticOperatorReplacement()

def test_add_to_sub_replacement(self):
self.assert_mutation('x + y + z', ['x - y + z', 'x + y - z'])
self.assert_mutation('x + y + z', ['(x - y) + z', '(x + y) - z'])

def test_sub_to_add_replacement(self):
self.assert_mutation('x - y', ['x + y'])
Expand Down Expand Up @@ -180,10 +180,10 @@ def test_not_mutate_augmented_assign(self):
self.assert_mutation('x += y', [])

def test_usub(self):
self.assert_mutation('(-x)', ['(+x)'])
self.assert_mutation('(-x)', ['+x'])

def test_uadd(self):
self.assert_mutation('(+x)', ['(-x)'])
self.assert_mutation('(+x)', ['-x'])


class AssignmentOperatorReplacementTest(OperatorTestCase):
Expand Down Expand Up @@ -299,17 +299,17 @@ def setUpClass(cls):
cls.op = operators.ConditionalOperatorInsertion()

def test_negate_while_condition(self):
self.assert_mutation("while x:\n pass", ["while (not x):\n pass"])
self.assert_mutation("while x:\n pass", ["while not x:\n pass"])

def test_negate_if_condition(self):
self.assert_mutation('if x:\n pass', ['if (not x):\n pass'])
self.assert_mutation('if x:\n pass', ['if not x:\n pass'])

def test_negate_if_and_elif_condition(self):
self.assert_mutation(
'if x:' + EOL + INDENT + 'pass' + EOL + 'elif y:' + EOL + INDENT + 'pass',
[
'if (not x):' + EOL + INDENT + 'pass' + EOL + 'elif y:' + EOL + INDENT + 'pass',
'if x:' + EOL + INDENT + 'pass' + EOL + 'elif (not y):' + EOL + INDENT + 'pass',
'if not x:' + EOL + INDENT + 'pass' + EOL + 'elif y:' + EOL + INDENT + 'pass',
'if x:' + EOL + INDENT + 'pass' + EOL + 'elif not y:' + EOL + INDENT + 'pass',
],
lines=[1, 3],
)
Expand Down Expand Up @@ -440,7 +440,7 @@ def test_not_covered_assign_node(self):

def test_not_covered_if_node(self):
self.assert_mutation('if False:' + EOL + INDENT + 'if False:' + EOL + 2 * INDENT + PASS,
['if (not False):' + EOL + INDENT + 'if False:' + EOL + 2 * INDENT + PASS],
['if not False:' + EOL + INDENT + 'if False:' + EOL + 2 * INDENT + PASS],
operator=operators.ConditionalOperatorInsertion(),
with_coverage=True)

Expand All @@ -452,7 +452,7 @@ def test_not_covered_expr_node(self):

def test_not_covered_while_node(self):
self.assert_mutation('while False:' + EOL + INDENT + 'while False:' + EOL + 2 * INDENT + PASS,
['while (not False):' + EOL + INDENT + 'while False:' + EOL + 2 * INDENT + PASS],
['while not False:' + EOL + INDENT + 'while False:' + EOL + 2 * INDENT + PASS],
operator=operators.ConditionalOperatorInsertion(),
with_coverage=True)

Expand Down

0 comments on commit ac9162a

Please sign in to comment.