• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TEN-framework / ten-framework / 21243152301

22 Jan 2026 09:28AM UTC coverage: 58.796% (-0.02%) from 58.818%
21243152301

Pull #2012

github

web-flow
Merge ccbd730a0 into 09ead2af7
Pull Request #2012: feat: support graph test in python

2 of 22 new or added lines in 1 file covered. (9.09%)

13 existing lines in 2 files now uncovered.

53158 of 90411 relevant lines covered (58.8%)

833706.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

65.37
/core/src/ten_runtime/binding/python/native/test/extension_tester.c
1
//
2
// Copyright © 2025 Agora
3
// This file is part of TEN Framework, an open source project.
4
// Licensed under the Apache License, Version 2.0, with certain conditions.
5
// Refer to the "LICENSE" file in the root directory for more information.
6
//
7
#include "include_internal/ten_runtime/binding/python/test/extension_tester.h"
8

9
#include <stdbool.h>
10

11
#include "include_internal/ten_runtime/binding/python/common/common.h"
12
#include "include_internal/ten_runtime/binding/python/common/error.h"
13
#include "include_internal/ten_runtime/binding/python/msg/audio_frame.h"
14
#include "include_internal/ten_runtime/binding/python/msg/cmd/cmd.h"
15
#include "include_internal/ten_runtime/binding/python/msg/data.h"
16
#include "include_internal/ten_runtime/binding/python/msg/video_frame.h"
17
#include "include_internal/ten_runtime/binding/python/test/env_tester.h"
18
#include "include_internal/ten_runtime/msg/msg.h"
19
#include "include_internal/ten_runtime/test/extension_tester.h"
20
#include "ten_runtime/binding/common.h"
21
#include "ten_runtime/test/env_tester.h"
22
#include "ten_runtime/test/env_tester_proxy.h"
23
#include "ten_utils/macro/check.h"
24
#include "ten_utils/macro/mark.h"
25

26
static bool ten_py_extension_tester_check_integrity(
27
    ten_py_extension_tester_t *self) {
229✔
28
  TEN_ASSERT(self, "Should not happen.");
229✔
29

30
  if (ten_signature_get(&self->signature) !=
229✔
31
      (ten_signature_t)TEN_PY_EXTENSION_TESTER_SIGNATURE) {
229✔
32
    return false;
×
33
  }
×
34

35
  return true;
229✔
36
}
229✔
37

38
static ten_py_extension_tester_t *ten_py_extension_tester_create_internal(
39
    PyTypeObject *py_type) {
27✔
40
  TEN_ASSERT(py_type, "Invalid argument");
27✔
41

42
  ten_py_extension_tester_t *py_extension_tester =
27✔
43
      (ten_py_extension_tester_t *)py_type->tp_alloc(py_type, 0);
27✔
44
  TEN_ASSERT(py_extension_tester, "Failed to allocate memory.");
27✔
45

46
  ten_signature_set(&py_extension_tester->signature,
27✔
47
                    TEN_PY_EXTENSION_TESTER_SIGNATURE);
27✔
48
  py_extension_tester->c_extension_tester = NULL;
27✔
49

50
  return py_extension_tester;
27✔
51
}
27✔
52

53
static void proxy_on_init(ten_extension_tester_t *extension_tester,
54
                          ten_env_tester_t *ten_env_tester) {
27✔
55
  TEN_ASSERT(extension_tester, "Invalid argument.");
27✔
56
  TEN_ASSERT(ten_extension_tester_check_integrity(extension_tester, true),
27✔
57
             "Invalid argument.");
27✔
58

59
  TEN_ASSERT(ten_env_tester, "Invalid argument.");
27✔
60
  TEN_ASSERT(ten_env_tester_check_integrity(ten_env_tester, true),
27✔
61
             "Invalid argument.");
27✔
62

63
  // About to call the Python function, so it's necessary to ensure that the GIL
64
  // has been acquired.
65
  PyGILState_STATE prev_state = ten_py_gil_state_ensure_internal();
27✔
66

67
  ten_py_extension_tester_t *py_extension_tester =
27✔
68
      (ten_py_extension_tester_t *)ten_binding_handle_get_me_in_target_lang(
27✔
69
          (ten_binding_handle_t *)extension_tester);
27✔
70
  TEN_ASSERT(py_extension_tester, "Invalid argument.");
27✔
71
  TEN_ASSERT(ten_py_extension_tester_check_integrity(py_extension_tester),
27✔
72
             "Invalid argument.");
27✔
73

74
  ten_py_ten_env_tester_t *py_ten_env_tester =
27✔
75
      ten_py_ten_env_tester_wrap(ten_env_tester);
27✔
76
  py_extension_tester->py_ten_env_tester = (PyObject *)py_ten_env_tester;
27✔
77
  TEN_ASSERT(py_ten_env_tester->actual_py_ten_env_tester, "Should not happen.");
27✔
78

79
  py_ten_env_tester->c_ten_env_tester_proxy =
27✔
80
      ten_env_tester_proxy_create(ten_env_tester, NULL);
27✔
81
  TEN_ASSERT(py_ten_env_tester->c_ten_env_tester_proxy, "Should not happen.");
27✔
82

83
  PyObject *py_res =
27✔
84
      PyObject_CallMethod((PyObject *)py_extension_tester, "_proxy_on_init",
27✔
85
                          "O", py_ten_env_tester->actual_py_ten_env_tester);
27✔
86
  Py_XDECREF(py_res);
27✔
87

88
  bool err_occurred = ten_py_check_and_clear_py_error();
27✔
89
  TEN_ASSERT(!err_occurred, "Should not happen.");
27✔
90

91
  ten_py_gil_state_release_internal(prev_state);
27✔
92
}
27✔
93

94
static void proxy_on_start(ten_extension_tester_t *extension_tester,
95
                           ten_env_tester_t *ten_env_tester) {
24✔
96
  TEN_ASSERT(extension_tester, "Invalid argument.");
24✔
97
  TEN_ASSERT(ten_extension_tester_check_integrity(extension_tester, true),
24✔
98
             "Invalid argument.");
24✔
99

100
  TEN_ASSERT(ten_env_tester, "Invalid argument.");
24✔
101
  TEN_ASSERT(ten_env_tester_check_integrity(ten_env_tester, true),
24✔
102
             "Invalid argument.");
24✔
103

104
  // About to call the Python function, so it's necessary to ensure that the GIL
105
  // has been acquired.
106
  PyGILState_STATE prev_state = ten_py_gil_state_ensure_internal();
24✔
107

108
  ten_py_extension_tester_t *py_extension_tester =
24✔
109
      (ten_py_extension_tester_t *)ten_binding_handle_get_me_in_target_lang(
24✔
110
          (ten_binding_handle_t *)extension_tester);
24✔
111
  TEN_ASSERT(py_extension_tester, "Invalid argument.");
24✔
112
  TEN_ASSERT(ten_py_extension_tester_check_integrity(py_extension_tester),
24✔
113
             "Invalid argument.");
24✔
114

115
  ten_py_ten_env_tester_t *py_ten_env_tester =
24✔
116
      (ten_py_ten_env_tester_t *)py_extension_tester->py_ten_env_tester;
24✔
117
  TEN_ASSERT(py_ten_env_tester, "Should not happen.");
24✔
118
  TEN_ASSERT(py_ten_env_tester->actual_py_ten_env_tester, "Should not happen.");
24✔
119

120
  PyObject *py_res =
24✔
121
      PyObject_CallMethod((PyObject *)py_extension_tester, "_proxy_on_start",
24✔
122
                          "O", py_ten_env_tester->actual_py_ten_env_tester);
24✔
123
  Py_XDECREF(py_res);
24✔
124

125
  bool err_occurred = ten_py_check_and_clear_py_error();
24✔
126
  TEN_ASSERT(!err_occurred, "Should not happen.");
24✔
127

128
  ten_py_gil_state_release_internal(prev_state);
24✔
129
}
24✔
130

131
static void proxy_on_stop(ten_extension_tester_t *extension_tester,
132
                          ten_env_tester_t *ten_env_tester) {
27✔
133
  TEN_ASSERT(extension_tester, "Invalid argument.");
27✔
134
  TEN_ASSERT(ten_extension_tester_check_integrity(extension_tester, true),
27✔
135
             "Invalid argument.");
27✔
136
  TEN_ASSERT(ten_env_tester, "Invalid argument.");
27✔
137
  TEN_ASSERT(ten_env_tester_check_integrity(ten_env_tester, true),
27✔
138
             "Invalid argument.");
27✔
139

140
  ten_py_extension_tester_t *py_extension_tester =
27✔
141
      (ten_py_extension_tester_t *)ten_binding_handle_get_me_in_target_lang(
27✔
142
          (ten_binding_handle_t *)extension_tester);
27✔
143
  TEN_ASSERT(py_extension_tester, "Invalid argument.");
27✔
144
  TEN_ASSERT(ten_py_extension_tester_check_integrity(py_extension_tester),
27✔
145
             "Invalid argument.");
27✔
146

147
  ten_py_ten_env_tester_t *py_ten_env_tester =
27✔
148
      (ten_py_ten_env_tester_t *)py_extension_tester->py_ten_env_tester;
27✔
149
  TEN_ASSERT(py_ten_env_tester, "Should not happen.");
27✔
150
  TEN_ASSERT(py_ten_env_tester->actual_py_ten_env_tester, "Should not happen.");
27✔
151

152
  // About to call the Python function, so it's necessary to ensure that the GIL
153
  // has been acquired.
154
  PyGILState_STATE prev_state = ten_py_gil_state_ensure_internal();
27✔
155

156
  PyObject *py_res =
27✔
157
      PyObject_CallMethod((PyObject *)py_extension_tester, "_proxy_on_stop",
27✔
158
                          "O", py_ten_env_tester->actual_py_ten_env_tester);
27✔
159
  Py_XDECREF(py_res);
27✔
160

161
  bool err_occurred = ten_py_check_and_clear_py_error();
27✔
162
  TEN_ASSERT(!err_occurred, "Should not happen.");
27✔
163

164
  ten_py_gil_state_release_internal(prev_state);
27✔
165
}
27✔
166

167
static void proxy_on_deinit(ten_extension_tester_t *extension_tester,
168
                            ten_env_tester_t *ten_env_tester) {
27✔
169
  TEN_ASSERT(extension_tester, "Invalid argument.");
27✔
170
  TEN_ASSERT(ten_extension_tester_check_integrity(extension_tester, true),
27✔
171
             "Invalid argument.");
27✔
172
  TEN_ASSERT(ten_env_tester, "Invalid argument.");
27✔
173
  TEN_ASSERT(ten_env_tester_check_integrity(ten_env_tester, true),
27✔
174
             "Invalid argument.");
27✔
175

176
  ten_py_extension_tester_t *py_extension_tester =
27✔
177
      (ten_py_extension_tester_t *)ten_binding_handle_get_me_in_target_lang(
27✔
178
          (ten_binding_handle_t *)extension_tester);
27✔
179
  TEN_ASSERT(py_extension_tester, "Invalid argument.");
27✔
180
  TEN_ASSERT(ten_py_extension_tester_check_integrity(py_extension_tester),
27✔
181
             "Invalid argument.");
27✔
182

183
  ten_py_ten_env_tester_t *py_ten_env_tester =
27✔
184
      (ten_py_ten_env_tester_t *)py_extension_tester->py_ten_env_tester;
27✔
185
  TEN_ASSERT(py_ten_env_tester, "Should not happen.");
27✔
186
  TEN_ASSERT(py_ten_env_tester->actual_py_ten_env_tester, "Should not happen.");
27✔
187

188
  // About to call the Python function, so it's necessary to ensure that the GIL
189
  // has been acquired.
190
  PyGILState_STATE prev_state = ten_py_gil_state_ensure_internal();
27✔
191

192
  PyObject *py_res =
27✔
193
      PyObject_CallMethod((PyObject *)py_extension_tester, "_proxy_on_deinit",
27✔
194
                          "O", py_ten_env_tester->actual_py_ten_env_tester);
27✔
195
  Py_XDECREF(py_res);
27✔
196

197
  bool err_occurred = ten_py_check_and_clear_py_error();
27✔
198
  TEN_ASSERT(!err_occurred, "Should not happen.");
27✔
199

200
  ten_py_gil_state_release_internal(prev_state);
27✔
201

202
  // Do not release `py_ten_env_tester->c_ten_env_tester_proxy` here, because
203
  // the upper layer may still need to call the API of `ten_env` (e.g., some
204
  // asynchronous operations) before `on_deinit_done`. Therefore, the proxy
205
  // should only be released after `on_deinit_done`.
206
  //
207
  // This practice of releasing `ten_env_proxy` at the very end (i.e., after the
208
  // `on_deinit_done` of the extension and the `on_stop_done` of the app) is a
209
  // common feature across all language bindings of TEN.
210
}
27✔
211

212
static void proxy_on_cmd(ten_extension_tester_t *extension_tester,
213
                         ten_env_tester_t *ten_env_tester,
214
                         ten_shared_ptr_t *cmd) {
13✔
215
  TEN_ASSERT(extension_tester, "Invalid argument.");
13✔
216
  TEN_ASSERT(ten_extension_tester_check_integrity(extension_tester, true),
13✔
217
             "Invalid argument.");
13✔
218
  TEN_ASSERT(ten_env_tester, "Invalid argument.");
13✔
219
  TEN_ASSERT(ten_env_tester_check_integrity(ten_env_tester, true),
13✔
220
             "Invalid argument.");
13✔
221
  TEN_ASSERT(cmd, "Invalid argument.");
13✔
222
  TEN_ASSERT(ten_msg_check_integrity(cmd), "Invalid argument.");
13✔
223

224
  // About to call the Python function, so it's necessary to ensure that the GIL
225
  // has been acquired.
226
  PyGILState_STATE prev_state = ten_py_gil_state_ensure_internal();
13✔
227

228
  ten_py_extension_tester_t *py_extension_tester =
13✔
229
      (ten_py_extension_tester_t *)ten_binding_handle_get_me_in_target_lang(
13✔
230
          (ten_binding_handle_t *)extension_tester);
13✔
231
  TEN_ASSERT(py_extension_tester, "Invalid argument.");
13✔
232
  TEN_ASSERT(ten_py_extension_tester_check_integrity(py_extension_tester),
13✔
233
             "Invalid argument.");
13✔
234

235
  PyObject *py_ten_env_tester = py_extension_tester->py_ten_env_tester;
13✔
236
  TEN_ASSERT(py_ten_env_tester, "Should not happen.");
13✔
237
  TEN_ASSERT(
13✔
238
      ((ten_py_ten_env_tester_t *)py_ten_env_tester)->actual_py_ten_env_tester,
13✔
239
      "Should not happen.");
13✔
240

241
  ten_py_cmd_t *py_cmd = ten_py_cmd_wrap(cmd);
13✔
242

243
  PyObject *py_res = PyObject_CallMethod(
13✔
244
      (PyObject *)py_extension_tester, "_proxy_on_cmd", "OO",
13✔
245
      ((ten_py_ten_env_tester_t *)py_ten_env_tester)->actual_py_ten_env_tester,
13✔
246
      py_cmd);
13✔
247
  Py_XDECREF(py_res);
13✔
248

249
  bool err_occurred = ten_py_check_and_clear_py_error();
13✔
250
  TEN_ASSERT(!err_occurred, "Should not happen.");
13✔
251

252
  ten_py_cmd_invalidate(py_cmd);
13✔
253

254
  ten_py_gil_state_release_internal(prev_state);
13✔
255
}
13✔
256

257
static void proxy_on_data(ten_extension_tester_t *extension_tester,
258
                          ten_env_tester_t *ten_env_tester,
259
                          ten_shared_ptr_t *data) {
×
260
  TEN_ASSERT(extension_tester, "Invalid argument.");
×
261
  TEN_ASSERT(ten_extension_tester_check_integrity(extension_tester, true),
×
262
             "Invalid argument.");
×
263
  TEN_ASSERT(ten_env_tester, "Invalid argument.");
×
264
  TEN_ASSERT(ten_env_tester_check_integrity(ten_env_tester, true),
×
265
             "Invalid argument.");
×
266
  TEN_ASSERT(data, "Invalid argument.");
×
267
  TEN_ASSERT(ten_msg_check_integrity(data), "Invalid argument.");
×
268

269
  // About to call the Python function, so it's necessary to ensure that the GIL
270
  // has been acquired.
271
  PyGILState_STATE prev_state = ten_py_gil_state_ensure_internal();
×
272

273
  ten_py_extension_tester_t *py_extension_tester =
×
274
      (ten_py_extension_tester_t *)ten_binding_handle_get_me_in_target_lang(
×
275
          (ten_binding_handle_t *)extension_tester);
×
276
  TEN_ASSERT(py_extension_tester, "Invalid argument.");
×
277
  TEN_ASSERT(ten_py_extension_tester_check_integrity(py_extension_tester),
×
278
             "Invalid argument.");
×
279

280
  PyObject *py_ten_env_tester = py_extension_tester->py_ten_env_tester;
×
281
  TEN_ASSERT(py_ten_env_tester, "Should not happen.");
×
282
  TEN_ASSERT(
×
283
      ((ten_py_ten_env_tester_t *)py_ten_env_tester)->actual_py_ten_env_tester,
×
284
      "Should not happen.");
×
285

286
  ten_py_data_t *py_data = ten_py_data_wrap(data);
×
287

288
  PyObject *py_res = PyObject_CallMethod(
×
289
      (PyObject *)py_extension_tester, "_proxy_on_data", "OO",
×
290
      ((ten_py_ten_env_tester_t *)py_ten_env_tester)->actual_py_ten_env_tester,
×
291
      py_data);
×
292
  Py_XDECREF(py_res);
×
293

294
  bool err_occurred = ten_py_check_and_clear_py_error();
×
295
  TEN_ASSERT(!err_occurred, "Should not happen.");
×
296

297
  ten_py_data_invalidate(py_data);
×
298

299
  ten_py_gil_state_release_internal(prev_state);
×
300
}
×
301

302
static void proxy_on_audio_frame(ten_extension_tester_t *extension_tester,
303
                                 ten_env_tester_t *ten_env_tester,
304
                                 ten_shared_ptr_t *audio_frame) {
×
305
  TEN_ASSERT(extension_tester, "Invalid argument.");
×
306
  TEN_ASSERT(ten_extension_tester_check_integrity(extension_tester, true),
×
307
             "Invalid argument.");
×
308
  TEN_ASSERT(ten_env_tester, "Invalid argument.");
×
309
  TEN_ASSERT(ten_env_tester_check_integrity(ten_env_tester, true),
×
310
             "Invalid argument.");
×
311
  TEN_ASSERT(audio_frame, "Invalid argument.");
×
312
  TEN_ASSERT(ten_msg_check_integrity(audio_frame), "Invalid argument.");
×
313

314
  // About to call the Python function, so it's necessary to ensure that the GIL
315
  // has been acquired.
316
  PyGILState_STATE prev_state = ten_py_gil_state_ensure_internal();
×
317

318
  ten_py_extension_tester_t *py_extension_tester =
×
319
      (ten_py_extension_tester_t *)ten_binding_handle_get_me_in_target_lang(
×
320
          (ten_binding_handle_t *)extension_tester);
×
321
  TEN_ASSERT(py_extension_tester, "Invalid argument.");
×
322
  TEN_ASSERT(ten_py_extension_tester_check_integrity(py_extension_tester),
×
323
             "Invalid argument.");
×
324

325
  PyObject *py_ten_env_tester = py_extension_tester->py_ten_env_tester;
×
326
  TEN_ASSERT(py_ten_env_tester, "Should not happen.");
×
327
  TEN_ASSERT(
×
328
      ((ten_py_ten_env_tester_t *)py_ten_env_tester)->actual_py_ten_env_tester,
×
329
      "Should not happen.");
×
330

331
  ten_py_audio_frame_t *py_audio_frame = ten_py_audio_frame_wrap(audio_frame);
×
332

333
  PyObject *py_res = PyObject_CallMethod(
×
334
      (PyObject *)py_extension_tester, "_proxy_on_audio_frame", "OO",
×
335
      ((ten_py_ten_env_tester_t *)py_ten_env_tester)->actual_py_ten_env_tester,
×
336
      py_audio_frame);
×
337
  Py_XDECREF(py_res);
×
338

339
  bool err_occurred = ten_py_check_and_clear_py_error();
×
340
  TEN_ASSERT(!err_occurred, "Should not happen.");
×
341

342
  ten_py_audio_frame_invalidate(py_audio_frame);
×
343

344
  ten_py_gil_state_release_internal(prev_state);
×
345
}
×
346

