Skip to content

Commit 675cb21

Browse files
committed
chore: split pytest files
1 parent fef2f5a commit 675cb21

File tree

3 files changed

+416
-410
lines changed

3 files changed

+416
-410
lines changed

tests/python/test_bigints.py

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
import pytest
2+
import pythonmonkey as pm
3+
import random
4+
5+
def test_eval_numbers_bigints():
6+
def test_bigint(py_number: int):
7+
js_number = pm.eval(f'{repr(py_number)}n')
8+
assert py_number == js_number
9+
10+
test_bigint(0)
11+
test_bigint(1)
12+
test_bigint(-1)
13+
14+
# CPython would reuse the objects for small ints in range [-5, 256]
15+
# Making sure we don't do any changes on them
16+
def test_cached_int_object(py_number):
17+
# type is still int
18+
assert type(py_number) == int
19+
assert type(py_number) != pm.bigint
20+
test_bigint(py_number)
21+
assert type(py_number) == int
22+
assert type(py_number) != pm.bigint
23+
# the value doesn't change
24+
# TODO (Tom Tang): Find a way to create a NEW int object with the same value, because int literals also reuse the cached int objects
25+
for _ in range(2):
26+
test_cached_int_object(0) # _PyLong_FromByteArray reuses the int 0 object,
27+
# see https://github.com/python/cpython/blob/3.9/Objects/longobject.c#L862
28+
for i in range(10):
29+
test_cached_int_object(random.randint(-5, 256))
30+
31+
test_bigint(18014398509481984) # 2**54
32+
test_bigint(-18014398509481984) # -2**54
33+
test_bigint(18446744073709551615) # 2**64-1
34+
test_bigint(18446744073709551616) # 2**64
35+
test_bigint(-18446744073709551617) # -2**64-1
36+
37+
limit = 2037035976334486086268445688409378161051468393665936250636140449354381299763336706183397376
38+
# = 2**300
39+
for i in range(10):
40+
py_number = random.randint(-limit, limit)
41+
test_bigint(py_number)
42+
43+
# TODO (Tom Tang): test -0 (negative zero)
44+
# There's no -0 in both Python int and JS BigInt,
45+
# but this could be possible in JS BigInt's internal representation as it uses a sign bit flag.
46+
# On the other hand, Python int uses `ob_size` 0 for 0, >0 for positive values, <0 for negative values
47+
48+
def test_eval_boxed_numbers_bigints():
49+
def test_boxed_bigint(py_number: int):
50+
# `BigInt()` can only be called without `new`
51+
# https://tc39.es/ecma262/#sec-bigint-constructor
52+
js_number = pm.eval(f'new Object({repr(py_number)}n)')
53+
assert py_number == js_number
54+
55+
test_boxed_bigint(0)
56+
test_boxed_bigint(1)
57+
test_boxed_bigint(-1)
58+
59+
limit = 2037035976334486086268445688409378161051468393665936250636140449354381299763336706183397376
60+
# = 2**300
61+
for i in range(10):
62+
py_number = random.randint(-limit, limit)
63+
test_boxed_bigint(py_number)
64+
65+
def test_eval_functions_bigints():
66+
ident = pm.eval("(a) => { return a }")
67+
add = pm.eval("(a, b) => { return a + b }")
68+
69+
int1 = random.randint(-1000000,1000000)
70+
bigint1 = pm.bigint(int1)
71+
assert int1 == bigint1
72+
73+
# should return pm.bigint
74+
assert type(ident(bigint1)) == pm.bigint
75+
assert ident(bigint1) is not bigint1
76+
# should return float (because JS number is float64)
77+
assert type(ident(int1)) == float
78+
assert ident(int1) == ident(bigint1)
79+
80+
# should raise exception on ints > (2^53-1), or < -(2^53-1)
81+
def not_raise(num):
82+
ident(num)
83+
def should_raise(num):
84+
with pytest.raises(OverflowError, match="Use pythonmonkey.bigint instead"):
85+
ident(num)
86+
not_raise(9007199254740991) # 2**53-1, 0x433_FFFFFFFFFFFFF in float64
87+
should_raise(9007199254740992) # 2**53, 0x434_0000000000000 in float64
88+
should_raise(9007199254740993) # 2**53+1, NOT 0x434_0000000000001 (2**53+2)
89+
not_raise(-9007199254740991) # -(2**53-1)
90+
should_raise(-9007199254740992) # -(2**53)
91+
should_raise(-9007199254740993) # -(2**53+1)
92+
93+
# should also raise exception on large integers (>=2**53) that can be exactly represented by a float64
94+
# in our current implementation
95+
should_raise(9007199254740994) # 2**53+2, 0x434_0000000000001 in float64
96+
should_raise(2**61+2**9) # 0x43C_0000000000001 in float64
97+
98+
# should raise "Use pythonmonkey.bigint" instead of `PyLong_AsLongLong`'s "OverflowError: int too big to convert" on ints larger than 64bits
99+
should_raise(2**65)
100+
should_raise(-2**65)
101+
not_raise(pm.bigint(2**65))
102+
not_raise(pm.bigint(-2**65))
103+
104+
# should raise JS error when mixing a BigInt with a number in arithmetic operations
105+
def should_js_error(a, b):
106+
with pytest.raises(pm.SpiderMonkeyError, match="can't convert BigInt to number"):
107+
add(a, b)
108+
should_js_error(pm.bigint(0), 0)
109+
should_js_error(pm.bigint(1), 2)
110+
should_js_error(3, pm.bigint(4))
111+
should_js_error(-5, pm.bigint(6))
112+
113+
assert add(pm.bigint(0), pm.bigint(0)) == 0
114+
assert add(pm.bigint(1), pm.bigint(0)) == 1
115+
assert add(pm.bigint(1), pm.bigint(2)) == 3
116+
assert add(pm.bigint(-1), pm.bigint(1)) == 0
117+
assert add(pm.bigint(2**60), pm.bigint(0)) == 1152921504606846976
118+
assert add(pm.bigint(2**65), pm.bigint(-2**65-1)) == -1
119+
120+
# fuzztest
121+
limit = 2037035976334486086268445688409378161051468393665936250636140449354381299763336706183397376 # 2**300
122+
for i in range(10):
123+
num1 = random.randint(-limit, limit)
124+
num2 = random.randint(-limit, limit)
125+
assert add(pm.bigint(num1), pm.bigint(num2)) == num1+num2
126+
127+
def test_eval_functions_bigint_factorial():
128+
factorial = pm.eval("(num) => {let r = 1n; for(let i = 0n; i<num; i++){r *= num - i}; return r}")
129+
assert factorial(pm.bigint(1)) == 1
130+
assert factorial(pm.bigint(18)) == 6402373705728000
131+
assert factorial(pm.bigint(19)) == 121645100408832000 # > Number.MAX_SAFE_INTEGER
132+
assert factorial(pm.bigint(21)) == 51090942171709440000 # > 64 bit int
133+
assert factorial(pm.bigint(35)) == 10333147966386144929666651337523200000000 # > 128 bit
134+
135+
def test_eval_functions_bigint_crc32():
136+
crc_table_at = pm.eval("""
137+
// translated from https://rosettacode.org/wiki/CRC-32#Python
138+
const crc_table = (function create_table() {
139+
const a = []
140+
for (let i = 0n; i < 256n; i++) {
141+
let k = i
142+
for (let j = 0n; j < 8n; j++) {
143+
// must use bigint here as js number is trimmed to int32 in bitwise operations
144+
if (k & 1n) k ^= 0x1db710640n
145+
k >>= 1n
146+
}
147+
a.push(k)
148+
}
149+
return a
150+
})();
151+
(n) => crc_table[n]
152+
""")
153+
assert type(crc_table_at(1)) == pm.bigint
154+
assert crc_table_at(0) == 0
155+
assert crc_table_at(1) == 1996959894
156+
assert crc_table_at(255) == 755167117 # last item

0 commit comments

Comments
 (0)