Merge branch 'doc/unit_test_multiple_targets' into 'master'

doc: add multi-target rules for writing unit tests

See merge request espressif/esp-idf!7438
This commit is contained in:
Ivan Grokhotkov 2020-02-18 23:30:02 +08:00
commit 75d0f38108
2 changed files with 108 additions and 2 deletions

View file

@ -25,7 +25,7 @@ Tests are added in a function in the C file as follows:
The first argument is a descriptive name for the test, the second argument is an identifier in square brackets.
Identifiers are used to group related test, or tests with specific properties.
.. note::
.. note::
There is no need to add a main function with ``UNITY_BEGIN()`` and ``UNITY_END()`` in each test case. ``unity_platform.c`` will run ``UNITY_BEGIN()`` autonomously, and run the test cases, then call ``UNITY_END()``.
The ``test`` subdirectory should contain a :ref:`component CMakeLists.txt <component-directories>`, since they are themselves, components. ESP-IDF uses the ``unity`` test framework and should be specified as a requirement for the component. Normally, components :ref:`should list their sources manually <cmake-file-globbing>`; for component tests however, this requirement is relaxed and the use of the ``SRC_DIRS`` argument in ``idf_component_register`` is advised.
@ -119,6 +119,70 @@ To support this, we can define multi-stage test cases, to group a set of test fu
Multi-stage test cases present a group of test functions to users. It needs user interactions (select cases and select different stages) to run the case.
Tests For Different Targets
---------------------------
Some tests (especially those related to hardware) cannot run on all targets. Below is a guide how
to make your unit tests run on only specified targets.
1. Wrap your test code by ``!(TEMPORARY_)DISABLED_FOR_TARGETS()`` macros and place them either in
the original test file, or sepeprate the code into files grouped by functions, but make sure all
these files will be processed by the compiler. E.g.: ::
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32, ESP8266)
TEST_CASE("a test that is not ready for esp32 and esp8266 yet", "[]")
{
}
#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32, ESP8266)
Once you need one of the tests to be compiled on a specified target, just modify the targets
in the disabled list. It's more encouraged to use some general conception that can be
described in ``soc_caps.h`` to control the disabling of tests. If this is done but some of the
tests are not ready yet, use both of them (and remove ``!(TEMPORARY_)DISABLED_FOR_TARGETS()``
later). E.g.: ::
#if SOC_SDIO_SLAVE_SUPPORTED
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP64)
TEST_CASE("a sdio slave tests that is not ready for esp64 yet", "[sdio_slave]")
{
//available for esp32 now, and will be available for esp64 in the future
}
#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP64)
#endif //SOC_SDIO_SLAVE_SUPPORTED
2. For test code that you are 100% for sure that will not be supported (e.g. no peripheral at
all), use ``DISABLED_FOR_TARGETS``; for test code that should be disabled temporarily, or due to
lack of runners, etc., use ``TEMPORARY_DISABLED_FOR_TARGETS``.
Some old ways of disabling unit tests for targets, that have obvious disadvantages, are deprecated:
- DON'T put the test code under ``test/target`` folder and use CMakeLists.txt to choose one of the
target folder. This is prevented because test code is more likely to be reused than the
implementations. If you put something into ``test/esp32`` just to avoid building it on esp32s2,
it's hard to make the code tidy if you want to enable the test again on esp32s3.
- DON'T use ``CONFIG_IDF_TARGET_xxx`` macros to disable the test items any more. This makes it
harder to track disabled tests and enable them again. Also, a black-list style ``#if !disabled``
is preferred to white-list style ``#if CONFIG_IDF_TARGET_xxx``, since you will not silently
disable cases when new targets are added in the future. But for test implementations, it's
allowed to use ``#if CONFIG_IDF_TARGET_xxx`` to pick one of the implementation code.
- Test item: some items that will be performed on some targets, but skipped on other
targets. E.g.
There are three test items SD 1-bit, SD 4-bit and SDSPI. For ESP32S2, which doesn't have
SD host, among the tests only SDSPI is enabled on ESP32S2.
- Test implementation: some code will always happen, but in different ways. E.g.
There is no SDIO PKT_LEN register on ESP8266. If you want to get the length from the slave
as a step in the test process, you can have different implementation code protected by
``#if CONFIG_IDF_TARGET_`` reading in different ways.
But please avoid using ``#else`` macro. When new target is added, the test case will fail at
building stage, so that the maintainer will be aware of this, and choose one of the
implementations explicitly.
Building Unit Test App
----------------------

View file

@ -34,7 +34,7 @@ C 文件可以包含多个测试用例。测试文件的名字要以 “test”
``test`` 子目录应包含 ref`组件 CMakeLists.txt <component-directories>`因为他们本身就是一种组件。ESP-IDF 使用了
``unity`` 测试框架,需要将其指定为组件的依赖项。通常,组件
ref`需要手动指定待编译的源文件 <cmake-file-globbing>`;但是,对于测试组件来说,这个要求被放宽为仅建议将参数 ``SRC_DIRS`` 用于 ``idf_component_register``
ref`需要手动指定待编译的源文件 <cmake-file-globbing>`;但是,对于测试组件来说,这个要求被放宽为仅建议将参数 ``SRC_DIRS`` 用于 ``idf_component_register``
总的来说,``test`` 子目录下最小的 CMakeLists.txt 文件可能如下所示:
@ -137,6 +137,48 @@ DUT2slave终端
多阶段测试用例向用户呈现了一组测试函数,它需要用户进行交互(选择用例并选择不同的阶段)来运行。
应用于不同芯片的单元测试
------------------------
某些测试(尤其与硬件相关的)无法在所有的芯片上执行。请参照本节让你的单元测试只在其中一部分芯片上执行。
1. 使用宏 ``!(TEMPORARY_)DISABLED_FOR_TARGETS()`` 保护你的测试代码,并将其放于原来的位置,或者放在另外准备以功能区分的文件。但请确保所有这些文件都会被编译器处理到。例: ::
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32, ESP8266) TEST_CASE("a test that is not ready for esp32 and esp8266 yet", "[]")
{
}
#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32, ESP8266)
一旦你需要其中一个测试在某个芯片上被编译,只需要修改禁止的芯片列表。我们更鼓励使用一些通用的概念(能在 ``soc_caps.h`` 中被清楚描述)来禁止某些单元测试。如果你已经这样做,但有一些测试还没有在新的芯片版本中被调试通过,请同时使用上述两种方法,当调试完成后再移除 ``!(TEMPORARY_)DISABLED_FOR_TARGETS()`` 。例: ::
#if SOC_SDIO_SLAVE_SUPPORTED
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP64)
TEST_CASE("a sdio slave tests that is not ready for esp64 yet", "[sdio_slave]")
{
//available for esp32 now, and will be available for esp64 in the future
}
#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP64)
#endif //SOC_SDIO_SLAVE_SUPPORTED
2. 对于某些你绝对肯定不可能被支持的测试(例如,芯片根本没有该外设),使用 ``DISABLED_FOR_TARGETS`` 来禁止该测试;对于其他只是临时性需要关闭的(例如,没有 runner 资源等),使用 ``TEMPORARY_DISABLED_FOR_TARGETS`` 来暂时关闭该测试。
请勿继续使用一些旧的禁止单元测试在一些芯片上执行的做法,因为他们具有明显的坏处:
- 请勿将测试代码放在 ``test/芯片版本`` 目录下面,然后用 CMakeLists.txt 来选择其中一个进行编译。这是因为测试代码比实现代码更容易被复用。如果你将一些代码放在 ``test/esp32`` 目录下来避免 esp32s2 芯片执行它,一旦你需要在新的芯片(比如 esp32s3 )中启用该测试,你会发现这种结构非常难以保持代码的整洁。
- 请勿继续使用 ``CONFIG_IDF_TARGET_xxx`` 宏来禁止某些测试在一些芯片上编译。这种 方法会让被禁止的测试项目难以追踪和重新打开。并且,相比于白名单式的 ``#if CONFIG_IDF_TARGET_xxx`` ,黑名单式的 ``#if !disabled`` 能避免新芯片引入时,这些测试被自动关闭。
但对于用于测试的一些实现, ``#if CONFIG_IDF_TARGET_xxx`` 仍可用于给不同芯片版本选择实现代码。测试项目和测试实现区分如下:
- 测试项目:某些你会在一些芯片上执行,而在另外一些上跳过的项目,例如:
有三个测试项目 SD 1-bit 、 SD 4-bit 和 SDSPI 。对于不支持 SD Host 外设的ESP32-S2 芯片,只有 SDSPI 一个项目需要被执行。
- 测试实现:某些代码永远会发生,但采取不同的做法。例如:
ESP8266 芯片没有 SDIO_PKT_LEN 寄存器。如果在测试过程中需要获取从机准备好的数据长度,你可以用 ``#if CONFIG_IDF_TARGET_`` 宏来选择读取该长度的不同方法实现。
但请注意避免使用 ``#else`` 宏。这样当新芯片被引入时,测试就会在编译阶段失败,提示维护者去显示选择一个正确的测试实现。
编译单元测试程序
----------------