diff --git a/src/dbus/server/pim/manager.cpp b/src/dbus/server/pim/manager.cpp index 0b3238fa..f4504093 100644 --- a/src/dbus/server/pim/manager.cpp +++ b/src/dbus/server/pim/manager.cpp @@ -259,7 +259,11 @@ class ViewResource : public Resource, public GDBusCXX::DBusObjectHelper ids, boost::bind(ViewResource::sendDone, m_self, - _1)); + _1, + &call == &m_contactsModified ? "ContactsModified()" : + &call == &m_contactsAdded ? "ContactsAdded()" : + &call == &m_contactsRemoved ? "ContactsRemoved()" : "???", + true)); } /** @@ -461,7 +465,9 @@ class ViewResource : public Resource, public GDBusCXX::DBusObjectHelper m_quiescent.start(getObject(), boost::bind(ViewResource::sendDone, m_self, - _1)); + _1, + "Quiescent()", + false)); } /** @@ -470,11 +476,13 @@ class ViewResource : public Resource, public GDBusCXX::DBusObjectHelper * or client. */ static void sendDone(const boost::weak_ptr &self, - const std::string &error) + const std::string &error, + const char *method, + bool required) { - if (!error.empty()) { + if (required && !error.empty()) { // remove view because it is no longer needed - SE_LOG_DEBUG(NULL, NULL, "ViewAgent method call failed, deleting view: %s", error.c_str()); + SE_LOG_DEBUG(NULL, NULL, "ViewAgent %s method call failed, deleting view: %s", method, error.c_str()); boost::shared_ptr r = self.lock(); if (r) { r->close(); diff --git a/src/dbus/server/pim/pim-manager-api.txt b/src/dbus/server/pim/pim-manager-api.txt index 27c0f1ea..5a2cf713 100644 --- a/src/dbus/server/pim/pim-manager-api.txt +++ b/src/dbus/server/pim/pim-manager-api.txt @@ -447,3 +447,21 @@ Methods: Some contacts were removed from the view. The contact which previous had index #start + count (if there was one) now has index #start, etc. + + void Quiescent(object view) + + The current content of the view is complete. No further + updates are expected until something changes again + (underlying data, ordering, active address books, + filter). + + Changing data (directly or via syncing) can trigger + multiple "Quiescent" signals, depending on when these + changes are reported by the underlying storage. + + Changing multiple settings will trigger one "Quiescent" + per change. + + Implementing the Quiescent() method in a ViewAgent + is optional. + diff --git a/src/dbus/server/pim/testpim.py b/src/dbus/server/pim/testpim.py index 7c41a2b9..1e5dadac 100755 --- a/src/dbus/server/pim/testpim.py +++ b/src/dbus/server/pim/testpim.py @@ -3015,6 +3015,71 @@ END:VCARD''']): ('quiescent',), ], self.view.events) + + @timeout(60) + def testDeadAgent(self): + '''TestContacts.testDeadAgent - an error from the agent kills the view''' + self.setUpView(search=None, peers=[], withSystemAddressBook=True) + + # Insert new contact. + # + # The names are chosen so that sorting by first name and sorting by last name needs to + # reverse the list. + for i, contact in enumerate([u'''BEGIN:VCARD +VERSION:3.0 +FN:John Doe +N:Doe;John +END:VCARD''', +]): + item = os.path.join(self.contacts, 'contact.vcf') + output = codecs.open(item, "w", "utf-8") + output.write(contact) + output.close() + logging.printf('inserting contact %d', i) + + out, err, returncode = self.runCmdline(['--import', self.contacts, 'backend=evolution-contacts']) + + # Plug into "ContactsAdded" method so that it throws an error. + original = self.view.processEvent + def intercept(message, event): + original(message, event) + if event[0] == 'added': + raise Exception('fake error') + self.view.processEvent = intercept + self.view.search([]) + self.runUntil('phone results', + check=lambda: self.assertEqual([], self.view.errors), + until=lambda: self.view.quiescentCount > 0) + self.assertEqual([('added', 0, 1), + ('quiescent',)], + self.view.events) + + # Expect an error, view should have been closed already. + with self.assertRaisesRegexp(dbus.DBusException, + "org.freedesktop.DBus.Error.UnknownMethod: No such interface `org._01.pim.contacts.ViewControl' on object at path .*"): + self.view.close() + + @timeout(60) + def testQuiescentOptional(self): + '''TestContacts.testQuiescentOptional - the Quiescent() method is allowed to fail''' + self.setUpView(search=None, peers=[], withSystemAddressBook=True) + + # Plug into "Quiescent" method so that it throws an error. + original = self.view.processEvent + def intercept(message, event): + original(message, event) + if event[0] == 'quiescent': + raise Exception('fake error') + self.view.processEvent = intercept + self.view.search([]) + self.runUntil('phone results', + check=lambda: self.assertEqual([], self.view.errors), + until=lambda: self.view.quiescentCount > 0) + self.assertEqual([('quiescent',)], + self.view.events) + self.view.close() + + if __name__ == '__main__': xdg = (os.path.join(os.path.abspath('.'), 'temp-testpim', 'config'), os.path.join(os.path.abspath('.'), 'temp-testpim', 'local', 'cache'))