diff --git a/resources/knapsack.py b/resources/knapsack.py index e1b694d238a19a210c8356d77f1e85d11905edf6..ea50f72da27e9127e0ab177dd7d844c094e025c7 100644 --- a/resources/knapsack.py +++ b/resources/knapsack.py @@ -35,6 +35,31 @@ class FractionalKnapsackAlgorithm(): Sets group of items to choose from. :param item_set: Set of items, essentially in JSON format. """ + # First validate item set. + if self.validate_item_set(item_set): + # Validation passed. Set values. + self._item_set_ = item_set + self._items_populated_ = True + + def validate_item_set(self, item_set): + """ + Runs the internal "validate_item_set" method. + On error, returns False. Otherwise returns True. + :param item_set: Potential "item set" to validate. + :return: True if validation passes. False otherwise. + """ + try: + self._validate_item_set(item_set) + return True + except (TypeError, KeyError, ValueError): + return False + + def _validate_item_set(self, item_set): + """ + Validates given item set to make sure it's the format we expect. + If validation fails, it will raise an error. + :param item_set: Potential "item set" to validate. + """ # Check that value is List type. if not isinstance(item_set, list): raise TypeError('Arg must be array of dict objects.') @@ -45,14 +70,20 @@ class FractionalKnapsackAlgorithm(): if not isinstance(item, dict): raise TypeError('Arg must be array of dict objects.') + # Check that 'benefit' key exists. if 'benefit' not in item.keys(): raise KeyError('Key "benefit" must be present in all items.') + # Check that 'cost' key exists. if 'cost' not in item.keys(): raise KeyError('Key "cost" must be present in all items.') - self._item_set_ = item_set - self._items_populated_ = True + # Check that 'benefit' key is parsable as int. + int(str(item['benefit']).strip()) + + # Check that 'cost' key is parsable as int. + int(str(item['cost']).strip()) + def set_max_weight(self, max_weight): """ diff --git a/tests/resources/knapsack.py b/tests/resources/knapsack.py index 08fc61a9ed6ed9110a36fa00a083cc36fd9f4b9e..656acc8bb4ae880094180f4003083b4c6a883641 100644 --- a/tests/resources/knapsack.py +++ b/tests/resources/knapsack.py @@ -65,30 +65,81 @@ class TestKnapsack(unittest.TestCase): self.assertFalse(self.knapsack._weight_populated_) def test_set_item_set_success(self): - # Test with set 1. + # Test success with set 1. self.knapsack.set_item_set(self.item_set_1) self.assertEqual(self.knapsack._item_set_, self.item_set_1) self.assertTrue(self.knapsack._items_populated_) - # Test with set 2. + # Test success with set 2. self.knapsack.set_item_set(self.item_set_2) self.assertEqual(self.knapsack._item_set_, self.item_set_2) self.assertTrue(self.knapsack._items_populated_) def test_set_item_set_failure(self): + with self.subTest('Arg is not array of dicts'): + # Passed None. + self.knapsack.set_item_set(None) + self.assertEqual(self.knapsack._item_set_, []) + self.assertFalse(self.knapsack._items_populated_) + + # Passed array with no dict items. + self.knapsack.set_item_set([1, 2, 3]) + self.assertEqual(self.knapsack._item_set_, []) + self.assertFalse(self.knapsack._items_populated_) + + with self.subTest('Dict item must have "benefit" and "cost" keys.'): + # Passed dict item missing "cost" key. + self.knapsack.set_item_set([{'benefit': 1}]) + self.assertEqual(self.knapsack._item_set_, []) + self.assertFalse(self.knapsack._items_populated_) + + # Passed dict item missing "benefit" key. + self.knapsack.set_item_set([{'cost': 1}]) + self.assertEqual(self.knapsack._item_set_, []) + self.assertFalse(self.knapsack._items_populated_) + + with self.subTest('Item values must be parsable as integers.'): + # Passed bad value for "benefit" key. + self.knapsack.set_item_set([{'benefit': 'a', 'cost': 1}]) + self.assertEqual(self.knapsack._item_set_, []) + self.assertFalse(self.knapsack._items_populated_) + + # Passed bad value for "cost" key. + self.knapsack.set_item_set([{'benefit': 1, 'cost': 'a'}]) + self.assertEqual(self.knapsack._item_set_, []) + self.assertFalse(self.knapsack._items_populated_) + + def test_validate_item_set(self): + # Test success with set 1. + self.assertTrue(self.knapsack.validate_item_set(self.item_set_1)) + + # Test success with set 2. + self.assertTrue(self.knapsack.validate_item_set(self.item_set_2)) + + # Test failure. + self.assertFalse(self.knapsack.validate_item_set(None)) + + def test__validate_item_set_failure(self): with self.subTest('Arg is not array of dicts'): with self.assertRaises(TypeError): - self.knapsack.set_item_set(None) + self.knapsack._validate_item_set(None) with self.assertRaises(TypeError): - self.knapsack.set_item_set([1, 2, 3]) + self.knapsack._validate_item_set([1, 2, 3]) with self.subTest('Dict item must have "benefit" and "cost" keys.'): with self.assertRaises(KeyError): - self.knapsack.set_item_set([{'benefit': 1}]) + self.knapsack._validate_item_set([{'benefit': 1}]) with self.assertRaises(KeyError): - self.knapsack.set_item_set([{'cost': 1}]) + self.knapsack._validate_item_set([{'cost': 1}]) + + with self.subTest('Item values must be parsable as integers.'): + with self.assertRaises(ValueError): + self.knapsack._validate_item_set([{'benefit': 'a', 'cost': 1}]) + + with self.assertRaises(ValueError): + self.knapsack._validate_item_set([{'benefit': 1, 'cost': 'a'}]) def test_set_max_weight_success(self): self.knapsack.set_max_weight(1)