Files
DTU-HMI/tests/test_p1_lcd_basic.c

290 lines
12 KiB
C
Raw Permalink 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.
/* -------------------------------------------------------------------------
* 文件名: test_p1_lcd_basic.c
* 作用:
* 对 LCD 基础模块进行单元测试,覆盖以下三个子模块:
* 1) lcd.c : 显存初始化、像素读写、边界保护
* 2) lcd_draw.c : 填充、反显、水平线/垂直线绘制
* 3) lcd_text.c : UTF-8 解码、字符串显示ASCII/中文/换行)与边界返回值
*
* 测试策略:
* - 每个 test_* 函数仅验证一个明确行为域,失败即立即返回非 0。
* - 使用 ASSERT_* 宏统一断言风格,失败时打印文件+行号。
* - main() 串行执行所有子用例,便于定位首个失败点。
*
* 设计说明:
* - 本文件只依赖公开头文件,不包含被测 .c实现黑盒+灰盒结合验证。
* - 通过“像素点结果”验证绘图函数,不依赖外设或真实 LCD 硬件。
* ------------------------------------------------------------------------- */
#include "../src/Drv/lcd/lcd.h"
#include "../src/Drv/lcd/lcd_draw.h"
#include "../src/Drv/lcd/lcd_text.h"
#include "test_common.h"
/* -------------------------------------------------------------------------
* 用例: test_lcd_init_and_pixel_rw
* 目标:
* - 验证 Lcd_Init() 会把显存清零(黑色)
* - 验证 Lcd_SetPixel/Lcd_GetPixel 的基本读写正确性
* - 验证越界写入/读取均返回错误码
*
* 覆盖函数:
* - Lcd_Init
* - Lcd_SetPixel
* - Lcd_GetPixel
* ------------------------------------------------------------------------- */
static int test_lcd_init_and_pixel_rw(void)
{
/* 初始化后显存应为全黑0x00 */
Lcd_Init();
/* 左上角像素校验 */
ASSERT_EQ_INT(LCD_COLOR_BLACK, Lcd_GetPixel(0, 0));
/* 右下角像素校验,确认全屏范围都被正确初始化 */
ASSERT_EQ_INT(LCD_COLOR_BLACK, Lcd_GetPixel(LCD_SIZE_X - 1, LCD_SIZE_Y - 1));
/* 在合法坐标写白点并回读 */
ASSERT_EQ_INT(LCD_OK, Lcd_SetPixel(1, 1, LCD_COLOR_WHITE));
ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(1, 1));
/* 非法颜色应返回错误码,且不改变原有像素值 */
ASSERT_EQ_INT(LCD_ERR_INVALID_COLOR, Lcd_SetPixel(1, 1, 0x7F));
ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(1, 1));
/* 越界写入应返回错误码x == LCD_SIZE_X 已越界 */
ASSERT_EQ_INT(LCD_ERR_OUT_OF_RANGE, Lcd_SetPixel(LCD_SIZE_X, 1, LCD_COLOR_WHITE));
/* 越界写入应返回错误码y == LCD_SIZE_Y 已越界 */
ASSERT_EQ_INT(LCD_ERR_OUT_OF_RANGE, Lcd_SetPixel(1, LCD_SIZE_Y, LCD_COLOR_WHITE));
/* 越界读取应返回错误码 */
ASSERT_EQ_INT(LCD_ERR_OUT_OF_RANGE, Lcd_GetPixel(LCD_SIZE_X, 1));
ASSERT_EQ_INT(LCD_ERR_OUT_OF_RANGE, Lcd_GetPixel(1, LCD_SIZE_Y));
/* 附加点位校验,确保越界写没有污染邻近合法区域 */
ASSERT_EQ_INT(LCD_COLOR_BLACK, Lcd_GetPixel(2, 2));
return 0;
}
/* -------------------------------------------------------------------------
* 用例: test_fillrect_and_invert
* 目标:
* - 验证 Lcd_FillRect 的闭区间填充行为(包含 right_x/bottom_y与错误码
* - 验证 Lcd_Invert 的半开区间行为([min, max))与错误码
* - 验证 Lcd_Invert 支持反向坐标输入(起点大于终点)
*
* 覆盖函数:
* - Lcd_FillRect
* - Lcd_Invert
* ------------------------------------------------------------------------- */
static int test_fillrect_and_invert(void)
{
Lcd_Init();
/* 填充 3x3 白色区域x=[2,4], y=[2,4](闭区间) */
ASSERT_EQ_INT(LCD_OK, Lcd_FillRect(2, 2, 4, 4, LCD_COLOR_WHITE));
/* 左上角在填充区内 */
ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(2, 2));
/* 右下角也在填充区内,验证 FillRect 含边界 */
ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(4, 4));
/* 区域外点保持黑色 */
ASSERT_EQ_INT(LCD_COLOR_BLACK, Lcd_GetPixel(5, 5));
/* Invert 采用 [min, max) 半开区间:会翻转 x=2..3, y=2..3 */
ASSERT_EQ_INT(LCD_OK, Lcd_Invert(2, 2, 4, 4));
/* 区域内像素从白翻转成 ~白 */
ASSERT_EQ_INT((uint8_t)~LCD_COLOR_WHITE, Lcd_GetPixel(2, 2));
ASSERT_EQ_INT((uint8_t)~LCD_COLOR_WHITE, Lcd_GetPixel(3, 3));
/* x=4/y=4 在半开区间外,不应被翻转 */
ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(4, 4));
/* 反向坐标也应可处理:再次翻转回白色 */
ASSERT_EQ_INT(LCD_OK, Lcd_Invert(4, 4, 2, 2));
ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(2, 2));
ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(3, 3));
/* 越界参数应返回错误码 */
ASSERT_EQ_INT(LCD_ERR_OUT_OF_RANGE, Lcd_FillRect(0, 0, LCD_SIZE_X, 4, LCD_COLOR_WHITE));
ASSERT_EQ_INT(LCD_ERR_INVALID_COLOR, Lcd_FillRect(0, 0, 1, 1, 0x7F));
ASSERT_EQ_INT(LCD_ERR_OUT_OF_RANGE, Lcd_Invert(0, 0, LCD_SIZE_X + 1, 4));
return 0;
}
/* -------------------------------------------------------------------------
* 用例: test_lineh_linev
* 目标:
* - 验证 Lcd_LineH/Lcd_LineV 的绘制范围与线宽、错误码
* - 验证水平/垂直线的“结束坐标不包含”语义
* - 验证 Lcd_LineH 的反向坐标能力
*
* 覆盖函数:
* - Lcd_LineH
* - Lcd_LineV
* ------------------------------------------------------------------------- */
static int test_lineh_linev(void)
{
Lcd_Init();
/* 水平线x=[10,14), y=[20,22);线宽=2 */
ASSERT_EQ_INT(LCD_OK, Lcd_LineH(10, 14, 20, 2, LCD_COLOR_WHITE));
/* 起点应该被绘制 */
ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(10, 20));
/* 范围内末端附近点x=13,y=21应为白 */
ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(13, 21));
/* xEnd 不包含x=14 不应被画 */
ASSERT_EQ_INT(LCD_COLOR_BLACK, Lcd_GetPixel(14, 20)); /* xEnd 不包含 */
/* 垂直线x=[40,42), y=[30,35);线宽=2 */
ASSERT_EQ_INT(LCD_OK, Lcd_LineV(30, 35, 40, 2, LCD_COLOR_WHITE));
/* 起点应被绘制 */
ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(40, 30));
/* 范围内末端附近点应为白 */
ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(41, 34));
/* yEnd 不包含y=35 不应被画 */
ASSERT_EQ_INT(LCD_COLOR_BLACK, Lcd_GetPixel(40, 35)); /* yEnd 不包含 */
/* 水平线支持反向 X函数内部应自动交换起止坐标 */
ASSERT_EQ_INT(LCD_OK, Lcd_LineH(14, 10, 50, 1, LCD_COLOR_WHITE));
ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(10, 50));
ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(13, 50));
ASSERT_EQ_INT(LCD_COLOR_BLACK, Lcd_GetPixel(14, 50));
ASSERT_EQ_INT(LCD_ERR_INVALID_COLOR, Lcd_LineH(10, 14, 20, 1, 0x7F));
ASSERT_EQ_INT(LCD_ERR_OUT_OF_RANGE, Lcd_LineH(10, 14, LCD_SIZE_Y, 1, LCD_COLOR_WHITE));
ASSERT_EQ_INT(LCD_ERR_OUT_OF_RANGE, Lcd_LineV(10, 14, LCD_SIZE_X, 1, LCD_COLOR_WHITE));
return 0;
}
/* -------------------------------------------------------------------------
* 工具函数: count_white_in_rect
* 作用:
* 统计指定闭区间矩形内白色像素数量。
* 用途:
* 文本渲染(尤其字形)不便逐像素写期望值时,用“白点数 > 0”验证
* “确实有内容被渲染”这一最小正确性。
*
* 参数:
* x0,y0 : 左上角(包含)
* x1,y1 : 右下角(包含)
*
* 返回:
* 区域内等于 LCD_COLOR_WHITE 的像素个数。
* ------------------------------------------------------------------------- */
static int count_white_in_rect(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
int count = 0;
for (uint16_t y = y0; y <= y1; y++) {
for (uint16_t x = x0; x <= x1; x++) {
if (Lcd_GetPixel(x, y) == LCD_COLOR_WHITE) {
count++;
}
}
}
return count;
}
/* -------------------------------------------------------------------------
* 用例: test_utf8_next_decode
* 目标:
* - 覆盖 utf8_next 对 1/2/3 字节 UTF-8 的解码路径
* - 验证不支持或非法输入时返回 0
*
* 覆盖函数:
* - utf8_next
* ------------------------------------------------------------------------- */
static int test_utf8_next_decode(void)
{
uint32_t unicode = 0;
const unsigned char ascii[] = "A"; /* 单字节 ASCII */
const unsigned char u2[] = {0xC2, 0xA2, 0x00}; /* 两字节: U+00A2 */
const unsigned char u3[] = {0xE4, 0xBD, 0xA0, 0x00}; /* 三字节: U+4F60 */
const unsigned char invalid[] = {0xF0, 0x00}; /* 四字节起始(当前实现不支持) */
const unsigned char trunc2[] = {0xC2, 0x00}; /* 两字节截断 */
const unsigned char trunc3[] = {0xE4, 0xBD, 0x00}; /* 三字节截断 */
/* ASCII: 返回长度 1码点 0x41 */
ASSERT_EQ_INT(1, utf8_next(ascii, &unicode));
ASSERT_EQ_U32(0x41u, unicode);
/* 两字节 UTF-8 */
ASSERT_EQ_INT(2, utf8_next(u2, &unicode));
ASSERT_EQ_U32(0x00A2u, unicode);
/* 三字节 UTF-8 */
ASSERT_EQ_INT(3, utf8_next(u3, &unicode));
ASSERT_EQ_U32(0x4F60u, unicode);
/* 非支持输入返回 0 */
ASSERT_EQ_INT(0, utf8_next(invalid, &unicode));
ASSERT_EQ_INT(0, utf8_next(trunc2, &unicode));
ASSERT_EQ_INT(0, utf8_next(trunc3, &unicode));
return 0;
}
/* -------------------------------------------------------------------------
* 用例: test_showstr_ascii_utf8_newline_and_bounds
* 目标:
* - 验证 Lcd_ShowStr 对 ASCII/中文渲染均生效
* - 验证换行逻辑(行高 + 行间距)正确
* - 验证越界起始坐标时返回错误码
*
* 覆盖函数:
* - Lcd_ShowStr
* ------------------------------------------------------------------------- */
static int test_showstr_ascii_utf8_newline_and_bounds(void)
{
uint8_t text_a[] = "A";
uint8_t text_cn[] = "";
uint8_t text_nl[] = "A\nB";
uint8_t text_cn_overflow_x[] = "你你";
uint8_t text_nl_overflow_y[] = "A\nB";
/* ASCII 渲染: 返回 0 且字形区域应存在白点 */
Lcd_Init();
ASSERT_EQ_INT(0, Lcd_ShowStr(0, 0, text_a));
ASSERT_TRUE(count_white_in_rect(0, 0, 6, 11) > 0);
/* 中文渲染: 13x12 区域内应存在白点 */
Lcd_Init();
ASSERT_EQ_INT(0, Lcd_ShowStr(10, 10, text_cn));
ASSERT_TRUE(count_white_in_rect(10, 10, 22, 21) > 0);
/* 换行渲染: "A\nB" 中 B 应下移到 y = 12 + rowSpace(2) = 14 */
Lcd_Init();
ASSERT_EQ_INT(0, Lcd_ShowStr(0, 0, text_nl));
ASSERT_TRUE(count_white_in_rect(0, 0, 6, 11) > 0); /* A */
ASSERT_TRUE(count_white_in_rect(0, 14, 6, 25) > 0); /* B: 12 高 + 2 行距 */
/* 起始越界x 方向越界应返回 -1 */
ASSERT_EQ_INT(-1, Lcd_ShowStr(LCD_SIZE_X - 6, 0, text_a));
/* 起始越界y 方向越界应返回 -2 */
ASSERT_EQ_INT(-2, Lcd_ShowStr(0, LCD_SIZE_Y - 12, text_a));
/* 中文第 2 个字符越过右边界时应返回 -1 */
Lcd_Init();
ASSERT_EQ_INT(-1, Lcd_ShowStr(LCD_SIZE_X - 13, 0, text_cn_overflow_x));
/* 换行后若超出底部边界,应返回 -2 */
Lcd_Init();
ASSERT_EQ_INT(-2, Lcd_ShowStr(0, LCD_SIZE_Y - 13, text_nl_overflow_y));
/* 预留接口目前为桩函数,返回 0 */
ASSERT_EQ_INT(0, Lcd_ShowTest(0, 0, text_a));
return 0;
}
/* -------------------------------------------------------------------------
* 测试入口:
* 按固定顺序执行所有子用例,任一失败立即返回 1。
* 约定:
* 返回 0 表示该测试程序通过。
* ------------------------------------------------------------------------- */
int main(void)
{
if (test_lcd_init_and_pixel_rw() != 0) return 1;
if (test_fillrect_and_invert() != 0) return 1;
if (test_lineh_linev() != 0) return 1;
if (test_utf8_next_decode() != 0) return 1;
if (test_showstr_ascii_utf8_newline_and_bounds() != 0) return 1;
return 0;
}