#!/usr/bin/env python3
# Copyright 2022 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
"""Tests the tokenized string decode module."""

from datetime import datetime
import math
import unittest

import tokenized_string_decoding_test_data as tokenized_string
import varint_test_data
from pw_tokenizer import decode
from pw_tokenizer import encode


def error(msg, value=None) -> str:
    """Formats msg as the message for an argument that failed to parse."""
    if value is None:
        return '<[{}]>'.format(msg)
    return '<[{} ({})]>'.format(msg, value)


class TestDecodeTokenized(unittest.TestCase):
    """Tests decoding tokenized strings with various arguments."""

    def test_decode_generated_data(self) -> None:
        self.assertGreater(len(tokenized_string.TEST_DATA), 100)

        for fmt, decoded, encoded in tokenized_string.TEST_DATA:
            self.assertEqual(decode.decode(fmt, encoded, True), decoded)

    def test_unicode_decode_errors(self) -> None:
        """Tests unicode errors, which do not occur in the C++ decoding code."""
        self.assertEqual(
            decode.decode('Why, %c', b'\x01', True),
            'Why, ' + error('%c ERROR', -1),
        )

        self.assertEqual(
            decode.decode('%sXY%+ldxy%u', b'\x83N\x80!\x01\x02', True),
            '{}XY{}xy{}'.format(
                error('%s ERROR', "'N\\x80!'"),
                error('%+ld SKIPPED', -1),
                error('%u SKIPPED', 1),
            ),
        )

        self.assertEqual(
            decode.decode('%s%lld%9u', b'\x82$\x80\x80', True),
            '{0}{1}{2}'.format(
                error("%s ERROR ('$\\x80')"),
                error('%lld SKIPPED'),
                error('%9u SKIPPED'),
            ),
        )

        self.assertEqual(
            decode.decode('%c', b'\xff\xff\xff\xff\x0f', True),
            error('%c ERROR', -2147483648),
        )

    def test_ignore_errors(self) -> None:
        self.assertEqual(decode.decode('Why, %c', b'\x01'), 'Why, %c')

        self.assertEqual(decode.decode('%s %d', b'\x01!'), '! %d')

    def test_pointer(self) -> None:
        """Tests pointer args, which are not natively supported in Python."""
        self.assertEqual(
            decode.decode('Hello: %p', b'\x00', True), 'Hello: 0x00000000'
        )
        self.assertEqual(
            decode.decode('%p%d%d', b'\x02\x80', True),
            '0x00000001<[%d ERROR]><[%d SKIPPED]>',
        )

    def test_nothing_printed_fails(self) -> None:
        result = decode.FormatString('%n').format(b'')
        self.assertFalse(result.ok())


class TestPercentLiteralDecoding(unittest.TestCase):
    """Tests decoding the %-literal in various invalid situations."""

    def test_percent(self) -> None:
        result = decode.FormatString('%%').format(b'')
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '%')
        self.assertEqual(result.remaining, b'')

    def test_percent_with_leading_plus_fails(self) -> None:
        result = decode.FormatString('%+%').format(b'')
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, b'')

    def test_percent_with_leading_negative(self) -> None:
        result = decode.FormatString('%-%').format(b'')
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, b'')

    def test_percent_with_leading_space(self) -> None:
        result = decode.FormatString('% %').format(b'')
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, b'')

    def test_percent_with_leading_hashtag(self) -> None:
        result = decode.FormatString('%#%').format(b'')
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, b'')

    def test_percent_with_leading_zero(self) -> None:
        result = decode.FormatString('%0%').format(b'')
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, b'')

    def test_percent_with_length(self) -> None:
        """Test that all length prefixes fail to decode with %."""

        result = decode.FormatString('%hh%').format(b'')
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%h%').format(b'')
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%l%').format(b'')
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%ll%').format(b'')
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%L%').format(b'')
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%j%').format(b'')
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%z%').format(b'')
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%t%').format(b'')
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, b'')

    def test_percent_with_width(self):
        result = decode.FormatString('%9%').format(b'')
        self.assertFalse(result.ok())

    def test_percent_with_multidigit_width(self):
        result = decode.FormatString('%10%').format(b'')
        self.assertFalse(result.ok())

    def test_percent_with_star_width(self):
        result = decode.FormatString('%*%').format(b'')
        self.assertFalse(result.ok())

    def test_percent_with_precision(self):
        result = decode.FormatString('%.5%').format(b'')
        self.assertFalse(result.ok())

    def test_percent_with_multidigit_precision(self):
        result = decode.FormatString('%.10%').format(b'')
        self.assertFalse(result.ok())

    def test_percent_with_star_precision(self):
        result = decode.FormatString('%.*%').format(b'')
        self.assertFalse(result.ok())


# pylint: disable=too-many-public-methods
class TestIntegerDecoding(unittest.TestCase):
    """Tests decoding variable-length integers."""

    def test_signed_integer_d(self) -> None:
        result = decode.FormatString('%d').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-10')
        self.assertEqual(result.remaining, b'')

    def test_signed_integer_d_with_minus(self) -> None:
        result = decode.FormatString('%-5d').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '10   ')
        self.assertEqual(result.remaining, b'')

    def test_signed_integer_d_with_plus(self) -> None:
        result = decode.FormatString('%+d').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '+10')
        self.assertEqual(result.remaining, b'')

    def test_signed_integer_d_with_blank_space(self) -> None:
        result = decode.FormatString('% d').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, ' 10')
        self.assertEqual(result.remaining, b'')

    def test_signed_integer_d_with_plus_and_blank_space_ignores_blank_space(
        self,
    ) -> None:
        result = decode.FormatString('%+ d').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '+10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('% +d').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '+10')
        self.assertEqual(result.remaining, b'')

    def test_signed_integer_d_with_hashtag(self) -> None:
        result = decode.FormatString('%#d').format(encode.encode_args(10))
        self.assertFalse(result.ok())

    def test_signed_integer_d_with_zero(self) -> None:
        result = decode.FormatString('%05d').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '00010')
        self.assertEqual(result.remaining, b'')

    def test_signed_integer_d_with_length(self) -> None:
        """Tests that length modifiers do not affect signed integer decoding."""
        result = decode.FormatString('%hhd').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%hd').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%ld').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%lld').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%jd').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%zd').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%td').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-10')
        self.assertEqual(result.remaining, b'')

    def test_signed_integer_d_with_width(self) -> None:
        result = decode.FormatString('%5d').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '  -10')
        self.assertEqual(result.remaining, b'')

    def test_signed_integer_d_with_width_and_0_flag(self) -> None:
        result = decode.FormatString('%05d').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-0010')

    def test_signed_integer_d_with_multidigit_width(self) -> None:
        result = decode.FormatString('%10d').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '       -10')

    def test_signed_integer_d_with_star_width(self) -> None:
        result = decode.FormatString('%*d').format(encode.encode_args(10, -10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '       -10')

    def test_signed_integer_d_with_missing_width_or_value(self) -> None:
        result = decode.FormatString('%*d').format(encode.encode_args(-10))
        self.assertFalse(result.ok())

    def test_signed_integer_d_with_precision(self) -> None:
        result = decode.FormatString('%.5d').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-00010')

    def test_signed_integer_d_with_multidigit_precision(self) -> None:
        result = decode.FormatString('%.10d').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-0000000010')

    def test_signed_integer_d_with_star_precision(self) -> None:
        result = decode.FormatString('%.*d').format(encode.encode_args(10, -10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-0000000010')

    def test_signed_integer_d_with_zero_precision(self) -> None:
        result = decode.FormatString('%.0d').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-10')

    def test_signed_integer_d_with_empty_precision(self) -> None:
        result = decode.FormatString('%.d').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-10')

    def test_zero_with_zero_precision(self) -> None:
        result = decode.FormatString('%.0d').format(encode.encode_args(0))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '')

    def test_zero_with_empty_precision(self) -> None:
        result = decode.FormatString('%.d').format(encode.encode_args(0))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '')

    def test_signed_integer_d_with_width_and_precision(self) -> None:
        result = decode.FormatString('%10.5d').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '    -00010')

    def test_signed_integer_d_with_star_width_and_precision(self) -> None:
        result = decode.FormatString('%*.*d').format(
            encode.encode_args(15, 10, -10)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '    -0000000010')

    def test_signed_integer_d_with_missing_precision_or_value(self) -> None:
        result = decode.FormatString('%.*d').format(encode.encode_args(-10))
        self.assertFalse(result.ok())

    def test_64_bit_specifier_workaround(self) -> None:
        result = decode.FormatString('%.u%.*lu%0*lu').format(
            encode.encode_args(0, 0, 0, 0, 0)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '0')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%.u%.*lu%0*lu').format(
            encode.encode_args(0, 0, 1, 9, 0)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '1000000000')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%.u%.*lu%0*lu').format(
            encode.encode_args(1, 9, 0, 9, 0)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '1000000000000000000')
        self.assertEqual(result.remaining, b'')

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

    def test_signed_integer_i(self) -> None:
        result = decode.FormatString('%i').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-10')
        self.assertEqual(result.remaining, b'')

    def test_signed_integer_i_with_minus(self) -> None:
        result = decode.FormatString('%-5i').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '10   ')
        self.assertEqual(result.remaining, b'')

    def test_signed_integer_i_with_plus(self) -> None:
        result = decode.FormatString('%+i').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '+10')
        self.assertEqual(result.remaining, b'')

    def test_signed_integer_i_with_blank_space(self) -> None:
        result = decode.FormatString('% i').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, ' 10')
        self.assertEqual(result.remaining, b'')

    def test_signed_integer_i_with_plus_and_blank_space_ignores_blank_space(
        self,
    ) -> None:
        result = decode.FormatString('%+ i').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '+10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('% +i').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '+10')
        self.assertEqual(result.remaining, b'')

    def test_signed_integer_i_with_hashtag(self) -> None:
        result = decode.FormatString('%#i').format(encode.encode_args(10))
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, encode.encode_args(10))

    def test_signed_integer_i_with_zero(self) -> None:
        result = decode.FormatString('%05i').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '00010')
        self.assertEqual(result.remaining, b'')

    def test_signed_integer_i_with_length(self) -> None:
        """Tests that length modifiers do not affect signed integer decoding."""
        result = decode.FormatString('%hhi').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%hi').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%li').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%lli').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%ji').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%zi').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%ti').format(encode.encode_args(-10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '-10')
        self.assertEqual(result.remaining, b'')

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

    def test_unsigned_integer(self) -> None:
        result = decode.FormatString('%u').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '10')
        self.assertEqual(result.remaining, b'')

    def test_unsigned_integer_with_hashtag(self) -> None:
        result = decode.FormatString('%#u').format(encode.encode_args(10))
        self.assertFalse(result.ok())

    def test_unsigned_integer_with_length(self) -> None:
        """Tests that length modifiers pass unsigned integer decoding."""
        result = decode.FormatString('%hhu').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%hu').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%lu').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%llu').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%ju').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%zu').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%tu').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '10')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%Lu').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '10')
        self.assertEqual(result.remaining, b'')

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

    def test_octal_integer(self) -> None:
        result = decode.FormatString('%o').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '12')
        self.assertEqual(result.remaining, b'')

    def test_octal_integer_with_hashtag(self) -> None:
        result = decode.FormatString('%#o').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '012')
        self.assertEqual(result.remaining, b'')

    def test_octal_integer_with_hashtag_and_width(self) -> None:
        result = decode.FormatString('%#10o').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '       012')
        self.assertEqual(result.remaining, b'')

    def test_octal_integer_with_hashtag_and_zero_and_width(self) -> None:
        result = decode.FormatString('%#010o').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '0000000012')
        self.assertEqual(result.remaining, b'')

    def test_octal_integer_with_minus_and_hashtag(self) -> None:
        result = decode.FormatString('%#-5o').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '012  ')
        self.assertEqual(result.remaining, b'')

    def test_octal_integer_with_plus_and_hashtag(self) -> None:
        result = decode.FormatString('%+#o').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '+012')
        self.assertEqual(result.remaining, b'')

    def test_octal_integer_with_space_and_hashtag(self) -> None:
        result = decode.FormatString('% #o').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, ' 012')
        self.assertEqual(result.remaining, b'')

    def test_octal_integer_with_zero_and_hashtag(self) -> None:
        result = decode.FormatString('%#05o').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '00012')
        self.assertEqual(result.remaining, b'')

    def test_octal_integer_with_plus_and_space_and_hashtag_ignores_space(
        self,
    ) -> None:
        result = decode.FormatString('%+ #o').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '+012')
        self.assertEqual(result.remaining, b'')

    def test_octal_integer_with_length(self) -> None:
        """Tests that length modifiers do not affect octal integer decoding."""
        result = decode.FormatString('%hho').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '12')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%ho').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '12')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%lo').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '12')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%llo').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '12')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%jo').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '12')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%zo').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '12')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%to').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '12')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%Lo').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '12')
        self.assertEqual(result.remaining, b'')

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

    def test_lowercase_hex_integer(self) -> None:
        result = decode.FormatString('%x').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'a')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_hex_integer_with_hashtag(self) -> None:
        result = decode.FormatString('%#x').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '0xa')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_hex_integer_with_length(self) -> None:
        """Tests that length modifiers do not affect lowercase hex decoding."""
        result = decode.FormatString('%hhx').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'a')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%hx').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'a')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%lx').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'a')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%llx').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'a')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%jx').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'a')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%zx').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'a')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%tx').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'a')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%Lx').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'a')
        self.assertEqual(result.remaining, b'')

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

    def test_uppercase_hex_integer(self) -> None:
        result = decode.FormatString('%X').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'A')
        self.assertEqual(result.remaining, b'')

    def test_uppercase_hex_integer_with_hashtag(self) -> None:
        result = decode.FormatString('%#X').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '0XA')
        self.assertEqual(result.remaining, b'')

    def test_uppercase_hex_integer_with_length(self) -> None:
        """Tests that length modifiers do not affect uppercase hex decoding."""
        result = decode.FormatString('%hhX').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'A')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%hX').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'A')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%lX').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'A')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%llX').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'A')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%jX').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'A')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%zX').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'A')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%tX').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'A')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%LX').format(encode.encode_args(10))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'A')
        self.assertEqual(result.remaining, b'')

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

    def test_decode_generated_data(self) -> None:
        test_data = varint_test_data.TEST_DATA
        self.assertGreater(len(test_data), 100)

        for signed_spec, signed, unsigned_spec, unsigned, encoded in test_data:
            self.assertEqual(
                int(signed),
                decode.FormatSpec.from_string(signed_spec)
                .decode(bytearray(encoded))
                .value,
            )

            self.assertEqual(
                int(unsigned),
                decode.FormatSpec.from_string(unsigned_spec)
                .decode(bytearray(encoded))
                .value,
            )


# pylint: disable=too-many-public-methods
class TestFloatDecoding(unittest.TestCase):
    """Tests decoding floating-point values using f or F."""

    def test_lowercase_float(self) -> None:
        result = decode.FormatString('%f').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_float_with_minus(self) -> None:
        result = decode.FormatString('%-10f').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000  ')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_float_with_plus(self) -> None:
        result = decode.FormatString('%+f').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '+2.200000')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_float_with_blank_space(self) -> None:
        result = decode.FormatString('% f').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, ' 2.200000')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_float_with_plus_and_blank_space_ignores_blank_space(
        self,
    ) -> None:
        result = decode.FormatString('%+ f').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '+2.200000')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('% +f').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '+2.200000')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_float_with_hashtag(self) -> None:
        result = decode.FormatString('%.0f').format(encode.encode_args(2.0))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%#.0f').format(encode.encode_args(2.0))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_float_with_zero(self) -> None:
        result = decode.FormatString('%010f').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '002.200000')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_float_with_length(self) -> None:
        """Tests that length modifiers do not affect f decoding."""
        result = decode.FormatString('%hhf').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%hf').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%lf').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%llf').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%jf').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%zf').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%tf').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%Lf').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_float_with_width(self) -> None:
        result = decode.FormatString('%9f').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, ' 2.200000')

    def test_lowercase_float_with_multidigit_width(self) -> None:
        result = decode.FormatString('%10f').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '  2.200000')

    def test_lowercase_float_with_star_width(self) -> None:
        result = decode.FormatString('%*f').format(encode.encode_args(10, 2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '  2.200000')

    def test_lowercase_float_non_number(self) -> None:
        result = decode.FormatString('%f').format(encode.encode_args(math.inf))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'inf')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_float_with_precision(self) -> None:
        result = decode.FormatString('%.4f').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2000')

    def test_lowercase_float_with_multidigit_precision(self) -> None:
        result = decode.FormatString('%.10f').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2000000477')

    def test_lowercase_float_with_star_preision(self) -> None:
        result = decode.FormatString('%.*f').format(encode.encode_args(10, 2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2000000477')

    def test_lowercase_float_with_zero_precision(self) -> None:
        result = decode.FormatString('%.0f').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2')

    def test_lowercase_float_with_empty_precision(self) -> None:
        result = decode.FormatString('%.f').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2')

    def test_lowercase_float_with_width_and_precision(self) -> None:
        result = decode.FormatString('%10.0f').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '         2')

    def test_lowercase_float_with_star_width_and_star_precision(self) -> None:
        result = decode.FormatString('%*.*f').format(
            encode.encode_args(20, 10, 2.2)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '        2.2000000477')

    def test_lowercase_float_non_number_with_minus(self) -> None:
        result = decode.FormatString('%-5f').format(
            encode.encode_args(math.inf)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'inf  ')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_float_non_number_with_plus(self) -> None:
        result = decode.FormatString('%+f').format(encode.encode_args(math.inf))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '+inf')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_float_non_number_with_blank_space(self) -> None:
        result = decode.FormatString('% f').format(encode.encode_args(math.inf))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, ' inf')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_float_non_number_with_plus_and_blank_ignores_blank(
        self,
    ) -> None:
        result = decode.FormatString('%+ f').format(
            encode.encode_args(math.inf)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '+inf')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('% +f').format(
            encode.encode_args(math.inf)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '+inf')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_float_non_number_with_hashtag(self) -> None:
        result = decode.FormatString('%#f').format(encode.encode_args(math.inf))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'inf')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_float_non_number_zero(self) -> None:
        result = decode.FormatString('%05f').format(
            encode.encode_args(math.inf)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '  inf')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_float_non_number_with_width(self) -> None:
        result = decode.FormatString('%9f').format(encode.encode_args(math.inf))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '      inf')

    def test_lowercase_float_non_number_with_multidigit_width(self) -> None:
        result = decode.FormatString('%10f').format(
            encode.encode_args(math.inf)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '       inf')

    def test_lowercase_float_non_number_with_star_width(self) -> None:
        result = decode.FormatString('%*f').format(
            encode.encode_args(10, math.inf)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '       inf')

    def test_lowercase_float_non_number_with_precision(self) -> None:
        result = decode.FormatString('%.4f').format(
            encode.encode_args(math.inf)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'inf')

    def test_lowercase_float_non_number_with_multidigit_precision(self) -> None:
        result = decode.FormatString('%.10f').format(
            encode.encode_args(math.inf)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'inf')

    def test_lowercase_float_non_number_with_star_preision(self) -> None:
        result = decode.FormatString('%.*f').format(
            encode.encode_args(10, math.inf)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'inf')

    def test_lowercase_float_non_number_with_zero_precision(self) -> None:
        result = decode.FormatString('%.0f').format(
            encode.encode_args(math.inf)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'inf')

    def test_lowercase_float_non_number_with_empty_precision(self) -> None:
        result = decode.FormatString('%.f').format(encode.encode_args(math.inf))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'inf')

    def test_lowercase_float_non_number_with_width_and_precision(self) -> None:
        result = decode.FormatString('%10.0f').format(
            encode.encode_args(math.inf)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '       inf')

    def test_lowercase_float_non_number_with_star_width_and_star_precision(
        self,
    ) -> None:
        result = decode.FormatString('%*.*f').format(
            encode.encode_args(10, 0, math.inf)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '       inf')

    def test_zero_with_zero_precision(self) -> None:
        result = decode.FormatString('%.0f').format(encode.encode_args(0.0))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '0')

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

    def test_uppercase_float(self) -> None:
        result = decode.FormatString('%F').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000')
        self.assertEqual(result.remaining, b'')

    def test_uppercase_float_with_length(self) -> None:
        """Tests that length modifiers do not affect F decoding."""
        result = decode.FormatString('%hhF').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%hF').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%lF').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%llF').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%jF').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%zF').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000')

        result = decode.FormatString('%tF').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000')
        self.assertEqual(result.remaining, b'')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%LF').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000')
        self.assertEqual(result.remaining, b'')

    def test_uppercase_float_non_number(self) -> None:
        result = decode.FormatString('%F').format(encode.encode_args(math.inf))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'INF')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_exponential(self) -> None:
        result = decode.FormatString('%e').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000e+00')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_exponential_with_length(self) -> None:
        """Tests that length modifiers do not affect e decoding."""
        result = decode.FormatString('%hhe').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000e+00')
        self.assertEqual(result.remaining, b'')

        # inclusive-language: disable
        result = decode.FormatString('%he').format(encode.encode_args(2.2))
        # inclusive-language: enable
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000e+00')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%le').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000e+00')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%lle').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000e+00')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%je').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000e+00')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%ze').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000e+00')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%te').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000e+00')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%Le').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000e+00')
        self.assertEqual(result.remaining, b'')

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

    def test_uppercase_exponential(self) -> None:
        result = decode.FormatString('%E').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000E+00')
        self.assertEqual(result.remaining, b'')

    def test_uppercase_exponential_with_length(self) -> None:
        """Tests that length modifiers do not affect E decoding."""
        result = decode.FormatString('%hhE').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000E+00')
        self.assertEqual(result.remaining, b'')

        # inclusive-language: disable
        result = decode.FormatString('%hE').format(encode.encode_args(2.2))
        # inclusive-language: enable
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000E+00')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%lE').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000E+00')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%llE').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000E+00')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%jE').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000E+00')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%zE').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000E+00')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%tE').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000E+00')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%LE').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.200000E+00')
        self.assertEqual(result.remaining, b'')

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

    def test_lowercase_shortest_take_normal(self) -> None:
        result = decode.FormatString('%g').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_shortest_take_exponential(self) -> None:
        result = decode.FormatString('%g').format(encode.encode_args(1048580.0))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '1.04858e+06')
        self.assertEqual(result.remaining, b'')

    def test_lowercase_shortest_with_length(self) -> None:
        """Tests that length modifiers do not affect g decoding."""
        result = decode.FormatString('%hhg').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%hg').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%lg').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%llg').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%jg').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%zg').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%tg').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%Lg').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2')
        self.assertEqual(result.remaining, b'')

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

    def test_uppercase_shortest_take_normal(self) -> None:
        result = decode.FormatString('%G').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2')
        self.assertEqual(result.remaining, b'')

    def test_uppercase_shortest_take_exponential(self) -> None:
        result = decode.FormatString('%G').format(encode.encode_args(1048580.0))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '1.04858E+06')
        self.assertEqual(result.remaining, b'')

    def test_uppercase_shortest_with_length(self) -> None:
        """Tests that length modifiers do not affect G decoding."""
        result = decode.FormatString('%hhG').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%hG').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%lG').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%llG').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%jG').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%zG').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%tG').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%LG').format(encode.encode_args(2.2))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '2.2')
        self.assertEqual(result.remaining, b'')


class TestCharDecoding(unittest.TestCase):
    """Tests decoding character values."""

    def test_char(self) -> None:
        result = decode.FormatString('%c').format(encode.encode_args(ord('c')))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'c')
        self.assertEqual(result.remaining, b'')

    def test_char_with_minus(self) -> None:
        result = decode.FormatString('%-5c').format(
            encode.encode_args(ord('c'))
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'c    ')
        self.assertEqual(result.remaining, b'')

    def test_char_with_plus(self) -> None:
        result = decode.FormatString('%+c').format(encode.encode_args(ord('c')))
        self.assertFalse(result.ok())

    def test_char_with_blank_space(self) -> None:
        result = decode.FormatString('% c').format(encode.encode_args(ord('c')))
        self.assertFalse(result.ok())

    def test_char_with_hashtag(self) -> None:
        result = decode.FormatString('%#c').format(encode.encode_args(ord('c')))
        self.assertFalse(result.ok())

    def test_char_with_zero(self) -> None:
        result = decode.FormatString('%0c').format(encode.encode_args(ord('c')))
        self.assertFalse(result.ok())

    def test_char_with_length(self) -> None:
        """Tests that length modifiers do not affectchar decoding."""
        result = decode.FormatString('%hhc').format(
            encode.encode_args(ord('c'))
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'c')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%hc').format(encode.encode_args(ord('c')))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'c')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%lc').format(encode.encode_args(ord('c')))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'c')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%llc').format(
            encode.encode_args(ord('c'))
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'c')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%jc').format(encode.encode_args(ord('c')))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'c')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%zc').format(encode.encode_args(ord('c')))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'c')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%tc').format(encode.encode_args(ord('c')))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'c')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%Lc').format(encode.encode_args(ord('c')))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'c')
        self.assertEqual(result.remaining, b'')

    def test_char_with_width(self) -> None:
        result = decode.FormatString('%5c').format(encode.encode_args(ord('c')))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '    c')

    def test_char_with_multidigit_width(self) -> None:
        result = decode.FormatString('%10c').format(
            encode.encode_args(ord('c'))
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '         c')

    def test_char_with_star_width(self) -> None:
        result = decode.FormatString('%*c').format(
            encode.encode_args(10, ord('c'))
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '         c')

    def test_char_with_precision(self) -> None:
        result = decode.FormatString('%.4c').format(
            encode.encode_args(ord('c'))
        )
        self.assertFalse(result.ok())

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

    def test_long_char(self) -> None:
        result = decode.FormatString('%lc').format(encode.encode_args(ord('c')))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'c')
        self.assertEqual(result.remaining, b'')

    def test_long_char_with_hashtag(self) -> None:
        result = decode.FormatString('%#lc').format(
            encode.encode_args(ord('c'))
        )
        self.assertFalse(result.ok())

    def test_long_char_with_zero(self) -> None:
        result = decode.FormatString('%0lc').format(
            encode.encode_args(ord('c'))
        )
        self.assertFalse(result.ok())