347
static void proxy_on_video_frame(ten_extension_tester_t *extension_tester,
348
                                 ten_env_tester_t *ten_env_tester,
349
                                 ten_shared_ptr_t *video_frame) {
×
350
  TEN_ASSERT(extension_tester, "Invalid argument.");
×
351
  TEN_ASSERT(ten_extension_tester_check_integrity(extension_tester, true),
×
352
             "Invalid argument.");
×
353
  TEN_ASSERT(ten_env_tester, "Invalid argument.");
×
354
  TEN_ASSERT(ten_env_tester_check_integrity(ten_env_tester, true),
×
355
             "Invalid argument.");
×
356
  TEN_ASSERT(video_frame, "Invalid argument.");
×
357
  TEN_ASSERT(ten_msg_check_integrity(video_frame), "Invalid argument.");
×
358

359
  // About to call the Python function, so it's necessary to ensure that the GIL
360
  // has been acquired.
361
  PyGILState_STATE prev_state = ten_py_gil_state_ensure_internal();
×
362

363
  ten_py_extension_tester_t *py_extension_tester =
×
364
      (ten_py_extension_tester_t *)ten_binding_handle_get_me_in_target_lang(
×
365
          (ten_binding_handle_t *)extension_tester);
×
366
  TEN_ASSERT(py_extension_tester, "Invalid argument.");
×
367
  TEN_ASSERT(ten_py_extension_tester_check_integrity(py_extension_tester),
×
368
             "Invalid argument.");
×
369

370
  PyObject *py_ten_env_tester = py_extension_tester->py_ten_env_tester;
×
371
  TEN_ASSERT(py_ten_env_tester, "Should not happen.");
×
372
  TEN_ASSERT(
×
373
      ((ten_py_ten_env_tester_t *)py_ten_env_tester)->actual_py_ten_env_tester,
×
374
      "Should not happen.");
×
375

376
  ten_py_video_frame_t *py_video_frame = ten_py_video_frame_wrap(video_frame);
×
377

378
  PyObject *py_res = PyObject_CallMethod(
×
379
      (PyObject *)py_extension_tester, "_proxy_on_video_frame", "OO",
×
380
      ((ten_py_ten_env_tester_t *)py_ten_env_tester)->actual_py_ten_env_tester,
×
381
      py_video_frame);
×
382
  Py_XDECREF(py_res);
×
383

384
  bool err_occurred = ten_py_check_and_clear_py_error();
×
385
  TEN_ASSERT(!err_occurred, "Should not happen.");
×
386

387
  ten_py_video_frame_invalidate(py_video_frame);
×
388

389
  ten_py_gil_state_release_internal(prev_state);
×
390
}
×
391

392
static ten_py_extension_tester_t *ten_py_extension_tester_init(
393
    ten_py_extension_tester_t *py_extension_tester, TEN_UNUSED PyObject *args,
394
    TEN_UNUSED PyObject *kw) {
27✔
395
  TEN_ASSERT(py_extension_tester, "Invalid argument.");
27✔
396
  TEN_ASSERT(ten_py_extension_tester_check_integrity(
27✔
397
                 (ten_py_extension_tester_t *)py_extension_tester),
27✔
398
             "Invalid argument.");
27✔
399

400
  py_extension_tester->c_extension_tester = ten_extension_tester_create(
27✔
401
      proxy_on_init, proxy_on_start, proxy_on_stop, proxy_on_deinit,
27✔
402
      proxy_on_cmd, proxy_on_data, proxy_on_audio_frame, proxy_on_video_frame);
27✔
403

404
  ten_binding_handle_set_me_in_target_lang(
27✔
405
      &py_extension_tester->c_extension_tester->binding_handle,
27✔
406
      py_extension_tester);
27✔
407
  py_extension_tester->py_ten_env_tester = Py_None;
27✔
408

409
  return py_extension_tester;
27✔
410
}
27✔
411

412
PyObject *ten_py_extension_tester_create(PyTypeObject *type,
413
                                         TEN_UNUSED PyObject *args,
414
                                         TEN_UNUSED PyObject *kwds) {
27✔
415
  ten_py_extension_tester_t *py_extension_tester =
27✔
416
      ten_py_extension_tester_create_internal(type);
27✔
417
  return (PyObject *)ten_py_extension_tester_init(py_extension_tester, args,
27✔
418
                                                  kwds);
27✔
419
}
27✔
420

