from __future__ import absolute_import import pickle import six from django.db import connection, models from django.db.models import F from django.test import TestCase from bitfield import Bit, BitField, BitHandler from bitfield.compat import bitand, bitor from .forms import BitFieldTestModelForm from .models import BitFieldTestModel class BitHandlerTest(TestCase): def test_comparison(self): bithandler_1 = BitHandler(0, ("FLAG_0", "FLAG_1", "FLAG_2", "FLAG_3")) bithandler_2 = BitHandler(1, ("FLAG_0", "FLAG_1", "FLAG_2", "FLAG_3")) bithandler_3 = BitHandler(0, ("FLAG_0", "FLAG_1", "FLAG_2", "FLAG_3")) assert bithandler_1 == bithandler_1 assert bithandler_1 != bithandler_2 assert bithandler_1 == bithandler_3 def test_defaults(self): bithandler = BitHandler(0, ("FLAG_0", "FLAG_1", "FLAG_2", "FLAG_3")) # Default value of 0. self.assertEquals(int(bithandler), 0) # Test bit numbers. self.assertEquals(int(bithandler.FLAG_0.number), 0) self.assertEquals(int(bithandler.FLAG_1.number), 1) self.assertEquals(int(bithandler.FLAG_2.number), 2) self.assertEquals(int(bithandler.FLAG_3.number), 3) # Negative test non-existant key. self.assertRaises(AttributeError, lambda: bithandler.FLAG_4) # Test bool(). self.assertEquals(bool(bithandler.FLAG_0), False) self.assertEquals(bool(bithandler.FLAG_1), False) self.assertEquals(bool(bithandler.FLAG_2), False) self.assertEquals(bool(bithandler.FLAG_3), False) def test_nonzero_default(self): bithandler = BitHandler(1, ("FLAG_0", "FLAG_1", "FLAG_2", "FLAG_3")) self.assertEquals(bool(bithandler.FLAG_0), True) self.assertEquals(bool(bithandler.FLAG_1), False) self.assertEquals(bool(bithandler.FLAG_2), False) self.assertEquals(bool(bithandler.FLAG_3), False) bithandler = BitHandler(2, ("FLAG_0", "FLAG_1", "FLAG_2", "FLAG_3")) self.assertEquals(bool(bithandler.FLAG_0), False) self.assertEquals(bool(bithandler.FLAG_1), True) self.assertEquals(bool(bithandler.FLAG_2), False) self.assertEquals(bool(bithandler.FLAG_3), False) bithandler = BitHandler(3, ("FLAG_0", "FLAG_1", "FLAG_2", "FLAG_3")) self.assertEquals(bool(bithandler.FLAG_0), True) self.assertEquals(bool(bithandler.FLAG_1), True) self.assertEquals(bool(bithandler.FLAG_2), False) self.assertEquals(bool(bithandler.FLAG_3), False) bithandler = BitHandler(4, ("FLAG_0", "FLAG_1", "FLAG_2", "FLAG_3")) self.assertEquals(bool(bithandler.FLAG_0), False) self.assertEquals(bool(bithandler.FLAG_1), False) self.assertEquals(bool(bithandler.FLAG_2), True) self.assertEquals(bool(bithandler.FLAG_3), False) def test_mutation(self): bithandler = BitHandler(0, ("FLAG_0", "FLAG_1", "FLAG_2", "FLAG_3")) self.assertEquals(bool(bithandler.FLAG_0), False) self.assertEquals(bool(bithandler.FLAG_1), False) self.assertEquals(bool(bithandler.FLAG_2), False) self.assertEquals(bool(bithandler.FLAG_3), False) bithandler = BitHandler(bithandler | 1, bithandler._keys) self.assertEquals(bool(bithandler.FLAG_0), True) self.assertEquals(bool(bithandler.FLAG_1), False) self.assertEquals(bool(bithandler.FLAG_2), False) self.assertEquals(bool(bithandler.FLAG_3), False) bithandler ^= 3 self.assertEquals(int(bithandler), 2) self.assertEquals(bool(bithandler & 1), False) bithandler.FLAG_0 = False self.assertEquals(bithandler.FLAG_0, False) bithandler.FLAG_1 = True self.assertEquals(bithandler.FLAG_0, False) self.assertEquals(bithandler.FLAG_1, True) bithandler.FLAG_2 = False self.assertEquals(bithandler.FLAG_0, False) self.assertEquals(bithandler.FLAG_1, True) self.assertEquals(bithandler.FLAG_2, False) class BitTest(TestCase): def test_int(self): bit = Bit(0) self.assertEquals(int(bit), 1) self.assertEquals(bool(bit), True) self.assertFalse(not bit) def test_comparison(self): self.assertEquals(Bit(0), Bit(0)) self.assertNotEquals(Bit(1), Bit(0)) self.assertNotEquals(Bit(0, 0), Bit(0, 1)) self.assertEquals(Bit(0, 1), Bit(0, 1)) self.assertEquals(Bit(0), 1) def test_and(self): self.assertEquals(1 & Bit(2), 0) self.assertEquals(1 & Bit(0), 1) self.assertEquals(1 & ~Bit(0), 0) self.assertEquals(Bit(0) & Bit(2), 0) self.assertEquals(Bit(0) & Bit(0), 1) self.assertEquals(Bit(0) & ~Bit(0), 0) def test_or(self): self.assertEquals(1 | Bit(2), 5) self.assertEquals(1 | Bit(5), 33) self.assertEquals(1 | ~Bit(2), -5) self.assertEquals(Bit(0) | Bit(2), 5) self.assertEquals(Bit(0) | Bit(5), 33) self.assertEquals(Bit(0) | ~Bit(2), -5) def test_xor(self): self.assertEquals(1 ^ Bit(2), 5) self.assertEquals(1 ^ Bit(0), 0) self.assertEquals(1 ^ Bit(1), 3) self.assertEquals(1 ^ Bit(5), 33) self.assertEquals(1 ^ ~Bit(2), -6) self.assertEquals(Bit(0) ^ Bit(2), 5) self.assertEquals(Bit(0) ^ Bit(0), 0) self.assertEquals(Bit(0) ^ Bit(1), 3) self.assertEquals(Bit(0) ^ Bit(5), 33) self.assertEquals(Bit(0) ^ ~Bit(2), -6) class BitFieldTest(TestCase): def test_basic(self): # Create instance and make sure flags are working properly. instance = BitFieldTestModel.objects.create(flags=1) self.assertTrue(instance.flags.FLAG_0) self.assertFalse(instance.flags.FLAG_1) self.assertFalse(instance.flags.FLAG_2) self.assertFalse(instance.flags.FLAG_3) def test_regression_1425(self): # Creating new instances shouldn't allow negative values. instance = BitFieldTestModel.objects.create(flags=-1) self.assertEqual(instance.flags._value, 15) self.assertTrue(instance.flags.FLAG_0) self.assertTrue(instance.flags.FLAG_1) self.assertTrue(instance.flags.FLAG_2) self.assertTrue(instance.flags.FLAG_3) cursor = connection.cursor() flags_field = BitFieldTestModel._meta.get_field("flags") flags_db_column = flags_field.db_column or flags_field.name cursor.execute( "INSERT INTO %s (%s) VALUES (-1)" % (BitFieldTestModel._meta.db_table, flags_db_column) ) # There should only be the one row we inserted through the cursor. instance = BitFieldTestModel.objects.get(flags=-1) self.assertTrue(instance.flags.FLAG_0) self.assertTrue(instance.flags.FLAG_1) self.assertTrue(instance.flags.FLAG_2) self.assertTrue(instance.flags.FLAG_3) instance.save() self.assertEqual(BitFieldTestModel.objects.filter(flags=15).count(), 2) self.assertEqual(BitFieldTestModel.objects.filter(flags__lt=0).count(), 0) def test_select(self): BitFieldTestModel.objects.create(flags=3) self.assertTrue( BitFieldTestModel.objects.filter(flags=BitFieldTestModel.flags.FLAG_1).exists() ) self.assertTrue( BitFieldTestModel.objects.filter(flags=BitFieldTestModel.flags.FLAG_0).exists() ) self.assertFalse( BitFieldTestModel.objects.exclude(flags=BitFieldTestModel.flags.FLAG_0).exists() ) self.assertFalse( BitFieldTestModel.objects.exclude(flags=BitFieldTestModel.flags.FLAG_1).exists() ) def test_update(self): instance = BitFieldTestModel.objects.create(flags=0) self.assertFalse(instance.flags.FLAG_0) BitFieldTestModel.objects.filter(pk=instance.pk).update( flags=bitor(F("flags"), BitFieldTestModel.flags.FLAG_1) ) instance = BitFieldTestModel.objects.get(pk=instance.pk) self.assertTrue(instance.flags.FLAG_1) BitFieldTestModel.objects.filter(pk=instance.pk).update( flags=bitor( F("flags"), ((~BitFieldTestModel.flags.FLAG_0 | BitFieldTestModel.flags.FLAG_3)) ) ) instance = BitFieldTestModel.objects.get(pk=instance.pk) self.assertFalse(instance.flags.FLAG_0) self.assertTrue(instance.flags.FLAG_1) self.assertTrue(instance.flags.FLAG_3) self.assertFalse( BitFieldTestModel.objects.filter(flags=BitFieldTestModel.flags.FLAG_0).exists() ) BitFieldTestModel.objects.filter(pk=instance.pk).update( flags=bitand(F("flags"), ~BitFieldTestModel.flags.FLAG_3) ) instance = BitFieldTestModel.objects.get(pk=instance.pk) self.assertFalse(instance.flags.FLAG_0) self.assertTrue(instance.flags.FLAG_1) self.assertFalse(instance.flags.FLAG_3) def test_update_with_handler(self): instance = BitFieldTestModel.objects.create(flags=0) self.assertFalse(instance.flags.FLAG_0) instance.flags.FLAG_1 = True BitFieldTestModel.objects.filter(pk=instance.pk).update( flags=bitor(F("flags"), instance.flags) ) instance = BitFieldTestModel.objects.get(pk=instance.pk) self.assertTrue(instance.flags.FLAG_1) def test_negate(self): BitFieldTestModel.objects.create( flags=BitFieldTestModel.flags.FLAG_0 | BitFieldTestModel.flags.FLAG_1 ) BitFieldTestModel.objects.create(flags=BitFieldTestModel.flags.FLAG_1) self.assertEqual( BitFieldTestModel.objects.filter(flags=~BitFieldTestModel.flags.FLAG_0).count(), 1 ) self.assertEqual( BitFieldTestModel.objects.filter(flags=~BitFieldTestModel.flags.FLAG_1).count(), 0 ) self.assertEqual( BitFieldTestModel.objects.filter(flags=~BitFieldTestModel.flags.FLAG_2).count(), 2 ) def test_default_value(self): instance = BitFieldTestModel.objects.create() self.assertTrue(instance.flags.FLAG_0) self.assertTrue(instance.flags.FLAG_1) self.assertFalse(instance.flags.FLAG_2) self.assertFalse(instance.flags.FLAG_3) def test_binary_capacity(self): import math from django.db.models.fields import BigIntegerField # Local maximum value, slow canonical algorithm MAX_COUNT = int(math.floor(math.log(BigIntegerField.MAX_BIGINT, 2))) # Big flags list flags = ["f" + six.text_type(i) for i in range(100)] try: BitField(flags=flags[:MAX_COUNT]) except ValueError: self.fail("It should work well with these flags") self.assertRaises(ValueError, BitField, flags=flags[: (MAX_COUNT + 1)]) def test_dictionary_init(self): flags = { 0: "zero", 1: "first", 10: "tenth", 2: "second", "wrongkey": "wrongkey", 100: "bigkey", -100: "smallkey", } try: bf = BitField(flags) except ValueError: self.fail("It should work well with these flags") self.assertEquals( bf.flags, ["zero", "first", "second", "", "", "", "", "", "", "", "tenth"] ) self.assertRaises(ValueError, BitField, flags={}) self.assertRaises(ValueError, BitField, flags={"wrongkey": "wrongkey"}) self.assertRaises(ValueError, BitField, flags={"1": "non_int_key"}) def test_defaults_as_key_names(self): class TestModel(models.Model): flags = BitField( flags=("FLAG_0", "FLAG_1", "FLAG_2", "FLAG_3"), default=("FLAG_1", "FLAG_2") ) field = TestModel._meta.get_field("flags") self.assertEquals(field.default, TestModel.flags.FLAG_1 | TestModel.flags.FLAG_2) class BitFieldSerializationTest(TestCase): def test_can_unserialize_bithandler(self): bf = BitFieldTestModel() bf.flags.FLAG_0 = 1 bf.flags.FLAG_1 = 0 data = pickle.dumps(bf) inst = pickle.loads(data) self.assertTrue(inst.flags.FLAG_0) self.assertFalse(inst.flags.FLAG_1) def test_pickle_integration(self): inst = BitFieldTestModel.objects.create(flags=1) data = pickle.dumps(inst) inst = pickle.loads(data) self.assertEquals(type(inst.flags), BitHandler) self.assertEquals(int(inst.flags), 1) def test_added_field(self): bf = BitFieldTestModel() bf.flags.FLAG_0 = 1 bf.flags.FLAG_1 = 0 bf.flags.FLAG_3 = 0 data = pickle.dumps(bf) inst = pickle.loads(data) self.assertTrue("FLAG_3" in inst.flags.keys()) class BitFormFieldTest(TestCase): def test_form_new_invalid(self): invalid_data_dicts = [ {"flags": ["FLAG_0", "FLAG_FLAG"]}, {"flags": ["FLAG_4"]}, {"flags": [1, 2]}, ] for invalid_data in invalid_data_dicts: form = BitFieldTestModelForm(data=invalid_data) self.assertFalse(form.is_valid()) def test_form_new(self): data_dicts = [{"flags": ["FLAG_0", "FLAG_1"]}, {"flags": ["FLAG_3"]}, {"flags": []}, {}] for data in data_dicts: form = BitFieldTestModelForm(data=data) self.failUnless(form.is_valid()) instance = form.save() flags = data["flags"] if "flags" in data else [] for k in BitFieldTestModel.flags: self.assertEquals(bool(getattr(instance.flags, k)), k in flags) def test_form_update(self): instance = BitFieldTestModel.objects.create(flags=0) for k in BitFieldTestModel.flags: self.assertFalse(bool(getattr(instance.flags, k))) data = {"flags": ["FLAG_0", "FLAG_1"]} form = BitFieldTestModelForm(data=data, instance=instance) self.failUnless(form.is_valid()) instance = form.save() for k in BitFieldTestModel.flags: self.assertEquals(bool(getattr(instance.flags, k)), k in data["flags"]) data = {"flags": ["FLAG_2", "FLAG_3"]} form = BitFieldTestModelForm(data=data, instance=instance) self.failUnless(form.is_valid()) instance = form.save() for k in BitFieldTestModel.flags: self.assertEquals(bool(getattr(instance.flags, k)), k in data["flags"]) data = {"flags": []} form = BitFieldTestModelForm(data=data, instance=instance) self.failUnless(form.is_valid()) instance = form.save() for k in BitFieldTestModel.flags: self.assertFalse(bool(getattr(instance.flags, k)))