-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_simulation.py
More file actions
292 lines (243 loc) · 11.7 KB
/
test_simulation.py
File metadata and controls
292 lines (243 loc) · 11.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
"""
Tests for the Simulation class.
"""
import glob
import pytest
import os
import os.path
from biosim.island_manager import Island
from biosim.landscape_structure import HighLand, LowLand, Desert, Water
from biosim.simulation import BioSim
from biosim.animal_kingdom import Herbivore, Carnivore
__author__ = 'Mahrin Tasfe'
__email__ = 'mahrin.tasfe@nmbu.no'
SEED = 12345678 # random seed for tests
class TestBioSimSimulation:
"""
Tests for Simulation class.
"""
@pytest.fixture(autouse=True)
def create_sim_with_no_pop_fixture(self):
""" Creates an empty island with no population before entering a test."""
self.island_map_string = """\
WWWWWWWWWWW
WLHLLDDDDDW
WLHLLDDDDDW
WLHLLDDDDDW
WLHLLDDDDDW
WWWWWWWWWWW"""
self.initial_pop = []
self.seed = SEED
self.sim_obj = BioSim(self.island_map_string, self.initial_pop, seed=self.seed,
vis_years=0)
yield
@pytest.fixture
def reset_params_fixture(self):
"""
Fixture resetting class parameters on both Herbivore & Carnivore. The fixture resets
Herbivore & Carnivore class-level parameters before a test.
"""
Herbivore.reset_params()
Carnivore.reset_params()
yield
def test_sim_create(self):
"""
Test that simulation can be created by just running setup.
"""
assert self.sim_obj
@pytest.mark.parametrize('animal_new_params', [('Herbivore', {'zeta': -3.2, 'xi': 1.8}),
('Herbivore', {'zeta': 3.2, 'xi': -1.8}),
('Herbivore', {'DeltaPhiMax': 100}),
('Carnivore', {'DeltaPhiMax': 0}),
('Dino', {'DeltaPhiMax': 10})])
def test_set_animal_parameters_illegal(self, animal_new_params):
"""
Test if ValueError is raised when illegal parameters for a species type is given.
"""
species, new_params = animal_new_params
with pytest.raises(ValueError):
self.sim_obj.set_animal_parameters(species, new_params)
@pytest.mark.parametrize('animal_class_type, animal_new_params',
[(Herbivore, ('Herbivore', {'zeta': 3.2, 'xi': 18})),
(Herbivore, ('Herbivore', {'zeta': 32, 'xi': 1.8})),
(Carnivore, ('Carnivore', {'DeltaPhiMax': 10}))])
def test_set_animal_parameters(self, animal_class_type, animal_new_params):
"""
Test if the set_params() is able to set the new given parameters properly.
"""
species, new_params = animal_new_params
self.sim_obj.set_animal_parameters(species, new_params)
for parameter_name in new_params:
assert animal_class_type.params[parameter_name] == new_params[parameter_name]
@pytest.mark.parametrize('landscape_new_params', [('R', {'f_max': 700}), ('S', {'f_max': 700}),
('T', {'f_max': 700})])
def test_set_params_illegal(self, landscape_new_params):
"""
Test if ValueError is raised when illegal parameters for a land type is given.
"""
landscape, new_params = landscape_new_params
with pytest.raises(ValueError):
self.sim_obj.set_landscape_parameters(landscape, new_params)
@pytest.mark.parametrize('landscape_class, landscape_new_params',
[(LowLand, ('L', {'f_max': 1000})), (HighLand, ('H', {'f_max': 100})),
(Desert, ('D', {'f_max': 1})), (Water, ('W', {'f_max': 50}))])
def test_set_landscape_parameters(self, landscape_class, landscape_new_params):
"""
Test if the set_params() is able to set the new given parameters properly.
"""
landscape, new_params = landscape_new_params
self.sim_obj.set_landscape_parameters(landscape, new_params)
for parameter_name in new_params:
assert landscape_class.params[parameter_name] == new_params[parameter_name]
@pytest.mark.parametrize('population, result',
(([{'loc': (4, 4),
'pop': [{'species': 'Carnivore', 'age': 5, 'weight': 20}
for _ in range(20)]},
{'loc': (3, 3),
'pop': [{'species': 'Herbivore', 'age': 5, 'weight': 20}
for _ in range(5)]}],
{'Herbivore': 5, 'Carnivore': 20}),
([{'loc': (5, 5),
'pop': [{'species': 'Carnivore', 'age': 5, 'weight': 20}
for _ in range(4)]},
{'loc': (2, 2),
'pop': [{'species': 'Herbivore', 'age': 5, 'weight': 20}
for _ in range(3)]}],
{'Herbivore': 3, 'Carnivore': 4})))
def test_add_population(self, population, result):
"""
Test if the add_population() works properly.
"""
self.sim_obj.add_population(population)
obtained_result = self.sim_obj.num_animals_per_species
assert obtained_result == result
def test_simulate_calls(self, mocker):
"""
Test that Biosim class's simulate() method calls Island class's annual_cycle_on_island()
method the correct number of times.
"""
# mocker.spy wraps Island.annual_cycle_on_island() so that we can get a call count.
mocker.spy(Island, 'annual_cycle_on_island')
simulated_years = 20
self.sim_obj.simulate(num_years=simulated_years)
# noinspection PyUnresolvedReferences
assert Island.annual_cycle_on_island.call_count == simulated_years
def test_last_simulated_year_after_simulate(self):
"""
Test that after calling the simulate() method for a fixed number of years,
the 'year' named property which returns the last simulated year works properly.
"""
last_simulated_year = 0
simulated_year = 2
for current_year in range(0, 10, simulated_year):
self.sim_obj.simulate(num_years=simulated_year)
last_simulated_year = last_simulated_year + simulated_year
assert self.sim_obj.year == last_simulated_year
@pytest.mark.parametrize('population, result',
(([{'loc': (4, 4),
'pop': [{'species': 'Carnivore', 'age': 5, 'weight': 20}
for _ in range(20)]},
{'loc': (3, 3),
'pop': [{'species': 'Herbivore', 'age': 5, 'weight': 20}
for _ in range(5)]}], 25),
([{'loc': (5, 5),
'pop': [{'species': 'Carnivore', 'age': 5, 'weight': 20}
for _ in range(4)]},
{'loc': (2, 2),
'pop': [{'species': 'Herbivore', 'age': 5, 'weight': 20}
for _ in range(3)]}], 7)))
def test_num_animals_class_property(self, population, result):
"""
Test that the 'num_animals' named property which returns the total number of animals
on the island works properly.
"""
self.sim_obj.add_population(population)
assert self.sim_obj.num_animals == result
@pytest.fixture
def reset_island_fixture():
"""
The fixture resets the dictionary holding the island map before and after leaving a test.
"""
Island.island_map = {}
yield
Island.island_map = {}
@pytest.mark.parametrize('ini_pop, result',
(([{'loc': (2, 2),
'pop': []}],
{'Herbivore': 0, 'Carnivore': 0}),
([{'loc': (2, 2),
'pop': [{'species': 'Carnivore', 'age': 5, 'weight': 20}
for _ in range(20)]},
{'loc': (3, 3),
'pop': [{'species': 'Herbivore', 'age': 5, 'weight': 20}
for _ in range(5)]}],
{'Herbivore': 5, 'Carnivore': 20}),
([{'loc': (2, 2),
'pop': [{'species': 'Carnivore', 'age': 5, 'weight': 20}
for _ in range(4)]},
{'loc': (2, 2),
'pop': [{'species': 'Herbivore', 'age': 5, 'weight': 20}
for _ in range(3)]}],
{'Herbivore': 3, 'Carnivore': 4})))
def test_initial_island_population(ini_pop, result, reset_island_fixture):
"""
Test that simulation is set up with correct number of initial population.
"""
island_map_string = """\
WWWWWWWWWWW
WLHLLDDDDDW
WLHLLDDDDDW
WWWWWWWWWWW"""
sim_obj = BioSim(island_map_string, ini_pop=ini_pop, seed=SEED, vis_years=0)
assert sim_obj.num_animals_per_species == result
@pytest.fixture
def fig_file_base():
"""Provides name for image base and delete image after test completion."""
f_base = "figfileroot"
yield f_base
for f in glob.glob(f"{f_base}_0*.png"):
os.remove(f)
def test_simulate_calls_vis_years(mocker, fig_file_base):
"""
Test that BioSim class's simulate() method calls Island class's annual_cycle_on_island()
method the correct number of times. This time with visualization on. Set up vis_years = 20.
After every 20 years, images will be saved.
"""
island_map_string = """\
WWWWWWWWWWW
WLHLLDDDDDW
WLHLLDDDDDW
WLHLLDDDDDW
WLHLLDDDDDW
WWWWWWWWWWW"""
initial_pop = []
sim_obj = BioSim(island_map_string, initial_pop, seed=SEED, vis_years=1,
img_base=fig_file_base, img_fmt='png', img_dir='.')
# mocker.spy wraps Island.annual_cycle_on_island() so that we can get a call count.
mocker.spy(Island, 'annual_cycle_on_island')
simulated_years = 2
sim_obj.simulate(num_years=simulated_years)
# noinspection PyUnresolvedReferences
assert Island.annual_cycle_on_island.call_count == simulated_years
def test_log_file_saved_with_directory_name_given():
"""Test that the log files are saved during simulation.
This test only checks when the user has given a directory name for saving the log file.
It does not test in case of absolute paths.
It tests whether the created log file has been saved in inside the exact directory that
user has given before the simulation started."""
log_file_directory = 'log_test_results'
for seed_val in range(4):
sim = BioSim(island_map="WWWW\nWLHW\nWWWW",
ini_pop=[],
seed=seed_val,
log_file=log_file_directory, vis_years=0)
sim.simulate(2)
# This is the formet of log_file name I have used for writing the csv file using seed
# Because user might be running code with several seed values.
complete_log_name = "seed" + str(seed_val) + "_simulation_population_data.csv"
complete_path_for_finding_log = '..//' + log_file_directory + '//' + complete_log_name
# If log_file not found in the correct location assert
assert os.path.isfile(complete_path_for_finding_log)
# After completion of work removing/deleting the csv file.
# For directory removal, it gives Permission Error.
os.remove(complete_path_for_finding_log)