--- /dev/null
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * 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 <gtest/gtest.h>
+#include <string.h>
+
+#include <cstdint>
+#include <map>
+#include <memory>
+#include <string>
+
+#include "bta/gatt/bta_gattc_int.h"
+#include "common/message_loop_thread.h"
+#include "stack/gatt/gatt_int.h"
+
+// TODO put this in common place
+extern std::map<std::string, int> mock_function_count_map;
+
+namespace param {
+struct {
+ uint16_t conn_id;
+ tGATT_STATUS status;
+ uint16_t handle;
+ uint16_t len;
+ uint8_t* value;
+ void* data;
+} bta_gatt_read_complete_callback;
+} // namespace param
+void bta_gatt_read_complete_callback(uint16_t conn_id, tGATT_STATUS status,
+ uint16_t handle, uint16_t len,
+ uint8_t* value, void* data) {
+ param::bta_gatt_read_complete_callback.conn_id = conn_id;
+ param::bta_gatt_read_complete_callback.status = status;
+ param::bta_gatt_read_complete_callback.handle = handle;
+ param::bta_gatt_read_complete_callback.len = len;
+ param::bta_gatt_read_complete_callback.value = value;
+ param::bta_gatt_read_complete_callback.data = data;
+}
+
+namespace param {
+struct {
+ uint16_t conn_id;
+ tGATT_STATUS status;
+ uint16_t handle;
+ void* data;
+} bta_gatt_write_complete_callback;
+} // namespace param
+
+void bta_gatt_write_complete_callback(uint16_t conn_id, tGATT_STATUS status,
+ uint16_t handle, void* data) {
+ param::bta_gatt_write_complete_callback.conn_id = conn_id;
+ param::bta_gatt_write_complete_callback.status = status;
+ param::bta_gatt_write_complete_callback.handle = handle;
+ param::bta_gatt_write_complete_callback.data = data;
+}
+
+namespace param {
+struct {
+ uint16_t conn_id;
+ tGATT_STATUS status;
+ void* data;
+} bta_gatt_configure_mtu_complete_callback;
+} // namespace param
+
+void bta_gatt_configure_mtu_complete_callback(uint16_t conn_id,
+ tGATT_STATUS status, void* data) {
+ param::bta_gatt_configure_mtu_complete_callback.conn_id = conn_id;
+ param::bta_gatt_configure_mtu_complete_callback.status = status;
+ param::bta_gatt_configure_mtu_complete_callback.data = data;
+}
+
+namespace param {
+struct {
+ tBTA_GATTC_EVT event;
+ tBTA_GATTC* p_data;
+} bta_gattc_event_complete_callback;
+} // namespace param
+
+void bta_gattc_event_complete_callback(tBTA_GATTC_EVT event,
+ tBTA_GATTC* p_data) {
+ param::bta_gattc_event_complete_callback.event = event;
+ param::bta_gattc_event_complete_callback.p_data = p_data;
+}
+
+class BtaGattTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ mock_function_count_map.clear();
+ param::bta_gatt_read_complete_callback = {};
+ param::bta_gatt_write_complete_callback = {};
+ param::bta_gatt_configure_mtu_complete_callback = {};
+ param::bta_gattc_event_complete_callback = {};
+ }
+
+ void TearDown() override {}
+
+ tBTA_GATTC_RCB app_control_block = {
+ .p_cback = bta_gattc_event_complete_callback,
+ };
+
+ tGATT_CL_COMPLETE gatt_cl_complete = {
+ .att_value =
+ {
+ .conn_id = 1,
+ .handle = 2,
+ .offset = 3,
+ .auth_req = GATT_AUTH_REQ_NONE,
+ .value = {10, 11, 12, 13},
+ .len = 4, // length of value above
+ },
+ };
+
+ tBTA_GATTC_SERV service_control_block = {
+ .mtu = 456,
+ };
+ tBTA_GATTC_DATA command_queue;
+
+ tBTA_GATTC_CLCB client_channel_control_block = {
+ .p_q_cmd = &command_queue,
+ .p_rcb = &app_control_block,
+ .p_srcb = &service_control_block,
+ .bta_conn_id = 456,
+ };
+};
+
+TEST_F(BtaGattTest, bta_gattc_op_cmpl_read) {
+ command_queue = {
+ .api_read = // tBTA_GATTC_API_READ
+ {
+ .hdr =
+ {
+ .event = BTA_GATTC_API_READ_EVT,
+ },
+ .handle = 123,
+ .read_cb = bta_gatt_read_complete_callback,
+ .read_cb_data = static_cast<void*>(this),
+ },
+ };
+
+ client_channel_control_block.p_q_cmd = &command_queue;
+
+ tBTA_GATTC_DATA data = {
+ .op_cmpl =
+ {
+ .op_code = GATTC_OPTYPE_READ,
+ .status = GATT_OUT_OF_RANGE,
+ .p_cmpl = &gatt_cl_complete,
+ },
+ };
+
+ bta_gattc_op_cmpl(&client_channel_control_block, &data);
+ ASSERT_EQ(1, mock_function_count_map["osi_free_and_reset"]);
+ ASSERT_EQ(456, param::bta_gatt_read_complete_callback.conn_id);
+ ASSERT_EQ(GATT_OUT_OF_RANGE, param::bta_gatt_read_complete_callback.status);
+ ASSERT_EQ(123, param::bta_gatt_read_complete_callback.handle);
+ ASSERT_EQ(4, param::bta_gatt_read_complete_callback.len);
+ ASSERT_EQ(10, param::bta_gatt_read_complete_callback.value[0]);
+ ASSERT_EQ(this, param::bta_gatt_read_complete_callback.data);
+}
+
+TEST_F(BtaGattTest, bta_gattc_op_cmpl_write) {
+ command_queue = {
+ .api_write = // tBTA_GATTC_API_WRITE
+ {
+ .hdr =
+ {
+ .event = BTA_GATTC_API_WRITE_EVT,
+ },
+ .handle = 123,
+ .write_cb = bta_gatt_write_complete_callback,
+ .write_cb_data = static_cast<void*>(this),
+ },
+ };
+
+ client_channel_control_block.p_q_cmd = &command_queue;
+
+ tBTA_GATTC_DATA data = {
+ .op_cmpl =
+ {
+ .op_code = GATTC_OPTYPE_WRITE,
+ .status = GATT_OUT_OF_RANGE,
+ .p_cmpl = &gatt_cl_complete,
+ },
+ };
+
+ bta_gattc_op_cmpl(&client_channel_control_block, &data);
+ ASSERT_EQ(1, mock_function_count_map["osi_free_and_reset"]);
+ ASSERT_EQ(456, param::bta_gatt_write_complete_callback.conn_id);
+ ASSERT_EQ(2, param::bta_gatt_write_complete_callback.handle);
+ ASSERT_EQ(GATT_OUT_OF_RANGE, param::bta_gatt_write_complete_callback.status);
+ ASSERT_EQ(this, param::bta_gatt_write_complete_callback.data);
+}
+
+TEST_F(BtaGattTest, bta_gattc_op_cmpl_config) {
+ command_queue = {
+ .api_mtu = // tBTA_GATTC_API_CFG_MTU
+ {
+ .hdr =
+ {
+ .event = BTA_GATTC_API_CFG_MTU_EVT,
+ },
+ .mtu_cb = bta_gatt_configure_mtu_complete_callback,
+ .mtu_cb_data = static_cast<void*>(this),
+ },
+ };
+
+ client_channel_control_block.p_q_cmd = &command_queue;
+
+ tBTA_GATTC_DATA data = {
+ .op_cmpl =
+ {
+ .op_code = GATTC_OPTYPE_CONFIG,
+ .status = GATT_PRC_IN_PROGRESS,
+ },
+ };
+
+ bta_gattc_op_cmpl(&client_channel_control_block, &data);
+ ASSERT_EQ(1, mock_function_count_map["osi_free_and_reset"]);
+ ASSERT_EQ(456, param::bta_gatt_configure_mtu_complete_callback.conn_id);
+
+ ASSERT_EQ(GATT_PRC_IN_PROGRESS,
+ param::bta_gatt_configure_mtu_complete_callback.status);
+ ASSERT_EQ(this, param::bta_gatt_configure_mtu_complete_callback.data);
+}
+
+TEST_F(BtaGattTest, bta_gattc_op_cmpl_execute) {
+ command_queue = {
+ .api_exec = // tBTA_GATTC_API_EXEC
+ {
+ .hdr =
+ {
+ .event = BTA_GATTC_API_EXEC_EVT,
+ },
+ },
+ };
+
+ client_channel_control_block.p_q_cmd = &command_queue;
+
+ tBTA_GATTC_DATA data = {
+ .op_cmpl =
+ {
+ .op_code = GATTC_OPTYPE_EXE_WRITE,
+ },
+ };
+
+ bta_gattc_op_cmpl(&client_channel_control_block, &data);
+ ASSERT_EQ(BTA_GATTC_EXEC_EVT, param::bta_gattc_event_complete_callback.event);
+ ASSERT_EQ(1, mock_function_count_map["osi_free_and_reset"]);
+}