<commit_before>/*
 * #%L
 * %%
 * Copyright (C) 2019 BMW Car IT GmbH
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
#include <chrono>
#include <functional>
#include <memory>
#include <string>

#include "tests/utils/Gmock.h"
#include "tests/utils/Gtest.h"

#include "joynr/BrokerUrl.h"
#include "joynr/MqttMessagingSkeleton.h"
#include "joynr/MqttReceiver.h"
#include "libjoynrclustercontroller/mqtt/MosquittoConnection.h"

#include "joynr/BroadcastSubscriptionRequest.h"
#include "joynr/ClusterControllerSettings.h"
#include "joynr/ImmutableMessage.h"
#include "joynr/MessagingQos.h"
#include "joynr/MessagingSettings.h"
#include "joynr/MulticastPublication.h"
#include "joynr/MulticastSubscriptionRequest.h"
#include "joynr/MutableMessage.h"
#include "joynr/MutableMessageFactory.h"
#include "joynr/Request.h"
#include "joynr/Settings.h"
#include "joynr/SingleThreadedIOService.h"
#include "joynr/SubscriptionRequest.h"
#include "joynr/system/RoutingTypes/MqttAddress.h"

#include "tests/JoynrTest.h"
#include "tests/mock/MockMessageRouter.h"

using ::testing::_;
using ::testing::A;
using ::testing::AllOf;
using ::testing::Eq;
using ::testing::Pointee;
using ::testing::Property;

using namespace ::testing;
using namespace joynr;

class MockMqttReceiver : public joynr::MqttReceiver
{
public:
    MockMqttReceiver(const MessagingSettings& messagingSettings,
                     const ClusterControllerSettings& ccSettings,
                     joynr::BrokerUrl brokerUrl,
                     std::chrono::seconds mqttKeepAliveTimeSeconds,
                     std::chrono::seconds mqttReconnectDelayTimeSeconds,
                     std::chrono::seconds mqttReconnectMaxDelayTimeSeconds,
                     bool isMqttExponentialBackoffEnabled,
                     std::string gbid,
                     std::string receiverId)
            : MqttReceiver(std::make_shared<MosquittoConnection>(ccSettings,
                                                                 brokerUrl,
                                                                 mqttKeepAliveTimeSeconds,
                                                                 mqttReconnectDelayTimeSeconds,
                                                                 mqttReconnectMaxDelayTimeSeconds,
                                                                 isMqttExponentialBackoffEnabled,
                                                                 receiverId,
                                                                 "gbid"),
                           messagingSettings,
                           "channelId",
                           gbid,
                           ccSettings.getMqttMulticastTopicPrefix())
    {
    }
    MOCK_METHOD1(subscribeToTopic, void(const std::string& topic));
    MOCK_METHOD1(unsubscribeFromTopic, void(const std::string& topic));
};

class MqttMessagingSkeletonTest : public ::testing::Test
{
public:
    MqttMessagingSkeletonTest()
            : _singleThreadedIOService(std::make_shared<SingleThreadedIOService>()),
              _mockMessageRouter(std::make_shared<MockMessageRouter>(
                      _singleThreadedIOService->getIOService())),
              _isLocalMessage(false),
              _settings(),
              _ccSettings(_settings),
              _brokerUrl("mqtt://testBrokerHost:1883"),
              _mqttKeepAliveTimeSeconds(1),
              _mqttReconnectDelayTimeSeconds(1),
              _mqttReconnectMaxDelayTimeSeconds(1),
              _isMqttExponentialBackoffEnabled(false),
              _receiverId("receiverId"),
              _testGbid("testGbid"),
              _mqttAddress(std::make_shared<joynr::system::RoutingTypes::MqttAddress>("fakegbid",
                                                                                "testtopic"))

    {
        _singleThreadedIOService->start();
    }

    ~MqttMessagingSkeletonTest()
    {
        _singleThreadedIOService->stop();
    }

    void SetUp()
    {
        // create a fake message
        std::string postFix;

        postFix = "_" + util::createUuid();
        _senderID = "senderId" + postFix;
        _receiverID = "receiverID" + postFix;
        _qosSettings = MessagingQos(456000);
        Request request;
        request.setMethodName("methodName");
        request.setParams(42, std::string("value"));
        std::vector<std::string> paramDatatypes;
        paramDatatypes.push_back("Integer");
        paramDatatypes.push_back("String");
        request.setParamDatatypes(paramDatatypes);

        _mutableMessage = _messageFactory.createRequest(
                _senderID, _receiverID, _qosSettings, request, _isLocalMessage);
        _serializedMqttAddress = joynr::serializer::serializeToJson(*_mqttAddress);
        _mutableMessage.setReplyTo(_serializedMqttAddress);
    }

protected:
    void transmitSetsIsReceivedFromGlobal();
    std::shared_ptr<SingleThreadedIOService> _singleThreadedIOService;
    std::shared_ptr<MockMessageRouter> _mockMessageRouter;
    MutableMessageFactory _messageFactory;
    MutableMessage _mutableMessage;
    std::string _senderID;
    std::string _receiverID;
    MessagingQos _qosSettings;
    const bool _isLocalMessage;
    Settings _settings;
    ClusterControllerSettings _ccSettings;

    joynr::BrokerUrl _brokerUrl;
    std::chrono::seconds _mqttKeepAliveTimeSeconds;
    std::chrono::seconds _mqttReconnectDelayTimeSeconds;
    std::chrono::seconds _mqttReconnectMaxDelayTimeSeconds;
    bool _isMqttExponentialBackoffEnabled;
    std::string _receiverId;
    std::string _testGbid;

    std::shared_ptr<joynr::system::RoutingTypes::MqttAddress> _mqttAddress;
    std::string _serializedMqttAddress;
};

MATCHER_P(pointerToMqttAddressWithChannelId, channelId, "")
{
    if (arg == nullptr) {
        return false;
    }
    std::shared_ptr<const joynr::system::RoutingTypes::MqttAddress> mqttAddress =
            std::dynamic_pointer_cast<const joynr::system::RoutingTypes::MqttAddress>(arg);
    if (mqttAddress == nullptr) {
        return false;
    }
    return mqttAddress->getTopic() == channelId;
}

MATCHER(isPointerToMqttAddress, "")
{
    if (arg == nullptr) {
        return false;
    }
    std::shared_ptr<const joynr::system::RoutingTypes::MqttAddress> mqttAddress =
            std::dynamic_pointer_cast<const joynr::system::RoutingTypes::MqttAddress>(arg);
    if (mqttAddress == nullptr) {
        return false;
    }
    return true;
}

TEST_F(MqttMessagingSkeletonTest, transmitTest)
{
    MqttMessagingSkeleton mqttMessagingSkeleton(
            _mockMessageRouter, nullptr, _ccSettings.getMqttMulticastTopicPrefix(), _testGbid);
    std::shared_ptr<ImmutableMessage> immutableMessage = _mutableMessage.getImmutableMessage();
    EXPECT_CALL(*_mockMessageRouter, route(immutableMessage, _)).Times(1);

    auto onFailure = [](const exceptions::JoynrRuntimeException&) { FAIL() << "onFailure called"; };
    mqttMessagingSkeleton.transmit(immutableMessage, onFailure);
}

TEST_F(MqttMessagingSkeletonTest, transmitTestWithMqttReplyToAddress)
{
    MqttMessagingSkeleton mqttMessagingSkeleton(
            _mockMessageRouter, nullptr, _ccSettings.getMqttMulticastTopicPrefix(), _testGbid);
    std::shared_ptr<ImmutableMessage> immutableMessage = _mutableMessage.getImmutableMessage();
    EXPECT_CALL(*_mockMessageRouter, route(immutableMessage, _)).Times(1);
    EXPECT_CALL(
            *_mockMessageRouter,
            addNextHop(
                    Eq(immutableMessage->getSender()),
                    AllOf(Property(
                                  &std::shared_ptr<const joynr::system::RoutingTypes::Address>::get,
                                  WhenDynamicCastTo<
                                          const joynr::system::RoutingTypes::MqttAddress*>(
                                          Pointee(AllOf(Property(&joynr::system::RoutingTypes::
                                                                         MqttAddress::getBrokerUri,
                                                                 Eq(_testGbid)),
                                                        Property(&joynr::system::RoutingTypes::
                                                                         MqttAddress::getTopic,
                                                                 Eq(_mqttAddress->getTopic())))))),
                          isPointerToMqttAddress()),
                    Eq(true), Eq(TimePoint::max().toMilliseconds()), Eq(false), _, _))
            .Times(1);
    auto onFailure = [](const exceptions::JoynrRuntimeException&) { FAIL() << "onFailure called"; };
    mqttMessagingSkeleton.transmit(immutableMessage, onFailure);
}

TEST_F(MqttMessagingSkeletonTest, transmitTestWithBrokenReplyToAddress)
{
    MqttMessagingSkeleton mqttMessagingSkeleton(
            _mockMessageRouter, nullptr, _ccSettings.getMqttMulticastTopicPrefix(), _testGbid);
    _mutableMessage.setReplyTo("thisisinvalid::==");
    std::shared_ptr<ImmutableMessage> immutableMessage = _mutableMessage.getImmutableMessage();
    auto semaphore = std::make_shared<joynr::Semaphore>(0);
    auto onFailure = [semaphore](const exceptions::JoynrRuntimeException&) {
        SUCCEED() << "onFailure called";
        semaphore->notify();
    };
    EXPECT_CALL(*_mockMessageRouter, route(_, _)).Times(0);
    EXPECT_CALL(*_mockMessageRouter, addNextHop(_, _, _, _, _, _, _)).Times(0);
    mqttMessagingSkeleton.transmit(immutableMessage, onFailure);
    ASSERT_TRUE(semaphore->waitFor(std::chrono::seconds(10)));
}

void MqttMessagingSkeletonTest::transmitSetsIsReceivedFromGlobal()
{
    MqttMessagingSkeleton mqttMessagingSkeleton(
            _mockMessageRouter, nullptr, _ccSettings.getMqttMulticastTopicPrefix(), _testGbid);
    std::shared_ptr<ImmutableMessage> immutableMessage = _mutableMessage.getImmutableMessage();
    EXPECT_FALSE(immutableMessage->isReceivedFromGlobal());
    auto onFailure = [](const exceptions::JoynrRuntimeException&) { FAIL() << "onFailure called"; };
    mqttMessagingSkeleton.transmit(immutableMessage, onFailure);
    EXPECT_TRUE(immutableMessage->isReceivedFromGlobal());
}

TEST_F(MqttMessagingSkeletonTest, transmitSetsReceivedFromGlobalForMulticastPublications)
{
    MulticastPublication publication;
    _mutableMessage =
            _messageFactory.createMulticastPublication(_senderID, _qosSettings, publication);
    transmitSetsIsReceivedFromGlobal();
}

TEST_F(MqttMessagingSkeletonTest, transmitSetsReceivedFromGlobalForRequests)
{
    Request request;
    _mutableMessage = _messageFactory.createRequest(
            _senderID, _receiverID, _qosSettings, request, _isLocalMessage);
    _mutableMessage.setReplyTo(_serializedMqttAddress);
    transmitSetsIsReceivedFromGlobal();
}

TEST_F(MqttMessagingSkeletonTest, transmitSetsReceivedFromGlobalForSubscriptionRequests)
{
    SubscriptionRequest request;
    _mutableMessage = _messageFactory.createSubscriptionRequest(
            _senderID, _receiverID, _qosSettings, request, _isLocalMessage);
    _mutableMessage.setReplyTo(_serializedMqttAddress);
    transmitSetsIsReceivedFromGlobal();
}

TEST_F(MqttMessagingSkeletonTest, transmitSetsIsReceivedFromGlobalForBroadcastSubscriptionRequests)
{
    BroadcastSubscriptionRequest request;
    _mutableMessage = _messageFactory.createBroadcastSubscriptionRequest(
            _senderID, _receiverID, _qosSettings, request, _isLocalMessage);
    _mutableMessage.setReplyTo(_serializedMqttAddress);
    transmitSetsIsReceivedFromGlobal();
}

TEST_F(MqttMessagingSkeletonTest, transmitSetsIsReceivedFromGlobalForMulticastSubscriptionRequests)
{
    MulticastSubscriptionRequest request;
    _mutableMessage = _messageFactory.createMulticastSubscriptionRequest(
            _senderID, _receiverID, _qosSettings, request, _isLocalMessage);
    _mutableMessage.setReplyTo(_serializedMqttAddress);
    transmitSetsIsReceivedFromGlobal();
}

TEST_F(MqttMessagingSkeletonTest, onMessageReceivedTest)
{
    MqttMessagingSkeleton mqttMessagingSkeleton(
            _mockMessageRouter, nullptr, _ccSettings.getMqttMulticastTopicPrefix(), _testGbid);
    std::unique_ptr<ImmutableMessage> immutableMessage = _mutableMessage.getImmutableMessage();

    EXPECT_CALL(*_mockMessageRouter,
                route(AllOf(MessageHasType(_mutableMessage.getType()),
                            ImmutableMessageHasPayload(_mutableMessage.getPayload())),
                      _))
            .Times(1);

    smrf::ByteVector serializedMessage = immutableMessage->getSerializedMessage();
    mqttMessagingSkeleton.onMessageReceived(std::move(serializedMessage));
}

TEST_F(MqttMessagingSkeletonTest, registerMulticastSubscription_subscribesToMqttTopic)
{
    std::string multicastId = "multicastId";
    Settings settings;
    ClusterControllerSettings ccSettings(settings);
    MessagingSettings messagingSettings(settings);
    auto mockMqttReceiver = std::make_shared<MockMqttReceiver>(messagingSettings,
                                                               ccSettings,
                                                               _brokerUrl,
                                                               _mqttKeepAliveTimeSeconds,
                                                               _mqttReconnectDelayTimeSeconds,
                                                               _mqttReconnectMaxDelayTimeSeconds,
                                                               _isMqttExponentialBackoffEnabled,
                                                               _testGbid,
                                                               _receiverId);
    MqttMessagingSkeleton mqttMessagingSkeleton(
            _mockMessageRouter, mockMqttReceiver, "", _testGbid);
    EXPECT_CALL(*mockMqttReceiver, subscribeToTopic(multicastId));
    mqttMessagingSkeleton.registerMulticastSubscription(multicastId);
}

TEST_F(MqttMessagingSkeletonTest, registerMulticastSubscription_subscribesToMqttTopicOnlyOnce)
{
    std::string multicastId = "multicastId";
    MessagingSettings messagingSettings(_settings);
    auto mockMqttReceiver = std::make_shared<MockMqttReceiver>(messagingSettings,
                                                               _ccSettings,
                                                               _brokerUrl,
                                                               _mqttKeepAliveTimeSeconds,
                                                               _mqttReconnectDelayTimeSeconds,
                                                               _mqttReconnectMaxDelayTimeSeconds,
                                                               _isMqttExponentialBackoffEnabled,
                                                               _testGbid,
                                                               _receiverId);
    MqttMessagingSkeleton mqttMessagingSkeleton(_mockMessageRouter, mockMqttReceiver,
                                                _ccSettings.getMqttMulticastTopicPrefix(),
                                                _testGbid);
    EXPECT_CALL(*mockMqttReceiver, subscribeToTopic(multicastId)).Times(1);
    mqttMessagingSkeleton.registerMulticastSubscription(multicastId);
    mqttMessagingSkeleton.registerMulticastSubscription(multicastId);
}

TEST_F(MqttMessagingSkeletonTest, unregisterMulticastSubscription_unsubscribesFromMqttTopic)
{
    std::string multicastId = "multicastId";
    MessagingSettings messagingSettings(_settings);
    auto mockMqttReceiver = std::make_shared<MockMqttReceiver>(messagingSettings,
                                                               _ccSettings,
                                                               _brokerUrl,
                                                               _mqttKeepAliveTimeSeconds,
                                                               _mqttReconnectDelayTimeSeconds,
                                                               _mqttReconnectMaxDelayTimeSeconds,
                                                               _isMqttExponentialBackoffEnabled,
                                                               _testGbid,
                                                               _receiverId);
    MqttMessagingSkeleton mqttMessagingSkeleton(_mockMessageRouter, mockMqttReceiver,
                                                _ccSettings.getMqttMulticastTopicPrefix(),
                                                _testGbid);
    EXPECT_CALL(*mockMqttReceiver, subscribeToTopic(multicastId)).Times(1);
    mqttMessagingSkeleton.registerMulticastSubscription(multicastId);

    EXPECT_CALL(*mockMqttReceiver, unsubscribeFromTopic(multicastId));
    mqttMessagingSkeleton.unregisterMulticastSubscription(multicastId);
}

TEST_F(MqttMessagingSkeletonTest,
       unregisterMulticastSubscription_unsubscribesFromMqttTopicOnlyForUnsubscribeOfLastReceiver)
{
    std::string multicastId = "multicastId";
    MessagingSettings messagingSettings(_settings);
    auto mockMqttReceiver = std::make_shared<MockMqttReceiver>(messagingSettings,
                                                               _ccSettings,
                                                               _brokerUrl,
                                                               _mqttKeepAliveTimeSeconds,
                                                               _mqttReconnectDelayTimeSeconds,
                                                               _mqttReconnectMaxDelayTimeSeconds,
                                                               _isMqttExponentialBackoffEnabled,
                                                               _testGbid,
                                                               _receiverId);
    MqttMessagingSkeleton mqttMessagingSkeleton(_mockMessageRouter, mockMqttReceiver,
                                                _ccSettings.getMqttMulticastTopicPrefix(),
                                                _testGbid);
    EXPECT_CALL(*mockMqttReceiver, subscribeToTopic(multicastId)).Times(1);
    mqttMessagingSkeleton.registerMulticastSubscription(multicastId);
    mqttMessagingSkeleton.registerMulticastSubscription(multicastId);

    EXPECT_CALL(*mockMqttReceiver, unsubscribeFromTopic(multicastId)).Times(0);
    mqttMessagingSkeleton.unregisterMulticastSubscription(multicastId);
    Mock::VerifyAndClearExpectations(mockMqttReceiver.get());

    EXPECT_CALL(*mockMqttReceiver, unsubscribeFromTopic(multicastId)).Times(1);
    mqttMessagingSkeleton.unregisterMulticastSubscription(multicastId);
}

TEST_F(MqttMessagingSkeletonTest, translateWildcard)
{
    std::map<std::string, std::string> input2expected = {
            {"partion0/partion1", "partion0/partion1"},
            {"partion0/partion1/*", "partion0/partion1/#"},
            {"partion0/partion1/+", "partion0/partion1/+"}};

    for (auto& testCase : input2expected) {
        std::string inputPartition = testCase.first;
        std::string expectedPartition = testCase.second;
        EXPECT_EQ(expectedPartition,
                  MqttMessagingSkeleton::translateMulticastWildcard(inputPartition));
    }
}
<commit_msg>[C++] Fix MqttMessagingSkeletonTest<commit_after>/*
 * #%L
 * %%
 * Copyright (C) 2019 BMW Car IT GmbH
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
#include <chrono>
#include <functional>
#include <memory>
#include <string>

#include "tests/utils/Gmock.h"
#include "tests/utils/Gtest.h"

#include "joynr/BrokerUrl.h"
#include "joynr/MqttMessagingSkeleton.h"
#include "joynr/MqttReceiver.h"
#include "libjoynrclustercontroller/mqtt/MosquittoConnection.h"

#include "joynr/BroadcastSubscriptionRequest.h"
#include "joynr/ClusterControllerSettings.h"
#include "joynr/ImmutableMessage.h"
#include "joynr/MessagingQos.h"
#include "joynr/MessagingSettings.h"
#include "joynr/MulticastPublication.h"
#include "joynr/MulticastSubscriptionRequest.h"
#include "joynr/MutableMessage.h"
#include "joynr/MutableMessageFactory.h"
#include "joynr/Request.h"
#include "joynr/Settings.h"
#include "joynr/SingleThreadedIOService.h"
#include "joynr/SubscriptionRequest.h"
#include "joynr/system/RoutingTypes/MqttAddress.h"

#include "tests/JoynrTest.h"
#include "tests/mock/MockMessageRouter.h"

using ::testing::_;
using ::testing::A;
using ::testing::AllOf;
using ::testing::Eq;
using ::testing::Pointee;
using ::testing::Property;

using namespace ::testing;
using namespace joynr;

class MockMqttReceiver : public joynr::MqttReceiver
{
public:
    MockMqttReceiver(const MessagingSettings& messagingSettings,
                     const ClusterControllerSettings& ccSettings,
                     joynr::BrokerUrl brokerUrl,
                     std::chrono::seconds mqttKeepAliveTimeSeconds,
                     std::chrono::seconds mqttReconnectDelayTimeSeconds,
                     std::chrono::seconds mqttReconnectMaxDelayTimeSeconds,
                     bool isMqttExponentialBackoffEnabled,
                     std::string gbid,
                     std::string receiverId)
            : MqttReceiver(std::make_shared<MosquittoConnection>(ccSettings,
                                                                 brokerUrl,
                                                                 mqttKeepAliveTimeSeconds,
                                                                 mqttReconnectDelayTimeSeconds,
                                                                 mqttReconnectMaxDelayTimeSeconds,
                                                                 isMqttExponentialBackoffEnabled,
                                                                 receiverId,
                                                                 "gbid"),
                           messagingSettings,
                           "channelId",
                           gbid,
                           ccSettings.getMqttMulticastTopicPrefix())
    {
    }
    MOCK_METHOD1(subscribeToTopic, void(const std::string& topic));
    MOCK_METHOD1(unsubscribeFromTopic, void(const std::string& topic));
};

class MqttMessagingSkeletonTest : public ::testing::Test
{
public:
    MqttMessagingSkeletonTest()
            : _singleThreadedIOService(std::make_shared<SingleThreadedIOService>()),
              _mockMessageRouter(std::make_shared<MockMessageRouter>(
                      _singleThreadedIOService->getIOService())),
              _isLocalMessage(false),
              _settings(),
              _ccSettings(_settings),
              _brokerUrl("mqtt://testBrokerHost:1883"),
              _mqttKeepAliveTimeSeconds(1),
              _mqttReconnectDelayTimeSeconds(1),
              _mqttReconnectMaxDelayTimeSeconds(1),
              _isMqttExponentialBackoffEnabled(false),
              _receiverId("receiverId"),
              _testGbid("testGbid"),
              _mqttAddress(std::make_shared<joynr::system::RoutingTypes::MqttAddress>("fakegbid",
                                                                                "testtopic"))

    {
        _singleThreadedIOService->start();
    }

    ~MqttMessagingSkeletonTest()
    {
        _singleThreadedIOService->stop();
    }

    void SetUp()
    {
        // create a fake message
        std::string postFix;

        postFix = "_" + util::createUuid();
        _senderID = "senderId" + postFix;
        _receiverID = "receiverID" + postFix;
        _qosSettings = MessagingQos(456000);
        Request request;
        request.setMethodName("methodName");
        request.setParams(42, std::string("value"));
        std::vector<std::string> paramDatatypes;
        paramDatatypes.push_back("Integer");
        paramDatatypes.push_back("String");
        request.setParamDatatypes(paramDatatypes);

        _mutableMessage = _messageFactory.createRequest(
                _senderID, _receiverID, _qosSettings, request, _isLocalMessage);
        _serializedMqttAddress = joynr::serializer::serializeToJson(*_mqttAddress);
        _mutableMessage.setReplyTo(_serializedMqttAddress);
    }

protected:
    void transmitSetsIsReceivedFromGlobal();
    std::shared_ptr<SingleThreadedIOService> _singleThreadedIOService;
    std::shared_ptr<MockMessageRouter> _mockMessageRouter;
    MutableMessageFactory _messageFactory;
    MutableMessage _mutableMessage;
    std::string _senderID;
    std::string _receiverID;
    MessagingQos _qosSettings;
    const bool _isLocalMessage;
    Settings _settings;
    ClusterControllerSettings _ccSettings;

    joynr::BrokerUrl _brokerUrl;
    std::chrono::seconds _mqttKeepAliveTimeSeconds;
    std::chrono::seconds _mqttReconnectDelayTimeSeconds;
    std::chrono::seconds _mqttReconnectMaxDelayTimeSeconds;
    bool _isMqttExponentialBackoffEnabled;
    std::string _receiverId;
    std::string _testGbid;

    std::shared_ptr<joynr::system::RoutingTypes::MqttAddress> _mqttAddress;
    std::string _serializedMqttAddress;
};

MATCHER_P(pointerToMqttAddressWithChannelId, channelId, "")
{
    if (arg == nullptr) {
        return false;
    }
    std::shared_ptr<const joynr::system::RoutingTypes::MqttAddress> mqttAddress =
            std::dynamic_pointer_cast<const joynr::system::RoutingTypes::MqttAddress>(arg);
    if (mqttAddress == nullptr) {
        return false;
    }
    return mqttAddress->getTopic() == channelId;
}

MATCHER(isPointerToMqttAddress, "")
{
    if (arg == nullptr) {
        return false;
    }
    std::shared_ptr<const joynr::system::RoutingTypes::MqttAddress> mqttAddress =
            std::dynamic_pointer_cast<const joynr::system::RoutingTypes::MqttAddress>(arg);
    if (mqttAddress == nullptr) {
        return false;
    }
    return true;
}

TEST_F(MqttMessagingSkeletonTest, transmitTest)
{
    std::shared_ptr<ImmutableMessage> immutableMessage = _mutableMessage.getImmutableMessage();
    EXPECT_CALL(*_mockMessageRouter, route(immutableMessage, _)).Times(1);
    MqttMessagingSkeleton mqttMessagingSkeleton(
            _mockMessageRouter, nullptr, _ccSettings.getMqttMulticastTopicPrefix(), _testGbid);

    auto onFailure = [](const exceptions::JoynrRuntimeException&) { FAIL() << "onFailure called"; };
    mqttMessagingSkeleton.transmit(immutableMessage, onFailure);
}

TEST_F(MqttMessagingSkeletonTest, transmitTestWithMqttReplyToAddress)
{
    std::shared_ptr<ImmutableMessage> immutableMessage = _mutableMessage.getImmutableMessage();
    EXPECT_CALL(*_mockMessageRouter, route(immutableMessage, _)).Times(1);
    EXPECT_CALL(
            *_mockMessageRouter,
            addNextHop(
                    Eq(immutableMessage->getSender()),
                    AllOf(Property(
                                  &std::shared_ptr<const joynr::system::RoutingTypes::Address>::get,
                                  WhenDynamicCastTo<
                                          const joynr::system::RoutingTypes::MqttAddress*>(
                                          Pointee(AllOf(Property(&joynr::system::RoutingTypes::
                                                                         MqttAddress::getBrokerUri,
                                                                 Eq(_testGbid)),
                                                        Property(&joynr::system::RoutingTypes::
                                                                         MqttAddress::getTopic,
                                                                 Eq(_mqttAddress->getTopic())))))),
                          isPointerToMqttAddress()),
                    Eq(true), Eq(TimePoint::max().toMilliseconds()), Eq(false), _, _))
            .Times(1);

    MqttMessagingSkeleton mqttMessagingSkeleton(
            _mockMessageRouter, nullptr, _ccSettings.getMqttMulticastTopicPrefix(), _testGbid);
    auto onFailure = [](const exceptions::JoynrRuntimeException&) { FAIL() << "onFailure called"; };
    mqttMessagingSkeleton.transmit(immutableMessage, onFailure);
}

TEST_F(MqttMessagingSkeletonTest, transmitTestWithBrokenReplyToAddress)
{
    EXPECT_CALL(*_mockMessageRouter, route(_, _)).Times(0);
    EXPECT_CALL(*_mockMessageRouter, addNextHop(_, _, _, _, _, _, _)).Times(0);
    MqttMessagingSkeleton mqttMessagingSkeleton(
            _mockMessageRouter, nullptr, _ccSettings.getMqttMulticastTopicPrefix(), _testGbid);
    _mutableMessage.setReplyTo("thisisinvalid::==");
    std::shared_ptr<ImmutableMessage> immutableMessage = _mutableMessage.getImmutableMessage();
    auto semaphore = std::make_shared<joynr::Semaphore>(0);
    auto onFailure = [semaphore](const exceptions::JoynrRuntimeException&) {
        SUCCEED() << "onFailure called";
        semaphore->notify();
    };

    mqttMessagingSkeleton.transmit(immutableMessage, onFailure);
    ASSERT_TRUE(semaphore->waitFor(std::chrono::seconds(10)));
}

void MqttMessagingSkeletonTest::transmitSetsIsReceivedFromGlobal()
{
    MqttMessagingSkeleton mqttMessagingSkeleton(
            _mockMessageRouter, nullptr, _ccSettings.getMqttMulticastTopicPrefix(), _testGbid);
    std::shared_ptr<ImmutableMessage> immutableMessage = _mutableMessage.getImmutableMessage();
    EXPECT_FALSE(immutableMessage->isReceivedFromGlobal());
    auto onFailure = [](const exceptions::JoynrRuntimeException&) { FAIL() << "onFailure called"; };
    mqttMessagingSkeleton.transmit(immutableMessage, onFailure);
    EXPECT_TRUE(immutableMessage->isReceivedFromGlobal());
}

TEST_F(MqttMessagingSkeletonTest, transmitSetsReceivedFromGlobalForMulticastPublications)
{
    MulticastPublication publication;
    _mutableMessage =
            _messageFactory.createMulticastPublication(_senderID, _qosSettings, publication);
    transmitSetsIsReceivedFromGlobal();
}

TEST_F(MqttMessagingSkeletonTest, transmitSetsReceivedFromGlobalForRequests)
{
    Request request;
    _mutableMessage = _messageFactory.createRequest(
            _senderID, _receiverID, _qosSettings, request, _isLocalMessage);
    _mutableMessage.setReplyTo(_serializedMqttAddress);
    transmitSetsIsReceivedFromGlobal();
}

TEST_F(MqttMessagingSkeletonTest, transmitSetsReceivedFromGlobalForSubscriptionRequests)
{
    SubscriptionRequest request;
    _mutableMessage = _messageFactory.createSubscriptionRequest(
            _senderID, _receiverID, _qosSettings, request, _isLocalMessage);
    _mutableMessage.setReplyTo(_serializedMqttAddress);
    transmitSetsIsReceivedFromGlobal();
}

TEST_F(MqttMessagingSkeletonTest, transmitSetsIsReceivedFromGlobalForBroadcastSubscriptionRequests)
{
    BroadcastSubscriptionRequest request;
    _mutableMessage = _messageFactory.createBroadcastSubscriptionRequest(
            _senderID, _receiverID, _qosSettings, request, _isLocalMessage);
    _mutableMessage.setReplyTo(_serializedMqttAddress);
    transmitSetsIsReceivedFromGlobal();
}

TEST_F(MqttMessagingSkeletonTest, transmitSetsIsReceivedFromGlobalForMulticastSubscriptionRequests)
{
    MulticastSubscriptionRequest request;
    _mutableMessage = _messageFactory.createMulticastSubscriptionRequest(
            _senderID, _receiverID, _qosSettings, request, _isLocalMessage);
    _mutableMessage.setReplyTo(_serializedMqttAddress);
    transmitSetsIsReceivedFromGlobal();
}

TEST_F(MqttMessagingSkeletonTest, onMessageReceivedTest)
{
    EXPECT_CALL(*_mockMessageRouter,
                route(AllOf(MessageHasType(_mutableMessage.getType()),
                            ImmutableMessageHasPayload(_mutableMessage.getPayload())),
                      _))
            .Times(1);
    MqttMessagingSkeleton mqttMessagingSkeleton(
            _mockMessageRouter, nullptr, _ccSettings.getMqttMulticastTopicPrefix(), _testGbid);
    std::unique_ptr<ImmutableMessage> immutableMessage = _mutableMessage.getImmutableMessage();

    smrf::ByteVector serializedMessage = immutableMessage->getSerializedMessage();
    mqttMessagingSkeleton.onMessageReceived(std::move(serializedMessage));
}

TEST_F(MqttMessagingSkeletonTest, registerMulticastSubscription_subscribesToMqttTopic)
{
    std::string multicastId = "multicastId";
    Settings settings;
    ClusterControllerSettings ccSettings(settings);
    MessagingSettings messagingSettings(settings);
    auto mockMqttReceiver = std::make_shared<MockMqttReceiver>(messagingSettings,
                                                               ccSettings,
                                                               _brokerUrl,
                                                               _mqttKeepAliveTimeSeconds,
                                                               _mqttReconnectDelayTimeSeconds,
                                                               _mqttReconnectMaxDelayTimeSeconds,
                                                               _isMqttExponentialBackoffEnabled,
                                                               _testGbid,
                                                               _receiverId);
    EXPECT_CALL(*mockMqttReceiver, subscribeToTopic(multicastId));
    MqttMessagingSkeleton mqttMessagingSkeleton(
            _mockMessageRouter, mockMqttReceiver, "", _testGbid);
    mqttMessagingSkeleton.registerMulticastSubscription(multicastId);
}

TEST_F(MqttMessagingSkeletonTest, registerMulticastSubscription_subscribesToMqttTopicOnlyOnce)
{
    std::string multicastId = "multicastId";
    MessagingSettings messagingSettings(_settings);
    auto mockMqttReceiver = std::make_shared<MockMqttReceiver>(messagingSettings,
                                                               _ccSettings,
                                                               _brokerUrl,
                                                               _mqttKeepAliveTimeSeconds,
                                                               _mqttReconnectDelayTimeSeconds,
                                                               _mqttReconnectMaxDelayTimeSeconds,
                                                               _isMqttExponentialBackoffEnabled,
                                                               _testGbid,
                                                               _receiverId);
    EXPECT_CALL(*mockMqttReceiver, subscribeToTopic(multicastId)).Times(1);
    MqttMessagingSkeleton mqttMessagingSkeleton(_mockMessageRouter, mockMqttReceiver,
                                                _ccSettings.getMqttMulticastTopicPrefix(),
                                                _testGbid);
    mqttMessagingSkeleton.registerMulticastSubscription(multicastId);
    mqttMessagingSkeleton.registerMulticastSubscription(multicastId);
}

TEST_F(MqttMessagingSkeletonTest, unregisterMulticastSubscription_unsubscribesFromMqttTopic)
{
    std::string multicastId = "multicastId";
    MessagingSettings messagingSettings(_settings);
    auto mockMqttReceiver = std::make_shared<MockMqttReceiver>(messagingSettings,
                                                               _ccSettings,
                                                               _brokerUrl,
                                                               _mqttKeepAliveTimeSeconds,
                                                               _mqttReconnectDelayTimeSeconds,
                                                               _mqttReconnectMaxDelayTimeSeconds,
                                                               _isMqttExponentialBackoffEnabled,
                                                               _testGbid,
                                                               _receiverId);
    EXPECT_CALL(*mockMqttReceiver, subscribeToTopic(multicastId)).Times(1);
    EXPECT_CALL(*mockMqttReceiver, unsubscribeFromTopic(multicastId));

    MqttMessagingSkeleton mqttMessagingSkeleton(_mockMessageRouter, mockMqttReceiver,
                                                _ccSettings.getMqttMulticastTopicPrefix(),
                                                _testGbid);
    mqttMessagingSkeleton.registerMulticastSubscription(multicastId);
    mqttMessagingSkeleton.unregisterMulticastSubscription(multicastId);
}

TEST_F(MqttMessagingSkeletonTest,
       unregisterMulticastSubscription_unsubscribesFromMqttTopicOnlyForUnsubscribeOfLastReceiver)
{
    std::string multicastId = "multicastId";
    MessagingSettings messagingSettings(_settings);
    auto mockMqttReceiver = std::make_shared<MockMqttReceiver>(messagingSettings,
                                                               _ccSettings,
                                                               _brokerUrl,
                                                               _mqttKeepAliveTimeSeconds,
                                                               _mqttReconnectDelayTimeSeconds,
                                                               _mqttReconnectMaxDelayTimeSeconds,
                                                               _isMqttExponentialBackoffEnabled,
                                                               _testGbid,
                                                               _receiverId);
    EXPECT_CALL(*mockMqttReceiver, subscribeToTopic(multicastId)).Times(1);
    EXPECT_CALL(*mockMqttReceiver, unsubscribeFromTopic(multicastId)).Times(0);
    EXPECT_CALL(*mockMqttReceiver, unsubscribeFromTopic(multicastId)).Times(1);

    MqttMessagingSkeleton mqttMessagingSkeleton(_mockMessageRouter, mockMqttReceiver,
                                                _ccSettings.getMqttMulticastTopicPrefix(),
                                                _testGbid);
    mqttMessagingSkeleton.registerMulticastSubscription(multicastId);
    mqttMessagingSkeleton.registerMulticastSubscription(multicastId);
    mqttMessagingSkeleton.unregisterMulticastSubscription(multicastId);
    mqttMessagingSkeleton.unregisterMulticastSubscription(multicastId);
}

TEST_F(MqttMessagingSkeletonTest, translateWildcard)
{
    std::map<std::string, std::string> input2expected = {
            {"partion0/partion1", "partion0/partion1"},
            {"partion0/partion1/*", "partion0/partion1/#"},
            {"partion0/partion1/+", "partion0/partion1/+"}};

    for (auto& testCase : input2expected) {
        std::string inputPartition = testCase.first;
        std::string expectedPartition = testCase.second;
        EXPECT_EQ(expectedPartition,
                  MqttMessagingSkeleton::translateMulticastWildcard(inputPartition));
    }
}
<|endoftext|>


<commit_before>#include "test/thinblockutil.h"
#include "blockheaderprocessor.h"
#include "bloom.h"
#include "chain.h"
#include "chainparams.h"
#include "inflightindex.h"
#include "merkleblock.h"
#include "net.h"
#include "process_xthinblock.h"
#include "testutil.h"
#include "util.h" // for fPrintToDebugLog
#include "xthin.h"
#include "compactthin.h"
#include "compactblockprocessor.h"
#include <boost/test/unit_test_suite.hpp>
#include <boost/test/test_tools.hpp>

template <class WORKER_TYPE>
struct DummyWorker : public WORKER_TYPE {

    DummyWorker(ThinBlockManager& m, NodeId i) :
        WORKER_TYPE(m, i), isBuilt(false), buildStubCalled(false)
    { }

    void buildStub(const StubData& s, const TxFinder& f, CNode& n) override {
        buildStubCalled = true;
        WORKER_TYPE::buildStub(s, f, n);
    }
    bool isStubBuilt(const uint256& block) const override {
        return isBuilt;
    }
    bool isBuilt;
    bool buildStubCalled;
};

struct DummyHeaderProcessor : public BlockHeaderProcessor {

    DummyHeaderProcessor() : headerOK(true), called(false) { }

    CBlockIndex* operator()(const std::vector<CBlockHeader>&, bool, bool) override {
        called = true;
        if (!headerOK)
            throw BlockHeaderError("header not OK");

        static CBlockIndex index;
        index.nHeight = 2;
        return &index;
    }
    bool requestConnectHeaders(const CBlockHeader& h, CNode& from, bool) override {
        return false;
    }
    bool headerOK;
    bool called;
};

struct XThinBlockSetup {

    XThinBlockSetup() :
        tmgr(std::unique_ptr<ThinBlockFinishedCallb>(new DummyFinishedCallb),
             std::unique_ptr<InFlightEraser>(new DummyInFlightEraser))
    {
        SelectParams(CBaseChainParams::MAIN);
        fPrintToDebugLog = false;
    }

    CDataStream stream(const XThinBlock& block) {
        CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);

        stream << block;
        return stream;;
    }

    XThinBlock xblock;
    DummyNode pfrom;
    ThinBlockManager tmgr;
    DummyHeaderProcessor headerprocessor;
};

struct DummyXThinProcessor : public XThinBlockProcessor {

    DummyXThinProcessor(CNode& f, ThinBlockWorker& w,
        BlockHeaderProcessor& h) : XThinBlockProcessor(f, w, h), misbehaved(0)
    { }

    virtual void misbehave(int howmuch) {
        misbehaved += howmuch;
    }
    int misbehaved;
};

BOOST_FIXTURE_TEST_SUITE(process_xthinblock_tests, XThinBlockSetup);

BOOST_AUTO_TEST_CASE(xthinblock_ignore_invalid) {
    XThinBlock xblock(TestBlock1(), CBloomFilter());;
    xblock.txHashes.clear(); // <- makes block invalid

    DummyWorker<XThinWorker> worker(tmgr, 42);
    worker.addWork(xblock.header.GetHash());
    CDataStream s = stream(xblock);
    DummyXThinProcessor process(pfrom, worker, headerprocessor);
    process(s, NullFinder(), MAX_BLOCK_SIZE, 1);

    // Should reset the worker.
    BOOST_CHECK(!worker.isWorking());

    // ...and misbehave the client.
    BOOST_CHECK_EQUAL(20, process.misbehaved);
    BOOST_CHECK(!worker.buildStubCalled);
    BOOST_CHECK_EQUAL("reject", pfrom.messages.at(0));
}

BOOST_AUTO_TEST_CASE(xthinblock_ignore_if_has_block_data) {
    // Add block to mapBlockIndex (so we already have it)
    XThinBlock xblock(TestBlock1(), CBloomFilter()); // <- has only coinbase
    DummyBlockIndexEntry dummyEntry(xblock.header.GetHash());

    DummyWorker<XThinWorker> worker(tmgr, 42);
    worker.addWork(xblock.header.GetHash());
    CDataStream s = stream(xblock);
    DummyXThinProcessor process(pfrom, worker, headerprocessor);
    process(s, NullFinder(), MAX_BLOCK_SIZE, 1);

    // peer should not be working on anything
    BOOST_CHECK(!worker.isWorking());
    BOOST_CHECK(pfrom.messages.empty()); //<- no re-requesting
}

BOOST_AUTO_TEST_CASE(xthinblock_header_is_processed) {
    XThinWorker worker(tmgr, 42);
    XThinBlock xblock(TestBlock1(), CBloomFilter());
    worker.addWork(xblock.header.GetHash());
    CDataStream s = stream(xblock);
    DummyXThinProcessor process(pfrom, worker, headerprocessor);
    process(s, NullFinder(), MAX_BLOCK_SIZE, 1);

    BOOST_CHECK(headerprocessor.called);
    BOOST_CHECK(worker.isStubBuilt(xblock.header.GetHash()));
    BOOST_CHECK(worker.isWorkingOn(xblock.header.GetHash()));
}

BOOST_AUTO_TEST_CASE(xthinblock_stop_if_header_fails) {
    XThinWorker worker(tmgr, 42);
    XThinBlock xblock(TestBlock1(), CBloomFilter());
    worker.addWork(xblock.header.GetHash());
    headerprocessor.headerOK = false;
    CDataStream s = stream(xblock);
    DummyXThinProcessor process(pfrom, worker, headerprocessor);
    process(s, NullFinder(), MAX_BLOCK_SIZE, 1);

    BOOST_CHECK(!worker.isWorkingOn(xblock.header.GetHash()));
    BOOST_CHECK(!worker.isStubBuilt(xblock.header.GetHash()));
}

BOOST_AUTO_TEST_CASE(xthinblock_rerequest_missing) {
    CBlock testblock = TestBlock1();

    // bloom filter will match all, so non will be included.
    XThinBlock xblock(testblock, CBloomFilter());
    XThinWorker worker(tmgr, 42);

    worker.addWork(xblock.header.GetHash());
    CDataStream s = stream(xblock);
    DummyXThinProcessor process(pfrom, worker, headerprocessor);
    process(s, NullFinder(), MAX_BLOCK_SIZE, 1);

    BOOST_CHECK(headerprocessor.called);
    BOOST_CHECK(worker.isStubBuilt(xblock.header.GetHash()));
    BOOST_CHECK(worker.isWorkingOn(xblock.header.GetHash()));

    // should have re-requested missing transaction
    BOOST_CHECK_EQUAL("get_xblocktx", pfrom.messages.at(0));

    // create a bloom filter that matches non, so we
    // don't have to re-request.
    CBloomFilter f;
    f.clear();
    pfrom.messages.clear();
    xblock = XThinBlock(TestBlock1(), f);
    s = stream(xblock);
    process(s, NullFinder(), MAX_BLOCK_SIZE, 1);
    BOOST_CHECK(pfrom.messages.empty());
}

// We want to call build stub even if we have one.
// This thinblock may contain transactions we're missing.
BOOST_AUTO_TEST_CASE(rebuild_stub) {
    DummyWorker<XThinWorker> worker(tmgr, 42);
    XThinBlock xblock(TestBlock1(), CBloomFilter());
    worker.addWork(xblock.header.GetHash());
    worker.isBuilt = true;
    CDataStream s = stream(xblock);
    DummyXThinProcessor process(pfrom, worker, headerprocessor);
    process(s, NullFinder(), MAX_BLOCK_SIZE, 1);

    BOOST_CHECK(worker.buildStubCalled);
}

BOOST_AUTO_TEST_SUITE_END();
BOOST_AUTO_TEST_SUITE(compactblockprocessor_tests);

// TODO: Move to compactblockprocessor_tests.cpp after thin block
// announcements are merged.
BOOST_AUTO_TEST_CASE(compactblockprocessor_fetch_full) {
    CBlock testblock = TestBlock1();

    DummyNode node;
    std::unique_ptr<ThinBlockManager> tmgr = GetDummyThinBlockMg();
    CompactWorker worker(*tmgr, node.id);
    DummyHeaderProcessor hprocessor;
    CompactBlockProcessor p(node, worker, hprocessor);

    // create a invalid compact block (obviously too big)
    CompactBlock block(TestBlock1(), CoinbaseOnlyPrefiller{});

    uint64_t currMaxBlockSize = 1000 * 100; // 100kb (for faster unittest)
    size_t tooManyTx = std::ceil(currMaxBlockSize * 1.05 / minTxSize()) + 1;
    block.prefilledtxn.resize(tooManyTx, block.prefilledtxn.at(0));

    CTxMemPool mpool(CFeeRate(0));
    CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
    stream << block;
    p(stream, mpool, currMaxBlockSize, 1);

    // should have rejected the block
    BOOST_CHECK_EQUAL(size_t(1), node.messages.size());
    BOOST_CHECK_EQUAL("reject", node.messages.at(0));
}

BOOST_AUTO_TEST_SUITE_END();
<commit_msg>Fix signature on test object<commit_after>#include "test/thinblockutil.h"
#include "blockheaderprocessor.h"
#include "bloom.h"
#include "chain.h"
#include "chainparams.h"
#include "inflightindex.h"
#include "merkleblock.h"
#include "net.h"
#include "process_xthinblock.h"
#include "testutil.h"
#include "util.h" // for fPrintToDebugLog
#include "xthin.h"
#include "compactthin.h"
#include "compactblockprocessor.h"
#include <boost/test/unit_test_suite.hpp>
#include <boost/test/test_tools.hpp>

template <class WORKER_TYPE>
struct DummyWorker : public WORKER_TYPE {

    DummyWorker(ThinBlockManager& m, NodeId i) :
        WORKER_TYPE(m, i), isBuilt(false), buildStubCalled(false)
    { }

    void buildStub(const StubData& s, const TxFinder& f, CNode& n) override {
        buildStubCalled = true;
        WORKER_TYPE::buildStub(s, f, n);
    }
    bool isStubBuilt(const uint256& block) const override {
        return isBuilt;
    }
    bool isBuilt;
    bool buildStubCalled;
};

struct DummyHeaderProcessor : public BlockHeaderProcessor {

    DummyHeaderProcessor() : headerOK(true), called(false) { }

    CBlockIndex* operator()(const std::vector<CBlockHeader>&, bool, bool) override {
        called = true;
        if (!headerOK)
            throw BlockHeaderError("header not OK");

        static CBlockIndex index;
        index.nHeight = 2;
        return &index;
    }
    bool requestConnectHeaders(const CBlockHeader& h, CNode& from, bool) override {
        return false;
    }
    bool headerOK;
    bool called;
};

struct XThinBlockSetup {

    XThinBlockSetup() :
        tmgr(std::unique_ptr<ThinBlockFinishedCallb>(new DummyFinishedCallb),
             std::unique_ptr<InFlightEraser>(new DummyInFlightEraser))
    {
        SelectParams(CBaseChainParams::MAIN);
        fPrintToDebugLog = false;
    }

    CDataStream stream(const XThinBlock& block) {
        CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);

        stream << block;
        return stream;;
    }

    XThinBlock xblock;
    DummyNode pfrom;
    ThinBlockManager tmgr;
    DummyHeaderProcessor headerprocessor;
};

struct DummyXThinProcessor : public XThinBlockProcessor {

    DummyXThinProcessor(CNode& f, ThinBlockWorker& w,
        BlockHeaderProcessor& h) : XThinBlockProcessor(f, w, h), misbehaved(0)
    { }

    void misbehave(int howmuch, const std::string&) override {
        misbehaved += howmuch;
    }
    int misbehaved;
};

BOOST_FIXTURE_TEST_SUITE(process_xthinblock_tests, XThinBlockSetup);

BOOST_AUTO_TEST_CASE(xthinblock_ignore_invalid) {
    XThinBlock xblock(TestBlock1(), CBloomFilter());;
    xblock.txHashes.clear(); // <- makes block invalid

    DummyWorker<XThinWorker> worker(tmgr, 42);
    worker.addWork(xblock.header.GetHash());
    CDataStream s = stream(xblock);
    DummyXThinProcessor process(pfrom, worker, headerprocessor);
    process(s, NullFinder(), MAX_BLOCK_SIZE, 1);

    // Should reset the worker.
    BOOST_CHECK(!worker.isWorking());

    // ...and misbehave the client.
    BOOST_CHECK_EQUAL(20, process.misbehaved);
    BOOST_CHECK(!worker.buildStubCalled);
    BOOST_CHECK_EQUAL("reject", pfrom.messages.at(0));
}

BOOST_AUTO_TEST_CASE(xthinblock_ignore_if_has_block_data) {
    // Add block to mapBlockIndex (so we already have it)
    XThinBlock xblock(TestBlock1(), CBloomFilter()); // <- has only coinbase
    DummyBlockIndexEntry dummyEntry(xblock.header.GetHash());

    DummyWorker<XThinWorker> worker(tmgr, 42);
    worker.addWork(xblock.header.GetHash());
    CDataStream s = stream(xblock);
    DummyXThinProcessor process(pfrom, worker, headerprocessor);
    process(s, NullFinder(), MAX_BLOCK_SIZE, 1);

    // peer should not be working on anything
    BOOST_CHECK(!worker.isWorking());
    BOOST_CHECK(pfrom.messages.empty()); //<- no re-requesting
}

BOOST_AUTO_TEST_CASE(xthinblock_header_is_processed) {
    XThinWorker worker(tmgr, 42);
    XThinBlock xblock(TestBlock1(), CBloomFilter());
    worker.addWork(xblock.header.GetHash());
    CDataStream s = stream(xblock);
    DummyXThinProcessor process(pfrom, worker, headerprocessor);
    process(s, NullFinder(), MAX_BLOCK_SIZE, 1);

    BOOST_CHECK(headerprocessor.called);
    BOOST_CHECK(worker.isStubBuilt(xblock.header.GetHash()));
    BOOST_CHECK(worker.isWorkingOn(xblock.header.GetHash()));
}

BOOST_AUTO_TEST_CASE(xthinblock_stop_if_header_fails) {
    XThinWorker worker(tmgr, 42);
    XThinBlock xblock(TestBlock1(), CBloomFilter());
    worker.addWork(xblock.header.GetHash());
    headerprocessor.headerOK = false;
    CDataStream s = stream(xblock);
    DummyXThinProcessor process(pfrom, worker, headerprocessor);
    process(s, NullFinder(), MAX_BLOCK_SIZE, 1);

    BOOST_CHECK(!worker.isWorkingOn(xblock.header.GetHash()));
    BOOST_CHECK(!worker.isStubBuilt(xblock.header.GetHash()));
}

BOOST_AUTO_TEST_CASE(xthinblock_rerequest_missing) {
    CBlock testblock = TestBlock1();

    // bloom filter will match all, so non will be included.
    XThinBlock xblock(testblock, CBloomFilter());
    XThinWorker worker(tmgr, 42);

    worker.addWork(xblock.header.GetHash());
    CDataStream s = stream(xblock);
    DummyXThinProcessor process(pfrom, worker, headerprocessor);
    process(s, NullFinder(), MAX_BLOCK_SIZE, 1);

    BOOST_CHECK(headerprocessor.called);
    BOOST_CHECK(worker.isStubBuilt(xblock.header.GetHash()));
    BOOST_CHECK(worker.isWorkingOn(xblock.header.GetHash()));

    // should have re-requested missing transaction
    BOOST_CHECK_EQUAL("get_xblocktx", pfrom.messages.at(0));

    // create a bloom filter that matches non, so we
    // don't have to re-request.
    CBloomFilter f;
    f.clear();
    pfrom.messages.clear();
    xblock = XThinBlock(TestBlock1(), f);
    s = stream(xblock);
    process(s, NullFinder(), MAX_BLOCK_SIZE, 1);
    BOOST_CHECK(pfrom.messages.empty());
}

// We want to call build stub even if we have one.
// This thinblock may contain transactions we're missing.
BOOST_AUTO_TEST_CASE(rebuild_stub) {
    DummyWorker<XThinWorker> worker(tmgr, 42);
    XThinBlock xblock(TestBlock1(), CBloomFilter());
    worker.addWork(xblock.header.GetHash());
    worker.isBuilt = true;
    CDataStream s = stream(xblock);
    DummyXThinProcessor process(pfrom, worker, headerprocessor);
    process(s, NullFinder(), MAX_BLOCK_SIZE, 1);

    BOOST_CHECK(worker.buildStubCalled);
}

BOOST_AUTO_TEST_SUITE_END();
BOOST_AUTO_TEST_SUITE(compactblockprocessor_tests);

// TODO: Move to compactblockprocessor_tests.cpp after thin block
// announcements are merged.
BOOST_AUTO_TEST_CASE(compactblockprocessor_fetch_full) {
    CBlock testblock = TestBlock1();

    DummyNode node;
    std::unique_ptr<ThinBlockManager> tmgr = GetDummyThinBlockMg();
    CompactWorker worker(*tmgr, node.id);
    DummyHeaderProcessor hprocessor;
    CompactBlockProcessor p(node, worker, hprocessor);

    // create a invalid compact block (obviously too big)
    CompactBlock block(TestBlock1(), CoinbaseOnlyPrefiller{});

    uint64_t currMaxBlockSize = 1000 * 100; // 100kb (for faster unittest)
    size_t tooManyTx = std::ceil(currMaxBlockSize * 1.05 / minTxSize()) + 1;
    block.prefilledtxn.resize(tooManyTx, block.prefilledtxn.at(0));

    CTxMemPool mpool(CFeeRate(0));
    CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
    stream << block;
    p(stream, mpool, currMaxBlockSize, 1);

    // should have rejected the block
    BOOST_CHECK_EQUAL(size_t(1), node.messages.size());
    BOOST_CHECK_EQUAL("reject", node.messages.at(0));
}

BOOST_AUTO_TEST_SUITE_END();
<|endoftext|>


<commit_before>// ImGui Win32 + DirectX9 binding
// https://github.com/ocornut/imgui

#include <imgui.h>
#include "imgui_impl_dx9.h"

// DirectX
#include <d3dx9.h>
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>

// Data
static HWND                     g_hWnd = 0;
static INT64                    g_Time = 0;
static INT64                    g_TicksPerSecond = 0;
static LPDIRECT3DDEVICE9        g_pd3dDevice = NULL;
static LPDIRECT3DVERTEXBUFFER9  g_pVB = NULL;
static int                      VERTEX_BUFFER_SIZE = 30000;     // TODO: Make vertex buffer smaller and grow dynamically as needed.

struct CUSTOMVERTEX
{
    D3DXVECTOR3 pos;
    D3DCOLOR    col;
    D3DXVECTOR2 uv;
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)

// This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure)
// If text or lines are blurry when integrating ImGui in your engine:
// - in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f)
static void ImGui_ImplDX9_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
{
    size_t total_vtx_count = 0;
    for (int n = 0; n < cmd_lists_count; n++)
        total_vtx_count += cmd_lists[n]->vtx_buffer.size();
    if (total_vtx_count == 0)
        return;

    // Copy and convert all vertices into a single contiguous buffer
    CUSTOMVERTEX* vtx_dst;
    if (g_pVB->Lock(0, (UINT)total_vtx_count, (void**)&vtx_dst, D3DLOCK_DISCARD) < 0)
        return;
    for (int n = 0; n < cmd_lists_count; n++)
    {
        const ImDrawList* cmd_list = cmd_lists[n];
        const ImDrawVert* vtx_src = &cmd_list->vtx_buffer[0];
        for (size_t i = 0; i < cmd_list->vtx_buffer.size(); i++)
        {
            vtx_dst->pos.x = vtx_src->pos.x;
            vtx_dst->pos.y = vtx_src->pos.y;
            vtx_dst->pos.z = 0.0f;
            vtx_dst->col = (vtx_src->col & 0xFF00FF00) | ((vtx_src->col & 0xFF0000)>>16) | ((vtx_src->col & 0xFF) << 16);     // RGBA --> ARGB for DirectX9
            vtx_dst->uv.x = vtx_src->uv.x;
            vtx_dst->uv.y = vtx_src->uv.y;
            vtx_dst++;
            vtx_src++;
        }
    }
    g_pVB->Unlock();
    g_pd3dDevice->SetPixelShader(NULL);
    g_pd3dDevice->SetVertexShader(NULL);
    g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof( CUSTOMVERTEX ) );
    g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );

    // Setup render state: alpha-blending, no face culling, no depth testing
    g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
    g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, false );
    g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, false );
    g_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, true );
    g_pd3dDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_ADD );
    g_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, false );
    g_pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
    g_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
    g_pd3dDevice->SetRenderState( D3DRS_SCISSORTESTENABLE, true );
    g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );
    g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_DIFFUSE );
    g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
    g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
    g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );

    // Setup orthographic projection matrix
    D3DXMATRIXA16 mat;
    D3DXMatrixIdentity(&mat);
    g_pd3dDevice->SetTransform(D3DTS_WORLD, &mat);
    g_pd3dDevice->SetTransform(D3DTS_VIEW, &mat);
    D3DXMatrixOrthoOffCenterLH(&mat, 0.5f, ImGui::GetIO().DisplaySize.x+0.5f, ImGui::GetIO().DisplaySize.y+0.5f, 0.5f, -1.0f, +1.0f);
    g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat);

    // Render command lists
    int vtx_offset = 0;
    for (int n = 0; n < cmd_lists_count; n++)
    {
        const ImDrawList* cmd_list = cmd_lists[n];
        for (size_t cmd_i = 0; cmd_i < cmd_list->commands.size(); cmd_i++)
        {
            const ImDrawCmd* pcmd = &cmd_list->commands[cmd_i];
            if (pcmd->user_callback)
            {
                pcmd->user_callback(cmd_list, pcmd);
            }
            else
            {
                const RECT r = { (LONG)pcmd->clip_rect.x, (LONG)pcmd->clip_rect.y, (LONG)pcmd->clip_rect.z, (LONG)pcmd->clip_rect.w };
                g_pd3dDevice->SetTexture( 0, (LPDIRECT3DTEXTURE9)pcmd->texture_id );
                g_pd3dDevice->SetScissorRect(&r);
                g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, vtx_offset, pcmd->vtx_count/3);
            }
            vtx_offset += pcmd->vtx_count;
        }
    }
}

LRESULT ImGui_ImplDX9_WndProcHandler(HWND, UINT msg, WPARAM wParam, LPARAM lParam)
{
    ImGuiIO& io = ImGui::GetIO();
    switch (msg)
    {
    case WM_LBUTTONDOWN:
        io.MouseDown[0] = true;
        return true;
    case WM_LBUTTONUP:
        io.MouseDown[0] = false;
        return true;
    case WM_RBUTTONDOWN:
        io.MouseDown[1] = true;
        return true;
    case WM_RBUTTONUP:
        io.MouseDown[1] = false;
        return true;
    case WM_MOUSEWHEEL:
        io.MouseWheel += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f;
        return true;
    case WM_MOUSEMOVE:
        io.MousePos.x = (signed short)(lParam);
        io.MousePos.y = (signed short)(lParam >> 16);
        return true;
    case WM_KEYDOWN:
        if (wParam < 256)
            io.KeysDown[wParam] = 1;
        return true;
    case WM_KEYUP:
        if (wParam < 256)
            io.KeysDown[wParam] = 0;
        return true;
    case WM_CHAR:
        // You can also use ToAscii()+GetKeyboardState() to retrieve characters.
        if (wParam > 0 && wParam < 0x10000)
            io.AddInputCharacter((unsigned short)wParam);
        return true;
    }
    return 0;
}

bool    ImGui_ImplDX9_Init(void* hwnd, IDirect3DDevice9* device)
{
    g_hWnd = (HWND)hwnd;
    g_pd3dDevice = device;

    if (!QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond))
        return false;
    if (!QueryPerformanceCounter((LARGE_INTEGER *)&g_Time))
        return false;

    ImGuiIO& io = ImGui::GetIO();
    io.KeyMap[ImGuiKey_Tab] = VK_TAB;                              // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array that we will update during the application lifetime.
    io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT;
    io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT;
    io.KeyMap[ImGuiKey_UpArrow] = VK_UP;
    io.KeyMap[ImGuiKey_DownArrow] = VK_UP;
    io.KeyMap[ImGuiKey_Home] = VK_HOME;
    io.KeyMap[ImGuiKey_End] = VK_END;
    io.KeyMap[ImGuiKey_Delete] = VK_DELETE;
    io.KeyMap[ImGuiKey_Backspace] = VK_BACK;
    io.KeyMap[ImGuiKey_Enter] = VK_RETURN;
    io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE;
    io.KeyMap[ImGuiKey_A] = 'A';
    io.KeyMap[ImGuiKey_C] = 'C';
    io.KeyMap[ImGuiKey_V] = 'V';
    io.KeyMap[ImGuiKey_X] = 'X';
    io.KeyMap[ImGuiKey_Y] = 'Y';
    io.KeyMap[ImGuiKey_Z] = 'Z';

    io.RenderDrawListsFn = ImGui_ImplDX9_RenderDrawLists;
    io.ImeWindowHandle = g_hWnd;

    return true;
}

void ImGui_ImplDX9_Shutdown()
{
    ImGui_ImplDX9_InvalidateDeviceObjects();
    ImGui::Shutdown();
    g_pd3dDevice = NULL;
    g_hWnd = 0;
}

static void ImGui_ImplDX9_CreateFontsTexture()
{
    ImGuiIO& io = ImGui::GetIO();

    // Build
    unsigned char* pixels;
    int width, height, bytes_per_pixel;
    io.Fonts->GetTexDataAsAlpha8(&pixels, &width, &height, &bytes_per_pixel);

    // Create DX9 texture
    LPDIRECT3DTEXTURE9 pTexture = NULL;
    if (D3DXCreateTexture(g_pd3dDevice, width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8, D3DPOOL_DEFAULT, &pTexture) < 0)
    {
        IM_ASSERT(0);
        return;
    }
    D3DLOCKED_RECT tex_locked_rect;
    if (pTexture->LockRect(0, &tex_locked_rect, NULL, 0) != D3D_OK)
    {
        IM_ASSERT(0);
        return;
    }
    for (int y = 0; y < height; y++)
        memcpy((unsigned char *)tex_locked_rect.pBits + tex_locked_rect.Pitch * y, pixels + (width * bytes_per_pixel) * y, (width * bytes_per_pixel));
    pTexture->UnlockRect(0);

    // Store our identifier
    io.Fonts->TexID = (void *)pTexture;
}

bool ImGui_ImplDX9_CreateDeviceObjects()
{
    if (!g_pd3dDevice)
        return false;

    if (g_pd3dDevice->CreateVertexBuffer(VERTEX_BUFFER_SIZE * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL) < 0)
        return false;

    ImGui_ImplDX9_CreateFontsTexture();
    return true;
}

void ImGui_ImplDX9_InvalidateDeviceObjects()
{
    if (!g_pd3dDevice)
        return;
    if (g_pVB)
    {
        g_pVB->Release();
        g_pVB = NULL;
    }
    if (LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)ImGui::GetIO().Fonts->TexID)
    {
        tex->Release();
        ImGui::GetIO().Fonts->TexID = 0;
    }
}

void ImGui_ImplDX9_NewFrame()
{
    if (!g_pVB)
        ImGui_ImplDX9_CreateDeviceObjects();

    ImGuiIO& io = ImGui::GetIO();

    // Setup display size (every frame to accommodate for window resizing)
    RECT rect;
    GetClientRect(g_hWnd, &rect);
    io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top));

    // Setup time step
    INT64 current_time;
    QueryPerformanceCounter((LARGE_INTEGER *)&current_time);
    io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond;
    g_Time = current_time;

    // Read keyboard modifiers inputs
    io.KeyCtrl = (GetKeyState(VK_CONTROL) & 0x8000) != 0;
    io.KeyShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0;
    io.KeyAlt = (GetKeyState(VK_MENU) & 0x8000) != 0;
    // io.KeysDown : filled by WM_KEYDOWN/WM_KEYUP events
    // io.MousePos : filled by WM_MOUSEMOVE events
    // io.MouseDown : filled by WM_*BUTTON* events
    // io.MouseWheel : filled by WM_MOUSEWHEEL events

    // Start the frame
    ImGui::NewFrame();
}
<commit_msg>Examples: Formatting #187<commit_after>// ImGui Win32 + DirectX9 binding
// https://github.com/ocornut/imgui

#include <imgui.h>
#include "imgui_impl_dx9.h"

// DirectX
#include <d3dx9.h>
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>

// Data
static HWND                     g_hWnd = 0;
static INT64                    g_Time = 0;
static INT64                    g_TicksPerSecond = 0;
static LPDIRECT3DDEVICE9        g_pd3dDevice = NULL;
static LPDIRECT3DVERTEXBUFFER9  g_pVB = NULL;
static int                      VERTEX_BUFFER_SIZE = 30000;     // TODO: Make vertex buffer smaller and grow dynamically as needed.

struct CUSTOMVERTEX
{
    D3DXVECTOR3 pos;
    D3DCOLOR    col;
    D3DXVECTOR2 uv;
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)

// This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure)
// If text or lines are blurry when integrating ImGui in your engine:
// - in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f)
static void ImGui_ImplDX9_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
{
    size_t total_vtx_count = 0;
    for (int n = 0; n < cmd_lists_count; n++)
        total_vtx_count += cmd_lists[n]->vtx_buffer.size();
    if (total_vtx_count == 0)
        return;

    // Copy and convert all vertices into a single contiguous buffer
    CUSTOMVERTEX* vtx_dst;
    if (g_pVB->Lock(0, (UINT)total_vtx_count, (void**)&vtx_dst, D3DLOCK_DISCARD) < 0)
        return;
    for (int n = 0; n < cmd_lists_count; n++)
    {
        const ImDrawList* cmd_list = cmd_lists[n];
        const ImDrawVert* vtx_src = &cmd_list->vtx_buffer[0];
        for (size_t i = 0; i < cmd_list->vtx_buffer.size(); i++)
        {
            vtx_dst->pos.x = vtx_src->pos.x;
            vtx_dst->pos.y = vtx_src->pos.y;
            vtx_dst->pos.z = 0.0f;
            vtx_dst->col = (vtx_src->col & 0xFF00FF00) | ((vtx_src->col & 0xFF0000)>>16) | ((vtx_src->col & 0xFF) << 16);     // RGBA --> ARGB for DirectX9
            vtx_dst->uv.x = vtx_src->uv.x;
            vtx_dst->uv.y = vtx_src->uv.y;
            vtx_dst++;
            vtx_src++;
        }
    }
    g_pVB->Unlock();
    g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof( CUSTOMVERTEX ) );
    g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );

    // Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing
    g_pd3dDevice->SetPixelShader( NULL );
    g_pd3dDevice->SetVertexShader( NULL );
    g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
    g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, false );
    g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, false );
    g_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, true );
    g_pd3dDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_ADD );
    g_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, false );
    g_pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
    g_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
    g_pd3dDevice->SetRenderState( D3DRS_SCISSORTESTENABLE, true );
    g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );
    g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_DIFFUSE );
    g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
    g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
    g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );

    // Setup orthographic projection matrix
    D3DXMATRIXA16 mat;
    D3DXMatrixIdentity(&mat);
    g_pd3dDevice->SetTransform(D3DTS_WORLD, &mat);
    g_pd3dDevice->SetTransform(D3DTS_VIEW, &mat);
    D3DXMatrixOrthoOffCenterLH(&mat, 0.5f, ImGui::GetIO().DisplaySize.x+0.5f, ImGui::GetIO().DisplaySize.y+0.5f, 0.5f, -1.0f, +1.0f);
    g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat);

    // Render command lists
    int vtx_offset = 0;
    for (int n = 0; n < cmd_lists_count; n++)
    {
        const ImDrawList* cmd_list = cmd_lists[n];
        for (size_t cmd_i = 0; cmd_i < cmd_list->commands.size(); cmd_i++)
        {
            const ImDrawCmd* pcmd = &cmd_list->commands[cmd_i];
            if (pcmd->user_callback)
            {
                pcmd->user_callback(cmd_list, pcmd);
            }
            else
            {
                const RECT r = { (LONG)pcmd->clip_rect.x, (LONG)pcmd->clip_rect.y, (LONG)pcmd->clip_rect.z, (LONG)pcmd->clip_rect.w };
                g_pd3dDevice->SetTexture( 0, (LPDIRECT3DTEXTURE9)pcmd->texture_id );
                g_pd3dDevice->SetScissorRect(&r);
                g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, vtx_offset, pcmd->vtx_count/3);
            }
            vtx_offset += pcmd->vtx_count;
        }
    }
}

LRESULT ImGui_ImplDX9_WndProcHandler(HWND, UINT msg, WPARAM wParam, LPARAM lParam)
{
    ImGuiIO& io = ImGui::GetIO();
    switch (msg)
    {
    case WM_LBUTTONDOWN:
        io.MouseDown[0] = true;
        return true;
    case WM_LBUTTONUP:
        io.MouseDown[0] = false;
        return true;
    case WM_RBUTTONDOWN:
        io.MouseDown[1] = true;
        return true;
    case WM_RBUTTONUP:
        io.MouseDown[1] = false;
        return true;
    case WM_MOUSEWHEEL:
        io.MouseWheel += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f;
        return true;
    case WM_MOUSEMOVE:
        io.MousePos.x = (signed short)(lParam);
        io.MousePos.y = (signed short)(lParam >> 16);
        return true;
    case WM_KEYDOWN:
        if (wParam < 256)
            io.KeysDown[wParam] = 1;
        return true;
    case WM_KEYUP:
        if (wParam < 256)
            io.KeysDown[wParam] = 0;
        return true;
    case WM_CHAR:
        // You can also use ToAscii()+GetKeyboardState() to retrieve characters.
        if (wParam > 0 && wParam < 0x10000)
            io.AddInputCharacter((unsigned short)wParam);
        return true;
    }
    return 0;
}

bool    ImGui_ImplDX9_Init(void* hwnd, IDirect3DDevice9* device)
{
    g_hWnd = (HWND)hwnd;
    g_pd3dDevice = device;

    if (!QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond))
        return false;
    if (!QueryPerformanceCounter((LARGE_INTEGER *)&g_Time))
        return false;

    ImGuiIO& io = ImGui::GetIO();
    io.KeyMap[ImGuiKey_Tab] = VK_TAB;                              // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array that we will update during the application lifetime.
    io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT;
    io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT;
    io.KeyMap[ImGuiKey_UpArrow] = VK_UP;
    io.KeyMap[ImGuiKey_DownArrow] = VK_UP;
    io.KeyMap[ImGuiKey_Home] = VK_HOME;
    io.KeyMap[ImGuiKey_End] = VK_END;
    io.KeyMap[ImGuiKey_Delete] = VK_DELETE;
    io.KeyMap[ImGuiKey_Backspace] = VK_BACK;
    io.KeyMap[ImGuiKey_Enter] = VK_RETURN;
    io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE;
    io.KeyMap[ImGuiKey_A] = 'A';
    io.KeyMap[ImGuiKey_C] = 'C';
    io.KeyMap[ImGuiKey_V] = 'V';
    io.KeyMap[ImGuiKey_X] = 'X';
    io.KeyMap[ImGuiKey_Y] = 'Y';
    io.KeyMap[ImGuiKey_Z] = 'Z';

    io.RenderDrawListsFn = ImGui_ImplDX9_RenderDrawLists;
    io.ImeWindowHandle = g_hWnd;

    return true;
}

void ImGui_ImplDX9_Shutdown()
{
    ImGui_ImplDX9_InvalidateDeviceObjects();
    ImGui::Shutdown();
    g_pd3dDevice = NULL;
    g_hWnd = 0;
}

static void ImGui_ImplDX9_CreateFontsTexture()
{
    ImGuiIO& io = ImGui::GetIO();

    // Build
    unsigned char* pixels;
    int width, height, bytes_per_pixel;
    io.Fonts->GetTexDataAsAlpha8(&pixels, &width, &height, &bytes_per_pixel);

    // Create DX9 texture
    LPDIRECT3DTEXTURE9 pTexture = NULL;
    if (D3DXCreateTexture(g_pd3dDevice, width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8, D3DPOOL_DEFAULT, &pTexture) < 0)
    {
        IM_ASSERT(0);
        return;
    }
    D3DLOCKED_RECT tex_locked_rect;
    if (pTexture->LockRect(0, &tex_locked_rect, NULL, 0) != D3D_OK)
    {
        IM_ASSERT(0);
        return;
    }
    for (int y = 0; y < height; y++)
        memcpy((unsigned char *)tex_locked_rect.pBits + tex_locked_rect.Pitch * y, pixels + (width * bytes_per_pixel) * y, (width * bytes_per_pixel));
    pTexture->UnlockRect(0);

    // Store our identifier
    io.Fonts->TexID = (void *)pTexture;
}

bool ImGui_ImplDX9_CreateDeviceObjects()
{
    if (!g_pd3dDevice)
        return false;

    if (g_pd3dDevice->CreateVertexBuffer(VERTEX_BUFFER_SIZE * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL) < 0)
        return false;

    ImGui_ImplDX9_CreateFontsTexture();
    return true;
}

void ImGui_ImplDX9_InvalidateDeviceObjects()
{
    if (!g_pd3dDevice)
        return;
    if (g_pVB)
    {
        g_pVB->Release();
        g_pVB = NULL;
    }
    if (LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)ImGui::GetIO().Fonts->TexID)
    {
        tex->Release();
        ImGui::GetIO().Fonts->TexID = 0;
    }
}

void ImGui_ImplDX9_NewFrame()
{
    if (!g_pVB)
        ImGui_ImplDX9_CreateDeviceObjects();

    ImGuiIO& io = ImGui::GetIO();

    // Setup display size (every frame to accommodate for window resizing)
    RECT rect;
    GetClientRect(g_hWnd, &rect);
    io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top));

    // Setup time step
    INT64 current_time;
    QueryPerformanceCounter((LARGE_INTEGER *)&current_time);
    io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond;
    g_Time = current_time;

    // Read keyboard modifiers inputs
    io.KeyCtrl = (GetKeyState(VK_CONTROL) & 0x8000) != 0;
    io.KeyShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0;
    io.KeyAlt = (GetKeyState(VK_MENU) & 0x8000) != 0;
    // io.KeysDown : filled by WM_KEYDOWN/WM_KEYUP events
    // io.MousePos : filled by WM_MOUSEMOVE events
    // io.MouseDown : filled by WM_*BUTTON* events
    // io.MouseWheel : filled by WM_MOUSEWHEEL events

    // Start the frame
    ImGui::NewFrame();
}
<|endoftext|>


<commit_before>//-----------------------------------------------------------------------bl-
//--------------------------------------------------------------------------
//
// QUESO - a library to support the Quantification of Uncertainty
// for Estimation, Simulation and Optimization
//
// Copyright (C) 2008-2015 The PECOS Development Team
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the Version 2.1 GNU Lesser General
// Public License as published by the Free Software Foundation.
//
// This library 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 this library; if not, write to the Free Software
// Foundation, Inc. 51 Franklin Street, Fifth Floor,
// Boston, MA  02110-1301  USA
//
//-----------------------------------------------------------------------el-

#include <queso/GslVector.h>
#include <queso/GslMatrix.h>
#include <queso/BoxSubset.h>
#include <queso/LinearLagrangeInterpolationSurrogate.h>
#include <queso/InterpolationSurrogateBuilder.h>
#include <queso/InterpolationSurrogateIOASCII.h>

double four_d_fn_1( double x, double y, double z, double a );
double four_d_fn_2( double x, double y, double z, double a );

/* We subclass InterpolationSurrogateBuilder to implement our model evaluation.
   In this case, it just calls the simple functions, but you may do much more
   complicated things. */
