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

#include "core/conf.h"
#include "core/htrace.h"
#include "core/scope.h"
#include "core/span.h"
#include "test/rtest.h"
#include "test/span_table.h"
#include "test/span_util.h"
#include "test/test.h"
#include "util/log.h"

#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define RECEIVER_TEST_TNAME "receiver-unit"

/**
 * @file rtestpp.cc
 *
 * The C receiver tests.
 */

struct rtest_data {
    struct htrace_conf *cnf;
    struct htrace_sampler *always;
    struct htracer *tracer;
};

static void get_receiver_test_trid(char *trid, size_t trid_len)
{
    snprintf(trid, trid_len, RECEIVER_TEST_TNAME "/%lld", (long long)getpid());
}

static int rtest_data_init(const char *conf_str, struct rtest_data **out)
{
    char *econf_str = NULL;
    struct rtest_data *rdata = calloc(1, sizeof(*(rdata)));
    EXPECT_NONNULL(rdata);
    if (asprintf(&econf_str, HTRACE_TRACER_ID"=%%{tname}/%%{pid};sampler=always;"
              "%s", conf_str) < 0) {
        fprintf(stderr, "asprintf(econf_str) failed: OOM\n");
        return EXIT_FAILURE;
    }
    rdata->cnf = htrace_conf_from_str(econf_str);
    free(econf_str);
    EXPECT_NONNULL(rdata->cnf);
    rdata->tracer = htracer_create(RECEIVER_TEST_TNAME, rdata->cnf);
    EXPECT_NONNULL(rdata->tracer);
    rdata->always = htrace_sampler_create(rdata->tracer, rdata->cnf);
    EXPECT_NONNULL(rdata->always);
    *out = rdata;
    return EXIT_SUCCESS;
}

static void rtest_data_free(struct rtest_data *rdata)
{
    htrace_sampler_free(rdata->always);
    rdata->always = NULL;
    htracer_free(rdata->tracer);
    rdata->tracer = NULL;
    htrace_conf_free(rdata->cnf);
    rdata->cnf = NULL;
    free(rdata);
}

static int rtest_verify_table_size(struct rtest *rt, struct span_table *st)
{
    EXPECT_INT_EQ(rt->spans_created, span_table_size(st));

    return EXIT_SUCCESS;
}

static int doit(struct rtest_data *rdata)
{
    struct htrace_scope *scope1, *scope2, *scope2_5;
    struct htrace_span_id span_id;

    scope1 = htrace_start_span(rdata->tracer, NULL, "part1");
    htrace_scope_get_span_id(scope1, &span_id);
    EXPECT_TRUE(0 !=
        htrace_span_id_compare(&INVALID_SPAN_ID, &span_id));
    htrace_scope_close(scope1);
    scope2 = htrace_start_span(rdata->tracer, NULL, "part2");
    htrace_scope_get_span_id(scope2, &span_id);
    EXPECT_TRUE(0 !=
        htrace_span_id_compare(&INVALID_SPAN_ID, &span_id));
    scope2_5 = htrace_start_span(rdata->tracer, NULL, "part2.5");
    htrace_scope_close(scope2_5);
    htrace_scope_close(scope2);
    return EXIT_SUCCESS;
}

int rtest_simple_run(struct rtest *rt, const char *conf_str)
{
    struct htrace_scope *scope0;
    struct rtest_data *rdata = NULL;

    EXPECT_INT_ZERO(rtest_data_init(conf_str, &rdata));
    EXPECT_NONNULL(rdata);
    scope0 = htrace_start_span(rdata->tracer, rdata->always, "doit");
    doit(rdata);
    htrace_scope_close(scope0);
    rt->spans_created = 4;
    rtest_data_free(rdata);
    return EXIT_SUCCESS;
}

int rtest_simple_verify(struct rtest *rt, struct span_table *st)
{
    struct htrace_span *span;
    struct htrace_span_id doit_id, part2_id;
    char trid[128];

    EXPECT_INT_ZERO(rtest_verify_table_size(rt, st));
    get_receiver_test_trid(trid, sizeof(trid));
    EXPECT_INT_ZERO(span_table_get(st, &span, "doit", trid));
    htrace_span_id_copy(&doit_id, &span->span_id);
    EXPECT_INT_ZERO(span->num_parents);

    EXPECT_INT_ZERO(span_table_get(st, &span, "part1", trid));
    EXPECT_INT_EQ(1, span->num_parents);
    EXPECT_TRUE(0 == htrace_span_id_compare(&doit_id, &span->parent.single));

    EXPECT_INT_ZERO(span_table_get(st, &span, "part2", trid));
    EXPECT_INT_EQ(1, span->num_parents);
    htrace_span_id_copy(&part2_id, &span->span_id);
    EXPECT_TRUE(0 == htrace_span_id_compare(&doit_id, &span->parent.single));

    EXPECT_INT_ZERO(span_table_get(st, &span, "part2.5", trid));
    EXPECT_INT_EQ(1, span->num_parents);
    EXPECT_TRUE(0 == htrace_span_id_compare(&part2_id, &span->parent.single));

    return EXIT_SUCCESS;
}

static struct rtest g_rtest_simple = {
    "rtest_simple",
    rtest_simple_run,
    rtest_simple_verify,
};

struct rtest * const g_rtests[] = {
    &g_rtest_simple,
    NULL
};

// vim: ts=4:sw=4:tw=79:et
