mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2024-12-27 16:28:53 +00:00
172 lines
5.6 KiB
Python
172 lines
5.6 KiB
Python
# Copyright (c) Twisted Matrix Laboratories.
|
|
# See LICENSE for details.
|
|
|
|
"""
|
|
Tests for the inotify-alike implementation L{allmydata.watchdog}.
|
|
"""
|
|
|
|
# Note: See https://twistedmatrix.com/trac/ticket/8915 for a proposal
|
|
# to avoid all of this duplicated code from Twisted.
|
|
|
|
from twisted.internet import defer, reactor
|
|
from twisted.python import filepath, runtime
|
|
|
|
from allmydata.frontends.magic_folder import get_inotify_module
|
|
from .common import (
|
|
AsyncTestCase,
|
|
skipIf,
|
|
)
|
|
inotify = get_inotify_module()
|
|
|
|
|
|
@skipIf(runtime.platformType == "win32", "inotify does not yet work on windows")
|
|
class INotifyTests(AsyncTestCase):
|
|
"""
|
|
Define all the tests for the basic functionality exposed by
|
|
L{inotify.INotify}.
|
|
"""
|
|
def setUp(self):
|
|
self.dirname = filepath.FilePath(self.mktemp())
|
|
self.dirname.createDirectory()
|
|
self.inotify = inotify.INotify()
|
|
self.inotify.startReading()
|
|
self.addCleanup(self.inotify.stopReading)
|
|
return super(INotifyTests, self).setUp()
|
|
|
|
|
|
def _notificationTest(self, mask, operation, expectedPath=None):
|
|
"""
|
|
Test notification from some filesystem operation.
|
|
|
|
@param mask: The event mask to use when setting up the watch.
|
|
|
|
@param operation: A function which will be called with the
|
|
name of a file in the watched directory and which should
|
|
trigger the event.
|
|
|
|
@param expectedPath: Optionally, the name of the path which is
|
|
expected to come back in the notification event; this will
|
|
also be passed to C{operation} (primarily useful when the
|
|
operation is being done to the directory itself, not a
|
|
file in it).
|
|
|
|
@return: A L{Deferred} which fires successfully when the
|
|
expected event has been received or fails otherwise.
|
|
"""
|
|
if expectedPath is None:
|
|
expectedPath = self.dirname.child("foo.bar")
|
|
notified = defer.Deferred()
|
|
def cbNotified(result):
|
|
(watch, filename, events) = result
|
|
self.assertEqual(filename.asBytesMode(), expectedPath.asBytesMode())
|
|
self.assertTrue(events & mask)
|
|
self.inotify.ignore(self.dirname)
|
|
notified.addCallback(cbNotified)
|
|
|
|
def notify_event(*args):
|
|
notified.callback(args)
|
|
self.inotify.watch(
|
|
self.dirname, mask=mask,
|
|
callbacks=[notify_event])
|
|
operation(expectedPath)
|
|
return notified
|
|
|
|
|
|
def test_modify(self):
|
|
"""
|
|
Writing to a file in a monitored directory sends an
|
|
C{inotify.IN_MODIFY} event to the callback.
|
|
"""
|
|
def operation(path):
|
|
with path.open("w") as fObj:
|
|
fObj.write(b'foo')
|
|
|
|
return self._notificationTest(inotify.IN_MODIFY, operation)
|
|
|
|
|
|
def test_attrib(self):
|
|
"""
|
|
Changing the metadata of a file in a monitored directory
|
|
sends an C{inotify.IN_ATTRIB} event to the callback.
|
|
"""
|
|
def operation(path):
|
|
# Create the file.
|
|
path.touch()
|
|
# Modify the file's attributes.
|
|
path.touch()
|
|
|
|
return self._notificationTest(inotify.IN_ATTRIB, operation)
|
|
|
|
|
|
def test_closeWrite(self):
|
|
"""
|
|
Closing a file which was open for writing in a monitored
|
|
directory sends an C{inotify.IN_CLOSE_WRITE} event to the
|
|
callback.
|
|
"""
|
|
def operation(path):
|
|
path.open("w").close()
|
|
|
|
return self._notificationTest(inotify.IN_CLOSE_WRITE, operation)
|
|
|
|
|
|
def test_delete(self):
|
|
"""
|
|
Deleting a file in a monitored directory sends an
|
|
C{inotify.IN_DELETE} event to the callback.
|
|
"""
|
|
expectedPath = self.dirname.child("foo.bar")
|
|
expectedPath.touch()
|
|
notified = defer.Deferred()
|
|
def cbNotified(result):
|
|
(watch, filename, events) = result
|
|
self.assertEqual(filename.asBytesMode(), expectedPath.asBytesMode())
|
|
self.assertTrue(events & inotify.IN_DELETE)
|
|
notified.addCallback(cbNotified)
|
|
self.inotify.watch(
|
|
self.dirname, mask=inotify.IN_DELETE,
|
|
callbacks=[lambda *args: notified.callback(args)])
|
|
expectedPath.remove()
|
|
return notified
|
|
|
|
|
|
def test_humanReadableMask(self):
|
|
"""
|
|
L{inotify.humanReadableMask} translates all the possible event masks to a
|
|
human readable string.
|
|
"""
|
|
for mask, value in inotify._FLAG_TO_HUMAN:
|
|
self.assertEqual(inotify.humanReadableMask(mask)[0], value)
|
|
|
|
checkMask = (
|
|
inotify.IN_CLOSE_WRITE | inotify.IN_ACCESS | inotify.IN_OPEN)
|
|
self.assertEqual(
|
|
set(inotify.humanReadableMask(checkMask)),
|
|
set(['close_write', 'access', 'open']))
|
|
|
|
|
|
def test_noAutoAddSubdirectory(self):
|
|
"""
|
|
L{inotify.INotify.watch} with autoAdd==False will stop inotify
|
|
from watching subdirectories created under the watched one.
|
|
"""
|
|
def _callback(wp, fp, mask):
|
|
# We are notified before we actually process new
|
|
# directories, so we need to defer this check.
|
|
def _():
|
|
try:
|
|
self.assertFalse(self.inotify._isWatched(subdir))
|
|
d.callback(None)
|
|
except Exception:
|
|
d.errback()
|
|
reactor.callLater(0, _)
|
|
|
|
checkMask = inotify.IN_ISDIR | inotify.IN_CREATE
|
|
self.inotify.watch(
|
|
self.dirname, mask=checkMask, autoAdd=False,
|
|
callbacks=[_callback])
|
|
subdir = self.dirname.child('test')
|
|
d = defer.Deferred()
|
|
subdir.createDirectory()
|
|
return d
|