如何为 IAR Embedded Workbench 项目配置 Ceedling?

How can I configure Ceedling for an IAR Embedded Workbench project?

我正在尝试为嵌入式应用程序开发一项新功能,我想使用测试驱动的方法来实现。

该项目是用纯 C 语言编写的,正在使用 IAR Embedded Workbench 6.60.1.5104 进行开发。我的目标是 LPC1788,这是一个 Cortex-M3 设备,所有开发都是在 64 位 Windows 7 机器上完成的。现在我更赞成在 PC 上而不是在目标硬件上对 运行 进行单元测试(RAM 非常有限)。

我看到一本关于这个主题的有用的书,叫做 Test Driven Development for Embedded C,它让我转向了 Unity、CppUTest、Ceedling 等工具。在研究了这些东西之后,我认为我最好的选择是配置 Ceedling (使用 Unity)为我的项目。但是,我不确定我需要采取哪些步骤来配置 Ceedling 以使用我当前的 IAR 工具链。

我已经安装了 Ceedling 并创建了 "blinky" 示例项目,我正在尝试使用 IAR 工具链构建和测试它。我已将 iccarm.exe 添加到我的路径并编辑 blinky/project.yml 如下所示:

---

# Notes:
# This is a fully tested project that demonstrates the use
# of a timer ISR to blink the on board LED of an Arduino UNO
:project:
  :use_exceptions: FALSE
  :use_test_preprocessor: TRUE
  :use_auxiliary_dependencies: TRUE
  :build_root: build
  :release_build: TRUE
  :test_file_prefix: test_

#You'll have to specify these
:environment:
  - :mcu: atmega328p
  - :f_cpu: 16000000UL 
  - :serial_port: COM8  #change this to the serial port you are using!!!
  - :objcopy: avr-objcopy
  # Uncomment these lines if you are using windows and don't have these tools in your path
  # - :path:
    # - C:\mingw\bin
    # - C:\WinAVR-20100110\bin
    # - C:\WinAVR-20100110\utils\bin
    # - #{ENV['PATH']}

:extension:
  :executable: .bin

:release_build:
  :output: blinky

