o
    ‹Ífó£  ã                   @   s:  d Z 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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dlmZmZmZmZmZmZmZmZmZmZmZm Z m!Z! dd	l"m#Z# dd
l$m%Z% ddl&m'Z' ddl(m)Z)m*Z*m+Z+ ej,dkr„dZ-ndZ-ddiZ.ddiZ/dZ-G dd„ de+ƒZ0dd„ Z1dd„ Z2dd„ Z3G dd„ dƒZ4G dd„ de0ƒZ5G dd „ d e0ƒZ6d!d"„ Z7G d#d$„ d$e+ƒZ8e'd%ƒZ9ee9du d&ƒG d'd(„ d(e0ƒƒZ:G d)d*„ d*e+ƒZ;ee%d+ƒ d,ƒG d-d.„ d.e+ƒƒZ<G d/d0„ d0e4ƒZ=G d1d2„ d2e=e0ƒZ>G d3d4„ d4e0ƒZ?G d5d6„ d6e+ƒZ@G d7d8„ d8e0ƒZAdS )9z´
Tests for L{twisted.python.release} and L{twisted.python._release}.

All of these tests are skipped on platforms other than Linux, as the release is
only ever performed on Linux.
é    N)ÚBytesIOÚStringIO)ÚCalledProcessError)ÚskipIf)ÚVersion)Úrelease)Ú
APIBuilderÚBuildAPIDocsScriptÚCheckNewsfragmentScriptÚ
GitCommandÚIVCSCommandÚNotWorkingDirectoryÚProjectÚSphinxBuilderÚfilePathDeltaÚfindTwistedProjectsÚgetRepositoryCommandÚreplaceInFileÚ
runCommand)ÚFilePath)Úwhich)ÚrequireModule)ÚFailTestÚSkipTestÚTestCaseÚwin32z*Release toolchain only supported on POSIX.ÚGITHUB_HEAD_REFúpre-commit-ci-update-configz1234-some-branch-nameúNot relevant within Debian.c                   @   ó   e Zd ZdZdd„ ZdS )ÚExternalTempdirTestCasez•
    A test case which has mkdir make directories outside of the usual spot, so
    that Git commands don't interfere with the Twisted checkout.
    c                 C   s"   t jt  ¡ d}|  tj|¡ |S )z)
        Make our own directory.
        )Údir)ÚtempfileÚmkdtempÚ
gettempdirÚ
addCleanupÚshutilÚrmtree)ÚselfÚnewDir© r*   úB/usr/lib/python3/dist-packages/twisted/python/test/test_release.pyÚmktempB   s   zExternalTempdirTestCase.mktempN)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r,   r*   r*   r*   r+   r    <   s    r    c                 C   sH   t ddd|  d¡ d¡jddgƒ t ddd|  d¡ d¡jddgƒ d	S )
zË
    Set some config in the repo that Git requires to make commits. This isn't
    needed in real usage, just for tests.

    @param path: The path to the Git repository.
    @type path: L{FilePath}
    ÚgitÚconfigz--filez.gitz	user.namez	"someone"z
user.emailz"someone@someplace.com"N)r   ÚchildÚpath©r4   r*   r*   r+   Ú
_gitConfigK   s$   úÿ
úÿr6   c                 C   s   t dd| jgƒ t| ƒ dS )zÃ
    Run a git init, and set some config that git requires. This isn't needed in
    real usage.

    @param path: The path to where the Git repo will be created.
    @type path: L{FilePath}
    r1   ÚinitN)r   r4   r6   r5   r*   r*   r+   Ú_gitIniti   s   r8   c                  O   s   d  t| i |¤Ž¡S )z§
    A convenience for generating _version.py data.

    @param args: Arguments to pass to L{Version}.
    @param kwargs: Keyword arguments to pass to L{Version}.
    z0from incremental import Version
__version__={!r})Úformatr   )ÚargsÚkwargsr*   r*   r+   Ú
genVersionu   s   ÿr<   c                   @   ó    e Zd ZdZdd„ Zdd„ ZdS )ÚStructureAssertingMixinz¥
    A mixin for L{TestCase} subclasses which provides some methods for
    asserting the structure and contents of directories and files on the
    filesystem.
    c                 C   s\   |D ])}|  |¡}t|| tƒr| ¡  |  ||| ¡ q| ||  dtj¡ 	¡ ¡ qdS )a#  
        Create a set of directories and files given a dict defining their
        structure.

        @param root: The directory in which to create the structure.  It must
            already exist.
        @type root: L{FilePath}

        @param dirDict: The dict defining the structure. Keys should be strings
            naming files, values should be strings describing file contents OR
            dicts describing subdirectories.  All files are written in binary
            mode.  Any string values are assumed to describe text files and
            will have their newlines replaced with the platform-native newline
            convention.  For example::

                {"foofile": "foocontents",
                 "bardir": {"barfile": "bar
contents"}}
        @type dirDict: C{dict}
        Ú
N)
r3   Ú
isinstanceÚdictÚcreateDirectoryÚcreateStructureÚ
setContentÚreplaceÚosÚlinesepÚencode)r(   ÚrootÚdirDictÚxr3   r*   r*   r+   rC   ˆ   s   
úz'StructureAssertingMixin.createStructurec                 C   sÆ   dd„ |  ¡ D ƒ}| ¡ D ]C\}}| |¡}t|ƒr"|  ||ƒ¡ n)t|tƒr:|  | ¡ |j› d¡ |  	||¡ n| 
¡  ¡  tjd¡}|  ||¡ | |¡ q|ra|  d|j› d|› ¡ dS dS )a  
        Assert that a directory is equivalent to one described by a dict.

        @param root: The filesystem directory to compare.
        @type root: L{FilePath}
        @param dirDict: The dict that should describe the contents of the
            directory. It should be the same structure as the C{dirDict}
            parameter to L{createStructure}.
        @type dirDict: C{dict}
        c                 S   s   g | ]}|  ¡ ‘qS r*   )Úbasename)Ú.0Úeachr*   r*   r+   Ú
