// Copyright (C) 2015-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
#include <cstring>
#include <future>
#include <numeric>
#include <random>
#include <algorithm>
#include <list>

#ifndef _WIN32
#include <arpa/inet.h>
#endif

#include <gtest/gtest.h>

#include <boost/asio.hpp>

#include <vsomeip/vsomeip.hpp>

#include "../../implementation/utility/include/byteorder.hpp"
#include "../../implementation/message/include/deserializer.hpp"
#include "../../implementation/message/include/serializer.hpp"
#include "../../implementation/service_discovery/include/service_discovery.hpp"
#include "../../implementation/service_discovery/include/message_impl.hpp"
#include "../../implementation/service_discovery/include/constants.hpp"
#include "../../implementation/service_discovery/include/enumeration_types.hpp"
#include "../../implementation/service_discovery/include/eventgroupentry_impl.hpp"
#include "../../implementation/service_discovery/include/serviceentry_impl.hpp"
#include "../../implementation/message/include/message_impl.hpp"
#include "../../implementation/service_discovery/include/option_impl.hpp"
#include "../../implementation/service_discovery/include/ipv4_option_impl.hpp"
#include "../../implementation/endpoints/include/tp.hpp"
#include "../../implementation/endpoints/include/tp_reassembler.hpp"
#include "../../implementation/message/include/payload_impl.hpp"

#include "someip_tp_test_globals.hpp"

static char* remote_address;
static char* local_address;

std::vector<someip_tp_test::test_mode_e> its_modes({
    someip_tp_test::test_mode_e::IN_SEQUENCE,
    someip_tp_test::test_mode_e::MIXED,
    someip_tp_test::test_mode_e::INCOMPLETE,
    someip_tp_test::test_mode_e::DUPLICATE,
    someip_tp_test::test_mode_e::OVERLAP,
    someip_tp_test::test_mode_e::OVERLAP_FRONT_BACK,
});

class someip_tp : public ::testing::TestWithParam<someip_tp_test::test_mode_e> {
public:
    someip_tp() :
        work_(std::make_shared<boost::asio::io_service::work>(io_)),
        io_thread_(std::bind(&someip_tp::io_run, this)),
        session_(0x0),
        sd_session_(0x0),
        address_remote_(boost::asio::ip::address::from_string(std::string(remote_address))),
        address_local_(boost::asio::ip::address::from_string(std::string(local_address))),
        runtime_(vsomeip::runtime::get()) {}
protected:
    void TearDown() {
        work_.reset();
        io_thread_.join();
        io_.stop();
    }

    void call_shutdown_method() {
        boost::system::error_code ec;
        std::uint8_t shutdown_call[] = {
            0x45, 0x45, 0x45, 0x01,
            0x00, 0x00, 0x00, 0x08,
            0xDD, 0xDD, 0x00, 0x01,
            0x01, 0x00, 0x00, 0x00 };
        boost::asio::ip::udp::socket::endpoint_type target_service(address_remote_,
                30001);
        boost::asio::ip::udp::socket udp_socket2(io_, boost::asio::ip::udp::v4());
        udp_socket2.send_to(boost::asio::buffer(shutdown_call), target_service);
        udp_socket2.shutdown(boost::asio::socket_base::shutdown_both, ec);
        udp_socket2.close(ec);
    }

    void io_run() {
        io_.run();
    }

    void offer_service(boost::asio::ip::udp::socket* const _udp_socket) {
        // offer the service
        std::uint8_t its_offer_service_message[] = {
            0xff, 0xff, 0x81, 0x00,
            0x00, 0x00, 0x00, 0x30, // length
            0x00, 0x00, 0x00, 0x01,
            0x01, 0x01, 0x02, 0x00,
            0xc0, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x10, // length entries array
            0x01, 0x00, 0x00, 0x20,
            0x67, 0x67, 0x00, 0x01, // service / instance
            0x00, 0xff, 0xff, 0xff, // major / ttl
            0x00, 0x00, 0x00, 0x00, // minor
            0x00, 0x00, 0x00, 0x0c, // length options array
            0x00, 0x09, 0x04, 0x00,
            0xff, 0xff, 0xff, 0xff, // slave address
            0x00, 0x11, 0x9c, 0x41,
        };
        std::memcpy(&its_offer_service_message[48], &address_local_.to_v4().to_bytes()[0], 4);
        std::uint16_t its_session = htons(++sd_session_);
        std::memcpy(&its_offer_service_message[10], &its_session, sizeof(its_session));

        boost::asio::ip::udp::socket::endpoint_type target_sd(address_remote_,30490);
        _udp_socket->send_to(boost::asio::buffer(its_offer_service_message), target_sd);
    }

    void subscribe_at_master(boost::asio::ip::udp::socket* const _udp_socket) {
        std::uint8_t its_subscription[] = {
            0xff, 0xff, 0x81, 0x00,
            0x00, 0x00, 0x00, 0x30, // length
            0x00, 0x00, 0x00, 0x01,
            0x01, 0x01, 0x02, 0x00,
            0xc0, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x10, // length entries array
            0x06, 0x00, 0x00, 0x10,
            0x45, 0x45, 0x00, 0x01, // service / instance
            0x00, 0xff, 0xff, 0xff, // major / ttl
            0x00, 0x00, 0x00, 0x01, // counter
            0x00, 0x00, 0x00, 0x0c, // length options array
            0x00, 0x09, 0x04, 0x00,
            0xff, 0xff, 0xff, 0xff, // slave address
            0x00, 0x11, 0x75, 0x31, // port 30001
        };
        std::memcpy(&its_subscription[48], &address_local_.to_v4().to_bytes()[0], 4);
        std::uint16_t its_session = htons(++sd_session_);
        std::memcpy(&its_subscription[10], &its_session, sizeof(its_session));

        boost::asio::ip::udp::socket::endpoint_type target_sd(address_remote_,30490);
        _udp_socket->send_to(boost::asio::buffer(its_subscription), target_sd);
    }

    /*
     * @brief custom version of tp::tp_split_message with adjustable segment size
     * needed to send overlapping segments within the 1392 byte segment size limit
     */
    vsomeip::tp::tp_split_messages_t split_message(const std::uint8_t * const _data,
                                             std::uint32_t _size , std::uint32_t _segment_size) {
        using namespace vsomeip::tp;
        using namespace vsomeip;
        tp_split_messages_t split_messages;

        if (_size < VSOMEIP_MAX_UDP_MESSAGE_SIZE) {
            std::cerr << __func__ << " called with size: " << std::dec << _size;
            return split_messages;
        }

        const auto data_end = _data + _size;

        for (auto current_offset = _data + 16; current_offset < data_end;) {
            auto msg = std::make_shared<message_buffer_t>();
            msg->reserve(VSOMEIP_FULL_HEADER_SIZE + sizeof(tp_header_t) + _segment_size);
            // copy the header
            msg->insert(msg->end(), _data, _data + VSOMEIP_FULL_HEADER_SIZE);
            // change the message type
            (*msg)[VSOMEIP_MESSAGE_TYPE_POS] = (*msg)[VSOMEIP_MESSAGE_TYPE_POS] | 0x20;
            // check if last segment
            const auto segment_end = current_offset + _segment_size;
            const bool is_last_segment = (segment_end >= data_end);
            // insert tp_header
            const tp_header_t header = htonl(
                    static_cast<tp_header_t>((current_offset - VSOMEIP_FULL_HEADER_SIZE - _data)) |
                    static_cast<tp_header_t>((is_last_segment) ? 0x0u : 0x1u));

            const byte_t * const headerp = reinterpret_cast<const byte_t*>(&header);
            msg->insert(msg->end(), headerp, headerp + sizeof(tp_header_t));

            // insert payload
            if (is_last_segment) {
                msg->insert(msg->end(), current_offset, data_end);
                current_offset = data_end;
            } else {
                msg->insert(msg->end(), current_offset, segment_end);
                current_offset += _segment_size;
            }
            // update length
            const length_t its_length = static_cast<length_t>(msg->size()
                                                    - VSOMEIP_SOMEIP_HEADER_SIZE);
            *(reinterpret_cast<length_t*>(&(*msg)[VSOMEIP_LENGTH_POS_MIN])) = htonl(its_length);
            split_messages.emplace_back(std::move(msg));
        }

        return split_messages;
    }

    void create_fragments(std::uint32_t _count, vsomeip::service_t _service,
                          vsomeip::instance_t _instance,
                          vsomeip::method_t _method,
                          vsomeip::message_type_e _message_type,
                          vsomeip::client_t _client,
                          vsomeip::session_t _session,
                          std::vector<vsomeip::message_buffer_ptr_t>* _target,
                          std::uint32_t _segment_size) {
        vsomeip::message_impl msg;
        msg.set_reliable(false);
        msg.set_service(_service);
        msg.set_instance(_instance);
        msg.set_method(_method);
        msg.set_message_type(_message_type);
        msg.set_return_code(vsomeip::return_code_e::E_OK);
        if (_client == vsomeip::ANY_CLIENT) {
            msg.set_client(0xDDDD);
        } else {
            msg.set_client(_client);
        }
        if (_session == 0xFFFF) {
            msg.set_session(++session_);
        } else {
            msg.set_session(_session);
        }
        std::vector<vsomeip::byte_t> its_payload_data;
        for (uint32_t i = 0; i < _count; i++) {
            its_payload_data.resize((i * _segment_size) + _segment_size, static_cast<std::uint8_t>(i));
        }
        std::shared_ptr<vsomeip::payload> payload = std::make_shared<vsomeip::payload_impl>(its_payload_data);
        msg.set_payload(payload);
        vsomeip::serializer its_serializer(0);
        msg.serialize(&its_serializer);

        *_target = split_message(its_serializer.get_data(), its_serializer.get_size(), _segment_size);
        its_serializer.reset();

    }

    vsomeip::message_buffer_t create_full_message(
            const std::vector<vsomeip::message_buffer_ptr_t>& _fragments) {
        auto its_reassembler = std::make_shared<vsomeip::tp::tp_reassembler>(
                std::numeric_limits<std::uint32_t>::max(), io_);
        vsomeip::message_buffer_t its_reassemlbed_msg;
        for (const auto& frag : _fragments) {
            const auto res =  its_reassembler->process_tp_message(&(*frag)[0],
                    std::uint32_t(frag->size()), address_local_, 12345);
            if (res.first) {
                its_reassemlbed_msg = res.second;
            }
        }
        its_reassembler->stop();
        return its_reassemlbed_msg;
    }

    std::vector<int> create_shuffled_seqeuence(std::uint32_t _count) {
        std::vector<int> its_indexes(_count);
        std::iota(its_indexes.begin(), its_indexes.end(), 0);
        std::random_device rd;
        std::mt19937 its_twister(rd());
        std::shuffle(its_indexes.begin(), its_indexes.end(), its_twister);
        return its_indexes;
    }
    void increase_segment_back(const vsomeip::message_buffer_ptr_t& _seg,
                                     std::uint32_t _amount) {
        _seg->resize(_seg->size() + _amount, 0xff);
        // update length
        *(reinterpret_cast<vsomeip::length_t*>(&((*_seg)[VSOMEIP_LENGTH_POS_MIN]))) =
                htonl(static_cast<vsomeip::length_t>(_seg->size() - VSOMEIP_SOMEIP_HEADER_SIZE));
    }

    void increase_segment_front(const vsomeip::message_buffer_ptr_t& _seg,
                                     std::uint32_t _amount) {
        // increase segment by amount
        _seg->insert(_seg->begin() + VSOMEIP_TP_PAYLOAD_POS, _amount, 0xff);

        // decrease offset by amount
        const vsomeip::tp::tp_header_t its_tp_header = VSOMEIP_BYTES_TO_LONG(
                (*_seg)[VSOMEIP_TP_HEADER_POS_MIN],
                (*_seg)[VSOMEIP_TP_HEADER_POS_MIN + 1],
                (*_seg)[VSOMEIP_TP_HEADER_POS_MIN + 2],
                (*_seg)[VSOMEIP_TP_HEADER_POS_MAX]);
        std::uint32_t its_offset = vsomeip::tp::tp::get_offset(its_tp_header);
        its_offset -= _amount;
        const vsomeip::tp::tp_header_t its_new_tp_header =
                htonl(static_cast<vsomeip::tp::tp_header_t>(its_offset |
                        static_cast<vsomeip::tp::tp_header_t>(its_tp_header & 0x1)));
        *(reinterpret_cast<vsomeip::tp::tp_header_t*>(
                &((*_seg)[VSOMEIP_TP_HEADER_POS_MIN]))) = its_new_tp_header;

        // update length
        *(reinterpret_cast<vsomeip::length_t*>(&((*_seg)[VSOMEIP_LENGTH_POS_MIN]))) =
                htonl(static_cast<vsomeip::length_t>(_seg->size() - VSOMEIP_SOMEIP_HEADER_SIZE));
    }

    void increase_segment_front_back(const vsomeip::message_buffer_ptr_t& _seg,
                                     std::uint32_t _amount) {
        increase_segment_front(_seg, _amount);
        increase_segment_back(_seg, _amount);
    }

    void decrease_segment_back(const vsomeip::message_buffer_ptr_t& _seg,
                                     std::uint32_t _amount) {
        _seg->resize(_seg->size() - _amount, 0xff);
        // update length
        *(reinterpret_cast<vsomeip::length_t*>(&((*_seg)[VSOMEIP_LENGTH_POS_MIN]))) =
                htonl(static_cast<vsomeip::length_t>(_seg->size() - VSOMEIP_SOMEIP_HEADER_SIZE));
    }

    void decrease_segment_front(const vsomeip::message_buffer_ptr_t& _seg,
                                     std::uint32_t _amount) {
        if (_amount % 16 != 0) {
            std::cerr << __func__ << ":" << __LINE__ << std::endl;
            return;
        }
        _seg->erase(_seg->begin() + VSOMEIP_TP_PAYLOAD_POS, _seg->begin() + VSOMEIP_TP_PAYLOAD_POS + _amount);
        // increase offset by amount
        const vsomeip::tp::tp_header_t its_tp_header = VSOMEIP_BYTES_TO_LONG(
                (*_seg)[VSOMEIP_TP_HEADER_POS_MIN],
                (*_seg)[VSOMEIP_TP_HEADER_POS_MIN + 1],
                (*_seg)[VSOMEIP_TP_HEADER_POS_MIN + 2],
                (*_seg)[VSOMEIP_TP_HEADER_POS_MAX]);
        std::uint32_t its_offset = vsomeip::tp::tp::get_offset(its_tp_header);
        its_offset += _amount;
        const vsomeip::tp::tp_header_t its_new_tp_header =
                htonl(static_cast<vsomeip::tp::tp_header_t>(its_offset |
                        static_cast<vsomeip::tp::tp_header_t>(its_tp_header & 0x1)));
        *(reinterpret_cast<vsomeip::tp::tp_header_t*>(
                &((*_seg)[VSOMEIP_TP_HEADER_POS_MIN]))) = its_new_tp_header;
        // update length
        *(reinterpret_cast<vsomeip::length_t*>(&((*_seg)[VSOMEIP_LENGTH_POS_MIN]))) =
                htonl(static_cast<vsomeip::length_t>(_seg->size() - VSOMEIP_SOMEIP_HEADER_SIZE));
    }

    void decrease_segment_front_back(const vsomeip::message_buffer_ptr_t& _seg,
                                     std::uint32_t _amount) {
        if (_amount % 16 != 0) {
            std::cerr << __func__ << ":" << __LINE__ << std::endl;
            return;
        }
        decrease_segment_back(_seg, _amount);
        decrease_segment_front(_seg, _amount);
    }


    enum order_e {
        ASCENDING,
        DESCENDING,
        MIXED_PREDEFINED,
        MIXED_RANDOM,
    };

    boost::asio::io_service io_;
    std::shared_ptr<boost::asio::io_service::work> work_;
    std::thread io_thread_;
    std::vector<vsomeip::message_buffer_ptr_t> fragments_request_to_master_;
    std::vector<vsomeip::message_buffer_ptr_t> fragments_response_of_master_;

    std::vector<vsomeip::message_buffer_ptr_t> fragments_received_as_server_;
    std::vector<vsomeip::message_buffer_ptr_t> fragments_response_to_master_;

    std::vector<vsomeip::message_buffer_ptr_t> fragments_event_from_master_;
    std::vector<vsomeip::message_buffer_ptr_t> fragments_event_to_master_;

    std::atomic<std::uint16_t> session_;
    std::atomic<std::uint16_t> sd_session_;
    boost::asio::ip::address address_remote_;
    boost::asio::ip::address address_local_;
    std::shared_ptr<vsomeip::runtime> runtime_;
    someip_tp_test::test_mode_e test_mode_ = GetParam();
};

INSTANTIATE_TEST_CASE_P(send_in_mode,
                        someip_tp,
                        ::testing::ValuesIn(its_modes));


/*
 * @test Send a big fragmented UDP request to the master and wait for the
 * response. Check that the received response is the same as the request (server
 * just echos the requests).
 * Wait for a big fragmented UDP message request from the master and send back
 * the response in the same size. Check that the request and response are
 * identical.
 * Do this two times one with fragments ordered ascending and one time descending.
 * Wait for the master to subscribe and send back two big, fragmented
 * notifications one with fragments ordered ascending and one descending
 * Subscribe at master and wait for one fragmented event.
 * With testmode INCOMPLETE incomplete fragments are send as well
 * With testmode MIXED instead of ascending/descedning order the fragments are
 * send in a predefined or in a random order
 */
TEST_P(someip_tp, send_in_mode)
{
    std::promise<void> remote_client_subscribed;
    std::atomic<std::uint16_t> remote_client_subscription_port(0);
    std::promise<void> offer_received;

    std::mutex udp_sd_socket_mutex;
    boost::asio::ip::udp::socket udp_sd_socket(io_,
            boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30490));

    boost::asio::ip::udp::socket udp_client_socket(io_,
            boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30001));

    boost::asio::ip::udp::socket udp_server_socket(io_,
            boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 40001));

    std::thread sd_receive_thread([&](){
        std::atomic<bool> keep_receiving(true);
        std::vector<std::uint8_t> receive_buffer(4096);
        std::vector<vsomeip::event_t> its_received_events;
        std::atomic<bool> service_offered(false);
        std::atomic<bool> client_subscribed(false);

        // join the sd multicast group 224.0.77.1
        udp_sd_socket.set_option(boost::asio::ip::multicast::join_group(
                            boost::asio::ip::address::from_string("224.0.77.1").to_v4()));
        while (keep_receiving) {
            boost::system::error_code error;
            std::size_t bytes_transferred = udp_sd_socket.receive(
                        boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error);
            if (error) {
                keep_receiving = false;
                ADD_FAILURE() << __func__ << " error: " << error.message();
                return;
            } else {
                vsomeip::deserializer its_deserializer(&receive_buffer[0], bytes_transferred, 0);
                vsomeip::service_t its_service = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_SERVICE_POS_MIN],
                                                                       receive_buffer[VSOMEIP_SERVICE_POS_MAX]);
                vsomeip::method_t its_method = VSOMEIP_BYTES_TO_WORD(receive_buffer[VSOMEIP_METHOD_POS_MIN],
                                                                     receive_buffer[VSOMEIP_METHOD_POS_MAX]);
                if (its_service == vsomeip::sd::service && its_method == vsomeip::sd::method) {
                    vsomeip::sd::message_impl sd_msg;
                    EXPECT_TRUE(sd_msg.deserialize(&its_deserializer));
                    EXPECT_EQ(1u, sd_msg.get_entries().size());
                    for (const auto& e : sd_msg.get_entries()) {
                        if (e->get_type() == vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP && !client_subscribed) {
                            EXPECT_TRUE(e->is_eventgroup_entry());
                            EXPECT_EQ(vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP, e->get_type());
                            EXPECT_EQ(1,e->get_num_options(1));
                            EXPECT_EQ(std::uint32_t(0xFFFFFF), e->get_ttl());
                            EXPECT_EQ(someip_tp_test::service_slave.service_id, e->get_service());
                            EXPECT_EQ(someip_tp_test::service_slave.instance_id, e->get_instance());
                            EXPECT_EQ(1u, sd_msg.get_options().size());
                            if (e->get_type() == vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP) {
                                std::shared_ptr<vsomeip::sd::eventgroupentry_impl> its_casted_entry =
                                        std::static_pointer_cast<vsomeip::sd::eventgroupentry_impl>(e);
                                EXPECT_EQ(someip_tp_test::service_slave.eventgroup_id,
                                          its_casted_entry->get_eventgroup());
                                std::shared_ptr<vsomeip::sd::option_impl> its_option =
                                        sd_msg.get_options().at(its_casted_entry->get_options(1)[0]);
                                EXPECT_TRUE(its_option > 0);
                                if(its_option->get_type() == vsomeip::sd::option_type_e::IP4_ENDPOINT) {
                                    std::shared_ptr<vsomeip::sd::ipv4_option_impl> its_ipv4_option =
                                            std::dynamic_pointer_cast<vsomeip::sd::ipv4_option_impl> (its_option);
                                    EXPECT_TRUE(its_ipv4_option > 0);
                                    EXPECT_EQ(vsomeip::sd::layer_four_protocol_e::UDP, its_ipv4_option->get_layer_four_protocol());
                                    EXPECT_EQ(address_remote_,
                                              boost::asio::ip::address(
                                                      boost::asio::ip::address_v4(its_ipv4_option->get_address())));
                                    remote_client_subscription_port = its_ipv4_option->get_port();
                                }
                                std::vector<vsomeip::byte_t> its_sub_ack(&receive_buffer[0], &receive_buffer[0] + VSOMEIP_FULL_HEADER_SIZE + 8 + (sd_msg.get_entries().size() * 16));
                                its_sub_ack[24] = static_cast<vsomeip::byte_t>(vsomeip::sd::entry_type_e::SUBSCRIBE_EVENTGROUP_ACK);
                                // fix length
                                const std::uint32_t its_length = htonl(static_cast<std::uint32_t>(its_sub_ack.size()) - VSOMEIP_SOMEIP_HEADER_SIZE);
                                std::memcpy(&its_sub_ack[4], &its_length, sizeof(its_length));
                                // set number of options to zero
                                its_sub_ack[27] = 0x0;
                                // update session
                                std::uint16_t its_session = htons(++sd_session_);
                                std::memcpy(&its_sub_ack[10], &its_session, sizeof(its_session));
                                boost::asio::ip::udp::socket::endpoint_type target_sd(address_remote_,30490);
                                {
                                    std::lock_guard<std::mutex> its_lock(udp_sd_socket_mutex);
                                    udp_sd_socket.send_to(boost::asio::buffer(its_sub_ack), target_sd);
                                }
                                std::cout << __LINE__ << ": master subscribed" << std::endl;
                                remote_client_subscribed.set_value();
                                client_subscribed = true;
                            }
                        } else if (e->get_type() == vsomeip::sd::entry_type_e::OFFER_SERVICE && !service_offered) {
                            EXPECT_TRUE(e->is_service_entry());
                            EXPECT_EQ(vsomeip::sd::entry_type_e::OFFER_SERVICE, e->get_type());
                            EXPECT_EQ(1u,e->get_num_options(1));
                            EXPECT_EQ(std::uint32_t(0xFFFFFF), e->get_ttl());
                            EXPECT_EQ(someip_tp_test::service.service_id, e->get_service());
                            EXPECT_EQ(someip_tp_test::service.instance_id, e->get_instance());
                            EXPECT_EQ(1u, sd_msg.get_options().size());
                            if (e->get_type() == vsomeip::sd::entry_type_e::OFFER_SERVICE) {
                                std::shared_ptr<vsomeip::sd::serviceentry_impl> its_casted_entry =
                                        std::static_pointer_cast<vsomeip::sd::serviceentry_impl>(e);
                                EXPECT_EQ(0u, its_casted_entry->get_minor_version());
                            }
                            offer_received.set_value();
                            service_offered = true;
                        }
                    }
                    if (service_offered && client_subscribed) {
                        keep_receiving = false;
                    }
                } else {
                    ADD_FAILURE() << " received non-sd message";
                    return;
                }
            }
        }
    });

    std::thread send_thread([&]() {
        boost::system::error_code ec;
        try {

            // wait until a offer was received
            if (std::future_status::timeout == offer_received.get_future().wait_for(std::chrono::seconds(10))) {
                ADD_FAILURE() << "Didn't receive offer within time";
                return;
            }

            {
                std::lock_guard<std::mutex> its_lock(udp_sd_socket_mutex);
                subscribe_at_master(&udp_sd_socket);
            }

            std::mutex all_fragments_received_mutex_;
            std::condition_variable all_fragments_received_cond_;
            bool wait_for_all_response_fragments_received_(true);
            std::uint32_t received_responses(0);
            bool wait_for_all_event_fragments_received_(true);

            std::thread udp_client_receive_thread([&]() {
                bool keep_receiving(true);
                std::vector<std::uint8_t> receive_buffer(4096);
                while (keep_receiving) {
                    boost::system::error_code error;
                    std::size_t bytes_transferred = udp_client_socket.receive(
                            boost::asio::buffer(receive_buffer, receive_buffer.capacity()), 0, error);
                    if (error) {
                        keep_receiving = false;
                        ADD_FAILURE() << __func__ << " error: " << error.message();
                        return;
                    } else {
                        std::uint32_t its_pos = 0;

                        while (bytes_transferred > 0) {
                            const std::uint32_t its_message_size = VSOMEIP_BYTES_TO_LONG(
                                    receive_buffer[its_pos + VSOMEIP_LENGTH_POS_MIN],
                                    receive_buffer[its_pos + VSOMEIP_LENGTH_POS_MIN + 1],
                                    receive_buffer[its_pos + VSOMEIP_LENGTH_POS_MIN + 2],
                                    receive_buffer[its_pos + VSOMEIP_LENGTH_POS_MIN + 3]) + VSOMEIP_SOMEIP_HEADER_SIZE;
                            std::cout << __LINE__ << ": received response " << its_message_size << std::endl;
                            vsomeip::deserializer its_deserializer(&receive_buffer[its_pos], its_message_size, 0);

                            vsomeip::service_t its_service = VSOMEIP_BYTES_TO_WORD(receive_buffer[its_pos + VSOMEIP_SERVICE_POS_MIN],
                                                                                   receive_buffer[its_pos + VSOMEIP_SERVICE_POS_MAX]);
                            vsomeip::method_t its_method = VSOMEIP_BYTES_TO_WORD(receive_buffer[its_pos + VSOMEIP_METHOD_POS_MIN],
                                                                                 receive_buffer[its_pos + VSOMEIP_METHOD_POS_MAX]);

                            vsomeip::message_impl msg;
                            EXPECT_TRUE(msg.deserialize(&its_deserializer));
                            if (msg.get_message_type() == vsomeip::message_type_e::MT_RESPONSE) {
                                EXPECT_EQ(vsomeip::message_type_e::MT_RESPONSE, msg.get_message_type());
                                EXPECT_EQ(someip_tp_test::service.service_id, msg.get_service());
                            } else if (msg.get_message_type() == vsomeip::message_type_e::MT_NOTIFICATION) {
                                std::cout << __LINE__ << ": received event" << std::endl;
                            } else if (vsomeip::tp::tp::tp_flag_is_set(receive_buffer[its_pos + VSOMEIP_MESSAGE_TYPE_POS]) &&
                                       vsomeip::tp::tp::tp_flag_unset(receive_buffer[its_pos + VSOMEIP_MESSAGE_TYPE_POS]) == vsomeip::message_type_e::MT_RESPONSE) {
                                EXPECT_EQ(someip_tp_test::service.service_id, its_service);
                                EXPECT_EQ(someip_tp_test::service.method_id, its_method);
                                auto its_buffer = std::make_shared<vsomeip::message_buffer_t>(&receive_buffer[its_pos], &receive_buffer[its_pos] + its_message_size);

                                fragments_response_of_master_.push_back(its_buffer);
                                if (fragments_response_of_master_.size() == someip_tp_test::number_of_fragments) {
                                    std::lock_guard<std::mutex> its_lock(all_fragments_received_mutex_);
                                    wait_for_all_response_fragments_received_ = false;
                                    std::cout << __LINE__ << ": received all response fragments as client" << std::endl;
                                    all_fragments_received_cond_.notify_one();
                                    if (++received_responses == 2 && !wait_for_all_event_fragments_received_) {
                                        std::cout << __LINE__ << ": received all responses as client --> Finished" << std::endl;
                                        keep_receiving = false;
                                    }
                                }
                            } else if (vsomeip::tp::tp::tp_flag_is_set(receive_buffer[its_pos + VSOMEIP_MESSAGE_TYPE_POS]) &&
                                    vsomeip::tp::tp::tp_flag_unset(receive_buffer[its_pos + VSOMEIP_MESSAGE_TYPE_POS]) == vsomeip::message_type_e::MT_NOTIFICATION) {
                                std::cout << __LINE__ << ": received event fragment" << std::endl;
                                EXPECT_EQ(someip_tp_test::service.service_id, its_service);
                                EXPECT_EQ(someip_tp_test::service.event_id, its_method);
                                auto its_buffer = std::make_shared<vsomeip::message_buffer_t>(&receive_buffer[its_pos], &receive_buffer[its_pos] + its_message_size);
                                fragments_event_from_master_.push_back(its_buffer);
                                if (fragments_event_from_master_.size() == someip_tp_test::number_of_fragments) {
                                    std::lock_guard<std::mutex> its_lock(all_fragments_received_mutex_);
                                    wait_for_all_event_fragments_received_ = false;
                                    std::cout << __LINE__ << ": received all event fragments as client --> Finished" << std::endl;
                                    all_fragments_received_cond_.notify_one();
                                    if (received_responses == 2) {
                                        keep_receiving = false;
                                    }
                                }

                            }
                            its_pos += its_message_size;
                            bytes_transferred -= its_message_size;
                        }
                    }
                }
            });

            // send SOMEI-TP message fragmented into 6 parts to service:
            boost::asio::ip::udp::socket::endpoint_type target_service(address_remote_, 30001);

            std::unique_lock<std::mutex> its_lock(all_fragments_received_mutex_);
            for (const order_e mode : {order_e::ASCENDING, order_e::DESCENDING}) {
                create_fragments(someip_tp_test::number_of_fragments, someip_tp_test::service.service_id,
                        someip_tp_test::service.instance_id,
                        someip_tp_test::service.method_id,
                        vsomeip::message_type_e::MT_REQUEST,
                        vsomeip::ANY_CLIENT, 0xffff,
                        &fragments_request_to_master_,
                        (test_mode_ == someip_tp_test::test_mode_e::OVERLAP ||
                            test_mode_ == someip_tp_test::test_mode_e::OVERLAP_FRONT_BACK) ?
                                vsomeip::tp::tp::tp_max_segment_length_ - 160 :
                                vsomeip::tp::tp::tp_max_segment_length_);
                if (mode == order_e::ASCENDING) {
                    if (test_mode_ == someip_tp_test::test_mode_e::MIXED) {
                        if (someip_tp_test::number_of_fragments != 6) {
                            ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                            return;
                        } else {
                            auto its_indexes = {4, 1, 3, 5, 2, 0};
                            std::cout << __LINE__ << ": using following predefined sequence to send request to master: ";
                            for (auto i : its_indexes) { std::cout << i << ", "; }
                            std::cout << std::endl;
                            for (int i : its_indexes) {
                                udp_client_socket.send_to(boost::asio::buffer(*fragments_request_to_master_[i]), target_service);
                            }
                        }
                    } else if (test_mode_ == someip_tp_test::test_mode_e::OVERLAP_FRONT_BACK) {
                        if (someip_tp_test::number_of_fragments != 6) {
                            ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                        } else {
                            auto its_indexes = {0,1,3,5,2,4};
                            std::cout << __LINE__ << ": using following predefined sequence to send request to master: ";
                            for (auto i : its_indexes) { std::cout << i << ", "; }
                            std::cout << std::endl;
                            // increase third segment by 16 byte at front and back
                            increase_segment_front_back(fragments_request_to_master_[2], 16);
                            increase_segment_front(fragments_request_to_master_[4], 16);
                            for (int i : its_indexes) {
                                udp_client_socket.send_to(boost::asio::buffer(*fragments_request_to_master_[i]), target_service);
                            }
                        }
                    } else if (test_mode_ == someip_tp_test::test_mode_e::DUPLICATE) {
                        if (someip_tp_test::number_of_fragments < 2) {
                            ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                            return;
                        }
                        for (auto iter = fragments_request_to_master_.begin();
                                iter != fragments_request_to_master_.end(); iter++) {
                            udp_client_socket.send_to(boost::asio::buffer(*(*iter)), target_service);
                            // send insert 2nd fragment twice
                            if (iter == fragments_request_to_master_.begin() + 1) {
                                udp_client_socket.send_to(boost::asio::buffer(*(*iter)), target_service);
                            }
                        }
                    } else {
                        if (test_mode_ == someip_tp_test::test_mode_e::INCOMPLETE) {
                            if (someip_tp_test::number_of_fragments < 3) {
                                ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                                return;
                            }
                            // send a request fragment with a different session ID first
                            vsomeip::message_buffer_t msg_incomplete(*fragments_request_to_master_[2]);
                            msg_incomplete[VSOMEIP_SESSION_POS_MIN] = 0x33;
                            msg_incomplete[VSOMEIP_SESSION_POS_MAX] = 0x33;
                            udp_client_socket.send_to(boost::asio::buffer(msg_incomplete), target_service);
                            // send a request from a different src port as well to test cleanup
                            boost::asio::ip::udp::socket udp_client_socket2(io_,
                                    boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30004));
                            msg_incomplete[VSOMEIP_SESSION_POS_MIN] = 0xcc;
                            msg_incomplete[VSOMEIP_SESSION_POS_MAX] = 0xcc;
                            udp_client_socket2.send_to(boost::asio::buffer(msg_incomplete), target_service);
                            boost::system::error_code ec;
                            udp_client_socket2.shutdown(boost::asio::socket_base::shutdown_both, ec);
                            udp_client_socket2.close(ec);
                        } else if (test_mode_ == someip_tp_test::test_mode_e::OVERLAP) {
                            if (someip_tp_test::number_of_fragments < 2) {
                                ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                                return;
                            }
                            increase_segment_back(fragments_request_to_master_[1], 16);
                        }
                        for (const auto& fragment : fragments_request_to_master_) {
                            udp_client_socket.send_to(boost::asio::buffer(*fragment), target_service);
                        }
                    }
                } else if (mode == order_e::DESCENDING) {
                    if (test_mode_ == someip_tp_test::test_mode_e::MIXED) {
                        std::vector<int> its_indexes = create_shuffled_seqeuence(someip_tp_test::number_of_fragments);
                        std::cout << __LINE__ << ": using following random sequence to send request to master: ";
                        for (auto i : its_indexes) { std::cout << i << ", "; }
                        std::cout << std::endl;
                        for (int i : its_indexes) {
                            udp_client_socket.send_to(boost::asio::buffer(*fragments_request_to_master_[i]), target_service);
                        }
                    } else if (test_mode_ == someip_tp_test::test_mode_e::OVERLAP_FRONT_BACK) {
                        if (someip_tp_test::number_of_fragments != 6) {
                            ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                        } else {
                            auto its_indexes = {5,3,2,4,1,0};
                            std::cout << __LINE__ << ": using following predefined sequence to send request to master: ";
                            for (auto i : its_indexes) { std::cout << i << ", "; }
                            std::cout << std::endl;
                            // increase third segment by 16 byte at front and back
                            increase_segment_front_back(fragments_request_to_master_[4], 16);
                            for (int i : its_indexes) {
                                udp_client_socket.send_to(boost::asio::buffer(*fragments_request_to_master_[i]), target_service);
                            }
                        }
                    } else if (test_mode_ == someip_tp_test::test_mode_e::DUPLICATE) {
                        if (someip_tp_test::number_of_fragments < 2) {
                            ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                            return;
                        }
                        for (auto iter = fragments_request_to_master_.rbegin();
                                iter != fragments_request_to_master_.rend(); iter++) {
                            udp_client_socket.send_to(boost::asio::buffer(*(*iter)), target_service);
                            // send insert 2nd last fragment twice
                            if (iter == fragments_request_to_master_.rbegin() + 1) {
                                udp_client_socket.send_to(boost::asio::buffer(*(*iter)), target_service);
                            }
                        }
                    } else {
                        if (test_mode_ == someip_tp_test::test_mode_e::INCOMPLETE) {
                            if (someip_tp_test::number_of_fragments < 4) {
                                ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                                return;
                            }
                            // send a request fragment with a different session ID first
                            vsomeip::message_buffer_t msg_incomplete(*fragments_request_to_master_[3]);
                            msg_incomplete[VSOMEIP_SESSION_POS_MIN] = 0x77;
                            msg_incomplete[VSOMEIP_SESSION_POS_MAX] = 0x77;
                            udp_client_socket.send_to(boost::asio::buffer(msg_incomplete), target_service);

                            // send a request from a different src port as well to test cleanup
                            boost::asio::ip::udp::socket udp_client_socket2(io_,
                                    boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30005));
                            msg_incomplete[VSOMEIP_SESSION_POS_MIN] = 0xdd;
                            msg_incomplete[VSOMEIP_SESSION_POS_MAX] = 0xdd;
                            udp_client_socket2.send_to(boost::asio::buffer(msg_incomplete), target_service);
                            boost::system::error_code ec;
                            udp_client_socket2.shutdown(boost::asio::socket_base::shutdown_both, ec);
                            udp_client_socket2.close(ec);
                        } else if (test_mode_ == someip_tp_test::test_mode_e::OVERLAP) {
                            if (someip_tp_test::number_of_fragments < 5) {
                                ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                                return;
                            }
                            // increase second last segment by 16 byte
                            increase_segment_back(fragments_request_to_master_[4], 16);
                        }
                        for (auto iter = fragments_request_to_master_.rbegin(); iter != fragments_request_to_master_.rend(); iter++) {
                            udp_client_socket.send_to(boost::asio::buffer(*(*iter)), target_service);
                        }
                    }
                }
                {
                    while (wait_for_all_response_fragments_received_) {
                        if (std::cv_status::timeout ==
                                all_fragments_received_cond_.wait_for(its_lock,
                                        std::chrono::seconds(20))) {
                            ADD_FAILURE() << "Didn't receive response to"
                                    " fragmented message within time: " << std::uint32_t(mode);
                            return;
                        } else {
                            EXPECT_EQ(someip_tp_test::number_of_fragments, fragments_request_to_master_.size());
                            // create complete message from request
                            if (test_mode_ == someip_tp_test::test_mode_e::OVERLAP) {
                                if (mode == ASCENDING) {
                                    if (someip_tp_test::number_of_fragments < 2) {
                                        ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                                        return;
                                    }
                                    // decrease second segment by 16 byte
                                    decrease_segment_back(fragments_request_to_master_[1], 16);
                                } else if (mode == DESCENDING) {
                                    if (someip_tp_test::number_of_fragments < 5) {
                                        ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                                        return;
                                    }
                                    // decrease fourth segment by 16 byte
                                    decrease_segment_back(fragments_request_to_master_[4], 16);
                                }
                            } else if (test_mode_ == someip_tp_test::test_mode_e::OVERLAP_FRONT_BACK) {
                                // remove the additional inserted bytes which weren't accepted on
                                // test masterside as they were overlapping
                                if (mode == ASCENDING) {
                                    decrease_segment_front_back(fragments_request_to_master_[2], 16);
                                    decrease_segment_front(fragments_request_to_master_[4], 16);
                                } else {
                                    decrease_segment_front_back(fragments_request_to_master_[4], 16);
                                }
                            }
                            vsomeip::message_buffer_t its_request = create_full_message(fragments_request_to_master_);
                            if (test_mode_ == someip_tp_test::test_mode_e::OVERLAP ||
                                test_mode_ == someip_tp_test::test_mode_e::OVERLAP_FRONT_BACK) {
                                EXPECT_EQ(VSOMEIP_FULL_HEADER_SIZE +
                                          someip_tp_test::number_of_fragments * (someip_tp_test::max_segment_size - 160),
                                          its_request.size());
                            } else {
                                EXPECT_EQ(VSOMEIP_FULL_HEADER_SIZE +
                                          someip_tp_test::number_of_fragments * someip_tp_test::max_segment_size,
                                          its_request.size());
                            }
                            if (test_mode_ == someip_tp_test::test_mode_e::OVERLAP && mode == ASCENDING) {
                                // response contains the additional 16 bytes of 2nd fragment instead
                                // of beginning of the 3rd fragment
                                for (std::uint32_t i = 0; i < 16; i++) {
                                    its_request[VSOMEIP_PAYLOAD_POS + 2 * (someip_tp_test::max_segment_size - 160) + i] = 0xff;
                                }
                            }

                            // create complete message from response
                            vsomeip::message_buffer_t its_response = create_full_message(fragments_response_of_master_);
                            if (test_mode_ == someip_tp_test::test_mode_e::OVERLAP ||
                                test_mode_ == someip_tp_test::test_mode_e::OVERLAP_FRONT_BACK) {
                                EXPECT_EQ(VSOMEIP_FULL_HEADER_SIZE +
                                          someip_tp_test::number_of_fragments * (someip_tp_test::max_segment_size - 160),
                                          its_response.size());
                            } else {
                                EXPECT_EQ(VSOMEIP_FULL_HEADER_SIZE +
                                          someip_tp_test::number_of_fragments * someip_tp_test::max_segment_size,
                                          its_response.size());
                            }
                            // change message type of response to request again
                            its_response[VSOMEIP_MESSAGE_TYPE_POS] = static_cast<vsomeip::byte_t>(vsomeip::message_type_e::MT_REQUEST);
                            // request and response should now be equal
                            EXPECT_EQ(its_response.size(), its_request.size());
                            EXPECT_EQ(its_response, its_request);
                            EXPECT_EQ(0, std::memcmp(static_cast<void*>(&its_response[0]),
                                                     static_cast<void*>(&its_request[0]),
                                                     its_response.size()));
                            fragments_response_of_master_.clear();
                        }
                    }
                    wait_for_all_response_fragments_received_ = true;
                }
                fragments_request_to_master_.clear();
            }

            while (wait_for_all_event_fragments_received_) {
                if (std::cv_status::timeout ==
                         all_fragments_received_cond_.wait_for(its_lock,
                                 std::chrono::seconds(20))) {
                     ADD_FAILURE() << "Didn't receive fragmented event from "
                             " master within time";
                 }
            }
            // check if received event is correct
            {
                EXPECT_EQ(someip_tp_test::number_of_fragments, fragments_event_from_master_.size());
                // create complete message from event
                vsomeip::message_buffer_t its_event = create_full_message(fragments_event_from_master_);
                vsomeip::session_t its_event_session =
                        VSOMEIP_BYTES_TO_WORD(its_event[VSOMEIP_SESSION_POS_MIN],
                                              its_event[VSOMEIP_SESSION_POS_MAX]);

                std::vector<vsomeip::message_buffer_ptr_t> its_cmp_event_fragments;
                create_fragments(someip_tp_test::number_of_fragments,
                                 someip_tp_test::service.service_id,
                                 someip_tp_test::service.instance_id,
                                 someip_tp_test::service.event_id,
                                 vsomeip::message_type_e::MT_NOTIFICATION,
                                 0x0, its_event_session, &its_cmp_event_fragments,
                                 (test_mode_ == someip_tp_test::test_mode_e::OVERLAP) ?
                                     vsomeip::tp::tp::tp_max_segment_length_ - 160 :
                                     vsomeip::tp::tp::tp_max_segment_length_);
                vsomeip::message_buffer_t its_cmp_event = create_full_message(its_cmp_event_fragments);
                EXPECT_EQ(its_cmp_event.size(), its_event.size());
                EXPECT_EQ(its_cmp_event, its_event);
                EXPECT_EQ(0, std::memcmp(static_cast<void*>(&its_cmp_event[0]),
                                         static_cast<void*>(&its_event[0]),
                                         its_cmp_event.size()));
            }
            its_lock.unlock();
            udp_client_receive_thread.join();
        } catch (const std::exception& _e) {
            ADD_FAILURE() << "catched exception: " << _e.what();
        }
    });

    std::mutex all_fragments_received_as_server_mutex_;
    std::unique_lock<std::mutex> all_fragments_received_as_server_lock(all_fragments_received_as_server_mutex_);
    std::condition_variable all_fragments_received_as_server_cond_;
    bool wait_for_all_fragments_received_as_server_(true);
    std::atomic<std::uint16_t> remote_client_request_port(0);

    std::thread udp_server_send_thread([&]() {
        // wait until client subscribed
        if (std::future_status::timeout == remote_client_subscribed.get_future().wait_for(std::chrono::seconds(10))) {
            ADD_FAILURE() << "Client didn't subscribe within time";
            return;
        }

        // send fragmented event to the master
        boost::asio::ip::udp::socket::endpoint_type master_client(address_remote_, remote_client_subscription_port);
        for (const order_e mode : {order_e::ASCENDING, order_e::DESCENDING}) {
            create_fragments(someip_tp_test::number_of_fragments,
                    someip_tp_test::service_slave.service_id,
                    someip_tp_test::service_slave.instance_id,
                    someip_tp_test::service_slave.event_id,
                    vsomeip::message_type_e::MT_NOTIFICATION,
                    vsomeip::ANY_CLIENT, 0xffff,
                    &fragments_event_to_master_,
                    (test_mode_ == someip_tp_test::test_mode_e::OVERLAP ||
                            test_mode_ == someip_tp_test::test_mode_e::OVERLAP_FRONT_BACK) ?
                            vsomeip::tp::tp::tp_max_segment_length_ - 160 :
                            vsomeip::tp::tp::tp_max_segment_length_);
            if (mode == order_e::ASCENDING) {
                if (test_mode_ == someip_tp_test::test_mode_e::MIXED) {
                    if (someip_tp_test::number_of_fragments != 6) {
                        ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                    } else {
                        auto its_indexes = {2, 3, 5, 1, 4, 0};
                        std::cout << __LINE__ << ": using following predefined sequence to send event to master: ";
                        for (auto i : its_indexes) { std::cout << i << ", "; }
                        std::cout << std::endl;
                        for (int i : its_indexes) {
                            udp_server_socket.send_to(boost::asio::buffer(*fragments_event_to_master_[i]), master_client);
                        }
                    }
                } else if (test_mode_ == someip_tp_test::test_mode_e::OVERLAP_FRONT_BACK) {
                    if (someip_tp_test::number_of_fragments != 6) {
                        ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                    } else {
                        auto its_indexes = {0,2,4,5,1,3};
                        std::cout << __LINE__ << ": using following predefined sequence to send event to master: ";
                        for (auto i : its_indexes) { std::cout << i << ", "; }
                        std::cout << std::endl;
                        // increase second segment by 16 byte at front and back
                        increase_segment_front_back(fragments_event_to_master_[1], 16);
                        increase_segment_front(fragments_event_to_master_[3], 16);

                        for (int i : its_indexes) {
                            udp_server_socket.send_to(boost::asio::buffer(*fragments_event_to_master_[i]), master_client);
                        }
                    }
                } else if (test_mode_ == someip_tp_test::test_mode_e::DUPLICATE) {
                    if (someip_tp_test::number_of_fragments < 2) {
                        ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                        return;
                    }
                    for (auto iter = fragments_event_to_master_.begin();
                            iter != fragments_event_to_master_.end(); iter++) {
                        udp_server_socket.send_to(boost::asio::buffer(*(*iter)), master_client);
                        // send insert 2nd fragment twice
                        if (iter == fragments_event_to_master_.begin() + 1) {
                            udp_server_socket.send_to(boost::asio::buffer(*(*iter)), master_client);
                            // send oversized fragment as well
                            increase_segment_back(*iter, 4);
                            udp_server_socket.send_to(boost::asio::buffer(*(*iter)), master_client);
                            decrease_segment_back(*iter, 4);
                        }
                    }
                } else {
                    if (test_mode_ == someip_tp_test::test_mode_e::INCOMPLETE) {
                        if (someip_tp_test::number_of_fragments < 3) {
                            ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                            return;
                        }
                        // send an event fragment with a different session ID first
                        vsomeip::message_buffer_t msg_incomplete(*fragments_event_to_master_[2]);
                        msg_incomplete[VSOMEIP_SESSION_POS_MIN] = 0x44;
                        msg_incomplete[VSOMEIP_SESSION_POS_MAX] = 0x44;
                        udp_server_socket.send_to(boost::asio::buffer(msg_incomplete), master_client);
                        // send a request with a different service ID as well to test cleanup
                        msg_incomplete[VSOMEIP_SERVICE_POS_MIN] = 0xdd;
                        msg_incomplete[VSOMEIP_SERVICE_POS_MAX] = 0xdd;
                        msg_incomplete[VSOMEIP_SESSION_POS_MIN] = 0xdd;
                        msg_incomplete[VSOMEIP_SESSION_POS_MAX] = 0xdd;
                        udp_server_socket.send_to(boost::asio::buffer(msg_incomplete), master_client);
                    } else if (test_mode_ == someip_tp_test::test_mode_e::OVERLAP) {
                        if (someip_tp_test::number_of_fragments < 2) {
                            ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                            return;
                        }
                        // increase second segment by 16 byte
                        increase_segment_back(fragments_event_to_master_[1], 16);

                        // send one oversize message as well
                        std::vector<vsomeip::message_buffer_ptr_t> oversized_event;
                        create_fragments(someip_tp_test::number_of_fragments + 1,
                                someip_tp_test::service_slave.service_id,
                                someip_tp_test::service_slave.instance_id,
                                someip_tp_test::service_slave.event_id,
                                vsomeip::message_type_e::MT_NOTIFICATION,
                                vsomeip::ANY_CLIENT, 0xffff,
                                &oversized_event,
                                vsomeip::tp::tp::tp_max_segment_length_);
                        for (const auto& fragment : oversized_event) {
                            udp_server_socket.send_to(boost::asio::buffer(*fragment), master_client);
                        }
                    }
                    for (const auto& fragment : fragments_event_to_master_) {
                        udp_server_socket.send_to(boost::asio::buffer(*fragment), master_client);
                    }
                }
            } else if (mode == order_e::DESCENDING) {
                if (test_mode_ == someip_tp_test::test_mode_e::MIXED) {
                    std::vector<int> its_indexes = create_shuffled_seqeuence(someip_tp_test::number_of_fragments);
                    std::cout << __LINE__ << ": using following random sequence to send event to master: ";
                    for (auto i : its_indexes) { std::cout << i << ", "; }
                    std::cout << std::endl;
                    for ( int i : its_indexes) {
                        udp_server_socket.send_to(boost::asio::buffer(*fragments_event_to_master_[i]), master_client);
                    }
                } else if (test_mode_ == someip_tp_test::test_mode_e::OVERLAP_FRONT_BACK) {
                    if (someip_tp_test::number_of_fragments != 6) {
                        ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                    } else {
                        auto its_indexes = {5,3,2,1,0,4};
                        std::cout << __LINE__ << ": using following predefined sequence to send event to master: ";
                        for (auto i : its_indexes) { std::cout << i << ", "; }
                        std::cout << std::endl;
                        // increase second last segment by 16 byte at front and back
                        increase_segment_front_back(fragments_event_to_master_[4], 16);
                        // update length
                        *(reinterpret_cast<vsomeip::length_t*>(&((*fragments_event_to_master_[4])[VSOMEIP_LENGTH_POS_MIN]))) =
                                htonl(static_cast<vsomeip::length_t>(fragments_event_to_master_[4]->size() - VSOMEIP_SOMEIP_HEADER_SIZE));
                        for (int i : its_indexes) {
                            udp_server_socket.send_to(boost::asio::buffer(*fragments_event_to_master_[i]), master_client);
                        }
                    }
                } else if (test_mode_ == someip_tp_test::test_mode_e::DUPLICATE) {
                    if (someip_tp_test::number_of_fragments < 2) {
                        ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                        return;
                    }
                    for (auto iter = fragments_event_to_master_.rbegin();
                            iter != fragments_event_to_master_.rend(); iter++) {
                        udp_server_socket.send_to(boost::asio::buffer(*(*iter)), master_client);
                        // send insert 2nd last fragment twice
                        if (iter == fragments_event_to_master_.rbegin() + 1) {
                            udp_server_socket.send_to(boost::asio::buffer(*(*iter)), master_client);
                        }
                    }
                } else {
                    if (test_mode_ == someip_tp_test::test_mode_e::INCOMPLETE) {
                        if (someip_tp_test::number_of_fragments < 4) {
                            ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                            return;
                        }
                        // send an event fragment with a different session ID first
                        vsomeip::message_buffer_t msg_incomplete(*fragments_event_to_master_[3]);
                        msg_incomplete[VSOMEIP_SESSION_POS_MIN] = 0x55;
                        msg_incomplete[VSOMEIP_SESSION_POS_MAX] = 0x55;
                        udp_server_socket.send_to(boost::asio::buffer(msg_incomplete), master_client);
                        // send a request with a different service ID as well to test cleanup
                        msg_incomplete[VSOMEIP_SERVICE_POS_MIN] = 0xbb;
                        msg_incomplete[VSOMEIP_SERVICE_POS_MAX] = 0xbb;
                        msg_incomplete[VSOMEIP_SESSION_POS_MIN] = 0xbb;
                        msg_incomplete[VSOMEIP_SESSION_POS_MAX] = 0xbb;
                        udp_server_socket.send_to(boost::asio::buffer(msg_incomplete), master_client);

                    } else if (test_mode_ == someip_tp_test::test_mode_e::OVERLAP) {
                        if (someip_tp_test::number_of_fragments < 5) {
                            ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                            return;
                        }
                        // increase second last segment by 16 byte
                        increase_segment_back(fragments_event_to_master_[4], 16);
                    }
                    for (auto iter = fragments_event_to_master_.rbegin(); iter != fragments_event_to_master_.rend(); iter++) {
                        udp_server_socket.send_to(boost::asio::buffer(*(*iter)), master_client);
                    }
                }
            }
            std::cout << __LINE__ << ": send event to master " << std::uint32_t(mode) << std::endl;
        }

        for (const order_e mode : {order_e::ASCENDING, order_e::DESCENDING}) {
            while (wait_for_all_fragments_received_as_server_) {
                if (std::cv_status::timeout ==
                        all_fragments_received_as_server_cond_.wait_for(all_fragments_received_as_server_lock,
                                std::chrono::seconds(20))) {
                    ADD_FAILURE() << "Didn't receive request from client within time: " << std::uint32_t(mode);
                    return;
                } else {
                    EXPECT_EQ(someip_tp_test::number_of_fragments, fragments_received_as_server_.size());
                    // create complete message from request of client
                    vsomeip::message_buffer_t its_request = create_full_message(fragments_received_as_server_);
                    if (test_mode_ == someip_tp_test::test_mode_e::OVERLAP ||
                            test_mode_ == someip_tp_test::test_mode_e::OVERLAP_FRONT_BACK) {
                        EXPECT_EQ(VSOMEIP_FULL_HEADER_SIZE +
                                  someip_tp_test::number_of_fragments * (someip_tp_test::max_segment_size - 160),
                                  its_request.size());
                    } else {
                        EXPECT_EQ(VSOMEIP_FULL_HEADER_SIZE +
                                  someip_tp_test::number_of_fragments * someip_tp_test::max_segment_size,
                                  its_request.size());
                    }
                    const vsomeip::client_t its_request_client =
                            VSOMEIP_BYTES_TO_WORD(its_request[VSOMEIP_CLIENT_POS_MIN],
                                                  its_request[VSOMEIP_CLIENT_POS_MAX]);
                    const vsomeip::session_t its_request_session =
                            VSOMEIP_BYTES_TO_WORD(its_request[VSOMEIP_SESSION_POS_MIN],
                                                  its_request[VSOMEIP_SESSION_POS_MAX]);
                    create_fragments(someip_tp_test::number_of_fragments,
                                     someip_tp_test::service_slave.service_id,
                                     someip_tp_test::service_slave.instance_id,
                                     someip_tp_test::service_slave.method_id,
                                     vsomeip::message_type_e::MT_RESPONSE,
                                     its_request_client,
                                     its_request_session,
                                     &fragments_response_to_master_,
                                     (test_mode_ == someip_tp_test::test_mode_e::OVERLAP ||
                                             test_mode_ == someip_tp_test::test_mode_e::OVERLAP_FRONT_BACK) ?
                                             vsomeip::tp::tp::tp_max_segment_length_ - 160:
                                             vsomeip::tp::tp::tp_max_segment_length_);
                    // create complete message from response
                    vsomeip::message_buffer_t its_response = create_full_message(fragments_response_to_master_);
                    // change the message type of the response to request for comparison
                    its_response[VSOMEIP_MESSAGE_TYPE_POS] = static_cast<vsomeip::byte_t>(vsomeip::message_type_e::MT_REQUEST);

                    EXPECT_EQ(its_response.size(), its_request.size());
                    EXPECT_EQ(its_response, its_request);
                    EXPECT_EQ(0, std::memcmp(static_cast<void*>(&its_response[0]),
                                             static_cast<void*>(&its_request[0]),
                                             its_response.size()));
                    // send back response
                    fragments_received_as_server_.clear();
                    EXPECT_GT(remote_client_request_port, 0);
                    boost::asio::ip::udp::socket::endpoint_type master_client(address_remote_, remote_client_request_port);
                    if (mode == order_e::ASCENDING) {
                        if (test_mode_ == someip_tp_test::test_mode_e::MIXED) {
                            if (someip_tp_test::number_of_fragments != 6) {
                                ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                            } else {
                                auto its_indexes = {4,2,0,1,3,5};
                                std::cout << __LINE__ << ": using following predefined sequence to send back response to master: ";
                                for (auto i : its_indexes) { std::cout << i << ", "; }
                                std::cout << std::endl;
                                for (int i : its_indexes) {
                                    udp_server_socket.send_to(boost::asio::buffer(*fragments_response_to_master_[i]), master_client);
                                }
                            }
                        } else if (test_mode_ == someip_tp_test::test_mode_e::OVERLAP_FRONT_BACK) {
                            if (someip_tp_test::number_of_fragments != 6) {
                                ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                            } else {
                                auto its_indexes = {0,2,4,3,5,1};
                                std::cout << __LINE__ << ": using following predefined sequence to send response to master: ";
                                for (auto i : its_indexes) { std::cout << i << ", "; }
                                std::cout << std::endl;
                                // increase fourth segment by 16 byte at front and back
                                increase_segment_front_back(fragments_response_to_master_[3], 16);
                                increase_segment_front(fragments_response_to_master_[1], 16);
                                for (int i : its_indexes) {
                                    udp_server_socket.send_to(boost::asio::buffer(*fragments_response_to_master_[i]), master_client);
                                }
                            }
                        } else if (test_mode_ == someip_tp_test::test_mode_e::DUPLICATE) {
                            if (someip_tp_test::number_of_fragments < 2) {
                                ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                                return;
                            }
                            for (auto iter = fragments_response_to_master_.begin();
                                    iter != fragments_response_to_master_.end(); iter++) {
                                udp_server_socket.send_to(boost::asio::buffer(*(*iter)), master_client);
                                // send 2nd fragment twice
                                if (iter == fragments_response_to_master_.begin() + 1) {
                                    udp_server_socket.send_to(boost::asio::buffer(*(*iter)), master_client);
                                    // send a fragment with invalid segment size as well
                                    decrease_segment_back(*iter, 16);
                                    increase_segment_back(*iter, 7);
                                    udp_server_socket.send_to(boost::asio::buffer(*(*iter)), master_client);
                                    increase_segment_back(*iter, 9);
                                }
                            }
                        } else {
                            if (test_mode_ == someip_tp_test::test_mode_e::INCOMPLETE) {
                                if (someip_tp_test::number_of_fragments < 5) {
                                    ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                                    return;
                                }
                                // send an event fragment with a different session ID first
                                vsomeip::message_buffer_t msg_incomplete(*fragments_response_to_master_[4]);
                                msg_incomplete[VSOMEIP_SESSION_POS_MIN] = 0x99;
                                msg_incomplete[VSOMEIP_SESSION_POS_MAX] = 0x99;
                                udp_server_socket.send_to(boost::asio::buffer(msg_incomplete), master_client);
                                // send a request with a different service ID as well to test cleanup
                                msg_incomplete[VSOMEIP_SERVICE_POS_MIN] = 0xaa;
                                msg_incomplete[VSOMEIP_SERVICE_POS_MAX] = 0xaa;
                                msg_incomplete[VSOMEIP_SESSION_POS_MIN] = 0xaa;
                                msg_incomplete[VSOMEIP_SESSION_POS_MAX] = 0xaa;
                                udp_server_socket.send_to(boost::asio::buffer(msg_incomplete), master_client);
                            } else if (test_mode_ == someip_tp_test::test_mode_e::OVERLAP) {
                                if (someip_tp_test::number_of_fragments < 2) {
                                    ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                                    return;
                                }
                                // increase second segment by 16 byte
                                increase_segment_back(fragments_response_to_master_[1], 16);
                            }
                            for (const auto& frag : fragments_response_to_master_) {
                                udp_server_socket.send_to(boost::asio::buffer(*frag), master_client);
                            }
                        }
                    } else if (mode == order_e::DESCENDING) {
                        if (test_mode_ == someip_tp_test::test_mode_e::MIXED) {
                            std::vector<int> its_indexes = create_shuffled_seqeuence(someip_tp_test::number_of_fragments);
                            std::cout << __LINE__ << ": using following random sequence to send back response to master: ";
                            for (auto i : its_indexes) { std::cout << i << ", "; }
                            std::cout << std::endl;
                            for ( int i : its_indexes) {
                                udp_server_socket.send_to(boost::asio::buffer(*fragments_response_to_master_[i]), master_client);
                            }
                        } else if (test_mode_ == someip_tp_test::test_mode_e::OVERLAP_FRONT_BACK) {
                            if (someip_tp_test::number_of_fragments != 6) {
                                ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                            } else {
                                auto its_indexes = {5,3,2,1,4,0};
                                std::cout << __LINE__ << ": using following predefined sequence to send response to master: ";
                                for (auto i : its_indexes) { std::cout << i << ", "; }
                                std::cout << std::endl;
                                // increase fith segment by 16 byte at front and back
                                increase_segment_front_back(fragments_response_to_master_[4], 16);
                                for (int i : its_indexes) {
                                    udp_server_socket.send_to(boost::asio::buffer(*fragments_response_to_master_[i]), master_client);
                                }
                            }
                        } else if (test_mode_ == someip_tp_test::test_mode_e::DUPLICATE) {
                            if (someip_tp_test::number_of_fragments < 2) {
                                ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                                return;
                            }
                            for (auto iter = fragments_response_to_master_.rbegin();
                                    iter != fragments_response_to_master_.rend(); iter++) {
                                udp_server_socket.send_to(boost::asio::buffer(*(*iter)), master_client);
                                // send insert 2nd last fragment twice
                                if (iter == fragments_response_to_master_.rbegin() + 1) {
                                    udp_server_socket.send_to(boost::asio::buffer(*(*iter)), master_client);
                                }
                            }
                        } else {
                            if (test_mode_ == someip_tp_test::test_mode_e::INCOMPLETE) {
                                if (someip_tp_test::number_of_fragments < 4) {
                                    ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                                    return;
                                }
                                // send an event fragment with a different session ID first
                                vsomeip::message_buffer_t msg_incomplete(*fragments_response_to_master_[3]);
                                msg_incomplete[VSOMEIP_SESSION_POS_MIN] = 0x66;
                                msg_incomplete[VSOMEIP_SESSION_POS_MAX] = 0x66;
                                udp_server_socket.send_to(boost::asio::buffer(msg_incomplete), master_client);
                                // send a request with a different service ID as well to test cleanup
                                msg_incomplete[VSOMEIP_SERVICE_POS_MIN] = 0xef;
                                msg_incomplete[VSOMEIP_SERVICE_POS_MAX] = 0xef;
                                msg_incomplete[VSOMEIP_SESSION_POS_MIN] = 0xef;
                                msg_incomplete[VSOMEIP_SESSION_POS_MAX] = 0xef;
                                udp_server_socket.send_to(boost::asio::buffer(msg_incomplete), master_client);
                            } else if (test_mode_ == someip_tp_test::test_mode_e::OVERLAP) {
                                if (someip_tp_test::number_of_fragments < 5) {
                                    ADD_FAILURE() << "line: " << __LINE__ << " needs adaption as number_of_fragments changed";
                                    return;
                                }
                                // increase second last segment by 16 byte
                                increase_segment_back(fragments_response_to_master_[4], 16);
                            }
                            for (auto iter = fragments_response_to_master_.rbegin();
                                      iter != fragments_response_to_master_.rend(); iter++) {
                                udp_server_socket.send_to(boost::asio::buffer(*(*iter)), master_client);
                            }
                        }
                    }
                }
            }
            wait_for_all_fragments_received_as_server_ = true;
        }
    });

    std::thread udp_server_receive_thread([&]() {
        {
            std::lock_guard<std::mutex> its_lock(udp_sd_socket_mutex);
            offer_service(&udp_sd_socket);
        }

        bool keep_receiving(true);
        std::vector<std::uint8_t> receive_buffer(4096);
        while (keep_receiving) {
            boost::system::error_code error;
            boost::asio::ip::udp::socket::endpoint_type its_remote_endpoint;
            std::size_t bytes_transferred = udp_server_socket.receive_from(
                    boost::asio::buffer(receive_buffer, receive_buffer.capacity()), its_remote_endpoint, 0, error);
            if (error) {
                keep_receiving = false;
                ADD_FAILURE() << __func__ << " error: " << error.message();
                return;
            } else {
                remote_client_request_port = its_remote_endpoint.port();
                std::uint32_t its_pos = 0;
                while (bytes_transferred > 0) {
                    const std::uint32_t its_message_size = VSOMEIP_BYTES_TO_LONG(
                            receive_buffer[its_pos + VSOMEIP_LENGTH_POS_MIN],
                            receive_buffer[its_pos + VSOMEIP_LENGTH_POS_MIN + 1],
                            receive_buffer[its_pos + VSOMEIP_LENGTH_POS_MIN + 2],
                            receive_buffer[its_pos + VSOMEIP_LENGTH_POS_MIN + 3]) + VSOMEIP_SOMEIP_HEADER_SIZE;
                    std::cout << __LINE__ << ": received request from master " << its_message_size << std::endl;
                    vsomeip::deserializer its_deserializer(&receive_buffer[its_pos], its_message_size, 0);

                    vsomeip::service_t its_service = VSOMEIP_BYTES_TO_WORD(receive_buffer[its_pos + VSOMEIP_SERVICE_POS_MIN],
                                                                           receive_buffer[its_pos + VSOMEIP_SERVICE_POS_MAX]);
                    vsomeip::method_t its_method = VSOMEIP_BYTES_TO_WORD(receive_buffer[its_pos + VSOMEIP_METHOD_POS_MIN],
                                                                         receive_buffer[its_pos + VSOMEIP_METHOD_POS_MAX]);
                    EXPECT_EQ(someip_tp_test::service_slave.service_id, its_service);
                    EXPECT_EQ(someip_tp_test::service_slave.method_id, its_method);
                    vsomeip::message_impl msg;
                    EXPECT_TRUE(msg.deserialize(&its_deserializer));
                    if (vsomeip::tp::tp::tp_flag_is_set(receive_buffer[its_pos + VSOMEIP_MESSAGE_TYPE_POS])) {
                        auto its_buffer = std::make_shared<vsomeip::message_buffer_t>(&receive_buffer[its_pos], &receive_buffer[its_pos] + its_message_size);

                        fragments_received_as_server_.push_back(its_buffer);
                        if (fragments_received_as_server_.size() == someip_tp_test::number_of_fragments) {
                            std::lock_guard<std::mutex> its_lock(all_fragments_received_as_server_mutex_);
                            wait_for_all_fragments_received_as_server_ = false;
                            std::cout << __LINE__ << ": received all fragments as server" << std::endl;
                            all_fragments_received_as_server_cond_.notify_one();
                            static int received_requests = 0;
                            if (++received_requests == 2) {
                                std::cout << __LINE__ << ": received all requests as server --> Finished" << std::endl;
                                keep_receiving = false;
                            }
                        }
                    }
                    its_pos += its_message_size;
                    bytes_transferred -= its_message_size;
                }
            }
        }
    });

    send_thread.join();
    sd_receive_thread.join();
    udp_server_receive_thread.join();
    udp_server_send_thread.join();

    if (test_mode_ == someip_tp_test::test_mode_e::INCOMPLETE) {
        std::cout << "Sleeping to let cleanup for unfinished TP message "
                "trigger on master side..." << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(11));
    }
    // shutdown the server
    call_shutdown_method();

    boost::system::error_code ec;
    udp_sd_socket.shutdown(boost::asio::socket_base::shutdown_both, ec);
    udp_sd_socket.close(ec);
    udp_client_socket.shutdown(boost::asio::socket_base::shutdown_both, ec);
    udp_client_socket.close(ec);
    udp_server_socket.shutdown(boost::asio::socket_base::shutdown_both, ec);
    udp_server_socket.close(ec);
}

