Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / lib / core / tests / TestCHIPCallback.cpp
1 /*
2  *
3  *    Copyright (c) 2020 Project CHIP Authors
4  *    All rights reserved.
5  *
6  *    Licensed under the Apache License, Version 2.0 (the "License");
7  *    you may not use this file except in compliance with the License.
8  *    You may obtain a copy of the License at
9  *
10  *        http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *    Unless required by applicable law or agreed to in writing, software
13  *    distributed under the License is distributed on an "AS IS" BASIS,
14  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *    See the License for the specific language governing permissions and
16  *    limitations under the License.
17  */
18
19 /**
20  *    @file
21  *      This file implements a test for  CHIP Callback
22  *
23  */
24 #include <core/CHIPCallback.h>
25 #include <support/CHIPMem.h>
26 #include <support/UnitTestRegistration.h>
27
28 #include <nlunit-test.h>
29
30 using namespace chip::Callback;
31
32 /**
33  * An example Callback registrar. Resumer::Resume() accepts Callbacks
34  *   to be run during the next call to Resumer::Dispatch().  In an environment
35  *   completely driven by callbacks, an application's main() would just call
36  *   something like Resumer::Dispatch() in a loop.
37  */
38 class Resumer : private CallbackDeque
39 {
40 public:
41     /**
42      * @brief run this callback next Dispatch
43      */
44     void Resume(Callback<> * cb)
45     {
46         // always first thing: cancel to take ownership of
47         //  cb members
48         Enqueue(cb->Cancel());
49     }
50
51     void Dispatch()
52     {
53         Cancelable ready;
54
55         DequeueAll(ready);
56
57         // runs the ready list
58         while (ready.mNext != &ready)
59         {
60             Callback<> * cb = Callback<>::FromCancelable(ready.mNext);
61
62             // one-shot semantics
63             cb->Cancel();
64             cb->mCall(cb->mContext);
65         }
66     }
67 };
68
69 static void increment(int * v)
70 {
71     (*v)++;
72 }
73
74 struct Resume
75 {
76     Callback<> * cb;
77     Resumer * resumer;
78 };
79
80 static void resume(struct Resume * me)
81 {
82     me->resumer->Resume(me->cb);
83 }
84
85 static void canceler(Cancelable * ca)
86 {
87     ca->Cancel();
88 }
89
90 static void ResumerTest(nlTestSuite * inSuite, void * inContext)
91 {
92     int n = 1;
93     Callback<> cb(reinterpret_cast<CallFn>(increment), &n);
94     Callback<> cancelcb(reinterpret_cast<CallFn>(canceler), cb.Cancel());
95     Resumer resumer;
96
97     // Resume() works
98     resumer.Resume(&cb);
99     resumer.Dispatch();
100     resumer.Resume(&cb);
101     resumer.Dispatch();
102     NL_TEST_ASSERT(inSuite, n == 3);
103
104     n = 1;
105     // test cb->Cancel() cancels
106     resumer.Resume(&cb);
107     cb.Cancel();
108     resumer.Dispatch();
109     NL_TEST_ASSERT(inSuite, n == 1);
110
111     n = 1;
112     // Cancel cb before Dispatch() gets around to us (tests FIFO *and* cancel() from readylist)
113     resumer.Resume(&cancelcb);
114     resumer.Resume(&cb);
115     resumer.Dispatch();
116     NL_TEST_ASSERT(inSuite, n == 1);
117
118     n = 1;
119     // 2nd Resume() cancels first registration
120     resumer.Resume(&cb);
121     resumer.Resume(&cb); // cancels previous registration
122     resumer.Dispatch();  // runs the list
123     resumer.Dispatch();  // runs an empty list
124     NL_TEST_ASSERT(inSuite, n == 2);
125
126     n = 1;
127     // Resume() during Dispatch() runs only once, but enqueues for next dispatch
128     struct Resume res = { .cb = &cb, .resumer = &resumer };
129     Callback<> resumecb(reinterpret_cast<CallFn>(resume), &res);
130     resumer.Resume(&cb);
131     resumer.Resume(&resumecb);
132     resumer.Dispatch();
133     NL_TEST_ASSERT(inSuite, n == 2);
134     resumer.Dispatch();
135     NL_TEST_ASSERT(inSuite, n == 3);
136
137     Callback<> * pcb = chip::Platform::New<Callback<>>(reinterpret_cast<CallFn>(increment), &n);
138
139     n = 1;
140     // cancel on destruct
141     resumer.Resume(pcb);
142     resumer.Dispatch();
143     NL_TEST_ASSERT(inSuite, n == 2);
144
145     resumer.Resume(pcb);
146     chip::Platform::Delete(pcb);
147     resumer.Dispatch();
148     NL_TEST_ASSERT(inSuite, n == 2);
149 }
150
151 /**
152  * An example Callback registrar. Notifier implements persistently-registered
153  *  semantics, and uses Callbacks with a non-default signature.
154  */
155 class Notifier : private CallbackDeque
156 {
157 public:
158     typedef void (*NotifyFn)(void *, int);
159
160     /**
161      * run all the callers
162      */
163     void Notify(int v)
164     {
165         for (Cancelable * ca = mNext; ca != this; ca = ca->mNext)
166         {
167             // persistent registration semantics, with data
168
169             Callback<NotifyFn> * cb = Callback<NotifyFn>::FromCancelable(ca);
170             cb->mCall(cb->mContext, v);
171         }
172     }
173
174     /**
175      * @brief example
176      */
177     static void Cancel(Cancelable * cb)
178     {
179         Dequeue(cb); // take off ready list
180     }
181
182     /**
183      * @brief illustrate a case where this needs notification of cancellation
184      */
185     void Register(Callback<NotifyFn> * cb) { Enqueue(cb->Cancel(), Cancel); }
186 };
187
188 static void increment_by(int * n, int by)
189 {
190     *n += by;
191 }
192
193 static void NotifierTest(nlTestSuite * inSuite, void * inContext)
194 {
195     int n = 1;
196     Callback<Notifier::NotifyFn> cb(reinterpret_cast<Notifier::NotifyFn>(increment_by), &n);
197     Callback<Notifier::NotifyFn> cancelcb(reinterpret_cast<Notifier::NotifyFn>(canceler), cb.Cancel());
198
199     // safe to call anytime
200     cb.Cancel();
201
202     Notifier notifier;
203
204     // Simple stuff works, e.g. and there's persistent registration
205     notifier.Register(&cb);
206     notifier.Notify(1);
207     notifier.Notify(8);
208     NL_TEST_ASSERT(inSuite, n == 10);
209
210     n = 1;
211     // Cancel cb before Dispatch() gets around to us (tests FIFO *and* cancel() from readylist)
212     notifier.Register(&cancelcb);
213     notifier.Register(&cb);
214     notifier.Notify(8);
215     NL_TEST_ASSERT(inSuite, n == 1);
216
217     cb.Cancel();
218     cancelcb.Cancel();
219 }
220
221 /**
222  *  Set up the test suite.
223  */
224 int TestCHIPCallback_Setup(void * inContext)
225 {
226     CHIP_ERROR error = chip::Platform::MemoryInit();
227     if (error != CHIP_NO_ERROR)
228         return FAILURE;
229     return SUCCESS;
230 }
231
232 /**
233  *  Tear down the test suite.
234  */
235 int TestCHIPCallback_Teardown(void * inContext)
236 {
237     chip::Platform::MemoryShutdown();
238     return SUCCESS;
239 }
240
241 /**
242  *   Test Suite. It lists all the test functions.
243  */
244
245 // clang-format off
246 static const nlTest sTests[] =
247 {
248     NL_TEST_DEF("ResumerTest", ResumerTest),
249     NL_TEST_DEF("NotifierTest", NotifierTest),
250
251     NL_TEST_SENTINEL()
252 };
253 // clang-format on
254
255 int TestCHIPCallback(void)
256 {
257     // clang-format off
258     nlTestSuite theSuite =
259         {
260         "CHIPCallback",
261         &sTests[0],
262         TestCHIPCallback_Setup,
263         TestCHIPCallback_Teardown
264     };
265     // clang-format on
266
267     nlTestRunner(&theSuite, nullptr);
268
269     return (nlTestRunnerStats(&theSuite));
270 }
271
272 CHIP_REGISTER_TEST_SUITE(TestCHIPCallback)