<commit_before>#include "direct.hxx"
#include "istream/istream.hxx"
#include "istream/istream_byte.hxx"
#include "istream/istream_cat.hxx"
#include "istream/istream_fail.hxx"
#include "istream/istream_four.hxx"
#include "istream/istream_head.hxx"
#include "istream/istream_hold.hxx"
#include "istream/istream_inject.hxx"
#include "istream/istream_later.hxx"

#include <glib.h>
#include <event.h>

#include <stdio.h>
#ifdef EXPECTED_RESULT
#include <string.h>
#endif

enum {
#ifdef NO_BLOCKING
    enable_blocking = false,
#else
    enable_blocking = true,
#endif
};

static inline GQuark
test_quark(void)
{
    return g_quark_from_static_string("test");
}

#ifndef FILTER_CLEANUP
static void
cleanup(void)
{
}
#endif

struct ctx {
    bool half;
    bool got_data, eof;
#ifdef EXPECTED_RESULT
    bool record;
    char buffer[sizeof(EXPECTED_RESULT) * 2];
    size_t buffer_length;
#endif
    struct istream *abort_istream;
    int abort_after;

    int block_after;
};

/*
 * istream handler
 *
 */

static size_t
my_istream_data(const void *data, size_t length, void *_ctx)
{
    struct ctx *ctx = (struct ctx *)_ctx;

    (void)data;

    //printf("data(%zu)\n", length);
    ctx->got_data = true;

    if (ctx->abort_istream != NULL && ctx->abort_after-- == 0) {
        GError *error = g_error_new_literal(test_quark(), 0, "abort_istream");
        istream_inject_fault(ctx->abort_istream, error);
        ctx->abort_istream = NULL;
        return 0;
    }

    if (ctx->half && length > 8)
        length = (length + 1) / 2;

    if (ctx->block_after >= 0) {
        --ctx->block_after;
        if (ctx->block_after == -1)
            /* block once */
            return 0;
    }

#ifdef EXPECTED_RESULT
    if (ctx->record) {
#ifdef __clang__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstring-plus-int"
#endif

        assert(ctx->buffer_length + length < sizeof(ctx->buffer));
        assert(memcmp(EXPECTED_RESULT + ctx->buffer_length, data, length) == 0);

#ifdef __clang__
#pragma GCC diagnostic pop
#endif

        if (ctx->buffer_length + length < sizeof(ctx->buffer))
            memcpy(ctx->buffer + ctx->buffer_length, data, length);
        ctx->buffer_length += length;
    }
#endif

    return length;
}

static ssize_t
my_istream_direct(gcc_unused FdType type, int fd,
                  size_t max_length, void *_ctx)
{
    struct ctx *ctx = (struct ctx *)_ctx;

    (void)fd;

    //printf("direct(%u, %zu)\n", type, max_length);
    ctx->got_data = true;

    if (ctx->abort_istream != NULL) {
        GError *error = g_error_new_literal(test_quark(), 0, "abort_istream");
        istream_inject_fault(ctx->abort_istream, error);
        ctx->abort_istream = NULL;
        return 0;
    }

    return max_length;
}

static void
my_istream_eof(void *_ctx)
{
    struct ctx *ctx = (struct ctx *)_ctx;

    //printf("eof\n");
    ctx->eof = true;
}

static void
my_istream_abort(GError *error, void *_ctx)
{
    struct ctx *ctx = (struct ctx *)_ctx;

    //g_printerr("%s\n", error->message);
    g_error_free(error);

#ifdef EXPECTED_RESULT
    assert(!ctx->record);
#endif

    //printf("abort\n");
    ctx->eof = true;
}

static const struct istream_handler my_istream_handler = {
    .data = my_istream_data,
    .direct = my_istream_direct,
    .eof = my_istream_eof,
    .abort = my_istream_abort,
};

/*
 * utils
 *
 */

static int
istream_read_event(struct istream *istream)
{
    istream_read(istream);
    return event_loop(EVLOOP_ONCE|EVLOOP_NONBLOCK);
}

static inline void
istream_read_expect(struct ctx *ctx, struct istream *istream)
{
    int ret;

    assert(!ctx->eof);

    ctx->got_data = false;

    ret = istream_read_event(istream);
    assert(ctx->eof || ctx->got_data || ret == 0);

    /* give istream_later another chance to breathe */
    event_loop(EVLOOP_ONCE|EVLOOP_NONBLOCK);
}

static void
run_istream_ctx(struct ctx *ctx, struct pool *pool, struct istream *istream)
{
    ctx->eof = false;

    gcc_unused off_t a1 = istream_available(istream, false);
    gcc_unused off_t a2 = istream_available(istream, true);

    istream_handler_set(istream, &my_istream_handler, ctx, 0);

    pool_unref(pool);
    pool_commit();

#ifndef NO_GOT_DATA_ASSERT
    while (!ctx->eof)
        istream_read_expect(ctx, istream);
#else
    for (int i = 0; i < 1000 && !ctx->eof; ++i)
           istream_read_event(istream);
#endif

#ifdef EXPECTED_RESULT
    if (ctx->record) {
        assert(ctx->buffer_length == sizeof(EXPECTED_RESULT) - 1);
        assert(memcmp(ctx->buffer, EXPECTED_RESULT, ctx->buffer_length) == 0);
    }
#endif

    cleanup();
    pool_commit();
}

static void
run_istream_block(struct pool *pool, struct istream *istream,
                  gcc_unused bool record,
                  int block_after)
{
    struct ctx ctx = {
        .abort_istream = NULL,
        .block_after = block_after,
#ifdef EXPECTED_RESULT
        .record = record,
#endif
    };

    run_istream_ctx(&ctx, pool, istream);
}

static void
run_istream(struct pool *pool, struct istream *istream, bool record)
{
    run_istream_block(pool, istream, record, -1);
}

/*
 * tests
 *
 */

/** normal run */
static void
test_normal(struct pool *pool)
{
    struct istream *istream;

    pool = pool_new_linear(pool, "test_normal", 8192);

    istream = create_test(pool, create_input(pool));
    assert(istream != NULL);
    assert(!istream_has_handler(istream));

    run_istream(pool, istream, true);
}

/** block once after n data() invocations */
static void
test_block(struct pool *parent_pool)
{
    struct pool *pool;

    for (int n = 0; n < 8; ++n) {
        struct istream *istream;

        pool = pool_new_linear(parent_pool, "test_block", 8192);

        istream = create_test(pool, create_input(pool));
        assert(istream != NULL);
        assert(!istream_has_handler(istream));

        run_istream_block(pool, istream, true, n);
    }
}

/** test with istream_byte */
static void
test_byte(struct pool *pool)
{
    struct istream *istream;

    pool = pool_new_linear(pool, "test_byte", 8192);

    istream = create_test(pool, istream_byte_new(*pool, *create_input(pool)));
    run_istream(pool, istream, true);
}

/** accept only half of the data */
static void
test_half(struct pool *pool)
{
    struct ctx ctx = {
        .eof = false,
        .half = true,
#ifdef EXPECTED_RESULT
        .record = true,
#endif
        .abort_istream = NULL,
        .block_after = -1,
    };

    pool = pool_new_linear(pool, "test_half", 8192);

    run_istream_ctx(&ctx, pool, create_test(pool, create_input(pool)));
}

/** input fails */
static void
test_fail(struct pool *pool)
{
    struct istream *istream;

    pool = pool_new_linear(pool, "test_fail", 8192);

    GError *error = g_error_new_literal(test_quark(), 0, "test_fail");
    istream = create_test(pool, istream_fail_new(pool, error));
    run_istream(pool, istream, false);
}

/** input fails after the first byte */
static void
test_fail_1byte(struct pool *pool)
{
    struct istream *istream;

    pool = pool_new_linear(pool, "test_fail_1byte", 8192);

    GError *error = g_error_new_literal(test_quark(), 0, "test_fail");
    istream = create_test(pool,
                          istream_cat_new(pool,
                                          istream_head_new(pool, create_input(pool),
                                                           1, false),
                                          istream_fail_new(pool, error),
                                          NULL));
    run_istream(pool, istream, false);
}

/** abort without handler */
static void
test_abort_without_handler(struct pool *pool)
{
    struct istream *istream;

    pool = pool_new_linear(pool, "test_abort_without_handler", 8192);

    istream = create_test(pool, create_input(pool));
    pool_unref(pool);
    pool_commit();

    istream_close_unused(istream);

    cleanup();
    pool_commit();
}

#ifndef NO_ABORT_ISTREAM

/** abort in handler */
static void
test_abort_in_handler(struct pool *pool)
{
    struct ctx ctx = {
        .eof = false,
        .half = false,
#ifdef EXPECTED_RESULT
        .record = false,
#endif
        .abort_after = 0,
        .block_after = -1,
    };

    pool = pool_new_linear(pool, "test_abort_in_handler", 8192);

    ctx.abort_istream = istream_inject_new(pool, create_input(pool));
    struct istream *istream = create_test(pool, ctx.abort_istream);
    istream_handler_set(istream, &my_istream_handler, &ctx, 0);
    pool_unref(pool);
    pool_commit();

    while (!ctx.eof) {
        istream_read_expect(&ctx, istream);
        event_loop(EVLOOP_ONCE|EVLOOP_NONBLOCK);
    }

    assert(ctx.abort_istream == NULL);

    cleanup();
    pool_commit();
}

/** abort in handler, with some data consumed */
static void
test_abort_in_handler_half(struct pool *pool)
{
    struct ctx ctx = {
        .eof = false,
        .half = true,
#ifdef EXPECTED_RESULT
        .record = false,
#endif
        .abort_after = 2,
        .block_after = -1,
    };

    pool = pool_new_linear(pool, "test_abort_in_handler_half", 8192);

    ctx.abort_istream = istream_inject_new(pool, istream_four_new(pool, create_input(pool)));
    struct istream *istream = create_test(pool, istream_byte_new(*pool, *ctx.abort_istream));
    istream_handler_set(istream, &my_istream_handler, &ctx, 0);
    pool_unref(pool);
    pool_commit();

    while (!ctx.eof) {
        istream_read_expect(&ctx, istream);
        event_loop(EVLOOP_ONCE|EVLOOP_NONBLOCK);
    }

    assert(ctx.abort_istream == NULL || ctx.abort_after >= 0);

    cleanup();
    pool_commit();
}

#endif

/** abort after 1 byte of output */
static void
test_abort_1byte(struct pool *pool)
{
    struct istream *istream;

    pool = pool_new_linear(pool, "test_abort_1byte", 8192);

    istream = istream_head_new(pool,
                               create_test(pool,
                                           create_input(pool)),
                               1, false);
    run_istream(pool, istream, false);
}

/** test with istream_later filter */
static void
test_later(struct pool *pool)
{
    struct istream *istream;

    pool = pool_new_linear(pool, "test_later", 8192);

    istream = create_test(pool, istream_later_new(pool, create_input(pool)));
    run_istream(pool, istream, true);
}

#ifdef EXPECTED_RESULT
/** test with large input and blocking handler */
static void
test_big_hold(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_big_hold", 8192);

    struct istream *istream = create_input(pool);
    for (unsigned i = 0; i < 1024; ++i)
        istream = istream_cat_new(pool, istream, create_input(pool), NULL);

    istream = create_test(pool, istream);
    struct istream *hold = istream_hold_new(pool, istream);

    istream_read(istream);

    istream_close_unused(hold);

    pool_unref(pool);
}
#endif

/*
 * main
 *
 */

