U
    1ÜŸj3V  ã                   @   sÔ   d dl Z d dlZd dlZd dlZd dlZd dlZd dlmZmZ dd„ Z	G dd„ deƒZ
ee	ƒ  dƒG dd	„ d	e
ƒƒZee	ƒ  dƒG d
d„ de
ƒƒZee	ƒ  dƒG dd„ deƒƒZee	ƒ  dƒG dd„ de
ƒƒZdS )é    N)ÚskipIfÚTestCasec                   C   s,   zt dƒ W n tk
r"   Y dS X dS d S )NZtwistedFT)Ú
__import__ÚImportError© r   r   ú?/tmp/pip-unpacked-wheel-43vki919/automat/_test/test_discover.pyÚisTwistedInstalled
   s
    r   c                       s\   e Zd ZdZ‡ fdd„Z‡ fdd„Zdd„ Zdd	„ Zd
d„ Zdd„ Z	dd„ Z
dd„ Z‡  ZS )Ú_WritesPythonModuleszG
    A helper that enables generating Python module test fixtures.
    c                    sv   t t| ƒ ¡  ddlm}m} ddlm} || _|| _|| _tt	j
 ¡ ƒ| _t	jd d … | _t ¡ | _|  | j¡ d S )Nr   )Ú	getModuleÚ
PythonPath)ÚFilePath)Úsuperr	   ÚsetUpÚtwisted.python.modulesr
   r   Ztwisted.python.filepathr   ÚsetÚsysÚmodulesÚkeysÚoriginalSysModulesÚpathÚsavedSysPathÚtempfileÚmkdtempÚpathDirÚmakeImportable)Úselfr
   r   r   ©Ú	__class__r   r   r      s    
z_WritesPythonModules.setUpc                    sP   t t| ƒ ¡  | jtjd d …< tj ¡ | j }|D ]}tj|= q2t	 
| j¡ d S ©N)r   r	   ÚtearDownr   r   r   r   r   r   ÚshutilÚrmtreer   )r   ZmodulesToDeleteÚmoduler   r   r   r   (   s    
z_WritesPythonModules.tearDownc                 C   s   t j |¡ d S r   )r   r   Úappend)r   r   r   r   r   r   2   s    z#_WritesPythonModules.makeImportablec              	   C   sJ   |   |¡}| |¡}t|jdƒ�}| t |¡¡ W 5 Q R X |  |jg¡S )NÚw)r   ÚchildÚopenr   ÚwriteÚtextwrapÚdedentr   )r   Úsourcer   Ú
moduleNameÚ	directoryr"   Úfr   r   r   ÚwriteSourceInto5   s
    

z$_WritesPythonModules.writeSourceIntoc                 C   s"   t j |¡\}}|  |||¡| S r   )Úosr   Úsplitextr.   )r   r*   r   r+   ZpythonModuleNameÚ_r   r   r   Ú
makeModule@   s    z_WritesPythonModules.makeModulec                 C   s   dd„ |  ¡ D ƒS )Nc                 S   s   i | ]}|j |“qS r   )Úname)Ú.0Úattrr   r   r   Ú
<dictcomp>E   s      z9_WritesPythonModules.attributesAsDict.<locals>.<dictcomp>)ZiterAttributes)r   ZhasIterAttributesr   r   r   ÚattributesAsDictD   s    z%_WritesPythonModules.attributesAsDictc                 C   s   |  ¡  |  |¡S r   )Úloadr7   )r   r"   r   r   r   ÚloadModuleAsDictG   s    z%_WritesPythonModules.loadModuleAsDictc                 C   s   |   |  |||¡¡S r   )r9   r2   )r   r*   r   r3   r   r   r   ÚmakeModuleAsDictK   s    z%_WritesPythonModules.makeModuleAsDict)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r   r   r.   r2   r7   r9   r:   Ú__classcell__r   r   r   r   r	      s   
r	   zTwisted is not installed.c                       s8   e Zd ZdZ‡ fdd„Zdd„ Zdd„ Zdd	„ Z‡  ZS )
ÚOriginalLocationTestsa˜  
    Tests that L{isOriginalLocation} detects when a
    L{PythonAttribute}'s FQPN refers to an object inside the module
    where it was defined.

    For example: A L{twisted.python.modules.PythonAttribute} with a
    name of 'foo.bar' that refers to a 'bar' object defined in module
    'baz' does *not* refer to bar's original location, while a
    L{PythonAttribute} with a name of 'baz.bar' does.

    c                    s$   t t| ƒ ¡  ddlm} || _d S )Né   )ÚisOriginalLocation)r   r@   r   Ú	_discoverrB   )r   rB   r   r   r   r   ]   s    zOriginalLocationTests.setUpc                 C   s,   d}|   || jd¡}|  |  |d ¡¡ dS )zŽ
        L{isOriginalLocation} returns False when the attribute refers to an
        object whose source module cannot be determined.
        z~        class Fake(object):
            pass
        hasEmptyModule = Fake()
        hasEmptyModule.__module__ = None
        zempty_module_attr.pyz empty_module_attr.hasEmptyModuleN)r:   r   ÚassertFalserB   )r   r*   Ú
moduleDictr   r   r   Útest_failsWithNoModulec   s
    ÿz,OriginalLocationTests.test_failsWithNoModulec                 C   s~   d}d}|   || jd¡ |  || jd¡}|  |  |d ¡¡ |  |  |d ¡¡ |d }|  |¡}|d }|  |  |¡¡ d	S )
z™
        L{isOriginalLocation} returns False when the attribute refers to
        an object outside of the module where that object was defined.
        zâ        class ImportThisClass(object):
            pass
        importThisObject = ImportThisClass()
        importThisNestingObject = ImportThisClass()
        importThisNestingObject.nestedObject = ImportThisClass()
        zž        from original import (ImportThisClass,
                              importThisObject,
                              importThisNestingObject)
        úoriginal.pyúimporting.pyzimporting.ImportThisClasszimporting.importThisObjectz!importing.importThisNestingObjectz.importing.importThisNestingObject.nestedObjectN)r2   r   r:   rD   rB   r7   )r   ÚoriginalSourceÚimportingSourceZimportingDictÚnestingObjectÚnestingObjectDictÚnestedObjectr   r   r   Útest_failsWithDifferentModuleu   s(      ÿÿÿ
ÿz3OriginalLocationTests.test_failsWithDifferentModulec                 C   sp   t  d¡}|  || jd¡}|  |  |d ¡¡ |  |  |d ¡¡ |d }|  |¡}|d }|  |  |¡¡ dS )z”
        L{isOriginalLocation} returns True when the attribute refers to an
        object inside the module where that object was defined.
        zé
        class ThisClassWasDefinedHere(object):
            pass
        anObject = ThisClassWasDefinedHere()
        aNestingObject = ThisClassWasDefinedHere()
        aNestingObject.nestedObject = ThisClassWasDefinedHere()
        zm.pyzm.ThisClassWasDefinedHerezm.aNestingObjectzm.aNestingObject.nestedObjectN)r(   r)   r:   r   Ú
assertTruerB   r7   )r   ZmSourceZmDictrK   rL   rM   r   r   r   Útest_succeedsWithSameModule›   s    ÿ	
z1OriginalLocationTests.test_succeedsWithSameModule)	r;   r<   r=   r>   r   rF   rN   rP   r?   r   r   r   r   r@   O   s
   &r@   c                       sv   e Zd ZdZ‡ fdd„Zdd„ Zddœdd	„Zd
d„ Zdd„ Zdd„ Z	dd„ Z
dd„ Zdd„ Zdd„ Zdd„ Z‡  ZS )ÚFindMachinesViaWrapperTestszÍ
    L{findMachinesViaWrapper} recursively yields FQPN,
    L{MethodicalMachine} pairs in and under a given
    L{twisted.python.modules.PythonModule} or
    L{twisted.python.modules.PythonAttribute}.
    c                    s$   t t| ƒ ¡  ddlm} || _d S )NrA   )ÚfindMachinesViaWrapper)r   rQ   r   rC   rR   )r   rR   r   r   r   r   ½   s    z!FindMachinesViaWrapperTests.setUpc                 C   s>   d}|   || jd¡}|d }|  d| ¡ ft|  |¡ƒ¡ dS )zÄ
        When given a L{twisted.python.modules.PythonAttribute} that refers
        directly to a L{MethodicalMachine}, L{findMachinesViaWrapper}
        yields that machine and its FQPN.
        úa        from automat import MethodicalMachine

        rootMachine = MethodicalMachine()
        úroot.pyúroot.rootMachineN©r:   r   ÚassertInr8   ÚlistrR   ©r   r*   rE   ÚrootMachiner   r   r   Útest_yieldsMachineÃ   s    
þz.FindMachinesViaWrapperTests.test_yieldsMachineN)Úreturnc                 C   s>   d}|   || jd¡}|d }|  d| ¡ ft|  |¡ƒ¡ dS )z¾
        When given a L{twisted.python.modules.PythonAttribute} that refers
        directly to a L{TypeMachine}, L{findMachinesViaWrapper} yields that
        machine and its FQPN.
        ae          from automat import TypeMachineBuilder
        from typing import Protocol, Callable
        class P(Protocol):
            def method(self) -> None: ...
        class C:...
        def buildBuilder() -> Callable[[C], P]:
            builder = TypeMachineBuilder(P, C)
            return builder.build()
        rootMachine = buildBuilder()
        rT   rU   NrV   rY   r   r   r   Útest_yieldsTypeMachineÖ   s    
þz2FindMachinesViaWrapperTests.test_yieldsTypeMachinec                 C   s@   d}|   || jd¡}|d }|  d| ¡ jft|  |¡ƒ¡ dS )zí
        When given a L{twisted.python.modules.PythonAttribute} that refers
        to a class that contains a L{MethodicalMachine} as a class
        variable, L{findMachinesViaWrapper} yields that machine and
        its FQPN.
        úŠ        from automat import MethodicalMachine

        class PythonClass(object):
            _classMachine = MethodicalMachine()
        ú	clsmod.pyúclsmod.PythonClassú clsmod.PythonClass._classMachineN)r:   r   rW   r8   Ú_classMachinerX   rR   ©r   r*   rE   ÚPythonClassr   r   r   Útest_yieldsMachineInClassï   s    þz5FindMachinesViaWrapperTests.test_yieldsMachineInClassc                 C   sB   d}|   || jd¡}|d }|  d| ¡ jjft|  |¡ƒ¡ dS )zô
        When given a L{twisted.python.modules.PythonAttribute} that refers
        to a nested class that contains a L{MethodicalMachine} as a
        class variable, L{findMachinesViaWrapper} yields that machine
        and its FQPN.
        úµ        from automat import MethodicalMachine

        class PythonClass(object):
            class NestedClass(object):
                _classMachine = MethodicalMachine()
        únestedcls.pyúnestedcls.PythonClassú/nestedcls.PythonClass.NestedClass._classMachineN)r:   r   rW   r8   ÚNestedClassrb   rX   rR   rc   r   r   r   Útest_yieldsMachineInNestedClass  s    
þûz;FindMachinesViaWrapperTests.test_yieldsMachineInNestedClassc                 C   sD   d}|   || jd¡}|  |¡d  ¡ }|  d|ft|  |¡ƒ¡ dS )zÏ
        When given a L{twisted.python.modules.PythonModule} that refers to
        a module that contains a L{MethodicalMachine},
        L{findMachinesViaWrapper} yields that machine and its FQPN.
        rS   rT   rU   N)r2   r   r9   r8   rW   rX   rR   )r   r*   r"   rZ   r   r   r   Útest_yieldsMachineInModule  s     ÿz6FindMachinesViaWrapperTests.test_yieldsMachineInModulec                 C   sF   d}|   || jd¡}|  |¡d  ¡ }|  d|jft|  |¡ƒ¡ dS )zê
        When given a L{twisted.python.modules.PythonModule} that refers to
        the original module of a class containing a
        L{MethodicalMachine}, L{findMachinesViaWrapper} yields that
        machine and its FQPN.
        r^   r_   r`   ra   N)r2   r   r9   r8   rW   rb   rX   rR   ©r   r*   r"   rd   r   r   r   Ú!test_yieldsMachineInClassInModule-  s    þz=FindMachinesViaWrapperTests.test_yieldsMachineInClassInModulec                 C   sH   d}|   || jd¡}|  |¡d  ¡ }|  d|jjft|  |¡ƒ¡ dS )zñ
        When given a L{twisted.python.modules.PythonModule} that refers to
        the original module of a nested class containing a
        L{MethodicalMachine}, L{findMachinesViaWrapper} yields that
        machine and its FQPN.
        rf   rg   rh   ri   N)	r2   r   r9   r8   rW   rj   rb   rX   rR   rm   r   r   r   Ú'test_yieldsMachineInNestedClassInModuleA  s    þûzCFindMachinesViaWrapperTests.test_yieldsMachineInNestedClassInModulec                 C   s@   d}d}|   || jd¡ |   || jd¡}|  t|  |¡ƒ¡ dS )aM  
        When given a L{twisted.python.modules.PythonAttribute} that refers
        to a class imported from another module, any
        L{MethodicalMachine}s on that class are ignored.

        This behavior ensures that a machine is only discovered on a
        class when visiting the module where that class was defined.
        z‹
        from automat import MethodicalMachine

        class PythonClass(object):
            _classMachine = MethodicalMachine()
        z2
        from original import PythonClass
        rG   rH   N©r2   r   rD   rX   rR   )r   rI   rJ   ZimportingModuler   r   r   Útest_ignoresImportedClassZ  s
    	z5FindMachinesViaWrapperTests.test_ignoresImportedClassc           
      C   sÀ   |   | jg¡}|  | j¡ d¡}| ¡  | d¡ ¡  d}|  ||jd¡ |d }t|  	|¡t
 d¡d�}|  |d ¡}|d  ¡ }|d	  ¡ }td|fd
|jfgt
 d¡d�}	|  |	|¡ dS )z`
        L{findMachinesViaWrapper} descends into packages to discover
        machines.
        Útest_packageú__init__.pyz¸
        from automat import MethodicalMachine


        class PythonClass(object):
            _classMachine = MethodicalMachine()


        rootMachine = MethodicalMachine()
        ú	module.pyr   ©Úkeyr"   ztest_package.module.rootMachineztest_package.module.PythonClassz-test_package.module.PythonClass._classMachineN)r   r   r   r%   ÚmakedirsÚtouchr2   r   ÚsortedrR   ÚoperatorÚ
itemgetterr9   r8   rb   ÚassertEqual)
r   Ú
pythonPathÚpackager*   rr   ÚmachinesrE   rZ   rd   ÚexpectedMachinesr   r   r   Útest_descendsIntoPackagess  s.    
 ÿþþøz5FindMachinesViaWrapperTests.test_descendsIntoPackagesc                 C   s,   d}|   || jd¡}|  t|  |¡ƒ¡ dS )z�
        L{findMachinesViaWrapper} ignores infinite loops.

        Note this test can't fail - it can only run forever!
        zh
        class InfiniteLoop(object):
            pass

        InfiniteLoop.loop = InfiniteLoop
        zloop.pyNrp   )r   r*   r"   r   r   r   Útest_infiniteLoopŸ  s    z-FindMachinesViaWrapperTests.test_infiniteLoop)r;   r<   r=   r>   r   r[   r]   re   rk   rl   rn   ro   rq   r�   r‚   r?   r   r   r   r   rQ   ´   s   ,rQ   c                   @   sx   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d„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ ZdS )ÚWrapFQPNTestszŸ
    Tests that ensure L{wrapFQPN} loads the
    L{twisted.python.modules.PythonModule} or
    L{twisted.python.modules.PythonAttribute} for a given FQPN.
    c                 C   sP   ddl m}m} ddlm}m}m}m} || _|| _|| _|| _|| _|| _d S )Nr   )ÚPythonModuleÚPythonAttributerA   )ÚwrapFQPNÚInvalidFQPNÚNoModuleÚNoObject)r   r„   r…   rC   r†   r‡   rˆ   r‰   )r   r„   r…   r†   r‡   rˆ   r‰   r   r   r   r   ·  s    zWrapFQPNTests.setUpc                 C   s2   |   || j¡ |  |j|j¡ |  | ¡ |¡ dS )zt
        Assert that a L{twisted.python.modules.PythonModule} refers to a
        particular Python module.
        N)ÚassertIsInstancer„   r|   r3   r;   ÚassertIsr8   )r   ÚmoduleWrapperr"   r   r   r   ÚassertModuleWrapperRefersToÂ  s    z)WrapFQPNTests.assertModuleWrapperRefersToc                 C   s0   |   || j¡ |  |j|¡ |  | ¡ |¡ dS )zw
        Assert that a L{twisted.python.modules.PythonAttribute} refers to a
        particular Python object.
        N)rŠ   r…   r|   r3   r‹   r8   )r   ZattributeWrapperÚfqpnÚobjr   r   r   ÚassertAttributeWrapperRefersToË  s    z,WrapFQPNTests.assertAttributeWrapperRefersToc              	   C   s&   |   | j¡� |  d¡ W 5 Q R X dS )zO
        L{wrapFQPN} raises L{InvalidFQPN} when given an empty string.
        Ú N©ÚassertRaisesr‡   r†   ©r   r   r   r   Útest_failsWithEmptyFQPNÔ  s    z%WrapFQPNTests.test_failsWithEmptyFQPNc              
   C   s0   dD ]&}|   | j¡� |  |¡ W 5 Q R X qdS )zl "
        L{wrapFQPN} raises L{InvalidFQPN} when given a badly-dotted
        FQPN.  (e.g., x..y).
        )z.failszfails.zthis..failsNr’   ©r   Úbadr   r   r   Útest_failsWithBadDottingÛ  s    z&WrapFQPNTests.test_failsWithBadDottingc                 C   s4   ddl }|  d¡}|  || j¡ |  | ¡ |¡ dS )z�
        L{wrapFQPN} returns a L{twisted.python.modules.PythonModule}
        referring to the single module a dotless FQPN describes.
        r   Nr/   )r/   r†   rŠ   r„   r‹   r8   )r   r/   rŒ   r   r   r   Útest_singleModuleä  s    
zWrapFQPNTests.test_singleModulec              	   C   s&   |   | j¡� |  d¡ W 5 Q R X dS )z~
        L{wrapFQPN} raises L{NoModule} when given a dotless FQPN that does
        not refer to a module or package.
        zthis is not an acceptable name!N)r“   rˆ   r†   r”   r   r   r   Ú*test_failsWithMissingSingleModuleOrPackageð  s    z8WrapFQPNTests.test_failsWithMissingSingleModuleOrPackagec                 C   s   ddl }|  |  d¡|¡ dS )z�
        L{wrapFQPN} returns a L{twisted.python.modules.PythonModule}
        referring to the single package a dotless FQPN describes.
        r   NÚxml)r›   r�   r†   ©r   r›   r   r   r   Útest_singlePackageø  s    z WrapFQPNTests.test_singlePackagec                 C   s    ddl }|  |  d¡|j¡ dS )z‘
        L{wrapFQPN} returns a L{twisted.python.modules.PythonModule}
        referring to the deepest package described by dotted FQPN.
        r   Nz	xml.etree)Z	xml.etreer�   r†   Úetreerœ   r   r   r   Útest_multiplePackages  s    z#WrapFQPNTests.test_multiplePackagesc                 C   s"   ddl }|  |  d¡|jj¡ dS )z�
        L{wrapFQPN} returns a L{twisted.python.modules.PythonModule}
        referring to the deepest module described by dotted FQPN.
        r   Nzxml.etree.ElementTree)Úxml.etree.ElementTreer�   r†   rž   ÚElementTreerœ   r   r   r   Ú test_multiplePackagesFinalModule
  s
     ÿz.WrapFQPNTests.test_multiplePackagesFinalModulec                 C   s"   ddl }|  |  d¡d|j¡ dS )zŸ
        L{wrapFQPN} returns a L{twisted.python.modules.PythonAttribute}
        referring to the deepest object an FQPN names, traversing one module.
        r   Nzos.path)r/   r�   r†   r   )r   r/   r   r   r   Útest_singleModuleObject  s      ÿz%WrapFQPNTests.test_singleModuleObjectc                 C   sL   ddl }ddl}d|jjjfd|jjffD ]\}}|  |  |¡||¡ q*dS )z¼
        L{wrapFQPN} returns a L{twisted.python.modules.PythonAttribute}
        referring to the deepest object described by an FQPN,
        descending through several packages.
        r   Nz xml.etree.ElementTree.fromstringz!automat.MethodicalMachine.__doc__)	r    Úautomatrž   r¡   Ú
