Detailed tests of svn.py
========================

.. :doctest:
.. :setup: zest.releaser.tests.functional.setup
.. :teardown: zest.releaser.tests.functional.teardown

Some initial imports:

    >>> from zest.releaser import svn
    >>> from zest.releaser.utils import system
    >>> import os

Project name
------------

The prepared svn project has a setup.py, so the name in there is used:

    >>> os.chdir(svnsourcedir)
    >>> checkout = svn.Subversion()
    >>> checkout.name
    'tha.example'

When the setup.py doesn't exist or doesn't return a proper name, we fall back
to the name in the svn url before trunk/tags:

    >>> orig = checkout.get_setup_py_name
    >>> checkout.get_setup_py_name= lambda: None  # Hack
    >>> checkout.name
    'tha.example'
    >>> checkout.get_setup_py_name = orig  # Restore hack


Diff and commit
---------------

Make a change:

    >>> setup_py = os.path.join(svnsourcedir, 'setup.py')
    >>> open(setup_py, 'a').write('\na = 2\n')
    >>> cmd = checkout.cmd_diff()
    >>> cmd
    'svn diff'
    >>> print system(cmd)
    Index: ...
    --- setup.py      (revision 3)
    +++ setup.py      (working copy)
    @@ -36,3 +36,5 @@
               'console_scripts': [
               ]},
           )
    +
    +a = 2

Commit it:

    >>> cmd = checkout.cmd_commit('small tweak')
    >>> cmd
    'svn commit -m "small tweak"'
    >>> print system(cmd)
    Sending        setup.py
    Transmitting file data...
    Committed revision 5.

Tags
----

Originally there are no tags:

    >>> checkout.available_tags()
    []

The name of the /tags directory (so: a plural) is succesfully detected:

    >>> checkout._tags_name
    'tags'

Create a tag and it will show up:

    >>> cmd = checkout.cmd_create_tag('0.1')
    >>> cmd
    'svn cp file://TESTREPO/tha.example/trunk file://TESTREPO/tha.example/tags/0.1 -m "Tagging 0.1"'
    >>> dont_care = system(cmd)
    >>> checkout.available_tags()
    ['0.1']

A tag url is important for subversion:

    >>> checkout.tag_url('holadijee')
    'file://TESTREPO/tha.example/tags/holadijee'

Make and commit a small change:

    >>> open(setup_py, 'a').write('\nb = 3\n')
    >>> cmd = checkout.cmd_commit('small tweak')
    >>> print system(cmd)
    Sending        setup.py
    Transmitting file data .
    Committed revision 7.

Now we can request the changes since a specific tag.  Note that we use the
``--non-interactive`` option to make sure it can be run from cronjobs or
post-commit hooks:

    >>> cmd = checkout.cmd_diff_last_commit_against_tag('0.1')
    >>> cmd
    'svn --non-interactive diff file://TESTREPO/tha.example/tags/0.1 file://TESTREPO/tha.example/trunk'
    >>> print system(cmd)
    Index: setup.py
    ===================================================================
    --- setup.py      (.../tags/0.1)  (revision 7)
    +++ setup.py      (.../trunk)     (revision 7)
    @@ -38,3 +38,5 @@
           )
    <BLANKLINE>
     a = 2
    +
    +b = 3


Making a tag checkout
---------------------

For checking out a tag, we first need a temp folder to do the checkout in:

    >>> temp = checkout.prepare_checkout_dir('somename')
    >>> temp
    'TMPDIR/somename...'
    >>> os.path.isdir(temp)
    True

The tag checkout command makes a checkout in that tempdir:

    >>> cmd = checkout.cmd_checkout_from_tag('0.1', temp)
    >>> cmd
    'svn co file://TESTREPO/tha.example/tags/0.1 TMPDIR/somename...'
    >>> print '\n'.join(sorted(system(cmd).splitlines()))
    A    TMPDIR/somename.../CHANGES.txt
    A    TMPDIR/somename.../README.txt
    A    TMPDIR/somename.../TODO.txt
    A    TMPDIR/somename.../bootstrap.py
    A    TMPDIR/somename.../buildout.cfg
    A    TMPDIR/somename.../grok-1.0a4.cfg
    A    TMPDIR/somename.../local-checkouts
    A    TMPDIR/somename.../setup.cfg
    A    TMPDIR/somename.../setup.py
    A    TMPDIR/somename.../src
    A    TMPDIR/somename.../src/tha
    A    TMPDIR/somename.../src/tha/__init__.py
    A    TMPDIR/somename.../src/tha/example
    A    TMPDIR/somename.../src/tha/example/USAGE.txt
    A    TMPDIR/somename.../src/tha/example/__init__.py
    A    TMPDIR/somename.../src/tha/example/tests
    A    TMPDIR/somename.../src/tha/example/tests/__init__.py
    A    TMPDIR/somename.../src/tha/example/tests/sample.txt
    A    TMPDIR/somename.../src/tha/example/tests/test.py
    Checked out revision 7.

The checkout is done directly in that temporary directory, so not in a newly
created subdirectory of it:

    >>> sorted(os.listdir(temp))
    ['.svn', 'CHANGES.txt', ...]


Tag corner cases
----------------

Both corner cases do a ``sys.exit()``.  This method has been monkey patched by
the test setup.

    >>> import sys
    >>> sys.exit(1)
    Traceback (most recent call last):
    ...
    RuntimeError: SYSTEM EXIT (code=1)

The information on tags is stored on the server, not locally.  So a network
connection is needed.  When there's a network problem, it is reported:

    >>> # Internal-detail hack
    >>> orig = checkout._cached_url
    >>> checkout._cached_url = 'http://non.existing/nonexisting/trunk'
    >>> # Actual call
    >>> checkout.available_tags()
    Traceback (most recent call last):
    ...
    RuntimeError: SYSTEM EXIT (code=None)
    >>> checkout._cached_url = orig

Subversion stores tags in the /tags directory.  Just a convention.  Sometimes
projects have only a /trunk set up and no tag directory yet.

Set up a new sample project in the repository:

    >>> repo_url
    'file://TESTREPO'
    >>> cmd = 'svn mkdir %s/sample -m "mkdir"' % repo_url
    >>> dont_care = system(cmd)
    >>> cmd = 'svn mkdir %s/sample/trunk -m "mkdir"' % repo_url
    >>> dont_care = system(cmd)

Make a checkout:

    >>> os.chdir(tempdir)
    >>> cmd = 'svn co %s/sample/trunk sample' % repo_url
    >>> dont_care = system(cmd)
    >>> os.chdir('sample')

Now look up the tags.  This asks us to create the tags/ directory.  For the
answering, set up the answer test mode:

    >>> from zest.releaser import utils
    >>> utils.TESTMODE = True
    >>> sample_checkout = svn.Subversion()

There is no tags dir yet, so the tags name detection (/tag or /tags) returns
none:

    >>> sample_checkout._tags_name == None
    True

Answer 'no' the first time we get asked to create the directory.  This means
zest.releaser has no business with this project, so exit.

    >>> utils.answers_for_testing = ['n']
    >>> sample_checkout.available_tags()
    Traceback (most recent call last):
    ...
    RuntimeError: SYSTEM EXIT (code=0)

Answer 'yes' and the tags dir gets created.  The available tags result is
obviously an empty list:

    >>> utils.answers_for_testing = ['y']
    >>> sample_checkout.available_tags()
    tags dir does not exist at file://TESTREPO/sample/tags
    Question: Shall I create it (Y/n)?
    Our reply: y
    <BLANKLINE>
    Committed revision 10.
    []
    >>> cmd = 'svn list %s/sample' % repo_url
    >>> print system(cmd)
    tags/
    trunk/


Singular /tag instead of /tags dir
----------------------------------

Subversion stores tags in the /tags directory.  Just a convention.  But for
instance on codespeak, the singular /tag (and /branch) is the convention.
Zest.releaser supports that.

Set up a new sample project in the repository:

    >>> repo_url
    'file://TESTREPO'
    >>> cmd = 'svn mkdir %s/singular -m "mkdir"' % repo_url
    >>> dont_care = system(cmd)
    >>> cmd = 'svn mkdir %s/singular/trunk -m "mkdir"' % repo_url
    >>> dont_care = system(cmd)

And make a /tag directory.  We don't support adding one ourselves (we always
suggest /tags, make a /tag dir yourself if you really really need it), but we
*do* detect and use one:

    >>> cmd = 'svn mkdir %s/singular/tag -m "mkdir"' % repo_url
    >>> dont_care = system(cmd)

