
/*
 * task-test.c:  a collection of svn_task__* tests
 *
 * ====================================================================
 *    Licensed to the Apache Software Foundation (ASF) under one
 *    or more contributor license agreements.  See the NOTICE file
 *    distributed with this work for additional information
 *    regarding copyright ownership.  The ASF licenses this file
 *    to you under the Apache License, Version 2.0 (the
 *    "License"); you may not use this file except in compliance
 *    with the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing,
 *    software distributed under the License is distributed on an
 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *    KIND, either express or implied.  See the License for the
 *    specific language governing permissions and limitations
 *    under the License.
 * ====================================================================
 */

/* ====================================================================
   To add tests, look toward the bottom of this file.

*/

#include <stdio.h>
#include <string.h>
#include <apr_pools.h>

#include "../svn_test.h"

#include "svn_sorts.h"
#include "private/svn_atomic.h"
#include "private/svn_task.h"

static svn_error_t *
test_null_task(apr_pool_t *pool)
{
  SVN_ERR(svn_task__run(1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                        pool, pool));
  SVN_ERR(svn_task__run(2, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                        pool, pool));

  return SVN_NO_ERROR;
}

static svn_error_t *
noop_process_func(void **result,
                  svn_task__t *task,
                  void *thread_context,
                  void *process_baton,
                  svn_cancel_func_t cancel_func,
                  void *cancel_baton,
                  apr_pool_t *result_pool,
                  apr_pool_t *scratch_pool)
{
  *result = NULL;
  return SVN_NO_ERROR;
}

static svn_error_t *
noop_output_func(svn_task__t *task,
                 void *result,
                 void *output_baton,
                 svn_cancel_func_t cancel_func,
                 void *cancel_baton,
                 apr_pool_t *result_pool,
                 apr_pool_t *scratch_pool)
{
  return SVN_NO_ERROR;
}

static svn_error_t *
noop_thead_context_constructor(void **thread_context,
                               void *baton,
                               apr_pool_t *result_pool,
                               apr_pool_t *scratch_pool)
{
  *thread_context = NULL;
  return SVN_NO_ERROR;
}

static svn_error_t *
noop_cancel_func(void *baton)
{
  return SVN_NO_ERROR;
}

static svn_error_t *
test_noop_task(apr_pool_t *pool)
{
  SVN_ERR(svn_task__run(1,
                        noop_process_func, NULL,
                        noop_output_func, NULL,
                        noop_thead_context_constructor, NULL,
                        noop_cancel_func, NULL, pool, pool));
  SVN_ERR(svn_task__run(2,
                        noop_process_func, NULL,
                        noop_output_func, NULL,
                        noop_thead_context_constructor, NULL,
                        noop_cancel_func, NULL, pool, pool));
  return SVN_NO_ERROR;
}

static svn_error_t *
counter_func(void **result,
             svn_task__t *task,
             void *thread_context,
             void *process_baton,
             svn_cancel_func_t cancel_func,
             void *cancel_baton,
             apr_pool_t *result_pool,
             apr_pool_t *scratch_pool)
{
  apr_int64_t value = *(apr_int64_t*)process_baton;

  apr_pool_t *sub_task_pool;
  apr_int64_t *partial_result;
  apr_int64_t *partial_baton;

  if (value > 1)
    {
      partial_result = apr_palloc(result_pool, sizeof(*partial_result));
      *partial_result = 1;
      value -= *partial_result;

      sub_task_pool = svn_task__create_process_pool(task);

      partial_baton = apr_palloc(sub_task_pool, sizeof(*partial_baton));
      *partial_baton = MAX(1, value / 2);
      value -= *partial_baton;

      SVN_ERR(svn_task__add_similar(task, sub_task_pool,
                                    partial_result, partial_baton));
    }

  if (cancel_func)
    SVN_ERR(cancel_func(cancel_baton));

  if (value > 1)
    {
      partial_result = apr_palloc(result_pool, sizeof(*partial_result));
      *partial_result = 1;
      value -= *partial_result;

      sub_task_pool = svn_task__create_process_pool(task);

      partial_baton = apr_palloc(sub_task_pool, sizeof(*partial_baton));
      *partial_baton = value - 1;
      value -= *partial_baton;

      SVN_ERR(svn_task__add_similar(task, sub_task_pool,
                                    partial_result, partial_baton));
    }

  partial_result = apr_palloc(result_pool, sizeof(*partial_result));
  *partial_result = value;
  *result = partial_result;

  return SVN_NO_ERROR;
}

static svn_error_t *
sum_func(svn_task__t *task,
         void *result,
         void *output_baton,
         svn_cancel_func_t cancel_func,
         void *cancel_baton,
         apr_pool_t *result_pool,
         apr_pool_t *scratch_pool)
{
  apr_int64_t *result_p = result;
  apr_int64_t *output_p = output_baton;

  if (result_p)
    *output_p += *result_p;

  if (cancel_func)
    SVN_ERR(cancel_func(cancel_baton));

  return SVN_NO_ERROR;
}

static svn_error_t *
test_counting(apr_pool_t *pool)
{
  apr_int64_t start = 1000000;
  apr_int64_t result = 0;
  SVN_ERR(svn_task__run(1, counter_func, &start, sum_func, &result,
                        NULL, NULL, NULL, NULL, pool, pool));
  SVN_TEST_ASSERT(result == start);

  result = 0;
  SVN_ERR(svn_task__run(4, counter_func, &start, sum_func, &result,
                        NULL, NULL, NULL, NULL, pool, pool));
  SVN_TEST_ASSERT(result == start);

  return SVN_NO_ERROR;
}

static svn_error_t *
cancel_at_10k(void *baton)
{
  if (*(apr_int64_t*)baton == 10000)
    return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL);

  return SVN_NO_ERROR;
}

static svn_error_t *
test_cancellation(apr_pool_t *pool)
{
  apr_int64_t start = 1000000;
  apr_int64_t result = 0;
  SVN_TEST_ASSERT_ERROR(svn_task__run(1, counter_func, &start, sum_func, &result,
                                      NULL, NULL, cancel_at_10k, &result,
                                      pool, pool),
                        SVN_ERR_CANCELLED);
  SVN_TEST_ASSERT(result == 10000);

  result = 0;
  SVN_TEST_ASSERT_ERROR(svn_task__run(8, counter_func, &start, sum_func, &result,
                                      NULL, NULL, cancel_at_10k, &result,
                                      pool, pool),
                        SVN_ERR_CANCELLED);
  SVN_TEST_ASSERT(result == 10000);

  return SVN_NO_ERROR;
}

/* An array of all test functions */

static int max_threads = 1;

static struct svn_test_descriptor_t test_funcs[] =
  {
    SVN_TEST_NULL,
    SVN_TEST_PASS2(test_null_task,
                   "null-task"),
    SVN_TEST_PASS2(test_noop_task,
                   "no-op task"),
    SVN_TEST_PASS2(test_counting,
                   "concurrent counting"),
    SVN_TEST_PASS2(test_cancellation,
                   "cancelling tasks"),
    SVN_TEST_NULL
  };

SVN_TEST_MAIN
