3
,sY                 @   s   d Z ddlmZmZmZ ddlZddlZddlmZ ddl	m
Z
mZ ddlmZmZmZ G dd	 d	eZG d
d deZG dd deZG dd deZG dd deZG dd deZG dd deZdS )z.
Tests for L{twisted._threads._threadworker}.
    )absolute_importdivisionprint_functionN)SynchronousTestCase)ThreadErrorlocal   )ThreadWorker
LockWorkerAlreadyQuitc               @   s   e Zd ZdZdS )FakeQueueEmptyz8
    L{FakeQueue}'s C{get} has exhausted the queue.
    N)__name__
__module____qualname____doc__ r   r   I/usr/lib/python3/dist-packages/twisted/_threads/test/test_threadworker.pyr      s   r   c               @   s   e Zd ZdZdS )WouldDeadlockzf
    If this were a real lock, you'd be deadlocked because the lock would be
    double-acquired.
    N)r   r   r   r   r   r   r   r   r      s   r   c               @   s    e Zd ZdZdd Zdd ZdS )
FakeThreadz
    A fake L{threading.Thread}.

    @ivar target: A target function to run.
    @type target: L{callable}

    @ivar started: Has this thread been started?
    @type started: L{bool}
    c             C   s   || _ d| _dS )z7
        Create a L{FakeThread} with a target.
        FN)targetstarted)selfr   r   r   r   __init__,   s    zFakeThread.__init__c             C   s
   d| _ dS )z)
        Set the "started" flag.
        TN)r   )r   r   r   r   start4   s    zFakeThread.startN)r   r   r   r   r   r   r   r   r   r   r   !   s   	r   c               @   s(   e Zd ZdZdd Zdd Zdd ZdS )		FakeQueuez
    A fake L{Queue} implementing C{put} and C{get}.

    @ivar items: A lit of items placed by C{put} but not yet retrieved by
        C{get}.
    @type items: L{list}
    c             C   s
   g | _ dS )z(
        Create a L{FakeQueue}.
        N)items)r   r   r   r   r   E   s    zFakeQueue.__init__c             C   s   | j j| dS )zv
        Put an item into the queue for later retrieval by L{FakeQueue.get}.

        @param item: any object
        N)r   append)r   itemr   r   r   putL   s    zFakeQueue.putc             C   s   | j st | j jdS )zR
        Get an item.

        @return: an item previously put by C{put}.
        r   )r   r   pop)r   r   r   r   getU   s    zFakeQueue.getN)r   r   r   r   r   r   r    r   r   r   r   r   <   s   	r   c               @   s(   e Zd ZdZdd Zdd Zdd ZdS )	FakeLockzi
    A stand-in for L{threading.Lock}.

    @ivar acquired: Whether this lock is presently acquired.
    c             C   s
   d| _ dS )z9
        Create a lock in the un-acquired state.
        FN)acquired)r   r   r   r   r   h   s    zFakeLock.__init__c             C   s   | j rt d| _ dS )zX
        Acquire the lock.  Raise an exception if the lock is already acquired.
        TN)r"   r   )r   r   r   r   acquireo   s    zFakeLock.acquirec             C   s   | j st d| _ dS )zf
        Release the lock.  Raise an exception if the lock is not presently
        acquired.
        FN)r"   r   )r   r   r   r   releasex   s    zFakeLock.releaseN)r   r   r   r   r   r#   r$   r   r   r   r   r!   a   s   	r!   c               @   s(   e Zd ZdZdd Zdd Zdd ZdS )	ThreadWorkerTestsz$
    Tests for L{ThreadWorker}.
    c                s,   g  _ t  _ fdd}t| j _dS )z4
        Create a worker with fake threads.
        c                s"   t | d}|j   jj| |S )N)r   )r   r   fakeThreadsr   )r   Z	newThread)r   r   r   startThread   s    
z,ThreadWorkerTests.setUp.<locals>.startThreadN)r&   r   Z	fakeQueuer	   worker)r   r'   r   )r   r   setUp   s    zThreadWorkerTests.setUpc                sx   | j t| jd | j | jd jd  fdd d _| jj  | j  jd | jt| jd j	 | j  jd dS )z
        L{ThreadWorker} calls its C{createThread} callable to create a thread,
        its C{createQueue} callable to create a queue, and then the thread's
        target pulls work from that queue.
           r   Tc                  s
   d _ d S )NT)doner   )doItr   r   r,      s    z@ThreadWorkerTests.test_startsThreadAndPerformsWork.<locals>.doItFN)
assertEquallenr&   r   r+   r(   doassertRaisesr   r   )r   r   )r,   r    test_startsThreadAndPerformsWork   s    z2ThreadWorkerTests.test_startsThreadAndPerformsWorkc             C   s0   | j j  | jt| j j | jt| j jt dS )z
        L{ThreadWorker.quit} causes future calls to L{ThreadWorker.do} and
        L{ThreadWorker.quit} to raise L{AlreadyQuit}.
        N)r(   quitr0   r   r/   list)r   r   r   r   test_quitPreventsFutureCalls   s    