421
void ten_py_extension_tester_destroy(PyObject *self) {
27✔
422
  ten_py_extension_tester_t *py_extension_tester =
27✔
423
      (ten_py_extension_tester_t *)self;
27✔
424
  TEN_ASSERT(py_extension_tester, "Invalid argument.");
27✔
425
  TEN_ASSERT(ten_py_extension_tester_check_integrity(
27✔
426
                 (ten_py_extension_tester_t *)py_extension_tester),
27✔
427
             "Invalid argument.");
27✔
428

429
  ten_extension_tester_destroy(py_extension_tester->c_extension_tester);
27✔
430
  Py_TYPE(self)->tp_free(self);
27✔
431
}
27✔
432

433
static PyObject *ten_py_extension_tester_set_test_mode_single(PyObject *self,
434
                                                              PyObject *args) {
27✔
435
  ten_py_extension_tester_t *py_extension_tester =
27✔
436
      (ten_py_extension_tester_t *)self;
27✔
437
  TEN_ASSERT(py_extension_tester, "Invalid argument.");
27✔
438
  TEN_ASSERT(ten_py_extension_tester_check_integrity(py_extension_tester),
27✔
439
             "Invalid argument.");
27✔
440

441
  if (PyTuple_GET_SIZE(args) != 2) {
54✔
442
    return ten_py_raise_py_value_error_exception(
×
443
        "Invalid argument count when extension_tester.set_test_mode_single.");
×
444
  }
×
445

446
  const char *addon_name = NULL;
27✔
447
  const char *property_json_str = NULL;
27✔
448
  if (!PyArg_ParseTuple(args, "sz", &addon_name, &property_json_str)) {
27✔
449
    return ten_py_raise_py_value_error_exception(
×
450
        "Failed to parse arguments when "
×
451
        "extension_tester.set_test_mode_single.");
×
452
  }
×
453

454
  ten_extension_tester_set_test_mode_single(
27✔
455
      py_extension_tester->c_extension_tester, addon_name, property_json_str);
27✔
456

457
  Py_RETURN_NONE;
27✔
458
}
27✔
459

460
static PyObject *ten_py_extension_tester_set_test_mode_graph(PyObject *self,
NEW
461
                                                             PyObject *args) {
×
NEW
462
  ten_py_extension_tester_t *py_extension_tester =
×
NEW
463
      (ten_py_extension_tester_t *)self;
×
NEW
464
  TEN_ASSERT(py_extension_tester, "Invalid argument.");
×
NEW
465
  TEN_ASSERT(ten_py_extension_tester_check_integrity(py_extension_tester),
×
NEW
466
             "Invalid argument.");
×
467

NEW
468
  if (PyTuple_GET_SIZE(args) != 1) {
×
NEW
469
    return ten_py_raise_py_value_error_exception(
×
NEW
470
        "Invalid argument count when extension_tester.set_test_mode_graph.");
×
NEW
471
  }
×
472

NEW
473
  const char *graph_json = NULL;
×
NEW
474
  if (!PyArg_ParseTuple(args, "s", &graph_json)) {
×
NEW
475
    return ten_py_raise_py_value_error_exception(
×
NEW
476
        "Failed to parse arguments when "
×
NEW
477
        "extension_tester.set_test_mode_graph.");
×
NEW
478
  }
×
479

NEW
480
  ten_extension_tester_set_test_mode_graph(
×
NEW
481
      py_extension_tester->c_extension_tester, graph_json);
×
482

NEW
483
  Py_RETURN_NONE;
×
NEW
484
}
×
485

486
static PyObject *ten_py_extension_tester_set_timeout(PyObject *self,
487
                                                     PyObject *args) {
3✔
488
  ten_py_extension_tester_t *py_extension_tester =
3✔
489
      (ten_py_extension_tester_t *)self;
3✔
490
  TEN_ASSERT(py_extension_tester, "Invalid argument.");
3✔
491
  TEN_ASSERT(ten_py_extension_tester_check_integrity(py_extension_tester),
3✔
492
             "Invalid argument.");
3✔
493

494
  if (PyTuple_GET_SIZE(args) != 1) {
6✔
495
    return ten_py_raise_py_value_error_exception(
×
496
        "Invalid argument count when extension_tester.set_timeout.");
×
497
  }
×
498

499
  uint64_t timeout_us = 0;
3✔
500
  if (!PyArg_ParseTuple(args, "K", &timeout_us)) {
3✔
501
    return ten_py_raise_py_value_error_exception(
×
502
        "Failed to parse arguments when "
×
503
        "extension_tester.set_timeout.");
×
504
  }
×
505

506
  ten_extension_tester_set_timeout(py_extension_tester->c_extension_tester,
3✔
507
                                   timeout_us);
3✔
508

509
  Py_RETURN_NONE;
3✔
510
}
3✔
511

