201 lines
7.3 KiB
Python
201 lines
7.3 KiB
Python
from contextlib import contextmanager, redirect_stderr, redirect_stdout
|
|
from io import StringIO
|
|
from importlib.machinery import SourceFileLoader
|
|
import os
|
|
import sys
|
|
import warnings
|
|
import shlex
|
|
from textwrap import dedent
|
|
from tempfile import NamedTemporaryFile
|
|
import unittest
|
|
|
|
|
|
class FixCSVTests(unittest.TestCase):
|
|
|
|
"""Tests for fix_csv.py"""
|
|
|
|
maxDiff = None
|
|
|
|
def test_pipe_file_to_csv_file(self):
|
|
old_contents = dedent("""
|
|
2012|Lexus|LFA
|
|
2009|GMC|Yukon XL 1500
|
|
1965|Ford|Mustang
|
|
2005|Hyundai|Sonata
|
|
1995|Mercedes-Benz|C-Class
|
|
""").lstrip()
|
|
expected = dedent("""
|
|
2012,Lexus,LFA
|
|
2009,GMC,Yukon XL 1500
|
|
1965,Ford,Mustang
|
|
2005,Hyundai,Sonata
|
|
1995,Mercedes-Benz,C-Class
|
|
""").lstrip()
|
|
with make_file(old_contents) as old, make_file("") as new:
|
|
output = run_program(f'fix_csv.py {old} {new}')
|
|
with open(new) as new_file:
|
|
new_contents = new_file.read()
|
|
self.assertEqual(expected, new_contents)
|
|
self.assertEqual("", output)
|
|
|
|
def test_delimiter_in_output(self):
|
|
old_contents = dedent("""
|
|
02|Waylon Jennings|Honky Tonk Heroes (Like Me)
|
|
04|Kris Kristofferson|To Beat The Devil
|
|
11|Johnny Cash|Folsom Prison Blues
|
|
13|Billy Joe Shaver|Low Down Freedom
|
|
21|Hank Williams III|Mississippi Mud
|
|
22|David Allan Coe|Willie, Waylon, And Me
|
|
24|Bob Dylan|House Of The Risin' Sun
|
|
""").lstrip()
|
|
expected = dedent("""
|
|
02,Waylon Jennings,Honky Tonk Heroes (Like Me)
|
|
04,Kris Kristofferson,To Beat The Devil
|
|
11,Johnny Cash,Folsom Prison Blues
|
|
13,Billy Joe Shaver,Low Down Freedom
|
|
21,Hank Williams III,Mississippi Mud
|
|
22,David Allan Coe,"Willie, Waylon, And Me"
|
|
24,Bob Dylan,House Of The Risin' Sun
|
|
""").lstrip()
|
|
with make_file(old_contents) as old, make_file("") as new:
|
|
output = run_program(f'fix_csv.py {old} {new}')
|
|
with open(new) as new_file:
|
|
new_contents = new_file.read()
|
|
self.assertEqual(expected, new_contents)
|
|
self.assertEqual("", output)
|
|
|
|
def test_original_file_is_unchanged(self):
|
|
old_contents = dedent("""
|
|
2012|Lexus|LFA
|
|
2009|GMC|Yukon XL 1500
|
|
""").lstrip()
|
|
with make_file(old_contents) as old, make_file("") as new:
|
|
run_program(f'fix_csv.py {old} {new}')
|
|
with open(old) as old_file:
|
|
contents = old_file.read()
|
|
self.assertEqual(old_contents, contents)
|
|
|
|
def test_call_with_too_many_files(self):
|
|
with make_file("") as old, make_file("") as new:
|
|
with self.assertRaises(BaseException):
|
|
run_program(f'fix_csv.py {old} {new} {old}')
|
|
|
|
# To test the Bonus part of this exercise, comment out the following line
|
|
@unittest.expectedFailure
|
|
def test_in_delimiter_and_in_quote(self):
|
|
old_contents = dedent("""
|
|
2012 Lexus "LFA"
|
|
2009 GMC 'Yukon XL 1500'
|
|
1995 "Mercedes-Benz" C-Class
|
|
""").lstrip()
|
|
expected1 = dedent("""
|
|
2012,Lexus,LFA
|
|
2009,GMC,'Yukon,XL,1500'
|
|
1995,Mercedes-Benz,C-Class
|
|
""").lstrip()
|
|
expected2 = dedent('''
|
|
2012,Lexus,"""LFA"""
|
|
2009,GMC,Yukon XL 1500
|
|
1995,"""Mercedes-Benz""",C-Class
|
|
''').lstrip()
|
|
with make_file(old_contents) as old, make_file("") as new:
|
|
run_program(f'fix_csv.py {old} {new} --in-delimiter=" "')
|
|
with open(new) as new_file:
|
|
self.assertEqual(expected1, new_file.read())
|
|
run_program(
|
|
f'''fix_csv.py --in-delimiter=" " --in-quote="'" {old} {new}'''
|
|
)
|
|
with open(new) as new_file:
|
|
self.assertEqual(expected2, new_file.read())
|
|
|
|
# To test the Bonus part of this exercise, comment out the following line
|
|
@unittest.expectedFailure
|
|
def test_autodetect_input_format(self):
|
|
contents1 = dedent("""
|
|
'2012' 'Lexus' 'LFA'
|
|
'2009' 'GMC' 'Yukon XL 1500'
|
|
'1995' 'Mercedes-Benz' 'C-Class'
|
|
""").lstrip()
|
|
expected1 = dedent("""
|
|
2012,Lexus,LFA
|
|
2009,GMC,Yukon XL 1500
|
|
1995,Mercedes-Benz,C-Class
|
|
""").lstrip()
|
|
with make_file(contents1) as old, make_file("") as new:
|
|
run_program(f'fix_csv.py {old} {new}')
|
|
with open(new) as new_file:
|
|
self.assertEqual(expected1, new_file.read())
|
|
contents2 = dedent("""
|
|
"02"\t"Waylon Jennings"\t"Honky Tonk Heroes (Like Me)"\t"3:29"
|
|
"04"\t"Kris Kristofferson"\t"To Beat The Devil"\t"4:05"
|
|
"11"\t"Johnny Cash"\t"Folsom Prison Blues"\t"2:51"
|
|
"13"\t"Billy Joe Shaver"\t"Low Down Freedom"\t"2:53"
|
|
"21"\t"Hank Williams III"\t"Mississippi Mud"\t"3:32"
|
|
"22"\t"David Allan Coe"\t"Willie, Waylon, And Me"\t"3:24"
|
|
"24"\t"Bob Dylan"\t"House Of The Risin' Sun"\t"5:20"
|
|
""").lstrip()
|
|
expected2 = dedent("""
|
|
02,Waylon Jennings,Honky Tonk Heroes (Like Me),3:29
|
|
04,Kris Kristofferson,To Beat The Devil,4:05
|
|
11,Johnny Cash,Folsom Prison Blues,2:51
|
|
13,Billy Joe Shaver,Low Down Freedom,2:53
|
|
21,Hank Williams III,Mississippi Mud,3:32
|
|
22,David Allan Coe,"Willie, Waylon, And Me",3:24
|
|
24,Bob Dylan,House Of The Risin' Sun,5:20
|
|
""").lstrip()
|
|
with make_file(contents2) as old, make_file("") as new:
|
|
run_program(f'fix_csv.py {old} {new}')
|
|
with open(new) as new_file:
|
|
self.assertEqual(expected2, new_file.read())
|
|
|
|
|
|
class DummyException(Exception):
|
|
"""No code will ever raise this exception."""
|
|
|
|
|
|
def run_program(arguments="", raises=DummyException):
|
|
"""
|
|
Run program at given path with given arguments.
|
|
|
|
If raises is specified, ensure the given exception is raised.
|
|
"""
|
|
arguments = arguments.replace('\\', '\\\\')
|
|
path, *args = shlex.split(arguments)
|
|
old_args = sys.argv
|
|
warnings.simplefilter("ignore", ResourceWarning)
|
|
try:
|
|
sys.argv = [path] + args
|
|
try:
|
|
if '__main__' in sys.modules:
|
|
del sys.modules['__main__']
|
|
with redirect_stdout(StringIO()) as output:
|
|
with redirect_stderr(output):
|
|
SourceFileLoader('__main__', path).load_module()
|
|
except raises:
|
|
return output.getvalue()
|
|
except SystemExit as e:
|
|
if e.args != (0,):
|
|
raise SystemExit(output.getvalue()) from e
|
|
finally:
|
|
sys.modules.pop('__main__', None)
|
|
if raises is not DummyException:
|
|
raise AssertionError("{} not raised".format(raises))
|
|
return output.getvalue()
|
|
finally:
|
|
sys.argv = old_args
|
|
|
|
|
|
@contextmanager
|
|
def make_file(contents=None):
|
|
"""Context manager providing name of a file containing given contents."""
|
|
with NamedTemporaryFile(mode='wt', encoding='utf-8', delete=False) as f:
|
|
if contents:
|
|
f.write(contents)
|
|
try:
|
|
yield f.name
|
|
finally:
|
|
os.remove(f.name)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main(verbosity=2) |