##########################################################################
#
# TestPackageList.py: test the PackageList module
#
#  Python-CDD is a library to make easier to build applications to
#  Custom Debian Distributions.
#  See http://projetos.ossystems.com.br/python-cdd for more information.
#
# ====================================================================
# Copyright (c) 2002-2005 O.S. Systems.	 All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution.
#
#########################################################################
# Authors: Otavio Salvador <otavio@ossystems.com.br>
#		   Marco Presi <zufus@debian.org>
#		   Free Ekanayaka <free@agnula.org>

import unittest

from TestBase		 import TestBase
from cdd.PackageList import *
from cdd			 import Package


class PackageListTests(TestBase):
	def __read_file (self, class_ref, filename):
		parse_in = open(filename, 'r')
		tagfile = apt_pkg.TagFile(parse_in)

		pkg_list = []
		for section in tagfile:
			pkg = class_ref(section)
			pkg_list.append(pkg)
		parse_in.close()

		return pkg_list

	def __create_list (self, filename):
		pkg_list = PackageList()
		packages = self.__read_file (Package.Package, filename)
		for package in packages:
			pkg_list.add(package)

		return pkg_list

	def test_1create_list (self):
		"""PackageList: initialization"""
		pkg_list = self.__create_list(self.aux_file('Packages'))
		self.failUnless (pkg_list.has_key('3dchess'),
						 "Failed to add a package to the list.")
		cppkg_list = PackageList(pkg_list)
		self.failUnless (cppkg_list.has_key('3dchess'),
						 "Failed to copy one list by another")

	def test_2items_list (self):
		"""PackageList: items"""
		pkg_list = self.__create_list(self.aux_file('Packages'))
		self.failUnlessEqual(pkg_list.items()[0][0], '3dchess',
							 "Items return invalid value")

	def test_2add_package (self):
		"""PackageList: check already exists exception"""
		pkg_list = self.__create_list(self.aux_file('Packages'))
		more = self.__read_file (Package.Package, self.aux_file('Packages'))
		# Adding to the list already existing packages generate
		self.failUnlessRaises(PackageAlreadyExists, pkg_list.add, more[0])

	def test_2get_package (self):
		"""PackageList: get a package by name"""
		pkg_list = self.__create_list(self.aux_file('Packages'))
		self.failUnlessRaises(PackageDoesNotExist, pkg_list.get, 'nonono')

	def test_3remove_package (self):
		"""PackageList: remove a package from the list"""
		pkg_list = self.__create_list(self.aux_file('Packages'))
		# Test to remove a non existing package (passing string)
		self.failUnlessRaises (PackageDoesNotExist, pkg_list.remove,
				   'fichissimo')

		# Test to remove a non existing package (passing package object)
		pkg_aux_list = self.__create_list(self.aux_file('Packages-a-b-c-d-e'))
		self.failUnlessRaises (PackageDoesNotExist,
				   pkg_list.remove, pkg_aux_list['a'])

		# Test remove
		initial_lenght = len(pkg_list)
		pkg_list.remove('3dchess')
		self.failIf (len(pkg_list) != (initial_lenght - 1),
					 "Returned an invalid size after removing a package.")
		pkg_list.remove('3ddesktop')
		self.failIf (len(pkg_list) != (initial_lenght - 2),
					 "Returned an invalid size after removing a package.")
		pkg_prov_list = self.__create_list(self.aux_file('Packages-a-b-c-d-e'))
		pkg_prov_list.remove('c')

	def test_3extend(self):
		"""PackageList: extend a list with packages form another"""
		pkg_list = self.__create_list(self.aux_file('Packages-a-d'))
		pkg_aux_list = self.__create_list(self.aux_file('Packages-a-b-c-d'))
		pkg_list.extend(pkg_aux_list)
		self.failUnlessEqual(len(pkg_list), 4,
							 "Extend has included wrong number of packages")

	def test_4filter_package (self):
		"""PackageList: add a package to list from a filter rule"""
		pkg_list = self.__create_list(self.aux_file('Packages'))
		pkg_backup = pkg_list
		np1 = pkg_list.filter({'priority' : 'important'})

		# There is only one package in section graphics
		self._checkPackageNames(np1, ["3dchess"])

		# Test filter by name.
		np1 = pkg_list.filter({'name':'3dc.*'})
		self._checkPackageNames(np1, ["3dchess"])

		# Test if filter was changed the orignal list.
		self.failUnlessEqual(len(pkg_backup), len(pkg_list),
							 "Filter has changed the list!")

		# Test Include-from.
		pkg_list2 = self.__create_list(self.aux_file('Packages-a-b-c-d-e'))
		pkg_backup2 = pkg_list2
		np2 = pkg_list2.filter({'include-from':self.aux_file('tasks')})
		self._checkPackageNames(np2, ["a", "e"])

		# Test Exclude-from.
		np3 = pkg_list2.filter({'exclude-from':self.aux_file('tasks')})
		self._checkPackageNames(np3, ["b", "c", "d"])

		# Test if filter was changed the original list.
		self.failUnless(len(pkg_backup2) == len(pkg_list2),
						"Filter has changed the list!")

		# Test filter by subsection.
		np4 = pkg_list2.filter({'subsection':'devel'})
		self._checkPackageNames(np4, ["a"])

		# Test if filter was changed the orignal list.
		self.failUnlessEqual(len(pkg_backup2), len(pkg_list2),
				 "Has changed the list!")

		# Test if was readded one package
		np5 = pkg_list.filter({'name':'3dc.*', 'subsection':'games'})

		# Test exceptions on filter
		self.failUnlessRaises(InvalidFilter, pkg_list.filter, {'invalid':'foo'})

		# Test wrong architecture
		self.failUnlessRaises(ValueError, pkg_list.filter,
				  {'include-from':self.aux_file('tasks')}, 'fooarch')

		# Test filter by negative subsection.
		got = pkg_list2.filter({'exclude-subsection':'devel', 'exclude-name': 'c'})
		self._checkPackageNames(got, ["b", "d", "e"])

		# Test filter by positive tag
		got = pkg_list2.filter({'field-Tag':'.*suite::kde'})
		self._checkPackageNames(got, ["a", "b"])

		# Test filter by negative tag
		got = pkg_list2.filter({'exclude-field-Tag':'.*suite::kde'})
		self._checkPackageNames(got, ["c", "d", "e"])


	def test_5depends_package (self):
		"""PackageList: A depends on B | C, D depends on C"""
		pkg_list = self.__create_list(self.aux_file('Packages-a-d'))
		pkg_list_full = self.__create_list(self.aux_file('Packages-a-b-c-d'))
		pkg_list.resolve_depends(pkg_list_full)
		self._checkPackageNames(pkg_list, ["a", "c", "d"])

	def test_5depends2_package (self):
		"""PackageList: A depends on B | C | D, E depends on D, C provides D"""
		pkg_list = self.__create_list(self.aux_file('Packages-a-e'))
		pkg_list_full = self.__create_list(self.aux_file('Packages-a-b-c-d-e'))
		pkg_list.resolve_depends(pkg_list_full)

		self._checkPackageNames(pkg_list, ["a", "e", "d"])

	def test_5depends_real_or_virtual (self):
		"""PackageList: A depends on B | virtualC, D provides virtualC"""
		pkg_list = self.__create_list(self.aux_file('Packages-real_or_virtual'))

		pkg_list_a_d = pkg_list.filter({'name':'a'})
		pkg_list_a_d.add(pkg_list['d'])
		pkg_list_a_d.resolve_depends(pkg_list)

		self._checkPackageNames(pkg_list_a_d, ["a", "d"])

	def test_5depends_real_or_virtual2 (self):
		"""PackageList: META depends on A and D, A depends on B | virtualC, D provides virtualC"""
		pkg_list = self.__create_list(self.aux_file('Packages-real_or_virtual'))

		pkg_list_selected = pkg_list.filter({'name':'meta'})
		pkg_list_selected.resolve_depends(pkg_list)

		self._checkPackageNames(pkg_list_selected, ["meta", "a", "d"])

	def test_6broken_dependencies (self):
		"""PackageList: broken dependencies"""
		pkg_list = self.__create_list(self.aux_file('BrokenPackages-a-b--d'))
		self.failUnlessRaises(BrokenDependencies, pkg_list.resolve_depends,
				  pkg_list)

		try:
			pkg_list.resolve_depends(pkg_list)
		except BrokenDependencies, e:
			self.failUnlessEqual(e.deps[0]["Package"], "d",
								 "Failed to return the broken package")
			self.failUnlessEqual(e.broken["d"], [[('c', '', '')]],
								 "Failed to return the broken dependencie group")

	def test_7unlisted_dependencies (self):
		"""PackageList: unlisted dependencies"""
		pkg_list = self.__create_list(self.aux_file('Packages-a-d'))
		pkg_list_full = self.__create_list(self.aux_file('Packages-a-b-c-d-e-f'))
		# d need c that need e, f | g"

		pkg_list.resolve_depends(pkg_list_full)
		self._checkPackageNames(pkg_list, ["a", "d", "c", "e", "f"])

	def test_8versioned_dependencies (self):
		"""PackageList: versioned dependencies"""
		pkg_list = self.__create_list(self.aux_file('Packages-a-d'))
		pkg_list_wrong_version = self.__create_list(self.aux_file\
										   ('Packages-versioned-dependencies'))
		pkg_list_right_version = self.__create_list(self.aux_file\
											('Packages-versioned-dependencies2'))
		pkg_list.resolve_depends([pkg_list_wrong_version,
								 pkg_list_right_version])

		self._checkPackageNames(pkg_list, ["a", "d", "c", "e", "f"])
		self.failUnlessEqual(pkg_list['f']['Version'], "0.3")

	def test_9whereis_dependencie(self):
		"""PackageList: whereis dependencie"""
		pkg_list = self.__create_list(self.aux_file('Packages-a-b-whereis-c'))
		pkg_list2 = self.__create_list(self.aux_file('Packages-hereis-c'))
		pkg_list.resolve_depends(pkg_list2)
		self.failUnlessEqual(pkg_list.whereis('c'), pkg_list2,
							 "Returned a invalid adress")

	def test_10invalid_depend_field (self):
		"""PackageList: invalid resolve_depends field"""
		pkg_list = self.__create_list(self.aux_file('Packages-a-d'))
		pkg_list_full = self.__create_list(self.aux_file('Packages-a-b-c-d'))

		pkg_list.resolve_depends(pkg_list_full)
		self.failUnlessRaises(SyntaxError, pkg_list.resolve_depends,
							  pkg_list_full, 'foo')

	def test_11orphan(self):
		"""PackageList: orphan denpendencies"""
		pkg_list = self.__create_list(self.aux_file('Packages-a-b-c-d-e'))
		self.failUnlessEqual(pkg_list.orphan_packages(), ['a', 'e'],
							 "Returned a wrong list of packages as orphaned")

		pkg_list2 = self.__create_list(self.aux_file\
									   ('Packages-a-b-c-d-e-virtualf'))
		self.failUnlessEqual(pkg_list2.orphan_packages(), ['a', 'e'],
							 "Returned a wrong list of packages as orphaned")

	def test_12whereis(self):
		"""PackageList: whereis a package?"""
		pkg_list = self.__create_list(self.aux_file('Packages'))
		self.failUnlessRaises(PackageDoesNotExist, pkg_list.whereis, 'nonono')
		pkg_list2 = self.__create_list(self.aux_file('Packages-a-b-c-d'))
		pkg_list.add(pkg_list2['a'], pkg_list2)
		self.failUnlessEqual(pkg_list.whereis('3ddesktop'), pkg_list,
							"Returned a invalid adress")
		self.failUnlessEqual(pkg_list.whereis('a'), pkg_list2,
							"Returned a invalid adress")


	def test_13virtual_looping (self):
		"""PackageList: virtual package looping"""
		pkg_list = self.__create_list(self.aux_file('VirtualPackagesLooping'))
		try:
			pkg_list.resolve_depends(pkg_list)
		except RuntimeError, e:
			self.fail("Raised a RuntimeError exception.", e)

	def _checkPackageNames(self, packages, expectedNames):
		expectedNames.sort()
		gotNames = packages.keys()
		gotNames.sort()
		self.failUnlessEqual(expectedNames, gotNames)


class VersionComparisonTests(unittest.TestCase):
	def test01(self):
		""" Simple """
		self.failUnless(allow_version("1.0", "1.1", "<"))
		self.failIf(allow_version("1.1", "1.1", "<"))
		self.failUnless(allow_version("1.0", "1.1", "<="))
		self.failUnless(allow_version("1.1", "1.1", "<="))

		self.failUnless(allow_version("2.1", "2.0", ">"))
		self.failIf(allow_version("2.1", "2.1", ">"))
		self.failUnless(allow_version("2.1", "2.0", ">="))
		self.failUnless(allow_version("2.1", "2.1", ">="))

		self.failUnless(allow_version("2.1", "2.1", "="))

	def test02(self):
		""" Multi digit numeric """
		self.failUnless(allow_version("1.9", "1.10", "<"))
		self.failIf(allow_version("1.10", "1.10", "<"))
		self.failUnless(allow_version("1.9", "1.10", "<="))
		self.failUnless(allow_version("1.10", "1.10", "<="))

		self.failUnless(allow_version("2.10", "2.9", ">"))
		self.failIf(allow_version("2.10", "2.10", ">"))
		self.failUnless(allow_version("2.10", "2.9", ">="))
		self.failUnless(allow_version("2.10", "2.10", ">="))

		self.failUnless(allow_version("2.10", "2.10", "="))

	def test03(self):
		""" with text """
		self.failUnless(allow_version("1.33-3", "1.40.8-2ubuntu2", "<"))

def suite():
	suite = unittest.TestSuite()

	suite.addTest(unittest.makeSuite(PackageListTests, 'test'))
	suite.addTest(unittest.makeSuite(VersionComparisonTests, 'test'))

	return suite


if __name__ == '__main__':
	log = logging.getLogger()
	log_handler = logging.FileHandler(sys.argv[0][:-3] + '.log')
	log.setLevel(logging.DEBUG)
	log.addHandler(log_handler)
	unittest.main()
