def test__PostcodeParser_parse__lenient(self):

        from wintersdeep_postcode.exceptions import ParseError
        postcode_parser = PostcodeParser(trim_whitespace=True,
                                         force_case=True,
                                         whitespace='lenient',
                                         validate=False)

        self.assertIsNotNone(postcode_parser("aa1 1aa"))
        self.assertIsNotNone(postcode_parser("AA11AA"))
        self.assertIsNotNone(postcode_parser("AA1  1AA"))
        self.assertIsNotNone(postcode_parser("AA1\t1AA"))
        self.assertIsNotNone(postcode_parser("AA1\t 1AA"))
        self.assertIsNotNone(postcode_parser(" AA1 1AA"))
        self.assertIsNotNone(postcode_parser("AA1 1AA "))
        self.assertIsNotNone(postcode_parser(" AA1 1AA "))
        self.assertRaises(ParseError, postcode_parser, 1)
        self.assertRaises(ParseError, postcode_parser, False)

        self.assertIsNotNone(postcode_parser("A1 1AA"))
        self.assertIsNotNone(postcode_parser("A11 1AA"))
        self.assertIsNotNone(postcode_parser("A1A 1AA"))
        self.assertIsNotNone(postcode_parser("AA1 1AA"))
        self.assertIsNotNone(postcode_parser("AA11 1AA"))
        self.assertIsNotNone(postcode_parser("AA1A 1AA"))
    def test__PostcodeParser_build_input_translator__nop(self):

        pipeline = PostcodeParser._build_input_translater(trim=False,
                                                          uppercase=False)

        self.assertEqual(pipeline("NO CHANGE"), "NO CHANGE")
        self.assertEqual(pipeline(" TRIM TEST\t "), " TRIM TEST\t ")
        self.assertEqual(pipeline("Uppercase Test"), "Uppercase Test")
    def test__PostcodeParser_build_input_translator__full(self):

        pipeline = PostcodeParser._build_input_translater(trim=True,
                                                          uppercase=True)

        self.assertEqual(pipeline("NO CHANGE"), "NO CHANGE")
        self.assertEqual(pipeline(" TRIM TEST\t "), "TRIM TEST")
        self.assertEqual(pipeline("Uppercase Test"), "UPPERCASE TEST")
    def test__PostcodeParser_get_parser_regex_list__all_types(self):

        from re import compile
        from wintersdeep_postcode.postcode_types import postcode_type_objects

        parser_list = PostcodeParser._get_parser_regex_list(type_list=None)
        compile_regex_type = compile("^$").__class__

        # make sure it appears we loaded all types (basic count check only)
        self.assertEqual(len(postcode_type_objects), len(parser_list))

        # and that the returned list appears usable
        for regex, factory in parser_list:
            self.assertIsInstance(regex, compile_regex_type)
            self.assertTrue(callable(factory))

        # and that the default list, is still the same as the None call.
        self.assertListEqual(parser_list,
                             PostcodeParser._get_parser_regex_list())
    def test__PostcodeParser_get_parser_regex_list__specific_type(self):

        from wintersdeep_postcode.postcode_types import postcode_type_objects

        test_type = postcode_type_objects[0]
        parser_list = PostcodeParser._get_parser_regex_list(
            type_list=[test_type.PostcodeType])

        # make sure it appears we loaded all types (basic count check only)
        self.assertEqual(len(parser_list), 1)
        self.assertIs(parser_list[0][1], test_type)
    def test__PostcodeParser_ctor__validate_keyword(self):

        from wintersdeep_postcode.postcode_types import StandardPostcode
        from wintersdeep_postcode.exceptions import ParseError, ValidationError

        # one postcode to check that validation errors are thrown, and another for parser errors
        postcode_invalid = "LL9 2XX"
        postcode_valid = "LL20 2XX"
        postcode_malformed = "LL20 XXX"

        postcode_parser = PostcodeParser(validate=True)
        self.assertRaises(ParseError, postcode_parser.parse,
                          postcode_malformed)
        self.assertRaises(ValidationError, postcode_parser.parse,
                          postcode_invalid)

        postcode = postcode_parser.parse(postcode_valid)
        self.assertIsInstance(postcode, StandardPostcode)
        self.assertFalse(postcode.validation_faults)
        self.assertTrue(postcode.is_validated)

        postcode_parser = PostcodeParser(validate=False)
        self.assertRaises(ParseError, postcode_parser.parse,
                          postcode_malformed)

        postcode = postcode_parser.parse(postcode_invalid)
        self.assertIsInstance(postcode, StandardPostcode)
        self.assertFalse(postcode.validation_faults)
        self.assertFalse(postcode.is_validated)
    def test__PostcodeParser_get_whitespace_pattern__lenient(self):

        whitespace_pattern = PostcodeParser._get_whitespace_pattern('lenient')
        test_regex = compile(f"^{whitespace_pattern}$")

        self.assertTrue(test_regex.match(" "))
        self.assertTrue(test_regex.match(""))
        self.assertTrue(test_regex.match("  "))
        self.assertTrue(test_regex.match("\t"))
        self.assertTrue(test_regex.match("\t "))
        self.assertTrue(test_regex.match(" \t "))
        self.assertFalse(test_regex.match("-"))
        self.assertFalse(test_regex.match("TEXT"))
    def test_parse_all_current_uk_postcodes__if_available(self):

        from os.path import exists

        root_relative_file_path = join("reference", "current-uk-postcodes.txt")
        file_path = join(PROJECT_ROOT_DIRECTORY, root_relative_file_path)

        if not exists(file_path):
            error_major = f"Can't run test without {root_relative_file_path}"
            error_minor = "this may not be checked-in/available for licencing or file size reasons."
            self.skipTest(f"{error_major}; {error_minor}")

        from wintersdeep_postcode.exceptions import ParseError

        with open(file_path, 'r') as file_handle:
            parser = PostcodeParser()
            postcode_string = file_handle.readline()
            while postcode_string:
                try:
                    postcode = parser(postcode_string)
                    self.assertTrue(postcode.is_validated)
                except ParseError as ex:
                    print(str(ex))
                postcode_string = file_handle.readline()
    def test__PostcodeParser_ctor__ignore_faults(self):

        from wintersdeep_postcode.postcode_types import StandardPostcode
        from wintersdeep_postcode.exceptions import ParseError, ValidationError

        # one postcode to check that validation errors are thrown, and another for parser errors
        postcode_invalid_but_ignored = "LL9 2XX"
        postcode_invalid_not_ignored = "HX10 2XX"
        postcode_valid = "LL20 2XX"
        postcode_malformed = "LL20 XXX"

        supress_error = StandardPostcode.ExpectedDoubleDigitDistrict

        for test_error in [supress_error, int(supress_error)]:

            postcode_parser = PostcodeParser(validate=True,
                                             ignored_faults=[test_error])

            self.assertRaises(ParseError, postcode_parser.parse,
                              postcode_malformed)

            postcode = postcode_parser.parse(postcode_valid)
            self.assertIsInstance(postcode, StandardPostcode)
            self.assertFalse(postcode.validation_faults)
            self.assertTrue(postcode.is_validated)

            postcode = postcode_parser.parse(postcode_invalid_but_ignored)
            self.assertIsInstance(postcode, StandardPostcode)
            self.assertEqual(len(postcode.validation_faults), 1)
            self.assertTrue(postcode.is_validated)

            try:
                postcode_parser.parse(postcode_invalid_not_ignored)
                self.fail(
                    f"Parsing '{postcode_invalid_not_ignored}' was expected to trigger an exception."
                )
            except ValidationError as ex:
                self.assertTrue(
                    int(StandardPostcode.ExpectedSingleDigitDistrict) in
                    ex.postcode.validation_faults)
                self.assertEqual(len(ex.postcode.validation_faults), 1)
                self.assertFalse(ex.postcode.is_validated)