fromstringZMethodicalMachiner>   r�   r†   )r   r›   r¤   rŽ   r�   r   r   r   Útest_multiplePackagesObject   s    
þz)WrapFQPNTests.test_multiplePackagesObjectc              
   C   s0   dD ]&}|   | j¡� |  |¡ W 5 Q R X qdS )z‚
        L{wrapFQPN} raises L{NoObject} when given an FQPN that contains a
        missing attribute, module, or package.
        )zxml.etree.nope!z*xml.etree.nope!.but.the.rest.is.believableN)r“   r‰   r†   r–   r   r   r   Ú4test_failsWithMultiplePackagesMissingModuleOrPackage/  s    zBWrapFQPNTests.test_failsWithMultiplePackagesMissingModuleOrPackageN)r;   r<   r=   r>   r   r�   r�   r•   r˜   r™   rš   r�   rŸ   r¢   r£   r¦   r§   r   r   r   r   rƒ   ¯  s   					rƒ   c                       s,   e Zd ZdZdZ‡ fdd„Zdd„ Z‡  ZS )ÚFindMachinesIntegrationTestszs
    Integration tests to check that L{findMachines} yields all
    machines discoverable at or below an FQPN.
    zÚ
    from automat import MethodicalMachine

    class PythonClass(object):
        _machine = MethodicalMachine()
        ignored = "i am ignored"

    rootLevel = MethodicalMachine()

    ignored = "i am ignored"
    c                    s¾   t t| ƒ ¡  ddlm} || _|  | j¡ d¡}| ¡  |  	| jg¡| _
|  | j|jd¡ | d¡}| ¡  | d¡ ¡  |  | j|jd¡ |  | j
d ¡| _|  | j
d d d ¡| _d S )NrA   )ÚfindMachinesrr   rs   Z
subpackagert   r"   )r   r¨   r   rC   r©   r   r   r%   rw   r   r}   r.   ÚSOURCEr   rx   r2   r9   ÚpackageDictrE   )r   r©   Z
packageDirZsubPackageDirr   r   r   r   L  s    
ÿz"FindMachinesIntegrationTests.setUpc           	      C   s–   t |  d¡t d¡d�}| jd  ¡ }| jd  ¡ }| jd }| ¡ }| jd }| ¡ }t d|fd|jfd|fd	|jfgt d¡d�}|  ||¡ d
S )z‡
        Given a top-level package FQPN, L{findMachines} discovers all
        L{MethodicalMachine} instances in and below it.
        rr   r   ru   ztest_package.rootLevelztest_package.PythonClassz(test_package.subpackage.module.rootLevelz*test_package.subpackage.module.PythonClassz!test_package.PythonClass._machinez3test_package.subpackage.module.PythonClass._machineN)	ry   r©   rz   r{   r«   r8   rE   Z_machiner|   )	r   r   ZtpRootLevelZtpPythonClassZmRLAttrZ
mRootLevelZmPCAttrZmPythonClassr€   r   r   r   Útest_discoverAllb  s$    

þü	öz-FindMachinesIntegrationTests.test_discoverAll)r;   r<   r=   r>   rª   r   r¬   r?   r   r   r   r   r¨   9  s   r¨   )rz   r/   r    r   r(   r   Zunittestr   r   r   r	   r@   rQ   rƒ   r¨   r   r   r   r   Ú<module>   s$   	<d { 
