From e95726b8a7803dbb23bfca470cf4b665cf8559a4 Mon Sep 17 00:00:00 2001 From: drebs Date: Wed, 5 Dec 2012 15:36:07 -0200 Subject: OpenStack backend can find what's changed. --- src/leap/soledad/openstack.py | 60 ++++++++++++++++++++++++++++++++------ src/leap/soledad/tests/__init__.py | 33 +++++++++++++++++++-- 2 files changed, 81 insertions(+), 12 deletions(-) diff --git a/src/leap/soledad/openstack.py b/src/leap/soledad/openstack.py index 8bbae8d8..7b7e656f 100644 --- a/src/leap/soledad/openstack.py +++ b/src/leap/soledad/openstack.py @@ -30,7 +30,9 @@ class OpenStackDatabase(CommonBackend): raise NotImplementedError(self.set_document_size_limit) def whats_changed(self, old_generation=0): - raise NotImplementedError(self.whats_changed) + # This method is implemented in TransactionLog because testing is + # easier like this for now, but it can be moved to here afterwards. + return self._transaction_log.whats_changed(old_generation) def get_doc(self, doc_id, include_deleted=False): # TODO: support deleted docs? @@ -179,22 +181,29 @@ class OpenStackSyncTarget(HTTPSyncTarget): class SimpleLog(object): - def __init__(self, log=None): + def __init__(self): self._log = [] - if log: - self._log = log + + def _set_log(self, log): + self._log = log + + def _get_log(self): + return self._log + + log = property( + _get_log, _set_log, doc="Log contents.") def append(self, msg): self._log.append(msg) def reduce(self, func, initializer=None): - return reduce(func, self._log, initializer) + return reduce(func, self.log, initializer) def map(self, func): - return map(func, self._log) + return map(func, self.log) def filter(self, func): - return filter(func, self._log) + return filter(func, self.log) class TransactionLog(SimpleLog): @@ -202,6 +211,15 @@ class TransactionLog(SimpleLog): A list of (generation, doc_id, transaction_id) tuples. """ + def _set_log(self, log): + self._log = log + + def _get_log(self): + return sorted(self._log, reverse=True) + + log = property( + _get_log, _set_log, doc="Log contents.") + def get_generation(self): """ Return the current generation. @@ -229,6 +247,30 @@ class TransactionLog(SimpleLog): return None return log[2] + def whats_changed(self, old_generation): + results = self.filter(lambda x: x[0] > old_generation) + seen = set() + changes = [] + newest_trans_id = '' + for generation, doc_id, trans_id in results: + if doc_id not in seen: + changes.append((doc_id, generation, trans_id)) + seen.add(doc_id) + if changes: + cur_gen = changes[0][1] # max generation + newest_trans_id = changes[0][2] + changes.reverse() + else: + results = self.log + if not results: + cur_gen = 0 + newest_trans_id = '' + else: + cur_gen, _, newest_trans_id = results[0] + + return cur_gen, newest_trans_id, changes + + class SyncLog(SimpleLog): """ @@ -236,7 +278,7 @@ class SyncLog(SimpleLog): """ def find_by_replica_uid(self, replica_uid): - if not self._log: + if not self.log: return () return self.reduce(lambda x, y: y if y[0] == replica_uid else x) @@ -256,7 +298,7 @@ class SyncLog(SimpleLog): Set the last-known generation and transaction id for the other database replica. """ - self._log = self.filter(lambda x: x[0] != other_replica_uid) + self.log = self.filter(lambda x: x[0] != other_replica_uid) self.append((other_replica_uid, other_generation, other_transaction_id)) diff --git a/src/leap/soledad/tests/__init__.py b/src/leap/soledad/tests/__init__.py index 50c99dd4..4f63648e 100644 --- a/src/leap/soledad/tests/__init__.py +++ b/src/leap/soledad/tests/__init__.py @@ -8,7 +8,11 @@ import os import u1db from soledad import leap, GPGWrapper -from soledad.openstack import SimpleLog, TransactionLog, SyncLog +from soledad.openstack import ( + SimpleLog, + TransactionLog, + SyncLog, + ) class EncryptedSyncTestCase(unittest.TestCase): @@ -53,7 +57,8 @@ class LogTestCase(unittest.TestCase): (3, "doc_2", "tran_2"), (1, "doc_1", "tran_1") ] - log = TransactionLog(data) + log = TransactionLog() + log.log = data self.assertEqual(log.get_generation(), 3, 'error getting generation') self.assertEqual(log.get_generation_info(), (3, 'tran_2'), 'error getting generation info') @@ -70,7 +75,8 @@ class LogTestCase(unittest.TestCase): ("replica_2", 2, "tran_2"), ("replica_1", 1, "tran_1") ] - log = SyncLog(data) + log = SyncLog() + log.log = data # test getting self.assertEqual(log.get_replica_gen_and_trans_id('replica_3'), (3, 'tran_3'), 'error getting replica gen and trans id') @@ -88,6 +94,27 @@ class LogTestCase(unittest.TestCase): self.assertEqual(log.get_replica_gen_and_trans_id('replica_3'), (3, 'tran_3'), 'error setting replica gen and trans id') + def test_whats_changed(self): + data = [ + (2, "doc_3", "tran_3"), + (3, "doc_2", "tran_2"), + (1, "doc_1", "tran_1") + ] + log = TransactionLog() + log.log = data + self.assertEqual( + log.whats_changed(3), + (3, "tran_2", []), + 'error getting whats changed.') + self.assertEqual( + log.whats_changed(2), + (3, "tran_2", [("doc_2",3,"tran_2")]), + 'error getting whats changed.') + self.assertEqual( + log.whats_changed(1), + (3, "tran_2", [("doc_3",2,"tran_3"),("doc_2",3,"tran_2")]), + 'error getting whats changed.') + # Key material for testing KEY_FINGERPRINT = "E36E738D69173C13D709E44F2F455E2824D18DDF" -- cgit v1.2.3