<commit_before>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "rpcsendv2.h"
#include "rpcnetwork.h"
#include "rpcserviceaddress.h"
#include <vespa/messagebus/emptyreply.h>
#include <vespa/messagebus/tracelevel.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/data/slime/slime.h>
#include <vespa/vespalib/data/databuffer.h>
#include <vespa/vespalib/util/compressor.h>
#include <vespa/fnet/frt/reflection.h>

using vespalib::make_string;
using vespalib::compression::CompressionConfig;
using vespalib::compression::decompress;
using vespalib::compression::compress;
using vespalib::DataBuffer;
using vespalib::ConstBufferRef;
using vespalib::stringref;
using vespalib::Memory;
using vespalib::Slime;
using vespalib::Version;
using namespace vespalib::slime;

namespace mbus {

namespace {

const char *METHOD_NAME   = "mbus.slime";
const char *METHOD_PARAMS = "bixbix";
const char *METHOD_RETURN = "bixbix";

Memory VERSION_F("version");
Memory ROUTE_F("route");
Memory SESSION_F("session");
Memory USERETRY_F("useretry");
Memory RETRYDELAY_F("retrydelay");
Memory RETRY_F("retry");
Memory TIMELEFT_F("timeleft");
Memory PROTOCOL_F("prot");
Memory TRACELEVEL_F("tracelevel");
Memory TRACE_F("trace");
Memory BLOB_F("msg");
Memory ERRORS_F("errors");
Memory CODE_F("code");
Memory MSG_F("msg");
Memory SERVICE_F("service");

}

bool RPCSendV2::isCompatible(stringref method, stringref request, stringref response)
{
    return  (method == METHOD_NAME) &&
            (request == METHOD_PARAMS) &&
            (response == METHOD_RETURN);
}

void
RPCSendV2::build(FRT_ReflectionBuilder & builder)
{
    builder.DefineMethod(METHOD_NAME, METHOD_PARAMS, METHOD_RETURN, true, FRT_METHOD(RPCSendV2::invoke), this);
    builder.MethodDesc("Send a message bus slime request and get a reply back.");
    builder.ParamDesc("header_encoding", "0=raw, 6=lz4");
    builder.ParamDesc("header_decoded_size", "Uncompressed header blob size");
    builder.ParamDesc("header_payload", "The message header blob in slime");
    builder.ParamDesc("body_encoding", "0=raw, 6=lz4");
    builder.ParamDesc("body_decoded_size", "Uncompressed body blob size");
    builder.ParamDesc("body_payload", "The message body blob in slime");
    builder.ReturnDesc("header_encoding",  "0=raw, 6=lz4");
    builder.ReturnDesc("header_decoded_size", "Uncompressed header blob size");
    builder.ReturnDesc("header_payload", "The reply header blob in slime.");
    builder.ReturnDesc("body_encoding",  "0=raw, 6=lz4");
    builder.ReturnDesc("body_decoded_size", "Uncompressed body blob size");
    builder.ReturnDesc("body_payload", "The reply body blob in slime.");
}

const char *
RPCSendV2::getReturnSpec() const {
    return METHOD_RETURN;
}

namespace {
class OutputBuf : public vespalib::Output {
public:
    OutputBuf(size_t estimatedSize) : _buf(estimatedSize) { }
    DataBuffer & getBuf() { return _buf; }
private:
    vespalib::WritableMemory reserve(size_t bytes) override {
        _buf.ensureFree(bytes);
        return vespalib::WritableMemory(_buf.getFree(), _buf.getFreeLen());
    }
    Output &commit(size_t bytes) override {
        _buf.moveFreeToData(bytes);
        return *this;
    }
    DataBuffer _buf;
};
}

void
RPCSendV2::encodeRequest(FRT_RPCRequest &req, const Version &version, const Route & route,
                         const RPCServiceAddress & address, const Message & msg, uint32_t traceLevel,
                         const PayLoadFiller &filler, uint64_t timeRemaining) const
{
    FRT_Values &args = *req.GetParams();
    req.SetMethodName(METHOD_NAME);
    // Place holder for auxillary data to be transfered later.
    args.AddInt8(CompressionConfig::NONE);
    args.AddInt32(0);
    args.AddData("", 0);

    Slime slime;
    Cursor & root = slime.setObject();

    root.setString(VERSION_F, version.toString());
    root.setString(ROUTE_F, route.toString());
    root.setString(SESSION_F, address.getSessionName());
    root.setBool(USERETRY_F, msg.getRetryEnabled());
    root.setLong(RETRY_F, msg.getRetry());
    root.setLong(TIMELEFT_F, timeRemaining);
    root.setString(PROTOCOL_F, msg.getProtocol());
    root.setLong(TRACELEVEL_F, traceLevel);
    filler.fill(BLOB_F, root);

    OutputBuf rBuf(8192);
    BinaryFormat::encode(slime, rBuf);
    ConstBufferRef toCompress(rBuf.getBuf().getData(), rBuf.getBuf().getDataLen());
    DataBuffer buf(vespalib::roundUp2inN(rBuf.getBuf().getDataLen()));
    CompressionConfig::Type type = compress(_net->getCompressionConfig(), toCompress, buf, false);

    args.AddInt8(type);
    args.AddInt32(toCompress.size());
    args.AddData(buf.stealBuffer(), buf.getDataLen());
}

namespace {

class ParamsV2 : public RPCSend::Params
{
public:
    ParamsV2(const FRT_Values &arg)
        : _slime()
    {
        uint8_t encoding = arg[0]._intval8;
        uint32_t uncompressedSize = arg[1]._intval32;
        DataBuffer uncompressed(arg[2]._data._buf, arg[2]._data._len);
        ConstBufferRef blob(arg[2]._data._buf, arg[2]._data._len);
        decompress(CompressionConfig::toType(encoding), uncompressedSize, blob, uncompressed, true);
        assert(uncompressedSize == uncompressed.getDataLen());
        BinaryFormat::decode(Memory(uncompressed.getData(), uncompressed.getDataLen()), _slime);
    }

    uint32_t getTraceLevel() const override { return _slime.get()[TRACELEVEL_F].asLong(); }
    bool useRetry() const override { return _slime.get()[USERETRY_F].asBool(); }
    uint32_t getRetries() const override { return _slime.get()[RETRY_F].asLong(); }
    uint64_t getRemainingTime() const override { return _slime.get()[TIMELEFT_F].asLong(); }

    Version getVersion() const override {
        return Version(_slime.get()[VERSION_F].asString().make_stringref());
    }
    stringref getRoute() const override {
        return _slime.get()[ROUTE_F].asString().make_stringref();
    }
    stringref getSession() const override {
        return _slime.get()[SESSION_F].asString().make_stringref();
    }
    stringref getProtocol() const override {
        return _slime.get()[PROTOCOL_F].asString().make_stringref();
    }
    BlobRef getPayload() const override {
        Memory m = _slime.get()[BLOB_F].asData();
        return BlobRef(m.data, m.size);
    }
private:
    Slime _slime;
};

}

std::unique_ptr<RPCSend::Params>
RPCSendV2::toParams(const FRT_Values &args) const
{
    return std::make_unique<ParamsV2>(args);
}

std::unique_ptr<Reply>
RPCSendV2::createReply(const FRT_Values & ret, const string & serviceName,
                       Error & error, vespalib::TraceNode & rootTrace) const
{
    uint8_t encoding = ret[0]._intval8;
    uint32_t uncompressedSize = ret[1]._intval32;
    DataBuffer uncompressed(ret[2]._data._buf, ret[2]._data._len);
    ConstBufferRef blob(ret[2]._data._buf, ret[2]._data._len);
    decompress(CompressionConfig::toType(encoding), uncompressedSize, blob, uncompressed, true);
    assert(uncompressedSize == uncompressed.getDataLen());
    Slime slime;
    BinaryFormat::decode(Memory(uncompressed.getData(), uncompressed.getDataLen()), slime);
    Inspector & root = slime.get();
    Version version(root[VERSION_F].asString().make_string());
    Memory payload = root[BLOB_F].asData();

    Reply::UP reply;
    if (payload.size > 0) {
        reply = decode(root[PROTOCOL_F].asString().make_stringref(), version, BlobRef(payload.data, payload.size), error);
    }
    if ( ! reply ) {
        reply.reset(new EmptyReply());
    }
    reply->setRetryDelay(root[RETRYDELAY_F].asDouble());
    Inspector & errors = root[ERRORS_F];
    for (uint32_t i = 0; i < errors.entries(); ++i) {
        Inspector & e = errors[i];
        Memory service = e[SERVICE_F].asString();
        reply->addError(Error(e[CODE_F].asLong(), e[MSG_F].asString().make_string(),
                              (service.size > 0) ? service.make_string() : serviceName));
    }
    rootTrace.addChild(TraceNode::decode(root[TRACE_F].asString().make_string()));
    return reply;
}

void
RPCSendV2::createResponse(FRT_Values & ret, const string & version, Reply & reply, Blob payload) const
{
    // Place holder for auxillary data to be transfered later.
    ret.AddInt8(CompressionConfig::NONE);
    ret.AddInt32(0);
    ret.AddData("", 0);

    Slime slime;
    Cursor & root = slime.setObject();

    root.setString(VERSION_F, version);
    root.setDouble(RETRYDELAY_F, reply.getRetryDelay());
    root.setString(PROTOCOL_F, reply.getProtocol());
    root.setData(BLOB_F, vespalib::Memory(payload.data(), payload.size()));
    if (reply.getTrace().getLevel() > 0) {
        root.setString(TRACE_F, reply.getTrace().getRoot().encode());
    }

    if (reply.getNumErrors() > 0) {
        Cursor & array = root.setArray(ERRORS_F);
        for (uint32_t i = 0; i < reply.getNumErrors(); ++i) {
            Cursor & error = array.addObject();
            error.setLong(CODE_F, reply.getError(i).getCode());
            error.setString(MSG_F, reply.getError(i).getMessage());
            error.setString(SERVICE_F, reply.getError(i).getService().c_str());
        }
    }

    OutputBuf rBuf(8192);
    BinaryFormat::encode(slime, rBuf);
    ConstBufferRef toCompress(rBuf.getBuf().getData(), rBuf.getBuf().getDataLen());
    DataBuffer buf(vespalib::roundUp2inN(rBuf.getBuf().getDataLen()));
    CompressionConfig::Type type = compress(_net->getCompressionConfig(), toCompress, buf, false);

    ret.AddInt8(type);
    ret.AddInt32(toCompress.size());
    ret.AddData(buf.stealBuffer(), buf.getDataLen());

}

} // namespace mbus
<commit_msg>body has moved to last 3 parameters.<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "rpcsendv2.h"
#include "rpcnetwork.h"
#include "rpcserviceaddress.h"
#include <vespa/messagebus/emptyreply.h>
#include <vespa/messagebus/tracelevel.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/data/slime/slime.h>
#include <vespa/vespalib/data/databuffer.h>
#include <vespa/vespalib/util/compressor.h>
#include <vespa/fnet/frt/reflection.h>

using vespalib::make_string;
using vespalib::compression::CompressionConfig;
using vespalib::compression::decompress;
using vespalib::compression::compress;
using vespalib::DataBuffer;
using vespalib::ConstBufferRef;
using vespalib::stringref;
using vespalib::Memory;
using vespalib::Slime;
using vespalib::Version;
using namespace vespalib::slime;

namespace mbus {

namespace {

const char *METHOD_NAME   = "mbus.slime";
const char *METHOD_PARAMS = "bixbix";
const char *METHOD_RETURN = "bixbix";

Memory VERSION_F("version");
Memory ROUTE_F("route");
Memory SESSION_F("session");
Memory USERETRY_F("useretry");
Memory RETRYDELAY_F("retrydelay");
Memory RETRY_F("retry");
Memory TIMELEFT_F("timeleft");
Memory PROTOCOL_F("prot");
Memory TRACELEVEL_F("tracelevel");
Memory TRACE_F("trace");
Memory BLOB_F("msg");
Memory ERRORS_F("errors");
Memory CODE_F("code");
Memory MSG_F("msg");
Memory SERVICE_F("service");

}

bool RPCSendV2::isCompatible(stringref method, stringref request, stringref response)
{
    return  (method == METHOD_NAME) &&
            (request == METHOD_PARAMS) &&
            (response == METHOD_RETURN);
}

void
RPCSendV2::build(FRT_ReflectionBuilder & builder)
{
    builder.DefineMethod(METHOD_NAME, METHOD_PARAMS, METHOD_RETURN, true, FRT_METHOD(RPCSendV2::invoke), this);
    builder.MethodDesc("Send a message bus slime request and get a reply back.");
    builder.ParamDesc("header_encoding", "0=raw, 6=lz4");
    builder.ParamDesc("header_decoded_size", "Uncompressed header blob size");
    builder.ParamDesc("header_payload", "The message header blob in slime");
    builder.ParamDesc("body_encoding", "0=raw, 6=lz4");
    builder.ParamDesc("body_decoded_size", "Uncompressed body blob size");
    builder.ParamDesc("body_payload", "The message body blob in slime");
    builder.ReturnDesc("header_encoding",  "0=raw, 6=lz4");
    builder.ReturnDesc("header_decoded_size", "Uncompressed header blob size");
    builder.ReturnDesc("header_payload", "The reply header blob in slime.");
    builder.ReturnDesc("body_encoding",  "0=raw, 6=lz4");
    builder.ReturnDesc("body_decoded_size", "Uncompressed body blob size");
    builder.ReturnDesc("body_payload", "The reply body blob in slime.");
}

const char *
RPCSendV2::getReturnSpec() const {
    return METHOD_RETURN;
}

namespace {
class OutputBuf : public vespalib::Output {
public:
    OutputBuf(size_t estimatedSize) : _buf(estimatedSize) { }
    DataBuffer & getBuf() { return _buf; }
private:
    vespalib::WritableMemory reserve(size_t bytes) override {
        _buf.ensureFree(bytes);
        return vespalib::WritableMemory(_buf.getFree(), _buf.getFreeLen());
    }
    Output &commit(size_t bytes) override {
        _buf.moveFreeToData(bytes);
        return *this;
    }
    DataBuffer _buf;
};
}

void
RPCSendV2::encodeRequest(FRT_RPCRequest &req, const Version &version, const Route & route,
                         const RPCServiceAddress & address, const Message & msg, uint32_t traceLevel,
                         const PayLoadFiller &filler, uint64_t timeRemaining) const
{
    FRT_Values &args = *req.GetParams();
    req.SetMethodName(METHOD_NAME);
    // Place holder for auxillary data to be transfered later.
    args.AddInt8(CompressionConfig::NONE);
    args.AddInt32(0);
    args.AddData("", 0);

    Slime slime;
    Cursor & root = slime.setObject();

    root.setString(VERSION_F, version.toString());
    root.setString(ROUTE_F, route.toString());
    root.setString(SESSION_F, address.getSessionName());
    root.setBool(USERETRY_F, msg.getRetryEnabled());
    root.setLong(RETRY_F, msg.getRetry());
    root.setLong(TIMELEFT_F, timeRemaining);
    root.setString(PROTOCOL_F, msg.getProtocol());
    root.setLong(TRACELEVEL_F, traceLevel);
    filler.fill(BLOB_F, root);

    OutputBuf rBuf(8192);
    BinaryFormat::encode(slime, rBuf);
    ConstBufferRef toCompress(rBuf.getBuf().getData(), rBuf.getBuf().getDataLen());
    DataBuffer buf(vespalib::roundUp2inN(rBuf.getBuf().getDataLen()));
    CompressionConfig::Type type = compress(_net->getCompressionConfig(), toCompress, buf, false);

    args.AddInt8(type);
    args.AddInt32(toCompress.size());
    args.AddData(buf.stealBuffer(), buf.getDataLen());
}

namespace {

class ParamsV2 : public RPCSend::Params
{
public:
    ParamsV2(const FRT_Values &arg)
        : _slime()
    {
        uint8_t encoding = arg[3]._intval8;
        uint32_t uncompressedSize = arg[4]._intval32;
        DataBuffer uncompressed(arg[5]._data._buf, arg[5]._data._len);
        ConstBufferRef blob(arg[5]._data._buf, arg[5]._data._len);
        decompress(CompressionConfig::toType(encoding), uncompressedSize, blob, uncompressed, true);
        assert(uncompressedSize == uncompressed.getDataLen());
        BinaryFormat::decode(Memory(uncompressed.getData(), uncompressed.getDataLen()), _slime);
    }

    uint32_t getTraceLevel() const override { return _slime.get()[TRACELEVEL_F].asLong(); }
    bool useRetry() const override { return _slime.get()[USERETRY_F].asBool(); }
    uint32_t getRetries() const override { return _slime.get()[RETRY_F].asLong(); }
    uint64_t getRemainingTime() const override { return _slime.get()[TIMELEFT_F].asLong(); }

    Version getVersion() const override {
        return Version(_slime.get()[VERSION_F].asString().make_stringref());
    }
    stringref getRoute() const override {
        return _slime.get()[ROUTE_F].asString().make_stringref();
    }
    stringref getSession() const override {
        return _slime.get()[SESSION_F].asString().make_stringref();
    }
    stringref getProtocol() const override {
        return _slime.get()[PROTOCOL_F].asString().make_stringref();
    }
    BlobRef getPayload() const override {
        Memory m = _slime.get()[BLOB_F].asData();
        return BlobRef(m.data, m.size);
    }
private:
    Slime _slime;
};

}

std::unique_ptr<RPCSend::Params>
RPCSendV2::toParams(const FRT_Values &args) const
{
    return std::make_unique<ParamsV2>(args);
}

std::unique_ptr<Reply>
RPCSendV2::createReply(const FRT_Values & ret, const string & serviceName,
                       Error & error, vespalib::TraceNode & rootTrace) const
{
    uint8_t encoding = ret[3]._intval8;
    uint32_t uncompressedSize = ret[4]._intval32;
    DataBuffer uncompressed(ret[5]._data._buf, ret[5]._data._len);
    ConstBufferRef blob(ret[5]._data._buf, ret[5]._data._len);
    decompress(CompressionConfig::toType(encoding), uncompressedSize, blob, uncompressed, true);
    assert(uncompressedSize == uncompressed.getDataLen());
    Slime slime;
    BinaryFormat::decode(Memory(uncompressed.getData(), uncompressed.getDataLen()), slime);
    Inspector & root = slime.get();
    Version version(root[VERSION_F].asString().make_string());
    Memory payload = root[BLOB_F].asData();

    Reply::UP reply;
    if (payload.size > 0) {
        reply = decode(root[PROTOCOL_F].asString().make_stringref(), version, BlobRef(payload.data, payload.size), error);
    }
    if ( ! reply ) {
        reply.reset(new EmptyReply());
    }
    reply->setRetryDelay(root[RETRYDELAY_F].asDouble());
    Inspector & errors = root[ERRORS_F];
    for (uint32_t i = 0; i < errors.entries(); ++i) {
        Inspector & e = errors[i];
        Memory service = e[SERVICE_F].asString();
        reply->addError(Error(e[CODE_F].asLong(), e[MSG_F].asString().make_string(),
                              (service.size > 0) ? service.make_string() : serviceName));
    }
    rootTrace.addChild(TraceNode::decode(root[TRACE_F].asString().make_string()));
    return reply;
}

void
RPCSendV2::createResponse(FRT_Values & ret, const string & version, Reply & reply, Blob payload) const
{
    // Place holder for auxillary data to be transfered later.
    ret.AddInt8(CompressionConfig::NONE);
    ret.AddInt32(0);
    ret.AddData("", 0);

    Slime slime;
    Cursor & root = slime.setObject();

    root.setString(VERSION_F, version);
    root.setDouble(RETRYDELAY_F, reply.getRetryDelay());
    root.setString(PROTOCOL_F, reply.getProtocol());
    root.setData(BLOB_F, vespalib::Memory(payload.data(), payload.size()));
    if (reply.getTrace().getLevel() > 0) {
        root.setString(TRACE_F, reply.getTrace().getRoot().encode());
    }

    if (reply.getNumErrors() > 0) {
        Cursor & array = root.setArray(ERRORS_F);
        for (uint32_t i = 0; i < reply.getNumErrors(); ++i) {
            Cursor & error = array.addObject();
            error.setLong(CODE_F, reply.getError(i).getCode());
            error.setString(MSG_F, reply.getError(i).getMessage());
            error.setString(SERVICE_F, reply.getError(i).getService().c_str());
        }
    }

    OutputBuf rBuf(8192);
    BinaryFormat::encode(slime, rBuf);
    ConstBufferRef toCompress(rBuf.getBuf().getData(), rBuf.getBuf().getDataLen());
    DataBuffer buf(vespalib::roundUp2inN(rBuf.getBuf().getDataLen()));
    CompressionConfig::Type type = compress(_net->getCompressionConfig(), toCompress, buf, false);

    ret.AddInt8(type);
    ret.AddInt32(toCompress.size());
    ret.AddData(buf.stealBuffer(), buf.getDataLen());

}

} // namespace mbus
<|endoftext|>


<commit_before>// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <addrdb.h>
#include <addrman.h>
#include <clientversion.h>
#include <test/setup_common.h>
#include <string>
#include <boost/test/unit_test.hpp>
#include <serialize.h>
#include <streams.h>
#include <net.h>
#include <netbase.h>
#include <chainparams.h>
#include <util/memory.h>
#include <util/system.h>

#include <memory>

class CAddrManSerializationMock : public CAddrMan
{
public:
    virtual void Serialize(CDataStream& s) const = 0;

    //! Ensure that bucket placement is always the same for testing purposes.
    void MakeDeterministic()
    {
        nKey.SetNull();
        insecure_rand = FastRandomContext(true);
    }
};

class CAddrManUncorrupted : public CAddrManSerializationMock
{
public:
    void Serialize(CDataStream& s) const override
    {
        CAddrMan::Serialize(s);
    }
};

class CAddrManCorrupted : public CAddrManSerializationMock
{
public:
    void Serialize(CDataStream& s) const override
    {
        // Produces corrupt output that claims addrman has 20 addrs when it only has one addr.
        unsigned char nVersion = 1;
        s << nVersion;
        s << ((unsigned char)32);
        s << nKey;
        s << 10; // nNew
        s << 10; // nTried

        int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
        s << nUBuckets;

        CService serv;
        BOOST_CHECK(Lookup("252.1.1.1", serv, 7777, false));
        CAddress addr = CAddress(serv, NODE_NONE);
        CNetAddr resolved;
        BOOST_CHECK(LookupHost("252.2.2.2", resolved, false));
        CAddrInfo info = CAddrInfo(addr, resolved);
        s << info;
    }
};

static CDataStream AddrmanToStream(CAddrManSerializationMock& _addrman)
{
    CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION);
    ssPeersIn << Params().MessageStart();
    ssPeersIn << _addrman;
    std::string str = ssPeersIn.str();
    std::vector<unsigned char> vchData(str.begin(), str.end());
    return CDataStream(vchData, SER_DISK, CLIENT_VERSION);
}

BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup)

BOOST_AUTO_TEST_CASE(cnode_listen_port)
{
    // test default
    unsigned short port = GetListenPort();
    BOOST_CHECK(port == Params().GetDefaultPort());
    // test set port
    unsigned short altPort = 12345;
    BOOST_CHECK(gArgs.SoftSetArg("-port", std::to_string(altPort)));
    port = GetListenPort();
    BOOST_CHECK(port == altPort);
}

BOOST_AUTO_TEST_CASE(caddrdb_read)
{
    CAddrManUncorrupted addrmanUncorrupted;
    addrmanUncorrupted.MakeDeterministic();

    CService addr1, addr2, addr3;
    BOOST_CHECK(Lookup("250.7.1.1", addr1, 8333, false));
    BOOST_CHECK(Lookup("250.7.2.2", addr2, 9999, false));
    BOOST_CHECK(Lookup("250.7.3.3", addr3, 9999, false));

    // Add three addresses to new table.
    CService source;
    BOOST_CHECK(Lookup("252.5.1.1", source, 8333, false));
    BOOST_CHECK(addrmanUncorrupted.Add(CAddress(addr1, NODE_NONE), source));
    BOOST_CHECK(addrmanUncorrupted.Add(CAddress(addr2, NODE_NONE), source));
    BOOST_CHECK(addrmanUncorrupted.Add(CAddress(addr3, NODE_NONE), source));

    // Test that the de-serialization does not throw an exception.
    CDataStream ssPeers1 = AddrmanToStream(addrmanUncorrupted);
    bool exceptionThrown = false;
    CAddrMan addrman1;

    BOOST_CHECK(addrman1.size() == 0);
    try {
        unsigned char pchMsgTmp[4];
        ssPeers1 >> pchMsgTmp;
        ssPeers1 >> addrman1;
    } catch (const std::exception&) {
        exceptionThrown = true;
    }

    BOOST_CHECK(addrman1.size() == 3);
    BOOST_CHECK(exceptionThrown == false);

    // Test that CAddrDB::Read creates an addrman with the correct number of addrs.
    CDataStream ssPeers2 = AddrmanToStream(addrmanUncorrupted);

    CAddrMan addrman2;
    CAddrDB adb;
    BOOST_CHECK(addrman2.size() == 0);
    BOOST_CHECK(adb.Read(addrman2, ssPeers2));
    BOOST_CHECK(addrman2.size() == 3);
}

BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
{
    CAddrManCorrupted addrmanCorrupted;
    addrmanCorrupted.MakeDeterministic();

    // Test that the de-serialization of corrupted addrman throws an exception.
    CDataStream ssPeers1 = AddrmanToStream(addrmanCorrupted);
    bool exceptionThrown = false;
    CAddrMan addrman1;
    BOOST_CHECK(addrman1.size() == 0);
    try {
        unsigned char pchMsgTmp[4];
        ssPeers1 >> pchMsgTmp;
        ssPeers1 >> addrman1;
    } catch (const std::exception&) {
        exceptionThrown = true;
    }
    // Even through de-serialization failed addrman is not left in a clean state.
    BOOST_CHECK(addrman1.size() == 1);
    BOOST_CHECK(exceptionThrown);

    // Test that CAddrDB::Read leaves addrman in a clean state if de-serialization fails.
    CDataStream ssPeers2 = AddrmanToStream(addrmanCorrupted);

    CAddrMan addrman2;
    CAddrDB adb;
    BOOST_CHECK(addrman2.size() == 0);
    BOOST_CHECK(!adb.Read(addrman2, ssPeers2));
    BOOST_CHECK(addrman2.size() == 0);
}

BOOST_AUTO_TEST_CASE(cnode_simple_test)
{
    SOCKET hSocket = INVALID_SOCKET;
    NodeId id = 0;
    int height = 0;

    in_addr ipv4Addr;
    ipv4Addr.s_addr = 0xa0b0c001;

    CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK);
    std::string pszDest;
    bool fInboundIn = false;

    // Test that fFeeler is false by default.
    std::unique_ptr<CNode> pnode1 = MakeUnique<CNode>(id++, NODE_NETWORK, height, hSocket, addr, 0, 0, CAddress(), pszDest, fInboundIn);
    BOOST_CHECK(pnode1->fInbound == false);
    BOOST_CHECK(pnode1->fFeeler == false);

    fInboundIn = true;
    std::unique_ptr<CNode> pnode2 = MakeUnique<CNode>(id++, NODE_NETWORK, height, hSocket, addr, 1, 1, CAddress(), pszDest, fInboundIn);
    BOOST_CHECK(pnode2->fInbound == true);
    BOOST_CHECK(pnode2->fFeeler == false);
}

// prior to PR #14728, this test triggers an undefined behavior
BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
{
    // set up local addresses; all that's necessary to reproduce the bug is
    // that a normal IPv4 address is among the entries, but if this address is
    // !IsRoutable the undefined behavior is easier to trigger deterministically
    {
        LOCK(cs_mapLocalHost);
        in_addr ipv4AddrLocal;
        ipv4AddrLocal.s_addr = 0x0100007f;
        CNetAddr addr = CNetAddr(ipv4AddrLocal);
        LocalServiceInfo lsi;
        lsi.nScore = 23;
        lsi.nPort = 42;
        mapLocalHost[addr] = lsi;
    }

    // create a peer with an IPv4 address
    in_addr ipv4AddrPeer;
    ipv4AddrPeer.s_addr = 0xa0b0c001;
    CAddress addr = CAddress(CService(ipv4AddrPeer, 7777), NODE_NETWORK);
    std::unique_ptr<CNode> pnode = MakeUnique<CNode>(0, NODE_NETWORK, 0, INVALID_SOCKET, addr, 0, 0, CAddress{}, std::string{}, false);
    pnode->fSuccessfullyConnected.store(true);

    // the peer claims to be reaching us via IPv6
    in6_addr ipv6AddrLocal;
    memset(ipv6AddrLocal.s6_addr, 0, 16);
    ipv6AddrLocal.s6_addr[0] = 0xcc;
    CAddress addrLocal = CAddress(CService(ipv6AddrLocal, 7777), NODE_NETWORK);
    pnode->SetAddrLocal(addrLocal);

    // before patch, this causes undefined behavior detectable with clang's -fsanitize=memory
    AdvertiseLocal(&*pnode);

    // suppress no-checks-run warning; if this test fails, it's by triggering a sanitizer
    BOOST_CHECK(1);
}

BOOST_AUTO_TEST_CASE(LimitedAndReachable_Network)
{
    BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_ONION), true);

    SetReachable(NET_IPV4, false);
    SetReachable(NET_IPV6, false);
    SetReachable(NET_ONION, false);

    BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), false);
    BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), false);
    BOOST_CHECK_EQUAL(IsReachable(NET_ONION), false);

    SetReachable(NET_IPV4, true);
    SetReachable(NET_IPV6, true);
    SetReachable(NET_ONION, true);

    BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_ONION), true);
}

BOOST_AUTO_TEST_CASE(LimitedAndReachable_NetworkCaseUnroutableAndInternal)
{
    BOOST_CHECK_EQUAL(IsReachable(NET_UNROUTABLE), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_INTERNAL), true);

    SetReachable(NET_UNROUTABLE, false);
    SetReachable(NET_INTERNAL, false);

    BOOST_CHECK_EQUAL(IsReachable(NET_UNROUTABLE), true); // Ignored for both networks
    BOOST_CHECK_EQUAL(IsReachable(NET_INTERNAL), true);
}

CNetAddr UtilBuildAddress(unsigned char p1, unsigned char p2, unsigned char p3, unsigned char p4)
{
    unsigned char ip[] = {p1, p2, p3, p4};

    struct sockaddr_in sa;
    memset(&sa, 0, sizeof(sockaddr_in)); // initialize the memory block
    memcpy(&(sa.sin_addr), &ip, sizeof(ip));
    return CNetAddr(sa.sin_addr);
}

BOOST_AUTO_TEST_CASE(LimitedAndReachable_CNetAddr)
{
    CNetAddr addr = UtilBuildAddress(0x001, 0x001, 0x001, 0x001); // 1.1.1.1

    SetReachable(NET_IPV4, true);
    BOOST_CHECK_EQUAL(IsReachable(addr), true);

    SetReachable(NET_IPV4, false);
    BOOST_CHECK_EQUAL(IsReachable(addr), false);

    SetReachable(NET_IPV4, true); // have to reset this, because this is stateful.
}

BOOST_AUTO_TEST_CASE(LocalAddress_BasicLifecycle)
{
    CService addr = CService(UtilBuildAddress(0x002, 0x001, 0x001, 0x001), 1000); // 2.1.1.1:1000

    SetReachable(NET_IPV4, true);

    BOOST_CHECK_EQUAL(IsLocal(addr), false);
    BOOST_CHECK_EQUAL(AddLocal(addr, 1000), true);
    BOOST_CHECK_EQUAL(IsLocal(addr), true);

    RemoveLocal(addr);
    BOOST_CHECK_EQUAL(IsLocal(addr), false);
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>test: Do not instantiate CAddrDB for static call<commit_after>// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <addrdb.h>
#include <addrman.h>
#include <clientversion.h>
#include <test/setup_common.h>
#include <string>
#include <boost/test/unit_test.hpp>
#include <serialize.h>
#include <streams.h>
#include <net.h>
#include <netbase.h>
#include <chainparams.h>
#include <util/memory.h>
#include <util/system.h>

#include <memory>

class CAddrManSerializationMock : public CAddrMan
{
public:
    virtual void Serialize(CDataStream& s) const = 0;

    //! Ensure that bucket placement is always the same for testing purposes.
    void MakeDeterministic()
    {
        nKey.SetNull();
        insecure_rand = FastRandomContext(true);
    }
};

class CAddrManUncorrupted : public CAddrManSerializationMock
{
public:
    void Serialize(CDataStream& s) const override
    {
        CAddrMan::Serialize(s);
    }
};

class CAddrManCorrupted : public CAddrManSerializationMock
{
public:
    void Serialize(CDataStream& s) const override
    {
        // Produces corrupt output that claims addrman has 20 addrs when it only has one addr.
        unsigned char nVersion = 1;
        s << nVersion;
        s << ((unsigned char)32);
        s << nKey;
        s << 10; // nNew
        s << 10; // nTried

        int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
        s << nUBuckets;

        CService serv;
        BOOST_CHECK(Lookup("252.1.1.1", serv, 7777, false));
        CAddress addr = CAddress(serv, NODE_NONE);
        CNetAddr resolved;
        BOOST_CHECK(LookupHost("252.2.2.2", resolved, false));
        CAddrInfo info = CAddrInfo(addr, resolved);
        s << info;
    }
};

static CDataStream AddrmanToStream(CAddrManSerializationMock& _addrman)
{
    CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION);
    ssPeersIn << Params().MessageStart();
    ssPeersIn << _addrman;
    std::string str = ssPeersIn.str();
    std::vector<unsigned char> vchData(str.begin(), str.end());
    return CDataStream(vchData, SER_DISK, CLIENT_VERSION);
}

BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup)

BOOST_AUTO_TEST_CASE(cnode_listen_port)
{
    // test default
    unsigned short port = GetListenPort();
    BOOST_CHECK(port == Params().GetDefaultPort());
    // test set port
    unsigned short altPort = 12345;
    BOOST_CHECK(gArgs.SoftSetArg("-port", std::to_string(altPort)));
    port = GetListenPort();
    BOOST_CHECK(port == altPort);
}

BOOST_AUTO_TEST_CASE(caddrdb_read)
{
    CAddrManUncorrupted addrmanUncorrupted;
    addrmanUncorrupted.MakeDeterministic();

    CService addr1, addr2, addr3;
    BOOST_CHECK(Lookup("250.7.1.1", addr1, 8333, false));
    BOOST_CHECK(Lookup("250.7.2.2", addr2, 9999, false));
    BOOST_CHECK(Lookup("250.7.3.3", addr3, 9999, false));

    // Add three addresses to new table.
    CService source;
    BOOST_CHECK(Lookup("252.5.1.1", source, 8333, false));
    BOOST_CHECK(addrmanUncorrupted.Add(CAddress(addr1, NODE_NONE), source));
    BOOST_CHECK(addrmanUncorrupted.Add(CAddress(addr2, NODE_NONE), source));
    BOOST_CHECK(addrmanUncorrupted.Add(CAddress(addr3, NODE_NONE), source));

    // Test that the de-serialization does not throw an exception.
    CDataStream ssPeers1 = AddrmanToStream(addrmanUncorrupted);
    bool exceptionThrown = false;
    CAddrMan addrman1;

    BOOST_CHECK(addrman1.size() == 0);
    try {
        unsigned char pchMsgTmp[4];
        ssPeers1 >> pchMsgTmp;
        ssPeers1 >> addrman1;
    } catch (const std::exception&) {
        exceptionThrown = true;
    }

    BOOST_CHECK(addrman1.size() == 3);
    BOOST_CHECK(exceptionThrown == false);

    // Test that CAddrDB::Read creates an addrman with the correct number of addrs.
    CDataStream ssPeers2 = AddrmanToStream(addrmanUncorrupted);

    CAddrMan addrman2;
    BOOST_CHECK(addrman2.size() == 0);
    BOOST_CHECK(CAddrDB::Read(addrman2, ssPeers2));
    BOOST_CHECK(addrman2.size() == 3);
}

BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
{
    CAddrManCorrupted addrmanCorrupted;
    addrmanCorrupted.MakeDeterministic();

    // Test that the de-serialization of corrupted addrman throws an exception.
    CDataStream ssPeers1 = AddrmanToStream(addrmanCorrupted);
    bool exceptionThrown = false;
    CAddrMan addrman1;
    BOOST_CHECK(addrman1.size() == 0);
    try {
        unsigned char pchMsgTmp[4];
        ssPeers1 >> pchMsgTmp;
        ssPeers1 >> addrman1;
    } catch (const std::exception&) {
        exceptionThrown = true;
    }
    // Even through de-serialization failed addrman is not left in a clean state.
    BOOST_CHECK(addrman1.size() == 1);
    BOOST_CHECK(exceptionThrown);

    // Test that CAddrDB::Read leaves addrman in a clean state if de-serialization fails.
    CDataStream ssPeers2 = AddrmanToStream(addrmanCorrupted);

    CAddrMan addrman2;
    BOOST_CHECK(addrman2.size() == 0);
    BOOST_CHECK(!CAddrDB::Read(addrman2, ssPeers2));
    BOOST_CHECK(addrman2.size() == 0);
}

BOOST_AUTO_TEST_CASE(cnode_simple_test)
{
    SOCKET hSocket = INVALID_SOCKET;
    NodeId id = 0;
    int height = 0;

    in_addr ipv4Addr;
    ipv4Addr.s_addr = 0xa0b0c001;

    CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK);
    std::string pszDest;
    bool fInboundIn = false;

    // Test that fFeeler is false by default.
    std::unique_ptr<CNode> pnode1 = MakeUnique<CNode>(id++, NODE_NETWORK, height, hSocket, addr, 0, 0, CAddress(), pszDest, fInboundIn);
    BOOST_CHECK(pnode1->fInbound == false);
    BOOST_CHECK(pnode1->fFeeler == false);

    fInboundIn = true;
    std::unique_ptr<CNode> pnode2 = MakeUnique<CNode>(id++, NODE_NETWORK, height, hSocket, addr, 1, 1, CAddress(), pszDest, fInboundIn);
    BOOST_CHECK(pnode2->fInbound == true);
    BOOST_CHECK(pnode2->fFeeler == false);
}

// prior to PR #14728, this test triggers an undefined behavior
BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
{
    // set up local addresses; all that's necessary to reproduce the bug is
    // that a normal IPv4 address is among the entries, but if this address is
    // !IsRoutable the undefined behavior is easier to trigger deterministically
    {
        LOCK(cs_mapLocalHost);
        in_addr ipv4AddrLocal;
        ipv4AddrLocal.s_addr = 0x0100007f;
        CNetAddr addr = CNetAddr(ipv4AddrLocal);
        LocalServiceInfo lsi;
        lsi.nScore = 23;
        lsi.nPort = 42;
        mapLocalHost[addr] = lsi;
    }

    // create a peer with an IPv4 address
    in_addr ipv4AddrPeer;
    ipv4AddrPeer.s_addr = 0xa0b0c001;
    CAddress addr = CAddress(CService(ipv4AddrPeer, 7777), NODE_NETWORK);
    std::unique_ptr<CNode> pnode = MakeUnique<CNode>(0, NODE_NETWORK, 0, INVALID_SOCKET, addr, 0, 0, CAddress{}, std::string{}, false);
    pnode->fSuccessfullyConnected.store(true);

    // the peer claims to be reaching us via IPv6
    in6_addr ipv6AddrLocal;
    memset(ipv6AddrLocal.s6_addr, 0, 16);
    ipv6AddrLocal.s6_addr[0] = 0xcc;
    CAddress addrLocal = CAddress(CService(ipv6AddrLocal, 7777), NODE_NETWORK);
    pnode->SetAddrLocal(addrLocal);

    // before patch, this causes undefined behavior detectable with clang's -fsanitize=memory
    AdvertiseLocal(&*pnode);

    // suppress no-checks-run warning; if this test fails, it's by triggering a sanitizer
    BOOST_CHECK(1);
}

BOOST_AUTO_TEST_CASE(LimitedAndReachable_Network)
{
    BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_ONION), true);

    SetReachable(NET_IPV4, false);
    SetReachable(NET_IPV6, false);
    SetReachable(NET_ONION, false);

    BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), false);
    BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), false);
    BOOST_CHECK_EQUAL(IsReachable(NET_ONION), false);

    SetReachable(NET_IPV4, true);
    SetReachable(NET_IPV6, true);
    SetReachable(NET_ONION, true);

    BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_ONION), true);
}

BOOST_AUTO_TEST_CASE(LimitedAndReachable_NetworkCaseUnroutableAndInternal)
{
    BOOST_CHECK_EQUAL(IsReachable(NET_UNROUTABLE), true);
    BOOST_CHECK_EQUAL(IsReachable(NET_INTERNAL), true);

    SetReachable(NET_UNROUTABLE, false);
    SetReachable(NET_INTERNAL, false);

    BOOST_CHECK_EQUAL(IsReachable(NET_UNROUTABLE), true); // Ignored for both networks
    BOOST_CHECK_EQUAL(IsReachable(NET_INTERNAL), true);
}

CNetAddr UtilBuildAddress(unsigned char p1, unsigned char p2, unsigned char p3, unsigned char p4)
{
    unsigned char ip[] = {p1, p2, p3, p4};

    struct sockaddr_in sa;
    memset(&sa, 0, sizeof(sockaddr_in)); // initialize the memory block
    memcpy(&(sa.sin_addr), &ip, sizeof(ip));
    return CNetAddr(sa.sin_addr);
}

BOOST_AUTO_TEST_CASE(LimitedAndReachable_CNetAddr)
{
    CNetAddr addr = UtilBuildAddress(0x001, 0x001, 0x001, 0x001); // 1.1.1.1

    SetReachable(NET_IPV4, true);
    BOOST_CHECK_EQUAL(IsReachable(addr), true);

    SetReachable(NET_IPV4, false);
    BOOST_CHECK_EQUAL(IsReachable(addr), false);

    SetReachable(NET_IPV4, true); // have to reset this, because this is stateful.
}

BOOST_AUTO_TEST_CASE(LocalAddress_BasicLifecycle)
{
    CService addr = CService(UtilBuildAddress(0x002, 0x001, 0x001, 0x001), 1000); // 2.1.1.1:1000

    SetReachable(NET_IPV4, true);

    BOOST_CHECK_EQUAL(IsLocal(addr), false);
    BOOST_CHECK_EQUAL(AddLocal(addr, 1000), true);
    BOOST_CHECK_EQUAL(IsLocal(addr), true);

    RemoveLocal(addr);
    BOOST_CHECK_EQUAL(IsLocal(addr), false);
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/messagebus/testlib/receptor.h>
#include <vespa/messagebus/testlib/simpleprotocol.h>
#include <vespa/messagebus/testlib/simplereply.h>
#include <vespa/messagebus/testlib/slobrok.h>
#include <vespa/messagebus/testlib/testserver.h>
#include <vespa/messagebus/network/rpcsendv1.h>
#include <vespa/messagebus/network/rpcsendv2.h>
#include <vespa/vespalib/testkit/testapp.h>

#include <vespa/log/log.h>
LOG_SETUP("sendadapter_test");

using namespace mbus;

////////////////////////////////////////////////////////////////////////////////
//
// Setup
//
////////////////////////////////////////////////////////////////////////////////

class TestProtocol : public mbus::SimpleProtocol {
private:
    mutable vespalib::Version _lastVersion;

public:
    typedef std::shared_ptr<TestProtocol> SP;
    mbus::Blob encode(const vespalib::Version &version, const mbus::Routable &routable) const override {
        _lastVersion = version;
        return mbus::SimpleProtocol::encode(version, routable);
    }
    mbus::Routable::UP decode(const vespalib::Version &version, mbus::BlobRef blob) const override {
        _lastVersion = version;
        return mbus::SimpleProtocol::decode(version, blob);
    }
    const vespalib::Version &getLastVersion() { return _lastVersion; }
};

class TestData {
public:
    Slobrok                 _slobrok;
    TestProtocol::SP        _srcProtocol;
    TestServer              _srcServer;
    SourceSession::UP       _srcSession;
    Receptor                _srcHandler;
    TestProtocol::SP        _itrProtocol;
    TestServer              _itrServer;
    IntermediateSession::UP _itrSession;
    Receptor                _itrHandler;
    TestProtocol::SP        _dstProtocol;
    TestServer              _dstServer;
    DestinationSession::UP  _dstSession;
    Receptor                _dstHandler;

public:
    TestData();
    ~TestData();
    bool start();
};

static const duration TIMEOUT_SECS = 6s;

TestData::TestData() :
    _slobrok(),
    _srcProtocol(new TestProtocol()),
    _srcServer(MessageBusParams().setRetryPolicy(IRetryPolicy::SP()).addProtocol(_srcProtocol),
               RPCNetworkParams(_slobrok.config())),
    _srcSession(),
    _srcHandler(),
    _itrProtocol(new TestProtocol()),
    _itrServer(MessageBusParams().addProtocol(_itrProtocol),
               RPCNetworkParams(_slobrok.config()).setIdentity(Identity("itr"))),
    _itrSession(),
    _itrHandler(),
    _dstProtocol(new TestProtocol()),
    _dstServer(MessageBusParams().addProtocol(_dstProtocol),
               RPCNetworkParams(_slobrok.config()).setIdentity(Identity("dst"))),
    _dstSession(),
    _dstHandler()
{ }

TestData::~TestData() = default;

bool
TestData::start()
{
    _srcSession = _srcServer.mb.createSourceSession(SourceSessionParams().setReplyHandler(_srcHandler));
    if ( ! _srcSession) {
        return false;
    }
    _itrSession = _itrServer.mb.createIntermediateSession(IntermediateSessionParams().setName("session").setMessageHandler(_itrHandler).setReplyHandler(_itrHandler));
    if ( ! _itrSession) {
        return false;
    }
    _dstSession = _dstServer.mb.createDestinationSession(DestinationSessionParams().setName("session").setMessageHandler(_dstHandler));
    if ( ! _dstSession) {
        return false;
    }
    if (!_srcServer.waitSlobrok("*/session", 2u)) {
        return false;
    }
    return true;
}

bool
testVersionedSend(TestData &data,
                         const vespalib::Version &srcVersion,
                         const vespalib::Version &itrVersion,
                         const vespalib::Version &dstVersion)
{
    LOG(info, "Sending from %s through %s to %s.",
        srcVersion.toString().c_str(), itrVersion.toString().c_str(), dstVersion.toString().c_str());
    data._srcServer.net.setVersion(srcVersion);
    data._itrServer.net.setVersion(itrVersion);
    data._dstServer.net.setVersion(dstVersion);

    Message::UP msg(new SimpleMessage("foo"));
    msg->getTrace().setLevel(9);
    if (!EXPECT_TRUE(data._srcSession->send(std::move(msg), Route::parse("itr/session dst/session")).isAccepted())) {
        return false;
    }
    msg = data._itrHandler.getMessage(TIMEOUT_SECS);
    if (!EXPECT_TRUE(msg)) {
        return false;
    }
    LOG(info, "Message version %s serialized at source.",
        data._srcProtocol->getLastVersion().toString().c_str());
    vespalib::Version minVersion = std::min(srcVersion, itrVersion);
    if (!EXPECT_TRUE(minVersion == data._srcProtocol->getLastVersion())) {
        return false;
    }

    LOG(info, "Message version %s reached intermediate.",
        data._itrProtocol->getLastVersion().toString().c_str());
    if (!EXPECT_TRUE(minVersion == data._itrProtocol->getLastVersion())) {
        return false;
    }
    data._itrSession->forward(std::move(msg));
    msg = data._dstHandler.getMessage(TIMEOUT_SECS);
    if (!EXPECT_TRUE(msg)) {
        return false;
    }
    LOG(info, "Message version %s serialized at intermediate.",
        data._itrProtocol->getLastVersion().toString().c_str());
    minVersion = std::min(itrVersion, dstVersion);
    if (!EXPECT_TRUE(minVersion == data._itrProtocol->getLastVersion())) {
        return false;
    }

    LOG(info, "Message version %s reached destination.",
        data._dstProtocol->getLastVersion().toString().c_str());
    if (!EXPECT_TRUE(minVersion == data._dstProtocol->getLastVersion())) {
        return false;
    }
    Reply::UP reply(new SimpleReply("bar"));
    reply->swapState(*msg);
    data._dstSession->reply(std::move(reply));
    reply = data._itrHandler.getReply();
    if (!EXPECT_TRUE(reply)) {
        return false;
    }
    LOG(info, "Reply version %s serialized at destination.",
        data._dstProtocol->getLastVersion().toString().c_str());
    if (!EXPECT_TRUE(minVersion == data._dstProtocol->getLastVersion())) {
        return false;
    }

    LOG(info, "Reply version %s reached intermediate.",
        data._itrProtocol->getLastVersion().toString().c_str());
    if (!EXPECT_TRUE(minVersion == data._itrProtocol->getLastVersion())) {
        return false;
    }
    data._itrSession->forward(std::move(reply));
    reply = data._srcHandler.getReply();
    if (!EXPECT_TRUE(reply)) {
        return false;
    }
    LOG(info, "Reply version %s serialized at intermediate.",
        data._dstProtocol->getLastVersion().toString().c_str());
    minVersion = std::min(srcVersion, itrVersion);
    if (!EXPECT_TRUE(minVersion == data._itrProtocol->getLastVersion())) {
        return false;
    }

    LOG(info, "Reply version %s reached source.",
        data._srcProtocol->getLastVersion().toString().c_str());
    if (!EXPECT_TRUE(minVersion == data._srcProtocol->getLastVersion())) {
        return false;
    }
    return true;
}

void
testSendAdapters(TestData &data, const std::vector<vespalib::Version> & versions)
{
    for (vespalib::Version src : versions) {
        for (vespalib::Version intermediate : versions) {
            for (vespalib::Version dst : versions) {
                EXPECT_TRUE(testVersionedSend(data, src, intermediate, dst));
            }
        }
    }
}

TEST("test that all known versions are present") {
    TestData data;
    ASSERT_TRUE(data.start());
    EXPECT_FALSE(data._srcServer.net.getSendAdapter(vespalib::Version(4, 999)) != nullptr);
    EXPECT_TRUE(data._srcServer.net.getSendAdapter(vespalib::Version(5, 0)) != nullptr);
    EXPECT_TRUE(dynamic_cast<mbus::RPCSendV1 *>(data._srcServer.net.getSendAdapter(vespalib::Version(5, 0))) != nullptr);
    EXPECT_TRUE(data._srcServer.net.getSendAdapter(vespalib::Version(6, 148)) != nullptr);
    EXPECT_TRUE(dynamic_cast<mbus::RPCSendV1 *>(data._srcServer.net.getSendAdapter(vespalib::Version(6, 148))) != nullptr);
    EXPECT_TRUE(data._srcServer.net.getSendAdapter(vespalib::Version(6, 149)) != nullptr);
    EXPECT_TRUE(dynamic_cast<mbus::RPCSendV2 *>(data._srcServer.net.getSendAdapter(vespalib::Version(6, 149))) != nullptr);
    EXPECT_TRUE(data._srcServer.net.getSendAdapter(vespalib::Version(9, 999)) != nullptr);
    EXPECT_TRUE(dynamic_cast<mbus::RPCSendV2 *>(data._srcServer.net.getSendAdapter(vespalib::Version(9, 999))) != nullptr);
}

TEST("test that we can send between multiple versions") {
    TestData data;
    ASSERT_TRUE(data.start());
    TEST_DO(testSendAdapters(data, {vespalib::Version(5, 0), vespalib::Version(6, 148), vespalib::Version(6, 149), vespalib::Version(9, 999)}));
}

TEST_MAIN() { TEST_RUN_ALL(); }
<commit_msg>Need larger timeout for testing on satuyrated hw.<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/messagebus/testlib/receptor.h>
#include <vespa/messagebus/testlib/simpleprotocol.h>
#include <vespa/messagebus/testlib/simplereply.h>
#include <vespa/messagebus/testlib/slobrok.h>
#include <vespa/messagebus/testlib/testserver.h>
#include <vespa/messagebus/network/rpcsendv1.h>
#include <vespa/messagebus/network/rpcsendv2.h>
#include <vespa/vespalib/testkit/testapp.h>

#include <vespa/log/log.h>
LOG_SETUP("sendadapter_test");

using namespace mbus;

////////////////////////////////////////////////////////////////////////////////
//
// Setup
//
////////////////////////////////////////////////////////////////////////////////

class TestProtocol : public mbus::SimpleProtocol {
private:
    mutable vespalib::Version _lastVersion;

public:
    typedef std::shared_ptr<TestProtocol> SP;
    mbus::Blob encode(const vespalib::Version &version, const mbus::Routable &routable) const override {
        _lastVersion = version;
        return mbus::SimpleProtocol::encode(version, routable);
    }
    mbus::Routable::UP decode(const vespalib::Version &version, mbus::BlobRef blob) const override {
        _lastVersion = version;
        return mbus::SimpleProtocol::decode(version, blob);
    }
    const vespalib::Version &getLastVersion() { return _lastVersion; }
};

class TestData {
public:
    Slobrok                 _slobrok;
    TestProtocol::SP        _srcProtocol;
    TestServer              _srcServer;
    SourceSession::UP       _srcSession;
    Receptor                _srcHandler;
    TestProtocol::SP        _itrProtocol;
    TestServer              _itrServer;
    IntermediateSession::UP _itrSession;
    Receptor                _itrHandler;
    TestProtocol::SP        _dstProtocol;
    TestServer              _dstServer;
    DestinationSession::UP  _dstSession;
    Receptor                _dstHandler;

public:
    TestData();
    ~TestData();
    bool start();
};

static const duration TIMEOUT_SECS = 60s;

TestData::TestData() :
    _slobrok(),
    _srcProtocol(new TestProtocol()),
    _srcServer(MessageBusParams().setRetryPolicy(IRetryPolicy::SP()).addProtocol(_srcProtocol),
               RPCNetworkParams(_slobrok.config())),
    _srcSession(),
    _srcHandler(),
    _itrProtocol(new TestProtocol()),
    _itrServer(MessageBusParams().addProtocol(_itrProtocol),
               RPCNetworkParams(_slobrok.config()).setIdentity(Identity("itr"))),
    _itrSession(),
    _itrHandler(),
    _dstProtocol(new TestProtocol()),
    _dstServer(MessageBusParams().addProtocol(_dstProtocol),
               RPCNetworkParams(_slobrok.config()).setIdentity(Identity("dst"))),
    _dstSession(),
    _dstHandler()
{ }

TestData::~TestData() = default;

bool
TestData::start()
{
    _srcSession = _srcServer.mb.createSourceSession(SourceSessionParams().setReplyHandler(_srcHandler));
    if ( ! _srcSession) {
        return false;
    }
    _itrSession = _itrServer.mb.createIntermediateSession(IntermediateSessionParams().setName("session").setMessageHandler(_itrHandler).setReplyHandler(_itrHandler));
    if ( ! _itrSession) {
        return false;
    }
    _dstSession = _dstServer.mb.createDestinationSession(DestinationSessionParams().setName("session").setMessageHandler(_dstHandler));
    if ( ! _dstSession) {
        return false;
    }
    if (!_srcServer.waitSlobrok("*/session", 2u)) {
        return false;
    }
    return true;
}

bool
testVersionedSend(TestData &data,
                         const vespalib::Version &srcVersion,
                         const vespalib::Version &itrVersion,
                         const vespalib::Version &dstVersion)
{
    LOG(info, "Sending from %s through %s to %s.",
        srcVersion.toString().c_str(), itrVersion.toString().c_str(), dstVersion.toString().c_str());
    data._srcServer.net.setVersion(srcVersion);
    data._itrServer.net.setVersion(itrVersion);
    data._dstServer.net.setVersion(dstVersion);

    Message::UP msg(new SimpleMessage("foo"));
    msg->getTrace().setLevel(9);
    if (!EXPECT_TRUE(data._srcSession->send(std::move(msg), Route::parse("itr/session dst/session")).isAccepted())) {
        return false;
    }
    msg = data._itrHandler.getMessage(TIMEOUT_SECS);
    if (!EXPECT_TRUE(msg)) {
        return false;
    }
    LOG(info, "Message version %s serialized at source.",
        data._srcProtocol->getLastVersion().toString().c_str());
    vespalib::Version minVersion = std::min(srcVersion, itrVersion);
    if (!EXPECT_TRUE(minVersion == data._srcProtocol->getLastVersion())) {
        return false;
    }

    LOG(info, "Message version %s reached intermediate.",
        data._itrProtocol->getLastVersion().toString().c_str());
    if (!EXPECT_TRUE(minVersion == data._itrProtocol->getLastVersion())) {
        return false;
    }
    data._itrSession->forward(std::move(msg));
    msg = data._dstHandler.getMessage(TIMEOUT_SECS);
    if (!EXPECT_TRUE(msg)) {
        return false;
    }
    LOG(info, "Message version %s serialized at intermediate.",
        data._itrProtocol->getLastVersion().toString().c_str());
    minVersion = std::min(itrVersion, dstVersion);
    if (!EXPECT_TRUE(minVersion == data._itrProtocol->getLastVersion())) {
        return false;
    }

    LOG(info, "Message version %s reached destination.",
        data._dstProtocol->getLastVersion().toString().c_str());
    if (!EXPECT_TRUE(minVersion == data._dstProtocol->getLastVersion())) {
        return false;
    }
    Reply::UP reply(new SimpleReply("bar"));
    reply->swapState(*msg);
    data._dstSession->reply(std::move(reply));
    reply = data._itrHandler.getReply();
    if (!EXPECT_TRUE(reply)) {
        return false;
    }
    LOG(info, "Reply version %s serialized at destination.",
        data._dstProtocol->getLastVersion().toString().c_str());
    if (!EXPECT_TRUE(minVersion == data._dstProtocol->getLastVersion())) {
        return false;
    }

    LOG(info, "Reply version %s reached intermediate.",
        data._itrProtocol->getLastVersion().toString().c_str());
    if (!EXPECT_TRUE(minVersion == data._itrProtocol->getLastVersion())) {
        return false;
    }
    data._itrSession->forward(std::move(reply));
    reply = data._srcHandler.getReply();
    if (!EXPECT_TRUE(reply)) {
        return false;
    }
    LOG(info, "Reply version %s serialized at intermediate.",
        data._dstProtocol->getLastVersion().toString().c_str());
    minVersion = std::min(srcVersion, itrVersion);
    if (!EXPECT_TRUE(minVersion == data._itrProtocol->getLastVersion())) {
        return false;
    }

    LOG(info, "Reply version %s reached source.",
        data._srcProtocol->getLastVersion().toString().c_str());
    if (!EXPECT_TRUE(minVersion == data._srcProtocol->getLastVersion())) {
        return false;
    }
    return true;
}

void
testSendAdapters(TestData &data, const std::vector<vespalib::Version> & versions)
{
    for (vespalib::Version src : versions) {
        for (vespalib::Version intermediate : versions) {
            for (vespalib::Version dst : versions) {
                EXPECT_TRUE(testVersionedSend(data, src, intermediate, dst));
            }
        }
    }
}

TEST("test that all known versions are present") {
    TestData data;
    ASSERT_TRUE(data.start());
    EXPECT_FALSE(data._srcServer.net.getSendAdapter(vespalib::Version(4, 999)) != nullptr);
    EXPECT_TRUE(data._srcServer.net.getSendAdapter(vespalib::Version(5, 0)) != nullptr);
    EXPECT_TRUE(dynamic_cast<mbus::RPCSendV1 *>(data._srcServer.net.getSendAdapter(vespalib::Version(5, 0))) != nullptr);
    EXPECT_TRUE(data._srcServer.net.getSendAdapter(vespalib::Version(6, 148)) != nullptr);
    EXPECT_TRUE(dynamic_cast<mbus::RPCSendV1 *>(data._srcServer.net.getSendAdapter(vespalib::Version(6, 148))) != nullptr);
    EXPECT_TRUE(data._srcServer.net.getSendAdapter(vespalib::Version(6, 149)) != nullptr);
    EXPECT_TRUE(dynamic_cast<mbus::RPCSendV2 *>(data._srcServer.net.getSendAdapter(vespalib::Version(6, 149))) != nullptr);
    EXPECT_TRUE(data._srcServer.net.getSendAdapter(vespalib::Version(9, 999)) != nullptr);
    EXPECT_TRUE(dynamic_cast<mbus::RPCSendV2 *>(data._srcServer.net.getSendAdapter(vespalib::Version(9, 999))) != nullptr);
}

TEST("test that we can send between multiple versions") {
    TestData data;
    ASSERT_TRUE(data.start());
    TEST_DO(testSendAdapters(data, {vespalib::Version(5, 0), vespalib::Version(6, 148), vespalib::Version(6, 149), vespalib::Version(9, 999)}));
}

TEST_MAIN() { TEST_RUN_ALL(); }
<|endoftext|>


<commit_before>// Based on Herb Sutter's talk at C++ and beyond 2012
// He made T t; mutable which I think is incorrect

#include <iostream>
#include <algorithm>
#include <string>
#include <thread>
#include <vector>

template <typename T>
class Monitor
{
	T t;
	std::mutex m;
public:
	Monitor(T _t) : t(_t) {}

	template <typename F>
	auto operator()(F f) -> decltype(f(t))
	{
		std::lock_guard<std::mutex> l(m);
		return f(t);
	}
};

int main(int argc, char* argv[])
{
	Monitor<std::ostream&> async_ios(std::ref(std::cout));

	std::vector<std::thread> threads;

	threads.emplace_back([&](){
			async_ios(
				[](std::ostream& io){ io << "Hello 1" << std::endl;});
			});
	threads.emplace_back([&](){
			async_ios(
				[](std::ostream& io){ io << "Hello 2" << std::endl;});
			});
	threads.emplace_back([&](){
			async_ios(
				[](std::ostream& io){ io << "Hello 3" << std::endl;});
			});
	threads.emplace_back([&](){
			async_ios(
				[](std::ostream& io){ io << "Hello 4" << std::endl;});
			});

	for(auto& t : threads) { t.join(); }
}

<commit_msg>Update Monitor/Monitor.cpp<commit_after>// Based on Herb Sutter's talk at C++ and beyond 2012
// He made operator() const which I think is incorrect

#include <iostream>
#include <algorithm>
#include <string>
#include <thread>
#include <vector>

template <typename T>
class Monitor
{
	T t;
	std::mutex m;
public:
	Monitor(T _t) : t(_t) {}

	template <typename F>
	auto operator()(F f) -> decltype(f(t))
	{
		std::lock_guard<std::mutex> l(m);
		return f(t);
	}
};

int main(int argc, char* argv[])
{
	Monitor<std::ostream&> async_ios(std::ref(std::cout));

	std::vector<std::thread> threads;

	threads.emplace_back([&](){
			async_ios(
				[](std::ostream& io){ io << "Hello 1" << std::endl;});
			});
	threads.emplace_back([&](){
			async_ios(
				[](std::ostream& io){ io << "Hello 2" << std::endl;});
			});
	threads.emplace_back([&](){
			async_ios(
				[](std::ostream& io){ io << "Hello 3" << std::endl;});
			});
	threads.emplace_back([&](){
			async_ios(
				[](std::ostream& io){ io << "Hello 4" << std::endl;});
			});

	for(auto& t : threads) { t.join(); }
}

<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2009-2010 TECHNOGERMA Systems France and/or its subsidiary(-ies).
** Contact: Technogerma Systems France Information (contact@technogerma.fr)
**
** This file is part of the CAMP library.
**
** CAMP is free software: you can redistribute it and/or modify
** it under the terms of the GNU Lesser General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CAMP 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 Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public License
** along with CAMP.  If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/

#include "mapper.hpp"
#include <camp/classget.hpp>
#include <boost/test/unit_test.hpp>

using namespace MapperTest;

//-----------------------------------------------------------------------------
struct MapperFixture
{
    MapperFixture()
    {
        metaclass = &camp::classByType<MyClass>();
    }

    const camp::Class* metaclass;
};

//-----------------------------------------------------------------------------
//                         Tests for mappers
//-----------------------------------------------------------------------------
BOOST_FIXTURE_TEST_SUITE(MAPPER, MapperFixture)

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(count)
{
    BOOST_CHECK_EQUAL(metaclass->propertyCount(), MyClass::propertyCount);
    BOOST_CHECK_EQUAL(metaclass->functionCount(), MyClass::functionCount);
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(types)
{
    BOOST_CHECK_EQUAL(metaclass->property(0).type(), camp::intType);
    BOOST_CHECK_EQUAL(metaclass->function(0).returnType(), camp::stringType);
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(argCount)
{
    BOOST_CHECK_EQUAL(metaclass->function(0).argCount(), 0);
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(propertyGet)
{
    MyClass object;

    BOOST_CHECK_EQUAL(metaclass->property("prop0").get(object), camp::Value(object.prop("prop0")));
    BOOST_CHECK_EQUAL(metaclass->property("prop1").get(object), camp::Value(object.prop("prop1")));
    BOOST_CHECK_EQUAL(metaclass->property("prop2").get(object), camp::Value(object.prop("prop2")));
    BOOST_CHECK_EQUAL(metaclass->property("prop3").get(object), camp::Value(object.prop("prop3")));
    BOOST_CHECK_EQUAL(metaclass->property("prop4").get(object), camp::Value(object.prop("prop4")));
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(propertySet)
{
    MyClass object;

    metaclass->property("prop0").set(object, 0);
    metaclass->property("prop1").set(object, 100);
    metaclass->property("prop2").set(object, 200);
    metaclass->property("prop3").set(object, 300);
    metaclass->property("prop4").set(object, 400);

    BOOST_CHECK_EQUAL(object.prop("prop0"), 0);
    BOOST_CHECK_EQUAL(object.prop("prop1"), 100);
    BOOST_CHECK_EQUAL(object.prop("prop2"), 200);
    BOOST_CHECK_EQUAL(object.prop("prop3"), 300);
    BOOST_CHECK_EQUAL(object.prop("prop4"), 400);
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(functionCall)
{
    MyClass object;

    BOOST_CHECK_EQUAL(metaclass->function("func0").call(object), camp::Value(object.func("func0")));
    BOOST_CHECK_EQUAL(metaclass->function("func1").call(object), camp::Value(object.func("func1")));
    BOOST_CHECK_EQUAL(metaclass->function("func2").call(object), camp::Value(object.func("func2")));
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>Fixed a compile error in unit tests<commit_after>/****************************************************************************
**
** Copyright (C) 2009-2010 TECHNOGERMA Systems France and/or its subsidiary(-ies).
** Contact: Technogerma Systems France Information (contact@technogerma.fr)
**
** This file is part of the CAMP library.
**
** CAMP is free software: you can redistribute it and/or modify
** it under the terms of the GNU Lesser General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CAMP 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 Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public License
** along with CAMP.  If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/

#include "mapper.hpp"
#include <camp/classget.hpp>
#include <boost/test/unit_test.hpp>

using namespace MapperTest;

//-----------------------------------------------------------------------------
struct MapperFixture
{
    MapperFixture()
    {
        metaclass = &camp::classByType<MyClass>();
    }

    const camp::Class* metaclass;
};

//-----------------------------------------------------------------------------
//                         Tests for mappers
//-----------------------------------------------------------------------------
BOOST_FIXTURE_TEST_SUITE(MAPPER, MapperFixture)

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(count)
{
    BOOST_CHECK_EQUAL(metaclass->propertyCount(), static_cast<std::size_t>(MyClass::propertyCount));
    BOOST_CHECK_EQUAL(metaclass->functionCount(), static_cast<std::size_t>(MyClass::functionCount));
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(types)
{
    BOOST_CHECK_EQUAL(metaclass->property(0).type(), camp::intType);
    BOOST_CHECK_EQUAL(metaclass->function(0).returnType(), camp::stringType);
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(argCount)
{
    BOOST_CHECK_EQUAL(metaclass->function(0).argCount(), 0);
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(propertyGet)
{
    MyClass object;

    BOOST_CHECK_EQUAL(metaclass->property("prop0").get(object), camp::Value(object.prop("prop0")));
    BOOST_CHECK_EQUAL(metaclass->property("prop1").get(object), camp::Value(object.prop("prop1")));
    BOOST_CHECK_EQUAL(metaclass->property("prop2").get(object), camp::Value(object.prop("prop2")));
    BOOST_CHECK_EQUAL(metaclass->property("prop3").get(object), camp::Value(object.prop("prop3")));
    BOOST_CHECK_EQUAL(metaclass->property("prop4").get(object), camp::Value(object.prop("prop4")));
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(propertySet)
{
    MyClass object;

    metaclass->property("prop0").set(object, 0);
    metaclass->property("prop1").set(object, 100);
    metaclass->property("prop2").set(object, 200);
    metaclass->property("prop3").set(object, 300);
    metaclass->property("prop4").set(object, 400);

    BOOST_CHECK_EQUAL(object.prop("prop0"), 0);
    BOOST_CHECK_EQUAL(object.prop("prop1"), 100);
    BOOST_CHECK_EQUAL(object.prop("prop2"), 200);
    BOOST_CHECK_EQUAL(object.prop("prop3"), 300);
    BOOST_CHECK_EQUAL(object.prop("prop4"), 400);
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(functionCall)
{
    MyClass object;

    BOOST_CHECK_EQUAL(metaclass->function("func0").call(object), camp::Value(object.func("func0")));
    BOOST_CHECK_EQUAL(metaclass->function("func1").call(object), camp::Value(object.func("func1")));
    BOOST_CHECK_EQUAL(metaclass->function("func2").call(object), camp::Value(object.func("func2")));
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>void runProtonAnalysisQA() {
  TStopwatch timer;
  timer.Start();

  runProof(200000,"/COMMON/COMMON/LHC08c11_10TeV_0.5T"); //use data sets
  //runProof(200); //use ascii files

  timer.Stop();
  timer.Print();
}

//_________________________________________________//
void runProof(Int_t stats = 0, const char* dataset = 0x0) {
  TStopwatch timer;
  timer.Start();

  TString outputFilename1 = "Protons.QA.root";
  TString outputFilename2 = "Protons.MC.QA.root";
  TString outputFilename3 = "Protons.QA.Histograms.root";

  printf("****** Connect to PROOF *******\n");
  TProof::Open("alicecaf.cern.ch");
  gProof->SetParallel();

  // Enable the Analysis Package
  gProof->UploadPackage("STEERBase.par");
  gProof->EnablePackage("STEERBase");
  gProof->UploadPackage("ESD.par");
  gProof->EnablePackage("ESD");
  gProof->UploadPackage("AOD.par");
  gProof->EnablePackage("AOD");
  gProof->UploadPackage("ANALYSIS.par");
  gProof->EnablePackage("ANALYSIS");
  gProof->UploadPackage("ANALYSISalice.par");
  gProof->EnablePackage("ANALYSISalice");
  gProof->UploadPackage("CORRFW.par");
  gProof->EnablePackage("CORRFW");
  gProof->UploadPackage("PWG2spectra.par");
  gProof->EnablePackage("PWG2spectra");

  gProof->Load("AliAnalysisTaskProtonsQA.cxx++");

  //____________________________________________//
  // Make the analysis manager
  AliAnalysisManager *mgr = new AliAnalysisManager("TestManager");
  AliVEventHandler* esdH = new AliESDInputHandler;
  mgr->SetInputEventHandler(esdH);
  AliMCEventHandler *mc = new AliMCEventHandler();
  mgr->SetMCtruthEventHandler(mc);

  //____________________________________________//
  // 1st Proton task
  AliAnalysisTaskProtonsQA *taskProtonsQA = new AliAnalysisTaskProtonsQA("TaskProtonsQA");
  mgr->AddTask(taskProtonsQA);

  // Create containers for input/output
  AliAnalysisDataContainer *cinput1 = mgr->CreateContainer("dataChain",
							   TChain::Class(),
							   AliAnalysisManager::kInputContainer);
  AliAnalysisDataContainer *coutput1 = mgr->CreateContainer("globalQAList",
							    TList::Class(),
							    AliAnalysisManager::kOutputContainer,
							    outputFilename1.Data());
  AliAnalysisDataContainer *coutput2 = mgr->CreateContainer("pdgCodeList",
							    TList::Class(),
							    AliAnalysisManager::kOutputContainer,
							    outputFilename2.Data());
  AliAnalysisDataContainer *coutput3 = mgr->CreateContainer("mcProcessList",
							    TList::Class(),
							    AliAnalysisManager::kOutputContainer,
							    outputFilename2.Data());
  AliAnalysisDataContainer *coutput4 = mgr->CreateContainer("acceptedCutList",
							    TList::Class(),
							    AliAnalysisManager::kOutputContainer,
							    outputFilename3.Data());
  AliAnalysisDataContainer *coutput5 = mgr->CreateContainer("acceptedDCAList",
							    TList::Class(),
							    AliAnalysisManager::kOutputContainer,
							    outputFilename3.Data());

  //____________________________________________//
  mgr->ConnectInput(taskProtonsQA,0,cinput1);
  mgr->ConnectOutput(taskProtonsQA,0,coutput1);
  mgr->ConnectOutput(taskProtonsQA,1,coutput2);
  mgr->ConnectOutput(taskProtonsQA,2,coutput3);
  mgr->ConnectOutput(taskProtonsQA,3,coutput4);
  mgr->ConnectOutput(taskProtonsQA,4,coutput5);
  if (!mgr->InitAnalysis()) return;
  mgr->PrintStatus();

  if(dataset)
    mgr->StartAnalysis("proof",dataset,stats);
  else {
    // You should get this macro and the txt file from:
    // http://aliceinfo.cern.ch/Offline/Analysis/CAF/
    gROOT->LoadMacro("CreateESDChain.C");
    TChain* chain = 0x0;
    chain = CreateESDChain("ESD82XX_30K.txt",stats);
    chain->SetBranchStatus("*Calo*",0);

    mgr->StartAnalysis("proof",chain);
    //mgr->StartAnalysis("local",chain);
  }

  timer.Stop();
  timer.Print();
}

//_________________________________________________//
Int_t setupPar(const char* pararchivename) {
  ///////////////////
  // Setup PAR File//
  ///////////////////
  if (pararchivename) {
    char processline[1024];
    sprintf(processline,".! tar xvzf %s.par",pararchivename);
    gROOT->ProcessLine(processline);
    const char* ocwd = gSystem->WorkingDirectory();
    gSystem->ChangeDirectory(pararchivename);

    // check for BUILD.sh and execute
    if (!gSystem->AccessPathName("PROOF-INF/BUILD.sh")) {
      printf("*******************************\n");
      printf("*** Building PAR archive    ***\n");
      printf("*******************************\n");

      if (gSystem->Exec("PROOF-INF/BUILD.sh")) {
        Error("runAnalysis","Cannot Build the PAR Archive! - Abort!");
        return -1;
      }
    }
    // check for SETUP.C and execute
    if (!gSystem->AccessPathName("PROOF-INF/SETUP.C")) {
      printf("*******************************\n");
      printf("*** Setup PAR archive       ***\n");
      printf("*******************************\n");
      gROOT->Macro("PROOF-INF/SETUP.C");
    }

    gSystem->ChangeDirectory("../");
  }
  return 1;
}
<commit_msg>Adding the file that didn't want to be added - sorry<commit_after>void runProtonAnalysisQA() {
  TStopwatch timer;
  timer.Start();

  runProof(200000,"/COMMON/COMMON/LHC08c11_10TeV_0.5T"); //use data sets
  //runInteractive("wn.xml");

  timer.Stop();
  timer.Print();
}

//_________________________________________________//
void runInteractive(const char *collectionfile) {
  TString outputFilename1 = "Protons.QA.root";
  TString outputFilename2 = "Protons.MC.QA.root";
  TString outputFilename3 = "Protons.QA.Histograms.root";
  TString outputFilename4 = "Protons.Efficiency.root";

  TGrid::Connect("alien://");

  //Setup the par files
  setupPar("STEERBase");
  gSystem->Load("libSTEERBase.so");
  setupPar("ESD");
  gSystem->Load("libESD.so");
  setupPar("AOD");
  gSystem->Load("libAOD.so");
  setupPar("ANALYSIS");
  gSystem->Load("libANALYSIS.so");
  setupPar("ANALYSISalice");
  gSystem->Load("libANALYSISalice.so");
  setupPar("CORRFW");
  gSystem->Load("libCORRFW.so");
  setupPar("PWG2spectra");
  gSystem->Load("libPWG2spectra.so");

  gROOT->LoadMacro("AliAnalysisTaskProtonsQA.cxx+");
  //____________________________________________//
  //Usage of event tags
  AliTagAnalysis *analysis = new AliTagAnalysis();
  TChain *chain = 0x0;
  chain = analysis->GetChainFromCollection(collectionfile,"esdTree");

  //____________________________________________//
  // Make the analysis manager
  AliAnalysisManager *mgr = new AliAnalysisManager("TestManager");
  AliVEventHandler* esdH = new AliESDInputHandler;
  mgr->SetInputEventHandler(esdH);
  AliMCEventHandler *mc = new AliMCEventHandler();
  mgr->SetMCtruthEventHandler(mc);

  //____________________________________________//
  // 1st Proton task
  AliAnalysisTaskProtonsQA *taskProtonsQA = new AliAnalysisTaskProtonsQA("TaskProtonsQA");
  mgr->AddTask(taskProtonsQA);

  // Create containers for input/output
  AliAnalysisDataContainer *cinput1 = mgr->CreateContainer("dataChain",
							   TChain::Class(),
							   AliAnalysisManager::kInputContainer);
  AliAnalysisDataContainer *coutput1 = mgr->CreateContainer("globalQAList",
							    TList::Class(),
							    AliAnalysisManager::kOutputContainer,
							    outputFilename1.Data());
  AliAnalysisDataContainer *coutput2 = mgr->CreateContainer("pdgCodeList",
							    TList::Class(),
							    AliAnalysisManager::kOutputContainer,
							    outputFilename2.Data());
  AliAnalysisDataContainer *coutput3 = mgr->CreateContainer("mcProcessList",
							    TList::Class(),
							    AliAnalysisManager::kOutputContainer,
							    outputFilename2.Data());
  AliAnalysisDataContainer *coutput4 = mgr->CreateContainer("acceptedCutList",
							    TList::Class(),
							    AliAnalysisManager::kOutputContainer,
							    outputFilename3.Data());
  AliAnalysisDataContainer *coutput5 = mgr->CreateContainer("acceptedDCAList",
							    TList::Class(),
							    AliAnalysisManager::kOutputContainer,
							    outputFilename3.Data());
  AliAnalysisDataContainer *coutput6 = mgr->CreateContainer("efficiencyList",
							    TList::Class(),
							    AliAnalysisManager::kOutputContainer,
							    outputFilename4.Data());

  //____________________________________________//
  mgr->ConnectInput(taskProtonsQA,0,cinput1);
  mgr->ConnectOutput(taskProtonsQA,0,coutput1);
  mgr->ConnectOutput(taskProtonsQA,1,coutput2);
  mgr->ConnectOutput(taskProtonsQA,2,coutput3);
  mgr->ConnectOutput(taskProtonsQA,3,coutput4);
  mgr->ConnectOutput(taskProtonsQA,4,coutput5);
  mgr->ConnectOutput(taskProtonsQA,5,coutput6);
  if (!mgr->InitAnalysis()) return;
  mgr->PrintStatus();
  mgr->StartAnalysis("local",chain);
}

//_________________________________________________//
void runProof(Int_t stats = 0, const char* dataset = 0x0) {
  TString outputFilename1 = "Protons.QA.root";
  TString outputFilename2 = "Protons.MC.QA.root";
  TString outputFilename3 = "Protons.QA.Histograms.root";
  TString outputFilename4 = "Protons.Efficiency.root";

  printf("****** Connect to PROOF *******\n");
  TProof::Open("alicecaf.cern.ch");
  gProof->SetParallel();

  // Enable the Analysis Package
  gProof->UploadPackage("STEERBase.par");
  gProof->EnablePackage("STEERBase");
  gProof->UploadPackage("ESD.par");
  gProof->EnablePackage("ESD");
  gProof->UploadPackage("AOD.par");
  gProof->EnablePackage("AOD");
  gProof->UploadPackage("ANALYSIS.par");
  gProof->EnablePackage("ANALYSIS");
  gProof->UploadPackage("ANALYSISalice.par");
  gProof->EnablePackage("ANALYSISalice");
  gProof->UploadPackage("CORRFW.par");
  gProof->EnablePackage("CORRFW");
  gProof->UploadPackage("PWG2spectra.par");
  gProof->EnablePackage("PWG2spectra");

  gProof->Load("AliAnalysisTaskProtonsQA.cxx++");

  //____________________________________________//
  // Make the analysis manager
  AliAnalysisManager *mgr = new AliAnalysisManager("TestManager");
  AliVEventHandler* esdH = new AliESDInputHandler;
  mgr->SetInputEventHandler(esdH);
  AliMCEventHandler *mc = new AliMCEventHandler();
  mgr->SetMCtruthEventHandler(mc);

  //____________________________________________//
  // 1st Proton task
  AliAnalysisTaskProtonsQA *taskProtonsQA = new AliAnalysisTaskProtonsQA("TaskProtonsQA");
  mgr->AddTask(taskProtonsQA);

  // Create containers for input/output
  AliAnalysisDataContainer *cinput1 = mgr->CreateContainer("dataChain",
							   TChain::Class(),
							   AliAnalysisManager::kInputContainer);
  AliAnalysisDataContainer *coutput1 = mgr->CreateContainer("globalQAList",
							    TList::Class(),
							    AliAnalysisManager::kOutputContainer,
							    outputFilename1.Data());
  AliAnalysisDataContainer *coutput2 = mgr->CreateContainer("pdgCodeList",
							    TList::Class(),
							    AliAnalysisManager::kOutputContainer,
							    outputFilename2.Data());
  AliAnalysisDataContainer *coutput3 = mgr->CreateContainer("mcProcessList",
							    TList::Class(),
							    AliAnalysisManager::kOutputContainer,
							    outputFilename2.Data());
  AliAnalysisDataContainer *coutput4 = mgr->CreateContainer("acceptedCutList",
							    TList::Class(),
							    AliAnalysisManager::kOutputContainer,
							    outputFilename3.Data());
  AliAnalysisDataContainer *coutput5 = mgr->CreateContainer("acceptedDCAList",
							    TList::Class(),
							    AliAnalysisManager::kOutputContainer,
							    outputFilename3.Data());
  AliAnalysisDataContainer *coutput6 = mgr->CreateContainer("efficiencyList",
							    TList::Class(),
							    AliAnalysisManager::kOutputContainer,
							    outputFilename4.Data());

  //____________________________________________//
  mgr->ConnectInput(taskProtonsQA,0,cinput1);
  mgr->ConnectOutput(taskProtonsQA,0,coutput1);
  mgr->ConnectOutput(taskProtonsQA,1,coutput2);
  mgr->ConnectOutput(taskProtonsQA,2,coutput3);
  mgr->ConnectOutput(taskProtonsQA,3,coutput4);
  mgr->ConnectOutput(taskProtonsQA,4,coutput5);
  mgr->ConnectOutput(taskProtonsQA,5,coutput6);
  if (!mgr->InitAnalysis()) return;
  mgr->PrintStatus();

  if(dataset)
    mgr->StartAnalysis("proof",dataset,stats);
  else {
    // You should get this macro and the txt file from:
    // http://aliceinfo.cern.ch/Offline/Analysis/CAF/
    gROOT->LoadMacro("CreateESDChain.C");
    TChain* chain = 0x0;
    chain = CreateESDChain("ESD82XX_30K.txt",stats);
    chain->SetBranchStatus("*Calo*",0);

    mgr->StartAnalysis("proof",chain);
    //mgr->StartAnalysis("local",chain);
  }
}

//_________________________________________________//
Int_t setupPar(const char* pararchivename) {
  ///////////////////
  // Setup PAR File//
  ///////////////////
  if (pararchivename) {
    char processline[1024];
    sprintf(processline,".! tar xvzf %s.par",pararchivename);
    gROOT->ProcessLine(processline);
    const char* ocwd = gSystem->WorkingDirectory();
    gSystem->ChangeDirectory(pararchivename);

    // check for BUILD.sh and execute
    if (!gSystem->AccessPathName("PROOF-INF/BUILD.sh")) {
      printf("*******************************\n");
      printf("*** Building PAR archive    ***\n");
      printf("*******************************\n");

      if (gSystem->Exec("PROOF-INF/BUILD.sh")) {
        Error("runAnalysis","Cannot Build the PAR Archive! - Abort!");
        return -1;
      }
    }
    // check for SETUP.C and execute
    if (!gSystem->AccessPathName("PROOF-INF/SETUP.C")) {
      printf("*******************************\n");
      printf("*** Setup PAR archive       ***\n");
      printf("*******************************\n");
      gROOT->Macro("PROOF-INF/SETUP.C");
    }

    gSystem->ChangeDirectory("../");
  }
  return 1;
}
<|endoftext|>


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

#include "pbrt.h"
#include "rng.h"
#include "efloat.h"
#include "parallel.h"

static float GetFloat(RNG &rng) {
    float f;
    do {
        f = BitsToFloat(rng.UniformUInt32());
    } while (std::isnan(f));
    return f;
}

static double GetDouble(RNG &rng) {
    double d;
    do {
        d = BitsToFloat(uint64_t(rng.UniformUInt32()) |
                        (uint64_t(rng.UniformUInt32()) << 32));
    } while (std::isnan(d));
    return d;
}

TEST(FloatingPoint, NextUpDownFloat) {
    EXPECT_GT(NextFloatUp(-0.f), 0.f);
    EXPECT_LT(NextFloatDown(0.f), 0.f);

    EXPECT_EQ(NextFloatUp((float)Infinity), (float)Infinity);
    EXPECT_LT(NextFloatDown((float)Infinity), (float)Infinity);

    EXPECT_EQ(NextFloatDown(-(float)Infinity), -(float)Infinity);
    EXPECT_GT(NextFloatUp(-(float)Infinity), -(float)Infinity);

    RNG rng;
    for (int i = 0; i < 100000; ++i) {
        float f = GetFloat(rng);
        if (std::isinf(f)) continue;

        EXPECT_EQ(std::nextafter(f, (float)Infinity), NextFloatUp(f));
        EXPECT_EQ(std::nextafter(f, -(float)Infinity), NextFloatDown(f));
    }
}

TEST(FloatingPoint, NextUpDownDouble) {
    EXPECT_GT(NextFloatUp(-0.), 0.);
    EXPECT_LT(NextFloatDown(0.), 0.);

    EXPECT_EQ(NextFloatUp((double)Infinity), (double)Infinity);
    EXPECT_LT(NextFloatDown((double)Infinity), (double)Infinity);

    EXPECT_EQ(NextFloatDown(-(double)Infinity), -(double)Infinity);
    EXPECT_GT(NextFloatUp(-(double)Infinity), -(double)Infinity);

    RNG rng(3);
    for (int i = 0; i < 100000; ++i) {
        double d = GetDouble(rng);
        if (std::isinf(d)) continue;

        EXPECT_EQ(std::nextafter(d, (double)Infinity), NextFloatUp(d));
        EXPECT_EQ(std::nextafter(d, -(double)Infinity), NextFloatDown(d));
    }
}

TEST(FloatingPoint, FloatBits) {
    RNG rng(1);
    for (int i = 0; i < 100000; ++i) {
        uint32_t ui = rng.UniformUInt32();
        float f = BitsToFloat(ui);
        if (std::isnan(f)) continue;

        EXPECT_EQ(ui, FloatToBits(f));
    }
}

TEST(FloatingPoint, DoubleBits) {
    RNG rng(2);
    for (int i = 0; i < 100000; ++i) {
        uint64_t ui = (uint64_t(rng.UniformUInt32()) |
                       (uint64_t(rng.UniformUInt32()) << 32));
        double f = BitsToFloat(ui);

        if (std::isnan(f)) continue;

        EXPECT_EQ(ui, FloatToBits(f));
    }
}

TEST(FloatingPoint, EFloat) {
    for (int trial = 0; trial < 100000; ++trial) {
        RNG rng(trial);
        // Return an exponentially-distributed floating-point value.
        auto getFloat = [&rng](Float minExp = -4., Float maxExp = 4.) {
            Float logu = Lerp(rng.UniformFloat(), minExp, maxExp);
            Float sign = rng.UniformFloat() < .5 ? -1. : 1.;
            return sign * std::pow(10, logu);
        };

        // We'll track a value in an EFloat and a precise version of the
        // same value. As we perform random arithmetic operations to ef,
        // we'll perform the same operations on efPrecise and make sure
        // that efPrecise remains within the error bounds that ef has
        // computed..
        EFloat ef = getFloat();
        long double efPrecise = (float)ef;

        for (int iter = 0; iter < 100; ++iter) {
            // Exit out if we've gone off to a bad place.
            if (std::isnan((float)ef) || std::isnan(efPrecise) ||
                std::isinf((float)ef) || std::isinf(efPrecise) ||
                std::isinf(ef.GetAbsoluteError()))
                break;

            // Make sure that the precise value is inside the bounds of the
            // EFloat's interval.
            EXPECT_GE(efPrecise, ef.LowerBound()) << "trial " << trial
                                                  << ", iter " << iter;
            EXPECT_LE(efPrecise, ef.UpperBound()) << "trial " << trial
                                                  << ", iter " << iter;

            // Choose a second value to (maybe) use as an operand below.
            EFloat ef2 = 0;
            long double efPrecise2;
            switch (rng.UniformUInt32(3)) {
            case 0: {
                // Choose a new random floating-point value; assume it is
                // fully precise.
                Float f = getFloat();
                ef2 = f;
                efPrecise2 = f;
                break;
            }
            case 1:
                // Use the same value as ef.
                ef2 = ef;
                efPrecise2 = efPrecise;
                break;
            case 2: {
                // Choose a random float and make up a small interval around it.
                Float f = getFloat();
                Float err = std::abs(getFloat(-8., -2.) * f);
                ef2 = EFloat(f, err);
                // Now compute a 'precise' value that's not equal to f, but
                // is instead somewhere within the error bounds.
                Float offset = rng.UniformFloat();
                efPrecise2 = (1. - offset) * ef2.LowerBound() +
                             offset * ef2.UpperBound();
                // Sometimes the result isn't actually inside the bounds,
                // due to round-off error.
                if (efPrecise2 >= ef2.UpperBound() ||
                    efPrecise2 <= ef2.LowerBound())
                    efPrecise2 = f;
#ifndef NDEBUG
                ef2 = EFloat(f, efPrecise2, err);
#else
                ef2 = EFloat(f, err);
#endif
                break;
            }
            }

            // Now do a random operation, upading the separate precise
            // value in the same manner.
            switch (rng.UniformUInt32(7)) {
            case 0:
                // Negation.
                ef = -ef;
                efPrecise = -efPrecise;
                break;
            case 1:
                // Addition.
                ef = ef + ef2;
                efPrecise = efPrecise + efPrecise2;
                break;
            case 2:
                // Subtraction.
                ef = ef - ef2;
                efPrecise = efPrecise - efPrecise2;
                break;
            case 3:
                // Multiplication.
                ef = ef * ef2;
                efPrecise = efPrecise * efPrecise2;
                break;
            case 4:
                // Division.
                if (ef.LowerBound() * ef.UpperBound() > 0 &&
                    ef2.LowerBound() * ef2.UpperBound() > 0) {
                    ef = ef / ef2;
                    efPrecise = efPrecise / efPrecise2;
                }
                break;
            case 5:
                // Sqrt.
                if (efPrecise >= 0 && ef.LowerBound() > 0) {
                    ef = sqrt(ef);
                    efPrecise = std::sqrt(efPrecise);
                }
                break;
            case 6:
                // Abs.
                ef = abs(ef);
                efPrecise = (efPrecise < 0) ? -efPrecise : efPrecise;
                break;
            }
        }
    }
}

TEST(FloatingPoint, AtomicFloat) {
    AtomicFloat af(0);
    Float f = 0.;
    EXPECT_EQ(f, af);
    af.Add(1.0251);
    f += 1.0251;
    EXPECT_EQ(f, af);
    af.Add(2.);
    f += 2.;
    EXPECT_EQ(f, af);
}
<commit_msg>Improve EFloat tests.<commit_after>
#include "tests/gtest/gtest.h"
#include <cmath>

#include "pbrt.h"
#include "rng.h"
#include "efloat.h"
#include "parallel.h"

static float GetFloat(RNG &rng) {
    float f;
    do {
        f = BitsToFloat(rng.UniformUInt32());
    } while (std::isnan(f));
    return f;
}

static double GetDouble(RNG &rng) {
    double d;
    do {
        d = BitsToFloat(uint64_t(rng.UniformUInt32()) |
                        (uint64_t(rng.UniformUInt32()) << 32));
    } while (std::isnan(d));
    return d;
}

TEST(FloatingPoint, NextUpDownFloat) {
    EXPECT_GT(NextFloatUp(-0.f), 0.f);
    EXPECT_LT(NextFloatDown(0.f), 0.f);

    EXPECT_EQ(NextFloatUp((float)Infinity), (float)Infinity);
    EXPECT_LT(NextFloatDown((float)Infinity), (float)Infinity);

    EXPECT_EQ(NextFloatDown(-(float)Infinity), -(float)Infinity);
    EXPECT_GT(NextFloatUp(-(float)Infinity), -(float)Infinity);

    RNG rng;
    for (int i = 0; i < 100000; ++i) {
        float f = GetFloat(rng);
        if (std::isinf(f)) continue;

        EXPECT_EQ(std::nextafter(f, (float)Infinity), NextFloatUp(f));
        EXPECT_EQ(std::nextafter(f, -(float)Infinity), NextFloatDown(f));
    }
}

TEST(FloatingPoint, NextUpDownDouble) {
    EXPECT_GT(NextFloatUp(-0.), 0.);
    EXPECT_LT(NextFloatDown(0.), 0.);

    EXPECT_EQ(NextFloatUp((double)Infinity), (double)Infinity);
    EXPECT_LT(NextFloatDown((double)Infinity), (double)Infinity);

    EXPECT_EQ(NextFloatDown(-(double)Infinity), -(double)Infinity);
    EXPECT_GT(NextFloatUp(-(double)Infinity), -(double)Infinity);

    RNG rng(3);
    for (int i = 0; i < 100000; ++i) {
        double d = GetDouble(rng);
        if (std::isinf(d)) continue;

        EXPECT_EQ(std::nextafter(d, (double)Infinity), NextFloatUp(d));
        EXPECT_EQ(std::nextafter(d, -(double)Infinity), NextFloatDown(d));
    }
}

TEST(FloatingPoint, FloatBits) {
    RNG rng(1);
    for (int i = 0; i < 100000; ++i) {
        uint32_t ui = rng.UniformUInt32();
        float f = BitsToFloat(ui);
        if (std::isnan(f)) continue;

        EXPECT_EQ(ui, FloatToBits(f));
    }
}

TEST(FloatingPoint, DoubleBits) {
    RNG rng(2);
    for (int i = 0; i < 100000; ++i) {
        uint64_t ui = (uint64_t(rng.UniformUInt32()) |
                       (uint64_t(rng.UniformUInt32()) << 32));
        double f = BitsToFloat(ui);

        if (std::isnan(f)) continue;

        EXPECT_EQ(ui, FloatToBits(f));
    }
}

TEST(FloatingPoint, AtomicFloat) {
    AtomicFloat af(0);
    Float f = 0.;
    EXPECT_EQ(f, af);
    af.Add(1.0251);
    f += 1.0251;
    EXPECT_EQ(f, af);
    af.Add(2.);
    f += 2.;
    EXPECT_EQ(f, af);
}

///////////////////////////////////////////////////////////////////////////
// EFloat tests

// Return an exponentially-distributed floating-point value.
static EFloat getFloat(RNG &rng, Float minExp = -6., Float maxExp = 6.) {
    Float logu = Lerp(rng.UniformFloat(), minExp, maxExp);
    Float val = std::pow(10, logu);

    // Choose a random error bound.
    Float err = 0;
    switch (rng.UniformUInt32(4)) {
    case 0:
        // no error
        break;
    case 1: {
        // small typical/reasonable error
        uint32_t ulpError = rng.UniformUInt32(1024);
        Float offset = BitsToFloat(FloatToBits(val) + ulpError);
        err = std::abs(offset - val);
        break;
    }
    case 2: {
        // bigger ~reasonable error
        uint32_t ulpError = rng.UniformUInt32(1024 * 1024);
        Float offset = BitsToFloat(FloatToBits(val) + ulpError);
        err = std::abs(offset - val);
        break;
    }
    case 3: {
        err = (4 * rng.UniformFloat()) * std::abs(val);
    }
    }
    Float sign = rng.UniformFloat() < .5 ? -1. : 1.;
    return EFloat(sign * val, err);
}

// Given an EFloat covering some range, choose a double-precision "precise"
// value that is in the EFloat's range.
static double getPrecise(const EFloat &ef, RNG &rng) {
    switch (rng.UniformUInt32(3)) {
    // 2/3 of the time, pick a value that is right at the end of the range;
    // this is a maximally difficult / adversarial choice, so should help
    // ferret out any bugs.
    case 0:
        return ef.LowerBound();
    case 1:
        return ef.UpperBound();
    case 2: {
        // Otherwise choose a value uniformly inside the EFloat's range.
        Float t = rng.UniformFloat();
        double p = (1 - t) * ef.LowerBound() + t * ef.UpperBound();
        if (p > ef.UpperBound()) p = ef.UpperBound();
        if (p < ef.LowerBound()) p = ef.LowerBound();
        return p;
    }
    }
    return (Float)ef;  // NOTREACHED
}

static const int kEFloatIters = 1000000;

TEST(EFloat, Abs) {
    for (int trial = 0; trial < kEFloatIters; ++trial) {
        RNG rng(trial);

        EFloat ef = getFloat(rng);
        double precise = getPrecise(ef, rng);

        EFloat efResult = abs(ef);
        double preciseResult = std::abs(precise);

        EXPECT_GE(preciseResult, efResult.LowerBound());
        EXPECT_LE(preciseResult, efResult.UpperBound());
    }
}

TEST(EFloat, Sqrt) {
    for (int trial = 0; trial < kEFloatIters; ++trial) {
        RNG rng(trial);

        EFloat ef = getFloat(rng);
        double precise = getPrecise(ef, rng);

        // If the error starts to get too big such that the interval is
        // relatively close to zero w.r.t. the center value, we can't
        // compute error bounds for sqrt; skip these.
        if (ef.GetAbsoluteError() > .25 * std::abs(ef.LowerBound())) continue;

        EFloat efResult = sqrt(abs(ef));
        double preciseResult = std::sqrt(std::abs(precise));

        EXPECT_GE(preciseResult, efResult.LowerBound());
        EXPECT_LE(preciseResult, efResult.UpperBound());
    }
}

TEST(EFloat, Add) {
    for (int trial = 0; trial < kEFloatIters; ++trial) {
        RNG rng(trial);

        EFloat ef[2] = {getFloat(rng), getFloat(rng)};
        double precise[2] = {getPrecise(ef[0], rng), getPrecise(ef[1], rng)};

        EFloat efResult = ef[0] + ef[1];
        float preciseResult = precise[0] + precise[1];

        EXPECT_GE(preciseResult, efResult.LowerBound());
        EXPECT_LE(preciseResult, efResult.UpperBound());
    }
}

TEST(EFloat, Sub) {
    for (int trial = 0; trial < kEFloatIters; ++trial) {
        RNG rng(trial);

        EFloat ef[2] = {getFloat(rng), getFloat(rng)};
        double precise[2] = {getPrecise(ef[0], rng), getPrecise(ef[1], rng)};

        EFloat efResult = ef[0] - ef[1];
        float preciseResult = precise[0] - precise[1];

        EXPECT_GE(preciseResult, efResult.LowerBound());
        EXPECT_LE(preciseResult, efResult.UpperBound());
    }
}

TEST(EFloat, Mul) {
    for (int trial = 0; trial < kEFloatIters; ++trial) {
        RNG rng(trial);

        EFloat ef[2] = {getFloat(rng), getFloat(rng)};
        double precise[2] = {getPrecise(ef[0], rng), getPrecise(ef[1], rng)};

        EFloat efResult = ef[0] * ef[1];
        float preciseResult = precise[0] * precise[1];

        EXPECT_GE(preciseResult, efResult.LowerBound());
        EXPECT_LE(preciseResult, efResult.UpperBound());
    }
}

TEST(EFloat, Div) {
    for (int trial = 0; trial < kEFloatIters; ++trial) {
        RNG rng(trial);

        EFloat ef[2] = {getFloat(rng), getFloat(rng)};
        double precise[2] = {getPrecise(ef[0], rng), getPrecise(ef[1], rng)};

        // As with sqrt, things get messy if the denominator's interval
        // straddles zero or is too close to zero w.r.t. its center value.
        if (ef[1].LowerBound() * ef[1].UpperBound() < 0. ||
            ef[1].GetAbsoluteError() > .25 * std::abs(ef[1].LowerBound()))
            continue;

        EFloat efResult = ef[0] / ef[1];
        float preciseResult = precise[0] / precise[1];

        EXPECT_GE(preciseResult, efResult.LowerBound());
        EXPECT_LE(preciseResult, efResult.UpperBound());
    }
}
<|endoftext|>