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