int main(int argc, char **argv) {
    struct pool *root_pool;
    struct event_base *event_base;

    (void)argc;
    (void)argv;

    direct_global_init();
    event_base = event_init();

    root_pool = pool_new_libc(NULL, "root");

    /* run test suite */

    test_normal(root_pool);
    if (enable_blocking)
        test_block(root_pool);
    if (enable_blocking)
        test_byte(root_pool);
    test_half(root_pool);
    test_fail(root_pool);
    test_fail_1byte(root_pool);
    test_abort_without_handler(root_pool);
#ifndef NO_ABORT_ISTREAM
    test_abort_in_handler(root_pool);
    if (enable_blocking)
        test_abort_in_handler_half(root_pool);
#endif
    test_abort_1byte(root_pool);
    test_later(root_pool);

#ifdef EXPECTED_RESULT
    test_big_hold(root_pool);
#endif

#ifdef CUSTOM_TEST
    test_custom(root_pool);
#endif

    /* cleanup */

    pool_unref(root_pool);
    pool_commit();

    pool_recycler_clear();

    event_base_free(event_base);
    direct_global_deinit();
}
<commit_msg>test/t_istream_filter: add another blocking test<commit_after>#include "direct.hxx"
#include "istream/istream.hxx"
#include "istream/istream_byte.hxx"
#include "istream/istream_cat.hxx"
#include "istream/istream_fail.hxx"
#include "istream/istream_four.hxx"
#include "istream/istream_head.hxx"
#include "istream/istream_hold.hxx"
#include "istream/istream_inject.hxx"
#include "istream/istream_later.hxx"

#include <glib.h>
#include <event.h>

#include <stdio.h>
#ifdef EXPECTED_RESULT
#include <string.h>
#endif

enum {
#ifdef NO_BLOCKING
    enable_blocking = false,
#else
    enable_blocking = true,
#endif
};

static inline GQuark
test_quark(void)
{
    return g_quark_from_static_string("test");
}

#ifndef FILTER_CLEANUP
static void
cleanup(void)
{
}
#endif

struct ctx {
    bool half;
    bool got_data, eof;
#ifdef EXPECTED_RESULT
    bool record;
    char buffer[sizeof(EXPECTED_RESULT) * 2];
    size_t buffer_length;
#endif
    struct istream *abort_istream;
    int abort_after;

    int block_after;

    bool block_byte, block_byte_state;
};

/*
 * istream handler
 *
 */

static size_t
my_istream_data(const void *data, size_t length, void *_ctx)
{
    struct ctx *ctx = (struct ctx *)_ctx;

    (void)data;

    //printf("data(%zu)\n", length);
    ctx->got_data = true;

    if (ctx->block_byte) {
        ctx->block_byte_state = !ctx->block_byte_state;
        if (ctx->block_byte_state)
            return 0;
    }

    if (ctx->abort_istream != NULL && ctx->abort_after-- == 0) {
        GError *error = g_error_new_literal(test_quark(), 0, "abort_istream");
        istream_inject_fault(ctx->abort_istream, error);
        ctx->abort_istream = NULL;
        return 0;
    }

    if (ctx->half && length > 8)
        length = (length + 1) / 2;

    if (ctx->block_after >= 0) {
        --ctx->block_after;
        if (ctx->block_after == -1)
            /* block once */
            return 0;
    }

#ifdef EXPECTED_RESULT
    if (ctx->record) {
#ifdef __clang__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstring-plus-int"
#endif

        assert(ctx->buffer_length + length < sizeof(ctx->buffer));
        assert(memcmp(EXPECTED_RESULT + ctx->buffer_length, data, length) == 0);

#ifdef __clang__
#pragma GCC diagnostic pop
#endif

        if (ctx->buffer_length + length < sizeof(ctx->buffer))
            memcpy(ctx->buffer + ctx->buffer_length, data, length);
        ctx->buffer_length += length;
    }
#endif

    return length;
}

static ssize_t
my_istream_direct(gcc_unused FdType type, int fd,
                  size_t max_length, void *_ctx)
{
    struct ctx *ctx = (struct ctx *)_ctx;

    (void)fd;

    //printf("direct(%u, %zu)\n", type, max_length);
    ctx->got_data = true;

    if (ctx->abort_istream != NULL) {
        GError *error = g_error_new_literal(test_quark(), 0, "abort_istream");
        istream_inject_fault(ctx->abort_istream, error);
        ctx->abort_istream = NULL;
        return 0;
    }

    return max_length;
}

static void
my_istream_eof(void *_ctx)
{
    struct ctx *ctx = (struct ctx *)_ctx;

    //printf("eof\n");
    ctx->eof = true;
}

static void
my_istream_abort(GError *error, void *_ctx)
{
    struct ctx *ctx = (struct ctx *)_ctx;

    //g_printerr("%s\n", error->message);
    g_error_free(error);

#ifdef EXPECTED_RESULT
    assert(!ctx->record);
#endif

    //printf("abort\n");
    ctx->eof = true;
}

static const struct istream_handler my_istream_handler = {
    .data = my_istream_data,
    .direct = my_istream_direct,
    .eof = my_istream_eof,
    .abort = my_istream_abort,
};

/*
 * utils
 *
 */

static int
istream_read_event(struct istream *istream)
{
    istream_read(istream);
    return event_loop(EVLOOP_ONCE|EVLOOP_NONBLOCK);
}

static inline void
istream_read_expect(struct ctx *ctx, struct istream *istream)
{
    int ret;

    assert(!ctx->eof);

    ctx->got_data = false;

    ret = istream_read_event(istream);
    assert(ctx->eof || ctx->got_data || ret == 0);

    /* give istream_later another chance to breathe */
    event_loop(EVLOOP_ONCE|EVLOOP_NONBLOCK);
}

static void
run_istream_ctx(struct ctx *ctx, struct pool *pool, struct istream *istream)
{
    ctx->eof = false;

    gcc_unused off_t a1 = istream_available(istream, false);
    gcc_unused off_t a2 = istream_available(istream, true);

    istream_handler_set(istream, &my_istream_handler, ctx, 0);

    pool_unref(pool);
    pool_commit();

#ifndef NO_GOT_DATA_ASSERT
    while (!ctx->eof)
        istream_read_expect(ctx, istream);
#else
    for (int i = 0; i < 1000 && !ctx->eof; ++i)
           istream_read_event(istream);
#endif

#ifdef EXPECTED_RESULT
    if (ctx->record) {
        assert(ctx->buffer_length == sizeof(EXPECTED_RESULT) - 1);
        assert(memcmp(ctx->buffer, EXPECTED_RESULT, ctx->buffer_length) == 0);
    }
#endif

    cleanup();
    pool_commit();
}

static void
run_istream_block(struct pool *pool, struct istream *istream,
                  gcc_unused bool record,
                  int block_after)
{
    struct ctx ctx = {
        .abort_istream = NULL,
        .block_after = block_after,
#ifdef EXPECTED_RESULT
        .record = record,
#endif
    };

    run_istream_ctx(&ctx, pool, istream);
}

static void
run_istream(struct pool *pool, struct istream *istream, bool record)
{
    run_istream_block(pool, istream, record, -1);
}

/*
 * tests
 *
 */

/** normal run */
static void
test_normal(struct pool *pool)
{
    struct istream *istream;

    pool = pool_new_linear(pool, "test_normal", 8192);

    istream = create_test(pool, create_input(pool));
    assert(istream != NULL);
    assert(!istream_has_handler(istream));

    run_istream(pool, istream, true);
}

/** block once after n data() invocations */
static void
test_block(struct pool *parent_pool)
{
    struct pool *pool;

    for (int n = 0; n < 8; ++n) {
        struct istream *istream;

        pool = pool_new_linear(parent_pool, "test_block", 8192);

        istream = create_test(pool, create_input(pool));
        assert(istream != NULL);
        assert(!istream_has_handler(istream));

        run_istream_block(pool, istream, true, n);
    }
}

/** test with istream_byte */
static void
test_byte(struct pool *pool)
{
    struct istream *istream;

    pool = pool_new_linear(pool, "test_byte", 8192);

    istream = create_test(pool, istream_byte_new(*pool, *create_input(pool)));
    run_istream(pool, istream, true);
}

/** block and consume one byte at a time */
static void
test_block_byte(struct pool *pool)
{
    struct istream *istream;

    pool = pool_new_linear(pool, "test_byte", 8192);

    istream = create_test(pool, istream_byte_new(*pool, *create_input(pool)));

    struct ctx ctx = {
        .abort_istream = NULL,
        .block_after = -1,
        .block_byte = true,
#ifdef EXPECTED_RESULT
        .record = true,
#endif
    };

    run_istream_ctx(&ctx, pool, istream);
}

/** accept only half of the data */
static void
test_half(struct pool *pool)
{
    struct ctx ctx = {
        .eof = false,
        .half = true,
#ifdef EXPECTED_RESULT
        .record = true,
#endif
        .abort_istream = NULL,
        .block_after = -1,
    };

    pool = pool_new_linear(pool, "test_half", 8192);

    run_istream_ctx(&ctx, pool, create_test(pool, create_input(pool)));
}

/** input fails */
static void
test_fail(struct pool *pool)
{
    struct istream *istream;

    pool = pool_new_linear(pool, "test_fail", 8192);

    GError *error = g_error_new_literal(test_quark(), 0, "test_fail");
    istream = create_test(pool, istream_fail_new(pool, error));
    run_istream(pool, istream, false);
}

/** input fails after the first byte */
static void
test_fail_1byte(struct pool *pool)
{
    struct istream *istream;

    pool = pool_new_linear(pool, "test_fail_1byte", 8192);

    GError *error = g_error_new_literal(test_quark(), 0, "test_fail");
    istream = create_test(pool,
                          istream_cat_new(pool,
                                          istream_head_new(pool, create_input(pool),
                                                           1, false),
                                          istream_fail_new(pool, error),
                                          NULL));
    run_istream(pool, istream, false);
}

/** abort without handler */
static void
test_abort_without_handler(struct pool *pool)
{
    struct istream *istream;

    pool = pool_new_linear(pool, "test_abort_without_handler", 8192);

    istream = create_test(pool, create_input(pool));
    pool_unref(pool);
    pool_commit();

    istream_close_unused(istream);

    cleanup();
    pool_commit();
}

#ifndef NO_ABORT_ISTREAM

/** abort in handler */
static void
test_abort_in_handler(struct pool *pool)
{
    struct ctx ctx = {
        .eof = false,
        .half = false,
#ifdef EXPECTED_RESULT
        .record = false,
#endif
        .abort_after = 0,
        .block_after = -1,
    };

    pool = pool_new_linear(pool, "test_abort_in_handler", 8192);

    ctx.abort_istream = istream_inject_new(pool, create_input(pool));
    struct istream *istream = create_test(pool, ctx.abort_istream);
    istream_handler_set(istream, &my_istream_handler, &ctx, 0);
    pool_unref(pool);
    pool_commit();

    while (!ctx.eof) {
        istream_read_expect(&ctx, istream);
        event_loop(EVLOOP_ONCE|EVLOOP_NONBLOCK);
    }

    assert(ctx.abort_istream == NULL);

    cleanup();
    pool_commit();
}

/** abort in handler, with some data consumed */
static void
test_abort_in_handler_half(struct pool *pool)
{
    struct ctx ctx = {
        .eof = false,
        .half = true,
#ifdef EXPECTED_RESULT
        .record = false,
#endif
        .abort_after = 2,
        .block_after = -1,
    };

    pool = pool_new_linear(pool, "test_abort_in_handler_half", 8192);

    ctx.abort_istream = istream_inject_new(pool, istream_four_new(pool, create_input(pool)));
    struct istream *istream = create_test(pool, istream_byte_new(*pool, *ctx.abort_istream));
    istream_handler_set(istream, &my_istream_handler, &ctx, 0);
    pool_unref(pool);
    pool_commit();

    while (!ctx.eof) {
        istream_read_expect(&ctx, istream);
        event_loop(EVLOOP_ONCE|EVLOOP_NONBLOCK);
    }

    assert(ctx.abort_istream == NULL || ctx.abort_after >= 0);

    cleanup();
    pool_commit();
}

#endif

/** abort after 1 byte of output */
static void
test_abort_1byte(struct pool *pool)
{
    struct istream *istream;

    pool = pool_new_linear(pool, "test_abort_1byte", 8192);

    istream = istream_head_new(pool,
                               create_test(pool,
                                           create_input(pool)),
                               1, false);
    run_istream(pool, istream, false);
}

/** test with istream_later filter */
static void
test_later(struct pool *pool)
{
    struct istream *istream;

    pool = pool_new_linear(pool, "test_later", 8192);

    istream = create_test(pool, istream_later_new(pool, create_input(pool)));
    run_istream(pool, istream, true);
}

#ifdef EXPECTED_RESULT
/** test with large input and blocking handler */
static void
test_big_hold(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_big_hold", 8192);

    struct istream *istream = create_input(pool);
    for (unsigned i = 0; i < 1024; ++i)
        istream = istream_cat_new(pool, istream, create_input(pool), NULL);

    istream = create_test(pool, istream);
    struct istream *hold = istream_hold_new(pool, istream);

    istream_read(istream);

    istream_close_unused(hold);

    pool_unref(pool);
}
#endif

/*
 * main
 *
 */

int main(int argc, char **argv) {
    struct pool *root_pool;
    struct event_base *event_base;

    (void)argc;
    (void)argv;

    direct_global_init();
    event_base = event_init();

    root_pool = pool_new_libc(NULL, "root");

    /* run test suite */

    test_normal(root_pool);
    if (enable_blocking) {
        test_block(root_pool);
        test_byte(root_pool);
        test_block_byte(root_pool);
    }
    test_half(root_pool);
    test_fail(root_pool);
    test_fail_1byte(root_pool);
    test_abort_without_handler(root_pool);
#ifndef NO_ABORT_ISTREAM
    test_abort_in_handler(root_pool);
    if (enable_blocking)
        test_abort_in_handler_half(root_pool);
#endif
    test_abort_1byte(root_pool);
    test_later(root_pool);

#ifdef EXPECTED_RESULT
    test_big_hold(root_pool);
#endif

#ifdef CUSTOM_TEST
    test_custom(root_pool);
#endif

    /* cleanup */

    pool_unref(root_pool);
    pool_commit();

    pool_recycler_clear();

    event_base_free(event_base);
    direct_global_deinit();
}
<|endoftext|>


<commit_before>#include <CUndo.h>

#include <CFuncs.h>
#include <cassert>
#include <climits>
#include <algorithm>

CUndo::
CUndo() :
 undo_group_(0), depth_(0), locked_(false)
{
}

CUndo::
~CUndo()
{
  std::for_each(undo_list_.begin(), undo_list_.end(), CDeletePointer());
  std::for_each(redo_list_.begin(), redo_list_.end(), CDeletePointer());
}

bool
CUndo::
isInGroup() const
{
  return (depth_ > 0);
}

bool
CUndo::
startGroup()
{
  if (locked())
    return false;

  if (depth_ == 0) {
    assert(undo_group_ == 0);

    undo_group_ = new CUndoGroup(this);
  }

  ++depth_;

  return true;
}

bool
CUndo::
endGroup()
{
  if (locked())
    return false;

  assert(depth_ > 0);

  --depth_;

  if (depth_ == 0) {
    undo_list_.push_back(undo_group_);

    undo_group_ = 0;
  }

  return true;
}

bool
CUndo::
addUndo(CUndoData *data)
{
  if (locked())
    return false;

  std::for_each(redo_list_.begin(), redo_list_.end(), CDeletePointer());

  redo_list_.clear();

  if (undo_group_ == 0) {
    startGroup();

    addUndo(data);

    endGroup();
  }
  else
    undo_group_->addUndo(data);

  return true;
}

bool
CUndo::
undoAll()
{
  return undo(INT_MAX);
}

bool
CUndo::
redoAll()
{
  return redo(INT_MAX);
}

bool
CUndo::
undo(uint n)
{
  bool flag = true;

  for (uint i = 0; i < n; ++i) {
    if (undo_list_.empty())
      return false;

    CUndoGroup *undo_group = undo_list_.back();

    undo_list_.pop_back();

    lock();

    if (! undo_group->undo())
      flag = false;

    unlock();

    redo_list_.push_back(undo_group);
  }

  return flag;
}

bool
CUndo::
redo(uint n)
{
  bool flag = true;

  for (uint i = 0; i < n; ++i) {
    if (redo_list_.empty())
      return false;

    CUndoGroup *undo_group = redo_list_.back();

    redo_list_.pop_back();

    lock();

    if (! undo_group->redo())
      flag = false;

    unlock();

    undo_list_.push_back(undo_group);
  }

  return flag;
}

void
CUndo::
clear()
{
  std::for_each(undo_list_.begin(), undo_list_.end(), CDeletePointer());
  std::for_each(redo_list_.begin(), redo_list_.end(), CDeletePointer());

  undo_list_.clear();
  redo_list_.clear();
}

bool
CUndo::
canUndo() const
{
  return (! undo_list_.empty());
}

bool
CUndo::
canRedo() const
{
  return (! redo_list_.empty());
}

void
CUndo::
lock()
{
  assert(! locked_);

  locked_ = true;
}

void
CUndo::
unlock()
{
  assert(locked_);

  locked_ = false;
}

//--------------------

CUndoGroup::
CUndoGroup(CUndo *) :
 desc_("")
{
}

CUndoGroup::
~CUndoGroup()
{
  std::for_each(data_list_.begin(), data_list_.end(), CDeletePointer());
}

void
CUndoGroup::
addUndo(CUndoData *data)
{
  data_list_.push_back(data);

  data->setGroup(this);
}

std::string
CUndoGroup::
getDesc() const
{
  if (desc_ != "") return desc_;

  if (data_list_.empty())
    return "";
  else
    return data_list_.front()->getDesc();
}

bool
CUndoGroup::
undo()
{
  CUndoData::setError(false);

  std::for_each(data_list_.rbegin(), data_list_.rend(), &CUndoData::execUndoFunc);

  return CUndoData::isError();
}

bool
CUndoGroup::
redo()
{
  CUndoData::setError(false);

  std::for_each(data_list_.begin(), data_list_.end(), &CUndoData::execRedoFunc);

  return CUndoData::isError();
}

//--------------------

CUndoData::
CUndoData() :
 group_(0), state_(UNDO_STATE)
{
}

CUndoData::
~CUndoData()
{
}

void
CUndoData::
execUndoFunc(CUndoData *data)
{
  data->setState(CUndoData::UNDO_STATE);

  CUndoData::setError(data->exec());
}

void
CUndoData::
execRedoFunc(CUndoData *data)
{
  data->setState(CUndoData::REDO_STATE);

  CUndoData::setError(data->exec());
}

bool
CUndoData::
setError(bool flag)
{
  static bool error;

  std::swap(flag, error);

  return flag;
}

bool
CUndoData::
isError()
{
  bool error;

  setError(error = setError(false));

  return error;
}
<commit_msg>new files<commit_after>#include <CUndo.h>

#include <CFuncs.h>
#include <cassert>
#include <climits>
#include <algorithm>

CUndo::
CUndo() :
 undo_group_(0), depth_(0), locked_(false)
{
}

CUndo::
~CUndo()
{
  std::for_each(undo_list_.begin(), undo_list_.end(), CDeletePointer());
  std::for_each(redo_list_.begin(), redo_list_.end(), CDeletePointer());
}

bool
CUndo::
isInGroup() const
{
  return (depth_ > 0);
}

bool
CUndo::
startGroup()
{
  if (locked())
    return false;

  if (depth_ == 0) {
    assert(undo_group_ == 0);

    undo_group_ = new CUndoGroup(this);
  }

  ++depth_;

  return true;
}

bool
CUndo::
endGroup()
{
  if (locked())
    return false;

  assert(depth_ > 0);

  --depth_;

  if (depth_ == 0) {
    undo_list_.push_back(undo_group_);

    undo_group_ = 0;
  }

  return true;
}

bool
CUndo::
addUndo(CUndoData *data)
{
  if (locked())
    return false;

  std::for_each(redo_list_.begin(), redo_list_.end(), CDeletePointer());

  redo_list_.clear();

  if (undo_group_ == 0) {
    startGroup();

    addUndo(data);

    endGroup();
  }
  else
    undo_group_->addUndo(data);

  return true;
}

bool
CUndo::
undoAll()
{
  return undo(INT_MAX);
}

bool
CUndo::
redoAll()
{
  return redo(INT_MAX);
}

bool
CUndo::
undo(uint n)
{
  bool flag = true;

  for (uint i = 0; i < n; ++i) {
    if (undo_list_.empty())
      return false;

    CUndoGroup *undo_group = undo_list_.back();

    undo_list_.pop_back();

    lock();

    if (! undo_group->undo())
      flag = false;

    unlock();

    redo_list_.push_back(undo_group);
  }

  return flag;
}

bool
CUndo::
redo(uint n)
{
  bool flag = true;

  for (uint i = 0; i < n; ++i) {
    if (redo_list_.empty())
      return false;

    CUndoGroup *undo_group = redo_list_.back();

    redo_list_.pop_back();

    lock();

    if (! undo_group->redo())
      flag = false;

    unlock();

    undo_list_.push_back(undo_group);
  }

  return flag;
}

void
CUndo::
clear()
{
  std::for_each(undo_list_.begin(), undo_list_.end(), CDeletePointer());
  std::for_each(redo_list_.begin(), redo_list_.end(), CDeletePointer());

  undo_list_.clear();
  redo_list_.clear();
}

bool
CUndo::
canUndo() const
{
  return (! undo_list_.empty());
}

bool
CUndo::
canRedo() const
{
  return (! redo_list_.empty());
}

void
CUndo::
lock()
{
  assert(! locked_);

  locked_ = true;
}

void
CUndo::
unlock()
{
  assert(locked_);

  locked_ = false;
}

//--------------------

CUndoGroup::
CUndoGroup(CUndo *undo) :
 undo_(undo), desc_("")
{
}

CUndoGroup::
~CUndoGroup()
{
  std::for_each(data_list_.begin(), data_list_.end(), CDeletePointer());
}

void
CUndoGroup::
addUndo(CUndoData *data)
{
  data_list_.push_back(data);

  data->setGroup(this);
}

std::string
CUndoGroup::
getDesc() const
{
  if (desc_ != "") return desc_;

  if (data_list_.empty())
    return "";
  else
    return data_list_.front()->getDesc();
}

bool
CUndoGroup::
undo()
{
  CUndoData::setError(false);

  std::for_each(data_list_.rbegin(), data_list_.rend(), &CUndoData::execUndoFunc);

  return CUndoData::isError();
}

bool
CUndoGroup::
redo()
{
  CUndoData::setError(false);

  std::for_each(data_list_.begin(), data_list_.end(), &CUndoData::execRedoFunc);

  return CUndoData::isError();
}

//--------------------

CUndoData::
CUndoData() :
 group_(0), state_(UNDO_STATE)
{
}

CUndoData::
~CUndoData()
{
}

void
CUndoData::
execUndoFunc(CUndoData *data)
{
  data->setState(CUndoData::UNDO_STATE);

  CUndoData::setError(data->exec());
}

void
CUndoData::
execRedoFunc(CUndoData *data)
{
  data->setState(CUndoData::REDO_STATE);

  CUndoData::setError(data->exec());
}

bool
CUndoData::
setError(bool flag)
{
  static bool error;

  std::swap(flag, error);

  return flag;
}

bool
CUndoData::
isError()
{
  bool error;

  setError(error = setError(false));

  return error;
}
<|endoftext|>


<commit_before>// __BEGIN_LICENSE__
// Copyright (C) 2006-2011 United States Government as represented by
// the Administrator of the National Aeronautics and Space Administration.
// All Rights Reserved.
// __END_LICENSE__

#include <vw/Camera/CameraModel.h>
#include <vw/Stereo/StereoModel.h>
#include <vw/Math/LevenbergMarquardt.h>

namespace vw {
namespace stereo {
  namespace detail {
    class PointLMA : public math::LeastSquaresModelBase<PointLMA> {
      const camera::CameraModel *m_camera1, *m_camera2;

    public:
      typedef Vector<double,4> result_type;
      typedef Vector<double,3> domain_type;
      typedef Matrix<double> jacobian_type;

      PointLMA( camera::CameraModel const* cam1,
                camera::CameraModel const* cam2 ) :
        m_camera1(cam1), m_camera2(cam2) {}

      inline result_type operator()( domain_type const& x ) const {
        Vector4 output;
        subvector(output,0,2) = m_camera1->point_to_pixel( x );
        subvector(output,2,2) = m_camera2->point_to_pixel( x );
        return output;
      }
    };
  }

ImageView<Vector3> StereoModel::operator()(ImageView<PixelMask<Vector2f> > const& disparity_map,
                                           ImageView<double> &error) const {

  // Error analysis
  double mean_error = 0.0;
  double max_error = 0.0;
  int32 point_count = 0;
  int32 divergent = 0;

  // Allocate xyz image and get pointer to buffer
  ImageView<Vector3> xyz(disparity_map.cols(), disparity_map.rows());
  error.set_size(disparity_map.cols(), disparity_map.rows());

  // Compute 3D position for each pixel in the disparity map
  vw_out() << "StereoModel: Applying camera models\n";
  for (int32 y = 0; y < disparity_map.rows(); y++) {
    if (y % 100 == 0) {
      printf("\tStereoModel computing points: %0.2f%% complete.\r", 100.0f*float(y)/disparity_map.rows());
      fflush(stdout);
    }
    for (int32 x = 0; x < disparity_map.cols(); x++) {
      if ( is_valid(disparity_map(x,y)) ) {
        xyz(x,y) = (*this)(Vector2( x, y),
                           Vector2( x+disparity_map(x,y)[0], y+disparity_map(x,y)[1]),
                           error(x,y) );

        if (error(x,y) >= 0) {
          // Keep track of error statistics
          if (error(x,y) > max_error)
            max_error = error(x,y);
          mean_error += error(x,y);
            ++point_count;
        } else {
          // rays diverge or are parallel
          xyz(x,y) = Vector3();
          divergent++;
        }
      } else {
        xyz(x,y) = Vector3();
        error(x,y) = 0;
      }
      }
  }

  if (divergent != 0)
    vw_out() << "WARNING in StereoModel: " << divergent
             << " rays diverged or were parallel!\n";

  vw_out() << "\tStereoModel computing points: Done.                  \n";
  vw_out() << "\tMean error = " << mean_error/double(point_count)
           << ",  Max error = " << max_error << std::endl;
    return xyz;
}

Vector3 StereoModel::operator()(Vector2 const& pix1,
                                Vector2 const& pix2, double& error ) const {

  try {
    // determine range by triangulation
    Vector3 vecFromA = m_camera1->pixel_to_vector(pix1);
    Vector3 vecFromB = m_camera2->pixel_to_vector(pix2);

    // If vecFromA and vecFromB are nearly parallel, there will be
    // very large numerical uncertainty about where to place the
    // point.  We set a threshold here to reject points that are
    // on nearly parallel rays.  The threshold of 1e-4 corresponds
    // to a convergence of less than theta = 0.81 degrees, so if
    // the two rays are within 0.81 degrees of being parallel, we
    // reject this point.
    //
    // This threshold was chosen empirically for now, but should
    // probably be revisited once a more rigorous analysis has
    // been completed. -mbroxton (11-MAR-07)
    if ( (1-dot_prod(vecFromA, vecFromB) < 1e-4 && !m_least_squares) ||
         (1-dot_prod(vecFromA, vecFromB) < 1e-5 && m_least_squares) ) {
      error = 0;
      return Vector3();
    }

    Vector3 originA = m_camera1->camera_center(pix1);
    Vector3 originB = m_camera2->camera_center(pix2);
    Vector3 result =
      triangulate_point(originA, vecFromA,
                        originB, vecFromB,
                        error);

    if ( m_least_squares )
      refine_point(pix1, pix2, result);

    // Reflect points that fall behind one of the two cameras
    if ( dot_prod(result - originA, vecFromA) < 0 ||
         dot_prod(result - originB, vecFromB) < 0 ) {
      result = -result + 2*originA;
    }

    return result;

  } catch (const camera::PixelToRayErr& /*e*/) {
    error = 0;
    return Vector3();
  }
}

double StereoModel::convergence_angle(Vector2 const& pix1, Vector2 const& pix2) const {
  return acos(dot_prod(m_camera1->pixel_to_vector(pix1),
                       m_camera2->pixel_to_vector(pix2)));
}

Vector3 StereoModel::triangulate_point(Vector3 const& pointA,
                                       Vector3 const& vecFromA,
                                       Vector3 const& pointB,
                                       Vector3 const& vecFromB,
                                       double& error) const {

  Vector3 v12 = cross_prod(vecFromA, vecFromB);
  Vector3 v1 = cross_prod(v12, vecFromA);
  Vector3 v2 = cross_prod(v12, vecFromB);

  Vector3 closestPointA = pointA + dot_prod(v2, pointB-pointA)/dot_prod(v2, vecFromA)*vecFromA;
  Vector3 closestPointB = pointB + dot_prod(v1, pointA-pointB)/dot_prod(v1, vecFromB)*vecFromB;

  error = norm_2(closestPointA - closestPointB);
  return 0.5 * (closestPointA + closestPointB);
}

void StereoModel::refine_point(Vector2 const& pix1,
                               Vector2 const& pix2,
                               Vector3& point) const {
  detail::PointLMA model( m_camera1, m_camera2 );
  Vector4 objective( pix1[0], pix1[1], pix2[0], pix2[1] );
  int status = 0;
  Vector3 npoint = levenberg_marquardt( model, point,
                                        objective, status );
  if ( status > 0 )
    point = npoint;
}

}} // vw::stereo
<commit_msg>stereo: Put a limit on iterations in LSQ triangulation<commit_after>// __BEGIN_LICENSE__
// Copyright (C) 2006-2011 United States Government as represented by
// the Administrator of the National Aeronautics and Space Administration.
// All Rights Reserved.
// __END_LICENSE__

#include <vw/Camera/CameraModel.h>
#include <vw/Stereo/StereoModel.h>
#include <vw/Math/LevenbergMarquardt.h>

namespace vw {
namespace stereo {
  namespace detail {
    class PointLMA : public math::LeastSquaresModelBase<PointLMA> {
      const camera::CameraModel *m_camera1, *m_camera2;

    public:
      typedef Vector<double,4> result_type;
      typedef Vector<double,3> domain_type;
      typedef Matrix<double> jacobian_type;

      PointLMA( camera::CameraModel const* cam1,
                camera::CameraModel const* cam2 ) :
        m_camera1(cam1), m_camera2(cam2) {}

      inline result_type operator()( domain_type const& x ) const {
        Vector4 output;
        subvector(output,0,2) = m_camera1->point_to_pixel( x );
        subvector(output,2,2) = m_camera2->point_to_pixel( x );
        return output;
      }
    };
  }

ImageView<Vector3> StereoModel::operator()(ImageView<PixelMask<Vector2f> > const& disparity_map,
                                           ImageView<double> &error) const {

  // Error analysis
  double mean_error = 0.0;
  double max_error = 0.0;
  int32 point_count = 0;
  int32 divergent = 0;

  // Allocate xyz image and get pointer to buffer
  ImageView<Vector3> xyz(disparity_map.cols(), disparity_map.rows());
  error.set_size(disparity_map.cols(), disparity_map.rows());

  // Compute 3D position for each pixel in the disparity map
  vw_out() << "StereoModel: Applying camera models\n";
  for (int32 y = 0; y < disparity_map.rows(); y++) {
    if (y % 100 == 0) {
      printf("\tStereoModel computing points: %0.2f%% complete.\r", 100.0f*float(y)/disparity_map.rows());
      fflush(stdout);
    }
    for (int32 x = 0; x < disparity_map.cols(); x++) {
      if ( is_valid(disparity_map(x,y)) ) {
        xyz(x,y) = (*this)(Vector2( x, y),
                           Vector2( x+disparity_map(x,y)[0], y+disparity_map(x,y)[1]),
                           error(x,y) );

        if (error(x,y) >= 0) {
          // Keep track of error statistics
          if (error(x,y) > max_error)
            max_error = error(x,y);
          mean_error += error(x,y);
            ++point_count;
        } else {
          // rays diverge or are parallel
          xyz(x,y) = Vector3();
          divergent++;
        }
      } else {
        xyz(x,y) = Vector3();
        error(x,y) = 0;
      }
      }
  }

  if (divergent != 0)
    vw_out() << "WARNING in StereoModel: " << divergent
             << " rays diverged or were parallel!\n";

  vw_out() << "\tStereoModel computing points: Done.                  \n";
  vw_out() << "\tMean error = " << mean_error/double(point_count)
           << ",  Max error = " << max_error << std::endl;
    return xyz;
}

Vector3 StereoModel::operator()(Vector2 const& pix1,
                                Vector2 const& pix2, double& error ) const {

  try {
    // determine range by triangulation
    Vector3 vecFromA = m_camera1->pixel_to_vector(pix1);
    Vector3 vecFromB = m_camera2->pixel_to_vector(pix2);

    // If vecFromA and vecFromB are nearly parallel, there will be
    // very large numerical uncertainty about where to place the
    // point.  We set a threshold here to reject points that are
    // on nearly parallel rays.  The threshold of 1e-4 corresponds
    // to a convergence of less than theta = 0.81 degrees, so if
    // the two rays are within 0.81 degrees of being parallel, we
    // reject this point.
    //
    // This threshold was chosen empirically for now, but should
    // probably be revisited once a more rigorous analysis has
    // been completed. -mbroxton (11-MAR-07)
    if ( (1-dot_prod(vecFromA, vecFromB) < 1e-4 && !m_least_squares) ||
         (1-dot_prod(vecFromA, vecFromB) < 1e-5 && m_least_squares) ) {
      error = 0;
      return Vector3();
    }

    Vector3 originA = m_camera1->camera_center(pix1);
    Vector3 originB = m_camera2->camera_center(pix2);
    Vector3 result =
      triangulate_point(originA, vecFromA,
                        originB, vecFromB,
                        error);

    if ( m_least_squares )
      refine_point(pix1, pix2, result);

    // Reflect points that fall behind one of the two cameras
    if ( dot_prod(result - originA, vecFromA) < 0 ||
         dot_prod(result - originB, vecFromB) < 0 ) {
      result = -result + 2*originA;
    }

    return result;

  } catch (const camera::PixelToRayErr& /*e*/) {
    error = 0;
    return Vector3();
  }
}

double StereoModel::convergence_angle(Vector2 const& pix1, Vector2 const& pix2) const {
  return acos(dot_prod(m_camera1->pixel_to_vector(pix1),
                       m_camera2->pixel_to_vector(pix2)));
}

Vector3 StereoModel::triangulate_point(Vector3 const& pointA,
                                       Vector3 const& vecFromA,
                                       Vector3 const& pointB,
                                       Vector3 const& vecFromB,
                                       double& error) const {

  Vector3 v12 = cross_prod(vecFromA, vecFromB);
  Vector3 v1 = cross_prod(v12, vecFromA);
  Vector3 v2 = cross_prod(v12, vecFromB);

  Vector3 closestPointA = pointA + dot_prod(v2, pointB-pointA)/dot_prod(v2, vecFromA)*vecFromA;
  Vector3 closestPointB = pointB + dot_prod(v1, pointA-pointB)/dot_prod(v1, vecFromB)*vecFromB;

  error = norm_2(closestPointA - closestPointB);
  return 0.5 * (closestPointA + closestPointB);
}

void StereoModel::refine_point(Vector2 const& pix1,
                               Vector2 const& pix2,
                               Vector3& point) const {
  detail::PointLMA model( m_camera1, m_camera2 );
  Vector4 objective( pix1[0], pix1[1], pix2[0], pix2[1] );
  int status = 0;
  Vector3 npoint = levenberg_marquardt( model, point,
                                        objective, status, 1e-3, 1e-6, 10 );
  if ( status > 0 )
    point = npoint;
}

}} // vw::stereo
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "stdio.h"

#include "FlyCapture2.h"
#include <iostream>
#include <sstream>
#include <iomanip>
#include <vector>
#include <future>
#include <chrono>
#include <ctime>
#include <thread>

using namespace FlyCapture2;
using namespace std;

void PrintError( Error error )
{
    error.PrintErrorTrace();
}

int get_current_time()
{
	chrono::milliseconds ms = chrono::duration_cast<chrono::milliseconds>(
		chrono::system_clock::now().time_since_epoch());
	return ms.count();
}

int main(int argc, char* argv[])
{
	const Mode k_fmt7Mode = MODE_7;
	const PixelFormat k_fmt7PixFmt = PIXEL_FORMAT_RAW16;
	const vector<int> SHUTTER_SPEEDS { 10,20,50,100,200,500 };

	BusManager busMgr;
	unsigned int numCameras;
	Camera cam;
	Error error;
	PGRGuid guid;

	// TEST FILE WRITE ACCESS
	cout << "Testing file access..." << endl;
    FILE* tempFile = fopen("test.txt", "w+");
    if (tempFile == NULL)  {
		cout << "Failed to create file in current folder.  Please check permissions." << endl;
    	return -1;
 	}
    fclose(tempFile);
    remove("test.txt");

	// TEST CAMERA DETECTION
	cout << "Testing camera detection..." << endl;
    error = busMgr.GetNumOfCameras(&numCameras);
    if (error != PGRERROR_OK){
    	PrintError( error );
    	return -1;
	}

	// GET CAMERA FROM INDEX
 	cout << "Fetching camera from port index" << endl;
	error = busMgr.GetCameraFromIndex(0, &guid);
    if (error != PGRERROR_OK) {
        PrintError( error );
        return -1;
    }

	// CONNECT
	cout << "Connecting to camera..." << endl;
    error = cam.Connect(&guid);
    if (error != PGRERROR_OK) {
	    PrintError( error );
    	return -1;
	}

	// GET CAMERA INFORMATION
	cout << "Fetching camera information..." << endl;
	CameraInfo camInfo;
	error = cam.GetCameraInfo(&camInfo);
	if (error != PGRERROR_OK) {
    	PrintError( error );
        return -1;
	}

	// CREATE FORMAT7 SETTINGS
	cout << "Creating fmt7 settings..." << endl;
  	Format7ImageSettings fmt7ImageSettings;
  	bool valid;
  	Format7PacketInfo fmt7PacketInfo;

  	fmt7ImageSettings.mode = k_fmt7Mode;
  	fmt7ImageSettings.offsetX = 0;
 	fmt7ImageSettings.offsetY = 0;
 	fmt7ImageSettings.width = 1920;
 	fmt7ImageSettings.height = 1200;
 	fmt7ImageSettings.pixelFormat = k_fmt7PixFmt;

	// VALIDATE FORMAT7 SETTINGS
	cout << "Validating fmt7 settings..." << endl;
  	error = cam.ValidateFormat7Settings(&fmt7ImageSettings,
										&valid,
				        				&fmt7PacketInfo );
  	if (error != PGRERROR_OK){
		PrintError( error );
		return -1;
  	}

  	if ( !valid ){
    	// Settings are not valid
    	cout << "Format7 settings are not valid" << endl;
    	return -1;
  	}

	// SET FORMAT7 SETTINGS
	cout << "Writing fmt7 settings..." << endl;
  	error = cam.SetFormat7Configuration(&fmt7ImageSettings,
										fmt7PacketInfo.recommendedBytesPerPacket );
  	if (error != PGRERROR_OK){
    	PrintError( error );
    	return -1;
	}

	// SEQUENCE
	cout << "Starting image sequence..." << endl;
	unsigned int imageCount = 0;
	for(auto const& shutter_speed: SHUTTER_SPEEDS) {
    	cout << "Writing image " << imageCount << " ..." << endl;
		imageCount++;

		// CREATE SHUTTER PROPERTY
  		Property prop;
  		prop.type = SHUTTER;
  		prop.onOff = true;
  		prop.autoManualMode = false;
  		prop.absControl = true;
  		prop.absValue = shutter_speed;

		// WRITE SHUTTE PROPERTY
		error = cam.SetProperty(&prop);
		if (error != PGRERROR_OK){
			PrintError( error );
	    	return -1;
  		}

		// START CAPTURE
		error = cam.StartCapture();
		if (error != PGRERROR_OK){
			PrintError( error );
			return -1;
  		}

		// DELAY FOR CLARITY
		this_thread::sleep_for(chrono::milliseconds(1000));

		// RETRIEVE IMAGE BUFFER
	    Image rawImage;

	    error = cam.RetrieveBuffer( &rawImage );
	    if (error != PGRERROR_OK) {
	    	PrintError( error );
    		return -1;
  		}

		// SET IMAGE DIMENSIONS
    	PixelFormat pixFormat;
    	unsigned int rows, cols, stride;
    	rawImage.GetDimensions( &rows, &cols, &stride, &pixFormat );

		// CONVERT IMAGE
    	Image convertedImage;
    	error = rawImage.Convert( PIXEL_FORMAT_BGRU, &convertedImage );
	    if (error != PGRERROR_OK){
			PrintError( error );
			return -1;
		}

		// SAVE IMAGE
		ostringstream filename;
		int ms_time = get_current_time();
		filename << "img_" << shutter_speed << "_" << ms_time << "_" << argv[1] << ".bmp";

    	error = convertedImage.Save( filename.str().c_str() );
    	if (error != PGRERROR_OK){
    		PrintError( error );
    		return -1;
		}

		// STOP CAPTURE
	    error = cam.StopCapture();
	    if (error != PGRERROR_OK){
	      PrintError( error );
	      return -1;
		}
	}

	// DISCONNECT
    error = cam.Disconnect();
    if (error != PGRERROR_OK){
    	PrintError( error );
    	return -1;
	}

	cout << "Done." << endl;
	return 0;
}
<commit_msg>changed filenames to order by gps and pi timestamps<commit_after>#include "stdafx.h"
#include "stdio.h"

#include "FlyCapture2.h"
#include <iostream>
#include <sstream>
#include <iomanip>
#include <vector>
#include <future>
#include <chrono>
#include <ctime>
#include <thread>

using namespace FlyCapture2;
using namespace std;

void PrintError( Error error )
{
    error.PrintErrorTrace();
}

int get_current_time()
{
	chrono::milliseconds ms = chrono::duration_cast<chrono::milliseconds>(
		chrono::system_clock::now().time_since_epoch());
	return ms.count();
}

int main(int argc, char* argv[])
{
	const Mode k_fmt7Mode = MODE_7;
	const PixelFormat k_fmt7PixFmt = PIXEL_FORMAT_RAW16;
	const vector<int> SHUTTER_SPEEDS { 10,20,50,100,200,500 };

	BusManager busMgr;
	unsigned int numCameras;
	Camera cam;
	Error error;
	PGRGuid guid;

	// TEST FILE WRITE ACCESS
	cout << "Testing file access..." << endl;
    FILE* tempFile = fopen("test.txt", "w+");
    if (tempFile == NULL)  {
		cout << "Failed to create file in current folder.  Please check permissions." << endl;
    	return -1;
 	}
    fclose(tempFile);
    remove("test.txt");

	// TEST CAMERA DETECTION
	cout << "Testing camera detection..." << endl;
    error = busMgr.GetNumOfCameras(&numCameras);
    if (error != PGRERROR_OK){
    	PrintError( error );
    	return -1;
	}

	// GET CAMERA FROM INDEX
 	cout << "Fetching camera from port index" << endl;
	error = busMgr.GetCameraFromIndex(0, &guid);
    if (error != PGRERROR_OK) {
        PrintError( error );
        return -1;
    }

	// CONNECT
	cout << "Connecting to camera..." << endl;
    error = cam.Connect(&guid);
    if (error != PGRERROR_OK) {
	    PrintError( error );
    	return -1;
	}

	// GET CAMERA INFORMATION
	cout << "Fetching camera information..." << endl;
	CameraInfo camInfo;
	error = cam.GetCameraInfo(&camInfo);
	if (error != PGRERROR_OK) {
    	PrintError( error );
        return -1;
	}

	// CREATE FORMAT7 SETTINGS
	cout << "Creating fmt7 settings..." << endl;
  	Format7ImageSettings fmt7ImageSettings;
  	bool valid;
  	Format7PacketInfo fmt7PacketInfo;

  	fmt7ImageSettings.mode = k_fmt7Mode;
  	fmt7ImageSettings.offsetX = 0;
 	fmt7ImageSettings.offsetY = 0;
 	fmt7ImageSettings.width = 1920;
 	fmt7ImageSettings.height = 1200;
 	fmt7ImageSettings.pixelFormat = k_fmt7PixFmt;

	// VALIDATE FORMAT7 SETTINGS
	cout << "Validating fmt7 settings..." << endl;
  	error = cam.ValidateFormat7Settings(&fmt7ImageSettings,
										&valid,
				        				&fmt7PacketInfo );
  	if (error != PGRERROR_OK){
		PrintError( error );
		return -1;
  	}

  	if ( !valid ){
    	// Settings are not valid
    	cout << "Format7 settings are not valid" << endl;
    	return -1;
  	}

	// SET FORMAT7 SETTINGS
	cout << "Writing fmt7 settings..." << endl;
  	error = cam.SetFormat7Configuration(&fmt7ImageSettings,
										fmt7PacketInfo.recommendedBytesPerPacket );
  	if (error != PGRERROR_OK){
    	PrintError( error );
    	return -1;
	}

	// SEQUENCE
	cout << "Starting image sequence..." << endl;
	unsigned int imageCount = 0;
	for(auto const& shutter_speed: SHUTTER_SPEEDS) {
    	cout << "Writing image " << imageCount << " ..." << endl;
		imageCount++;

		// CREATE SHUTTER PROPERTY
  		Property prop;
  		prop.type = SHUTTER;
  		prop.onOff = true;
  		prop.autoManualMode = false;
  		prop.absControl = true;
  		prop.absValue = shutter_speed;

		// WRITE SHUTTE PROPERTY
		error = cam.SetProperty(&prop);
		if (error != PGRERROR_OK){
			PrintError( error );
	    	return -1;
  		}

		// START CAPTURE
		error = cam.StartCapture();
		if (error != PGRERROR_OK){
			PrintError( error );
			return -1;
  		}

		// DELAY FOR CLARITY
		this_thread::sleep_for(chrono::milliseconds(1000));

		// RETRIEVE IMAGE BUFFER
	    Image rawImage;

	    error = cam.RetrieveBuffer( &rawImage );
	    if (error != PGRERROR_OK) {
	    	PrintError( error );
    		return -1;
  		}

		// SET IMAGE DIMENSIONS
    	PixelFormat pixFormat;
    	unsigned int rows, cols, stride;
    	rawImage.GetDimensions( &rows, &cols, &stride, &pixFormat );

		// CONVERT IMAGE
    	Image convertedImage;
    	error = rawImage.Convert( PIXEL_FORMAT_BGRU, &convertedImage );
	    if (error != PGRERROR_OK){
			PrintError( error );
			return -1;
		}

		// SAVE IMAGE
		ostringstream filename;
		int ms_time = get_current_time();
		filename << "img_gps_" << argv[1] << "_pi_" << ms_time << "_shutter_" << shutter_speed << ".bmp";

    	error = convertedImage.Save( filename.str().c_str() );
    	if (error != PGRERROR_OK){
    		PrintError( error );
    		return -1;
		}

		// STOP CAPTURE
	    error = cam.StopCapture();
	    if (error != PGRERROR_OK){
	      PrintError( error );
	      return -1;
		}
	}

	// DISCONNECT
    error = cam.Disconnect();
    if (error != PGRERROR_OK){
    	PrintError( error );
    	return -1;
	}

	cout << "Done." << endl;
	return 0;
}
<|endoftext|>