template<class V, class M>
class MyInterpolationBuilder : public QUESO::InterpolationSurrogateBuilder<V,M>
{
public:
  MyInterpolationBuilder( QUESO::InterpolationSurrogateDataSet<V,M>& data )
    : QUESO::InterpolationSurrogateBuilder<V,M>(data)
  {};

  virtual ~MyInterpolationBuilder(){};

  virtual void evaluate_model( const V & domainVector, std::vector<double>& values )
  { queso_assert_equal_to( domainVector.sizeGlobal(), 4);
    queso_assert_equal_to( values.size(), 2);
    values[0] = four_d_fn_1(domainVector[0],domainVector[1],domainVector[2],domainVector[3]);
    values[1] = four_d_fn_2(domainVector[0],domainVector[1],domainVector[2],domainVector[3]);
  };
};

int main(int argc, char ** argv)
{
  if( argc < 2 )
    {
      std::cerr << "ERROR: Must run program as '4d_interp <inputfile>'." << std::endl;
      exit(1);
    }
  std::string filename = argv[1];

  MPI_Init(&argc, &argv);
  QUESO::FullEnvironment env(MPI_COMM_WORLD, filename.c_str(), "", NULL);

  // Define the parameter space. It's 4-dimensional in this example.
  QUESO::VectorSpace<QUESO::GslVector, QUESO::GslMatrix>
    paramSpace(env,"param_", 4, NULL);

  // Define parameter bounds
  QUESO::GslVector paramMins(paramSpace.zeroVector());
  paramMins[0] = -1;
  paramMins[1] = -0.5;
  paramMins[2] = 1.1;
  paramMins[3] = -2.1;

  QUESO::GslVector paramMaxs(paramSpace.zeroVector());
  paramMaxs[0] = 0.9;
  paramMaxs[1] = 3.14;
  paramMaxs[2] = 2.1;
  paramMaxs[3] = 4.1;

  // Define parameter domain.
  QUESO::BoxSubset<QUESO::GslVector, QUESO::GslMatrix>
    paramDomain("param_", paramSpace, paramMins, paramMaxs);

  // How many points to use in each coordinate direction of parameter space.
  // Since we're using an interpolation surrogate, it will evenly space the points
  // in each coordinate direction. These are the points at which the model
  // will be evaluated.
  std::vector<unsigned int> n_points(4);
  n_points[0] = 101;
  n_points[1] = 51;
  n_points[2] = 31;
  n_points[3] = 41;

  // Construct data object. We have two functions we're interpolating
  // at the same time, so n_datasets = 2.
  unsigned int n_datasets = 2;
  QUESO::InterpolationSurrogateDataSet<QUESO::GslVector, QUESO::GslMatrix>
    data(paramDomain,n_points,n_datasets);

  // Construct builder. The builder will add the values from the model evaluations
  // so the data object must stay alive.
  MyInterpolationBuilder<QUESO::GslVector,QUESO::GslMatrix>
    builder( data );

  // The expensive part. The builder will now evaluate the model for all the
  // desired points in parameter space. This will build both interpolants.
  builder.build_values();

  // Now that we've built the data, we write it out so we can reuse it later
  QUESO::InterpolationSurrogateIOASCII<QUESO::GslVector, QUESO::GslMatrix>
    data_writer;

  // Write each datasets separately. We'll be able to read each one in
  // individually and construct separate LinearLagrangeInterpolationSurrogate
  // objects.
  //data_writer.write( "4d_interp_data_1.dat", data.get_dataset(0) );
  //data_writer.write( "4d_interp_data_2.dat", data.get_dataset(1) );

  // The builder put the model values into the data, so now we can give the data
  // to the interpolation surrogate. This object can now be used in a likelihood
  // function for example. Here, we just illustrate calling the surrogate model
  // evaluation.
  QUESO::LinearLagrangeInterpolationSurrogate<QUESO::GslVector,QUESO::GslMatrix>
    four_d_surrogate_1( data.get_dataset(0) );

  QUESO::LinearLagrangeInterpolationSurrogate<QUESO::GslVector,QUESO::GslMatrix>
    four_d_surrogate_2( data.get_dataset(1) );

  // A point in parameter space at which we want to use the surrogate
  QUESO::GslVector domainVector(paramSpace.zeroVector());
  domainVector[0] = -0.4;
  domainVector[1] = 3.0;
  domainVector[2] = 1.5;
  domainVector[3] = 1.65;

  // Evaluate the surrogate model at the given point in parameter space
  // Because the exact function is quadrilinear, our interpolated value
  // should be exact.
  double value_1 = four_d_surrogate_1.evaluate(domainVector);
  double value_2 = four_d_surrogate_2.evaluate(domainVector);

  for( int r = 0; r < env.fullComm().NumProc(); r++ )
    {
      if( env.fullRank() == r )
        {
          std::cout << "======================================" << std::endl
                    << "Processor: " << env.fullRank() << std::endl
                    << "Interpolated value for model 1: " << value_1 << std::endl
                    << "Exact value for model 1: " << four_d_fn_1( domainVector[0],domainVector[1],domainVector[2],domainVector[3])
                    << std::endl
                    << "Interpolated value for model 2: " << value_2 << std::endl
                    << "Exact value for model 2: " << four_d_fn_2( domainVector[0],domainVector[1],domainVector[2],domainVector[3])
                    << std::endl
                    << "======================================" << std::endl;
        }

      MPI_Barrier(MPI_COMM_WORLD);
    }

  MPI_Finalize();

  return 0;
}

double four_d_fn_1( double x, double y, double z, double a )
{
  return 3.0 + 2.5*x - 3.1*y + 2.71*z + 3.14*a
    + 0.1*x*y + 1.2*x*z + 0.5*y*z + 0.1*x*a + 1.1*y*a + 2.1*z*a
    + 0.3*x*y*a + 0.5*x*z*a + 1.2*y*z*a + 0.9*x*y*z
    + 2.5*x*y*z*a;
}

double four_d_fn_2( double x, double y, double z, double a )
{
  return 2.0 + 1.5*x - 2.1*y + 1.71*z + 2.14*a
    + 0.01*x*y + 0.2*x*z + 0.05*y*z + 0.01*x*a + 2.1*y*a + 3.1*z*a
    + 1.3*x*y*a + 1.5*x*z*a + 2.2*y*z*a + 1.9*x*y*z
    + 3.5*x*y*z*a;
}
<commit_msg>Updated InterpolationSurrogate example data writing<commit_after>//-----------------------------------------------------------------------bl-
//--------------------------------------------------------------------------
//
// QUESO - a library to support the Quantification of Uncertainty
// for Estimation, Simulation and Optimization
//
// Copyright (C) 2008-2015 The PECOS Development Team
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the Version 2.1 GNU Lesser General
// Public License as published by the Free Software Foundation.
//
// This library 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 this library; if not, write to the Free Software
// Foundation, Inc. 51 Franklin Street, Fifth Floor,
// Boston, MA  02110-1301  USA
//
//-----------------------------------------------------------------------el-

#include <queso/GslVector.h>
#include <queso/GslMatrix.h>
#include <queso/BoxSubset.h>
#include <queso/LinearLagrangeInterpolationSurrogate.h>
#include <queso/InterpolationSurrogateBuilder.h>
#include <queso/InterpolationSurrogateIOASCII.h>

double four_d_fn_1( double x, double y, double z, double a );
double four_d_fn_2( double x, double y, double z, double a );

/* We subclass InterpolationSurrogateBuilder to implement our model evaluation.
   In this case, it just calls the simple functions, but you may do much more
   complicated things. */
template<class V, class M>
class MyInterpolationBuilder : public QUESO::InterpolationSurrogateBuilder<V,M>
{
public:
  MyInterpolationBuilder( QUESO::InterpolationSurrogateDataSet<V,M>& data )
    : QUESO::InterpolationSurrogateBuilder<V,M>(data)
  {};

  virtual ~MyInterpolationBuilder(){};

  virtual void evaluate_model( const V & domainVector, std::vector<double>& values )
  { queso_assert_equal_to( domainVector.sizeGlobal(), 4);
    queso_assert_equal_to( values.size(), 2);
    values[0] = four_d_fn_1(domainVector[0],domainVector[1],domainVector[2],domainVector[3]);
    values[1] = four_d_fn_2(domainVector[0],domainVector[1],domainVector[2],domainVector[3]);
  };
};

