=====================
Remote Task Execution
=====================

This package provides an implementation of a remote task execution Web service
that allows to execute pre-defined tasks on another server. See more info 
about the TaskService in README.txt. This test will test the TaskServiceStub
implementation. The only different is, that the TaskServiceStub will handle 
task implementation providing ITaskStub interfaces rather then ITask. This way
we can register stub tasks for a testing setup. See also another usecase for
a task service stub implementation which is working with XML-RPC in the 
package lovely.transcoding. 

Let's now start by creating a task service stub:

  >>> from lovely import remotetask
  >>> from lovely.remotetask import testing
  >>> service = testing.TaskServiceStub()

We can discover the available tasks:

  >>> service.getAvailableTasks()
  {}

This list is initially empty, because we have not registered any tasks. Let's
now define a task that simply echos an input string:

  >>> def echo(input):
  ...     return input

  >>> import lovely.remotetask.task
  >>> echoTask = remotetask.task.SimpleTask(echo)

The only API requirement on the converter is to be callable. Now we make sure
that the task works:

  >>> echoTask(service, 1, input={'foo': 'blah'})
  {'foo': 'blah'}

Let's now register the task as a utility. Note that we need to register the
echo utility used in the REAME.txt tests for the ITaskStub interface:

  >>> import zope.component
  >>> zope.component.provideUtility(echoTask, provides=testing.ITaskStub, 
  ...     name='echo')

The echo task is now available in the service:

  >>> service.getAvailableTasks()
  {u'echo': <SimpleTask <function echo ...>>}


Since the service cannot instantaneously complete a task, incoming jobs are
managed by a queue. First we request the echo task to be executed:

  >>> jobid = service.add(u'echo', {'foo': 'bar'})
  >>> jobid
  1

Let's also see wat's happen if we add a non existent task:

  >>> service.add(u'undefined', {'foo': 'bar'})
  Traceback (most recent call last):
  ...
  ValueError: Task does not exist

The ``add()`` function schedules the task called "echo" to be executed with
the specified arguments. The method returns a job id with which we can inquire
about the job.

  >>> service.getStatus(jobid)
  'queued'

Since the job has not been processed, the status is set to "queued". Further,
there is no result available yet:

  >>> service.getResult(jobid) is None
  True

As long as the job is not being processed, it can be cancelled:

  >>> service.cancel(jobid)
  >>> service.getStatus(jobid)
  'cancelled'

Let's also see wat's happen if we cancel a non existent task:

  >>> service.cancel(u'undefined')

Let's now readd a job:

  >>> jobid = service.add(u'echo', {'foo': 'bar'})

The jobs in the queue are processed by calling the service's ``process()``
method:

  >>> service.process()

This method is usually called by other application logic, but we have to call
it manually here, since none of the other infrastructure is setup.

  >>> service.getStatus(jobid)
  'completed'
  >>> service.getResult(jobid)
  {'foo': 'bar'}

Now, let's define a new task that causes an error:

  >>> def error(input):
  ...     raise remotetask.task.TaskError('An error occurred.')

  >>> zope.component.provideUtility(
  ...     remotetask.task.SimpleTask(error), provides=testing.ITaskStub, 
  ...     name='error')

Now add and execute it:

  >>> jobid = service.add(u'error')
  >>> service.process()

Let's now see what happened:

  >>> service.getStatus(jobid)
  'error'
  >>> service.getError(jobid)
  'An error occurred.'

For management purposes, the service also allows you to inspect all jobs:

  >>> dict(service.jobs)
  {1: <Job 1>, 2: <Job 2>, 3: <Job 3>}
