Skip to content

Commit

Permalink
add tests for CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
relikd committed Oct 30, 2021
1 parent a05626f commit 478adfc
Show file tree
Hide file tree
Showing 2 changed files with 313 additions and 1 deletion.
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ uninstall:

.PHONY: test
test:
python3 tests/test_icnsutil.py
@echo 'Test lib...'
@python3 tests/test_icnsutil.py
@echo
@echo 'Test CLI...'
@python3 tests/test_cli.py

dist-env:
@echo Creating virtual environment...
Expand Down
308 changes: 308 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
#!/usr/bin/env python3
import unittest
import shutil # rmtree, copy
import os # chdir, listdir, makedirs, path, remove
from subprocess import run, PIPE
if __name__ == '__main__':
import sys
sys.path[0] = os.path.dirname(sys.path[0])
from icnsutil import RawData, PIL_ENABLED, __version__


def main():
# ensure working dir is correct
os.chdir(os.path.join(os.path.dirname(__file__), 'fixtures'))
print('Running tests with PIL_ENABLED =', PIL_ENABLED)
unittest.main()
exit()


###############
# CLI tests #
###############

def run_cli(args): # relative to fixtures folder
exec_path = os.path.join(os.pardir, os.pardir, 'icnsutil', 'cli.py')
return run([sys.executable, exec_path] + args, stdout=PIPE, stderr=PIPE)


class TestCLI(unittest.TestCase):
def test_help(self):
val1 = run_cli([]).stdout
val2 = run_cli(['--help']).stdout
self.assertEqual(val1, val2)

def test_version(self):
val1 = run_cli(['--version']).stdout
self.assertEqual(val1.split()[1], bytes(__version__, 'utf8'))


class TestCLI_export(unittest.TestCase):
def setUp(self):
self.OUTDIR = 'tmp_cli_out_export'
if os.path.isdir(self.OUTDIR):
shutil.rmtree(self.OUTDIR)
os.makedirs(self.OUTDIR, exist_ok=True)

def tearDown(self):
shutil.rmtree(self.OUTDIR)

def assert_files(self, infile, args, sorted_out_files):
r = run_cli(['e', infile, '-o', self.OUTDIR] + args)
files = sorted(os.listdir(self.OUTDIR))
if r.returncode == 0:
self.assertListEqual(files, sorted_out_files)
return r

def test_extract(self):
self.assert_files('rgb.icns', [], [
'128x128-mask8b.bin', '128x128.rgb',
'16x16-icon1b-mask1b.bin', '16x16-mask8b.bin', '16x16.rgb',
'32x32-icon1b-mask1b.bin', '32x32-mask8b.bin', '32x32.rgb'])
for size, fname in [
(64, '16x16-icon1b-mask1b.bin'),
(1024, '32x32-mask8b.bin'),
(14005, '128x128.rgb'),
]:
fsize = os.path.getsize(os.path.join(self.OUTDIR, fname))
self.assertEqual(size, fsize)

def test_extract_by_key(self):
self.assert_files('rgb.icns', ['-k'], [
'ICN#.bin', 'ics#.bin', 'il32.rgb', 'is32.rgb',
'it32.rgb', 'l8mk.bin', 's8mk.bin', 't8mk.bin'])

def test_extract_w_png(self):
self.assert_files('selected.icns', [], [
'16x16.argb', '[email protected]', '18x18.argb', '[email protected]',
'24x24.png', '[email protected]', '32x32.argb', '[email protected]',
'info.plist', 'selected.icns'])

def test_extract_png_only(self):
self.assert_files('selected.icns', ['--png-only'], [
'[email protected]', '[email protected]', '24x24.png',
'[email protected]', '[email protected]'])

def test_extract_nested(self):
self.assert_files('selected.icns', ['-r', '--png-only'], [
'[email protected]', '[email protected]', '24x24.png',
'[email protected]', '[email protected]', 'selected.icns.export'])
files = sorted(os.listdir(
os.path.join(self.OUTDIR, 'selected.icns.export')))
self.assertListEqual(files, [
'[email protected]', '[email protected]', '24x24.png',
'[email protected]', '[email protected]'])

def test_extract_convert_wo_pil(self):
self.assert_files('icp4rgb.icns', [], [
'16x16-mask8b.bin', '16x16.rgb', '32x32-mask8b.bin', '32x32.rgb'])

@unittest.skipUnless(PIL_ENABLED, 'PIL_ENABLED == False')
def test_extract_convert_w_pil(self):
self.assert_files('icp4rgb.icns', ['-c'], ['16x16.png', '32x32.png'])


class TestCLI_compose(unittest.TestCase):
def setUp(self):
self.OUTFILE = 'tmp_cli_out_compose.icns'
if os.path.exists(self.OUTFILE):
os.remove(self.OUTFILE)

def tearDown(self):
os.remove(self.OUTFILE)

def test_ext_completion(self):
fname = self.OUTFILE[:-5] # remove '.icns'
run_cli(['c', fname, 'rgb.icns.png'])
os.path.exists(self.OUTFILE) # will append .icns to filename

def test_force(self):
args = ['c', self.OUTFILE, 'rgb.icns.png']
r = run_cli(args)
self.assertEqual(r.returncode, 0)
r = run_cli(args)
self.assertEqual(r.returncode, 1) # without force overwrite
self.assertTrue(b'force' in r.stderr.lower())
self.assertTrue(b'-f' in r.stderr)
r = run_cli(args + ['-f'])
self.assertEqual(r.returncode, 0) # with force overwrite

def assert_conv_file(self, fname, icns_key, arg=[]):
run_cli(['c', self.OUTFILE, fname] + arg)
s_orig = os.path.getsize(fname)
s_icns = os.path.getsize(self.OUTFILE)
is_icns = fname.endswith('.icns')
self.assertEqual(s_orig + (8 if is_icns else 16), s_icns)
with open(self.OUTFILE, 'rb') as fp:
key, size = RawData.icns_header_read(fp.read(8))
self.assertEqual(size, s_icns)
key, size = RawData.icns_header_read(fp.read(8))
self.assertEqual(size, s_orig + (0 if is_icns else 8))
self.assertEqual(key, icns_key)

def test_png(self):
self.assert_conv_file('rgb.icns.png', 'icp4')

def test_jp2(self):
self.assert_conv_file('256x256.jp2', 'ic08')
self.assert_conv_file('18x18.j2k', 'icsb', arg=['-f'])

def test_argb(self):
self.assert_conv_file('rgb.icns.argb', 'ic04')

def test_icns(self):
self.assert_conv_file('selected.icns', 'slct')

def test_rgb(self):
self.assert_conv_file('rgb.icns.rgb', 'is32')


class TestCLI_update(unittest.TestCase):
def setUp(self):
self.OUTFILE = 'tmp_cli_out_update.icns'
if os.path.exists(self.OUTFILE):
os.remove(self.OUTFILE)
shutil.copy('icp4rgb.icns', self.OUTFILE)

def tearDown(self):
os.remove(self.OUTFILE)

def test_different_outfile(self):
other_out = 'tmp_cli_out_update_other.icns'
run_cli(['u', '-o', other_out, self.OUTFILE, '-set', 'TOC=1'])
s1 = os.path.getsize(self.OUTFILE)
s2 = os.path.getsize('icp4rgb.icns')
self.assertEqual(s1, s2) # outfile should not be modified!
s3 = os.path.getsize(other_out)
self.assertNotEqual(s1, s3)
self.assertEqual(s3, s1 + (8 + 4 * 8))
os.remove(other_out)

def assertUpdate(self, args, expected_diff):
s1 = os.path.getsize(self.OUTFILE)
run_cli(['u', self.OUTFILE] + args)
s2 = os.path.getsize(self.OUTFILE)
self.assertEqual(s2, s1 + expected_diff)

def test_remove(self):
self.assertUpdate(['-rm', 's8mk', 'l8mk'], - (256 + 8 + 1024 + 8))

def test_add(self):
# dropping icns file header (-8) adding icns entry header (+8)
self.assertUpdate(['-set', 'selected=selected.icns'], 15587 - 8 + 8)

def test_rm_add(self):
self.assertUpdate(['-set', 'Toc', '-rm', 's8mk'], 8 + 3 * 8 - 256 - 8)

def test_unmodified(self):
self.assertUpdate(['-rm', 'toc', 'is32', 'it32', 'il32'], 0)


class TestCLI_print(unittest.TestCase):
def test_single(self):
ret = run_cli(['p', 'rgb.icns']).stdout
for x in [b'rgb.icns', b'ICN#', b'il32', b'l8mk', b'ics#', b'is32',
b's8mk', b'it32', b't8mk', b'16x16', b'32x32', b'128x128']:
self.assertTrue(x in ret)
for x in [b'icp4rgb.icns', b'icp4', b'icp5']:
self.assertFalse(x in ret)
self.assertFalse(b'offset' in ret)

def test_verbose(self):
ret = run_cli(['p', '-v', 'rgb.icns']).stdout
self.assertTrue(b'offset' in ret)

def test_multiple(self):
ret = run_cli(['p', 'rgb.icns', 'icp4rgb.icns']).stdout
for x in [b'rgb.icns', b'icp4rgb.icns', b'icp4', b'icp5']:
self.assertTrue(x in ret)
self.assertFalse(b'offset' in ret)


class TestCLI_verify(unittest.TestCase):
def test_ok(self):
ret = run_cli(['t', 'rgb.icns']).stdout
self.assertTrue(b'rgb.icns' in ret)
self.assertTrue(b'OK' in ret)

def test_multiple(self):
ret = run_cli(['t', 'rgb.icns', 'icp4rgb.icns']).stdout
self.assertTrue(b'rgb.icns' in ret)
self.assertTrue(b'icp4rgb.icns' in ret)
self.assertTrue(b'OK' in ret)

def test_fail(self):
ret = run_cli(['t', '18x18.j2k']).stdout
self.assertTrue(b'18x18.j2k' in ret)
self.assertTrue(b'Not an ICNS file' in ret)
self.assertFalse(b'OK' in ret)

def test_quiet(self):
ret = run_cli(['t', '-q', 'rgb.icns', 'icp4rgb.icns']).stdout
self.assertEqual(ret, b'')
ret = run_cli(['t', '-q', '18x18.j2k', 'rgb.icns']).stdout
self.assertTrue(b'18x18.j2k' in ret)
self.assertFalse(b'rgb.icns' in ret)
self.assertFalse(b'OK' in ret)


@unittest.skipUnless(PIL_ENABLED, 'PIL_ENABLED == False')
class TestCLI_convert(unittest.TestCase):
def assertConvert(self, source, ext):
dest = 'tmp_cli_out_convert.' + ext
run_cli(['img', dest, source]).stdout
self.assertTrue(os.path.exists(dest))
s = os.path.getsize(dest)
os.remove(dest)
return s

def test_to_png(self):
for expected_size, fname in [
(103, '18x18.j2k'),
(4778, '256x256.jp2'),
(813, 'rgb.icns.png'),
(813, 'rgb.icns.rgb'),
]:
size = self.assertConvert(fname, 'png')
self.assertEqual(size, expected_size)

def test_to_argb(self):
for expected_size, fname in [
(822, '18x18.j2k'),
(14865, '256x256.jp2'),
(713, 'rgb.icns.png'),
(713, 'rgb.icns.rgb'),
]:
size = self.assertConvert(fname, 'argb')
self.assertEqual(size, expected_size)

def test_to_rgb(self):
for expected_size, fname, expected_mask_size in [
(812, '18x18.j2k', 324),
(13851, '256x256.jp2', 65536),
(705, 'rgb.icns.png', 256),
(705, 'rgb.icns.rgb', 256),
]:
size = self.assertConvert(fname, 'rgb')
self.assertEqual(size, expected_size)
mask_size = os.path.getsize('tmp_cli_out_convert.rgb.mask')
self.assertEqual(mask_size, expected_mask_size)
os.remove('tmp_cli_out_convert.rgb.mask')

def test_without_dest_name(self):
src = 'tmp_cli_out_convert.jp2'
shutil.copy('18x18.j2k', src)
for ext, size in [('png', 103), ('argb', 822), ('rgb', 812)]:
run_cli(['img', ext, src]).stdout
self.assertTrue(os.path.exists(src + '.' + ext))
self.assertEqual(os.path.getsize(src + '.' + ext), size)
os.remove(src + '.' + ext)
os.remove(src)
# test if mask was created
self.assertTrue(os.path.exists(src + '.rgb.mask'))
self.assertEqual(os.path.getsize(src + '.rgb.mask'), 324)
os.remove(src + '.rgb.mask')


if __name__ == '__main__':
main()

0 comments on commit 478adfc

Please sign in to comment.