mirror of
https://github.com/m5stack/M5UnitUnified.git
synced 2026-05-20 11:24:51 -07:00
364 lines
9.5 KiB
C++
364 lines
9.5 KiB
C++
/*
|
|
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
/*
|
|
UnitTest for M5UnitComponent
|
|
*/
|
|
#include <gtest/gtest.h>
|
|
#include <M5UnitComponent.hpp>
|
|
#include <M5UnitUnified.hpp>
|
|
#include <m5_unit_component/adapter.hpp>
|
|
#include "unit_dummy.hpp"
|
|
#include <Wire.h>
|
|
#include <SPI.h>
|
|
#include <M5Unified.h>
|
|
#include <M5HAL.hpp>
|
|
#include <esp32-hal-i2c.h>
|
|
|
|
using namespace m5::unit;
|
|
|
|
constexpr uint32_t I2C_FREQ{400000U};
|
|
|
|
// Board-aware I2C add: same 3-branch logic as I2CComponentTestBase
|
|
static bool add_with_i2c(UnitUnified& units, Component& u)
|
|
{
|
|
auto board = M5.getBoard();
|
|
if (board == m5::board_t::board_ArduinoNessoN1) {
|
|
auto sda = M5.getPin(m5::pin_name_t::port_b_out);
|
|
auto scl = M5.getPin(m5::pin_name_t::port_b_in);
|
|
m5::hal::bus::I2CBusConfig i2c_cfg;
|
|
i2c_cfg.pin_sda = m5::hal::gpio::getPin(sda);
|
|
i2c_cfg.pin_scl = m5::hal::gpio::getPin(scl);
|
|
auto i2c_bus = m5::hal::bus::i2c::getBus(i2c_cfg);
|
|
return units.add(u, i2c_bus ? i2c_bus.value() : nullptr);
|
|
}
|
|
if (board == m5::board_t::board_M5NanoC6) {
|
|
return units.add(u, M5.Ex_I2C);
|
|
}
|
|
// Standard boards: Wire
|
|
auto pin_num_sda = M5.getPin(m5::pin_name_t::port_a_sda);
|
|
auto pin_num_scl = M5.getPin(m5::pin_name_t::port_a_scl);
|
|
if (i2cIsInit(0)) {
|
|
Wire.end();
|
|
}
|
|
Wire.begin(pin_num_sda, pin_num_scl, I2C_FREQ);
|
|
return units.add(u, Wire);
|
|
}
|
|
|
|
// GPIO add helper
|
|
static bool add_with_gpio(UnitUnified& units, Component& u)
|
|
{
|
|
auto rx = M5.getPin(m5::pin_name_t::port_b_in);
|
|
auto tx = M5.getPin(m5::pin_name_t::port_b_out);
|
|
if (rx < 0 || tx < 0) {
|
|
// Fallback to port_a if port_b unavailable
|
|
rx = M5.getPin(m5::pin_name_t::port_a_pin1);
|
|
tx = M5.getPin(m5::pin_name_t::port_a_pin2);
|
|
}
|
|
return units.add(u, rx, tx);
|
|
}
|
|
|
|
// UART add helper
|
|
static bool add_with_uart(UnitUnified& units, Component& u)
|
|
{
|
|
#if SOC_UART_NUM > 2
|
|
auto& s = Serial2;
|
|
#elif SOC_UART_NUM > 1
|
|
auto& s = Serial1;
|
|
#else
|
|
#error "Not enough Serial"
|
|
#endif
|
|
return units.add(u, s);
|
|
}
|
|
|
|
// SPI add helper
|
|
static bool add_with_spi(UnitUnified& units, Component& u)
|
|
{
|
|
SPISettings settings{1000000, MSBFIRST, SPI_MODE0};
|
|
return units.add(u, SPI, settings);
|
|
}
|
|
|
|
// Test: I2C unit add/begin/update lifecycle (success path)
|
|
TEST(Component, Update)
|
|
{
|
|
UnitUnified units;
|
|
UnitDummy u;
|
|
EXPECT_FALSE(u.isRegistered());
|
|
|
|
{
|
|
auto cfg = u.component_config();
|
|
EXPECT_FALSE(cfg.self_update); // false as default
|
|
|
|
EXPECT_EQ(u.count, 0U);
|
|
EXPECT_TRUE(add_with_i2c(units, u));
|
|
|
|
units.update(); // Dont call u.update() because unit was not begun.
|
|
EXPECT_EQ(u.count, 0U);
|
|
|
|
EXPECT_TRUE(units.begin());
|
|
units.update(); // Call u.update()
|
|
EXPECT_EQ(u.count, 1U);
|
|
|
|
cfg.self_update = true;
|
|
u.component_config(cfg);
|
|
cfg = u.component_config();
|
|
EXPECT_TRUE(cfg.self_update);
|
|
|
|
units.update(); // Don't call u.update()
|
|
EXPECT_EQ(u.count, 1U);
|
|
|
|
u.update(); // If component_config.self_update is true, you have to call it yourself
|
|
EXPECT_EQ(u.count, 2U);
|
|
}
|
|
}
|
|
|
|
// Test: assign should fail for units with wrong access attribute
|
|
TEST(Component, AssignFailMismatch)
|
|
{
|
|
UnitUnified units;
|
|
|
|
// GPIO unit should fail to assign via I2C
|
|
{
|
|
UnitDummyGPIO gpio_unit;
|
|
EXPECT_TRUE(gpio_unit.canAccessGPIO());
|
|
EXPECT_FALSE(gpio_unit.canAccessI2C());
|
|
EXPECT_FALSE(add_with_i2c(units, gpio_unit));
|
|
}
|
|
|
|
// UART unit should fail to assign via I2C
|
|
{
|
|
UnitDummyUART uart_unit;
|
|
EXPECT_TRUE(uart_unit.canAccessUART());
|
|
EXPECT_FALSE(uart_unit.canAccessI2C());
|
|
EXPECT_FALSE(add_with_i2c(units, uart_unit));
|
|
}
|
|
|
|
// SPI unit should fail to assign via I2C
|
|
{
|
|
UnitDummySPI spi_unit;
|
|
EXPECT_TRUE(spi_unit.canAccessSPI());
|
|
EXPECT_FALSE(spi_unit.canAccessI2C());
|
|
EXPECT_FALSE(add_with_i2c(units, spi_unit));
|
|
}
|
|
|
|
// Unit with no access attribute should fail
|
|
{
|
|
UnitDummyNone none_unit;
|
|
EXPECT_FALSE(none_unit.canAccessI2C());
|
|
EXPECT_FALSE(none_unit.canAccessGPIO());
|
|
EXPECT_FALSE(none_unit.canAccessUART());
|
|
EXPECT_FALSE(none_unit.canAccessSPI());
|
|
EXPECT_FALSE(add_with_i2c(units, none_unit));
|
|
}
|
|
}
|
|
|
|
// Test: units with combined access attributes should succeed on matching bus
|
|
TEST(Component, AssignSuccessCombined)
|
|
{
|
|
UnitUnified units;
|
|
|
|
// I2C+SPI unit should succeed on I2C
|
|
{
|
|
UnitDummyI2CSPI i2cspi_unit;
|
|
EXPECT_TRUE(i2cspi_unit.canAccessI2C());
|
|
EXPECT_TRUE(i2cspi_unit.canAccessSPI());
|
|
EXPECT_FALSE(i2cspi_unit.canAccessGPIO());
|
|
EXPECT_FALSE(i2cspi_unit.canAccessUART());
|
|
EXPECT_TRUE(add_with_i2c(units, i2cspi_unit));
|
|
}
|
|
|
|
// All-access unit should succeed on I2C
|
|
{
|
|
UnitDummyAll all_unit;
|
|
EXPECT_TRUE(all_unit.canAccessI2C());
|
|
EXPECT_TRUE(all_unit.canAccessGPIO());
|
|
EXPECT_TRUE(all_unit.canAccessUART());
|
|
EXPECT_TRUE(all_unit.canAccessSPI());
|
|
EXPECT_TRUE(add_with_i2c(units, all_unit));
|
|
}
|
|
}
|
|
|
|
// Test: GPIO unit add success
|
|
TEST(Component, AddGPIO)
|
|
{
|
|
UnitUnified units;
|
|
UnitDummyGPIO u;
|
|
EXPECT_TRUE(u.canAccessGPIO());
|
|
EXPECT_EQ(u.adapter()->type(), Adapter::Type::Unknown);
|
|
EXPECT_TRUE(add_with_gpio(units, u));
|
|
EXPECT_EQ(u.adapter()->type(), Adapter::Type::GPIO);
|
|
}
|
|
|
|
// Test: UART unit add success
|
|
TEST(Component, AddUART)
|
|
{
|
|
UnitUnified units;
|
|
UnitDummyUART u;
|
|
EXPECT_TRUE(u.canAccessUART());
|
|
EXPECT_EQ(u.adapter()->type(), Adapter::Type::Unknown);
|
|
EXPECT_TRUE(add_with_uart(units, u));
|
|
EXPECT_EQ(u.adapter()->type(), Adapter::Type::UART);
|
|
}
|
|
|
|
// Test: SPI unit add success
|
|
TEST(Component, AddSPI)
|
|
{
|
|
UnitUnified units;
|
|
UnitDummySPI u;
|
|
EXPECT_TRUE(u.canAccessSPI());
|
|
EXPECT_EQ(u.adapter()->type(), Adapter::Type::Unknown);
|
|
EXPECT_TRUE(add_with_spi(units, u));
|
|
EXPECT_EQ(u.adapter()->type(), Adapter::Type::SPI);
|
|
}
|
|
|
|
// Test: cross-bus mismatch failures (GPIO/UART/SPI units on wrong bus)
|
|
TEST(Component, CrossBusMismatch)
|
|
{
|
|
// I2C unit should fail on GPIO/UART/SPI
|
|
{
|
|
UnitUnified units;
|
|
UnitDummy i2c_unit;
|
|
EXPECT_FALSE(add_with_gpio(units, i2c_unit));
|
|
}
|
|
{
|
|
UnitUnified units;
|
|
UnitDummy i2c_unit;
|
|
EXPECT_FALSE(add_with_uart(units, i2c_unit));
|
|
}
|
|
{
|
|
UnitUnified units;
|
|
UnitDummy i2c_unit;
|
|
EXPECT_FALSE(add_with_spi(units, i2c_unit));
|
|
}
|
|
|
|
// GPIO unit should fail on I2C/UART/SPI
|
|
{
|
|
UnitUnified units;
|
|
UnitDummyGPIO gpio_unit;
|
|
EXPECT_FALSE(add_with_i2c(units, gpio_unit));
|
|
}
|
|
{
|
|
UnitUnified units;
|
|
UnitDummyGPIO gpio_unit;
|
|
EXPECT_FALSE(add_with_uart(units, gpio_unit));
|
|
}
|
|
{
|
|
UnitUnified units;
|
|
UnitDummyGPIO gpio_unit;
|
|
EXPECT_FALSE(add_with_spi(units, gpio_unit));
|
|
}
|
|
|
|
// UART unit should fail on I2C/GPIO/SPI
|
|
{
|
|
UnitUnified units;
|
|
UnitDummyUART uart_unit;
|
|
EXPECT_FALSE(add_with_i2c(units, uart_unit));
|
|
}
|
|
{
|
|
UnitUnified units;
|
|
UnitDummyUART uart_unit;
|
|
EXPECT_FALSE(add_with_gpio(units, uart_unit));
|
|
}
|
|
{
|
|
UnitUnified units;
|
|
UnitDummyUART uart_unit;
|
|
EXPECT_FALSE(add_with_spi(units, uart_unit));
|
|
}
|
|
|
|
// SPI unit should fail on I2C/GPIO/UART
|
|
{
|
|
UnitUnified units;
|
|
UnitDummySPI spi_unit;
|
|
EXPECT_FALSE(add_with_i2c(units, spi_unit));
|
|
}
|
|
{
|
|
UnitUnified units;
|
|
UnitDummySPI spi_unit;
|
|
EXPECT_FALSE(add_with_gpio(units, spi_unit));
|
|
}
|
|
{
|
|
UnitUnified units;
|
|
UnitDummySPI spi_unit;
|
|
EXPECT_FALSE(add_with_uart(units, spi_unit));
|
|
}
|
|
}
|
|
|
|
// Test: combined-access units on matching buses
|
|
TEST(Component, CombinedAccessMultiBus)
|
|
{
|
|
// I2C+SPI unit should succeed on both I2C and SPI
|
|
{
|
|
UnitUnified units;
|
|
UnitDummyI2CSPI u;
|
|
EXPECT_TRUE(add_with_i2c(units, u));
|
|
}
|
|
{
|
|
UnitUnified units;
|
|
UnitDummyI2CSPI u;
|
|
EXPECT_TRUE(add_with_spi(units, u));
|
|
}
|
|
// I2C+SPI unit should fail on GPIO and UART
|
|
{
|
|
UnitUnified units;
|
|
UnitDummyI2CSPI u;
|
|
EXPECT_FALSE(add_with_gpio(units, u));
|
|
}
|
|
{
|
|
UnitUnified units;
|
|
UnitDummyI2CSPI u;
|
|
EXPECT_FALSE(add_with_uart(units, u));
|
|
}
|
|
|
|
// All-access unit should succeed on everything
|
|
{
|
|
UnitUnified units;
|
|
UnitDummyAll u;
|
|
EXPECT_TRUE(add_with_i2c(units, u));
|
|
}
|
|
{
|
|
UnitUnified units;
|
|
UnitDummyAll u;
|
|
EXPECT_TRUE(add_with_gpio(units, u));
|
|
}
|
|
{
|
|
UnitUnified units;
|
|
UnitDummyAll u;
|
|
EXPECT_TRUE(add_with_uart(units, u));
|
|
}
|
|
{
|
|
UnitUnified units;
|
|
UnitDummyAll u;
|
|
EXPECT_TRUE(add_with_spi(units, u));
|
|
}
|
|
}
|
|
|
|
// Test: order and adapter after add/begin
|
|
TEST(Component, OrderAndAdapter)
|
|
{
|
|
UnitUnified units;
|
|
UnitDummy u;
|
|
|
|
EXPECT_EQ(u.order(), 0U);
|
|
EXPECT_EQ(u.adapter()->type(), Adapter::Type::Unknown);
|
|
|
|
EXPECT_TRUE(add_with_i2c(units, u));
|
|
|
|
// After add, order should be > 0 and adapter type should be I2C
|
|
EXPECT_GT(u.order(), 0U);
|
|
EXPECT_EQ(u.adapter()->type(), Adapter::Type::I2C);
|
|
}
|
|
|
|
// Test: duplicate add should fail
|
|
TEST(Component, DuplicateAdd)
|
|
{
|
|
UnitUnified units;
|
|
UnitDummy u;
|
|
|
|
EXPECT_TRUE(add_with_i2c(units, u));
|
|
EXPECT_TRUE(u.isRegistered());
|
|
// Second add of same unit should fail
|
|
EXPECT_FALSE(add_with_i2c(units, u));
|
|
}
|