1#!/usr/bin/env python
2
3from __future__ import print_function
4
5import glob
6import os
7import os.path
8import re
9import shutil
10import subprocess
11
12import util
13
14
15#
16# Utility Functions
17#
18
19def _file_offsets_for_archive(path, xsl_path):
20	subprocess.check_call(["xar", "--dump-toc=heap_toc.xml", "-f", path])
21	try:
22		offsets = subprocess.check_output(["xsltproc", os.path.realpath(xsl_path), "heap_toc.xml"])
23		matches = [re.match("^(.+)\s([^\s]+)$", offset) for offset in offsets.splitlines()]
24		offsets = [(match.groups()[0], int(match.groups()[1])) for match in matches]
25		return offsets
26	finally:
27		os.unlink("heap_toc.xml")
28
29#
30# Test Cases
31#
32
33def normal_heap(filename):
34	with util.directory_created("scratch") as directory:
35		shutil.copy("/bin/ls", os.path.join(directory, "ls"))
36		shutil.copy(os.path.join(directory, "ls"), os.path.join(directory, "foo"))
37		with util.chdir(directory):
38			with util.archive_created(os.path.join("..", "heap.xar"), ".") as path:
39				# Verify file offsets are as we expect
40				offsets = _file_offsets_for_archive(path, os.path.join("..", "heap1.xsl"))
41				(f1, o1) = offsets[0]
42				(f2, o2) = offsets[1]
43
44				assert o1 < o2, "offset for first file \"{f1}\" ({o1}) greater than or equal to offset for last file \"{f2}\" ({o2})".format(f1=f1, o1=o1, f2=f2, o2=o2)
45
46				# Make sure extraction goes all right
47				with util.directory_created("extracted") as extracted:
48					subprocess.check_call(["xar", "-x", "-f", path, "-C", extracted])
49
50def coalesce_heap(filename):
51	with util.directory_created("scratch") as directory:
52		shutil.copy("/bin/ls", os.path.join(directory, "ls"))
53		shutil.copy(os.path.join(directory, "ls"), os.path.join(directory, "foo"))
54		with util.chdir(directory):
55			with util.archive_created(os.path.join("..", "heap.xar"), ".", "--coalesce-heap") as path:
56				# Verify file offsets are as we expect
57				offsets = _file_offsets_for_archive(path, os.path.join("..", "heap1.xsl"))
58				(f1, o1) = offsets[0]
59				(f2, o2) = offsets[1]
60
61				# Make sure extraction goes all right
62				with util.directory_created("extracted") as extracted:
63					subprocess.check_call(["xar", "-x", "-f", path, "-C", extracted])
64
65TEST_CASES = (normal_heap, coalesce_heap)
66
67if __name__ == "__main__":
68	for case in TEST_CASES:
69		try:
70			case("{f}.xar".format(f=case.func_name))
71			print("PASSED: {f}".format(f=case.func_name))
72		except (AssertionError, IOError, subprocess.CalledProcessError):
73			import sys, os
74			print("FAILED: {f}".format(f=case.func_name))
75			sys.excepthook(*sys.exc_info())
76			print("")
77