Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests/test_inotify_c.py failures on big endian systems #804

Closed
mgorny opened this issue Jun 11, 2021 · 1 comment
Closed

tests/test_inotify_c.py failures on big endian systems #804

mgorny opened this issue Jun 11, 2021 · 1 comment
Labels

Comments

@mgorny
Copy link
Contributor

mgorny commented Jun 11, 2021

FAILED tests/test_inotify_c.py::test_late_double_deletion - _queue.Empty
ERROR tests/test_inotify_c.py::test_late_double_deletion - assert not True
ERROR tests/test_inotify_c.py::test_raise_error - assert not True

This is on aadb53f with Python 3.9.4. I have reproduced it on ppc64 system but I've gotten reports of the same problem on hppa and sparc systems. I've also gotten confirmation that the tests pass on ppc64le, so while I'm not 100% sure, I think this is an endianness issue.

That said, I suppose you already know but for completeness I have to say that using ctypes and hardcoding system constants is generally a bad idea (and Linux is known to have different constant values across different architectures, though it may not be the case for inotify). It's better to use cffi or even true C extension.

Full pytest output: pytest.txt

==================================== ERRORS ====================================
________________ ERROR at teardown of test_late_double_deletion ________________

function = <function test_late_double_deletion at 0x3f07738b6d30>

    def teardown_function(function):
        rm(p(''), recursive=True)
        try:
>           assert not emitter.is_alive()
E           assert not True
E            +  where True = <bound method Thread.is_alive of <InotifyEmitter(Thread-49, started daemon 69301226885360)>>()
E            +    where <bound method Thread.is_alive of <InotifyEmitter(Thread-49, started daemon 69301226885360)>> = <InotifyEmitter(Thread-49, started daemon 69301226885360)>.is_alive

function   = <function test_late_double_deletion at 0x3f07738b6d30>

tests/test_inotify_c.py:50: AssertionError
____________________ ERROR at teardown of test_raise_error _____________________

function = <function test_raise_error at 0x3f07738b6dc0>

    def teardown_function(function):
        rm(p(''), recursive=True)
        try:
>           assert not emitter.is_alive()
E           assert not True
E            +  where True = <bound method Thread.is_alive of <InotifyEmitter(Thread-49, started daemon 69301226885360)>>()
E            +    where <bound method Thread.is_alive of <InotifyEmitter(Thread-49, started daemon 69301226885360)>> = <InotifyEmitter(Thread-49, started daemon 69301226885360)>.is_alive

function   = <function test_raise_error at 0x3f07738b6dc0>

tests/test_inotify_c.py:50: AssertionError
=================================== FAILURES ===================================
__________________________ test_late_double_deletion ___________________________

monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x3f0773865790>

    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]

evt_cls    = <class 'watchdog.events.DirCreatedEvent'>
fakeclose  = <function test_late_double_deletion.<locals>.fakeclose at 0x3f07738adc10>
fakeread   = <function test_late_double_deletion.<locals>.fakeread at 0x3f07738ad550>
inotify_add_watch = <function test_late_double_deletion.<locals>.inotify_add_watch at 0x3f07738adaf0>
inotify_c  = <module 'watchdog.observers.inotify_c' from '/home/mgorny/venv/lib/python3.9/site-packages/watchdog/observers/inotify_c.py'>
inotify_fd = <tests.test_inotify_c.FD object at 0x3f07727c9a90>
inotify_init = <function test_late_double_deletion.<locals>.inotify_init at 0x3f07738adca0>
inotify_rm_watch = <function test_late_double_deletion.<locals>.inotify_rm_watch at 0x3f07738adb80>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x3f0773865790>
os_close_bkp = <built-in function close>
os_read_bkp = <built-in function read>

tests/test_inotify_c.py:118: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <queue.Queue object at 0x3f07738654f0>, block = True, timeout = 5

    def get(self, block=True, timeout=None):
        '''Remove and return an item from the queue.
    
        If optional args 'block' is true and 'timeout' is None (the default),
        block if necessary until an item is available. If 'timeout' is
        a non-negative number, it blocks at most 'timeout' seconds and raises
        the Empty exception if no item was available within that time.
        Otherwise ('block' is false), return an item if one is immediately
        available, else raise the Empty exception ('timeout' is ignored
        in that case).
        '''
        with self.not_empty:
            if not block:
                if not self._qsize():
                    raise Empty
            elif timeout is None:
                while not self._qsize():
                    self.not_empty.wait()
            elif timeout < 0:
                raise ValueError("'timeout' must be a non-negative number")
            else:
                endtime = time() + timeout
                while not self._qsize():
                    remaining = endtime - time()
                    if remaining <= 0.0:
>                       raise Empty
E                       _queue.Empty

block      = True
endtime    = 657242.677718715
remaining  = -0.00019170099403709173
self       = <queue.Queue object at 0x3f07738654f0>
timeout    = 5

/usr/lib/python3.9/queue.py:179: Empty
@mgorny
Copy link
Contributor Author

mgorny commented Aug 19, 2021

So I've finally gotten around to debug it… and realized that the test mock hardcodes raw struct inotify with little-endian numbers. Will make a PR today.

mgorny added a commit to mgorny/watchdog that referenced this issue Aug 19, 2021
Fix the mocked inotify data to respect system endianness.  Instead of
harcoding the raw data, reconstruct it using struct.pack(), respecting
host endianness.  This should also benefit readability a bit.

Closes gorakhargosh#804
mgorny added a commit to mgorny/watchdog that referenced this issue Aug 19, 2021
Fix the mocked inotify data to respect system endianness.  Instead of
harcoding the raw data, reconstruct it using struct.pack(), respecting
host endianness.  This should also benefit readability a bit.

Closes gorakhargosh#804
mgorny added a commit to mgorny/watchdog that referenced this issue Aug 19, 2021
Fix the mocked inotify data to respect system endianness.  Instead of
harcoding the raw data, reconstruct it using struct.pack(), respecting
host endianness.  This should also benefit readability a bit.

Closes gorakhargosh#804
mgorny added a commit to mgorny/watchdog that referenced this issue Aug 19, 2021
Fix the mocked inotify data to respect system endianness.  Instead of
harcoding the raw data, reconstruct it using struct.pack(), respecting
host endianness.  This should also benefit readability a bit.

Closes gorakhargosh#804
mgorny added a commit to mgorny/watchdog that referenced this issue Aug 19, 2021
Fix the mocked inotify data to respect system endianness.  Instead of
harcoding the raw data, reconstruct it using struct.pack(), respecting
host endianness.  This should also benefit readability a bit.

Closes gorakhargosh#804
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants