toy-hardware/main/bluetooth_provisioning_test.cc

499 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @file bluetooth_provisioning_test.cc
* @brief 蓝牙配网功能测试文件
* @author AI Assistant
* @date 2024-01-01
*/
#include "bluetooth_provisioning.h"
#include "bluetooth_provisioning_config.h"
#include <cstring>
#include <cassert>
#include <iostream>
#include <chrono>
#include <thread>
// 为了避免IDE环境中的头文件错误使用条件编译
#ifdef ESP_PLATFORM
#include "esp_wifi.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
#else
// 在非ESP环境中定义必要的宏和类型
#define ESP_LOGI(tag, format, ...) printf("[INFO][%s] " format "\n", tag, ##__VA_ARGS__)
#define ESP_LOGE(tag, format, ...) printf("[ERROR][%s] " format "\n", tag, ##__VA_ARGS__)
#define ESP_LOGW(tag, format, ...) printf("[WARN][%s] " format "\n", tag, ##__VA_ARGS__)
#define pdMS_TO_TICKS(ms) (ms)
typedef void* TaskHandle_t;
void vTaskDelay(int ticks) { std::this_thread::sleep_for(std::chrono::milliseconds(ticks)); }
void vTaskDelete(TaskHandle_t task) { (void)task; }
int xTaskCreate(void (*task_func)(void*), const char* name, int stack_size, void* params, int priority, TaskHandle_t* handle) {
(void)task_func; (void)name; (void)stack_size; (void)params; (void)priority; (void)handle;
return 1; // 模拟成功
}
#endif
#define TAG "BluetoothProvisioningTest"
// 测试事件组
static EventGroupHandle_t test_event_group = nullptr;
#define TEST_WIFI_CONNECTED_BIT BIT0
#define TEST_WIFI_FAILED_BIT BIT1
#define TEST_BT_CONNECTED_BIT BIT2
#define TEST_TIMEOUT_BIT BIT3
// 测试结果统计
static struct {
int total_tests;
int passed_tests;
int failed_tests;
} test_stats = {0, 0, 0};
/**
* @brief 测试事件回调函数
*/
void test_provisioning_callback(BluetoothProvisioningEvent event, void* data) {
switch (event) {
case BluetoothProvisioningEvent::STATE_CHANGED:
ESP_LOGI(TAG, "[测试] 配网状态变更");
break;
case BluetoothProvisioningEvent::CLIENT_CONNECTED:
ESP_LOGI(TAG, "[测试] 蓝牙客户端已连接");
xEventGroupSetBits(test_event_group, TEST_BT_CONNECTED_BIT);
break;
case BluetoothProvisioningEvent::CLIENT_DISCONNECTED:
ESP_LOGI(TAG, "[测试] 蓝牙客户端已断开");
xEventGroupClearBits(test_event_group, TEST_BT_CONNECTED_BIT);
break;
case BluetoothProvisioningEvent::WIFI_CREDENTIALS: {
WiFiCredentials* credentials = static_cast<WiFiCredentials*>(data);
ESP_LOGI(TAG, "[测试] 收到WiFi凭据: SSID=%s", credentials->ssid.c_str());
break;
}
case BluetoothProvisioningEvent::WIFI_CONNECTED: {
esp_ip4_addr_t* ip = static_cast<esp_ip4_addr_t*>(data);
ESP_LOGI(TAG, "[测试] WiFi连接成功: IP=" IPSTR, IP2STR(ip));
xEventGroupSetBits(test_event_group, TEST_WIFI_CONNECTED_BIT);
break;
}
case BluetoothProvisioningEvent::WIFI_FAILED: {
uint8_t* reason = static_cast<uint8_t*>(data);
ESP_LOGE(TAG, "[测试] WiFi连接失败: 原因=%d", *reason);
xEventGroupSetBits(test_event_group, TEST_WIFI_FAILED_BIT);
break;
}
default:
ESP_LOGD(TAG, "[测试] 未处理的事件: %d", static_cast<int>(event));
break;
}
}
/**
* @brief 测试辅助函数 - 记录测试结果
*/
void record_test_result(const char* test_name, bool passed) {
test_stats.total_tests++;
if (passed) {
test_stats.passed_tests++;
ESP_LOGI(TAG, "✅ [测试通过] %s", test_name);
} else {
test_stats.failed_tests++;
ESP_LOGE(TAG, "❌ [测试失败] %s", test_name);
}
}
/**
* @brief 测试1基本初始化和反初始化
*/
bool test_basic_initialization() {
ESP_LOGI(TAG, "开始测试:基本初始化和反初始化");
BluetoothProvisioning* prov = new BluetoothProvisioning();
if (!prov) {
return false;
}
// 测试初始化
bool init_result = prov->Initialize();
if (!init_result) {
delete prov;
return false;
}
// 检查初始状态
BluetoothProvisioningState state = prov->GetState();
if (state != BluetoothProvisioningState::IDLE) {
prov->Deinitialize();
delete prov;
return false;
}
// 测试反初始化
bool deinit_result = prov->Deinitialize();
delete prov;
return init_result && deinit_result;
}
/**
* @brief 测试2配网服务启动和停止
*/
bool test_provisioning_start_stop() {
ESP_LOGI(TAG, "开始测试:配网服务启动和停止");
BluetoothProvisioning* prov = new BluetoothProvisioning();
if (!prov || !prov->Initialize()) {
delete prov;
return false;
}
// 测试启动配网
bool start_result = prov->StartProvisioning("测试设备");
if (!start_result) {
prov->Deinitialize();
delete prov;
return false;
}
// 检查状态
BluetoothProvisioningState state = prov->GetState();
if (state != BluetoothProvisioningState::ADVERTISING) {
prov->Deinitialize();
delete prov;
return false;
}
// 等待一段时间
vTaskDelay(pdMS_TO_TICKS(2000));
// 测试停止配网
bool stop_result = prov->StopProvisioning();
// 检查状态
state = prov->GetState();
bool state_ok = (state == BluetoothProvisioningState::IDLE);
prov->Deinitialize();
delete prov;
return start_result && stop_result && state_ok;
}
/**
* @brief 测试3回调函数设置和触发
*/
bool test_callback_functionality() {
ESP_LOGI(TAG, "开始测试:回调函数设置和触发");
BluetoothProvisioning* prov = new BluetoothProvisioning();
if (!prov || !prov->Initialize()) {
delete prov;
return false;
}
// 设置回调函数
prov->SetCallback(test_provisioning_callback);
// 启动配网(这会触发状态变更回调)
bool start_result = prov->StartProvisioning("回调测试设备");
// 等待回调触发
vTaskDelay(pdMS_TO_TICKS(1000));
// 停止配网
prov->StopProvisioning();
prov->Deinitialize();
delete prov;
return start_result;
}
/**
* @brief 测试4状态管理
*/
bool test_state_management() {
ESP_LOGI(TAG, "开始测试:状态管理");
BluetoothProvisioning* prov = new BluetoothProvisioning();
if (!prov) {
return false;
}
// 检查初始状态
BluetoothProvisioningState state = prov->GetState();
if (state != BluetoothProvisioningState::IDLE) {
delete prov;
return false;
}
// 初始化后检查状态
if (!prov->Initialize()) {
delete prov;
return false;
}
state = prov->GetState();
if (state != BluetoothProvisioningState::IDLE) {
prov->Deinitialize();
delete prov;
return false;
}
// 启动配网后检查状态
prov->StartProvisioning("状态测试设备");
state = prov->GetState();
bool advertising_state_ok = (state == BluetoothProvisioningState::ADVERTISING);
// 停止配网后检查状态
prov->StopProvisioning();
state = prov->GetState();
bool idle_state_ok = (state == BluetoothProvisioningState::IDLE);
prov->Deinitialize();
delete prov;
return advertising_state_ok && idle_state_ok;
}
/**
* @brief 测试5错误处理
*/
bool test_error_handling() {
ESP_LOGI(TAG, "开始测试:错误处理");
BluetoothProvisioning* prov = new BluetoothProvisioning();
if (!prov) {
return false;
}
// 测试未初始化时启动配网
bool should_fail = prov->StartProvisioning("错误测试设备");
if (should_fail) {
// 这应该失败
delete prov;
return false;
}
// 正常初始化
if (!prov->Initialize()) {
delete prov;
return false;
}
// 测试重复初始化
bool repeat_init = prov->Initialize();
if (!repeat_init) {
// 重复初始化应该返回true已经初始化
prov->Deinitialize();
delete prov;
return false;
}
// 测试重复启动配网
prov->StartProvisioning("错误测试设备1");
bool repeat_start = prov->StartProvisioning("错误测试设备2");
if (!repeat_start) {
// 重复启动应该返回true已经在运行
prov->Deinitialize();
delete prov;
return false;
}
prov->StopProvisioning();
prov->Deinitialize();
delete prov;
return true;
}
/**
* @brief 测试6内存管理
*/
bool test_memory_management() {
ESP_LOGI(TAG, "开始测试:内存管理");
size_t free_heap_before = esp_get_free_heap_size();
ESP_LOGI(TAG, "测试前可用内存: %d 字节", free_heap_before);
// 创建和销毁多个实例
for (int i = 0; i < 3; i++) {
BluetoothProvisioning* prov = new BluetoothProvisioning();
if (!prov) {
return false;
}
if (prov->Initialize()) {
prov->StartProvisioning("内存测试设备");
vTaskDelay(pdMS_TO_TICKS(500));
prov->StopProvisioning();
prov->Deinitialize();
}
delete prov;
vTaskDelay(pdMS_TO_TICKS(100));
}
size_t free_heap_after = esp_get_free_heap_size();
ESP_LOGI(TAG, "测试后可用内存: %d 字节", free_heap_after);
// 检查内存泄漏(允许一定的误差)
int memory_diff = free_heap_before - free_heap_after;
bool memory_ok = (memory_diff < 1024); // 允许1KB的误差
if (!memory_ok) {
ESP_LOGW(TAG, "可能存在内存泄漏: %d 字节", memory_diff);
}
return memory_ok;
}
/**
* @brief 运行所有测试
*/
void run_all_tests() {
ESP_LOGI(TAG, "=== 开始蓝牙配网功能测试 ===");
// 创建测试事件组
test_event_group = xEventGroupCreate();
if (!test_event_group) {
ESP_LOGE(TAG, "测试事件组创建失败");
return;
}
// 运行各项测试
record_test_result("基本初始化和反初始化", test_basic_initialization());
record_test_result("配网服务启动和停止", test_provisioning_start_stop());
record_test_result("回调函数设置和触发", test_callback_functionality());
record_test_result("状态管理", test_state_management());
record_test_result("错误处理", test_error_handling());
record_test_result("内存管理", test_memory_management());
// 输出测试结果
ESP_LOGI(TAG, "=== 测试结果统计 ===");
ESP_LOGI(TAG, "总测试数: %d", test_stats.total_tests);
ESP_LOGI(TAG, "通过测试: %d", test_stats.passed_tests);
ESP_LOGI(TAG, "失败测试: %d", test_stats.failed_tests);
if (test_stats.failed_tests == 0) {
ESP_LOGI(TAG, "🎉 所有测试通过!蓝牙配网功能正常");
} else {
ESP_LOGE(TAG, "⚠️ 有 %d 个测试失败,请检查实现", test_stats.failed_tests);
}
// 清理资源
vEventGroupDelete(test_event_group);
test_event_group = nullptr;
}
/**
* @brief 蓝牙配网测试任务
*/
void bluetooth_provisioning_test_task(void* pvParameters) {
ESP_LOGI(TAG, "蓝牙配网测试任务启动");
// 等待系统稳定
vTaskDelay(pdMS_TO_TICKS(2000));
// 运行测试
run_all_tests();
ESP_LOGI(TAG, "蓝牙配网测试任务完成");
vTaskDelete(nullptr);
}
/**
* @brief 启动蓝牙配网测试
*
* 在主程序中调用此函数来启动测试
*/
void start_bluetooth_provisioning_test() {
BaseType_t ret = xTaskCreate(
bluetooth_provisioning_test_task,
"bt_prov_test",
8192,
nullptr,
3, // 较低优先级
nullptr
);
if (ret != pdPASS) {
ESP_LOGE(TAG, "蓝牙配网测试任务创建失败");
} else {
ESP_LOGI(TAG, "蓝牙配网测试任务已创建");
}
}
/**
* @brief 简单的配网功能演示
*
* 演示如何使用蓝牙配网功能
*/
void bluetooth_provisioning_demo() {
ESP_LOGI(TAG, "=== 蓝牙配网功能演示 ===");
BluetoothProvisioning* prov = new BluetoothProvisioning();
if (!prov) {
ESP_LOGE(TAG, "配网对象创建失败");
return;
}
// 设置回调
prov->SetCallback(test_provisioning_callback);
// 初始化
if (!prov->Initialize()) {
ESP_LOGE(TAG, "配网初始化失败");
delete prov;
return;
}
// 启动配网
if (!prov->StartProvisioning("小智AI-演示")) {
ESP_LOGE(TAG, "配网启动失败");
prov->Deinitialize();
delete prov;
return;
}
ESP_LOGI(TAG, "配网服务已启动请使用配网APP连接设备");
ESP_LOGI(TAG, "设备名称: 小智AI-演示");
ESP_LOGI(TAG, "等待客户端连接...");
// 运行30秒演示
for (int i = 0; i < 30; i++) {
BluetoothProvisioningState state = prov->GetState();
const char* state_names[] = {
"空闲", "初始化中", "广播中", "已连接",
"配网中", "成功", "失败", "已停止"
};
ESP_LOGI(TAG, "当前状态: %s, 客户端连接: %s",
state_names[static_cast<int>(state)],
prov->IsClientConnected() ? "" : "");
if (state == BluetoothProvisioningState::SUCCESS) {
ESP_LOGI(TAG, "配网成功!演示结束");
break;
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
// 清理资源
prov->StopProvisioning();
prov->Deinitialize();
delete prov;
ESP_LOGI(TAG, "演示结束");
}