class WeakGuard;
public:
- // enum class AutoNotifyPolicy {
- // NEVER,
- // ALWAYS,
- // UPDATED
- // };
+ enum class AutoNotifyPolicy {
+ NEVER,
+ ALWAYS,
+ UPDATED
+ };
+
+ enum class SetRequestHandlerPolicy {
+ DEFAULT,
+ ACCEPTANCE
+ };
using Ptr = std::shared_ptr< ResourceObject >;
using ConstPtr = std::shared_ptr< const ResourceObject >;
virtual void notify() const;
- // void setAutoNotifyPolicy(AutoNotifyPolicy);
+ void setAutoNotifyPolicy(AutoNotifyPolicy);
+ AutoNotifyPolicy getAutoNotifyPolicy() const;
+
+ void setSetRequestHandlerPolicy(SetRequestHandlerPolicy);
+ SetRequestHandlerPolicy getSetRequestHandlerPolicy() const;
private:
ResourceObject(uint8_t, ResourceAttributes&&);
OCEntityHandlerResult handleObserve(std::shared_ptr< OC::OCResourceRequest >);
void expectOwnLock() const;
+ void autoNotify(bool, AutoNotifyPolicy) const;
+ void autoNotifyIfNeeded(const std::string& , const ResourceAttributes::Value& );
private:
const uint8_t m_properties;
GetRequestHandler m_getRequestHandler;
SetRequestHandler m_setRequestHandler;
+ AutoNotifyPolicy m_autoNotifyPolicy;
+ SetRequestHandlerPolicy m_setRequestHandlerPolicy;
std::unordered_map< std::string, AttributeUpdatedHandler >
m_keyAttributesUpdatedHandlers;
public:
LockGuard(const ResourceObject&);
LockGuard(const ResourceObject::Ptr);
+ LockGuard(const ResourceObject&, AutoNotifyPolicy);
+ LockGuard(const ResourceObject::Ptr, AutoNotifyPolicy);
~LockGuard();
LockGuard(const LockGuard&) = delete;
private:
const ResourceObject& m_resourceObject;
+ AutoNotifyPolicy m_autoNotifyPolicy;
+ std::function<void()> m_autoNotifyFunc;
};
class ResourceObject::WeakGuard
WeakGuard& operator=(const WeakGuard&) = delete;
WeakGuard& operator=(WeakGuard&&) = delete;
+ bool hasLocked() const;
+
private:
- const ResourceObject& m_serverResource;
bool m_hasLocked;
+ const ResourceObject& m_serverResource;
};
}
}
std::shared_ptr< OC::OCResourceRequest > ocRequest, RESPONSE&& response)
{
auto ocResponse = response.getHandler()->buildResponse(resource);
-
ocResponse->setRequestHandle(ocRequest->getRequestHandle());
ocResponse->setResourceHandle(ocRequest->getResourceHandle());
return RESPONSE::defaultAction();
}
+ typedef void (ResourceObject::* AutoNotifyFunc)(bool, ResourceObject::AutoNotifyPolicy) const;
+
+ std::function <void ()> createAutoNotifyInvoker(AutoNotifyFunc autoNotifyFunc,
+ const ResourceObject& resourceObject, const ResourceAttributes& resourceAttributes,
+ ResourceObject::AutoNotifyPolicy autoNotifyPolicy)
+ {
+ if(autoNotifyPolicy == ResourceObject::AutoNotifyPolicy::UPDATED)
+ {
+ auto&& compareAttributesFunc =
+ std::bind(std::not_equal_to<ResourceAttributes>(),
+ resourceAttributes,
+ std::cref(resourceAttributes));
+ return std::bind(autoNotifyFunc,
+ &resourceObject, std::move(compareAttributesFunc), autoNotifyPolicy);
+ }
+ else if(autoNotifyPolicy == ResourceObject::AutoNotifyPolicy::ALWAYS)
+ {
+ return std::bind(autoNotifyFunc,
+ &resourceObject, true, autoNotifyPolicy);
+ }
+ return {};
+ }
} // unnamed namespace
m_resourceAttributes{ std::move(attrs) },
m_getRequestHandler{ },
m_setRequestHandler{ },
+ m_autoNotifyPolicy { AutoNotifyPolicy::ALWAYS },
+ m_setRequestHandlerPolicy { SetRequestHandlerPolicy::DEFAULT },
m_keyAttributesUpdatedHandlers{ },
m_lockOwner{ },
m_mutex{ },
const ResourceAttributes::Value& value)
{
WeakGuard lock(*this);
+
+ if(lock.hasLocked())
+ {
+ autoNotifyIfNeeded(key, value);
+ }
+
m_resourceAttributes[key] = value;
}
void ResourceObject::setAttribute(const std::string& key, ResourceAttributes::Value&& value)
{
WeakGuard lock(*this);
+
+ if(lock.hasLocked())
+ {
+ autoNotifyIfNeeded(key, value);
+ }
+
m_resourceAttributes[key] = std::move(value);
}
void ResourceObject::setAttribute(std::string&& key, const ResourceAttributes::Value& value)
{
WeakGuard lock(*this);
+
+ if(lock.hasLocked())
+ {
+ autoNotifyIfNeeded(key, value);
+ }
+
m_resourceAttributes[std::move(key)] = value;
}
void ResourceObject::setAttribute(std::string&& key, ResourceAttributes::Value&& value)
{
WeakGuard lock(*this);
+
+ if(lock.hasLocked())
+ {
+ autoNotifyIfNeeded(key, value);
+ }
+
m_resourceAttributes[std::move(key)] = std::move(value);
}
return (bool) m_keyAttributesUpdatedHandlers.erase(key);
}
+ void ResourceObject::autoNotifyIfNeeded(const std::string& key,
+ const ResourceAttributes::Value& value)
+ {
+ autoNotify( m_resourceAttributes.contains(key) == false
+ || m_resourceAttributes.at(key) != value
+ , m_autoNotifyPolicy);
+ }
+
+ void ResourceObject::setAutoNotifyPolicy(AutoNotifyPolicy policy)
+ {
+ m_autoNotifyPolicy = policy;
+ }
+
+ ResourceObject::AutoNotifyPolicy ResourceObject::getAutoNotifyPolicy() const
+ {
+ return m_autoNotifyPolicy;
+ }
+
+ void ResourceObject::setSetRequestHandlerPolicy(SetRequestHandlerPolicy policy)
+ {
+ m_setRequestHandlerPolicy = policy;
+ }
+
+ ResourceObject::SetRequestHandlerPolicy ResourceObject::getSetRequestHandlerPolicy() const
+ {
+ return m_setRequestHandlerPolicy;
+ }
+
+ void ResourceObject::autoNotify(
+ bool isAttributesChanged, AutoNotifyPolicy autoNotifyPolicy) const
+ {
+ if(autoNotifyPolicy == AutoNotifyPolicy::NEVER) return;
+ if(autoNotifyPolicy == AutoNotifyPolicy::UPDATED && isAttributesChanged == false) return;
+ notify();
+ }
+
OCEntityHandlerResult ResourceObject::entityHandler(
std::shared_ptr< OC::OCResourceRequest > request)
{
}
}
+ autoNotify(!replaced.empty(), m_autoNotifyPolicy);
return sendResponse(*this, request, response);
}
}
ResourceObject::LockGuard::LockGuard(const ResourceObject::Ptr ptr) :
- LockGuard{ *ptr }
+ LockGuard{ *ptr , ptr->getAutoNotifyPolicy() }
{
}
ResourceObject::LockGuard::LockGuard(
const ResourceObject& serverResource) :
- m_resourceObject(serverResource)
+ LockGuard{ serverResource, serverResource.getAutoNotifyPolicy()}
+ {
+ }
+
+ ResourceObject::LockGuard::LockGuard(
+ const ResourceObject::Ptr ptr, AutoNotifyPolicy autoNotifyPolicy) :
+ LockGuard{ *ptr, autoNotifyPolicy}
+ {
+ }
+
+ ResourceObject::LockGuard::LockGuard(
+ const ResourceObject& serverResource, AutoNotifyPolicy autoNotifyPolicy) :
+ m_resourceObject(serverResource),
+ m_autoNotifyPolicy(autoNotifyPolicy)
{
if (m_resourceObject.m_lockOwner == std::this_thread::get_id())
{
m_resourceObject.m_mutex.lock();
m_resourceObject.m_lockOwner = std::this_thread::get_id();
+
+ m_autoNotifyFunc = ::createAutoNotifyInvoker(&ResourceObject::autoNotify,
+ m_resourceObject, m_resourceObject.m_resourceAttributes, m_autoNotifyPolicy);
}
ResourceObject::LockGuard::~LockGuard()
{
+ if(m_autoNotifyFunc) m_autoNotifyFunc();
m_resourceObject.m_lockOwner = std::thread::id();
m_resourceObject.m_mutex.unlock();
}
ResourceObject::WeakGuard::WeakGuard(
const ResourceObject& serverResource) :
- m_serverResource(serverResource), m_hasLocked{ false }
+ m_hasLocked{ false }, m_serverResource(serverResource)
{
if (m_serverResource.m_lockOwner != std::this_thread::get_id())
{
m_serverResource.m_mutex.unlock();
}
}
+
+ bool ResourceObject::WeakGuard::hasLocked() const
+ {
+ return m_hasLocked;
+ }
}
}
}
+class AutoNotifyTest: public ResourceObjectTest
+{
+
+public:
+ using NotifyAllObservers = OCStackResult (*)(OCResourceHandle);
+ int value{ 100 };
+
+protected:
+ void initMocks() override
+ {
+ mocks.OnCallFuncOverload(
+ static_cast< NotifyAllObservers >
+ (OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
+ }
+};
+
+TEST_F(AutoNotifyTest, DefalutAutoNotifyPolicyIsAlways)
+{
+ ASSERT_EQ(ResourceObject::AutoNotifyPolicy::ALWAYS, server->getAutoNotifyPolicy());
+}
+
+TEST_F(AutoNotifyTest, SetAutoNotifyPolicyBySetter)
+{
+ server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::NEVER);
+
+ ASSERT_EQ(ResourceObject::AutoNotifyPolicy::NEVER,server->getAutoNotifyPolicy());
+}
+
+TEST_F(AutoNotifyTest, WorkingWithNeverPolicyWhenAttributesNoChangeByGetAttributes)
+{
+ server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::NEVER);
+ {
+ ResourceObject::LockGuard lock(server);
+ server->setAttribute(KEY, value);
+ }
+
+ mocks.NeverCallFuncOverload(
+ static_cast<NotifyAllObservers>
+ (OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
+
+ ResourceObject::LockGuard lock(server);
+ server->setAttribute(KEY, value);
+}
+
+TEST_F(AutoNotifyTest, WorkingWithNeverPolicyWhenAttributesChangeByGetAttributes)
+{
+ server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::NEVER);
+
+ mocks.NeverCallFuncOverload(
+ static_cast<NotifyAllObservers>
+ (OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
+
+ ResourceObject::LockGuard lock(server);
+ server->setAttribute(KEY, value);}
+
+TEST_F(AutoNotifyTest, WorkingWithAlwaysPolicyWhenAttributesNoChangeByGetAttributes)
+{
+ server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::ALWAYS);
+ {
+ ResourceObject::LockGuard lock(server);
+ server->setAttribute(KEY, value);
+ }
+
+ mocks.ExpectCallFuncOverload(
+ static_cast<NotifyAllObservers>
+ (OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
+
+ ResourceObject::LockGuard lock(server);
+ server->setAttribute(KEY, value);
+}
+
+TEST_F(AutoNotifyTest, WorkingWithAlwaysPolicyWhenAttributesChangeByGetAttributes)
+{
+ server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::ALWAYS);
+
+ mocks.ExpectCallFuncOverload(
+ static_cast<NotifyAllObservers>
+ (OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
+
+ ResourceObject::LockGuard lock{ server };
+ server->setAttribute(KEY, value);
+}
+
+TEST_F(AutoNotifyTest, WorkingWithUpdatedPolicyWhenAttributesNoChangeByGetAttributes)
+{
+ server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::UPDATED);
+ {
+ ResourceObject::LockGuard lock(server);
+ server->setAttribute(KEY, value);
+ }
+
+ mocks.NeverCallFuncOverload(
+ static_cast<NotifyAllObservers>
+ (OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
+
+ ResourceObject::LockGuard lock{ server };
+ server->setAttribute(KEY, value);
+}
+
+TEST_F(AutoNotifyTest, WorkingWithUpdatedPolicyWhenAttributesChangeByGetAttributes)
+{
+ server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::UPDATED);
+
+ mocks.ExpectCallFuncOverload(
+ static_cast<NotifyAllObservers>
+ (OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
+
+ ResourceObject::LockGuard lock{ server };
+ server->setAttribute(KEY, value);
+}
+
+TEST_F(AutoNotifyTest, WorkingWithNeverPolicyWhenAttributesNoChangeBySetAttribute)
+{
+ server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::NEVER);
+ server->setAttribute(KEY, value);
+
+ mocks.NeverCallFuncOverload(
+ static_cast<NotifyAllObservers>
+ (OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
+
+ server->setAttribute(KEY, value);
+}
+
+TEST_F(AutoNotifyTest, WorkingWithNeverPolicyWhenAttributesChangeBySetAttribute)
+{
+ server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::NEVER);
+
+ mocks.NeverCallFuncOverload(
+ static_cast<NotifyAllObservers>
+ (OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
+
+ server->setAttribute(KEY, value);
+}
+
+TEST_F(AutoNotifyTest, WorkingWithAlwaysPolicyWhenAttributesNoChangeBySetAttribute)
+{
+ server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::ALWAYS);
+ server->setAttribute(KEY, value);
+
+ mocks.ExpectCallFuncOverload(
+ static_cast<NotifyAllObservers>
+ (OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
+
+ server->setAttribute(KEY, value);
+}
+
+TEST_F(AutoNotifyTest, WorkingWithAlwaysPolicyWhenAttributesChangeBySetAttribute)
+{
+ server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::ALWAYS);
+
+ mocks.ExpectCallFuncOverload(
+ static_cast<NotifyAllObservers>
+ (OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
+
+ server->setAttribute(KEY, value);
+}
+
+TEST_F(AutoNotifyTest, WorkingWithUpdatedPolicyWhenAttributesNoChangeBySetAttribute)
+{
+ server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::UPDATED);
+ server->setAttribute(KEY, value);
+
+ mocks.NeverCallFuncOverload(
+ static_cast<NotifyAllObservers>
+ (OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
+
+ server->setAttribute(KEY, value);
+}
+
+TEST_F(AutoNotifyTest, WorkingWithUpdatedPolicyWhenAttributesChangeBySetAttribute)
+{
+ server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::UPDATED);
+
+ mocks.ExpectCallFuncOverload(
+ static_cast<NotifyAllObservers>
+ (OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
+
+ server->setAttribute(KEY, value);
+}
+
class ResourceObjectHandlingRequestTest: public ResourceObjectTest
{
reinterpret_cast<OCResourceHandle>(0x4321);
public:
- OCResourceRequest::Ptr createRequest(OCMethod method = OC_REST_GET)
+ OCResourceRequest::Ptr createRequest(OCMethod method = OC_REST_GET, OCRepresentation ocRep = {})
{
auto request = make_shared<OCResourceRequest>();
OCEntityHandlerRequest ocEntityHandlerRequest { 0 };
OC::MessageContainer mc;
- OCRepresentation ocRep;
mc.addRepresentation(ocRep);
- string json = mc.getJSONRepresentation(OCInfoFormat::ExcludeOC);
-
+ string json = mc.getJSONRepresentation(OCInfoFormat::IncludeOC);
ocEntityHandlerRequest.requestHandle = fakeRequestHandle;
ocEntityHandlerRequest.resource = fakeResourceHandle;
ocEntityHandlerRequest.method = method;
static_cast<registerResourceSig>(OCPlatform::registerResource)).Do(
bind(&ResourceObjectHandlingRequestTest::registerResourceFake,
this, _1, _2, _3, _4, _5, _6));
-
mocks.OnCallFunc(OCPlatform::unregisterResource).Return(OC_STACK_OK);
}
};
}
+class AutoNotifySetHandlingRequestTest: public ResourceObjectHandlingRequestTest
+{
+public:
+ using NotifyAllObservers = OCStackResult (*)(OCResourceHandle);
+ using SendResponse = OCStackResult (*)(std::shared_ptr<OCResourceResponse>);
+
+public:
+ OCRepresentation createOCRepresentation()
+ {
+ OCRepresentation ocRep;
+
+ constexpr int value{ 100 };
+ vector<string> interface{"oic.if.baseline"};
+ vector<string> type{"core.light"};
+
+ ocRep.setValue("power", value);
+ ocRep.setUri(RESOURCE_URI);
+ ocRep.setResourceInterfaces(interface);
+ ocRep.setResourceTypes(type);
+
+ return ocRep;
+ }
+
+ void initMocks() override
+ {
+ ResourceObjectHandlingRequestTest::initMocks();
+ mocks.OnCallFunc(OCPlatform::sendResponse).Return(OC_STACK_OK);
+ }
+};
+
+TEST_F(AutoNotifySetHandlingRequestTest, WorkingWithNeverPolicyWhenAttributesChangeBySetRequest)
+{
+ server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::NEVER);
+ server->setSetRequestHandlerPolicy(ResourceObject::SetRequestHandlerPolicy::ACCEPTANCE);
+
+ mocks.NeverCallFuncOverload(
+ static_cast<NotifyAllObservers>
+ (OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
+
+ handler(createRequest(OC_REST_PUT, createOCRepresentation()));
+}
+
+TEST_F(AutoNotifySetHandlingRequestTest, WorkingWithAlwaysPolicyWhenAttributesNoChangeBySetRequest)
+{
+ server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::ALWAYS);
+ server->setSetRequestHandlerPolicy(ResourceObject::SetRequestHandlerPolicy::ACCEPTANCE);
+
+ mocks.ExpectCallFuncOverload(
+ static_cast<NotifyAllObservers>
+ (OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
+
+ handler(createRequest(OC_REST_PUT));
+}
+
+TEST_F(AutoNotifySetHandlingRequestTest, WorkingWithUpdatedPolicyWhenAttributesChangeBySetRequest)
+{
+ server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::UPDATED);
+ server->setSetRequestHandlerPolicy(ResourceObject::SetRequestHandlerPolicy::ACCEPTANCE);
+
+ mocks.ExpectCallFuncOverload(
+ static_cast<NotifyAllObservers>
+ (OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
+
+ handler(createRequest(OC_REST_PUT, createOCRepresentation()));
+}
+
+TEST_F(AutoNotifySetHandlingRequestTest, WorkingWithUpdatedPolicyWhenAttributesNoChangeBySetRequest)
+{
+ server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::UPDATED);
+ server->setSetRequestHandlerPolicy(ResourceObject::SetRequestHandlerPolicy::ACCEPTANCE);
+
+ mocks.NeverCallFuncOverload(
+ static_cast<NotifyAllObservers>
+ (OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
+
+ handler(createRequest(OC_REST_PUT));
+}
+
class ResourceObjectSynchronizationTest: public ResourceObjectHandlingRequestTest
{