Coverage for tests/unit/misc/test_numerical_conversions.py: 100%

43 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2025-04-04 03:33 -0600

1from __future__ import annotations 

2 

3import random 

4from math import isclose, isinf, isnan 

5 

6import pytest 

7 

8from muutils.misc import shorten_numerical_to_str, str_to_numeric 

9 

10 

11@pytest.mark.parametrize( 

12 "quantity, expected", 

13 [ 

14 ("5", 5), 

15 ("-5", -5), 

16 ("0.1", 0.1), 

17 ("-0.1", -0.1), 

18 ("1/5", 0.2), 

19 ("-1/5", -0.2), 

20 ("1K", 1000.0), 

21 ("-1K", -1000.0), 

22 ("1.5M", 1500000.0), 

23 ("-1.5M", -1500000.0), 

24 ("2.5B", 2.5 * 1e9), 

25 ("-2.5B", -2.5 * 1e9), 

26 ("1/2M", 0.5 * 1e6), 

27 ("-1/2M", -0.5 * 1e6), 

28 ("100q", 100 * 1e15), 

29 ("-100q", -100 * 1e15), 

30 ("0.001Q", 0.001 * 1e18), 

31 ("-0.001Q", -0.001 * 1e18), 

32 ("1.23", 1.23), 

33 ("-1.23", -1.23), 

34 ("4.5678e2", 456.78), 

35 ("-4.5678e2", -456.78), 

36 ], 

37) 

38def test_str_to_numeric(quantity, expected): 

39 assert str_to_numeric(quantity) == expected 

40 

41 

42@pytest.mark.parametrize( 

43 "x, s", 

44 [ 

45 ("inf", 1), 

46 ("INF", 1), 

47 ("infinity", 1), 

48 ("INFINITY", 1), 

49 ("-inf", -1), 

50 ("-INF", -1), 

51 ("-infinity", -1), 

52 ("-INFINITY", -1), 

53 ], 

54) 

55def test_str_to_numeric_inf(x: str, s: int): 

56 x_num: float = str_to_numeric(x) 

57 assert isinf(x_num) 

58 if s == 1: 

59 assert x_num > 0 

60 else: 

61 assert x_num < 0 

62 

63 

64def test_str_to_numeric_nan(): 

65 assert isnan(str_to_numeric("nan")) 

66 assert isnan(str_to_numeric("NAN")) 

67 

68 

69@pytest.mark.parametrize( 

70 "x", 

71 [ 

72 "1/0", 

73 "-1/0", 

74 "5/0", 

75 "-5/0", 

76 ], 

77) 

78def test_div_by_zero(x: str): 

79 with pytest.raises(ZeroDivisionError): 

80 str_to_numeric(x) 

81 

82 

83@pytest.mark.parametrize( 

84 "quantity", 

85 [ 

86 "invalid", 

87 "5.5.5", 

88 "1/2/3", 

89 "1QQ", 

90 "1K5", 

91 "1.2.3M", 

92 "1e1e1", 

93 "None", 

94 "null", 

95 "nil", 

96 "nanan", 

97 "infinitesimal", 

98 "infinity and beyond", 

99 "True or False", 

100 "a lot!", 

101 "12*&**@&#!dkjadhkj", 

102 ], 

103) 

104def test_str_to_numeric_invalid(quantity): 

105 with pytest.raises(ValueError): 

106 str_to_numeric(quantity) 

107 

108 

109@pytest.mark.parametrize( 

110 "quantity, mapping, expected", 

111 [ 

112 ("5k", {"k": 1e3}, 5000), 

113 ("-5k", {"k": 1e3}, -5000), 

114 ("2.5x", {"x": 1e5}, 2.5 * 1e5), 

115 ("-2.5x", {"x": 1e5}, -2.5 * 1e5), 

116 ("1/4y", {"y": 1e2}, 0.25 * 1e2), 

117 ("-1/4y", {"y": 1e2}, -0.25 * 1e2), 

118 ("1.2LOL", {"LOL": 1e6}, 1.2 * 1e6), 

119 ("-1.2LOL", {"LOL": 1e6}, -1.2 * 1e6), 

120 ], 

121) 

122def test_str_to_numeric_custom_mapping(quantity, mapping, expected): 

123 assert str_to_numeric(quantity, mapping) == expected 

124 

125 

126@pytest.mark.parametrize( 

127 "quantity, small_as_decimal, precision, expected", 

128 [ 

129 (1234, True, 1, "1.2K"), 

130 (1234, False, 1, "1K"), 

131 (1234, True, 2, "1.23K"), 

132 (1234567, True, 1, "1.2M"), 

133 (1234567890, True, 1, "1.2B"), 

134 (1234567890123, True, 1, "1.2t"), 

135 (1234567890123456, True, 1, "1.2q"), 

136 (1234567890123456789, True, 1, "1.2Q"), 

137 ], 

138) 

139def test_shorten_numerical_to_str(quantity, small_as_decimal, precision, expected): 

140 assert shorten_numerical_to_str(quantity, small_as_decimal, precision) == expected 

141 

142 

143@pytest.mark.parametrize( 

144 "exponent_range, n_tests", 

145 [ 

146 ((1, 18), 1000), 

147 ((-2, 2), 1000), 

148 ], 

149) 

150def test_round_trip_fuzzing(exponent_range, n_tests): 

151 for _ in range(n_tests): 

152 exponent: int = random.randint(*exponent_range) 

153 mantissa: float = random.uniform(1, 10) 

154 num_sign: int = random.choice([-1, 1]) 

155 num: float = num_sign * mantissa * (10**exponent) 

156 

157 shortened: str = shorten_numerical_to_str(num) 

158 print(f"num: {num}, shortened: {shortened}") 

159 restored: float = str_to_numeric(shortened) 

160 

161 assert isclose( 

162 num, restored, rel_tol=1e-1 

163 ), f"Failed for num: {num}, shortened: {shortened}, restored: {restored}"