<listcomp>¯   s    z;StructureAssertingMixin.assertStructure.<locals>.<listcomp>z is not a dir!r?   zThere were extra children in z: N)ÚchildrenÚitemsr3   ÚcallableÚ
assertTruer@   rA   Úisdirr4   ÚassertStructureÚ
getContentÚdecoderE   rF   rG   ÚassertEqualÚremoveÚfail)r(   rI   rJ   rP   ÚpathSegmentÚexpectationr3   Úactualr*   r*   r+   rU   ¤   s   

ÿz'StructureAssertingMixin.assertStructureN)r-   r.   r/   r0   rC   rU   r*   r*   r*   r+   r>      s    r>   c                   @   sB   e Zd ZdZdd„ Zddd„Zdd„ Zd	d
„ Zdd„ Zdd„ Z	dS )ÚProjectTestsz=
    There is a first-class representation of a project.
    c                 C   s`   |   t|ƒt|ƒ¡ t|t d¡d}t|t d¡d}t||ƒD ]\}}|   |j|j¡ q!dS )zA
        Assert that two lists of L{Project}s are equal.
        Ú	directory)ÚkeyN)rX   ÚlenÚsortedÚoperatorÚ
attrgetterÚzipr_   )r(   ÚobservedProjectsÚexpectedProjectsÚobservedÚexpectedr*   r*   r+   ÚassertProjectsEqualÄ   s   
ÿ
ÿÿz ProjectTests.assertProjectsEqualNc                 C   sˆ   |du r
t |  ¡ ƒ}|d  d¡}|}|D ]}| |¡}| ¡ s$| ¡  | d¡ d¡ q| d¡ ¡  | d¡ t|Ž  ¡ ¡ t	|ƒS )a(  
        Make a Twisted-style project in the given base directory.

        @param baseDirectory: The directory to create files in
            (as a L{FilePath).
        @param version: The version information for the project.
        @return: L{Project} pointing to the created project.
        Nr   Ú.ú__init__.pyó    Únewsfragmentsú_version.py)
r   r,   Úsplitr3   ÚexistsrB   rD   r<   rH   r   )r(   ÚversionÚbaseDirectoryÚsegmentsr_   Úsegmentr*   r*   r+   ÚmakeProjectÒ   s   	
zProjectTests.makeProjectc                 G   s&   t |  ¡ ƒ}|D ]}|  ||¡ q|S )zŒ
        Create a series of projects underneath a temporary base directory.

        @return: A L{FilePath} for the base directory.
        )r   r,   rv   )r(   Úversionsrs   rr   r*   r*   r+   ÚmakeProjectsè   s   zProjectTests.makeProjectsc                 C   s&   d}|   |¡}|  | ¡ t|Ž ¡ dS )z5
        Project objects know their version.
        )Útwistedé   é   r   N)rv   rX   Ú
getVersionr   )r(   rr   Úprojectr*   r*   r+   Útest_getVersionó   s   
zProjectTests.test_getVersionc                 C   s&   t tdƒƒ}|  t|ƒd|j ¡ dS )zH
        The representation of a Project is Project(directory).
        ÚbarzProject(%r)N)r   r   rX   Úreprr_   )r(   Úfoor*   r*   r+   Ú	test_reprû   s   zProjectTests.test_reprc                 C   sB   |   dd¡}t|ƒ}|  |t| d¡ƒt| d¡ d¡ƒg¡ dS )zÜ
        findTwistedStyleProjects finds all projects underneath a particular
        directory. A 'project' is defined by the existence of a 'newsfragments'
        directory and is returned as a Project object.
        )r   rz   é   r   )zfoo.barr   é   é   r   r   N)rx   r   rj   r   r3   )r(   rs   Úprojectsr*   r*   r+   Útest_findTwistedStyleProjects  s   þþz*ProjectTests.test_findTwistedStyleProjects©N)
r-   r.   r/   r0   rj   rv   rx   r~   r‚   r‡   r*   r*   r*   r+   r^   ¿   s    
r^   c                   @   r=   )ÚUtilityTestsz<
    Tests for various utility functions for releasing.
    c                 C   s4   t  ¡ }dd„ }|  ttj|¡ |  |t  ¡ ¡ dS )z 
        Test that the runChdirSafe is actually safe, i.e., it still
        changes back to the original directory even if an error is
        raised.
        c                   S   s    t  d¡ t  d¡ dd  d S )NÚ	releaseChr{   r   )rF   ÚmkdirÚchdirr*   r*   r*   r+   Ú
chAndBreak   s   

z+UtilityTests.test_chdir.<locals>.chAndBreakN)rF   ÚgetcwdÚassertRaisesÚZeroDivisionErrorr   ÚrunChdirSaferX   )r(   Úcwdr   r*   r*   r+   Ú
test_chdir  s   zUtilityTests.test_chdirc                 C   sâ   d}t ddƒ}| |¡ W d  ƒ n1 sw   Y  | dd¡}tdddiƒ t dƒ}|  | ¡ |¡ W d  ƒ n1 s@w   Y  | dd¡}tdddiƒ t dƒ}|  | ¡ |¡ W d  ƒ dS 1 sjw   Y  dS )z³
        L{replaceInFile} replaces data in a file based on a dict. A key from
        the dict that is found in the file is replaced with the corresponding
        value.
        zfoo
hey hey $VER
bar
zrelease.replaceÚwNz$VERz2.0.0z3.0.0)ÚopenÚwriterE   r   rX   Úread)r(   ÚcontentÚoutfri   Úfr*   r*   r+   Útest_replaceInFile(  s   ÿ
ÿ
"ÿzUtilityTests.test_replaceInFileN)r-   r.   r/   r0   r“   r›   r*   r*   r*   r+   r‰     ó    r‰   c                    s   t  ˆ ¡‡ fdd„ƒ}|S )aP  
    A decorator which makes APIBuilder tests not fail because of intermittent
    network failures -- mamely, APIBuilder being unable to get the "object
    inventory" of other projects.

    @param func: The function to decorate.

    @return: A decorated function which won't fail if the object inventory
        fetching fails.
    c               
      sN   z
ˆ | i |¤Ž W d S  t y& } z|jd  d¡r!td |¡ƒ‚‚ d }~ww )Nr   z%'Failed to get object inventory from zWThis test is prone to intermittent network errors. See ticket 8753. Exception was: {!r})r   r:   Ú
startswithr   r9   )ÚaÚkwÚe©Úfuncr*   r+   ÚwrapperI  s   ü€øz(doNotFailOnNetworkError.<locals>.wrapper)Ú	functoolsÚwraps)r¢   r£   r*   r¡   r+   ÚdoNotFailOnNetworkError=  s   r¦   c                   @   r=   )ÚDoNotFailTestsz/
    Tests for L{doNotFailOnNetworkError}.
    c              
      óP   t ‡ fdd„ƒ}z|ƒ  W dS  ty' } zˆ  |t¡ W Y d}~dS d}~ww )z³
        When the test raises L{FailTest} and the assertion failure starts with
        "'Failed to get object inventory from ", the test will be skipped
        instead.
        c                      ó   ˆ   dd¡ d S )Nz(Failed to get object inventory from blahÚ ©rX   r*   ©r(   r*   r+   Úinnerf  ó   z8DoNotFailTests.test_skipsOnAssertionError.<locals>.innerN)r¦   Ú	ExceptionÚassertIsInstancer   ©r(   r­   r    r*   r¬   r+   Útest_skipsOnAssertionError_  s   €ÿz)DoNotFailTests.test_skipsOnAssertionErrorc              
      r¨   )zz
        If there is a L{FailTest} that is not the intersphinx fetching error,
        it will be passed through.
        c                      r©   )Nz	Error!!!!rª   r«   r*   r¬   r*   r+   r­   u  r®   z>DoNotFailTests.test_doesNotSkipOnDifferentError.<locals>.innerN)r¦   r¯   r°   r   r±   r*   r¬   r+   Ú test_doesNotSkipOnDifferentErroro  s   €ÿz/DoNotFailTests.test_doesNotSkipOnDifferentErrorN)r-   r.   r/   r0   r²   r³   r*   r*   r*   r+   r§   Z  rœ   r§   ÚpydoctorzPydoctor is not present.c                   @   sD   e Zd ZdZedd„ ƒZedd„ ƒZedd„ ƒZdd	„ Zd
d„ Z	dS )ÚAPIBuilderTestsz"
    Tests for L{APIBuilder}.
    c                 C   sn  t ƒ }|  td|¡ d}d}d}d}d}d}t|  ¡ ƒ |¡}| ¡  | d¡ d	 ||¡ 	¡ ¡ t|  ¡ ƒ}	t
ƒ }
|
 |||||	¡ |	 d
¡}|  | ¡ d|	j›d¡ |  d|› d|› d| ¡  ¡ d¡ |	 d¡}|  | ¡ d|j›d¡ |  || ¡  ¡ d¡ |  d|› d|› d| ¡  ¡ ¡ |  d||f | ¡  ¡ ¡ |  || ¡  ¡ ¡ |  | ¡ d¡ dS )zt
        L{APIBuilder.build} writes an index file which includes the name of the
        project specified.
        ÚstdoutÚFoobarÚquuxúscheme:projectúscheme:sourceútext in docstringúshould also appear in outputrl   z(def foo():
    '{}'
def _bar():
    '{}'ú
index.htmlú
API index ú did not exist.z	<a href="z" class="projecthome">z</a>ú+Project name/location not in file contents.ú	quux.htmlúPackage documentation file ú,Docstring not in package documentation file.ú/z-/__init__.py" class="sourceLink">(source)</a>z2<a class="sourceLink" href="%s/%s/__init__.py#L1">rm   N)r   ÚpatchÚsysr   r,   r3   ÚmakedirsrD   r9   rH   r   ÚbuildrS   rq   r4   ÚassertInrV   rW   rX   Úgetvalue)r(   r¶   ÚprojectNameÚpackageNameÚ
projectURLÚ	sourceURLÚ	docstringÚprivateDocstringÚ	inputPathÚ
outputPathÚbuilderÚ	indexPathÚquuxPathr*   r*   r+   Ú
test_buildˆ  s`   
ü
ÿ
ý
þ
ý
ýÿ
ýzAPIBuilderTests.test_buildc           	      C   s(  t ƒ }|  td|¡ d}t|  ¡ ƒ}| d¡}| ¡  | d¡ d |¡ 	¡ ¡ | d¡ t
ddddƒ 	¡ ¡ t|  ¡ ƒ}tƒ }| ||¡ | d	¡}|  | ¡ d
|j› d¡ |  d| ¡  ¡ d¡ | d¡}|  | ¡ d|j›d¡ |  || ¡  ¡ d¡ |  d| ¡  ¡ ¡ |  | ¡ d¡ dS )z
        L{BuildAPIDocsScript.buildAPIDocs} builds the API docs with values
        appropriate for the Twisted project.
        r¶   r»   ry   rl   zdef foo():
    '{}'
ro   r{   r   r½   r¾   r¿   zD<a href="https://twistedmatrix.com/" class="projecthome">Twisted</a>rÀ   ztwisted.htmlrÂ   rÃ   zw<a href="https://github.com/twisted/twisted/tree/twisted-1.0.0/src/twisted/__init__.py" class="sourceLink">(source)</a>rm   N)r   rÅ   rÆ   r   r,   r3   rÇ   rD   r9   rH   r<   r	   ÚbuildAPIDocsrS   rq   r4   rÉ   rV   rW   rX   rÊ   )	r(   r¶   rÏ   ÚprojectRootÚpackagePathrÒ   ÚscriptrÔ   ÚtwistedPathr*   r*   r+   Útest_buildWithPolicyÉ  sN   

ÿ
ÿ
ÿ
ý
þ
ý
ýz$APIBuilderTests.test_buildWithPolicyc                 C   s*  t ƒ }|  td|¡ d}d}d}d}d}d}t|  ¡ ƒ |¡}| ¡  | d¡ d	 ||¡ 	¡ ¡ t|  ¡ ƒ}	t
ƒ }
|
 |||||	¡ |	 d
¡}|  | ¡ d|j›d¡ |  || ¡  ¡ d¡ |  d| ¡  ¡ ¡ |  d| ¡  ¡ ¡ |  || ¡  ¡ ¡ |  d| d¡ ¡  ¡ ¡ |  | ¡ d¡ dS )zT
        The templates and System for Twisted includes adding deprecations.
        r¶   r·   r¸   r¹   rº   r»   r¼   rl   at  from twisted.python.deprecate import deprecated
from incremental import Version
@deprecated(Version('Twisted', 15, 0, 0), 'Baz')
def foo():
    '{}'
from twisted.python import deprecate
import incremental
@deprecate.deprecated(incremental.Version('Twisted', 16, 0, 0))
def _bar():
    '{}'
@deprecated(Version('Twisted', 14, 2, 3), replacement='stuff')
class Baz:
    passrÁ   rÂ   r¿   rÃ   z=foo was deprecated in Twisted 15.0.0; please use Baz instead.z&_bar was deprecated in Twisted 16.0.0.z?Baz was deprecated in Twisted 14.2.3; please use stuff instead.zquux.Baz.htmlrm   N)r   rÅ   rÆ   r   r,   r3   rÇ   rD   r9   rH   r   rÈ   rS   rq   r4   rÉ   rV   rW   ÚsiblingrX   rÊ   )r(   r¶   rË   rÌ   rÍ   rÎ   rÏ   rÐ   rÑ   rÒ   rÓ   rÕ   r*   r*   r+   Útest_buildWithDeprecatedÿ  sP   
ñ
þ
ý
þÿþz(APIBuilderTests.test_buildWithDeprecatedc                 C   s@   t ƒ }|  t|jg ¡ |  t|jdg¡ |  t|jg d¢¡ dS )z‰
        SystemExit is raised when the incorrect number of command line
        arguments are passed to the API building script.
        r   )r   r   ÚbazN)r	   r   Ú
SystemExitÚmain©r(   rÚ   r*   r*   r+   Ú-test_apiBuilderScriptMainRequiresTwoArgumentsB  s   z=APIBuilderTests.test_apiBuilderScriptMainRequiresTwoArgumentsc                    sD   t ƒ }g ‰ ‡ fdd„|_| ddg¡ |  ˆ tdƒtdƒfg¡ dS )zk
        The API building script invokes the same code that
        L{test_buildWithPolicy} tests.
        c                    s   ˆ   | |f¡S rˆ   )Úappend)rž   Úb©Úcallsr*   r+   Ú<lambda>S  s    z;APIBuilderTests.test_apiBuilderScriptMain.<locals>.<lambda>ÚhelloÚthereN)r	   r×   rá   rX   r   râ   r*   ræ   r+   Útest_apiBuilderScriptMainL  s
   z)APIBuilderTests.test_apiBuilderScriptMainN)
r-   r.   r/   r0   r¦   rÖ   rÜ   rÞ   rã   rë   r*   r*   r*   r+   rµ   ‚  s    
@
5
B
rµ   c                   @   s0   e Zd ZdZdd„ Zdd„ Zdd„ Zdd	„ Zd
S )ÚFilePathDeltaTestsz%
    Tests for L{filePathDelta}.
    c                 C   s    |   ttdƒtdƒƒdg¡ dS )zU
        L{filePathDelta} can create a simple relative path to a child path.
        ú/foo/barz/foo/bar/bazrß   N©rX   r   r   r¬   r*   r*   r+   Útest_filePathDeltaSubdir]  s   ÿz+FilePathDeltaTests.test_filePathDeltaSubdirc                 C   s"   |   ttdƒtdƒƒddg¡ dS )ze
        L{filePathDelta} can traverse upwards to create relative paths to
        siblings.
        rí   z/foo/bazú..rß   Nrî   r¬   r*   r*   r+   Útest_filePathDeltaSiblingDire  s   ÿz/FilePathDeltaTests.test_filePathDeltaSiblingDirc                 C   ó"   |   ttdƒtdƒƒg d¢¡ dS )zx
        L{filePathDelta} can create relative paths to totally unrelated paths
        for maximum portability.
        rí   z	/baz/quux)rð   rð   rß   r¸   Nrî   r¬   r*   r*   r+   Útest_filePathNoCommonElementsn  ó   þz0FilePathDeltaTests.test_filePathNoCommonElementsc                 C   rò   )zŽ
        L{filePathDelta} doesn't take into account final elements when
        comparing 2 paths, but stops at the first difference.
        z/foo/bar/bar/spamz/foo/bar/baz/spam)rð   rð   rß   ÚspamNrî   r¬   r*   r*   r+   Ú$test_filePathDeltaSimilarEndElementsx  rô   z7FilePathDeltaTests.test_filePathDeltaSimilarEndElementsN)r-   r.   r/   r0   rï   rñ   ró   rö   r*   r*   r*   r+   rì   X  s    	
rì   zsphinx-buildzSphinx not available.c                   @   sl   e Zd ZdZdZe e¡ZdZe e¡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 )ÚSphinxBuilderTestsaš  
    Tests for L{SphinxBuilder}.

    @note: This test case depends on twisted.web, which violates the standard
        Twisted practice of not having anything in twisted.python depend on
        other Twisted packages and opens up the possibility of creating
        circular dependencies.  Do not use this as an example of how to
        structure your dependencies.

    @ivar builder: A plain L{SphinxBuilder}.

    @ivar sphinxDir: A L{FilePath} representing a directory to be used for
        containing a Sphinx project.

    @ivar sourceDir: A L{FilePath} representing a directory to be used for
        containing the source files for a Sphinx project.
    zb                  source_suffix = '.rst'
                  master_doc = 'index'
                  a                     ==============
                   This is a Test
                   ==============

                   This is only a test
                   -------------------

                   In case you hadn't figured it out yet, this is a test.
                   c                 C   s:   t ƒ | _t|  ¡ ƒ| _| j d¡| _| j ¡  | j| _dS )zF
        Set up a few instance variables that will be useful.
        ÚdocsN)	r   rÓ   r   r,   ÚtwistedRootDirr3   Ú	sphinxDirrÇ   Ú	sourceDirr¬   r*   r*   r+   ÚsetUp©  s
   
zSphinxBuilderTests.setUpc                 C   s4   | j  d¡ | j ¡ ¡ | j  d¡ | j ¡ ¡ dS )a!  
        Create a fake Sphinx project for test purposes.

        Creates a fake Sphinx project with the absolute minimum of source
        files.  This includes a single source file ('index.rst') and the
        smallest 'conf.py' file possible in order to find that source file.
        zconf.pyú	index.rstN)rû   r3   rD   ÚconfContentrH   ÚindexContentr¬   r*   r*   r+   ÚcreateFakeSphinxProjectµ  s   z*SphinxBuilderTests.createFakeSphinxProjectc                 C   sR   |  |¡}|  | ¡ ¡ | ¡ }|  t|ƒdk¡ |j d¡r'|  d|¡ dS dS )a\  
        Helper which verifies that C{fileName} exists in C{fileDir} and it has
        some content.

        @param fileDir: A path to a directory.
        @type fileDir: L{FilePath}

        @param fileName: The last path segment of a file which may exist within
            C{fileDir}.
        @type fileName: L{str}

        @raise FailTest: If C{fileDir.child(fileName)}:

                1. Does not exist.

                2. Is empty.

                3. In the case where it's a path to a C{.html} file, the
                   content looks like an HTML file.

        @return: L{None}
        r   z.htmls   <bodyN)r3   rS   rq   rV   ra   r4   ÚendswithrÉ   )r(   ÚfileDirÚfileNameÚfpathÚ	fcontentsr*   r*   r+   ÚverifyFileExistsÀ  s   
ÿz#SphinxBuilderTests.verifyFileExistsc                 C   s"   |   ¡  | j | j¡ |  ¡  dS )zT
        Creates and builds a fake Sphinx project using a L{SphinxBuilder}.
        N)r   rÓ   rÈ   rú   ÚverifyBuiltr¬   r*   r*   r+   rÖ   ä  s   zSphinxBuilderTests.test_buildc                 C   s*   |   ¡  | j | j ¡ jg¡ |  ¡  dS )zV
        Creates and builds a fake Sphinx project as if via the command line.
        N)r   rÓ   rá   rú   Úparentr4   r  r¬   r*   r*   r+   Ú	test_mainì  s   zSphinxBuilderTests.test_mainc                 C   sž   t ƒ }|  td|¡ |  ¡  | j d¡ d¡}| d¡ W d  ƒ n1 s'w   Y  |  t	| j
j| j ¡ jg¡}|  |jd¡ |  d| ¡ ¡ |  ¡  dS )z
        Creates and builds a fake Sphinx project as if via the command line,
        failing if there are any warnings.
        r¶   rý   rž   s   
.. _malformed-link-target
Nr{   zmalformed hyperlink target)r   rÅ   rÆ   r   rú   r3   r•   r–   r   rà   rÓ   rá   r  r4   rX   ÚcoderÉ   rÊ   r  )r(   Úoutputrš   Ú	exceptionr*   r*   r+   Útest_warningsAreErrorsô  s   ÿÿz)SphinxBuilderTests.test_warningsAreErrorsc                 C   sr   | j  d¡}|  | ¡ ¡ | d¡}|  | ¡ ¡ |  |d¡ |  |d¡ |  |d¡ |  |d¡ |  |d¡ dS )	z>
        Verify that a sphinx project has been built.
        ÚdocÚdoctreesr½   zgenindex.htmlzobjects.invzsearch.htmlzsearchindex.jsN)rú   rÝ   rS   rT   r3   ÚassertFalserq   r  )r(   ÚhtmlDirÚ
doctreeDirr*   r*   r+   r    s   
zSphinxBuilderTests.verifyBuiltc                 C   ó   |   t| jj| j¡ dS )zg
        Check that SphinxBuilder.build fails when run against a non-sphinx
        directory.
        N)r   r   rÓ   rÈ   rú   r¬   r*   r*   r+   Útest_failToBuild  s   z#SphinxBuilderTests.test_failToBuildN)r-   r.   r/   r0   rþ   ÚtextwrapÚdedentrÿ   rü   r   r  rÖ   r	  r  r  r  r*   r*   r*   r+   r÷   ƒ  s    


