<commit_before>/*
 * #%L
 * %%
 * Copyright (C) 2011 - 2017 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 <string>
#include <unordered_set>
#include <memory>

#include <gtest/gtest.h>
#include <gmock/gmock.h>

#include "joynr/DiscoveryQos.h"
#include "joynr/Arbitrator.h"
#include "joynr/exceptions/NoCompatibleProviderFoundException.h"
#include "joynr/types/Version.h"
#include "joynr/ArbitrationStrategyFunction.h"
#include "joynr/LastSeenArbitrationStrategyFunction.h"
#include "joynr/QosArbitrationStrategyFunction.h"
#include "joynr/FixedParticipantArbitrationStrategyFunction.h"
#include "joynr/KeywordArbitrationStrategyFunction.h"
#include "joynr/Semaphore.h"
#include "joynr/types/DiscoveryEntryWithMetaInfo.h"
#include "joynr/Future.h"

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

using ::testing::AtLeast;
using ::testing::Throw;
using ::testing::Return;
using ::testing::_;
using ::testing::A;

using namespace joynr;

static const std::string domain("unittest-domain");
static const std::string interfaceName("unittest-interface");
static const std::string exceptionMsgNoEntriesFound =
        "No entries found for domain: " + domain + ", interface: " + interfaceName;
static const std::string exceptionMsgUnableToLookup =
        "Unable to lookup provider (domain: " + domain + ", interface: " + interfaceName
        + ") from discovery. Error: ";

class MockArbitrator : public Arbitrator {
public:
    MockArbitrator(const std::string& domain,
                       const std::string& interfaceName,
                       const joynr::types::Version& interfaceVersion,
                       std::weak_ptr<joynr::system::IDiscoveryAsync> discoveryProxy,
                       const DiscoveryQos& discoveryQos,
                       std::unique_ptr<const ArbitrationStrategyFunction> arbitrationStrategyFunction) : Arbitrator(domain,
                               interfaceName,
                               interfaceVersion,
                               discoveryProxy,
                               discoveryQos,
                               std::move(arbitrationStrategyFunction)){};

    MOCK_METHOD0(attemptArbitration, void (void));
};

class ArbitratorTest : public ::testing::Test {
public:
    ArbitratorTest() :
        lastSeenArbitrationStrategyFunction(std::make_unique<const LastSeenArbitrationStrategyFunction>()),
        qosArbitrationStrategyFunction(std::make_unique<const QosArbitrationStrategyFunction>()),
        keywordArbitrationStrategyFunction(std::make_unique<const KeywordArbitrationStrategyFunction>()),
        fixedParticipantArbitrationStrategyFunction(std::make_unique<const FixedParticipantArbitrationStrategyFunction>()),
        lastSeenDateMs(0),
        expiryDateMs(0),
        defaultDiscoveryTimeoutMs(30000),
        defaultRetryIntervalMs(1000),
        publicKeyId("publicKeyId"),
        mockDiscovery(std::make_shared<MockDiscovery>())
        {}

    void testExceptionFromDiscoveryProxy(std::shared_ptr<Arbitrator> arbitrator, const DiscoveryQos& discoveryQos);
    void testExceptionEmptyResult(std::shared_ptr<Arbitrator> arbitrator, const DiscoveryQos& discoveryQos);

    std::unique_ptr<const ArbitrationStrategyFunction> lastSeenArbitrationStrategyFunction;
    std::unique_ptr<const ArbitrationStrategyFunction> qosArbitrationStrategyFunction;
    std::unique_ptr<const ArbitrationStrategyFunction> keywordArbitrationStrategyFunction;
    std::unique_ptr<const ArbitrationStrategyFunction> fixedParticipantArbitrationStrategyFunction;

protected:
    std::int64_t lastSeenDateMs;
    std::int64_t expiryDateMs;
    std::int64_t defaultDiscoveryTimeoutMs;
    std::int64_t defaultRetryIntervalMs;
    std::string publicKeyId;
    ADD_LOGGER(ArbitratorTest)
    std::shared_ptr<MockDiscovery> mockDiscovery;
    Semaphore semaphore;
};

TEST_F(ArbitratorTest, arbitrationTimeout) {
    types::Version providerVersion;
    std::int64_t discoveryTimeoutMs = std::chrono::milliseconds(1000).count();
    std::int64_t retryIntervalMs = std::chrono::milliseconds(450).count();
    DiscoveryQos discoveryQos;
    discoveryQos.setDiscoveryTimeoutMs(discoveryTimeoutMs);
    discoveryQos.setRetryIntervalMs(retryIntervalMs);
    auto mockArbitrator = std::make_shared<MockArbitrator>("domain",
                    "interfaceName",
                    providerVersion,
                    mockDiscovery,
                    discoveryQos,
                    move(lastSeenArbitrationStrategyFunction));

    auto onSuccess = [](const types::DiscoveryEntryWithMetaInfo&) {
        FAIL();
    };

    auto onError = [this](const exceptions::DiscoveryException& exception) {
        EXPECT_THAT(exception,
                    joynrException(
                        joynr::exceptions::DiscoveryException::TYPE_NAME(),
                        "Arbitration could not be finished in time."));
        semaphore.notify();
    };

    auto start = std::chrono::system_clock::now();

    EXPECT_CALL(*mockArbitrator, attemptArbitration()).Times(AtLeast(1));
    mockArbitrator->startArbitration(onSuccess, onError);

    // Wait for timeout
    // Wait for more than discoveryTimeoutMs milliseconds since it might take some time until the 
    // timeout is reported
    EXPECT_TRUE(semaphore.waitFor(std::chrono::milliseconds(discoveryTimeoutMs * 10)));

    auto now = std::chrono::system_clock::now();

    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);

    JOYNR_LOG_DEBUG(logger(), "Time elapsed for unsuccessful arbitration : {}", elapsed.count());
    ASSERT_GE(elapsed.count(), discoveryTimeoutMs);
}

// Test that the Arbitrator selects the last seen provider
TEST_F(ArbitratorTest, getLastSeen) {
    DiscoveryQos discoveryQos;
    discoveryQos.setArbitrationStrategy(DiscoveryQos::ArbitrationStrategy::LAST_SEEN);
    discoveryQos.setDiscoveryTimeoutMs(defaultDiscoveryTimeoutMs);
    discoveryQos.setRetryIntervalMs(defaultRetryIntervalMs);
    joynr::types::Version providerVersion(47, 11);
    auto lastSeenArbitrator = std::make_shared<Arbitrator>(domain,
                    interfaceName,
                    providerVersion,
                    mockDiscovery,
                    discoveryQos,
                    move(lastSeenArbitrationStrategyFunction));

    std::int64_t latestLastSeenDateMs = 7;
    std::string lastSeenParticipantId = std::to_string(latestLastSeenDateMs);
    types::ProviderQos providerQos(
                      std::vector<types::CustomParameter>(),// custom provider parameters
                      42,                                   // priority
                      joynr::types::ProviderScope::GLOBAL,  // discovery scope
                      false                                 // supports on change notifications
    );

    // Create a list of discovery entries
    std::vector<joynr::types::DiscoveryEntryWithMetaInfo> discoveryEntries;
    for (std::int64_t i = 0; i <= latestLastSeenDateMs; i++) {
        int64_t lastSeenDateMs = i;   std::string participantId = std::to_string(i);
        discoveryEntries.push_back(joynr::types::DiscoveryEntryWithMetaInfo(
                                 providerVersion,<commit_msg>[C++] call stopArbitration at the end of the arbitrationTimeout test<commit_after>/*
 * #%L
 * %%
 * Copyright (C) 2011 - 2017 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 <string>
#include <unordered_set>
#include <memory>

#include <gtest/gtest.h>
#include <gmock/gmock.h>

#include "joynr/DiscoveryQos.h"
#include "joynr/Arbitrator.h"
#include "joynr/exceptions/NoCompatibleProviderFoundException.h"
#include "joynr/types/Version.h"
#include "joynr/ArbitrationStrategyFunction.h"
#include "joynr/LastSeenArbitrationStrategyFunction.h"
#include "joynr/QosArbitrationStrategyFunction.h"
#include "joynr/FixedParticipantArbitrationStrategyFunction.h"
#include "joynr/KeywordArbitrationStrategyFunction.h"
#include "joynr/Semaphore.h"
#include "joynr/types/DiscoveryEntryWithMetaInfo.h"
#include "joynr/Future.h"

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

using ::testing::AtLeast;
using ::testing::Throw;
using ::testing::Return;
using ::testing::_;
using ::testing::A;

using namespace joynr;

static const std::string domain("unittest-domain");
static const std::string interfaceName("unittest-interface");
static const std::string exceptionMsgNoEntriesFound =
        "No entries found for domain: " + domain + ", interface: " + interfaceName;
static const std::string exceptionMsgUnableToLookup =
        "Unable to lookup provider (domain: " + domain + ", interface: " + interfaceName
        + ") from discovery. Error: ";

class MockArbitrator : public Arbitrator {
public:
    MockArbitrator(const std::string& domain,
                       const std::string& interfaceName,
                       const joynr::types::Version& interfaceVersion,
                       std::weak_ptr<joynr::system::IDiscoveryAsync> discoveryProxy,
                       const DiscoveryQos& discoveryQos,
                       std::unique_ptr<const ArbitrationStrategyFunction> arbitrationStrategyFunction) : Arbitrator(domain,
                               interfaceName,
                               interfaceVersion,
                               discoveryProxy,
                               discoveryQos,
                               std::move(arbitrationStrategyFunction)){};

    MOCK_METHOD0(attemptArbitration, void (void));
};

class ArbitratorTest : public ::testing::Test {
public:
    ArbitratorTest() :
        lastSeenArbitrationStrategyFunction(std::make_unique<const LastSeenArbitrationStrategyFunction>()),
        qosArbitrationStrategyFunction(std::make_unique<const QosArbitrationStrategyFunction>()),
        keywordArbitrationStrategyFunction(std::make_unique<const KeywordArbitrationStrategyFunction>()),
        fixedParticipantArbitrationStrategyFunction(std::make_unique<const FixedParticipantArbitrationStrategyFunction>()),
        lastSeenDateMs(0),
        expiryDateMs(0),
        defaultDiscoveryTimeoutMs(30000),
        defaultRetryIntervalMs(1000),
        publicKeyId("publicKeyId"),
        mockDiscovery(std::make_shared<MockDiscovery>())
        {}

    void testExceptionFromDiscoveryProxy(std::shared_ptr<Arbitrator> arbitrator, const DiscoveryQos& discoveryQos);
    void testExceptionEmptyResult(std::shared_ptr<Arbitrator> arbitrator, const DiscoveryQos& discoveryQos);

    std::unique_ptr<const ArbitrationStrategyFunction> lastSeenArbitrationStrategyFunction;
    std::unique_ptr<const ArbitrationStrategyFunction> qosArbitrationStrategyFunction;
    std::unique_ptr<const ArbitrationStrategyFunction> keywordArbitrationStrategyFunction;
    std::unique_ptr<const ArbitrationStrategyFunction> fixedParticipantArbitrationStrategyFunction;

protected:
    std::int64_t lastSeenDateMs;
    std::int64_t expiryDateMs;
    std::int64_t defaultDiscoveryTimeoutMs;
    std::int64_t defaultRetryIntervalMs;
    std::string publicKeyId;
    ADD_LOGGER(ArbitratorTest)
    std::shared_ptr<MockDiscovery> mockDiscovery;
    Semaphore semaphore;
};

TEST_F(ArbitratorTest, arbitrationTimeout) {
    types::Version providerVersion;
    std::int64_t discoveryTimeoutMs = std::chrono::milliseconds(1000).count();
    std::int64_t retryIntervalMs = std::chrono::milliseconds(450).count();
    DiscoveryQos discoveryQos;
    discoveryQos.setDiscoveryTimeoutMs(discoveryTimeoutMs);
    discoveryQos.setRetryIntervalMs(retryIntervalMs);
    auto mockArbitrator = std::make_shared<MockArbitrator>("domain",
                    "interfaceName",
                    providerVersion,
                    mockDiscovery,
                    discoveryQos,
                    move(lastSeenArbitrationStrategyFunction));

    auto onSuccess = [](const types::DiscoveryEntryWithMetaInfo&) {
        FAIL();
    };

    auto onError = [this](const exceptions::DiscoveryException& exception) {
        EXPECT_THAT(exception,
                    joynrException(
                        joynr::exceptions::DiscoveryException::TYPE_NAME(),
                        "Arbitration could not be finished in time."));
        semaphore.notify();
    };

    auto start = std::chrono::system_clock::now();

    EXPECT_CALL(*mockArbitrator, attemptArbitration()).Times(AtLeast(1));
    mockArbitrator->startArbitration(onSuccess, onError);

    // Wait for timeout
    // Wait for more than discoveryTimeoutMs milliseconds since it might take some time until the 
    // timeout is reported
    EXPECT_TRUE(semaphore.waitFor(std::chrono::milliseconds(discoveryTimeoutMs * 10)));

    auto now = std::chrono::system_clock::now();

    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);

    JOYNR_LOG_DEBUG(logger(), "Time elapsed for unsuccessful arbitration : {}", elapsed.count());
    ASSERT_GE(elapsed.count(), discoveryTimeoutMs);
    mockArbitrator->stopArbitration();
}

// Test that the Arbitrator selects the last seen provider
TEST_F(ArbitratorTest, getLastSeen) {
    DiscoveryQos discoveryQos;
    discoveryQos.setArbitrationStrategy(DiscoveryQos::ArbitrationStrategy::LAST_SEEN);
    discoveryQos.setDiscoveryTimeoutMs(defaultDiscoveryTimeoutMs);
    discoveryQos.setRetryIntervalMs(defaultRetryIntervalMs);
    joynr::types::Version providerVersion(47, 11);
    auto lastSeenArbitrator = std::make_shared<Arbitrator>(domain,
                    interfaceName,
                    providerVersion,
                    mockDiscovery,
                    discoveryQos,
                    move(lastSeenArbitrationStrategyFunction));

    std::int64_t latestLastSeenDateMs = 7;
    std::string lastSeenParticipantId = std::to_string(latestLastSeenDateMs);
    types::ProviderQos providerQos(
                      std::vector<types::CustomParameter>(),// custom provider parameters
                      42,                                   // priority
                      joynr::types::ProviderScope::GLOBAL,  // discovery scope
                      false                                 // supports on change notifications
    );

    // Create a list of discovery entries
    std::vector<joynr::types::DiscoveryEntryWithMetaInfo> discoveryEntries;
    for (std::int64_t i = 0; i <= latestLastSeenDateMs; i++) {
        int64_t lastSeenDateMs = i;   std::string participantId = std::to_string(i);
        discoveryEntries.push_back(joynr::types::DiscoveryEntryWithMetaInfo(
                                 providerVersion,
{'fraction_non_alphanumeric': 0.09710664447573934, 'fraction_numerical': 0.007681474843169889, 'mean_word_length': 3.489367816091954, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 54, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3418594', 'n_tokens_mistral': 4381, 'n_tokens_neox': 4168, 'n_words': 861}