src/server/test/event.c

Wed, 27 Nov 2024 23:00:07 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Wed, 27 Nov 2024 23:00:07 +0100
changeset 563
6ca97c99173e
parent 552
4ed0e46aa9dc
permissions
-rw-r--r--

add TODO to use a future ucx feature

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2024 Olaf Wintermann. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "event.h"

#include "../daemon/event.h"

typedef struct EVTest {
    EventHandler *h;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    void *data1;
    void *data2;
    int i1;
    int i2;
} EVTest;

UCX_TEST(test_evhandler_create) {
    EventHandlerConfig cfg1 = { .nthreads = 1};
    
    EventHandlerConfig cfg4 = { .nthreads = 4};
    
    UCX_TEST_BEGIN;
    
    EVHandler *ev1 = evhandler_create(&cfg1);
    UCX_TEST_ASSERT(ev1, "evhandler_create (1) failed");
    UCX_TEST_ASSERT(ev1->numins == 1, "ev1 wrong number of instances");
    
    EVHandler *ev2 = evhandler_create(&cfg4);
    UCX_TEST_ASSERT(ev2, "evhandler_create (2) failed");
    UCX_TEST_ASSERT(ev2->numins == 4, "ev2 wrong number of instances");
    
    
    evhandler_shutdown(ev1);
    evhandler_shutdown(ev2);
    
    evhandler_wait_and_destroy(ev1);
    evhandler_wait_and_destroy(ev2);
    
    UCX_TEST_END;
}

static int test_event_send_fn(EventHandler *h, Event *event) {
    EVTest *test = event->cookie;
    test->i1 = 1;
    test->i2 = h == test->h;
    
    pthread_mutex_lock(&test->mutex);
    pthread_cond_signal(&test->cond);
    pthread_mutex_unlock(&test->mutex);
    
    return 0;
}

static void testdata_wait_for_completion(EVTest *testdata) {
    time_t tstart = time(NULL);
    while(!testdata->i1) {
        time_t t = time(NULL);
        if(t - tstart > 10) {
            break;
        }
        
        pthread_mutex_lock(&testdata->mutex);
        pthread_cond_wait(&testdata->cond, &testdata->mutex);
        pthread_mutex_unlock(&testdata->mutex);
    }
}

UCX_TEST(test_event_send) {
    EventHandlerConfig cfg = { .nthreads = 1};
    
    EVHandler *ev = evhandler_create(&cfg);
    EventHandler *h = ev_instance(ev);
    
    EVTest testdata;
    ZERO(&testdata, sizeof(EVTest));
    testdata.h = h;
    pthread_mutex_init(&testdata.mutex, NULL);
    pthread_cond_init(&testdata.cond, NULL);
    
    UCX_TEST_BEGIN;
    
    // test sending a single event
    // the event signals completion in the testdata object
    // wait up to 10 seconds for completion (it should be instantly)
    
    Event evt;
    ZERO(&evt, sizeof(Event));
    evt.fn = test_event_send_fn;
    evt.cookie = &testdata;
    
    int ret = event_send(h, &evt);
    
    // wait for event finish
    testdata_wait_for_completion(&testdata);
    
    UCX_TEST_ASSERT(!ret, "event_send failed");
    UCX_TEST_ASSERT(testdata.i1, "event callback not called");
    UCX_TEST_ASSERT(testdata.i2, "event callback received wrong event handler pointer");
    
    UCX_TEST_END;
    
    pthread_mutex_destroy(&testdata.mutex);
    pthread_cond_destroy(&testdata.cond);
    
    evhandler_shutdown(ev);
    evhandler_wait_and_destroy(ev);
}

#define EV_TEST_NUM_EVENTS 2000

static int test_event_send_multi_fn_count(EventHandler *h, Event *event) {
    EVTest *test = event->cookie;
    
    test->i2++;
    
    return 0;
}

static int test_event_send_multi_fn_end(EventHandler *h, Event *event) {
    EVTest *test = event->cookie;
    test->i1 = 1;
    
    pthread_mutex_lock(&test->mutex);
    pthread_cond_signal(&test->cond);
    pthread_mutex_unlock(&test->mutex);
    
    return 0;
}

static int test_event_send_multi_fn1(EventHandler *h, Event *event) {
    EVTest *test = event->cookie;
    test->i2 = 0;
    
    for(int i=0;i<EV_TEST_NUM_EVENTS;i++) {
        Event *newevent = malloc(sizeof(Event));
        ZERO(newevent, sizeof(Event));
        newevent->fn = test_event_send_multi_fn_count;
        newevent->finish = ev_free_event;
        newevent->cookie = test;
        event_send(h, newevent);
    }
    
    Event *finish_event = malloc(sizeof(Event));
    ZERO(finish_event, sizeof(Event));
    finish_event->fn = test_event_send_multi_fn_end;
    finish_event->finish = ev_free_event;
    finish_event->cookie = test;
    event_send(h, finish_event);
    
    return 0;
}

UCX_TEST(test_event_send_multi) {
    EventHandlerConfig cfg = { .nthreads = 1};
    
    EVHandler *ev = evhandler_create(&cfg);
    EventHandler *h = ev_instance(ev);
    
    EVTest testdata;
    ZERO(&testdata, sizeof(EVTest));
    testdata.h = h;
    pthread_mutex_init(&testdata.mutex, NULL);
    pthread_cond_init(&testdata.cond, NULL);
    
    UCX_TEST_BEGIN;
    
    // test sending multiple events
    // the first callback test_event_send_multi_fn1 adds additional
    // EV_TEST_NUM_EVENTS events to the handler + an additional
    // finishing event, that notifies completion
    
    Event evt;
    ZERO(&evt, sizeof(Event));
    evt.fn = test_event_send_multi_fn1;
    evt.cookie = &testdata;
    
    int ret = event_send(h, &evt);
    
    // wait for event finish
    testdata_wait_for_completion(&testdata);
    
    UCX_TEST_ASSERT(!ret, "event_send failed");
    UCX_TEST_ASSERT(testdata.i1, "event callback not called");
    UCX_TEST_ASSERT(testdata.i2 == EV_TEST_NUM_EVENTS, "event callback received wrong event handler pointer");
    
    UCX_TEST_END;
    
    pthread_mutex_destroy(&testdata.mutex);
    pthread_cond_destroy(&testdata.cond);
    
    evhandler_shutdown(ev);
    evhandler_wait_and_destroy(ev);
}

mercurial