1#!/usr/bin/env python 2 3from __future__ import print_function 4 5import os 6import os.path 7import shutil 8import subprocess 9import xattr 10 11import util 12 13# Notes: 14# * Due to internal buffer sizing, "large" should be at least 4096 bytes for 15# uncompressed archives and archives with individual file compression, and 16# and at least 32768 bytes for archives with compressed heaps. 17# * For large files, we intentionally use a non-base-2 file size to ensure we 18# catch file extraction bugs for files with a size not equal to the internal 19# read buffer size (which is base-2). 20# * For large extended attributes, we do the same thing. 21 22# 23# Utility Functions 24# 25 26class MissingExtendedAttributeError(AssertionError): 27 pass 28 29def _random_big_data(bytes=65536, path="/dev/random"): 30 """ 31 Returns a random string with the number of bytes requested. Due to xar 32 implementation details, this should be greater than 4096 (32768 for 33 compressed heap testing). 34 35 """ 36 with open(path, "r") as f: 37 return f.read(bytes) 38 39def _test_xattr_on_file_with_contents(filename, file_contents, xattrs=[], xar_create_flags=[], xar_extract_flags=[]): 40 try: 41 # Write file out 42 with open(filename, "w") as f: 43 f.write(file_contents) 44 for (key, value) in xattrs: 45 xattr.setxattr(f, key, value) 46 47 # Store it into a xarchive 48 archive_name = "{f}.xar".format(f=filename) 49 with util.archive_created(archive_name, filename, *xar_create_flags) as path: 50 # Extract xarchive 51 with util.directory_created("extracted") as directory: 52 # Validate resulting xattrs 53 subprocess.check_call(["xar", "-x", "-C", directory, "-f", path] + xar_extract_flags) 54 for (key, value) in xattrs: 55 try: 56 assert xattr.getxattr(os.path.join(directory, filename), key) == value, "extended attribute \"{n}\" has incorrect contents after extraction".format(n=key) 57 except KeyError: 58 raise MissingExtendedAttributeError("extended attribute \"{n}\" missing after extraction".format(n=key)) 59 60 # Validate file contents 61 with open(os.path.join(directory, filename), "r") as f: 62 if f.read() != file_contents: 63 raise MissingExtendedAttributeError("archived file \"{f}\" has has incorrect contents after extraction".format(f=filename)) 64 finally: 65 os.unlink(filename) 66 67 68# 69# Test Cases 70# 71 72# Note: xar currently drops empty extended attributes (and any xar_prop_t with empty contents, actually). The empty 73# tests are commented out awaiting a day when this might be different. 74 75# def empty_xattr_empty_file(filename): 76# _test_xattr_on_file_with_contents(filename, "", xattrs=[("foo", "")]) 77 78def small_xattr_empty_file(filename): 79 _test_xattr_on_file_with_contents(filename, "", xattrs=[("foo", "1234")]) 80 81def large_xattr_empty_file(filename): 82 _test_xattr_on_file_with_contents(filename, "", xattrs=[("foo", _random_big_data(5000))]) 83 84# def empty_xattr_small_file(filename): 85# _test_xattr_on_file_with_contents(filename, "small.file.contents", xattrs=[("foo", "")]) 86 87def small_xattr_small_file(filename): 88 _test_xattr_on_file_with_contents(filename, "small.file.contents", xattrs=[("foo", "1234")]) 89 90def large_xattr_small_file(filename): 91 _test_xattr_on_file_with_contents(filename, "small.file.contents", xattrs=[("foo", _random_big_data(4567))]) 92 93# def empty_xattr_large_file(filename): 94# _test_xattr_on_file_with_contents(filename, _random_big_data(10000000), xattrs=[("foo", "")]) 95 96def small_xattr_large_file(filename): 97 _test_xattr_on_file_with_contents(filename, _random_big_data(5000000), xattrs=[("foo", "1234")]) 98 99def large_xattr_large_file(filename): 100 _test_xattr_on_file_with_contents(filename, _random_big_data(9876543), xattrs=[("foo", _random_big_data(6543))]) 101 102def multiple_xattrs(filename): 103 _test_xattr_on_file_with_contents(filename, "", xattrs=[("foo", "bar"), ("baz", "1234"), ("quux", "more")]) # ("empty", "") 104 105def distribution_create(filename): 106 try: 107 _test_xattr_on_file_with_contents(filename, "dummy", xattrs=[("foo", "bar")], xar_create_flags=["--distribution"]) 108 except MissingExtendedAttributeError: 109 pass 110 else: 111 raise AssertionError("no error encountered") 112 113# Note: xar currently has no way to exclude a property on extraction. This test is commented out awaiting the day 114# when it can. 115 116# def distribution_extract(filename): 117# try: 118# _test_xattr_on_file_with_contents(filename, "dummy", xattrs=[("foo", "bar")], xar_extract_flags=["--distribution"]) 119# except MissingExtendedAttributeError: 120# pass 121# else: 122# raise AssertionError("no error encountered") 123 124 125TEST_CASES = (small_xattr_empty_file, large_xattr_empty_file, 126 small_xattr_small_file, large_xattr_small_file, 127 small_xattr_large_file, large_xattr_large_file, 128 multiple_xattrs, distribution_create) 129 130if __name__ == "__main__": 131 for case in TEST_CASES: 132 try: 133 case(case.func_name) 134 print("PASSED: {f}".format(f=case.func_name)) 135 except (AssertionError, IOError, subprocess.CalledProcessError): 136 import sys, os 137 print("FAILED: {f}".format(f=case.func_name)) 138 sys.excepthook(*sys.exc_info()) 139 print("") 140