#ifndef _WIN32
int main(int argc, char** argv) {
    ::testing::InitGoogleTest(&argc, argv);
    if(argc < 3) {
        std::cerr << "Please pass an target, local IP address and test mode to this binary like: "
                << argv[0] << " 10.0.3.1 10.0.3.202 TP_IN_SEQUENCE" << std::endl;
        std::cerr << "Testmodes are [ IN_SEQUENCE, MIXED, INCOMPLETE, OVERLAP, OVERLAP_FRONT_BACK ]" << std::endl;
    } else {
        remote_address = argv[1];
        local_address = argv[2];
        std::string its_testmode = argv[3];
        if (its_testmode == std::string("IN_SEQUENCE")) {
            ::testing::GTEST_FLAG(filter) = "*send_in_mode/0";
        } else if (its_testmode == std::string("MIXED")) {
            ::testing::GTEST_FLAG(filter) = "*send_in_mode/1";
        } else if (its_testmode == std::string("INCOMPLETE")) {
            ::testing::GTEST_FLAG(filter) = "*send_in_mode/2";
        } else if (its_testmode == std::string("DUPLICATE")) {
            ::testing::GTEST_FLAG(filter) = "*send_in_mode/3";
        } else if (its_testmode == std::string("OVERLAP")) {
            ::testing::GTEST_FLAG(filter) = "*send_in_mode/4";
        } else if (its_testmode == std::string("OVERLAP_FRONT_BACK")) {
            ::testing::GTEST_FLAG(filter) = "*send_in_mode/5";
        }
    }
    return RUN_ALL_TESTS();
}
#endif