Make a checkout:

    >>> os.chdir(tempdir)
    >>> cmd = 'svn co %s/singular/trunk singular' % repo_url
    >>> dont_care = system(cmd)
    >>> os.chdir('singular')

Now look up the tags.  This asks us to create the tags/ directory.  For the
answering, set up the answer test mode:

    >>> from zest.releaser import utils
    >>> utils.TESTMODE = True

Fire up zest.releaser's subversion support.  The _tags_name() method figures
out the correct name:

    >>> singular_checkout = svn.Subversion()
    >>> singular_checkout._tags_name
    'tag'

There are no tags yet:

    >>> singular_checkout.available_tags()
    []

Tag urls are formed correctly with the singular name:

    >>> singular_checkout.tag_url('0.1')
    'file://TESTREPO/singular/tag/0.1'

Tag and checkout commands also use the singular:

    >>> cmd = singular_checkout.cmd_checkout_from_tag('0.1', temp)
    >>> cmd
    'svn co file://TESTREPO/singular/tag/0.1 TMPDIR/somename...'
    >>> cmd = singular_checkout.cmd_create_tag('0.1')
    >>> cmd
    'svn cp file://TESTREPO/singular/trunk file://TESTREPO/singular/tag/0.1 -m "Tagging 0.1"'

If we have branch checkout, the base url is still extracted correctly, even if
the singular /tag and /branch is used:

    >>> cmd = 'svn mkdir %s/singular/branch -m "mkdir"' % repo_url
    >>> dont_care = system(cmd)
    >>> cmd = 'svn mkdir %s/singular/branch/mybranch -m "mkdir"' % repo_url
    >>> dont_care = system(cmd)
    >>> cmd = 'svn mkdir %s/singular/tag/mytag -m "mkdir"' % repo_url
    >>> dont_care = system(cmd)

    >>> os.chdir(tempdir)
    >>> cmd = 'svn co %s/singular/branch/mybranch mysingularbranch' % repo_url
    >>> dont_care = system(cmd)
    >>> cmd = 'svn co %s/singular/tag/mytag mysingulartag' % repo_url
    >>> dont_care = system(cmd)

    >>> os.chdir(tempdir)
    >>> os.chdir('mysingularbranch')
    >>> singular_checkout = svn.Subversion()
    >>> singular_checkout._base_from_svn()
    'file://TESTREPO/singular/'

    >>> os.chdir(tempdir)
    >>> os.chdir('mysingulartag')
    >>> singular_checkout = svn.Subversion()
    >>> singular_checkout._base_from_svn()
    'file://TESTREPO/singular/'


Pushing changes
---------------

For svn, committing is enough, no need to push stuff to the server as that
happens automatically:

    >>> checkout.push_commands()
    []


Tags within a trunk
--------------------

Some legacy projects, often converted from CVS, have multiple subprojects
under a single trunk.  This creates URLs that contain a regular svn layout
within an top level trunk. For example::

    trunk/
        projectA/
            trunk/
            tags/
            branches/

        projectB
            trunk/
            tags/
            branches/

Create trunk/ for both projects:

    >>> repo_url
    'file://TESTREPO'
    >>> cmd = 'svn mkdir --parents %s/trunk/projectA/trunk -m"mk projectA"' % repo_url
    >>> dont_care = system(cmd)
    >>> cmd = 'svn mkdir %s/trunk/projectB/trunk -m "mkdir projectB"' % repo_url
    >>> dont_care = system(cmd)

Make a checkout:

    >>> os.chdir(tempdir)
    >>> cmd = 'svn co %s/trunk' % repo_url
    >>> dont_care = system(cmd)

Now check that we try to create the proper trunk/projectA/tags directory

    >>> os.chdir('trunk/projectA/trunk')
    >>> from zest.releaser import utils
    >>> utils.TESTMODE = True
    >>> sample_checkout = svn.Subversion()
    >>> utils.answers_for_testing = ['y']
    >>> sample_checkout.available_tags()
    tags dir does not exist at file://TESTREPO/trunk/projectA/tags
    Question: Shall I create it (Y/n)?
    Our reply: y
    <BLANKLINE>
    Committed revision 18.
    []
    >>> cmd = 'svn list -R %s/trunk' % repo_url
    >>> print system(cmd)
    projectA/
    projectA/tags/
    projectA/trunk/
    <BLANKLINE>
