#!/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()