This instance will be upgraded to Heptapod 0.19.0rc2 on 2021-01-18 at 11:00 UTC+1 (a few minutes of downtime)

Commit b9591f11 authored by Yuya Nishihara's avatar Yuya Nishihara

repomanager: keep instance of closing repository until service stopped cleanly

parent a8c83e9e3b87
import mock, unittest
from mercurial import ui
from PyQt4.QtCore import QEventLoop, QTimer
from mercurial import ui as uimod
from tortoisehg.hgqt import cmdcore, thgrepo
import helpers
def mockrepo(ui, path):
m = mock.MagicMock(ui=ui, root=path)
m.unfiltered = lambda: m
return m
def mockwatcher(repo, parent=None):
m = mock.MagicMock()
m.isMonitoring.return_value = False
return m
LOCAL_SIGNALS = ['repositoryOpened', 'repositoryClosed']
MAPPED_SIGNALS = [
# signal name, example arguments
......@@ -20,10 +30,11 @@ MAPPED_SIGNALS = [
class RepoManagerMockedTest(unittest.TestCase):
def setUp(self):
self.hgrepopatcher = mock.patch('mercurial.hg.repository', new=mockrepo)
self.watcherpatcher = mock.patch('tortoisehg.hgqt.thgrepo.RepoWatcher')
self.watcherpatcher = mock.patch('tortoisehg.hgqt.thgrepo.RepoWatcher',
new=mockwatcher)
self.hgrepopatcher.start()
self.watcherpatcher.start()
self.repoman = thgrepo.RepoManager(ui.ui())
self.repoman = thgrepo.RepoManager(uimod.ui())
for signame in LOCAL_SIGNALS + [s for s, _a in MAPPED_SIGNALS]:
slot = mock.Mock()
......@@ -112,3 +123,71 @@ class RepoManagerMockedTest(unittest.TestCase):
self.assertFalse(self.repositoryClosed.called)
self.repoman.releaseRepoAgent('/a')
self.repositoryClosed.assert_called_once_with('/a')
def waitForRepositoryClosed(repoman, path, timeout=5000):
loop = QEventLoop()
repoman.repositoryClosed.connect(loop.quit)
timer = QTimer(interval=timeout, singleShot=True)
timer.timeout.connect(loop.quit)
timer.start()
while repoman.repoAgent(path) and timer.isActive():
loop.exec_()
def waitForServiceStopped(repoagent, timeout=5000):
if not repoagent.isServiceRunning():
return
loop = QEventLoop()
repoagent.serviceStopped.connect(loop.quit)
QTimer.singleShot(timeout, loop.quit)
loop.exec_()
def waitForUnbusy(repoagent, timeout=5000):
if not repoagent.isBusy():
return
loop = QEventLoop()
repoagent.busyChanged.connect(loop.quit)
QTimer.singleShot(timeout, loop.quit)
loop.exec_()
class RepoManagerServiceTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
tmpdir = helpers.mktmpdir(cls.__name__)
cls.hg = hg = helpers.HgClient(tmpdir)
hg.init()
def setUp(self):
ui = uimod.ui()
ui.setconfig('tortoisehg', 'cmdworker', 'server')
ui.setconfig('tortoisehg', 'monitorrepo', 'never')
self.repoman = thgrepo.RepoManager(ui)
def tearDown(self):
if self.repoman.repoAgent(self.hg.path):
self.repoman.releaseRepoAgent(self.hg.path)
waitForRepositoryClosed(self.repoman, self.hg.path)
thgrepo._repocache.clear()
def test_close(self):
a = self.repoman.openRepoAgent(self.hg.path)
a.runCommand(['root']) # start service
waitForUnbusy(a)
self.assertTrue(a.isServiceRunning())
self.repoman.releaseRepoAgent(self.hg.path)
self.assertTrue(self.repoman.repoAgent(self.hg.path))
waitForRepositoryClosed(self.repoman, self.hg.path)
self.assertFalse(self.repoman.repoAgent(self.hg.path))
def test_reopen_about_to_be_closed(self):
a1 = self.repoman.openRepoAgent(self.hg.path)
a1.runCommand(['root']) # start service
waitForUnbusy(a1)
self.repoman.releaseRepoAgent(self.hg.path)
# increase refcount again
a2 = self.repoman.openRepoAgent(self.hg.path)
self.assertTrue(a1 is a2)
# repository should be available after service stopped
waitForServiceStopped(a1)
self.assertTrue(self.repoman.repoAgent(self.hg.path))
......@@ -506,6 +506,8 @@ class RepoManager(QObject):
SIGNAL('repositoryChanged(QString)')),
(SIGNAL('repositoryDestroyed()'),
SIGNAL('repositoryDestroyed(QString)')),
(SIGNAL('serviceStopped()'),
SLOT('_tryCloseRepoAgent(QString)')),
(SIGNAL('busyChanged(bool)'),
SLOT('_mapBusyChanged(QString)')),
]
......@@ -514,6 +516,7 @@ class RepoManager(QObject):
super(RepoManager, self).__init__(parent)
self._ui = ui
self._openagents = {} # path: (agent, refcount)
# refcount=0 means the repo is about to be closed
self._sigmappers = []
for _sig, slot in self._SIGNALMAP:
......@@ -550,16 +553,28 @@ class RepoManager(QObject):
"""Decrement refcount of RepoAgent and close it if possible"""
path = _normreporoot(path)
agent, refcount = self._openagents[path]
self._openagents[path] = (agent, refcount - 1)
if refcount > 1:
self._openagents[path] = (agent, refcount - 1)
return
# close child agents first, which may reenter to releaseRepoAgent()
agent.releaseSubRepoAgents()
if agent.isServiceRunning():
self._ui.debug('stopping service: %s\n' % hglib.fromunicode(path))
agent.stopService()
else:
self._tryCloseRepoAgent(path)
@pyqtSlot(unicode)
def _tryCloseRepoAgent(self, path):
path = unicode(path)
agent, refcount = self._openagents[path]
if refcount > 0:
# repo may be reopen before its services stopped
return
self._ui.debug('closing repo: %s\n' % hglib.fromunicode(path))
agent, _refcount = self._openagents.pop(path)
agent.stopService()
del self._openagents[path]
# TODO: disconnected automatically if _repocache does not exist
for (sig, _slot), mapper in zip(self._SIGNALMAP, self._sigmappers):
QObject.disconnect(agent, sig, mapper, SLOT('map()'))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment