Repository: Axelrod-Python/Axelrod

Issue:
Test classification of strategy class as well as strategy player
@mojones noticed a bug in the classification of Win Stay Lose Shift: see #506.

I fixed it in #511, but really the test I added to #511 should be a test in the player class. I tried that but didn't get a failing test. Needs investigating :)

Discussion:

Patch:
diff --git a/axelrod/strategies/cycler.py b/axelrod/strategies/cycler.py
index 599e97a5..e3dd9c39 100644
--- a/axelrod/strategies/cycler.py
+++ b/axelrod/strategies/cycler.py
@@ -1,5 +1,6 @@
 from axelrod import Actions, Player, init_args
 
+import copy
 
 class AntiCycler(Player):
     """
@@ -74,18 +75,27 @@ class Cycler(Player):
 
 
 class CyclerCCD(Cycler):
+    classifier = copy.copy(Cycler.classifier)
+    classifier['memory_depth'] = 2
+
     @init_args
     def __init__(self, cycle="CCD"):
         Cycler.__init__(self, cycle=cycle)
 
 
 class CyclerCCCD(Cycler):
+    classifier = copy.copy(Cycler.classifier)
+    classifier['memory_depth'] = 3
+
     @init_args
     def __init__(self, cycle="CCCD"):
         Cycler.__init__(self, cycle=cycle)
 
 
 class CyclerCCCCCD(Cycler):
+    classifier = copy.copy(Cycler.classifier)
+    classifier['memory_depth'] = 5
+
     @init_args
     def __init__(self, cycle="CCCCCD"):
         Cycler.__init__(self, cycle=cycle)
diff --git a/axelrod/strategies/gobymajority.py b/axelrod/strategies/gobymajority.py
index fba5f73d..efc0d525 100644
--- a/axelrod/strategies/gobymajority.py
+++ b/axelrod/strategies/gobymajority.py
@@ -1,5 +1,7 @@
 from axelrod import Actions, Player, init_args
 
+import copy
+
 C, D = Actions.C, Actions.D
 
 
@@ -77,6 +79,8 @@ class GoByMajority40(GoByMajority):
     """
     GoByMajority player with a memory of 40.
     """
+    classifier = copy.copy(GoByMajority.classifier)
+    classifier['memory_depth'] = 40
 
     @init_args
     def __init__(self, memory_depth=40, soft=True):
@@ -88,6 +92,8 @@ class GoByMajority20(GoByMajority):
     """
     GoByMajority player with a memory of 20.
     """
+    classifier = copy.copy(GoByMajority.classifier)
+    classifier['memory_depth'] = 20
 
     @init_args
     def __init__(self, memory_depth=20, soft=True):
@@ -99,6 +105,8 @@ class GoByMajority10(GoByMajority):
     """
     GoByMajority player with a memory of 10.
     """
+    classifier = copy.copy(GoByMajority.classifier)
+    classifier['memory_depth'] = 10
 
     @init_args
     def __init__(self, memory_depth=10, soft=True):
@@ -110,6 +118,8 @@ class GoByMajority5(GoByMajority):
     """
     GoByMajority player with a memory of 5.
     """
+    classifier = copy.copy(GoByMajority.classifier)
+    classifier['memory_depth'] = 5
 
     @init_args
     def __init__(self, memory_depth=5, soft=True):
@@ -136,6 +146,8 @@ class HardGoByMajority40(HardGoByMajority):
     """
     HardGoByMajority player with a memory of 40.
     """
+    classifier = copy.copy(GoByMajority.classifier)
+    classifier['memory_depth'] = 40
 
     @init_args
     def __init__(self, memory_depth=40, soft=False):
@@ -147,6 +159,8 @@ class HardGoByMajority20(HardGoByMajority):
     """
     HardGoByMajority player with a memory of 20.
     """
+    classifier = copy.copy(GoByMajority.classifier)
+    classifier['memory_depth'] = 20
 
     @init_args
     def __init__(self, memory_depth=20, soft=False):
@@ -158,6 +172,8 @@ class HardGoByMajority10(HardGoByMajority):
     """
     HardGoByMajority player with a memory of 10.
     """
+    classifier = copy.copy(GoByMajority.classifier)
+    classifier['memory_depth'] = 10
 
     @init_args
     def __init__(self, memory_depth=10, soft=False):
@@ -169,6 +185,8 @@ class HardGoByMajority5(HardGoByMajority):
     """
     HardGoByMajority player with a memory of 5.
     """
+    classifier = copy.copy(GoByMajority.classifier)
+    classifier['memory_depth'] = 5
 
     @init_args
     def __init__(self, memory_depth=5, soft=False):
diff --git a/axelrod/strategies/meta.py b/axelrod/strategies/meta.py
index 2f16d4b8..c2d5b60f 100644
--- a/axelrod/strategies/meta.py
+++ b/axelrod/strategies/meta.py
@@ -289,6 +289,14 @@ class MetaMixer(MetaPlayer):
     """
 
     name = "Meta Mixer"
+    classifier = {
+        'memory_depth': float('inf'),  # Long memory
+        'stochastic': True,
+        'makes_use_of': set(),
+        'inspects_source': False,
+        'manipulates_source': False,
+        'manipulates_state': False
+    }
 
     def __init__(self, team=None, distribution=None):
 


Test patch:
diff --git a/axelrod/tests/unit/test_gambler.py b/axelrod/tests/unit/test_gambler.py
index 1448103f..c59bb8d3 100755
--- a/axelrod/tests/unit/test_gambler.py
+++ b/axelrod/tests/unit/test_gambler.py
@@ -8,6 +8,8 @@ import random
 from .test_player import TestPlayer, TestHeadsUp
 from axelrod import random_choice, Actions
 
+import copy
+
 C, D = axelrod.Actions.C, axelrod.Actions.D
 
 
@@ -25,6 +27,9 @@ class TestGambler(TestPlayer):
         'manipulates_state': False
     }
 
+    expected_class_classifier = copy.copy(expected_classifier)
+    expected_class_classifier['memory_depth'] = float('inf')
+
     def test_init(self):
         # Test empty table
         player = self.player(dict())
diff --git a/axelrod/tests/unit/test_gobymajority.py b/axelrod/tests/unit/test_gobymajority.py
index 52883322..40d3b9e2 100644
--- a/axelrod/tests/unit/test_gobymajority.py
+++ b/axelrod/tests/unit/test_gobymajority.py
@@ -126,6 +126,15 @@ def factory_TestGoByRecentMajority(L, soft=True):
             name = "Hard Go By Majority: %i" % L
             player = getattr(axelrod, 'HardGoByMajority%i' % L)
 
+            expected_classifier = {
+                'stochastic': False,
+                'memory_depth': L,
+                'makes_use_of': set(),
+                'inspects_source': False,
+                'manipulates_source': False,
+                'manipulates_state': False
+            }
+
             def test_initial_strategy(self):
                 """Starts by defecting."""
                 self.first_play_test(D)
diff --git a/axelrod/tests/unit/test_lookerup.py b/axelrod/tests/unit/test_lookerup.py
index 49de2ce9..ce447ae1 100755
--- a/axelrod/tests/unit/test_lookerup.py
+++ b/axelrod/tests/unit/test_lookerup.py
@@ -4,6 +4,8 @@ import axelrod
 from .test_player import TestPlayer, TestHeadsUp
 from axelrod.strategies.lookerup import create_lookup_table_keys
 
+import copy
+
 C, D = axelrod.Actions.C, axelrod.Actions.D
 
 
@@ -13,7 +15,7 @@ class TestLookerUp(TestPlayer):
     player = axelrod.LookerUp
 
     expected_classifier = {
-        'memory_depth': 1, # Default TFT table
+        'memory_depth': 1,  # Default TfT
         'stochastic': False,
         'makes_use_of': set(),
         'inspects_source': False,
@@ -21,6 +23,9 @@ class TestLookerUp(TestPlayer):
         'manipulates_state': False
     }
 
+    expected_class_classifier = copy.copy(expected_classifier)
+    expected_class_classifier['memory_depth'] = float('inf')
+
     def test_init(self):
         # Test empty table
         player = self.player(dict())
@@ -113,6 +118,7 @@ class TestLookerUp(TestPlayer):
         self.responses_test([C, C, D], [D, D, C], [D])
 
 
+
 class TestEvolvedLookerUp(TestPlayer):
 
     name = "EvolvedLookerUp"
diff --git a/axelrod/tests/unit/test_meta.py b/axelrod/tests/unit/test_meta.py
index c8355d79..25810483 100644
--- a/axelrod/tests/unit/test_meta.py
+++ b/axelrod/tests/unit/test_meta.py
@@ -3,7 +3,7 @@
 import random
 
 import axelrod
-import unittest
+import copy
 
 from .test_player import TestPlayer
 
@@ -26,7 +26,7 @@ class TestMetaPlayer(TestPlayer):
         'manipulates_state': False
     }
 
-    def classifier_test(self):
+    def classifier_test(self, expected_class_classifier=None):
         player = self.player()
         classifier = dict()
         for key in ['stochastic',
@@ -47,6 +47,12 @@ class TestMetaPlayer(TestPlayer):
                              msg="%s - Behaviour: %s != Expected Behaviour: %s" %
                              (key, player.classifier[key], classifier[key]))
 
+        # Test that player has same classifier as it's class unless otherwise
+        # specified
+        if expected_class_classifier is None:
+            expected_class_classifier = player.classifier
+        self.assertEqual(expected_class_classifier, self.player.classifier)
+
     def test_reset(self):
         p1 = self.player()
         p2 = axelrod.Cooperator()
@@ -70,6 +76,10 @@ class TestMetaMajority(TestMetaPlayer):
         'manipulates_state': False
     }
 
+    expected_class_classifier = copy.copy(expected_classifier)
+    expected_class_classifier['stochastic'] = False
+    expected_class_classifier['makes_use_of'] = set([])
+
     def test_strategy(self):
 
         P1 = axelrod.MetaMajority()
@@ -96,6 +106,10 @@ class TestMetaMinority(TestMetaPlayer):
         'manipulates_state': False
     }
 
+    expected_class_classifier = copy.copy(expected_classifier)
+    expected_class_classifier['stochastic'] = False
+    expected_class_classifier['makes_use_of'] = set([])
+
     def test_team(self):
         team = [axelrod.Cooperator]
         player = self.player(team=team)
@@ -127,6 +141,10 @@ class TestMetaWinner(TestMetaPlayer):
         'manipulates_state': False
     }
 
+    expected_class_classifier = copy.copy(expected_classifier)
+    expected_class_classifier['stochastic'] = False
+    expected_class_classifier['makes_use_of'] = set([])
+
     def test_strategy(self):
 
         P1 = axelrod.MetaWinner(team = [axelrod.Cooperator, axelrod.Defector])
@@ -206,6 +224,10 @@ class TestMetaMajorityMemoryOne(TestMetaPlayer):
         'manipulates_state': False
     }
 
+    expected_class_classifier = copy.copy(expected_classifier)
+    expected_class_classifier['stochastic'] = False
+    expected_class_classifier['makes_use_of'] = set([])
+
     def test_strategy(self):
         self.first_play_test(C)
 
@@ -222,6 +244,10 @@ class TestMetaWinnerMemoryOne(TestMetaPlayer):
         'manipulates_state': False
     }
 
+    expected_class_classifier = copy.copy(expected_classifier)
+    expected_class_classifier['stochastic'] = False
+    expected_class_classifier['makes_use_of'] = set([])
+
     def test_strategy(self):
         self.first_play_test(C)
 
@@ -237,6 +263,11 @@ class TestMetaMajorityFiniteMemory(TestMetaPlayer):
         'manipulates_state': False
     }
 
+    expected_class_classifier = copy.copy(expected_classifier)
+    expected_class_classifier['stochastic'] = False
+    expected_class_classifier['makes_use_of'] = set([])
+
+
     def test_strategy(self):
         self.first_play_test(C)
 
@@ -252,6 +283,11 @@ class TestMetaWinnerFiniteMemory(TestMetaPlayer):
         'manipulates_state': False
     }
 
+    expected_class_classifier = copy.copy(expected_classifier)
+    expected_class_classifier['stochastic'] = False
+    expected_class_classifier['makes_use_of'] = set([])
+
+
     def test_strategy(self):
         self.first_play_test(C)
 
@@ -267,6 +303,11 @@ class TestMetaMajorityLongMemory(TestMetaPlayer):
         'manipulates_state': False
     }
 
+    expected_class_classifier = copy.copy(expected_classifier)
+    expected_class_classifier['stochastic'] = False
+    expected_class_classifier['makes_use_of'] = set([])
+
+
     def test_strategy(self):
         self.first_play_test(C)
 
@@ -282,6 +323,10 @@ class TestMetaWinnerLongMemory(TestMetaPlayer):
         'manipulates_state': False
     }
 
+    expected_class_classifier = copy.copy(expected_classifier)
+    expected_class_classifier['stochastic'] = False
+    expected_class_classifier['makes_use_of'] = set([])
+
     def test_strategy(self):
         self.first_play_test(C)
 
@@ -298,6 +343,9 @@ class TestMetaMixer(TestMetaPlayer):
         'manipulates_state': False
     }
 
+    expected_class_classifier = copy.copy(expected_classifier)
+    expected_class_classifier['makes_use_of'] = set()
+
     def test_strategy(self):
 
         team = [axelrod.TitForTat, axelrod.Cooperator, axelrod.Grudger]
diff --git a/axelrod/tests/unit/test_player.py b/axelrod/tests/unit/test_player.py
index 11a89e9a..601fd396 100644
--- a/axelrod/tests/unit/test_player.py
+++ b/axelrod/tests/unit/test_player.py
@@ -116,6 +116,7 @@ class TestOpponent(Player):
 class TestPlayer(unittest.TestCase):
     "A Test class from which other player test classes are inherited"
     player = TestOpponent
+    expected_class_classifier = None
 
     def test_initialisation(self):
         """Test that the player initiates correctly."""
@@ -126,7 +127,7 @@ class TestPlayer(unittest.TestCase):
                     {'length': -1, 'game': DefaultGame, 'noise': 0})
             self.assertEqual(player.cooperations, 0)
             self.assertEqual(player.defections, 0)
-            self.classifier_test()
+            self.classifier_test(self.expected_class_classifier)
 
     def test_repr(self):
         """Test that the representation is correct."""
@@ -237,12 +238,19 @@ class TestPlayer(unittest.TestCase):
             random_seed=random_seed, attrs=attrs)
 
 
-    def classifier_test(self):
+    def classifier_test(self, expected_class_classifier=None):
         """Test that the keys in the expected_classifier dictionary give the
         expected values in the player classifier dictionary. Also checks that
         two particular keys (memory_depth and stochastic) are in the
         dictionary."""
         player = self.player()
+
+        # Test that player has same classifier as it's class unless otherwise
+        # specified
+        if expected_class_classifier is None:
+            expected_class_classifier = player.classifier
+        self.assertEqual(expected_class_classifier, self.player.classifier)
+
         self.assertTrue('memory_depth' in player.classifier,
                         msg="memory_depth not in classifier")
         self.assertTrue('stochastic' in player.classifier,