class TestStringDecoding(unittest.TestCase):
    """Tests decoding string values."""

    def test_string(self) -> None:
        result = decode.FormatString('%s').format(encode.encode_args('hello'))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'hello')
        self.assertEqual(result.remaining, b'')

    def test_string_with_minus(self) -> None:
        result = decode.FormatString('%-6s').format(encode.encode_args('hello'))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'hello ')
        self.assertEqual(result.remaining, b'')

    def test_string_with_plus(self) -> None:
        result = decode.FormatString('%+s').format(encode.encode_args('hello'))
        self.assertFalse(result.ok())

    def test_string_with_blank_space(self) -> None:
        result = decode.FormatString('% s').format(encode.encode_args('hello'))
        self.assertFalse(result.ok())

    def test_string_with_hashtag(self) -> None:
        result = decode.FormatString('%#s').format(encode.encode_args('hello'))
        self.assertFalse(result.ok())

    def test_string_with_zero(self) -> None:
        result = decode.FormatString('%0s').format(encode.encode_args('hello'))
        self.assertFalse(result.ok())

    def test_string_with_length(self) -> None:
        """Tests that length modifiers do not affect string values (s)."""
        result = decode.FormatString('%hhs').format(encode.encode_args('hello'))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'hello')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%hs').format(encode.encode_args('hello'))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'hello')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%ls').format(encode.encode_args('hello'))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'hello')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%lls').format(encode.encode_args('hello'))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'hello')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%js').format(encode.encode_args('hello'))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'hello')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%zs').format(encode.encode_args('hello'))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'hello')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%ts').format(encode.encode_args('hello'))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'hello')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%Ls').format(encode.encode_args('hello'))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'hello')
        self.assertEqual(result.remaining, b'')

    def test_string_with_width(self) -> None:
        result = decode.FormatString('%6s').format(encode.encode_args('hello'))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, ' hello')

    def test_string_with_width_does_not_pad_a_string_with_same_length(
        self,
    ) -> None:
        result = decode.FormatString('%5s').format(encode.encode_args('hello'))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'hello')

    def test_string_with_multidigit_width(self) -> None:
        result = decode.FormatString('%10s').format(encode.encode_args('hello'))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '     hello')

    def test_string_with_star_width(self) -> None:
        result = decode.FormatString('%*s').format(
            encode.encode_args(10, 'hello')
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '     hello')

    def test_string_with_precision(self) -> None:
        result = decode.FormatString('%.3s').format(encode.encode_args('hello'))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'hel')

    def test_string_with_multidigit_precision(self) -> None:
        result = decode.FormatString('%.10s').format(
            encode.encode_args('hello')
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'hello')

    def test_string_with_star_precision(self) -> None:
        result = decode.FormatString('%.*s').format(
            encode.encode_args(3, 'hello')
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'hel')

    def test_string_with_width_and_precision(self) -> None:
        result = decode.FormatString('%10.3s').format(
            encode.encode_args('hello')
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '       hel')

    def test_string_with_star_with_and_star_precision(self) -> None:
        result = decode.FormatString('%*.*s').format(
            encode.encode_args(10, 3, 'hello')
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '       hel')

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

    def test_long_string(self) -> None:
        result = decode.FormatString('%ls').format(encode.encode_args('hello'))
        self.assertTrue(result.ok())
        self.assertEqual(result.value, 'hello')
        self.assertEqual(result.remaining, b'')

    def test_long_string_with_hashtag(self) -> None:
        result = decode.FormatString('%#ls').format(encode.encode_args('hello'))
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, encode.encode_args('hello'))

    def test_long_string_with_zero(self) -> None:
        result = decode.FormatString('%0ls').format(encode.encode_args('hello'))
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, encode.encode_args('hello'))


class TestPointerDecoding(unittest.TestCase):
    """Tests decoding pointer values."""

    def test_pointer(self) -> None:
        result = decode.FormatString('%p').format(
            encode.encode_args(0xDEADBEEF)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '0xDEADBEEF')
        self.assertEqual(result.remaining, b'')

    def test_pointer_with_minus(self) -> None:
        result = decode.FormatString('%-12p').format(
            encode.encode_args(0xDEADBEEF)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '0xDEADBEEF  ')
        self.assertEqual(result.remaining, b'')

    def test_pointer_with_plus(self) -> None:
        result = decode.FormatString('%+p').format(
            encode.encode_args(0xDEADBEEF)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '+0xDEADBEEF')
        self.assertEqual(result.remaining, b'')

    def test_pointer_with_blank_space(self) -> None:
        result = decode.FormatString('% p').format(
            encode.encode_args(0xDEADBEEF)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, ' 0xDEADBEEF')
        self.assertEqual(result.remaining, b'')

    def test_pointer_with_hashtag(self) -> None:
        result = decode.FormatString('%#p').format(
            encode.encode_args(0xDEADBEEF)
        )
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, encode.encode_args(0xDEADBEEF))

    def test_pointer_with_zero(self) -> None:
        result = decode.FormatString('%0p').format(
            encode.encode_args(0xDEADBEEF)
        )
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, encode.encode_args(0xDEADBEEF))

    def test_pointer_with_length(self) -> None:
        """Tests that length modifiers do not affect decoding pointers (p)."""
        result = decode.FormatString('%hhp').format(
            encode.encode_args(0xDEADBEEF)
        )
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, encode.encode_args(0xDEADBEEF))

        result = decode.FormatString('%hp').format(
            encode.encode_args(0xDEADBEEF)
        )
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, encode.encode_args(0xDEADBEEF))

        result = decode.FormatString('%lp').format(
            encode.encode_args(0xDEADBEEF)
        )
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, encode.encode_args(0xDEADBEEF))

        result = decode.FormatString('%llp').format(
            encode.encode_args(0xDEADBEEF)
        )
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, encode.encode_args(0xDEADBEEF))

        result = decode.FormatString('%jp').format(
            encode.encode_args(0xDEADBEEF)
        )
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, encode.encode_args(0xDEADBEEF))

        result = decode.FormatString('%zp').format(
            encode.encode_args(0xDEADBEEF)
        )
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, encode.encode_args(0xDEADBEEF))

        result = decode.FormatString('%tp').format(
            encode.encode_args(0xDEADBEEF)
        )
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, encode.encode_args(0xDEADBEEF))

        result = decode.FormatString('%Lp').format(
            encode.encode_args(0xDEADBEEF)
        )
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, encode.encode_args(0xDEADBEEF))

    def test_pointer_with_width(self) -> None:
        result = decode.FormatString('%9p').format(
            encode.encode_args(0xDEADBEEF)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '0xDEADBEEF')
        self.assertEqual(result.remaining, b'')

    def test_pointer_with_multidigit_width(self) -> None:
        result = decode.FormatString('%11p').format(
            encode.encode_args(0xDEADBEEF)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, ' 0xDEADBEEF')
        self.assertEqual(result.remaining, b'')

    def test_pointer_with_star_width(self) -> None:
        result = decode.FormatString('%*p').format(
            encode.encode_args(10, 0xDEADBEEF)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '0xDEADBEEF')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%*p').format(
            encode.encode_args(15, 0xDEADBEEF)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '     0xDEADBEEF')
        self.assertEqual(result.remaining, b'')

    def test_pointer_with_precision(self) -> None:
        result = decode.FormatString('%.10p').format(
            encode.encode_args(0xDEADBEEF)
        )
        self.assertFalse(result.ok())
        self.assertEqual(result.remaining, encode.encode_args(0xDEADBEEF))

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

    def test_pointer_0_padding(self) -> None:
        result = decode.FormatString('%p').format(
            encode.encode_args(0x00000000)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '0x00000000')
        self.assertEqual(result.remaining, b'')

    def test_pointer_0_with_width(self) -> None:
        result = decode.FormatString('%9p').format(
            encode.encode_args(0x00000000)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '0x00000000')
        self.assertEqual(result.remaining, b'')

    def test_pointer_0_with_multidigit_width(self) -> None:
        result = decode.FormatString('%11p').format(
            encode.encode_args(0x00000000)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, ' 0x00000000')
        self.assertEqual(result.remaining, b'')

    def test_pointer_0_with_star_width(self) -> None:
        result = decode.FormatString('%*p').format(
            encode.encode_args(10, 0x00000000)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '0x00000000')
        self.assertEqual(result.remaining, b'')

        result = decode.FormatString('%*p').format(
            encode.encode_args(15, 0x00000000)
        )
        self.assertTrue(result.ok())
        self.assertEqual(result.value, '     0x00000000')
        self.assertEqual(result.remaining, b'')


