Improve tests for str to Fraction conversion (GH-134010)

This commit is contained in:
Serhiy Storchaka 2025-05-14 20:16:07 +03:00 committed by GitHub
parent 0afbd4e42a
commit 17d0fec702
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,7 +1,7 @@
"""Tests for Lib/fractions.py.""" """Tests for Lib/fractions.py."""
from decimal import Decimal from decimal import Decimal
from test.support import requires_IEEE_754 from test.support import requires_IEEE_754, adjust_int_max_str_digits
import math import math
import numbers import numbers
import operator import operator
@ -395,12 +395,14 @@ class FractionTest(unittest.TestCase):
def testFromString(self): def testFromString(self):
self.assertEqual((5, 1), _components(F("5"))) self.assertEqual((5, 1), _components(F("5")))
self.assertEqual((5, 1), _components(F("005")))
self.assertEqual((3, 2), _components(F("3/2"))) self.assertEqual((3, 2), _components(F("3/2")))
self.assertEqual((3, 2), _components(F("3 / 2"))) self.assertEqual((3, 2), _components(F("3 / 2")))
self.assertEqual((3, 2), _components(F(" \n +3/2"))) self.assertEqual((3, 2), _components(F(" \n +3/2")))
self.assertEqual((-3, 2), _components(F("-3/2 "))) self.assertEqual((-3, 2), _components(F("-3/2 ")))
self.assertEqual((13, 2), _components(F(" 013/02 \n "))) self.assertEqual((13, 2), _components(F(" 0013/002 \n ")))
self.assertEqual((16, 5), _components(F(" 3.2 "))) self.assertEqual((16, 5), _components(F(" 3.2 ")))
self.assertEqual((16, 5), _components(F("003.2")))
self.assertEqual((-16, 5), _components(F(" -3.2 "))) self.assertEqual((-16, 5), _components(F(" -3.2 ")))
self.assertEqual((-3, 1), _components(F(" -3. "))) self.assertEqual((-3, 1), _components(F(" -3. ")))
self.assertEqual((3, 5), _components(F(" .6 "))) self.assertEqual((3, 5), _components(F(" .6 ")))
@ -419,116 +421,101 @@ class FractionTest(unittest.TestCase):
self.assertRaisesMessage( self.assertRaisesMessage(
ZeroDivisionError, "Fraction(3, 0)", ZeroDivisionError, "Fraction(3, 0)",
F, "3/0") F, "3/0")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '3/'", def check_invalid(s):
F, "3/") msg = "Invalid literal for Fraction: " + repr(s)
self.assertRaisesMessage( self.assertRaisesMessage(ValueError, msg, F, s)
ValueError, "Invalid literal for Fraction: '/2'",
F, "/2") check_invalid("3/")
self.assertRaisesMessage( check_invalid("/2")
# Denominators don't need a sign. # Denominators don't need a sign.
ValueError, "Invalid literal for Fraction: '3/+2'", check_invalid("3/+2")
F, "3/+2") check_invalid("3/-2")
self.assertRaisesMessage( # Imitate float's parsing.
# Imitate float's parsing. check_invalid("+ 3/2")
ValueError, "Invalid literal for Fraction: '+ 3/2'", check_invalid("- 3/2")
F, "+ 3/2") # Avoid treating '.' as a regex special character.
self.assertRaisesMessage( check_invalid("3a2")
# Avoid treating '.' as a regex special character. # Don't accept combinations of decimals and rationals.
ValueError, "Invalid literal for Fraction: '3a2'", check_invalid("3/7.2")
F, "3a2") check_invalid("3.2/7")
self.assertRaisesMessage( # No space around dot.
# Don't accept combinations of decimals and rationals. check_invalid("3 .2")
ValueError, "Invalid literal for Fraction: '3/7.2'", check_invalid("3. 2")
F, "3/7.2") # No space around e.
self.assertRaisesMessage( check_invalid("3.2 e1")
# Don't accept combinations of decimals and rationals. check_invalid("3.2e 1")
ValueError, "Invalid literal for Fraction: '3.2/7'", # Fractional part don't need a sign.
F, "3.2/7") check_invalid("3.+2")
self.assertRaisesMessage( check_invalid("3.-2")
# Allow 3. and .3, but not . # Only accept base 10.
ValueError, "Invalid literal for Fraction: '.'", check_invalid("0x10")
F, ".") check_invalid("0x10/1")
self.assertRaisesMessage( check_invalid("1/0x10")
ValueError, "Invalid literal for Fraction: '_'", check_invalid("0x10.")
F, "_") check_invalid("0x10.1")
self.assertRaisesMessage( check_invalid("1.0x10")
ValueError, "Invalid literal for Fraction: '_1'", check_invalid("1.0e0x10")
F, "_1") # Only accept decimal digits.
self.assertRaisesMessage( check_invalid("³")
ValueError, "Invalid literal for Fraction: '1__2'", check_invalid("³/2")
F, "1__2") check_invalid("3/²")
self.assertRaisesMessage( check_invalid("³.2")
ValueError, "Invalid literal for Fraction: '/_'", check_invalid("3.²")
F, "/_") check_invalid("3.2e²")
self.assertRaisesMessage( check_invalid("¼")
ValueError, "Invalid literal for Fraction: '1_/'", # Allow 3. and .3, but not .
F, "1_/") check_invalid(".")
self.assertRaisesMessage( check_invalid("_")
ValueError, "Invalid literal for Fraction: '_1/'", check_invalid("_1")
F, "_1/") check_invalid("1__2")
self.assertRaisesMessage( check_invalid("/_")
ValueError, "Invalid literal for Fraction: '1__2/'", check_invalid("1_/")
F, "1__2/") check_invalid("_1/")
self.assertRaisesMessage( check_invalid("1__2/")
ValueError, "Invalid literal for Fraction: '1/_'", check_invalid("1/_")
F, "1/_") check_invalid("1/_1")
self.assertRaisesMessage( check_invalid("1/1__2")
ValueError, "Invalid literal for Fraction: '1/_1'", check_invalid("1._111")
F, "1/_1") check_invalid("1.1__1")
self.assertRaisesMessage( check_invalid("1.1e+_1")
ValueError, "Invalid literal for Fraction: '1/1__2'", check_invalid("1.1e+1__1")
F, "1/1__2") check_invalid("123.dd")
self.assertRaisesMessage( check_invalid("123.5_dd")
ValueError, "Invalid literal for Fraction: '1._111'", check_invalid("dd.5")
F, "1._111") check_invalid("7_dd")
self.assertRaisesMessage( check_invalid("1/dd")
ValueError, "Invalid literal for Fraction: '1.1__1'", check_invalid("1/123_dd")
F, "1.1__1") check_invalid("789edd")
self.assertRaisesMessage( check_invalid("789e2_dd")
ValueError, "Invalid literal for Fraction: '1.1e+_1'",
F, "1.1e+_1")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '1.1e+1__1'",
F, "1.1e+1__1")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '123.dd'",
F, "123.dd")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '123.5_dd'",
F, "123.5_dd")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: 'dd.5'",
F, "dd.5")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '7_dd'",
F, "7_dd")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '1/dd'",
F, "1/dd")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '1/123_dd'",
F, "1/123_dd")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '789edd'",
F, "789edd")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '789e2_dd'",
F, "789e2_dd")
# Test catastrophic backtracking. # Test catastrophic backtracking.
val = "9"*50 + "_" val = "9"*50 + "_"
self.assertRaisesMessage( check_invalid(val)
ValueError, "Invalid literal for Fraction: '" + val + "'", check_invalid("1/" + val)
F, val) check_invalid("1." + val)
self.assertRaisesMessage( check_invalid("." + val)
ValueError, "Invalid literal for Fraction: '1/" + val + "'", check_invalid("1.1+e" + val)
F, "1/" + val) check_invalid("1.1e" + val)
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '1." + val + "'", def test_limit_int(self):
F, "1." + val) maxdigits = 5000
self.assertRaisesMessage( with adjust_int_max_str_digits(maxdigits):
ValueError, "Invalid literal for Fraction: '1.1+e" + val + "'", val = '1' * maxdigits
F, "1.1+e" + val) num = (10**maxdigits - 1)//9
self.assertEqual((num, 1), _components(F(val)))
self.assertRaises(ValueError, F, val + '1')
self.assertEqual((num, 2), _components(F(val + '/2')))
self.assertRaises(ValueError, F, val + '1/2')
self.assertEqual((1, num), _components(F('1/' + val)))
self.assertRaises(ValueError, F, '1/1' + val)
self.assertEqual(((10**(maxdigits+1) - 1)//9, 10**maxdigits),
_components(F('1.' + val)))
self.assertRaises(ValueError, F, '1.1' + val)
self.assertEqual((num, 10**maxdigits), _components(F('.' + val)))
self.assertRaises(ValueError, F, '.1' + val)
self.assertRaises(ValueError, F, '1.1e1' + val)
self.assertEqual((11, 10), _components(F('1.1e' + '0' * maxdigits)))
self.assertRaises(ValueError, F, '1.1e' + '0' * (maxdigits+1))
def testImmutable(self): def testImmutable(self):
r = F(7, 3) r = F(7, 3)