From eeb8c043f3eaebb090a402b0ecb12606ef3d2890 Mon Sep 17 00:00:00 2001 From: E Shaw Date: Sat, 5 Aug 2017 01:28:40 +0800 Subject: [PATCH 01/11] started. retrieving old notes --- axelrod/tests/unit/test_tournament.py | 1248 ++++++++++++------------- axelrod/tournament.py | 7 +- 2 files changed, 628 insertions(+), 627 deletions(-) diff --git a/axelrod/tests/unit/test_tournament.py b/axelrod/tests/unit/test_tournament.py index 36cc585a3..e0fb544fe 100644 --- a/axelrod/tests/unit/test_tournament.py +++ b/axelrod/tests/unit/test_tournament.py @@ -61,193 +61,191 @@ def setUpClass(cls): cls.filename = "test_outputs/test_tournament.csv" - def test_init(self): - tournament = axelrod.Tournament( - name=self.test_name, - players=self.players, - game=self.game, - turns=self.test_turns, - noise=0.2) - self.assertEqual(len(tournament.players), len(test_strategies)) - self.assertIsInstance( - tournament.players[0].match_attributes['game'], axelrod.Game - ) - self.assertEqual(tournament.game.score((C, C)), (3, 3)) - self.assertEqual(tournament.turns, self.test_turns) - self.assertEqual(tournament.repetitions, 10) - self.assertEqual(tournament.name, 'test') - self.assertIsInstance(tournament._logger, logging.Logger) - self.assertEqual(tournament.noise, 0.2) - anonymous_tournament = axelrod.Tournament(players=self.players) - self.assertEqual(anonymous_tournament.name, 'axelrod') - - def test_init_with_match_attributes(self): - tournament = axelrod.Tournament( - players=self.players, - match_attributes={"length": float('inf')}) - mg = tournament.match_generator - match_params = mg.build_single_match_params() - self.assertEqual(match_params["match_attributes"], - {"length": float('inf')}) - - def test_warning(self): - tournament = axelrod.Tournament(name=self.test_name, - players=self.players, - game=self.game, - turns=10, - repetitions=1) - with warnings.catch_warnings(record=True) as w: - # Check that a warning is raised if no results set is built and no - # filename is given - results = tournament.play(build_results=False, progress_bar=False) - self.assertEqual(len(w), 1) - - with warnings.catch_warnings(record=True) as w: - # Check that no warning is raised if no results set is built and a - # is filename given - - tournament.play(build_results=False, - filename=self.filename, progress_bar=False) - self.assertEqual(len(w), 0) - - def test_serial_play(self): - # Test that we get an instance of ResultSet - tournament = axelrod.Tournament( - name=self.test_name, - players=self.players, - game=self.game, - turns=axelrod.DEFAULT_TURNS, - repetitions=self.test_repetitions) - results = tournament.play(progress_bar=False) - self.assertIsInstance(results, axelrod.ResultSet) - - # Test that _run_serial_repetitions is called with empty matches list - tournament = axelrod.Tournament( - name=self.test_name, - players=self.players, - game=self.game, - turns=axelrod.DEFAULT_TURNS, - repetitions=self.test_repetitions) - results = tournament.play(progress_bar=False) - self.assertEqual(tournament.num_interactions, 75) - - def test_serial_play_with_different_game(self): - # Test that a non default game is passed to the result set - game = axelrod.Game(p=-1, r=-1, s=-1, t=-1) - tournament = axelrod.Tournament( - name=self.test_name, - players=self.players, - game=game, - turns=1, - repetitions=1) - results = tournament.play(progress_bar=False) - self.assertEqual(results.game.RPST(), (-1, -1, -1, -1)) - - def test_no_progress_bar_play(self): - """Test that progress bar is not created for progress_bar=False""" - tournament = axelrod.Tournament( - name=self.test_name, - players=self.players, - game=self.game, - turns=axelrod.DEFAULT_TURNS, - repetitions=self.test_repetitions) - - - # Test with build results - results = tournament.play(progress_bar=False) - self.assertIsInstance(results, axelrod.ResultSet) - # Check that no progress bar was created - call_progress_bar = lambda: tournament.progress_bar.total - self.assertRaises(AttributeError, call_progress_bar) - - # Test without build results - results = tournament.play(progress_bar=False, build_results=False, - filename=self.filename) - self.assertIsNone(results) - results = axelrod.ResultSetFromFile(self.filename, progress_bar=False) - self.assertIsInstance(results, axelrod.ResultSet) - self.assertRaises(AttributeError, call_progress_bar) - - def test_progress_bar_play(self): - """Test that progress bar is created by default and with True argument""" - tournament = axelrod.Tournament( - name=self.test_name, - players=self.players, - game=self.game, - turns=axelrod.DEFAULT_TURNS, - repetitions=self.test_repetitions) - - results = tournament.play() - self.assertIsInstance(results, axelrod.ResultSet) - self.assertEqual(tournament.progress_bar.total, 15) - self.assertEqual(tournament.progress_bar.total, - tournament.progress_bar.n) - - results = tournament.play(progress_bar=True) - self.assertIsInstance(results, axelrod.ResultSet) - self.assertEqual(tournament.progress_bar.total, 15) - self.assertEqual(tournament.progress_bar.total, - tournament.progress_bar.n) + # def test_init(self): + # tournament = axelrod.Tournament( + # name=self.test_name, + # players=self.players, + # game=self.game, + # turns=self.test_turns, + # noise=0.2) + # self.assertEqual(len(tournament.players), len(test_strategies)) + # self.assertIsInstance( + # tournament.players[0].match_attributes['game'], axelrod.Game + # ) + # self.assertEqual(tournament.game.score((C, C)), (3, 3)) + # self.assertEqual(tournament.turns, self.test_turns) + # self.assertEqual(tournament.repetitions, 10) + # self.assertEqual(tournament.name, 'test') + # self.assertIsInstance(tournament._logger, logging.Logger) + # self.assertEqual(tournament.noise, 0.2) + # anonymous_tournament = axelrod.Tournament(players=self.players) + # self.assertEqual(anonymous_tournament.name, 'axelrod') + # + # def test_init_with_match_attributes(self): + # tournament = axelrod.Tournament( + # players=self.players, + # match_attributes={"length": float('inf')}) + # mg = tournament.match_generator + # match_params = mg.build_single_match_params() + # self.assertEqual(match_params["match_attributes"], + # {"length": float('inf')}) + # + # def test_warning(self): + # tournament = axelrod.Tournament(name=self.test_name, + # players=self.players, + # game=self.game, + # turns=10, + # repetitions=1) + # with warnings.catch_warnings(record=True) as w: + # # Check that a warning is raised if no results set is built and no + # # filename is given + # results = tournament.play(build_results=False, progress_bar=False) + # self.assertEqual(len(w), 1) + # + # with warnings.catch_warnings(record=True) as w: + # # Check that no warning is raised if no results set is built and a + # # is filename given + # + # tournament.play(build_results=False, + # filename=self.filename, progress_bar=False) + # self.assertEqual(len(w), 0) + # + # def test_serial_play(self): + # # Test that we get an instance of ResultSet + # tournament = axelrod.Tournament( + # name=self.test_name, + # players=self.players, + # game=self.game, + # turns=axelrod.DEFAULT_TURNS, + # repetitions=self.test_repetitions) + # results = tournament.play(progress_bar=False) + # self.assertIsInstance(results, axelrod.ResultSet) + # + # # Test that _run_serial_repetitions is called with empty matches list + # tournament = axelrod.Tournament( + # name=self.test_name, + # players=self.players, + # game=self.game, + # turns=axelrod.DEFAULT_TURNS, + # repetitions=self.test_repetitions) + # results = tournament.play(progress_bar=False) + # self.assertEqual(tournament.num_interactions, 75) + # + # def test_serial_play_with_different_game(self): + # # Test that a non default game is passed to the result set + # game = axelrod.Game(p=-1, r=-1, s=-1, t=-1) + # tournament = axelrod.Tournament( + # name=self.test_name, + # players=self.players, + # game=game, + # turns=1, + # repetitions=1) + # results = tournament.play(progress_bar=False) + # self.assertEqual(results.game.RPST(), (-1, -1, -1, -1)) + # + # def test_no_progress_bar_play(self): + # """Test that progress bar is not created for progress_bar=False""" + # tournament = axelrod.Tournament( + # name=self.test_name, + # players=self.players, + # game=self.game, + # turns=axelrod.DEFAULT_TURNS, + # repetitions=self.test_repetitions) + # + # + # # Test with build results + # results = tournament.play(progress_bar=False) + # self.assertIsInstance(results, axelrod.ResultSet) + # # Check that no progress bar was created + # call_progress_bar = lambda: tournament.progress_bar.total + # self.assertRaises(AttributeError, call_progress_bar) + # + # # Test without build results + # results = tournament.play(progress_bar=False, build_results=False, + # filename=self.filename) + # self.assertIsNone(results) + # results = axelrod.ResultSetFromFile(self.filename, progress_bar=False) + # self.assertIsInstance(results, axelrod.ResultSet) + # self.assertRaises(AttributeError, call_progress_bar) + # + # def test_progress_bar_play(self): + # """Test that progress bar is created by default and with True argument""" + # tournament = axelrod.Tournament( + # name=self.test_name, + # players=self.players, + # game=self.game, + # turns=axelrod.DEFAULT_TURNS, + # repetitions=self.test_repetitions) + # + # results = tournament.play() + # self.assertIsInstance(results, axelrod.ResultSet) + # self.assertEqual(tournament.progress_bar.total, 15) + # self.assertEqual(tournament.progress_bar.total, + # tournament.progress_bar.n) + # + # results = tournament.play(progress_bar=True) + # self.assertIsInstance(results, axelrod.ResultSet) + # self.assertEqual(tournament.progress_bar.total, 15) + # self.assertEqual(tournament.progress_bar.total, + # tournament.progress_bar.n) + # + # # Test without build results + # results = tournament.play(progress_bar=True, build_results=False, + # filename=self.filename) + # self.assertIsNone(results) + # results = axelrod.ResultSetFromFile(self.filename) + # self.assertIsInstance(results, axelrod.ResultSet) + # self.assertEqual(tournament.progress_bar.total, 15) + # self.assertEqual(tournament.progress_bar.total, + # tournament.progress_bar.n) + # + # @unittest.skipIf(axelrod.on_windows, + # "Parallel processing not supported on Windows") + # def test_progress_bar_play_parallel(self): + # """Test that tournament plays when asking for progress bar for parallel + # tournament""" + # tournament = axelrod.Tournament( + # name=self.test_name, + # players=self.players, + # game=self.game, + # turns=axelrod.DEFAULT_TURNS, + # repetitions=self.test_repetitions) + # + # results = tournament.play(processes=2) + # self.assertIsInstance(results, axelrod.ResultSet) + # + # results = tournament.play(progress_bar=True) + # self.assertIsInstance(results, axelrod.ResultSet) + # + # @given(tournament=tournaments(min_size=2, max_size=5, min_turns=2, + # max_turns=10, min_repetitions=2, + # max_repetitions=4)) + # @settings(max_examples=10, timeout=0) + # @example(tournament=axelrod.Tournament(players=[s() for s in + # test_strategies], turns=test_turns, repetitions=test_repetitions) + # ) + # + # # These two examples are to make sure #465 is fixed. + # # As explained there: /~https://github.com/Axelrod-Python/Axelrod/issues/465, + # # these two examples were identified by hypothesis. + # @example(tournament= + # axelrod.Tournament(players=[axelrod.BackStabber(), + # axelrod.MindReader()], + # turns=2, repetitions=1), + # ) + # @example(tournament= + # axelrod.Tournament(players=[axelrod.BackStabber(), + # axelrod.ThueMorse()], + # turns=2, repetitions=1), + # ) + # def test_property_serial_play(self, tournament): + # """Test serial play using hypothesis""" + # # Test that we get an instance of ResultSet + # results = tournament.play(progress_bar=False) + # self.assertIsInstance(results, axelrod.ResultSet) + # self.assertEqual(results.nplayers, len(tournament.players)) + # self.assertEqual(results.players, [str(p) for p in tournament.players]) - # Test without build results - results = tournament.play(progress_bar=True, build_results=False, - filename=self.filename) - self.assertIsNone(results) - results = axelrod.ResultSetFromFile(self.filename) - self.assertIsInstance(results, axelrod.ResultSet) - self.assertEqual(tournament.progress_bar.total, 15) - self.assertEqual(tournament.progress_bar.total, - tournament.progress_bar.n) - - @unittest.skipIf(axelrod.on_windows, - "Parallel processing not supported on Windows") - def test_progress_bar_play_parallel(self): - """Test that tournament plays when asking for progress bar for parallel - tournament""" - tournament = axelrod.Tournament( - name=self.test_name, - players=self.players, - game=self.game, - turns=axelrod.DEFAULT_TURNS, - repetitions=self.test_repetitions) - - results = tournament.play(processes=2) - self.assertIsInstance(results, axelrod.ResultSet) - - results = tournament.play(progress_bar=True) - self.assertIsInstance(results, axelrod.ResultSet) - - @given(tournament=tournaments(min_size=2, max_size=5, min_turns=2, - max_turns=10, min_repetitions=2, - max_repetitions=4)) - @settings(max_examples=10, timeout=0) - @example(tournament=axelrod.Tournament(players=[s() for s in - test_strategies], turns=test_turns, repetitions=test_repetitions) - ) - - # These two examples are to make sure #465 is fixed. - # As explained there: /~https://github.com/Axelrod-Python/Axelrod/issues/465, - # these two examples were identified by hypothesis. - @example(tournament= - axelrod.Tournament(players=[axelrod.BackStabber(), - axelrod.MindReader()], - turns=2, repetitions=1), - ) - @example(tournament= - axelrod.Tournament(players=[axelrod.BackStabber(), - axelrod.ThueMorse()], - turns=2, repetitions=1), - ) - def test_property_serial_play(self, tournament): - """Test serial play using hypothesis""" - # Test that we get an instance of ResultSet - results = tournament.play(progress_bar=False) - self.assertIsInstance(results, axelrod.ResultSet) - self.assertEqual(results.nplayers, len(tournament.players)) - self.assertEqual(results.players, [str(p) for p in tournament.players]) - - @unittest.skipIf(axelrod.on_windows, - "Parallel processing not supported on Windows") def test_parallel_play(self): # Test that we get an instance of ResultSet tournament = axelrod.Tournament( @@ -256,7 +254,7 @@ def test_parallel_play(self): game=self.game, turns=axelrod.DEFAULT_TURNS, repetitions=self.test_repetitions) - results = tournament.play(processes=2, progress_bar=False) + results = tournament.play(processes=4, progress_bar=False) self.assertIsInstance(results, axelrod.ResultSet) self.assertEqual(tournament.num_interactions, 75) @@ -273,444 +271,444 @@ def test_parallel_play(self): scores = tournament.play(processes=2, progress_bar=False).scores self.assertEqual(len(scores), len(players)) - def test_run_serial(self): - tournament = axelrod.Tournament( - name=self.test_name, - players=self.players, - game=self.game, - turns=axelrod.DEFAULT_TURNS, - repetitions=self.test_repetitions) - tournament._write_interactions = MagicMock( - name='_write_interactions') - self.assertTrue(tournament._run_serial()) - - # Get the calls made to write_interactions - calls = tournament._write_interactions.call_args_list - self.assertEqual(len(calls), 15) - - @unittest.skipIf(axelrod.on_windows, - "Parallel processing not supported on Windows") - def test_run_parallel(self): - tournament = axelrod.Tournament( - name=self.test_name, - players=self.players, - game=self.game, - turns=axelrod.DEFAULT_TURNS, - repetitions=self.test_repetitions) - tournament._write_interactions = MagicMock( - name='_write_interactions') - self.assertTrue(tournament._run_parallel()) - - # Get the calls made to write_interactions - calls = tournament._write_interactions.call_args_list - self.assertEqual(len(calls), 15) - - @unittest.skipIf(axelrod.on_windows, - "Parallel processing not supported on Windows") - def test_n_workers(self): - max_processes = cpu_count() - - tournament = axelrod.Tournament( - name=self.test_name, - players=self.players, - game=self.game, - turns=axelrod.DEFAULT_TURNS, - repetitions=self.test_repetitions) - self.assertEqual(tournament._n_workers(processes=1), max_processes) - - tournament = axelrod.Tournament( - name=self.test_name, - players=self.players, - game=self.game, - turns=axelrod.DEFAULT_TURNS, - repetitions=self.test_repetitions) - self.assertEqual(tournament._n_workers(processes=max_processes+2), - max_processes) - - @unittest.skipIf(axelrod.on_windows, - "Parallel processing not supported on Windows") - @unittest.skipIf( - cpu_count() < 2, - "not supported on single processor machines") - def test_2_workers(self): - # This is a separate test with a skip condition because we - # cannot guarantee that the tests will always run on a machine - # with more than one processor - tournament = axelrod.Tournament( - name=self.test_name, - players=self.players, - game=self.game, - turns=axelrod.DEFAULT_TURNS, - repetitions=self.test_repetitions,) - self.assertEqual(tournament._n_workers(processes=2), 2) - - @unittest.skipIf(axelrod.on_windows, - "Parallel processing not supported on Windows") - def test_start_workers(self): - workers = 2 - work_queue = Queue() - done_queue = Queue() - tournament = axelrod.Tournament( - name=self.test_name, - players=self.players, - game=self.game, - turns=axelrod.DEFAULT_TURNS, - repetitions=self.test_repetitions) - chunks = tournament.match_generator.build_match_chunks() - for chunk in chunks: - work_queue.put(chunk) - tournament._start_workers(workers, work_queue, done_queue) - - stops = 0 - while stops < workers: - payoffs = done_queue.get() - if payoffs == 'STOP': - stops += 1 - self.assertEqual(stops, workers) - - @unittest.skipIf(axelrod.on_windows, - "Parallel processing not supported on Windows") - def test_worker(self): - tournament = axelrod.Tournament( - name=self.test_name, - players=self.players, - game=self.game, - turns=axelrod.DEFAULT_TURNS, - repetitions=self.test_repetitions) - - work_queue = Queue() - chunks = tournament.match_generator.build_match_chunks() - count = 0 - for chunk in chunks: - work_queue.put(chunk) - count += 1 - work_queue.put('STOP') - - done_queue = Queue() - tournament._worker(work_queue, done_queue) - for r in range(count): - new_matches = done_queue.get() - for index_pair, matches in new_matches.items(): - self.assertIsInstance(index_pair, tuple) - self.assertEqual(len(matches), self.test_repetitions) - queue_stop = done_queue.get() - self.assertEqual(queue_stop, 'STOP') - - def test_build_result_set(self): - tournament = axelrod.Tournament( - name=self.test_name, - players=self.players, - game=self.game, - turns=axelrod.DEFAULT_TURNS, - repetitions=self.test_repetitions) - results = tournament.play(progress_bar=False) - self.assertIsInstance(results, axelrod.ResultSet) - - # Test in memory - results = tournament.play(progress_bar=False, in_memory=True) - self.assertIsInstance(results, axelrod.ResultSet) - - def test_no_build_result_set(self): - tournament = axelrod.Tournament( - name=self.test_name, - players=self.players, - game=self.game, - turns=axelrod.DEFAULT_TURNS, - repetitions=self.test_repetitions) - - results = tournament.play(build_results=False, filename=self.filename, - progress_bar=False) - self.assertIsNone(results) - - # Checking that results were written properly - results = axelrod.ResultSetFromFile(self.filename, progress_bar=False) - self.assertIsInstance(results, axelrod.ResultSet) - - @given(turns=integers(min_value=1, max_value=200)) - @example(turns=3) - @example(turns=axelrod.DEFAULT_TURNS) - def test_play_matches(self, turns): - tournament = axelrod.Tournament(name=self.test_name, - players=self.players, - game=self.game, - repetitions=self.test_repetitions) - - def make_chunk_generator(): - for player1_index in range(len(self.players)): - for player2_index in range(player1_index, len(self.players)): - index_pair = (player1_index, player2_index) - match_params = {"turns": turns, "game": self.game} - yield (index_pair, match_params, self.test_repetitions) - - chunk_generator = make_chunk_generator() - interactions = {} - for chunk in chunk_generator: - result = tournament._play_matches(chunk) - for index_pair, inters in result.items(): - try: - interactions[index_pair].append(inters) - except KeyError: - interactions[index_pair] = [inters] - - self.assertEqual(len(interactions), 15) - - for index_pair, inter in interactions.items(): - self.assertEqual(len(index_pair), 2) - for plays in inter: - # Check that have the expected number of repetitions - self.assertEqual(len(plays), self.test_repetitions) - for repetition in plays: - # Check that have the correct length for each rep - self.assertEqual(len(repetition), turns) - - # Check that matches no longer exist - self.assertEqual((len(list(chunk_generator))), 0) - - def test_match_cache_is_used(self): - """ - Create two Random players that are classified as deterministic. - As they are deterministic the cache will be used. - """ - FakeRandom = axelrod.Random - FakeRandom.classifier["stochastic"] = False - p1 = FakeRandom() - p2 = FakeRandom() - tournament = axelrod.Tournament((p1, p2), turns=5, repetitions=2) - results = tournament.play(progress_bar=False) - for player_scores in results.scores: - self.assertEqual(player_scores[0], player_scores[1]) - - def test_write_interactions(self): - tournament = axelrod.Tournament( - - name=self.test_name, - players=self.players, - game=self.game, - turns=2, - repetitions=2) - tournament._write_interactions = MagicMock(name='_write_interactions') - # Mocking this as it is called by play - tournament._build_result_set = MagicMock(name='_build_result_set') - self.assertTrue(tournament.play(filename=self.filename, - progress_bar=False)) - tournament.outputfile.close() # Normally closed by `build_result_set` - - # Get the calls made to write_interactions - calls = tournament._write_interactions.call_args_list - self.assertEqual(len(calls), 15) - - # Test when running in memory - tournament._write_interactions = MagicMock(name='_write_interactions') - self.assertTrue(tournament.play(filename=self.filename, - progress_bar=False, - in_memory=False)) - # Get the calls made to write_interactions - calls = tournament._write_interactions.call_args_list - self.assertEqual(len(calls), 15) - tournament.outputfile.close() # Normally closed by `write_interactions` - - def test_write_to_csv(self): - tournament = axelrod.Tournament( - name=self.test_name, - players=self.players, - game=self.game, - turns=2, - repetitions=2) - tournament.play(filename=self.filename, progress_bar=False) - with open(self.filename, 'r') as f: - written_data = [[int(r[0]), int(r[1])] + r[2:] for r in csv.reader(f)] - expected_data = [[0, 1, 'Cooperator', 'Tit For Tat', 'CC', 'CC'], - [0, 1, 'Cooperator', 'Tit For Tat', 'CC', 'CC'], - [1, 2, 'Tit For Tat', 'Defector', 'CD', 'DD'], - [1, 2, 'Tit For Tat', 'Defector', 'CD', 'DD'], - [0, 0, 'Cooperator', 'Cooperator', 'CC', 'CC'], - [0, 0, 'Cooperator', 'Cooperator', 'CC', 'CC'], - [3, 3, 'Grudger', 'Grudger', 'CC', 'CC'], - [3, 3, 'Grudger', 'Grudger', 'CC', 'CC'], - [2, 2, 'Defector', 'Defector', 'DD', 'DD'], - [2, 2, 'Defector', 'Defector', 'DD', 'DD'], - [4, 4, 'Soft Go By Majority', 'Soft Go By Majority', 'CC', 'CC'], - [4, 4, 'Soft Go By Majority', 'Soft Go By Majority', 'CC', 'CC'], - [1, 4, 'Tit For Tat', 'Soft Go By Majority', 'CC', 'CC'], - [1, 4, 'Tit For Tat', 'Soft Go By Majority', 'CC', 'CC'], - [1, 1, 'Tit For Tat', 'Tit For Tat', 'CC', 'CC'], - [1, 1, 'Tit For Tat', 'Tit For Tat', 'CC', 'CC'], - [1, 3, 'Tit For Tat', 'Grudger', 'CC', 'CC'], - [1, 3, 'Tit For Tat', 'Grudger', 'CC', 'CC'], - [2, 3, 'Defector', 'Grudger', 'DD', 'CD'], - [2, 3, 'Defector', 'Grudger', 'DD', 'CD'], - [0, 4, 'Cooperator', 'Soft Go By Majority', 'CC', 'CC'], - [0, 4, 'Cooperator', 'Soft Go By Majority', 'CC', 'CC'], - [2, 4, 'Defector', 'Soft Go By Majority', 'DD', 'CD'], - [2, 4, 'Defector', 'Soft Go By Majority', 'DD', 'CD'], - [0, 3, 'Cooperator', 'Grudger', 'CC', 'CC'], - [0, 3, 'Cooperator', 'Grudger', 'CC', 'CC'], - [3, 4, 'Grudger', 'Soft Go By Majority', 'CC', 'CC'], - [3, 4, 'Grudger', 'Soft Go By Majority', 'CC', 'CC'], - [0, 2, 'Cooperator', 'Defector', 'CC', 'DD'], - [0, 2, 'Cooperator', 'Defector', 'CC', 'DD']] - self.assertEqual(sorted(written_data), sorted(expected_data)) - - -class TestProbEndTournament(unittest.TestCase): - - @classmethod - def setUpClass(cls): - cls.game = axelrod.Game() - cls.players = [s() for s in test_strategies] - cls.test_name = 'test' - cls.test_repetitions = test_repetitions - cls.test_prob_end = test_prob_end - - def test_init(self): - tournament = axelrod.Tournament(name=self.test_name, - players=self.players, - game=self.game, - prob_end=self.test_prob_end, - noise=0.2) - self.assertEqual(tournament.match_generator.prob_end, tournament.prob_end) - self.assertEqual(len(tournament.players), len(test_strategies)) - self.assertEqual(tournament.game.score((C, C)), (3, 3)) - self.assertIsNone(tournament.turns) - self.assertEqual(tournament.repetitions, 10) - self.assertEqual(tournament.name, 'test') - self.assertIsInstance(tournament._logger, logging.Logger) - self.assertEqual(tournament.noise, 0.2) - anonymous_tournament = axelrod.Tournament(players=self.players) - self.assertEqual(anonymous_tournament.name, 'axelrod') - - @given(tournament=prob_end_tournaments(min_size=2, max_size=5, - min_prob_end=.1, - max_prob_end=.9, - min_repetitions=2, - max_repetitions=4)) - @settings(max_examples=50, timeout=0) - @example(tournament= - axelrod.Tournament(players=[s() for s in test_strategies], - prob_end=.2, repetitions=test_repetitions)) - - # These two examples are to make sure #465 is fixed. - # As explained there: /~https://github.com/Axelrod-Python/Axelrod/issues/465, - # these two examples were identified by hypothesis. - @example(tournament= - axelrod.Tournament(players=[axelrod.BackStabber(), - axelrod.MindReader()], - prob_end=.2, repetitions=1)) - @example(tournament= - axelrod.Tournament(players=[axelrod.ThueMorse(), - axelrod.MindReader()], - prob_end=.2, repetitions=1)) - def test_property_serial_play(self, tournament): - """Test serial play using hypothesis""" - # Test that we get an instance of ResultSet - results = tournament.play(progress_bar=False) - self.assertIsInstance(results, axelrod.ResultSet) - self.assertEqual(results.nplayers, len(tournament.players)) - self.assertEqual(results.players, [str(p) for p in tournament.players]) - - -class TestSpatialTournament(unittest.TestCase): - - @classmethod - def setUpClass(cls): - cls.game = axelrod.Game() - cls.players = [s() for s in test_strategies] - cls.test_name = 'test' - cls.test_repetitions = test_repetitions - cls.test_turns = test_turns - cls.test_edges = test_edges - - def test_init(self): - tournament = axelrod.Tournament(name=self.test_name, - players=self.players, - game=self.game, - turns=self.test_turns, - edges=self.test_edges, - noise=0.2) - self.assertEqual(tournament.match_generator.edges, tournament.edges) - self.assertEqual(len(tournament.players), len(test_strategies)) - self.assertEqual(tournament.game.score((C, C)), (3, 3)) - self.assertEqual(tournament.turns, 100) - self.assertEqual(tournament.repetitions, 10) - self.assertEqual(tournament.name, 'test') - self.assertIsInstance(tournament._logger, logging.Logger) - self.assertEqual(tournament.noise, 0.2) - self.assertEqual(tournament.match_generator.noise, 0.2) - anonymous_tournament = axelrod.Tournament(players=self.players) - self.assertEqual(anonymous_tournament.name, 'axelrod') - - @given(strategies=strategy_lists(strategies=deterministic_strategies, - min_size=2, max_size=2), - turns=integers(min_value=1, max_value=20), - repetitions=integers(min_value=1, max_value=5), - noise=floats(min_value=0, max_value=1), - seed=integers(min_value=0, max_value=4294967295)) - @settings(max_examples=50, timeout=0) - def test_complete_tournament(self, strategies, turns, repetitions, - noise, seed): - """ - A test to check that a spatial tournament on the complete multigraph - gives the same results as the round robin. - """ - - players = [s() for s in strategies] - # edges - edges = [] - for i in range(0, len(players)): - for j in range(i, len(players)): - edges.append((i, j)) - - # create a round robin tournament - tournament = axelrod.Tournament(players, repetitions=repetitions, - turns=turns, noise=noise) - # create a complete spatial tournament - spatial_tournament = axelrod.Tournament(players, - repetitions=repetitions, - turns=turns, - noise=noise, - edges=edges) - - axelrod.seed(seed) - results = tournament.play(progress_bar=False) - axelrod.seed(seed) - spatial_results = spatial_tournament.play(progress_bar=False) - - self.assertEqual(results.ranked_names, spatial_results.ranked_names) - self.assertEqual(results.nplayers, spatial_results.nplayers) - self.assertEqual(results.repetitions, spatial_results.repetitions) - self.assertEqual(results.payoff_diffs_means, - spatial_results.payoff_diffs_means) - self.assertEqual(results.payoff_matrix, spatial_results.payoff_matrix) - self.assertEqual(results.payoff_stddevs, spatial_results.payoff_stddevs) - self.assertEqual(results.payoffs, spatial_results.payoffs) - self.assertEqual(results.cooperating_rating, - spatial_results.cooperating_rating) - self.assertEqual(results.cooperation, spatial_results.cooperation) - self.assertEqual(results.normalised_cooperation, - spatial_results.normalised_cooperation) - self.assertEqual(results.normalised_scores, - spatial_results.normalised_scores) - self.assertEqual(results.good_partner_matrix, - spatial_results.good_partner_matrix) - self.assertEqual(results.good_partner_rating, - spatial_results.good_partner_rating) - - def test_particular_tournament(self): - """A test for a tournament that has caused failures during some bug - fixing""" - players = [axelrod.Cooperator(), axelrod.Defector(), - axelrod.TitForTat(), axelrod.Grudger()] - edges = [(0, 2), (0, 3), (1, 2), (1, 3)] - tournament = axelrod.Tournament(players, edges=edges) - results = tournament.play(progress_bar=False) - expected_ranked_names = ['Cooperator', 'Tit For Tat', - 'Grudger', 'Defector'] - self.assertEqual(results.ranked_names, expected_ranked_names) - - # Check that this tournament runs with noise - tournament = axelrod.Tournament(players, edges=edges, noise=.5) - results = tournament.play(progress_bar=False) - self.assertIsInstance(results, axelrod.ResultSet) +# def test_run_serial(self): +# tournament = axelrod.Tournament( +# name=self.test_name, +# players=self.players, +# game=self.game, +# turns=axelrod.DEFAULT_TURNS, +# repetitions=self.test_repetitions) +# tournament._write_interactions = MagicMock( +# name='_write_interactions') +# self.assertTrue(tournament._run_serial()) +# +# # Get the calls made to write_interactions +# calls = tournament._write_interactions.call_args_list +# self.assertEqual(len(calls), 15) +# +# @unittest.skipIf(axelrod.on_windows, +# "Parallel processing not supported on Windows") +# def test_run_parallel(self): +# tournament = axelrod.Tournament( +# name=self.test_name, +# players=self.players, +# game=self.game, +# turns=axelrod.DEFAULT_TURNS, +# repetitions=self.test_repetitions) +# tournament._write_interactions = MagicMock( +# name='_write_interactions') +# self.assertTrue(tournament._run_parallel()) +# +# # Get the calls made to write_interactions +# calls = tournament._write_interactions.call_args_list +# self.assertEqual(len(calls), 15) +# +# @unittest.skipIf(axelrod.on_windows, +# "Parallel processing not supported on Windows") +# def test_n_workers(self): +# max_processes = cpu_count() +# +# tournament = axelrod.Tournament( +# name=self.test_name, +# players=self.players, +# game=self.game, +# turns=axelrod.DEFAULT_TURNS, +# repetitions=self.test_repetitions) +# self.assertEqual(tournament._n_workers(processes=1), max_processes) +# +# tournament = axelrod.Tournament( +# name=self.test_name, +# players=self.players, +# game=self.game, +# turns=axelrod.DEFAULT_TURNS, +# repetitions=self.test_repetitions) +# self.assertEqual(tournament._n_workers(processes=max_processes+2), +# max_processes) +# +# @unittest.skipIf(axelrod.on_windows, +# "Parallel processing not supported on Windows") +# @unittest.skipIf( +# cpu_count() < 2, +# "not supported on single processor machines") +# def test_2_workers(self): +# # This is a separate test with a skip condition because we +# # cannot guarantee that the tests will always run on a machine +# # with more than one processor +# tournament = axelrod.Tournament( +# name=self.test_name, +# players=self.players, +# game=self.game, +# turns=axelrod.DEFAULT_TURNS, +# repetitions=self.test_repetitions,) +# self.assertEqual(tournament._n_workers(processes=2), 2) +# +# @unittest.skipIf(axelrod.on_windows, +# "Parallel processing not supported on Windows") +# def test_start_workers(self): +# workers = 2 +# work_queue = Queue() +# done_queue = Queue() +# tournament = axelrod.Tournament( +# name=self.test_name, +# players=self.players, +# game=self.game, +# turns=axelrod.DEFAULT_TURNS, +# repetitions=self.test_repetitions) +# chunks = tournament.match_generator.build_match_chunks() +# for chunk in chunks: +# work_queue.put(chunk) +# tournament._start_workers(workers, work_queue, done_queue) +# +# stops = 0 +# while stops < workers: +# payoffs = done_queue.get() +# if payoffs == 'STOP': +# stops += 1 +# self.assertEqual(stops, workers) +# +# @unittest.skipIf(axelrod.on_windows, +# "Parallel processing not supported on Windows") +# def test_worker(self): +# tournament = axelrod.Tournament( +# name=self.test_name, +# players=self.players, +# game=self.game, +# turns=axelrod.DEFAULT_TURNS, +# repetitions=self.test_repetitions) +# +# work_queue = Queue() +# chunks = tournament.match_generator.build_match_chunks() +# count = 0 +# for chunk in chunks: +# work_queue.put(chunk) +# count += 1 +# work_queue.put('STOP') +# +# done_queue = Queue() +# tournament._worker(work_queue, done_queue) +# for r in range(count): +# new_matches = done_queue.get() +# for index_pair, matches in new_matches.items(): +# self.assertIsInstance(index_pair, tuple) +# self.assertEqual(len(matches), self.test_repetitions) +# queue_stop = done_queue.get() +# self.assertEqual(queue_stop, 'STOP') +# +# def test_build_result_set(self): +# tournament = axelrod.Tournament( +# name=self.test_name, +# players=self.players, +# game=self.game, +# turns=axelrod.DEFAULT_TURNS, +# repetitions=self.test_repetitions) +# results = tournament.play(progress_bar=False) +# self.assertIsInstance(results, axelrod.ResultSet) +# +# # Test in memory +# results = tournament.play(progress_bar=False, in_memory=True) +# self.assertIsInstance(results, axelrod.ResultSet) +# +# def test_no_build_result_set(self): +# tournament = axelrod.Tournament( +# name=self.test_name, +# players=self.players, +# game=self.game, +# turns=axelrod.DEFAULT_TURNS, +# repetitions=self.test_repetitions) +# +# results = tournament.play(build_results=False, filename=self.filename, +# progress_bar=False) +# self.assertIsNone(results) +# +# # Checking that results were written properly +# results = axelrod.ResultSetFromFile(self.filename, progress_bar=False) +# self.assertIsInstance(results, axelrod.ResultSet) +# +# @given(turns=integers(min_value=1, max_value=200)) +# @example(turns=3) +# @example(turns=axelrod.DEFAULT_TURNS) +# def test_play_matches(self, turns): +# tournament = axelrod.Tournament(name=self.test_name, +# players=self.players, +# game=self.game, +# repetitions=self.test_repetitions) +# +# def make_chunk_generator(): +# for player1_index in range(len(self.players)): +# for player2_index in range(player1_index, len(self.players)): +# index_pair = (player1_index, player2_index) +# match_params = {"turns": turns, "game": self.game} +# yield (index_pair, match_params, self.test_repetitions) +# +# chunk_generator = make_chunk_generator() +# interactions = {} +# for chunk in chunk_generator: +# result = tournament._play_matches(chunk) +# for index_pair, inters in result.items(): +# try: +# interactions[index_pair].append(inters) +# except KeyError: +# interactions[index_pair] = [inters] +# +# self.assertEqual(len(interactions), 15) +# +# for index_pair, inter in interactions.items(): +# self.assertEqual(len(index_pair), 2) +# for plays in inter: +# # Check that have the expected number of repetitions +# self.assertEqual(len(plays), self.test_repetitions) +# for repetition in plays: +# # Check that have the correct length for each rep +# self.assertEqual(len(repetition), turns) +# +# # Check that matches no longer exist +# self.assertEqual((len(list(chunk_generator))), 0) +# +# def test_match_cache_is_used(self): +# """ +# Create two Random players that are classified as deterministic. +# As they are deterministic the cache will be used. +# """ +# FakeRandom = axelrod.Random +# FakeRandom.classifier["stochastic"] = False +# p1 = FakeRandom() +# p2 = FakeRandom() +# tournament = axelrod.Tournament((p1, p2), turns=5, repetitions=2) +# results = tournament.play(progress_bar=False) +# for player_scores in results.scores: +# self.assertEqual(player_scores[0], player_scores[1]) +# +# def test_write_interactions(self): +# tournament = axelrod.Tournament( +# +# name=self.test_name, +# players=self.players, +# game=self.game, +# turns=2, +# repetitions=2) +# tournament._write_interactions = MagicMock(name='_write_interactions') +# # Mocking this as it is called by play +# tournament._build_result_set = MagicMock(name='_build_result_set') +# self.assertTrue(tournament.play(filename=self.filename, +# progress_bar=False)) +# tournament.outputfile.close() # Normally closed by `build_result_set` +# +# # Get the calls made to write_interactions +# calls = tournament._write_interactions.call_args_list +# self.assertEqual(len(calls), 15) +# +# # Test when running in memory +# tournament._write_interactions = MagicMock(name='_write_interactions') +# self.assertTrue(tournament.play(filename=self.filename, +# progress_bar=False, +# in_memory=False)) +# # Get the calls made to write_interactions +# calls = tournament._write_interactions.call_args_list +# self.assertEqual(len(calls), 15) +# tournament.outputfile.close() # Normally closed by `write_interactions` +# +# def test_write_to_csv(self): +# tournament = axelrod.Tournament( +# name=self.test_name, +# players=self.players, +# game=self.game, +# turns=2, +# repetitions=2) +# tournament.play(filename=self.filename, progress_bar=False) +# with open(self.filename, 'r') as f: +# written_data = [[int(r[0]), int(r[1])] + r[2:] for r in csv.reader(f)] +# expected_data = [[0, 1, 'Cooperator', 'Tit For Tat', 'CC', 'CC'], +# [0, 1, 'Cooperator', 'Tit For Tat', 'CC', 'CC'], +# [1, 2, 'Tit For Tat', 'Defector', 'CD', 'DD'], +# [1, 2, 'Tit For Tat', 'Defector', 'CD', 'DD'], +# [0, 0, 'Cooperator', 'Cooperator', 'CC', 'CC'], +# [0, 0, 'Cooperator', 'Cooperator', 'CC', 'CC'], +# [3, 3, 'Grudger', 'Grudger', 'CC', 'CC'], +# [3, 3, 'Grudger', 'Grudger', 'CC', 'CC'], +# [2, 2, 'Defector', 'Defector', 'DD', 'DD'], +# [2, 2, 'Defector', 'Defector', 'DD', 'DD'], +# [4, 4, 'Soft Go By Majority', 'Soft Go By Majority', 'CC', 'CC'], +# [4, 4, 'Soft Go By Majority', 'Soft Go By Majority', 'CC', 'CC'], +# [1, 4, 'Tit For Tat', 'Soft Go By Majority', 'CC', 'CC'], +# [1, 4, 'Tit For Tat', 'Soft Go By Majority', 'CC', 'CC'], +# [1, 1, 'Tit For Tat', 'Tit For Tat', 'CC', 'CC'], +# [1, 1, 'Tit For Tat', 'Tit For Tat', 'CC', 'CC'], +# [1, 3, 'Tit For Tat', 'Grudger', 'CC', 'CC'], +# [1, 3, 'Tit For Tat', 'Grudger', 'CC', 'CC'], +# [2, 3, 'Defector', 'Grudger', 'DD', 'CD'], +# [2, 3, 'Defector', 'Grudger', 'DD', 'CD'], +# [0, 4, 'Cooperator', 'Soft Go By Majority', 'CC', 'CC'], +# [0, 4, 'Cooperator', 'Soft Go By Majority', 'CC', 'CC'], +# [2, 4, 'Defector', 'Soft Go By Majority', 'DD', 'CD'], +# [2, 4, 'Defector', 'Soft Go By Majority', 'DD', 'CD'], +# [0, 3, 'Cooperator', 'Grudger', 'CC', 'CC'], +# [0, 3, 'Cooperator', 'Grudger', 'CC', 'CC'], +# [3, 4, 'Grudger', 'Soft Go By Majority', 'CC', 'CC'], +# [3, 4, 'Grudger', 'Soft Go By Majority', 'CC', 'CC'], +# [0, 2, 'Cooperator', 'Defector', 'CC', 'DD'], +# [0, 2, 'Cooperator', 'Defector', 'CC', 'DD']] +# self.assertEqual(sorted(written_data), sorted(expected_data)) +# +# +# class TestProbEndTournament(unittest.TestCase): +# +# @classmethod +# def setUpClass(cls): +# cls.game = axelrod.Game() +# cls.players = [s() for s in test_strategies] +# cls.test_name = 'test' +# cls.test_repetitions = test_repetitions +# cls.test_prob_end = test_prob_end +# +# def test_init(self): +# tournament = axelrod.Tournament(name=self.test_name, +# players=self.players, +# game=self.game, +# prob_end=self.test_prob_end, +# noise=0.2) +# self.assertEqual(tournament.match_generator.prob_end, tournament.prob_end) +# self.assertEqual(len(tournament.players), len(test_strategies)) +# self.assertEqual(tournament.game.score((C, C)), (3, 3)) +# self.assertIsNone(tournament.turns) +# self.assertEqual(tournament.repetitions, 10) +# self.assertEqual(tournament.name, 'test') +# self.assertIsInstance(tournament._logger, logging.Logger) +# self.assertEqual(tournament.noise, 0.2) +# anonymous_tournament = axelrod.Tournament(players=self.players) +# self.assertEqual(anonymous_tournament.name, 'axelrod') +# +# @given(tournament=prob_end_tournaments(min_size=2, max_size=5, +# min_prob_end=.1, +# max_prob_end=.9, +# min_repetitions=2, +# max_repetitions=4)) +# @settings(max_examples=50, timeout=0) +# @example(tournament= +# axelrod.Tournament(players=[s() for s in test_strategies], +# prob_end=.2, repetitions=test_repetitions)) +# +# # These two examples are to make sure #465 is fixed. +# # As explained there: /~https://github.com/Axelrod-Python/Axelrod/issues/465, +# # these two examples were identified by hypothesis. +# @example(tournament= +# axelrod.Tournament(players=[axelrod.BackStabber(), +# axelrod.MindReader()], +# prob_end=.2, repetitions=1)) +# @example(tournament= +# axelrod.Tournament(players=[axelrod.ThueMorse(), +# axelrod.MindReader()], +# prob_end=.2, repetitions=1)) +# def test_property_serial_play(self, tournament): +# """Test serial play using hypothesis""" +# # Test that we get an instance of ResultSet +# results = tournament.play(progress_bar=False) +# self.assertIsInstance(results, axelrod.ResultSet) +# self.assertEqual(results.nplayers, len(tournament.players)) +# self.assertEqual(results.players, [str(p) for p in tournament.players]) +# +# +# class TestSpatialTournament(unittest.TestCase): +# +# @classmethod +# def setUpClass(cls): +# cls.game = axelrod.Game() +# cls.players = [s() for s in test_strategies] +# cls.test_name = 'test' +# cls.test_repetitions = test_repetitions +# cls.test_turns = test_turns +# cls.test_edges = test_edges +# +# def test_init(self): +# tournament = axelrod.Tournament(name=self.test_name, +# players=self.players, +# game=self.game, +# turns=self.test_turns, +# edges=self.test_edges, +# noise=0.2) +# self.assertEqual(tournament.match_generator.edges, tournament.edges) +# self.assertEqual(len(tournament.players), len(test_strategies)) +# self.assertEqual(tournament.game.score((C, C)), (3, 3)) +# self.assertEqual(tournament.turns, 100) +# self.assertEqual(tournament.repetitions, 10) +# self.assertEqual(tournament.name, 'test') +# self.assertIsInstance(tournament._logger, logging.Logger) +# self.assertEqual(tournament.noise, 0.2) +# self.assertEqual(tournament.match_generator.noise, 0.2) +# anonymous_tournament = axelrod.Tournament(players=self.players) +# self.assertEqual(anonymous_tournament.name, 'axelrod') +# +# @given(strategies=strategy_lists(strategies=deterministic_strategies, +# min_size=2, max_size=2), +# turns=integers(min_value=1, max_value=20), +# repetitions=integers(min_value=1, max_value=5), +# noise=floats(min_value=0, max_value=1), +# seed=integers(min_value=0, max_value=4294967295)) +# @settings(max_examples=50, timeout=0) +# def test_complete_tournament(self, strategies, turns, repetitions, +# noise, seed): +# """ +# A test to check that a spatial tournament on the complete multigraph +# gives the same results as the round robin. +# """ +# +# players = [s() for s in strategies] +# # edges +# edges = [] +# for i in range(0, len(players)): +# for j in range(i, len(players)): +# edges.append((i, j)) +# +# # create a round robin tournament +# tournament = axelrod.Tournament(players, repetitions=repetitions, +# turns=turns, noise=noise) +# # create a complete spatial tournament +# spatial_tournament = axelrod.Tournament(players, +# repetitions=repetitions, +# turns=turns, +# noise=noise, +# edges=edges) +# +# axelrod.seed(seed) +# results = tournament.play(progress_bar=False) +# axelrod.seed(seed) +# spatial_results = spatial_tournament.play(progress_bar=False) +# +# self.assertEqual(results.ranked_names, spatial_results.ranked_names) +# self.assertEqual(results.nplayers, spatial_results.nplayers) +# self.assertEqual(results.repetitions, spatial_results.repetitions) +# self.assertEqual(results.payoff_diffs_means, +# spatial_results.payoff_diffs_means) +# self.assertEqual(results.payoff_matrix, spatial_results.payoff_matrix) +# self.assertEqual(results.payoff_stddevs, spatial_results.payoff_stddevs) +# self.assertEqual(results.payoffs, spatial_results.payoffs) +# self.assertEqual(results.cooperating_rating, +# spatial_results.cooperating_rating) +# self.assertEqual(results.cooperation, spatial_results.cooperation) +# self.assertEqual(results.normalised_cooperation, +# spatial_results.normalised_cooperation) +# self.assertEqual(results.normalised_scores, +# spatial_results.normalised_scores) +# self.assertEqual(results.good_partner_matrix, +# spatial_results.good_partner_matrix) +# self.assertEqual(results.good_partner_rating, +# spatial_results.good_partner_rating) +# +# def test_particular_tournament(self): +# """A test for a tournament that has caused failures during some bug +# fixing""" +# players = [axelrod.Cooperator(), axelrod.Defector(), +# axelrod.TitForTat(), axelrod.Grudger()] +# edges = [(0, 2), (0, 3), (1, 2), (1, 3)] +# tournament = axelrod.Tournament(players, edges=edges) +# results = tournament.play(progress_bar=False) +# expected_ranked_names = ['Cooperator', 'Tit For Tat', +# 'Grudger', 'Defector'] +# self.assertEqual(results.ranked_names, expected_ranked_names) +# +# # Check that this tournament runs with noise +# tournament = axelrod.Tournament(players, edges=edges, noise=.5) +# results = tournament.play(progress_bar=False) +# self.assertIsInstance(results, axelrod.ResultSet) class TestProbEndingSpatialTournament(unittest.TestCase): diff --git a/axelrod/tournament.py b/axelrod/tournament.py index c84c15551..2c52702b4 100644 --- a/axelrod/tournament.py +++ b/axelrod/tournament.py @@ -4,6 +4,7 @@ from multiprocessing import Process, Queue, cpu_count from tempfile import NamedTemporaryFile import warnings +import os import tqdm @@ -134,7 +135,7 @@ def play(self, build_results: bool = True, filename: str = None, "Tournament results will not be accessible since " "build_results=False and no filename was supplied.") - if (processes is None) or (on_windows): + if processes is None: self._run_serial(progress_bar=progress_bar) else: self._run_parallel(processes=processes, progress_bar=progress_bar) @@ -257,6 +258,8 @@ def _run_parallel(self, processes: int=2, progress_bar: bool = False work_queue.put(chunk) self._start_workers(workers, work_queue, done_queue) + print('finished start_worker', done_queue.qsize()) + self._process_done_queue(workers, done_queue, progress_bar=progress_bar) return True @@ -313,7 +316,6 @@ def _process_done_queue(self, workers: int, done_queue: Queue, stops = 0 while stops < workers: results = done_queue.get() - if results == 'STOP': stops += 1 else: @@ -336,6 +338,7 @@ def _worker(self, work_queue: Queue, done_queue: Queue): """ for chunk in iter(work_queue.get, 'STOP'): interactions = self._play_matches(chunk) + print(os.getpid()) done_queue.put(interactions) done_queue.put('STOP') return True From 0ef85486d24e996f3f218ab1c33d80c253381c99 Mon Sep 17 00:00:00 2001 From: E Shaw Date: Sat, 5 Aug 2017 01:58:28 +0800 Subject: [PATCH 02/11] tests set up. --- axelrod/tests/unit/test_tournament.py | 264 ++++++++++++++------------ axelrod/tournament.py | 2 - 2 files changed, 138 insertions(+), 128 deletions(-) diff --git a/axelrod/tests/unit/test_tournament.py b/axelrod/tests/unit/test_tournament.py index e0fb544fe..f4c0dceb8 100644 --- a/axelrod/tests/unit/test_tournament.py +++ b/axelrod/tests/unit/test_tournament.py @@ -198,24 +198,23 @@ def setUpClass(cls): # self.assertEqual(tournament.progress_bar.total, 15) # self.assertEqual(tournament.progress_bar.total, # tournament.progress_bar.n) - # - # @unittest.skipIf(axelrod.on_windows, - # "Parallel processing not supported on Windows") - # def test_progress_bar_play_parallel(self): - # """Test that tournament plays when asking for progress bar for parallel - # tournament""" - # tournament = axelrod.Tournament( - # name=self.test_name, - # players=self.players, - # game=self.game, - # turns=axelrod.DEFAULT_TURNS, - # repetitions=self.test_repetitions) - # - # results = tournament.play(processes=2) - # self.assertIsInstance(results, axelrod.ResultSet) - # - # results = tournament.play(progress_bar=True) - # self.assertIsInstance(results, axelrod.ResultSet) + + @unittest.skip + def test_progress_bar_play_parallel(self): # TODO fix it! + """Test that tournament plays when asking for progress bar for parallel + tournament""" + tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=self.game, + turns=axelrod.DEFAULT_TURNS, + repetitions=self.test_repetitions) + + results = tournament.play(processes=2) + self.assertIsInstance(results, axelrod.ResultSet) + + results = tournament.play(progress_bar=True) + self.assertIsInstance(results, axelrod.ResultSet) # # @given(tournament=tournaments(min_size=2, max_size=5, min_turns=2, # max_turns=10, min_repetitions=2, @@ -271,6 +270,25 @@ def test_parallel_play(self): scores = tournament.play(processes=2, progress_bar=False).scores self.assertEqual(len(scores), len(players)) + def test_parallel_play_with_writing_to_file_on_windows(self): + """ + The default setting for `play` is to write to a NamedTemporaryFile. + This is disabled on Windows. This test ensures that parallel_play + does not conflict with writing the results to file. + """ + tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=self.game, + turns=axelrod.DEFAULT_TURNS, + repetitions=self.test_repetitions) + + results = tournament.play(processes=2, progress_bar=False, + filename=self.filename) + self.assertIsInstance(results, axelrod.ResultSet) + self.assertEqual(tournament.num_interactions, 75) + + # def test_run_serial(self): # tournament = axelrod.Tournament( # name=self.test_name, @@ -286,114 +304,108 @@ def test_parallel_play(self): # calls = tournament._write_interactions.call_args_list # self.assertEqual(len(calls), 15) # -# @unittest.skipIf(axelrod.on_windows, -# "Parallel processing not supported on Windows") -# def test_run_parallel(self): -# tournament = axelrod.Tournament( -# name=self.test_name, -# players=self.players, -# game=self.game, -# turns=axelrod.DEFAULT_TURNS, -# repetitions=self.test_repetitions) -# tournament._write_interactions = MagicMock( -# name='_write_interactions') -# self.assertTrue(tournament._run_parallel()) -# -# # Get the calls made to write_interactions -# calls = tournament._write_interactions.call_args_list -# self.assertEqual(len(calls), 15) -# -# @unittest.skipIf(axelrod.on_windows, -# "Parallel processing not supported on Windows") -# def test_n_workers(self): -# max_processes = cpu_count() -# -# tournament = axelrod.Tournament( -# name=self.test_name, -# players=self.players, -# game=self.game, -# turns=axelrod.DEFAULT_TURNS, -# repetitions=self.test_repetitions) -# self.assertEqual(tournament._n_workers(processes=1), max_processes) -# -# tournament = axelrod.Tournament( -# name=self.test_name, -# players=self.players, -# game=self.game, -# turns=axelrod.DEFAULT_TURNS, -# repetitions=self.test_repetitions) -# self.assertEqual(tournament._n_workers(processes=max_processes+2), -# max_processes) -# -# @unittest.skipIf(axelrod.on_windows, -# "Parallel processing not supported on Windows") -# @unittest.skipIf( -# cpu_count() < 2, -# "not supported on single processor machines") -# def test_2_workers(self): -# # This is a separate test with a skip condition because we -# # cannot guarantee that the tests will always run on a machine -# # with more than one processor -# tournament = axelrod.Tournament( -# name=self.test_name, -# players=self.players, -# game=self.game, -# turns=axelrod.DEFAULT_TURNS, -# repetitions=self.test_repetitions,) -# self.assertEqual(tournament._n_workers(processes=2), 2) -# -# @unittest.skipIf(axelrod.on_windows, -# "Parallel processing not supported on Windows") -# def test_start_workers(self): -# workers = 2 -# work_queue = Queue() -# done_queue = Queue() -# tournament = axelrod.Tournament( -# name=self.test_name, -# players=self.players, -# game=self.game, -# turns=axelrod.DEFAULT_TURNS, -# repetitions=self.test_repetitions) -# chunks = tournament.match_generator.build_match_chunks() -# for chunk in chunks: -# work_queue.put(chunk) -# tournament._start_workers(workers, work_queue, done_queue) -# -# stops = 0 -# while stops < workers: -# payoffs = done_queue.get() -# if payoffs == 'STOP': -# stops += 1 -# self.assertEqual(stops, workers) -# -# @unittest.skipIf(axelrod.on_windows, -# "Parallel processing not supported on Windows") -# def test_worker(self): -# tournament = axelrod.Tournament( -# name=self.test_name, -# players=self.players, -# game=self.game, -# turns=axelrod.DEFAULT_TURNS, -# repetitions=self.test_repetitions) -# -# work_queue = Queue() -# chunks = tournament.match_generator.build_match_chunks() -# count = 0 -# for chunk in chunks: -# work_queue.put(chunk) -# count += 1 -# work_queue.put('STOP') -# -# done_queue = Queue() -# tournament._worker(work_queue, done_queue) -# for r in range(count): -# new_matches = done_queue.get() -# for index_pair, matches in new_matches.items(): -# self.assertIsInstance(index_pair, tuple) -# self.assertEqual(len(matches), self.test_repetitions) -# queue_stop = done_queue.get() -# self.assertEqual(queue_stop, 'STOP') -# + def test_run_parallel(self): + class PickleableMock(MagicMock): + def __reduce__(self): + return MagicMock, () + + tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=self.game, + turns=axelrod.DEFAULT_TURNS, + repetitions=self.test_repetitions) + tournament._write_interactions = PickleableMock( + name='_write_interactions') + self.assertTrue(tournament._run_parallel()) + + # Get the calls made to write_interactions + calls = tournament._write_interactions.call_args_list + self.assertEqual(len(calls), 15) + + def test_n_workers(self): + max_processes = cpu_count() + + tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=self.game, + turns=axelrod.DEFAULT_TURNS, + repetitions=self.test_repetitions) + self.assertEqual(tournament._n_workers(processes=1), max_processes) + + tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=self.game, + turns=axelrod.DEFAULT_TURNS, + repetitions=self.test_repetitions) + self.assertEqual(tournament._n_workers(processes=max_processes+2), + max_processes) + + @unittest.skipIf( + cpu_count() < 2, + "not supported on single processor machines") + def test_2_workers(self): + # This is a separate test with a skip condition because we + # cannot guarantee that the tests will always run on a machine + # with more than one processor + tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=self.game, + turns=axelrod.DEFAULT_TURNS, + repetitions=self.test_repetitions,) + self.assertEqual(tournament._n_workers(processes=2), 2) + + def test_start_workers(self): + workers = 2 + work_queue = Queue() + done_queue = Queue() + tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=self.game, + turns=axelrod.DEFAULT_TURNS, + repetitions=self.test_repetitions) + chunks = tournament.match_generator.build_match_chunks() + for chunk in chunks: + work_queue.put(chunk) + tournament._start_workers(workers, work_queue, done_queue) + + stops = 0 + while stops < workers: + payoffs = done_queue.get() + if payoffs == 'STOP': + stops += 1 + self.assertEqual(stops, workers) + + def test_worker(self): + tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=self.game, + turns=axelrod.DEFAULT_TURNS, + repetitions=self.test_repetitions) + + work_queue = Queue() + chunks = tournament.match_generator.build_match_chunks() + count = 0 + for chunk in chunks: + work_queue.put(chunk) + count += 1 + work_queue.put('STOP') + + done_queue = Queue() + tournament._worker(work_queue, done_queue) + for r in range(count): + new_matches = done_queue.get() + for index_pair, matches in new_matches.items(): + self.assertIsInstance(index_pair, tuple) + self.assertEqual(len(matches), self.test_repetitions) + queue_stop = done_queue.get() + self.assertEqual(queue_stop, 'STOP') + # def test_build_result_set(self): # tournament = axelrod.Tournament( # name=self.test_name, diff --git a/axelrod/tournament.py b/axelrod/tournament.py index 2c52702b4..14c9f7daa 100644 --- a/axelrod/tournament.py +++ b/axelrod/tournament.py @@ -258,7 +258,6 @@ def _run_parallel(self, processes: int=2, progress_bar: bool = False work_queue.put(chunk) self._start_workers(workers, work_queue, done_queue) - print('finished start_worker', done_queue.qsize()) self._process_done_queue(workers, done_queue, progress_bar=progress_bar) @@ -338,7 +337,6 @@ def _worker(self, work_queue: Queue, done_queue: Queue): """ for chunk in iter(work_queue.get, 'STOP'): interactions = self._play_matches(chunk) - print(os.getpid()) done_queue.put(interactions) done_queue.put('STOP') return True From ffbd427e0be6d97fcb66aac2287afb7622018155 Mon Sep 17 00:00:00 2001 From: E Shaw Date: Sat, 5 Aug 2017 23:26:06 +0800 Subject: [PATCH 03/11] basic file handling. ugly --- axelrod/tournament.py | 52 +++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/axelrod/tournament.py b/axelrod/tournament.py index 14c9f7daa..622c09090 100644 --- a/axelrod/tournament.py +++ b/axelrod/tournament.py @@ -81,17 +81,19 @@ def setup_output(self, filename=None, in_memory=False): """Open a CSV writer for tournament output.""" if in_memory: self.interactions_dict = {} - self.writer = None + outputfile = None + writer = None else: if filename: - self.outputfile = open(filename, 'w') + outputfile = open(filename, 'w') else: # Setup a temporary file - self.outputfile = NamedTemporaryFile(mode='w') - filename = self.outputfile.name - self.writer = csv.writer(self.outputfile, lineterminator='\n') + outputfile = NamedTemporaryFile(mode='w') + filename = outputfile.name + writer = csv.writer(outputfile, lineterminator='\n') # Save filename for loading ResultSet later self.filename = filename + return outputfile, writer def play(self, build_results: bool = True, filename: str = None, processes: int = None, progress_bar: bool = True, @@ -128,7 +130,7 @@ def play(self, build_results: bool = True, filename: str = None, if on_windows and (filename is None): # pragma: no cover in_memory = True - self.setup_output(filename, in_memory) + outputfile, writer = self.setup_output(filename, in_memory) if not build_results and not filename: warnings.warn( @@ -136,23 +138,25 @@ def play(self, build_results: bool = True, filename: str = None, "build_results=False and no filename was supplied.") if processes is None: - self._run_serial(progress_bar=progress_bar) + self._run_serial(progress_bar=progress_bar, writer=writer) else: - self._run_parallel(processes=processes, progress_bar=progress_bar) + self._run_parallel(processes=processes, progress_bar=progress_bar, + writer=writer) if progress_bar: self.progress_bar.close() # Make sure that python has finished writing to disk if not in_memory: - self.outputfile.flush() + outputfile.flush() if build_results: return self._build_result_set(progress_bar=progress_bar, keep_interactions=keep_interactions, in_memory=in_memory) - elif not in_memory: - self.outputfile.close() + outputfile.close() + # elif not in_memory: + # self.outputfile.close() def _build_result_set(self, progress_bar: bool = True, keep_interactions: bool = False, @@ -173,7 +177,7 @@ def _build_result_set(self, progress_bar: bool = True, players=[str(p) for p in self.players], keep_interactions=keep_interactions, game=self.game) - self.outputfile.close() + # self.outputfile.close() else: result_set = ResultSet( players=[str(p) for p in self.players], @@ -183,7 +187,8 @@ def _build_result_set(self, progress_bar: bool = True, game=self.game) return result_set - def _run_serial(self, progress_bar: bool = False) -> bool: + def _run_serial(self, progress_bar: bool = False, + writer: csv.writer = None) -> bool: """ Run all matches in serial @@ -197,21 +202,21 @@ def _run_serial(self, progress_bar: bool = False) -> bool: for chunk in chunks: results = self._play_matches(chunk) - self._write_interactions(results) + self._write_interactions(results, writer=writer) if progress_bar: self.progress_bar.update(1) return True - def _write_interactions(self, results): + def _write_interactions(self, results, writer: csv.writer = None): """Write the interactions to file or to a dictionary""" - if self.writer is not None: - self._write_interactions_to_file(results) + if writer is not None: + self._write_interactions_to_file(results, writer) elif self.interactions_dict is not None: self._write_interactions_to_dict(results) - def _write_interactions_to_file(self, results): + def _write_interactions_to_file(self, results, writer: csv.writer): """Write the interactions to csv.""" for index_pair, interactions in results.items(): for interaction in interactions: @@ -222,7 +227,7 @@ def _write_interactions_to_file(self, results): history2 = actions_to_str([i[1] for i in interaction]) row.append(history1) row.append(history2) - self.writer.writerow(row) + writer.writerow(row) self.num_interactions += 1 def _write_interactions_to_dict(self, results): @@ -235,7 +240,8 @@ def _write_interactions_to_dict(self, results): self.interactions_dict[index_pair] = [interaction] self.num_interactions += 1 - def _run_parallel(self, processes: int=2, progress_bar: bool = False + def _run_parallel(self, processes: int=2, progress_bar: bool = False, + writer: csv.writer = None ) -> bool: """ Run all matches in parallel @@ -259,7 +265,8 @@ def _run_parallel(self, processes: int=2, progress_bar: bool = False self._start_workers(workers, work_queue, done_queue) - self._process_done_queue(workers, done_queue, progress_bar=progress_bar) + self._process_done_queue(workers, done_queue, progress_bar=progress_bar, + writer=writer) return True @@ -299,6 +306,7 @@ def _start_workers(self, workers: int, work_queue: Queue, return True def _process_done_queue(self, workers: int, done_queue: Queue, + writer: csv.writer=None, progress_bar: bool = False): """ Retrieves the matches from the parallel sub-processes @@ -318,7 +326,7 @@ def _process_done_queue(self, workers: int, done_queue: Queue, if results == 'STOP': stops += 1 else: - self._write_interactions(results) + self._write_interactions(results, writer) if progress_bar: self.progress_bar.update(1) From d7200594b93d213f5caded527923c2d969ba2386 Mon Sep 17 00:00:00 2001 From: E Shaw Date: Wed, 9 Aug 2017 01:45:19 +0800 Subject: [PATCH 04/11] branch switch --- axelrod/tests/unit/test_tournament.py | 13 +++++++++++++ axelrod/tournament.py | 24 +++++++++++++++--------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/axelrod/tests/unit/test_tournament.py b/axelrod/tests/unit/test_tournament.py index f4c0dceb8..f6561a329 100644 --- a/axelrod/tests/unit/test_tournament.py +++ b/axelrod/tests/unit/test_tournament.py @@ -270,6 +270,19 @@ def test_parallel_play(self): scores = tournament.play(processes=2, progress_bar=False).scores self.assertEqual(len(scores), len(players)) + # TODO remove + def test_all_strat(self): + turns = 5 + players = [s() for s in axelrod.strategies] + tournament = axelrod.Tournament( + name=self.test_name, + players=players, + game=self.game, + turns=turns, + repetitions=1) + scores = tournament.play(processes=4, progress_bar=True).scores + + def test_parallel_play_with_writing_to_file_on_windows(self): """ The default setting for `play` is to write to a NamedTemporaryFile. diff --git a/axelrod/tournament.py b/axelrod/tournament.py index 622c09090..9815f6dc3 100644 --- a/axelrod/tournament.py +++ b/axelrod/tournament.py @@ -123,9 +123,9 @@ def play(self, build_results: bool = True, filename: str = None, ------- axelrod.ResultSetFromFile """ - if progress_bar: - self.progress_bar = tqdm.tqdm(total=len(self.match_generator), - desc="Playing matches") + # if progress_bar: + # self.progress_bar = tqdm.tqdm(total=len(self.match_generator), + # desc="Playing matches") if on_windows and (filename is None): # pragma: no cover in_memory = True @@ -143,8 +143,8 @@ def play(self, build_results: bool = True, filename: str = None, self._run_parallel(processes=processes, progress_bar=progress_bar, writer=writer) - if progress_bar: - self.progress_bar.close() + # if progress_bar: + # self.progress_bar.close() # Make sure that python has finished writing to disk if not in_memory: @@ -264,8 +264,12 @@ def _run_parallel(self, processes: int=2, progress_bar: bool = False, work_queue.put(chunk) self._start_workers(workers, work_queue, done_queue) + if progress_bar: + pbar = tqdm.tqdm(desc='hi', total=self.match_generator.size) + else: + pbar = None - self._process_done_queue(workers, done_queue, progress_bar=progress_bar, + self._process_done_queue(workers, done_queue, progress_bar=pbar, writer=writer) return True @@ -307,7 +311,7 @@ def _start_workers(self, workers: int, work_queue: Queue, def _process_done_queue(self, workers: int, done_queue: Queue, writer: csv.writer=None, - progress_bar: bool = False): + progress_bar: tqdm.tqdm = None): """ Retrieves the matches from the parallel sub-processes @@ -328,8 +332,10 @@ def _process_done_queue(self, workers: int, done_queue: Queue, else: self._write_interactions(results, writer) - if progress_bar: - self.progress_bar.update(1) + if progress_bar is not None: + progress_bar.update(1) + if progress_bar is not None: + progress_bar.close() return True def _worker(self, work_queue: Queue, done_queue: Queue): From f262e1b8571bc53cfac67daa0ae289cbe735a8b1 Mon Sep 17 00:00:00 2001 From: E Shaw Date: Fri, 11 Aug 2017 01:27:04 +0800 Subject: [PATCH 05/11] finished main issues. working on tests --- axelrod/result_set.py | 12 +- axelrod/tests/unit/test_tournament.py | 392 ++++++++++++++++---------- axelrod/tournament.py | 165 +++++------ 3 files changed, 333 insertions(+), 236 deletions(-) diff --git a/axelrod/result_set.py b/axelrod/result_set.py index c5fbb85e4..3829ddd9d 100644 --- a/axelrod/result_set.py +++ b/axelrod/result_set.py @@ -1,5 +1,6 @@ from collections import namedtuple, Counter import csv +import sys from numpy import mean, nanmedian, std import tqdm @@ -38,8 +39,8 @@ def __init__(self, players, interactions, repetitions=False, ---------- players : list a list of player objects. - interactions : list - a list of dictionaries mapping tuples of player indices to + interactions : dict + a dictionary mapping tuples of player indices to interactions (1 for each repetition) repetitions : int The number of repetitions @@ -76,7 +77,7 @@ def create_progress_bar(self, desc=None): desc : string A description. """ - return tqdm.tqdm(total=self.num_matches, desc=desc) + return tqdm.tqdm(total=self.num_matches, desc=desc, file=sys.stderr) def _update_players(self, index_pair, players): """ @@ -743,7 +744,7 @@ def _build_score_related_metrics(self, progress_bar=False, if progress_bar: self.progress_bar = tqdm.tqdm(total=13 + 2 * self.nplayers, - desc="Finishing") + file=sys.stderr, desc="Finishing") self._summarise_normalised_scores() self._summarise_normalised_cooperation() @@ -1014,7 +1015,8 @@ def create_progress_bar(self, desc=None): if not self.num_interactions: with open(self.filename) as f: self.num_interactions = sum(1 for line in f) - return tqdm.tqdm(total=self.num_interactions, desc=desc) + return tqdm.tqdm(total=self.num_interactions, desc=desc, + file=sys.stderr) def _read_players_and_repetition_numbers(self, progress_bar=False): """ diff --git a/axelrod/tests/unit/test_tournament.py b/axelrod/tests/unit/test_tournament.py index f6561a329..9a72503ec 100644 --- a/axelrod/tests/unit/test_tournament.py +++ b/axelrod/tests/unit/test_tournament.py @@ -1,18 +1,24 @@ """Tests for the main tournament class.""" +from contextlib import redirect_stderr import csv +import io import logging from multiprocessing import Queue, cpu_count +import os import unittest from unittest.mock import MagicMock import warnings from hypothesis import given, example, settings from hypothesis.strategies import integers, floats +from tqdm import tqdm + from axelrod.tests.property import (tournaments, prob_end_tournaments, spatial_tournaments, strategy_lists) +from axelrod.tournament import _close_objects import axelrod @@ -61,148 +67,175 @@ def setUpClass(cls): cls.filename = "test_outputs/test_tournament.csv" - # def test_init(self): - # tournament = axelrod.Tournament( - # name=self.test_name, - # players=self.players, - # game=self.game, - # turns=self.test_turns, - # noise=0.2) - # self.assertEqual(len(tournament.players), len(test_strategies)) - # self.assertIsInstance( - # tournament.players[0].match_attributes['game'], axelrod.Game - # ) - # self.assertEqual(tournament.game.score((C, C)), (3, 3)) - # self.assertEqual(tournament.turns, self.test_turns) - # self.assertEqual(tournament.repetitions, 10) - # self.assertEqual(tournament.name, 'test') - # self.assertIsInstance(tournament._logger, logging.Logger) - # self.assertEqual(tournament.noise, 0.2) - # anonymous_tournament = axelrod.Tournament(players=self.players) - # self.assertEqual(anonymous_tournament.name, 'axelrod') - # - # def test_init_with_match_attributes(self): - # tournament = axelrod.Tournament( - # players=self.players, - # match_attributes={"length": float('inf')}) - # mg = tournament.match_generator - # match_params = mg.build_single_match_params() - # self.assertEqual(match_params["match_attributes"], - # {"length": float('inf')}) - # - # def test_warning(self): - # tournament = axelrod.Tournament(name=self.test_name, - # players=self.players, - # game=self.game, - # turns=10, - # repetitions=1) - # with warnings.catch_warnings(record=True) as w: - # # Check that a warning is raised if no results set is built and no - # # filename is given - # results = tournament.play(build_results=False, progress_bar=False) - # self.assertEqual(len(w), 1) - # - # with warnings.catch_warnings(record=True) as w: - # # Check that no warning is raised if no results set is built and a - # # is filename given - # - # tournament.play(build_results=False, - # filename=self.filename, progress_bar=False) - # self.assertEqual(len(w), 0) - # - # def test_serial_play(self): - # # Test that we get an instance of ResultSet - # tournament = axelrod.Tournament( - # name=self.test_name, - # players=self.players, - # game=self.game, - # turns=axelrod.DEFAULT_TURNS, - # repetitions=self.test_repetitions) - # results = tournament.play(progress_bar=False) - # self.assertIsInstance(results, axelrod.ResultSet) - # - # # Test that _run_serial_repetitions is called with empty matches list - # tournament = axelrod.Tournament( - # name=self.test_name, - # players=self.players, - # game=self.game, - # turns=axelrod.DEFAULT_TURNS, - # repetitions=self.test_repetitions) - # results = tournament.play(progress_bar=False) - # self.assertEqual(tournament.num_interactions, 75) - # - # def test_serial_play_with_different_game(self): - # # Test that a non default game is passed to the result set - # game = axelrod.Game(p=-1, r=-1, s=-1, t=-1) - # tournament = axelrod.Tournament( - # name=self.test_name, - # players=self.players, - # game=game, - # turns=1, - # repetitions=1) - # results = tournament.play(progress_bar=False) - # self.assertEqual(results.game.RPST(), (-1, -1, -1, -1)) - # - # def test_no_progress_bar_play(self): - # """Test that progress bar is not created for progress_bar=False""" - # tournament = axelrod.Tournament( - # name=self.test_name, - # players=self.players, - # game=self.game, - # turns=axelrod.DEFAULT_TURNS, - # repetitions=self.test_repetitions) - # - # - # # Test with build results - # results = tournament.play(progress_bar=False) - # self.assertIsInstance(results, axelrod.ResultSet) - # # Check that no progress bar was created - # call_progress_bar = lambda: tournament.progress_bar.total - # self.assertRaises(AttributeError, call_progress_bar) - # - # # Test without build results - # results = tournament.play(progress_bar=False, build_results=False, - # filename=self.filename) - # self.assertIsNone(results) - # results = axelrod.ResultSetFromFile(self.filename, progress_bar=False) - # self.assertIsInstance(results, axelrod.ResultSet) - # self.assertRaises(AttributeError, call_progress_bar) - # - # def test_progress_bar_play(self): - # """Test that progress bar is created by default and with True argument""" - # tournament = axelrod.Tournament( - # name=self.test_name, - # players=self.players, - # game=self.game, - # turns=axelrod.DEFAULT_TURNS, - # repetitions=self.test_repetitions) - # - # results = tournament.play() - # self.assertIsInstance(results, axelrod.ResultSet) - # self.assertEqual(tournament.progress_bar.total, 15) - # self.assertEqual(tournament.progress_bar.total, - # tournament.progress_bar.n) - # - # results = tournament.play(progress_bar=True) - # self.assertIsInstance(results, axelrod.ResultSet) - # self.assertEqual(tournament.progress_bar.total, 15) - # self.assertEqual(tournament.progress_bar.total, - # tournament.progress_bar.n) - # - # # Test without build results - # results = tournament.play(progress_bar=True, build_results=False, - # filename=self.filename) - # self.assertIsNone(results) - # results = axelrod.ResultSetFromFile(self.filename) - # self.assertIsInstance(results, axelrod.ResultSet) - # self.assertEqual(tournament.progress_bar.total, 15) - # self.assertEqual(tournament.progress_bar.total, - # tournament.progress_bar.n) + def test_init(self): + tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=self.game, + turns=self.test_turns, + noise=0.2) + self.assertEqual(len(tournament.players), len(test_strategies)) + self.assertIsInstance( + tournament.players[0].match_attributes['game'], axelrod.Game + ) + self.assertEqual(tournament.game.score((C, C)), (3, 3)) + self.assertEqual(tournament.turns, self.test_turns) + self.assertEqual(tournament.repetitions, 10) + self.assertEqual(tournament.name, 'test') + self.assertIsInstance(tournament._logger, logging.Logger) + self.assertEqual(tournament.noise, 0.2) + anonymous_tournament = axelrod.Tournament(players=self.players) + self.assertEqual(anonymous_tournament.name, 'axelrod') - @unittest.skip - def test_progress_bar_play_parallel(self): # TODO fix it! + def test_init_with_match_attributes(self): + tournament = axelrod.Tournament( + players=self.players, + match_attributes={"length": float('inf')}) + mg = tournament.match_generator + match_params = mg.build_single_match_params() + self.assertEqual(match_params["match_attributes"], + {"length": float('inf')}) + + def test_warning(self): + tournament = axelrod.Tournament(name=self.test_name, + players=self.players, + game=self.game, + turns=10, + repetitions=1) + with warnings.catch_warnings(record=True) as w: + # Check that a warning is raised if no results set is built and no + # filename is given + results = tournament.play(build_results=False, progress_bar=False) + self.assertEqual(len(w), 1) + + with warnings.catch_warnings(record=True) as w: + # Check that no warning is raised if no results set is built and a + # is filename given + + tournament.play(build_results=False, + filename=self.filename, progress_bar=False) + self.assertEqual(len(w), 0) + + def test_serial_play(self): + # Test that we get an instance of ResultSet + tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=self.game, + turns=axelrod.DEFAULT_TURNS, + repetitions=self.test_repetitions) + results = tournament.play(progress_bar=False) + self.assertIsInstance(results, axelrod.ResultSet) + + # Test that _run_serial_repetitions is called with empty matches list + tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=self.game, + turns=axelrod.DEFAULT_TURNS, + repetitions=self.test_repetitions) + results = tournament.play(progress_bar=False) + self.assertEqual(tournament.num_interactions, 75) + + def test_serial_play_with_different_game(self): + # Test that a non default game is passed to the result set + game = axelrod.Game(p=-1, r=-1, s=-1, t=-1) + tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=game, + turns=1, + repetitions=1) + results = tournament.play(progress_bar=False) + self.assertEqual(results.game.RPST(), (-1, -1, -1, -1)) + + def test_no_progress_bar_play(self): + """Test that progress bar is not created for progress_bar=False""" + tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=self.game, + turns=axelrod.DEFAULT_TURNS, + repetitions=self.test_repetitions) + + + # Test with build results + tournament.use_progress_bar = True + err = io.StringIO() + with redirect_stderr(err): + results = tournament.play(progress_bar=False) + self.assertIsInstance(results, axelrod.ResultSet) + # Check that play re-assigned self.use_progress_bar + self.assertFalse(tournament.use_progress_bar) + # Check that no progress bar wrote output. + self.assertEqual(err.getvalue(), '') + + + # Test without build results + tournament.use_progress_bar = True + err = io.StringIO() + with redirect_stderr(err): + results = tournament.play(progress_bar=False, build_results=False, + filename=self.filename) + self.assertIsNone(results) + self.assertFalse(tournament.use_progress_bar) + self.assertEqual(err.getvalue(), '') + + results = axelrod.ResultSetFromFile(self.filename, progress_bar=False) + self.assertIsInstance(results, axelrod.ResultSet) + + def test_progress_bar_play(self): + """Test that progress bar is created by default and with True argument""" + tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=self.game, + turns=axelrod.DEFAULT_TURNS, + repetitions=self.test_repetitions) + + tournament.use_progress_bar = False + err = io.StringIO() + with redirect_stderr(err): + results = tournament.play() + self.assertIsInstance(results, axelrod.ResultSet) + # Check that play re-assigned self.use_progress_bar + self.assertTrue(tournament.use_progress_bar) + # Check that progress bar wrote output. + std_err_str = err.getvalue() + self.assertIn('Playing matches:', std_err_str) + self.assertIn('Analysing:', std_err_str) + self.assertIn('100%', std_err_str) + + tournament.use_progress_bar = False + err = io.StringIO() + with redirect_stderr(err): + results = tournament.play(progress_bar=True) + self.assertIsInstance(results, axelrod.ResultSet) + self.assertTrue(tournament.use_progress_bar) + std_err_str = err.getvalue() + self.assertIn('Playing matches:', std_err_str) + self.assertIn('Analysing:', std_err_str) + self.assertIn('100%', std_err_str) + + # Test without build results + tournament.use_progress_bar = False + err = io.StringIO() + with redirect_stderr(err): + results = tournament.play(progress_bar=True, build_results=False, + filename=self.filename) + self.assertIsNone(results) + self.assertTrue(tournament.use_progress_bar) + std_err_str = err.getvalue() + self.assertIn('Playing matches:', std_err_str) + self.assertNotIn('Analysing:', std_err_str) + self.assertIn('100%', std_err_str) + + results = axelrod.ResultSetFromFile(self.filename) + self.assertIsInstance(results, axelrod.ResultSet) + + def test_progress_bar_play_parallel(self): """Test that tournament plays when asking for progress bar for parallel - tournament""" + tournament and that progress bar is created.""" tournament = axelrod.Tournament( name=self.test_name, players=self.players, @@ -210,10 +243,28 @@ def test_progress_bar_play_parallel(self): # TODO fix it! turns=axelrod.DEFAULT_TURNS, repetitions=self.test_repetitions) - results = tournament.play(processes=2) + err = io.StringIO() + with redirect_stderr(err): + results = tournament.play(progress_bar=False, processes=2) + stderr_output = err.getvalue() + + self.assertNotIn('Playing matches', stderr_output) self.assertIsInstance(results, axelrod.ResultSet) - results = tournament.play(progress_bar=True) + err = io.StringIO() + with redirect_stderr(err): + results = tournament.play(progress_bar=True, processes=2) + stderr_output = err.getvalue() + + self.assertIn('Playing matches', stderr_output) + self.assertIsInstance(results, axelrod.ResultSet) + + err = io.StringIO() + with redirect_stderr(err): + results = tournament.play(processes=2) + stderr_output = err.getvalue() + + self.assertIn('Playing matches', stderr_output) self.assertIsInstance(results, axelrod.ResultSet) # # @given(tournament=tournaments(min_size=2, max_size=5, min_turns=2, @@ -271,6 +322,7 @@ def test_parallel_play(self): self.assertEqual(len(scores), len(players)) # TODO remove + @unittest.skip def test_all_strat(self): turns = 5 players = [s() for s in axelrod.strategies] @@ -282,13 +334,7 @@ def test_all_strat(self): repetitions=1) scores = tournament.play(processes=4, progress_bar=True).scores - - def test_parallel_play_with_writing_to_file_on_windows(self): - """ - The default setting for `play` is to write to a NamedTemporaryFile. - This is disabled on Windows. This test ensures that parallel_play - does not conflict with writing the results to file. - """ + def test_parallel_play_with_writing_to_file(self): tournament = axelrod.Tournament( name=self.test_name, players=self.players, @@ -316,7 +362,7 @@ def test_parallel_play_with_writing_to_file_on_windows(self): # # Get the calls made to write_interactions # calls = tournament._write_interactions.call_args_list # self.assertEqual(len(calls), 15) -# + def test_run_parallel(self): class PickleableMock(MagicMock): def __reduce__(self): @@ -735,6 +781,52 @@ def test_worker(self): # results = tournament.play(progress_bar=False) # self.assertIsInstance(results, axelrod.ResultSet) + def test_close_objects_with_none(self): + self.assertIsNone(_close_objects(None, None)) + + def test_close_objects_with_file_objs(self): + f1 = open('to_delete_1', 'w') + f2 = open('to_delete_2', 'w') + f2.close() + f2 = open('to_delete_2', 'r') + + self.assertFalse(f1.closed) + self.assertFalse(f2.closed) + + _close_objects(f1, f2) + + self.assertTrue(f1.closed) + self.assertTrue(f2.closed) + + os.remove('to_delete_1') + os.remove('to_delete_2') + + def test_close_objects_with_tqdm(self): + pbar_1 = tqdm(range(5)) + pbar_2 = tqdm(total=10, desc='hi', file=io.StringIO()) + + self.assertFalse(pbar_1.disable) + self.assertFalse(pbar_2.disable) + + _close_objects(pbar_1, pbar_2) + + self.assertTrue(pbar_1.disable) + self.assertTrue(pbar_2.disable) + + def test_close_objects_with_different_objects(self): + file = open('to_delete_1', 'w') + pbar = tqdm(range(5)) + num = 5 + empty = None + word = 'hi' + + _close_objects(file, pbar, num, empty, word) + + self.assertTrue(pbar.disable) + self.assertTrue(file.closed) + + os.remove('to_delete_1') + class TestProbEndingSpatialTournament(unittest.TestCase): diff --git a/axelrod/tournament.py b/axelrod/tournament.py index 9815f6dc3..f29cf1c75 100644 --- a/axelrod/tournament.py +++ b/axelrod/tournament.py @@ -2,13 +2,15 @@ import csv import logging from multiprocessing import Process, Queue, cpu_count -from tempfile import NamedTemporaryFile +from tempfile import mkstemp +from typing import Any import warnings import os +import sys import tqdm -from axelrod import on_windows, DEFAULT_TURNS +from axelrod import DEFAULT_TURNS from axelrod.player import Player from axelrod.action import actions_to_str from .game import Game @@ -77,23 +79,22 @@ def __init__(self, players: List[Player], match_attributes=match_attributes) self._logger = logging.getLogger(__name__) + self.use_progress_bar = True + self.filename = None + self._temp_file_descriptor = None + def setup_output(self, filename=None, in_memory=False): - """Open a CSV writer for tournament output.""" + """assign/create filename to self and file descriptor if file should + be deleted once `play` is finished. """ + temp_file_descriptor = None if in_memory: self.interactions_dict = {} - outputfile = None - writer = None - else: - if filename: - outputfile = open(filename, 'w') - else: - # Setup a temporary file - outputfile = NamedTemporaryFile(mode='w') - filename = outputfile.name - writer = csv.writer(outputfile, lineterminator='\n') - # Save filename for loading ResultSet later - self.filename = filename - return outputfile, writer + filename = None + if not in_memory and filename is None: + temp_file_descriptor, filename = mkstemp() + + self.filename = filename + self._temp_file_descriptor = temp_file_descriptor def play(self, build_results: bool = True, filename: str = None, processes: int = None, progress_bar: bool = True, @@ -123,14 +124,9 @@ def play(self, build_results: bool = True, filename: str = None, ------- axelrod.ResultSetFromFile """ - # if progress_bar: - # self.progress_bar = tqdm.tqdm(total=len(self.match_generator), - # desc="Playing matches") + self.use_progress_bar = progress_bar - if on_windows and (filename is None): # pragma: no cover - in_memory = True - - outputfile, writer = self.setup_output(filename, in_memory) + self.setup_output(filename, in_memory) if not build_results and not filename: warnings.warn( @@ -138,28 +134,24 @@ def play(self, build_results: bool = True, filename: str = None, "build_results=False and no filename was supplied.") if processes is None: - self._run_serial(progress_bar=progress_bar, writer=writer) + self._run_serial() else: - self._run_parallel(processes=processes, progress_bar=progress_bar, - writer=writer) + self._run_parallel(processes=processes) - # if progress_bar: - # self.progress_bar.close() + result_set = None + if build_results: + result_set = self._build_result_set( + keep_interactions=keep_interactions, in_memory=in_memory + ) - # Make sure that python has finished writing to disk - if not in_memory: - outputfile.flush() + if self._temp_file_descriptor is not None: + os.close(self._temp_file_descriptor) + os.remove(self.filename) + self.filename = None - if build_results: - return self._build_result_set(progress_bar=progress_bar, - keep_interactions=keep_interactions, - in_memory=in_memory) - outputfile.close() - # elif not in_memory: - # self.outputfile.close() - - def _build_result_set(self, progress_bar: bool = True, - keep_interactions: bool = False, + return result_set + + def _build_result_set(self, keep_interactions: bool = False, in_memory: bool = False): """ Build the result set (used by the play method) @@ -171,52 +163,65 @@ def _build_result_set(self, progress_bar: bool = True, if not in_memory: result_set = ResultSetFromFile( filename=self.filename, - progress_bar=progress_bar, + progress_bar=self.use_progress_bar, num_interactions=self.num_interactions, repetitions=self.repetitions, players=[str(p) for p in self.players], keep_interactions=keep_interactions, game=self.game) - # self.outputfile.close() else: result_set = ResultSet( players=[str(p) for p in self.players], interactions=self.interactions_dict, repetitions=self.repetitions, - progress_bar=progress_bar, + progress_bar=self.use_progress_bar, game=self.game) return result_set - def _run_serial(self, progress_bar: bool = False, - writer: csv.writer = None) -> bool: - """ - Run all matches in serial - - Parameters - ---------- + def _run_serial(self) -> bool: + """Run all matches in serial.""" - progress_bar : bool - Whether or not to update the tournament progress bar - """ chunks = self.match_generator.build_match_chunks() + file, writer = self._get_file_objects() + progress_bar = self._get_progress_bar() + for chunk in chunks: results = self._play_matches(chunk) self._write_interactions(results, writer=writer) - if progress_bar: - self.progress_bar.update(1) + if self.use_progress_bar: + progress_bar.update(1) + + _close_objects(file, progress_bar) return True - def _write_interactions(self, results, writer: csv.writer = None): + def _get_file_objects(self): + """Returns the file object and writer for writing results or + (None, None) if self.filename is None""" + file_obj = None + writer = None + if self.filename is not None: + file_obj = open(self.filename, 'w') + writer = csv.writer(file_obj, lineterminator='\n') + return file_obj, writer + + def _get_progress_bar(self): + if self.use_progress_bar: + return tqdm.tqdm(total=self.match_generator.size, file=sys.stderr, + desc="Playing matches") + else: + return None + + def _write_interactions(self, results, writer=None): """Write the interactions to file or to a dictionary""" if writer is not None: - self._write_interactions_to_file(results, writer) + self._write_interactions_to_file(results, writer) elif self.interactions_dict is not None: - self._write_interactions_to_dict(results) + self._write_interactions_to_dict(results) - def _write_interactions_to_file(self, results, writer: csv.writer): + def _write_interactions_to_file(self, results, writer): """Write the interactions to csv.""" for index_pair, interactions in results.items(): for interaction in interactions: @@ -240,21 +245,18 @@ def _write_interactions_to_dict(self, results): self.interactions_dict[index_pair] = [interaction] self.num_interactions += 1 - def _run_parallel(self, processes: int=2, progress_bar: bool = False, - writer: csv.writer = None - ) -> bool: + def _run_parallel(self, processes: int=2) -> bool: """ Run all matches in parallel Parameters ---------- - progress_bar : bool - Whether or not to update the tournament progress bar + processes : int + How many processes to use. """ # At first sight, it might seem simpler to use the multiprocessing Pool - # Class rather than Processes and Queues. However, Pool can only accept - # target functions which can be pickled and instance methods cannot. + # Class rather than Processes and Queues. However, this way is faster. work_queue = Queue() done_queue = Queue() workers = self._n_workers(processes=processes) @@ -264,13 +266,8 @@ def _run_parallel(self, processes: int=2, progress_bar: bool = False, work_queue.put(chunk) self._start_workers(workers, work_queue, done_queue) - if progress_bar: - pbar = tqdm.tqdm(desc='hi', total=self.match_generator.size) - else: - pbar = None - self._process_done_queue(workers, done_queue, progress_bar=pbar, - writer=writer) + self._process_done_queue(workers, done_queue) return True @@ -309,9 +306,7 @@ def _start_workers(self, workers: int, work_queue: Queue, process.start() return True - def _process_done_queue(self, workers: int, done_queue: Queue, - writer: csv.writer=None, - progress_bar: tqdm.tqdm = None): + def _process_done_queue(self, workers: int, done_queue: Queue): """ Retrieves the matches from the parallel sub-processes @@ -321,9 +316,10 @@ def _process_done_queue(self, workers: int, done_queue: Queue, The number of sub-processes in existence done_queue : multiprocessing.Queue A queue containing the output dictionaries from each round robin - progress_bar : bool - Whether or not to update the tournament progress bar """ + file, writer = self._get_file_objects() + progress_bar = self._get_progress_bar() + stops = 0 while stops < workers: results = done_queue.get() @@ -332,10 +328,10 @@ def _process_done_queue(self, workers: int, done_queue: Queue, else: self._write_interactions(results, writer) - if progress_bar is not None: + if self.use_progress_bar: progress_bar.update(1) - if progress_bar is not None: - progress_bar.close() + + _close_objects(file, progress_bar) return True def _worker(self, work_queue: Queue, done_queue: Queue): @@ -382,3 +378,10 @@ def _play_matches(self, chunk): match.play() interactions[index_pair].append(match.result) return interactions + + +def _close_objects(*objs): + """If the objects have a `close` method, closes them.""" + for obj in objs: + if hasattr(obj, 'close'): + obj.close() From f501335111ed8f5339453a5246e10b5a799595b2 Mon Sep 17 00:00:00 2001 From: E Shaw Date: Fri, 11 Aug 2017 03:37:22 +0800 Subject: [PATCH 06/11] tournament.py testing is finished. --- axelrod/tests/unit/test_tournament.py | 935 ++++++++++++++------------ axelrod/tournament.py | 1 - 2 files changed, 505 insertions(+), 431 deletions(-) diff --git a/axelrod/tests/unit/test_tournament.py b/axelrod/tests/unit/test_tournament.py index 9a72503ec..97b9b69f9 100644 --- a/axelrod/tests/unit/test_tournament.py +++ b/axelrod/tests/unit/test_tournament.py @@ -67,6 +67,15 @@ def setUpClass(cls): cls.filename = "test_outputs/test_tournament.csv" + def setUp(self): + self.test_tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=self.game, + turns=2, + repetitions=1 + ) + def test_init(self): tournament = axelrod.Tournament( name=self.test_name, @@ -116,6 +125,91 @@ def test_warning(self): filename=self.filename, progress_bar=False) self.assertEqual(len(w), 0) + def test_setup_output_in_memory_overrides_filename(self): + self.assertIsNone(self.test_tournament.filename) + self.assertIsNone(self.test_tournament._temp_file_descriptor) + self.assertFalse(hasattr(self.test_tournament, 'interactions_dict')) + + self.test_tournament.setup_output(self.filename, in_memory=True) + + self.assertIsNone(self.test_tournament.filename) + self.assertIsNone(self.test_tournament._temp_file_descriptor) + self.assertEqual(self.test_tournament.interactions_dict, {}) + + def test_setup_output_with_filename(self): + + self.test_tournament.setup_output(self.filename, in_memory=False) + + self.assertEqual(self.test_tournament.filename, self.filename) + self.assertIsNone(self.test_tournament._temp_file_descriptor) + self.assertFalse(hasattr(self.test_tournament, 'interactions_dict')) + + def test_setup_output_no_filename_no_in_memory(self): + self.test_tournament.setup_output() + + self.assertIsInstance(self.test_tournament.filename, str) + self.assertIsInstance(self.test_tournament._temp_file_descriptor, int) + self.assertFalse(hasattr(self.test_tournament, 'interactions_dict')) + + os.close(self.test_tournament._temp_file_descriptor) + os.remove(self.test_tournament.filename) + + def test_play_changes_use_progress_bar(self): + self.assertTrue(self.test_tournament.use_progress_bar) + + self.test_tournament.play(progress_bar=False) + self.assertFalse(self.test_tournament.use_progress_bar) + + self.test_tournament.play(progress_bar=True) + self.assertTrue(self.test_tournament.use_progress_bar) + + def test_play_changes_temp_file_descriptor(self): + self.assertIsNone(self.test_tournament._temp_file_descriptor) + + # No file descriptor for a named file. + self.test_tournament.play(filename=self.filename, in_memory=False, + progress_bar=False) + self.assertIsNone(self.test_tournament._temp_file_descriptor) + + # No file descriptor for in_memory. + self.test_tournament.play(filename=None, in_memory=True, + progress_bar=False) + self.assertIsNone(self.test_tournament._temp_file_descriptor) + + # Temp file creates file descriptor. + self.test_tournament.play(filename=None, in_memory=False, + progress_bar=False) + self.assertIsInstance(self.test_tournament._temp_file_descriptor, int) + + def test_play_tempfile_removed(self): + self.test_tournament.play(filename=None, in_memory=False, + progress_bar=False) + + self.assertFalse(os.path.isfile(self.test_tournament.filename)) + + def test_get_file_objects_no_filename(self): + file, writer = self.test_tournament._get_file_objects() + self.assertIsNone(file) + self.assertIsNone(writer) + + def test_get_file_object_with_filename(self): + self.test_tournament.filename = self.filename + file, writer = self.test_tournament._get_file_objects() + self.assertIsInstance(file, io.TextIOWrapper) + self.assertEqual(writer.__class__.__name__, 'writer') + + def test_get_progress_bar(self): + self.test_tournament.use_progress_bar = False + pbar = self.test_tournament._get_progress_bar() + self.assertIsNone(pbar) + + self.test_tournament.use_progress_bar = True + pbar = self.test_tournament._get_progress_bar() + self.assertIsInstance(pbar, tqdm) + self.assertEqual(pbar.desc, 'Playing matches: ') + self.assertEqual(pbar.n, 0) + self.assertEqual(pbar.total, self.test_tournament.match_generator.size) + def test_serial_play(self): # Test that we get an instance of ResultSet tournament = axelrod.Tournament( @@ -160,25 +254,20 @@ def test_no_progress_bar_play(self): # Test with build results - tournament.use_progress_bar = True err = io.StringIO() with redirect_stderr(err): results = tournament.play(progress_bar=False) self.assertIsInstance(results, axelrod.ResultSet) - # Check that play re-assigned self.use_progress_bar - self.assertFalse(tournament.use_progress_bar) # Check that no progress bar wrote output. self.assertEqual(err.getvalue(), '') # Test without build results - tournament.use_progress_bar = True err = io.StringIO() with redirect_stderr(err): results = tournament.play(progress_bar=False, build_results=False, filename=self.filename) self.assertIsNone(results) - self.assertFalse(tournament.use_progress_bar) self.assertEqual(err.getvalue(), '') results = axelrod.ResultSetFromFile(self.filename, progress_bar=False) @@ -193,42 +282,35 @@ def test_progress_bar_play(self): turns=axelrod.DEFAULT_TURNS, repetitions=self.test_repetitions) - tournament.use_progress_bar = False err = io.StringIO() with redirect_stderr(err): results = tournament.play() self.assertIsInstance(results, axelrod.ResultSet) - # Check that play re-assigned self.use_progress_bar - self.assertTrue(tournament.use_progress_bar) # Check that progress bar wrote output. - std_err_str = err.getvalue() - self.assertIn('Playing matches:', std_err_str) - self.assertIn('Analysing:', std_err_str) - self.assertIn('100%', std_err_str) + stderr_output = err.getvalue() + self.assertIn('Playing matches:', stderr_output) + self.assertIn('Analysing:', stderr_output) + self.assertIn('100%', stderr_output) - tournament.use_progress_bar = False err = io.StringIO() with redirect_stderr(err): results = tournament.play(progress_bar=True) self.assertIsInstance(results, axelrod.ResultSet) - self.assertTrue(tournament.use_progress_bar) - std_err_str = err.getvalue() - self.assertIn('Playing matches:', std_err_str) - self.assertIn('Analysing:', std_err_str) - self.assertIn('100%', std_err_str) + stderr_output = err.getvalue() + self.assertIn('Playing matches:', stderr_output) + self.assertIn('Analysing:', stderr_output) + self.assertIn('100%', stderr_output) # Test without build results - tournament.use_progress_bar = False err = io.StringIO() with redirect_stderr(err): results = tournament.play(progress_bar=True, build_results=False, filename=self.filename) self.assertIsNone(results) - self.assertTrue(tournament.use_progress_bar) - std_err_str = err.getvalue() - self.assertIn('Playing matches:', std_err_str) - self.assertNotIn('Analysing:', std_err_str) - self.assertIn('100%', std_err_str) + stderr_output = err.getvalue() + self.assertIn('Playing matches:', stderr_output) + self.assertNotIn('Analysing:', stderr_output) + self.assertIn('100%', stderr_output) results = axelrod.ResultSetFromFile(self.filename) self.assertIsInstance(results, axelrod.ResultSet) @@ -243,58 +325,61 @@ def test_progress_bar_play_parallel(self): turns=axelrod.DEFAULT_TURNS, repetitions=self.test_repetitions) + # progress_bar = False err = io.StringIO() with redirect_stderr(err): results = tournament.play(progress_bar=False, processes=2) - stderr_output = err.getvalue() - - self.assertNotIn('Playing matches', stderr_output) + self.assertEqual(err.getvalue(), '') self.assertIsInstance(results, axelrod.ResultSet) + # progress_bar = True err = io.StringIO() with redirect_stderr(err): results = tournament.play(progress_bar=True, processes=2) stderr_output = err.getvalue() self.assertIn('Playing matches', stderr_output) + self.assertIn('100%', stderr_output) self.assertIsInstance(results, axelrod.ResultSet) + # progress_bar is default err = io.StringIO() with redirect_stderr(err): results = tournament.play(processes=2) stderr_output = err.getvalue() self.assertIn('Playing matches', stderr_output) + self.assertIn('100%', stderr_output) + self.assertIsInstance(results, axelrod.ResultSet) + + @given(tournament=tournaments(min_size=2, max_size=5, min_turns=2, + max_turns=10, min_repetitions=2, + max_repetitions=4)) + @settings(max_examples=10, timeout=0) + @example(tournament=axelrod.Tournament(players=[s() for s in + test_strategies], turns=test_turns, repetitions=test_repetitions) + ) + + # These two examples are to make sure #465 is fixed. + # As explained there: /~https://github.com/Axelrod-Python/Axelrod/issues/465, + # these two examples were identified by hypothesis. + @example(tournament= + axelrod.Tournament(players=[axelrod.BackStabber(), + axelrod.MindReader()], + turns=2, repetitions=1), + ) + @example(tournament= + axelrod.Tournament(players=[axelrod.BackStabber(), + axelrod.ThueMorse()], + turns=2, repetitions=1), + ) + def test_property_serial_play(self, tournament): + """Test serial play using hypothesis""" + # Test that we get an instance of ResultSet + results = tournament.play(progress_bar=False) self.assertIsInstance(results, axelrod.ResultSet) - # - # @given(tournament=tournaments(min_size=2, max_size=5, min_turns=2, - # max_turns=10, min_repetitions=2, - # max_repetitions=4)) - # @settings(max_examples=10, timeout=0) - # @example(tournament=axelrod.Tournament(players=[s() for s in - # test_strategies], turns=test_turns, repetitions=test_repetitions) - # ) - # - # # These two examples are to make sure #465 is fixed. - # # As explained there: /~https://github.com/Axelrod-Python/Axelrod/issues/465, - # # these two examples were identified by hypothesis. - # @example(tournament= - # axelrod.Tournament(players=[axelrod.BackStabber(), - # axelrod.MindReader()], - # turns=2, repetitions=1), - # ) - # @example(tournament= - # axelrod.Tournament(players=[axelrod.BackStabber(), - # axelrod.ThueMorse()], - # turns=2, repetitions=1), - # ) - # def test_property_serial_play(self, tournament): - # """Test serial play using hypothesis""" - # # Test that we get an instance of ResultSet - # results = tournament.play(progress_bar=False) - # self.assertIsInstance(results, axelrod.ResultSet) - # self.assertEqual(results.nplayers, len(tournament.players)) - # self.assertEqual(results.players, [str(p) for p in tournament.players]) + self.assertEqual(results.nplayers, len(tournament.players)) + self.assertEqual(results.players, [str(p) for p in tournament.players]) def test_parallel_play(self): # Test that we get an instance of ResultSet @@ -321,19 +406,6 @@ def test_parallel_play(self): scores = tournament.play(processes=2, progress_bar=False).scores self.assertEqual(len(scores), len(players)) - # TODO remove - @unittest.skip - def test_all_strat(self): - turns = 5 - players = [s() for s in axelrod.strategies] - tournament = axelrod.Tournament( - name=self.test_name, - players=players, - game=self.game, - turns=turns, - repetitions=1) - scores = tournament.play(processes=4, progress_bar=True).scores - def test_parallel_play_with_writing_to_file(self): tournament = axelrod.Tournament( name=self.test_name, @@ -348,20 +420,20 @@ def test_parallel_play_with_writing_to_file(self): self.assertEqual(tournament.num_interactions, 75) -# def test_run_serial(self): -# tournament = axelrod.Tournament( -# name=self.test_name, -# players=self.players, -# game=self.game, -# turns=axelrod.DEFAULT_TURNS, -# repetitions=self.test_repetitions) -# tournament._write_interactions = MagicMock( -# name='_write_interactions') -# self.assertTrue(tournament._run_serial()) -# -# # Get the calls made to write_interactions -# calls = tournament._write_interactions.call_args_list -# self.assertEqual(len(calls), 15) + def test_run_serial(self): + tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=self.game, + turns=axelrod.DEFAULT_TURNS, + repetitions=self.test_repetitions) + tournament._write_interactions = MagicMock( + name='_write_interactions') + self.assertTrue(tournament._run_serial()) + + # Get the calls made to write_interactions + calls = tournament._write_interactions.call_args_list + self.assertEqual(len(calls), 15) def test_run_parallel(self): class PickleableMock(MagicMock): @@ -465,367 +537,319 @@ def test_worker(self): queue_stop = done_queue.get() self.assertEqual(queue_stop, 'STOP') -# def test_build_result_set(self): -# tournament = axelrod.Tournament( -# name=self.test_name, -# players=self.players, -# game=self.game, -# turns=axelrod.DEFAULT_TURNS, -# repetitions=self.test_repetitions) -# results = tournament.play(progress_bar=False) -# self.assertIsInstance(results, axelrod.ResultSet) -# -# # Test in memory -# results = tournament.play(progress_bar=False, in_memory=True) -# self.assertIsInstance(results, axelrod.ResultSet) -# -# def test_no_build_result_set(self): -# tournament = axelrod.Tournament( -# name=self.test_name, -# players=self.players, -# game=self.game, -# turns=axelrod.DEFAULT_TURNS, -# repetitions=self.test_repetitions) -# -# results = tournament.play(build_results=False, filename=self.filename, -# progress_bar=False) -# self.assertIsNone(results) -# -# # Checking that results were written properly -# results = axelrod.ResultSetFromFile(self.filename, progress_bar=False) -# self.assertIsInstance(results, axelrod.ResultSet) -# -# @given(turns=integers(min_value=1, max_value=200)) -# @example(turns=3) -# @example(turns=axelrod.DEFAULT_TURNS) -# def test_play_matches(self, turns): -# tournament = axelrod.Tournament(name=self.test_name, -# players=self.players, -# game=self.game, -# repetitions=self.test_repetitions) -# -# def make_chunk_generator(): -# for player1_index in range(len(self.players)): -# for player2_index in range(player1_index, len(self.players)): -# index_pair = (player1_index, player2_index) -# match_params = {"turns": turns, "game": self.game} -# yield (index_pair, match_params, self.test_repetitions) -# -# chunk_generator = make_chunk_generator() -# interactions = {} -# for chunk in chunk_generator: -# result = tournament._play_matches(chunk) -# for index_pair, inters in result.items(): -# try: -# interactions[index_pair].append(inters) -# except KeyError: -# interactions[index_pair] = [inters] -# -# self.assertEqual(len(interactions), 15) -# -# for index_pair, inter in interactions.items(): -# self.assertEqual(len(index_pair), 2) -# for plays in inter: -# # Check that have the expected number of repetitions -# self.assertEqual(len(plays), self.test_repetitions) -# for repetition in plays: -# # Check that have the correct length for each rep -# self.assertEqual(len(repetition), turns) -# -# # Check that matches no longer exist -# self.assertEqual((len(list(chunk_generator))), 0) -# -# def test_match_cache_is_used(self): -# """ -# Create two Random players that are classified as deterministic. -# As they are deterministic the cache will be used. -# """ -# FakeRandom = axelrod.Random -# FakeRandom.classifier["stochastic"] = False -# p1 = FakeRandom() -# p2 = FakeRandom() -# tournament = axelrod.Tournament((p1, p2), turns=5, repetitions=2) -# results = tournament.play(progress_bar=False) -# for player_scores in results.scores: -# self.assertEqual(player_scores[0], player_scores[1]) -# -# def test_write_interactions(self): -# tournament = axelrod.Tournament( -# -# name=self.test_name, -# players=self.players, -# game=self.game, -# turns=2, -# repetitions=2) -# tournament._write_interactions = MagicMock(name='_write_interactions') -# # Mocking this as it is called by play -# tournament._build_result_set = MagicMock(name='_build_result_set') -# self.assertTrue(tournament.play(filename=self.filename, -# progress_bar=False)) -# tournament.outputfile.close() # Normally closed by `build_result_set` -# -# # Get the calls made to write_interactions -# calls = tournament._write_interactions.call_args_list -# self.assertEqual(len(calls), 15) -# -# # Test when running in memory -# tournament._write_interactions = MagicMock(name='_write_interactions') -# self.assertTrue(tournament.play(filename=self.filename, -# progress_bar=False, -# in_memory=False)) -# # Get the calls made to write_interactions -# calls = tournament._write_interactions.call_args_list -# self.assertEqual(len(calls), 15) -# tournament.outputfile.close() # Normally closed by `write_interactions` -# -# def test_write_to_csv(self): -# tournament = axelrod.Tournament( -# name=self.test_name, -# players=self.players, -# game=self.game, -# turns=2, -# repetitions=2) -# tournament.play(filename=self.filename, progress_bar=False) -# with open(self.filename, 'r') as f: -# written_data = [[int(r[0]), int(r[1])] + r[2:] for r in csv.reader(f)] -# expected_data = [[0, 1, 'Cooperator', 'Tit For Tat', 'CC', 'CC'], -# [0, 1, 'Cooperator', 'Tit For Tat', 'CC', 'CC'], -# [1, 2, 'Tit For Tat', 'Defector', 'CD', 'DD'], -# [1, 2, 'Tit For Tat', 'Defector', 'CD', 'DD'], -# [0, 0, 'Cooperator', 'Cooperator', 'CC', 'CC'], -# [0, 0, 'Cooperator', 'Cooperator', 'CC', 'CC'], -# [3, 3, 'Grudger', 'Grudger', 'CC', 'CC'], -# [3, 3, 'Grudger', 'Grudger', 'CC', 'CC'], -# [2, 2, 'Defector', 'Defector', 'DD', 'DD'], -# [2, 2, 'Defector', 'Defector', 'DD', 'DD'], -# [4, 4, 'Soft Go By Majority', 'Soft Go By Majority', 'CC', 'CC'], -# [4, 4, 'Soft Go By Majority', 'Soft Go By Majority', 'CC', 'CC'], -# [1, 4, 'Tit For Tat', 'Soft Go By Majority', 'CC', 'CC'], -# [1, 4, 'Tit For Tat', 'Soft Go By Majority', 'CC', 'CC'], -# [1, 1, 'Tit For Tat', 'Tit For Tat', 'CC', 'CC'], -# [1, 1, 'Tit For Tat', 'Tit For Tat', 'CC', 'CC'], -# [1, 3, 'Tit For Tat', 'Grudger', 'CC', 'CC'], -# [1, 3, 'Tit For Tat', 'Grudger', 'CC', 'CC'], -# [2, 3, 'Defector', 'Grudger', 'DD', 'CD'], -# [2, 3, 'Defector', 'Grudger', 'DD', 'CD'], -# [0, 4, 'Cooperator', 'Soft Go By Majority', 'CC', 'CC'], -# [0, 4, 'Cooperator', 'Soft Go By Majority', 'CC', 'CC'], -# [2, 4, 'Defector', 'Soft Go By Majority', 'DD', 'CD'], -# [2, 4, 'Defector', 'Soft Go By Majority', 'DD', 'CD'], -# [0, 3, 'Cooperator', 'Grudger', 'CC', 'CC'], -# [0, 3, 'Cooperator', 'Grudger', 'CC', 'CC'], -# [3, 4, 'Grudger', 'Soft Go By Majority', 'CC', 'CC'], -# [3, 4, 'Grudger', 'Soft Go By Majority', 'CC', 'CC'], -# [0, 2, 'Cooperator', 'Defector', 'CC', 'DD'], -# [0, 2, 'Cooperator', 'Defector', 'CC', 'DD']] -# self.assertEqual(sorted(written_data), sorted(expected_data)) -# -# -# class TestProbEndTournament(unittest.TestCase): -# -# @classmethod -# def setUpClass(cls): -# cls.game = axelrod.Game() -# cls.players = [s() for s in test_strategies] -# cls.test_name = 'test' -# cls.test_repetitions = test_repetitions -# cls.test_prob_end = test_prob_end -# -# def test_init(self): -# tournament = axelrod.Tournament(name=self.test_name, -# players=self.players, -# game=self.game, -# prob_end=self.test_prob_end, -# noise=0.2) -# self.assertEqual(tournament.match_generator.prob_end, tournament.prob_end) -# self.assertEqual(len(tournament.players), len(test_strategies)) -# self.assertEqual(tournament.game.score((C, C)), (3, 3)) -# self.assertIsNone(tournament.turns) -# self.assertEqual(tournament.repetitions, 10) -# self.assertEqual(tournament.name, 'test') -# self.assertIsInstance(tournament._logger, logging.Logger) -# self.assertEqual(tournament.noise, 0.2) -# anonymous_tournament = axelrod.Tournament(players=self.players) -# self.assertEqual(anonymous_tournament.name, 'axelrod') -# -# @given(tournament=prob_end_tournaments(min_size=2, max_size=5, -# min_prob_end=.1, -# max_prob_end=.9, -# min_repetitions=2, -# max_repetitions=4)) -# @settings(max_examples=50, timeout=0) -# @example(tournament= -# axelrod.Tournament(players=[s() for s in test_strategies], -# prob_end=.2, repetitions=test_repetitions)) -# -# # These two examples are to make sure #465 is fixed. -# # As explained there: /~https://github.com/Axelrod-Python/Axelrod/issues/465, -# # these two examples were identified by hypothesis. -# @example(tournament= -# axelrod.Tournament(players=[axelrod.BackStabber(), -# axelrod.MindReader()], -# prob_end=.2, repetitions=1)) -# @example(tournament= -# axelrod.Tournament(players=[axelrod.ThueMorse(), -# axelrod.MindReader()], -# prob_end=.2, repetitions=1)) -# def test_property_serial_play(self, tournament): -# """Test serial play using hypothesis""" -# # Test that we get an instance of ResultSet -# results = tournament.play(progress_bar=False) -# self.assertIsInstance(results, axelrod.ResultSet) -# self.assertEqual(results.nplayers, len(tournament.players)) -# self.assertEqual(results.players, [str(p) for p in tournament.players]) -# -# -# class TestSpatialTournament(unittest.TestCase): -# -# @classmethod -# def setUpClass(cls): -# cls.game = axelrod.Game() -# cls.players = [s() for s in test_strategies] -# cls.test_name = 'test' -# cls.test_repetitions = test_repetitions -# cls.test_turns = test_turns -# cls.test_edges = test_edges -# -# def test_init(self): -# tournament = axelrod.Tournament(name=self.test_name, -# players=self.players, -# game=self.game, -# turns=self.test_turns, -# edges=self.test_edges, -# noise=0.2) -# self.assertEqual(tournament.match_generator.edges, tournament.edges) -# self.assertEqual(len(tournament.players), len(test_strategies)) -# self.assertEqual(tournament.game.score((C, C)), (3, 3)) -# self.assertEqual(tournament.turns, 100) -# self.assertEqual(tournament.repetitions, 10) -# self.assertEqual(tournament.name, 'test') -# self.assertIsInstance(tournament._logger, logging.Logger) -# self.assertEqual(tournament.noise, 0.2) -# self.assertEqual(tournament.match_generator.noise, 0.2) -# anonymous_tournament = axelrod.Tournament(players=self.players) -# self.assertEqual(anonymous_tournament.name, 'axelrod') -# -# @given(strategies=strategy_lists(strategies=deterministic_strategies, -# min_size=2, max_size=2), -# turns=integers(min_value=1, max_value=20), -# repetitions=integers(min_value=1, max_value=5), -# noise=floats(min_value=0, max_value=1), -# seed=integers(min_value=0, max_value=4294967295)) -# @settings(max_examples=50, timeout=0) -# def test_complete_tournament(self, strategies, turns, repetitions, -# noise, seed): -# """ -# A test to check that a spatial tournament on the complete multigraph -# gives the same results as the round robin. -# """ -# -# players = [s() for s in strategies] -# # edges -# edges = [] -# for i in range(0, len(players)): -# for j in range(i, len(players)): -# edges.append((i, j)) -# -# # create a round robin tournament -# tournament = axelrod.Tournament(players, repetitions=repetitions, -# turns=turns, noise=noise) -# # create a complete spatial tournament -# spatial_tournament = axelrod.Tournament(players, -# repetitions=repetitions, -# turns=turns, -# noise=noise, -# edges=edges) -# -# axelrod.seed(seed) -# results = tournament.play(progress_bar=False) -# axelrod.seed(seed) -# spatial_results = spatial_tournament.play(progress_bar=False) -# -# self.assertEqual(results.ranked_names, spatial_results.ranked_names) -# self.assertEqual(results.nplayers, spatial_results.nplayers) -# self.assertEqual(results.repetitions, spatial_results.repetitions) -# self.assertEqual(results.payoff_diffs_means, -# spatial_results.payoff_diffs_means) -# self.assertEqual(results.payoff_matrix, spatial_results.payoff_matrix) -# self.assertEqual(results.payoff_stddevs, spatial_results.payoff_stddevs) -# self.assertEqual(results.payoffs, spatial_results.payoffs) -# self.assertEqual(results.cooperating_rating, -# spatial_results.cooperating_rating) -# self.assertEqual(results.cooperation, spatial_results.cooperation) -# self.assertEqual(results.normalised_cooperation, -# spatial_results.normalised_cooperation) -# self.assertEqual(results.normalised_scores, -# spatial_results.normalised_scores) -# self.assertEqual(results.good_partner_matrix, -# spatial_results.good_partner_matrix) -# self.assertEqual(results.good_partner_rating, -# spatial_results.good_partner_rating) -# -# def test_particular_tournament(self): -# """A test for a tournament that has caused failures during some bug -# fixing""" -# players = [axelrod.Cooperator(), axelrod.Defector(), -# axelrod.TitForTat(), axelrod.Grudger()] -# edges = [(0, 2), (0, 3), (1, 2), (1, 3)] -# tournament = axelrod.Tournament(players, edges=edges) -# results = tournament.play(progress_bar=False) -# expected_ranked_names = ['Cooperator', 'Tit For Tat', -# 'Grudger', 'Defector'] -# self.assertEqual(results.ranked_names, expected_ranked_names) -# -# # Check that this tournament runs with noise -# tournament = axelrod.Tournament(players, edges=edges, noise=.5) -# results = tournament.play(progress_bar=False) -# self.assertIsInstance(results, axelrod.ResultSet) + def test_build_result_set(self): + tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=self.game, + turns=axelrod.DEFAULT_TURNS, + repetitions=self.test_repetitions) + results = tournament.play(progress_bar=False) + self.assertIsInstance(results, axelrod.ResultSet) - def test_close_objects_with_none(self): - self.assertIsNone(_close_objects(None, None)) + # Test in memory + results = tournament.play(progress_bar=False, in_memory=True) + self.assertIsInstance(results, axelrod.ResultSet) - def test_close_objects_with_file_objs(self): - f1 = open('to_delete_1', 'w') - f2 = open('to_delete_2', 'w') - f2.close() - f2 = open('to_delete_2', 'r') + def test_no_build_result_set(self): + tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=self.game, + turns=axelrod.DEFAULT_TURNS, + repetitions=self.test_repetitions) - self.assertFalse(f1.closed) - self.assertFalse(f2.closed) + results = tournament.play(build_results=False, filename=self.filename, + progress_bar=False) + self.assertIsNone(results) - _close_objects(f1, f2) + # Checking that results were written properly + results = axelrod.ResultSetFromFile(self.filename, progress_bar=False) + self.assertIsInstance(results, axelrod.ResultSet) - self.assertTrue(f1.closed) - self.assertTrue(f2.closed) + @given(turns=integers(min_value=1, max_value=200)) + @example(turns=3) + @example(turns=axelrod.DEFAULT_TURNS) + def test_play_matches(self, turns): + tournament = axelrod.Tournament(name=self.test_name, + players=self.players, + game=self.game, + repetitions=self.test_repetitions) + + def make_chunk_generator(): + for player1_index in range(len(self.players)): + for player2_index in range(player1_index, len(self.players)): + index_pair = (player1_index, player2_index) + match_params = {"turns": turns, "game": self.game} + yield (index_pair, match_params, self.test_repetitions) + + chunk_generator = make_chunk_generator() + interactions = {} + for chunk in chunk_generator: + result = tournament._play_matches(chunk) + for index_pair, inters in result.items(): + try: + interactions[index_pair].append(inters) + except KeyError: + interactions[index_pair] = [inters] + + self.assertEqual(len(interactions), 15) + + for index_pair, inter in interactions.items(): + self.assertEqual(len(index_pair), 2) + for plays in inter: + # Check that have the expected number of repetitions + self.assertEqual(len(plays), self.test_repetitions) + for repetition in plays: + # Check that have the correct length for each rep + self.assertEqual(len(repetition), turns) + + # Check that matches no longer exist + self.assertEqual((len(list(chunk_generator))), 0) + + def test_match_cache_is_used(self): + """ + Create two Random players that are classified as deterministic. + As they are deterministic the cache will be used. + """ + FakeRandom = axelrod.Random + FakeRandom.classifier["stochastic"] = False + p1 = FakeRandom() + p2 = FakeRandom() + tournament = axelrod.Tournament((p1, p2), turns=5, repetitions=2) + results = tournament.play(progress_bar=False) + for player_scores in results.scores: + self.assertEqual(player_scores[0], player_scores[1]) - os.remove('to_delete_1') - os.remove('to_delete_2') + def test_write_interactions(self): + tournament = axelrod.Tournament( - def test_close_objects_with_tqdm(self): - pbar_1 = tqdm(range(5)) - pbar_2 = tqdm(total=10, desc='hi', file=io.StringIO()) + name=self.test_name, + players=self.players, + game=self.game, + turns=2, + repetitions=2) + tournament._write_interactions = MagicMock(name='_write_interactions') + # Mocking this as it is called by play + tournament._build_result_set = MagicMock(name='_build_result_set') + self.assertTrue(tournament.play(filename=self.filename, + progress_bar=False)) - self.assertFalse(pbar_1.disable) - self.assertFalse(pbar_2.disable) + # Get the calls made to write_interactions + calls = tournament._write_interactions.call_args_list + self.assertEqual(len(calls), 15) - _close_objects(pbar_1, pbar_2) + # Test when running in memory + tournament._write_interactions = MagicMock(name='_write_interactions') + self.assertTrue(tournament.play(filename=self.filename, + progress_bar=False, + in_memory=False)) + # Get the calls made to write_interactions + calls = tournament._write_interactions.call_args_list + self.assertEqual(len(calls), 15) - self.assertTrue(pbar_1.disable) - self.assertTrue(pbar_2.disable) + def test_write_to_csv(self): + tournament = axelrod.Tournament( + name=self.test_name, + players=self.players, + game=self.game, + turns=2, + repetitions=2) + tournament.play(filename=self.filename, progress_bar=False) + with open(self.filename, 'r') as f: + written_data = [[int(r[0]), int(r[1])] + r[2:] for r in csv.reader(f)] + expected_data = [[0, 1, 'Cooperator', 'Tit For Tat', 'CC', 'CC'], + [0, 1, 'Cooperator', 'Tit For Tat', 'CC', 'CC'], + [1, 2, 'Tit For Tat', 'Defector', 'CD', 'DD'], + [1, 2, 'Tit For Tat', 'Defector', 'CD', 'DD'], + [0, 0, 'Cooperator', 'Cooperator', 'CC', 'CC'], + [0, 0, 'Cooperator', 'Cooperator', 'CC', 'CC'], + [3, 3, 'Grudger', 'Grudger', 'CC', 'CC'], + [3, 3, 'Grudger', 'Grudger', 'CC', 'CC'], + [2, 2, 'Defector', 'Defector', 'DD', 'DD'], + [2, 2, 'Defector', 'Defector', 'DD', 'DD'], + [4, 4, 'Soft Go By Majority', 'Soft Go By Majority', 'CC', 'CC'], + [4, 4, 'Soft Go By Majority', 'Soft Go By Majority', 'CC', 'CC'], + [1, 4, 'Tit For Tat', 'Soft Go By Majority', 'CC', 'CC'], + [1, 4, 'Tit For Tat', 'Soft Go By Majority', 'CC', 'CC'], + [1, 1, 'Tit For Tat', 'Tit For Tat', 'CC', 'CC'], + [1, 1, 'Tit For Tat', 'Tit For Tat', 'CC', 'CC'], + [1, 3, 'Tit For Tat', 'Grudger', 'CC', 'CC'], + [1, 3, 'Tit For Tat', 'Grudger', 'CC', 'CC'], + [2, 3, 'Defector', 'Grudger', 'DD', 'CD'], + [2, 3, 'Defector', 'Grudger', 'DD', 'CD'], + [0, 4, 'Cooperator', 'Soft Go By Majority', 'CC', 'CC'], + [0, 4, 'Cooperator', 'Soft Go By Majority', 'CC', 'CC'], + [2, 4, 'Defector', 'Soft Go By Majority', 'DD', 'CD'], + [2, 4, 'Defector', 'Soft Go By Majority', 'DD', 'CD'], + [0, 3, 'Cooperator', 'Grudger', 'CC', 'CC'], + [0, 3, 'Cooperator', 'Grudger', 'CC', 'CC'], + [3, 4, 'Grudger', 'Soft Go By Majority', 'CC', 'CC'], + [3, 4, 'Grudger', 'Soft Go By Majority', 'CC', 'CC'], + [0, 2, 'Cooperator', 'Defector', 'CC', 'DD'], + [0, 2, 'Cooperator', 'Defector', 'CC', 'DD']] + self.assertEqual(sorted(written_data), sorted(expected_data)) + + +class TestProbEndTournament(unittest.TestCase): - def test_close_objects_with_different_objects(self): - file = open('to_delete_1', 'w') - pbar = tqdm(range(5)) - num = 5 - empty = None - word = 'hi' + @classmethod + def setUpClass(cls): + cls.game = axelrod.Game() + cls.players = [s() for s in test_strategies] + cls.test_name = 'test' + cls.test_repetitions = test_repetitions + cls.test_prob_end = test_prob_end - _close_objects(file, pbar, num, empty, word) + def test_init(self): + tournament = axelrod.Tournament(name=self.test_name, + players=self.players, + game=self.game, + prob_end=self.test_prob_end, + noise=0.2) + self.assertEqual(tournament.match_generator.prob_end, tournament.prob_end) + self.assertEqual(len(tournament.players), len(test_strategies)) + self.assertEqual(tournament.game.score((C, C)), (3, 3)) + self.assertIsNone(tournament.turns) + self.assertEqual(tournament.repetitions, 10) + self.assertEqual(tournament.name, 'test') + self.assertIsInstance(tournament._logger, logging.Logger) + self.assertEqual(tournament.noise, 0.2) + anonymous_tournament = axelrod.Tournament(players=self.players) + self.assertEqual(anonymous_tournament.name, 'axelrod') - self.assertTrue(pbar.disable) - self.assertTrue(file.closed) + @given(tournament=prob_end_tournaments(min_size=2, max_size=5, + min_prob_end=.1, + max_prob_end=.9, + min_repetitions=2, + max_repetitions=4)) + @settings(max_examples=50, timeout=0) + @example(tournament= + axelrod.Tournament(players=[s() for s in test_strategies], + prob_end=.2, repetitions=test_repetitions)) + + # These two examples are to make sure #465 is fixed. + # As explained there: /~https://github.com/Axelrod-Python/Axelrod/issues/465, + # these two examples were identified by hypothesis. + @example(tournament= + axelrod.Tournament(players=[axelrod.BackStabber(), + axelrod.MindReader()], + prob_end=.2, repetitions=1)) + @example(tournament= + axelrod.Tournament(players=[axelrod.ThueMorse(), + axelrod.MindReader()], + prob_end=.2, repetitions=1)) + def test_property_serial_play(self, tournament): + """Test serial play using hypothesis""" + # Test that we get an instance of ResultSet + results = tournament.play(progress_bar=False) + self.assertIsInstance(results, axelrod.ResultSet) + self.assertEqual(results.nplayers, len(tournament.players)) + self.assertEqual(results.players, [str(p) for p in tournament.players]) - os.remove('to_delete_1') + +class TestSpatialTournament(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.game = axelrod.Game() + cls.players = [s() for s in test_strategies] + cls.test_name = 'test' + cls.test_repetitions = test_repetitions + cls.test_turns = test_turns + cls.test_edges = test_edges + + def test_init(self): + tournament = axelrod.Tournament(name=self.test_name, + players=self.players, + game=self.game, + turns=self.test_turns, + edges=self.test_edges, + noise=0.2) + self.assertEqual(tournament.match_generator.edges, tournament.edges) + self.assertEqual(len(tournament.players), len(test_strategies)) + self.assertEqual(tournament.game.score((C, C)), (3, 3)) + self.assertEqual(tournament.turns, 100) + self.assertEqual(tournament.repetitions, 10) + self.assertEqual(tournament.name, 'test') + self.assertIsInstance(tournament._logger, logging.Logger) + self.assertEqual(tournament.noise, 0.2) + self.assertEqual(tournament.match_generator.noise, 0.2) + anonymous_tournament = axelrod.Tournament(players=self.players) + self.assertEqual(anonymous_tournament.name, 'axelrod') + + @given(strategies=strategy_lists(strategies=deterministic_strategies, + min_size=2, max_size=2), + turns=integers(min_value=1, max_value=20), + repetitions=integers(min_value=1, max_value=5), + noise=floats(min_value=0, max_value=1), + seed=integers(min_value=0, max_value=4294967295)) + @settings(max_examples=50, timeout=0) + def test_complete_tournament(self, strategies, turns, repetitions, + noise, seed): + """ + A test to check that a spatial tournament on the complete multigraph + gives the same results as the round robin. + """ + + players = [s() for s in strategies] + # edges + edges = [] + for i in range(0, len(players)): + for j in range(i, len(players)): + edges.append((i, j)) + + # create a round robin tournament + tournament = axelrod.Tournament(players, repetitions=repetitions, + turns=turns, noise=noise) + # create a complete spatial tournament + spatial_tournament = axelrod.Tournament(players, + repetitions=repetitions, + turns=turns, + noise=noise, + edges=edges) + + axelrod.seed(seed) + results = tournament.play(progress_bar=False) + axelrod.seed(seed) + spatial_results = spatial_tournament.play(progress_bar=False) + + self.assertEqual(results.ranked_names, spatial_results.ranked_names) + self.assertEqual(results.nplayers, spatial_results.nplayers) + self.assertEqual(results.repetitions, spatial_results.repetitions) + self.assertEqual(results.payoff_diffs_means, + spatial_results.payoff_diffs_means) + self.assertEqual(results.payoff_matrix, spatial_results.payoff_matrix) + self.assertEqual(results.payoff_stddevs, spatial_results.payoff_stddevs) + self.assertEqual(results.payoffs, spatial_results.payoffs) + self.assertEqual(results.cooperating_rating, + spatial_results.cooperating_rating) + self.assertEqual(results.cooperation, spatial_results.cooperation) + self.assertEqual(results.normalised_cooperation, + spatial_results.normalised_cooperation) + self.assertEqual(results.normalised_scores, + spatial_results.normalised_scores) + self.assertEqual(results.good_partner_matrix, + spatial_results.good_partner_matrix) + self.assertEqual(results.good_partner_rating, + spatial_results.good_partner_rating) + + def test_particular_tournament(self): + """A test for a tournament that has caused failures during some bug + fixing""" + players = [axelrod.Cooperator(), axelrod.Defector(), + axelrod.TitForTat(), axelrod.Grudger()] + edges = [(0, 2), (0, 3), (1, 2), (1, 3)] + tournament = axelrod.Tournament(players, edges=edges) + results = tournament.play(progress_bar=False) + expected_ranked_names = ['Cooperator', 'Tit For Tat', + 'Grudger', 'Defector'] + self.assertEqual(results.ranked_names, expected_ranked_names) + + # Check that this tournament runs with noise + tournament = axelrod.Tournament(players, edges=edges, noise=.5) + results = tournament.play(progress_bar=False) + self.assertIsInstance(results, axelrod.ResultSet) class TestProbEndingSpatialTournament(unittest.TestCase): @@ -919,3 +943,54 @@ def test_one_turn_tournament(self, tournament, seed): one_turn_results.wins) self.assertEqual(prob_end_results.cooperation, one_turn_results.cooperation) + + +class TestHelperFunctions(unittest.TestCase): + def test_close_objects_with_none(self): + self.assertIsNone(_close_objects(None, None)) + + def test_close_objects_with_file_objs(self): + f1 = open('to_delete_1', 'w') + f2 = open('to_delete_2', 'w') + f2.close() + f2 = open('to_delete_2', 'r') + + self.assertFalse(f1.closed) + self.assertFalse(f2.closed) + + _close_objects(f1, f2) + + self.assertTrue(f1.closed) + self.assertTrue(f2.closed) + + os.remove('to_delete_1') + os.remove('to_delete_2') + + def test_close_objects_with_tqdm(self): + pbar_1 = tqdm(range(5)) + pbar_2 = tqdm(total=10, desc='hi', file=io.StringIO()) + + self.assertFalse(pbar_1.disable) + self.assertFalse(pbar_2.disable) + + _close_objects(pbar_1, pbar_2) + + self.assertTrue(pbar_1.disable) + self.assertTrue(pbar_2.disable) + + def test_close_objects_with_different_objects(self): + file = open('to_delete_1', 'w') + pbar = tqdm(range(5)) + num = 5 + empty = None + word = 'hi' + + _close_objects(file, pbar, num, empty, word) + + self.assertTrue(pbar.disable) + self.assertTrue(file.closed) + + os.remove('to_delete_1') + +if __name__ == '__main__': + unittest.main() diff --git a/axelrod/tournament.py b/axelrod/tournament.py index f29cf1c75..db6e5ec50 100644 --- a/axelrod/tournament.py +++ b/axelrod/tournament.py @@ -147,7 +147,6 @@ def play(self, build_results: bool = True, filename: str = None, if self._temp_file_descriptor is not None: os.close(self._temp_file_descriptor) os.remove(self.filename) - self.filename = None return result_set From d84fe745847f5fff363d6559956cf9e99735c2c1 Mon Sep 17 00:00:00 2001 From: E Shaw Date: Fri, 11 Aug 2017 03:43:18 +0800 Subject: [PATCH 07/11] mypy fix in tournament. removed skip in test_fingerprint.py --- axelrod/tests/unit/test_fingerprint.py | 2 -- axelrod/tournament.py | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/axelrod/tests/unit/test_fingerprint.py b/axelrod/tests/unit/test_fingerprint.py index 459e487c5..605af5e1e 100644 --- a/axelrod/tests/unit/test_fingerprint.py +++ b/axelrod/tests/unit/test_fingerprint.py @@ -196,8 +196,6 @@ def test_serial_fingerprint(self): self.assertEqual(edge_keys, self.expected_edges) self.assertEqual(coord_keys, self.expected_points) - @unittest.skipIf(axl.on_windows, - "Parallel processing not supported on Windows") def test_parallel_fingerprint(self): af = AshlockFingerprint(self.strategy, self.probe) af.fingerprint(turns=10, repetitions=2, step=0.5, processes=2, diff --git a/axelrod/tournament.py b/axelrod/tournament.py index db6e5ec50..1a9283268 100644 --- a/axelrod/tournament.py +++ b/axelrod/tournament.py @@ -80,8 +80,8 @@ def __init__(self, players: List[Player], self._logger = logging.getLogger(__name__) self.use_progress_bar = True - self.filename = None - self._temp_file_descriptor = None + self.filename = None # type: str + self._temp_file_descriptor = None # type: int def setup_output(self, filename=None, in_memory=False): """assign/create filename to self and file descriptor if file should From fe69ea10b98cc36b82afa1d214fadd907ed51e0b Mon Sep 17 00:00:00 2001 From: E Shaw Date: Fri, 11 Aug 2017 04:10:15 +0800 Subject: [PATCH 08/11] small fixes --- axelrod/tests/unit/test_tournament.py | 23 ++++++++++++++++++++++- axelrod/tournament.py | 5 ++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/axelrod/tests/unit/test_tournament.py b/axelrod/tests/unit/test_tournament.py index 97b9b69f9..22ca61d84 100644 --- a/axelrod/tests/unit/test_tournament.py +++ b/axelrod/tests/unit/test_tournament.py @@ -187,6 +187,27 @@ def test_play_tempfile_removed(self): self.assertFalse(os.path.isfile(self.test_tournament.filename)) + def test_play_resets_filename_and_temp_file_descriptor_each_time(self): + self.test_tournament.play(progress_bar=False) + self.assertIsInstance(self.test_tournament._temp_file_descriptor, int) + self.assertIsInstance(self.test_tournament.filename, str) + old_filename = self.test_tournament.filename + + self.test_tournament.play(filename=self.filename, progress_bar=False) + self.assertIsNone(self.test_tournament._temp_file_descriptor) + self.assertEqual(self.test_tournament.filename, self.filename) + self.assertNotEqual(old_filename, self.test_tournament.filename) + + self.test_tournament.play(progress_bar=False) + self.assertIsInstance(self.test_tournament._temp_file_descriptor, int) + self.assertIsInstance(self.test_tournament.filename, str) + self.assertNotEqual(old_filename, self.test_tournament.filename) + self.assertNotEqual(self.test_tournament.filename, self.filename) + + self.test_tournament.play(in_memory=True, progress_bar=False) + self.assertIsNone(self.test_tournament._temp_file_descriptor) + self.assertIsNone(self.test_tournament.filename) + def test_get_file_objects_no_filename(self): file, writer = self.test_tournament._get_file_objects() self.assertIsNone(file) @@ -389,7 +410,7 @@ def test_parallel_play(self): game=self.game, turns=axelrod.DEFAULT_TURNS, repetitions=self.test_repetitions) - results = tournament.play(processes=4, progress_bar=False) + results = tournament.play(processes=2, progress_bar=False) self.assertIsInstance(results, axelrod.ResultSet) self.assertEqual(tournament.num_interactions, 75) diff --git a/axelrod/tournament.py b/axelrod/tournament.py index 1a9283268..f4ae81259 100644 --- a/axelrod/tournament.py +++ b/axelrod/tournament.py @@ -3,7 +3,6 @@ import logging from multiprocessing import Process, Queue, cpu_count from tempfile import mkstemp -from typing import Any import warnings import os import sys @@ -84,8 +83,8 @@ def __init__(self, players: List[Player], self._temp_file_descriptor = None # type: int def setup_output(self, filename=None, in_memory=False): - """assign/create filename to self and file descriptor if file should - be deleted once `play` is finished. """ + """assign/create `filename` to `self`. If file should be deleted once + `play` is finished, assign a file descriptor. """ temp_file_descriptor = None if in_memory: self.interactions_dict = {} From bb299981f48a1aa9df4c5835051a9567d3a4329e Mon Sep 17 00:00:00 2001 From: E Shaw Date: Sat, 12 Aug 2017 00:17:15 +0800 Subject: [PATCH 09/11] fixed test coverage problem (hopefully) --- axelrod/tests/unit/test_tournament.py | 13 +++++++++---- axelrod/tournament.py | 1 - 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/axelrod/tests/unit/test_tournament.py b/axelrod/tests/unit/test_tournament.py index 22ca61d84..8a710cf22 100644 --- a/axelrod/tests/unit/test_tournament.py +++ b/axelrod/tests/unit/test_tournament.py @@ -6,6 +6,7 @@ import logging from multiprocessing import Queue, cpu_count import os +import pickle import unittest from unittest.mock import MagicMock import warnings @@ -440,7 +441,6 @@ def test_parallel_play_with_writing_to_file(self): self.assertIsInstance(results, axelrod.ResultSet) self.assertEqual(tournament.num_interactions, 75) - def test_run_serial(self): tournament = axelrod.Tournament( name=self.test_name, @@ -469,6 +469,14 @@ def __reduce__(self): repetitions=self.test_repetitions) tournament._write_interactions = PickleableMock( name='_write_interactions') + + # For test coverage purposes. This confirms PickleableMock can be + # pickled exactly once. Windows multi-processing must pickle this Mock + # exactly once during testing. + pickled = pickle.loads(pickle.dumps(tournament)) + self.assertIsInstance(pickled._write_interactions, MagicMock) + self.assertRaises(pickle.PicklingError, pickle.dumps, pickled) + self.assertTrue(tournament._run_parallel()) # Get the calls made to write_interactions @@ -1012,6 +1020,3 @@ def test_close_objects_with_different_objects(self): self.assertTrue(file.closed) os.remove('to_delete_1') - -if __name__ == '__main__': - unittest.main() diff --git a/axelrod/tournament.py b/axelrod/tournament.py index f4ae81259..12a8d9139 100644 --- a/axelrod/tournament.py +++ b/axelrod/tournament.py @@ -264,7 +264,6 @@ def _run_parallel(self, processes: int=2) -> bool: work_queue.put(chunk) self._start_workers(workers, work_queue, done_queue) - self._process_done_queue(workers, done_queue) return True From 304dafad7df90068d09abe951f843dcc0b55097f Mon Sep 17 00:00:00 2001 From: E Shaw Date: Tue, 15 Aug 2017 02:53:47 +0800 Subject: [PATCH 10/11] removed on_windows. updated tests and fingerprint. small fixes --- axelrod/__init__.py | 3 - axelrod/fingerprint.py | 17 ++- axelrod/result_set.py | 8 +- axelrod/tests/integration/test_tournament.py | 2 - axelrod/tests/unit/test_fingerprint.py | 46 ++++++ axelrod/tests/unit/test_tournament.py | 143 ++++++++++++------- axelrod/tests/unit/test_windows_detection.py | 19 --- axelrod/tournament.py | 13 +- 8 files changed, 157 insertions(+), 94 deletions(-) delete mode 100644 axelrod/tests/unit/test_windows_detection.py diff --git a/axelrod/__init__.py b/axelrod/__init__.py index 0fe5a15bb..284ca434b 100644 --- a/axelrod/__init__.py +++ b/axelrod/__init__.py @@ -1,6 +1,3 @@ -import os - -on_windows = os.name == 'nt' DEFAULT_TURNS = 200 # The order of imports matters! diff --git a/axelrod/fingerprint.py b/axelrod/fingerprint.py index 2f1cca9c7..f1d7e8583 100644 --- a/axelrod/fingerprint.py +++ b/axelrod/fingerprint.py @@ -1,11 +1,12 @@ from collections import namedtuple -from tempfile import NamedTemporaryFile +import os +from tempfile import mkstemp import matplotlib.pyplot as plt import numpy as np import tqdm import axelrod as axl -from axelrod import on_windows, Player +from axelrod import Player from axelrod.strategy_transformers import JossAnnTransformer, DualTransformer from axelrod.interaction_utils import ( compute_final_score_per_turn, read_interactions_from_file) @@ -300,11 +301,9 @@ def fingerprint( the values are the mean score for the corresponding interactions. """ - if on_windows and (filename is None): # pragma: no cover - in_memory = True - elif filename is None: - outputfile = NamedTemporaryFile(mode='w') - filename = outputfile.name + temp_file_descriptor = None + if not in_memory and filename is None: + temp_file_descriptor, filename = mkstemp() edges, tourn_players = self.construct_tournament_elements( step, progress_bar=progress_bar) @@ -324,6 +323,10 @@ def fingerprint( self.interactions = read_interactions_from_file( filename, progress_bar=progress_bar) + if temp_file_descriptor is not None: + os.close(temp_file_descriptor) + os.remove(filename) + self.data = generate_data(self.interactions, self.points, edges) return self.data diff --git a/axelrod/result_set.py b/axelrod/result_set.py index 3829ddd9d..480af41af 100644 --- a/axelrod/result_set.py +++ b/axelrod/result_set.py @@ -1,6 +1,5 @@ from collections import namedtuple, Counter import csv -import sys from numpy import mean, nanmedian, std import tqdm @@ -77,7 +76,7 @@ def create_progress_bar(self, desc=None): desc : string A description. """ - return tqdm.tqdm(total=self.num_matches, desc=desc, file=sys.stderr) + return tqdm.tqdm(total=self.num_matches, desc=desc) def _update_players(self, index_pair, players): """ @@ -744,7 +743,7 @@ def _build_score_related_metrics(self, progress_bar=False, if progress_bar: self.progress_bar = tqdm.tqdm(total=13 + 2 * self.nplayers, - file=sys.stderr, desc="Finishing") + desc="Finishing") self._summarise_normalised_scores() self._summarise_normalised_cooperation() @@ -1015,8 +1014,7 @@ def create_progress_bar(self, desc=None): if not self.num_interactions: with open(self.filename) as f: self.num_interactions = sum(1 for line in f) - return tqdm.tqdm(total=self.num_interactions, desc=desc, - file=sys.stderr) + return tqdm.tqdm(total=self.num_interactions, desc=desc) def _read_players_and_repetition_numbers(self, progress_bar=False): """ diff --git a/axelrod/tests/integration/test_tournament.py b/axelrod/tests/integration/test_tournament.py index 437f44bc7..839791ddd 100644 --- a/axelrod/tests/integration/test_tournament.py +++ b/axelrod/tests/integration/test_tournament.py @@ -49,8 +49,6 @@ def test_serial_play(self): actual_outcome = sorted(zip(self.player_names, scores)) self.assertEqual(actual_outcome, self.expected_outcome) - @unittest.skipIf(axelrod.on_windows, - "Parallel processing not supported on Windows") def test_parallel_play(self): tournament = axelrod.Tournament( name=self.test_name, diff --git a/axelrod/tests/unit/test_fingerprint.py b/axelrod/tests/unit/test_fingerprint.py index 605af5e1e..bab713e1e 100644 --- a/axelrod/tests/unit/test_fingerprint.py +++ b/axelrod/tests/unit/test_fingerprint.py @@ -1,4 +1,7 @@ +import os +from tempfile import mkstemp import unittest +from unittest.mock import patch from hypothesis import given import axelrod as axl from axelrod.fingerprint import (create_points, create_jossann, create_probes, @@ -17,6 +20,22 @@ C, D = axl.Action.C, axl.Action.D +class RecordedMksTemp(object): + """This object records all results from RecordedMksTemp.mkstemp. It's for + testing that temp files are created and then destroyed.""" + record = [] + + @staticmethod + def mkstemp(*args, **kwargs): + temp_file_info = mkstemp(*args, **kwargs) + RecordedMksTemp.record.append(temp_file_info) + return temp_file_info + + @staticmethod + def reset_record(): + RecordedMksTemp.record = [] + + class TestFingerprint(unittest.TestCase): @classmethod @@ -165,6 +184,33 @@ def test_progress_bar_fingerprint(self): progress_bar=True) self.assertEqual(sorted(data.keys()), self.expected_points) + @patch('axelrod.fingerprint.mkstemp', RecordedMksTemp.mkstemp) + def test_temp_file_creation(self): + + RecordedMksTemp.reset_record() + af = AshlockFingerprint(self.strategy, self.probe) + filename = "test_outputs/test_fingerprint.csv" + + # No temp file is created. + af.fingerprint(turns=1, repetitions=1, step=0.5, progress_bar=False, + in_memory=True) + af.fingerprint(turns=1, repetitions=1, step=0.5, progress_bar=False, + in_memory=True, filename=filename) + af.fingerprint(turns=1, repetitions=1, step=0.5, progress_bar=False, + in_memory=False, filename=filename) + + self.assertEqual(RecordedMksTemp.record, []) + + # Temp file is created and destroyed. + af.fingerprint(turns=1, repetitions=1, step=0.5, progress_bar=False, + in_memory=False, filename=None) + + self.assertEqual(len(RecordedMksTemp.record), 1) + filename = RecordedMksTemp.record[0][1] + self.assertIsInstance(filename, str) + self.assertNotEqual(filename, '') + self.assertFalse(os.path.isfile(filename)) + def test_fingerprint_with_filename(self): filename = "test_outputs/test_fingerprint.csv" af = AshlockFingerprint(self.strategy, self.probe) diff --git a/axelrod/tests/unit/test_tournament.py b/axelrod/tests/unit/test_tournament.py index 8a710cf22..0dc955b90 100644 --- a/axelrod/tests/unit/test_tournament.py +++ b/axelrod/tests/unit/test_tournament.py @@ -1,6 +1,5 @@ """Tests for the main tournament class.""" -from contextlib import redirect_stderr import csv import io import logging @@ -8,7 +7,7 @@ import os import pickle import unittest -from unittest.mock import MagicMock +from unittest.mock import MagicMock, patch import warnings from hypothesis import given, example, settings @@ -42,6 +41,21 @@ if not s().classifier['stochastic']] +class RecordedTQDM(tqdm): + """This is a tqdm.tqdm that keeps a record of every RecordedTQDM created. + It is used to test that progress bars were correctly created and then + closed.""" + record = [] + + def __init__(self, *args, **kwargs): + super(RecordedTQDM, self).__init__(*args, **kwargs) + RecordedTQDM.record.append(self) + + @classmethod + def reset_record(cls): + cls.record = [] + + class TestTournament(unittest.TestCase): @classmethod @@ -155,6 +169,14 @@ def test_setup_output_no_filename_no_in_memory(self): os.close(self.test_tournament._temp_file_descriptor) os.remove(self.test_tournament.filename) + def test_play_resets_num_interactions(self): + self.assertEqual(self.test_tournament.num_interactions, 0) + self.test_tournament.play(progress_bar=False) + self.assertEqual(self.test_tournament.num_interactions, 15) + + self.test_tournament.play(progress_bar=False) + self.assertEqual(self.test_tournament.num_interactions, 15) + def test_play_changes_use_progress_bar(self): self.assertTrue(self.test_tournament.use_progress_bar) @@ -232,6 +254,15 @@ def test_get_progress_bar(self): self.assertEqual(pbar.n, 0) self.assertEqual(pbar.total, self.test_tournament.match_generator.size) + new_edges = [(0, 1), (1, 2), (2, 3), (3, 4)] + new_tournament = axelrod.Tournament(players=self.players, + edges=new_edges) + new_tournament.use_progress_bar = True + pbar = new_tournament._get_progress_bar() + self.assertEqual(pbar.desc, 'Playing matches: ') + self.assertEqual(pbar.n, 0) + self.assertEqual(pbar.total, len(new_edges)) + def test_serial_play(self): # Test that we get an instance of ResultSet tournament = axelrod.Tournament( @@ -265,6 +296,7 @@ def test_serial_play_with_different_game(self): results = tournament.play(progress_bar=False) self.assertEqual(results.game.RPST(), (-1, -1, -1, -1)) + @patch('tqdm.tqdm', RecordedTQDM) def test_no_progress_bar_play(self): """Test that progress bar is not created for progress_bar=False""" tournament = axelrod.Tournament( @@ -276,25 +308,24 @@ def test_no_progress_bar_play(self): # Test with build results - err = io.StringIO() - with redirect_stderr(err): - results = tournament.play(progress_bar=False) + RecordedTQDM.reset_record() + results = tournament.play(progress_bar=False) self.assertIsInstance(results, axelrod.ResultSet) # Check that no progress bar wrote output. - self.assertEqual(err.getvalue(), '') + self.assertEqual(RecordedTQDM.record, []) # Test without build results - err = io.StringIO() - with redirect_stderr(err): - results = tournament.play(progress_bar=False, build_results=False, - filename=self.filename) + RecordedTQDM.reset_record() + results = tournament.play(progress_bar=False, build_results=False, + filename=self.filename) self.assertIsNone(results) - self.assertEqual(err.getvalue(), '') + self.assertEqual(RecordedTQDM.record, []) results = axelrod.ResultSetFromFile(self.filename, progress_bar=False) self.assertIsInstance(results, axelrod.ResultSet) + @patch('tqdm.tqdm', RecordedTQDM) def test_progress_bar_play(self): """Test that progress bar is created by default and with True argument""" tournament = axelrod.Tournament( @@ -304,39 +335,43 @@ def test_progress_bar_play(self): turns=axelrod.DEFAULT_TURNS, repetitions=self.test_repetitions) - err = io.StringIO() - with redirect_stderr(err): - results = tournament.play() + RecordedTQDM.reset_record() + results = tournament.play() self.assertIsInstance(results, axelrod.ResultSet) # Check that progress bar wrote output. - stderr_output = err.getvalue() - self.assertIn('Playing matches:', stderr_output) - self.assertIn('Analysing:', stderr_output) - self.assertIn('100%', stderr_output) - - err = io.StringIO() - with redirect_stderr(err): - results = tournament.play(progress_bar=True) + self.assertEqual(len(RecordedTQDM.record), 3) + play_pbar = RecordedTQDM.record[0] + self.assertEqual(play_pbar.desc, 'Playing matches: ') + self.assertEqual(play_pbar.n, 15) + self.assertEqual(play_pbar.total, 15) + self.assertTrue(all(pbar.disable for pbar in RecordedTQDM.record)) + + RecordedTQDM.reset_record() + results = tournament.play(progress_bar=True) self.assertIsInstance(results, axelrod.ResultSet) - stderr_output = err.getvalue() - self.assertIn('Playing matches:', stderr_output) - self.assertIn('Analysing:', stderr_output) - self.assertIn('100%', stderr_output) + self.assertEqual(len(RecordedTQDM.record), 3) + play_pbar = RecordedTQDM.record[0] + self.assertEqual(play_pbar.desc, 'Playing matches: ') + self.assertEqual(play_pbar.n, 15) + self.assertEqual(play_pbar.total, 15) + self.assertTrue(play_pbar.disable) # Test without build results - err = io.StringIO() - with redirect_stderr(err): - results = tournament.play(progress_bar=True, build_results=False, - filename=self.filename) + RecordedTQDM.reset_record() + results = tournament.play(progress_bar=True, build_results=False, + filename=self.filename) self.assertIsNone(results) - stderr_output = err.getvalue() - self.assertIn('Playing matches:', stderr_output) - self.assertNotIn('Analysing:', stderr_output) - self.assertIn('100%', stderr_output) + self.assertEqual(len(RecordedTQDM.record), 1) + play_pbar = RecordedTQDM.record[0] + self.assertEqual(play_pbar.desc, 'Playing matches: ') + self.assertEqual(play_pbar.n, 15) + self.assertEqual(play_pbar.total, 15) + self.assertTrue(play_pbar.disable) results = axelrod.ResultSetFromFile(self.filename) self.assertIsInstance(results, axelrod.ResultSet) + @patch('tqdm.tqdm', RecordedTQDM) def test_progress_bar_play_parallel(self): """Test that tournament plays when asking for progress bar for parallel tournament and that progress bar is created.""" @@ -348,32 +383,36 @@ def test_progress_bar_play_parallel(self): repetitions=self.test_repetitions) # progress_bar = False - err = io.StringIO() - with redirect_stderr(err): - results = tournament.play(progress_bar=False, processes=2) - self.assertEqual(err.getvalue(), '') + RecordedTQDM.reset_record() + results = tournament.play(progress_bar=False, processes=2) + self.assertEqual(RecordedTQDM.record, []) self.assertIsInstance(results, axelrod.ResultSet) - # progress_bar = True - err = io.StringIO() - with redirect_stderr(err): - results = tournament.play(progress_bar=True, processes=2) - stderr_output = err.getvalue() - self.assertIn('Playing matches', stderr_output) - self.assertIn('100%', stderr_output) + # progress_bar = True + RecordedTQDM.reset_record() + results = tournament.play(progress_bar=True, processes=2) self.assertIsInstance(results, axelrod.ResultSet) - # progress_bar is default - err = io.StringIO() - with redirect_stderr(err): - results = tournament.play(processes=2) - stderr_output = err.getvalue() + self.assertEqual(len(RecordedTQDM.record), 3) + play_pbar = RecordedTQDM.record[0] + self.assertEqual(play_pbar.desc, 'Playing matches: ') + self.assertEqual(play_pbar.n, 15) + self.assertEqual(play_pbar.total, 15) + self.assertTrue(play_pbar.disable) - self.assertIn('Playing matches', stderr_output) - self.assertIn('100%', stderr_output) + # progress_bar is default + RecordedTQDM.reset_record() + results = tournament.play(processes=2) self.assertIsInstance(results, axelrod.ResultSet) + self.assertEqual(len(RecordedTQDM.record), 3) + play_pbar = RecordedTQDM.record[0] + self.assertEqual(play_pbar.desc, 'Playing matches: ') + self.assertEqual(play_pbar.n, 15) + self.assertEqual(play_pbar.total, 15) + self.assertTrue(play_pbar.disable) + @given(tournament=tournaments(min_size=2, max_size=5, min_turns=2, max_turns=10, min_repetitions=2, max_repetitions=4)) diff --git a/axelrod/tests/unit/test_windows_detection.py b/axelrod/tests/unit/test_windows_detection.py deleted file mode 100644 index cbdf7800c..000000000 --- a/axelrod/tests/unit/test_windows_detection.py +++ /dev/null @@ -1,19 +0,0 @@ -"""Tests Windows Detection.""" -import unittest -import os -import axelrod - - -class TestWindowsDetection(unittest.TestCase): - - @unittest.skipIf(os.name == 'nt', - "Skip this test if on windows") - def test_detection_on_not_windows(self): - """Test when not on windows""" - self.assertFalse(axelrod.on_windows) # pragma: no cover - - @unittest.skipIf(os.name != 'nt', - "Skip this test if not on windows") - def test_detection_on_not_windows(self): - """Test when on windows""" - self.assertTrue(axelrod.on_windows) # pragma: no cover diff --git a/axelrod/tournament.py b/axelrod/tournament.py index 12a8d9139..9383dd6ce 100644 --- a/axelrod/tournament.py +++ b/axelrod/tournament.py @@ -5,7 +5,6 @@ from tempfile import mkstemp import warnings import os -import sys import tqdm @@ -123,6 +122,8 @@ def play(self, build_results: bool = True, filename: str = None, ------- axelrod.ResultSetFromFile """ + self.num_interactions = 0 + self.use_progress_bar = progress_bar self.setup_output(filename, in_memory) @@ -181,7 +182,7 @@ def _run_serial(self) -> bool: chunks = self.match_generator.build_match_chunks() - file, writer = self._get_file_objects() + out_file, writer = self._get_file_objects() progress_bar = self._get_progress_bar() for chunk in chunks: @@ -191,7 +192,7 @@ def _run_serial(self) -> bool: if self.use_progress_bar: progress_bar.update(1) - _close_objects(file, progress_bar) + _close_objects(out_file, progress_bar) return True @@ -207,7 +208,7 @@ def _get_file_objects(self): def _get_progress_bar(self): if self.use_progress_bar: - return tqdm.tqdm(total=self.match_generator.size, file=sys.stderr, + return tqdm.tqdm(total=self.match_generator.size, desc="Playing matches") else: return None @@ -314,7 +315,7 @@ def _process_done_queue(self, workers: int, done_queue: Queue): done_queue : multiprocessing.Queue A queue containing the output dictionaries from each round robin """ - file, writer = self._get_file_objects() + out_file, writer = self._get_file_objects() progress_bar = self._get_progress_bar() stops = 0 @@ -328,7 +329,7 @@ def _process_done_queue(self, workers: int, done_queue: Queue): if self.use_progress_bar: progress_bar.update(1) - _close_objects(file, progress_bar) + _close_objects(out_file, progress_bar) return True def _worker(self, work_queue: Queue, done_queue: Queue): From efb8135082bc20740de73c8a58d589c8e26df453 Mon Sep 17 00:00:00 2001 From: E Shaw Date: Tue, 15 Aug 2017 20:49:44 +0800 Subject: [PATCH 11/11] minor tweeks --- axelrod/tests/unit/test_tournament.py | 36 +++++++++++---------------- axelrod/tournament.py | 3 +-- 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/axelrod/tests/unit/test_tournament.py b/axelrod/tests/unit/test_tournament.py index 0dc955b90..95fdeef97 100644 --- a/axelrod/tests/unit/test_tournament.py +++ b/axelrod/tests/unit/test_tournament.py @@ -311,7 +311,7 @@ def test_no_progress_bar_play(self): RecordedTQDM.reset_record() results = tournament.play(progress_bar=False) self.assertIsInstance(results, axelrod.ResultSet) - # Check that no progress bar wrote output. + # Check that no progress bar was created. self.assertEqual(RecordedTQDM.record, []) @@ -325,6 +325,12 @@ def test_no_progress_bar_play(self): results = axelrod.ResultSetFromFile(self.filename, progress_bar=False) self.assertIsInstance(results, axelrod.ResultSet) + def assert_play_pbar_correct_total_and_finished(self, pbar, total): + self.assertEqual(pbar.desc, 'Playing matches: ') + self.assertEqual(pbar.total, total) + self.assertEqual(pbar.n, total) + self.assertTrue(pbar.disable, True) + @patch('tqdm.tqdm', RecordedTQDM) def test_progress_bar_play(self): """Test that progress bar is created by default and with True argument""" @@ -338,12 +344,11 @@ def test_progress_bar_play(self): RecordedTQDM.reset_record() results = tournament.play() self.assertIsInstance(results, axelrod.ResultSet) - # Check that progress bar wrote output. + # Check that progress bar was created, updated and closed. self.assertEqual(len(RecordedTQDM.record), 3) play_pbar = RecordedTQDM.record[0] - self.assertEqual(play_pbar.desc, 'Playing matches: ') - self.assertEqual(play_pbar.n, 15) - self.assertEqual(play_pbar.total, 15) + self.assert_play_pbar_correct_total_and_finished(play_pbar, total=15) + # Check all progress bars are closed. self.assertTrue(all(pbar.disable for pbar in RecordedTQDM.record)) RecordedTQDM.reset_record() @@ -351,10 +356,7 @@ def test_progress_bar_play(self): self.assertIsInstance(results, axelrod.ResultSet) self.assertEqual(len(RecordedTQDM.record), 3) play_pbar = RecordedTQDM.record[0] - self.assertEqual(play_pbar.desc, 'Playing matches: ') - self.assertEqual(play_pbar.n, 15) - self.assertEqual(play_pbar.total, 15) - self.assertTrue(play_pbar.disable) + self.assert_play_pbar_correct_total_and_finished(play_pbar, total=15) # Test without build results RecordedTQDM.reset_record() @@ -363,10 +365,7 @@ def test_progress_bar_play(self): self.assertIsNone(results) self.assertEqual(len(RecordedTQDM.record), 1) play_pbar = RecordedTQDM.record[0] - self.assertEqual(play_pbar.desc, 'Playing matches: ') - self.assertEqual(play_pbar.n, 15) - self.assertEqual(play_pbar.total, 15) - self.assertTrue(play_pbar.disable) + self.assert_play_pbar_correct_total_and_finished(play_pbar, total=15) results = axelrod.ResultSetFromFile(self.filename) self.assertIsInstance(results, axelrod.ResultSet) @@ -388,7 +387,6 @@ def test_progress_bar_play_parallel(self): self.assertEqual(RecordedTQDM.record, []) self.assertIsInstance(results, axelrod.ResultSet) - # progress_bar = True RecordedTQDM.reset_record() results = tournament.play(progress_bar=True, processes=2) @@ -396,10 +394,7 @@ def test_progress_bar_play_parallel(self): self.assertEqual(len(RecordedTQDM.record), 3) play_pbar = RecordedTQDM.record[0] - self.assertEqual(play_pbar.desc, 'Playing matches: ') - self.assertEqual(play_pbar.n, 15) - self.assertEqual(play_pbar.total, 15) - self.assertTrue(play_pbar.disable) + self.assert_play_pbar_correct_total_and_finished(play_pbar, total=15) # progress_bar is default RecordedTQDM.reset_record() @@ -408,10 +403,7 @@ def test_progress_bar_play_parallel(self): self.assertEqual(len(RecordedTQDM.record), 3) play_pbar = RecordedTQDM.record[0] - self.assertEqual(play_pbar.desc, 'Playing matches: ') - self.assertEqual(play_pbar.n, 15) - self.assertEqual(play_pbar.total, 15) - self.assertTrue(play_pbar.disable) + self.assert_play_pbar_correct_total_and_finished(play_pbar, total=15) @given(tournament=tournaments(min_size=2, max_size=5, min_turns=2, max_turns=10, min_repetitions=2, diff --git a/axelrod/tournament.py b/axelrod/tournament.py index 9383dd6ce..d67e05e55 100644 --- a/axelrod/tournament.py +++ b/axelrod/tournament.py @@ -210,8 +210,7 @@ def _get_progress_bar(self): if self.use_progress_bar: return tqdm.tqdm(total=self.match_generator.size, desc="Playing matches") - else: - return None + return None def _write_interactions(self, results, writer=None): """Write the interactions to file or to a dictionary"""