123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 |
- import isc.log
- import unittest
- from isc.dns import Name, RRset, RRClass, RRType, RRTTL, Rdata
- from isc.xfrin.diff import Diff, NoSuchZone
- class TestError(Exception):
- """
- Just to have something to be raised during the tests.
- Not used outside.
- """
- pass
- class DiffTest(unittest.TestCase):
- """
- Tests for the isc.xfrin.diff.Diff class.
- It also plays role of a data source and an updater, so it can manipulate
- some test variables while being called.
- """
- def setUp(self):
- """
- This sets internal variables so we can see nothing was called yet.
- It also creates some variables used in multiple tests.
- """
-
- self.__updater_requested = False
- self.__compact_called = False
- self.__data_operations = []
- self.__apply_called = False
- self.__commit_called = False
- self.__broken_called = False
-
- self.__rrclass = RRClass.IN()
- self.__type = RRType.A()
- self.__ttl = RRTTL(3600)
-
-
- self.__rrset1 = RRset(Name('a.example.org.'), self.__rrclass,
- self.__type, self.__ttl)
- self.__rdata = Rdata(self.__type, self.__rrclass, '192.0.2.1')
- self.__rrset1.add_rdata(self.__rdata)
- self.__rrset2 = RRset(Name('b.example.org.'), self.__rrclass,
- self.__type, self.__ttl)
- self.__rrset2.add_rdata(self.__rdata)
-
- self.__rrset_empty = RRset(Name('empty.example.org.'), self.__rrclass,
- self.__type, self.__ttl)
- self.__rrset_multi = RRset(Name('multi.example.org.'), self.__rrclass,
- self.__type, self.__ttl)
- self.__rrset_multi.add_rdata(self.__rdata)
- self.__rrset_multi.add_rdata(Rdata(self.__type, self.__rrclass,
- '192.0.2.2'))
- def __mock_compact(self):
- """
- This can be put into the diff to hook into its compact method and see
- if it gets called.
- """
- self.__compact_called = True
- def __mock_apply(self):
- """
- This can be put into the diff to hook into its apply method and see
- it gets called.
- """
- self.__apply_called = True
- def __broken_operation(self, *args):
- """
- This can be used whenever an operation should fail. It raises TestError.
- It should take whatever amount of parameters needed, so it can be put
- quite anywhere.
- """
- self.__broken_called = True
- raise TestError("Test error")
- def commit(self):
- """
- This is part of pretending to be a zone updater. This notes the commit
- was called.
- """
- self.__commit_called = True
- def add_rrset(self, rrset):
- """
- This one is part of pretending to be a zone updater. It writes down
- addition of an rrset was requested.
- """
- self.__data_operations.append(('add', rrset))
- def remove_rrset(self, rrset):
- """
- This one is part of pretending to be a zone updater. It writes down
- removal of an rrset was requested.
- """
- self.__data_operations.append(('remove', rrset))
- def get_class(self):
- """
- This one is part of pretending to be a zone updater. It returns
- the IN class.
- """
- return self.__rrclass
- def get_updater(self, zone_name, replace):
- """
- This one pretends this is the data source client and serves
- getting an updater.
- If zone_name is 'none.example.org.', it returns None, otherwise
- it returns self.
- """
-
- self.assertFalse(replace)
- self.__updater_requested = True
-
- if zone_name == Name('none.example.org.'):
- return None
- else:
- return self
- def test_create(self):
- """
- This test the case when the diff is successfuly created. It just
- tries it does not throw and gets the updater.
- """
- diff = Diff(self, Name('example.org.'))
- self.assertTrue(self.__updater_requested)
- self.assertEqual([], diff.get_buffer())
- def test_create_nonexist(self):
- """
- Try to create a diff on a zone that doesn't exist. This should
- raise a correct exception.
- """
- self.assertRaises(NoSuchZone, Diff, self, Name('none.example.org.'))
- self.assertTrue(self.__updater_requested)
- def __data_common(self, diff, method, name):
- """
- Common part of test for test_add and test_remove.
- """
-
- self.assertRaises(ValueError, method, self.__rrset_empty)
- self.assertRaises(ValueError, method, self.__rrset_multi)
-
- self.assertEqual([], diff.get_buffer())
-
- method(self.__rrset1)
- method(self.__rrset2)
- dlist = [(name, self.__rrset1), (name, self.__rrset2)]
- self.assertEqual(dlist, diff.get_buffer())
-
-
- self.assertRaises(ValueError, method, self.__rrset_empty)
- self.assertEqual(dlist, diff.get_buffer())
- def test_add(self):
- """
- Try to add few items into the diff and see they are stored in there.
- Also try passing an rrset that has differnt amount of RRs than 1.
- """
- diff = Diff(self, Name('example.org.'))
- self.__data_common(diff, diff.add_data, 'add')
- def test_remove(self):
- """
- Try scheduling removal of few items into the diff and see they are
- stored in there.
- Also try passing an rrset that has different amount of RRs than 1.
- """
- diff = Diff(self, Name('example.org.'))
- self.__data_common(diff, diff.remove_data, 'remove')
- def test_apply(self):
- """
- Schedule few additions and check the apply works by passing the
- data into the updater.
- """
-
- diff = Diff(self, Name('example.org.'))
- diff.add_data(self.__rrset1)
- diff.remove_data(self.__rrset2)
- dlist = [('add', self.__rrset1), ('remove', self.__rrset2)]
- self.assertEqual(dlist, diff.get_buffer())
-
- diff.compact = self.__mock_compact
- diff.apply()
-
- self.assertTrue(self.__compact_called)
-
-
- self.assertEqual(dlist, self.__data_operations)
-
-
- self.assertEqual([], diff.get_buffer())
- def test_commit(self):
- """
- If we call a commit, it should first apply whatever changes are
- left (we hook into that instead of checking the effect) and then
- the commit on the updater should have been called.
- Then we check it raises value error for whatever operation we try.
- """
- diff = Diff(self, Name('example.org.'))
- diff.add_data(self.__rrset1)
- orig_apply = diff.apply
- diff.apply = self.__mock_apply
- diff.commit()
- self.assertTrue(self.__apply_called)
- self.assertTrue(self.__commit_called)
-
- self.assertEqual([], self.__data_operations)
-
- self.assertRaises(ValueError, diff.commit)
- self.assertRaises(ValueError, diff.add_data, self.__rrset2)
- self.assertRaises(ValueError, diff.remove_data, self.__rrset1)
- diff.apply = orig_apply
- self.assertRaises(ValueError, diff.apply)
-
-
- diff.compact()
- def test_autoapply(self):
- """
- Test the apply is called all by itself after 100 tasks are added.
- """
- diff = Diff(self, Name('example.org.'))
-
-
-
- def check():
- self.assertEqual(100, len(diff.get_buffer()))
- self.__mock_apply()
- orig_apply = diff.apply
- diff.apply = check
-
- for i in range(0, 99):
- diff.add_data(self.__rrset1)
- expected = [('add', self.__rrset1)] * 99
- self.assertEqual(expected, diff.get_buffer())
- self.assertFalse(self.__apply_called)
-
-
-
- diff.add_data(self.__rrset1)
-
-
-
- self.assertTrue(self.__apply_called)
-
- orig_apply()
- self.assertEqual([], diff.get_buffer())
-
- self.__apply_called = False
- for i in range(0, 99):
- diff.remove_data(self.__rrset2)
- expected = [('remove', self.__rrset2)] * 99
- self.assertEqual(expected, diff.get_buffer())
- self.assertFalse(self.__apply_called)
- diff.remove_data(self.__rrset2)
- self.assertTrue(self.__apply_called)
- def test_compact(self):
- """
- Test the compaction works as expected, eg. it compacts only consecutive
- changes of the same operation and on the same domain/type.
- The test case checks that it does merge them, but also puts some
- different operations "in the middle", changes the type and name and
- places the same kind of change further away of each other to see they
- are not merged in that case.
- """
- diff = Diff(self, Name('example.org.'))
-
- diff.compact()
- self.assertEqual([], diff.get_buffer())
-
-
-
- data = [
- ('add', 'a', 'A', ['192.0.2.1', '192.0.2.2']),
-
- ('add', 'a', 'AAAA', ['2001:db8::1', '2001:db8::2']),
-
- ('remove', 'a', 'AAAA', ['2001:db8::3']),
-
- ('remove', 'b', 'AAAA', ['2001:db8::4']),
-
-
- ('add', 'a', 'A', ['192.0.2.3'])
- ]
-
- for (op, nprefix, rrtype, rdata) in data:
- name = Name(nprefix + '.example.org.')
- rrtype_obj = RRType(rrtype)
- for rdatum in rdata:
- rrset = RRset(name, self.__rrclass, rrtype_obj, self.__ttl)
- rrset.add_rdata(Rdata(rrtype_obj, self.__rrclass, rdatum))
- if op == 'add':
- diff.add_data(rrset)
- else:
- diff.remove_data(rrset)
-
- diff.compact()
-
-
-
- def check():
- buf = diff.get_buffer()
- self.assertEqual(len(data), len(buf))
- for (expected, received) in zip(data, buf):
- (eop, ename, etype, edata) = expected
- (rop, rrrset) = received
- self.assertEqual(eop, rop)
- ename_obj = Name(ename + '.example.org.')
- self.assertEqual(ename_obj, rrrset.get_name())
-
- self.assertEqual(etype, str(rrrset.get_type()))
- rdata = rrrset.get_rdata()
- self.assertEqual(len(edata), len(rdata))
-
- for (edatum, rdatum) in zip(edata, rdata):
- self.assertEqual(edatum, str(rdatum))
- check()
-
- diff.compact()
- check()
- def test_wrong_class(self):
- """
- Test a wrong class of rrset is rejected.
- """
- diff = Diff(self, Name('example.org.'))
- rrset = RRset(Name('a.example.org.'), RRClass.CH(), RRType.NS(),
- self.__ttl)
- rrset.add_rdata(Rdata(RRType.NS(), RRClass.CH(), 'ns.example.org.'))
- self.assertRaises(ValueError, diff.add_data, rrset)
- self.assertRaises(ValueError, diff.remove_data, rrset)
- def __do_raise_test(self):
- """
- Do a raise test. Expects that one of the operations is exchanged for
- broken version.
- """
- diff = Diff(self, Name('example.org.'))
- diff.add_data(self.__rrset1)
- diff.remove_data(self.__rrset2)
- self.assertRaises(TestError, diff.commit)
- self.assertTrue(self.__broken_called)
- self.assertRaises(ValueError, diff.add_data, self.__rrset1)
- self.assertRaises(ValueError, diff.remove_data, self.__rrset2)
- self.assertRaises(ValueError, diff.commit)
- self.assertRaises(ValueError, diff.apply)
- def test_raise_add(self):
- """
- Test the exception from add_rrset is propagated and the diff can't be
- used afterwards.
- """
- self.add_rrset = self.__broken_operation
- self.__do_raise_test()
- def test_raise_remove(self):
- """
- Test the exception from remove_rrset is propagated and the diff can't be
- used afterwards.
- """
- self.remove_rrset = self.__broken_operation
- self.__do_raise_test()
- def test_raise_commit(self):
- """
- Test the exception from updater's commit gets propagated and it can't be
- used afterwards.
- """
- self.commit = self.__broken_operation
- self.__do_raise_test()
- if __name__ == "__main__":
- isc.log.init("bind10")
- unittest.main()
|