<commit_before>// Copyright (c) 2019, 2021 by  Robert Bosch GmbH. All rights reserved.
// Copyright (c) 2021 by Apex.AI Inc. All rights reserved.
//
// Licensed 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.
//
// SPDX-License-Identifier: Apache-2.0

#include "iceoryx_posh/internal/mepoo/memory_manager.hpp"
#include "iceoryx_posh/internal/mepoo/shared_chunk.hpp"
#include "iceoryx_posh/mepoo/chunk_header.hpp"
#include "iceoryx_utils/internal/posix_wrapper/shared_memory_object/allocator.hpp"
#include "test.hpp"

using namespace ::testing;

using namespace iox::mepoo;

class SharedChunk_Test : public Test
{
  public:
    void SetUp() override
    {
    }
    void TearDown() override
    {
    }

    ChunkManagement* GetChunkManagement(void* memoryChunk)
    {
        ChunkManagement* v = static_cast<ChunkManagement*>(chunkMgmtPool.getChunk());
        auto chunkSettingsResult = ChunkSettings::create(USER_PAYLOAD_SIZE, iox::CHUNK_DEFAULT_USER_PAYLOAD_ALIGNMENT);
        EXPECT_FALSE(chunkSettingsResult.has_error());
        if (chunkSettingsResult.has_error())
        {
            return nullptr;
        }
        auto& chunkSettings = chunkSettingsResult.value();
        ChunkHeader* chunkHeader = new (memoryChunk) ChunkHeader(mempool.getChunkSize(), chunkSettings);

        new (v) ChunkManagement{chunkHeader, &mempool, &chunkMgmtPool};
        return v;
    }

    static constexpr uint32_t CHUNK_SIZE{64U};
    static constexpr uint32_t NUMBER_OF_CHUNKS{10U};
    static constexpr uint32_t USER_PAYLOAD_SIZE{64U};

    char memory[4096U];
    iox::posix::Allocator allocator{memory, 4096U};
    MemPool mempool{sizeof(ChunkHeader) + USER_PAYLOAD_SIZE, 10U, allocator, allocator};
    MemPool chunkMgmtPool{64U, 10U, allocator, allocator};
    void* memoryChunk{mempool.getChunk()};
    ChunkManagement* chunkManagement = GetChunkManagement(memoryChunk);
    SharedChunk sut{chunkManagement};
};

TEST_F(SharedChunk_Test, SharedChunkObjectUpOnInitilizationSetsTheChunkHeaderToNullPointer)
{
    SharedChunk sut;

    EXPECT_THAT(sut.getChunkHeader(), Eq(nullptr));
}

TEST_F(SharedChunk_Test, VerifyCopyConstructorOfSharedChunk)
{
    SharedChunk sut1(sut);

    EXPECT_EQ((sut1.getChunkHeader())->chunkSize(), (sut.getChunkHeader())->chunkSize());
    EXPECT_EQ(sut.release(), sut1.release());
}

TEST_F(SharedChunk_Test, VerifyMoveConstructorOfSharedChunk)
{
    SharedChunk sut1(chunkManagement);
    ChunkHeader* header = sut1.getChunkHeader();

    SharedChunk sut2(std::move(sut1));

    ASSERT_EQ(sut1.getChunkHeader(), nullptr);
    ASSERT_EQ(sut2.getChunkHeader(), header);
    EXPECT_EQ((sut2.getChunkHeader())->chunkSize(), (sizeof(ChunkHeader) + USER_PAYLOAD_SIZE));
}

TEST_F(SharedChunk_Test, VerifiyCopyAssigmentWithSharedChunk)
{
    SharedChunk sut1;

    sut1 = sut;

    EXPECT_EQ((sut1.getChunkHeader())->chunkSize(), (sut.getChunkHeader())->chunkSize());
    EXPECT_EQ(sut.release(), sut1.release());
}

TEST_F(SharedChunk_Test, VerifiyMoveAssigmentForSharedChunk)
{
    SharedChunk sut1(chunkManagement);
    SharedChunk sut2;
    ChunkHeader* header = sut1.getChunkHeader();

    sut2 = std::move(sut1);

    ASSERT_EQ(sut1.getChunkHeader(), nullptr);
    ASSERT_EQ(sut2.getChunkHeader(), header);
    EXPECT_EQ((sut2.getChunkHeader())->chunkSize(), (sizeof(ChunkHeader) + USER_PAYLOAD_SIZE));
}

TEST_F(SharedChunk_Test, CompareWithSameMemoryChunkComparesToUserPayload)
{
    EXPECT_THAT(sut == sut.getUserPayload(), Eq(true));
}

TEST_F(SharedChunk_Test, GetChunkHeaderMethodReturnsNullPointerWhenSharedChunkObjectIsInitialisedWithNullPointer)
{
    SharedChunk sut;

    EXPECT_THAT(sut.getChunkHeader(), Eq(nullptr));
}

TEST_F(SharedChunk_Test, GetChunkHeaderMethodReturnsValidPointerWhenSharedChunkObjectIsInitialisedWithAValidPointer)
{
    void* newChunk = mempool.getChunk();
    SharedChunk sut(GetChunkManagement(newChunk));

    EXPECT_THAT(sut.getChunkHeader(), Eq(newChunk));
}

TEST_F(SharedChunk_Test, EqualityOperatorOnTwoSharedChunkWithTheSameContentReturnsTrue)
{
    SharedChunk sut1{chunkManagement};

    EXPECT_TRUE(sut == sut1);
}

TEST_F(SharedChunk_Test, EqualityOperatorOnTwoSharedChunkWithDifferentContentReturnsFalse)
{
    SharedChunk sut1;

    EXPECT_FALSE(sut == sut1);
}

TEST_F(SharedChunk_Test, EqualityOperatorOnSharedChunkAndSharedChunkPayloadWithDifferentChunkManagementsReturnFalse)
{
    SharedChunk sut1;

    EXPECT_FALSE(sut1 == sut.getUserPayload());
}

TEST_F(SharedChunk_Test, EqualityOperatorOnSharedChunkAndSharedChunkPayloadWithSameChunkManagementsReturnTrue)
{
    SharedChunk sut1{chunkManagement};

    EXPECT_TRUE(sut == sut1.getUserPayload());
}

TEST_F(SharedChunk_Test, BoolOperatorOnValidSharedChunkReturnsTrue)
{
    EXPECT_TRUE(sut);
}

TEST_F(SharedChunk_Test, BoolOperatorOnSharedChunkWithChunkManagementAsNullPointerReturnsFalse)
{
    SharedChunk sut;

    EXPECT_FALSE(sut);
}

TEST_F(SharedChunk_Test, GetUserPayloadMethodReturnsNullPointerWhen_m_chunkmanagmentIsInvalid)
{
    SharedChunk sut1;

    EXPECT_THAT(sut1.getUserPayload(), Eq(nullptr));
}

TEST_F(SharedChunk_Test, GetUserPayloadMethodReturnsValidPointerWhen_m_chunkmanagmentIsValid)
{
    using DATA_TYPE = uint32_t;
    constexpr DATA_TYPE USER_DATA{7337U};
    ChunkHeader* newChunk = static_cast<ChunkHeader*>(mempool.getChunk());

    auto chunkSettingsResult = ChunkSettings::create(sizeof(DATA_TYPE), alignof(DATA_TYPE));
    ASSERT_FALSE(chunkSettingsResult.has_error());
    auto& chunkSettings = chunkSettingsResult.value();

    new (newChunk) ChunkHeader(mempool.getChunkSize(), chunkSettings);
    new (static_cast<DATA_TYPE*>(newChunk->userPayload())) DATA_TYPE{USER_DATA};

    iox::mepoo::SharedChunk sut1(GetChunkManagement(newChunk));
    EXPECT_THAT(*static_cast<DATA_TYPE*>(sut1.getUserPayload()), Eq(USER_DATA));
}

TEST_F(SharedChunk_Test, MultipleSharedChunksCleanup)
{
    {
        SharedChunk sut3, sut4, sut5;
        {
            {
                SharedChunk sut6, sut7, sut8;
                {
                    iox::mepoo::SharedChunk sut2(GetChunkManagement(mempool.getChunk()));

                    sut3 = sut2;
                    sut4 = sut2;
                    sut5 = sut3;
                    sut6 = sut5;
                    sut7 = sut4;
                    sut8 = sut2;

                    EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(2U));
                    EXPECT_THAT(mempool.getUsedChunks(), Eq(2U));
                }
                EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(2U));
                EXPECT_THAT(mempool.getUsedChunks(), Eq(2U));
            }
            EXPECT_THAT(mempool.getUsedChunks(), Eq(2U));
            EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(2U));
        }
        EXPECT_THAT(mempool.getUsedChunks(), Eq(2U));
        EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(2U));
    }
    EXPECT_THAT(mempool.getUsedChunks(), Eq(1U));
    EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(1U));
}

TEST_F(SharedChunk_Test, MultipleChunksCleanup)
{
    {
        iox::mepoo::SharedChunk sut2(GetChunkManagement(mempool.getChunk()));
        {
            iox::mepoo::SharedChunk sut2(GetChunkManagement(mempool.getChunk()));
            {
                iox::mepoo::SharedChunk sut2(GetChunkManagement(mempool.getChunk()));
                iox::mepoo::SharedChunk sut4(GetChunkManagement(mempool.getChunk()));
                {
                    iox::mepoo::SharedChunk sut2(GetChunkManagement(mempool.getChunk()));
                    iox::mepoo::SharedChunk sut4(GetChunkManagement(mempool.getChunk()));
                    {
                        iox::mepoo::SharedChunk sut2(GetChunkManagement(mempool.getChunk()));
                        iox::mepoo::SharedChunk sut4(GetChunkManagement(mempool.getChunk()));
                        EXPECT_THAT(mempool.getUsedChunks(), Eq(9U));
                        EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(9U));
                    }
                    EXPECT_THAT(mempool.getUsedChunks(), Eq(7U));
                    EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(7U));
                }
                EXPECT_THAT(mempool.getUsedChunks(), Eq(5U));
                EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(5U));
            }
            EXPECT_THAT(mempool.getUsedChunks(), Eq(3U));
            EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(3U));
        }
        EXPECT_THAT(mempool.getUsedChunks(), Eq(2U));
        EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(2U));
    }
    EXPECT_THAT(mempool.getUsedChunks(), Eq(1U));
    EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(1U));
}

TEST_F(SharedChunk_Test, NonEqualityOperatorOnTwoSharedChunkWithDifferentContentReturnsTrue)
{
    SharedChunk sut1;

    EXPECT_TRUE(sut1 != sut);
}

TEST_F(SharedChunk_Test, NonEqualityOperatorOnTwoSharedChunkWithSameContentReturnsFalse)
{
    SharedChunk sut1{chunkManagement};

    EXPECT_FALSE(sut1 != sut);
}

TEST_F(SharedChunk_Test, NonEqualityOperatorOnSharedChunkAndSharedChunkPayloadWithDifferentChunkManagementsReturnTrue)
{
    SharedChunk sut1;

    EXPECT_TRUE(sut != sut1.getUserPayload());
}

TEST_F(SharedChunk_Test, NonEqualityOperatorOnSharedChunkAndSharedChunkPayloadWithSameChunkManagementsReturnFalse)
{
    SharedChunk sut1{chunkManagement};

    EXPECT_FALSE(sut != sut1.getUserPayload());
}

TEST_F(SharedChunk_Test, ReleaseMethodReturnsChunkManagementPointerOfSharedChunkObjectAndSetsTheChunkHeaderToNull)
{
    ChunkManagement* returnValue = sut.release();

    EXPECT_EQ(returnValue, chunkManagement);
    EXPECT_EQ(sut.getChunkHeader(), nullptr);
}
<commit_msg>iox-#496 Review comments fix: Refactoring the testcases of Move and copy constructors and assignment operators<commit_after>// Copyright (c) 2019, 2021 by  Robert Bosch GmbH. All rights reserved.
// Copyright (c) 2021 by Apex.AI Inc. All rights reserved.
//
// Licensed 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.
//
// SPDX-License-Identifier: Apache-2.0

#include "iceoryx_posh/internal/mepoo/memory_manager.hpp"
#include "iceoryx_posh/internal/mepoo/shared_chunk.hpp"
#include "iceoryx_posh/mepoo/chunk_header.hpp"
#include "iceoryx_utils/internal/posix_wrapper/shared_memory_object/allocator.hpp"
#include "test.hpp"

using namespace ::testing;

using namespace iox::mepoo;

class SharedChunk_Test : public Test
{
  public:
    void SetUp() override
    {
    }
    void TearDown() override
    {
    }

    ChunkManagement* GetChunkManagement(void* memoryChunk)
    {
        ChunkManagement* v = static_cast<ChunkManagement*>(chunkMgmtPool.getChunk());
        auto chunkSettingsResult = ChunkSettings::create(USER_PAYLOAD_SIZE, iox::CHUNK_DEFAULT_USER_PAYLOAD_ALIGNMENT);
        EXPECT_FALSE(chunkSettingsResult.has_error());
        if (chunkSettingsResult.has_error())
        {
            return nullptr;
        }
        auto& chunkSettings = chunkSettingsResult.value();
        ChunkHeader* chunkHeader = new (memoryChunk) ChunkHeader(mempool.getChunkSize(), chunkSettings);

        new (v) ChunkManagement{chunkHeader, &mempool, &chunkMgmtPool};
        return v;
    }

    static constexpr uint32_t CHUNK_SIZE{64U};
    static constexpr uint32_t NUMBER_OF_CHUNKS{10U};
    static constexpr uint32_t USER_PAYLOAD_SIZE{64U};

    char memory[4096U];
    iox::posix::Allocator allocator{memory, 4096U};
    MemPool mempool{sizeof(ChunkHeader) + USER_PAYLOAD_SIZE, 10U, allocator, allocator};
    MemPool chunkMgmtPool{64U, 10U, allocator, allocator};
    void* memoryChunk{mempool.getChunk()};
    ChunkManagement* chunkManagement = GetChunkManagement(memoryChunk);
    SharedChunk sut{chunkManagement};
};

TEST_F(SharedChunk_Test, SharedChunkObjectUpOnInitilizationSetsTheChunkHeaderToNullPointer)
{
    SharedChunk sut;

    EXPECT_THAT(sut.getChunkHeader(), Eq(nullptr));
}

TEST_F(SharedChunk_Test, VerifyCopyConstructorOfSharedChunk)
{
    SharedChunk sut1(sut);

    EXPECT_EQ(sut.release(), sut1.release());
}

TEST_F(SharedChunk_Test, VerifyMoveConstructorOfSharedChunk)
{
    SharedChunk sut1(chunkManagement);

    SharedChunk sut2(std::move(sut1));

    EXPECT_THAT(sut1, Eq(false));
    EXPECT_THAT(sut2.release(), Eq(chunkManagement));
}

TEST_F(SharedChunk_Test, VerifiyCopyAssigmentWithSharedChunk)
{
    SharedChunk sut1;

    sut1 = sut;

    EXPECT_EQ(sut.release(), sut1.release());
}

TEST_F(SharedChunk_Test, VerifiyMoveAssigmentForSharedChunk)
{
    SharedChunk sut1(chunkManagement);
    SharedChunk sut2;

    sut2 = std::move(sut1);

    EXPECT_THAT(sut1, Eq(false));
    EXPECT_THAT(sut2.release(), Eq(chunkManagement));
}

TEST_F(SharedChunk_Test, CompareWithSameMemoryChunkComparesToUserPayload)
{
    EXPECT_THAT(sut == sut.getUserPayload(), Eq(true));
}

TEST_F(SharedChunk_Test, GetChunkHeaderMethodReturnsNullPointerWhenSharedChunkObjectIsInitialisedWithNullPointer)
{
    SharedChunk sut;

    EXPECT_THAT(sut.getChunkHeader(), Eq(nullptr));
}

TEST_F(SharedChunk_Test, GetChunkHeaderMethodReturnsValidPointerWhenSharedChunkObjectIsInitialisedWithAValidPointer)
{
    void* newChunk = mempool.getChunk();
    SharedChunk sut(GetChunkManagement(newChunk));

    EXPECT_THAT(sut.getChunkHeader(), Eq(newChunk));
}

TEST_F(SharedChunk_Test, EqualityOperatorOnTwoSharedChunkWithTheSameContentReturnsTrue)
{
    SharedChunk sut1{chunkManagement};

    EXPECT_TRUE(sut == sut1);
}

TEST_F(SharedChunk_Test, EqualityOperatorOnTwoSharedChunkWithDifferentContentReturnsFalse)
{
    SharedChunk sut1;

    EXPECT_FALSE(sut == sut1);
}

TEST_F(SharedChunk_Test, EqualityOperatorOnSharedChunkAndSharedChunkPayloadWithDifferentChunkManagementsReturnFalse)
{
    SharedChunk sut1;

    EXPECT_FALSE(sut1 == sut.getUserPayload());
}

TEST_F(SharedChunk_Test, EqualityOperatorOnSharedChunkAndSharedChunkPayloadWithSameChunkManagementsReturnTrue)
{
    SharedChunk sut1{chunkManagement};

    EXPECT_TRUE(sut == sut1.getUserPayload());
}

TEST_F(SharedChunk_Test, BoolOperatorOnValidSharedChunkReturnsTrue)
{
    EXPECT_TRUE(sut);
}

TEST_F(SharedChunk_Test, BoolOperatorOnSharedChunkWithChunkManagementAsNullPointerReturnsFalse)
{
    SharedChunk sut;

    EXPECT_FALSE(sut);
}

TEST_F(SharedChunk_Test, GetUserPayloadMethodReturnsNullPointerWhen_m_chunkmanagmentIsInvalid)
{
    SharedChunk sut1;

    EXPECT_THAT(sut1.getUserPayload(), Eq(nullptr));
}

TEST_F(SharedChunk_Test, GetUserPayloadMethodReturnsValidPointerWhen_m_chunkmanagmentIsValid)
{
    using DATA_TYPE = uint32_t;
    constexpr DATA_TYPE USER_DATA{7337U};
    ChunkHeader* newChunk = static_cast<ChunkHeader*>(mempool.getChunk());

    auto chunkSettingsResult = ChunkSettings::create(sizeof(DATA_TYPE), alignof(DATA_TYPE));
    ASSERT_FALSE(chunkSettingsResult.has_error());
    auto& chunkSettings = chunkSettingsResult.value();

    new (newChunk) ChunkHeader(mempool.getChunkSize(), chunkSettings);
    new (static_cast<DATA_TYPE*>(newChunk->userPayload())) DATA_TYPE{USER_DATA};

    iox::mepoo::SharedChunk sut1(GetChunkManagement(newChunk));
    EXPECT_THAT(*static_cast<DATA_TYPE*>(sut1.getUserPayload()), Eq(USER_DATA));
}

TEST_F(SharedChunk_Test, MultipleSharedChunksCleanup)
{
    {
        SharedChunk sut3, sut4, sut5;
        {
            {
                SharedChunk sut6, sut7, sut8;
                {
                    iox::mepoo::SharedChunk sut2(GetChunkManagement(mempool.getChunk()));

                    sut3 = sut2;
                    sut4 = sut2;
                    sut5 = sut3;
                    sut6 = sut5;
                    sut7 = sut4;
                    sut8 = sut2;

                    EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(2U));
                    EXPECT_THAT(mempool.getUsedChunks(), Eq(2U));
                }
                EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(2U));
                EXPECT_THAT(mempool.getUsedChunks(), Eq(2U));
            }
            EXPECT_THAT(mempool.getUsedChunks(), Eq(2U));
            EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(2U));
        }
        EXPECT_THAT(mempool.getUsedChunks(), Eq(2U));
        EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(2U));
    }
    EXPECT_THAT(mempool.getUsedChunks(), Eq(1U));
    EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(1U));
}

TEST_F(SharedChunk_Test, MultipleChunksCleanup)
{
    {
        iox::mepoo::SharedChunk sut2(GetChunkManagement(mempool.getChunk()));
        {
            iox::mepoo::SharedChunk sut2(GetChunkManagement(mempool.getChunk()));
            {
                iox::mepoo::SharedChunk sut2(GetChunkManagement(mempool.getChunk()));
                iox::mepoo::SharedChunk sut4(GetChunkManagement(mempool.getChunk()));
                {
                    iox::mepoo::SharedChunk sut2(GetChunkManagement(mempool.getChunk()));
                    iox::mepoo::SharedChunk sut4(GetChunkManagement(mempool.getChunk()));
                    {
                        iox::mepoo::SharedChunk sut2(GetChunkManagement(mempool.getChunk()));
                        iox::mepoo::SharedChunk sut4(GetChunkManagement(mempool.getChunk()));
                        EXPECT_THAT(mempool.getUsedChunks(), Eq(9U));
                        EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(9U));
                    }
                    EXPECT_THAT(mempool.getUsedChunks(), Eq(7U));
                    EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(7U));
                }
                EXPECT_THAT(mempool.getUsedChunks(), Eq(5U));
                EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(5U));
            }
            EXPECT_THAT(mempool.getUsedChunks(), Eq(3U));
            EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(3U));
        }
        EXPECT_THAT(mempool.getUsedChunks(), Eq(2U));
        EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(2U));
    }
    EXPECT_THAT(mempool.getUsedChunks(), Eq(1U));
    EXPECT_THAT(chunkMgmtPool.getUsedChunks(), Eq(1U));
}

TEST_F(SharedChunk_Test, NonEqualityOperatorOnTwoSharedChunkWithDifferentContentReturnsTrue)
{
    SharedChunk sut1;

    EXPECT_TRUE(sut1 != sut);
}

TEST_F(SharedChunk_Test, NonEqualityOperatorOnTwoSharedChunkWithSameContentReturnsFalse)
{
    SharedChunk sut1{chunkManagement};

    EXPECT_FALSE(sut1 != sut);
}

TEST_F(SharedChunk_Test, NonEqualityOperatorOnSharedChunkAndSharedChunkPayloadWithDifferentChunkManagementsReturnTrue)
{
    SharedChunk sut1;

    EXPECT_TRUE(sut != sut1.getUserPayload());
}

TEST_F(SharedChunk_Test, NonEqualityOperatorOnSharedChunkAndSharedChunkPayloadWithSameChunkManagementsReturnFalse)
{
    SharedChunk sut1{chunkManagement};

    EXPECT_FALSE(sut != sut1.getUserPayload());
}

TEST_F(SharedChunk_Test, ReleaseMethodReturnsChunkManagementPointerOfSharedChunkObjectAndSetsTheChunkHeaderToNull)
{
    ChunkManagement* returnValue = sut.release();

    EXPECT_EQ(returnValue, chunkManagement);
    EXPECT_EQ(sut.getChunkHeader(), nullptr);
}
<|endoftext|>


<commit_before>/*
  Q Light Controller
  olaoutthread.cpp

  Copyright (c) Simon Newton

  Licensed 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.txt

  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.
*/

#include <QDebug>
#include <ola/Callback.h>
#include "olaoutthread.h"

OlaOutThread::OlaOutThread()
    : QThread()
    , m_init_run(false)
    , m_ss(NULL)
    , m_pipe(NULL)
    , m_client(NULL)
{
}

/*
 * Clean up.
 */
OlaOutThread::~OlaOutThread()
{
    wait();
    if (m_client)
    {
        m_client->Stop();
        delete m_client;
    }

    if (m_pipe)
        delete m_pipe;

    cleanup();
}

/*
 * Start the OLA thread
 *
 * @return true if sucessfull, false otherwise
 */
bool OlaOutThread::start(Priority priority)
{
    if (!init())
        return false;

    if (!m_pipe)
    {
        // setup the pipe to recv dmx data on
        m_pipe = new ola::io::LoopbackDescriptor();
        m_pipe->Init();

        m_pipe->SetOnData(ola::NewCallback(this, &OlaOutThread::new_pipe_data));
        m_pipe->SetOnClose(ola::NewSingleCallback(this, &OlaOutThread::pipe_closed));
        m_ss->AddReadDescriptor(m_pipe);
    }

    QThread::start(priority);
    return true;
}

/*
 * Close the socket which stops the thread.
 */
void OlaOutThread::stop()
{
    if (m_pipe)
        m_pipe->CloseClient();
    return;
}

/*
 * Run the select server.
 */
void OlaOutThread::run()
{
    m_ss->Run();
    return;
}

/*
 * Send the new data over the socket so that the other thread picks it up.
 * @param universe the universe nmuber this data is for
 * @param data a pointer to the data
 * @param channels the number of channels
 */
int OlaOutThread::write_dmx(unsigned int universe, const QByteArray& data)
{
    m_data.universe = universe;
    memcpy(m_data.data, data.data(), data.size());
    if (m_pipe)
        m_pipe->Send((uint8_t*) &m_data, sizeof(m_data));
    return 0;
}

/*
 * Called when the pipe used to communicate between QLC and OLA is closed
 */
void OlaOutThread::pipe_closed()
{
    // We don't need to delete the socket here because that gets done in the
    // Destructor.
    m_ss->Terminate();
}

/*
 * Called when there is data to be read on the pipe socket.
 */
void OlaOutThread::new_pipe_data()
{
    dmx_data data;
    unsigned int data_read;
    int ret = m_pipe->Receive((uint8_t*) &data, sizeof(data), data_read);
    if (ret < 0)
    {
        qCritical() << "olaout: socket receive failed";
        return;
    }

    m_buffer.Set(data.data, data_read - sizeof(data.universe));
    if (!m_client->SendDmx(data.universe, m_buffer))
        qWarning() << "olaout:: SendDmx() failed";
}

/*
 * Setup the OlaCallbackClient to communicate with the server.
 * @return true if the setup worked corectly.
 */
bool OlaOutThread::setup_client(ola::io::ConnectedDescriptor *descriptor)
{
    if (!m_client)
    {
        m_client = new ola::OlaCallbackClient(descriptor);
        if (!m_client->Setup())
        {
            qWarning() << "olaout: client setup failed";
            delete m_client;
            m_client = NULL;
            return false;
        }
        m_ss->AddReadDescriptor(descriptor);
    }
    return true;
}

/*
 * Cleanup after the main destructor has run
 */
void OlaStandaloneClient::cleanup()
{
    if (m_tcp_socket)
    {
        if (m_ss)
            m_ss->RemoveReadDescriptor(m_tcp_socket);
        delete m_tcp_socket;
        m_tcp_socket = NULL;
    }

    if (m_ss)
        delete m_ss;
}

/*
 * Setup the standalone client.
 * @return true is successful.
 */
bool OlaStandaloneClient::init()
{
    if (m_init_run)
        return true;

    if (!m_ss)
        m_ss = new ola::io::SelectServer();

    if (!m_tcp_socket)
    {
        ola::network::IPV4SocketAddress server_address(
            ola::network::IPV4Address::Loopback(), ola::OLA_DEFAULT_PORT);
        m_tcp_socket = ola::network::TCPSocket::Connect(server_address);
        if (!m_tcp_socket)
        {
            qWarning() << "olaout: Connect failed, is OLAD running?";
            delete m_tcp_socket;
            m_tcp_socket = NULL;
            delete m_ss;
            m_ss = NULL;
            return false;
        }
    }

    if (!setup_client(m_tcp_socket))
    {
        m_tcp_socket->Close();
        delete m_tcp_socket;
        m_tcp_socket = NULL;
        delete m_ss;
        m_ss = NULL;
        return false;
    }
    m_init_run = true;
    return true;
}

/*
 * Clean up the embedded server.
 */
void OlaEmbeddedServer::cleanup()
{
    if (m_daemon)
        delete m_daemon;

    if (m_pipe_socket)
        delete m_pipe_socket;
}

/*
 * Setup the embedded server.
 * @return true is successful.
 */
bool OlaEmbeddedServer::init()
{
    if (m_init_run)
        return true;

    ola::OlaServer::Options options;
    options.http_enable = true;
    options.http_port = ola::OlaServer::DEFAULT_HTTP_PORT;
    m_daemon = new ola::OlaDaemon(options);
    if (!m_daemon->Init())
    {
        qWarning() << "OLA Server failed init";
        delete m_daemon;
        m_daemon = NULL;
        return false;
    }
    m_ss = m_daemon->GetSelectServer();

    // setup the pipe socket used to communicate with the OlaServer
    if (!m_pipe_socket)
    {
        m_pipe_socket = new ola::io::PipeDescriptor();
        if (!m_pipe_socket->Init())
        {
            qWarning() << "olaout: pipe failed";
            delete m_pipe_socket;
            m_pipe_socket = NULL;
            delete m_daemon;
            m_daemon = NULL;
            return false;
        }
    }

    if (!setup_client(m_pipe_socket))
    {
        delete m_pipe_socket;
        m_pipe_socket = NULL;
        delete m_daemon;
        m_daemon = NULL;
        return false;
    }

    m_daemon->GetOlaServer()->NewConnection(m_pipe_socket->OppositeEnd());
    m_init_run = true;
    return true;
}
<commit_msg>OLA:  Fill extra output buffer with zeros<commit_after>/*
  Q Light Controller
  olaoutthread.cpp

  Copyright (c) Simon Newton

  Licensed 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.txt

  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.
*/

#include <QDebug>
#include <ola/Callback.h>
#include "olaoutthread.h"

OlaOutThread::OlaOutThread()
    : QThread()
    , m_init_run(false)
    , m_ss(NULL)
    , m_pipe(NULL)
    , m_client(NULL)
{
}

/*
 * Clean up.
 */
OlaOutThread::~OlaOutThread()
{
    wait();
    if (m_client)
    {
        m_client->Stop();
        delete m_client;
    }

    if (m_pipe)
        delete m_pipe;

    cleanup();
}

/*
 * Start the OLA thread
 *
 * @return true if sucessfull, false otherwise
 */
bool OlaOutThread::start(Priority priority)
{
    if (!init())
        return false;

    if (!m_pipe)
    {
        // setup the pipe to recv dmx data on
        m_pipe = new ola::io::LoopbackDescriptor();
        m_pipe->Init();

        m_pipe->SetOnData(ola::NewCallback(this, &OlaOutThread::new_pipe_data));
        m_pipe->SetOnClose(ola::NewSingleCallback(this, &OlaOutThread::pipe_closed));
        m_ss->AddReadDescriptor(m_pipe);
    }

    QThread::start(priority);
    return true;
}

/*
 * Close the socket which stops the thread.
 */
void OlaOutThread::stop()
{
    if (m_pipe)
        m_pipe->CloseClient();
    return;
}

/*
 * Run the select server.
 */
void OlaOutThread::run()
{
    m_ss->Run();
    return;
}

/*
 * Send the new data over the socket so that the other thread picks it up.
 * @param universe the universe nmuber this data is for
 * @param data a pointer to the data
 * @param channels the number of channels
 */
int OlaOutThread::write_dmx(unsigned int universe, const QByteArray& data)
{
    if (m_pipe)
    {
        Q_ASSERT(data.size() <= (int)sizeof(m_data.data));

        m_data.universe = universe;
        memset(m_data.data, 0, sizeof(m_data.data));
        memcpy(m_data.data, data.data(), data.size());

        m_pipe->Send((uint8_t*) &m_data, sizeof(m_data));
    }
    return 0;
}

/*
 * Called when the pipe used to communicate between QLC and OLA is closed
 */
void OlaOutThread::pipe_closed()
{
    // We don't need to delete the socket here because that gets done in the
    // Destructor.
    m_ss->Terminate();
}

/*
 * Called when there is data to be read on the pipe socket.
 */
void OlaOutThread::new_pipe_data()
{
    dmx_data data;
    unsigned int data_read;
    int ret = m_pipe->Receive((uint8_t*) &data, sizeof(data), data_read);
    if (ret < 0)
    {
        qCritical() << "olaout: socket receive failed";
        return;
    }

    m_buffer.Set(data.data, data_read - sizeof(data.universe));
    if (!m_client->SendDmx(data.universe, m_buffer))
        qWarning() << "olaout:: SendDmx() failed";
}

/*
 * Setup the OlaCallbackClient to communicate with the server.
 * @return true if the setup worked corectly.
 */
bool OlaOutThread::setup_client(ola::io::ConnectedDescriptor *descriptor)
{
    if (!m_client)
    {
        m_client = new ola::OlaCallbackClient(descriptor);
        if (!m_client->Setup())
        {
            qWarning() << "olaout: client setup failed";
            delete m_client;
            m_client = NULL;
            return false;
        }
        m_ss->AddReadDescriptor(descriptor);
    }
    return true;
}

/*
 * Cleanup after the main destructor has run
 */
void OlaStandaloneClient::cleanup()
{
    if (m_tcp_socket)
    {
        if (m_ss)
            m_ss->RemoveReadDescriptor(m_tcp_socket);
        delete m_tcp_socket;
        m_tcp_socket = NULL;
    }

    if (m_ss)
        delete m_ss;
}

/*
 * Setup the standalone client.
 * @return true is successful.
 */
bool OlaStandaloneClient::init()
{
    if (m_init_run)
        return true;

    if (!m_ss)
        m_ss = new ola::io::SelectServer();

    if (!m_tcp_socket)
    {
        ola::network::IPV4SocketAddress server_address(
            ola::network::IPV4Address::Loopback(), ola::OLA_DEFAULT_PORT);
        m_tcp_socket = ola::network::TCPSocket::Connect(server_address);
        if (!m_tcp_socket)
        {
            qWarning() << "olaout: Connect failed, is OLAD running?";
            delete m_tcp_socket;
            m_tcp_socket = NULL;
            delete m_ss;
            m_ss = NULL;
            return false;
        }
    }

    if (!setup_client(m_tcp_socket))
    {
        m_tcp_socket->Close();
        delete m_tcp_socket;
        m_tcp_socket = NULL;
        delete m_ss;
        m_ss = NULL;
        return false;
    }
    m_init_run = true;
    return true;
}

/*
 * Clean up the embedded server.
 */
void OlaEmbeddedServer::cleanup()
{
    if (m_daemon)
        delete m_daemon;

    if (m_pipe_socket)
        delete m_pipe_socket;
}

/*
 * Setup the embedded server.
 * @return true is successful.
 */
bool OlaEmbeddedServer::init()
{
    if (m_init_run)
        return true;

    ola::OlaServer::Options options;
    options.http_enable = true;
    options.http_port = ola::OlaServer::DEFAULT_HTTP_PORT;
    m_daemon = new ola::OlaDaemon(options);
    if (!m_daemon->Init())
    {
        qWarning() << "OLA Server failed init";
        delete m_daemon;
        m_daemon = NULL;
        return false;
    }
    m_ss = m_daemon->GetSelectServer();

    // setup the pipe socket used to communicate with the OlaServer
    if (!m_pipe_socket)
    {
        m_pipe_socket = new ola::io::PipeDescriptor();
        if (!m_pipe_socket->Init())
        {
            qWarning() << "olaout: pipe failed";
            delete m_pipe_socket;
            m_pipe_socket = NULL;
            delete m_daemon;
            m_daemon = NULL;
            return false;
        }
    }

    if (!setup_client(m_pipe_socket))
    {
        delete m_pipe_socket;
        m_pipe_socket = NULL;
        delete m_daemon;
        m_daemon = NULL;
        return false;
    }

    m_daemon->GetOlaServer()->NewConnection(m_pipe_socket->OppositeEnd());
    m_init_run = true;
    return true;
}
<|endoftext|>