[resource-manipulation] Add Test Helper class
[platform/upstream/iotivity.git] / service / resource-manipulation / src / serverBuilder / unittests / ResourceObjectTest.cpp
1 //******************************************************************
2 //
3 // Copyright 2015 Samsung Electronics All Rights Reserved.
4 //
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 //
11 //      http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 //
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
20
21 #include <UnitTestHelper.h>
22
23 #include <ResourceObject.h>
24
25 #include <OCPlatform.h>
26
27 using namespace std;
28 using namespace std::placeholders;
29
30 using namespace OIC::Service;
31 using namespace OC;
32
33 typedef OCStackResult (*registerResource)(OCResourceHandle&, string&, const string&, const string&,
34                            EntityHandler, uint8_t );
35
36 typedef OCStackResult (*NotifyAllObservers)(OCResourceHandle);
37
38 constexpr char RESOURCE_URI[]{ "a/test" };
39 constexpr char RESOURCE_TYPE[]{ "resourceType" };
40 constexpr char KEY[]{ "key" };
41 constexpr int value{ 100 };
42
43 TEST(ResourceObjectBuilderCreateTest, ThrowIfUriIsInvalid)
44 {
45     ASSERT_THROW(ResourceObject::Builder("", "", "").build(), PlatformException);
46 }
47
48 class ResourceObjectBuilderTest: public TestWithMock
49 {
50 protected:
51     void SetUp()
52     {
53         TestWithMock::SetUp();
54
55         mocks.OnCallFuncOverload(static_cast< registerResource >(OCPlatform::registerResource))
56                 .Return(OC_STACK_OK);
57     }
58 };
59
60 TEST_F(ResourceObjectBuilderTest, RegisterResourceWhenCallCreate)
61 {
62     mocks.ExpectCallFuncOverload(static_cast< registerResource >(OCPlatform::registerResource))
63             .Return(OC_STACK_OK);
64
65     ResourceObject::Builder(RESOURCE_URI, RESOURCE_TYPE, "").build();
66 }
67
68 TEST_F(ResourceObjectBuilderTest, ResourceServerHasPropertiesSetByBuilder)
69 {
70     auto serverResource = ResourceObject::Builder(RESOURCE_URI, RESOURCE_TYPE, "").
71             setDiscoverable(false).setObservable(true).build();
72
73     EXPECT_FALSE(serverResource->isDiscoverable());
74     EXPECT_TRUE(serverResource->isObservable());
75 }
76
77 TEST_F(ResourceObjectBuilderTest, ResourceServerHasAttrsSetByBuilder)
78 {
79     ResourceAttributes attrs;
80     attrs[KEY] = 100;
81
82     auto serverResource = ResourceObject::Builder(RESOURCE_URI, RESOURCE_TYPE, "").
83             setAttributes(attrs).build();
84
85     ResourceObject::LockGuard lock{ serverResource, ResourceObject::AutoNotifyPolicy::NEVER };
86     EXPECT_EQ(attrs, serverResource->getAttributes());
87 }
88
89
90 class ResourceObjectTest: public TestWithMock
91 {
92 public:
93     ResourceObject::Ptr server;
94
95 protected:
96     void SetUp()
97     {
98         TestWithMock::SetUp();
99
100         initMocks();
101
102         server = ResourceObject::Builder(RESOURCE_URI, RESOURCE_TYPE, "").build();
103
104         initResourceObject();
105     }
106
107     virtual void initMocks()
108     {
109         mocks.OnCallFuncOverload(static_cast< registerResource >(OCPlatform::registerResource)).
110                 Return(OC_STACK_OK);
111
112         mocks.OnCallFunc(OCPlatform::unregisterResource).Return(OC_STACK_OK);
113     }
114
115     virtual void initResourceObject() {
116         server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::NEVER);
117     }
118 };
119
120 TEST_F(ResourceObjectTest, AccessAttributesWithLock)
121 {
122     {
123         ResourceObject::LockGuard lock{ server };
124         auto& attr = server->getAttributes();
125         attr[KEY] = value;
126     }
127
128     ASSERT_EQ(value, server->getAttribute<int>(KEY));
129 }
130
131 TEST_F(ResourceObjectTest, ThrowIfTryToAccessAttributesWithoutGuard)
132 {
133     ASSERT_THROW(server->getAttributes(), NoLockException);
134 }
135
136 TEST_F(ResourceObjectTest, SettingAttributesWithinGuardDoesntCauseDeadLock)
137 {
138     {
139         ResourceObject::LockGuard guard{ server };
140         server->setAttribute(KEY, value);
141     }
142
143     ASSERT_EQ(value, server->getAttribute<int>(KEY));
144 }
145
146
147 class AutoNotifyTest: public ResourceObjectTest
148 {
149 protected:
150     void initMocks()
151     {
152         mocks.OnCallFuncOverload(static_cast< NotifyAllObservers >(
153                 OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
154     }
155
156     virtual void initResourceObject() {
157         // intended blank
158     }
159 };
160
161 TEST_F(AutoNotifyTest, DefalutAutoNotifyPolicyIsUpdated)
162 {
163     ASSERT_EQ(ResourceObject::AutoNotifyPolicy::UPDATED, server->getAutoNotifyPolicy());
164 }
165
166 TEST_F(AutoNotifyTest, AutoNotifyPolicyCanBeSet)
167 {
168     server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::NEVER);
169
170     ASSERT_EQ(ResourceObject::AutoNotifyPolicy::NEVER, server->getAutoNotifyPolicy());
171 }
172
173 TEST_F(AutoNotifyTest, WithUpdatedPolicy_NeverBeNotifiedIfAttributeIsNotChanged)
174 {
175     server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::UPDATED);
176     server->setAttribute(KEY, value);
177
178     mocks.NeverCallFuncOverload(static_cast< NotifyAllObservers >(
179             OC::OCPlatform::notifyAllObservers));
180
181     server->setAttribute(KEY, value);
182 }
183
184 TEST_F(AutoNotifyTest, WithUpdatedPolicy_WillBeNotifiedIfAttributeIsChanged)
185 {
186     server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::UPDATED);
187     server->setAttribute(KEY, value);
188
189     mocks.ExpectCallFuncOverload(static_cast< NotifyAllObservers >(
190             OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
191
192     server->setAttribute(KEY, value + 1);
193 }
194
195 TEST_F(AutoNotifyTest, WithUpdatedPolicy_WillBeNotifiedIfValueIsAdded)
196 {
197     constexpr char newKey[]{ "newKey" };
198     server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::UPDATED);
199
200     mocks.ExpectCallFuncOverload(static_cast< NotifyAllObservers >(
201             OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
202
203     server->setAttribute(newKey, value);
204 }
205
206 TEST_F(AutoNotifyTest, WithNeverPolicy_NeverBeNotifiedEvenIfAttributeIsChanged)
207 {
208     server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::NEVER);
209
210     mocks.NeverCallFuncOverload(static_cast< NotifyAllObservers >(
211             OC::OCPlatform::notifyAllObservers));
212
213     ResourceObject::LockGuard lock{ server };
214     server->setAttribute(KEY, value);
215 }
216
217 TEST_F(AutoNotifyTest, WithAlwaysPolicy_WillBeNotifiedEvenIfAttributeIsNotChanged)
218 {
219     server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::ALWAYS);
220     server->setAttribute(KEY, value);
221
222     mocks.ExpectCallFuncOverload(static_cast< NotifyAllObservers >(
223             OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
224
225     server->setAttribute(KEY, value);
226 }
227
228
229 class AutoNotifyWithGuardTest: public AutoNotifyTest
230 {
231 };
232
233 TEST_F(AutoNotifyWithGuardTest, GuardFollowsServerPolicyByDefault)
234 {
235     server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::UPDATED);
236
237     mocks.ExpectCallFuncOverload(static_cast< NotifyAllObservers >(
238             OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
239
240     ResourceObject::LockGuard guard{ server };
241     server->setAttribute(KEY, value);
242 }
243
244 TEST_F(AutoNotifyWithGuardTest, GuardCanOverridePolicy)
245 {
246     server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::ALWAYS);
247
248     mocks.NeverCallFuncOverload(static_cast< NotifyAllObservers >(
249             OC::OCPlatform::notifyAllObservers));
250
251     ResourceObject::LockGuard guard{ server, ResourceObject::AutoNotifyPolicy::NEVER };
252     server->getAttributes()[KEY] = value;
253 }
254
255 TEST_F(AutoNotifyWithGuardTest, GuardInvokesNotifyWhenDestroyed)
256 {
257     server->setAutoNotifyPolicy(ResourceObject::AutoNotifyPolicy::NEVER);
258
259     mocks.ExpectCallFuncOverload(static_cast< NotifyAllObservers >(
260             OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
261
262     {
263         ResourceObject::LockGuard guard{ server, ResourceObject::AutoNotifyPolicy::ALWAYS };
264         server->setAttribute(KEY, value);
265     }
266
267     mocks.NeverCallFuncOverload(static_cast< NotifyAllObservers >(
268                OC::OCPlatform::notifyAllObservers)).Return(OC_STACK_OK);
269
270     server->setAttribute(KEY, value);
271 }
272
273
274
275 class ResourceObjectHandlingRequestTest: public ResourceObjectTest
276 {
277 public:
278     EntityHandler handler;
279
280     static constexpr OCRequestHandle fakeRequestHandle =
281             reinterpret_cast<OCRequestHandle>(0x1234);
282     static constexpr OCResourceHandle fakeResourceHandle =
283             reinterpret_cast<OCResourceHandle>(0x4321);
284
285 public:
286     OCResourceRequest::Ptr createRequest(OCMethod method = OC_REST_GET, OCRepresentation ocRep =
287             OCRepresentation{})
288     {
289         auto request = make_shared<OCResourceRequest>();
290
291         OCEntityHandlerRequest ocEntityHandlerRequest { 0 };
292         OC::MessageContainer mc;
293
294         mc.addRepresentation(ocRep);
295
296         ocEntityHandlerRequest.requestHandle = fakeRequestHandle;
297         ocEntityHandlerRequest.resource = fakeResourceHandle;
298         ocEntityHandlerRequest.method = method;
299         ocEntityHandlerRequest.payload = reinterpret_cast<OCPayload*>(mc.getPayload());
300
301         formResourceRequest(OC_REQUEST_FLAG, &ocEntityHandlerRequest, request);
302
303         return request;
304     }
305
306 protected:
307     OCStackResult registerResourceFake(OCResourceHandle&, string&, const string&,
308             const string&, EntityHandler handler, uint8_t)
309     {
310         this->handler = handler;
311         return OC_STACK_OK;
312     }
313
314     void initMocks()
315     {
316         mocks.OnCallFuncOverload(
317             static_cast<registerResource>(OCPlatform::registerResource)).Do(
318                     bind(&ResourceObjectHandlingRequestTest::registerResourceFake,
319                             this, _1, _2, _3, _4, _5, _6));
320         mocks.OnCallFunc(OCPlatform::unregisterResource).Return(OC_STACK_OK);
321     }
322 };
323
324 TEST_F(ResourceObjectHandlingRequestTest, CallSendResponseWhenReceiveRequest)
325 {
326     mocks.ExpectCallFunc(OCPlatform::sendResponse).Return(OC_STACK_OK);
327
328     ASSERT_EQ(OC_EH_OK, handler(createRequest()));
329 }
330
331 TEST_F(ResourceObjectHandlingRequestTest, ReturnErrorCodeWhenSendResponseFailed)
332 {
333     mocks.ExpectCallFunc(OCPlatform::sendResponse).Return(OC_STACK_ERROR);
334
335     ASSERT_EQ(OC_EH_ERROR, handler(createRequest()));
336 }
337
338 TEST_F(ResourceObjectHandlingRequestTest, SendResponseWithSameHandlesPassedByRequest)
339 {
340     mocks.ExpectCallFunc(OCPlatform::sendResponse).Match(
341             [](const shared_ptr<OCResourceResponse> response)
342             {
343                 return response->getRequestHandle() == fakeRequestHandle &&
344                         response->getResourceHandle() == fakeResourceHandle;
345             }
346     ).Return(OC_STACK_OK);
347
348     ASSERT_EQ(OC_EH_OK, handler(createRequest()));
349 }
350
351 TEST_F(ResourceObjectHandlingRequestTest, SendResponseWithRCSResponseResults)
352 {
353     constexpr int errorCode{ 1999 };
354     constexpr OCEntityHandlerResult result{ OC_EH_SLOW };
355
356     server->setGetRequestHandler(
357             [](const RCSRequest&, ResourceAttributes&) -> RCSGetResponse
358             {
359                 return RCSGetResponse::create(result, errorCode);
360             }
361     );
362
363     mocks.ExpectCallFunc(OCPlatform::sendResponse).Match(
364             [](const shared_ptr<OCResourceResponse> response)
365             {
366                 return response->getErrorCode() == errorCode &&
367                         response->getResponseResult() == result;
368             }
369     ).Return(OC_STACK_OK);
370
371     ASSERT_EQ(OC_EH_OK, handler(createRequest()));
372 }
373
374 TEST_F(ResourceObjectHandlingRequestTest, SendSetResponseWithCustomAttrsAndResults)
375 {
376     constexpr int errorCode{ 1999 };
377     constexpr OCEntityHandlerResult result{ OC_EH_SLOW };
378     constexpr char value[]{ "value" };
379
380     server->setSetRequestHandler(
381             [](const RCSRequest&, ResourceAttributes&) -> RCSSetResponse
382             {
383                 ResourceAttributes attrs;
384                 attrs[KEY] = value;
385                 return RCSSetResponse::create(attrs, result, errorCode);
386             }
387     );
388
389     mocks.ExpectCallFunc(OCPlatform::sendResponse).Match(
390             [](const shared_ptr<OCResourceResponse> response)
391             {
392                 return value == response->getResourceRepresentation()[KEY].getValue<std::string>()
393                         && response->getErrorCode() == errorCode
394                         && response->getResponseResult() == result;
395             }
396     ).Return(OC_STACK_OK);
397
398     ASSERT_EQ(OC_EH_OK, handler(createRequest(OC_REST_PUT)));
399 }
400
401
402 class SetRequestHandlerPolicyTest: public ResourceObjectHandlingRequestTest
403 {
404 public:
405     typedef OCStackResult (*SendResponse)(std::shared_ptr<OCResourceResponse>);
406
407 public:
408     OCRepresentation createOCRepresentation()
409     {
410         OCRepresentation ocRep;
411
412         vector<string> interface{"oic.if.baseline"};
413         vector<string> type{"core.light"};
414
415         ocRep.setUri(RESOURCE_URI);
416         ocRep.setResourceInterfaces(interface);
417         ocRep.setResourceTypes(type);
418
419         return ocRep;
420     }
421
422     void initMocks()
423     {
424         ResourceObjectHandlingRequestTest::initMocks();
425         mocks.OnCallFunc(OCPlatform::sendResponse).Return(OC_STACK_OK);
426     }
427 };
428
429 TEST_F(SetRequestHandlerPolicyTest, DefalutSetRequestHandlerPolicyIsNever)
430 {
431     ASSERT_EQ(ResourceObject::SetRequestHandlerPolicy::NEVER,
432                 server->getSetRequestHandlerPolicy());
433 }
434
435 TEST_F(SetRequestHandlerPolicyTest, SetRequestHandlerPolicyCanBeSet)
436 {
437     server->setSetRequestHandlerPolicy(ResourceObject::SetRequestHandlerPolicy::ACCEPTANCE);
438
439     ASSERT_EQ(ResourceObject::SetRequestHandlerPolicy::ACCEPTANCE,
440                 server->getSetRequestHandlerPolicy());
441 }
442
443 TEST_F(SetRequestHandlerPolicyTest, WithNeverPolicy_NotAddedIfReceivedNewKeyValuePair)
444 {
445     OCRepresentation ocRep = createOCRepresentation();
446     ocRep.setValue("NewKey", value);
447     server->setSetRequestHandlerPolicy(ResourceObject::SetRequestHandlerPolicy::NEVER);
448
449     handler(createRequest(OC_REST_PUT, ocRep));
450
451     ResourceObject::LockGuard guard{ server };
452     ASSERT_FALSE((server->getAttributes()).contains("NewKey"));
453 }
454
455 TEST_F(SetRequestHandlerPolicyTest, WithAcceptancePolicy_WillBeAddedIfReceivedNewKeyValuePair)
456 {
457     OCRepresentation ocRep = createOCRepresentation();
458     ocRep.setValue("NewKey", value);
459     server->setSetRequestHandlerPolicy(ResourceObject::SetRequestHandlerPolicy::ACCEPTANCE);
460
461     handler(createRequest(OC_REST_PUT, ocRep));
462
463     ResourceObject::LockGuard guard{ server };
464     ASSERT_TRUE((server->getAttributes()).contains("NewKey"));
465 }
466
467
468 class ResourceObjectSynchronizationTest: public ResourceObjectHandlingRequestTest
469 {
470 public:
471
472     static void withLock(ResourceObject::Ptr serverResource, int count)
473     {
474         for (int i=0; i<count; ++i)
475         {
476             ResourceObject::LockGuard lock{ serverResource };
477
478             auto& attrs = serverResource->getAttributes();
479
480             attrs[KEY] = attrs[KEY].get<int>() + 1;
481         }
482     }
483
484     static void withSetter(ResourceObject::Ptr serverResource, int count)
485     {
486         for (int i=0; i<count; ++i)
487         {
488             ResourceObject::LockGuard lock{ serverResource };
489
490             serverResource->setAttribute(KEY, serverResource->getAttribute<int>(KEY) + 1);
491         }
492     }
493 };
494
495 TEST_F(ResourceObjectSynchronizationTest, MultipleAccessToServerResource)
496 {
497     int expected { 0 };
498     vector<thread> threads;
499
500     server->setAttribute(KEY, 0);
501
502     for (int i = 20; i >= 0; --i) {
503         int count = 5000 + i * 100;
504         threads.push_back(thread { withLock, server, count });
505         expected += count;
506     }
507
508     for (int i = 20; i >= 0; --i) {
509         int count = 5000 + i * 100;
510         threads.push_back(thread { withSetter, server, count });
511         expected +=count;
512     }
513
514     for (auto& t : threads)
515     {
516         t.join();
517     }
518
519     ASSERT_EQ(expected, server->getAttribute<int>(KEY));
520 }
521
522 TEST_F(ResourceObjectSynchronizationTest, MultipleAccessToServerResourceWithRequests)
523 {
524     int expected { 0 };
525     vector<thread> threads;
526
527     mocks.OnCallFunc(OCPlatform::sendResponse).Return(OC_STACK_OK);
528
529     server->setAttribute(KEY, 0);
530
531     for (int i = 20; i >= 0; --i) {
532         int count = 5000 + i * 100;
533         threads.push_back(thread{ withLock, server, count });
534         expected += count;
535     }
536
537     for (int i = 20; i >= 0; --i) {
538         int count = 5000 + i * 100;
539         threads.push_back(thread{ withSetter, server, count });
540         expected +=count;
541     }
542
543     threads.push_back(thread{
544         [this]()
545         {
546             for (int i=0; i<10000; ++i)
547             {
548                 if (i % 5 == 0) handler(createRequest(OC_REST_OBSERVE));
549                 handler(createRequest((i & 1) ? OC_REST_GET : OC_REST_PUT));
550             }
551         }
552     });
553
554     for (auto& t : threads)
555     {
556         t.join();
557     }
558
559     ASSERT_EQ(expected, server->getAttribute<int>(KEY));
560 }