Merge pull request #566 from tahoe-lafs/2990.magic-folder-testcase-base

Change Magic Folder unit test TestCase base class

Fixes: ticket:2990
This commit is contained in:
Jean-Paul Calderone 2019-03-11 08:06:52 -04:00 committed by GitHub
commit 68d4f22207
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 80 additions and 72 deletions

0
newsfragments/2990.minor Normal file
View File

View File

@ -6,6 +6,10 @@ import re
import time
from datetime import datetime
from eliot.twisted import (
DeferredContext,
)
from twisted.trial import unittest
from twisted.internet import defer
from twisted.internet import reactor
@ -23,7 +27,9 @@ from allmydata.util.fileutil import abspath_expanduser_unicode
from allmydata.util.encodingutil import unicode_to_argv
from allmydata.frontends.magic_folder import MagicFolder
from allmydata import uri
from ...util.eliotutil import (
log_call_deferred,
)
class MagicFolderCLITestMixin(CLITestMixin, GridTestMixin, NonASCIIPathMixin):
def setUp(self):
@ -35,12 +41,12 @@ class MagicFolderCLITestMixin(CLITestMixin, GridTestMixin, NonASCIIPathMixin):
d = self.do_cli("magic-folder", "--debug", "create", "magic:", client_num=client_num)
def _done((rc,stdout,stderr)):
self.failUnlessEqual(rc, 0, stdout + stderr)
self.failUnlessIn("Alias 'magic' created", stdout)
self.assertIn("Alias 'magic' created", stdout)
# self.failUnlessIn("joined new magic-folder", stdout)
# self.failUnlessIn("Successfully created magic-folder", stdout)
self.failUnlessEqual(stderr, "")
aliases = get_aliases(self.get_clientdir(i=client_num))
self.failUnlessIn("magic", aliases)
self.assertIn("magic", aliases)
self.failUnless(aliases["magic"].startswith("URI:DIR2:"))
d.addCallback(_done)
return d
@ -125,8 +131,8 @@ class MagicFolderCLITestMixin(CLITestMixin, GridTestMixin, NonASCIIPathMixin):
mf_yaml = fileutil.read(os.path.join(self.get_clientdir(i=client_num), "private", "magic_folders.yaml"))
local_dir_utf8 = local_dir.encode('utf-8')
magic_folder_config = "[magic_folder]\nenabled = True"
self.failUnlessIn(magic_folder_config, client_config)
self.failUnlessIn(local_dir_utf8, mf_yaml)
self.assertIn(magic_folder_config, client_config)
self.assertIn(local_dir_utf8, mf_yaml)
def create_invite_join_magic_folder(self, nickname, local_dir):
nickname_arg = unicode_to_argv(nickname)
@ -146,14 +152,15 @@ class MagicFolderCLITestMixin(CLITestMixin, GridTestMixin, NonASCIIPathMixin):
return d
# XXX should probably just be "tearDown"...
@log_call_deferred(action_type=u"test:cli:magic-folder:cleanup")
def cleanup(self, res):
d = defer.succeed(None)
d = DeferredContext(defer.succeed(None))
def _clean(ign):
return self.magicfolder.disownServiceParent()
d.addCallback(_clean)
d.addCallback(lambda ign: res)
return d
return d.result
def init_magicfolder(self, client_num, upload_dircap, collective_dircap, local_magic_dir, clock):
dbfile = abspath_expanduser_unicode(u"magicfolder_default.sqlite", base=self.get_clientdir(i=client_num))

View File

@ -4,9 +4,12 @@ import stat, shutil, json
import mock
from os.path import join, exists, isdir
from twisted.trial import unittest
from twisted.internet import defer, task, reactor
from testtools import (
skipIf,
)
from eliot.twisted import DeferredContext
from allmydata.interfaces import (
@ -20,7 +23,11 @@ from allmydata.util.encodingutil import get_filesystem_encoding, to_filepath
from allmydata.util.consumer import download_to_data
from allmydata.test.no_network import GridTestMixin
from allmydata.test.common_util import ReallyEqualMixin
from allmydata.test.common import ShouldFailMixin
from .common import (
ShouldFailMixin,
SyncTestCase,
AsyncTestCase,
)
from .cli.test_magic_folder import MagicFolderCLITestMixin
from allmydata.frontends import magic_folder
@ -36,9 +43,6 @@ from allmydata.mutable.common import (
UnrecoverableFileError,
)
from .eliotutil import (
eliot_logged_test,
)
from ..util.eliotutil import (
inline_callbacks,
log_call_deferred,
@ -46,23 +50,20 @@ from ..util.eliotutil import (
_debug = False
class TestCase(unittest.TestCase):
"""
A ``TestCase`` which collects helpful behaviors for subclasses.
Those behaviors are:
* Each test method will be run in a unique Eliot action context which
identifies the test and collects all Eliot log messages emitted by that
test (including setUp and tearDown messages).
"""
@eliot_logged_test
def run(self, result):
return super(TestCase, self).run(result)
try:
magic_folder.get_inotify_module()
except NotImplementedError:
support_missing = True
support_message = (
"Magic Folder support can only be tested for-real on an OS that "
"supports inotify or equivalent."
)
else:
support_missing = False
support_message = None
class NewConfigUtilTests(TestCase):
class NewConfigUtilTests(SyncTestCase):
def setUp(self):
# some tests look at the umask of created directories or files
@ -269,7 +270,7 @@ class NewConfigUtilTests(TestCase):
)
class LegacyConfigUtilTests(TestCase):
class LegacyConfigUtilTests(SyncTestCase):
def setUp(self):
# create a valid 'old style' magic-folder configuration
@ -413,7 +414,7 @@ class LegacyConfigUtilTests(TestCase):
class MagicFolderDbTests(TestCase):
class MagicFolderDbTests(SyncTestCase):
def setUp(self):
self.temp = abspath_expanduser_unicode(unicode(self.mktemp()))
@ -1107,10 +1108,8 @@ class MagicFolderAliceBobTestMixin(MagicFolderCLITestMixin, ShouldFailMixin, Rea
# XXX this should be shortened -- as in, any cases not covered by
# the other tests in here should get their own minimal test-case.
@skipIf(sys.platform == "win32", "Still inotify problems on Windows (FIXME)")
def test_alice_bob(self):
if sys.platform == "win32":
raise unittest.SkipTest("Still inotify problems on Windows (FIXME)")
d = defer.succeed(None)
# XXX FIXME just quickly porting this test via aliases -- the
@ -1471,7 +1470,7 @@ class SingleMagicFolderTestMixin(MagicFolderCLITestMixin, ShouldFailMixin, Reall
"""
def setUp(self):
super(SingleMagicFolderTestMixin, self).setUp()
self.assertIs(None, super(SingleMagicFolderTestMixin, self).setUp())
temp = self.mktemp()
self.basedir = abspath_expanduser_unicode(temp.decode(get_filesystem_encoding()))
self.magicfolder = None
@ -1491,16 +1490,23 @@ class SingleMagicFolderTestMixin(MagicFolderCLITestMixin, ShouldFailMixin, Reall
from foolscap.eventual import flushEventualQueue
self.addCleanup(flushEventualQueue)
d = self.create_invite_join_magic_folder(self.alice_nickname, self.local_dir)
# Sometimes a collective scan fails with UnrecoverableFileError. It's
# not clear to me why. :/ This fixes the issue, though, and all other
# asserted-about behavior is provided whether this case is hit or not.
self.addCleanup(
lambda: self.eliot_logger.flushTracebacks(UnrecoverableFileError)
)
d = DeferredContext(self.create_invite_join_magic_folder(self.alice_nickname, self.local_dir))
d.addCallback(self._restart_client)
# note: _restart_client ultimately sets self.magicfolder to not-None
return d
return d.result
def tearDown(self):
d = super(SingleMagicFolderTestMixin, self).tearDown()
d = DeferredContext(super(SingleMagicFolderTestMixin, self).tearDown())
d.addCallback(self.cleanup)
shutil.rmtree(self.basedir, ignore_errors=True)
return d
return d.result
def _createdb(self):
dbfile = abspath_expanduser_unicode(u"magicfolder_default.sqlite", base=self.basedir)
@ -1509,12 +1515,14 @@ class SingleMagicFolderTestMixin(MagicFolderCLITestMixin, ShouldFailMixin, Reall
self.failUnlessEqual(mdb.VERSION, 1)
return mdb
@log_call_deferred(action_type=u"restart-client")
def _restart_client(self, ign):
#print "_restart_client"
d = self.restart_client()
d = DeferredContext(self.restart_client())
d.addCallback(self._wait_until_started)
return d
return d.result
@log_call_deferred(action_type=u"wait-until-started")
def _wait_until_started(self, ign):
#print "_wait_until_started"
self.magicfolder = self.get_client().getServiceNamed('magic-folder-default')
@ -1575,12 +1583,12 @@ class SingleMagicFolderTestMixin(MagicFolderCLITestMixin, ShouldFailMixin, Reall
pathinfo = fileutil.get_pathinfo(path2)
db.did_upload_version(relpath2, 0, 'URI:LIT:2', 'URI:LIT:1', 0, pathinfo)
db_entry = db.get_db_entry(relpath2)
self.failUnlessFalse(magic_folder.is_new_file(pathinfo, db_entry))
self.assertFalse(magic_folder.is_new_file(pathinfo, db_entry))
different_pathinfo = fileutil.PathInfo(isdir=False, isfile=True, islink=False,
exists=True, size=0, mtime_ns=pathinfo.mtime_ns,
ctime_ns=pathinfo.ctime_ns)
self.failUnlessTrue(magic_folder.is_new_file(different_pathinfo, db_entry))
self.assertTrue(magic_folder.is_new_file(different_pathinfo, db_entry))
def _test_magicfolder_start_service(self):
# what is this even testing?
@ -1595,6 +1603,7 @@ class SingleMagicFolderTestMixin(MagicFolderCLITestMixin, ShouldFailMixin, Reall
d.addCallback(lambda ign: self.failUnlessReallyEqual(self._get_count('uploader.dirs_monitored'), 0))
return d
@skipIf(sys.platform == "linux2", "fails on certain linux flavors: see ticket #2834")
def test_move_tree(self):
"""
create an empty directory tree and 'mv' it into the magic folder,
@ -1666,7 +1675,6 @@ class SingleMagicFolderTestMixin(MagicFolderCLITestMixin, ShouldFailMixin, Reall
d.addCallback(lambda ign: self.failUnlessReallyEqual(self._get_count('uploader.directories_created'), 2))
return d.result
test_move_tree.todo = "fails on certain linux flavors: see ticket #2834"
def test_persistence(self):
"""
@ -1854,25 +1862,25 @@ class SingleMagicFolderTestMixin(MagicFolderCLITestMixin, ShouldFailMixin, Reall
return d.result
class MockTestAliceBob(MagicFolderAliceBobTestMixin, TestCase):
@skipIf(support_missing, support_message)
class MockTestAliceBob(MagicFolderAliceBobTestMixin, AsyncTestCase):
inject_inotify = True
def setUp(self):
d = super(MockTestAliceBob, self).setUp()
self.inotify = fake_inotify
self.patch(magic_folder, 'get_inotify_module', lambda: self.inotify)
return d
return super(MockTestAliceBob, self).setUp()
class MockTest(SingleMagicFolderTestMixin, TestCase):
@skipIf(support_missing, support_message)
class MockTest(SingleMagicFolderTestMixin, AsyncTestCase):
"""This can run on any platform, and even if twisted.internet.inotify can't be imported."""
inject_inotify = True
def setUp(self):
d = super(MockTest, self).setUp()
self.inotify = fake_inotify
self.patch(magic_folder, 'get_inotify_module', lambda: self.inotify)
return d
return super(MockTest, self).setUp()
def test_errors(self):
self.set_up_grid(oneshare=True)
@ -1993,28 +2001,29 @@ class MockTest(SingleMagicFolderTestMixin, TestCase):
# test magic-folder statistics
d.addCallback(lambda res: self.GET("statistics"))
def _got_stats(res):
self.failUnlessIn("Operational Statistics", res)
self.failUnlessIn("Magic Folder", res)
self.failUnlessIn("<li>Local Directories Monitored: 1 directories</li>", res)
self.failUnlessIn("<li>Files Uploaded: 1 files</li>", res)
self.failUnlessIn("<li>Files Queued for Upload: 0 files</li>", res)
self.failUnlessIn("<li>Failed Uploads: 0 files</li>", res)
self.failUnlessIn("<li>Files Downloaded: 0 files</li>", res)
self.failUnlessIn("<li>Files Queued for Download: 0 files</li>", res)
self.failUnlessIn("<li>Failed Downloads: 0 files</li>", res)
self.assertIn("Operational Statistics", res)
self.assertIn("Magic Folder", res)
self.assertIn("<li>Local Directories Monitored: 1 directories</li>", res)
self.assertIn("<li>Files Uploaded: 1 files</li>", res)
self.assertIn("<li>Files Queued for Upload: 0 files</li>", res)
self.assertIn("<li>Failed Uploads: 0 files</li>", res)
self.assertIn("<li>Files Downloaded: 0 files</li>", res)
self.assertIn("<li>Files Queued for Download: 0 files</li>", res)
self.assertIn("<li>Failed Downloads: 0 files</li>", res)
d.addCallback(_got_stats)
d.addCallback(lambda res: self.GET("statistics?t=json"))
def _got_stats_json(res):
data = json.loads(res)
self.failUnlessEqual(data["counters"]["magic_folder.uploader.dirs_monitored"], 1)
self.failUnlessEqual(data["counters"]["magic_folder.uploader.objects_succeeded"], 1)
self.failUnlessEqual(data["counters"]["magic_folder.uploader.files_uploaded"], 1)
self.failUnlessEqual(data["counters"]["magic_folder.uploader.objects_queued"], 0)
self.assertEqual(data["counters"]["magic_folder.uploader.dirs_monitored"], 1)
self.assertEqual(data["counters"]["magic_folder.uploader.objects_succeeded"], 1)
self.assertEqual(data["counters"]["magic_folder.uploader.files_uploaded"], 1)
self.assertEqual(data["counters"]["magic_folder.uploader.objects_queued"], 0)
d.addCallback(_got_stats_json)
return d.result
class RealTest(SingleMagicFolderTestMixin, TestCase):
@skipIf(support_missing, support_message)
class RealTest(SingleMagicFolderTestMixin, AsyncTestCase):
"""This is skipped unless both Twisted and the platform support inotify."""
inject_inotify = False
@ -2024,7 +2033,8 @@ class RealTest(SingleMagicFolderTestMixin, TestCase):
return d
class RealTestAliceBob(MagicFolderAliceBobTestMixin, TestCase):
@skipIf(support_missing, support_message)
class RealTestAliceBob(MagicFolderAliceBobTestMixin, AsyncTestCase):
"""This is skipped unless both Twisted and the platform support inotify."""
inject_inotify = False
@ -2032,12 +2042,3 @@ class RealTestAliceBob(MagicFolderAliceBobTestMixin, TestCase):
d = super(RealTestAliceBob, self).setUp()
self.inotify = magic_folder.get_inotify_module()
return d
try:
magic_folder.get_inotify_module()
except NotImplementedError:
msg = "Magic Folder support can only be tested for-real on an OS that " + \
"supports inotify or equivalent."
for klass in [RealTest, MockTest, MockTestAliceBob, RealTestAliceBob]:
klass.skip = msg