#!/usr/bin/env python2.4
from basetest import BaseTest
import sys, tempfile, os, shutil
from StringIO import StringIO
import unittest
from logging import getLogger, DEBUG, INFO
#getLogger().setLevel(DEBUG)

sys.path.insert(0, '..')

from zeroinstall.zerostore import Store, manifest, BadDigest, cli
from zeroinstall import SafeException, support

class TestStore(BaseTest):
	def setUp(self):
		BaseTest.setUp(self)

		self.store_parent = tempfile.mktemp()
		os.mkdir(self.store_parent, 0700)
		self.store = Store(self.store_parent + '/implementations')
		os.mkdir(self.store.dir, 0700)

		self.tmp = tempfile.mktemp()
		os.mkdir(self.tmp)
	
	def tearDown(self):
		BaseTest.tearDown(self)

		support.ro_rmtree(self.store_parent)
		support.ro_rmtree(self.tmp)
	
	def testInit(self):
		assert os.path.isdir(self.store.dir)
		self.assertEquals([], os.listdir(self.store.dir))

	def testEmptyManifest(self):
		lines = list(manifest.generate_manifest(self.tmp))
		self.assertEquals([], lines)

	def testSimpleManifest(self):
		path = os.path.join(self.tmp, 'MyFile')
		f = file(path, 'w')
		f.write('Hello')
		f.close()
		os.utime(path, (1, 2))
		lines = list(manifest.generate_manifest(self.tmp))
		self.assertEquals(['F f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0 2 5 MyFile'],
				lines)

	def testLinkManifest(self):
		path = os.path.join(self.tmp, 'MyLink')
		os.symlink('Hello', path)
		lines = list(manifest.generate_manifest(self.tmp))
		self.assertEquals(['S f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0 5 MyLink'],
				lines)

	def testVerify(self):
		path = os.path.join(self.tmp, 'MyLink')
		os.symlink('Hello', path)
		mfile = os.path.join(self.tmp, '.manifest')
		for alg_name in ['sha1', 'sha256', 'sha1new']:
			try:
				alg = manifest.get_algorithm(alg_name)
				added_digest = alg.getID(manifest.add_manifest_file(self.tmp, alg))
				digest = alg.new_digest()
				digest.update('Hello')
				self.assertEquals("S %s 5 MyLink\n" % digest.hexdigest(),
						file(mfile).read())
				manifest.verify(self.tmp, added_digest)
				os.chmod(self.tmp, 0700)
				os.unlink(mfile)
			except BadDigest, ex:
				raise Exception("%s: %s\n%s" % (alg_name, ex, ex.detail))
	
	def populate_sample(self, target):
		"""Create a set of files, links and directories in target for testing."""
		path = os.path.join(target, 'MyFile')
		f = file(path, 'w')
		f.write('Hello')
		f.close()
		os.utime(path, (1, 2))

		subdir = os.path.join(target, 'My Dir')
		os.mkdir(subdir)

		subfile = os.path.join(subdir, '!a file!')
		f = file(subfile, 'w')
		f.write('Some data.')
		f.close()
		os.utime(subfile, (1, 2))

		subfile += '.exe'
		f = file(subfile, 'w')
		f.write('Some code.')
		f.close()
		os.chmod(subfile, 0500)
		os.utime(subfile, (1, 2))

		os.symlink('/the/symlink/target',
			   os.path.join(target, 'a symlink'))
	
	def testOptimise(self):
		sample = os.path.join(self.tmp, 'sample')
		os.mkdir(sample)
		self.populate_sample(sample)
		self.store.add_dir_to_cache('sha1new=7e3eb25a072988f164bae24d33af69c1814eb99a',
					   sample,
					   try_helper = False)
		subfile = os.path.join(sample, 'My Dir', '!a file!.exe')
		mtime = os.stat(subfile).st_mtime
		os.chmod(subfile, 0755)
		stream = file(subfile, 'w')
		stream.write('Extra!\n')
		stream.close()
		os.utime(subfile, (mtime, mtime))
		self.store.add_dir_to_cache('sha1new=40861a33dba4e7c26d37505bd9693511808c0c35',
					   sample,
					   try_helper = False)

		impl_a = self.store.lookup('sha1new=7e3eb25a072988f164bae24d33af69c1814eb99a')
		impl_b = self.store.lookup('sha1new=40861a33dba4e7c26d37505bd9693511808c0c35')

		def same_inode(name):
			info_a = os.lstat(os.path.join(impl_a, name))
			info_b = os.lstat(os.path.join(impl_b, name))
			return info_a.st_ino == info_b.st_ino

		assert not same_inode('My Dir/!a file!')
		assert not same_inode('My Dir/!a file!.exe')

		old_stdout = sys.stdout
		sys.stdout = StringIO()
		try:
			cli.do_optimise([self.store.dir])
			got = sys.stdout.getvalue()
		finally:
			sys.stdout = old_stdout
		assert 'Space freed up : 15 bytes' in got

		old_stdout = sys.stdout
		sys.stdout = StringIO()
		try:
			cli.do_optimise([self.store.dir])
			got = sys.stdout.getvalue()
		finally:
			sys.stdout = old_stdout
		assert 'No duplicates found; no changes made.' in got

		assert same_inode('My Dir/!a file!')
		assert not same_inode('My Dir/!a file!.exe')
	
	def testCopy(self):
		sha1 = manifest.get_algorithm('sha1')
		sha1new = manifest.get_algorithm('sha1new')
		source = os.path.join(self.tmp, 'badname')
		os.mkdir(source)

		self.populate_sample(source)

		lines = list(sha1new.generate_manifest(source))
		self.assertEquals(['F f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0 2 5 MyFile',
				   'S 570b0ce957ab43e774c82fca0ea3873fc452278b 19 a symlink',
				   'D /My Dir',
				   'F 0236ef92e1e37c57f0eb161e7e2f8b6a8face705 2 10 !a file!',
				   'X b4ab02f2c791596a980fd35f51f5d92ee0b4705c 2 10 !a file!.exe'],
				lines)
		digest = sha1.getID(manifest.add_manifest_file(source, sha1))

		copy = tempfile.mktemp()
		os.mkdir(copy)
		try:
			# Source must be in the form alg=value
			try:
				cli.do_copy([source, copy])
				assert 0
			except BadDigest, ex:
				assert 'badname' in str(ex)
			source, badname = os.path.join(self.tmp, digest), source
			os.rename(badname, source)

			# Can't copy sha1 implementations (unsafe)
			try:
				cli.do_copy([source, copy])
			except SafeException, ex:
				assert 'sha1' in str(ex)

			# Already have a .manifest
			try:
				manifest.add_manifest_file(source, sha1new)
				assert 0
			except SafeException, ex:
				assert '.manifest' in str(ex)

			os.chmod(source, 0700)
			os.unlink(os.path.join(source, '.manifest'))

			# Switch to sha1new
			digest = sha1new.getID(manifest.add_manifest_file(source, sha1new))
			source, badname = os.path.join(self.tmp, digest), source
			os.rename(badname, source)

			cli.do_copy([source, copy])

			self.assertEquals('Hello', file(os.path.join(copy, digest, 'MyFile')).read())
		finally:
			support.ro_rmtree(copy)

suite = unittest.makeSuite(TestStore)
if __name__ == '__main__':
	sys.argv.append('-v')
	unittest.main()


syntax highlighted by Code2HTML, v. 0.9.1