是否可以对嵌入式软件的 C 宏进行单元测试?

yav*_*ens 2 c macros unit-testing avr

我是如何为嵌入式软件测试一些特定的 C 宏的。

例如,如果我有以下宏:

/*
Set a pin as an input
port  (B,C, D or E) 
pin  - pin to set (0-7)
*/
#define _SET_INPUT_PIN(port,pin) DDR ## port &= ~(1<<pin)
#define SET_INPUT_PIN(...) _SET_INPUT_PIN(__VA_ARGS__)
Run Code Online (Sandbox Code Playgroud)

我想用两个端口测试它,一个存在(LED_OK)另一个不存在(LED_FAIL):

#define LED_OK   D,3
#define LED_FAIL D,8
Run Code Online (Sandbox Code Playgroud)

当我尝试测试它时,LED_OK 和 LED_FAIL 都可以正常工作,但某种警告/失败应该提醒 LED_FAIL 不存在,因为 PORTD 只定义了引脚 0 到 7。

那么,当我通过LED 时,如何检查“ pin ”是否在范围内?

Lun*_*din 5

这太晦涩了。一般来说,我强烈建议不要隐藏超琐碎的东西,比如设置/清除抽象层后面的引脚。

相反,您应该编写如下内容:

#define LED_DDR  DDRD
#define LED_PORT PORTD
#define LED_PIN  (1u << 3)

LED_DDR  |= LED_PIN;  // set pin to output
LED_PORT |= LED_PIN;  // set pin to 1
LED_PORT &= ~LED_PIN; // set pin to 0
LED_PORT &= (uint8_t)~LED_PIN; // set pin to 0 with pedantic type safety (MISRA-C etc)
LED_PORT ^= LED_PIN;  // toggle pin
Run Code Online (Sandbox Code Playgroud)

您无法真正编写更清晰的代码,因此超出此范围的任何抽象层都注定要失败。它不会增加清晰度,但可能会增加错误。从人们试图这样做的 SO/EE 站点上的数千个粗略尝试中可以看出。或者从硅供应商的膨胀软件库中看到。


因此,适当的测试不会测试晦涩的宏,SET_INPUT_PIN而是质疑它的存在。测试的名称是代码审查。这也会指出以下问题:

  • 绝对没有必要把它变成一个可变参数宏,因为它应该只接受 2 个参数——不多也不少。这不过是纯粹的混淆。
  • 此宏不提供类型安全。调用者实际上可以将任何内容传递给它,并且有很大的机会静默传递编译。
  • 以下划线开头,后跟大写字母的标识符是为库保留的。所以不要命名宏_S等,它可能会与编译器库发生冲突。
  • 宏写得很天真。那里的平均编码标准将( (DDR ## port) &= ~(1 <<(pin)) )在宏参数和最终表达式周围强制使用括号。
  • 该宏使用有1符号整数常量,一旦我们在可能具有 16 位int. 然后我们会到处都是未定义的行为错误。应该是1u
  • 有隐式提升,所以宏实际上返回一个 16 位负值int。这是无益和危险的。