From c3eee25ff95bea4f44d2aa056059d1fba9bfdf1e Mon Sep 17 00:00:00 2001 From: Kristoffer Richardsson Date: Thu, 17 Nov 2016 21:09:40 +0100 Subject: [PATCH] Updated unit tests (rake) to be called from make. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reason is that make manages the build configuration (defines and so on) and we want to reuse them in the unit tests. When rake is called from make, all defines (actually compiler flags) are passed on to rake to be used when building the unit tests. Also added a light weight “annotation” that allows unit test files to be excluded based on defines. --- Makefile | 3 + README.md | 40 +++++++++---- Rakefile | 3 + test/deck/drivers/src/TestLpsTdoaTag.c | 2 + test/deck/drivers/src/TestLpsTwrTag.c | 2 + tools/build/test | 2 +- tools/test/rakefile_helper.rb | 83 +++++++++++++------------- 7 files changed, 83 insertions(+), 52 deletions(-) diff --git a/Makefile b/Makefile index aa900d5f35..34a1f87a39 100644 --- a/Makefile +++ b/Makefile @@ -414,3 +414,6 @@ include tools/make/targets.mk #include dependencies -include $(DEPS) + +unit: + rake unit "DEFINES=$(CFLAGS)" "FILES=$(FILES)" diff --git a/README.md b/README.md index f14a970b1e..00d1647507 100644 --- a/README.md +++ b/README.md @@ -148,9 +148,37 @@ openocd : Launch OpenOCD # Unit testing +## Running all unit tests + +With the environment set up locally + + make unit + +with the docker builder image and the toolbelt + + tb make unit + +## Running one unit test + +When working with one specific file it is often convinient to run only one unit test + + make unit FILES=test/utils/src/TestNum.c + +or with the toolbelt + + tb make unit FILES=test/utils/src/TestNum.c + +## Running unit tests with specific build settings + +Defines are managed by make and are passed on to the unit test code. Use the +normal ways of configuring make when running tests. For instance to run test +for Crazyflie 1 + + make unit PLATFORM=CF1 + ## Dependencies -Frameworks for unit testing are pulled in as git submodules. +Frameworks for unit testing and mocking are pulled in as git submodules. The testing framework uses ruby and rake to generate and run code. @@ -159,13 +187,3 @@ image (bitcraze/builder) that contains all tools needed. All scripts in the tools/build directory are intended to be run in the image. The [toolbelt](https://wiki.bitcraze.io/projects:dockerbuilderimage:index) makes it easy to run the tool scripts. - -### Running unit tests - -With the environment set up locally - - rake - -with the docker builder image and the toolbelt - - tb test diff --git a/Rakefile b/Rakefile index ada6c5a435..b66c82b6fa 100644 --- a/Rakefile +++ b/Rakefile @@ -14,6 +14,9 @@ DEFAULT_CONFIG_FILE = './tools/test/gcc.yml' configure_toolchain(DEFAULT_CONFIG_FILE) task :unit do + # This prevents all argumets after 'unit' to be interpreted as targets by rake + ARGV.each { |a| task a.to_sym do ; end } + if ARGV.length == 0 parse_and_run_tests([]) else diff --git a/test/deck/drivers/src/TestLpsTdoaTag.c b/test/deck/drivers/src/TestLpsTdoaTag.c index 697e390080..3f7b2dca26 100644 --- a/test/deck/drivers/src/TestLpsTdoaTag.c +++ b/test/deck/drivers/src/TestLpsTdoaTag.c @@ -1,3 +1,5 @@ +// @IGNORE_IF_NOT PLATFORM_CF2 + // File under test lpsTwrTag.h #include "lpsTdoaTag.h" diff --git a/test/deck/drivers/src/TestLpsTwrTag.c b/test/deck/drivers/src/TestLpsTwrTag.c index b97a65618d..5c3b75c012 100644 --- a/test/deck/drivers/src/TestLpsTwrTag.c +++ b/test/deck/drivers/src/TestLpsTwrTag.c @@ -1,3 +1,5 @@ +// @IGNORE_IF_NOT PLATFORM_CF2 + // File under test lpsTwrTag.h #include "lpsTwrTag.h" diff --git a/tools/build/test b/tools/build/test index a0f8a01a3f..f5ec60b1a2 100755 --- a/tools/build/test +++ b/tools/build/test @@ -3,4 +3,4 @@ set -e scriptDir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) -rake -f ${scriptDir}/../../Rakefile unit "${@}" +make unit "${@}" diff --git a/tools/test/rakefile_helper.rb b/tools/test/rakefile_helper.rb index c5a4c97c0f..2d1c746dea 100644 --- a/tools/test/rakefile_helper.rb +++ b/tools/test/rakefile_helper.rb @@ -171,15 +171,14 @@ def report_summary end def parse_and_run_tests(args) + defines = find_defines_in_args(args) test_files = find_test_files_in_args(args) # No file names found in the args, find all files that are unit test files if test_files.length == 0 - test_files = get_unit_test_files + test_files = exclude_test_files(get_unit_test_files(), defines) end - defines = find_defines_in_args(args) - run_tests(test_files, defines) end @@ -288,56 +287,60 @@ def build_application(main) link_it(main_base, obj_list) end - # Any argument without '=' in it will be considered to be a file name def find_test_files_in_args(args) - args.select do |arg| - not arg.include? '=' + key = 'FILES=' + args.each do |arg| + if arg.start_with?(key) + return arg[(key.length)..-1].split(' ') + end end end # Parse the arguments and find all defines that are passed in on the command line - # We support two formats - # 1. SOME_DEF=SOME_VALUE ==> generate define based on context - # 2. "EXTRA_CFLAGS=-DFIRST_DEF -DSECOND_DEF" ==> just set the define(s) in the list + # Defines are part of compiler flags and start with -D, for instance -DMY_DEFINE + # All compiler flags are passed in as one string def find_defines_in_args(args) - non_files = args.select do |arg| - arg.include? '=' - end - - defines = [] - non_files.each do |arg| - parts = arg.split "=" - - if parts[0] == "EXTRA_CFLAGS" - defines.concat parse_cflags(parts[1]) - else - defines.concat handle_make_arg(parts[0], parts[1]) + key = 'DEFINES=' + args.each do |arg| + if arg.start_with?(key) + return extract_defines(arg[(key.length)..-1]) end end - return defines end - def parse_cflags(flags) - flags.split(' ').map do |flag| - # Remove '-D' and return the rest of the string - flag[2..-1] + def extract_defines(arg) + arg.split(' ').select {|part| part.start_with?('-D')}.map {|flag| flag[2..-1]} + end + + def exclude_test_files(files, defines) + files.select do |file| + annotation_keep_file?(file, defines) end end - def handle_make_arg(name, value) - case name - when 'PLATFORM' - return ['PLATFORM_' + value] - when 'ESTIMATOR' - return ['ESTIMATOR_TYPE_' + value] - when 'CONTROLLER' - return ['CONTROLLER_TYPE_' + value] - when 'POWER_DISTRIBUTION' - return ['POWER_DISTRIBUTION_TYPE_' + value] - when 'DEBUG' - return ['DEBUG=' + value] - else - [] + + # WARNING This implementation is fairly brittle. Basically only intended for cases such as + # // @IGNORE_IF_NOT PLATFORM_CF2 + # Ignores values of defines, so this would fail and keep the file + # param to gradle: -DMYDEFINE=0 + # // @IGNORE_IF_NOT MYDEFINE + def annotation_keep_file?(file, defines) + ignore_str = '@IGNORE_IF_NOT' + + File.foreach( file ) do |line| + if line.include? ignore_str + tokens = line.split(' ') + index = tokens.index ignore_str + + if tokens.length >= (index + 2) + condition = tokens[index + 1] + return defines.detect {|define| define == condition} + else + return false + end + end end + + return true end end