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

Rework basic tests and update a number of explicit tests #820

Merged
merged 11 commits into from
Jan 18, 2017
Merged

Conversation

marcharper
Copy link
Member

@marcharper marcharper commented Jan 16, 2017

I'm simplifying #807 by porting out some of the changes. This PR mostly contains updates to the testing suite including:

  • Reworked some history internals to give more representative simulations
  • TestHeadsUp is now TestMatch. We should expand and encourage testing actual matches and their outcomes.
  • markov_test([C, D, D, C]) is now second_play_test(C, D, D, C) (name is debatable)
  • responses_test got some upgrades: the argument order is now responses_test(responses, history1=None, history2=None, ...) so you can test the first X actions with e.g. responses_test([C,D,D,...]) without any other arguments
  • Some tests were rewritten
  • Many PEP8 and docstring fixes.
  • Some updates to the testing docs

You may note some additional warnings now in the test suite -- if the requested history of play is impossible then a warning will be raised (but the test can still pass). This is to encourage the writing of better tests. We can suppress these.

@marcharper
Copy link
Member Author

There's one doc build test failing that I'm not sure how to fix.

@drvinceknight
Copy link
Member

@marcharper, I've just pushed 81853d6 to this branch (I hope that's ok). I believe that should fix your rest syntax errors.

Copy link
Member

@drvinceknight drvinceknight left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all looks great @marcharper, I have a couple of requests about the documentation where I feel things could be tightened up a bit as well as possibly changing history1, history2 as variables in responses_test to be player_history, opponent_history (or perhaps the other way around: I'm not entirely sure).

When this goes it it will close #726

# Need to retain history for opponents that examine opponents history
# Do a deep copy just to be safe
super().__init__()
self.history = copy.deepcopy(player.history)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we not need this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new code below it copies the list element by element using update_history.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. Thanks.



def simulate_play(P1, P2, h1=None, h2=None):
def simulate_play(player1, player2, h1=None, h2=None):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

history1, history2 perhaps?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed h1 and h2 to action1 and action2.

@@ -43,13 +39,11 @@ class TestTrickyCooperator(TestPlayer):
}

def test_strategy(self):
"""Starts by cooperating."""
# Starts by cooperating.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why an inline comment instead of a docstring?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the docstring doesn't fully describe the function and a lot of others were this way, so I standardized them. We can write better doc strings for these if you want but it's essentially always the same thing. The inline comments are better since they explain what each group of assertions is testing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm indifferent, although in terms of PEP8 standards is a docstring not what is usual here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure but the docstring is either generic for all instances of test_strategy, i.e. """Tests that the strategy returns the expected actions.""" or it's essentially a repeat of the strategy's docstring. Is there a preference?

in the four possible second rounds of play, depending on the move the the
strategy and the opponent in the first round.
* `responses_test(responses, history1=None, history2=None, ...)` is a powerful
test that can handle a variety of situations, testing the first X actions, the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

n actions? (not overly insistant but personally prefer n for discrete variables...)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

:code:`strategy` method in the :code:`TitForTat` class works as expected:

1. If the opponent's last strategy was :code:`C`: then :code:`TitForTat` should
cooperate::

self.responses_test([C] * 4, [C, C, C, C], [C])
self.responses_test([C], [C] * 4, [C, C, C, C])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still not immediately clear to me, perhaps for clarity write this (and the class above) with:

self.responses_test(responses=[C], history1=[C, C, C, C], history2=[C, C, C, C]),

I also wonder if the variable names (here and in the class) should be opponent_history, player_history?

In your explanation here you have not made it clear why you have a list of 4 C, perhaps a sentence like here we also are able to test the response :code:C is obtained after a history of 4 consecutive C for each player. (I also feel that the mix of [C] *4 and [C, C, C, C] is not helpful: perhaps we just stick with one of them?).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just used the test that was already there; I'll improve the language.

self.assertEqual(P1.strategy(P2), 'C')
This is equivalent to choosing an opponent will play C or D as needed and
checking the next move. This function can also take an optional random seed
argument `seed`.

3. The member function :code:`responses_test` takes arbitrary histories for each
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm not mistaken you have already mentioned responses_test on line 77. Could this be brought together?


2. If the opponent's last strategy was :code:`D`: then :code:`TitForTat` should
defect::

self.responses_test([C] * 5, [C, C, C, C, D], [D])
self.responses_test([D], [C] * 5, [C, C, C, C, D])

We have added some convenience member functions to the :code:`TestPlayer` class.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has now been said above right?

and so will not actually test the strategy correctly. The test suite will
warn you if it detects a mismatch in simulated history and actual history.

Note also that in general it is not a good idea to manually set the history
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this sentence necessary?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About setting the history? Indeed it is, setting the history directly will often not set the member instances correctly leading to an erroneous test.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I agree with what you say in the sentence (because I know why which the reader might not) but with this framework there's not much need for the sentence (as people are unlikely to do it right?) or at least you could add the explanation?

@@ -113,7 +113,7 @@ opponent has not played yet) will cooperate::
except IndexError:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should be deleted.

self.first_play_test(C, seed=11)
self.first_play_test(D, seed=23)

* `second_play_test(rCC, rCD, rDC, rDD, seed=None)` tests the strategies actions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You discuss these 3 functions lower down in terms of the member functions, I think you could remove this paragraph and just start by illustrating with an example (TitForTat). Or perhaps something else but it's not ideal to have these twice.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the extra examples are useful?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes the extra example (that was not the point I was making). You introduce these functions twice, from a newcomer/beginner's point of view that can be confusing.

I'll write a modification and send you a PR and you can see what I'm trying to say.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll also fix your rest syntax (you can build the docs locally by running make html).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@drvinceknight
Copy link
Member

Any thoughts on:

I also wonder if the variable names (here and in the class) should be opponent_history, player_history?

@drvinceknight
Copy link
Member

Have opened #825 to this branch with what I had in mind.

Also: fix rst syntax.
@marcharper
Copy link
Member Author

I'm fine with changing the argument names for response test. Sorry I had to get on a plane and didn't get to finish and push the updates.

@drvinceknight
Copy link
Member

I'm fine with changing the argument names for response test. Sorry I had to get on a plane and didn't get to finish and push the updates.

No problem at all 👍 :)

@drvinceknight
Copy link
Member

I'm fine with changing the argument names for response test.

Let me know if you'd like me to do this or we could have this as a further PR :)

@marcharper
Copy link
Member Author

I can do it -- pycharm can globally find-replace

@drvinceknight
Copy link
Member

I can do it -- pycharm can globally find-replace

Winning :)

@marcharper
Copy link
Member Author

@drvinceknight Let me know if I've missed anything, and if you'd like docstrings for test_strategy.

@drvinceknight
Copy link
Member

@drvinceknight Let me know if I've missed anything, and if you'd like docstrings for test_strategy

No: you got everything for me. Thanks! This is an awesome contribution and I think really tidies up our testing framework! :D

You may note some additional warnings now in the test suite -- if the requested history of play is impossible then a warning will be raised (but the test can still pass). This is to encourage the writing of better tests. We can suppress these.

Yeah: we can open up an issue to clean up the warnings we're now getting once this is in. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants