diff --git a/core/include_internal/ten_runtime/common/constant_str.h b/core/include_internal/ten_runtime/common/constant_str.h index 6d61ffe05..f82cf237e 100644 --- a/core/include_internal/ten_runtime/common/constant_str.h +++ b/core/include_internal/ten_runtime/common/constant_str.h @@ -166,4 +166,4 @@ #define TEN_STR_RESULT_RETURN_POLICY "result_return_policy" #define TEN_STR_FIRST_ERROR_OR_FIRST_OK "first_error_or_first_ok" #define TEN_STR_FIRST_ERROR_OR_LAST_OK "first_error_or_last_ok" -#define TEN_STR_EACH_IMMEDIATELY "each_immediately" +#define TEN_STR_EACH_OK_AND_ERROR "each_ok_and_error" diff --git a/core/include_internal/ten_runtime/path/result_return_policy.h b/core/include_internal/ten_runtime/path/result_return_policy.h index 728aeebd0..b93f60f07 100644 --- a/core/include_internal/ten_runtime/path/result_return_policy.h +++ b/core/include_internal/ten_runtime/path/result_return_policy.h @@ -22,8 +22,8 @@ typedef enum TEN_RESULT_RETURN_POLICY { // Similar to the above, except return the last received one. TEN_RESULT_RETURN_POLICY_FIRST_ERROR_OR_LAST_OK, - // Return each result immediately as it is received. - TEN_RESULT_RETURN_POLICY_EACH_IMMEDIATELY, + // Return each result as it is received, regardless of its status. + TEN_RESULT_RETURN_POLICY_EACH_OK_AND_ERROR, // More modes is allowed, and could be added here in case needed. } TEN_RESULT_RETURN_POLICY; diff --git a/core/src/ten_runtime/binding/python/native/ten_env/ten_env_send_cmd.c b/core/src/ten_runtime/binding/python/native/ten_env/ten_env_send_cmd.c index 063ff309f..1be2a6933 100644 --- a/core/src/ten_runtime/binding/python/native/ten_env/ten_env_send_cmd.c +++ b/core/src/ten_runtime/binding/python/native/ten_env/ten_env_send_cmd.c @@ -74,8 +74,8 @@ static void proxy_send_xxx_callback(ten_extension_t *extension, Py_XDECREF(arglist); - bool is_final = ten_cmd_result_is_final(cmd_result, NULL); - if (is_final) { + bool is_completed = ten_cmd_result_is_completed(cmd_result, NULL); + if (is_completed) { Py_XDECREF(cb_func); } diff --git a/core/src/ten_runtime/path/path_group.c b/core/src/ten_runtime/path/path_group.c index 24486f2ab..f35bb5e2f 100644 --- a/core/src/ten_runtime/path/path_group.c +++ b/core/src/ten_runtime/path/path_group.c @@ -217,7 +217,7 @@ ten_path_t *ten_path_group_resolve(ten_path_t *path, TEN_PATH_TYPE type) { case TEN_RESULT_RETURN_POLICY_FIRST_ERROR_OR_LAST_OK: return ten_path_group_resolve_in_one_fail_and_all_ok_return(members, type, true); - case TEN_RESULT_RETURN_POLICY_EACH_IMMEDIATELY: + case TEN_RESULT_RETURN_POLICY_EACH_OK_AND_ERROR: // In this policy, we return the current path immediately. return path; default: diff --git a/core/src/ten_runtime/path/path_table.c b/core/src/ten_runtime/path/path_table.c index 30ffd65ae..dd671cb83 100644 --- a/core/src/ten_runtime/path/path_table.c +++ b/core/src/ten_runtime/path/path_table.c @@ -526,7 +526,7 @@ ten_shared_ptr_t *ten_path_table_determine_actual_cmd_result( ten_path_group_t *path_group = ten_path_get_group(path); switch (path_group->policy) { - case TEN_RESULT_RETURN_POLICY_EACH_IMMEDIATELY: { + case TEN_RESULT_RETURN_POLICY_EACH_OK_AND_ERROR: { bool last_one = ten_path_table_remove_path_from_group(self, path_type, path); diff --git a/core/src/ten_runtime/path/result_return_policy.c b/core/src/ten_runtime/path/result_return_policy.c index 1b5901f15..448d3802f 100644 --- a/core/src/ten_runtime/path/result_return_policy.c +++ b/core/src/ten_runtime/path/result_return_policy.c @@ -19,8 +19,8 @@ TEN_RESULT_RETURN_POLICY ten_result_return_policy_from_string( } else if (ten_c_string_is_equal(policy_str, TEN_STR_FIRST_ERROR_OR_LAST_OK)) { return TEN_RESULT_RETURN_POLICY_FIRST_ERROR_OR_LAST_OK; - } else if (ten_c_string_is_equal(policy_str, TEN_STR_EACH_IMMEDIATELY)) { - return TEN_RESULT_RETURN_POLICY_EACH_IMMEDIATELY; + } else if (ten_c_string_is_equal(policy_str, TEN_STR_EACH_OK_AND_ERROR)) { + return TEN_RESULT_RETURN_POLICY_EACH_OK_AND_ERROR; } else { return TEN_RESULT_RETURN_POLICY_INVALID; } @@ -33,8 +33,8 @@ const char *ten_result_return_policy_to_string( return TEN_STR_FIRST_ERROR_OR_FIRST_OK; case TEN_RESULT_RETURN_POLICY_FIRST_ERROR_OR_LAST_OK: return TEN_STR_FIRST_ERROR_OR_LAST_OK; - case TEN_RESULT_RETURN_POLICY_EACH_IMMEDIATELY: - return TEN_STR_EACH_IMMEDIATELY; + case TEN_RESULT_RETURN_POLICY_EACH_OK_AND_ERROR: + return TEN_STR_EACH_OK_AND_ERROR; default: return NULL; } diff --git a/core/src/ten_rust/src/json_schema/data/property.schema.json b/core/src/ten_rust/src/json_schema/data/property.schema.json index 4bd035b49..206a432f4 100644 --- a/core/src/ten_rust/src/json_schema/data/property.schema.json +++ b/core/src/ten_rust/src/json_schema/data/property.schema.json @@ -29,6 +29,14 @@ "minLength": 1, "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" }, + "result_return_policy": { + "type": "string", + "enum": [ + "each_ok_and_error", + "first_error_or_first_ok", + "first_error_or_last_ok" + ] + }, "valueType": { "type": "string", "enum": [ @@ -310,6 +318,9 @@ "name": { // The api name should be alphanumericCharacters. "$ref": "#/$defs/alphanumericCharacters" }, + "result_return_policy": { + "$ref": "#/$defs/result_return_policy" + }, "dest": { "$ref": "#/$defs/dataDests" } @@ -329,6 +340,9 @@ "name": { // The api name should be alphanumericCharacters. "$ref": "#/$defs/alphanumericCharacters" }, + "result_return_policy": { + "$ref": "#/$defs/result_return_policy" + }, "dest": { "$ref": "#/$defs/cmdDests" } diff --git a/tests/ten_runtime/integration/pytest.ini b/tests/ten_runtime/integration/pytest.ini index 9135415f1..745d47309 100644 --- a/tests/ten_runtime/integration/pytest.ini +++ b/tests/ten_runtime/integration/pytest.ini @@ -17,7 +17,9 @@ addopts = --ignore=tests/ten_runtime/integration/python/go_app_async_extension_python/go_app_async_extension_python_app/ten_packages/extension/default_extension_python/tests/ --ignore=tests/ten_runtime/integration/python/large_json_python/large_json_python_app/ten_packages/extension/default_extension_python/tests/ --ignore=tests/ten_runtime/integration/python/multi_process_python/multi_process_python_app/ten_packages/extension/default_extension_python/tests/ - --ignore=tests/ten_runtime/integration/python/multiple_results_python/multiple_results_python_app/ten_packages/extension/default_extension_python/tests/ + --ignore=tests/ten_runtime/integration/python/multiple_results_python_1/multiple_results_python_1_app/ten_packages/extension/default_extension_python/tests/ + --ignore=tests/ten_runtime/integration/python/multiple_results_python_2/multiple_results_python_2_app/ten_packages/extension/default_extension_python/tests/ + --ignore=tests/ten_runtime/integration/python/multiple_results_python_3/multiple_results_python_3_app/ten_packages/extension/default_extension_python/tests/ --ignore=tests/ten_runtime/integration/python/resp_handler_yield_python/resp_handler_yield_python_app/ten_packages/extension/default_extension_python/tests/ --ignore=tests/ten_runtime/integration/python/send_cmd_discard_result_python/send_cmd_discard_result_python_app/ten_packages/extension/default_extension_python/tests/ --ignore=tests/ten_runtime/integration/python/send_cmd_python/send_cmd_python_app/ten_packages/extension/default_extension_python/tests/ diff --git a/tests/ten_runtime/integration/python/BUILD.gn b/tests/ten_runtime/integration/python/BUILD.gn index 5ec10dd53..e9c7494ce 100644 --- a/tests/ten_runtime/integration/python/BUILD.gn +++ b/tests/ten_runtime/integration/python/BUILD.gn @@ -26,7 +26,9 @@ group("python") { "go_app_async_extension_python", "large_json_python", "multi_process_python", - "multiple_results_python", + "multiple_results_python_1", + "multiple_results_python_2", + "multiple_results_python_3", "resp_handler_yield_python", "send_cmd_discard_result_python", "send_cmd_python", diff --git a/tests/ten_runtime/integration/python/multiple_results_python/BUILD.gn b/tests/ten_runtime/integration/python/multiple_results_python_1/BUILD.gn similarity index 69% rename from tests/ten_runtime/integration/python/multiple_results_python/BUILD.gn rename to tests/ten_runtime/integration/python/multiple_results_python_1/BUILD.gn index d7c59ff4c..4837a4d0e 100644 --- a/tests/ten_runtime/integration/python/multiple_results_python/BUILD.gn +++ b/tests/ten_runtime/integration/python/multiple_results_python_1/BUILD.gn @@ -7,17 +7,17 @@ import("//build/ten_runtime/feature/test.gni") import("//build/ten_runtime/ten.gni") -ten_package_test_prepare_app("multiple_results_python_app") { +ten_package_test_prepare_app("multiple_results_python_1_app") { src_app = "default_app_python" src_app_language = "python" - generated_app_src_root_dir_name = "multiple_results_python_app" + generated_app_src_root_dir_name = "multiple_results_python_1_app" replace_files_after_install_app = [ - "multiple_results_python_app/manifest.json", - "multiple_results_python_app/property.json", + "multiple_results_python_1_app/manifest.json", + "multiple_results_python_1_app/property.json", ] - replace_files_after_install_all = [ "multiple_results_python_app/ten_packages/extension/default_extension_python/extension.py" ] + replace_files_after_install_all = [ "multiple_results_python_1_app/ten_packages/extension/default_extension_python/extension.py" ] if (ten_enable_package_manager) { deps = [ @@ -31,7 +31,7 @@ ten_package_test_prepare_app("multiple_results_python_app") { } ten_package_test_prepare_auxiliary_resources( - "multiple_results_python_test_files") { + "multiple_results_python_1_test_files") { resources = [ "//tests/ten_runtime/integration/common=>common", "__init__.py", @@ -42,9 +42,9 @@ ten_package_test_prepare_auxiliary_resources( } } -group("multiple_results_python") { +group("multiple_results_python_1") { deps = [ - ":multiple_results_python_app", - ":multiple_results_python_test_files", + ":multiple_results_python_1_app", + ":multiple_results_python_1_test_files", ] } diff --git a/tests/ten_runtime/integration/python/multiple_results_python/__init__.py b/tests/ten_runtime/integration/python/multiple_results_python_1/__init__.py similarity index 100% rename from tests/ten_runtime/integration/python/multiple_results_python/__init__.py rename to tests/ten_runtime/integration/python/multiple_results_python_1/__init__.py diff --git a/tests/ten_runtime/integration/python/multiple_results_python/multiple_results_python_app/manifest.json b/tests/ten_runtime/integration/python/multiple_results_python_1/multiple_results_python_1_app/manifest.json similarity index 100% rename from tests/ten_runtime/integration/python/multiple_results_python/multiple_results_python_app/manifest.json rename to tests/ten_runtime/integration/python/multiple_results_python_1/multiple_results_python_1_app/manifest.json diff --git a/tests/ten_runtime/integration/python/multiple_results_python/multiple_results_python_app/property.json b/tests/ten_runtime/integration/python/multiple_results_python_1/multiple_results_python_1_app/property.json similarity index 100% rename from tests/ten_runtime/integration/python/multiple_results_python/multiple_results_python_app/property.json rename to tests/ten_runtime/integration/python/multiple_results_python_1/multiple_results_python_1_app/property.json diff --git a/tests/ten_runtime/integration/python/multiple_results_python/multiple_results_python_app/ten_packages/extension/default_extension_python/extension.py b/tests/ten_runtime/integration/python/multiple_results_python_1/multiple_results_python_1_app/ten_packages/extension/default_extension_python/extension.py similarity index 100% rename from tests/ten_runtime/integration/python/multiple_results_python/multiple_results_python_app/ten_packages/extension/default_extension_python/extension.py rename to tests/ten_runtime/integration/python/multiple_results_python_1/multiple_results_python_1_app/ten_packages/extension/default_extension_python/extension.py diff --git a/tests/ten_runtime/integration/python/multiple_results_python/test_case.py b/tests/ten_runtime/integration/python/multiple_results_python_1/test_case.py similarity index 77% rename from tests/ten_runtime/integration/python/multiple_results_python/test_case.py rename to tests/ten_runtime/integration/python/multiple_results_python_1/test_case.py index c822c47ef..f545decdf 100644 --- a/tests/ten_runtime/integration/python/multiple_results_python/test_case.py +++ b/tests/ten_runtime/integration/python/multiple_results_python_1/test_case.py @@ -1,5 +1,5 @@ """ -Test multiple_results_python. +Test multiple_results_python_1. """ import subprocess @@ -20,7 +20,7 @@ def http_request(): ) -def test_multiple_results_python(): +def test_multiple_results_python_1(): """Test client and app server.""" base_path = os.path.dirname(os.path.abspath(__file__)) root_dir = os.path.join(base_path, "../../../../../") @@ -40,20 +40,20 @@ def test_multiple_results_python(): my_env["PATH"] = os.path.join(venv_dir, "bin") + os.pathsep + my_env["PATH"] if sys.platform == "win32": - print("test_multiple_results_python doesn't support win32") + print("test_multiple_results_python_1 doesn't support win32") assert False elif sys.platform == "darwin": # client depends on some libraries in the TEN app. my_env["DYLD_LIBRARY_PATH"] = os.path.join( - base_path, "multiple_results_python_app/lib" + base_path, "multiple_results_python_1_app/lib" ) else: # client depends on some libraries in the TEN app. my_env["LD_LIBRARY_PATH"] = os.path.join( - base_path, "multiple_results_python_app/lib" + base_path, "multiple_results_python_1_app/lib" ) - app_root_path = os.path.join(base_path, "multiple_results_python_app") + app_root_path = os.path.join(base_path, "multiple_results_python_1_app") tman_install_cmd = [ os.path.join(root_dir, "ten_manager/bin/tman"), @@ -72,7 +72,7 @@ def test_multiple_results_python(): tman_install_process.wait() bootstrap_cmd = os.path.join( - base_path, "multiple_results_python_app/bin/bootstrap" + base_path, "multiple_results_python_1_app/bin/bootstrap" ) bootstrap_process = subprocess.Popen( @@ -84,14 +84,14 @@ def test_multiple_results_python(): if os.path.exists(os.path.join(base_path, "use_asan_lib_marker")): libasan_path = os.path.join( base_path, - "multiple_results_python_app/ten_packages/system/ten_runtime/lib/libasan.so", + "multiple_results_python_1_app/ten_packages/system/ten_runtime/lib/libasan.so", ) if os.path.exists(libasan_path): my_env["LD_PRELOAD"] = libasan_path server_cmd = os.path.join( - base_path, "multiple_results_python_app/bin/start" + base_path, "multiple_results_python_1_app/bin/start" ) server = subprocess.Popen( @@ -104,11 +104,11 @@ def test_multiple_results_python(): is_started = http.is_app_started("127.0.0.1", 8002, 30) if not is_started: - print("The multiple_results_python is not started after 30 seconds.") + print("The multiple_results_python_1 is not started after 30 seconds.") server.kill() exit_code = server.wait() - print("The exit code of multiple_results_python: ", exit_code) + print("The exit code of multiple_results_python_1: ", exit_code) assert exit_code == 0 assert 0 @@ -123,10 +123,12 @@ def test_multiple_results_python(): finally: is_stopped = http.stop_app("127.0.0.1", 8002, 30) if not is_stopped: - print("The multiple_results_python can not stop after 30 seconds.") + print( + "The multiple_results_python_1 can not stop after 30 seconds." + ) server.kill() exit_code = server.wait() - print("The exit code of multiple_results_python: ", exit_code) + print("The exit code of multiple_results_python_1: ", exit_code) assert exit_code == 0 diff --git a/tests/ten_runtime/integration/python/multiple_results_python_2/BUILD.gn b/tests/ten_runtime/integration/python/multiple_results_python_2/BUILD.gn new file mode 100644 index 000000000..3dce11094 --- /dev/null +++ b/tests/ten_runtime/integration/python/multiple_results_python_2/BUILD.gn @@ -0,0 +1,50 @@ +# +# Copyright © 2024 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("//build/ten_runtime/feature/test.gni") +import("//build/ten_runtime/ten.gni") + +ten_package_test_prepare_app("multiple_results_python_2_app") { + src_app = "default_app_python" + src_app_language = "python" + generated_app_src_root_dir_name = "multiple_results_python_2_app" + + replace_files_after_install_app = [ + "multiple_results_python_2_app/manifest.json", + "multiple_results_python_2_app/property.json", + ] + + replace_files_after_install_all = [ "multiple_results_python_2_app/ten_packages/extension/default_extension_python/extension.py" ] + + if (ten_enable_package_manager) { + deps = [ + "//core/src/ten_manager", + "//packages/core_apps/default_app_python:upload_default_app_python_to_server", + "//packages/core_extensions/default_extension_python:upload_default_extension_python_to_server", + "//packages/example_extensions/simple_echo_cpp:upload_simple_echo_cpp_to_server", + "//packages/example_extensions/simple_http_server_cpp:upload_simple_http_server_cpp_to_server", + ] + } +} + +ten_package_test_prepare_auxiliary_resources( + "multiple_results_python_2_test_files") { + resources = [ + "//tests/ten_runtime/integration/common=>common", + "__init__.py", + "test_case.py", + ] + if (enable_sanitizer) { + resources += [ "//tests/ten_runtime/integration/tools/use_asan_lib_marker=>use_asan_lib_marker" ] + } +} + +group("multiple_results_python_2") { + deps = [ + ":multiple_results_python_2_app", + ":multiple_results_python_2_test_files", + ] +} diff --git a/tests/ten_runtime/integration/python/multiple_results_python_2/__init__.py b/tests/ten_runtime/integration/python/multiple_results_python_2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/ten_runtime/integration/python/multiple_results_python_2/multiple_results_python_2_app/manifest.json b/tests/ten_runtime/integration/python/multiple_results_python_2/multiple_results_python_2_app/manifest.json new file mode 100644 index 000000000..1cfe6ef55 --- /dev/null +++ b/tests/ten_runtime/integration/python/multiple_results_python_2/multiple_results_python_2_app/manifest.json @@ -0,0 +1,19 @@ +{ + "dependencies": [ + { + "type": "system", + "name": "ten_runtime", + "version": "0.3.0" + }, + { + "type": "extension", + "name": "simple_http_server_cpp", + "version": "0.1.0" + }, + { + "type": "extension", + "name": "default_extension_python", + "version": "0.3.0" + } + ] +} \ No newline at end of file diff --git a/tests/ten_runtime/integration/python/multiple_results_python_2/multiple_results_python_2_app/property.json b/tests/ten_runtime/integration/python/multiple_results_python_2/multiple_results_python_2_app/property.json new file mode 100644 index 000000000..a0dd94adf --- /dev/null +++ b/tests/ten_runtime/integration/python/multiple_results_python_2/multiple_results_python_2_app/property.json @@ -0,0 +1,76 @@ +{ + "_ten": { + "log_level": 2, + "predefined_graphs": [ + { + "name": "default", + "auto_start": true, + "nodes": [ + { + "type": "extension", + "name": "simple_http_server_cpp", + "addon": "simple_http_server_cpp", + "extension_group": "default_extension_group", + "property": { + "server_port": 8002 + } + }, + { + "type": "extension", + "name": "default_extension_python_1", + "addon": "default_extension_python", + "extension_group": "test1" + }, + { + "type": "extension", + "name": "default_extension_python_2", + "addon": "default_extension_python", + "extension_group": "test2" + }, + { + "type": "extension", + "name": "default_extension_python_3", + "addon": "default_extension_python", + "extension_group": "test2" + } + ], + "connections": [ + { + "extension_group": "default_extension_group", + "extension": "simple_http_server_cpp", + "cmd": [ + { + "name": "test", + "dest": [ + { + "extension_group": "test1", + "extension": "default_extension_python_1" + } + ] + } + ] + }, + { + "extension_group": "test1", + "extension": "default_extension_python_1", + "cmd": [ + { + "name": "hello", + "dest": [ + { + "extension_group": "test2", + "extension": "default_extension_python_2" + }, + { + "extension_group": "test2", + "extension": "default_extension_python_3" + } + ] + } + ] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/tests/ten_runtime/integration/python/multiple_results_python_2/multiple_results_python_2_app/ten_packages/extension/default_extension_python/extension.py b/tests/ten_runtime/integration/python/multiple_results_python_2/multiple_results_python_2_app/ten_packages/extension/default_extension_python/extension.py new file mode 100644 index 000000000..65506b382 --- /dev/null +++ b/tests/ten_runtime/integration/python/multiple_results_python_2/multiple_results_python_2_app/ten_packages/extension/default_extension_python/extension.py @@ -0,0 +1,57 @@ +# +# Copyright © 2024 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 ( + Extension, + TenEnv, + Cmd, + StatusCode, + CmdResult, +) + + +class DefaultExtension(Extension): + def __init__(self, name: str) -> None: + super().__init__(name) + self.name = name + + self.__counter = 0 + + def on_init(self, ten_env: TenEnv) -> None: + ten_env.log_debug("on_init") + ten_env.on_init_done() + + def check_hello(self, ten_env: TenEnv, result: CmdResult, receivedCmd: Cmd): + self.__counter += 1 + + if self.__counter == 1: + assert result.is_completed() is True + ten_env.log_info("receive 1 cmd result") + + respCmd = CmdResult.create(StatusCode.OK) + respCmd.set_property_string("detail", "nbnb") + ten_env.return_result(respCmd, receivedCmd) + else: + assert 0 + + def on_cmd(self, ten_env: TenEnv, cmd: Cmd) -> None: + cmd_json = cmd.to_json() + ten_env.log_debug(f"on_cmd json: {cmd_json}") + + if self.name == "default_extension_python_1": + new_cmd = Cmd.create("hello") + ten_env.send_cmd( + new_cmd, + lambda ten_env, result: self.check_hello(ten_env, result, cmd), + ) + elif self.name == "default_extension_python_2": + ten_env.log_info("create respCmd") + respCmd = CmdResult.create(StatusCode.OK) + ten_env.return_result(respCmd, cmd) + elif self.name == "default_extension_python_3": + ten_env.log_info("create respCmd") + respCmd = CmdResult.create(StatusCode.OK) + ten_env.return_result(respCmd, cmd) diff --git a/tests/ten_runtime/integration/python/multiple_results_python_2/test_case.py b/tests/ten_runtime/integration/python/multiple_results_python_2/test_case.py new file mode 100644 index 000000000..a9e872093 --- /dev/null +++ b/tests/ten_runtime/integration/python/multiple_results_python_2/test_case.py @@ -0,0 +1,134 @@ +""" +Test multiple_results_python_2. +""" + +import subprocess +import os +import sys +from sys import stdout +from .common import http + + +def http_request(): + return http.post( + "http://127.0.0.1:8002/", + { + "_ten": { + "name": "test", + }, + }, + ) + + +def test_multiple_results_python_2(): + """Test client and app server.""" + base_path = os.path.dirname(os.path.abspath(__file__)) + root_dir = os.path.join(base_path, "../../../../../") + + # Create virtual environment. + venv_dir = os.path.join(base_path, "venv") + subprocess.run([sys.executable, "-m", "venv", venv_dir]) + + my_env = os.environ.copy() + + # Set the required environment variables for the test. + my_env["PYTHONMALLOC"] = "malloc" + my_env["PYTHONDEVMODE"] = "1" + + # Launch virtual environment. + my_env["VIRTUAL_ENV"] = venv_dir + my_env["PATH"] = os.path.join(venv_dir, "bin") + os.pathsep + my_env["PATH"] + + if sys.platform == "win32": + print("test_multiple_results_python_2 doesn't support win32") + assert False + elif sys.platform == "darwin": + # client depends on some libraries in the TEN app. + my_env["DYLD_LIBRARY_PATH"] = os.path.join( + base_path, "multiple_results_python_2_app/lib" + ) + else: + # client depends on some libraries in the TEN app. + my_env["LD_LIBRARY_PATH"] = os.path.join( + base_path, "multiple_results_python_2_app/lib" + ) + + app_root_path = os.path.join(base_path, "multiple_results_python_2_app") + + tman_install_cmd = [ + os.path.join(root_dir, "ten_manager/bin/tman"), + "--config-file", + os.path.join(root_dir, "tests/local_registry/config.json"), + "install", + ] + + tman_install_process = subprocess.Popen( + tman_install_cmd, + stdout=stdout, + stderr=subprocess.STDOUT, + env=my_env, + cwd=app_root_path, + ) + tman_install_process.wait() + + bootstrap_cmd = os.path.join( + base_path, "multiple_results_python_2_app/bin/bootstrap" + ) + + bootstrap_process = subprocess.Popen( + bootstrap_cmd, stdout=stdout, stderr=subprocess.STDOUT, env=my_env + ) + bootstrap_process.wait() + + if sys.platform == "linux": + if os.path.exists(os.path.join(base_path, "use_asan_lib_marker")): + libasan_path = os.path.join( + base_path, + "multiple_results_python_2_app/ten_packages/system/ten_runtime/lib/libasan.so", + ) + + if os.path.exists(libasan_path): + my_env["LD_PRELOAD"] = libasan_path + + server_cmd = os.path.join( + base_path, "multiple_results_python_2_app/bin/start" + ) + + server = subprocess.Popen( + server_cmd, + stdout=stdout, + stderr=subprocess.STDOUT, + env=my_env, + cwd=app_root_path, + ) + + is_started = http.is_app_started("127.0.0.1", 8002, 30) + if not is_started: + print("The multiple_results_python_2 is not started after 30 seconds.") + + server.kill() + exit_code = server.wait() + print("The exit code of multiple_results_python_2: ", exit_code) + + assert exit_code == 0 + assert 0 + + return + + try: + resp = http_request() + assert resp != 500 + print(resp) + + finally: + is_stopped = http.stop_app("127.0.0.1", 8002, 30) + if not is_stopped: + print( + "The multiple_results_python_2 can not stop after 30 seconds." + ) + server.kill() + + exit_code = server.wait() + print("The exit code of multiple_results_python_2: ", exit_code) + + assert exit_code == 0 diff --git a/tests/ten_runtime/integration/python/multiple_results_python_3/BUILD.gn b/tests/ten_runtime/integration/python/multiple_results_python_3/BUILD.gn new file mode 100644 index 000000000..aab3f7d7a --- /dev/null +++ b/tests/ten_runtime/integration/python/multiple_results_python_3/BUILD.gn @@ -0,0 +1,50 @@ +# +# Copyright © 2024 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("//build/ten_runtime/feature/test.gni") +import("//build/ten_runtime/ten.gni") + +ten_package_test_prepare_app("multiple_results_python_3_app") { + src_app = "default_app_python" + src_app_language = "python" + generated_app_src_root_dir_name = "multiple_results_python_3_app" + + replace_files_after_install_app = [ + "multiple_results_python_3_app/manifest.json", + "multiple_results_python_3_app/property.json", + ] + + replace_files_after_install_all = [ "multiple_results_python_3_app/ten_packages/extension/default_extension_python/extension.py" ] + + if (ten_enable_package_manager) { + deps = [ + "//core/src/ten_manager", + "//packages/core_apps/default_app_python:upload_default_app_python_to_server", + "//packages/core_extensions/default_extension_python:upload_default_extension_python_to_server", + "//packages/example_extensions/simple_echo_cpp:upload_simple_echo_cpp_to_server", + "//packages/example_extensions/simple_http_server_cpp:upload_simple_http_server_cpp_to_server", + ] + } +} + +ten_package_test_prepare_auxiliary_resources( + "multiple_results_python_3_test_files") { + resources = [ + "//tests/ten_runtime/integration/common=>common", + "__init__.py", + "test_case.py", + ] + if (enable_sanitizer) { + resources += [ "//tests/ten_runtime/integration/tools/use_asan_lib_marker=>use_asan_lib_marker" ] + } +} + +group("multiple_results_python_3") { + deps = [ + ":multiple_results_python_3_app", + ":multiple_results_python_3_test_files", + ] +} diff --git a/tests/ten_runtime/integration/python/multiple_results_python_3/__init__.py b/tests/ten_runtime/integration/python/multiple_results_python_3/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/ten_runtime/integration/python/multiple_results_python_3/multiple_results_python_3_app/manifest.json b/tests/ten_runtime/integration/python/multiple_results_python_3/multiple_results_python_3_app/manifest.json new file mode 100644 index 000000000..1cfe6ef55 --- /dev/null +++ b/tests/ten_runtime/integration/python/multiple_results_python_3/multiple_results_python_3_app/manifest.json @@ -0,0 +1,19 @@ +{ + "dependencies": [ + { + "type": "system", + "name": "ten_runtime", + "version": "0.3.0" + }, + { + "type": "extension", + "name": "simple_http_server_cpp", + "version": "0.1.0" + }, + { + "type": "extension", + "name": "default_extension_python", + "version": "0.3.0" + } + ] +} \ No newline at end of file diff --git a/tests/ten_runtime/integration/python/multiple_results_python_3/multiple_results_python_3_app/property.json b/tests/ten_runtime/integration/python/multiple_results_python_3/multiple_results_python_3_app/property.json new file mode 100644 index 000000000..81e4fe29d --- /dev/null +++ b/tests/ten_runtime/integration/python/multiple_results_python_3/multiple_results_python_3_app/property.json @@ -0,0 +1,77 @@ +{ + "_ten": { + "log_level": 2, + "predefined_graphs": [ + { + "name": "default", + "auto_start": true, + "nodes": [ + { + "type": "extension", + "name": "simple_http_server_cpp", + "addon": "simple_http_server_cpp", + "extension_group": "default_extension_group", + "property": { + "server_port": 8002 + } + }, + { + "type": "extension", + "name": "default_extension_python_1", + "addon": "default_extension_python", + "extension_group": "test1" + }, + { + "type": "extension", + "name": "default_extension_python_2", + "addon": "default_extension_python", + "extension_group": "test2" + }, + { + "type": "extension", + "name": "default_extension_python_3", + "addon": "default_extension_python", + "extension_group": "test2" + } + ], + "connections": [ + { + "extension_group": "default_extension_group", + "extension": "simple_http_server_cpp", + "cmd": [ + { + "name": "test", + "dest": [ + { + "extension_group": "test1", + "extension": "default_extension_python_1" + } + ] + } + ] + }, + { + "extension_group": "test1", + "extension": "default_extension_python_1", + "cmd": [ + { + "name": "hello", + "result_return_policy": "each_ok_and_error", + "dest": [ + { + "extension_group": "test2", + "extension": "default_extension_python_2" + }, + { + "extension_group": "test2", + "extension": "default_extension_python_3" + } + ] + } + ] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/tests/ten_runtime/integration/python/multiple_results_python_3/multiple_results_python_3_app/ten_packages/extension/default_extension_python/extension.py b/tests/ten_runtime/integration/python/multiple_results_python_3/multiple_results_python_3_app/ten_packages/extension/default_extension_python/extension.py new file mode 100644 index 000000000..b66ab3ada --- /dev/null +++ b/tests/ten_runtime/integration/python/multiple_results_python_3/multiple_results_python_3_app/ten_packages/extension/default_extension_python/extension.py @@ -0,0 +1,60 @@ +# +# Copyright © 2024 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 ( + Extension, + TenEnv, + Cmd, + StatusCode, + CmdResult, +) + + +class DefaultExtension(Extension): + def __init__(self, name: str) -> None: + super().__init__(name) + self.name = name + + self.__counter = 0 + + def on_init(self, ten_env: TenEnv) -> None: + ten_env.log_debug("on_init") + ten_env.on_init_done() + + def check_hello(self, ten_env: TenEnv, result: CmdResult, receivedCmd: Cmd): + self.__counter += 1 + + if self.__counter == 1: + assert result.is_completed() is False + ten_env.log_info("receive 1 cmd result") + elif self.__counter == 2: + assert result.is_completed() is True + ten_env.log_info("receive 2 cmd result") + + respCmd = CmdResult.create(StatusCode.OK) + respCmd.set_property_string("detail", "nbnb") + ten_env.return_result(respCmd, receivedCmd) + else: + assert 0 + + def on_cmd(self, ten_env: TenEnv, cmd: Cmd) -> None: + cmd_json = cmd.to_json() + ten_env.log_debug(f"on_cmd json: {cmd_json}") + + if self.name == "default_extension_python_1": + new_cmd = Cmd.create("hello") + ten_env.send_cmd( + new_cmd, + lambda ten_env, result: self.check_hello(ten_env, result, cmd), + ) + elif self.name == "default_extension_python_2": + ten_env.log_info("create respCmd") + respCmd = CmdResult.create(StatusCode.OK) + ten_env.return_result(respCmd, cmd) + elif self.name == "default_extension_python_3": + ten_env.log_info("create respCmd") + respCmd = CmdResult.create(StatusCode.OK) + ten_env.return_result(respCmd, cmd) diff --git a/tests/ten_runtime/integration/python/multiple_results_python_3/test_case.py b/tests/ten_runtime/integration/python/multiple_results_python_3/test_case.py new file mode 100644 index 000000000..179ff5fd7 --- /dev/null +++ b/tests/ten_runtime/integration/python/multiple_results_python_3/test_case.py @@ -0,0 +1,134 @@ +""" +Test multiple_results_python_3. +""" + +import subprocess +import os +import sys +from sys import stdout +from .common import http + + +def http_request(): + return http.post( + "http://127.0.0.1:8002/", + { + "_ten": { + "name": "test", + }, + }, + ) + + +def test_multiple_results_python_3(): + """Test client and app server.""" + base_path = os.path.dirname(os.path.abspath(__file__)) + root_dir = os.path.join(base_path, "../../../../../") + + # Create virtual environment. + venv_dir = os.path.join(base_path, "venv") + subprocess.run([sys.executable, "-m", "venv", venv_dir]) + + my_env = os.environ.copy() + + # Set the required environment variables for the test. + my_env["PYTHONMALLOC"] = "malloc" + my_env["PYTHONDEVMODE"] = "1" + + # Launch virtual environment. + my_env["VIRTUAL_ENV"] = venv_dir + my_env["PATH"] = os.path.join(venv_dir, "bin") + os.pathsep + my_env["PATH"] + + if sys.platform == "win32": + print("test_multiple_results_python_3 doesn't support win32") + assert False + elif sys.platform == "darwin": + # client depends on some libraries in the TEN app. + my_env["DYLD_LIBRARY_PATH"] = os.path.join( + base_path, "multiple_results_python_3_app/lib" + ) + else: + # client depends on some libraries in the TEN app. + my_env["LD_LIBRARY_PATH"] = os.path.join( + base_path, "multiple_results_python_3_app/lib" + ) + + app_root_path = os.path.join(base_path, "multiple_results_python_3_app") + + tman_install_cmd = [ + os.path.join(root_dir, "ten_manager/bin/tman"), + "--config-file", + os.path.join(root_dir, "tests/local_registry/config.json"), + "install", + ] + + tman_install_process = subprocess.Popen( + tman_install_cmd, + stdout=stdout, + stderr=subprocess.STDOUT, + env=my_env, + cwd=app_root_path, + ) + tman_install_process.wait() + + bootstrap_cmd = os.path.join( + base_path, "multiple_results_python_3_app/bin/bootstrap" + ) + + bootstrap_process = subprocess.Popen( + bootstrap_cmd, stdout=stdout, stderr=subprocess.STDOUT, env=my_env + ) + bootstrap_process.wait() + + if sys.platform == "linux": + if os.path.exists(os.path.join(base_path, "use_asan_lib_marker")): + libasan_path = os.path.join( + base_path, + "multiple_results_python_3_app/ten_packages/system/ten_runtime/lib/libasan.so", + ) + + if os.path.exists(libasan_path): + my_env["LD_PRELOAD"] = libasan_path + + server_cmd = os.path.join( + base_path, "multiple_results_python_3_app/bin/start" + ) + + server = subprocess.Popen( + server_cmd, + stdout=stdout, + stderr=subprocess.STDOUT, + env=my_env, + cwd=app_root_path, + ) + + is_started = http.is_app_started("127.0.0.1", 8002, 30) + if not is_started: + print("The multiple_results_python_3 is not started after 30 seconds.") + + server.kill() + exit_code = server.wait() + print("The exit code of multiple_results_python_3: ", exit_code) + + assert exit_code == 0 + assert 0 + + return + + try: + resp = http_request() + assert resp != 500 + print(resp) + + finally: + is_stopped = http.stop_app("127.0.0.1", 8002, 30) + if not is_stopped: + print( + "The multiple_results_python_3 can not stop after 30 seconds." + ) + server.kill() + + exit_code = server.wait() + print("The exit code of multiple_results_python_3: ", exit_code) + + assert exit_code == 0 diff --git a/tests/ten_runtime/smoke/cmd_result_test/multiple_result_3.cc b/tests/ten_runtime/smoke/cmd_result_test/multiple_result_3.cc index 3de7bef5c..4c32e1d9b 100644 --- a/tests/ten_runtime/smoke/cmd_result_test/multiple_result_3.cc +++ b/tests/ten_runtime/smoke/cmd_result_test/multiple_result_3.cc @@ -150,7 +150,7 @@ TEST(CmdResultTest, MultipleResult3) { // NOLINT "extension": "test_extension_1", "cmd": [{ "name": "hello_world", - "result_return_policy": "each_immediately", + "result_return_policy": "each_ok_and_error", "dest": [{ "app": "msgpack://127.0.0.1:8001/", "extension_group": "basic_extension_group",