class TestFormattedString(unittest.TestCase):
    """Tests scoring how successfully a formatted string decoded."""

    def test_no_args(self) -> None:
        result = decode.FormatString('string').format(b'')

        self.assertTrue(result.ok())
        self.assertEqual(result.score(), (True, True, 0, 0, datetime.max))

    def test_one_arg(self) -> None:
        result = decode.FormatString('%d').format(encode.encode_args(0))

        self.assertTrue(result.ok())
        self.assertEqual(result.score(), (True, True, 0, 1, datetime.max))

    def test_missing_args(self) -> None:
        result = decode.FormatString('%p%d%d').format(b'\x02\x80')

        self.assertFalse(result.ok())
        self.assertEqual(result.score(), (False, True, -2, 3, datetime.max))
        self.assertGreater(result.score(), result.score(datetime.now()))
        self.assertGreater(
            result.score(datetime.now()), result.score(datetime.min)
        )

    def test_compare_score(self) -> None:
        all_args_ok = decode.FormatString('%d%d%d').format(
            encode.encode_args(0, 0, 0)
        )
        missing_one_arg = decode.FormatString('%d%d%d').format(
            encode.encode_args(0, 0)
        )
        missing_two_args = decode.FormatString('%d%d%d').format(
            encode.encode_args(0)
        )
        all_args_extra_data = decode.FormatString('%d%d%d').format(
            encode.encode_args(0, 0, 0, 1)
        )
        missing_one_arg_extra_data = decode.FormatString('%d%d%d').format(
            b'\0' + b'\x80' * 100
        )

        self.assertGreater(all_args_ok.score(), missing_one_arg.score())
        self.assertGreater(missing_one_arg.score(), missing_two_args.score())
        self.assertGreater(
            missing_two_args.score(), all_args_extra_data.score()
        )
        self.assertGreater(
            all_args_extra_data.score(), missing_one_arg_extra_data.score()
        )


if __name__ == '__main__':
    unittest.main()
