Skip to content

Instantly share code, notes, and snippets.

@jmaicher
Last active August 9, 2016 20:59

Revisions

  1. Julian Maicher renamed this gist Oct 27, 2014. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  2. Julian Maicher revised this gist Oct 27, 2014. 2 changed files with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions foobar_test.py
    Original file line number Diff line number Diff line change
    @@ -28,6 +28,7 @@ def run_unittest_without_output():

    def unittest_with_mutant(mutant):
    code = compile(mutant, '<string>', 'exec')
    # don't do that at home
    exec code in globals()
    return run_unittest_without_output()

    File renamed without changes.
  3. Julian Maicher renamed this gist Oct 27, 2014. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  4. Julian Maicher created this gist Oct 27, 2014.
    10 changes: 10 additions & 0 deletions foobar.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,10 @@
    class FooBar:

    @classmethod
    def baz(cls, x):
    if (x % 3 == 0) or (x % 5 == 0):
    return "foo"
    elif x % 2 == 0:
    return "bar"
    else:
    return "baz"
    57 changes: 57 additions & 0 deletions foobar_test.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,57 @@
    import unittest
    import ast, astunparse, inspect
    from foobar import FooBar
    from mutate import mutate

    class FooBarTest(unittest.TestCase):

    # missing case
    def test_foo(self):
    # self.assertEquals(FooBar.baz(9), 'foo')
    pass

    def test_bar(self):
    self.assertEquals(FooBar.baz(2), 'bar')

    def test_baz(self):
    self.assertEquals(FooBar.baz(7), 'baz')


    def run_unittest_without_output():
    suite = unittest.TestSuite()
    for test in ['test_foo', 'test_bar', 'test_baz']:
    suite.addTest(FooBarTest(test))
    result = unittest.TestResult()
    suite.run(result)
    return result


    def unittest_with_mutant(mutant):
    code = compile(mutant, '<string>', 'exec')
    exec code in globals()
    return run_unittest_without_output()


    def unittest_with_mutants(mutants):
    print "Running mutation tests..."
    passedWithMutant = False
    for mutant in mutants:
    result = unittest_with_mutant(mutant)
    if result.wasSuccessful():
    # mutant passed => failed
    passedWithMutant = True
    source = astunparse.unparse(mutant)
    print "FAILED - Everything passed with the following mutant: %s" % source

    if not passedWithMutant:
    print "OK - Tested with %i mutant(s), everything covered" % len(mutants)


    if __name__ == '__main__':
    # Test without mutations
    unittest.main(exit=False)

    # Test with mutations
    source_ast = ast.parse(inspect.getsource(FooBar))
    mutants = mutate(source_ast)
    unittest_with_mutants(mutants)
    35 changes: 35 additions & 0 deletions mutate.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,35 @@
    import ast, _ast, copy

    class Mutator(ast.NodeTransformer):
    MUTATIONS = [
    # (FromOp, ToOp)
    (_ast.Or, _ast.And),
    (_ast.And, _ast.Or)
    ]

    def mutate(self, root, mutation):
    self._changed = False
    self._mutation = mutation

    mutant = self.visit(root)
    return (mutant, self._changed)

    def visit_BoolOp(self, node):
    (FromOp, ToOp) = self._mutation
    if(type(node.op) == FromOp):
    self._changed = True
    node.op = ToOp()

    return node

    # returns all mutants for given ast
    def mutate(ast):
    mutants = []
    mutator = Mutator()

    for mutation in Mutator.MUTATIONS:
    (mutant, mutated) = mutator.mutate(copy.deepcopy(ast), mutation)
    if mutated:
    mutants.append(mutant)

    return mutants
    19 changes: 19 additions & 0 deletions stdout
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    ...
    ----------------------------------------------------------------------
    Ran 3 tests in 0.001s

    OK

    Running mutation tests...
    FAILED - Everything passed with the following mutant:

    class FooBar:

    @classmethod
    def baz(cls, x):
    if (((x % 3) == 0) and ((x % 5) == 0)):
    return 'foo'
    elif ((x % 2) == 0):
    return 'bar'
    else:
    return 'baz'