aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_parse_util.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_parse_util.py')
-rw-r--r--tests/test_parse_util.py415
1 files changed, 415 insertions, 0 deletions
diff --git a/tests/test_parse_util.py b/tests/test_parse_util.py
new file mode 100644
index 0000000..5642277
--- /dev/null
+++ b/tests/test_parse_util.py
@@ -0,0 +1,415 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Test suite to test the :mod:`parse_type.parse_util` module.
+"""
+
+from __future__ import absolute_import, print_function
+from .parse_type_test import TestCase, unittest
+from parse_type.parse_util \
+ import Field, FieldParser, FormatSpec, make_format_spec
+
+
+# -----------------------------------------------------------------------------
+# TEST CASE:
+# -----------------------------------------------------------------------------
+class TestField(TestCase):
+ EMPTY_FORMAT_FIELDS = [
+ Field(), #< Empty field.
+ Field("name"), #< Named field without format.
+ Field("name", ""), #< Named field with format=empty-string.
+ Field(format=""), #< Field with format=empty-string.
+ ]
+ NONEMPTY_FORMAT_FIELDS = [
+ Field(format="Number"), #< Typed field without name".
+ Field("name", "Number"), #< Named and typed field".
+ ]
+ INVALID_FORMAT_FIELDS = [
+ Field(format="<"), #< Align without type.
+ Field(format="_<"), #< Fill and align without type.
+ Field(format="_<10"), #< Fill, align and width without type.
+ Field(format="_<098"), #< Fill, align, zero and width without type.
+ ]
+ FIELDS = EMPTY_FORMAT_FIELDS + NONEMPTY_FORMAT_FIELDS + INVALID_FORMAT_FIELDS
+
+ def test_is_typed__returns_true_for_nonempty_format(self):
+ fields = self.NONEMPTY_FORMAT_FIELDS + self.INVALID_FORMAT_FIELDS
+ for field in fields:
+ self.assertTrue(field.has_format, "Field: %s" % field)
+
+ def test_is_typed__returns_false_for_empty_format(self):
+ fields = self.EMPTY_FORMAT_FIELDS
+ for field in fields:
+ self.assertFalse(field.has_format, "Field: %s" % field)
+
+ def test_format_spec__returns_none_if_format_is_empty(self):
+ for field in self.EMPTY_FORMAT_FIELDS:
+ self.assertIsNone(field.format_spec, "Field: %s" % field)
+
+ def test_format_spec__if_format_is_nonempty_and_valid(self):
+ for field in self.NONEMPTY_FORMAT_FIELDS:
+ self.assertIsNotNone(field.format_spec)
+ self.assertIsInstance(field.format_spec, FormatSpec)
+
+ def test_format_spec__raises_error_if_nonempty_format_is_invalid(self):
+ for field in self.INVALID_FORMAT_FIELDS:
+ with self.assertRaises(ValueError):
+ field.format_spec
+
+ def test_format_spec__is_lazy_evaluated(self):
+ fields = [Field(), Field("name"),
+ Field("name", "type"), Field(format="type")]
+ for field in fields:
+ self.assertIsNone(field._format_spec)
+ if field.format:
+ _ = field.format_spec.type
+ self.assertIsNotNone(field.format_spec)
+ else:
+ self.assertIsNone(field.format_spec)
+
+ def test_set_format_invalidates_format_spec(self):
+ field = Field(format="Number")
+ self.assertEqual(field.format, "Number")
+ self.assertEqual(field.format_spec.type, "Number")
+ self.assertEqual(field.format_spec.align, None)
+
+ field.set_format("<ManyNumbers")
+ self.assertEqual(field.format, "<ManyNumbers")
+ self.assertEqual(field.format_spec.type, "ManyNumbers")
+ self.assertEqual(field.format_spec.align, '<')
+
+ def test_to_string_conversion(self):
+ test_data = [
+ (Field(), "{}"),
+ (Field("name"), "{name}"),
+ (Field(format="type"), "{:type}"),
+ (Field("name", "type"), "{name:type}"),
+ ]
+ for field, expected_text in test_data:
+ text = str(field)
+ self.assertEqual(text, expected_text)
+
+ def test_equal__with_field(self):
+ for field in self.FIELDS:
+ other = field
+ self.assertEqual(field, other)
+
+ def test_equal__with_string(self):
+ for field in self.FIELDS:
+ other = str(field)
+ self.assertEqual(field, other)
+
+ def test_equal__with_unsupported(self):
+ UNSUPPORTED_TYPES = [None, make_format_spec(), True, False, 10]
+ field = Field()
+ for other in UNSUPPORTED_TYPES:
+ with self.assertRaises(ValueError):
+ field == other
+
+ def test_not_equal__with_field(self):
+ for field in self.FIELDS:
+ other2 = Field(field.name, "XXX")
+ self.assertNotEqual(field.format, other2.format)
+ self.assertNotEqual(field, other2)
+
+ other3 = Field("xxx", field.format)
+ self.assertNotEqual(field.name, other3.name)
+ self.assertNotEqual(field, other3)
+
+ def test_not_equal__with_string(self):
+ for field in self.FIELDS:
+ other2 = Field(field.name, "XXX")
+ other2_text = str(other2)
+ self.assertNotEqual(field.format, other2.format)
+ self.assertNotEqual(field, other2_text)
+
+ other3 = Field("xxx", field.format)
+ other3_text = str(other3)
+ self.assertNotEqual(field.name, other3.name)
+ self.assertNotEqual(field, other3_text)
+
+ def test_not_equal__with_unsupported(self):
+ UNSUPPORTED_TYPES = [None, make_format_spec(), True, False, 10]
+ field = Field()
+ for other in UNSUPPORTED_TYPES:
+ with self.assertRaises(ValueError):
+ field != other
+
+
+# -----------------------------------------------------------------------------
+# TEST CASE:
+# -----------------------------------------------------------------------------
+class TestFieldFormatSpec(TestCase):
+ """
+ Test Field.extract_format_spec().
+
+ FORMAT-SPEC SCHEMA:
+ [[fill]align][0][width][.precision][type]
+ """
+
+ def assertValidFormatWidth(self, width):
+ self.assertIsInstance(width, str)
+ for char in width:
+ self.assertTrue(char.isdigit())
+
+ def assertValidFormatAlign(self, align):
+ self.assertIsInstance(align, str)
+ self.assertEqual(len(align), 1)
+ self.assertIn(align, Field.ALIGN_CHARS)
+
+ def assertValidFormatPrecision(self, precision):
+ self.assertIsInstance(precision, str)
+ for char in precision:
+ self.assertTrue(char.isdigit())
+
+ def test_extract_format_spec__with_empty_string_raises_error(self):
+ with self.assertRaises(ValueError) as cm:
+ Field.extract_format_spec("")
+ self.assertIn("INVALID-FORMAT", str(cm.exception))
+
+ def test_extract_format_spec__with_type(self):
+ format_types = ["d", "w", "Number", "Number?", "Number*", "Number+"]
+ for format_type in format_types:
+ format_spec = Field.extract_format_spec(format_type)
+ expected_spec = make_format_spec(format_type)
+ self.assertEqual(format_spec.type, format_type)
+ self.assertEqual(format_spec.width, "")
+ self.assertEqual(format_spec.zero, False)
+ self.assertIsNone(format_spec.align)
+ self.assertIsNone(format_spec.fill)
+ self.assertEqual(format_spec, expected_spec)
+
+ def test_extract_format_spec_with_width_only_raises_error(self):
+ # -- INVALID-FORMAT: Width without type.
+ with self.assertRaises(ValueError) as cm:
+ Field.extract_format_spec("123")
+ self.assertEqual("INVALID-FORMAT: 123 (without type)", str(cm.exception))
+
+ def test_extract_format_spec__with_zero_only_raises_error(self):
+ # -- INVALID-FORMAT: Width without type.
+ with self.assertRaises(ValueError) as cm:
+ Field.extract_format_spec("0")
+ self.assertEqual("INVALID-FORMAT: 0 (without type)", str(cm.exception))
+
+ def test_extract_format_spec__with_align_only_raises_error(self):
+ # -- INVALID-FORMAT: Width without type.
+ for align in Field.ALIGN_CHARS:
+ with self.assertRaises(ValueError) as cm:
+ Field.extract_format_spec(align)
+ self.assertEqual("INVALID-FORMAT: %s (without type)" % align,
+ str(cm.exception))
+
+ def test_extract_format_spec_with_fill_and_align_only_raises_error(self):
+ # -- INVALID-FORMAT: Width without type.
+ fill = "_"
+ for align in Field.ALIGN_CHARS:
+ with self.assertRaises(ValueError) as cm:
+ format = fill + align
+ Field.extract_format_spec(format)
+ self.assertEqual("INVALID-FORMAT: %s (without type)" % format,
+ str(cm.exception))
+
+ def test_extract_format_spec__with_width_and_type(self):
+ formats = ["1s", "2d", "6s", "10d", "60f", "123456789s"]
+ for format in formats:
+ format_spec = Field.extract_format_spec(format)
+ expected_type = format[-1]
+ expected_width = format[:-1]
+ expected_spec = make_format_spec(type=expected_type,
+ width=expected_width)
+ self.assertEqual(format_spec, expected_spec)
+ self.assertValidFormatWidth(format_spec.width)
+
+ def test_extract_format_spec__with_precision_and_type(self):
+ formats = [".2d", ".6s", ".6f"]
+ for format in formats:
+ format_spec = Field.extract_format_spec(format)
+ expected_type = format[-1]
+ expected_precision = format[1:-1]
+ expected_spec = make_format_spec(type=expected_type,
+ precision=expected_precision)
+ self.assertEqual(format_spec, expected_spec)
+ self.assertValidFormatPrecision(format_spec.precision)
+
+ def test_extract_format_spec__with_zero_and_type(self):
+ formats = ["0s", "0d", "0Number", "0Number+"]
+ for format in formats:
+ format_spec = Field.extract_format_spec(format)
+ expected_type = format[1:]
+ expected_spec = make_format_spec(type=expected_type, zero=True)
+ self.assertEqual(format_spec, expected_spec)
+
+ def test_extract_format_spec__with_align_and_type(self):
+ # -- ALIGN_CHARS = "<>=^"
+ formats = ["<s", ">d", "=Number", "^Number+"]
+ for format in formats:
+ format_spec = Field.extract_format_spec(format)
+ expected_align = format[0]
+ expected_type = format[1:]
+ expected_spec = make_format_spec(type=expected_type,
+ align=expected_align)
+ self.assertEqual(format_spec, expected_spec)
+ self.assertValidFormatAlign(format_spec.align)
+
+ def test_extract_format_spec__with_fill_align_and_type(self):
+ # -- ALIGN_CHARS = "<>=^"
+ formats = ["X<s", "_>d", "0=Number", " ^Number+"]
+ for format in formats:
+ format_spec = Field.extract_format_spec(format)
+ expected_fill = format[0]
+ expected_align = format[1]
+ expected_type = format[2:]
+ expected_spec = make_format_spec(type=expected_type,
+ align=expected_align, fill=expected_fill)
+ self.assertEqual(format_spec, expected_spec)
+ self.assertValidFormatAlign(format_spec.align)
+
+ # -- ALIGN_CHARS = "<>=^"
+ FORMAT_AND_FORMAT_SPEC_DATA = [
+ ("^010Number+", make_format_spec(type="Number+", width="10",
+ zero=True, align="^", fill=None)),
+ ("X<010Number+", make_format_spec(type="Number+", width="10",
+ zero=True, align="<", fill="X")),
+ ("_>0098Number?", make_format_spec(type="Number?", width="098",
+ zero=True, align=">", fill="_")),
+ ("*=129Number*", make_format_spec(type="Number*", width="129",
+ zero=False, align="=", fill="*")),
+ ("X129Number?", make_format_spec(type="X129Number?", width="",
+ zero=False, align=None, fill=None)),
+ (".3Number", make_format_spec(type="Number", width="",
+ zero=False, align=None, fill=None,
+ precision="3")),
+ ("6.2Number", make_format_spec(type="Number", width="6",
+ zero=False, align=None, fill=None,
+ precision="2")),
+ ]
+
+ def test_extract_format_spec__with_all(self):
+ for format, expected_spec in self.FORMAT_AND_FORMAT_SPEC_DATA:
+ format_spec = Field.extract_format_spec(format)
+ self.assertEqual(format_spec, expected_spec)
+ self.assertValidFormatWidth(format_spec.width)
+ if format_spec.align is not None:
+ self.assertValidFormatAlign(format_spec.align)
+
+ def test_make_format(self):
+ for expected_format, format_spec in self.FORMAT_AND_FORMAT_SPEC_DATA:
+ format = Field.make_format(format_spec)
+ self.assertEqual(format, expected_format)
+ format_spec2 = Field.extract_format_spec(format)
+ self.assertEqual(format_spec2, format_spec)
+
+
+# -----------------------------------------------------------------------------
+# TEST CASE:
+# -----------------------------------------------------------------------------
+class TestFieldParser(TestCase):
+ INVALID_FIELDS = ["", "{", "}", "xxx", "name:type", ":type"]
+ VALID_FIELD_DATA = [
+ ("{}", Field()),
+ ("{name}", Field("name")),
+ ("{:type}", Field(format="type")),
+ ("{name:type}", Field("name", "type"))
+ ]
+
+ #def assertFieldEqual(self, actual, expected):
+ # message = "FAILED: %s == %s" % (actual, expected)
+ # self.assertIsInstance(actual, Field)
+ # self.assertIsInstance(expected, Field)
+ # self.assertEqual(actual, expected, message)
+ # # self.assertEqual(actual.name, expected.name, message)
+ # # self.assertEqual(actual.format, expected.format, message)
+
+ def test_parse__raises_error_with_missing_or_partial_braces(self):
+ for field_text in self.INVALID_FIELDS:
+ with self.assertRaises(ValueError):
+ FieldParser.parse(field_text)
+
+ def test_parse__with_valid_fields(self):
+ for field_text, expected_field in self.VALID_FIELD_DATA:
+ field = FieldParser.parse(field_text)
+ self.assertEqual(field, expected_field)
+
+ def test_extract_fields__without_field(self):
+ prefix = "XXX ___"
+ suffix = "XXX {{escaped_field}} {{escaped_field:xxx_type}} XXX"
+ field_texts = [prefix, suffix, prefix + suffix, suffix + prefix]
+
+ for field_text in field_texts:
+ fields = list(FieldParser.extract_fields(field_text))
+ self.assertEqual(len(fields), 0)
+
+ def test_extract_fields__with_one_field(self):
+ prefix = "XXX ___"
+ suffix = "XXX {{escaped_field}} {{escaped_field:xxx_type}} XXX"
+
+ for field_text, expected_field in self.VALID_FIELD_DATA:
+ fields = list(FieldParser.extract_fields(field_text))
+ self.assertEqual(len(fields), 1)
+ self.assertSequenceEqual(fields, [expected_field])
+
+ field_text2 = prefix + field_text + suffix
+ fields2 = list(FieldParser.extract_fields(field_text2))
+ self.assertEqual(len(fields2), 1)
+ self.assertSequenceEqual(fields, fields2)
+
+ def test_extract_fields__with_many_fields(self):
+ MANY_FIELDS_DATA = [
+ ("{}xxx{name2}", [Field(), Field("name2")]),
+ ("{name1}yyy{:type2}", [Field("name1"), Field(format="type2")]),
+ ("{:type1}xxx{name2}{name3:type3}",
+ [Field(format="type1"), Field("name2"), Field("name3", "type3")]),
+ ]
+ prefix = "XXX ___"
+ suffix = "XXX {{escaped_field}} {{escaped_field:xxx_type}} XXX"
+
+ for field_text, expected_fields in MANY_FIELDS_DATA:
+ fields = list(FieldParser.extract_fields(field_text))
+ self.assertEqual(len(fields), len(expected_fields))
+ self.assertSequenceEqual(fields, expected_fields)
+
+ field_text2 = prefix + field_text + suffix
+ fields2 = list(FieldParser.extract_fields(field_text2))
+ self.assertEqual(len(fields2), len(expected_fields))
+ self.assertSequenceEqual(fields2, expected_fields)
+
+
+ def test_extract_types(self):
+ MANY_TYPES_DATA = [
+ ("{}xxx{name2}", []),
+ ("{name1}yyy{:type2}", ["type2"]),
+ ("{:type1}xxx{name2}{name3:type3}", ["type1", "type3"]),
+ ]
+
+ for field_text, expected_types in MANY_TYPES_DATA:
+ type_names = list(FieldParser.extract_types(field_text))
+ self.assertEqual(len(type_names), len(expected_types))
+ self.assertSequenceEqual(type_names, expected_types)
+
+
+# -----------------------------------------------------------------------------
+# 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.