int main(int argc, char ** argv)
{
  if( argc < 2 )
    {
      std::cerr << "ERROR: Must run program as '4d_interp <inputfile>'." << std::endl;
      exit(1);
    }
  std::string filename = argv[1];

  MPI_Init(&argc, &argv);
  QUESO::FullEnvironment env(MPI_COMM_WORLD, filename.c_str(), "", NULL);

  // Define the parameter space. It's 4-dimensional in this example.
  QUESO::VectorSpace<QUESO::GslVector, QUESO::GslMatrix>
    paramSpace(env,"param_", 4, NULL);

  // Define parameter bounds
  QUESO::GslVector paramMins(paramSpace.zeroVector());
  paramMins[0] = -1;
  paramMins[1] = -0.5;
  paramMins[2] = 1.1;
  paramMins[3] = -2.1;

  QUESO::GslVector paramMaxs(paramSpace.zeroVector());
  paramMaxs[0] = 0.9;
  paramMaxs[1] = 3.14;
  paramMaxs[2] = 2.1;
  paramMaxs[3] = 4.1;

  // Define parameter domain.
  QUESO::BoxSubset<QUESO::GslVector, QUESO::GslMatrix>
    paramDomain("param_", paramSpace, paramMins, paramMaxs);

  // How many points to use in each coordinate direction of parameter space.
  // Since we're using an interpolation surrogate, it will evenly space the points
  // in each coordinate direction. These are the points at which the model
  // will be evaluated.
  std::vector<unsigned int> n_points(4);
  n_points[0] = 101;
  n_points[1] = 51;
  n_points[2] = 31;
  n_points[3] = 41;

  // Construct data object. We have two functions we're interpolating
  // at the same time, so n_datasets = 2.
  unsigned int n_datasets = 2;
  QUESO::InterpolationSurrogateDataSet<QUESO::GslVector, QUESO::GslMatrix>
    data(paramDomain,n_points,n_datasets);

  // Construct builder. The builder will add the values from the model evaluations
  // so the data object must stay alive.
  MyInterpolationBuilder<QUESO::GslVector,QUESO::GslMatrix>
    builder( data );

  // The expensive part. The builder will now evaluate the model for all the
  // desired points in parameter space. This will build both interpolants.
  builder.build_values();

  // Now that we've built the data, we write it out so we can reuse it later
  QUESO::InterpolationSurrogateIOASCII<QUESO::GslVector, QUESO::GslMatrix>
    data_writer;

  // Write each datasets separately. We'll be able to read each one in
  // individually and construct separate LinearLagrangeInterpolationSurrogate
  // objects. If we have more than 1 processor at our disposal, have the
  // second data set written out by someone other than processor 0 since
  // processor 0 will be writing out the first data set.
  data_writer.write( "4d_interp_data_1.dat", data.get_dataset(0) );

  if( env.fullComm().NumProc() > 1 )
    data_writer.write( "4d_interp_data_2.dat", data.get_dataset(1), 1 );
  else
    data_writer.write( "4d_interp_data_2.dat", data.get_dataset(1) );

  // The builder put the model values into the data, so now we can give the data
  // to the interpolation surrogate. This object can now be used in a likelihood
  // function for example. Here, we just illustrate calling the surrogate model
  // evaluation.
  QUESO::LinearLagrangeInterpolationSurrogate<QUESO::GslVector,QUESO::GslMatrix>
    four_d_surrogate_1( data.get_dataset(0) );

  QUESO::LinearLagrangeInterpolationSurrogate<QUESO::GslVector,QUESO::GslMatrix>
    four_d_surrogate_2( data.get_dataset(1) );

  // A point in parameter space at which we want to use the surrogate
  QUESO::GslVector domainVector(paramSpace.zeroVector());
  domainVector[0] = -0.4;
  domainVector[1] = 3.0;
  domainVector[2] = 1.5;
  domainVector[3] = 1.65;

  // Evaluate the surrogate model at the given point in parameter space
  // Because the exact function is quadrilinear, our interpolated value
  // should be exact.
  double value_1 = four_d_surrogate_1.evaluate(domainVector);
  double value_2 = four_d_surrogate_2.evaluate(domainVector);

  for( int r = 0; r < env.fullComm().NumProc(); r++ )
    {
      if( env.fullRank() == r )
        {
          std::cout << "======================================" << std::endl
                    << "Processor: " << env.fullRank() << std::endl
                    << "Interpolated value for model 1: " << value_1 << std::endl
                    << "Exact value for model 1: " << four_d_fn_1( domainVector[0],domainVector[1],domainVector[2],domainVector[3])
                    << std::endl
                    << "Interpolated value for model 2: " << value_2 << std::endl
                    << "Exact value for model 2: " << four_d_fn_2( domainVector[0],domainVector[1],domainVector[2],domainVector[3])
                    << std::endl
                    << "======================================" << std::endl;
        }

      MPI_Barrier(MPI_COMM_WORLD);
    }

  MPI_Finalize();

  return 0;
}

double four_d_fn_1( double x, double y, double z, double a )
{
  return 3.0 + 2.5*x - 3.1*y + 2.71*z + 3.14*a
    + 0.1*x*y + 1.2*x*z + 0.5*y*z + 0.1*x*a + 1.1*y*a + 2.1*z*a
    + 0.3*x*y*a + 0.5*x*z*a + 1.2*y*z*a + 0.9*x*y*z
    + 2.5*x*y*z*a;
}

double four_d_fn_2( double x, double y, double z, double a )
{
  return 2.0 + 1.5*x - 2.1*y + 1.71*z + 2.14*a
    + 0.01*x*y + 0.2*x*z + 0.05*y*z + 0.01*x*a + 2.1*y*a + 3.1*z*a
    + 1.3*x*y*a + 1.5*x*z*a + 2.2*y*z*a + 1.9*x*y*z
    + 3.5*x*y*z*a;
}
<|endoftext|>