From 4a447ba7c397c119bd6ac76031c9c0afc2aabd87 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Sun, 22 May 2022 15:40:07 -0400 Subject: [PATCH] Updated the unittests for the species module and added a validate function. --- spatialpy/core/species.py | 63 ++++++++++++----- test/unit_tests/test_species.py | 119 ++++++++++++++++++++++---------- 2 files changed, 126 insertions(+), 56 deletions(-) diff --git a/spatialpy/core/species.py b/spatialpy/core/species.py index 99e85353..e7533750 100644 --- a/spatialpy/core/species.py +++ b/spatialpy/core/species.py @@ -31,19 +31,6 @@ class Species(): :type restrict_to: int, str, list of ints or list of strs """ def __init__(self, name=None, diffusion_coefficient=None, restrict_to=None): - if name is None: - raise SpeciesError("Species must have a name") - if not isinstance(name, str): - raise SpeciesError("Species name must be a string") - - if diffusion_coefficient is None: - raise SpeciesError("Species must have a diffusion_coefficient.") - if not (isinstance(diffusion_coefficient, (Parameter, str, float, int)) or \ - type(diffusion_coefficient).__name__ == 'Parameter'): - raise SpeciesError("Diffusion coefficient must be a spatialpy.Parameter, str, float, or int.") - if isinstance(diffusion_coefficient, (float, int)) and diffusion_coefficient < 0: - raise SpeciesError("Diffusion coefficient must be non-negative.") - if not (restrict_to is None or isinstance(restrict_to, (str, int, list))): raise SpeciesError("Restrict_to must be an int, str or list of ints or strs.") if restrict_to is not None and isinstance(restrict_to, (int, str)): @@ -58,6 +45,8 @@ def __init__(self, name=None, diffusion_coefficient=None, restrict_to=None): for type_id in restrict_to: self.restrict_to.append(f"type_{type_id}") + self.validate() + def __str__(self): print_string = f"{self.name}: {str(self.diffusion_coefficient)}" return print_string @@ -71,10 +60,48 @@ def set_diffusion_coefficient(self, diffusion_coefficient): :raises SpeciesError: If diffusion_coefficient is negative or not a valid type. """ - if not (isinstance(diffusion_coefficient, (Parameter, str, float, int)) or \ - type(diffusion_coefficient).__name__ == 'Parameter'): - raise SpeciesError("Diffusion coefficient must be a spatialpy.Parameter, str, float, or int.") - if diffusion_coefficient < 0: - raise SpeciesError("Diffusion coefficient must be non-negative.") + self.validate(diffusion_coefficient=diffusion_coefficient, coverage="diffusion_coefficient") self.diffusion_coefficient = diffusion_coefficient + + def validate(self, diffusion_coefficient=None, coverage="all"): + """ + Validate the species. + + :param coverage: The scope of attributes to validate. Set to an attribute name to restrict validation \ + to a specific attribute. + :type coverage: str + + :raises SpeciesError: Attribute is of invalid type. Required attribute set to None. \ + Attribute is value outside of accepted bounds. + """ + # Check name + if coverage in ("all", "name"): + if self.name is None: + raise SpeciesError("name can't be None type.") + if not isinstance(self.name, str): + raise SpeciesError(f"name must be of type str not {type(self.name)}.") + if self.name == "": + raise SpeciesError("name can't be an empty str.") + + # Check diffusion coefficient + if coverage in ("all", "diffusion_coefficient"): + if coverage == "all" and diffusion_coefficient is None: + diffusion_coefficient = self.diffusion_coefficient + + if diffusion_coefficient is None: + raise SpeciesError("diffusion_coefficient can't be None type.") + if not (isinstance(diffusion_coefficient, (Parameter, str, float, int)) or \ + type(diffusion_coefficient).__name__ == 'Parameter'): + errmsg = "diffusion_coefficient must be of type SpatialPy.Parameter, " + errmsg += f"str, int, float not {type(diffusion_coefficient)}" + raise SpeciesError(errmsg) + if isinstance(diffusion_coefficient, (int, float)) and diffusion_coefficient < 0: + raise SpeciesError("diffusion_coefficient must be a positive value.") + + # Check restrict_to + if coverage in ("all", "restrict_to"): + if not (self.restrict_to is None or isinstance(self.restrict_to, list)): + raise SpeciesError("restrict_to must be None or of type int, str, or list") + if self.restrict_to is not None and len(self.restrict_to) == 0: + raise SpeciesError("restrict_to can't be an empty list.") diff --git a/test/unit_tests/test_species.py b/test/unit_tests/test_species.py index d1569a4b..af62456e 100644 --- a/test/unit_tests/test_species.py +++ b/test/unit_tests/test_species.py @@ -25,55 +25,59 @@ class TestSpecies(unittest.TestCase): Unit tests for spatialpy.Species. ################################################################################################ ''' + def setUp(self): + self.species = Species(name="test_species", diffusion_coefficient=0) + def test_constructor(self): """ Test the Species constructor. """ species = Species(name="test_species", diffusion_coefficient=0) self.assertEqual(species.name, "test_species") self.assertEqual(species.diffusion_coefficient, 0) - def test_constructor__no_name(self): """ Test the Species constructor without name. """ with self.assertRaises(SpeciesError): species = Species(diffusion_coefficient=0) - - def test_constructor__name_not_str(self): - """ Test the Species constructor with non-str name. """ - with self.assertRaises(SpeciesError): - species = Species(name=0, diffusion_coefficient=0) - + def test_constructor__invalid_name(self): + """ Test the Species constructor with an invalid name. """ + test_names = ["", None, 0, 0.5, [0]] + for test_name in test_names: + with self.subTest(name=test_name): + with self.assertRaises(SpeciesError): + species = Species(name=test_name, diffusion_coefficient=0) def test_constructor__no_diffusion_coefficient(self): """ Test the Species constructor without diffusion_coefficient. """ with self.assertRaises(SpeciesError): species = Species(name="test_species") - def test_constructor__negative_diffusion_coefficient(self): """ Test the Species constructor with negative diffusion_coefficient. """ with self.assertRaises(SpeciesError): species = Species(name="test_species", diffusion_coefficient=-1) - def test_constructor__parameter_diffusion_coefficient(self): """ Test the Species constructor with a parameter for diffusion_coefficient. """ test_parameter = Parameter(name="test_parameter", expression=0.5) species = Species(name="test_species", diffusion_coefficient=test_parameter) self.assertIsInstance(species.diffusion_coefficient, Parameter) - - def test_constructor__diffusion_coefficient_not_str_int_or_float(self): - """ Test the Species constructor with non-str, non-int, or non-float diffusion_coefficient. """ - with self.assertRaises(SpeciesError): - species = Species(name="test_species", diffusion_coefficient=[0]) - - - def test_constructor__restrict_to_not_accepted_type(self): - """ Test the Species constructor with a restrict_to that is not the proper type. """ - with self.assertRaises(SpeciesError): - species = Species(name="test_species", diffusion_coefficient="0", restrict_to=1.5) - + def test_constructor__invalid_diffusion_coefficient(self): + """ Test the Species constructor with an invalid diffusion_coefficient. """ + test_dcs = [None, [0]] + for test_dc in test_dcs: + with self.subTest(initial_value=test_dc): + with self.assertRaises(SpeciesError): + species = Species(name="test_species", diffusion_coefficient=test_dc) + + def test_constructor__invalid_restrict_to(self): + """ Test the Species constructor with an invalid restrict_to. """ + test_rts = [0.5, [], (5, 2), {'x': 5}] + for test_rt in test_rts: + with self.subTest(restrict_to=test_rt): + with self.assertRaises(SpeciesError): + species = Species(name="test_species", diffusion_coefficient="0", restrict_to=test_rt) def test_constructor__int_restrict_to(self): """ Test the Species constructor with a int restrict_to. """ @@ -81,36 +85,75 @@ def test_constructor__int_restrict_to(self): self.assertIsInstance(species.restrict_to, list) self.assertEqual(species.restrict_to, ["type_1"]) - def test_constructor__str_restrict_to(self): """ Test the Species constructor with a string restrict_to. """ species = Species(name="test_species", diffusion_coefficient=0, restrict_to="Walls") self.assertIsInstance(species.restrict_to, list) self.assertEqual(species.restrict_to, ["type_Walls"]) - def test___str___(self): """ Test Species.__str__ method. """ - species = Species(name="test_species", diffusion_coefficient=0) - self.assertIsInstance(str(species), str) - + self.assertIsInstance(str(self.species), str) def test_set_diffusion_coefficient(self): """ Test Species.set_diffusion_coefficient method. """ - species = Species(name="test_species", diffusion_coefficient=0) - species.set_diffusion_coefficient(diffusion_coefficient=1) - self.assertEqual(species.diffusion_coefficient, 1) + self.species.set_diffusion_coefficient(diffusion_coefficient=1) + self.assertEqual(self.species.diffusion_coefficient, 1) + def test_set_diffusion_coefficient__invalid_diffusion_coefficient(self): + """ Test Species.set_diffusion_coefficient method with an invalid diffusion_coefficient. """ + test_dcs = [None, [1]] + for test_dc in test_dcs: + with self.subTest(diffusion_coefficient=test_dc): + with self.assertRaises(SpeciesError): + self.species.set_diffusion_coefficient(diffusion_coefficient=test_dc) def test_set_diffusion_coefficient__negative_diffusion_coefficient(self): """ Test Species.set_diffusion_coefficient method with negative diffusion_coefficient. """ - species = Species(name="test_species", diffusion_coefficient=0) - with self.assertRaises(SpeciesError): - species.set_diffusion_coefficient(diffusion_coefficient=-1) - - - def test_set_diffusion_coefficient__diffusion_coefficient_not_str_int_or_float(self): - """ Test Species.set_diffusion_coefficient method with non-str, non-int, or non-float diffusion_coefficient. """ - species = Species(name="test_species", diffusion_coefficient=0) with self.assertRaises(SpeciesError): - species.set_diffusion_coefficient(diffusion_coefficient=[0]) + self.species.set_diffusion_coefficient(diffusion_coefficient=-1) + + def test_validate__invalid_name(self): + """ Test Species.validate with an invalid name. """ + test_names = ["", None, 0, 0.5, [0]] + for test_name in test_names: + with self.subTest(name=test_name): + with self.assertRaises(SpeciesError): + self.species.name = test_name + self.species.validate() + + def test_validate__invalid_diffusion_coefficient(self): + """ Test Species.validate with an invalid diffusion_coefficient. """ + test_dcs = [None, [0]] + for test_dc in test_dcs: + with self.subTest(initial_value=test_dc): + with self.assertRaises(SpeciesError): + self.species.diffusion_coefficient = test_dc + self.species.validate() + + def test_validate__invalid_diffusion_coefficient_value(self): + """ Test Species.validate with an invalid diffusion_coefficient value. """ + test_dcs = [-0.5, -1, -3, -5] + for test_dc in test_dcs: + with self.subTest(initial_value=test_dc): + with self.assertRaises(SpeciesError): + self.species.diffusion_coefficient = test_dc + self.species.validate() + + def test_validate__invalid_restrict_to(self): + """ Test Species.validate with an invalid restrict_to. """ + test_rts = ["5", 1, 0.5, [], (1, 2), {'x': 1}] + for test_rt in test_rts: + with self.subTest(restrict_to=test_rt): + with self.assertRaises(SpeciesError): + self.species.restrict_to = test_rt + self.species.validate() + + def test_comp_time_of_validate(self): + """ Check the computation time of validate. """ + import time + from datetime import datetime + start = time.time() + self.species.validate() + tic = datetime.utcfromtimestamp(time.time() - start) + print(f"Total time to run validate: {tic.strftime('%M mins %S secs %f msecs')}")