Coverage Report

Created: 2026-05-16 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}