假设我们有以下复杂的 struct (在C中).它包括固定长度的数组、不同大小的成员、枚举和其他 struct .此 struct 未打包,无法打包.
struct {
uint8_t smallNum;
/* uint8_t align0 */
/* uint8_t align1 */
/* uint8_t align2 */
uint32_t arrayOfBigNums[5];
bool isFalse;
/* uint8_t align0 */
/* uint8_t align1 */
/* uint8_t align2 */
struct myOtherStruct;
/* uint8_t align0 */
enum mySmallEnum;
...
uint8_t aByte;
/* uint8_t align0 */
/* uint8_t align1 */
} myStruct;
为了进行单元测试,我们想用随机数据来初始化这个 struct .测试包括获取该 struct 、将其序列化、将其写入闪存、读取、反序列化,然后判断数据是否没有更改.
或者生成所有可能的值,或者生成一个较小的随机子集并对其进行测试.那么,人们将如何着手做这件事呢?
直接的解决办法如下:
myStruct testData = {0};
myStruct readData = {0};
testData.smallNum = rand();
testData.arrayOfBigNums[0] = rand();
testData.arrayOfBigNums[1] = rand();
....
testData.aByte = rand();
save(&testData);
load(&readData);
int ret = memcmp(&readData, &testData, sizeof(myStruct));
// ret == 0. Good!
但该解决方案不可扩展.向 struct 添加另一个字段或增加数组将需要更改单元测试.此外,它将是一个很长的初始化函数,容易出现人为错误.
另一种解决方案是生成一个随机的字节数组,然后将其memcpy
写入我们的 struct .虽然这是一个很好的 idea ,但实际上它行不通,因为我们的 struct 没有打包.有些字节现在为0,将来也始终为0.
myStruct testData = {0};
myStruct readData = {0};
uint8_t* randomData = genRandomData(sizeof(myStruct));
memcpy((uint8_t*)&testData, randomData, sizeof(myStruct));
save(&testData);
load(&readData);
int ret = memcmp(&readData, &testData, sizeof(myStruct));
// ret != 0 because deserialization will fill the struct properly.
// Ignoring junk bytes in padding
我目前的工作解决方案是在前一个方案的基础上迭代改进.使用0xff初始化 struct ,将其强制转换为字节数组,并使用所有内容对其进行掩码.但据我所知,这是无害的.
myStruct structMask = {MACRO_TO_FILL_WITH_MANY(-1)};
myStruct testData = {0};
myStruct readData = {0};
uint8_t* randomData = genRandomData(sizeof(myStruct));
memcpy((uint8_t*)&testData, randomData, sizeof(myStruct));
for(size_t i = 0; i < sizeof(myStruct); i++){
((uint8_t*)&testData)[i] &= ((uint8_t*)&structMask)[i];
}
save(&testData);
load(&readData);
int ret = memcmp(&readData, &testData, sizeof(myStruct));
// ret != 0 because deserialization will fill the struct properly.
// Ignoring junk bytes in padding
该解决方案也不是最优的,因为它没有考虑枚举限制.像bool(来自stdbool.h)这样的类型被存储为0/1,这很好,但是枚举仍然可以有未定义的值.因此,我们需要手动对所有枚举值取模.
寻找更通用/更健壮的解决方案.