z.ThreadWorkerTests.test_quitPreventsFutureCallsN)r   r   r   r   r)   r1   r4   r   r   r   r   r%      s   r%   c               @   sH   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d Zdd Z	dd Z
dS )LockWorkerTestsz"
    Tests for L{LockWorker}.
    c             C   s    t  }|j  | jt|j dS )ze
        The L{FakeLock} test fixture will alert us if there's a potential
        deadlock.
        N)r!   r#   r0   r   )r   lockr   r   r   test_fakeDeadlock   s    z!LockWorkerTests.test_fakeDeadlockc             C   s>   t  }| jt|j |j  | jd|j  | jt|j dS )zk
        The L{FakeLock} test fixture will alert us if there's a potential
        double-release.
        N)r!   r0   r   r$   r#   r-   )r   r6   r   r   r   test_fakeDoubleRelease   s
    z&LockWorkerTests.test_fakeDoubleReleasec                sb   t  }t  t |} fddd_|j | jjd | jjd | j jd dS )zp
        L{LockWorker.do} immediately performs the work it's given, while the
        lock is acquired.
        c                  s   d_  j_d S )NT)r+   r"   r   )r6   workr   r   r9      s    z@LockWorkerTests.test_doExecutesImmediatelyWithLock.<locals>.workFTN)r   r!   r
   r+   r/   r-   r"   )r   Zstorager(   r   )r6   r9   r   "test_doExecutesImmediatelyWithLock   s    

z2LockWorkerTests.test_doExecutesImmediatelyWithLockc                sb   t  tt g g   fddd_j | jddg | j ddg dS )z
        If L{LockWorker.do} is called recursively, it postpones the inner call
        until the outer one is complete.
        c                  sN    j d7  _ jj   jj tdk r<j  j d8  _ d S )Nr*   r   )levelr   r"   r.   r/   r   )r"   levelsr6   r9   r(   r   r   r9      s    
z6LockWorkerTests.test_doUnwindsReentrancy.<locals>.workr   r*   TN)r!   r
   r   r;   r/   r-   )r   r   )r"   r<   r6   r9   r(   r   test_doUnwindsReentrancy   s    
z(LockWorkerTests.test_doUnwindsReentrancyc             C   sn   t  }tj|}t|t }d}| j| d |j  tj  | j	| d | j
t|j | j
t|jt dS )z
        L{LockWorker.quit} frees the resources associated with its lock and
        causes further calls to C{do} and C{quit} to fail.
        N)r!   weakrefrefr
   r   ZassertIsNotr2   gccollectassertIsr0   r   r/   r3   )r   r6   r?   r(   r   r   r   	test_quit   s    
zLockWorkerTests.test_quitc                s   t   tj }t t fddd_ fddd_j jjd jjd j jd d t	j
  j| d dS )z
        If L{LockWorker.quit} is invoked during a call to L{LockWorker.do}, all
        recursive work scheduled with L{LockWorker.do} will be completed and
        the lock will be released.
        c                  s,   j  j  jtj t d _d S )NT)r/   r2   r0   r   r3   completer   )phase1phase2r   r(   r   r   rE     s    
z5LockWorkerTests.test_quitWhileWorking.<locals>.phase1Fc                  s   d_  j_d S )NT)rD   r"   r   )r6   rF   r   r   rF     s    z5LockWorkerTests.test_quitWhileWorking.<locals>.phase2TN)r!   r>   r?   r
   r   rD   r/   r-   r"   r@   rA   rB   )r   r?   r   )r6   rE   rF   r   r(   r   test_quitWhileWorking  s    

z%LockWorkerTests.test_quitWhileWorkingc             C   s2   G dd dt }|t t }| jt|jt dS )z
        If L{LockWorker.do} is called concurrently with L{LockWorker.quit}, and
        C{quit} wins the race before C{do} gets the lock attribute, then
        L{AlreadyQuit} will be raised.
        c               @   s&   e Zd Zdd Zdd ZeeeZdS )zALockWorkerTests.test_quitWhileGettingLock.<locals>.RacyLockWorkerc             S   s   | j   | jd S )N_lock)r2   __dict__)r   r   r   r   	_lock_get*  s    zKLockWorkerTests.test_quitWhileGettingLock.<locals>.RacyLockWorker._lock_getc             S   s   || j d< d S )NrH   )rI   )r   valuer   r   r   	_lock_set-  s    zKLockWorkerTests.test_quitWhileGettingLock.<locals>.RacyLockWorker._lock_setN)r   r   r   rJ   rL   propertyrH   r   r   r   r   RacyLockWorker)  s   rN   N)r
   r!   r   r0   r   r/   r3   )r   rN   r(   r   r   r   test_quitWhileGettingLock#  s    	z)LockWorkerTests.test_quitWhileGettingLockN)r   r   r   r   r7   r8   r:   r=   rC   rG   rO   r   r   r   r   r5      s   
r5   )r   Z
__future__r   r   r   r@   r>   Ztwisted.trial.unittestr   Z	threadingr   r    r	   r
   r   	Exceptionr   r   objectr   r   r!   r%   r5   r   r   r   r   <module>   s   %"/