-
-
Notifications
You must be signed in to change notification settings - Fork 708
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #375 from danilobellini/inotify
- Loading branch information
Showing
4 changed files
with
151 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
from __future__ import unicode_literals | ||
import os | ||
import pytest | ||
import logging | ||
import contextlib | ||
from tests import Queue | ||
from functools import partial | ||
from .shell import rm, mkdtemp | ||
from watchdog.utils import platform | ||
from watchdog.events import DirCreatedEvent, DirDeletedEvent, DirModifiedEvent | ||
from watchdog.observers.api import ObservedWatch | ||
|
||
if platform.is_linux(): | ||
from watchdog.observers.inotify import InotifyFullEmitter, InotifyEmitter | ||
|
||
|
||
logging.basicConfig(level=logging.DEBUG) | ||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def setup_function(function): | ||
global p, event_queue | ||
tmpdir = os.path.realpath(mkdtemp()) | ||
p = partial(os.path.join, tmpdir) | ||
event_queue = Queue() | ||
|
||
|
||
@contextlib.contextmanager | ||
def watching(path=None, use_full_emitter=False): | ||
path = p('') if path is None else path | ||
global emitter | ||
Emitter = InotifyFullEmitter if use_full_emitter else InotifyEmitter | ||
emitter = Emitter(event_queue, ObservedWatch(path, recursive=True)) | ||
emitter.start() | ||
yield | ||
emitter.stop() | ||
emitter.join(5) | ||
|
||
|
||
def teardown_function(function): | ||
rm(p(''), recursive=True) | ||
assert not emitter.is_alive() | ||
|
||
|
||
@pytest.mark.skipif(not platform.is_linux(), | ||
reason="Testing with inotify messages (Linux only)") | ||
def test_late_double_deletion(monkeypatch): | ||
inotify_fd = type(str("FD"), (object,), {})() # Empty object | ||
inotify_fd.last = 0 | ||
inotify_fd.wds = [] | ||
|
||
# CREATE DELETE CREATE DELETE DELETE_SELF IGNORE DELETE_SELF IGNORE | ||
inotify_fd.buf = ( | ||
# IN_CREATE|IS_DIR (wd = 1, path = subdir1) | ||
b"\x01\x00\x00\x00\x00\x01\x00\x40\x00\x00\x00\x00\x10\x00\x00\x00" | ||
b"\x73\x75\x62\x64\x69\x72\x31\x00\x00\x00\x00\x00\x00\x00\x00\x00" | ||
# IN_DELETE|IS_DIR (wd = 1, path = subdir1) | ||
b"\x01\x00\x00\x00\x00\x02\x00\x40\x00\x00\x00\x00\x10\x00\x00\x00" | ||
b"\x73\x75\x62\x64\x69\x72\x31\x00\x00\x00\x00\x00\x00\x00\x00\x00" | ||
) * 2 + ( | ||
# IN_DELETE_SELF (wd = 2) | ||
b"\x02\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" | ||
# IN_IGNORE (wd = 2) | ||
b"\x02\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" | ||
# IN_DELETE_SELF (wd = 3) | ||
b"\x03\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" | ||
# IN_IGNORE (wd = 3) | ||
b"\x03\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" | ||
) | ||
|
||
os_read_bkp = os.read | ||
def fakeread(fd, length): | ||
if fd is inotify_fd: | ||
result, fd.buf = fd.buf[:length], fd.buf[length:] | ||
return result | ||
return os_read_bkp(fd, length) | ||
|
||
os_close_bkp = os.close | ||
def fakeclose(fd): | ||
if fd is not inotify_fd: | ||
os_close_bkp(fd) | ||
|
||
def inotify_init(): | ||
return inotify_fd | ||
|
||
def inotify_add_watch(fd, path, mask): | ||
fd.last += 1 | ||
logger.debug("New wd = %d" % fd.last) | ||
fd.wds.append(fd.last) | ||
return fd.last | ||
|
||
def inotify_rm_watch(fd, wd): | ||
logger.debug("Removing wd = %d" % wd) | ||
fd.wds.remove(wd) | ||
return 0 | ||
|
||
# Mocks the API! | ||
from watchdog.observers import inotify_c | ||
monkeypatch.setattr(os, "read", fakeread) | ||
monkeypatch.setattr(os, "close", fakeclose) | ||
monkeypatch.setattr(inotify_c, "inotify_init", inotify_init) | ||
monkeypatch.setattr(inotify_c, "inotify_add_watch", inotify_add_watch) | ||
monkeypatch.setattr(inotify_c, "inotify_rm_watch", inotify_rm_watch) | ||
|
||
with watching(p('')): | ||
# Watchdog Events | ||
for evt_cls in [DirCreatedEvent, DirDeletedEvent] * 2: | ||
event = event_queue.get(timeout=5)[0] | ||
assert isinstance(event, evt_cls) | ||
assert event.src_path == p('subdir1') | ||
event = event_queue.get(timeout=5)[0] | ||
assert isinstance(event, DirModifiedEvent) | ||
assert event.src_path == p('').rstrip(os.path.sep) | ||
|
||
assert inotify_fd.last == 3 # Number of directories | ||
assert inotify_fd.buf == b"" # Didn't miss any event | ||
assert inotify_fd.wds == [2, 3] # Only 1 is removed explicitly |