Skip to content

Instantly share code, notes, and snippets.

@japsu
Created April 27, 2012 09:52
Show Gist options
  • Save japsu/2507971 to your computer and use it in GitHub Desktop.
Save japsu/2507971 to your computer and use it in GitHub Desktop.
Phone number formatting and a minimal-boilerplate testing library
12:50:03 <&Japsu> Koodaa suomalaisten puhelinnumeroiden muotoilua, päädy toteuttamaan oma minimal-boilerplate-testauskirjasto.
Traceback (most recent call last):
File "finphone.py", line 73, in <module>
assert verify.check(), verify.report()
AssertionError: FAILURE (passed 10, failed 15, total 25)
FAIL #1: split_area_code
INPUT: '035551234'
OUTPUT: ['', '035551234']
EXPECTED: ('03', '5551234')
FAIL #2: split_area_code
INPUT: '095551234'
OUTPUT: ['', '095551234']
EXPECTED: ('09', '5551234')
FAIL #3: split_area_code
INPUT: '0195551234'
OUTPUT: ['', '0195551234']
EXPECTED: ('019', '5551234')
FAIL #4: split_area_code
INPUT: '0505551234'
OUTPUT: ['', '0505551234']
EXPECTED: ('050', '5551234')
FAIL #5: split_area_code
INPUT: '0500555123'
OUTPUT: ['', '0500555123']
EXPECTED: ('0500', '555123')
FAIL #6: split_area_code
INPUT: '0440555123'
OUTPUT: ['', '0440555123']
EXPECTED: ('0440', '555123')
FAIL #7: format_finnish_phone_number
INPUT: '0505551234'
OUTPUT: '0 050 555 1234'
EXPECTED: '050 555 1234'
FAIL #8: format_finnish_phone_number
INPUT: '0500555123'
OUTPUT: '0 050 055 5123'
EXPECTED: '0500 555 123'
FAIL #9: format_finnish_phone_number
INPUT: '035551234'
OUTPUT: '0 035 551 234'
EXPECTED: '03 555 1234'
FAIL #10: format_finnish_phone_number
INPUT: '0195551234'
OUTPUT: '0 019 555 1234'
EXPECTED: '019 555 1234'
FAIL #11: format_finnish_phone_number
INPUT: '09555123'
OUTPUT: '0 095 55123'
EXPECTED: '09 555 123'
FAIL #12: format_finnish_phone_number
INPUT: '0800123123'
OUTPUT: '0 080 012 3123'
EXPECTED: '0800 123 123'
FAIL #13: format_finnish_phone_number
INPUT: '02055512345678'
OUTPUT: '0 020 555 123 45678'
EXPECTED: '020 555 123 45678'
FAIL #14: format_finnish_phone_number
INPUT: '020202'
OUTPUT: '0 020 202'
EXPECTED: '020 202'
FAIL #15: format_finnish_phone_number
INPUT: '+358505551234'
OUTPUT: '0 +35 850 555 1234'
EXPECTED: '050 555 1234'
FAILURE (passed 10, failed 15, total 25)
#!/usr/bin/env python
from itertools import chain
from verify import verify
GROUPS_TEST_DATA = [
('12', ['12']),
('123', ['123']),
('1234', ['1234']),
('12345', ['12345']),
('123456', ['123', '456']),
('1234567', ['123', '4567']),
('12345678', ['123', '45678']),
('123456789', ['123', '456', '789']),
(range(10, 15), [range(10, 15)]),
(range(10, 16), [range(10, 13), range(13, 16)]),
]
@verify(GROUPS_TEST_DATA, list)
def split_into_groups(digits, min_length=3):
# XXX horrendous recursive generator
split, splat = digits[:min_length], digits[min_length:]
if len(splat) < min_length:
yield split + splat
else:
yield split
for i in split_into_groups(splat, min_length):
yield i
AREA_CODE_TEST_DATA = [
('035551234', ('03', '5551234')),
('095551234', ('09', '5551234')),
('0195551234', ('019', '5551234')),
('0505551234', ('050', '5551234')),
('0500555123', ('0500', '555123')),
('0440555123', ('0440', '555123')),
]
SHORT_AREA_CODES = ['03', '05', '09']
@verify(AREA_CODE_TEST_DATA, list)
def split_area_code(phone_number):
return '', phone_number
PHONE_NUMBER_TEST_DATA = [
('0505551234', '050 555 1234'), # basic format of GSM numbers
('0500555123', '0500 555 123'), # NMT to GSM transitional numbers
('035551234', '03 555 1234'), # basic format of landline numbers
('0195551234', '019 555 1234'), # some areas have a three-digit area code
('09555123', '09 555 123'), # there are short 6-digit phone numbers at least in the capital area
('0800123123', '0800 123 123'), # "recreational" services
('02055512345678', '020 555 123 45678'), # extremely long numbers possible in corporate phones
('020202', '020 202'), # fuck fonecta ;) ;)
('+358505551234', '050 555 1234'), # convert international phone numbers to Finnish format
]
@verify(PHONE_NUMBER_TEST_DATA)
def format_finnish_phone_number(ugly):
area_code, local_number = split_area_code(ugly)
groups = split_into_groups(local_number)
return " ".join(chain(['0' + area_code], groups))
if __name__ == "__main__":
assert verify.check(), verify.report()
12:57:12 <@Japsu> Koodaa suomalaisten puhelinnumerojen muotoilua, päädy toteuttamaan oma minimal-boilerplate-testauskirjasto: https://gist.github.com/2507971
12:58:33 <@Lmmz> puuttuu testicase, jossa kansainvälinen prefiksi on muodossa 00358!
12:58:44 <@Japsu> hienoa, kiitos, täytyypä lisätä!
12:58:59 <@Japsu> itse finphone.py on siis hyvinkin kesken koska päädyin koodaamaan verify.py:tä!
12:59:00 <@Lmmz> ja vireve-numerot
12:59:04 <@Lmmz> virve jopa
12:59:07 <@Japsu> mitäs ne on muodoltaan?
12:59:38 <@Japsu> tajusin just, et 0500, 050, 05 on aikamoinen klusteri
12:59:54 <@Japsu> ratkaisu: listaan kaikki suuntanumerot preferenssijärjestyksessä
13:00:47 <@Lmmz> niissä virvenumeroissa on monissa aika pitkä prefiksi, muistaakseni joku viisnumeroinen
13:00:55 <@Lmmz> sitten on esim. PV:n numerot, joissa on prefiksinä 0299
13:01:23 <@Lmmz> esim. 0299 530342
13:01:26 <@Japsu> hmmjoo
13:01:52 <@Lmmz> muillakin viranomaisilla oli jotain jänniä numeroita nykyään, poliisilla taitaa kans olla joku valtakunnallinen "suuntanumero"
13:02:05 <@Japsu> täytynee tehdä toi silleen että se yrittää löytää mahdollisimman pitkän suuntanumeron
#!/usr/bin/env python
# TODO: make verify into a factory called Verify that produces parametrized decorators so that
# verify = Verify()
# @verify(MY_EXAMPLES)
# def myfun():
# ....
# verify.check()
def identity(x):
return x
class verify(object):
instances = list()
failures = None
total = None
def __init__(self, test_data, treat_output=identity):
self.test_data = test_data
self.treat_output = treat_output
def __call__(self, func):
self.func = func
self.instances.append(self)
return func
@classmethod
def check(cls):
cls.failures = list()
cls.total = 0
for instance in cls.instances:
for example in instance.test_data:
cls.total += 1
input, expected = example
output = instance.func(input)
output = instance.treat_output(output)
if output != expected:
cls.failures.append((instance.func, input, output, expected))
return not cls.failures
@classmethod
def clear(cls):
cls.instances = list()
cls.total = None
cls.failures = None
@classmethod
def report(cls):
msgs = list()
failures = len(cls.failures)
passes = cls.total - failures
if cls.failures is None:
msgs.append('NO TESTS WERE RUN')
elif not cls.failures:
msgs.append('SUCCESS (passed {passes})'.format(**locals()))
else:
failure_headline = 'FAILURE (passed {passes}, failed {failures}, total {cls.total})'.format(**locals())
msgs.append(failure_headline)
for (num, (func, input, output, expected)) in enumerate(cls.failures):
num += 1
msgs.append('FAIL #{num}: {func.__name__}\nINPUT: {input!r}\nOUTPUT: {output!r}\nEXPECTED: {expected!r}'.format(**locals()))
msgs.append(failure_headline)
msgs.append('')
return '\n\n'.join(msgs)
def test_verify():
TEST1 = zip(range(5), range(5))
func1 = verify(TEST1)(identity)
assert verify.check(), verify.failures
print verify.report()
verify.clear()
TEST2 = [('1','1'), ('1', '2')]
func2 = verify(TEST2)(identity)
assert not verify.check(), verify.failures
assert verify.failures == [(identity, '1', '1', '2')], verify.failures
if __name__ == "__main__":
test_verify()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment