o
    ¯bè;  ã                   @   s¨   d Z ddlZddlZddlmZmZ ddlmZ ddlm	Z	 ddl
mZ ddlmZ dZd	Ze ¡ rBe	d
ƒdu rBe	dƒdu rBdZdZG dd„ deƒZG dd„ deƒZdS )z'
Tests for L{twisted.python.lockfile}.
é    N)ÚskipIfÚ
skipUnless)Úlockfile)ÚrequireModule)Úplatform)ÚTestCaseFÚ zwin32api.OpenProcessÚ
pywintypesTzZOn windows, lockfile.kill is not implemented in the absence of win32api and/or pywintypes.c                   @   sx   e Zd ZdZdd„ Zee ¡ dƒdd„ ƒZdd„ Z	ee ¡ d	ƒd
d„ ƒZ
eeeƒdd„ ƒZeeeƒdd„ ƒZdd„ ZdS )Ú	UtilTestszM
    Tests for the helper functions used to implement L{FilesystemLock}.
    c                 C   s:   |   ¡ }t d|¡ |  ttjd|¡}|  |jtj¡ dS )z 
        L{lockfile.symlink} raises L{OSError} with C{errno} set to L{EEXIST}
        when an attempt is made to create a symlink which already exists.
        ÚfooN)Úmktempr   ÚsymlinkÚassertRaisesÚOSErrorÚassertEqualÚerrnoÚEEXIST©ÚselfÚnameÚexc© r   ú</usr/lib/python3/dist-packages/twisted/test/test_lockfile.pyÚtest_symlinkEEXIST&   s   zUtilTests.test_symlinkEEXISTúBspecial rename EIO handling only necessary and correct on Windows.c                 C   sD   |   ¡ }dd„ }|  td|¡ |  ttj|d¡}|  |jtj¡ dS )a<  
        L{lockfile.symlink} raises L{OSError} with C{errno} set to L{EIO} when
        the underlying L{rename} call fails with L{EIO}.

        Renaming a file on Windows may fail if the target of the rename is in
        the process of being deleted (directory deletion appears not to be
        atomic).
        c                 S   ó   t tjd ƒ‚©N©r   r   ÚEIO©ÚsrcÚdstr   r   r   Ú
fakeRename?   ó   z4UtilTests.test_symlinkEIOWindows.<locals>.fakeRenameÚrenamer   N)	r   Úpatchr   r   ÚIOErrorr   r   r   r   )r   r   r"   r   r   r   r   Útest_symlinkEIOWindows0   s
   z UtilTests.test_symlinkEIOWindowsc                 C   s,   |   ¡ }|  ttj|¡}|  |jtj¡ dS )zŸ
        L{lockfile.readlink} raises L{OSError} with C{errno} set to L{ENOENT}
        when an attempt is made to read a symlink which does not exist.
        N)r   r   r   r   Úreadlinkr   r   ÚENOENTr   r   r   r   Útest_readlinkENOENTF   s   zUtilTests.test_readlinkENOENTúGspecial readlink EACCES handling only necessary and correct on Windows.c                 C   sB   |   ¡ }dd„ }|  td|¡ |  ttj|¡}|  |jtj¡ dS )a\  
        L{lockfile.readlink} raises L{OSError} with C{errno} set to L{EACCES}
        on Windows when the underlying file open attempt fails with C{EACCES}.

        Opening a file on Windows may fail if the path is inside a directory
        which is in the process of being deleted (directory deletion appears
        not to be atomic).
        c                 S   r   r   ©r   r   ÚEACCES)ÚpathÚmoder   r   r   ÚfakeOpen^   r#   z6UtilTests.test_readlinkEACCESWindows.<locals>.fakeOpenÚ_openN)	r   r%   r   r   r&   r(   r   r   r-   )r   r   r0   r   r   r   r   Útest_readlinkEACCESWindowsO   s
   z$UtilTests.test_readlinkEACCESWindowsc                 C   s   t  t ¡ d¡ dS )z}
        L{lockfile.kill} returns without error if passed the PID of a
        process which exists and signal C{0}.
        r   N)r   ÚkillÚosÚgetpid©r   r   r   r   Ú	test_kille   s   zUtilTests.test_killc                 C   s&   |   ttjdd¡}|  |jtj¡ dS )z
        L{lockfile.kill} raises L{OSError} with errno of L{ESRCH} if
        passed a PID which does not correspond to any process.
        iÿÿÿr   N)r   r   r   r3   r   r   ÚESRCH)r   r   r   r   r   Útest_killESRCHm   s   zUtilTests.test_killESRCHc                 C   s6   |   tdd¡ t |  ¡ ¡}| ¡  |  | ¡ ¡ dS )zµ
        Verify that when L{lockfile.kill} does end up as None (e.g. on Windows
        without pywin32), it doesn't end up being called and raising a
        L{TypeError}.
        r3   N)r%   r   ÚFilesystemLockr   ÚlockÚassertFalse)r   Úflr   r   r   Útest_noKillCallw   s   zUtilTests.test_noKillCallN)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r   r   Ú	isWindowsr'   r*   r2   r   ÚskipKillÚskipKillReasonr7   r9   r>   r   r   r   r   r
   !   s&    
þ
	þ


	r
   c                   @   sÔ   e Zd Zdd„ Zdd„ Zee ¡ dƒdd„ ƒZdd	„ Z	d
d„ Z
dd„ Zdd„ Zdd„ Zee ¡ dƒdd„ ƒZee ¡ dƒdd„ ƒZdd„ Zdd„ Zee ¡ dƒdd„ ƒZdd„ Zd d!„ Zd"d#„ Zd$d%„ Zd&d'„ Zd(S ))ÚLockingTestsc                    sL   ‡ fdd„}|   td|¡ |  ¡ }t |¡}|  t|j¡}|  |jˆ ¡ d S )Nc                    s
   t ˆ d ƒ‚r   )r   )ÚsourceÚdest©r   r   r   ÚfakeSymlink…   ó   
z3LockingTests._symlinkErrorTest.<locals>.fakeSymlinkr   )	r%   r   r   r:   r   r   r;   r   r   )r   r   rJ   Úlockfr;   r   r   rI   r   Ú_symlinkErrorTest„   s   
zLockingTests._symlinkErrorTestc                 C   s   |   tj¡ dS )z†
        An exception raised by C{symlink} other than C{EEXIST} is passed up to
        the caller of L{FilesystemLock.lock}.
        N)rM   r   ÚENOSYSr6   r   r   r   Útest_symlinkError   s   zLockingTests.test_symlinkErrorz9POSIX-specific error propagation not expected on Windows.c                 C   s   |   tj¡ |   tj¡ dS )a   
        An L{OSError} raised by C{symlink} on a POSIX platform with an errno of
        C{EACCES} or C{EIO} is passed to the caller of L{FilesystemLock.lock}.

        On POSIX, unlike on Windows, these are unexpected errors which cannot
        be handled by L{FilesystemLock}.
        N)rM   r   r-   r   r6   r   r   r   Útest_symlinkErrorPOSIX–   s   z#LockingTests.test_symlinkErrorPOSIXc                 C   s<   |   ¡ }t |¡}|  | ¡ ¡ |  |j¡ |  |j¡ dS )z‹
        If the lock has never been held, it can be acquired and the C{clean}
        and C{locked} attributes are set to C{True}.
        N)r   r   r:   Ú
assertTruer;   ÚcleanÚlocked©r   rL   r;   r   r   r   Útest_cleanlyAcquire¥   s
   
z LockingTests.test_cleanlyAcquirec                 C   sh   |   ¡ }t |¡}|  | ¡ ¡ | ¡  |  |j¡ t |¡}|  | ¡ ¡ |  |j¡ |  |j¡ dS )zŒ
        If a lock is released cleanly, it can be re-acquired and the C{clean}
        and C{locked} attributes are set to C{True}.
        N)	r   r   r:   rQ   r;   Úunlockr<   rS   rR   rT   r   r   r   Útest_cleanlyRelease°   s   

z LockingTests.test_cleanlyReleasec                 C   sH   |   ¡ }t |¡}|  | ¡ ¡ t |¡}|  | ¡ ¡ |  |j¡ dS )zK
        If a lock is currently locked, it cannot be locked again.
        N)r   r   r:   rQ   r;   r<   rS   )r   rL   Ú	firstLockÚ
secondLockr   r   r   Útest_cannotLockLockedÀ   s   

z"LockingTests.test_cannotLockLockedc                    s„   d‰ ‡ fdd„}|   ¡ }|  td|¡ t tˆ ƒ|¡ t |¡}|  | ¡ ¡ |  |j	¡ |  |j
¡ |  t |¡tt ¡ ƒ¡ dS )zÇ
        If a lock was held by a process which no longer exists, it can be
        acquired, the C{clean} attribute is set to C{False}, and the
        C{locked} attribute is set to C{True}.
        i90  c                    s,   |dkr
t tjd ƒ‚| ˆ krt tjd ƒ‚d S )Nr   ©r   r   ÚEPERMr8   ©ÚpidÚsignal©Úownerr   r   ÚfakeKillÔ   ó
   ÿz4LockingTests.test_uncleanlyAcquire.<locals>.fakeKillr3   N)r   r%   r   r   Ústrr:   rQ   r;   r<   rR   rS   r   r(   r4   r5   )r   rb   rL   r;   r   r`   r   Útest_uncleanlyAcquireÌ   s   
z"LockingTests.test_uncleanlyAcquirec                    ó~   ‡ ‡fdd„}|   td|¡‰dd„ }|   td|¡ |  ¡ ‰ t ˆ ¡}t tdƒˆ ¡ |  | ¡ ¡ |  |j¡ |  |j	¡ dS )	zð
        If the lock is initially held but then released before it can be
        examined to determine if the process which held it still exists, it is
        acquired and the C{clean} and C{locked} attributes are set to C{True}.
        c                    s   t  ˆ ¡ ˆ ¡  t  | ¡S r   )r   ÚrmlinkÚrestorer(   ©r   ©rL   ÚreadlinkPatchr   r   ÚfakeReadlinkì   s   

z?LockingTests.test_lockReleasedBeforeCheck.<locals>.fakeReadlinkr(   c                 S   ó,   |dkr
t tjd ƒ‚| dkrt tjd ƒ‚d S ©Nr   éu¨  r[   r]   r   r   r   rb   õ   rc   z;LockingTests.test_lockReleasedBeforeCheck.<locals>.fakeKillr3   ro   N©
r%   r   r   r:   r   rd   rQ   r;   rR   rS   )r   rl   rb   r;   r   rj   r   Útest_lockReleasedBeforeCheckå   ó   
z)LockingTests.test_lockReleasedBeforeCheckr   c                 C   sF   dd„ }|   td|¡ |  ¡ }t |¡}|  | ¡ ¡ |  |j¡ dS )a“  
        If the lock is released while an attempt is made to acquire
        it, the lock attempt fails and C{FilesystemLock.lock} returns
        C{False}.  This can happen on Windows when L{lockfile.symlink}
        fails with L{IOError} of C{EIO} because another process is in
        the middle of a call to L{os.rmdir} (implemented in terms of
        RemoveDirectory) which is not atomic.
        c                 S   r   r   r   r   r   r   r   rJ     s   zGLockingTests.test_lockReleasedDuringAcquireSymlink.<locals>.fakeSymlinkr   N)r%   r   r   r:   r<   r;   rS   )r   rJ   rL   r;   r   r   r   Ú%test_lockReleasedDuringAcquireSymlink  s   
z2LockingTests.test_lockReleasedDuringAcquireSymlinkr+   c                 C   sV   dd„ }|   td|¡ |  ¡ }t |¡}t tdƒ|¡ |  | ¡ ¡ |  |j¡ dS )z»
        If the lock is initially held but is released while an attempt
        is made to acquire it, the lock attempt fails and
        L{FilesystemLock.lock} returns C{False}.
        c                 S   r   r   r,   ri   r   r   r   rl   )  s   zILockingTests.test_lockReleasedDuringAcquireReadlink.<locals>.fakeReadlinkr(   ro   N)	r%   r   r   r:   r   rd   r<   r;   rS   )r   rl   rL   r;   r   r   r   Ú&test_lockReleasedDuringAcquireReadlink  s   
z3LockingTests.test_lockReleasedDuringAcquireReadlinkc                    sj   ‡ ‡fdd„}|   td|¡ |  ¡ }t tdƒ|¡ t |¡}|  ˆ|j¡}|  |j	ˆ ¡ |  
|j¡ d S )Nc                    s
   ˆˆ d ƒ‚r   r   ri   ©r   ÚexceptionTyper   r   rl   8  rK   z5LockingTests._readlinkErrorTest.<locals>.fakeReadlinkr(   ro   )r%   r   r   r   rd   r:   r   r;   r   r   r<   rS   )r   rv   r   rl   rL   r;   r   r   ru   r   Ú_readlinkErrorTest7  s   
zLockingTests._readlinkErrorTestc                 C   s    |   ttj¡ |   ttj¡ dS )z‡
        An exception raised by C{readlink} other than C{ENOENT} is passed up to
        the caller of L{FilesystemLock.lock}.
        N)rw   r   r   rN   r&   r6   r   r   r   Útest_readlinkErrorG  s   zLockingTests.test_readlinkErrorc                 C   s    |   ttj¡ |   ttj¡ dS )zú
        Any L{IOError} raised by C{readlink} on a POSIX platform passed to the
        caller of L{FilesystemLock.lock}.

        On POSIX, unlike on Windows, these are unexpected errors which cannot
        be handled by L{FilesystemLock}.
        N)rw   r&   r   rN   r-   r6   r   r   r   Útest_readlinkErrorPOSIXO  s   z$LockingTests.test_readlinkErrorPOSIXc                    rf   )	z×
        If a second process cleans up the lock after a first one checks the
        lock and finds that no process is holding it, the first process does
        not fail when it tries to clean up the lock.
        c                    s   ˆ  ¡  t ˆ ¡ t | ¡S r   )rh   r   rg   ri   ©rL   ÚrmlinkPatchr   r   Ú
fakeRmlinke  s   

z?LockingTests.test_lockCleanedUpConcurrently.<locals>.fakeRmlinkrg   c                 S   rm   rn   r[   r]   r   r   r   rb   n  rc   z=LockingTests.test_lockCleanedUpConcurrently.<locals>.fakeKillr3   ro   Nrp   )r   r|   rb   r;   r   rz   r   Útest_lockCleanedUpConcurrently^  rr   z+LockingTests.test_lockCleanedUpConcurrentlyc                 C   s|   dd„ }|   td|¡ dd„ }|   td|¡ |  ¡ }t tdƒ|¡ t |¡}|  t|j¡}|  	|j
t
j¡ |  |j¡ dS )	z…
        An exception raised by L{rmlink} other than C{ENOENT} is passed up
        to the caller of L{FilesystemLock.lock}.
        c                 S   r   r   )r   r   rN   ri   r   r   r   r|   ƒ  r#   z1LockingTests.test_rmlinkError.<locals>.fakeRmlinkrg   c                 S   rm   rn   r[   r]   r   r   r   rb   ˆ  rc   z/LockingTests.test_rmlinkError.<locals>.fakeKillr3   ro   N)r%   r   r   r   rd   r:   r   r   r;   r   r   rN   r<   rS   )r   r|   rb   rL   r;   r   r   r   r   Útest_rmlinkError}  s   
zLockingTests.test_rmlinkErrorc                 C   sf   dd„ }|   td|¡ |  ¡ }t tdƒ|¡ t |¡}|  t|j¡}|  	|j
t
j¡ |  |j¡ dS )z¶
        If L{kill} raises an exception other than L{OSError} with errno set to
        C{ESRCH}, the exception is passed up to the caller of
        L{FilesystemLock.lock}.
        c                 S   r   r   )r   r   r\   r]   r   r   r   rb   ¡  r#   z-LockingTests.test_killError.<locals>.fakeKillr3   ro   N)r%   r   r   r   rd   r:   r   r   r;   r   r   r\   r<   rS   )r   rb   rL   r;   r   r   r   r   Útest_killErrorš  s   
zLockingTests.test_killErrorc                 C   s<   |   ¡ }t tt ¡ d ƒ|¡ t |¡}|  t|j	¡ dS )z‚
        L{FilesystemLock.unlock} raises L{ValueError} if called for a lock
        which is held by a different process.
        é   N)
r   r   r   rd   r4   r5   r:   r   Ú
ValueErrorrV   rT   r   r   r   Útest_unlockOther°  s   
zLockingTests.test_unlockOtherc                 C   s\   |   ¡ }|  t |¡¡ t |¡}|  | ¡ ¡ |  t |¡¡ | ¡  |  t |¡¡ dS )zp
        L{isLocked} returns C{True} if the named lock is currently locked,
        C{False} otherwise.
        N)r   r<   r   ÚisLockedr:   rQ   r;   rV   rT   r   r   r   Útest_isLockedº  s   
zLockingTests.test_isLockedN)r?   r@   rA   rM   rO   r   r   rC   rP   rU   rW   rZ   re   rq   r   rs   rt   rw   rx   ry   r}   r~   r   r‚   r„   r   r   r   r   rF   ƒ   sF    þ
þ
þ
þ

rF   )rB   r   r4   Úunittestr   r   Útwisted.pythonr   Útwisted.python.reflectr   Útwisted.python.runtimer   Útwisted.trial.unittestr   rD   rE   rC   r
   rF   r   r   r   r   Ú<module>   s$   ÿb