""" Test tools module """ from __future__ import division, print_function import os from os.path import join as pjoin, dirname import stat import shutil from ..tools import (back_tick, unique_by_index, ensure_writable, chmod_perms, ensure_permissions, parse_install_name, zip2dir, dir2zip, find_package_dirs, cmp_contents, get_archs, lipo_fuse, replace_signature, validate_signature, add_rpath) from ..tmpdirs import InTemporaryDirectory from .pytest_tools import assert_true, assert_false, assert_equal, \ assert_raises DATA_PATH = pjoin(dirname(__file__), 'data') LIB32 = pjoin(DATA_PATH, 'liba32.dylib') LIB64 = pjoin(DATA_PATH, 'liba.dylib') LIBBOTH = pjoin(DATA_PATH, 'liba_both.dylib') LIB64A = pjoin(DATA_PATH, 'liba.a') ARCH_64 = frozenset(['x86_64']) ARCH_32 = frozenset(['i386']) ARCH_BOTH = ARCH_64 | ARCH_32 def test_back_tick(): cmd = 'python -c "print(\'Hello\')"' assert_equal(back_tick(cmd), "Hello") assert_equal(back_tick(cmd, ret_err=True), ("Hello", "")) assert_equal(back_tick(cmd, True, False), (b"Hello", b"")) cmd = 'python -c "raise ValueError()"' assert_raises(RuntimeError, back_tick, cmd) def test_uniqe_by_index(): assert_equal(unique_by_index([1, 2, 3, 4]), [1, 2, 3, 4]) assert_equal(unique_by_index([1, 2, 2, 4]), [1, 2, 4]) assert_equal(unique_by_index([4, 2, 2, 1]), [4, 2, 1]) def gen(): yield 4 yield 2 yield 2 yield 1 assert_equal(unique_by_index(gen()), [4, 2, 1]) def test_ensure_permissions(): # Test decorator to ensure permissions with InTemporaryDirectory(): # Write, set zero permissions sts = {} for fname, contents in (('test.read', 'A line\n'), ('test.write', 'B line')): with open(fname, 'wt') as fobj: fobj.write(contents) os.chmod(fname, 0) sts[fname] = chmod_perms(fname) def read_file(fname): with open(fname, 'rt') as fobj: contents = fobj.read() return contents fixed_read_file = ensure_permissions(stat.S_IRUSR)(read_file) non_read_file = ensure_permissions(stat.S_IWUSR)(read_file) def write_file(fname, contents): with open(fname, 'wt') as fobj: fobj.write(contents) fixed_write_file = ensure_permissions(stat.S_IWUSR)(write_file) non_write_file = ensure_permissions(stat.S_IRUSR)(write_file) # Read fails with default, no permissions assert_raises(IOError, read_file, 'test.read') # Write fails with default, no permissions assert_raises(IOError, write_file, 'test.write', 'continues') # Read fails with wrong permissions assert_raises(IOError, non_read_file, 'test.read') # Write fails with wrong permissions assert_raises(IOError, non_write_file, 'test.write', 'continues') # Read succeeds with fixed function assert_equal(fixed_read_file('test.read'), 'A line\n') # Write fails, no permissions assert_raises(IOError, non_write_file, 'test.write', 'continues') # Write succeeds with fixed function fixed_write_file('test.write', 'continues') assert_equal(fixed_read_file('test.write'), 'continues') # Permissions are as before for fname, st in sts.items(): assert_equal(chmod_perms(fname), st) def test_ensure_writable(): # Test ensure writable decorator with InTemporaryDirectory(): with open('test.bin', 'wt') as fobj: fobj.write('A line\n') # Set to user rw, else r os.chmod('test.bin', 0o644) st = os.stat('test.bin') @ensure_writable def foo(fname): pass foo('test.bin') assert_equal(os.stat('test.bin'), st) # No-one can write os.chmod('test.bin', 0o444) st = os.stat('test.bin') foo('test.bin') assert_equal(os.stat('test.bin'), st) def test_parse_install_name(): # otool on versions previous to Catalina line0 = ('/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore ' '(compatibility version 1.2.0, current version 1.11.0)') name, cpver, cuver = ( '/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore', '1.2.0', '1.11.0') assert parse_install_name(line0) == (name, cpver, cuver) # otool on Catalina line1 = ('/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore ' '(compatibility version 1.2.0, current version 1.11.0, weak)') assert parse_install_name(line1) == (name, cpver, cuver) def _write_file(filename, contents): with open(filename, 'wt') as fobj: fobj.write(contents) def test_zip2(): # Test utilities to unzip and zip up with InTemporaryDirectory(): os.mkdir('a_dir') os.mkdir('zips') _write_file(pjoin('a_dir', 'file1.txt'), 'File one') s_dir = pjoin('a_dir', 's_dir') os.mkdir(s_dir) _write_file(pjoin(s_dir, 'file2.txt'), 'File two') zip_fname = pjoin('zips', 'my.zip') dir2zip('a_dir', zip_fname) zip2dir(zip_fname, 'another_dir') assert_equal(os.listdir('another_dir'), ['file1.txt', 's_dir']) assert_equal(os.listdir(pjoin('another_dir', 's_dir')), ['file2.txt']) # Try zipping from a subdirectory, with a different extension dir2zip(s_dir, 'another.ext') # Remove original tree just to be sure shutil.rmtree('a_dir') zip2dir('another.ext', 'third_dir') assert_equal(os.listdir('third_dir'), ['file2.txt']) # Check permissions kept in zip unzip cycle os.mkdir('a_dir') permissions = stat.S_IRUSR | stat.S_IWGRP | stat.S_IXGRP fname = pjoin('a_dir', 'permitted_file') _write_file(fname, 'Some script or something') os.chmod(fname, permissions) dir2zip('a_dir', 'test.zip') zip2dir('test.zip', 'another_dir') out_fname = pjoin('another_dir', 'permitted_file') assert_equal(os.stat(out_fname).st_mode & 0o777, permissions) def test_find_package_dirs(): # Test utility for finding package directories with InTemporaryDirectory(): os.mkdir('to_test') a_dir = pjoin('to_test', 'a_dir') b_dir = pjoin('to_test', 'b_dir') c_dir = pjoin('to_test', 'c_dir') for dir in (a_dir, b_dir, c_dir): os.mkdir(dir) assert_equal(find_package_dirs('to_test'), set([])) _write_file(pjoin(a_dir, '__init__.py'), "# a package") assert_equal(find_package_dirs('to_test'), {a_dir}) _write_file(pjoin(c_dir, '__init__.py'), "# another package") assert_equal(find_package_dirs('to_test'), {a_dir, c_dir}) # Not recursive assert_equal(find_package_dirs('.'), set()) _write_file(pjoin('to_test', '__init__.py'), "# base package") # Also - strips '.' for current directory assert_equal(find_package_dirs('.'), {'to_test'}) def test_cmp_contents(): # Binary compare of filenames assert_true(cmp_contents(__file__, __file__)) with InTemporaryDirectory(): with open('first', 'wb') as fobj: fobj.write(b'abc\x00\x10\x13\x10') with open('second', 'wb') as fobj: fobj.write(b'abc\x00\x10\x13\x11') assert_false(cmp_contents('first', 'second')) with open('third', 'wb') as fobj: fobj.write(b'abc\x00\x10\x13\x10') assert_true(cmp_contents('first', 'third')) with open('fourth', 'wb') as fobj: fobj.write(b'abc\x00\x10\x13\x10\x00') assert_false(cmp_contents('first', 'fourth')) def test_get_archs_fuse(): # Test routine to get architecture types from file assert_equal(get_archs(LIB32), ARCH_32) assert_equal(get_archs(LIB64), ARCH_64) assert_equal(get_archs(LIB64A), ARCH_64) assert_equal(get_archs(LIBBOTH), ARCH_BOTH) assert_raises(RuntimeError, get_archs, 'not_a_file') with InTemporaryDirectory(): lipo_fuse(LIB32, LIB64, 'anotherlib') assert_equal(get_archs('anotherlib'), ARCH_BOTH) lipo_fuse(LIB64, LIB32, 'anotherlib') assert_equal(get_archs('anotherlib'), ARCH_BOTH) shutil.copyfile(LIB32, 'libcopy32') lipo_fuse('libcopy32', LIB64, 'anotherlib') assert_equal(get_archs('anotherlib'), ARCH_BOTH) assert_raises(RuntimeError, lipo_fuse, 'libcopy32', LIB32, 'yetanother') shutil.copyfile(LIB64, 'libcopy64') assert_raises(RuntimeError, lipo_fuse, 'libcopy64', LIB64, 'yetanother') def test_validate_signature(): # Fully test the validate_signature tool def check_signature(filename): """Raises RuntimeError if codesign can not verify the signature.""" back_tick(['codesign', '--verify', filename], raise_err=True) with InTemporaryDirectory(): # Copy a binary file to test with, any binary file would work shutil.copyfile(LIBBOTH, 'libcopy') # validate_signature does not add missing signatures validate_signature('libcopy') # codesign should raise an error (missing signature) assert_raises(RuntimeError, check_signature, 'libcopy') replace_signature('libcopy', '-') # Force this file to be signed validate_signature('libcopy') # Cover the `is already valid` code path check_signature('libcopy') # codesign now accepts the file # Alter the contents of this file, this will invalidate the signature add_rpath('libcopy', '/dummy/path') # codesign should raise a new error (invalid signature) assert_raises(RuntimeError, check_signature, 'libcopy') validate_signature('libcopy') # Replace the broken signature check_signature('libcopy')