/** * Trigger: The dispatcher receives a Publication. * Expected: The SubscriptionManager retrieves the correct SubscriptionCallback and the * Interpreter executes it correctly */ TEST_F(SubscriptionTest, receive_publication ) { qRegisterMetaType<SubscriptionPublication>("SubscriptionPublication"); // getType is used by the ReplyInterpreterFactory to create an interpreter for the reply // so this has to match with the type being passed to the dispatcher in the reply ON_CALL(*mockReplyCaller, getType()).WillByDefault(Return(QString("QtGpsLocation"))); // Use a semaphore to count and wait on calls to the mockSubscriptionListener QSemaphore semaphore(0); EXPECT_CALL(*mockSubscriptionListener, onReceive(A<const types::QtGpsLocation&>())) .WillRepeatedly(ReleaseSemaphore(&semaphore)); //register the subscription on the consumer side QString attributeName = "Location"; auto subscriptionQos = QSharedPointer<QtSubscriptionQos>(new QtOnChangeWithKeepAliveSubscriptionQos( 80, // validity_ms 100, // minInterval_ms 200, // maxInterval_ms 80 // alertInterval_ms )); SubscriptionRequest subscriptionRequest; //construct a reply containing a QtGpsLocation SubscriptionPublication subscriptionPublication; subscriptionPublication.setSubscriptionId(subscriptionRequest.getSubscriptionId()); QList<QVariant> response; response.append(QVariant::fromValue(types::QtGpsLocation::createQt(gpsLocation1))); subscriptionPublication.setResponse(response); QSharedPointer<SubscriptionCallback<types::QtGpsLocation>> subscriptionCallback( new SubscriptionCallback<types::QtGpsLocation>(mockSubscriptionListener)); // subscriptionRequest is an out param subscriptionManager->registerSubscription( attributeName, subscriptionCallback, subscriptionQos, subscriptionRequest); // incoming publication from the provider JoynrMessage msg = messageFactory.createSubscriptionPublication( QString::fromStdString(providerParticipantId), QString::fromStdString(proxyParticipantId), qos, subscriptionPublication); dispatcher.receive(msg); // Assert that only one subscription message is received by the subscription listener ASSERT_TRUE(semaphore.tryAcquire(1, 1000)); ASSERT_FALSE(semaphore.tryAcquire(1, 250)); }
/** * 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)); }
/** * Trigger: The dispatcher receives an enum Publication. * Expected: The SubscriptionManager retrieves the correct SubscriptionCallback and the * Interpreter executes it correctly */ TEST_F(SubscriptionTest, receive_enumPublication ) { // getType is used by the ReplyInterpreterFactory to create an interpreter for the reply // so this has to match with the type being passed to the dispatcher in the reply ON_CALL(*mockReplyCaller, getType()).WillByDefault(Return(std::string("TestEnum"))); // Use a semaphore to count and wait on calls to the mockTestEnumSubscriptionListener Semaphore semaphore(0); EXPECT_CALL(*mockTestEnumSubscriptionListener, onReceive(A<const joynr::tests::testTypes::TestEnum::Enum&>())) .WillRepeatedly(ReleaseSemaphore(&semaphore)); //register the subscription on the consumer side std::string attributeName = "testEnum"; Variant subscriptionQos = Variant::make<OnChangeWithKeepAliveSubscriptionQos>(OnChangeWithKeepAliveSubscriptionQos( 500, // validity_ms 1000, // minInterval_ms 2000, // maxInterval_ms 1000 // alertInterval_ms )); SubscriptionRequest subscriptionRequest; //construct a reply containing a GpsLocation SubscriptionPublication subscriptionPublication; subscriptionPublication.setSubscriptionId(subscriptionRequest.getSubscriptionId()); std::vector<Variant> response; response.push_back(Variant::make<joynr::tests::testTypes::TestEnum::Enum>(tests::testTypes::TestEnum::ZERO)); subscriptionPublication.setResponse(response); auto subscriptionCallback = std::make_shared<SubscriptionCallback<joynr::tests::testTypes::TestEnum::Enum>>(mockTestEnumSubscriptionListener); // subscriptionRequest is an out param subscriptionManager->registerSubscription( attributeName, subscriptionCallback, subscriptionQos, subscriptionRequest); // incoming publication from the provider JoynrMessage msg = messageFactory.createSubscriptionPublication( providerParticipantId, proxyParticipantId, qos, subscriptionPublication); dispatcher.receive(msg); // Assert that only one subscription message is received by the subscription listener ASSERT_TRUE(semaphore.waitFor(std::chrono::seconds(1))); ASSERT_FALSE(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) { // 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); std::string attributeName = "Location"; Variant subscriptionQos = Variant::make<OnChangeWithKeepAliveSubscriptionQos>(OnChangeWithKeepAliveSubscriptionQos( 1200, // validity_ms 10, // minInterval_ms 500, // maxInterval_ms 1100 // 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); // wait for two requests from the subscription ASSERT_TRUE(semaphore.waitFor(std::chrono::seconds(1))); ASSERT_TRUE(semaphore.waitFor(std::chrono::seconds(1))); SubscriptionStop subscriptionStop; subscriptionStop.setSubscriptionId(subscriptionRequest.getSubscriptionId()); // receive a subscription stop message msg = messageFactory.createSubscriptionStop( proxyParticipantId, providerParticipantId, qos, subscriptionStop); dispatcher.receive(msg); ASSERT_FALSE(semaphore.waitFor(std::chrono::seconds(1))); }
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); }