From cf75ae33a8ef71c9e9dc77e1eb19eb6f56e0817f Mon Sep 17 00:00:00 2001 From: xxxxl_sun <31622273+sunxilin@users.noreply.github.com> Date: Mon, 13 Jan 2025 20:30:43 +0800 Subject: [PATCH] feat: add log api in Python tester (#553) Co-authored-by: Hu Yueh-Wei --- .vscode/launch.json | 8 +- .../binding/cpp/detail/ten_env_proxy.h | 40 +-- .../cpp/detail/test/env_tester_proxy.h | 28 +- core/include/ten_runtime/test/env_tester.h | 7 + .../binding/python/test/env_tester.h | 3 + .../ten_runtime/test/extension_tester.h | 3 + .../binding/python/interface/ten/__init__.py | 5 +- .../python/interface/ten/async_test.py | 270 ++++++++++++++++++ .../interface/ten/libten_runtime_python.pyi | 8 + .../binding/python/interface/ten/test.py | 9 +- .../binding/python/interface/ten/test_base.py | 65 +++++ .../native/test/env_tester/env_tester.c | 1 + .../native/test/env_tester/env_tester_log.c | 104 +++++++ .../python/native/test/extension_tester.c | 28 +- core/src/ten_runtime/test/env_tester.c | 91 +++++- core/src/ten_runtime/test/env_tester_proxy.c | 16 +- core/src/ten_runtime/test/extension_tester.c | 12 +- .../tests/test_mock.py | 4 +- .../tests/test_async_basic.py | 51 ++++ .../tests/test_async_outer_thread.py | 53 ++++ .../tests/test_basic.py | 6 +- .../tests/test_outer_thread.py | 4 +- .../tests/test_set_property.py | 4 +- 23 files changed, 754 insertions(+), 66 deletions(-) create mode 100644 core/src/ten_runtime/binding/python/interface/ten/async_test.py create mode 100644 core/src/ten_runtime/binding/python/interface/ten/test_base.py create mode 100644 core/src/ten_runtime/binding/python/native/test/env_tester/env_tester_log.c create mode 100644 tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_async_basic.py create mode 100644 tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_async_outer_thread.py diff --git a/.vscode/launch.json b/.vscode/launch.json index a350214b0f..cd5d6775d1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -645,7 +645,7 @@ "environment": [ { "name": "PYTHONPATH", - "value": "${workspaceFolder}/out/linux/x64/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/.ten/app/ten_packages/system/ten_runtime_python/lib:${workspaceFolder}/out/linux/x64/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/.ten/app/ten_packages/system/ten_runtime_python/interface" + "value": "${workspaceFolder}/out/linux/x64/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/.ten/app/ten_packages/system/ten_runtime_python/lib:${workspaceFolder}/out/linux/x64/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/.ten/app/ten_packages/system/ten_runtime_python/interface:${workspaceFolder}/out/linux/x64/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/.ten/app" }, { "name": "LD_PRELOAD", @@ -840,7 +840,11 @@ "name": "Python Debugger: Python File", "type": "debugpy", "request": "launch", - "program": "${file}" + "program": "${workspaceFolder}/out/linux/x64/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_async_outer_thread.py", + "env": { + "PYTHONPATH": "${workspaceFolder}/out/linux/x64/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/.ten/app/ten_packages/system/ten_runtime_python/lib:${workspaceFolder}/out/linux/x64/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/.ten/app/ten_packages/system/ten_runtime_python/interface:${workspaceFolder}/out/linux/x64/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/.ten/app", + "LD_PRELOAD": "${workspaceFolder}/out/linux/x64/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/.ten/app/ten_packages/system/ten_runtime/lib/libasan.so" + } } ], "compounds": [ diff --git a/core/include/ten_runtime/binding/cpp/detail/ten_env_proxy.h b/core/include/ten_runtime/binding/cpp/detail/ten_env_proxy.h index 4e3a7dd4b5..ad0ee58d08 100644 --- a/core/include/ten_runtime/binding/cpp/detail/ten_env_proxy.h +++ b/core/include/ten_runtime/binding/cpp/detail/ten_env_proxy.h @@ -25,20 +25,20 @@ using notify_std_func_t = std::function; using notify_std_with_user_data_func_t = std::function; -struct proxy_notify_info_t { - explicit proxy_notify_info_t(notify_std_func_t &&func) +struct proxy_notify_ctx_t { + explicit proxy_notify_ctx_t(notify_std_func_t &&func) : notify_std_func(std::move(func)) {} - explicit proxy_notify_info_t(notify_std_with_user_data_func_t &&func, - void *user_data) + explicit proxy_notify_ctx_t(notify_std_with_user_data_func_t &&func, + void *user_data) : notify_std_with_user_data_func(std::move(func)), user_data(user_data) {} - ~proxy_notify_info_t() = default; + ~proxy_notify_ctx_t() = default; // @{ - proxy_notify_info_t(const proxy_notify_info_t &) = delete; - proxy_notify_info_t &operator=(const proxy_notify_info_t &) = delete; - proxy_notify_info_t(proxy_notify_info_t &&) = delete; - proxy_notify_info_t &operator=(proxy_notify_info_t &&) = delete; + proxy_notify_ctx_t(const proxy_notify_ctx_t &) = delete; + proxy_notify_ctx_t &operator=(const proxy_notify_ctx_t &) = delete; + proxy_notify_ctx_t(proxy_notify_ctx_t &&) = delete; + proxy_notify_ctx_t &operator=(proxy_notify_ctx_t &&) = delete; // @} notify_std_func_t notify_std_func; @@ -50,20 +50,20 @@ struct proxy_notify_info_t { inline void proxy_notify(::ten_env_t *ten_env, void *data = nullptr) { TEN_ASSERT(data, "Invalid argument."); - auto *info = static_cast(data); + auto *ctx = static_cast(data); auto *cpp_ten_env = static_cast(ten_binding_handle_get_me_in_target_lang( reinterpret_cast(ten_env))); - if (info->notify_std_func != nullptr) { - auto func = info->notify_std_func; + if (ctx->notify_std_func != nullptr) { + auto func = ctx->notify_std_func; func(*cpp_ten_env); - } else if (info->notify_std_with_user_data_func != nullptr) { - auto func = info->notify_std_with_user_data_func; - func(*cpp_ten_env, info->user_data); + } else if (ctx->notify_std_with_user_data_func != nullptr) { + auto func = ctx->notify_std_with_user_data_func; + func(*cpp_ten_env, ctx->user_data); } - delete info; + delete ctx; } } // namespace @@ -124,13 +124,13 @@ class ten_env_proxy_t { bool notify(notify_std_func_t &¬ify_func, bool sync = false, error_t *err = nullptr) { - auto *info = new proxy_notify_info_t(std::move(notify_func)); + auto *ctx = new proxy_notify_ctx_t(std::move(notify_func)); auto rc = - ten_env_proxy_notify(c_ten_env_proxy, proxy_notify, info, sync, + ten_env_proxy_notify(c_ten_env_proxy, proxy_notify, ctx, sync, err != nullptr ? err->get_c_error() : nullptr); if (!rc) { - delete info; + delete ctx; } return rc; @@ -138,7 +138,7 @@ class ten_env_proxy_t { bool notify(notify_std_with_user_data_func_t &¬ify_func, void *user_data, bool sync = false, error_t *err = nullptr) { - auto *info = new proxy_notify_info_t(std::move(notify_func), user_data); + auto *info = new proxy_notify_ctx_t(std::move(notify_func), user_data); auto rc = ten_env_proxy_notify(c_ten_env_proxy, proxy_notify, info, sync, diff --git a/core/include/ten_runtime/binding/cpp/detail/test/env_tester_proxy.h b/core/include/ten_runtime/binding/cpp/detail/test/env_tester_proxy.h index b2e797c10b..9d3fd232aa 100644 --- a/core/include/ten_runtime/binding/cpp/detail/test/env_tester_proxy.h +++ b/core/include/ten_runtime/binding/cpp/detail/test/env_tester_proxy.h @@ -21,18 +21,18 @@ namespace { using tester_notify_std_func_t = std::function; -struct tester_proxy_notify_info_t { - explicit tester_proxy_notify_info_t(tester_notify_std_func_t &&func) +struct tester_proxy_notify_ctx_t { + explicit tester_proxy_notify_ctx_t(tester_notify_std_func_t &&func) : notify_std_func(std::move(func)) {} - ~tester_proxy_notify_info_t() = default; + ~tester_proxy_notify_ctx_t() = default; // @{ - tester_proxy_notify_info_t(const tester_proxy_notify_info_t &) = delete; - tester_proxy_notify_info_t &operator=(const tester_proxy_notify_info_t &) = + tester_proxy_notify_ctx_t(const tester_proxy_notify_ctx_t &) = delete; + tester_proxy_notify_ctx_t &operator=(const tester_proxy_notify_ctx_t &) = delete; - tester_proxy_notify_info_t(tester_proxy_notify_info_t &&) = delete; - tester_proxy_notify_info_t &operator=(tester_proxy_notify_info_t &&) = delete; + tester_proxy_notify_ctx_t(tester_proxy_notify_ctx_t &&) = delete; + tester_proxy_notify_ctx_t &operator=(tester_proxy_notify_ctx_t &&) = delete; // @} tester_notify_std_func_t notify_std_func; @@ -41,17 +41,17 @@ struct tester_proxy_notify_info_t { inline void proxy_notify(::ten_env_tester_t *ten_env, void *data = nullptr) { TEN_ASSERT(data, "Invalid argument."); - auto *info = static_cast(data); + auto *ctx = static_cast(data); auto *cpp_ten_env = static_cast(ten_binding_handle_get_me_in_target_lang( reinterpret_cast(ten_env))); - if (info->notify_std_func != nullptr) { - auto func = info->notify_std_func; + if (ctx->notify_std_func != nullptr) { + auto func = ctx->notify_std_func; func(*cpp_ten_env); } - delete info; + delete ctx; } } // namespace @@ -101,13 +101,13 @@ class ten_env_tester_proxy_t { return false; } - auto *info = new tester_proxy_notify_info_t(std::move(notify_func)); + auto *ctx = new tester_proxy_notify_ctx_t(std::move(notify_func)); auto rc = ten_env_tester_proxy_notify( - c_ten_env_tester_proxy, proxy_notify, info, + c_ten_env_tester_proxy, proxy_notify, ctx, err != nullptr ? err->get_c_error() : nullptr); if (!rc) { - delete info; + delete ctx; } return rc; diff --git a/core/include/ten_runtime/test/env_tester.h b/core/include/ten_runtime/test/env_tester.h index 1fa9d31fd2..66ac6598e0 100644 --- a/core/include/ten_runtime/test/env_tester.h +++ b/core/include/ten_runtime/test/env_tester.h @@ -10,6 +10,7 @@ #include "ten_utils/lib/error.h" #include "ten_utils/lib/smart_ptr.h" +#include "ten_utils/log/log.h" typedef struct ten_env_tester_t ten_env_tester_t; @@ -55,3 +56,9 @@ TEN_RUNTIME_API bool ten_env_tester_return_result( TEN_RUNTIME_API bool ten_env_tester_stop_test(ten_env_tester_t *self, ten_error_t *error); + +TEN_RUNTIME_API bool ten_env_tester_log(ten_env_tester_t *self, + TEN_LOG_LEVEL level, + const char *func_name, + const char *file_name, size_t line_no, + const char *msg, ten_error_t *error); diff --git a/core/include_internal/ten_runtime/binding/python/test/env_tester.h b/core/include_internal/ten_runtime/binding/python/test/env_tester.h index 3400db19e1..dd5f94448d 100644 --- a/core/include_internal/ten_runtime/binding/python/test/env_tester.h +++ b/core/include_internal/ten_runtime/binding/python/test/env_tester.h @@ -62,5 +62,8 @@ TEN_RUNTIME_PRIVATE_API PyObject *ten_py_ten_env_tester_send_video_frame( TEN_RUNTIME_PRIVATE_API PyObject *ten_py_ten_env_tester_return_result( PyObject *self, PyObject *args); +TEN_RUNTIME_PRIVATE_API PyObject *ten_py_ten_env_tester_log(PyObject *self, + PyObject *args); + TEN_RUNTIME_PRIVATE_API bool ten_py_ten_env_tester_check_integrity( ten_py_ten_env_tester_t *self); diff --git a/core/include_internal/ten_runtime/test/extension_tester.h b/core/include_internal/ten_runtime/test/extension_tester.h index 9aebd0659b..6f7b129369 100644 --- a/core/include_internal/ten_runtime/test/extension_tester.h +++ b/core/include_internal/ten_runtime/test/extension_tester.h @@ -65,5 +65,8 @@ struct ten_extension_tester_t { TEN_RUNTIME_API bool ten_extension_tester_check_integrity( ten_extension_tester_t *self, bool check_thread); +TEN_RUNTIME_PRIVATE_API bool ten_extension_tester_thread_call_by_me( + ten_extension_tester_t *self); + TEN_RUNTIME_PRIVATE_API void test_app_ten_env_send_cmd(ten_env_t *ten_env, void *user_data); diff --git a/core/src/ten_runtime/binding/python/interface/ten/__init__.py b/core/src/ten_runtime/binding/python/interface/ten/__init__.py index 83614b1276..72b889fd5f 100644 --- a/core/src/ten_runtime/binding/python/interface/ten/__init__.py +++ b/core/src/ten_runtime/binding/python/interface/ten/__init__.py @@ -21,8 +21,9 @@ from .audio_frame import AudioFrame, AudioFrameDataFmt from .data import Data from .log_level import LogLevel -from .test import ExtensionTester, TenEnvTester from .error import TenError +from .test import ExtensionTester, TenEnvTester +from .async_test import AsyncExtensionTester, AsyncTenEnvTester # Specify what should be imported when a user imports * from the # ten_runtime_python package. @@ -48,4 +49,6 @@ "ExtensionTester", "TenEnvTester", "TenError", + "AsyncExtensionTester", + "AsyncTenEnvTester", ] diff --git a/core/src/ten_runtime/binding/python/interface/ten/async_test.py b/core/src/ten_runtime/binding/python/interface/ten/async_test.py new file mode 100644 index 0000000000..0dcb6a9ad4 --- /dev/null +++ b/core/src/ten_runtime/binding/python/interface/ten/async_test.py @@ -0,0 +1,270 @@ +# +# Copyright © 2025 Agora +# This file is part of TEN Framework, an open source project. +# Licensed under the Apache License, Version 2.0, with certain conditions. +# Refer to the "LICENSE" file in the root directory for more information. +# +import asyncio +import os +import threading +import traceback +from typing import Optional, final, Optional + +from libten_runtime_python import _ExtensionTester +from .cmd_result import CmdResult +from .error import TenError +from .cmd import Cmd +from .data import Data +from .audio_frame import AudioFrame +from .video_frame import VideoFrame +from .test import TenEnvTester +from .test_base import TenEnvTesterBase + +CmdResultTuple = tuple[Optional[CmdResult], Optional[TenError]] + + +class AsyncTenEnvTester(TenEnvTesterBase): + def __init__( + self, + ten_env_tester: TenEnvTester, + loop: asyncio.AbstractEventLoop, + thread: threading.Thread, + ) -> None: + super().__init__(ten_env_tester._internal) + + self._ten_loop = loop + self._ten_thread = thread + + def __del__(self) -> None: + pass + + async def send_cmd(self, cmd: Cmd) -> CmdResultTuple: + q = asyncio.Queue(maxsize=1) + self._internal.send_cmd( + cmd, + lambda _, result, error: asyncio.run_coroutine_threadsafe( + q.put([result, error]), + self._ten_loop, + ), # type: ignore + ) + [result, error] = await q.get() + + if result is not None: + assert result.is_completed() + + return result, error + + async def send_data(self, data: Data) -> Optional[TenError]: + q = asyncio.Queue(maxsize=1) + self._internal.send_data( + data, + lambda _, error: asyncio.run_coroutine_threadsafe( + q.put([error]), + self._ten_loop, + ), # type: ignore + ) + error = await q.get() + return error + + async def send_audio_frame( + self, audio_frame: AudioFrame + ) -> Optional[TenError]: + q = asyncio.Queue(maxsize=1) + self._internal.send_audio_frame( + audio_frame, + lambda _, error: asyncio.run_coroutine_threadsafe( + q.put(error), + self._ten_loop, + ), # type: ignore + ) + error = await q.get() + return error + + async def send_video_frame( + self, video_frame: VideoFrame + ) -> Optional[TenError]: + q = asyncio.Queue(maxsize=1) + self._internal.send_video_frame( + video_frame, + lambda _, error: asyncio.run_coroutine_threadsafe( + q.put(error), + self._ten_loop, + ), # type: ignore + ) + error = await q.get() + return error + + async def return_result( + self, + cmd_result: CmdResult, + target_cmd: Cmd, + ) -> None: + q = asyncio.Queue(maxsize=1) + self._internal.return_result( + cmd_result, + target_cmd, + lambda _, error: asyncio.run_coroutine_threadsafe( + q.put(error), + self._ten_loop, + ), # type: ignore + ) + error = await q.get() + if error is not None: + raise RuntimeError(error.err_msg()) + + def stop_test(self) -> None: + return self._internal.stop_test() + + +class AsyncExtensionTester(_ExtensionTester): + def __init__(self) -> None: + self._ten_stop_event = asyncio.Event() + + def __del__(self) -> None: + self._ten_stop_event.set() + if hasattr(self, "_ten_thread"): + self._ten_thread.join() + + def _exit_on_exception( + self, async_ten_env_tester: AsyncTenEnvTester, e: Exception + ): + traceback_info = traceback.format_exc() + async_ten_env_tester.log_fatal( + f"Uncaught exception: {e} \ntraceback: {traceback_info}" + ) + os._exit(1) + + async def _thread_routine(self, ten_env_tester: TenEnvTester) -> None: + self._ten_loop = asyncio.get_running_loop() + self._async_ten_env_tester = AsyncTenEnvTester( + ten_env_tester, self._ten_loop, self._ten_thread + ) + + await self._wrapper_on_start(self._async_ten_env_tester) + ten_env_tester.on_start_done() + + # Suspend the thread until stopEvent is set. + await self._ten_stop_event.wait() + + async def _stop_thread(self): + self._ten_stop_event.set() + + @final + def _proxy_on_start(self, ten_env_tester: TenEnvTester) -> None: + self._ten_thread = threading.Thread( + target=asyncio.run, args=(self._thread_routine(ten_env_tester),) + ) + self._ten_thread.start() + + async def _wrapper_on_start( + self, ten_env_tester: AsyncTenEnvTester + ) -> None: + try: + await self.on_start(ten_env_tester) + except Exception as e: + self._exit_on_exception(ten_env_tester, e) + + @final + def _proxy_on_stop(self, ten_env_tester: TenEnvTester) -> None: + asyncio.run_coroutine_threadsafe(self._stop_thread(), self._ten_loop) + + @final + def _proxy_on_cmd(self, ten_env_tester: TenEnvTester, cmd: Cmd) -> None: + asyncio.run_coroutine_threadsafe( + self._wrapper_on_cmd(self._async_ten_env_tester, cmd), + self._ten_loop, + ) + + async def _wrapper_on_cmd( + self, ten_env_tester: AsyncTenEnvTester, cmd: Cmd + ) -> None: + try: + await self.on_cmd(ten_env_tester, cmd) + except Exception as e: + self._exit_on_exception(ten_env_tester, e) + + @final + def _proxy_on_data(self, ten_env_tester: TenEnvTester, data: Data) -> None: + asyncio.run_coroutine_threadsafe( + self._wrapper_on_data(self._async_ten_env_tester, data), + self._ten_loop, + ) + + async def _wrapper_on_data( + self, ten_env_tester: AsyncTenEnvTester, data: Data + ) -> None: + try: + await self.on_data(ten_env_tester, data) + except Exception as e: + self._exit_on_exception(ten_env_tester, e) + + @final + def _proxy_on_audio_frame( + self, ten_env_tester: TenEnvTester, audio_frame: AudioFrame + ) -> None: + asyncio.run_coroutine_threadsafe( + self._wrapper_on_audio_frame( + self._async_ten_env_tester, audio_frame + ), + self._ten_loop, + ) + + async def _wrapper_on_audio_frame( + self, ten_env_tester: AsyncTenEnvTester, audio_frame: AudioFrame + ) -> None: + try: + await self.on_audio_frame(ten_env_tester, audio_frame) + except Exception as e: + self._exit_on_exception(ten_env_tester, e) + + @final + def _proxy_on_video_frame( + self, ten_env_tester: TenEnvTester, video_frame: VideoFrame + ) -> None: + asyncio.run_coroutine_threadsafe( + self._wrapper_on_video_frame( + self._async_ten_env_tester, video_frame + ), + self._ten_loop, + ) + + async def _wrapper_on_video_frame( + self, ten_env_tester: AsyncTenEnvTester, video_frame: VideoFrame + ) -> None: + try: + await self.on_video_frame(ten_env_tester, video_frame) + except Exception as e: + self._exit_on_exception(ten_env_tester, e) + + @final + def set_test_mode_single( + self, addon_name: str, property_json_str: Optional[str] = None + ) -> None: + return _ExtensionTester.set_test_mode_single( + self, addon_name, property_json_str + ) + + @final + def run(self) -> None: + return _ExtensionTester.run(self) + + async def on_start(self, ten_env_tester: AsyncTenEnvTester) -> None: + pass + + async def on_cmd(self, ten_env_tester: AsyncTenEnvTester, cmd: Cmd) -> None: + pass + + async def on_data( + self, ten_env_tester: AsyncTenEnvTester, data: Data + ) -> None: + pass + + async def on_audio_frame( + self, ten_env_tester: AsyncTenEnvTester, audio_frame: AudioFrame + ) -> None: + pass + + async def on_video_frame( + self, ten_env_tester: AsyncTenEnvTester, video_frame: VideoFrame + ) -> None: + pass diff --git a/core/src/ten_runtime/binding/python/interface/ten/libten_runtime_python.pyi b/core/src/ten_runtime/binding/python/interface/ten/libten_runtime_python.pyi index a32553904c..4bafb028c6 100644 --- a/core/src/ten_runtime/binding/python/interface/ten/libten_runtime_python.pyi +++ b/core/src/ten_runtime/binding/python/interface/ten/libten_runtime_python.pyi @@ -264,6 +264,14 @@ class _TenEnvTester: error_handler: TestErrorHandler, ) -> None: ... def stop_test(self) -> None: ... + def log( + self, + level: LogLevel, + func_name: Optional[str], + file_name: Optional[str], + line_no: int, + msg: str, + ) -> None: ... class _ExtensionTester: def set_test_mode_single( diff --git a/core/src/ten_runtime/binding/python/interface/ten/test.py b/core/src/ten_runtime/binding/python/interface/ten/test.py index 8a22202373..817aa11a8b 100644 --- a/core/src/ten_runtime/binding/python/interface/ten/test.py +++ b/core/src/ten_runtime/binding/python/interface/ten/test.py @@ -7,6 +7,7 @@ from typing import Callable, Optional, final, Optional from libten_runtime_python import _ExtensionTester, _TenEnvTester +from .test_base import TenEnvTesterBase from .cmd_result import CmdResult from .error import TenError from .cmd import Cmd @@ -25,10 +26,10 @@ class TenEnvTester: ... # type: ignore ErrorHandler = Optional[Callable[[TenEnvTester, Optional[TenError]], None]] -class TenEnvTester: +class TenEnvTester(TenEnvTesterBase): def __init__(self, internal_obj: _TenEnvTester) -> None: - self._internal = internal_obj + super().__init__(internal_obj) def __del__(self) -> None: pass @@ -86,6 +87,10 @@ def _proxy_on_start(self, ten_env_tester: TenEnvTester) -> None: def on_start(self, ten_env_tester: TenEnvTester) -> None: ten_env_tester.on_start_done() + @final + def _proxy_on_stop(self, ten_env_tester: TenEnvTester) -> None: + pass + @final def _proxy_on_cmd(self, ten_env_tester: TenEnvTester, cmd: Cmd) -> None: self.on_cmd(ten_env_tester, cmd) diff --git a/core/src/ten_runtime/binding/python/interface/ten/test_base.py b/core/src/ten_runtime/binding/python/interface/ten/test_base.py new file mode 100644 index 0000000000..a39c42aba4 --- /dev/null +++ b/core/src/ten_runtime/binding/python/interface/ten/test_base.py @@ -0,0 +1,65 @@ +# +# Copyright © 2025 Agora +# This file is part of TEN Framework, an open source project. +# Licensed under the Apache License, Version 2.0, with certain conditions. +# Refer to the "LICENSE" file in the root directory for more information. +# +import inspect +from libten_runtime_python import _TenEnvTester +from .log_level import LogLevel + + +class TenEnvTesterBase: + + def __init__(self, internal_obj: _TenEnvTester) -> None: + self._internal = internal_obj + + def __del__(self) -> None: + pass + + def log_verbose(self, msg: str) -> None: + self._log_internal(LogLevel.VERBOSE, msg, 2) + + def log_debug(self, msg: str) -> None: + self._log_internal(LogLevel.DEBUG, msg, 2) + + def log_info(self, msg: str) -> None: + self._log_internal(LogLevel.INFO, msg, 2) + + def log_warn(self, msg: str) -> None: + self._log_internal(LogLevel.WARN, msg, 2) + + def log_error(self, msg: str) -> None: + self._log_internal(LogLevel.ERROR, msg, 2) + + def log_fatal(self, msg: str) -> None: + self._log_internal(LogLevel.FATAL, msg, 2) + + def _log_internal(self, level: LogLevel, msg: str, skip: int) -> None: + # Get the current frame. + frame = inspect.currentframe() + if frame is not None: + try: + # Skip the specified number of frames. + for _ in range(skip): + if frame is not None: + frame = frame.f_back + else: + break + + if frame is not None: + # Extract information from the caller's frame. + file_name = frame.f_code.co_filename + func_name = frame.f_code.co_name + line_no = frame.f_lineno + + return self._internal.log( + level, func_name, file_name, line_no, msg + ) + finally: + # A defensive programming practice to ensure immediate cleanup + # of potentially complex reference cycles. + del frame + + # Fallback in case of failure to get caller information. + return self._internal.log(level, None, None, 0, msg) diff --git a/core/src/ten_runtime/binding/python/native/test/env_tester/env_tester.c b/core/src/ten_runtime/binding/python/native/test/env_tester/env_tester.c index 83aa06e398..8898d7b03a 100644 --- a/core/src/ten_runtime/binding/python/native/test/env_tester/env_tester.c +++ b/core/src/ten_runtime/binding/python/native/test/env_tester/env_tester.c @@ -155,6 +155,7 @@ PyTypeObject *ten_py_ten_env_tester_type(void) { NULL}, {"return_result", ten_py_ten_env_tester_return_result, METH_VARARGS, NULL}, + {"log", ten_py_ten_env_tester_log, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL}, }; diff --git a/core/src/ten_runtime/binding/python/native/test/env_tester/env_tester_log.c b/core/src/ten_runtime/binding/python/native/test/env_tester/env_tester_log.c new file mode 100644 index 0000000000..5ed0ac576c --- /dev/null +++ b/core/src/ten_runtime/binding/python/native/test/env_tester/env_tester_log.c @@ -0,0 +1,104 @@ +// +// Copyright © 2025 Agora +// This file is part of TEN Framework, an open source project. +// Licensed under the Apache License, Version 2.0, with certain conditions. +// Refer to the "LICENSE" file in the root directory for more information. +// +#include "include_internal/ten_runtime/binding/python/common/error.h" +#include "include_internal/ten_runtime/binding/python/test/env_tester.h" +#include "ten_runtime/test/env_tester.h" +#include "ten_runtime/test/env_tester_proxy.h" +#include "ten_utils/log/log.h" +#include "ten_utils/macro/check.h" +#include "ten_utils/macro/mark.h" +#include "ten_utils/macro/memory.h" + +typedef struct ten_env_tester_notify_log_ctx_t { + int32_t level; + const char *func_name; + const char *file_name; + size_t line_no; + const char *msg; + ten_event_t *completed; +} ten_env_tester_notify_log_ctx_t; + +static ten_env_tester_notify_log_ctx_t *ten_env_tester_notify_log_ctx_create( + int32_t level, const char *func_name, const char *file_name, size_t line_no, + const char *msg) { + ten_env_tester_notify_log_ctx_t *ctx = + TEN_MALLOC(sizeof(ten_env_tester_notify_log_ctx_t)); + TEN_ASSERT(ctx, "Failed to allocate memory."); + + ctx->level = level; + ctx->func_name = func_name; + ctx->file_name = file_name; + ctx->line_no = line_no; + ctx->msg = msg; + ctx->completed = ten_event_create(0, 1); + + return ctx; +} + +static void ten_env_tester_notify_log_ctx_destroy( + ten_env_tester_notify_log_ctx_t *ctx) { + TEN_ASSERT(ctx, "Invalid argument."); + + ten_event_destroy(ctx->completed); + + TEN_FREE(ctx); +} + +static void ten_py_ten_env_tester_log_proxy_notify( + ten_env_tester_t *ten_env_tester, void *user_data) { + ten_env_tester_notify_log_ctx_t *ctx = user_data; + TEN_ASSERT(ctx, "Should not happen."); + + ten_env_tester_log(ten_env_tester, ctx->level, ctx->func_name, ctx->file_name, + ctx->line_no, ctx->msg, NULL); + + ten_event_set(ctx->completed); +} + +PyObject *ten_py_ten_env_tester_log(PyObject *self, TEN_UNUSED PyObject *args) { + ten_py_ten_env_tester_t *py_ten_env_tester = (ten_py_ten_env_tester_t *)self; + TEN_ASSERT(py_ten_env_tester && + ten_py_ten_env_tester_check_integrity(py_ten_env_tester), + "Invalid argument."); + + if (PyTuple_GET_SIZE(args) != 5) { + return ten_py_raise_py_value_error_exception( + "Invalid argument count when ten_env.log."); + } + + TEN_LOG_LEVEL level = TEN_LOG_LEVEL_INVALID; + const char *func_name = NULL; + const char *file_name = NULL; + size_t line_no = 0; + const char *msg = NULL; + if (!PyArg_ParseTuple(args, "izzis", &level, &func_name, &file_name, &line_no, + &msg)) { + return ten_py_raise_py_value_error_exception( + "Failed to parse argument when ten_env.log."); + } + + ten_error_t err; + ten_error_init(&err); + + ten_env_tester_notify_log_ctx_t *ctx = ten_env_tester_notify_log_ctx_create( + level, func_name, file_name, line_no, msg); + + if (!ten_env_tester_proxy_notify(py_ten_env_tester->c_ten_env_tester_proxy, + ten_py_ten_env_tester_log_proxy_notify, ctx, + &err)) { + TEN_ASSERT(false, "Should not happen."); + } + + PyThreadState *saved_py_thread_state = PyEval_SaveThread(); + ten_event_wait(ctx->completed, -1); + PyEval_RestoreThread(saved_py_thread_state); + + ten_error_deinit(&err); + ten_env_tester_notify_log_ctx_destroy(ctx); + + Py_RETURN_NONE; +} diff --git a/core/src/ten_runtime/binding/python/native/test/extension_tester.c b/core/src/ten_runtime/binding/python/native/test/extension_tester.c index fe3a657015..55d60b6d4f 100644 --- a/core/src/ten_runtime/binding/python/native/test/extension_tester.c +++ b/core/src/ten_runtime/binding/python/native/test/extension_tester.c @@ -105,18 +105,28 @@ static void proxy_on_stop(ten_extension_tester_t *extension_tester, ten_py_extension_tester_check_integrity(py_extension_tester), "Invalid argument."); - PyObject *py_ten_env_tester = py_extension_tester->py_ten_env_tester; + ten_py_ten_env_tester_t *py_ten_env_tester = + (ten_py_ten_env_tester_t *)py_extension_tester->py_ten_env_tester; TEN_ASSERT(py_ten_env_tester, "Should not happen."); - TEN_ASSERT( - ((ten_py_ten_env_tester_t *)py_ten_env_tester)->actual_py_ten_env_tester, - "Should not happen."); + TEN_ASSERT(py_ten_env_tester->actual_py_ten_env_tester, "Should not happen."); - // Release the ten_env_tester_proxy. - ten_env_tester_proxy_release( - ((ten_py_ten_env_tester_t *)py_ten_env_tester)->c_ten_env_tester_proxy, - NULL); + // About to call the Python function, so it's necessary to ensure that the GIL + // has been acquired. + PyGILState_STATE prev_state = ten_py_gil_state_ensure_internal(); - ((ten_py_ten_env_tester_t *)py_ten_env_tester)->c_ten_env_tester_proxy = NULL; + PyObject *py_res = + PyObject_CallMethod((PyObject *)py_extension_tester, "_proxy_on_stop", + "O", py_ten_env_tester->actual_py_ten_env_tester); + Py_XDECREF(py_res); + + bool err_occurred = ten_py_check_and_clear_py_error(); + TEN_ASSERT(!err_occurred, "Should not happen."); + + ten_py_gil_state_release_internal(prev_state); + + // Release the ten_env_tester_proxy. + ten_env_tester_proxy_release(py_ten_env_tester->c_ten_env_tester_proxy, NULL); + py_ten_env_tester->c_ten_env_tester_proxy = NULL; ten_env_tester_on_stop_done(ten_env_tester, NULL); } diff --git a/core/src/ten_runtime/test/env_tester.c b/core/src/ten_runtime/test/env_tester.c index d23bf8015c..e79434ab27 100644 --- a/core/src/ten_runtime/test/env_tester.c +++ b/core/src/ten_runtime/test/env_tester.c @@ -22,6 +22,7 @@ #include "ten_utils/io/runloop.h" #include "ten_utils/lib/error.h" #include "ten_utils/lib/signature.h" +#include "ten_utils/lib/string.h" #include "ten_utils/macro/mark.h" #include "ten_utils/macro/memory.h" @@ -71,7 +72,7 @@ void ten_env_tester_destroy(ten_env_tester_t *self) { TEN_FREE(self); } -typedef struct ten_extension_tester_send_cmd_info_t { +typedef struct ten_env_tester_send_cmd_ctx_t { ten_extension_tester_t *tester; ten_shared_ptr_t *cmd; ten_shared_ptr_t *cmd_result; @@ -80,7 +81,7 @@ typedef struct ten_extension_tester_send_cmd_info_t { ten_error_t *err; } ten_env_tester_send_cmd_ctx_t; -typedef struct ten_extension_tester_send_msg_info_t { +typedef struct ten_env_tester_send_msg_ctx_t { ten_extension_tester_t *tester; ten_shared_ptr_t *msg; ten_env_tester_error_handler_func_t handler; @@ -88,7 +89,7 @@ typedef struct ten_extension_tester_send_msg_info_t { ten_error_t *err; } ten_env_tester_send_msg_ctx_t; -typedef struct ten_extension_tester_return_result_info_t { +typedef struct ten_env_tester_return_result_ctx_t { ten_extension_tester_t *tester; ten_shared_ptr_t *result; ten_shared_ptr_t *target_cmd; @@ -97,6 +98,15 @@ typedef struct ten_extension_tester_return_result_info_t { ten_error_t *err; } ten_env_tester_return_result_ctx_t; +typedef struct ten_env_tester_notify_log_ctx_t { + ten_env_tester_t *ten_env_tester; + TEN_LOG_LEVEL level; + ten_string_t func_name; + ten_string_t file_name; + size_t line_no; + ten_string_t msg; +} ten_env_tester_notify_log_ctx_t; + static ten_env_tester_send_cmd_ctx_t *ten_extension_tester_send_cmd_ctx_create( ten_extension_tester_t *tester, ten_shared_ptr_t *cmd, ten_env_tester_cmd_result_handler_func_t handler, void *handler_user_data) { @@ -214,6 +224,52 @@ static void ten_extension_tester_return_result_ctx_destroy( TEN_FREE(self); } +static ten_env_tester_notify_log_ctx_t *ten_env_tester_notify_log_ctx_create( + ten_env_tester_t *ten_env_tester, TEN_LOG_LEVEL level, + const char *func_name, const char *file_name, size_t line_no, + const char *msg) { + TEN_ASSERT(ten_env_tester, "Invalid argument."); + + ten_env_tester_notify_log_ctx_t *self = + TEN_MALLOC(sizeof(ten_env_tester_notify_log_ctx_t)); + TEN_ASSERT(self, "Failed to allocate memory."); + + self->ten_env_tester = ten_env_tester; + self->level = level; + self->line_no = line_no; + + if (func_name) { + ten_string_init_from_c_str(&self->func_name, func_name, strlen(func_name)); + } else { + ten_string_init(&self->func_name); + } + + if (file_name) { + ten_string_init_from_c_str(&self->file_name, file_name, strlen(file_name)); + } else { + ten_string_init(&self->file_name); + } + + if (msg) { + ten_string_init_from_c_str(&self->msg, msg, strlen(msg)); + } else { + ten_string_init(&self->msg); + } + + return self; +} + +static void ten_env_tester_notify_log_ctx_destroy( + ten_env_tester_notify_log_ctx_t *ctx) { + TEN_ASSERT(ctx, "Invalid argument."); + + ten_string_deinit(&ctx->func_name); + ten_string_deinit(&ctx->file_name); + ten_string_deinit(&ctx->msg); + + TEN_FREE(ctx); +} + static void ten_extension_tester_execute_error_handler_task(void *self, void *arg) { ten_extension_tester_t *tester = self; @@ -681,6 +737,35 @@ bool ten_env_tester_stop_test(ten_env_tester_t *self, ten_error_t *err) { err); } +static void test_extension_ten_env_log(ten_env_t *self, void *user_data) { + ten_env_tester_notify_log_ctx_t *ctx = user_data; + TEN_ASSERT(ctx, "Should not happen."); + + ten_env_log(self, ctx->level, ten_string_get_raw_str(&ctx->func_name), + ten_string_get_raw_str(&ctx->file_name), ctx->line_no, + ten_string_get_raw_str(&ctx->msg)); + + ten_env_tester_notify_log_ctx_destroy(ctx); +} + +bool ten_env_tester_log(ten_env_tester_t *self, TEN_LOG_LEVEL level, + const char *func_name, const char *file_name, + size_t line_no, const char *msg, ten_error_t *error) { + TEN_ASSERT(self && ten_env_tester_check_integrity(self), "Invalid argument."); + + ten_env_tester_notify_log_ctx_t *ctx = ten_env_tester_notify_log_ctx_create( + self, level, func_name, file_name, line_no, msg); + TEN_ASSERT(ctx, "Allocation failed."); + + bool rc = ten_env_proxy_notify(self->tester->test_extension_ten_env_proxy, + test_extension_ten_env_log, ctx, false, error); + if (!rc) { + ten_env_tester_notify_log_ctx_destroy(ctx); + } + + return rc; +} + bool ten_env_tester_on_start_done(ten_env_tester_t *self, ten_error_t *err) { TEN_ASSERT(self && ten_env_tester_check_integrity(self), "Invalid argument."); diff --git a/core/src/ten_runtime/test/env_tester_proxy.c b/core/src/ten_runtime/test/env_tester_proxy.c index f3c0a13124..e71fb29fe1 100644 --- a/core/src/ten_runtime/test/env_tester_proxy.c +++ b/core/src/ten_runtime/test/env_tester_proxy.c @@ -110,9 +110,17 @@ bool ten_env_tester_proxy_notify(ten_env_tester_proxy_t *self, ten_env_tester_t *ten_env_tester = self->ten_env_tester; TEN_ASSERT(ten_env_tester, "Should not happen."); - int rc = ten_runloop_post_task_tail(ten_env_tester->tester->tester_runloop, - (ten_runloop_task_func_t)notify_func, - ten_env_tester, user_data); + bool result = true; + + ten_extension_tester_t *tester = ten_env_tester->tester; + if (ten_extension_tester_thread_call_by_me(tester)) { + notify_func(self->ten_env_tester, user_data); + } else { + int rc = ten_runloop_post_task_tail(tester->tester_runloop, + (ten_runloop_task_func_t)notify_func, + self->ten_env_tester, user_data); + result = rc == 0; + } - return rc == 0; + return result; } diff --git a/core/src/ten_runtime/test/extension_tester.c b/core/src/ten_runtime/test/extension_tester.c index bc372aadc9..a945fcb01a 100644 --- a/core/src/ten_runtime/test/extension_tester.c +++ b/core/src/ten_runtime/test/extension_tester.c @@ -57,6 +57,15 @@ bool ten_extension_tester_check_integrity(ten_extension_tester_t *self, return true; } +bool ten_extension_tester_thread_call_by_me(ten_extension_tester_t *self) { + TEN_ASSERT(self, "Invalid argument."); + TEN_ASSERT(ten_extension_tester_check_integrity(self, false), + "Invalid argument."); + + return ten_thread_equal(NULL, ten_sanitizer_thread_check_get_belonging_thread( + &self->thread_check)); +} + ten_extension_tester_t *ten_extension_tester_create( ten_extension_tester_on_start_func_t on_start, ten_extension_tester_on_stop_func_t on_stop, @@ -444,8 +453,7 @@ void ten_extension_tester_on_test_extension_start( } } -void ten_extension_tester_on_test_extension_stop( - ten_extension_tester_t *self) { +void ten_extension_tester_on_test_extension_stop(ten_extension_tester_t *self) { TEN_ASSERT(self && ten_extension_tester_check_integrity(self, true), "Invalid argument."); diff --git a/tests/ten_runtime/integration/python/standalone_test_async_python/default_async_extension_python/tests/test_mock.py b/tests/ten_runtime/integration/python/standalone_test_async_python/default_async_extension_python/tests/test_mock.py index 4fef188c14..eba255ee02 100644 --- a/tests/ten_runtime/integration/python/standalone_test_async_python/default_async_extension_python/tests/test_mock.py +++ b/tests/ten_runtime/integration/python/standalone_test_async_python/default_async_extension_python/tests/test_mock.py @@ -28,7 +28,7 @@ def check_weather( assert result is not None statusCode = result.get_status_code() - print("receive weather, status:" + str(statusCode)) + ten_env.log_info("receive weather, status:" + str(statusCode)) if statusCode == StatusCode.OK: detail = result.get_property_string("detail") @@ -49,7 +49,7 @@ def on_start(self, ten_env: TenEnvTester) -> None: ten_env.on_start_done() def on_cmd(self, ten_env: TenEnvTester, cmd: Cmd) -> None: - print("ExtensionTesterMock on_cmd: " + cmd.get_name()) + ten_env.log_info("ExtensionTesterMock on_cmd: " + cmd.get_name()) if cmd.get_name() == "query_weather": cmd_result = CmdResult.create(StatusCode.OK) diff --git a/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_async_basic.py b/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_async_basic.py new file mode 100644 index 0000000000..a443009cfa --- /dev/null +++ b/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_async_basic.py @@ -0,0 +1,51 @@ +# +# Copyright © 2025 Agora +# This file is part of TEN Framework, an open source project. +# Licensed under the Apache License, Version 2.0, with certain conditions. +# Refer to the "LICENSE" file in the root directory for more information. +# +from ten import ( + Cmd, + Data, + AudioFrame, + VideoFrame, + StatusCode, + AsyncExtensionTester, + AsyncTenEnvTester, +) + + +class AsyncExtensionTesterBasic(AsyncExtensionTester): + async def on_start(self, ten_env: AsyncTenEnvTester) -> None: + await ten_env.send_data(Data.create("test")) + await ten_env.send_audio_frame(AudioFrame.create("test")) + await ten_env.send_video_frame(VideoFrame.create("test")) + + new_cmd = Cmd.create("hello_world") + + ten_env.log_info("send hello_world") + result, error = await ten_env.send_cmd( + new_cmd, + ) + if error is not None: + assert False, error + + assert result is not None + + statusCode = result.get_status_code() + ten_env.log_info("receive hello_world, status:" + str(statusCode)) + + ten_env.log_info("tester on_start_done") + + if statusCode == StatusCode.OK: + ten_env.stop_test() + + +def test_basic(): + tester = AsyncExtensionTesterBasic() + tester.set_test_mode_single("default_extension_python") + tester.run() + + +if __name__ == "__main__": + test_basic() diff --git a/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_async_outer_thread.py b/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_async_outer_thread.py new file mode 100644 index 0000000000..7124d8a3c3 --- /dev/null +++ b/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_async_outer_thread.py @@ -0,0 +1,53 @@ +# +# Copyright © 2025 Agora +# This file is part of TEN Framework, an open source project. +# Licensed under the Apache License, Version 2.0, with certain conditions. +# Refer to the "LICENSE" file in the root directory for more information. +# +import asyncio +from ten import ( + AsyncExtensionTester, + AsyncTenEnvTester, + Cmd, + Data, + AudioFrame, + VideoFrame, + StatusCode, +) + + +class AsyncExtensionTesterOuterThread(AsyncExtensionTester): + async def send_msgs(self, ten_env: AsyncTenEnvTester): + await ten_env.send_data(Data.create("test")) + await ten_env.send_audio_frame(AudioFrame.create("test")) + await ten_env.send_video_frame(VideoFrame.create("test")) + + new_cmd = Cmd.create("hello_world") + result, error = await ten_env.send_cmd( + new_cmd, + ) + if error is not None: + assert False, error + + assert result is not None + + statusCode = result.get_status_code() + ten_env.log_info("receive hello_world, status:" + str(statusCode)) + + if statusCode == StatusCode.OK: + ten_env.stop_test() + + async def on_start(self, ten_env: AsyncTenEnvTester) -> None: + asyncio.create_task(self.send_msgs(ten_env)) + + ten_env.log_info("tester on_start_done") + + +def test_basic(): + tester = AsyncExtensionTesterOuterThread() + tester.set_test_mode_single("default_extension_python") + tester.run() + + +if __name__ == "__main__": + test_basic() diff --git a/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_basic.py b/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_basic.py index 7a847dbee0..867c7205cf 100644 --- a/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_basic.py +++ b/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_basic.py @@ -31,7 +31,7 @@ def check_hello( assert result is not None statusCode = result.get_status_code() - print("receive hello_world, status:" + str(statusCode)) + ten_env.log_info("receive hello_world, status:" + str(statusCode)) if statusCode == StatusCode.OK: ten_env.stop_test() @@ -39,7 +39,7 @@ def check_hello( def on_start(self, ten_env: TenEnvTester) -> None: new_cmd = Cmd.create("hello_world") - print("send hello_world") + ten_env.log_info("send hello_world") ten_env.send_cmd( new_cmd, lambda ten_env, result, error: self.check_hello( @@ -51,7 +51,7 @@ def on_start(self, ten_env: TenEnvTester) -> None: ten_env.send_audio_frame(AudioFrame.create("test")) ten_env.send_video_frame(VideoFrame.create("test")) - print("tester on_start_done") + ten_env.log_info("tester on_start_done") ten_env.on_start_done() diff --git a/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_outer_thread.py b/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_outer_thread.py index 104ecc4c3a..f86a35777b 100644 --- a/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_outer_thread.py +++ b/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_outer_thread.py @@ -32,7 +32,7 @@ def check_hello( assert result is not None statusCode = result.get_status_code() - print("receive hello_world, status:" + str(statusCode)) + ten_env.log_info("receive hello_world, status:" + str(statusCode)) if statusCode == StatusCode.OK: ten_env.stop_test() @@ -54,7 +54,7 @@ def on_start(self, ten_env: TenEnvTester) -> None: self.thread = threading.Thread(target=self.send_msgs, args=(ten_env,)) self.thread.start() - print("tester on_start_done") + ten_env.log_info("tester on_start_done") ten_env.on_start_done() diff --git a/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_set_property.py b/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_set_property.py index 71912f1b2e..d519ade4ce 100644 --- a/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_set_property.py +++ b/tests/ten_runtime/integration/python/standalone_test_python/default_extension_python/tests/test_set_property.py @@ -28,7 +28,7 @@ def check_greeting( assert result is not None statusCode = result.get_status_code() - print("receive hello_world, status:" + str(statusCode)) + ten_env.log_info("receive hello_world, status:" + str(statusCode)) if statusCode == StatusCode.OK: detail = result.get_property_string("detail") @@ -46,7 +46,7 @@ def on_start(self, ten_env: TenEnvTester) -> None: ), ) - print("tester on_start_done") + ten_env.log_info("tester on_start_done") ten_env.on_start_done()