Repository: projectmesa/mesa
Pull request #916 (2020-08-31 13:23:30)

increased number of test to fix codecov patch
-added tests for fixed parameters only (run same parameters over multiple iterations)
-added  tests for 1 processor in multiprocessor (this is for debugging purposes to find errors.) 





Patch:
diff --git a/.travis.yml b/.travis.yml
index feadfa27..74e0fb7e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,7 +5,6 @@ os:
 cache:
   pip: true
 python:
-  - "3.5"
   - "3.6"
   - "3.7"
   - "3.8"
diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py
index e48c3e87..6a480117 100644
--- a/mesa/batchrunner.py
+++ b/mesa/batchrunner.py
@@ -105,10 +105,10 @@ class FixedBatchRunner:
         self.agent_reporters = agent_reporters
 
         if self.model_reporters:
-            self.model_vars = OrderedDict()
+            self.model_vars = {}
 
         if self.agent_reporters:
-            self.agent_vars = OrderedDict()
+            self.agent_vars = {}
 
         # Make Compatible with Python 3.5
         self.datacollector_model_reporters = OrderedDict()
@@ -395,7 +395,6 @@ class ParameterSampler:
         raise StopIteration()
 
 
-# TODO: No difference- deleting will remove whitespace issue
 class BatchRunner(FixedBatchRunner):
     """ This class is instantiated with a model class, and model parameters
     associated with one or more values. It is also instantiated with model and
@@ -451,16 +450,28 @@ class BatchRunner(FixedBatchRunner):
             display_progress: Display progress bar with time estimation?
 
         """
-        super().__init__(
-            model_cls,
-            ParameterProduct(variable_parameters),
-            fixed_parameters,
-            iterations,
-            max_steps,
-            model_reporters,
-            agent_reporters,
-            display_progress,
-        )
+        if variable_parameters is None:
+            super().__init__(
+                model_cls,
+                variable_parameters,
+                fixed_parameters,
+                iterations,
+                max_steps,
+                model_reporters,
+                agent_reporters,
+                display_progress,
+            )
+        else:
+            super().__init__(
+                model_cls,
+                ParameterProduct(variable_parameters),
+                fixed_parameters,
+                iterations,
+                max_steps,
+                model_reporters,
+                agent_reporters,
+                display_progress,
+            )
 
 
 class BatchRunnerMP(BatchRunner):