:paths:
  :test:
    - +:test/**
    - -:test/support
  :source:
    - src/**
  :support:
    - test/support

:defines:
  # in order to add common defines:
  #  1) remove the trailing [] from the :common: section
  #  2) add entries to the :common: section (e.g. :test: has TEST defined)
  :commmon: &common_defines []
  :test:
    - *common_defines
    - TEST
  :test_preprocess:
    - *common_defines
    - TEST

:tools:
  :release_compiler:
    :executable: avr-gcc
    :arguments:
      - 
      - -DTARGET
      - -DF_CPU=#{ENV['F_CPU']}
      - -mmcu=#{ENV['MCU']}
      - -Iinclude/
      - -Wall
      - -Os
      - -c
      - -o 
  :release_linker:
    :executable: avr-gcc
    :arguments:
      - -mmcu=#{ENV['MCU']}
      - 
      - -o .bin

:cmock:
  :mock_prefix: mock_
  :when_no_prototypes: :warn
  :enforce_strict_ordering: TRUE
  :plugins:
    - :ignore
  :treat_as:
    uint8:    HEX8
    uint16:   HEX16
    uint32:   UINT32
    int8:     INT8
    bool:     UINT8

:tools:
  :test_file_preprocessor:
    :executable: iccarm
    :name: 'IAR test file preprocessor'

  :test_includes_preprocessor:
    :executable: iccarm
    :name: 'IAR test includes preprocessor'

  :test_compiler:
    :executable: iccarm
    :name: 'IAR test compiler'

  :test_linker:
    :executable: iccarm
    :name: 'IAR test linker'

  :release_compiler:
    :executable: iccarm
    :name: 'IAR release compiler'

  :release_linker:
    :executable: iccarm
    :name: 'IAR release linker'

:plugins:
  :load_paths:
    - vendor/ceedling/plugins
  :enabled:
    - stdout_pretty_tests_report
    - module_generator
...

这与默认 project.yml 之间的唯一区别是第二个 :tools 部分下的内容。

我的猜测是我正朝着正确的方向前进,但我不确定 iccarm.exe 是否是用于工具链所有这些部分的正确可执行文件以及我需要传递哪些参数.

如果我可以配置 Ceedling 以使用 IAR 工具链构建和测试 blinky 项目,我希望我应该能够为我的实际项目应用相同的配置。如果我现在尝试 运行ning rake,我会得到以下输出:

$ rake


Test 'test_BlinkTask.c'
-----------------------
rake aborted!
Errno::ENOENT: No such file or directory @ rb_sysopen - build/test/preprocess/files/test_BlinkTask.c
C:/Users/davidfallah/Documents/IAR Projects/blinky/vendor/ceedling/lib/ceedling/preprocessinator_extractor.rb:18:in `readlines'
C:/Users/davidfallah/Documents/IAR Projects/blinky/vendor/ceedling/lib/ceedling/preprocessinator_extractor.rb:18:in `extract_base_file_from_preprocessed_expansion'
C:/Users/davidfallah/Documents/IAR Projects/blinky/vendor/ceedling/lib/ceedling/preprocessinator_file_handler.rb:14:in `preprocess_file'
C:/Users/davidfallah/Documents/IAR Projects/blinky/vendor/ceedling/lib/ceedling/preprocessinator.rb:40:in `preprocess_file'
C:/Users/davidfallah/Documents/IAR Projects/blinky/vendor/ceedling/lib/ceedling/preprocessinator.rb:12:in `block in setup'
C:/Users/davidfallah/Documents/IAR Projects/blinky/vendor/ceedling/lib/ceedling/preprocessinator_helper.rb:33:in `preprocess_test_file'
C:/Users/davidfallah/Documents/IAR Projects/blinky/vendor/ceedling/lib/ceedling/preprocessinator.rb:25:in `preprocess_test_and_invoke_test_mocks'
C:/Users/davidfallah/Documents/IAR Projects/blinky/vendor/ceedling/lib/ceedling/test_invoker.rb:42:in `block in setup_and_invoke'
C:/Users/davidfallah/Documents/IAR Projects/blinky/vendor/ceedling/lib/ceedling/test_invoker.rb:32:in `setup_and_invoke'
C:/Users/davidfallah/Documents/IAR Projects/blinky/vendor/ceedling/lib/ceedling/tasks_tests.rake:11:in `block (2 levels) in <top (required)>'
Tasks: TOP => default => test:all
(See full trace by running task with --trace)

--------------------
OVERALL TEST SUMMARY
--------------------

No tests executed.

我假设这是因为测试文件预处理器应该在 build/test/preprocess/files 目录下复制测试文件,目前还没有发生。

经过一番挖掘后,我发现这个 example configuration file for Unity 看起来可能会有帮助。它适用于像我正在使用的 IAR EW/Cortex M3 环境。这可能会指示我需要在我的 Ceedling project.yml:

中指定哪些配置选项

如果我能让 Ceedling 使用 IAR 工具链构建和测试 blinky 项目,我希望我可以调整它以用于我的实际项目。任何帮助将不胜感激。

这是一场斗争,但我相信我已经设法配置 Ceedling 来帮助测试我的项目。希望这对其他希望在 IAR 项目中使用 Ceedling 的人有用。

Ceedling CLI 有一个命令 (ceedling new <proj_name>),允许您使用 Ceedling 期望的结构创建新项目。您还可以指定 现有 项目的名称,在这种情况下,它只会添加必要的文件以使其与 Ceedling 兼容,这就是我对我的项目所做的。

作为参考,执行此步骤后我的项目结构如下所示:

.
├── build
│   ├── artifacts
│   │   └── test
│   ├── docs
│   ├── exe
│   ├── list
│   ├── logs
│   ├── obj
│   ├── temp
│   └── test
│       ├── cache
│       ├── dependencies
│       ├── list.i
│       ├── mocks
│       ├── out
│       ├── results
│       ├── runners
│       └── tests.map
├── project.yml
├── rakefile.rb
├── src
│   └── main
│       ├── c
│       │   ├── canDatabase.c
│       ├── include
│       │   ├── canDatabase.h
│       ├── python
│       └── resources
├── test
│   ├── support
│   └── test_canDatabase.c
├── <my_project>.dep
├── <my_project>.ewd
├── <my_project>.ewp
├── <my_project>.eww
├── vendor
│   └── ceedling
│       ├── docs
│       ├── lib
│       ├── plugins
│       └── vendor
└── version.properties

之后,我查看了 IAR 工具的参考手册,并研究了 IAR Embedded Workbench 在构建示例项目时的输出,正如@user694733 所建议的。我使用此信息编辑我的 project.yml,如下所示:

:project:
  :use_exceptions: FALSE
  :use_test_preprocessor: FALSE
  :use_auxiliary_dependencies: TRUE
  :build_root: build
  :release_build: FALSE
  :test_file_prefix: test_

:environment:
  - :path:
    - 'C:\Program Files (x86)\IAR Systems\Embedded Workbench 6.5\arm\bin'
    - 'C:\Program Files (x86)\IAR Systems\Embedded Workbench 6.5\common\bin'
    - #{ENV['PATH']}

:extension:
  :executable: .out

:paths:
  :test:
    - +:test/**
    - -:test/support
  :source:
    - src/main/c/**
    - src/main/include/**
    - src/main/resources/**
  :support:
    - test/support

:defines:
  :commmon: &common_defines []
  :test:
    - *common_defines
    - TEST
  :test_preprocess:
    - *common_defines
    - TEST

:cmock:
  :mock_prefix: mock_
  :when_no_prototypes: :warn
  :enforce_strict_ordering: TRUE
  :plugins:
    - :ignore
    - :callback
  :treat_as:
    uint8:    HEX8
    uint16:   HEX16
    uint32:   UINT32
    int8:     INT8
    bool:     UINT8

:tools:
  :test_compiler:
    :executable: iccarm
    :name: 'IAR test compiler'
    :arguments:
      - -D _DLIB_FILE_DESCRIPTOR=1
      - --debug
      - --endian=little
      - --cpu=Cortex-M3
      - -e
      - --fpu=None
      - -Ol
      - --preprocess "build/test/list"
      - --dlib_config "C:/Program Files (x86)/IAR Systems/Embedded Workbench 6.5/arm/INC/c/DLib_Config_Normal.h"
      - -I"$": COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE
      - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR
      - -o ""
      - --diag_suppress=Pa050
      - '""'

  :test_linker:
    :executable: ilinkarm
    :name: 'IAR test linker'
    :arguments:
      - --vfe
      - --redirect _Printf=_PrintfFull
      - --redirect _Scanf=_ScanfFull
      - --semihosting
      - --config "C:/Program Files (x86)/IAR Systems/Embedded Workbench 6.5/arm/config/generic_cortex.icf"
      - --map "build/test/tests.map"
      - -o ""
      - '""'

  :test_fixture:
    :executable: cspybat
    :name: 'CSpyBat test runner'
    :arguments:
      - '"C:\Program Files (x86)\IAR Systems\Embedded Workbench 6.5\arm\bin\armproc.dll"'
      - '"C:\Program Files (x86)\IAR Systems\Embedded Workbench 6.5\arm\bin\armsim2.dll"'
      - '""'
      - --plugin "C:\Program Files (x86)\IAR Systems\Embedded Workbench 6.5\arm\bin\armbat.dll"
      - --backend -B
      - --endian=little
      - --cpu=Cortex-M3
      - --fpu=None
      - --semihosting

:plugins:
  :load_paths:
    - vendor/ceedling/plugins
  :enabled:
    - stdout_pretty_tests_report
    - module_generator
...

这似乎是测试设计用于 Cortex-M3 设备的代码的合适配置。

我还编辑了 rakefile.rb 以确保在每次测试之前清理生成的测试文件 运行,因为这是让测试结果一致打印的必要条件。

PROJECT_CEEDLING_ROOT = "vendor/ceedling"
load "#{PROJECT_CEEDLING_ROOT}/lib/ceedling.rb"

Ceedling.load_project

task :default => %w[ clean test:all ]

然后我能够定义和 运行 单元测试。以下是摘自 test_canDatabase.c:

#include "unity.h"
#include "canDatabase.h"

uint32_t actualId;
uint8_t actualPayload[8];
uint8_t actualPayloadLen;
uint8_t actualCanPort;

void mockHandler(uint32_t id, uint8_t payload[8], uint8_t payloadLen, uint8_t canPort)
{
  actualId = id;
  actualPayloadLen = payloadLen;
  actualCanPort = canPort;

  for (int i=0; i < payloadLen; i++)
  {
    actualPayload[i] = payload[i];
  }
}

void setUp(void)
{
  actualId = 0;
  actualPayloadLen = 0;
  actualCanPort = 0;

  for (int i=0; i < 8; i++)
  {
    actualPayload[i] = 0;
  }

  CANDB_Init(mockHandler);
}

void tearDown(void) {}

void test_Register_Tx_Definition()
{
  // GIVEN a CAN Tx message definition.
  CAN_TX_MESSAGE_DEFINITION_T definition;
  definition.id = 0;

  // WHEN we register the definition in the CAN database.
  int err = CANDB_RegisterTxDefinition(definition);

  // THEN the database should return SUCCESS (0x0).
  TEST_ASSERT_EQUAL_MESSAGE(0x0, err, "Registration should succeed");
}

void test_Register_Tx_Definition_Twice()
{
  // GIVEN a CAN Tx message definition.
  CAN_TX_MESSAGE_DEFINITION_T definition;
  definition.id = 0;

  // WHEN we register the definition once.
  CANDB_RegisterTxDefinition(definition);

  // AND we register the definition again.
  int err = CANDB_RegisterTxDefinition(definition);

  // THEN the database should return SUCCESS (0x0).
  TEST_ASSERT_EQUAL_MESSAGE(0x0, err, "Re-registration should succeed");
}

我现在可以通过从终端调用 "ceedling" 来 运行 自动测试(项目根目录是当前工作目录):

$ ceedling
---------------------
BUILD FAILURE SUMMARY
---------------------
Unit test failures.


Cleaning build artifacts...
(For large projects, this task may take a long time to complete)



Test 'test_canDatabase.c'
-------------------------
Generating runner for test_canDatabase.c...
Compiling test_canDatabase_runner.c...
Compiling test_canDatabase.c...
Compiling unity.c...
Compiling canDatabase.c...
Compiling cmock.c...
Linking test_canDatabase.out...
Running test_canDatabase.out...

-----------
TEST OUTPUT
-----------
[test_canDatabase.c]
  - ""
  - "     IAR C-SPY Command Line Utility V6.6.0.2752"
  - "     Copyright 2000-2013 IAR Systems AB."
  - ""
  - ""

-------------------
FAILED TEST SUMMARY
-------------------
[test_canDatabase.c]
  Test: test_Register_More_Than_Max_Allowed_Definitions
  At line (84): "Expected 1 Was 0. Registration > CANDB_MAX_TX_DEFINITIONS should fail"

  Test: test_Activate_Tx_Definition_With_Hardcoded_Payload
  At line (124): "Expected 0x00000001 Was 0x00000000. Incorrect ID"

--------------------
OVERALL TEST SUMMARY
--------------------
TESTED:  4
PASSED:  2
FAILED:  2
IGNORED: 0

您的回答非常有帮助,我正在为 Cortex-M4 和 IAR 7.5 研究类似的方法,并考虑分享我的 project.yml 添加部分。

:tools:
  :test_compiler:
    :executable: iccarm
    :name: 'IAR test compiler'
    :arguments:
      - -D _DLIB_FILE_DESCRIPTOR=1 
      - --diag_suppress Pa050,Pa082,Pa039,Pe186
      - --no_cse
      - --no_unroll
      - --no_inline
      - --no_code_motion
      - --no_tbaa
      - --no_clustering
      - --no_scheduling
      - --debug
      - --endian=little
      - --cpu=Cortex-M4
      - -e
      - --fpu=VFPv4_sp
      - --dlib_config "C:\Program Files (x86)\IAR Systems\Embedded Workbench 7.5\arm\INC\c\DLib_Config_Normal.h"
      - --preprocess "build/test/list"
      - -On
      - --c++
      - --no_exceptions
      - --no_rtti
      - -I"$": COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE
      - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR
      - -o ""
      - '""'

  :test_linker:
    :executable: ilinkarm
    :name: 'IAR test linker'
    :arguments:
      - --vfe
      - --redirect _Printf=_PrintfFull
      - --redirect _Scanf=_ScanfFull
      - --semihosting
      - --keep __checksum
      - --entry __iar_program_start
      - --place_holder __checksum,4,.checksum,4
      - --define_symbol __checksum_begin=0x8020000
      - --define_symbol __checksum_end=0x80dfffb
      - --no_exceptions 
      - --config "C:\Users\jseinfeld\RubymineProjects\m4\common\cb\proj\flash.icf"
      - --map "build/test/m4.map"
      - -o ""
      - '""'

  :test_fixture:
    :executable: cspybat
    :name: 'CSpyBat test runner'
    :arguments:
      - '"C:\Program Files (x86)\IAR Systems\Embedded Workbench 7.5\arm\bin\armproc.dll"'
      - '"C:\Program Files (x86)\IAR Systems\Embedded Workbench 7.5\arm\bin\armsim2.dll"'
      - '""'
      - --plugin "C:\Program Files (x86)\IAR Systems\Embedded Workbench 7.5\arm\bin\armbat.dll"
      - --backend -B
      - --endian=little
      - --cpu=Cortex-M4
      - --fpu=VFPv4_sp
      - --semihosting