Unity(纯C语言单元测试框架!不是那个Unity3d)配置指南

译者注:译者博客(http://blog.csdn.net/lin_strong),转载请保留这条。此为Unity手册的翻译,仅供学习交流使用,请勿用于商业用途。
翻译的资料是公开的,在docs/UnityConfigurationGuide.md,我想应该不会有什么版权问题,如涉及版权问题,请联系我删除文章。

文章目录

Unity配置指南

C 标准、编译器和微控制器

嵌入式软件世界拥有自己的挑战。编译器支持不同修订版的C标准。它们忽视一些要求,这有
时在会使得语言更有用。有时是为了简化它们的支持。有时是由于它们目标微控制器的特殊性。
模拟器们则使事情更加复杂了。

Unity被设计为可运行在几乎任何由C编译器支持的东西上。要是不需要任何配置就能实现这个
目标的话就太棒了。尽管在有些目标上已经很接近这个梦想了,但遗憾的是并不都可以。很可
能你至少需要修改一些在这篇文档中描述的配置选项。

所有的Unity配置选项都是#defines。其中大部分都是简单的定义。一些是带有参数的宏。
它们在unity_internals.h头文件中。除非你真的需要,其实不必打开那个文件。那个文件
就是跨平台库十分难搭建的一个证明。往好了想,它也证明了许多复杂性可以集中在一个地
方以使得其他地方更加一致和简单。

使用这些选项

你是在使用一个目标特定的编译器和一个模拟器或者一个本地编译器并没有什么差别。不管在
哪种情况下,你都有许多选择以配置这些选项:

  1. 因为这些选项是通过C定义来指定的,你可以通过命令行传递大部分这些选项给你的编译器。
    即使你在使用一个强制你使用它们IDE来进行所有配置的嵌入式目标,那也总会有一个地方可以
    配置你项目的编译器的定义。
  2. 你可以创建一个定制的unity_config.h配置文件(放在你工具链的搜索路径中)。在这个文件
    中,列出特定于你的目标的定义和宏。你唯一要做的就是定义UNITY_INCLUDE_CONFIG_H,然后,
    Unity会在unity_config.h中进一步寻找它可能需要的定义。

选项

整数类型

如果你做了很久的C开发者,你很可能已经知道C中整型值会随着目标平台而变化。C标准的
规矩是,int匹配目标微处理器的寄存器大小,以及int大小与其他整型大小的关系。一
个目标平台上int可能是16位的,到了另一个上可能就成64位的了。编译器中有更多遵从
C99以及之后的标准的特定类型,但当然不是每个编译器都这样。因此,Unity有许多特性能
帮助调整自己以满足你需要的整型大小。它最开始会尝试自动完成这事。

UNITY_EXCLUDE_STDINT_H

Unity猜测你的类型做的头件事是检查stdint.h。这个文件会包含定义如UINT_MAX
Unity可以用其来了解你的系统。可能你不想要它做这件事(昂,为什么?)或者(更可能)
是因为你的系统不支持stdint.h。如果是这样的话,你应该定义这个宏。这样,
Unity就知道要跳过这个文件,而你也不会得到编译错误了。

示例:

    #define UNITY_EXCLUDE_STDINT_H
UNITY_EXCLUDE_LIMITS_H

做的第二件事是检查limits.h。一些不支持stdint.h的编译器会补偿你个limits.h
如果你同样不想让Unity检查这个文件,定义这个宏以使Unity跳过这个包含。

示例:

    #define UNITY_EXCLUDE_LIMITS_H

如果你把上面两个功能都禁用了,那你就得自己进行配置了。别担心。即使这样也没那么糟…
如果你不喜欢默认配置的话,你只需要定义几个东西就好了。

UNITY_INT_WIDTH

定义这个为你系统上int的比特数。默认为32位。

示例:

    #define UNITY_INT_WIDTH 16
UNITY_LONG_WIDTH

定义这个为你系统上long的比特数。默认为32位。这被用于表明你的系统支不支持64位。
是需要指定一个long还是long long来获得64位值。在16位系统上,这个选项应该忽略。

示例:

    #define UNITY_LONG_WIDTH 16
UNITY_POINTER_WIDTH

定义这个为你系统上一个指针的比特数。默认为32位。如果你编译器警告说对指针进行了
强制类型转换,那你要看看这个。

示例:

    #define UNITY_POINTER_WIDTH 64
UNITY_SUPPORT_64

Unity如果自动探测到了系统的64位支持,或者如果你的intlong或指针宽度大于32位
的话,它会自动包含64位支持。如果其他选项都没帮你完成这事的话,你可以定义这个宏以
启用64位支持。在小目标上启用64位支持可能会对大小和速度有重大影响,所以如果你不需要
的话,不要定义它。

示例:

    #define UNITY_SUPPORT_64

浮点数类型

在嵌入式世界中,某个目标一点都不支持浮点数运算或者只支持单精度一点都不稀奇。我们
能够猜测整型的大小是因为总是有整型的,起码有一个字节的整型。另一方面,浮点数有时
根本就没有。在这些平台上尝试include float.h会导致一个错误。这导致只能人工配置。

UNITY_INCLUDE_FLOAT
UNITY_EXCLUDE_FLOAT
UNITY_INCLUDE_DOUBLE
UNITY_EXCLUDE_DOUBLE

默认的,Unity猜测你想要单精度浮点数支持,但是不想要双精度的。你可以使用这儿的
include和exclude选项来改变这个行为。你可以按你需要include这两个、其中一个、或
都不要。对于启用了的精度,以下浮点数选项也可以用。

示例:

    // 这是多奇怪的处理器?
    #define UNITY_EXCLUDE_FLOAT
    #define UNITY_INCLUDE_DOUBLE
UNITY_EXCLUDE_FLOAT_PRINT

Unity的目标是尽可能小的开销,它尽可能避免调用标准库(一些嵌入式平台没有标准库!)。
因此,它用于打印整型值的例程是极简硬编码的。因此,测试失败时是否展示浮点数值是可选
的。默认地,Unity会在断言失败时打印浮点数的真实值(比如”Expected 4.56 Was 4.68”)。
不想要这个额外支持的话,你可以使用这个宏定义来把断言失败消息替换为像
”Values Not Within Delta”。如果你想要更啰嗦的浮点数断言失败消息,使用这个选项来给出
更加正确的故障消息。

示例:

    #define UNITY_EXCLUDE_FLOAT_PRINT
UNITY_FLOAT_TYPE

如果启用了浮点数支持,Unity假设你想要你的FLOAT断言比较的是标准C float。如果你的
编译器支持特殊的浮点数类型,你可以通过使用这个定义重写这个行为。

示例:

    #define UNITY_FLOAT_TYPE float16_t
UNITY_DOUBLE_TYPE

如果启用了浮点数支持,Unity假设你想要你的DOUBLE断言比较的是标准C double。
如果你想要改变它,你可以使用这个选项指定其他东西。比如,定义UNITY_DOUBLE_TYPE
long double 可以在你的64位处理器上启用巨浮点数类型以替代标准的double

示例:

    #define UNITY_DOUBLE_TYPE long double
UNITY_FLOAT_PRECISION
UNITY_DOUBLE_PRECISION

如果你在Unity断言指南中查阅UNITY_ASSERT_EQUAL_FLOATUNITY_ASSERT_EQUAL_DOUBLE
你会发现它们实际上并不是真的断言两个值是相等的,而是"足够接近"到相等。“足够接近”
是由这些精度配置选项控制的。如果你在使用32位float和/或64位double(大部分处理器上是这样),
你应该不需要修改这些选项。它们都被设为给你大约一个有效bit的误差。float是0.00001,
double是10-12。想要了解更多的细节,见Unity断言指南的附件。

示例:

    #define UNITY_FLOAT_PRECISION 0.001f

杂项

UNITY_EXCLUDE_STDDEF_H

Unity使用NULL宏,它定义了一个null指针常量的值,默认定义在stddef.h中。如果你想要
在自己的宏中提供它,你应该通过添加这个宏定义到配置中以排除stddef.h头文件。

示例:

    #define UNITY_EXCLUDE_STDDEF_H

工具集定制

除了以上列出的选项,还有许多其他选项,它们在你需要为你特定工具链定制Unity行为时
能派上用场。可能你不需要动这些东西…但在特定的平台,特别是运行在模拟器中的那些,
可能需要做一些事情以正确运行。这些宏有助于这些情况。

UNITY_OUTPUT_CHAR(a)
UNITY_OUTPUT_FLUSH()
UNITY_OUTPUT_START()
UNITY_OUTPUT_COMPLETE()

默认的,Unity在运行时会往stdout打印它的结果。在你使用一个本地编译器来进行测试时,
这几乎总可以完美工作。在一些模拟器上也没有问题,只要这些模拟器将stdout路由回了
命令行。然而,有时模拟器不支持输出结果,或者你也许需要把结果路由到其他地方。这种
情况下,你应该定义UNITY_OUTPUT_CHAR宏。这个宏一次接受一个字符(作为int,因为这
是标志Cputchar函数最常用的输入)。你可用任意函数替代它。

示例:
假设你被迫在一个没有stdout的嵌入式处理器上运行你的测试套件。你决定路由的你测试结果
输出到一个定制的串行RS232_putc()函数,你会像这样写:

    #include "RS232_header.h"
    ...
    #define UNITY_OUTPUT_CHAR(a) RS232_putc(a)
    #define UNITY_OUTPUT_START() RS232_config(115200,1,8,0)
    #define UNITY_OUTPUT_FLUSH() RS232_flush()
    #define UNITY_OUTPUT_COMPLETE() RS232_close()

注意:
UNITY_OUTPUT_FLUSH()可以通过指定UNITY_USE_FLUSH_STDOUT很方便地被设为标志输出flush
函数。不需要其他定义。

UNITY_WEAK_ATTRIBUTE
UNITY_WEAK_PRAGMA
UNITY_NO_WEAK

对于一些目标,Unity可以让要求的setUp()和tearDown()函数变成可选的。这对于测试写作者
十分便利,因为setUp和tearDown常常什么都不做。如果你在使用gcc或clang,这个选项会自动
帮你定义。其他编译器也可以支持这个行为,如果他们支持一个称为weak函数的C特性。weak函
数是指,除非在某处定义了一个non-weak版本的同个函数,它就会被编译进可执行程序的函数。
如果发现了一个non-weak版本,weak版本就会被忽略,好像它不存在。如果你的编译器支持这
个特性,你可以通过定义UNITY_WEAK_ATTRIBUTE或UNITY_WEAK_PRAGMA为标识一个函数为weak
所需的函数属性以让Unity知道。如果你的编译器缺少对weak函数的支持,那你就得每次都定义
setUp和tearDown函数(尽管它们可以,且经常是空的)。你可以通过定义UNITY_NO_WEAK以强制
Unity不要使用weak函数。这个特性最常见的选项是:

示例:

    #define UNITY_WEAK_ATTRIBUTE weak
    #define UNITY_WEAK_ATTRIBUTE __attribute__((weak))
    #define UNITY_WEAK_PRAGMA
    #define UNITY_NO_WEAK
UNITY_PTR_ATTRIBUTE

一些编译器需要给指针一个定制的属性,像nearfar。在这些情况下,你可以通过定义
这个选项为你想要的属性以给Unity一个安全的默认值。

示例:

    #define UNITY_PTR_ATTRIBUTE __attribute__((far))
    #define UNITY_PTR_ATTRIBUTE near
UNITY_PRINT_EOL

默认的,Unity在每一行输出的最后输出\n。这对于如Ceedling这样的脚本来说很好分析,
但这不一定适用于你的系统。你可以随意的重写它,改成你想要的东西。

示例:

    #define UNITY_PRINT_EOL { UNITY_OUTPUT_CHAR('\r'); UNITY_OUTPUT_CHAR('\n') }
UNITY_EXCLUDE_DETAILS

如果你必须节省一丝一毫的内存的话,那就是这个选项了。Unity存储了一个内部便签集,其
被用于传递额外的细节信息。它被用于像CMock这样的系统以报告是哪个函数或参数出了错。
如果你不在使用CMock,并且你没将其用于其他东西,那你可以排除掉它。

示例:

    #define UNITY_EXCLUDE_DETAILS
UNITY_EXCLUDE_SETJMP

如果你的嵌入式系统不支持标准库的setjmp,你可以通过这个定义来排除Unity对setjmp的依赖。
然而,排除依赖是有代价的。你将无法为你的测试使用定制的helper函数,你将不能使用像CMock
这样的工具。但话说回来,如果你的编译器连setjmp都不支持,那很可能你甚至都没有内存放这些
东西…所以这个这个选项是为这些场合存在的。

示例:

    #define UNITY_EXCLUDE_SETJMP
UNITY_OUTPUT_COLOR

如果你想要使用ANSI转义码添加颜色,你可以使用这个定义。

示例:

    #define UNITY_OUTPUT_COLOR

深入虎穴

偶尔可能上面的选项仍无法满足你的需求。如果你在使用一个本地工具链(如Mac上的clang)
编译和执行你的测试,它们很可能完全够了。这些选项应该也足够你应对大部分从本地命令
行运行目标模拟器的情况了。但特别是如果你必须在你的目标硬件上运行你的测试套件,你
的Unity配置将需要特别的帮助。这个特别的帮助通常处于两个地方之一:main()函数或者
RUN_TEST宏。我们看看是怎么回事。

main()

每个测试模块都是自己编译和运行的,与你工程中的其他测试文件相互独立。因此每个测试
文件都有一个main函数。这个main函数将需要包含初始化你的系统到可工作状态的所有
必要代码。在你必须设置内存映射或为你的测试结果初始化输出信道时这就特别明显了。

一个简单的main函数看起来像这样子:

    int main(void) {
        UNITY_BEGIN();
        RUN_TEST(test_TheFirst);
        RUN_TEST(test_TheSecond);
        RUN_TEST(test_TheThird);
        return UNITY_END();
    }

你可以看到我们的main函数并没有接受任何参数。对于我们最基本的情况,我们不需要参数,
因为我们每次就把所有的测试都运行了。一开始,我们调用了UNITY_BEGIN。我们(以任意
想要的顺序)运行每个测试。最后,我们调用UNITY_END,返回其返回值(返回值就是失败的
总数)。

很容易看的出来,你可以在所有测试用例运行之前或者所有测试用例完成之后添加代码。
这使得你能做系统等级的setup或teardown,在一些特殊情况下可能需要。

RUN_TEST

每个测试用例函数都会使用RUN_TEST宏来调用。它的工作是展开执行单个测试用例函数
所需的各种setup和teardown。包括捕获失败,调用测试模块的setUp()tearDown()
函数并调用UnityConcludeTest()。如果使用了CMock或者测试覆盖率,这里还会使用额
外的桩。一个简单的最小RUN_TEST宏看起来像这样:

    #define RUN_TEST(testfunc) \
        UNITY_NEW_TEST(#testfunc) \
        if (TEST_PROTECT()) { \
            setUp(); \
            testfunc(); \
        } \
        if (TEST_PROTECT() && (!TEST_IS_IGNORED)) \
            tearDown(); \
        UnityConcludeTest();

所以这真是TM个宏呀,呵?它让你大概看了下对于每个测试用例,Unity需要做些什么事情。
对于每个测试用例,我们声明它是一个新的测试。然后我们运行setUp和我们的测试函数。
这些是运行在一个TEST_PROTECT语句块中的,TEST_PROTECT函数用于处理测试中发生的
失败。然后,假如我们的测试仍然在运行且没有被忽略,我们运行tearDown。不管咋样,
我们的最后一个是在继续下一个测试前结束这一个。

假如你需要添加一个对fsync的调用以强迫所有的输出数据在每个测试后flush到一个文件中。
你应该简单地插入它到你的UnityConcludeTest调用后。也许你想要在每个测试结果集前后
写一个xml标签。再次,你可以通过添加行到这个宏中来实现。更新这个宏适用于那种你需要
在整个测试套件中,在每单个测试用例前后添加行为的情况。

愉快的移植

这篇向导中的定义和宏应该能帮助你移植Unity到所有我们能想象的到的C目标上。如果你
遇到了一两个障碍,尽管在论坛上寻求帮助。我们热爱好的挑战!

Find The Latest of This And More at ThrowTheSwitch.org


以下是中英对照版


Unity Configuration Guide

Unity配置指南

C Standards, Compilers and Microcontrollers

C 标准、编译器和微控制器

The embedded software world contains its challenges. Compilers support different
revisions of the C Standard. They ignore requirements in places, sometimes to
make the language more usable in some special regard. Sometimes it’s to simplify
their support. Sometimes it’s due to specific quirks of the microcontroller they
are targeting. Simulators add another dimension to this menagerie.
嵌入式软件世界拥有自己的挑战。编译器支持不同修订版的C标准。它们忽视一些要求,这有
时在会使得语言更有用。有时是为了简化它们的支持。有时是由于它们目标微控制器的特殊性。
模拟器们则使事情更加复杂了。

Unity is designed to run on almost anything that is targeted by a C compiler. It
would be awesome if this could be done with zero configuration. While there are
some targets that come close to this dream, it is sadly not universal. It is
likely that you are going to need at least a couple of the configuration options
described in this document.
Unity被设计为可运行在几乎任何由C编译器支持的东西上。要是不需要任何配置就能实现这个
目标的话就太棒了。尽管在有些目标上已经很接近这个梦想了,但遗憾的是并不都可以。很可
能你至少需要修改一些在这篇文档中描述的配置选项。

All of Unity’s configuration options are #defines. Most of these are simple
definitions. A couple are macros with arguments. They live inside the
unity_internals.h header file. We don’t necessarily recommend opening that file
unless you really need to. That file is proof that a cross-platform library is
challenging to build. From a more positive perspective, it is also proof that a
great deal of complexity can be centralized primarily to one place to
provide a more consistent and simple experience elsewhere.
所有的Unity配置选项都是#defines。其中大部分都是简单的定义。一些是带有参数的宏。
它们在unity_internals.h头文件中。除非你真的需要,其实不必打开那个文件。那个文件
就是跨平台库十分难搭建的一个证明。往好了想,它也证明了许多复杂性可以集中在一个地
方以使得其他地方更加一致和简单。

Using These Options

使用这些选项

It doesn’t matter if you’re using a target-specific compiler and a simulator or
a native compiler. In either case, you’ve got a couple choices for configuring
these options:
你是在使用一个目标特定的编译器和一个模拟器或者一个本地编译器并没有什么差别。不管在
哪种情况下,你都有许多选择以配置这些选项:

  1. Because these options are specified via C defines, you can pass most of these
    options to your compiler through command line compiler flags. Even if you’re
    using an embedded target that forces you to use their overbearing IDE for all
    configuration, there will be a place somewhere in your project to configure
    defines for your compiler.
  2. You can create a custom unity_config.h configuration file (present in your
    toolchain’s search paths). In this file, you will list definitions and macros
    specific to your target. All you must do is define UNITY_INCLUDE_CONFIG_H and
    Unity will rely on unity_config.h for any further definitions it may need.
  3. 因为这些选项是通过C定义来指定的,你可以通过命令行传递大部分这些选项给你的编译器。
    即使你在使用一个强制你使用它们IDE来进行所有配置的嵌入式目标,那也总会有一个地方可以
    配置你项目的编译器的定义。
  4. 你可以创建一个定制的unity_config.h配置文件(放在你工具链的搜索路径中)。在这个文件
    中,列出特定于你的目标的定义和宏。你唯一要做的就是定义UNITY_INCLUDE_CONFIG_H,然后,
    Unity会在unity_config.h中进一步寻找它可能需要的定义。

The Options

选项

Integer Types

整数类型

If you’ve been a C developer for long, you probably already know that C’s
concept of an integer varies from target to target. The C Standard has rules
about the int matching the register size of the target microprocessor. It has
rules about the int and how its size relates to other integer types. An int
on one target might be 16 bits while on another target it might be 64. There are
more specific types in compilers compliant with C99 or later, but that’s
certainly not every compiler you are likely to encounter. Therefore, Unity has a
number of features for helping to adjust itself to match your required integer
sizes. It starts off by trying to do it automatically.
如果你做了很久的C开发者,你很可能已经知道C中整型值会随着目标平台而变化。C标准的
规矩是,int匹配目标微处理器的寄存器大小,以及int大小与其他整型大小的关系。一
个目标平台上int可能是16位的,到了另一个上可能就成64位的了。编译器中有更多遵从
C99以及之后的标准的特定类型,但当然不是每个编译器都这样。因此,Unity有许多特性能
帮助调整自己以满足你需要的整型大小。它最开始会尝试自动完成这事。

UNITY_EXCLUDE_STDINT_H

The first thing that Unity does to guess your types is check stdint.h.
This file includes defines like UINT_MAX that Unity can use to
learn a lot about your system. It’s possible you don’t want it to do this
(um. why not?) or (more likely) it’s possible that your system doesn’t
support stdint.h. If that’s the case, you’re going to want to define this.
That way, Unity will know to skip the inclusion of this file and you won’t
be left with a compiler error.
Unity猜测你的类型做的头件事是检查stdint.h。这个文件会包含定义如UINT_MAX
Unity可以用其来了解你的系统。可能你不想要它做这件事(昂,为什么?)或者(更可能)
是因为你的系统不支持stdint.h。如果是这样的话,你应该定义这个宏。这样,
Unity就知道要跳过这个文件,而你也不会得到编译错误了。

Example:

    #define UNITY_EXCLUDE_STDINT_H
UNITY_EXCLUDE_LIMITS_H

The second attempt to guess your types is to check limits.h. Some compilers
that don’t support stdint.h could include limits.h instead. If you don’t
want Unity to check this file either, define this to make it skip the inclusion.
做的第二件事是检查limits.h。一些不支持stdint.h的编译器会补偿你个limits.h
如果你同样不想让Unity检查这个文件,定义这个宏以使Unity跳过这个包含。

Example:

    #define UNITY_EXCLUDE_LIMITS_H

If you’ve disabled both of the automatic options above, you’re going to have to
do the configuration yourself. Don’t worry. Even this isn’t too bad… there are
just a handful of defines that you are going to specify if you don’t like the
defaults.
如果你把上面两个功能都禁用了,那你就得自己进行配置了。别担心。即使这样也没那么糟…
如果你不喜欢默认配置的话,你只需要定义几个东西就好了。

UNITY_INT_WIDTH

Define this to be the number of bits an int takes up on your system. The
default, if not autodetected, is 32 bits.
定义这个为你系统上int的比特数。默认为32位。

Example:

    #define UNITY_INT_WIDTH 16
UNITY_LONG_WIDTH

Define this to be the number of bits a long takes up on your system. The
default, if not autodetected, is 32 bits. This is used to figure out what kind
of 64-bit support your system can handle. Does it need to specify a long or a
long long to get a 64-bit value. On 16-bit systems, this option is going to be
ignored.
定义这个为你系统上long的比特数。默认为32位。这被用于表明你的系统支不支持64位。
是需要指定一个long还是long long来获得64位值。在16位系统上,这个选项应该忽略。

Example:

    #define UNITY_LONG_WIDTH 16
UNITY_POINTER_WIDTH

Define this to be the number of bits a pointer takes up on your system. The
default, if not autodetected, is 32-bits. If you’re getting ugly compiler
warnings about casting from pointers, this is the one to look at.
定义这个为你系统上一个指针的比特数。默认为32位。如果你编译器警告说对指针进行了
强制类型转换,那你要看看这个。

Example:

    #define UNITY_POINTER_WIDTH 64
UNITY_SUPPORT_64

Unity will automatically include 64-bit support if it auto-detects it, or if
your int, long, or pointer widths are greater than 32-bits. Define this to
enable 64-bit support if none of the other options already did it for you. There
can be a significant size and speed impact to enabling 64-bit support on small
targets, so don’t define it if you don’t need it.
Unity如果自动探测到了系统的64位支持,或者如果你的intlong或指针宽度大于32位
的话,它会自动包含64位支持。如果其他选项都没帮你完成这事的话,你可以定义这个宏以
启用64位支持。在小目标上启用64位支持可能会对大小和速度有重大影响,所以如果你不需要
的话,不要定义它。

Example:

    #define UNITY_SUPPORT_64

Floating Point Types

浮点数类型

In the embedded world, it’s not uncommon for targets to have no support for
floating point operations at all or to have support that is limited to only
single precision. We are able to guess integer sizes on the fly because integers
are always available in at least one size. Floating point, on the other hand, is
sometimes not available at all. Trying to include float.h on these platforms
would result in an error. This leaves manual configuration as the only option.
在嵌入式世界中,某个目标一点都不支持浮点数运算或者只支持单精度一点都不稀奇。我们
能够猜测整型的大小是因为总是有整型的,起码有一个字节的整型。另一方面,浮点数有时
根本就没有。在这些平台上尝试include float.h会导致一个错误。这导致只能人工配置。

UNITY_INCLUDE_FLOAT
UNITY_EXCLUDE_FLOAT
UNITY_INCLUDE_DOUBLE
UNITY_EXCLUDE_DOUBLE

By default, Unity guesses that you will want single precision floating point
support, but not double precision. It’s easy to change either of these using the
include and exclude options here. You may include neither, either, or both, as
suits your needs. For features that are enabled, the following floating point
options also become available.
默认的,Unity猜测你想要单精度浮点数支持,但是不想要双精度的。你可以使用这儿的
include和exclude选项来改变这个行为。你可以按你需要include这两个、其中一个、或
都不要。对于启用了的精度,以下浮点数选项也可以用。

Example:

    //what manner of strange processor is this?
    #define UNITY_EXCLUDE_FLOAT
    #define UNITY_INCLUDE_DOUBLE
UNITY_EXCLUDE_FLOAT_PRINT

Unity aims for as small of a footprint as possible and avoids most standard
library calls (some embedded platforms don’t have a standard library!). Because
of this, its routines for printing integer values are minimalist and hand-coded.
Therefore, the display of floating point values during a failure are optional.
By default, Unity will print the actual results of floating point assertion
failure (e.g. ”Expected 4.56 Was 4.68”). To not include this extra support, you
can use this define to instead respond to a failed assertion with a message like
”Values Not Within Delta”. If you would like verbose failure messages for floating
point assertions, use these options to give more explicit failure messages.
Unity的目标是尽可能小的开销,它尽可能避免调用标准库(一些嵌入式平台没有标准库!)。
因此,它用于打印整型值的例程是极简硬编码的。因此,测试失败时是否展示浮点数值是可选
的。默认地,Unity会在断言失败时打印浮点数的真实值(比如”Expected 4.56 Was 4.68”)。
不想要这个额外支持的话,你可以使用这个宏定义来把断言失败消息替换为像
”Values Not Within Delta”。如果你想要更啰嗦的浮点数断言失败消息,使用这个选项来给出
更加正确的故障消息。

Example:

    #define UNITY_EXCLUDE_FLOAT_PRINT
UNITY_FLOAT_TYPE

If enabled, Unity assumes you want your FLOAT asserts to compare standard C
floats. If your compiler supports a specialty floating point type, you can
always override this behavior by using this definition.
如果启用了浮点数支持,Unity假设你想要你的FLOAT断言比较的是标准C float。如果你的
编译器支持特殊的浮点数类型,你可以通过使用这个定义重写这个行为。

Example:

    #define UNITY_FLOAT_TYPE float16_t
UNITY_DOUBLE_TYPE

If enabled, Unity assumes you want your DOUBLE asserts to compare standard C
doubles. If you would like to change this, you can specify something else by
using this option. For example, defining UNITY_DOUBLE_TYPE to long double
could enable gargantuan floating point types on your 64-bit processor instead of
the standard double.
如果启用了浮点数支持,Unity假设你想要你的DOUBLE断言比较的是标准C double。
如果你想要改变它,你可以使用这个选项指定其他东西。比如,定义UNITY_DOUBLE_TYPE
long double 可以在你的64位处理器上启用巨浮点数类型以替代标准的double

Example:

    #define UNITY_DOUBLE_TYPE long double
UNITY_FLOAT_PRECISION
UNITY_DOUBLE_PRECISION

If you look up UNITY_ASSERT_EQUAL_FLOAT and UNITY_ASSERT_EQUAL_DOUBLE as
documented in the big daddy Unity Assertion Guide, you will learn that they are
not really asserting that two values are equal but rather that two values are
“close enough” to equal. “Close enough” is controlled by these precision
configuration options. If you are working with 32-bit floats and/or 64-bit
doubles (the normal on most processors), you should have no need to change these
options. They are both set to give you approximately 1 significant bit in either
direction. The float precision is 0.00001 while the double is 10-12.
For further details on how this works, see the appendix of the Unity Assertion
Guide.
如果你在Unity断言指南中查阅UNITY_ASSERT_EQUAL_FLOATUNITY_ASSERT_EQUAL_DOUBLE
你会发现它们实际上并不是真的断言两个值是相等的,而是"足够接近"到相等。“足够接近”
是由这些精度配置选项控制的。如果你在使用32位float和/或64位double(大部分处理器上是这样),
你应该不需要修改这些选项。它们都被设为给你大约一个有效bit的误差。float是0.00001,
double是10-12。想要了解更多的细节,见Unity断言指南的附件。

Example:

    #define UNITY_FLOAT_PRECISION 0.001f

Miscellaneous

杂项

UNITY_EXCLUDE_STDDEF_H

Unity uses the NULL macro, which defines the value of a null pointer constant,
defined in stddef.h by default. If you want to provide
your own macro for this, you should exclude the stddef.h header file by adding this
define to your configuration.
Unity使用NULL宏,它定义了一个null指针常量的值,默认定义在stddef.h中。如果你想要
在自己的宏中提供它,你应该通过添加这个宏定义到配置中以排除stddef.h头文件。

Example:

    #define UNITY_EXCLUDE_STDDEF_H

Toolset Customization

工具集定制

In addition to the options listed above, there are a number of other options
which will come in handy to customize Unity’s behavior for your specific
toolchain. It is possible that you may not need to touch any of these… but
certain platforms, particularly those running in simulators, may need to jump
through extra hoops to run properly. These macros will help in those
situations.
除了以上列出的选项,还有许多其他选项,它们在你需要为你特定工具链定制Unity行为时
能派上用场。可能你不需要动这些东西…但在特定的平台,特别是运行在模拟器中的那些,
可能需要做一些事情以正确运行。这些宏有助于这些情况。

UNITY_OUTPUT_CHAR(a)
UNITY_OUTPUT_FLUSH()
UNITY_OUTPUT_START()
UNITY_OUTPUT_COMPLETE()

By default, Unity prints its results to stdout as it runs. This works
perfectly fine in most situations where you are using a native compiler for
testing. It works on some simulators as well so long as they have stdout
routed back to the command line. There are times, however, where the simulator
will lack support for dumping results or you will want to route results
elsewhere for other reasons. In these cases, you should define the
UNITY_OUTPUT_CHAR macro. This macro accepts a single character at a time (as
an int, since this is the parameter type of the standard C putchar function
most commonly used). You may replace this with whatever function call you like.
默认的,Unity在运行时会往stdout打印它的结果。在你使用一个本地编译器来进行测试时,
这几乎总可以完美工作。在一些模拟器上也没有问题,只要这些模拟器将stdout路由回了
命令行。然而,有时模拟器不支持输出结果,或者你也许需要把结果路由到其他地方。这种
情况下,你应该定义UNITY_OUTPUT_CHAR宏。这个宏一次接受一个字符(作为int,因为这
是标志Cputchar函数最常用的输入)。你可用任意函数替代它。

Example:
Say you are forced to run your test suite on an embedded processor with no
stdout option. You decide to route your test result output to a custom serial
RS232_putc() function you wrote like thus:
假设你被迫在一个没有stdout的嵌入式处理器上运行你的测试套件。你决定路由的你测试结果
输出到一个定制的串行RS232_putc()函数,你会像这样写:

    #include "RS232_header.h"
    ...
    #define UNITY_OUTPUT_CHAR(a) RS232_putc(a)
    #define UNITY_OUTPUT_START() RS232_config(115200,1,8,0)
    #define UNITY_OUTPUT_FLUSH() RS232_flush()
    #define UNITY_OUTPUT_COMPLETE() RS232_close()

Note:
UNITY_OUTPUT_FLUSH() can be set to the standard out flush function simply by
specifying UNITY_USE_FLUSH_STDOUT. No other defines are required.
注意:
UNITY_OUTPUT_FLUSH()可以通过指定UNITY_USE_FLUSH_STDOUT很方便地被设为标志输出flush
函数。不需要其他定义。

UNITY_WEAK_ATTRIBUTE
UNITY_WEAK_PRAGMA
UNITY_NO_WEAK

For some targets, Unity can make the otherwise required setUp() and tearDown()
functions optional. This is a nice convenience for test writers since setUp and
tearDown don’t often actually do anything. If you’re using gcc or clang, this
option is automatically defined for you. Other compilers can also support this
behavior, if they support a C feature called weak functions. A weak function is
a function that is compiled into your executable unless a non-weak version of
the same function is defined elsewhere. If a non-weak version is found, the weak
version is ignored as if it never existed. If your compiler supports this feature,
you can let Unity know by defining UNITY_WEAK_ATTRIBUTE or UNITY_WEAK_PRAGMA as
the function attributes that would need to be applied to identify a function as
weak. If your compiler lacks support for weak functions, you will always need to
define setUp and tearDown functions (though they can be and often will be just
empty). You can also force Unity to NOT use weak functions by defining
UNITY_NO_WEAK. The most common options for this feature are:
对于一些目标,Unity可以让要求的setUp()和tearDown()函数变成可选的。这对于测试写作者
十分便利,因为setUp和tearDown常常什么都不做。如果你在使用gcc或clang,这个选项会自动
帮你定义。其他编译器也可以支持这个行为,如果他们支持一个称为weak函数的C特性。weak函
数是指,除非在某处定义了一个non-weak版本的同个函数,它就会被编译进可执行程序的函数。
如果发现了一个non-weak版本,weak版本就会被忽略,好像它不存在。如果你的编译器支持这
个特性,你可以通过定义UNITY_WEAK_ATTRIBUTE或UNITY_WEAK_PRAGMA为标识一个函数为weak
所需的函数属性以让Unity知道。如果你的编译器缺少对weak函数的支持,那你就得每次都定义
setUp和tearDown函数(尽管它们可以,且经常是空的)。你可以通过定义UNITY_NO_WEAK以强制
Unity不要使用weak函数。这个特性最常见的选项是:

Example:

    #define UNITY_WEAK_ATTRIBUTE weak
    #define UNITY_WEAK_ATTRIBUTE __attribute__((weak))
    #define UNITY_WEAK_PRAGMA
    #define UNITY_NO_WEAK
UNITY_PTR_ATTRIBUTE

Some compilers require a custom attribute to be assigned to pointers, like
near or far. In these cases, you can give Unity a safe default for these by
defining this option with the attribute you would like.
一些编译器需要给指针一个定制的属性,像nearfar。在这些情况下,你可以通过定义
这个选项为你想要的属性以给Unity一个安全的默认值。

Example:

    #define UNITY_PTR_ATTRIBUTE __attribute__((far))
    #define UNITY_PTR_ATTRIBUTE near
UNITY_PRINT_EOL

By default, Unity outputs \n at the end of each line of output. This is easy
to parse by the scripts, by Ceedling, etc, but it might not be ideal for YOUR
system. Feel free to override this and to make it whatever you wish.
默认的,Unity在每一行输出的最后输出\n。这对于如Ceedling这样的脚本来说很好分析,
但这不一定适用于你的系统。你可以随意的重写它,改成你想要的东西。

Example:

    #define UNITY_PRINT_EOL { UNITY_OUTPUT_CHAR('\r'); UNITY_OUTPUT_CHAR('\n') }
UNITY_EXCLUDE_DETAILS

This is an option for if you absolutely must squeeze every byte of memory out of
your system. Unity stores a set of internal scratchpads which are used to pass
extra detail information around. It’s used by systems like CMock in order to
report which function or argument flagged an error. If you’re not using CMock and
you’re not using these details for other things, then you can exclude them.
如果你必须节省一丝一毫的内存的话,那就是这个选项了。Unity存储了一个内部便签集,其
被用于传递额外的细节信息。它被用于像CMock这样的系统以报告是哪个函数或参数出了错。
如果你不在使用CMock,并且你没将其用于其他东西,那你可以排除掉它。

Example:

    #define UNITY_EXCLUDE_DETAILS
UNITY_EXCLUDE_SETJMP

If your embedded system doesn’t support the standard library setjmp, you can
exclude Unity’s reliance on this by using this define. This dropped dependence
comes at a price, though. You will be unable to use custom helper functions for
your tests, and you will be unable to use tools like CMock. Very likely, if your
compiler doesn’t support setjmp, you wouldn’t have had the memory space for those
things anyway, though… so this option exists for those situations.
如果你的嵌入式系统不支持标准库的setjmp,你可以通过这个定义来排除Unity对setjmp的依赖。
然而,排除依赖是有代价的。你将无法为你的测试使用定制的helper函数,你将不能使用像CMock
这样的工具。但话说回来,如果你的编译器连setjmp都不支持,那很可能你甚至都没有内存放这些
东西…所以这个这个选项是为这些场合存在的。

Example:

    #define UNITY_EXCLUDE_SETJMP
UNITY_OUTPUT_COLOR

If you want to add color using ANSI escape codes you can use this define.
如果你想要使用ANSI转义码添加颜色,你可以使用这个定义。

Example:

    #define UNITY_OUTPUT_COLOR

Getting Into The Guts

深入虎穴

There will be cases where the options above aren’t quite going to get everything
perfect. They are likely sufficient for any situation where you are compiling
and executing your tests with a native toolchain (e.g. clang on Mac). These
options may even get you through the majority of cases encountered in working
with a target simulator run from your local command line. But especially if you
must run your test suite on your target hardware, your Unity configuration will
require special help. This special help will usually reside in one of two
places: the main() function or the RUN_TEST macro. Let’s look at how these
work.
偶尔可能上面的选项仍无法满足你的需求。如果你在使用一个本地工具链(如Mac上的clang)
编译和执行你的测试,它们很可能完全够了。这些选项应该也足够你应对大部分从本地命令
行运行目标模拟器的情况了。但特别是如果你必须在你的目标硬件上运行你的测试套件,你
的Unity配置将需要特别的帮助。这个特别的帮助通常处于两个地方之一:main()函数或者
RUN_TEST宏。我们看看是怎么回事。

main()

Each test module is compiled and run on its own, separate from the other test
files in your project. Each test file, therefore, has a main function. This
main function will need to contain whatever code is necessary to initialize
your system to a workable state. This is particularly true for situations where
you must set up a memory map or initialize a communication channel for the
output of your test results.
每个测试模块都是自己编译和运行的,与你工程中的其他测试文件相互独立。因此每个测试
文件都有一个main函数。这个main函数将需要包含初始化你的系统到可工作状态的所有
必要代码。在你必须设置内存映射或为你的测试结果初始化输出信道时这就特别明显了。

A simple main function looks something like this:
一个简单的main函数看起来像这样子:

    int main(void) {
        UNITY_BEGIN();
        RUN_TEST(test_TheFirst);
        RUN_TEST(test_TheSecond);
        RUN_TEST(test_TheThird);
        return UNITY_END();
    }

You can see that our main function doesn’t bother taking any arguments. For our
most barebones case, we’ll never have arguments because we just run all the
tests each time. Instead, we start by calling UNITY_BEGIN. We run each test
(in whatever order we wish). Finally, we call UNITY_END, returning its return
value (which is the total number of failures).
你可以看到我们的main函数并没有接受任何参数。对于我们最基本的情况,我们不需要参数,
因为我们每次就把所有的测试都运行了。一开始,我们调用了UNITY_BEGIN。我们(以任意
想要的顺序)运行每个测试。最后,我们调用UNITY_END,返回其返回值(返回值就是失败的
总数)。

It should be easy to see that you can add code before any test cases are run or
after all the test cases have completed. This allows you to do any needed
system-wide setup or teardown that might be required for your special
circumstances.
很容易看的出来,你可以在所有测试用例运行之前或者所有测试用例完成之后添加代码。
这使得你能做系统等级的setup或teardown,在一些特殊情况下可能需要。

RUN_TEST

The RUN_TEST macro is called with each test case function. Its job is to
perform whatever setup and teardown is necessary for executing a single test
case function. This includes catching failures, calling the test module’s
setUp() and tearDown() functions, and calling UnityConcludeTest(). If
using CMock or test coverage, there will be additional stubs in use here. A
simple minimalist RUN_TEST macro looks something like this:
每个测试用例函数都会使用RUN_TEST宏来调用。它的工作是展开执行单个测试用例函数
所需的各种setup和teardown。包括捕获失败,调用测试模块的setUp()tearDown()
函数并调用UnityConcludeTest()。如果使用了CMock或者测试覆盖率,这里还会使用额
外的桩。一个简单的最小RUN_TEST宏看起来像这样:

    #define RUN_TEST(testfunc) \
        UNITY_NEW_TEST(#testfunc) \
        if (TEST_PROTECT()) { \
            setUp(); \
            testfunc(); \
        } \
        if (TEST_PROTECT() && (!TEST_IS_IGNORED)) \
            tearDown(); \
        UnityConcludeTest();

So that’s quite a macro, huh? It gives you a glimpse of what kind of stuff Unity
has to deal with for every single test case. For each test case, we declare that
it is a new test. Then we run setUp and our test function. These are run
within a TEST_PROTECT block, the function of which is to handle failures that
occur during the test. Then, assuming our test is still running and hasn’t been
ignored, we run tearDown. No matter what, our last step is to conclude this
test before moving on to the next.
所以这真是TM个宏呀,呵?它让你大概看了下对于每个测试用例,Unity需要做些什么事情。
对于每个测试用例,我们声明它是一个新的测试。然后我们运行setUp和我们的测试函数。
这些是运行在一个TEST_PROTECT语句块中的,TEST_PROTECT函数用于处理测试中发生的
失败。然后,假如我们的测试仍然在运行且没有被忽略,我们运行tearDown。不管咋样,
我们的最后一个是在继续下一个测试前结束这一个。

Let’s say you need to add a call to fsync to force all of your output data to
flush to a file after each test. You could easily insert this after your
UnityConcludeTest call. Maybe you want to write an xml tag before and after
each result set. Again, you could do this by adding lines to this macro. Updates
to this macro are for the occasions when you need an action before or after
every single test case throughout your entire suite of tests.
假如你需要添加一个对fsync的调用以强迫所有的输出数据在每个测试后flush到一个文件中。
你应该简单地插入它到你的UnityConcludeTest调用后。也许你想要在每个测试结果集前后
写一个xml标签。再次,你可以通过添加行到这个宏中来实现。更新这个宏适用于那种你需要
在整个测试套件中,在每单个测试用例前后添加行为的情况。

Happy Porting

愉快的移植

The defines and macros in this guide should help you port Unity to just about
any C target we can imagine. If you run into a snag or two, don’t be afraid of
asking for help on the forums. We love a good challenge!
这篇向导中的定义和宏应该能帮助你移植Unity到所有我们能想象的到的C目标上。如果你
遇到了一两个障碍,尽管在论坛上寻求帮助。我们热爱好的挑战!

Find The Latest of This And More at ThrowTheSwitch.org

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值