blob: fe20fd80c7e907215c494f9fc6fc25595f0ebb80 [file] [log] [blame]
#!/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()