/src/open62541_15/tests/testing-plugins/testing_networklayers.c
Line | Count | Source |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | | |
5 | | #include "testing_networklayers.h" |
6 | | |
7 | 0 | #define TEST_CM_MAX_CONNS 16 |
8 | | |
9 | | typedef struct { |
10 | | uintptr_t connId; |
11 | | void *application; |
12 | | void *context; |
13 | | UA_ConnectionManager_connectionCallback callback; |
14 | | size_t rxCount; |
15 | | size_t txCount; |
16 | | } TestCMConnection; |
17 | | |
18 | | /* TestCM embeds UA_ConnectionManager as its first member so that a |
19 | | * UA_ConnectionManager* can be cast directly to TestCM* and back. */ |
20 | | typedef struct TestCM { |
21 | | UA_ConnectionManager cm; |
22 | | void *context; /* opaque user context, get/set via public API */ |
23 | | UA_ByteString lastSent; /* last buffer stolen by sendWithConnection */ |
24 | | UA_StatusCode (*sendWithConnectionOverload)(UA_ConnectionManager *cm, |
25 | | uintptr_t connectionId, |
26 | | const UA_KeyValueMap *params, |
27 | | UA_ByteString *buf); |
28 | | TestCMConnection conns[TEST_CM_MAX_CONNS]; |
29 | | size_t connCount; |
30 | | uintptr_t nextConnId; |
31 | | } TestCM; |
32 | | |
33 | | void |
34 | 0 | TestConnectionManager_setContext(UA_ConnectionManager *cm, void *context) { |
35 | 0 | ((TestCM *)(void *)cm)->context = context; |
36 | 0 | } |
37 | | |
38 | | void * |
39 | 0 | TestConnectionManager_getContext(UA_ConnectionManager *cm) { |
40 | 0 | return ((TestCM *)(void *)cm)->context; |
41 | 0 | } |
42 | | |
43 | | const UA_ByteString * |
44 | 0 | TestConnectionManager_getLastSent(UA_ConnectionManager *cm) { |
45 | 0 | return &((TestCM *)(void *)cm)->lastSent; |
46 | 0 | } |
47 | | |
48 | | UA_StatusCode |
49 | | TestConnectionManager_createConnection(UA_ConnectionManager *cm, |
50 | | void *application, |
51 | | void *context, |
52 | | UA_ConnectionManager_connectionCallback connectionCallback, |
53 | 0 | uintptr_t *outConnectionId) { |
54 | 0 | TestCM *tcm = (TestCM *)(void *)cm; |
55 | 0 | if(!tcm || !connectionCallback || !outConnectionId) |
56 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
57 | | |
58 | 0 | size_t slot = TEST_CM_MAX_CONNS; |
59 | 0 | for(size_t i = 0; i < TEST_CM_MAX_CONNS; i++) { |
60 | 0 | if(!tcm->conns[i].callback) { |
61 | 0 | slot = i; |
62 | 0 | break; |
63 | 0 | } |
64 | 0 | } |
65 | 0 | if(slot == TEST_CM_MAX_CONNS) |
66 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
67 | | |
68 | 0 | uintptr_t connId = tcm->nextConnId++; |
69 | 0 | tcm->conns[slot].connId = connId; |
70 | 0 | tcm->conns[slot].application = application; |
71 | 0 | tcm->conns[slot].context = context; |
72 | 0 | tcm->conns[slot].callback = connectionCallback; |
73 | 0 | tcm->connCount++; |
74 | 0 | *outConnectionId = connId; |
75 | 0 | return UA_STATUSCODE_GOOD; |
76 | 0 | } |
77 | | |
78 | | UA_StatusCode |
79 | | TestConnectionManager_removeConnection(UA_ConnectionManager *cm, |
80 | 0 | uintptr_t connectionId) { |
81 | 0 | TestCM *tcm = (TestCM *)(void *)cm; |
82 | 0 | if(!tcm) |
83 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
84 | | |
85 | 0 | for(size_t i = 0; i < TEST_CM_MAX_CONNS; i++) { |
86 | 0 | if(!tcm->conns[i].callback || tcm->conns[i].connId != connectionId) |
87 | 0 | continue; |
88 | 0 | tcm->conns[i].callback = NULL; |
89 | 0 | tcm->conns[i].context = NULL; |
90 | 0 | tcm->conns[i].application = NULL; |
91 | 0 | tcm->connCount--; |
92 | 0 | if(tcm->connCount == 0 && |
93 | 0 | tcm->cm.eventSource.state == UA_EVENTSOURCESTATE_STOPPING) |
94 | 0 | tcm->cm.eventSource.state = UA_EVENTSOURCESTATE_STOPPED; |
95 | 0 | return UA_STATUSCODE_GOOD; |
96 | 0 | } |
97 | 0 | return UA_STATUSCODE_BADNOTFOUND; |
98 | 0 | } |
99 | | |
100 | | UA_StatusCode |
101 | | TestConnectionManager_inject(UA_ConnectionManager *cm, |
102 | | uintptr_t connectionId, |
103 | | UA_ConnectionState state, |
104 | | const UA_KeyValueMap *params, |
105 | 0 | const UA_ByteString *msg) { |
106 | 0 | TestCM *tcm = (TestCM *)(void *)cm; |
107 | 0 | if(!tcm) |
108 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
109 | | |
110 | 0 | UA_KeyValueMap emptyKvm = UA_KEYVALUEMAP_NULL; |
111 | 0 | UA_ByteString emptyMsg = UA_BYTESTRING_NULL; |
112 | 0 | const UA_KeyValueMap *injectParams = params ? params : &emptyKvm; |
113 | 0 | UA_ByteString injectMsg = msg ? *msg : emptyMsg; |
114 | |
|
115 | 0 | for(size_t i = 0; i < TEST_CM_MAX_CONNS; i++) { |
116 | 0 | if(!tcm->conns[i].callback || tcm->conns[i].connId != connectionId) |
117 | 0 | continue; |
118 | | |
119 | 0 | tcm->conns[i].callback(cm, tcm->conns[i].connId, |
120 | 0 | tcm->conns[i].application, |
121 | 0 | &tcm->conns[i].context, |
122 | 0 | state, injectParams, injectMsg); |
123 | |
|
124 | 0 | if(injectMsg.length > 0) |
125 | 0 | tcm->conns[i].rxCount++; |
126 | |
|
127 | 0 | if(state == UA_CONNECTIONSTATE_CLOSING || |
128 | 0 | state == UA_CONNECTIONSTATE_CLOSED) { |
129 | 0 | tcm->conns[i].callback = NULL; |
130 | 0 | tcm->conns[i].context = NULL; |
131 | 0 | tcm->conns[i].application = NULL; |
132 | 0 | tcm->connCount--; |
133 | 0 | } |
134 | |
|
135 | 0 | return UA_STATUSCODE_GOOD; |
136 | 0 | } |
137 | | |
138 | 0 | return UA_STATUSCODE_BADNOTFOUND; |
139 | 0 | } |
140 | | |
141 | | static UA_StatusCode |
142 | 0 | testCM_eventSourceStart(UA_EventSource *es) { |
143 | 0 | es->state = UA_EVENTSOURCESTATE_STARTED; |
144 | 0 | return UA_STATUSCODE_GOOD; |
145 | 0 | } |
146 | | |
147 | | static void |
148 | 495 | testCM_eventSourceStop(UA_EventSource *es) { |
149 | 495 | UA_ConnectionManager *cm = (UA_ConnectionManager *)es; |
150 | 495 | TestCM *tcm = (TestCM *)(void *)cm; |
151 | 495 | UA_assert(tcm); |
152 | | |
153 | 495 | if(tcm->connCount == 0) { |
154 | 495 | es->state = UA_EVENTSOURCESTATE_STOPPED; |
155 | 495 | return; |
156 | 495 | } |
157 | | |
158 | 0 | es->state = UA_EVENTSOURCESTATE_STOPPING; |
159 | |
|
160 | 0 | UA_KeyValueMap emptyKvm = UA_KEYVALUEMAP_NULL; |
161 | 0 | UA_ByteString emptyMsg = UA_BYTESTRING_NULL; |
162 | 0 | for(size_t i = 0; i < TEST_CM_MAX_CONNS; i++) { |
163 | 0 | if(!tcm->conns[i].callback) |
164 | 0 | continue; |
165 | 0 | tcm->conns[i].callback(cm, tcm->conns[i].connId, |
166 | 0 | tcm->conns[i].application, |
167 | 0 | &tcm->conns[i].context, |
168 | 0 | UA_CONNECTIONSTATE_CLOSING, |
169 | 0 | &emptyKvm, emptyMsg); |
170 | | /* Remove the connection entry so connCount drops to 0 and the CM can |
171 | | * transition to STOPPED. The callback (e.g. DiscoveryManager) must NOT |
172 | | * call closeConnection in response to a CLOSING notification — that is |
173 | | * the CM's responsibility during shutdown. */ |
174 | 0 | tcm->conns[i].callback = NULL; |
175 | 0 | tcm->conns[i].context = NULL; |
176 | 0 | tcm->conns[i].application = NULL; |
177 | 0 | tcm->connCount--; |
178 | 0 | } |
179 | 0 | es->state = UA_EVENTSOURCESTATE_STOPPED; |
180 | 0 | } |
181 | | |
182 | | static UA_StatusCode |
183 | 495 | testCM_eventSourceFree(UA_EventSource *es) { |
184 | 495 | UA_ConnectionManager *cm = (UA_ConnectionManager *)es; |
185 | 495 | TestCM *tcm = (TestCM *)(void *)cm; |
186 | 495 | UA_assert(tcm); |
187 | | |
188 | 495 | testCM_eventSourceStop(es); |
189 | 495 | UA_ByteString_clear(&tcm->lastSent); |
190 | 495 | UA_free(tcm); |
191 | 495 | return UA_STATUSCODE_GOOD; |
192 | 495 | } |
193 | | |
194 | | static UA_StatusCode |
195 | | testCM_sendWithConnection(UA_ConnectionManager *cm, uintptr_t connectionId, |
196 | 0 | const UA_KeyValueMap *params, UA_ByteString *buf) { |
197 | 0 | TestCM *tcm = (TestCM *)(void *)cm; |
198 | | /* Update tx counter if the connection is tracked (opportunistic). */ |
199 | 0 | for(size_t i = 0; i < TEST_CM_MAX_CONNS; i++) { |
200 | 0 | if(tcm->conns[i].callback && tcm->conns[i].connId == connectionId) { |
201 | 0 | tcm->conns[i].txCount++; |
202 | 0 | break; |
203 | 0 | } |
204 | 0 | } |
205 | 0 | if(tcm->sendWithConnectionOverload) |
206 | 0 | return tcm->sendWithConnectionOverload(cm, connectionId, params, buf); |
207 | | /* Default: steal the buffer into lastSent */ |
208 | 0 | UA_ByteString_clear(&tcm->lastSent); |
209 | 0 | tcm->lastSent = *buf; |
210 | 0 | UA_ByteString_init(buf); |
211 | 0 | return UA_STATUSCODE_GOOD; |
212 | 0 | } |
213 | | |
214 | | static UA_StatusCode |
215 | | testCM_openConnection(UA_ConnectionManager *cm, const UA_KeyValueMap *params, |
216 | | void *application, void *context, |
217 | 0 | UA_ConnectionManager_connectionCallback cb) { |
218 | 0 | (void)params; |
219 | 0 | uintptr_t connId; |
220 | 0 | UA_StatusCode ret = TestConnectionManager_createConnection(cm, application, |
221 | 0 | context, cb, &connId); |
222 | 0 | if(ret != UA_STATUSCODE_GOOD) |
223 | 0 | return ret; |
224 | 0 | UA_KeyValueMap emptyKvm = UA_KEYVALUEMAP_NULL; |
225 | 0 | UA_ByteString emptyMsg = UA_BYTESTRING_NULL; |
226 | 0 | return TestConnectionManager_inject(cm, connId, UA_CONNECTIONSTATE_ESTABLISHED, |
227 | 0 | &emptyKvm, &emptyMsg); |
228 | 0 | } |
229 | | |
230 | | static UA_StatusCode |
231 | 0 | testCM_closeConnection(UA_ConnectionManager *cm, uintptr_t connectionId) { |
232 | 0 | UA_KeyValueMap emptyKvm = UA_KEYVALUEMAP_NULL; |
233 | 0 | UA_ByteString emptyMsg = UA_BYTESTRING_NULL; |
234 | 0 | TestConnectionManager_inject(cm, connectionId, UA_CONNECTIONSTATE_CLOSING, |
235 | 0 | &emptyKvm, &emptyMsg); |
236 | 0 | return TestConnectionManager_removeConnection(cm, connectionId); |
237 | 0 | } |
238 | | |
239 | | UA_StatusCode |
240 | | TestConnectionManager_getCounters(UA_ConnectionManager *cm, uintptr_t connectionId, |
241 | 0 | size_t *rxCount, size_t *txCount) { |
242 | 0 | TestCM *tcm = (TestCM *)(void *)cm; |
243 | 0 | for(size_t i = 0; i < TEST_CM_MAX_CONNS; i++) { |
244 | 0 | if(!tcm->conns[i].callback || tcm->conns[i].connId != connectionId) |
245 | 0 | continue; |
246 | 0 | if(rxCount) *rxCount = tcm->conns[i].rxCount; |
247 | 0 | if(txCount) *txCount = tcm->conns[i].txCount; |
248 | 0 | return UA_STATUSCODE_GOOD; |
249 | 0 | } |
250 | 0 | return UA_STATUSCODE_BADNOTFOUND; |
251 | 0 | } |
252 | | |
253 | | static UA_StatusCode |
254 | | testCM_allocNetworkBuffer(UA_ConnectionManager *cm, uintptr_t connectionId, |
255 | 0 | UA_ByteString *buf, size_t bufSize) { |
256 | 0 | (void)cm; |
257 | 0 | (void)connectionId; |
258 | 0 | return UA_ByteString_allocBuffer(buf, bufSize); |
259 | 0 | } |
260 | | |
261 | | static void |
262 | | testCM_freeNetworkBuffer(UA_ConnectionManager *cm, uintptr_t connectionId, |
263 | 0 | UA_ByteString *buf) { |
264 | 0 | (void)cm; |
265 | 0 | (void)connectionId; |
266 | 0 | UA_ByteString_clear(buf); |
267 | 0 | } |
268 | | |
269 | | UA_ConnectionManager * |
270 | | TestConnectionManager_new(const char *protocol, |
271 | 495 | const TestConnectionManager_CallbackOverloads *overloads) { |
272 | 495 | if(!protocol || !protocol[0]) |
273 | 0 | return NULL; |
274 | | |
275 | 495 | TestCM *tcm = (TestCM*)UA_calloc(1, sizeof(TestCM)); |
276 | 495 | if(!tcm) |
277 | 0 | return NULL; |
278 | | |
279 | 495 | tcm->nextConnId = 100; |
280 | | |
281 | 495 | UA_ConnectionManager *cm = &tcm->cm; |
282 | 495 | cm->eventSource.next = NULL; |
283 | 495 | cm->eventSource.eventSourceType = UA_EVENTSOURCETYPE_CONNECTIONMANAGER; |
284 | 495 | cm->eventSource.name = UA_STRING((char*)(uintptr_t)"test-cm"); |
285 | 495 | cm->eventSource.eventLoop = NULL; |
286 | 495 | cm->eventSource.params = UA_KEYVALUEMAP_NULL; |
287 | 495 | cm->eventSource.state = UA_EVENTSOURCESTATE_FRESH; |
288 | 495 | cm->eventSource.start = testCM_eventSourceStart; |
289 | 495 | cm->eventSource.stop = testCM_eventSourceStop; |
290 | 495 | cm->eventSource.free = testCM_eventSourceFree; |
291 | 495 | cm->protocol = UA_STRING((char*)(uintptr_t)protocol); |
292 | 495 | cm->openConnection = (overloads && overloads->openConnection) ? |
293 | 495 | overloads->openConnection : testCM_openConnection; |
294 | 495 | cm->sendWithConnection = testCM_sendWithConnection; |
295 | 495 | cm->closeConnection = (overloads && overloads->closeConnection) ? |
296 | 495 | overloads->closeConnection : testCM_closeConnection; |
297 | 495 | tcm->sendWithConnectionOverload = overloads ? overloads->sendWithConnection : NULL; |
298 | 495 | cm->allocNetworkBuffer = testCM_allocNetworkBuffer; |
299 | 495 | cm->freeNetworkBuffer = testCM_freeNetworkBuffer; |
300 | | |
301 | 495 | return cm; |
302 | 495 | } |