<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 Context {
    bool half = false;
    bool got_data;
    bool eof = false;
#ifdef EXPECTED_RESULT
    bool record = false;
    char buffer[sizeof(EXPECTED_RESULT) * 2];
    size_t buffer_length = 0;
#endif
    struct istream *abort_istream = nullptr;
    int abort_after = 0;

    int block_after = -1;

    bool block_byte = false, block_byte_state = false;
};

/*
 * istream handler
 *
 */

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

    (void)data;

    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 != nullptr && 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 = nullptr;
        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)
{
    auto &ctx = *(Context *)_ctx;

    (void)fd;

    ctx.got_data = true;

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

    return max_length;
}

static void
my_istream_eof(void *_ctx)
{
    auto &ctx = *(Context *)_ctx;

    ctx.eof = true;
}

static void
my_istream_abort(GError *error, void *_ctx)
{
    auto &ctx = *(Context *)_ctx;

    g_error_free(error);

#ifdef EXPECTED_RESULT
    assert(!ctx.record);
#endif

    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(Context &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(Context &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)
{
    Context ctx;
    ctx.block_after = block_after;
#ifdef EXPECTED_RESULT
    ctx.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 != nullptr);
    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 != nullptr);
        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)));

    Context ctx;
    ctx.block_byte = true;
#ifdef EXPECTED_RESULT
    ctx.record = true;
#endif

    run_istream_ctx(ctx, pool, istream);
}

/** accept only half of the data */
static void
test_half(struct pool *pool)
{
    Context ctx;
    ctx.half = true;
#ifdef EXPECTED_RESULT
    ctx.record = true;
#endif

    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),
                                          nullptr));
    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)
{
    Context ctx;
    ctx.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 == nullptr);

    cleanup();
    pool_commit();
}

/** abort in handler, with some data consumed */
static void
test_abort_in_handler_half(struct pool *pool)
{
    Context ctx;
    ctx.half = true;
    ctx.abort_after = 2;

    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 == nullptr || 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), nullptr);

    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(nullptr, "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();
}
<commit_msg>test/t_istream_filter: make variables more local<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 Context {
    bool half = false;
    bool got_data;
    bool eof = false;
#ifdef EXPECTED_RESULT
    bool record = false;
    char buffer[sizeof(EXPECTED_RESULT) * 2];
    size_t buffer_length = 0;
#endif
    struct istream *abort_istream = nullptr;
    int abort_after = 0;

    int block_after = -1;

    bool block_byte = false, block_byte_state = false;
};

/*
 * istream handler
 *
 */

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

    (void)data;

    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 != nullptr && 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 = nullptr;
        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)
{
    auto &ctx = *(Context *)_ctx;

    (void)fd;

    ctx.got_data = true;

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

    return max_length;
}

static void
my_istream_eof(void *_ctx)
{
    auto &ctx = *(Context *)_ctx;

    ctx.eof = true;
}

static void
my_istream_abort(GError *error, void *_ctx)
{
    auto &ctx = *(Context *)_ctx;

    g_error_free(error);

#ifdef EXPECTED_RESULT
    assert(!ctx.record);
#endif

    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(Context &ctx, struct istream *istream)
{
    assert(!ctx.eof);

    ctx.got_data = false;

    const auto 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(Context &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)
{
    Context ctx;
    ctx.block_after = block_after;
#ifdef EXPECTED_RESULT
    ctx.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)
{
    pool = pool_new_linear(pool, "test_normal", 8192);

    auto *istream = create_test(pool, create_input(pool));
    assert(istream != nullptr);
    assert(!istream_has_handler(istream));

    run_istream(pool, istream, true);
}

/** block once after n data() invocations */
static void
test_block(struct pool *parent_pool)
{
    for (int n = 0; n < 8; ++n) {
        struct istream *istream;

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

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

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

/** test with istream_byte */
static void
test_byte(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_byte", 8192);

    auto *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)
{
    pool = pool_new_linear(pool, "test_byte", 8192);

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

    Context ctx;
    ctx.block_byte = true;
#ifdef EXPECTED_RESULT
    ctx.record = true;
#endif

    run_istream_ctx(ctx, pool, istream);
}

/** accept only half of the data */
static void
test_half(struct pool *pool)
{
    Context ctx;
    ctx.half = true;
#ifdef EXPECTED_RESULT
    ctx.record = true;
#endif

    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)
{
    pool = pool_new_linear(pool, "test_fail", 8192);

    GError *error = g_error_new_literal(test_quark(), 0, "test_fail");
    auto *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)
{
    pool = pool_new_linear(pool, "test_fail_1byte", 8192);

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

/** abort without handler */
static void
test_abort_without_handler(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_abort_without_handler", 8192);

    auto *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)
{
    Context ctx;
    ctx.block_after = -1;

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

    ctx.abort_istream = istream_inject_new(pool, create_input(pool));
    auto *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 == nullptr);

    cleanup();
    pool_commit();
}

/** abort in handler, with some data consumed */
static void
test_abort_in_handler_half(struct pool *pool)
{
    Context ctx;
    ctx.half = true;
    ctx.abort_after = 2;

    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)));
    auto *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 == nullptr || ctx.abort_after >= 0);

    cleanup();
    pool_commit();
}

#endif

/** abort after 1 byte of output */
static void
test_abort_1byte(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_abort_1byte", 8192);

    auto *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)
{
    pool = pool_new_linear(pool, "test_later", 8192);

    auto *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), nullptr);

    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) {
    (void)argc;
    (void)argv;

    direct_global_init();
    auto *const event_base = event_init();

    auto *const root_pool = pool_new_libc(nullptr, "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>// Wheels - various C++ utilities
//
// Written in 2013 by Martinho Fernandes <martinho.fernandes@gmail.com>
//
// To the extent possible under law, the author(s) have dedicated all copyright and related
// and neighboring rights to this software to the public domain worldwide. This software is
// distributed without any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>

// INVOKE tests

#include <wheels/fun/invoke.h++>

#include <catch.h++>

TEST_CASE("invoke", "INVOKE tests") {
    struct local { int i; } l;
    wheels::fun::invoke<void>(&local::i, local{0});
    CHECK(wheels::fun::invoke<double>(&local::i, local{0}) == 0.0);
    CHECK(&wheels::fun::invoke(&local::i, l) == &l.i);
}

<commit_msg>Add tests for invoke<commit_after>// Wheels - various C++ utilities
//
// Written in 2013 by Martinho Fernandes <martinho.fernandes@gmail.com>
//
// To the extent possible under law, the author(s) have dedicated all copyright and related
// and neighboring rights to this software to the public domain worldwide. This software is
// distributed without any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>

// INVOKE tests

#include <wheels/fun/invoke.h++>

#include <catch.h++>

namespace test1 {
    const int expected_result = 42;
    const double expected_arg1 = 17.23;
    const int expected_arg2 = true;
    int called{};
    double arg1{};
    bool arg2{};
    int call(double a1, bool a2) {
        ++called;
        arg1 = a1;
        arg2 = a2;
        return expected_result;
    }
} // namespace fun1
TEST_CASE("invoke", "INVOKE tests") {
    SECTION("function", "invoking functions") {
        test1::called = 0;
        auto result = wheels::fun::invoke(test1::call, test1::expected_arg1, test1::expected_arg2);

        CHECK((std::is_same<decltype(wheels::fun::invoke(test1::call, 0, 0)), int>::value));
        CHECK(test1::called == 1);
        CHECK(result == test1::expected_result);
        CHECK(test1::arg1 == test1::expected_arg1);
        CHECK(test1::arg2 == test1::expected_arg2);
    }
    SECTION("funobj", "invoking function objects") {
        struct local {
            double a1;
            int operator()(bool a2) {
                return test1::call(a1, a2);
            }
        };
        local l { test1::expected_arg1 };
        test1::called = 0;
        auto result = wheels::fun::invoke(l, test1::expected_arg2);

        CHECK((std::is_same<decltype(wheels::fun::invoke(l, 0)), int>::value));
        CHECK(test1::called == 1);
        CHECK(result == test1::expected_result);
        CHECK(test1::arg1 == test1::expected_arg1);
        CHECK(test1::arg2 == test1::expected_arg2);
    }
    SECTION("ptmo", "invoking pointer to member object") {
        struct local {
            int r;
        };
        {
            local l { test1::expected_result };
            auto&& result = wheels::fun::invoke(&local::r, l);

            CHECK((std::is_same<decltype(result), int&>::value));
            CHECK(result == test1::expected_result);
        }
        {
            auto&& result = wheels::fun::invoke(&local::r, local{ test1::expected_result });

            CHECK((std::is_same<decltype(result), int&&>::value));
        }
    }
    SECTION("ptmf", "invoking pointer to member function") {
        struct local {
            double a1;
            int f(bool a2) {
                return test1::call(a1, a2);
            }
        };
        local l { test1::expected_arg1 };
        test1::called = 0;
        auto result = wheels::fun::invoke(&local::f, l, test1::expected_arg2);

        CHECK((std::is_same<decltype(result), int>::value));
        CHECK(test1::called == 1);
        CHECK(result == test1::expected_result);
        CHECK(test1::arg1 == test1::expected_arg1);
        CHECK(test1::arg2 == test1::expected_arg2);
    }
    SECTION("result", "invoking with forced result type") {
        auto result = wheels::fun::invoke<unsigned>(test1::call, test1::expected_arg1, test1::expected_arg2);
        CHECK((std::is_same<decltype(result), unsigned>::value));
        CHECK(result == unsigned(test1::expected_result));
    }
    SECTION("void", "invoking discarding result") {
        CHECK((std::is_same<decltype(wheels::fun::invoke<void>(test1::call, 0, 0)), void>::value));
    }
}

<|endoftext|>


<commit_before>#include "gtest/gtest.h"

#include "cnglob.h"
#include "strpak.h"

TEST(strpak, compare_case_insensitive_string)
{
	// Set up strings to compare
	const char* hello_upper_case = "HELLO";
	const char* hello_lower_case = "hello";
	const char* hello_wrong = "H3llo";
	const char* long_string_upper_case = "This IS a LONG string.";
	const char* long_string_lower_case = "this is a long string ";
	const char* long_string_wrong_case = "this is a wrong string.";

	// Test _stricmp
	EXPECT_EQ( _stricmp(hello_upper_case, hello_lower_case), 0);
	EXPECT_GT( _stricmp(hello_upper_case, hello_wrong), 0);
	EXPECT_GT( _stricmp(long_string_upper_case, long_string_lower_case), 0);
	EXPECT_LT( _stricmp(long_string_upper_case, long_string_wrong_case), 0);

	// Test _strnicmp
	EXPECT_EQ( _strnicmp(hello_upper_case, hello_lower_case, 5), 0);
	EXPECT_GT( _strnicmp(hello_upper_case, hello_wrong, 4), 0);
	EXPECT_EQ( _strnicmp(hello_upper_case, hello_wrong, 1), 0);
	EXPECT_GT( _strnicmp(long_string_upper_case, long_string_lower_case, 22), 0);
	EXPECT_EQ( _strnicmp(long_string_upper_case, long_string_lower_case, 20), 0);
	EXPECT_LT( _strnicmp(long_string_upper_case, long_string_wrong_case, 22), 0);
	EXPECT_EQ( _strnicmp(long_string_upper_case, long_string_wrong_case, 10), 0);
}

TEST(strpak, convert_case_functions)
{
	// Set up strings
	char uppercaseString[] = "this should be uppercase.";
	char lowercaseString[] = "THIS SHOULD BE LOWERCASE.";
	char randomCaseString[] = "Hello There ARE maNy cases HERE!";
	char numberString[] = "Testing numbers ASHRAE205 (#).";

	// Test convert uppercase strings
	EXPECT_STREQ( _strupr(uppercaseString), "THIS SHOULD BE UPPERCASE.");
	EXPECT_STREQ( _strupr(randomCaseString), "HELLO THERE ARE MANY CASES HERE!");
	EXPECT_STREQ( _strupr(numberString), "TESTING NUMBERS ASHRAE205 (#)");

	// Test convert lowercase strings
	strcpy(randomCaseString, "Hello There ARE maNy cases HERE!");
	strcpy(numberString, "There are 12311 numbers (#).");
	EXPECT_STREQ( _strlwr(lowercaseString), "this should be lowercase.");
	EXPECT_STREQ( _strlwr(randomCaseString), "hello there are many cases here!");
	EXPECT_STREQ( _strlwr(numberString), "testing numbers ashrae205 (#).");
}
<commit_msg>Fix string in unit test.<commit_after>#include "gtest/gtest.h"

#include "cnglob.h"
#include "strpak.h"

TEST(strpak, compare_case_insensitive_string)
{
	// Set up strings to compare
	const char* hello_upper_case = "HELLO";
	const char* hello_lower_case = "hello";
	const char* hello_wrong = "H3llo";
	const char* long_string_upper_case = "This IS a LONG string.";
	const char* long_string_lower_case = "this is a long string ";
	const char* long_string_wrong_case = "this is a wrong string.";

	// Test _stricmp
	EXPECT_EQ( _stricmp(hello_upper_case, hello_lower_case), 0);
	EXPECT_GT( _stricmp(hello_upper_case, hello_wrong), 0);
	EXPECT_GT( _stricmp(long_string_upper_case, long_string_lower_case), 0);
	EXPECT_LT( _stricmp(long_string_upper_case, long_string_wrong_case), 0);

	// Test _strnicmp
	EXPECT_EQ( _strnicmp(hello_upper_case, hello_lower_case, 5), 0);
	EXPECT_GT( _strnicmp(hello_upper_case, hello_wrong, 4), 0);
	EXPECT_EQ( _strnicmp(hello_upper_case, hello_wrong, 1), 0);
	EXPECT_GT( _strnicmp(long_string_upper_case, long_string_lower_case, 22), 0);
	EXPECT_EQ( _strnicmp(long_string_upper_case, long_string_lower_case, 20), 0);
	EXPECT_LT( _strnicmp(long_string_upper_case, long_string_wrong_case, 22), 0);
	EXPECT_EQ( _strnicmp(long_string_upper_case, long_string_wrong_case, 10), 0);
}

TEST(strpak, convert_case_functions)
{
	// Set up strings
	char uppercaseString[] = "this should be uppercase.";
	char lowercaseString[] = "THIS SHOULD BE LOWERCASE.";
	char randomCaseString[] = "Hello There ARE maNy cases HERE!";
	char numberString[] = "Testing numbers ASHRAE205 (#).";

	// Test convert uppercase strings
	EXPECT_STREQ( _strupr(uppercaseString), "THIS SHOULD BE UPPERCASE.");
	EXPECT_STREQ( _strupr(randomCaseString), "HELLO THERE ARE MANY CASES HERE!");
	EXPECT_STREQ( _strupr(numberString), "TESTING NUMBERS ASHRAE205 (#)");

	// Test convert lowercase strings
	strcpy(randomCaseString, "Hello There ARE maNy cases HERE!");
	strcpy(numberString, "Testing numbers ASHRAE205 (#).");
	EXPECT_STREQ( _strlwr(lowercaseString), "this should be lowercase.");
	EXPECT_STREQ( _strlwr(randomCaseString), "hello there are many cases here!");
	EXPECT_STREQ( _strlwr(numberString), "testing numbers ashrae205 (#).");
}
<|endoftext|>


<commit_before>#include <caf/all.hpp>

#include "vast/bitstream.h"
#include "vast/bitmap_index.h"
#include "vast/configuration.h"
#include "vast/event.h"
#include "vast/expression.h"
#include "vast/file_system.h"
#include "vast/program.h"
#include "vast/segment.h"
#include "vast/io/serialization.h"

#include "framework/unit.h"

#include "test/unit/bro_logs.h"

using namespace caf;
using namespace vast;

SUITE("actors")

namespace {

// Because we're running multiple TCP tests sequentially, we need to give the
// OS a bit of time to release the ports from previous test. This function sets
// different ports for different program instances as an alternative to waiting
// for the same ports to become free again.
void set_ports(configuration& config, uint64_t instance)
{
  uint16_t port = 42000 + instance * 5;
  *config["tracker.port"] = port++;
  *config["receiver.port"] = port++;
  *config["archive.port"] = port++;
  *config["index.port"] = port++;
  *config["search.port"] = port;
}

} // namespace <anonymous>

TEST("all-in-one import")
{
  configuration cfg;
  set_ports(cfg, 1);
  *cfg['v'] = 0;
  *cfg['V'] = 5;
  *cfg['C'] = true;
  *cfg['I'] = true;
  *cfg['r'] = m57_day11_18::ftp;
  *cfg['p'] = "m57_day11_18";

  REQUIRE(cfg.verify());

  anon_send(spawn<program>(cfg), atom("run"));
  await_all_actors_done();

  auto dir = path{*cfg.get("directory")};
  auto ftp = dir / "index" / "m57_day11_18" / "types" / "ftp";

  REQUIRE(exists(dir));
  REQUIRE(exists(ftp));

  uint64_t size;
  address_bitmap_index<default_bitstream> abmi;
  port_bitmap_index<default_bitstream> pbmi;

  REQUIRE(vast::io::unarchive(ftp / "id" / "orig_h" / "index", size, abmi));
  REQUIRE(vast::io::unarchive(ftp / "id" / "orig_p" / "index", size, pbmi));

  REQUIRE(size == 3); // Event ID 1 is the first valid ID.
  REQUIRE(size == abmi.size());
  REQUIRE(size == pbmi.size());

  auto eq = relational_operator::equal;
  auto orig_h = abmi.lookup(eq, *to<address>("192.168.1.105"));
  auto orig_p = pbmi.lookup(greater, *to<port>("49320/?"));

  REQUIRE(orig_h);
  CHECK((*orig_h)[1] == 1);
  CHECK((*orig_h)[2] == 1);

  REQUIRE(orig_p);
  CHECK((*orig_p)[1] == 1);
  CHECK((*orig_p)[2] == 0);

  CHECK(rm(dir));
}

TEST("basic actor integrity")
{
  // First spawn the core.
  configuration core_config;
  set_ports(core_config, 2);
  *core_config['v'] = 0;
  *core_config['V'] = 5;
  *core_config['C'] = true;
  *core_config['p'] = "m57-ssl";
  REQUIRE(core_config.verify());

  auto core = spawn<program>(core_config);
  anon_send(core, atom("run"));

  // Wait until the TCP sockets of the core have bound.
  std::this_thread::sleep_for(std::chrono::milliseconds(500));

  // Import a single Bro log.
  configuration import_config;
  set_ports(import_config, 2);
  *import_config['v'] = 0;
  *import_config['V'] = 5;
  *import_config['I'] = true;
  *import_config['r'] = m57_day11_18::ssl;
  *import_config["import.batch-size"] = 10;
  *import_config["archive.max-segment-size"] = 1;
  REQUIRE(import_config.verify());

  // Terminates after import completes.
  auto import = spawn<program>(import_config);

  // Pull down core afterwards.
  import->link_to(core);
  anon_send(import, atom("run"));

  await_all_actors_done();

  // Restart a new core.
  set_ports(core_config, 3);
  *core_config['v'] = 0;
  *core_config['V'] = 5;
  *core_config['C'] = true;
  *core_config['p'] = "m57-conn";
  REQUIRE(core_config.verify());

  core = spawn<program>(core_config);
  anon_send(core, atom("run"));

  scoped_actor self;
  auto fail = others() >> [&]
  {
    std::cerr
      << "unexpected message from " << self->last_sender().id() << ": "
      << to_string(self->last_dequeued()) << std::endl;

    REQUIRE(false);
  };

  // Test whether the archive has the correct segment.
  self->send(core, atom("archive"));
  self->receive([&](actor archive) { self->send(archive, event_id(100)); });
  self->receive(
      on_arg_match >> [&](segment const& s)
      {
        CHECK(s.meta().base == 1);
        CHECK(s.meta().events == 113);

        // Check the last ssl.log entry.
        segment::reader r{s};
        auto e = r.read(113);
        REQUIRE(e);
        CHECK(get<record>(*e)->at(1) == "XBy0ZlNNWuj");
        CHECK(get<record>(*e)->at(3) == "TLSv10");
      },
      fail);

  // Test whether a manual index lookup succeeds.
  auto pops = to<expr::ast>("id.resp_p == 995/?");
  REQUIRE(pops);

  self->send(core, atom("index"));
  self->receive(
      [&](actor index) { self->send(index, atom("query"), *pops, self); });

  self->receive(
      on(atom("success")) >> [&]
      {
        REQUIRE(true);
      },
      fail);

  self->receive(
      on_arg_match >> [&](bitstream const& hits)
      {
        CHECK(hits.count() == 46);
        CHECK(hits.find_first() == 4);
      },
      fail);

  self->receive(
      on(atom("progress"), arg_match) >> [=](double progress, uint64_t hits)
      {
        CHECK(progress == 1.0);
        CHECK(hits == 46);
      },
      fail);

  // Construct a simple query and verify that the results are correct.
  self->send(core, atom("search"));
  self->receive(
      [&](actor search)
      {
        auto q = "id.resp_p == 995/?";
        self->sync_send(search, atom("query"), self, q).await((
            [&](expr::ast const& ast, actor qry)
            {
              CHECK(ast == *pops);
              self->send(qry, atom("extract"), uint64_t{46});
            },
            fail));
      },
      fail);

  self->receive(
      on(atom("progress"), arg_match) >> [=](double progress, uint64_t hits)
      {
        CHECK(progress == 1.0);
        CHECK(hits == 46);
      },
      fail);

  auto i = 0;
  self->receive_for(i, 46) (
    [&](event const& e)
    {
      // Verify contents of a few random events.
      if (e.id() == 4)
        CHECK(get<record>(e)->at(1) == "reRxJaOOlO9");

      if (e.id() == 42)
      {
        CHECK(get<record>(e)->at(1) == "7e0gZmKgGS4");
        CHECK(get<record>(e)->at(4) == "TLS_RSA_WITH_RC4_128_MD5");
      }

      // The last event.
      if (e.id() == 103)
        CHECK(get<record>(e)->at(1) == "mXRBhfuUqag");
    },
    fail);

  // A query always sends a "done" atom before terminating.
  self->receive(
      on(atom("done")) >> [&]
      {
        REQUIRE(true);
      },
      fail);

  // Now import another log file.
  set_ports(import_config, 3);
  *import_config['r'] = m57_day11_18::conn;
  import = self->spawn<program, monitored>(import_config);
  anon_send(import, atom("run"));
  self->receive(
      on_arg_match >> [&](down_msg const& d) { CHECK(d.reason == exit::done); },
      fail);

  // Wait for the segment to arrive at the receiver.
  std::this_thread::sleep_for(std::chrono::seconds(1));

  self->send(core, atom("index"));
  self->receive(
      [&](actor index)
      {
        self->sync_send(index, atom("flush")).await(
            on_arg_match >> [&](actor task_tree)
            {
              anon_send(task_tree, atom("notify"), self);
              self->receive(
                  on(atom("done")) >> [&]
                  {
                    CHECK(self->last_sender() == task_tree);

                    auto dir = path{*core_config.get("directory")};
                    auto p = dir / "index" / "m57-conn" / "types" / "conn";
                    REQUIRE(exists(p));
                  },
                  fail);
            },
            fail);
      });

  // Issue a query against both conn and ssl.
  self->send(core, atom("search"));
  self->receive(
      [&](actor search)
      {
        auto q = "id.resp_p == 443/? && ssl.server_name ni \"mozilla\"";
        self->sync_send(search, atom("query"), self, q).await((
            [&](expr::ast const&, actor qry)
            {
              // Extract all results.
              self->send(qry, atom("extract"), uint64_t{0});
              self->monitor(qry);
            },
            fail));
      },
      fail);

  bool done = false;
  size_t n = 0;
  self->do_receive(
      [&](event const&)
      {
        ++n;
      },
      on(atom("progress"), arg_match) >> [=](double, uint64_t)
      {
        REQUIRE(true);
      },
      on(atom("done")) >> [&]
      {
        CHECK(n == 15);
      },
      [&](down_msg const& d)
      {
        // Query terminates after having extracted all events.
        CHECK(d.reason == exit::done);
        done = true;
      },
      fail
      ).until([&done] { return done; });

  self->send_exit(core, exit::done);
  self->await_all_other_actors_done();

  CHECK(rm(path{*core_config.get("directory")}));
}
<commit_msg>Delete potentially stale unit test directory.<commit_after>#include <caf/all.hpp>

#include "vast/bitstream.h"
#include "vast/bitmap_index.h"
#include "vast/configuration.h"
#include "vast/event.h"
#include "vast/expression.h"
#include "vast/file_system.h"
#include "vast/program.h"
#include "vast/segment.h"
#include "vast/io/serialization.h"

#include "framework/unit.h"

#include "test/unit/bro_logs.h"

using namespace caf;
using namespace vast;

SUITE("actors")

namespace {

// Because we're running multiple TCP tests sequentially, we need to give the
// OS a bit of time to release the ports from previous test. This function sets
// different ports for different program instances as an alternative to waiting
// for the same ports to become free again.
void set_ports(configuration& config, uint64_t instance)
{
  uint16_t port = 42000 + instance * 5;
  *config["tracker.port"] = port++;
  *config["receiver.port"] = port++;
  *config["archive.port"] = port++;
  *config["index.port"] = port++;
  *config["search.port"] = port;
}

} // namespace <anonymous>

TEST("all-in-one import")
{
  configuration cfg;
  set_ports(cfg, 1);
  *cfg['v'] = 0;
  *cfg['V'] = 5;
  *cfg['C'] = true;
  *cfg['I'] = true;
  *cfg['r'] = m57_day11_18::ftp;
  *cfg['p'] = "m57_day11_18";
  REQUIRE(cfg.verify());

  path dir = *cfg.get("directory");
  if (exists(dir))
    REQUIRE(rm(dir));

  anon_send(spawn<program>(cfg), atom("run"));
  await_all_actors_done();

  auto ftp = dir / "index" / "m57_day11_18" / "types" / "ftp";

  REQUIRE(exists(dir));
  REQUIRE(exists(ftp));

  uint64_t size;
  address_bitmap_index<default_bitstream> abmi;
  port_bitmap_index<default_bitstream> pbmi;

  REQUIRE(vast::io::unarchive(ftp / "id" / "orig_h" / "index", size, abmi));
  REQUIRE(vast::io::unarchive(ftp / "id" / "orig_p" / "index", size, pbmi));

  REQUIRE(size == 3); // Event ID 1 is the first valid ID.
  REQUIRE(size == abmi.size());
  REQUIRE(size == pbmi.size());

  auto eq = relational_operator::equal;
  auto orig_h = abmi.lookup(eq, *to<address>("192.168.1.105"));
  auto orig_p = pbmi.lookup(greater, *to<port>("49320/?"));

  REQUIRE(orig_h);
  CHECK((*orig_h)[1] == 1);
  CHECK((*orig_h)[2] == 1);

  REQUIRE(orig_p);
  CHECK((*orig_p)[1] == 1);
  CHECK((*orig_p)[2] == 0);

  CHECK(rm(dir));
}

TEST("basic actor integrity")
{
  // First spawn the core.
  configuration core_config;
  set_ports(core_config, 2);
  *core_config['v'] = 0;
  *core_config['V'] = 5;
  *core_config['C'] = true;
  *core_config['p'] = "m57-ssl";
  REQUIRE(core_config.verify());

  path dir = *core_config.get("directory");
  if (exists(dir))
    REQUIRE(rm(dir));

  auto core = spawn<program>(core_config);
  anon_send(core, atom("run"));

  // Wait until the TCP sockets of the core have bound.
  std::this_thread::sleep_for(std::chrono::milliseconds(500));

  // Import a single Bro log.
  configuration import_config;
  set_ports(import_config, 2);
  *import_config['v'] = 0;
  *import_config['V'] = 5;
  *import_config['I'] = true;
  *import_config['r'] = m57_day11_18::ssl;
  *import_config["import.batch-size"] = 10;
  *import_config["archive.max-segment-size"] = 1;
  REQUIRE(import_config.verify());

  // Terminates after import completes.
  auto import = spawn<program>(import_config);

  // Pull down core afterwards.
  import->link_to(core);
  anon_send(import, atom("run"));

  await_all_actors_done();

  // Restart a new core.
  set_ports(core_config, 3);
  *core_config['v'] = 0;
  *core_config['V'] = 5;
  *core_config['C'] = true;
  *core_config['p'] = "m57-conn";
  REQUIRE(core_config.verify());

  core = spawn<program>(core_config);
  anon_send(core, atom("run"));

  scoped_actor self;
  auto fail = others() >> [&]
  {
    std::cerr
      << "unexpected message from " << self->last_sender().id() << ": "
      << to_string(self->last_dequeued()) << std::endl;

    REQUIRE(false);
  };

  // Test whether the archive has the correct segment.
  self->send(core, atom("archive"));
  self->receive([&](actor archive) { self->send(archive, event_id(100)); });
  self->receive(
      on_arg_match >> [&](segment const& s)
      {
        CHECK(s.meta().base == 1);
        CHECK(s.meta().events == 113);

        // Check the last ssl.log entry.
        segment::reader r{s};
        auto e = r.read(113);
        REQUIRE(e);
        CHECK(get<record>(*e)->at(1) == "XBy0ZlNNWuj");
        CHECK(get<record>(*e)->at(3) == "TLSv10");
      },
      fail);

  // Test whether a manual index lookup succeeds.
  auto pops = to<expr::ast>("id.resp_p == 995/?");
  REQUIRE(pops);

  self->send(core, atom("index"));
  self->receive(
      [&](actor index) { self->send(index, atom("query"), *pops, self); });

  self->receive(
      on(atom("success")) >> [&]
      {
        REQUIRE(true);
      },
      fail);

  self->receive(
      on_arg_match >> [&](bitstream const& hits)
      {
        CHECK(hits.count() == 46);
        CHECK(hits.find_first() == 4);
      },
      fail);

  self->receive(
      on(atom("progress"), arg_match) >> [=](double progress, uint64_t hits)
      {
        CHECK(progress == 1.0);
        CHECK(hits == 46);
      },
      fail);

  // Construct a simple query and verify that the results are correct.
  self->send(core, atom("search"));
  self->receive(
      [&](actor search)
      {
        auto q = "id.resp_p == 995/?";
        self->sync_send(search, atom("query"), self, q).await((
            [&](expr::ast const& ast, actor qry)
            {
              CHECK(ast == *pops);
              self->send(qry, atom("extract"), uint64_t{46});
            },
            fail));
      },
      fail);

  self->receive(
      on(atom("progress"), arg_match) >> [=](double progress, uint64_t hits)
      {
        CHECK(progress == 1.0);
        CHECK(hits == 46);
      },
      fail);

  auto i = 0;
  self->receive_for(i, 46) (
    [&](event const& e)
    {
      // Verify contents of a few random events.
      if (e.id() == 4)
        CHECK(get<record>(e)->at(1) == "reRxJaOOlO9");

      if (e.id() == 42)
      {
        CHECK(get<record>(e)->at(1) == "7e0gZmKgGS4");
        CHECK(get<record>(e)->at(4) == "TLS_RSA_WITH_RC4_128_MD5");
      }

      // The last event.
      if (e.id() == 103)
        CHECK(get<record>(e)->at(1) == "mXRBhfuUqag");
    },
    fail);

  // A query always sends a "done" atom before terminating.
  self->receive(
      on(atom("done")) >> [&]
      {
        REQUIRE(true);
      },
      fail);

  // Now import another log file.
  set_ports(import_config, 3);
  *import_config['r'] = m57_day11_18::conn;
  import = self->spawn<program, monitored>(import_config);
  anon_send(import, atom("run"));
  self->receive(
      on_arg_match >> [&](down_msg const& d) { CHECK(d.reason == exit::done); },
      fail);

  // Wait for the segment to arrive at the receiver.
  std::this_thread::sleep_for(std::chrono::seconds(1));

  self->send(core, atom("index"));
  self->receive(
      [&](actor index)
      {
        self->sync_send(index, atom("flush")).await(
            on_arg_match >> [&](actor task_tree)
            {
              anon_send(task_tree, atom("notify"), self);
              self->receive(
                  on(atom("done")) >> [&]
                  {
                    CHECK(self->last_sender() == task_tree);

                    auto p = dir / "index" / "m57-conn" / "types" / "conn";
                    REQUIRE(exists(p));
                  },
                  fail);
            },
            fail);
      });

  // Issue a query against both conn and ssl.
  self->send(core, atom("search"));
  self->receive(
      [&](actor search)
      {
        auto q = "id.resp_p == 443/? && ssl.server_name ni \"mozilla\"";
        self->sync_send(search, atom("query"), self, q).await((
            [&](expr::ast const&, actor qry)
            {
              // Extract all results.
              self->send(qry, atom("extract"), uint64_t{0});
              self->monitor(qry);
            },
            fail));
      },
      fail);

  bool done = false;
  size_t n = 0;
  self->do_receive(
      [&](event const&)
      {
        ++n;
      },
      on(atom("progress"), arg_match) >> [=](double, uint64_t)
      {
        REQUIRE(true);
      },
      on(atom("done")) >> [&]
      {
        CHECK(n == 15);
      },
      [&](down_msg const& d)
      {
        // Query terminates after having extracted all events.
        CHECK(d.reason == exit::done);
        done = true;
      },
      fail
      ).until([&done] { return done; });

  self->send_exit(core, exit::done);
  self->await_all_other_actors_done();

  CHECK(rm(*core_config.get("directory")));
}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "genericproject.h"

#include "genericbuildconfiguration.h"
#include "genericmakestep.h"
#include "genericprojectconstants.h"

#include <coreplugin/documentmanager.h>
#include <coreplugin/icontext.h>
#include <coreplugin/icore.h>
#include <coreplugin/mimedatabase.h>
#include <cpptools/cpptoolsconstants.h>
#include <cpptools/ModelManagerInterface.h>
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/abi.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/headerpath.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <qtsupport/customexecutablerunconfiguration.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>

#include <QDir>
#include <QProcessEnvironment>

using namespace Core;
using namespace ProjectExplorer;

namespace GenericProjectManager {
namespace Internal {

////////////////////////////////////////////////////////////////////////////////////
//
// GenericProject
//
////////////////////////////////////////////////////////////////////////////////////

GenericProject::GenericProject(Manager *manager, const QString &fileName)
    : m_manager(manager),
      m_fileName(fileName)
{
    setProjectContext(Context(GenericProjectManager::Constants::PROJECTCONTEXT));
    setProjectLanguages(Context(ProjectExplorer::Constants::LANG_CXX));

    QFileInfo fileInfo(m_fileName);
    QDir dir = fileInfo.dir();

    m_projectName      = fileInfo.completeBaseName();
    m_filesFileName    = QFileInfo(dir, m_projectName + QLatin1String(".files")).absoluteFilePath();
    m_includesFileName = QFileInfo(dir, m_projectName + QLatin1String(".includes")).absoluteFilePath();
    m_configFileName   = QFileInfo(dir, m_projectName + QLatin1String(".config")).absoluteFilePath();

    m_creatorIDocument  = new GenericProjectFile(this, m_fileName, GenericProject::Everything);
    m_filesIDocument    = new GenericProjectFile(this, m_filesFileName, GenericProject::Files);
    m_includesIDocument = new GenericProjectFile(this, m_includesFileName, GenericProject::Configuration);
    m_configIDocument   = new GenericProjectFile(this, m_configFileName, GenericProject::Configuration);

    DocumentManager::addDocument(m_creatorIDocument);
    DocumentManager::addDocument(m_filesIDocument);
    DocumentManager::addDocument(m_includesIDocument);
    DocumentManager::addDocument(m_configIDocument);

    m_rootNode = new GenericProjectNode(this, m_creatorIDocument);

    m_manager->registerProject(this);
}

GenericProject::~GenericProject()
{
    m_codeModelFuture.cancel();
    m_manager->unregisterProject(this);

    delete m_rootNode;
}

QString GenericProject::filesFileName() const
{
    return m_filesFileName;
}

QString GenericProject::includesFileName() const
{
    return m_includesFileName;
}

QString GenericProject::configFileName() const
{
    return m_configFileName;
}

static QStringList readLines(const QString &absoluteFileName)
{
    QStringList lines;

    QFile file(absoluteFileName);
    if (file.open(QFile::ReadOnly)) {
        QTextStream stream(&file);

        forever {
            QString line = stream.readLine();
            if (line.isNull())
                break;

            lines.append(line);
        }
    }

    return lines;
}

bool GenericProject::saveRawFileList(const QStringList &rawFileList)
{
    // Make sure we can open the file for writing
    Utils::FileSaver saver(filesFileName(), QIODevice::Text);
    if (!saver.hasError()) {
        QTextStream stream(saver.file());
        foreach (const QString &filePath, rawFileList)
            stream << filePath << QLatin1Char('\n');
        saver.setResult(&stream);
    }
    if (!saver.finalize(ICore::mainWindow()))
        return false;
    refresh(GenericProject::Files);
    return true;
}

bool GenericProject::addFiles(const QStringList &filePaths)
{
    QStringList newList = m_rawFileList;

    QDir baseDir(QFileInfo(m_fileName).dir());
    foreach (const QString &filePath, filePaths)
        newList.append(baseDir.relativeFilePath(filePath));

    return saveRawFileList(newList);
}

bool GenericProject::removeFiles(const QStringList &filePaths)
{
    QStringList newList = m_rawFileList;

    foreach (const QString &filePath, filePaths) {
        QHash<QString, QString>::iterator i = m_rawListEntries.find(filePath);
        if (i != m_rawListEntries.end())
            newList.removeOne(i.value());
    }

    return saveRawFileList(newList);
}

bool GenericProject::setFiles(const QStringList &filePaths)
{
    QStringList newList;
    QDir baseDir(QFileInfo(m_fileName).dir());
    foreach (const QString &filePath, filePaths)
        newList.append(baseDir.relativeFilePath(filePath));

    return saveRawFileList(newList);
}

bool GenericProject::renameFile(const QString &filePath, const QString &newFilePath)
{
    QStringList newList = m_rawFileList;

    QHash<QString, QString>::iterator i = m_rawListEntries.find(filePath);
    if (i != m_rawListEntries.end()) {
        int index = newList.indexOf(i.value());
        if (index != -1) {
            QDir baseDir(QFileInfo(m_fileName).dir());
            newList.replace(index, baseDir.relativeFilePath(newFilePath));
        }
    }

    return saveRawFileList(newList);
}

void GenericProject::parseProject(RefreshOptions options)
{
    if (options & Files) {
        m_rawListEntries.clear();
        m_rawFileList = readLines(filesFileName());
        m_files = processEntries(m_rawFileList, &m_rawListEntries);
    }

    if (options & Configuration) {
        m_projectIncludePaths = processEntries(readLines(includesFileName()));

        // TODO: Possibly load some configuration from the project file
        //QSettings projectInfo(m_fileName, QSettings::IniFormat);

        m_defines.clear();

        QFile configFile(configFileName());
        if (configFile.open(QFile::ReadOnly))
            m_defines = configFile.readAll();
    }

    if (options & Files)
        emit fileListChanged();
}

void GenericProject::refresh(RefreshOptions options)
{
    QSet<QString> oldFileList;
    if (!(options & Configuration))
        oldFileList = m_files.toSet();

    parseProject(options);

    if (options & Files)
        m_rootNode->refresh(oldFileList);

    CPlusPlus::CppModelManagerInterface *modelManager =
        CPlusPlus::CppModelManagerInterface::instance();

    if (modelManager) {
        CPlusPlus::CppModelManagerInterface::ProjectInfo pinfo = modelManager->projectInfo(this);
        pinfo.clearProjectParts();
        CPlusPlus::CppModelManagerInterface::ProjectPart::Ptr part(
                    new CPlusPlus::CppModelManagerInterface::ProjectPart);

        Kit *k = activeTarget() ? activeTarget()->kit() : KitManager::instance()->defaultKit();
        if (ToolChain *tc = ToolChainKitInformation::toolChain(k)) {
            QStringList cxxflags; // FIXME: Can we do better?
            part->defines = tc->predefinedMacros(cxxflags);
            part->defines += '\n';

            foreach (const HeaderPath &headerPath, tc->systemHeaderPaths(cxxflags, SysRootKitInformation::sysRoot(k))) {
                if (headerPath.kind() == HeaderPath::FrameworkHeaderPath)
                    part->frameworkPaths.append(headerPath.path());
                else
                    part->includePaths.append(headerPath.path());
            }
        }

        part->includePaths += allIncludePaths();
        part->defines += m_defines;

        // ### add _defines.

        // Add any C/C++ files to be parsed
        QStringList cppMimeTypes;
        cppMimeTypes << QLatin1String(CppTools::Constants::C_SOURCE_MIMETYPE)
                     << QLatin1String(CppTools::Constants::C_HEADER_MIMETYPE)
                     << QLatin1String(CppTools::Constants::CPP_SOURCE_MIMETYPE)
                     << QLatin1String(CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE)
                     << QLatin1String(CppTools::Constants::CPP_HEADER_MIMETYPE);

        const Core::MimeDatabase *mimeDatabase = Core::ICore::mimeDatabase();
        foreach (const QString &file, files()) {
            const Core::MimeType mimeType = mimeDatabase->findByFile(QFileInfo(file));
            if (cppMimeTypes.contains(mimeType.type()))
                part->sourceFiles += file;
        }

        QStringList filesToUpdate;

        if (options & Configuration) {
            filesToUpdate = part->sourceFiles;
            filesToUpdate.append(CPlusPlus::CppModelManagerInterface::configurationFileName());
            // Full update, if there's a code model update, cancel it
            m_codeModelFuture.cancel();
        } else if (options & Files) {
            // Only update files that got added to the list
            QSet<QString> newFileList = part->sourceFiles.toSet();
            newFileList.subtract(oldFileList);
            filesToUpdate.append(newFileList.toList());
        }

        pinfo.appendProjectPart(part);

        modelManager->updateProjectInfo(pinfo);
        m_codeModelFuture = modelManager->updateSourceFiles(filesToUpdate);
    }
}

/**
 * Expands environment variables in the given \a string when they are written
 * like $$(VARIABLE).
 */
static void expandEnvironmentVariables(const QProcessEnvironment &env, QString &string)
{
    static QRegExp candidate(QLatin1String("\\$\\$\\((.+)\\)"));

    int index = candidate.indexIn(string);
    while (index != -1) {
        const QString value = env.value(candidate.cap(1));

        string.replace(index, candidate.matchedLength(), value);
        index += value.length();

        index = candidate.indexIn(string, index);
    }
}

/**
 * Expands environment variables and converts the path from relative to the
 * project to an absolute path.
 *
 * The \a map variable is an optional argument that will map the returned
 * absolute paths back to their original \a entries.
 */
QStringList GenericProject::processEntries(const QStringList &paths,
                                           QHash<QString, QString> *map) const
{
    const QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
    const QDir projectDir(QFileInfo(m_fileName).dir());

    QStringList absolutePaths;
    foreach (const QString &path, paths) {
        QString trimmedPath = path.trimmed();
        if (trimmedPath.isEmpty())
            continue;

        expandEnvironmentVariables(env, trimmedPath);

        trimmedPath = Utils::FileName::fromUserInput(trimmedPath).toString();

        const QString absPath = QFileInfo(projectDir, trimmedPath).absoluteFilePath();
        absolutePaths.append(absPath);
        if (map)
            map->insert(absPath, trimmedPath);
    }
    absolutePaths.removeDuplicates();
    return absolutePaths;
}

QStringList GenericProject::allIncludePaths() const
{
    QStringList paths;
    paths += m_includePaths;
    paths += m_projectIncludePaths;
    paths.removeDuplicates();
    return paths;
}

QStringList GenericProject::projectIncludePaths() const
{
    return m_projectIncludePaths;
}

QStringList GenericProject::files() const
{
    return m_files;
}

QStringList GenericProject::includePaths() const
{
    return m_includePaths;
}

void GenericProject::setIncludePaths(const QStringList &includePaths)
{
    m_includePaths = includePaths;
}

QByteArray GenericProject::defines() const
{
    return m_defines;
}

QString GenericProject::displayName() const
{
    return m_projectName;
}

Id GenericProject::id() const
{
    return Id(Constants::GENERICPROJECT_ID);
}

IDocument *GenericProject::document() const
{
    return m_creatorIDocument;
}

IProjectManager *GenericProject::projectManager() const
{
    return m_manager;
}

GenericProjectNode *GenericProject::rootProjectNode() const
{
    return m_rootNode;
}

QStringList GenericProject::files(FilesMode fileMode) const
{
    Q_UNUSED(fileMode)
    return m_files;
}

QStringList GenericProject::buildTargets() const
{
    QStringList targets;
    targets.append(QLatin1String("all"));
    targets.append(QLatin1String("clean"));
    return targets;
}

bool GenericProject::fromMap(const QVariantMap &map)
{
    if (!Project::fromMap(map))
        return false;

    Kit *defaultKit = KitManager::instance()->defaultKit();
    if (!activeTarget() && defaultKit)
        addTarget(createTarget(defaultKit));

    // Sanity check: We need both a buildconfiguration and a runconfiguration!
    QList<Target *> targetList = targets();
    foreach (Target *t, targetList) {
        if (!t->activeBuildConfiguration()) {
            removeTarget(t);
            delete t;
            continue;
        }
        if (!t->activeRunConfiguration())
            t->addRunConfiguration(new QtSupport::CustomExecutableRunConfiguration(t));
    }

    setIncludePaths(allIncludePaths());

    refresh(Everything);
    return true;
}

////////////////////////////////////////////////////////////////////////////////////
//
// GenericProjectFile
//
////////////////////////////////////////////////////////////////////////////////////

GenericProjectFile::GenericProjectFile(GenericProject *parent, QString fileName, GenericProject::RefreshOptions options)
    : IDocument(parent),
      m_project(parent),
      m_fileName(fileName),
      m_options(options)
{ }

bool GenericProjectFile::save(QString *, const QString &, bool)
{
    return false;
}

QString GenericProjectFile::fileName() const
{
    return m_fileName;
}

QString GenericProjectFile::defaultPath() const
{
    return QString();
}

QString GenericProjectFile::suggestedFileName() const
{
    return QString();
}

QString GenericProjectFile::mimeType() const
{
    return QLatin1String(Constants::GENERICMIMETYPE);
}

bool GenericProjectFile::isModified() const
{
    return false;
}

bool GenericProjectFile::isSaveAsAllowed() const
{
    return false;
}

void GenericProjectFile::rename(const QString &newName)
{
    // Can't happen
    Q_UNUSED(newName);
    QTC_CHECK(false);
}

IDocument::ReloadBehavior GenericProjectFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
{
    Q_UNUSED(state)
    Q_UNUSED(type)
    return BehaviorSilent;
}

bool GenericProjectFile::reload(QString *errorString, ReloadFlag flag, ChangeType type)
{
    Q_UNUSED(errorString)
    Q_UNUSED(flag)
    if (type == TypePermissions)
        return true;
    m_project->refresh(m_options);
    return true;
}

} // namespace Internal
} // namespace GenericProjectManager
<commit_msg>Speed up the identification of C++ files in the generic project<commit_after>/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "genericproject.h"

#include "genericbuildconfiguration.h"
#include "genericmakestep.h"
#include "genericprojectconstants.h"

#include <coreplugin/documentmanager.h>
#include <coreplugin/icontext.h>
#include <coreplugin/icore.h>
#include <coreplugin/mimedatabase.h>
#include <cpptools/cpptoolsconstants.h>
#include <cpptools/ModelManagerInterface.h>
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/abi.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/headerpath.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <qtsupport/customexecutablerunconfiguration.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>

#include <QDir>
#include <QProcessEnvironment>

using namespace Core;
using namespace ProjectExplorer;

namespace GenericProjectManager {
namespace Internal {

static QList<Core::MimeType> cppMimeTypes()
{
    QStringList mimeTypesNames;
    mimeTypesNames << QLatin1String(CppTools::Constants::C_SOURCE_MIMETYPE)
                   << QLatin1String(CppTools::Constants::C_HEADER_MIMETYPE)
                   << QLatin1String(CppTools::Constants::CPP_SOURCE_MIMETYPE)
                   << QLatin1String(CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE)
                   << QLatin1String(CppTools::Constants::CPP_HEADER_MIMETYPE);

    QList<Core::MimeType> mimeTypes;

    const Core::MimeDatabase *mimeDatabase = Core::ICore::mimeDatabase();
    foreach (const QString &typeName, mimeTypesNames) {
        Core::MimeType mimeType = mimeDatabase->findByType(typeName);
        if (!mimeType.isNull())
            mimeTypes.append(mimeType);
    }

    return mimeTypes;
}

////////////////////////////////////////////////////////////////////////////////////
//
// GenericProject
//
////////////////////////////////////////////////////////////////////////////////////

GenericProject::GenericProject(Manager *manager, const QString &fileName)
    : m_manager(manager),
      m_fileName(fileName)
{
    setProjectContext(Context(GenericProjectManager::Constants::PROJECTCONTEXT));
    setProjectLanguages(Context(ProjectExplorer::Constants::LANG_CXX));

    QFileInfo fileInfo(m_fileName);
    QDir dir = fileInfo.dir();

    m_projectName      = fileInfo.completeBaseName();
    m_filesFileName    = QFileInfo(dir, m_projectName + QLatin1String(".files")).absoluteFilePath();
    m_includesFileName = QFileInfo(dir, m_projectName + QLatin1String(".includes")).absoluteFilePath();
    m_configFileName   = QFileInfo(dir, m_projectName + QLatin1String(".config")).absoluteFilePath();

    m_creatorIDocument  = new GenericProjectFile(this, m_fileName, GenericProject::Everything);
    m_filesIDocument    = new GenericProjectFile(this, m_filesFileName, GenericProject::Files);
    m_includesIDocument = new GenericProjectFile(this, m_includesFileName, GenericProject::Configuration);
    m_configIDocument   = new GenericProjectFile(this, m_configFileName, GenericProject::Configuration);

    DocumentManager::addDocument(m_creatorIDocument);
    DocumentManager::addDocument(m_filesIDocument);
    DocumentManager::addDocument(m_includesIDocument);
    DocumentManager::addDocument(m_configIDocument);

    m_rootNode = new GenericProjectNode(this, m_creatorIDocument);

    m_manager->registerProject(this);
}

GenericProject::~GenericProject()
{
    m_codeModelFuture.cancel();
    m_manager->unregisterProject(this);

    delete m_rootNode;
}

QString GenericProject::filesFileName() const
{
    return m_filesFileName;
}

QString GenericProject::includesFileName() const
{
    return m_includesFileName;
}

QString GenericProject::configFileName() const
{
    return m_configFileName;
}

static QStringList readLines(const QString &absoluteFileName)
{
    QStringList lines;

    QFile file(absoluteFileName);
    if (file.open(QFile::ReadOnly)) {
        QTextStream stream(&file);

        forever {
            QString line = stream.readLine();
            if (line.isNull())
                break;

            lines.append(line);
        }
    }

    return lines;
}

bool GenericProject::saveRawFileList(const QStringList &rawFileList)
{
    // Make sure we can open the file for writing
    Utils::FileSaver saver(filesFileName(), QIODevice::Text);
    if (!saver.hasError()) {
        QTextStream stream(saver.file());
        foreach (const QString &filePath, rawFileList)
            stream << filePath << QLatin1Char('\n');
        saver.setResult(&stream);
    }
    if (!saver.finalize(ICore::mainWindow()))
        return false;
    refresh(GenericProject::Files);
    return true;
}

bool GenericProject::addFiles(const QStringList &filePaths)
{
    QStringList newList = m_rawFileList;

    QDir baseDir(QFileInfo(m_fileName).dir());
    foreach (const QString &filePath, filePaths)
        newList.append(baseDir.relativeFilePath(filePath));

    return saveRawFileList(newList);
}

bool GenericProject::removeFiles(const QStringList &filePaths)
{
    QStringList newList = m_rawFileList;

    foreach (const QString &filePath, filePaths) {
        QHash<QString, QString>::iterator i = m_rawListEntries.find(filePath);
        if (i != m_rawListEntries.end())
            newList.removeOne(i.value());
    }

    return saveRawFileList(newList);
}

bool GenericProject::setFiles(const QStringList &filePaths)
{
    QStringList newList;
    QDir baseDir(QFileInfo(m_fileName).dir());
    foreach (const QString &filePath, filePaths)
        newList.append(baseDir.relativeFilePath(filePath));

    return saveRawFileList(newList);
}

bool GenericProject::renameFile(const QString &filePath, const QString &newFilePath)
{
    QStringList newList = m_rawFileList;

    QHash<QString, QString>::iterator i = m_rawListEntries.find(filePath);
    if (i != m_rawListEntries.end()) {
        int index = newList.indexOf(i.value());
        if (index != -1) {
            QDir baseDir(QFileInfo(m_fileName).dir());
            newList.replace(index, baseDir.relativeFilePath(newFilePath));
        }
    }

    return saveRawFileList(newList);
}

void GenericProject::parseProject(RefreshOptions options)
{
    if (options & Files) {
        m_rawListEntries.clear();
        m_rawFileList = readLines(filesFileName());
        m_files = processEntries(m_rawFileList, &m_rawListEntries);
    }

    if (options & Configuration) {
        m_projectIncludePaths = processEntries(readLines(includesFileName()));

        // TODO: Possibly load some configuration from the project file
        //QSettings projectInfo(m_fileName, QSettings::IniFormat);

        m_defines.clear();

        QFile configFile(configFileName());
        if (configFile.open(QFile::ReadOnly))
            m_defines = configFile.readAll();
    }

    if (options & Files)
        emit fileListChanged();
}

void GenericProject::refresh(RefreshOptions options)
{
    QSet<QString> oldFileList;
    if (!(options & Configuration))
        oldFileList = m_files.toSet();

    parseProject(options);

    if (options & Files)
        m_rootNode->refresh(oldFileList);

    CPlusPlus::CppModelManagerInterface *modelManager =
        CPlusPlus::CppModelManagerInterface::instance();

    if (modelManager) {
        CPlusPlus::CppModelManagerInterface::ProjectInfo pinfo = modelManager->projectInfo(this);
        pinfo.clearProjectParts();
        CPlusPlus::CppModelManagerInterface::ProjectPart::Ptr part(
                    new CPlusPlus::CppModelManagerInterface::ProjectPart);

        Kit *k = activeTarget() ? activeTarget()->kit() : KitManager::instance()->defaultKit();
        if (ToolChain *tc = ToolChainKitInformation::toolChain(k)) {
            QStringList cxxflags; // FIXME: Can we do better?
            part->defines = tc->predefinedMacros(cxxflags);
            part->defines += '\n';

            foreach (const HeaderPath &headerPath, tc->systemHeaderPaths(cxxflags, SysRootKitInformation::sysRoot(k))) {
                if (headerPath.kind() == HeaderPath::FrameworkHeaderPath)
                    part->frameworkPaths.append(headerPath.path());
                else
                    part->includePaths.append(headerPath.path());
            }
        }

        part->includePaths += allIncludePaths();
        part->defines += m_defines;

        // ### add _defines.

        // Add any C/C++ files to be parsed
        const QList<Core::MimeType> mimeTypes = cppMimeTypes();
        QFileInfo fileInfo;

        foreach (const QString &file, files()) {
            fileInfo.setFile(file);
            foreach (const Core::MimeType &mimeType, mimeTypes) {
                if (mimeType.matchesFile(fileInfo)) {
                    part->sourceFiles += file;
                    break;
                }
            }
        }

        QStringList filesToUpdate;

        if (options & Configuration) {
            filesToUpdate = part->sourceFiles;
            filesToUpdate.append(CPlusPlus::CppModelManagerInterface::configurationFileName());
            // Full update, if there's a code model update, cancel it
            m_codeModelFuture.cancel();
        } else if (options & Files) {
            // Only update files that got added to the list
            QSet<QString> newFileList = part->sourceFiles.toSet();
            newFileList.subtract(oldFileList);
            filesToUpdate.append(newFileList.toList());
        }

        pinfo.appendProjectPart(part);

        modelManager->updateProjectInfo(pinfo);
        m_codeModelFuture = modelManager->updateSourceFiles(filesToUpdate);
    }
}

/**
 * Expands environment variables in the given \a string when they are written
 * like $$(VARIABLE).
 */
static void expandEnvironmentVariables(const QProcessEnvironment &env, QString &string)
{
    static QRegExp candidate(QLatin1String("\\$\\$\\((.+)\\)"));

    int index = candidate.indexIn(string);
    while (index != -1) {
        const QString value = env.value(candidate.cap(1));

        string.replace(index, candidate.matchedLength(), value);
        index += value.length();

        index = candidate.indexIn(string, index);
    }
}

/**
 * Expands environment variables and converts the path from relative to the
 * project to an absolute path.
 *
 * The \a map variable is an optional argument that will map the returned
 * absolute paths back to their original \a entries.
 */
QStringList GenericProject::processEntries(const QStringList &paths,
                                           QHash<QString, QString> *map) const
{
    const QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
    const QDir projectDir(QFileInfo(m_fileName).dir());

    QStringList absolutePaths;
    foreach (const QString &path, paths) {
        QString trimmedPath = path.trimmed();
        if (trimmedPath.isEmpty())
            continue;

        expandEnvironmentVariables(env, trimmedPath);

        trimmedPath = Utils::FileName::fromUserInput(trimmedPath).toString();

        const QString absPath = QFileInfo(projectDir, trimmedPath).absoluteFilePath();
        absolutePaths.append(absPath);
        if (map)
            map->insert(absPath, trimmedPath);
    }
    absolutePaths.removeDuplicates();
    return absolutePaths;
}

QStringList GenericProject::allIncludePaths() const
{
    QStringList paths;
    paths += m_includePaths;
    paths += m_projectIncludePaths;
    paths.removeDuplicates();
    return paths;
}

QStringList GenericProject::projectIncludePaths() const
{
    return m_projectIncludePaths;
}

QStringList GenericProject::files() const
{
    return m_files;
}

QStringList GenericProject::includePaths() const
{
    return m_includePaths;
}

void GenericProject::setIncludePaths(const QStringList &includePaths)
{
    m_includePaths = includePaths;
}

QByteArray GenericProject::defines() const
{
    return m_defines;
}

QString GenericProject::displayName() const
{
    return m_projectName;
}

Id GenericProject::id() const
{
    return Id(Constants::GENERICPROJECT_ID);
}

IDocument *GenericProject::document() const
{
    return m_creatorIDocument;
}

IProjectManager *GenericProject::projectManager() const
{
    return m_manager;
}

GenericProjectNode *GenericProject::rootProjectNode() const
{
    return m_rootNode;
}

QStringList GenericProject::files(FilesMode fileMode) const
{
    Q_UNUSED(fileMode)
    return m_files;
}

QStringList GenericProject::buildTargets() const
{
    QStringList targets;
    targets.append(QLatin1String("all"));
    targets.append(QLatin1String("clean"));
    return targets;
}

bool GenericProject::fromMap(const QVariantMap &map)
{
    if (!Project::fromMap(map))
        return false;

    Kit *defaultKit = KitManager::instance()->defaultKit();
    if (!activeTarget() && defaultKit)
        addTarget(createTarget(defaultKit));

    // Sanity check: We need both a buildconfiguration and a runconfiguration!
    QList<Target *> targetList = targets();
    foreach (Target *t, targetList) {
        if (!t->activeBuildConfiguration()) {
            removeTarget(t);
            delete t;
            continue;
        }
        if (!t->activeRunConfiguration())
            t->addRunConfiguration(new QtSupport::CustomExecutableRunConfiguration(t));
    }

    setIncludePaths(allIncludePaths());

    refresh(Everything);
    return true;
}

////////////////////////////////////////////////////////////////////////////////////
//
// GenericProjectFile
//
////////////////////////////////////////////////////////////////////////////////////

GenericProjectFile::GenericProjectFile(GenericProject *parent, QString fileName, GenericProject::RefreshOptions options)
    : IDocument(parent),
      m_project(parent),
      m_fileName(fileName),
      m_options(options)
{ }

bool GenericProjectFile::save(QString *, const QString &, bool)
{
    return false;
}

QString GenericProjectFile::fileName() const
{
    return m_fileName;
}

QString GenericProjectFile::defaultPath() const
{
    return QString();
}

QString GenericProjectFile::suggestedFileName() const
{
    return QString();
}

QString GenericProjectFile::mimeType() const
{
    return QLatin1String(Constants::GENERICMIMETYPE);
}

bool GenericProjectFile::isModified() const
{
    return false;
}

bool GenericProjectFile::isSaveAsAllowed() const
{
    return false;
}

void GenericProjectFile::rename(const QString &newName)
{
    // Can't happen
    Q_UNUSED(newName);
    QTC_CHECK(false);
}

IDocument::ReloadBehavior GenericProjectFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
{
    Q_UNUSED(state)
    Q_UNUSED(type)
    return BehaviorSilent;
}

bool GenericProjectFile::reload(QString *errorString, ReloadFlag flag, ChangeType type)
{
    Q_UNUSED(errorString)
    Q_UNUSED(flag)
    if (type == TypePermissions)
        return true;
    m_project->refresh(m_options);
    return true;
}

} // namespace Internal
} // namespace GenericProjectManager
<|endoftext|>


<commit_before>#include <cppa/cppa.hpp>

#include "vast/bitstream.h"
#include "vast/configuration.h"
#include "vast/event.h"
#include "vast/expression.h"
#include "vast/file_system.h"
#include "vast/program.h"
#include "vast/segment.h"

#include "framework/unit.h"

#include "test/unit/bro_logs.h"

using namespace cppa;
using namespace vast;

SUITE("actors")

namespace {

// Because we're running multiple TCP tests sequentially, we need to give the
// OS a bit of time to release the ports from previous test. This function sets
// different ports for different program instances as an alternative to waiting
// for the same ports to become free again.
void set_ports(configuration& config, uint64_t instance)
{
  uint16_t port = 42000 + instance * 5;
  *config["tracker.port"] = port++;
  *config["receiver.port"] = port++;
  *config["archive.port"] = port++;
  *config["index.port"] = port++;
  *config["search.port"] = port;
}

} // namespace <anonymous>

TEST("ingestion (all-in-one)")
{
  configuration cfg;
  set_ports(cfg, 0);
  *cfg['v'] = 0;
  *cfg['V'] = 5;
  *cfg['a'] = true;
  *cfg['I'] = true;
  *cfg['r'] = m57_day11_18::ftp;

  REQUIRE(cfg.verify());

  anon_send_exit(spawn<program>(cfg), exit::done);
  await_all_actors_done();

  CHECK(rm(path{*cfg.get("directory")}));
}

TEST("ingestion (two programs)")
{
  configuration core_config;
  set_ports(core_config, 1);
  *core_config['v'] = 0;
  *core_config['V'] = 5;
  *core_config['a'] = true;
  REQUIRE(core_config.verify());

  auto core = spawn<program>(core_config);

  configuration ingest_config;
  set_ports(ingest_config, 1);
  *ingest_config['v'] = 0;
  *ingest_config['V'] = 5;
  *ingest_config['I'] = true;
  *ingest_config['r'] = m57_day11_18::ssl;
  REQUIRE(ingest_config.verify());

  // Wait until the TCP sockets of the core have bound.
  ::sleep(1);

  // Terminates after ingestion completes.
  spawn<program>(ingest_config)->link_to(core);
  await_all_actors_done();
}

TEST("actor integrity")
{
  configuration cfg;
  set_ports(cfg, 2);
  *cfg['v'] = 0;
  *cfg['V'] = 5;
  *cfg['a'] = true;
  REQUIRE(cfg.verify());

  scoped_actor self;
  auto core = spawn<program>(cfg);

  auto fail = others() >> [&]
  {
    std::cerr << to_string(self->last_dequeued()) << std::endl;
    REQUIRE(false);
  };

  //
  // Archive
  //
  self->send(core, atom("archive"));
  self->receive([&](actor archive) { self->send(archive, event_id(100)); });
  self->receive(
      (on_arg_match >> [&](segment const& s)
      {
        CHECK(s.base() == 1);
        CHECK(s.events() == 113);

        // Check the last ssl.log entry.
        segment::reader r{&s};
        auto e = r.read(113);
        REQUIRE(e);
        CHECK((*e)[1] == "XBy0ZlNNWuj");
        CHECK((*e)[3] == "TLSv10");
      })
      .or_else(fail));

  //
  // Index
  //
  auto q = to<expr::ast>("id.resp_p == 995/?");
  REQUIRE(q);

  self->send(core, atom("index"));
  self->receive([&](actor idx) { self->send(idx, atom("query"), *q, self); });

  self->receive(
      (on(atom("success")) >> [&]
      {
        REQUIRE(true);
      })
      .or_else(fail));

  self->receive(
      (on(atom("progress"), arg_match) >> [=](double progress, uint64_t hits)
      {
        CHECK(progress == 0.0);
        CHECK(hits == 0);
      })
      .or_else(fail));

  self->receive(
      (on_arg_match >> [&](bitstream const& hits)
      {
        CHECK(hits.count() == 46);
        CHECK(hits.find_first() == 4);
      })
      .or_else(fail));

  self->receive(
      (on(atom("progress"), arg_match) >> [=](double progress, uint64_t hits)
      {
        CHECK(progress == 1.0);
        CHECK(hits == 46);
      })
      .or_else(fail));

  self->send_exit(core, exit::done);
  self->await_all_other_actors_done();

  CHECK(rm(path{*cfg.get("directory")}));
}
<commit_msg>Simplify failure definition.<commit_after>#include <cppa/cppa.hpp>

#include "vast/bitstream.h"
#include "vast/configuration.h"
#include "vast/event.h"
#include "vast/expression.h"
#include "vast/file_system.h"
#include "vast/program.h"
#include "vast/segment.h"

#include "framework/unit.h"

#include "test/unit/bro_logs.h"

using namespace cppa;
using namespace vast;

SUITE("actors")

namespace {

// Because we're running multiple TCP tests sequentially, we need to give the
// OS a bit of time to release the ports from previous test. This function sets
// different ports for different program instances as an alternative to waiting
// for the same ports to become free again.
void set_ports(configuration& config, uint64_t instance)
{
  uint16_t port = 42000 + instance * 5;
  *config["tracker.port"] = port++;
  *config["receiver.port"] = port++;
  *config["archive.port"] = port++;
  *config["index.port"] = port++;
  *config["search.port"] = port;
}

} // namespace <anonymous>

TEST("ingestion (all-in-one)")
{
  configuration cfg;
  set_ports(cfg, 0);
  *cfg['v'] = 0;
  *cfg['V'] = 5;
  *cfg['a'] = true;
  *cfg['I'] = true;
  *cfg['r'] = m57_day11_18::ftp;

  REQUIRE(cfg.verify());

  anon_send_exit(spawn<program>(cfg), exit::done);
  await_all_actors_done();

  CHECK(rm(path{*cfg.get("directory")}));
}

TEST("ingestion (two programs)")
{
  configuration core_config;
  set_ports(core_config, 1);
  *core_config['v'] = 0;
  *core_config['V'] = 5;
  *core_config['a'] = true;
  REQUIRE(core_config.verify());

  auto core = spawn<program>(core_config);

  configuration ingest_config;
  set_ports(ingest_config, 1);
  *ingest_config['v'] = 0;
  *ingest_config['V'] = 5;
  *ingest_config['I'] = true;
  *ingest_config['r'] = m57_day11_18::ssl;
  REQUIRE(ingest_config.verify());

  // Wait until the TCP sockets of the core have bound.
  ::sleep(1);

  // Terminates after ingestion completes.
  spawn<program>(ingest_config)->link_to(core);
  await_all_actors_done();
}

TEST("actor integrity")
{
  configuration cfg;
  set_ports(cfg, 2);
  *cfg['v'] = 0;
  *cfg['V'] = 5;
  *cfg['a'] = true;
  REQUIRE(cfg.verify());

  scoped_actor self;
  auto core = spawn<program>(cfg);

  auto fail = others() >> [&]
  {
    std::cerr << to_string(self->last_dequeued()) << std::endl;
    REQUIRE(false);
  };

  //
  // Archive
  //
  self->send(core, atom("archive"));
  self->receive([&](actor archive) { self->send(archive, event_id(100)); });
  self->receive(
      on_arg_match >> [&](segment const& s)
      {
        CHECK(s.base() == 1);
        CHECK(s.events() == 113);

        // Check the last ssl.log entry.
        segment::reader r{&s};
        auto e = r.read(113);
        REQUIRE(e);
        CHECK((*e)[1] == "XBy0ZlNNWuj");
        CHECK((*e)[3] == "TLSv10");
      },
      fail);

  //
  // Index
  //
  auto q = to<expr::ast>("id.resp_p == 995/?");
  REQUIRE(q);

  self->send(core, atom("index"));
  self->receive([&](actor idx) { self->send(idx, atom("query"), *q, self); });

  self->receive(
      on(atom("success")) >> [&]
      {
        REQUIRE(true);
      },
      fail);

  self->receive(
      on(atom("progress"), arg_match) >> [=](double progress, uint64_t hits)
      {
        CHECK(progress == 0.0);
        CHECK(hits == 0);
      },
      fail);

  self->receive(
      on_arg_match >> [&](bitstream const& hits)
      {
        CHECK(hits.count() == 46);
        CHECK(hits.find_first() == 4);
      },
      fail);

  self->receive(
      on(atom("progress"), arg_match) >> [=](double progress, uint64_t hits)
      {
        CHECK(progress == 1.0);
        CHECK(hits == 46);
      },
      fail);

  self->send_exit(core, exit::done);
  self->await_all_other_actors_done();

  CHECK(rm(path{*cfg.get("directory")}));
}
<|endoftext|>