您描述的是低级 I/O 功能。由于fmemopen() 不是 100% 可移植的(在 Linux 上,我怀疑它会吱吱作响),那么您需要为自己提供一些您编写的可移植的东西,它足够接近,以便您可以(仅)在必要时使用您的代理函数并使用尽可能使用本机功能。当然,即使在您的原生环境中,您也应该能够强制使用您的函数,以便您可以测试您的代码。
可以使用已知数据测试此代码,以确保您提取输入流中的所有字符并可以忠实地返回它们。如果原始数据采用特定的字节序,您可以确保您的“更大”类型——假设是stream_read-uint2()、stream_read_uint4()、stream_read_string() 等函数——都表现得适当。对于这个阶段,您并不真正需要实际数据;您可以制造适合自己和测试的数据。
一旦完成,您还需要编写代码来读取较大类型的数据,并确保这些更高级别的函数实际上可以准确地解释二进制数据并调用适当的操作。为此,您最终需要提供格式的示例;在此阶段之前,您可能可以摆脱自己制造的数据。但是,一旦您阅读了实际文件,您就需要处理这些文件的示例。或者,您必须根据自己的理解来制造它们,并尽可能地进行测试。这有多容易取决于二进制格式记录的清楚程度。
其中一个关键的测试和调试工具将是可以为您呈现数据的规范“转储”功能。我使用的方案是:
extern void dump_XyzType(FILE *fp, const char *tag, const XyzType *data);
流是不言而喻的;通常是stderr,但是通过将其作为参数,您可以将数据获取到任何打开的文件中。 tag 包含在打印的信息中;它应该是唯一的,以识别呼叫的位置。最后一个参数是指向数据类型的指针。您可以分析并打印它。您应该借此机会断言所有您能想到的有效性检查,以防止出现问题。
您可以使用, const char *file, int line, const char *func 扩展接口,并安排在调用中添加__FILE__、__LINE__ 和__func__。我从来没有完全需要它,但如果我要这样做,我会使用:
#define DUMP_XyzType(fp, tag, data) \
dump_XyzType(fp, tag, data, __FILE__, __LINE__, __func__)
举个例子,我处理一个DATETIME类型,所以我有一个函数
extern void dump_datetime(FILE *fp, const char *tag, const ifx_dtime_t *dp);
我这周使用的一个测试可以被说服转储一个日期时间值,它给出了:
DATETIME: Input value -- address 0x7FFF2F27CAF0
Qualifier: 3594 -- type DATETIME YEAR TO SECOND
DECIMAL: +20120913212219 -- address 0x7FFF2F27CAF2
E: +7, S = 1 (+), N = 7, M = 20 12 09 13 21 22 19
您可能会或可能不会在其中看到值2012-09-13 21:22:19。有趣的是,这个函数本身调用了家族中的另一个函数dump_decimal() 来打印出十进制值。一年后,我会升级限定符打印以包括十六进制版本,它更容易阅读(3594 是 0x0E0A,这对于已知为 14 位 (E) 的人来说很容易理解,从 YEAR 开始(第二0) 到秒(A),从十进制版本来看肯定不是那么明显。当然,信息是类型字符串中的:DATETIME YEAR TO SECOND。(十进制格式对局外人来说有点难以理解,但很清楚对于知道有一个指数 (E)、一个符号 (S)、许多(百分位)数字 (N = 7) 和实际数字 (M = ...) 的内部人员。是的,这个名字 decimal严格来说是用词不当,因为它使用以 100 为底或百分数表示。)
默认情况下,该测试不会产生该级别的详细信息,但我只需使用足够高级别的调试集(通过命令行选项)运行它。我认为这是另一个有价值的功能。
运行测试的最安静方式产生:
test.bigintcvasc.......PASS (phases: 4 of 4 run, 4 pass, 0 fail)(tests: 92 run, 89 pass, 3 fail, 3 expected failures)
test.deccvasc..........PASS (phases: 4 of 4 run, 4 pass, 0 fail)(tests: 60 run, 60 pass, 0 fail)
test.decround..........PASS (phases: 1 of 1 run, 1 pass, 0 fail)(tests: 89 run, 89 pass, 0 fail)
test.dtcvasc...........PASS (phases: 25 of 25 run, 25 pass, 0 fail)(tests: 97 run, 97 pass, 0 fail)
test.interval..........PASS (phases: 15 of 15 run, 15 pass, 0 fail)(tests: 178 run, 178 pass, 0 fail)
test.intofmtasc........PASS (phases: 2 of 2 run, 2 pass, 0 fail)(tests: 12 run, 8 pass, 4 fail, 4 expected failures)
test.rdtaddinv.........PASS (phases: 3 of 3 run, 3 pass, 0 fail)(tests: 69 run, 69 pass, 0 fail)
test.rdtimestr.........PASS (phases: 1 of 1 run, 1 pass, 0 fail)(tests: 16 run, 16 pass, 0 fail)
test.rdtsub............PASS (phases: 1 of 1 run, 1 pass, 0 fail)(tests: 19 run, 15 pass, 4 fail, 4 expected failures)
每个程序都标识自己及其状态(通过或失败)和汇总统计信息。我一直在寻找错误并修复我偶然发现的错误以外的错误,因此存在一些“预期失败”。那应该是暂时的状态;它允许我合法地声称测试都通过了。如果我想要更多细节,我可以运行任何阶段的任何测试(测试的子集有些相关,尽管“有点”实际上是任意的),并查看完整的结果等。如图所示,运行这组测试只需不到一秒钟的时间。
我发现这在有重复计算的情况下很有用 - 但我不得不在某个时候计算或验证每一项测试的正确答案。