#!/usr/bin/env python2

import unittest
from pyvap import Store, UnsolvableError, UndeterminedError, Maybe
from pyvap.constraints import BitwiseOr
import pyvap.types

class TestBasics(unittest.TestCase):

	def setUp(self):
		self.store = Store()
		self.a = self.store.IntVar('a')
		self.b = self.store.IntVar('b')
		self.constraint = BitwiseOr(self.a, self.b)
		self.out = self.constraint.result
		self.out.name = 'out'

		self.testvalues = set()
		self.testvalues.update(range(-3,3))
		self.testvalues.update(range(-49, 50, 37))

	def test_type(self):
		self.assertIs(type(self.out), pyvap.types.IntVar)

	def test_attributes(self):
		self.assertEqual(self.constraint.operands, (self.a, self.b))
		self.assertIs(self.constraint.operands[0], self.a)
		self.assertIs(self.constraint.operands[1], self.b)

	def test_limits(self):
		default = self.store.IntVar()
		self.assertEqual(self.a.mini,   default.mini)
		self.assertEqual(self.a.maxi,   default.maxi)
		self.assertEqual(self.b.mini,   default.mini)
		self.assertEqual(self.b.maxi,   default.maxi)
		self.assertEqual(self.out.mini, default.mini)
		self.assertEqual(self.out.maxi, default.maxi)

	def test_different_stores_raises(self):
		extra = Store().IntVar('extra')
		with self.assertRaisesRegexp(ValueError, 'Different stores'):
			BitwiseOr(self.a, extra)
		with self.assertRaisesRegexp(ValueError, 'Different stores'):
			BitwiseOr(extra, self.a)

	def test_operator(self):
		out = self.a | self.b
		self.a.value = 10
		self.b.value = 7
		self.assertEqual(out.value, 15)

	def test_exact(self):
		for aval in self.testvalues:
			for bval in self.testvalues:
				outval = aval | bval

				# first one way...
				self.store._push()
				self.a.value = aval
				self.b.value = bval
				self.assertEqual(self.out.value, outval)
				self.store._pop()

				# then another.
				self.store._push()
				self.b.value = bval
				self.a.value = aval
				self.assertEqual(self.out.value, outval)
				self.store._pop()

	def test_raises(self):
		# an UnsolvableError must ALWAYS be raised when all values are
		# determined without fulfilling the constraint.
		for aval in self.testvalues:
			for bval in self.testvalues:
				for oval in (x for x in self.testvalues if x != aval | bval):
					self.store._push()
					self.a.value = aval
					self.b.value = bval
					with self.assertRaises(UnsolvableError):
						self.out.value = oval
					self.store._pop()

					self.store._push()
					self.out.value = oval
					with self.assertRaises(UnsolvableError):
						self.b.value = bval
						self.a.value = aval
					self.store._pop()

					self.store._push()
					self.out.value = oval
					with self.assertRaises(UnsolvableError):
						self.a.value = aval
						self.b.value = bval
					self.store._pop()

if __name__ == '__main__':
	unittest.main()
