text: # encoding: utf-8
  """
  This script tests utility functions.
  """
  
  import os
  import os.path
  import shutil
  import sys
  import tempfile
  
  try:
  	import unittest2 as unittest
  except ImportError:
  	import unittest
  
  from pathspec.util import iter_tree, RecursionError
  
  
  class IterTreeTest(unittest.TestCase):
  	"""
  	The ``IterTreeTest`` class tests `pathspec.util.iter_tree()`.
  	"""
  
  	def make_dirs(self, dirs):
  		"""
  		Create the specified directories.
  		"""
  		for dir in dirs:
  			os.mkdir(os.path.join(self.temp_dir, self.ospath(dir)))
  
  	def make_files(self, files):
  		"""
  		Create the specified files.
  		"""
  		for file in files:
  			self.mkfile(os.path.join(self.temp_dir, self.ospath(file)))
  
  	def make_links(self, links):
  		"""
  		Create the specified links.
  		"""
  		for link, node in links:
  			os.symlink(os.path.join(self.temp_dir, self.ospath(node)), os.path.join(self.temp_dir, self.ospath(link)))
  
  	@staticmethod
  	def mkfile(file):
  		"""
  		Creates an empty file.
  		"""
  		with open(file, 'wb'):
  			pass
  
  	@staticmethod
  	def ospath(path):
  		"""
  		Convert the POSIX path to a native OS path.
  		"""
  		return os.path.join(*path.split('/'))
  
  	def require_realpath(self):
  		"""
  		Skips the test if `os.path.realpath` does not properly support
  		symlinks.
  		"""
  		if self.broken_realpath:
  			raise unittest.SkipTest("`os.path.realpath` is broken.")
  
  	def require_symlink(self):
  		"""
  		Skips the test if `os.symlink` is not supported.
  		"""
  		if self.no_symlink:
  			raise unittest.SkipTest("`os.symlink` is not supported.")
  
  	def setUp(self):
  		"""
  		Called before each test.
  		"""
  		self.temp_dir = tempfile.mkdtemp()
  
  	def tearDown(self):
  		"""
  		Called after each test.
  		"""
  		shutil.rmtree(self.temp_dir)
  
  	def test_1_files(self):
  		"""
  		Tests to make sure all files are found.
  		"""
  		self.make_dirs([
  			'Empty',
  			'Dir',
  			'Dir/Inner',
  		])
  		self.make_files([
  			'a',
  			'b',
  			'Dir/c',
  			'Dir/d',
  			'Dir/Inner/e',
  			'Dir/Inner/f',
  		])
  		results = set(iter_tree(self.temp_dir))
  		self.assertEqual(results, set(map(self.ospath, [
  			'a',
  			'b',
  			'Dir/c',
  			'Dir/d',
  			'Dir/Inner/e',
  			'Dir/Inner/f',
  		])))
  
  	def test_2_0_check_symlink(self):
  		"""
  		Tests whether links can be created.
  		"""
  		# NOTE: Windows does not support `os.symlink` for Python 2. Windows
  		# Vista and greater supports `os.symlink` for Python 3.2+.
  		no_symlink = None
  		try:
  			file = os.path.join(self.temp_dir, 'file')
  			link = os.path.join(self.temp_dir, 'link')
  			self.mkfile(file)
  
  			try:
  				os.symlink(file, link)
  			except (AttributeError, NotImplementedError):
  				no_symlink = True
  				raise
  			no_symlink = False
  
  		finally:
  			self.__class__.no_symlink = no_symlink
  
  	def test_2_1_check_realpath(self):
  		"""
  		Tests whether `os.path.realpath` works properly with symlinks.
  		"""
  		# NOTE: Windows does not follow symlinks with `os.path.realpath`
  		# which is what we use to detect recursion. See <https://bugs.python.org/issue9949>
  		# for details.
  		broken_realpath = None
  		try:
  			self.require_symlink()
  			file = os.path.join(self.temp_dir, 'file')
  			link = os.path.join(self.temp_dir, 'link')
  			self.mkfile(file)
  			os.symlink(file, link)
  
  			try:
  				self.assertEqual(os.path.realpath(file), os.path.realpath(link))
  			except AssertionError:
  				broken_realpath = True
  				raise
  			broken_realpath = False
  
  		finally:
  			self.__class__.broken_realpath = broken_realpath
  
  	def test_2_2_links(self):
  		"""
  		Tests to make sure links to directories and files work.
  		"""
  		self.require_symlink()
  		self.make_dirs([
  			'Dir',
  		])
  		self.make_files([
  			'a',
  			'b',
  			'Dir/c',
  			'Dir/d',
  		])
  		self.make_links([
  			('ax', 'a'),
  			('bx', 'b'),
  			('Dir/cx', 'Dir/c'),
  			('Dir/dx', 'Dir/d'),
  		])
  		results = set(iter_tree(self.temp_dir))
  		self.assertEqual(results, set(map(self.ospath, [
  			'a',
  			'ax',
  			'b',
  			'bx',
  			'Dir/c',
  			'Dir/cx',
  			'Dir/d',
  			'Dir/dx',
  		])))
  
  	def test_2_3_sideways_links(self):
  		"""
  		Tests to make sure the same directory can be encountered multiple
  		times via links.
  		"""
  		self.require_symlink()
  		self.make_dirs([
  			'Dir',
  			'Dir/Target',
  		])
  		self.make_files([
  			'Dir/Target/file',
  		])
  		self.make_links([
  			('Ax', 'Dir'),
  			('Bx', 'Dir'),
  			('Cx', 'Dir/Target'),
  			('Dx', 'Dir/Target'),
  			('Dir/Ex', 'Dir/Target'),
  			('Dir/Fx', 'Dir/Target'),
  		])
  		results = set(iter_tree(self.temp_dir))
  		self.assertEqual(results, set(map(self.ospath, [
  			'Ax/Ex/file',
  			'Ax/Fx/file',
  			'Ax/Target/file',
  			'Bx/Ex/file',
  			'Bx/Fx/file',
  			'Bx/Target/file',
  			'Cx/file',
  			'Dx/file',
  			'Dir/Ex/file',
  			'Dir/Fx/file',
  			'Dir/Target/file',
  		])))
  
  	def test_2_4_recursive_links(self):
  		"""
  		Tests detection of recursive links.
  		"""
  		self.require_symlink()
  		self.require_realpath()
  		self.make_dirs([
  			'Dir',
  		])
  		self.make_files([
  			'Dir/file',
  		])
  		self.make_links([
  			('Dir/Self', 'Dir'),
  		])
  		with self.assertRaises(RecursionError) as context:
  			set(iter_tree(self.temp_dir))
  		self.assertEqual(context.exception.first_path, 'Dir')
  		self.assertEqual(context.exception.second_path, self.ospath('Dir/Self'))
  
  	def test_2_5_recursive_circular_links(self):
  		"""
  		Tests detection of recursion through circular links.
  		"""
  		self.require_symlink()
  		self.require_realpath()
  		self.make_dirs([
  			'A',
  			'B',
  			'C',
  		])
  		self.make_files([
  			'A/d',
  			'B/e',
  			'C/f',
  		])
  		self.make_links([
  			('A/Bx', 'B'),
  			('B/Cx', 'C'),
  			('C/Ax', 'A'),
  		])
  		with self.assertRaises(RecursionError) as context:
  			set(iter_tree(self.temp_dir))
  		self.assertIn(context.exception.first_path, ('A', 'B', 'C'))
  		self.assertEqual(context.exception.second_path, {
  			'A': self.ospath('A/Bx/Cx/Ax'),
  			'B': self.ospath('B/Cx/Ax/Bx'),
  			'C': self.ospath('C/Ax/Bx/Cx'),
  		}[context.exception.first_path])
  