Skip to content

Instantly share code, notes, and snippets.

@rvprasad
Last active October 12, 2017 18:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rvprasad/208105655e1d3f401165306963dea1dd to your computer and use it in GitHub Desktop.
Save rvprasad/208105655e1d3f401165306963dea1dd to your computer and use it in GitHub Desktop.
A script to demonstrate how to use property-based testing to test str_to_int function. Not all tests are required.
# Python -- v3.6
# https://docs.pytest.org/en/latest/ -- v3.2.1
# http://hypothesis.readthedocs.io/en/latest/ -- v3.7
from hypothesis import assume, given
from hypothesis.strategies import text
import pytest
rep2int = {
'1':1,
'2':2,
'3':3,
'4':4,
'5':5,
'6':6,
'7':7,
'8':8,
'9':9,
'0':0
}
def str_to_int(s):
'''
Return X if s represents a valid integer X
Raise ValueError if s does not represent a valid integer
'''
tmp1 = list(s)
tmp1.reverse()
val = 0
pos = 0
signSeen = False
numSeen = False
for i in tmp1:
if i in rep2int:
val += rep2int[i] * (10 ** pos)
numSeen = True
elif i == '-' or i == '+':
if signSeen:
raise ValueError("Invalid repr")
val *= -1 if i == '-' else 1
signSeen = True
else:
raise ValueError("Invalid repr")
pos += 1
if not numSeen:
raise ValueError("Invalid repr")
return val
@given(text(min_size=1, max_size=15))
def test_invalid_repr(s):
# assume clause can be simplified by using regex
assume(
not(
all(i in list('1234567890-+') for i in s) and
s.count('-') + s.count('+') < 2 and
(s.count('-') == 0 or s.index('-') == 0 and len(s) > 1) and
(s.count('+') == 0 or s.index('+') == 0 and len(s) > 1)
)
)
with pytest.raises(ValueError):
str_to_int(s)
@given(text(alphabet='+1234567890', min_size=1, max_size=15))
def test_positive_int_repr(s):
assume(
s.count('+') < 2 and
(s.count('+') == 0 or s.index('+') == 0 and len(s) > 1)
)
if all(i in ['0', '+'] for i in s):
assert str_to_int(s) == 0
else:
assert str_to_int(s) > 0
@given(text(alphabet='-1234567890', min_size=1, max_size=15))
def test_negative_int_repr(s):
assume(s.count('-') == 1 and s.index('-') == 0 and len(s) > 1)
# assuming we deem -0 as valid repr
if all(i in ['0', '-'] for i in s):
assert str_to_int(s) == 0
else:
assert str_to_int(s) < 0
@given(text(alphabet='1234567890', min_size=1, max_size=1))
def test_single_digit_int_repr(s):
assert str_to_int(s) == rep2int[s]
@given(text(alphabet='-+1234567890', min_size=1, max_size=15))
def test_multi_digit_int_repr(s):
assume(
s.count('-') + s.count('+') < 2 and
(s.count('-') == 0 or s.index('-') == 0 and len(s) > 1) and
(s.count('+') == 0 or s.index('+') == 0 and len(s) > 1)
)
val = str_to_int(s)
revS = list(s)
sgn = 1
if revS[0] == '+':
del revS[0]
if revS[0] == '-':
del revS[0]
sgn = -1
revS.reverse()
for i in range(0, len(revS)):
tmp1 = str_to_int(revS[i])
tmp2 = val * sgn // (10 ** i)
tmp3 = tmp2 - (tmp2 // 10) * 10
assert tmp1 == tmp3
@given(text(alphabet='+-1234567890', min_size=1, max_size=15))
def test_convert_back(s):
assume(
s.count('-') + s.count('+') < 2 and
(s.count('-') == 0 or s.index('-') == 0 and len(s) > 1) and
(s.count('+') == 0 or s.index('+') == 0 and len(s) > 1)
)
# this would be the simplest test for all valid repr
tmp1 = ""
sgn = ''
nonZeroSeen = False
for i in s:
if i == '-':
sgn = '-'
elif i == '+':
pass
elif i != '0':
nonZeroSeen = True
if nonZeroSeen:
tmp1 += i
tmp1 = sgn + tmp1 if tmp1 else '0'
assert str(str_to_int(s)) == tmp1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment