/*
This file is part of GNUnet.
Copyright (C) 2020--2021 GNUnet e.V.
GNUnet is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.
GNUnet is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
SPDX-License-Identifier: AGPL3.0-or-later
*/
/**
* @file messenger/testing_messenger_barrier.c
* @author Tobias Frisch
* @brief A simple test-case setup for the messenger service
*/
#include "testing_messenger_setup.h"
#include
#include "platform.h"
#include "gnunet_util_lib.h"
#include "gnunet_testbed_logger_service.h"
#include "gnunet_testbed_service.h"
#include "gnunet_testing_lib.h"
#include "gnunet_messenger_service.h"
#include "testing_messenger_barrier.h"
#define TEST_ROOM "test"
#define TEST_NAME "tester"
struct test_properties;
struct test_peer {
struct test_properties *props;
unsigned int num;
struct GNUNET_SCHEDULER_Task *op_task;
struct GNUNET_TESTBED_Operation *op;
struct GNUNET_TESTBED_Peer *peer;
struct GNUNET_PeerIdentity peer_id;
struct GNUNET_BarrierWaitHandle *wait;
struct GNUNET_MESSENGER_Handle *handle;
struct GNUNET_MESSENGER_Room *room;
unsigned int peer_messages;
const char *message;
};
struct test_properties {
const struct test_configuration *cfg;
unsigned int num_hosts;
struct GNUNET_SCHEDULER_Task *die_task;
struct GNUNET_SCHEDULER_Task *end_task;
struct GNUNET_BarrierHandle *barrier;
struct test_peer *peers;
unsigned int num_peer;
int status;
};
static void
shutdown_cb (void *cls)
{
struct test_properties *properties = cls;
for (unsigned int i = 0; i < properties->num_peer; i++)
{
struct test_peer *peer = &properties->peers[i];
GNUNET_assert(peer != NULL);
if (peer->op_task)
GNUNET_SCHEDULER_cancel(peer->op_task);
peer->op_task = NULL;
if (peer->op)
GNUNET_TESTBED_operation_done (peer->op);
peer->op = NULL;
if (peer->wait)
GNUNET_cancel_wait_barrier(peer->wait);
peer->wait = NULL;
if (peer->room)
GNUNET_MESSENGER_close_room (peer->room);
peer->room = NULL;
if (peer->handle)
GNUNET_MESSENGER_disconnect (peer->handle);
peer->handle = NULL;
}
if (properties->die_task)
GNUNET_SCHEDULER_cancel(properties->die_task);
properties->die_task = NULL;
properties->end_task = NULL;
if (properties->barrier)
GNUNET_cancel_barrier(properties->barrier);
properties->barrier = NULL;
}
static void
end_cb (void *cls)
{
struct test_properties *properties = cls;
GNUNET_assert(properties != NULL);
properties->die_task = NULL;
int status = 0;
for (unsigned int i = 0; i < properties->num_peer; i++)
{
struct test_peer *peer = &properties->peers[i];
GNUNET_assert(peer != NULL);
const int members = GNUNET_MESSENGER_iterate_members(peer->room, NULL, NULL);
GNUNET_assert (members >= 0);
if (peer->props->num_peer != (unsigned int) members)
{
fprintf (stderr, "Testcase failed (members: %d/%u).\n", members, peer->props->num_peer);
status = 1;
break;
}
}
GNUNET_SCHEDULER_shutdown ();
properties->status = status;
}
static void
end_badly_cb (void *cls)
{
struct test_properties *properties = cls;
GNUNET_assert(properties != NULL);
fprintf (stderr, "Testcase failed (timeout).\n");
end_cb (properties);
properties->status = 1;
}
static void
end_operation_cb (void *cls)
{
struct test_peer *peer = cls;
GNUNET_assert(peer != NULL);
peer->op_task = NULL;
fprintf (stderr, "Testcase failed (operation: '%s').\n", peer->message);
GNUNET_SCHEDULER_shutdown ();
}
static void
end_error_cb (void *cls)
{
struct test_peer *peer = cls;
GNUNET_assert(peer != NULL);
peer->op_task = NULL;
fprintf (stderr, "Testcase failed (error: '%s').\n", peer->message);
GNUNET_free (peer);
GNUNET_SCHEDULER_shutdown ();
}
static void
barrier2_wait_cb (void *cls, struct GNUNET_BarrierWaitHandle *waiting, int status)
{
struct test_peer *peer = cls;
GNUNET_assert(peer != NULL);
if (peer->wait == waiting)
peer->wait = NULL;
}
static void
barrier_wait_cb (void *cls, struct GNUNET_BarrierWaitHandle *waiting, int status)
{
struct test_peer *peer = cls;
GNUNET_assert(peer != NULL);
if (peer->wait == waiting)
peer->wait = NULL;
if (0 != (peer->props->cfg->stages[peer->num - 1] & 0x02))
{
unsigned int door = peer->props->cfg->doors[peer->num - 1];
if (door == 0)
door = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, peer->props->cfg->count);
else
door = door - 1;
struct GNUNET_HashCode hash;
GNUNET_CRYPTO_hash (TEST_ROOM, sizeof(TEST_ROOM), &hash);
struct GNUNET_MESSENGER_Room *room;
room = GNUNET_MESSENGER_enter_room(peer->handle, &(peer->props->peers[door].peer_id), &hash);
if (peer->room)
GNUNET_assert(room == peer->room);
else
GNUNET_assert(room != NULL);
peer->room = room;
}
}
/**
* Function called whenever a message is received or sent.
*
* @param cls Closure
* @param room Room
* @param sender Sender
* @param message Message
* @param hash Hash of message
* @param flags Flags of message
*/
static void
on_message (void *cls, struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Contact *sender,
const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash,
enum GNUNET_MESSENGER_MessageFlags flags)
{
struct test_peer *peer = cls;
GNUNET_assert(peer != NULL);
fprintf (stderr, "Peer: %s; [%s] Message: %s (%s)\n",
GNUNET_i2s(&(peer->peer_id)),
GNUNET_sh2s(&(message->header.sender_id)),
GNUNET_MESSENGER_name_of_kind(message->header.kind),
GNUNET_h2s(hash));
if (GNUNET_MESSENGER_KIND_PEER == message->header.kind)
peer->peer_messages++;
if (peer->props->num_hosts == peer->peer_messages)
peer->wait = GNUNET_wait_barrier (peer->props->barrier, &barrier2_wait_cb, peer);
else if (peer->props->num_hosts < peer->peer_messages)
{
if (peer->wait)
GNUNET_cancel_wait_barrier(peer->wait);
peer->wait = NULL;
if (peer->op_task)
GNUNET_SCHEDULER_cancel(peer->op_task);
peer->message = "peer";
peer->op_task = GNUNET_SCHEDULER_add_now (&end_operation_cb, peer);
}
}
static void
second_stage (void *cls)
{
struct test_peer *peer = cls;
GNUNET_assert(peer != NULL);
peer->op_task = NULL;
struct GNUNET_HashCode hash;
GNUNET_CRYPTO_hash (TEST_ROOM, sizeof(TEST_ROOM), &hash);
if (0 != (peer->props->cfg->stages[peer->num - 1] & 0x10))
{
struct GNUNET_MESSENGER_Room *room;
room = GNUNET_MESSENGER_open_room (peer->handle, &hash);
if (peer->room)
GNUNET_assert(room == peer->room);
else
GNUNET_assert(room != NULL);
peer->room = room;
}
if (0 != (peer->props->cfg->stages[peer->num - 1] & 0x20))
{
unsigned int door = peer->props->cfg->doors[peer->num - 1];
if (door == 0)
door = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, peer->props->cfg->count);
else
door = door - 1;
struct GNUNET_MESSENGER_Room *room;
room = GNUNET_MESSENGER_enter_room(peer->handle, &(peer->props->peers[door].peer_id), &hash);
if (peer->room)
GNUNET_assert(room == peer->room);
else
GNUNET_assert(room != NULL);
peer->room = room;
}
}
static void
on_peer (void *cb_cls, struct GNUNET_TESTBED_Operation *op, const struct GNUNET_TESTBED_PeerInformation *pinfo,
const char *emsg)
{
struct test_peer *peer = cb_cls;
GNUNET_assert(peer != NULL);
if (emsg)
{
peer->message = GNUNET_strdup(emsg);
peer->op_task = GNUNET_SCHEDULER_add_now (&end_error_cb, peer);
return;
}
if (!pinfo)
{
peer->message = "info";
peer->op_task = GNUNET_SCHEDULER_add_now (&end_operation_cb, peer);
return;
}
if (pinfo->pit != GNUNET_TESTBED_PIT_CONFIGURATION)
{
peer->message = "config";
peer->op_task = GNUNET_SCHEDULER_add_now (&end_operation_cb, peer);
return;
}
peer->handle = GNUNET_MESSENGER_connect (pinfo->result.cfg, TEST_NAME, NULL, NULL, &on_message, peer);
GNUNET_assert(GNUNET_OK == GNUNET_CRYPTO_get_peer_identity(
pinfo->result.cfg, &(peer->peer_id)
));
if (0 != (peer->props->cfg->stages[peer->num - 1] & 0x01))
{
struct GNUNET_HashCode hash;
GNUNET_CRYPTO_hash (TEST_ROOM, sizeof(TEST_ROOM), &hash);
peer->room = GNUNET_MESSENGER_open_room (peer->handle, &hash);
GNUNET_assert(peer->room != NULL);
}
else
peer->room = NULL;
peer->wait = GNUNET_wait_barrier (peer->props->barrier, &barrier_wait_cb, peer);
}
/**
* Main function for a peer of the testcase.
*
* @param cls Closure
* @param event Information about the event
*/
static void
run (void *cls, const struct GNUNET_TESTBED_EventInformation *event)
{
struct test_properties *properties = cls;
GNUNET_assert(properties != NULL);
if (GNUNET_TESTBED_ET_PEER_START != event->type)
{
fprintf (stderr, "Testcase failed (operation: 'start').\n");
GNUNET_SCHEDULER_shutdown ();
return;
}
struct test_peer *peer = &(properties->peers[properties->num_peer++]);
peer->props = properties;
peer->num = properties->num_peer;
peer->peer = event->details.peer_start.peer;
peer->op = GNUNET_TESTBED_peer_get_information (peer->peer, GNUNET_TESTBED_PIT_CONFIGURATION, on_peer, peer);
}
static void
barrier2_cb (void *cls, struct GNUNET_BarrierHandle *barrier, int status)
{
struct test_properties *properties = cls;
GNUNET_assert(properties != NULL);
if (properties->barrier == barrier)
properties->barrier = NULL;
if (GNUNET_SYSERR == status)
{
fprintf (stderr, "Testcase failed (operation: 'barrier2').\n");
GNUNET_SCHEDULER_shutdown ();
return;
}
else if (GNUNET_OK == status)
{
if (properties->die_task)
GNUNET_SCHEDULER_cancel(properties->die_task);
properties->die_task = GNUNET_SCHEDULER_add_delayed (
GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, properties->cfg->count),
&end_cb, properties
);
}
}
static void
barrier_cb (void *cls, struct GNUNET_BarrierHandle *barrier, int status)
{
struct test_properties *properties = cls;
GNUNET_assert(properties != NULL);
if (properties->barrier == barrier)
properties->barrier = NULL;
else if (!properties->barrier)
return;
if (properties->num_peer != properties->cfg->count)
{
fprintf (stderr, "Testcase failed (operation: 'process').\n");
GNUNET_SCHEDULER_shutdown ();
return;
}
if (GNUNET_SYSERR == status)
{
fprintf (stderr, "Testcase failed (operation: 'barrier').\n");
GNUNET_SCHEDULER_shutdown ();
return;
}
else if (GNUNET_OK == status)
{
properties->barrier = GNUNET_init_barrier (properties->num_peer, &barrier2_cb, properties);
for (unsigned int i = 0; i < properties->num_peer; i++)
properties->peers[i].op_task = GNUNET_SCHEDULER_add_now (&second_stage, &(properties->peers[i]));
}
}
static void
init (void *cls, struct GNUNET_TESTBED_RunHandle *h, unsigned int num_peers, struct GNUNET_TESTBED_Peer **peers,
unsigned int links_succeeded, unsigned int links_failed)
{
struct test_properties *properties = cls;
GNUNET_assert(properties != NULL);
properties->end_task = GNUNET_SCHEDULER_add_shutdown(&shutdown_cb, properties);
properties->die_task = GNUNET_SCHEDULER_add_delayed (
GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, properties->cfg->count * 5),
&end_badly_cb, properties
);
}
int
GNUNET_run_messenger_setup (const char* test_name, const struct test_configuration *cfg)
{
struct test_properties properties;
memset(&properties, 0, sizeof(properties));
properties.cfg = cfg;
properties.peers = GNUNET_new_array(cfg->count, struct test_peer);
for (unsigned int i = 0; i < cfg->count; i++)
if (0 != (cfg->stages[i] & 0x11))
properties.num_hosts++;
properties.status = 1;
properties.barrier = GNUNET_init_barrier (cfg->count, &barrier_cb, &properties);
if (GNUNET_OK != GNUNET_TESTBED_test_run (test_name, "test_messenger_api.conf",
cfg->count,
(1LL << GNUNET_TESTBED_ET_PEER_START),
&run, &properties,
&init, &properties))
return 1;
GNUNET_free(properties.peers);
return properties.status;
}