1#!/usr/bin/env python
2
3from __future__ import print_function
4
5import os
6import os.path
7import subprocess
8
9import util
10
11#
12# Utility Functions
13#
14
15def _assert_same_inodes(path1, path2):
16	assert os.path.exists(path1), "missing hard-linked-file {p}".format(p=path1)
17	assert os.path.exists(path1), "missing hard-linked-file {p}".format(p=path2)
18	stat1 = os.lstat(path1)
19	stat2 = os.lstat(path2)
20	assert stat1.st_ino == stat2.st_ino, "inodes do not match for hard-linked files \"{p1}\" and \"{p2}\"".format(p1=path1, p2=path2)
21
22
23#
24# Test Cases
25#
26
27def hard_link_in_directory(filename):
28	with util.directory_created("hardlink_scratch") as directory:
29		util.touch(os.path.join(directory, "a"))
30		os.link(os.path.join(directory, "a"), os.path.join(directory, "b"))
31
32		with util.archive_created("hardlink_archive.xar", "hardlink_scratch") as path:
33			with util.directory_created("hardlink_extracted") as extracted:
34				subprocess.check_call(["xar", "-x", "-C", extracted, "-f", path])
35				_assert_same_inodes(os.path.join(extracted, "hardlink_scratch", "a"), os.path.join(extracted, "hardlink_scratch", "b"))
36
37def hard_link_in_cwd(filename):
38	with util.directory_created("hardlink_scratch") as directory:
39		with util.chdir(directory):
40			util.touch("a")
41			os.link("a", "b")
42			with util.archive_created(os.path.join("..", "hardlink_archive.xar"), ".") as path:
43				with util.directory_created(os.path.join("..", "hardlink_extracted")) as extracted:
44					subprocess.check_call(["xar", "-x", "-C", extracted, "-f", path])
45					_assert_same_inodes(os.path.join(extracted, "a"), os.path.join(extracted, "b"))
46
47def hard_link_identical_files(filename):
48	with util.directory_created("hardlink_scratch") as directory:
49		with open(os.path.join(directory, "a"), "w") as f:
50			f.write("1234samecontent")
51		with open(os.path.join(directory, "b"), "w") as f:
52			f.write("1234samecontent")
53		with util.archive_created("hardlink_archive.xar", "hardlink_scratch", "--link-same") as path:
54			with util.directory_created("hardlink_extracted") as extracted:
55				subprocess.check_call(["xar", "-x", "-C", extracted, "-f", path])
56				_assert_same_inodes(os.path.join(extracted, "hardlink_scratch", "a"), os.path.join(extracted, "hardlink_scratch", "b"))
57
58TEST_CASES = (hard_link_in_directory, hard_link_in_cwd, hard_link_identical_files)
59
60if __name__ == "__main__":
61	for case in TEST_CASES:
62		try:
63			case("{f}.xar".format(f=case.func_name))
64			print("PASSED: {f}".format(f=case.func_name))
65		except (AssertionError, IOError, subprocess.CalledProcessError):
66			import sys, os
67			print("FAILED: {f}".format(f=case.func_name))
68			sys.excepthook(*sys.exc_info())
69			print("")
70