512
static PyObject *ten_py_extension_tester_run(PyObject *self,
513
                                             TEN_UNUSED PyObject *args) {
27✔
514
  ten_py_extension_tester_t *py_extension_tester =
27✔
515
      (ten_py_extension_tester_t *)self;
27✔
516

517
  TEN_ASSERT(py_extension_tester, "Invalid argument.");
27✔
518
  TEN_ASSERT(ten_py_extension_tester_check_integrity(py_extension_tester),
27✔
519
             "Invalid argument.");
27✔
520

521
  TEN_LOGI("ten_py_extension_tester_run");
27✔
522

523
  PyThreadState *saved_py_thread_state = PyEval_SaveThread();
27✔
524

525
  ten_error_t err;
27✔
526
  TEN_ERROR_INIT(err);
27✔
527

528
  ten_py_error_t *py_error = NULL;
27✔
529

530
  // This ia a blocking operation.
531
  bool rc =
27✔
532
      ten_extension_tester_run(py_extension_tester->c_extension_tester, &err);
27✔
533
  if (!rc) {
27✔
534
    py_error = ten_py_error_wrap(&err);
6✔
535
  }
6✔
536

537
  PyEval_RestoreThread(saved_py_thread_state);
27✔
538

539
  TEN_LOGI("ten_py_extension_tester_run done: %d", rc);
27✔
540

541
  ten_error_deinit(&err);
27✔
542

543
  if (py_error) {
27✔
544
    return (PyObject *)py_error;
6✔
545
  }
6✔
546

547
  Py_RETURN_NONE;
27✔
548
}
27✔
549

550
static PyTypeObject *ten_py_extension_tester_py_type(void) {
5✔
551
  static PyMethodDef py_methods[] = {
5✔
552
      {"set_test_mode_single_internal",
5✔
553
       ten_py_extension_tester_set_test_mode_single, METH_VARARGS, NULL},
5✔
554
      {"set_test_mode_graph_internal",
5✔
555
       ten_py_extension_tester_set_test_mode_graph, METH_VARARGS, NULL},
5✔
556
      {"set_timeout", ten_py_extension_tester_set_timeout, METH_VARARGS, NULL},
5✔
557
      {"run_internal", ten_py_extension_tester_run, METH_VARARGS, NULL},
5✔
558
      {NULL, NULL, 0, NULL},
5✔
559
  };
5✔
560

561
  static PyTypeObject py_type = {
5✔
562
      PyVarObject_HEAD_INIT(NULL, 0).tp_name =
5✔
563
          "libten_runtime_python._ExtensionTester",
5✔
564
      .tp_doc = PyDoc_STR("_ExtensionTester"),
5✔
565
      .tp_basicsize = sizeof(ten_py_extension_tester_t),
5✔
566
      .tp_itemsize = 0,
5✔
567
      .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
5✔
568
      .tp_new = ten_py_extension_tester_create,
5✔
569
      .tp_init = NULL,
5✔
570
      .tp_dealloc = ten_py_extension_tester_destroy,
5✔
571
      .tp_getset = NULL,
5✔
572
      .tp_methods = py_methods,
5✔
573
  };
5✔
574

575
  return &py_type;
5✔
576
}
5✔
577

578
bool ten_py_extension_tester_init_for_module(PyObject *module) {
5✔
579
  PyTypeObject *py_type = ten_py_extension_tester_py_type();
5✔
580
  if (PyType_Ready(py_type) < 0) {
5✔
581
    ten_py_raise_py_system_error_exception(
×
582
        "Python ExtensionTester class is not ready.");
×
583

584
    TEN_ASSERT(0, "Should not happen.");
×
585
    return false;
×
586
  }
×
587

588
  if (PyModule_AddObjectRef(module, "_ExtensionTester", (PyObject *)py_type) <
5✔
589
      0) {
5✔
590
    ten_py_raise_py_import_error_exception(
×
591
        "Failed to add Python type to module.");
×
592

593
    TEN_ASSERT(0, "Should not happen.");
×
594
    return false;
×
595
  }
×
596
  return true;
5✔
597
}
5✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc