Files
2026-04-07 23:00:25 +09:00

796 lines
28 KiB
C++

/*
* SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
UnitTest for UnitFinger2
*/
#include <gtest/gtest.h>
#include <Wire.h>
#include <M5Unified.h>
#include <M5UnitUnified.hpp>
#include <googletest/test_template.hpp>
#include <googletest/test_helper.hpp>
#include <unit/unit_Finger2.hpp>
#include <chrono>
#include <thread>
#include <iostream>
#include <esp_random.h>
#include <algorithm>
using namespace m5::unit::googletest;
using namespace m5::unit;
using namespace m5::unit::finger2;
using namespace m5::unit::finger2::command;
using m5::unit::types::elapsed_time_t;
extern const uint8_t template_data[]; // template_data.cpp
extern const size_t template_data_size; // template_data.cpp
class TestFinger2 : public UARTComponentTestBase<UnitFinger2> {
protected:
virtual UnitFinger2* get_instance() override
{
auto ptr = new m5::unit::UnitFinger2();
return ptr;
}
virtual HardwareSerial* init_serial() override
{
auto pin_num_in = M5.getPin(m5::pin_name_t::port_c_rxd);
auto pin_num_out = M5.getPin(m5::pin_name_t::port_c_txd);
if (pin_num_in < 0 || pin_num_out < 0) {
// NanoC6: Ex_I2C.setPort() registers m5gfx::i2c on GPIO 1/2;
// Wire.end() alone won't release it, causing dual-driver conflict on uart_driver_install
if (M5.getBoard() == m5::board_t::board_M5NanoC6) {
M5.Ex_I2C.release();
}
Wire.end();
pin_num_in = M5.getPin(m5::pin_name_t::port_a_pin1);
pin_num_out = M5.getPin(m5::pin_name_t::port_a_pin2);
}
// clang-format off
#if defined(CONFIG_IDF_TARGET_ESP32C6)
auto& s = Serial1;
#elif SOC_UART_NUM > 2
auto& s = Serial2;
#elif SOC_UART_NUM > 1
auto& s = Serial1;
#else
#error "Not enough Serial"
#endif
// clang-format on
// M5_LOGI("getPin: %d,%d", pin_num_in, pin_num_out);
s.end();
s.begin(115200, SERIAL_8N1, pin_num_in, pin_num_out);
return &s;
}
void reset_serial(const uint32_t baud = 115200)
{
auto pin_num_in = M5.getPin(m5::pin_name_t::port_c_rxd);
auto pin_num_out = M5.getPin(m5::pin_name_t::port_c_txd);
if (pin_num_in < 0 || pin_num_out < 0) {
// NanoC6: Ex_I2C.setPort() registers m5gfx::i2c on GPIO 1/2;
// Wire.end() alone won't release it, causing dual-driver conflict on uart_driver_install
if (M5.getBoard() == m5::board_t::board_M5NanoC6) {
M5.Ex_I2C.release();
}
Wire.end();
pin_num_in = M5.getPin(m5::pin_name_t::port_a_pin1);
pin_num_out = M5.getPin(m5::pin_name_t::port_a_pin2);
}
// M5_LOGI("%u getPin: %d,%d", baud, pin_num_in, pin_num_out);
serial->end();
serial->begin(baud, SERIAL_8N1, pin_num_in, pin_num_out);
while (serial->available()) {
serial->read();
}
}
};
namespace {
constexpr WorkMode workmode_table[] = {
WorkMode::ScheduledSleep,
WorkMode::AlwaysActive,
};
constexpr LEDMode led_table[] = {
LEDMode::Breath, LEDMode::Blink, LEDMode::On, LEDMode::Off, LEDMode::Fadein, LEDMode::Fadeout,
};
constexpr auto_enroll_flag_t enroll_flags_table[] = {
0,
auto_enroll_flag::DONT_RETURN_INTERMEDIATE_RESULTS,
auto_enroll_flag::ALLOW_OVERWRITE_PAGE,
auto_enroll_flag::DONT_RETURN_INTERMEDIATE_RESULTS | auto_enroll_flag::ALLOW_OVERWRITE_PAGE,
auto_enroll_flag::PROHIBIT_DUPLICATE_TEMPLATE,
auto_enroll_flag::PROHIBIT_DUPLICATE_TEMPLATE | auto_enroll_flag::DONT_RETURN_INTERMEDIATE_RESULTS,
auto_enroll_flag::PROHIBIT_DUPLICATE_TEMPLATE | auto_enroll_flag::ALLOW_OVERWRITE_PAGE,
auto_enroll_flag::PROHIBIT_DUPLICATE_TEMPLATE | auto_enroll_flag::DONT_RETURN_INTERMEDIATE_RESULTS |
auto_enroll_flag::ALLOW_OVERWRITE_PAGE,
auto_enroll_flag::NO_NEED_RELEASE_FINGER,
auto_enroll_flag::NO_NEED_RELEASE_FINGER | auto_enroll_flag::DONT_RETURN_INTERMEDIATE_RESULTS,
auto_enroll_flag::NO_NEED_RELEASE_FINGER | auto_enroll_flag::ALLOW_OVERWRITE_PAGE,
auto_enroll_flag::NO_NEED_RELEASE_FINGER | auto_enroll_flag::DONT_RETURN_INTERMEDIATE_RESULTS |
auto_enroll_flag::ALLOW_OVERWRITE_PAGE,
auto_enroll_flag::NO_NEED_RELEASE_FINGER | auto_enroll_flag::PROHIBIT_DUPLICATE_TEMPLATE,
auto_enroll_flag::NO_NEED_RELEASE_FINGER | auto_enroll_flag::PROHIBIT_DUPLICATE_TEMPLATE |
auto_enroll_flag::DONT_RETURN_INTERMEDIATE_RESULTS,
auto_enroll_flag::NO_NEED_RELEASE_FINGER | auto_enroll_flag::PROHIBIT_DUPLICATE_TEMPLATE |
auto_enroll_flag::ALLOW_OVERWRITE_PAGE,
auto_enroll_flag::NO_NEED_RELEASE_FINGER | auto_enroll_flag::PROHIBIT_DUPLICATE_TEMPLATE |
auto_enroll_flag::DONT_RETURN_INTERMEDIATE_RESULTS | auto_enroll_flag::ALLOW_OVERWRITE_PAGE,
};
constexpr auto_identify_flag_t identify_flags_table[] = {
0,
auto_identify_flag::DONT_RETURN_INTERMEDIATE_RESULTS,
};
void wait_sleep(UnitFinger2* u)
{
m5::utility::delay(10 * 1000 + 100);
bool awake{true};
while (awake) {
bool b{};
if (u->readModuleStatus(b)) {
// M5_LOGW("Awake? %u", b);
awake = b;
}
m5::utility::delay(100);
}
}
} // namespace
TEST_F(TestFinger2, Basic)
{
SCOPED_TRACE(ustr);
/////
auto cfg = unit->config();
EXPECT_EQ(unit->deviceAddress(), 0xFFFFFFFFU);
EXPECT_EQ(unit->capacity(), 100U);
EXPECT_EQ(unit->imageWidth(), 80);
EXPECT_EQ(unit->imageHeight(), 208);
bool awake{};
EXPECT_TRUE(unit->readModuleStatus(awake));
EXPECT_TRUE(awake);
WorkMode mode{};
EXPECT_TRUE(unit->readWorkMode(mode));
EXPECT_EQ(mode, cfg.work_mode);
for (auto&& wm : workmode_table) {
auto s = m5::utility::formatString("WorkMode:%u", wm);
SCOPED_TRACE(s.c_str());
EXPECT_TRUE(unit->writeWorkMode(wm));
EXPECT_TRUE(unit->readWorkMode(mode));
EXPECT_EQ(mode, wm);
//
uint8_t sec{};
EXPECT_TRUE(unit->readSleepTime(sec));
EXPECT_EQ(sec, cfg.sleep_time);
EXPECT_FALSE(unit->writeSleepTime(0));
EXPECT_FALSE(unit->writeSleepTime(9));
EXPECT_FALSE(unit->writeSleepTime(255));
EXPECT_TRUE(unit->readSleepTime(sec));
EXPECT_EQ(sec, cfg.sleep_time);
EXPECT_TRUE(unit->writeSleepTime(254));
EXPECT_TRUE(unit->readSleepTime(sec));
EXPECT_EQ(sec, 254);
EXPECT_TRUE(unit->writeSleepTime(128));
EXPECT_TRUE(unit->readSleepTime(sec));
EXPECT_EQ(sec, 128);
EXPECT_TRUE(unit->writeSleepTime(10));
EXPECT_TRUE(unit->readSleepTime(sec));
EXPECT_EQ(sec, 10);
EXPECT_TRUE(unit->writeSleepTime(cfg.sleep_time));
EXPECT_TRUE(unit->readSleepTime(sec));
EXPECT_EQ(sec, cfg.sleep_time);
SystemBasicParams params{};
EXPECT_TRUE(unit->readSystemParams(params));
EXPECT_NE(params.status, 0U);
// EXPECT_EQ(params.template_size, 0x2EC8u);
// EXPECT_EQ(params.sensor_type,0x????);
EXPECT_EQ(params.database_capacity, 100u);
EXPECT_EQ(params.score_level, 3);
EXPECT_EQ(params.address, 0xFFFFFFFFu);
EXPECT_GE(params.packet_size, 0u);
EXPECT_LE(params.packet_size, 3u);
EXPECT_EQ(params.address, 0xFFFFFFFFu);
EXPECT_EQ(params.baud_rate, 6u); // 57600 only (STM32 <-> Chip)
//
EXPECT_FALSE(unit->writeSystemRegister(RegisterID::PacketSize, 4));
EXPECT_FALSE(unit->writeSystemRegister(RegisterID::PacketSize, 255));
uint32_t itime[4]{};
for (uint8_t ps = 0; ps <= 3; ++ps) {
EXPECT_TRUE(unit->writeSystemRegister(RegisterID::PacketSize, ps));
EXPECT_TRUE(unit->readSystemParams(params));
EXPECT_EQ(params.packet_size, ps);
uint8_t info[512]{};
uint8_t info_empty[512]{};
auto s = m5::utility::millis();
EXPECT_TRUE(unit->readInformationPage(info));
itime[ps] = m5::utility::millis() - s;
EXPECT_TRUE(memcmp(info, info_empty, sizeof(info)) != 0);
}
EXPECT_GT(itime[0], itime[1]);
EXPECT_GT(itime[1], itime[2]);
EXPECT_GT(itime[2], itime[3]);
EXPECT_TRUE(unit->writeSystemRegister(RegisterID::PacketSize, 1));
EXPECT_TRUE(unit->readSystemParams(params));
EXPECT_EQ(params.packet_size, 1);
//
std::vector<uint32_t> rval{};
for (uint_fast8_t i = 0; i < 100; ++i) {
uint32_t v{};
EXPECT_TRUE(unit->readRandomNumber(v));
rval.push_back(v);
}
std::set<uint32_t> rset(rval.begin(), rval.end());
EXPECT_NE(rset.size(), 1);
bool state{}, state2{};
EXPECT_TRUE(unit->handshake(state));
EXPECT_TRUE(state);
EXPECT_TRUE(unit->checkSensor(state2));
EXPECT_TRUE(state2);
uint8_t sn[32]{};
uint8_t sn_empty[32]{};
EXPECT_TRUE(unit->readSerialNumber(sn));
EXPECT_TRUE(memcmp(sn, sn_empty, sizeof(sn)) != 0);
uint8_t ver{};
EXPECT_TRUE(unit->readFirmwareVersion(ver));
EXPECT_NE(ver, 0u);
}
}
TEST_F(TestFinger2, LED)
{
SCOPED_TRACE(ustr);
/////
EXPECT_TRUE(unit->writeSleepTime(10));
for (auto&& wm : workmode_table) {
auto s = m5::utility::formatString("WorkMode:%u", wm);
SCOPED_TRACE(s.c_str());
EXPECT_TRUE(unit->writeWorkMode(wm));
// In sleep
if (wm == WorkMode::ScheduledSleep) {
wait_sleep(unit.get());
for (auto&& mode : led_table) {
EXPECT_FALSE(unit->writeControlLED(mode, LEDColor::White, 2, LEDColor::Blue));
}
LEDColor clr2[2] = {LEDColor::Red, LEDColor::Yellow};
EXPECT_FALSE(unit->writeControlLEDRainbow(5 /*0.5sec*/, clr2, 2, 3));
continue;
}
// In active
for (auto&& mode : led_table) {
M5_LOGI("LED:%u", mode);
M5.Speaker.tone(2000, 20);
EXPECT_TRUE(unit->writeControlLED(mode, LEDColor::White, 2, LEDColor::Blue));
m5::utility::delay(3000);
}
LEDColor clr2[2] = {LEDColor::Red, LEDColor::Yellow};
LEDColor clr5[5] = {LEDColor::Red, LEDColor::Yellow, LEDColor::Blue, LEDColor::Cyan, LEDColor::Green};
LEDColor clr10[10] = {LEDColor::Red, LEDColor::Yellow, LEDColor::Blue, LEDColor::Cyan, LEDColor::Green,
LEDColor::White, LEDColor::Magenta, LEDColor::White, LEDColor::Green, LEDColor::Cyan};
M5.Speaker.tone(3000, 20);
EXPECT_TRUE(unit->writeControlLEDRainbow(5 /*0.5sec*/, clr2, 2, 3));
m5::utility::delay(2 * 1000);
M5.Speaker.tone(3000, 20);
EXPECT_TRUE(unit->writeControlLEDRainbow(2 /*0.2sec*/, clr5, 5, 3));
m5::utility::delay(2 * 1000);
M5.Speaker.tone(3000, 20);
EXPECT_TRUE(unit->writeControlLEDRainbow(1 /*0.1sec*/, clr10, 10, 1));
m5::utility::delay(2 * 1000);
M5.Speaker.tone(4000, 20);
EXPECT_TRUE(unit->writeControlLED(LEDMode::Breath, LEDColor::Blue, 36, LEDColor::Blue));
}
}
TEST_F(TestFinger2, Notepad)
{
SCOPED_TRACE(ustr);
/////
uint8_t buf[32]{};
constexpr uint8_t empty[32]{};
constexpr uint8_t write1[1] = {0x52};
constexpr uint8_t write9[9] = {0x99, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A};
constexpr uint8_t write32[32] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20};
constexpr uint8_t write33[33] = {0x11, 0x12, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21};
for (auto&& wm : workmode_table) {
auto s = m5::utility::formatString("WorkMode:%u", wm);
SCOPED_TRACE(s.c_str());
EXPECT_TRUE(unit->writeWorkMode(wm));
// In sleep
if (wm == WorkMode::ScheduledSleep) {
wait_sleep(unit.get());
for (uint8_t page = 0; page < 8; ++page) {
EXPECT_FALSE(unit->writeNotepad(page, write1, sizeof(write1)));
EXPECT_FALSE(unit->writeNotepad(page, write9, sizeof(write9)));
EXPECT_FALSE(unit->writeNotepad(page, write32, sizeof(write32)));
EXPECT_FALSE(unit->writeNotepad(page, write33, sizeof(write33)));
EXPECT_FALSE(unit->readNotepad(buf, page));
}
EXPECT_FALSE(unit->writeNotepad(8, write1, sizeof(write1)));
EXPECT_FALSE(unit->writeNotepad(255, write1, sizeof(write1)));
EXPECT_FALSE(unit->readNotepad(buf, 8));
EXPECT_FALSE(unit->readNotepad(buf, 255));
continue;
}
// In active
for (uint8_t page = 0; page < 8; ++page) {
auto s = m5::utility::formatString("Page:%u", page);
SCOPED_TRACE(s.c_str());
// Clear
EXPECT_TRUE(unit->writeNotepad(page, empty, sizeof(empty)));
EXPECT_TRUE(unit->readNotepad(buf, page));
EXPECT_TRUE(memcmp(buf, empty, 32) == 0);
// Write...
EXPECT_TRUE(unit->writeNotepad(page, write1, sizeof(write1)));
EXPECT_TRUE(unit->readNotepad(buf, page));
EXPECT_EQ(buf[0], write1[0]);
EXPECT_TRUE(memcmp(buf + 1, empty + 1, 31) == 0);
EXPECT_TRUE(unit->writeNotepad(page, write9, sizeof(write9)));
EXPECT_TRUE(unit->readNotepad(buf, page));
EXPECT_TRUE(memcmp(buf, write9, 9) == 0);
EXPECT_TRUE(memcmp(buf + 9, empty + 9, 32 - 9) == 0);
EXPECT_TRUE(unit->writeNotepad(page, write32, sizeof(write32)));
EXPECT_TRUE(unit->readNotepad(buf, page));
EXPECT_TRUE(memcmp(buf, write32, 32) == 0);
EXPECT_TRUE(unit->writeNotepad(page, write33, sizeof(write33)));
EXPECT_TRUE(unit->readNotepad(buf, page));
EXPECT_TRUE(memcmp(buf, write33, 32) == 0);
EXPECT_TRUE(unit->writeNotepad(page, write9, sizeof(write9)));
EXPECT_TRUE(unit->readNotepad(buf, page));
EXPECT_TRUE(memcmp(buf, write9, 9) == 0);
EXPECT_TRUE(memcmp(buf + 9, empty + 9, 32 - 9) == 0);
}
}
}
TEST_F(TestFinger2, Template)
{
SCOPED_TRACE(ustr);
/////
std::vector<uint8_t> tbuf{};
tbuf.resize(UnitFinger2::TEMPLATE_SIZE);
for (auto&& wm : workmode_table) {
auto s = m5::utility::formatString("WorkMode:%u", wm);
SCOPED_TRACE(s.c_str());
EXPECT_TRUE(unit->writeWorkMode(wm));
// In sleep
if (wm == WorkMode::ScheduledSleep) {
wait_sleep(unit.get());
EXPECT_FALSE(unit->clear());
uint16_t num{};
EXPECT_FALSE(unit->readValidTemplates(num));
uint16_t low{}, high{};
uint8_t table[32]{};
EXPECT_FALSE(unit->readIndexTable(table));
EXPECT_FALSE(unit->findLowestAvailablePage(low));
EXPECT_FALSE(unit->findHighestAvailablePage(high));
EXPECT_FALSE(unit->existsTemplate(0));
EXPECT_FALSE(unit->existsTemplate(100));
EXPECT_FALSE(unit->existsTemplate(65535));
EXPECT_FALSE(unit->writeTemplateAllBatches(template_data, template_data_size));
uint16_t actual{};
EXPECT_FALSE(unit->storeTemplate(100));
EXPECT_FALSE(unit->storeTemplate(65535));
EXPECT_FALSE(unit->deleteTemplate(50));
EXPECT_FALSE(unit->readTemplateAllBatches(actual, tbuf.data(), tbuf.size()));
continue;
}
// In active
EXPECT_TRUE(unit->clear()); // all clear
uint16_t num{};
EXPECT_TRUE(unit->readValidTemplates(num));
EXPECT_EQ(num, 0);
uint16_t low{}, high{};
EXPECT_TRUE(unit->findLowestAvailablePage(low));
EXPECT_TRUE(unit->findHighestAvailablePage(high));
EXPECT_EQ(low, 0);
EXPECT_EQ(high, 99);
//
EXPECT_TRUE(unit->writeTemplateAllBatches(template_data, template_data_size));
EXPECT_TRUE(unit->storeTemplate(50));
EXPECT_TRUE(unit->readValidTemplates(num));
EXPECT_EQ(num, 1);
EXPECT_TRUE(unit->findLowestAvailablePage(low));
EXPECT_TRUE(unit->findHighestAvailablePage(high));
EXPECT_EQ(low, 0);
EXPECT_EQ(high, 99);
EXPECT_TRUE(unit->deleteTemplate(50));
EXPECT_TRUE(unit->readValidTemplates(num));
EXPECT_EQ(num, 0);
EXPECT_TRUE(unit->findLowestAvailablePage(low));
EXPECT_TRUE(unit->findHighestAvailablePage(high));
EXPECT_EQ(low, 0);
EXPECT_EQ(high, 99);
//
EXPECT_TRUE(unit->storeTemplate(0));
EXPECT_TRUE(unit->readValidTemplates(num));
EXPECT_EQ(num, 1);
EXPECT_TRUE(unit->findLowestAvailablePage(low));
EXPECT_TRUE(unit->findHighestAvailablePage(high));
EXPECT_EQ(low, 1);
EXPECT_EQ(high, 99);
EXPECT_TRUE(unit->storeTemplate(99));
EXPECT_TRUE(unit->readValidTemplates(num));
EXPECT_EQ(num, 2);
EXPECT_TRUE(unit->findLowestAvailablePage(low));
EXPECT_TRUE(unit->findHighestAvailablePage(high));
EXPECT_EQ(low, 1);
EXPECT_EQ(high, 98);
for (uint8_t i = 1; i < 50; ++i) {
auto prev_num = num;
auto prev_low = low;
auto prev_high = high;
M5_LOGI("Reg:%u, %u / %u", low, high, num);
EXPECT_FALSE(unit->existsTemplate(low));
EXPECT_TRUE(unit->storeTemplate(low));
EXPECT_TRUE(unit->existsTemplate(low));
EXPECT_TRUE(unit->readValidTemplates(num));
// M5_LOGW("L:%u", num);
EXPECT_EQ(num, prev_num + 1);
EXPECT_FALSE(unit->existsTemplate(high));
EXPECT_TRUE(unit->storeTemplate(high));
EXPECT_TRUE(unit->existsTemplate(high));
EXPECT_TRUE(unit->readValidTemplates(num));
// M5_LOGW("H:%u", num);
EXPECT_EQ(num, prev_num + 2);
EXPECT_TRUE(unit->findLowestAvailablePage(low));
EXPECT_TRUE(unit->findHighestAvailablePage(high));
if (num < 100) {
// M5_LOGW("(%u,%u) (%u,%u)", low, prev_low, high, prev_high);
EXPECT_EQ(low, prev_low + 1);
EXPECT_EQ(high, prev_high - 1);
}
}
EXPECT_TRUE(unit->existsTemplate(0));
EXPECT_TRUE(unit->existsTemplate(99));
EXPECT_FALSE(unit->existsTemplate(100));
EXPECT_FALSE(unit->existsTemplate(65535));
EXPECT_TRUE(unit->readValidTemplates(num));
EXPECT_EQ(num, 100u);
EXPECT_TRUE(unit->findLowestAvailablePage(low));
EXPECT_TRUE(unit->findHighestAvailablePage(high));
EXPECT_EQ(low, 0xFFFFu);
EXPECT_EQ(high, 0xFFFFu);
EXPECT_FALSE(unit->storeTemplate(100u));
EXPECT_FALSE(unit->storeTemplate(65535u));
uint16_t actual{};
EXPECT_TRUE(unit->readTemplateAllBatches(actual, tbuf.data(), tbuf.size()));
EXPECT_EQ(actual, tbuf.size());
auto all = num;
EXPECT_FALSE(unit->deleteTemplate(100));
EXPECT_FALSE(unit->deleteTemplate(65535));
for (uint_fast8_t i = 0; i < 50; ++i) {
// del 49-0
M5_LOGI("del:%u", i);
EXPECT_TRUE(unit->deleteTemplate(49 - i));
// EXPECT_FALSE(unit->deleteTemplate(49 - i));
EXPECT_TRUE(unit->readValidTemplates(num));
--all;
EXPECT_EQ(num, all);
EXPECT_TRUE(unit->findLowestAvailablePage(low));
EXPECT_TRUE(unit->findHighestAvailablePage(high));
EXPECT_EQ(low, 49 - i);
EXPECT_EQ(high, (i == 0) ? 49 : 50 + i - 1);
// del 50-99
M5_LOGI("del:%u", i + 50);
EXPECT_TRUE(unit->deleteTemplate(50 + i));
// EXPECT_FALSE(unit->deleteTemplate(50 + i));
EXPECT_TRUE(unit->readValidTemplates(num));
--all;
EXPECT_EQ(num, all);
EXPECT_TRUE(unit->findLowestAvailablePage(low));
EXPECT_TRUE(unit->findHighestAvailablePage(high));
EXPECT_EQ(low, 49 - i);
EXPECT_EQ(high, 50 + i);
}
}
}
TEST_F(TestFinger2, Finger)
{
SCOPED_TRACE(ustr);
/////
for (auto&& wm : workmode_table) {
auto s = m5::utility::formatString("WorkMode:%u", wm);
SCOPED_TRACE(s.c_str());
EXPECT_TRUE(unit->writeWorkMode(wm));
// In sleep
if (wm == WorkMode::ScheduledSleep) {
wait_sleep(unit.get());
bool detected{};
EXPECT_FALSE(unit->capture(detected, false));
EXPECT_FALSE(unit->capture(detected, true));
uint8_t percentage{};
bool quality{};
EXPECT_FALSE(unit->readImageInformation(percentage, quality));
std::vector<uint8_t> img{};
EXPECT_FALSE(unit->readImage(img));
for (uint8_t bid = 1; bid <= 5; ++bid) {
EXPECT_FALSE(unit->generateCharacteristic(bid));
}
EXPECT_FALSE(unit->generateCharacteristic(0));
EXPECT_FALSE(unit->generateCharacteristic(6));
EXPECT_FALSE(unit->generateCharacteristic(255));
EXPECT_FALSE(unit->generateTemplate());
bool matched{};
uint16_t score{}, page{};
EXPECT_FALSE(unit->match(matched, score));
EXPECT_FALSE(unit->search(matched, page, score));
EXPECT_FALSE(unit->searchNow(matched, page, score));
for (uint8_t bid = 1; bid <= 5; ++bid) {
EXPECT_FALSE(unit->loadTemplate(bid, 50));
}
EXPECT_FALSE(unit->loadTemplate(0, 50));
EXPECT_FALSE(unit->loadTemplate(6, 50));
EXPECT_FALSE(unit->loadTemplate(255, 50));
continue;
}
bool detected{};
EXPECT_TRUE(unit->capture(detected, false));
EXPECT_FALSE(detected);
EXPECT_TRUE(unit->capture(detected, true));
EXPECT_FALSE(detected);
uint8_t percentage{};
bool quality{};
EXPECT_FALSE(unit->readImageInformation(percentage, quality));
std::vector<uint8_t> img{};
EXPECT_TRUE(unit->readImage(img)); // Get empty image
// m5::utility::log::dump(img.data(), img.size(), false);
for (uint8_t bid = 1; bid <= 5; ++bid) {
EXPECT_FALSE(unit->generateCharacteristic(bid));
}
EXPECT_FALSE(unit->generateCharacteristic(0));
EXPECT_FALSE(unit->generateCharacteristic(6));
EXPECT_FALSE(unit->generateCharacteristic(255));
EXPECT_FALSE(unit->generateTemplate());
for (uint8_t bid = 1; bid <= 5; ++bid) {
EXPECT_FALSE(unit->storeTemplate(52, bid));
}
EXPECT_FALSE(unit->storeTemplate(52, 0));
EXPECT_FALSE(unit->storeTemplate(52, 6));
EXPECT_FALSE(unit->storeTemplate(52, 255));
for (uint8_t bid = 1; bid <= 5; ++bid) {
EXPECT_FALSE(unit->storeTemplate(100, bid));
EXPECT_FALSE(unit->storeTemplate(65535, bid));
}
bool matched{};
uint16_t score{}, page{};
EXPECT_TRUE(unit->match(matched, score));
EXPECT_FALSE(matched);
// M5_LOGW(">>>> %u %u", matched, score);
EXPECT_TRUE(unit->search(matched, page, score));
EXPECT_FALSE(matched);
EXPECT_EQ(score, 0u);
EXPECT_EQ(page, 0u);
// M5_LOGW(">>>> %u %u %u", matched, score, page);
EXPECT_TRUE(unit->searchNow(matched, page, score));
EXPECT_FALSE(matched);
EXPECT_EQ(score, 0u);
EXPECT_EQ(page, 0u);
// M5_LOGW(">>>> %u %u %u", matched, score, page);
//
EXPECT_TRUE(unit->writeTemplateAllBatches(template_data, template_data_size));
EXPECT_TRUE(unit->storeTemplate(50));
for (uint8_t bid = 1; bid <= 5; ++bid) {
EXPECT_TRUE(unit->loadTemplate(bid, 50));
}
EXPECT_FALSE(unit->loadTemplate(0, 50));
EXPECT_FALSE(unit->loadTemplate(6, 50));
EXPECT_FALSE(unit->loadTemplate(255, 50));
}
}
TEST_F(TestFinger2, Automatic)
{
SCOPED_TRACE(ustr);
for (auto&& wm : workmode_table) {
auto s = m5::utility::formatString("WorkMode:%u", wm);
SCOPED_TRACE(s.c_str());
EXPECT_TRUE(unit->writeWorkMode(wm));
// In sleep
if (wm == WorkMode::ScheduledSleep) {
wait_sleep(unit.get());
ConfirmCode confirm{};
EXPECT_FALSE(unit->autoEnroll(confirm, 0));
bool matched{};
uint16_t page{}, score{};
EXPECT_FALSE(unit->autoIdentify(matched, page, score));
continue;
}
ConfirmCode confirm{};
EXPECT_FALSE(unit->autoEnroll(confirm, 0));
bool matched{};
uint16_t page{}, score{};
EXPECT_FALSE(unit->autoIdentify(matched, page, score));
EXPECT_FALSE(unit->autoEnroll(confirm, 100));
EXPECT_FALSE(unit->autoEnroll(confirm, 65535));
EXPECT_FALSE(unit->autoEnroll(confirm, 0, 0));
EXPECT_FALSE(unit->autoEnroll(confirm, 0, 6));
EXPECT_FALSE(unit->autoEnroll(confirm, 0, 255));
for (auto&& eflags : enroll_flags_table) {
EXPECT_FALSE(unit->autoEnroll(confirm, 0, 5, eflags));
}
EXPECT_FALSE(unit->autoIdentify(matched, page, score, 100));
EXPECT_FALSE(unit->autoIdentify(matched, page, score, 0xFFFE));
EXPECT_FALSE(unit->autoIdentify(matched, page, score, 0xFFFF, 2));
EXPECT_FALSE(unit->autoIdentify(matched, page, score, 0xFFFF, 255));
for (auto&& iflags : identify_flags_table) {
EXPECT_FALSE(unit->autoIdentify(matched, page, score, 0xFFFF, 0, iflags));
}
}
}
// --- deprecated API compatibility (no hardware required) ---
TEST_F(TestFinger2, DeprecatedAPI)
{
// LEDMode::Bleath is a deprecated alias for LEDMode::Breath
EXPECT_EQ(LEDMode::Bleath, LEDMode::Breath);
EXPECT_EQ(static_cast<uint8_t>(LEDMode::Bleath), static_cast<uint8_t>(LEDMode::Breath));
// NO_NEED_RELAESE_FINGER is a deprecated alias for NO_NEED_RELEASE_FINGER
EXPECT_EQ(auto_enroll_flag::NO_NEED_RELAESE_FINGER, auto_enroll_flag::NO_NEED_RELEASE_FINGER);
// ResettFailed is a deprecated alias for ResetFailed
EXPECT_EQ(ConfirmCode::ResettFailed, ConfirmCode::ResetFailed);
// initializeFailed is a deprecated alias for InitializeFailed
EXPECT_EQ(ConfirmCode::initializeFailed, ConfirmCode::InitializeFailed);
}
TEST_F(TestFinger2, BeginConfig)
{
SCOPED_TRACE(ustr);
// Verify that begin() applied config values to hardware
auto cfg = unit->config();
WorkMode mode{};
EXPECT_TRUE(unit->readWorkMode(mode));
EXPECT_EQ(mode, cfg.work_mode);
uint8_t sec{};
EXPECT_TRUE(unit->readSleepTime(sec));
EXPECT_EQ(sec, cfg.sleep_time);
// Write non-default values, re-read to verify
EXPECT_TRUE(unit->writeWorkMode(WorkMode::AlwaysActive));
EXPECT_TRUE(unit->readWorkMode(mode));
EXPECT_EQ(mode, WorkMode::AlwaysActive);
EXPECT_TRUE(unit->writeSleepTime(128));
EXPECT_TRUE(unit->readSleepTime(sec));
EXPECT_EQ(sec, 128);
// Restore defaults
EXPECT_TRUE(unit->writeWorkMode(cfg.work_mode));
EXPECT_TRUE(unit->writeSleepTime(cfg.sleep_time));
}
#if 0
// page:0
0x3ffb202c| 00 03 06 09 0C 0F 12 15 18 1B 1E 21 24 27 2A 2D |
0x3ffb203c| 30 33 36 39 3C 3F 42 45 48 4B 4E 51 54 57 5A 5D |
48, 49, 50
#endif