Test patch:
diff --git a/tests/test_batchrunner.py b/tests/test_batchrunner.py
index 40368e17..24de7015 100644
--- a/tests/test_batchrunner.py
+++ b/tests/test_batchrunner.py
@@ -37,8 +37,8 @@ class MockModel(Model):
 
     def __init__(
         self,
-        variable_model_param,
-        variable_agent_param,
+        variable_model_param=None,
+        variable_agent_param=None,
         fixed_model_param=None,
         schedule=None,
         **kwargs
@@ -56,8 +56,12 @@ class MockModel(Model):
         self.init_agents()
 
     def init_agents(self):
+        if self.variable_agent_param is None:
+            agent_val = 1
+        else:
+            agent_val = self.variable_agent_param
         for i in range(self.n_agents):
-            self.schedule.add(MockAgent(i, self, self.variable_agent_param))
+            self.schedule.add(MockAgent(i, self, agent_val))
 
     def get_local_model_param(self):
         return 42
@@ -113,6 +117,20 @@ class TestBatchRunner(unittest.TestCase):
         batch.run_all()
         return batch
 
+    def launch_batch_processing_fixed(self):
+        # Adding second batchrun to test fixed params increase coverage
+        batch2 = BatchRunner(
+            self.mock_model,
+            fixed_parameters={"fixed": "happy"},
+            iterations=4,
+            max_steps=self.max_steps,
+            model_reporters=self.model_reporters,
+            agent_reporters=None,
+        )
+
+        batch2.run_all()
+        return batch2
+
     @property
     def model_runs(self):
         """
@@ -141,11 +159,19 @@ class TestBatchRunner(unittest.TestCase):
         # extra columns with run index and agentId
         expected_cols = (len(self.variable_params) + len(self.agent_reporters) + 2)
         self.assertEqual(agent_vars.shape, (self.model_runs * NUM_AGENTS, expected_cols))
+        assert "agent_val" in list(agent_vars.columns)
+        assert "val_non_existent" not in list(agent_vars.columns)
+        assert "agent_id" in list(agent_collector[(0, 1, 1)].columns)
+        assert "Step" in list(agent_collector[(0, 1, 5)].index.names)
+        assert "nose" not in list(agent_collector[(0, 1, 1)].columns)
 
         self.assertEqual(
             agent_collector[(0, 1, 0)].shape, (NUM_AGENTS * self.max_steps, 2)
         )
 
+        with self.assertRaises(KeyError):
+            agent_collector[(900, "k" , 3)]
+
     def test_model_with_fixed_parameters_as_kwargs(self):
         """
         Test that model with fixed parameters passed like kwargs is
@@ -155,11 +181,23 @@ class TestBatchRunner(unittest.TestCase):
         batch = self.launch_batch_processing()
         model_vars = batch.get_model_vars_dataframe()
         agent_vars = batch.get_agent_vars_dataframe()
-
         self.assertEqual(len(model_vars), len(agent_vars))
         self.assertEqual(len(model_vars), self.model_runs)
         self.assertEqual(model_vars["reported_fixed_value"].unique(), ["Fixed"])
 
+    def test_model_with_only_fixed_parameters(self):
+        """
+        Test that model with only fixed parameters and multiple iterations is
+        properly handled
+        """
+        batch = self.launch_batch_processing_fixed()
+        model_vars = batch.get_model_vars_dataframe()
+        self.assertEqual(len(model_vars), 4)
+        self.assertEqual(model_vars["fixed"].unique(), ["happy"])
+
+        with self.assertRaises(AttributeError):
+            batch.get_agent_vars_dataframe()
+
     def test_model_with_variable_and_fixed_kwargs(self):
         self.mock_model = MockMixedModel
         self.model_reporters = {
diff --git a/tests/test_batchrunnerMP.py b/tests/test_batchrunnerMP.py
index 3608dfc4..1a45e171 100644
--- a/tests/test_batchrunnerMP.py
+++ b/tests/test_batchrunnerMP.py
@@ -9,7 +9,7 @@ from mesa import Agent, Model
 from mesa.time import BaseScheduler
 from mesa.datacollection import DataCollector
 from mesa.batchrunner import BatchRunnerMP, ParameterProduct, ParameterSampler
-from multiprocessing import freeze_support
+from multiprocessing import freeze_support, cpu_count
 
 NUM_AGENTS = 7
 
@@ -115,6 +115,25 @@ class TestBatchRunnerMP(unittest.TestCase):
         batch.run_all()
         return batch
 
+    def launch_batch_processing_debug(self):
+        '''
+        Tests with one processor for debugging purposes
+        '''
+
+        batch = BatchRunnerMP(
+            self.mock_model,
+            nr_processes=1,
+            variable_parameters=self.variable_params,
+            fixed_parameters=self.fixed_params,
+            iterations=self.iterations,
+            max_steps=self.max_steps,
+            model_reporters=self.model_reporters,
+            agent_reporters=self.agent_reporters,
+        )
+
+        batch.run_all()
+        return batch
+
     @property
     def model_runs(self):
         """
@@ -122,26 +141,35 @@ class TestBatchRunnerMP(unittest.TestCase):
         """
         return reduce(mul, map(len, self.variable_params.values())) * self.iterations
 
-    def test_model_level_vars(self):
-        """
-        Test that model-level variable collection is of the correct size
-        """
-        batch = self.launch_batch_processing()
-        model_vars = batch.get_model_vars_dataframe()
-        model_collector = batch.get_collector_model()
+    def batch_model_vars(self, results):
+        model_vars = results.get_model_vars_dataframe()
+        model_collector = results.get_collector_model()
         expected_cols = (len(self.variable_params) + len(self.model_reporters) + 1)  # extra column with run index
         self.assertEqual(model_vars.shape, (self.model_runs, expected_cols))
         self.assertEqual(len(model_collector.keys()), self.model_runs)
 
-    def test_agent_level_vars(self):
+    def test_model_level_vars(self):
         """
-        Test that agent-level variable collection is of the correct size
+        Test that model-level variable collection is of the correct size
         """
         batch = self.launch_batch_processing()
-        agent_vars = batch.get_agent_vars_dataframe()
-        agent_collector = batch.get_collector_agents()
+        assert batch.processes == cpu_count()
+        assert batch.processes != 1
+        self.batch_model_vars(batch)
+
+        batch2 = self.launch_batch_processing_debug()
+        self.batch_model_vars(batch2)
+
+    def batch_agent_vars(self, result):
+        agent_vars = result.get_agent_vars_dataframe()
+        agent_collector = result.get_collector_agents()
         # extra columns with run index and agentId
         expected_cols = (len(self.variable_params) + len(self.agent_reporters) + 2)
+        assert "agent_val" in list(agent_vars.columns)
+        assert "val_non_existent" not in list(agent_vars.columns)
+        assert "agent_id" in list(agent_collector[(0, 1, 1)].columns)
+        assert "Step" in list(agent_collector[(0, 1, 5)].index.names)
+        assert "nose" not in list(agent_collector[(0, 1, 1)].columns)
 
         self.assertEqual(
             agent_vars.shape, (self.model_runs * NUM_AGENTS, expected_cols)
@@ -151,6 +179,16 @@ class TestBatchRunnerMP(unittest.TestCase):
             agent_collector[(0, 1, 0)].shape, (NUM_AGENTS * self.max_steps, 2)
         )
 
+    def test_agent_level_vars(self):
+        """
+        Test that agent-level variable collection is of the correct size
+        """
+        batch = self.launch_batch_processing()
+        self.batch_agent_vars(batch)
+
+        batch2 = self.launch_batch_processing_debug()
+        self.batch_agent_vars(batch2)
+
     def test_model_with_fixed_parameters_as_kwargs(self):
         """
         Test that model with fixed parameters passed like kwargs is
diff --git a/tests/test_datacollector.py b/tests/test_datacollector.py
index 3dc02d0a..03c02af8 100644
--- a/tests/test_datacollector.py
+++ b/tests/test_datacollector.py
@@ -52,14 +52,19 @@ class MockModel(Model):
                 "total_agents": lambda m: m.schedule.get_agent_count(),
                 "model_value": "model_val",
                 "model_calc": self.schedule.get_agent_count(),
-                "model_calc_comp": [self.model_calc_comp, [3, 4]],
+                "model_calc_comp": [self.test_model_calc_comp, [3, 4]],
+                "model_calc_fail": [self.test_model_calc_comp, [12, 0]]
             },
             {"value": lambda a: a.val, "value2": "val2"},
             {"Final_Values": ["agent_id", "final_value"]},
         )
 
-    def model_calc_comp(self, input1, input2):
-        return (self.model_val * input1) / input2
+    def test_model_calc_comp(self, input1, input2):
+        if input2 > 0:
+            return (self.model_val * input1) / input2
+        else:
+            assert ValueError
+            return None
 
     def step(self):
         self.schedule.step()
@@ -87,6 +92,7 @@ class TestDataCollector(unittest.TestCase):
         assert "model_value" in data_collector.model_vars
         assert "model_calc" in data_collector.model_vars
         assert "model_calc_comp" in data_collector.model_vars
+        assert "model_calc_fail" in data_collector.model_vars
         assert len(data_collector.model_vars["total_agents"]) == 7
         assert len(data_collector.model_vars["model_value"]) == 7
         assert len(data_collector.model_vars["model_calc"]) == 7
@@ -99,18 +105,29 @@ class TestDataCollector(unittest.TestCase):
             assert element == 10
         for element in data_collector.model_vars['model_calc_comp']:
             assert element == 75
+        for element in data_collector.model_vars["model_calc_fail"]:
+            assert element is None
 
     def test_agent_records(self):
         """
         Test agent-level variable collection.
         """
         data_collector = self.model.datacollector
+        agent_table = data_collector.get_agent_vars_dataframe()
+
         assert len(data_collector._agent_records) == 7
         for step, records in data_collector._agent_records.items():
             assert len(records) == 10
             for values in records:
                 assert len(values) == 4
 
+        assert 'value' in list(agent_table.columns)
+        assert "value2" in list(agent_table.columns)
+        assert "value3" not in list(agent_table.columns)
+
+        with self.assertRaises(KeyError):
+            data_collector._agent_records[8]
+
     def test_table_rows(self):
         """
         Test table collection
@@ -136,7 +153,7 @@ class TestDataCollector(unittest.TestCase):
         model_vars = data_collector.get_model_vars_dataframe()
         agent_vars = data_collector.get_agent_vars_dataframe()
         table_df = data_collector.get_table_dataframe("Final_Values")
-        assert model_vars.shape == (7, 4)
+        assert model_vars.shape == (7, 5)
         assert agent_vars.shape == (70, 2)
         assert table_df.shape == (10, 2)