$r÷   c                   @   sL   e Zd 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 )ÚCommandsTestMixinzF
    Test mixin for the VCS commands used by the release scripts.
    r   c                 C   ó   t |  ¡ ƒ| _d S rˆ   )r   r,   ÚtmpDirr¬   r*   r*   r+   rü   $  ó   zCommandsTestMixin.setUpc                 C   ó"   |   | j¡}|  | j |¡¡ dS )zŽ
        Calling the C{ensureIsWorkingDirectory} VCS command's method on a valid
        working directory doesn't produce any error.
        N)ÚmakeRepositoryr  ÚassertIsNoneÚcreateCommandÚensureIsWorkingDirectory©r(   ÚreposDirr*   r*   r+   Ú1test_ensureIsWorkingDirectoryWithWorkingDirectory'  ó   zCCommandsTestMixin.test_ensureIsWorkingDirectoryWithWorkingDirectoryc                 C   r  )z¡
        Calling the C{ensureIsWorkingDirectory} VCS command's method on an
        invalid working directory raises a L{NotWorkingDirectory} exception.
        N)r   r   r  r  r  r¬   r*   r*   r+   Ú4test_ensureIsWorkingDirectoryWithNonWorkingDirectory/  s
   ýzFCommandsTestMixin.test_ensureIsWorkingDirectoryWithNonWorkingDirectoryc                 C   r  )zŠ
        Calling the C{isStatusClean} VCS command's method on a repository with
        no pending modifications returns C{True}.
        N)r  r  rS   r  ÚisStatusCleanr   r*   r*   r+   Útest_statusClean:  r#  z"CommandsTestMixin.test_statusCleanc                 C   s2   |   | j¡}| d¡ d¡ |  | j |¡¡ dS )z‹
        Calling the C{isStatusClean} VCS command's method on a repository with
        no pending modifications returns C{False}.
        ú	some-fileó	   somethingN)r  r  r3   rD   r  r  r%  r   r*   r*   r+   Útest_statusNotCleanB  s   z%CommandsTestMixin.test_statusNotCleanc                 C   sb   |   | j¡}| d¡}| d¡ |  |¡ |  | ¡ ¡ | j |¡ | 	d¡ |  
| ¡ d¡ dS )zr
        Calling the C{remove} VCS command's method remove the specified path
        from the directory.
        r'  r(  FzFile still existsN)r  r  r3   rD   ÚcommitRepositoryrS   rq   r  rY   Úrestatr  )r(   r!  ÚtestFiler*   r*   r+   Útest_removeK  s   



zCommandsTestMixin.test_removec              	   C   s†   dddit ddddƒddit ddddƒdœd	œd
œ}|  | j¡}|  ||¡ |  |¡ t|  ¡ ƒ d¡}| j 	||¡ |  
||¡ dS )zŠ
        The C{exportTo} VCS command's method export the content of the
        repository as identical in a specified directory.
        zHi this is 1.0.0.ÚREADMEzHi this is 1.0.0ry   r{   r   ztwisted.web)rn   ro   )rn   ro   Úweb)z
README.rstry   ÚexportN)r<   r  r  rC   r*  r   r,   r3   r  ÚexportTorU   )r(   Ú	structurer!  Ú	exportDirr*   r*   r+   Útest_exportZ  s   þýþ
zCommandsTestMixin.test_exportN)r-   r.   r/   r0   Úskiprü   r"  r$  r&  r)  r-  r4  r*   r*   r*   r+   r    s    	r  c                   @   s(   e Zd ZdZdZeZdd„ Zdd„ ZdS )ÚGitCommandTestz^
    Specific L{CommandsTestMixin} related to Git repositories through
    L{GitCommand}.
    r   c                 C   s   t |ƒ |S )zÿ
        Create a Git repository in the specified path.

        @type root: L{FilePath}
        @params root: The directory to create the Git repository into.

        @return: The path to the repository just created.
        @rtype: L{FilePath}
        )r8   )r(   rI   r*   r*   r+   r  }  s   
zGitCommandTest.makeRepositoryc                 C   s<   t dd|jdgt |jd ¡ ƒ t dd|jdddgƒ dS )	z·
        Add and commit all the files from the Git repository specified.

        @type repository: L{FilePath}
        @params repository: The Git repository to commit into.
        r1   z-CÚaddz/*Úcommitú-mÚhopN)r   r4   Úglob)r(   Ú
repositoryr*   r*   r+   r*  Š  s   ÿzGitCommandTest.commitRepositoryN)	r-   r.   r/   r0   r5  r   r  r  r*  r*   r*   r*   r+   r6  s  s    r6  c                   @   s(   e Zd ZdZdd„ Zdd„ Zdd„ ZdS )	ÚRepositoryCommandDetectionTestz
    Test the L{getRepositoryCommand} to access the right set of VCS commands
    depending on the repository manipulated.
    c                 C   r  rˆ   )r   r,   Úreposr¬   r*   r*   r+   rü     r  z$RepositoryCommandDetectionTest.setUpc                 C   s$   t | jƒ t| jƒ}|  |t¡ dS )zV
        L{getRepositoryCommand} from a Git repository returns L{GitCommand}.
        N)r8   r>  r   ÚassertIsr   )r(   Úcmdr*   r*   r+   Útest_git   s   

z'RepositoryCommandDetectionTest.test_gitc                 C   s   |   tt| j¡ dS )z˜
        L{getRepositoryCommand} from a directory which doesn't look like a Git
        repository produces a L{NotWorkingDirectory} exception.
        N)r   r   r   r>  r¬   r*   r*   r+   Útest_unknownRepository¨  s   z5RepositoryCommandDetectionTest.test_unknownRepositoryN)r-   r.   r/   r0   rü   rA  rB  r*   r*   r*   r+   r=  —  s
    r=  c                   @   r   )ÚVCSCommandInterfaceTestszF
    Test that the VCS command classes implement their interface.
    c                 C   s   |   t t¡¡ dS )z:
        L{GitCommand} implements L{IVCSCommand}.
        N)rS   r   ÚimplementedByr   r¬   r*   r*   r+   rA  µ  s   z!VCSCommandInterfaceTests.test_gitN)r-   r.   r/   r0   rA  r*   r*   r*   r+   rC  °  s    rC  c                   @   s|   e Zd 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 )ÚCheckNewsfragmentScriptTestsz%
    L{CheckNewsfragmentScript}.
    r   c                 C   s´   t |  ¡ ƒ| _t| jƒ tg d¢| jjd | j d¡ d¡ tdd| j d¡jg| jjd tg d¢| jjd t |  ¡ ƒ| _tdd| jj| jjgƒ t	| jƒ |  
td	t¡ d S )
N)r1   Úcheckoutú-bÚtrunk©r’   Útests   test!r1   r7  )r1   r8  r9  ÚinitialÚcloneÚenviron)r   r,   Úoriginr8   r   r4   r3   rD   Úrepor6   rÅ   rF   ÚGENERIC_CI_ENVIRONr¬   r*   r*   r+   rü   Ã  s   
 
z"CheckNewsfragmentScriptTests.setUpc                 C   sR   g }|   t¡}t|jƒ g ¡ W d  ƒ n1 sw   Y  |  |jjd¡ dS )z6
        Too few arguments returns a failure.
        N)z/Must specify one argument: the Twisted checkout)r   rà   r
   rä   rá   rX   r  r:   ©r(   Úlogsr    r*   r*   r+   Útest_noArgsÒ  s   ÿÿz(CheckNewsfragmentScriptTests.test_noArgsc                 C   óÂ   t g d¢| jjd | j d¡}| d¡ t dd|j|jg| jjd t g d¢| jjd g }|  t¡}t|jƒ 	| jjg¡ W d  ƒ n1 sJw   Y  |  
|jjd	¡ |  
|d
 d¡ dS )zd
        If there are changes from trunk, then there should also be a
        newsfragment.
        ©r1   rF  rG  ÚmypatchrI  Úsomefileó   changer1   r7  ©r1   r8  r9  z	some fileN©r{   éÿÿÿÿú-No newsfragment found. Have you committed it?©r   rO  r4   r3   rD   r   rà   r
   rä   rá   rX   r  r:   ©r(   rW  rR  r    r*   r*   r+   Ú!test_diffFromTrunkNoNewsfragmentsß  s   
ÿz>CheckNewsfragmentScriptTests.test_diffFromTrunkNoNewsfragmentsc                 C   s|   t g d¢| jjd g }|  t¡}t|jƒ | jjg¡ W d  ƒ n1 s'w   Y  |  |j	j
d¡ |  |d d¡ dS )ze
        If there are no changes from trunk, then no need to check the
        newsfragments
        rU  rI  N©r   r[  ú9On trunk or no diffs from trunk; no need to look at this.)r   rO  r4   r   rà   r
   rä   rá   rX   r  r:   rQ  r*   r*   r+   Útest_noChangeFromTrunkó  s   ÿÿz3CheckNewsfragmentScriptTests.test_noChangeFromTrunkc                 C   sh   g }|   t¡}t|jƒ | jjg¡ W d  ƒ n1 sw   Y  |  |jj	d¡ |  |d d¡ dS )z9
        Running it on trunk always gives green.
        Nr`  r[  ra  )
r   rà   r
   rä   rá   rO  r4   rX   r  r:   rQ  r*   r*   r+   Ú
test_trunk  s   ÿÿz'CheckNewsfragmentScriptTests.test_trunkc                 C   rT  )z~
        Running it on a release branch returns green if there is no
        newsfragments even if there are changes.
        ©r1   rF  rG  zrelease-16.11111-9001rI  rW  rX  r1   r7  rY  Nr`  r[  z/Release branch with no newsfragments, all good.r]  r^  r*   r*   r+   Útest_release  s   ÿ
ÿz)CheckNewsfragmentScriptTests.test_releasec                 C   óð   t g d¢| jjd | j d¡ d¡}| ¡  | d¡}| d¡ | j d¡}| d¡ t d	d
|j|jg| jjd t g d¢| jjd g }|  t¡}t|j	ƒ 
| jjg¡ W d  ƒ n1 saw   Y  |  |jjd¡ |  |d d¡ dS )zd
        Running it on a release branch returns red if there are new
        newsfragments.
        rd  rI  ry   rn   ú	1234.miscrm   rW  ó   Boor1   r7  ©r1   r8  r9  ÚfragmentNrZ  r[  z1No newsfragments should be on the release branch.©r   rO  r4   r3   rÇ   rD   r   rà   r
   rä   rá   rX   r  r:   ©r(   rn   rj  Ú	unrelatedrR  r    r*   r*   r+   Útest_releaseWithNewsfragments)  s"   ÿ


ÿz:CheckNewsfragmentScriptTests.test_releaseWithNewsfragmentsc                 C   sÐ   t g d¢| jjd | j d¡}| d¡ t dd|j|jg| jjd t g d¢| jjd g }|  tdt¡ |  t	¡}t
|jƒ | jjg¡ W d	  ƒ n1 sQw   Y  |  |jjd
¡ |  |d d¡ d	S )zƒ
        Running it on the autoupdate branch returns green if there is no
        newsfragments even if there are changes.
        ©r1   rF  rG  r   rI  rW  rX  r1   r7  rY  rM  Nr`  r[  z3Autoupdated branch with no newsfragments, all good.)r   rO  r4   r3   rD   rÅ   rF   ÚPRECOMMIT_CI_ENVIRONr   rà   r
   rä   rá   rX   r  r:   r^  r*   r*   r+   Útest_preCommitAutoupdateE  s    ÿ
ÿÿz5CheckNewsfragmentScriptTests.test_preCommitAutoupdatec                 C   sþ   t g d¢| jjd | j d¡ d¡}| ¡  | d¡}| d¡ | j d¡}| d¡ t d	d
|j|jg| jjd t g d¢| jjd g }|  tdt¡ |  	t
¡}t|jƒ | jjg¡ W d  ƒ n1 shw   Y  |  |jjd¡ |  |d d¡ dS )zi
        Running it on the autoupdate branch returns red if there are new
        newsfragments.
        ro  rI  ry   rn   rg  rm   rW  rh  r1   r7  ri  rM  NrZ  r[  z<No newsfragments should be present on an autoupdated branch.)r   rO  r4   r3   rÇ   rD   rÅ   rF   rp  r   rà   r
   rä   rá   rX   r  r:   rl  r*   r*   r+   Ú)test_preCommitAutoupdateWithNewsfragments_  s(   ÿ


ÿÿzFCheckNewsfragmentScriptTests.test_preCommitAutoupdateWithNewsfragmentsc                 C   sÖ   t g d¢| jjd | j d¡ d¡}| ¡  | d¡}| d¡ t dd|jg| jjd t g d	¢| jjd g }|  t¡}t|j	ƒ 
| jjg¡ W d
  ƒ n1 sTw   Y  |  |jjd¡ |  |d d¡ d
S )zR
        Running it on a branch with only a quotefile change gives green.
        ©r1   rF  rG  Ú	quotefilerI  rø   ÚfunzTwisted.Quotess	   Beep boopr1   r7  )r1   r8  r9  ÚquotesNr`  r[  z+Quotes change only; no newsfragment needed.rk  )r(   ru  rv  rR  r    r*   r*   r+   Útest_onlyQuotes~  s   

ÿz,CheckNewsfragmentScriptTests.test_onlyQuotesc                 C   rf  )zn
        Running it on a branch with a fragment in the newsfragments dir added
        returns green.
        rs  rI  ry   rn   rg  rm   rW  rh  r1   r7  ©r1   r8  r9  ÚnewsfragmentNr`  r[  ú%Found twisted/newsfragments/1234.miscrk  rl  r*   r*   r+   Útest_newsfragmentAdded”  ó   


ÿz3CheckNewsfragmentScriptTests.test_newsfragmentAddedc                 C   rf  )zo
        Running it on a branch with a non-fragment in the topfiles dir does not
        return green.
        rs  rI  ry   Útopfilesz1234.txtrm   rW  rh  r1   r7  )r1   r8  r9  znot topfileNrZ  r[  r\  rk  )r(   r}  ÚnotFragmentrm  rR  r    r*   r*   r+   Útest_topfileButNotFragmentAdded®  r|  z<CheckNewsfragmentScriptTests.test_topfileButNotFragmentAddedc                 C   sî   t g d¢| jjd | j d¡ d¡}| ¡  | d¡}| d¡ | d¡}| d¡ t d	d
|j|jg| jjd t g d¢| jjd g }|  t¡}t|j	ƒ 
| jjg¡ W d  ƒ n1 s`w   Y  |  |jjd¡ |  |d d¡ dS )zœ
        Running it on a branch with a fragment in the topfiles dir added
        returns green, even if there are other files in the topfiles dir.
        rs  rI  ry   rn   rg  rm   rW  rh  r1   r7  rx  Nr`  r[  rz  rk  rl  r*   r*   r+   Ú/test_newsfragmentAddedButWithOtherNewsfragmentsÈ  s   



ÿzLCheckNewsfragmentScriptTests.test_newsfragmentAddedButWithOtherNewsfragmentsN)r-   r.   r/   r0   r5  rü   rS  r_  rb  rc  re  rn  rq  rr  rw  r{  r  r€  r*   r*   r*   r+   rE  ¼  s     rE  )Br0   r¤   r;  rc   rF   r&   rÆ   r"   r  Úior   r   Ú
subprocessr   Úunittestr   Úincrementalr   Útwisted.pythonr   Útwisted.python._releaser   r	   r
   r   r   r   r   r   r   r   r   r   r   Útwisted.python.filepathr   Útwisted.python.procutilsr   Útwisted.python.reflectr   Útwisted.trial.unittestr   r   r   Úplatformr5  rp  rP  r    r6   r8   r<   r>   r^   r‰   r¦   r§   r´   rµ   rì   r÷   r  r6  r=  rC  rE  r*   r*   r*   r+   Ú<module>   s^   <
>T*% V+ V$