Add common module that expiryTimer.
[platform/upstream/iotivity.git] / service / resource-manipulation / modules / 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 <gtest/gtest.h>
22 #include <HippoMocks/hippomocks.h>
23
24 #include <ResourceObject.h>
25
26 #include <OCPlatform.h>
27
28 using namespace std;
29 using namespace std::placeholders;
30
31 using namespace testing;
32
33 using namespace OIC::Service;
34 using namespace OC;
35
36 typedef OCStackResult (*registerResourceSig)(OCResourceHandle&,
37                        string&,
38                        const string&,
39                        const string&,
40                        EntityHandler,
41                        uint8_t );
42
43 static constexpr char RESOURCE_URI[]{ "a/test" };
44 static constexpr char RESOURCE_TYPE[]{ "resourceType" };
45 static constexpr char KEY[]{ "key" };
46
47 TEST(ResourceObjectBuilderCreateTest, ThrowIfUriIsInvalid)
48 {
49     ASSERT_THROW(ResourceObject::Builder("", "", "").build(), PlatformException);
50 }
51
52 class ResourceObjectBuilderTest: public Test
53 {
54 public:
55     MockRepository mocks;
56
57 protected:
58     void SetUp() override
59     {
60         mocks.OnCallFuncOverload(static_cast<registerResourceSig>(OCPlatform::registerResource))
61                 .Return(OC_STACK_OK);
62     }
63 };
64
65 TEST_F(ResourceObjectBuilderTest, RegisterResourceWhenCallCreate)
66 {
67     mocks.ExpectCallFuncOverload(static_cast<registerResourceSig>(OCPlatform::registerResource))
68             .Return(OC_STACK_OK);
69     ResourceObject::Builder(RESOURCE_URI, RESOURCE_TYPE, "").build();
70 }
71
72 TEST_F(ResourceObjectBuilderTest, ResourceServerHasPropertiesSetByBuilder)
73 {
74     auto serverResource = ResourceObject::Builder(RESOURCE_URI, RESOURCE_TYPE, "").
75             setDiscoverable(false).setObservable(true).build();
76
77     EXPECT_FALSE(serverResource->isDiscoverable());
78     EXPECT_TRUE(serverResource->isObservable());
79 }
80
81 TEST_F(ResourceObjectBuilderTest, ResourceServerHasAttrsSetByBuilder)
82 {
83     ResourceAttributes attrs;
84     attrs[KEY] = 100;
85
86     auto serverResource = ResourceObject::Builder(RESOURCE_URI, RESOURCE_TYPE, "").
87             setAttributes(attrs).build();
88
89     ResourceObject::LockGuard lock{ serverResource };
90     EXPECT_EQ(attrs, serverResource->getAttributes());
91 }
92
93
94 class ResourceObjectTest: public Test
95 {
96 public:
97     MockRepository mocks;
98     ResourceObject::Ptr server;
99
100 protected:
101     void SetUp() override
102     {
103         initMocks();
104         server = ResourceObject::Builder(RESOURCE_URI, RESOURCE_TYPE, "").build();
105     }
106
107     virtual void initMocks()
108     {
109         mocks.OnCallFuncOverload(static_cast< registerResourceSig >(OCPlatform::registerResource)).
110                 Return(OC_STACK_OK);
111
112         mocks.OnCallFunc(OCPlatform::unregisterResource).Return(OC_STACK_OK);
113     }
114 };
115
116 TEST_F(ResourceObjectTest, AccessAttributesWithLock)
117 {
118     constexpr int value{ 100 };
119
120     {
121         ResourceObject::LockGuard lock{ server };
122         auto& attr = server->getAttributes();
123         attr[KEY] = value;
124     }
125
126     ASSERT_EQ(value, server->getAttribute<int>(KEY));
127 }
128
129 TEST_F(ResourceObjectTest, ThrowIfTryToAccessAttributesWithoutLock)
130 {
131     ASSERT_THROW(server->getAttributes(), NoLockException);
132 }
133
134 TEST_F(ResourceObjectTest, ThrowIfLockRecursively)
135 {
136     ResourceObject::LockGuard lock{ server };
137
138     ASSERT_THROW(ResourceObject::LockGuard again{ server }, DeadLockException);
139 }
140
141 TEST_F(ResourceObjectTest, AccessingAttributesWithMethodsWithinLockDoesntCauseDeadLock)
142 {
143     constexpr int value{ 100 };
144
145     {
146         ResourceObject::LockGuard lock{ server };
147         server->setAttribute(KEY, value);
148     }
149
150     ASSERT_EQ(value, server->getAttribute<int>(KEY));
151 }
152
153
154
155 class ResourceObjectHandlingRequestTest: public ResourceObjectTest
156 {
157 public:
158     EntityHandler handler;
159
160     static constexpr OCRequestHandle fakeRequestHandle =
161             reinterpret_cast<OCRequestHandle>(0x1234);
162     static constexpr OCResourceHandle fakeResourceHandle =
163             reinterpret_cast<OCResourceHandle>(0x4321);
164
165 public:
166     OCResourceRequest::Ptr createRequest(OCMethod method = OC_REST_GET)
167     {
168         auto request = make_shared<OCResourceRequest>();
169
170         OCEntityHandlerRequest ocEntityHandlerRequest { 0 };
171         OC::MessageContainer mc;
172         OCRepresentation ocRep;
173
174         mc.addRepresentation(ocRep);
175
176         string json = mc.getJSONRepresentation(OCInfoFormat::ExcludeOC);
177
178         ocEntityHandlerRequest.requestHandle = fakeRequestHandle;
179         ocEntityHandlerRequest.resource = fakeResourceHandle;
180         ocEntityHandlerRequest.method = method;
181         ocEntityHandlerRequest.reqJSONPayload = &json[0];
182
183         formResourceRequest(OC_REQUEST_FLAG, &ocEntityHandlerRequest, request);
184
185         return request;
186     }
187
188 protected:
189     OCStackResult registerResourceFake(OCResourceHandle&, string&, const string&,
190             const string&, EntityHandler handler, uint8_t)
191     {
192         this->handler = handler;
193         return OC_STACK_OK;
194     }
195
196     void initMocks() override
197     {
198         mocks.OnCallFuncOverload(
199             static_cast<registerResourceSig>(OCPlatform::registerResource)).Do(
200                     bind(&ResourceObjectHandlingRequestTest::registerResourceFake,
201                             this, _1, _2, _3, _4, _5, _6));
202
203         mocks.OnCallFunc(OCPlatform::unregisterResource).Return(OC_STACK_OK);
204     }
205 };
206
207 TEST_F(ResourceObjectHandlingRequestTest, CallSendResponseWhenReceiveRequest)
208 {
209     mocks.ExpectCallFunc(OCPlatform::sendResponse).Return(OC_STACK_OK);
210
211     ASSERT_EQ(OC_EH_OK, handler(createRequest()));
212 }
213
214 TEST_F(ResourceObjectHandlingRequestTest, ReturnErrorCodeWhenSendResponseFailed)
215 {
216     mocks.ExpectCallFunc(OCPlatform::sendResponse).Return(OC_STACK_ERROR);
217
218     ASSERT_EQ(OC_EH_ERROR, handler(createRequest()));
219 }
220
221 TEST_F(ResourceObjectHandlingRequestTest, SendResponseWithSameHandlesPassedByRequest)
222 {
223     mocks.ExpectCallFunc(OCPlatform::sendResponse).Match(
224             [](const shared_ptr<OCResourceResponse> response)
225             {
226                 return response->getRequestHandle() == fakeRequestHandle &&
227                         response->getResourceHandle() == fakeResourceHandle;
228             }
229     ).Return(OC_STACK_OK);
230
231     ASSERT_EQ(OC_EH_OK, handler(createRequest()));
232 }
233
234 TEST_F(ResourceObjectHandlingRequestTest, SendResponseWithPrimitiveResponseResults)
235 {
236     constexpr int errorCode{ 1999 };
237     constexpr OCEntityHandlerResult result{ OC_EH_SLOW };
238
239     server->setGetRequestHandler(
240             [](const PrimitiveRequest&, ResourceAttributes&) -> PrimitiveGetResponse
241             {
242                 return PrimitiveGetResponse::create(result, errorCode);
243             }
244     );
245
246     mocks.ExpectCallFunc(OCPlatform::sendResponse).Match(
247             [](const shared_ptr<OCResourceResponse> response)
248             {
249                 return response->getErrorCode() == errorCode &&
250                         response->getResponseResult() == result;
251             }
252     ).Return(OC_STACK_OK);
253
254     ASSERT_EQ(OC_EH_OK, handler(createRequest()));
255 }
256
257 TEST_F(ResourceObjectHandlingRequestTest, SendSetResponseWithCustomAttrsAndResults)
258 {
259     constexpr int errorCode{ 1999 };
260     constexpr OCEntityHandlerResult result{ OC_EH_SLOW };
261     constexpr char value[]{ "value" };
262
263     server->setSetRequestHandler(
264             [](const PrimitiveRequest&, ResourceAttributes&) -> PrimitiveSetResponse
265             {
266                 ResourceAttributes attrs;
267                 attrs[KEY] = value;
268                 return PrimitiveSetResponse::create(attrs, result, errorCode);
269             }
270     );
271
272     mocks.ExpectCallFunc(OCPlatform::sendResponse).Match(
273             [](const shared_ptr<OCResourceResponse> response)
274             {
275                 return value == response->getResourceRepresentation()[KEY].getValue<std::string>()
276                         && response->getErrorCode() == errorCode
277                         && response->getResponseResult() == result;
278             }
279     ).Return(OC_STACK_OK);
280
281     ASSERT_EQ(OC_EH_OK, handler(createRequest(OC_REST_PUT)));
282 }
283
284
285
286 class ResourceObjectSynchronizationTest: public ResourceObjectHandlingRequestTest
287 {
288 public:
289
290     static void withLock(ResourceObject::Ptr serverResource, int count)
291     {
292         for (int i=0; i<count; ++i)
293         {
294             ResourceObject::LockGuard lock{ serverResource };
295
296             auto& attrs = serverResource->getAttributes();
297
298             attrs[KEY] = attrs[KEY].get<int>() + 1;
299         }
300     }
301
302     static void withSetter(ResourceObject::Ptr serverResource, int count)
303     {
304         for (int i=0; i<count; ++i)
305         {
306             ResourceObject::LockGuard lock{ serverResource };
307
308             serverResource->setAttribute(KEY, serverResource->getAttribute<int>(KEY) + 1);
309         }
310     }
311 };
312
313 TEST_F(ResourceObjectSynchronizationTest, MultipleAccessToServerResource)
314 {
315     int expected { 0 };
316     vector<thread> threads;
317
318     server->setAttribute(KEY, 0);
319
320     for (int i = 20; i >= 0; --i) {
321         int count = 5000 + i * 100;
322         threads.push_back(thread { withLock, server, count });
323         expected += count;
324     }
325
326     for (int i = 20; i >= 0; --i) {
327         int count = 5000 + i * 100;
328         threads.push_back(thread { withSetter, server, count });
329         expected +=count;
330     }
331
332     for (auto& t : threads)
333     {
334         t.join();
335     }
336
337     ASSERT_EQ(expected, server->getAttribute<int>(KEY));
338 }
339
340 TEST_F(ResourceObjectSynchronizationTest, MultipleAccessToServerResourceWithRequests)
341 {
342     int expected { 0 };
343     vector<thread> threads;
344
345     mocks.OnCallFunc(OCPlatform::sendResponse).Return(OC_STACK_OK);
346
347     server->setAttribute(KEY, 0);
348
349     for (int i = 20; i >= 0; --i) {
350         int count = 5000 + i * 100;
351         threads.push_back(thread{ withLock, server, count });
352         expected += count;
353     }
354
355     for (int i = 20; i >= 0; --i) {
356         int count = 5000 + i * 100;
357         threads.push_back(thread{ withSetter, server, count });
358         expected +=count;
359     }
360
361     threads.push_back(thread{
362         [this]()
363         {
364             for (int i=0; i<10000; ++i)
365             {
366                 if (i % 5 == 0) handler(createRequest(OC_REST_OBSERVE));
367                 handler(createRequest((i & 1) ? OC_REST_GET : OC_REST_PUT));
368             }
369         }
370     });
371
372     for (auto& t : threads)
373     {
374         t.join();
375     }
376
377     ASSERT_EQ(expected, server->getAttribute<int>(KEY));
378 }