.. -*- mode: doctest -*-

Detailed documentation
======================

The recipe z3c.recipe.usercrontab is a small recipe to facilitate the
installing of cronjobs into user crontabs.

    >>> from z3c.recipe.usercrontab.usercrontab import UserCrontabManager


Entry handling
--------------

A user crontab manager manages a user's crontab for one specific buildout
part.  The part ends up in the identifier.  We'll use 'test' here.

    >>> c = UserCrontabManager(identifier='test')

In these tests, we can fake a crontab by filling the list of cron entries
manually:

    >>> c.crontab = ['@reboot echo "hello world"']
    >>> print c # Handy shortcut
    @reboot echo "hello world"

Now, we're adding an entry to it using the official way.  The entry is
surrounded by markers:

    >>> c.add_entry('@reboot echo "I just got added"')
    >>> print c
    @reboot echo "hello world"
    <BLANKLINE>
    # Generated by test
    @reboot echo "I just got added"
    # END test
    <BLANKLINE>

Removing entries also works.  As long as the "Generated by" markers are
present, it doesn't matter which entry you remove: everything surrounded by
the markers is zapped:

    >>> c.del_entry('bla bla') == 1
    True
    >>> print c
    @reboot echo "hello world"

Pre-0.6, a WARNING environment variable was used.  An entry (which content
matters now!) is found there:

    >>> c.crontab = ['@reboot echo "hello world"',
    ...              'WARNING="Everything below is added by bla bla',
    ...              '@reboot echo "old entry 1"',
    ...              '@reboot echo "old entry 2"']
    >>> print c
    @reboot echo "hello world"
    WARNING="Everything below is added by bla bla
    @reboot echo "old entry 1"
    @reboot echo "old entry 2"
    >>> c.del_entry('@reboot echo "old entry 1"')
    1
    >>> print c
    @reboot echo "hello world"
    WARNING="Everything below is added by bla bla
    @reboot echo "old entry 2"

Removing the last remaining entry under WARNING also removes the WARNING:

    >>> c.del_entry('@reboot echo "old entry 2"')
    1
    >>> print c
    @reboot echo "hello world"

Briefly in the 0.5 version, a 'BUILDOUT' environment variable was used for
grouping items per buildout. Now for some up/downgrade testing.  0.5.1 removes
the environment variable again. We'll add an entry with such a (now
deprecated) "grouping environment variable". First the start situation:

    >>> c.crontab=[
    ...     'WARNING="Everything below is added by bla bla',
    ...     'BUILDOUT=my/buildout',
    ...     '@reboot echo nothing happens']
    >>> print c
    WARNING="Everything below is added by bla bla
    BUILDOUT=my/buildout
    @reboot echo nothing happens

Doing anything (adding/removing) zaps BUILDOUT statement:

    >>> c.del_entry('nonexisting')
    0
    >>> print c
    WARNING="Everything below is added by bla bla
    @reboot echo nothing happens

And just to make sure, deleting that entry empties out the whole file:

    >>> c.del_entry('@reboot echo nothing happens')
    1
    >>> print c
    <BLANKLINE>


Read/write crontab methods
--------------------------

Next, test the read_crontab and write_crontab methods; we'll use
``cat`` and a temporary file to not modifiy the crontab of the user
running these tests:

    >>> import tempfile
    >>> t = tempfile.NamedTemporaryFile('w')
    >>> crontestfile = t.name
    >>> t.write("#dummy\n")

    >>> c = UserCrontabManager(readcrontab="cat %s" % crontestfile,
    ...                        writecrontab="cat >%s" % crontestfile,
    ...                        identifier='test')
    >>> c.read_crontab()
    >>> a = repr(c)
    >>> c.add_entry('# improbable entry')
    >>> c.write_crontab()
    >>> c.read_crontab()
    >>> b =repr(c)
    >>> a == b
    False

Now, delete this entry again and make sure the old crontab is restored:

    >>> c.del_entry('# improbable entry') == 1
    True
    >>> c.write_crontab()
    >>> c.read_crontab()
    >>> b = repr(c)
    >>> a == b
    True


Buildout recipe usage
---------------------

Do the buildout shuffle:

    >>> write('buildout.cfg',
    ... '''
    ... [buildout]
    ... parts = foo
    ...
    ... [foo]
    ... recipe = z3c.recipe.usercrontab
    ... times = # @reboot
    ... command = echo nothing happens
    ... readcrontab = cat %(crontest)s
    ... writecrontab = cat >%(crontest)s
    ... ''' % ( { 'crontest': crontestfile } ))

    >>> import os
    >>> print 'start', system(buildout)
    start...
    Installing foo.
    <BLANKLINE>

Check that it really was added to the crontab:

    >>> c.read_crontab()
    >>> b = repr(c)
    >>> a == b
    False

    >>> '# @reboot\techo nothing happens' in c.crontab
    True
    >>> print c
    <BLANKLINE>
    # Generated by /sample-buildout [foo]
    # @reboot   echo nothing happens
    # END /sample-buildout [foo]
    <BLANKLINE>
    
Uninstall the recipe:

    >>> write('buildout.cfg',
    ... '''
    ... [buildout]
    ... parts =
    ... ''' % ( { 'crontest': crontestfile } ))
    >>> print 'start', system(buildout)
    start...
    Uninstalling foo.
    Running uninstall recipe.
    <BLANKLINE>

And check that its entry was removed (i.e., the contents of the
crontab are the same as when this test was started; in any case, the
teardown from the testrunner makes sure the old situation is
restored):

    >>> c.read_crontab()
    >>> b = repr(c)
    >>> a == b
    True

A second part installs fine:

    >>> write('buildout.cfg',
    ... '''
    ... [buildout]
    ... parts = foo bar
    ...
    ... [foo]
    ... recipe = z3c.recipe.usercrontab
    ... times = # @reboot
    ... command = echo nothing happens
    ... readcrontab = cat %(crontest)s
    ... writecrontab = cat >%(crontest)s
    ...
    ... [bar]
    ... recipe = z3c.recipe.usercrontab
    ... times = # @reboot
    ... command = echo something happens
    ... readcrontab = cat %(crontest)s
    ... writecrontab = cat >%(crontest)s
    ... ''' % ( { 'crontest': crontestfile } ))
    >>> print 'start', system(buildout)
    start...
    Installing foo.
    Installing bar.
    <BLANKLINE>
    >>> c.read_crontab()
    >>> print c
    <BLANKLINE>
    # Generated by /sample-buildout [foo]
    # @reboot   echo nothing happens
    # END /sample-buildout [foo]
    <BLANKLINE>
    <BLANKLINE>
    # Generated by /sample-buildout [bar]
    # @reboot   echo something happens
    # END /sample-buildout [bar]
    <BLANKLINE>

Uninstalling also works fine

    >>> write('buildout.cfg',
    ... '''
    ... [buildout]
    ... parts =
    ... ''' % ( { 'crontest': crontestfile } ))
    >>> print 'start', system(buildout)
    start...
    Uninstalling bar.
    Running uninstall recipe.
    Uninstalling foo.
    Running uninstall recipe.
    <BLANKLINE>


Safety valves
-------------

If the section has been removed, nothing can be found by the uninstall.  You
get warnings that way:

    >>> write('buildout.cfg',
    ... '''
    ... [buildout]
    ... parts = foo
    ...
    ... [foo]
    ... recipe = z3c.recipe.usercrontab
    ... times = # @reboot
    ... command = echo nothing happens
    ... readcrontab = cat %(crontest)s
    ... writecrontab = cat >%(crontest)s
    ... ''' % ( { 'crontest': crontestfile } ))

    >>> import os
    >>> print 'start', system(buildout)
    start...
    Installing foo.
    <BLANKLINE>
    >>> c.crontab = []
    >>> c.write_crontab()
    >>> write('buildout.cfg',
    ... '''
    ... [buildout]
    ... parts =
    ... ''' % ( { 'crontest': crontestfile } ))
    >>> print 'start', system(buildout)
    start...
    Uninstalling foo.
    Running uninstall recipe.
    foo: WARNING: Did not find a crontab-entry during uninstall; please check manually if everything was removed correctly
    <BLANKLINE>

Another test: pre-0.6 config simulation:

    >>> write('buildout.cfg',
    ... '''
    ... [buildout]
    ... parts = foo
    ...
    ... [foo]
    ... recipe = z3c.recipe.usercrontab
    ... times = # @reboot
    ... command = echo nothing happens
    ... readcrontab = cat %(crontest)s
    ... writecrontab = cat >%(crontest)s
    ... ''' % ( { 'crontest': crontestfile } ))

    >>> import os
    >>> print 'start', system(buildout)
    start...
    Installing foo.
    <BLANKLINE>
    >>> c.crontab = ['WARNING="Everything below is added by bla bla"',
    ...              'BUILDOUT=/somewhere/out/there',
    ...              '# @reboot\techo nothing happens']
    >>> c.write_crontab()
    >>> write('buildout.cfg',
    ... '''
    ... [buildout]
    ... parts =
    ... ''' % ( { 'crontest': crontestfile } ))
    >>> print 'start', system(buildout)
    start...
    Uninstalling foo.
    Running uninstall recipe.
    <BLANKLINE>
    >>> c.read_crontab()
    >>> print c
    <BLANKLINE>
