Blame test/mock_buckets.c

Packit 3adb1e
/* ====================================================================
Packit 3adb1e
 *    Licensed to the Apache Software Foundation (ASF) under one
Packit 3adb1e
 *    or more contributor license agreements.  See the NOTICE file
Packit 3adb1e
 *    distributed with this work for additional information
Packit 3adb1e
 *    regarding copyright ownership.  The ASF licenses this file
Packit 3adb1e
 *    to you under the Apache License, Version 2.0 (the
Packit 3adb1e
 *    "License"); you may not use this file except in compliance
Packit 3adb1e
 *    with the License.  You may obtain a copy of the License at
Packit 3adb1e
 *
Packit 3adb1e
 *      http://www.apache.org/licenses/LICENSE-2.0
Packit 3adb1e
 *
Packit 3adb1e
 *    Unless required by applicable law or agreed to in writing,
Packit 3adb1e
 *    software distributed under the License is distributed on an
Packit 3adb1e
 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
Packit 3adb1e
 *    KIND, either express or implied.  See the License for the
Packit 3adb1e
 *    specific language governing permissions and limitations
Packit 3adb1e
 *    under the License.
Packit 3adb1e
 * ====================================================================
Packit 3adb1e
 */
Packit 3adb1e
Packit 3adb1e
#include <apr_pools.h>
Packit 3adb1e
Packit 3adb1e
#include "serf.h"
Packit 3adb1e
#include "serf_bucket_util.h"
Packit 3adb1e
#include "test_serf.h"
Packit 3adb1e
Packit 3adb1e
/* This bucket uses a list of count - data/len - status actions (provided by the
Packit 3adb1e
   test case), to control the read / read_iovec operations. */
Packit 3adb1e
typedef struct {
Packit 3adb1e
    mockbkt_action *actions;
Packit 3adb1e
    int len;
Packit 3adb1e
    const char *current_data;
Packit 3adb1e
    int remaining_data;
Packit 3adb1e
    int current_action;
Packit 3adb1e
    int remaining_times;
Packit 3adb1e
} mockbkt_context_t;
Packit 3adb1e
Packit 3adb1e
serf_bucket_t *serf_bucket_mock_create(mockbkt_action *actions,
Packit 3adb1e
                                       int len,
Packit 3adb1e
                                       serf_bucket_alloc_t *allocator)
Packit 3adb1e
{
Packit 3adb1e
    mockbkt_context_t *ctx;
Packit 3adb1e
Packit 3adb1e
    ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
Packit 3adb1e
    ctx->actions = actions;
Packit 3adb1e
    ctx->len = len;
Packit 3adb1e
    ctx->current_data = 0l;
Packit 3adb1e
    ctx->remaining_data = -1;
Packit 3adb1e
    ctx->current_action = 0;
Packit 3adb1e
    ctx->remaining_times = -1;
Packit 3adb1e
Packit 3adb1e
    return serf_bucket_create(&serf_bucket_type_mock, allocator, ctx);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t next_action(mockbkt_context_t *ctx)
Packit 3adb1e
{
Packit 3adb1e
    mockbkt_action *action;
Packit 3adb1e
Packit 3adb1e
    while (1)
Packit 3adb1e
    {
Packit 3adb1e
        if (ctx->current_action >= ctx->len)
Packit 3adb1e
            return APR_EOF;
Packit 3adb1e
Packit 3adb1e
        action = &ctx->actions[ctx->current_action];
Packit 3adb1e
Packit 3adb1e
        if (ctx->remaining_times == 0) {
Packit 3adb1e
            ctx->current_action++;
Packit 3adb1e
            ctx->remaining_times = -1;
Packit 3adb1e
            ctx->remaining_data = -1;
Packit 3adb1e
            continue;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        if (ctx->remaining_data <= 0) {
Packit 3adb1e
            ctx->current_data = action->data;
Packit 3adb1e
            ctx->remaining_times = action->times;
Packit 3adb1e
            ctx->remaining_data = strlen(action->data);
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        return APR_SUCCESS;
Packit 3adb1e
    }
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t serf_mock_readline(serf_bucket_t *bucket,
Packit 3adb1e
                                       int acceptable, int *found,
Packit 3adb1e
                                       const char **data, apr_size_t *len)
Packit 3adb1e
{
Packit 3adb1e
    mockbkt_context_t *ctx = bucket->data;
Packit 3adb1e
    mockbkt_action *action;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
    const char *start_line;
Packit 3adb1e
Packit 3adb1e
    status = next_action(ctx);
Packit 3adb1e
    if (status) {
Packit 3adb1e
        *len = 0;
Packit 3adb1e
        return status;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    action = &ctx->actions[ctx->current_action];
Packit 3adb1e
    start_line = *data = ctx->current_data;
Packit 3adb1e
    *len = ctx->remaining_data;
Packit 3adb1e
Packit 3adb1e
    serf_util_readline(&start_line, len, acceptable, found);
Packit 3adb1e
Packit 3adb1e
    /* See how much ctx->current moved forward. */
Packit 3adb1e
    *len = start_line - ctx->current_data;
Packit 3adb1e
    ctx->remaining_data -= *len;
Packit 3adb1e
    ctx->current_data += *len;
Packit 3adb1e
    if (ctx->remaining_data == 0)
Packit 3adb1e
        ctx->remaining_times--;
Packit 3adb1e
Packit 3adb1e
    return ctx->remaining_data ? APR_SUCCESS : action->status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t serf_mock_read(serf_bucket_t *bucket,
Packit 3adb1e
                                   apr_size_t requested,
Packit 3adb1e
                                   const char **data, apr_size_t *len)
Packit 3adb1e
{
Packit 3adb1e
    mockbkt_context_t *ctx = bucket->data;
Packit 3adb1e
    mockbkt_action *action;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    status = next_action(ctx);
Packit 3adb1e
    if (status) {
Packit 3adb1e
        *len = 0;
Packit 3adb1e
        return status;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    action = &ctx->actions[ctx->current_action];
Packit 3adb1e
    *len = requested < ctx->remaining_data ? requested : ctx->remaining_data;
Packit 3adb1e
    *data = ctx->current_data;
Packit 3adb1e
Packit 3adb1e
    ctx->remaining_data -= *len;
Packit 3adb1e
    ctx->current_data += *len;
Packit 3adb1e
Packit 3adb1e
    if (ctx->remaining_data == 0)
Packit 3adb1e
        ctx->remaining_times--;
Packit 3adb1e
Packit 3adb1e
    return ctx->remaining_data ? APR_SUCCESS : action->status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t serf_mock_peek(serf_bucket_t *bucket,
Packit 3adb1e
                                   const char **data,
Packit 3adb1e
                                   apr_size_t *len)
Packit 3adb1e
{
Packit 3adb1e
    mockbkt_context_t *ctx = bucket->data;
Packit 3adb1e
    mockbkt_action *action;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    status = next_action(ctx);
Packit 3adb1e
    if (status)
Packit 3adb1e
        return status;
Packit 3adb1e
Packit 3adb1e
    action = &ctx->actions[ctx->current_action];
Packit 3adb1e
    *len = ctx->remaining_data;
Packit 3adb1e
    *data = ctx->current_data;
Packit 3adb1e
Packit 3adb1e
    /* peek only returns an error, APR_EOF or APR_SUCCESS.
Packit 3adb1e
       APR_EAGAIN is returned as APR_SUCCESS. */
Packit 3adb1e
    if (SERF_BUCKET_READ_ERROR(action->status))
Packit 3adb1e
        return status;
Packit 3adb1e
Packit 3adb1e
    return action->status == APR_EOF ? APR_EOF : APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* An action { "", 0, APR_EAGAIN } means that serf should exit serf_context_run
Packit 3adb1e
   and pass the buck back to the application. As long as no new data arrives,
Packit 3adb1e
   this action remains active.
Packit 3adb1e
 
Packit 3adb1e
   This function allows the 'application' to trigger the arrival of more data.
Packit 3adb1e
   If the current action is { "", 0, APR_EAGAIN }, reduce the number of times
Packit 3adb1e
   the action should run by one, and proceed with the next action if needed.
Packit 3adb1e
 */
Packit 3adb1e
apr_status_t serf_bucket_mock_more_data_arrived(serf_bucket_t *bucket)
Packit 3adb1e
{
Packit 3adb1e
    mockbkt_context_t *ctx = bucket->data;
Packit 3adb1e
    mockbkt_action *action;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    status = next_action(ctx);
Packit 3adb1e
    if (status)
Packit 3adb1e
        return status;
Packit 3adb1e
Packit 3adb1e
    action = &ctx->actions[ctx->current_action];
Packit 3adb1e
    if (ctx->remaining_data == 0 && action->status == APR_EAGAIN) {
Packit 3adb1e
        ctx->remaining_times--;
Packit 3adb1e
        action->times--;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
const serf_bucket_type_t serf_bucket_type_mock = {
Packit 3adb1e
    "MOCK",
Packit 3adb1e
    serf_mock_read,
Packit 3adb1e
    serf_mock_readline,
Packit 3adb1e
    serf_default_read_iovec,
Packit 3adb1e
    serf_default_read_for_sendfile,
Packit 3adb1e
    serf_default_read_bucket,
Packit 3adb1e
    serf_mock_peek,
Packit 3adb1e
    serf_default_destroy_and_data,
Packit 3adb1e
};
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
/* internal test for the mock buckets */
Packit 3adb1e
static void test_basic_mock_bucket(CuTest *tc)
Packit 3adb1e
{
Packit 3adb1e
    serf_bucket_t *mock_bkt;
Packit 3adb1e
    apr_pool_t *test_pool = tc->testBaton;
Packit 3adb1e
    serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool, NULL,
Packit 3adb1e
                                                              NULL);
Packit 3adb1e
    /* read one line */
Packit 3adb1e
    {
Packit 3adb1e
        mockbkt_action actions[]= {
Packit 3adb1e
            { 1, "HTTP/1.1 200 OK" CRLF, APR_EOF },
Packit 3adb1e
        };
Packit 3adb1e
        mock_bkt = serf_bucket_mock_create(actions, 1, alloc);
Packit 3adb1e
        read_and_check_bucket(tc, mock_bkt,
Packit 3adb1e
                              "HTTP/1.1 200 OK" CRLF);
Packit 3adb1e
Packit 3adb1e
        mock_bkt = serf_bucket_mock_create(actions, 1, alloc);
Packit 3adb1e
        readlines_and_check_bucket(tc, mock_bkt, SERF_NEWLINE_CRLF,
Packit 3adb1e
                                   "HTTP/1.1 200 OK" CRLF, 1);
Packit 3adb1e
    }
Packit 3adb1e
    /* read one line, character per character */
Packit 3adb1e
    {
Packit 3adb1e
        apr_status_t status;
Packit 3adb1e
        const char *expected = "HTTP/1.1 200 OK" CRLF;
Packit 3adb1e
        mockbkt_action actions[]= {
Packit 3adb1e
            { 1, "HTTP/1.1 200 OK" CRLF, APR_EOF },
Packit 3adb1e
        };
Packit 3adb1e
        mock_bkt = serf_bucket_mock_create(actions, 1, alloc);
Packit 3adb1e
        do
Packit 3adb1e
        {
Packit 3adb1e
            const char *data;
Packit 3adb1e
            apr_size_t len;
Packit 3adb1e
Packit 3adb1e
            status = serf_bucket_read(mock_bkt, 1, &data, &len;;
Packit 3adb1e
            CuAssert(tc, "Got error during bucket reading.",
Packit 3adb1e
                     !SERF_BUCKET_READ_ERROR(status));
Packit 3adb1e
            CuAssert(tc, "Read more data than expected.",
Packit 3adb1e
                     strlen(expected) >= len);
Packit 3adb1e
            CuAssert(tc, "Read data is not equal to expected.",
Packit 3adb1e
                     strncmp(expected, data, len) == 0);
Packit 3adb1e
            CuAssert(tc, "Read more data than requested.",
Packit 3adb1e
                     len <= 1);
Packit 3adb1e
Packit 3adb1e
            expected += len;
Packit 3adb1e
        } while(!APR_STATUS_IS_EOF(status));
Packit 3adb1e
        
Packit 3adb1e
        CuAssert(tc, "Read less data than expected.", strlen(expected) == 0);
Packit 3adb1e
    }
Packit 3adb1e
    /* read multiple lines */
Packit 3adb1e
    {
Packit 3adb1e
        mockbkt_action actions[]= {
Packit 3adb1e
            { 1, "HTTP/1.1 200 OK" CRLF, APR_SUCCESS },
Packit 3adb1e
            { 1, "Content-Type: text/plain" CRLF, APR_EOF },
Packit 3adb1e
        };
Packit 3adb1e
        mock_bkt = serf_bucket_mock_create(actions, 2, alloc);
Packit 3adb1e
        readlines_and_check_bucket(tc, mock_bkt, SERF_NEWLINE_CRLF,
Packit 3adb1e
                                   "HTTP/1.1 200 OK" CRLF
Packit 3adb1e
                                   "Content-Type: text/plain" CRLF, 2);
Packit 3adb1e
    }
Packit 3adb1e
    /* read empty line */
Packit 3adb1e
    {
Packit 3adb1e
        mockbkt_action actions[]= {
Packit 3adb1e
            { 1, "HTTP/1.1 200 OK" CRLF, APR_SUCCESS },
Packit 3adb1e
            { 1, "", APR_EAGAIN },
Packit 3adb1e
            { 1, "Content-Type: text/plain" CRLF, APR_EOF },
Packit 3adb1e
        };
Packit 3adb1e
        mock_bkt = serf_bucket_mock_create(actions, 3, alloc);
Packit 3adb1e
        read_and_check_bucket(tc, mock_bkt,
Packit 3adb1e
                              "HTTP/1.1 200 OK" CRLF
Packit 3adb1e
                              "Content-Type: text/plain" CRLF);
Packit 3adb1e
        mock_bkt = serf_bucket_mock_create(actions, 3, alloc);
Packit 3adb1e
        readlines_and_check_bucket(tc, mock_bkt, SERF_NEWLINE_CRLF,
Packit 3adb1e
                                   "HTTP/1.1 200 OK" CRLF
Packit 3adb1e
                                   "Content-Type: text/plain" CRLF, 2);
Packit 3adb1e
    }
Packit 3adb1e
    /* read empty line */
Packit 3adb1e
    {
Packit 3adb1e
        mockbkt_action actions[]= {
Packit 3adb1e
            { 1, "HTTP/1.1 200 OK" CR, APR_SUCCESS },
Packit 3adb1e
            { 1, "", APR_EAGAIN },
Packit 3adb1e
            { 1, LF, APR_EOF },
Packit 3adb1e
        };
Packit 3adb1e
        mock_bkt = serf_bucket_mock_create(actions,
Packit 3adb1e
                                           sizeof(actions)/sizeof(actions[0]),
Packit 3adb1e
                                           alloc);
Packit 3adb1e
        read_and_check_bucket(tc, mock_bkt,
Packit 3adb1e
                              "HTTP/1.1 200 OK" CRLF);
Packit 3adb1e
Packit 3adb1e
        mock_bkt = serf_bucket_mock_create(actions,
Packit 3adb1e
                                           sizeof(actions)/sizeof(actions[0]),
Packit 3adb1e
                                           alloc);
Packit 3adb1e
        readlines_and_check_bucket(tc, mock_bkt, SERF_NEWLINE_CRLF,
Packit 3adb1e
                                   "HTTP/1.1 200 OK" CRLF, 1);
Packit 3adb1e
    }
Packit 3adb1e
    /* test more_data_arrived */
Packit 3adb1e
    {
Packit 3adb1e
        apr_status_t status;
Packit 3adb1e
        const char *data;
Packit 3adb1e
        apr_size_t len;
Packit 3adb1e
        int i;
Packit 3adb1e
Packit 3adb1e
        mockbkt_action actions[]= {
Packit 3adb1e
            { 1, "", APR_EAGAIN },
Packit 3adb1e
            { 1, "blabla", APR_EOF },
Packit 3adb1e
        };
Packit 3adb1e
        mock_bkt = serf_bucket_mock_create(actions,
Packit 3adb1e
                                           sizeof(actions)/sizeof(actions[0]),
Packit 3adb1e
                                           alloc);
Packit 3adb1e
Packit 3adb1e
        for (i = 0; i < 5; i++) {
Packit 3adb1e
            status = serf_bucket_peek(mock_bkt, &data, &len;;
Packit 3adb1e
            CuAssertIntEquals(tc, APR_SUCCESS, status);
Packit 3adb1e
            CuAssertIntEquals(tc, 0, len);
Packit 3adb1e
            CuAssertIntEquals(tc, '\0', *data);
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        serf_bucket_mock_more_data_arrived(mock_bkt);
Packit 3adb1e
Packit 3adb1e
        status = serf_bucket_peek(mock_bkt, &data, &len;;
Packit 3adb1e
        CuAssertIntEquals(tc, APR_EOF, status);
Packit 3adb1e
        CuAssertIntEquals(tc, 6, len);
Packit 3adb1e
        CuAssert(tc, "Read data is not equal to expected.",
Packit 3adb1e
                 strncmp("blabla", data, len) == 0);
Packit 3adb1e
    }
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
CuSuite *test_mock_bucket(void)
Packit 3adb1e
{
Packit 3adb1e
    CuSuite *suite = CuSuiteNew();
Packit 3adb1e
Packit 3adb1e
    CuSuiteSetSetupTeardownCallbacks(suite, test_setup, test_teardown);
Packit 3adb1e
Packit 3adb1e
    SUITE_ADD_TEST(suite, test_basic_mock_bucket);
Packit 3adb1e
Packit 3adb1e
    return suite;
Packit 3adb1e
}