Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 246 documentation: simple_math example #256

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
ad4238f
#246: Started working on simple example for demonstrating how to use …
trbedwards Oct 30, 2017
d9c57d1
#246: Added docs for using simple example 'adam' in toctree
trbedwards Oct 30, 2017
d358c1f
Added .idea to gitignore and exluded directories in conf.py
trbedwards Oct 30, 2017
da7d80a
#246: Added a link to examples/adam in quickstart docs
trbedwards Oct 30, 2017
dae0b3c
First commit of ten-pin bowling game example for test run.
walter2 Oct 30, 2017
f04ba19
Implemented strike and spare scroing. Plus tests.
walter2 Oct 30, 2017
70bb9ab
config.yml file cosmic-ray run.
walter2 Oct 30, 2017
6585316
#246: renamed adam in examples to simple_math
trbedwards Oct 30, 2017
6ee3bd8
Merge branch 'issue-246-documentation' of https://github.com/trbedwar…
trbedwards Oct 30, 2017
27da967
#246: Filled in the unit tests for simple_math.py
trbedwards Oct 30, 2017
05f0fb6
#246: Fixed bugs with cosmic-ray-bad_tests.conf and relative imports
trbedwards Oct 30, 2017
bf4bfc2
#246: Fixed problems with the simple_math example and updated the doc…
trbedwards Oct 30, 2017
eb7bc85
#246: Improved the documentation for the simple_math example
trbedwards Oct 30, 2017
0ed9938
Improved the documentation for simple_math
trbedwards Nov 5, 2017
45d7bca
More edits to the documentation
trbedwards Nov 5, 2017
7004d13
Added .idea to gitignore and exluded directories in conf.py
trbedwards Oct 30, 2017
a49a8e3
Merge branch 'issue-246-documentation' of https://github.com/trbedwar…
trbedwards Nov 14, 2017
ede7750
Added simple_math example
trbedwards Oct 30, 2017
7fc7e49
Merge branch 'issue-246-documentation' of https://github.com/trbedwar…
trbedwards Nov 14, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ test_project/*.json
.cache
.hypothesis
.csearchindex
.idea
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '.idea']

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
Expand Down
4 changes: 4 additions & 0 deletions docs/examples.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Examples
========

.. include:: ../examples/simple_math/simple_math.rst
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ And, of course, patches and ideas are welcome.
distributed
implementation
tests
examples



Expand Down
3 changes: 3 additions & 0 deletions docs/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,6 @@ Ray, you can run these tests, too, like this:
In this case we're passing the ``--verbose`` flag to the ``exec``
command so that you can see what Cosmic Ray is doing. If everything goes
as expected, the ``cr-report`` command will report a 0% survival rate.

See :ref:`examples-simple_math` for a step-by-step guide for
dealing with tests that have a non-zero mutation survival rate.
13 changes: 13 additions & 0 deletions examples/bowling_game_score_calculator/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Run the adam tests with unittest
module: score_calculator

baseline: 10

exclude-modules:

test-runner:
name: unittest
args: test_score_calculator

execution-engine:
name: local
48 changes: 48 additions & 0 deletions examples/bowling_game_score_calculator/score_calculator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""
This is a simple class to demonstrate the cosmic-ray library.
The BowlingGame class keeps and calculates the score of a ten-pin bowling
game for one player.
The traditional bowling scoring is used:
https://en.wikipedia.org/wiki/Ten-pin_bowling#Traditional_scoring
"""


ALL_PINS = 10

class BowlingGame():
def __init__(self):
self.score_count = 0
self.spare = False
self.strike = False

def score(self):
return self.score_count

def roll(self, first_roll, second_roll):
frame_result = first_roll + second_roll
self._handle_spare_and_strikes(first_roll, frame_result)
self.score_count += frame_result

def _handle_spare_and_strikes(self, first_roll, frame_result):
self._award_previous_spare_count(first_roll)
self._award_previous_strike_count(frame_result)
self._check_for_strike(first_roll)
self._check_for_spare(frame_result)

def _award_previous_spare_count(self, first_roll):
if self.spare == True:
self.score_count += first_roll
self.spare = False

def _award_previous_strike_count(self, frame_result):
if self.strike == True:
self.score_count += frame_result
self.strike = False

def _check_for_strike(self, first_roll):
if first_roll == ALL_PINS:
self.strike = True

def _check_for_spare(self, frame_result):
if frame_result == ALL_PINS and not self.strike:
self.spare = True
62 changes: 62 additions & 0 deletions examples/bowling_game_score_calculator/test_score_calculator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import unittest

from score_calculator import BowlingGame


class ScoreCalculatorTest(unittest.TestCase):
def setUp(self):
self.game = BowlingGame()

def test_create_bowling_game(self):
self.assertIsInstance(self.game, BowlingGame)

def test_the_score_of_a_new_game_is_zero(self):
self.assertEqual(self.game.score(), 0)

def test_the_count_of_the_first_frame_is_added_to_the_score(self):
self.game.roll(2, 3)
self.assertEqual(self.game.score(), 5)

def test_multiple_frame_results_are_kept_in_the_score(self):
self.game.roll(2, 4)
self.game.roll(6, 2)
self.assertEqual(self.game.score(), 14)

def test_spares_are_detected_for_the_next_frame(self):
self.game.roll(6, 4)
self.assertTrue(self.game.spare)

def test_previous_spare_results_in_that_next_roll_points_are_doubled(self):
self.game.roll(6, 4)
self.game.roll(5, 3)
self.assertEqual(self.game.score(), 23)

def test_double_spares_are_counted_correctly(self):
self.game.roll(6, 4)
self.game.roll(5, 5)
self.game.roll(8, 0)
self.assertEqual(self.game.score(), 41)

def test_the_spare_flag_is_removed_in_the_next_frame(self):
self.game.roll(6, 4)
self.game.roll(1, 1)
self.game.roll(2, 0)
self.assertEqual(self.game.score(), 15)

def test_a_strike_is_detected_and_no_spare_flag_is_set(self):
self.game.roll(10, 0)
self.assertTrue(self.game.strike)
self.assertFalse(self.game.spare)

def test_previous_strike_doubles_the_next_frame_pin_count(self):
self.game.roll(10, 0)
self.game.roll(5, 4)
self.assertEqual(self.game.score(), 28)

def test_the_strike_flag_is_removed_in_the_next_frame(self):
self.game.roll(10, 0)
self.game.roll(1, 1)
self.game.roll(2, 5)
self.assertEqual(self.game.score(), 21)

# case with strike after spare or the way around
1 change: 1 addition & 0 deletions examples/simple_math/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.json
13 changes: 13 additions & 0 deletions examples/simple_math/cosmic-ray-bad_tests.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Run the simple_math tests with pytest
module: simple_math

baseline: 10

exclude-modules:

test-runner:
name: pytest
args: -x test_simple_math_bad.py

execution-engine:
name: local
13 changes: 13 additions & 0 deletions examples/simple_math/cosmic-ray-good_tests.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Run the simple_math tests with pytest
module: simple_math

baseline: 10

exclude-modules:

test-runner:
name: pytest
args: -x test_simple_math_good.py

execution-engine:
name: local
26 changes: 26 additions & 0 deletions examples/simple_math/simple_math.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""
-----------
Simple Math
-----------

A set of simple math functions.
This is paired up with a test suite and intended to be run with cosmic-ray.
The idea is that cosmic-ray should kill every mutant when that suite is run;
if it doesn't, then we've got a problem.
"""


def mult_by_2(x):
return x + x


def square(x):
return x*x


def cube(x):
return x*x*x


def is_positive(x):
return x > 0
71 changes: 71 additions & 0 deletions examples/simple_math/simple_math.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
.. _examples-simple_math:

Improving the tests for a simple module
---------------------------------------

This example demonstrates how to use cosmic-ray to improve the testing
suite for a module called ``simple_math``. The code is located in the
``examples/simple_math`` directory.

::

# examples/simple_math/simple_math.py

def mult_by_2(x):
return x + x

def square(x):
return x*x

def cube(x):
return x*x*x

def is_positive(x):
return x > 0


We would like to measure the performance of a testing suite,
``test_simple_math_bad.py``, with intention to improve it.
First run cosmic-ray on the so-called 'bad' testing suite.

::

cosmic-ray init cosmic-ray-bad_tests.conf bad_session
cosmic-ray --verbose exec bad_session
cosmic-ray dump bad_session | cr-report

You should end up with at least one mutant that survives. This is because the test
``test_mult_by_2`` from ``test_simple_math_bad.py`` still passes when we replace
``x + x`` with ``x * x`` or ``x ** x``, as they all return the same answer, ``4``,
when ``x = 2``.

Here is the bad test that lets the mutant(s) survive:

::
# examples/simple_math/test_simple_math_bad.py

def test_mult_by_2():
assert mult_by_2(2) == 4

To fix this bad test, we decorate it so that a range
of values of `x` are tested:

::
# examples/simple_math/test_simple_math_good.py

@pytest.mark.parametrize('x', range(-5, 5))
def test_mult_by_2(x):
assert mult_by_2(x) == x * 2

Now this test should fail for all the mutations to the underlying
function ``mult_by_2``, which is what we want it to do.
Run cosmic-ray again on the new testing suite, ``test_simple_math_good.py``

::

cosmic-ray init cosmic-ray-good_tests.conf good_session
cosmic-ray --verbose exec good_session
cosmic-ray dump good_session | cr-report

You should now get 0% survival rate for the mutants. This means your
testing suite is now more robust.
25 changes: 25 additions & 0 deletions examples/simple_math/test_simple_math_bad.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from simple_math import square, cube, mult_by_2, is_positive


def test_square():
assert square(3) == 9


def test_cube():
assert cube(2) == 8


def test_mult_by_2():
assert mult_by_2(2) == 4


def test_is_positive_for_positive_numbers():
assert is_positive(1)
assert is_positive(2)
assert is_positive(3)


def test_is_positive_for_non_positive_numbers():
assert not is_positive(0)
assert not is_positive(-1)
assert not is_positive(-2)
28 changes: 28 additions & 0 deletions examples/simple_math/test_simple_math_good.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from simple_math import square, cube, mult_by_2, is_positive

import pytest

def test_square():
assert square(3) == 9


def test_cube():
assert cube(2) == 8


@pytest.mark.parametrize('x', range(-5, 5))
def test_mult_by_2(x):
assert mult_by_2(x) == x * 2


def test_is_positive_for_positive_numbers():
assert is_positive(1)
assert is_positive(2)
assert is_positive(3)


def test_is_positive_for_non_positive_numbers():
assert not is_positive(0)
assert not is_positive(-1)
assert not is_positive(-2)