remove _configtracker and write a tested "PendingConfigure" helper class for managing configure/started states for test accounts

This commit is contained in:
holger krekel
2022-05-02 23:09:35 +02:00
parent 026dd4480e
commit 7fbdb42695
4 changed files with 146 additions and 49 deletions

View File

@@ -67,6 +67,9 @@ class Account(object):
""" re-enable logging. """
self._logging = True
def __repr__(self):
return "<Account path={}>".format(self.db_path)
# def __del__(self):
# self.shutdown()
@@ -571,6 +574,8 @@ class Account(object):
""" add an account plugin which implements one or more of
the :class:`deltachat.hookspec.PerAccount` hooks.
"""
if name and self._pm.has_plugin(name=name):
self._pm.unregister(name=name)
self._pm.register(plugin, name=name)
self._pm.check_pending()
return plugin

View File

@@ -9,12 +9,13 @@ import fnmatch
import time
import weakref
import tempfile
from queue import Queue
from typing import List, Callable
import pytest
import requests
from . import Account, const
from . import Account, const, account_hookimpl
from .events import FFIEventLogger, FFIEventTracker
from _pytest._code import Source
@@ -210,6 +211,56 @@ def data(request):
return Data()
class PendingConfigure:
CONFIGURING = "CONFIGURING"
CONFIGURED = "CONFIGURED"
POSTPROCESSED = "POSTPROCESSED"
def __init__(self):
self._configured_events = Queue()
self._account2state = {}
def add_account(self, acc, reconfigure=False):
class PendingTracker:
@account_hookimpl
def ac_configure_completed(this, success):
self._configured_events.put((acc, success))
acc.add_account_plugin(PendingTracker(), name="pending_tracker")
self._account2state[acc] = self.CONFIGURING
acc.configure(reconfigure=reconfigure)
print("started configure on pending", acc)
def wait_all(self, onconfigured=lambda x: None):
""" Wait for all accounts to finish configuration.
"""
print("wait_all finds accounts=", self._account2state)
for acc, state in self._account2state.items():
if state == self.CONFIGURED:
onconfigured(acc)
self._account2state[acc] = self.POSTPROCESSED
while self.CONFIGURING in self._account2state.values():
acc, success = self._pop_one()
onconfigured(acc)
self._account2state[acc] = self.POSTPROCESSED
print("finished, account2state", self._account2state)
def wait_one(self, account):
if self._account2state[account] == self.CONFIGURING:
while 1:
acc, success = self._pop_one()
if acc == account:
break
def _pop_one(self):
acc, success = self._configured_events.get()
if not success:
pytest.fail("configuring online account failed: {}".format(acc))
self._account2state[acc] = self.CONFIGURED
return (acc, success)
class ACFactory:
_finalizers: List[Callable[[], None]]
_accounts: List[Account]
@@ -224,6 +275,8 @@ class ACFactory:
self._finalizers = []
self._accounts = []
self._pending_configure = PendingConfigure()
self._imap_cleaned = set()
self._preconfigured_keys = ["alice", "bob", "charlie",
"dom", "elena", "fiona"]
self.set_logging_default(False)
@@ -302,11 +355,18 @@ class ACFactory:
self._preconfigure_key(ac, addr)
return ac
def new_online_configuring_account(self, **kwargs):
configdict = self.get_next_liveconfig()
def new_online_configuring_account(self, cloned_from=None, **kwargs):
if cloned_from is None:
configdict = self.get_next_liveconfig()
else:
# XXX we might want to transfer the key to the new account
configdict = dict(
addr=cloned_from.get_config("addr"),
mail_pw=cloned_from.get_config("mail_pw"),
)
configdict.update(kwargs)
ac = self.prepare_account_from_liveconfig(configdict)
ac._configtracker = ac.configure()
self._pending_configure.add_account(ac)
return ac
def prepare_account_from_liveconfig(self, configdict):
@@ -320,40 +380,32 @@ class ACFactory:
return ac
def new_cloned_configuring_account(self, account):
""" Clones addr, mail_pw, mvbox_move, sentbox_watch and the
direct_imap object of an online account. This simulates the user setting
up a new device without importing a backup.
"""
# XXX we might want to transfer the key to the new account
ac = self.prepare_account_from_liveconfig(dict(
addr=account.get_config("addr"),
mail_pw=account.get_config("mail_pw"),
))
if hasattr(account, "direct_imap"):
# Attach the existing direct_imap. If we did not do this, a new one would be created and
# delete existing messages (see dc_account_extra_configure(configure))
ac.direct_imap = account.direct_imap
ac._configtracker = ac.configure()
return ac
return self.new_online_configuring_account(cloned_from=account)
def bring_accounts_online(self):
for acc in self._accounts:
self.wait_configure(acc)
acc.start_io()
print("waiting for inbox IDLE to become ready")
acc._evtracker.wait_idle_inbox_ready()
logger = FFIEventLogger(acc, logid=acc._logid, init_time=self.init_time)
acc.add_account_plugin(logger)
acc.log("inbox IDLE ready!")
def _onconfigure_start_io(self, acc):
acc.start_io()
print(acc._logid, "waiting for inbox IDLE to become ready")
acc._evtracker.wait_idle_inbox_ready()
self.init_direct_imap_and_logging(acc)
acc.get_device_chat().mark_noticed()
acc._evtracker.consume_events()
acc.log("inbox IDLE ready")
def init_direct_imap_and_logging(self, acc):
""" idempotent function for initializing direct_imap and logging for an account. """
self.init_direct_imap(acc)
logger = FFIEventLogger(acc, logid=acc._logid, init_time=self.init_time)
acc.add_account_plugin(logger, name=acc._logid)
def wait_configure(self, acc):
if hasattr(acc, "_configtracker"):
acc._configtracker.wait_finish()
acc._evtracker.consume_events()
acc.get_device_chat().mark_noticed()
del acc._configtracker
if not hasattr(acc, "direct_imap"):
self.init_direct_imap(acc)
self._pending_configure.wait_one(acc)
self.init_direct_imap_and_logging(acc)
acc._evtracker.consume_events()
def bring_accounts_online(self):
print("bringing accounts online")
self._pending_configure.wait_all(onconfigured=self._onconfigure_start_io)
print("all accounts online")
def get_online_accounts(self, num):
# to reduce number of log events logging starts after accounts can receive
@@ -397,15 +449,19 @@ class ACFactory:
def init_direct_imap(self, acc):
from deltachat.direct_imap import DirectImap
if not hasattr(acc, "direct_imap"):
acc.direct_imap = imap = DirectImap(acc)
acc.direct_imap = DirectImap(acc)
addr = acc.get_config("addr")
if addr not in self._imap_cleaned:
imap = acc.direct_imap
for folder in imap.list_folders():
if folder.lower() == "inbox" or folder.lower() == "deltachat":
assert imap.select_folder(folder)
imap.delete("1:*", expunge=True)
else:
imap.conn.folder.delete(folder)
acc.log("imap cleaned for addr {}".format(addr))
self._imap_cleaned.add(addr)
def dump_imap_summary(self, logfile):
for ac in self._accounts: