From fde38e8794821493bbd6682f6ff92c34cd4c8ecf Mon Sep 17 00:00:00 2001 From: Inkyun Kil Date: Tue, 19 Sep 2023 10:26:49 +0900 Subject: [PATCH] Improves code coverage Change-Id: Ife7d246d0b337177dbef86ae48866555e192f8ce Signed-off-by: Inkyun Kil --- mock/pkgmgr_info_mock.cc | 44 +++++++++++++++++++ mock/pkgmgr_info_mock.h | 42 ++++++++++++++++++ src/message_port_common.c | 10 +++++ src/message_port_local.c | 35 +++++++++++++++ src/message_port_remote.c | 22 ++++++++++ test/unit_tests/test_message_port.cc | 85 ++++++++++++++++++++++++++---------- 6 files changed, 215 insertions(+), 23 deletions(-) create mode 100644 mock/pkgmgr_info_mock.cc create mode 100644 mock/pkgmgr_info_mock.h diff --git a/mock/pkgmgr_info_mock.cc b/mock/pkgmgr_info_mock.cc new file mode 100644 index 0000000..bddab73 --- /dev/null +++ b/mock/pkgmgr_info_mock.cc @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "pkgmgr_info_mock.h" + +#include "mock_hook.h" +#include "test_fixture.h" + +extern "C" int pkgmgrinfo_appinfo_get_usr_appinfo(const char* arg1, uid_t arg2, + pkgmgrinfo_appinfo_h* arg3) { + return MOCK_HOOK_P3(PkgmgrInfoMock, pkgmgrinfo_appinfo_get_usr_appinfo, + arg1, arg2, arg3); +} + +extern "C" int pkgmgrinfo_appinfo_is_preload( + pkgmgrinfo_appinfo_h arg1, bool* arg2) { + return MOCK_HOOK_P2(PkgmgrInfoMock, pkgmgrinfo_appinfo_is_preload, arg1, arg2); +} + +extern "C" int pkgmgrinfo_appinfo_destroy_appinfo(pkgmgrinfo_appinfo_h arg1) { + return MOCK_HOOK_P1(PkgmgrInfoMock, pkgmgrinfo_appinfo_destroy_appinfo, + arg1); +} + +extern "C" int pkgmgrinfo_pkginfo_compare_usr_app_cert_info( + const char* arg1, const char* arg2, uid_t arg3, + pkgmgrinfo_cert_compare_result_type_e* arg4) { + return MOCK_HOOK_P4(PkgmgrInfoMock, pkgmgrinfo_pkginfo_compare_usr_app_cert_info, + arg1, arg2, arg3, arg4); +} \ No newline at end of file diff --git a/mock/pkgmgr_info_mock.h b/mock/pkgmgr_info_mock.h new file mode 100644 index 0000000..2c4b586 --- /dev/null +++ b/mock/pkgmgr_info_mock.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MOCK_PKGMGR_INFO_MOCK_H_ +#define MOCK_PKGMGR_INFO_MOCK_H_ + +#include +#include +#include + +#include "module_mock.h" + +class PkgmgrInfoMock : public virtual ModuleMock { + public: + virtual ~PkgmgrInfoMock() {} + + MOCK_METHOD1(pkgmgrinfo_appinfo_destroy_appinfo, + int(pkgmgrinfo_appinfo_h)); + MOCK_METHOD2(pkgmgrinfo_appinfo_is_preload, + int(pkgmgrinfo_appinfo_h, bool*)); + MOCK_METHOD3(pkgmgrinfo_appinfo_get_usr_appinfo, + int(const char*, uid_t, pkgmgrinfo_appinfo_h*)); + MOCK_METHOD4(pkgmgrinfo_pkginfo_compare_usr_app_cert_info, + int(const char*, const char*, uid_t, + pkgmgrinfo_cert_compare_result_type_e*)); + +}; + +#endif // MOCK_PKGMGR_INFO_MOCK_H_ diff --git a/src/message_port_common.c b/src/message_port_common.c index 2230e6b..cf515b1 100644 --- a/src/message_port_common.c +++ b/src/message_port_common.c @@ -175,8 +175,10 @@ int read_string_from_socket(int fd, char **buffer, int *string_len) if (*string_len > 0 && *string_len < MAX_MESSAGE_SIZE) { *buffer = (char *)calloc(*string_len, sizeof(char)); if (*buffer == NULL) { +/* LCOV_EXCL_START */ LOGE("Out of memory."); return MESSAGE_PORT_ERROR_IO_ERROR; +/* LCOV_EXCL_STOP */ } if (read_socket(fd, *buffer, *string_len, &nb) != MESSAGE_PORT_ERROR_NONE) { LOGE("read socket fail"); @@ -307,8 +309,10 @@ char *get_encoded_name(const char *remote_app_id, const char *port_name, bool is int bus_name_len = strlen(remote_app_id) + strlen(port_name) + 2; char *bus_name = (char *)calloc(bus_name_len, sizeof(char)); if (bus_name == NULL) { +/* LCOV_EXCL_START */ _LOGE("bus_name calloc failed"); return 0; +/* LCOV_EXCL_STOP */ } snprintf(bus_name, bus_name_len, "%s_%s", remote_app_id, port_name); @@ -316,11 +320,13 @@ char *get_encoded_name(const char *remote_app_id, const char *port_name, bool is // MD5_Init mdctx = EVP_MD_CTX_new(); if (mdctx == NULL) { +/* LCOV_EXCL_START */ if (bus_name) free(bus_name); _LOGE("EVP_MD_CTX_new failed!!"); return 0; +/* LCOV_EXCL_STOP */ } EVP_DigestInit_ex(mdctx, EVP_md5(), NULL); @@ -334,9 +340,11 @@ char *get_encoded_name(const char *remote_app_id, const char *port_name, bool is unsigned char *c = (unsigned char *)calloc(md5_digest_len, sizeof(unsigned char)); if (c == NULL) { +/* LCOV_EXCL_START */ _LOGE("calloc failed!!"); EVP_MD_CTX_free(mdctx); return 0; +/* LCOV_EXCL_STOP */ } EVP_DigestFinal_ex(mdctx, c, &md5_digest_len); @@ -344,11 +352,13 @@ char *get_encoded_name(const char *remote_app_id, const char *port_name, bool is md5_digest = (char *)calloc(encoded_bus_name_len, sizeof(char)); if (md5_digest == NULL) { +/* LCOV_EXCL_START */ if (c) free(c); _LOGE("md5_digest calloc failed!!"); return 0; +/* LCOV_EXCL_STOP */ } snprintf(md5_digest, encoded_bus_name_len, "%s", MESSAGEPORT_BUS_NAME_PREFIX); diff --git a/src/message_port_local.c b/src/message_port_local.c index af75486..f7fcce9 100644 --- a/src/message_port_local.c +++ b/src/message_port_local.c @@ -288,22 +288,29 @@ static port_list_info_s *__set_remote_port_info(const char *remote_app_id, const port_list_info_s *port_info = (port_list_info_s *)calloc(1, sizeof(port_list_info_s)); if (!port_info) { +/* LCOV_EXCL_START */ ret_val = MESSAGE_PORT_ERROR_OUT_OF_MEMORY; goto out; +/* LCOV_EXCL_STOP */ } port_info->port_name = strdup(remote_port); if (!port_info->port_name) { +/* LCOV_EXCL_START */ ret_val = MESSAGE_PORT_ERROR_OUT_OF_MEMORY; goto out; +/* LCOV_EXCL_STOP */ } port_info->is_trusted = is_trusted; port_info->encoded_bus_name = get_encoded_name(remote_app_id, remote_port, is_trusted); if (port_info->encoded_bus_name == NULL) { +/* LCOV_EXCL_START */ ret_val = MESSAGE_PORT_ERROR_OUT_OF_MEMORY; goto out; +/* LCOV_EXCL_STOP */ } port_info->send_sock_fd = 0; out: +/* LCOV_EXCL_START */ if (ret_val != MESSAGE_PORT_ERROR_NONE) { if (port_info) { FREE_AND_NULL(port_info->port_name); @@ -312,6 +319,7 @@ out: } return NULL; } +/* LCOV_EXCL_STOP */ return port_info; } @@ -322,14 +330,18 @@ static message_port_remote_app_info_s *__set_remote_app_info(const char *remote_ remote_app_info = (message_port_remote_app_info_s *)calloc(1, sizeof(message_port_remote_app_info_s)); if (!remote_app_info) { +/* LCOV_EXCL_START */ ret_val = MESSAGE_PORT_ERROR_OUT_OF_MEMORY; goto out; +/* LCOV_EXCL_STOP */ } remote_app_info->remote_app_id = strdup(remote_app_id); if (remote_app_info->remote_app_id == NULL) { +/* LCOV_EXCL_START */ ret_val = MESSAGE_PORT_ERROR_OUT_OF_MEMORY; goto out; +/* LCOV_EXCL_STOP */ } out: @@ -418,22 +430,27 @@ static int __create_port_key_info( port_key_info_s *_key_info = (port_key_info_s *) calloc(1, sizeof(port_key_info_s)); if (_key_info == NULL) { +/* LCOV_EXCL_START */ ret_val = MESSAGE_PORT_ERROR_OUT_OF_MEMORY; _LOGE("out of memory"); goto out; +/* LCOV_EXCL_STOP */ } _key_info->port_name = strdup(port_info->port_name); if (_key_info->port_name == NULL) { +/* LCOV_EXCL_START */ ret_val = MESSAGE_PORT_ERROR_OUT_OF_MEMORY; _LOGE("out of memory"); goto out; +/* LCOV_EXCL_STOP */ } _key_info->is_trusted = port_info->is_trusted; _key_info->remote_app_id = strdup(port_info->remote_app_info->remote_app_id); if (_key_info->remote_app_id == NULL) { +/* LCOV_EXCL_START */ ret_val = MESSAGE_PORT_ERROR_OUT_OF_MEMORY; _LOGE("out of memory"); goto out; @@ -449,6 +466,8 @@ out: free(_key_info); } } +/* LCOV_EXCL_STOP */ + return ret_val; } @@ -465,8 +484,10 @@ static int __get_remote_port_info(const char *remote_app_id, const char *remote_ remote_app_info = __set_remote_app_info(remote_app_id, remote_port, is_trusted); if (remote_app_info == NULL) { +/* LCOV_EXCL_START */ ret_val = MESSAGE_PORT_ERROR_OUT_OF_MEMORY; goto out; +/* LCOV_EXCL_STOP */ } g_hash_table_insert(__remote_app_info, remote_app_info->remote_app_id, remote_app_info); } @@ -474,8 +495,10 @@ static int __get_remote_port_info(const char *remote_app_id, const char *remote_ port_info.port_name = strdup(remote_port); if (port_info.port_name == NULL) { +/* LCOV_EXCL_START */ ret_val = MESSAGE_PORT_ERROR_OUT_OF_MEMORY; goto out; +/* LCOV_EXCL_STOP */ } port_info.is_trusted = is_trusted; cb_list = g_list_find_custom(remote_app_info->port_list, &port_info, @@ -485,8 +508,10 @@ static int __get_remote_port_info(const char *remote_app_id, const char *remote_ if (cb_list == NULL) { port_list_info_s *tmp = __set_remote_port_info(remote_app_id, remote_port, is_trusted); if (tmp == NULL) { +/* LCOV_EXCL_START */ ret_val = MESSAGE_PORT_ERROR_OUT_OF_MEMORY; goto out; +/* LCOV_EXCL_STOP */ } remote_app_info->port_list = g_list_append(remote_app_info->port_list, tmp); tmp->remote_app_info = remote_app_info; @@ -835,9 +860,11 @@ static int __push_delayed_message(port_list_info_s *port_info, message->local_port_len = tmp_size; message->local_port_name = strdup(local_port); if (message->local_port_name == NULL) { +/* LCOV_EXCL_START */ _LOGE("local_port_name strdup fail"); ret = MESSAGE_PORT_ERROR_OUT_OF_MEMORY; goto out; +/* LCOV_EXCL_STOP */ } message->is_bidirection = is_bidirection; message->local_trusted = local_trusted; @@ -845,9 +872,11 @@ static int __push_delayed_message(port_list_info_s *port_info, message->data_len = data_len; message->data = (bundle_raw *)strdup((const char *)kb_data); if (message->data == NULL) { +/* LCOV_EXCL_START */ _LOGE("data strdup fail"); ret = MESSAGE_PORT_ERROR_OUT_OF_MEMORY; goto out; +/* LCOV_EXCL_STOP */ } @@ -860,9 +889,11 @@ static int __push_delayed_message(port_list_info_s *port_info, if (port_info->delay_src_id == 0) { delay_info = (delay_port_info *)calloc(1, sizeof(delay_port_info)); if (delay_info == NULL) { +/* LCOV_EXCL_START */ _LOGE("out of memory"); ret = MESSAGE_PORT_ERROR_OUT_OF_MEMORY; goto out; +/* LCOV_EXCL_STOP */ } ret = __create_port_key_info(port_info, &delay_info->key_info); @@ -1227,16 +1258,20 @@ int watch_remote_port(int *watcher_id, const char *remote_app_id, const char *re registered_cb_info->user_data = user_data; registered_cb_info->remote_app_id = strdup(remote_app_info->remote_app_id); if (registered_cb_info->remote_app_id == NULL) { +/* LCOV_EXCL_START */ free(registered_cb_info); _LOGE("Failed to alloc memory"); return MESSAGE_PORT_ERROR_OUT_OF_MEMORY; +/* LCOV_EXCL_STOP */ } registered_cb_info->remote_port = strdup(port_info->port_name); if (registered_cb_info->remote_port == NULL) { +/* LCOV_EXCL_START */ free(registered_cb_info->remote_app_id); free(registered_cb_info); _LOGE("Failed to alloc memory"); return MESSAGE_PORT_ERROR_OUT_OF_MEMORY; +/* LCOV_EXCL_STOP */ } registered_cb_info->watcher_id = g_bus_watch_name_on_connection( diff --git a/src/message_port_remote.c b/src/message_port_remote.c index 0b3debe..c18dc48 100644 --- a/src/message_port_remote.c +++ b/src/message_port_remote.c @@ -250,8 +250,10 @@ static message_port_pkt_s *__message_port_recv_raw(int fd) pkt = (message_port_pkt_s *)calloc(sizeof(message_port_pkt_s), 1); if (pkt == NULL) { +/* LCOV_EXCL_START */ close(fd); return NULL; +/* LCOV_EXCL_STOP */ } if (read_string_from_socket(fd, (char **)&pkt->remote_port_name, &pkt->remote_port_name_len) != MESSAGE_PORT_ERROR_NONE) { @@ -439,8 +441,10 @@ static callback_key_info_s *__create_callback_key_info(message_port_callback_inf calloc(1, sizeof(callback_key_info_s)); if (_key_info == NULL) { +/* LCOV_EXCL_START */ _LOGE("out of memory"); return NULL; +/* LCOV_EXCL_STOP */ } _key_info->local_id = callback_info->local_id; @@ -457,31 +461,39 @@ static message_port_callback_info_s *__create_callback_info(message_port_local_p callback_info = (message_port_callback_info_s *)calloc(1, sizeof(message_port_callback_info_s)); if (callback_info == NULL) { +/* LCOV_EXCL_START */ _LOGE("out of memory"); return NULL; +/* LCOV_EXCL_STOP */ } local_info = (message_port_local_port_info_s *)calloc(1, sizeof(message_port_local_port_info_s)); if (local_info == NULL) { +/* LCOV_EXCL_START */ ret = false; _LOGE("out of memory"); goto out; +/* LCOV_EXCL_STOP */ } callback_info->local_id = mi->local_id; callback_info->local_info = local_info; callback_info->remote_app_id = strdup(local_appid); if (callback_info->remote_app_id == NULL) { +/* LCOV_EXCL_START */ ret = false; _LOGE("out of memory"); goto out; +/* LCOV_EXCL_STOP */ } local_info->port_name = strdup(mi->port_name); if (local_info->port_name == NULL) { +/* LCOV_EXCL_START */ ret = false; _LOGE("out of memory"); goto out; +/* LCOV_EXCL_STOP */ } local_info->callback = mi->callback; @@ -507,8 +519,10 @@ static bool __callback_info_append(message_port_callback_info_s *callback_info) if (callback_info_list == NULL) { head_callback_info = (message_port_callback_info_s *)calloc(1, sizeof(message_port_callback_info_s)); if (head_callback_info == NULL) { +/* LCOV_EXCL_START */ _LOGE("fail to alloc head_callback_info"); return false; +/* LCOV_EXCL_STOP */ } head_callback_info->local_id = 0; head_callback_info->remote_app_id = NULL; @@ -780,17 +794,21 @@ static bool __check_sender_validation(GVariant *parameters, const char *sender, _LOGD("insert sender !!!!! %s", sender); _sender = strdup(sender); if (_sender == NULL) { +/* LCOV_EXCL_START */ _LOGE("out of memory"); free(watcher_id); return false; +/* LCOV_EXCL_STOP */ } _appid = strdup(local_appid); if (_appid == NULL) { +/* LCOV_EXCL_START */ _LOGE("out of memory"); free(watcher_id); free(_sender); return false; +/* LCOV_EXCL_STOP */ } g_hash_table_insert(__sender_appid_hash, (gpointer)_sender, (gpointer)_appid); @@ -903,8 +921,10 @@ static int __register_dbus_interface(const char *port_name, bool is_trusted) introspection_xml = (char *)calloc(introspection_xml_len, sizeof(char)); if (!introspection_xml) { +/* LCOV_EXCL_START */ _LOGE("out of memory"); goto out; +/* LCOV_EXCL_STOP */ } @@ -979,9 +999,11 @@ static bool __message_port_register_port(const int local_id, const char *local_p mi->is_trusted = is_trusted; mi->port_name = strdup(local_port); if (mi->port_name == NULL) { +/* LCOV_EXCL_START */ _LOGE("Malloc failed (%s)", local_port); free(mi); return false; +/* LCOV_EXCL_STOP */ } mi->local_id = local_id; mi->user_data = user_data; diff --git a/test/unit_tests/test_message_port.cc b/test/unit_tests/test_message_port.cc index 425dbe9..f06938b 100644 --- a/test/unit_tests/test_message_port.cc +++ b/test/unit_tests/test_message_port.cc @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include @@ -92,27 +92,6 @@ extern "C" gint g_unix_fd_list_append(GUnixFDList* list, return 0; } -extern "C" int pkgmgrinfo_appinfo_get_usr_appinfo(const char* appid, uid_t uid, - pkgmgrinfo_appinfo_h* handle) { - return 0; -} - -extern "C" int pkgmgrinfo_appinfo_is_preload( - pkgmgrinfo_appinfo_h handle, bool* preload) { - *preload = false; - return 0; -} - -extern "C" int pkgmgrinfo_appinfo_destroy_appinfo(pkgmgrinfo_appinfo_h handle) { - return 0; -} - -extern "C" int pkgmgrinfo_pkginfo_compare_usr_app_cert_info( - const char* lhs_app_id, const char* rhs_app_id, uid_t uid, - pkgmgrinfo_cert_compare_result_type_e* compare_result) { - *compare_result = PMINFO_CERT_COMPARE_MATCH; - return 0; -} GVariant* __call_sync_reply; extern "C" GVariant* g_dbus_connection_call_sync( @@ -126,7 +105,8 @@ extern "C" GVariant* g_dbus_connection_call_sync( return g_variant_new("(u)", 1); } -class Mocks : public ::testing::NiceMock {}; +class Mocks : public ::testing::NiceMock, + public ::testing::NiceMock {}; class MessagePortTest : public TestFixture { public: @@ -483,6 +463,17 @@ TEST_F(MessagePortTest, message_port_send_trusted_message_n) { EXPECT_EQ(ret, MESSAGE_PORT_ERROR_INVALID_PARAMETER); } +TEST_F(MessagePortTest, message_port_send_trusted_message_n2) { + EXPECT_CALL(GetMock(), + g_dbus_message_new_method_call(_, _, _, _)). + WillOnce(Return(nullptr)); + + tizen_base::Bundle message; + int ret = message_port_send_trusted_message( + "test", "PORT", message.GetHandle()); + EXPECT_EQ(ret, MESSAGE_PORT_ERROR_OUT_OF_MEMORY); +} + TEST_F(MessagePortTest, message_port_unregister_local_port) { int ret = message_port_unregister_local_port(1); EXPECT_EQ(ret, MESSAGE_PORT_ERROR_NONE); @@ -505,12 +496,14 @@ TEST_F(MessagePortTest, message_port_unregister_trusted_local_port_n) { TEST_F(MessagePortTest, message_port_check_remote_port) { bool exist; + int ret = message_port_check_remote_port("test", "test", &exist); EXPECT_EQ(ret, MESSAGE_PORT_ERROR_NONE); } TEST_F(MessagePortTest, message_port_check_remote_port_n) { bool exist; + int ret = message_port_check_remote_port(nullptr, "test", &exist); EXPECT_EQ(ret, MESSAGE_PORT_ERROR_INVALID_PARAMETER); } @@ -527,9 +520,55 @@ TEST_F(MessagePortTest, message_port_check_trusted_remote_port) { } TEST_F(MessagePortTest, message_port_check_trusted_remote_port_n) { + EXPECT_CALL(GetMock(), + pkgmgrinfo_appinfo_get_usr_appinfo(_, _, _)). + Times(8). + WillOnce(Return(1)). + WillOnce(Return(0)). + WillOnce(Return(0)). + WillOnce(Return(1)). + WillOnce(Return(0)). + WillOnce(Return(0)). + WillOnce(Return(0)). + WillOnce(Return(0)); + EXPECT_CALL(GetMock(), + pkgmgrinfo_appinfo_is_preload(_, _)). + Times(6). + WillOnce(Return(1)). + WillOnce(Return(0)). + WillOnce(Return(0)). + WillOnce(Return(1)). + WillOnce(DoAll(SetArgPointee<1>(true), + Return(0))). + WillOnce(DoAll(SetArgPointee<1>(true), + Return(0))); + EXPECT_CALL(GetMock(), + pkgmgrinfo_pkginfo_compare_usr_app_cert_info(_, _, _, _)). + WillRepeatedly(Return(-1)); bool exist; int ret = message_port_check_trusted_remote_port(nullptr, "PORT", &exist); EXPECT_EQ(ret, MESSAGE_PORT_ERROR_INVALID_PARAMETER); + + //For code coverage + __call_sync_reply = g_variant_new("(b)", true); + ret = message_port_check_trusted_remote_port("test3", "PORT", &exist); + EXPECT_EQ(ret, MESSAGE_PORT_ERROR_CERTIFICATE_NOT_MATCH); + + __call_sync_reply = g_variant_new("(b)", true); + ret = message_port_check_trusted_remote_port("test4", "PORT", &exist); + EXPECT_EQ(ret, MESSAGE_PORT_ERROR_CERTIFICATE_NOT_MATCH); + + __call_sync_reply = g_variant_new("(b)", true); + ret = message_port_check_trusted_remote_port("test5", "PORT", &exist); + EXPECT_EQ(ret, MESSAGE_PORT_ERROR_CERTIFICATE_NOT_MATCH); + + __call_sync_reply = g_variant_new("(b)", true); + ret = message_port_check_trusted_remote_port("test6", "PORT", &exist); + EXPECT_EQ(ret, MESSAGE_PORT_ERROR_CERTIFICATE_NOT_MATCH); + + __call_sync_reply = g_variant_new("(b)", true); + ret = message_port_check_trusted_remote_port("test7", "PORT", &exist); + EXPECT_EQ(ret, MESSAGE_PORT_ERROR_NONE); } static void __registration_event_cb(const char* remote_app_id, -- 2.7.4