aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_cardinality_field.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_cardinality_field.py')
-rwxr-xr-xtests/test_cardinality_field.py328
1 files changed, 328 insertions, 0 deletions
diff --git a/tests/test_cardinality_field.py b/tests/test_cardinality_field.py
new file mode 100755
index 0000000..3c59cb0
--- /dev/null
+++ b/tests/test_cardinality_field.py
@@ -0,0 +1,328 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Test experiment for parse.
+Add cardinality format field after type:
+
+ "... {person:Person?} ..." -- CARDINALITY: Zero or one, 0..1 (optional)
+ "... {persons:Person*} ..." -- CARDINALITY: Zero or more, 0..N (many0)
+ "... {persons:Person+} ..." -- CARDINALITY: One or more, 1..N (many)
+
+
+REQUIRES:
+ parse >= 1.5.3.1 ('pattern' attribute support and further extensions)
+
+STATUS:
+ IDEA, working prototype with patched parse module, but not accepted.
+"""
+
+from __future__ import absolute_import
+from .parse_type_test \
+ import TestCase, parse_number, unittest
+from .test_cardinality import CardinalityTypeBuilderTest
+from parse_type import Cardinality
+from parse_type.cardinality_field \
+ import CardinalityField, CardinalityFieldTypeBuilder, MissingTypeError
+
+
+# -------------------------------------------------------------------------
+# TEST CASE: TestParseTypeWithCardinalityField
+# -------------------------------------------------------------------------
+class TestCardinalityField(TestCase):
+ VALID_TYPE_NAMES = ["Number?", "Number*", "Number+"]
+ INVALID_TYPE_NAMES = ["?Invalid", "Inval*d", "In+valid"]
+
+ def test_pattern_chars(self):
+ for pattern_char in CardinalityField.pattern_chars:
+ self.assertIn(pattern_char, CardinalityField.from_char_map)
+
+ def test_to_from_char_map_symmetry(self):
+ for cardinality, char in CardinalityField.to_char_map.items():
+ self.assertEqual(cardinality, CardinalityField.from_char_map[char])
+ for char, cardinality in CardinalityField.from_char_map.items():
+ self.assertEqual(char, CardinalityField.to_char_map[cardinality])
+
+ def test_matches_type_name(self):
+ for type_name in self.VALID_TYPE_NAMES:
+ self.assertTrue(CardinalityField.matches_type(type_name))
+
+ for type_name in self.INVALID_TYPE_NAMES:
+ self.assertFalse(CardinalityField.matches_type(type_name))
+
+ def test_split_type__with_valid_special_names(self):
+ actual = CardinalityField.split_type("Color?")
+ self.assertEqual(actual, ("Color", Cardinality.optional))
+ self.assertEqual(actual, ("Color", Cardinality.zero_or_one))
+
+ actual = CardinalityField.split_type("Color+")
+ self.assertEqual(actual, ("Color", Cardinality.many))
+ self.assertEqual(actual, ("Color", Cardinality.one_or_more))
+
+ actual = CardinalityField.split_type("Color*")
+ self.assertEqual(actual, ("Color", Cardinality.many0))
+ self.assertEqual(actual, ("Color", Cardinality.zero_or_more))
+
+ def test_split_type__with_valid_special_names2(self):
+ for type_name in self.VALID_TYPE_NAMES:
+ self.assertTrue(CardinalityField.matches_type(type_name))
+ cardinality_char = type_name[-1]
+ expected_basename = type_name[:-1]
+ expected_cardinality = CardinalityField.from_char_map[cardinality_char]
+ expected = (expected_basename, expected_cardinality)
+ actual = CardinalityField.split_type(type_name)
+ self.assertEqual(actual, expected)
+
+ def test_split_type__with_cardinality_one(self):
+ actual = CardinalityField.split_type("Color")
+ self.assertEqual(actual, ("Color", Cardinality.one))
+
+ def test_split_type__with_invalid_names(self):
+ for type_name in self.INVALID_TYPE_NAMES:
+ expected = (type_name, Cardinality.one)
+ actual = CardinalityField.split_type(type_name)
+ self.assertEqual(actual, expected)
+ self.assertFalse(CardinalityField.matches_type(type_name))
+
+ def test_make_type__with_cardinality_one(self):
+ expected = "Number"
+ type_name = CardinalityField.make_type("Number", Cardinality.one)
+ self.assertEqual(type_name, expected)
+ self.assertFalse(CardinalityField.matches_type(type_name))
+
+ def test_make_type__with_cardinality_optional(self):
+ expected = "Number?"
+ type_name = CardinalityField.make_type("Number", Cardinality.optional)
+ self.assertEqual(type_name, expected)
+ self.assertTrue(CardinalityField.matches_type(type_name))
+
+ type_name2 = CardinalityField.make_type("Number", Cardinality.zero_or_one)
+ self.assertEqual(type_name2, expected)
+ self.assertEqual(type_name2, type_name)
+
+ def test_make_type__with_cardinality_many(self):
+ expected = "Number+"
+ type_name = CardinalityField.make_type("Number", Cardinality.many)
+ self.assertEqual(type_name, expected)
+ self.assertTrue(CardinalityField.matches_type(type_name))
+
+ type_name2 = CardinalityField.make_type("Number", Cardinality.one_or_more)
+ self.assertEqual(type_name2, expected)
+ self.assertEqual(type_name2, type_name)
+
+ def test_make_type__with_cardinality_many0(self):
+ expected = "Number*"
+ type_name = CardinalityField.make_type("Number", Cardinality.many0)
+ self.assertEqual(type_name, expected)
+ self.assertTrue(CardinalityField.matches_type(type_name))
+
+ type_name2 = CardinalityField.make_type("Number", Cardinality.zero_or_more)
+ self.assertEqual(type_name2, expected)
+ self.assertEqual(type_name2, type_name)
+
+ def test_split_type2make_type__symmetry_with_valid_names(self):
+ for type_name in self.VALID_TYPE_NAMES:
+ primary_name, cardinality = CardinalityField.split_type(type_name)
+ type_name2 = CardinalityField.make_type(primary_name, cardinality)
+ self.assertEqual(type_name, type_name2)
+
+ def test_split_type2make_type__symmetry_with_cardinality_one(self):
+ for type_name in self.INVALID_TYPE_NAMES:
+ primary_name, cardinality = CardinalityField.split_type(type_name)
+ type_name2 = CardinalityField.make_type(primary_name, cardinality)
+ self.assertEqual(type_name, primary_name)
+ self.assertEqual(type_name, type_name2)
+ self.assertEqual(cardinality, Cardinality.one)
+
+# -------------------------------------------------------------------------
+# TEST CASE:
+# -------------------------------------------------------------------------
+class TestCardinalityFieldTypeBuilder(CardinalityTypeBuilderTest):
+ INVALID_TYPE_DICT_DATA = [
+ (dict(), "empty type_dict"),
+ (dict(NumberX=parse_number), "non-empty type_dict (wrong name)"),
+ ]
+
+ # -- UTILITY METHODS:
+ def generate_type_variants(self,type_name):
+ for pattern_char in CardinalityField.pattern_chars:
+ special_name = "%s%s" % (type_name.strip(), pattern_char)
+ self.assertTrue(CardinalityField.matches_type(special_name))
+ yield special_name
+
+ # -- METHOD: CardinalityFieldTypeBuilder.create_type_variant()
+ def test_create_type_variant__with_many_and_type_converter(self):
+ type_builder = CardinalityFieldTypeBuilder
+ parse_candidate = type_builder.create_type_variant("Number+",
+ type_converter=parse_number)
+ self.check_parse_number_with_many(parse_candidate, "Number+")
+
+ def test_create_type_variant__with_optional_and_type_dict(self):
+ type_builder = CardinalityFieldTypeBuilder
+ parse_candidate = type_builder.create_type_variant("Number?",
+ dict(Number=parse_number))
+ self.check_parse_number_with_optional(parse_candidate, "Number?")
+
+ def test_create_type_variant__with_many_and_type_dict(self):
+ type_builder = CardinalityFieldTypeBuilder
+ parse_candidate = type_builder.create_type_variant("Number+",
+ dict(Number=parse_number))
+ self.check_parse_number_with_many(parse_candidate, "Number+")
+
+ def test_create_type_variant__with_many0_and_type_dict(self):
+ type_builder = CardinalityFieldTypeBuilder
+ parse_candidate = type_builder.create_type_variant("Number*",
+ dict(Number=parse_number))
+ self.check_parse_number_with_many0(parse_candidate, "Number*")
+
+ def test_create_type_variant__can_create_all_variants(self):
+ type_builder = CardinalityFieldTypeBuilder
+ for special_name in self.generate_type_variants("Number"):
+ # -- CASE: type_converter
+ parse_candidate = type_builder.create_type_variant(special_name,
+ parse_number)
+ self.assertTrue(callable(parse_candidate))
+
+ # -- CASE: type_dict
+ parse_candidate = type_builder.create_type_variant(special_name,
+ dict(Number=parse_number))
+ self.assertTrue(callable(parse_candidate))
+
+ def test_create_type_variant__raises_error_with_invalid_type_name(self):
+ type_builder = CardinalityFieldTypeBuilder
+ for invalid_type_name in TestCardinalityField.INVALID_TYPE_NAMES:
+ with self.assertRaises(ValueError):
+ type_builder.create_type_variant(invalid_type_name,
+ parse_number)
+
+ def test_create_type_variant__raises_error_with_missing_primary_type(self):
+ type_builder = CardinalityFieldTypeBuilder
+ for special_name in self.generate_type_variants("Number"):
+ for type_dict, description in self.INVALID_TYPE_DICT_DATA:
+ with self.assertRaises(MissingTypeError):
+ type_builder.create_type_variant(special_name, type_dict)
+
+
+ # -- METHOD: CardinalityFieldTypeBuilder.create_type_variants()
+ def test_create_type_variants__all(self):
+ type_builder = CardinalityFieldTypeBuilder
+ special_names = ["Number?", "Number+", "Number*"]
+ type_dict = dict(Number=parse_number)
+ new_types = type_builder.create_type_variants(special_names, type_dict)
+ self.assertSequenceEqual(set(new_types.keys()), set(special_names))
+ self.assertEqual(len(new_types), 3)
+
+ parse_candidate = new_types["Number?"]
+ self.check_parse_number_with_optional(parse_candidate, "Number?")
+ parse_candidate = new_types["Number+"]
+ self.check_parse_number_with_many(parse_candidate, "Number+")
+ parse_candidate = new_types["Number*"]
+ self.check_parse_number_with_many0(parse_candidate, "Number*")
+
+ def test_create_type_variants__raises_error_with_invalid_type_name(self):
+ type_builder = CardinalityFieldTypeBuilder
+ for invalid_type_name in TestCardinalityField.INVALID_TYPE_NAMES:
+ type_dict = dict(Number=parse_number)
+ with self.assertRaises(ValueError):
+ type_names = [invalid_type_name]
+ type_builder.create_type_variants(type_names, type_dict)
+
+ def test_create_missing_type_variants__raises_error_with_missing_primary_type(self):
+ type_builder = CardinalityFieldTypeBuilder
+ for special_name in self.generate_type_variants("Number"):
+ for type_dict, description in self.INVALID_TYPE_DICT_DATA:
+ self.assertNotIn("Number", type_dict)
+ with self.assertRaises(MissingTypeError):
+ names = [special_name]
+ type_builder.create_type_variants(names, type_dict)
+
+
+ # -- METHOD: CardinalityFieldTypeBuilder.create_missing_type_variants()
+ def test_create_missing_type_variants__all_missing(self):
+ type_builder = CardinalityFieldTypeBuilder
+ missing_names = ["Number?", "Number+", "Number*"]
+ new_types = type_builder.create_missing_type_variants(missing_names,
+ dict(Number=parse_number))
+ self.assertSequenceEqual(set(new_types.keys()), set(missing_names))
+ self.assertEqual(len(new_types), 3)
+
+ def test_create_missing_type_variants__none_missing(self):
+ # -- PREPARE: Create all types and store them in the type_dict.
+ type_builder = CardinalityFieldTypeBuilder
+ type_names = ["Number?", "Number+", "Number*"]
+ all_type_names = ["Number", "Number?", "Number+", "Number*"]
+ type_dict = dict(Number=parse_number)
+ new_types = type_builder.create_missing_type_variants(type_names,
+ type_dict)
+ type_dict.update(new_types)
+ self.assertSequenceEqual(set(new_types.keys()), set(type_names))
+ self.assertSequenceEqual(set(type_dict.keys()), set(all_type_names))
+
+ # -- TEST: All special types are already stored in the type_dict.
+ new_types2 = type_builder.create_missing_type_variants(type_names,
+ type_dict)
+ self.assertEqual(len(new_types2), 0)
+
+ def test_create_missing_type_variants__some_missing(self):
+ # -- PREPARE: Create some types and store them in the type_dict.
+ type_builder = CardinalityFieldTypeBuilder
+ special_names = ["Number?", "Number+", "Number*"]
+ type_names1 = ["Number?", "Number*"]
+ type_names2 = special_names
+ type_dict = dict(Number=parse_number)
+ new_types = type_builder.create_missing_type_variants(type_names1,
+ type_dict)
+ type_dict.update(new_types)
+ self.assertSequenceEqual(set(new_types.keys()), set(type_names1))
+ self.assertSequenceEqual(set(type_dict.keys()),
+ set(["Number", "Number?", "Number*"]))
+
+ # -- TEST: All special types are already stored in the type_dict.
+ new_types2 = type_builder.create_missing_type_variants(type_names2,
+ type_dict)
+ self.assertEqual(len(new_types2), 1)
+ self.assertSequenceEqual(set(new_types2.keys()), set(["Number+"]))
+
+ def test_create_type_variant__raises_error_with_invalid_type_name(self):
+ type_builder = CardinalityFieldTypeBuilder
+ for invalid_type_name in TestCardinalityField.INVALID_TYPE_NAMES:
+ type_dict = dict(Number=parse_number)
+ with self.assertRaises(ValueError):
+ type_names = [invalid_type_name]
+ type_builder.create_missing_type_variants(type_names, type_dict)
+
+ def test_create_missing_type_variants__raises_error_with_missing_primary_type(self):
+ type_builder = CardinalityFieldTypeBuilder
+ for special_name in self.generate_type_variants("Number"):
+ for type_dict, description in self.INVALID_TYPE_DICT_DATA:
+ self.assertNotIn("Number", type_dict)
+ with self.assertRaises(MissingTypeError):
+ names = [special_name]
+ type_builder.create_missing_type_variants(names, type_dict)
+
+
+# -----------------------------------------------------------------------------
+# MAIN:
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+ unittest.main()
+
+
+# Copyright (c) 2012-2013 by Jens Engel (https://github/jenisys/parse_type)
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.