/** * Trigger: The dispatcher receives a SubscriptionRequest. * Expected: The PublicationManager creates a PublisherRunnable and polls * the MockCaller for the attribute. */ TEST_F(SubscriptionTest, receive_subscriptionRequestAndPollAttribute) { // Use a semaphore to count and wait on calls to the mockRequestCaller Semaphore semaphore(0); EXPECT_CALL(*mockRequestCaller, getLocation(_,_)) .WillRepeatedly( DoAll( Invoke(mockRequestCaller.get(), &MockTestRequestCaller::invokeLocationOnSuccessFct), ReleaseSemaphore(&semaphore))); std::string attributeName = "Location"; Variant subscriptionQos = Variant::make<OnChangeWithKeepAliveSubscriptionQos>(OnChangeWithKeepAliveSubscriptionQos( 500, // validity_ms 1000, // minInterval_ms 2000, // maxInterval_ms 1000 // alertInterval_ms )); std::string subscriptionId = "SubscriptionID"; SubscriptionRequest subscriptionRequest; subscriptionRequest.setSubscriptionId(subscriptionId); subscriptionRequest.setSubscribeToName(attributeName); subscriptionRequest.setQos(subscriptionQos); JoynrMessage msg = messageFactory.createSubscriptionRequest( proxyParticipantId, providerParticipantId, qos, subscriptionRequest); dispatcher.addRequestCaller(providerParticipantId, mockRequestCaller); dispatcher.receive(msg); // Wait for a call to be made to the mockRequestCaller ASSERT_TRUE(semaphore.waitFor(std::chrono::seconds(1))); }
/** * Precondition: A provider is registered and there is at least one subscription for it. * Trigger: A subscription stop message is received * Expected: The PublicationManager stops the publications for this provider */ TEST_F(SubscriptionTest, stopMessage_stopsPublications) { qRegisterMetaType<QtOnChangeWithKeepAliveSubscriptionQos>("QtOnChangeWithKeepAliveSubscriptionQos"); qRegisterMetaType<SubscriptionRequest>("SubscriptionRequest"); // Use a semaphore to count and wait on calls to the mockRequestCaller QSemaphore semaphore(0); EXPECT_CALL(*mockRequestCaller, getLocation(_)) .WillRepeatedly( DoAll( Invoke(mockRequestCaller.data(), &MockTestRequestCaller::invokeOnSuccessFct), ReleaseSemaphore(&semaphore))); dispatcher.addRequestCaller(providerParticipantId, mockRequestCaller); QString attributeName = "Location"; auto subscriptionQos = QSharedPointer<QtSubscriptionQos>(new QtOnChangeWithKeepAliveSubscriptionQos( 1200, // validity_ms 10, // minInterval_ms 100, // maxInterval_ms 1100 // alertInterval_ms )); QString subscriptionId = "SubscriptionID"; SubscriptionRequest subscriptionRequest; subscriptionRequest.setSubscriptionId(subscriptionId); subscriptionRequest.setSubscribeToName(attributeName); subscriptionRequest.setQos(subscriptionQos); JoynrMessage msg = messageFactory.createSubscriptionRequest( QString::fromStdString(proxyParticipantId), QString::fromStdString(providerParticipantId), qos, subscriptionRequest); // first received message with subscription request dispatcher.receive(msg); // wait for two requests from the subscription ASSERT_TRUE(semaphore.tryAcquire(2, 1000)); SubscriptionStop subscriptionStop; subscriptionStop.setSubscriptionId(subscriptionRequest.getSubscriptionId()); // receive a subscription stop message msg = messageFactory.createSubscriptionStop( QString::fromStdString(proxyParticipantId), QString::fromStdString(providerParticipantId), qos, subscriptionStop); dispatcher.receive(msg); // assert that less than 2 requests happen in the next 300 milliseconds ASSERT_FALSE(semaphore.tryAcquire(2, 300)); }
void ClassDeserializerImpl<SubscriptionRequest>::deserialize( SubscriptionRequest& subscriptionRequest, IObject& o) { while (o.hasNextField()) { IField& field = o.nextField(); if (field.name() == "subscriptionId") { subscriptionRequest.setSubscriptionId(field.value()); } if (field.name() == "subscribedToName") { subscriptionRequest.setSubscribeToName(field.value()); } if (field.name() == "qos") { Variant qos = convertVariant(field.value()); subscriptionRequest.setQos(qos); } } }
/** * Precondition: Dispatcher receives a SubscriptionRequest for a not(yet) existing Provider. * Trigger: The provider is registered. * Expected: The PublicationManager registers the provider and notifies the PublicationManager * to restore waiting Subscriptions */ TEST_F(SubscriptionTest, receive_RestoresSubscription) { qRegisterMetaType<QtOnChangeWithKeepAliveSubscriptionQos>("QtOnChangeWithKeepAliveSubscriptionQos"); qRegisterMetaType<SubscriptionRequest>("SubscriptionRequest"); // Use a semaphore to count and wait on calls to the mockRequestCaller QSemaphore semaphore(0); EXPECT_CALL( *mockRequestCaller, getLocation(A<std::function<void(const types::Localisation::GpsLocation&)>>()) ) .WillOnce(DoAll( Invoke(mockRequestCaller.data(), &MockTestRequestCaller::invokeOnSuccessFct), ReleaseSemaphore(&semaphore) )); QString attributeName = "Location"; auto subscriptionQos = QSharedPointer<QtSubscriptionQos>(new QtOnChangeWithKeepAliveSubscriptionQos( 80, // validity_ms 100, // minInterval_ms 200, // maxInterval_ms 80 // alertInterval_ms )); QString subscriptionId = "SubscriptionID"; SubscriptionRequest subscriptionRequest; subscriptionRequest.setSubscriptionId(subscriptionId); subscriptionRequest.setSubscribeToName(attributeName); subscriptionRequest.setQos(subscriptionQos); JoynrMessage msg = messageFactory.createSubscriptionRequest( QString::fromStdString(proxyParticipantId), QString::fromStdString(providerParticipantId), qos, subscriptionRequest); // first received message with subscription request dispatcher.receive(msg); dispatcher.addRequestCaller(providerParticipantId, mockRequestCaller); ASSERT_TRUE(semaphore.tryAcquire(1,15000)); //Try to acquire a semaphore for up to 5 seconds. Acquireing the semaphore will only work, if the mockRequestCaller has been called //and will be much faster than waiting for 500ms to make sure it has been called }
/** * Precondition: A provider is registered and there is at least one subscription for it. * Trigger: The request caller is removed from the dispatcher * Expected: The PublicationManager stops all subscriptions for this provider */ TEST_F(SubscriptionTest, removeRequestCaller_stopsPublications) { // Use a semaphore to count and wait on calls to the mockRequestCaller Semaphore semaphore(0); EXPECT_CALL(*mockRequestCaller, getLocation(_,_)) .WillRepeatedly( DoAll( Invoke(mockRequestCaller.get(), &MockTestRequestCaller::invokeLocationOnSuccessFct), ReleaseSemaphore(&semaphore))); dispatcher.addRequestCaller(providerParticipantId, mockRequestCaller); Variant subscriptionQos = Variant::make<OnChangeWithKeepAliveSubscriptionQos>(OnChangeWithKeepAliveSubscriptionQos( 1200, // validity_ms 10, // minInterval_ms 100, // maxInterval_ms 1100 // alertInterval_ms )); std::string subscriptionId = "SubscriptionID"; SubscriptionRequest subscriptionRequest; subscriptionRequest.setSubscriptionId(subscriptionId); std::string attributeName = "Location"; subscriptionRequest.setSubscribeToName(attributeName); subscriptionRequest.setQos(subscriptionQos); JoynrMessage msg = messageFactory.createSubscriptionRequest( proxyParticipantId, providerParticipantId, qos, subscriptionRequest); // first received message with subscription request dispatcher.receive(msg); // wait for two requests from the subscription ASSERT_TRUE(semaphore.waitFor(std::chrono::seconds(1))); ASSERT_TRUE(semaphore.waitFor(std::chrono::seconds(1))); // remove the request caller dispatcher.removeRequestCaller(providerParticipantId); // assert that less than 2 requests happen in the next 300 milliseconds semaphore.waitFor(std::chrono::milliseconds(300)); ASSERT_FALSE(semaphore.waitFor(std::chrono::milliseconds(300))); }
/** * Precondition: Dispatcher receives a SubscriptionRequest for a not(yet) existing Provider. * Trigger: The provider is registered. * Expected: The PublicationManager registers the provider and notifies the PublicationManager * to restore waiting Subscriptions */ TEST_F(SubscriptionTest, receive_RestoresSubscription) { // Use a semaphore to count and wait on calls to the mockRequestCaller Semaphore semaphore(0); EXPECT_CALL( *mockRequestCaller, getLocation(A<std::function<void(const types::Localisation::GpsLocation&)>>(), A<std::function<void(const joynr::exceptions::ProviderRuntimeException&)>>()) ) .WillOnce(DoAll( Invoke(mockRequestCaller.get(), &MockTestRequestCaller::invokeLocationOnSuccessFct), ReleaseSemaphore(&semaphore) )); std::string attributeName = "Location"; Variant subscriptionQos = Variant::make<OnChangeWithKeepAliveSubscriptionQos>(OnChangeWithKeepAliveSubscriptionQos( 500, // validity_ms 1000, // minInterval_ms 2000, // maxInterval_ms 1000 // alertInterval_ms )); std::string subscriptionId = "SubscriptionID"; SubscriptionRequest subscriptionRequest; subscriptionRequest.setSubscriptionId(subscriptionId); subscriptionRequest.setSubscribeToName(attributeName); subscriptionRequest.setQos(subscriptionQos); JoynrMessage msg = messageFactory.createSubscriptionRequest( proxyParticipantId, providerParticipantId, qos, subscriptionRequest); // first received message with subscription request dispatcher.receive(msg); dispatcher.addRequestCaller(providerParticipantId, mockRequestCaller); ASSERT_TRUE(semaphore.waitFor(std::chrono::seconds(15))); //Try to acquire a semaphore for up to 15 seconds. Acquireing the semaphore will only work, if the mockRequestCaller has been called //and will be much faster than waiting for 1s to make sure it has been called }
/** * Trigger: The dispatcher receives a SubscriptionRequest. * Expected: The PublicationManager creates a PublisherRunnable and polls * the MockCaller for the attribute. */ TEST_F(SubscriptionTest, receive_subscriptionRequestAndPollAttribute) { qRegisterMetaType<QtOnChangeWithKeepAliveSubscriptionQos>("QtOnChangeWithKeepAliveSubscriptionQos"); qRegisterMetaType<SubscriptionRequest>("SubscriptionRequest"); // Use a semaphore to count and wait on calls to the mockRequestCaller QSemaphore semaphore(0); EXPECT_CALL(*mockRequestCaller, getLocation(_)) .WillRepeatedly( DoAll( Invoke(mockRequestCaller.data(), &MockTestRequestCaller::invokeOnSuccessFct), ReleaseSemaphore(&semaphore))); QString attributeName = "Location"; auto subscriptionQos = QSharedPointer<QtSubscriptionQos>(new QtOnChangeWithKeepAliveSubscriptionQos( 80, // validity_ms 100, // minInterval_ms 200, // maxInterval_ms 80 // alertInterval_ms )); QString subscriptionId = "SubscriptionID"; SubscriptionRequest subscriptionRequest; subscriptionRequest.setSubscriptionId(subscriptionId); subscriptionRequest.setSubscribeToName(attributeName); subscriptionRequest.setQos(subscriptionQos); JoynrMessage msg = messageFactory.createSubscriptionRequest( QString::fromStdString(proxyParticipantId), QString::fromStdString(providerParticipantId), qos, subscriptionRequest); dispatcher.addRequestCaller(providerParticipantId, mockRequestCaller); dispatcher.receive(msg); // Wait for a call to be made to the mockRequestCaller ASSERT_TRUE(semaphore.tryAcquire(1,1000)); }
TEST_F(SubscriptionTest, sendPublication_attributeWithSingleArrayParam) { std::string subscriptionId = "SubscriptionID"; Variant subscriptionQos = Variant::make<OnChangeSubscriptionQos>(OnChangeSubscriptionQos( 800, // validity_ms 0 // minInterval_ms )); // Use a semaphore to count and wait on calls to the mockRequestCaller Semaphore semaphore(0); SubscriptionRequest subscriptionRequest; subscriptionRequest.setSubscriptionId(subscriptionId); subscriptionRequest.setSubscribeToName("listOfStrings"); subscriptionRequest.setQos(subscriptionQos); EXPECT_CALL( *provider, getListOfStrings(A<std::function<void(const std::vector<std::string> &)>>(), A<std::function<void(const joynr::exceptions::ProviderRuntimeException&)>>()) ) .WillOnce(DoAll( Invoke(provider.get(), &MockTestProvider::invokeListOfStringsOnSuccess), ReleaseSemaphore(&semaphore) )); auto mockMessageRouter = std::make_shared<MockMessageRouter>(); JoynrMessageSender* joynrMessageSender = new JoynrMessageSender(mockMessageRouter); /* ensure the serialization succeeds and the first publication is send to the proxy */ EXPECT_CALL(*mockMessageRouter, route( AllOf( A<JoynrMessage>(), Property(&JoynrMessage::getHeaderFrom, Eq(providerParticipantId)), Property(&JoynrMessage::getHeaderTo, Eq(proxyParticipantId))), _ )); publicationManager->add( proxyParticipantId, providerParticipantId, requestCaller, subscriptionRequest, joynrMessageSender); ASSERT_TRUE(semaphore.waitFor(std::chrono::seconds(15))); std::vector<std::string> listOfStrings; listOfStrings.push_back("1"); listOfStrings.push_back("2"); /* ensure the value change leads to another publication */ Mock::VerifyAndClear(mockMessageRouter.get()); EXPECT_CALL(*mockMessageRouter, route( AllOf( A<JoynrMessage>(), Property(&JoynrMessage::getHeaderFrom, Eq(providerParticipantId)), Property(&JoynrMessage::getHeaderTo, Eq(proxyParticipantId))), _ )); provider->listOfStringsChanged(listOfStrings); delete joynrMessageSender; }
void SubscriptionManager::registerSubscription( const std::string& subscribeToName, std::shared_ptr<ISubscriptionCallback> subscriptionCaller, const Variant& qosVariant, SubscriptionRequest& subscriptionRequest) { // Register the subscription std::string subscriptionId = subscriptionRequest.getSubscriptionId(); JOYNR_LOG_DEBUG(logger, "Subscription registered. ID={}", subscriptionId); if (subscriptions.contains(subscriptionId)) { // pre-existing subscription: remove it first from the internal data structure unregisterSubscription(subscriptionId); } std::int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::system_clock::now().time_since_epoch()).count(); subscriptionRequest.setQos(qosVariant); const SubscriptionQos* qos = subscriptionRequest.getSubscriptionQosPtr(); if (qos->getExpiryDateMs() != SubscriptionQos::NO_EXPIRY_DATE() && qos->getExpiryDateMs() < now) { throw std::invalid_argument("Subscription ExpiryDate " + std::to_string(qos->getExpiryDateMs()) + " in the past. Now: " + std::to_string(now)); } auto subscription = std::make_shared<Subscription>(subscriptionCaller); subscriptions.insert(subscriptionId, subscription); { std::lock_guard<std::recursive_mutex> subscriptionLocker(subscription->mutex); if (SubscriptionUtil::getAlertInterval(qosVariant) > 0 && SubscriptionUtil::getPeriodicPublicationInterval(qosVariant) > 0) { JOYNR_LOG_DEBUG(logger, "Will notify if updates are missed."); std::int64_t alertAfterInterval = SubscriptionUtil::getAlertInterval(qosVariant); JoynrTimePoint expiryDate(std::chrono::milliseconds(qos->getExpiryDateMs())); if (qos->getExpiryDateMs() == SubscriptionQos::NO_EXPIRY_DATE()) { expiryDate = JoynrTimePoint( std::chrono::milliseconds(std::numeric_limits<std::int64_t>::max())); } std::int64_t periodicPublicationInterval = SubscriptionUtil::getPeriodicPublicationInterval(qosVariant); subscription->missedPublicationRunnableHandle = missedPublicationScheduler->schedule( new MissedPublicationRunnable(expiryDate, periodicPublicationInterval, subscriptionId, subscription, *this, alertAfterInterval), std::chrono::milliseconds(alertAfterInterval)); } else if (qos->getExpiryDateMs() != SubscriptionQos::NO_EXPIRY_DATE()) { std::int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::system_clock::now().time_since_epoch()).count(); subscription->subscriptionEndRunnableHandle = missedPublicationScheduler->schedule( new SubscriptionEndRunnable(subscriptionId, *this), std::chrono::milliseconds(qos->getExpiryDateMs() - now)); } } subscriptionRequest.setSubscriptionId(subscriptionId); subscriptionRequest.setSubscribeToName(subscribeToName); }