我正在尝试为嵌入式应用程序开发一个新功能,我希望使用测试驱动的方法。
该项目是用纯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:
- ${1}
- -DTARGET
- -DF_CPU=#{ENV['F_CPU']}
- -mmcu=#{ENV['MCU']}
- -Iinclude/
- -Wall
- -Os
- -c
- -o ${2}
:release_linker:
:executable: avr-gcc
:arguments:
- -mmcu=#{ENV['MCU']}
- ${1}
- -o ${2}.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
是否是用于工具链所有这些部分的正确可执行文件以及我需要传递的参数。< / p>
如果我可以使用IAR工具链配置Ceedling构建和测试blinky项目,我希望我能够为我的实际项目应用相同的配置。如果我现在尝试运行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
项目,我希望我能够使其适应我的实际项目。任何帮助将不胜感激。
答案 0 :(得分:3)
这是一场斗争,但我相信我已经设法配置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 "${2}"
- --diag_suppress=Pa050
- '"${1}"'
: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 "${2}"
- '"${1}"'
: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"'
- '"${1}"'
- --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
答案 1 :(得分:2)
您的回答非常有帮助,我正在为IAR 7.5开发Cortex-M4的类似方法,并考虑共享我的部分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 "${2}"
- '"${1}"'
: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 "${2}"
- '"${1}"'
: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"'
- '"${1}"'
- --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