Skip to content

Commit

Permalink
[Feature] Add possibility to run one test case. Part I
Browse files Browse the repository at this point in the history
Enable posssibility to run single or multiple tests cases using:
   ceedling test:<test_file> --test_case=<test_case_name>

Enable filter for excluding test case name using --exclude_test_case arg:
   ceedling test:all --exclude_test_case=<test_case_name>

Update CeedlingPacket.md with information about new functionality
  • Loading branch information
luze-mobica committed Jan 25, 2023
1 parent 7fa217d commit f522ed8
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 2 deletions.
4 changes: 4 additions & 0 deletions bin/ceedling
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,10 @@ else
options[:list_tasks] = true
when /^project:(\w+)/
ENV['CEEDLING_USER_PROJECT_FILE'] = "#{$1}.yml"
when /^--test_case=(\w+)/
ENV['CEEDLING_INCLUDE_TEST_CASE_NAME'] = $1
when /^--exclude_test_case=(\w+)/
ENV['CEEDLING_EXCLUDE_TEST_CASE_NAME'] = $1
else
options[:args].push(v)
end
Expand Down
53 changes: 53 additions & 0 deletions docs/CeedlingPacket.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,59 @@ Ceedling (more on this later).
whose path contains foo/bar. Note: both directory separator characters
/ and \ are valid.

* `ceedling test:* --test_case=<test_case_name> `
Execute test case which match **test_case_name**. Option available only after
setting up **cmdline_args** to true under **test_runner** in project.yml:

```
:test_runner:
:cmdline_args: true
```

For instance, if you have file test_gpio.c with defined 3 tests:

- test_gpio_start
- test_gpio_configure_proper
- test_gpio_configure_fail_pin_not_allowed

and you want to run only configure tests, you can call:

```ceedling test:gpio --test_case=configure```

---
**Limitation**

The Unity implementation use test case name as substring which will be search in your test case names. If you pass only **gpio** and your file under test contains **gpio** in the name, it will run all tests from it. This is connected with the logic, how Unity generates test_<file_name_runner.c> files. In such case, it is suggested to use full name of test case.

---

* `ceedling test:* --exclude_test_case=<test_case_name> `
Execute test case which does not match **test_case_name**. Option available only after
setting up **cmdline_args** to true under **test_runner** in project.yml:

```
:test_runner:
:cmdline_args: true
```

For instance, if you have file test_gpio.c with defined 3 tests:

- test_gpio_start
- test_gpio_configure_proper
- test_gpio_configure_fail_pin_not_allowed

and you want to run only start tests, you can call:

```ceedling test:gpio --exclude_test_case=configure```

---
**Limitation**

The Unity implementation use test case name as substring which will be search in your test case names. If you pass only **gpio** and your file under test contains **gpio** in the name, it will run all tests from it. This is connected with the logic, how Unity generates test_<file_name_runner.c> files. In such case, it is suggested to use full name of test case.

---


* `ceedling release`:

Build all source into a release artifact (if the release build option
Expand Down
5 changes: 5 additions & 0 deletions lib/ceedling/configurator_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -400,8 +400,13 @@ def collect_vendor_defines(in_hash)

def collect_test_and_vendor_defines(in_hash)
defines = in_hash[:defines_test].clone

require_relative 'unity_utils.rb'
cmd_line_define = UnityUtils.update_defines_if_args_enables(in_hash)

vendor_defines = get_vendor_defines(in_hash)
defines.concat(vendor_defines) if vendor_defines
defines.concat(cmd_line_define) if cmd_line_define

return {:collection_defines_test_and_vendor => defines}
end
Expand Down
4 changes: 3 additions & 1 deletion lib/ceedling/generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ class Generator
:file_path_utils,
:streaminator,
:plugin_manager,
:file_wrapper
:file_wrapper,
:unity_utils


def generate_shallow_includes_list(context, file)
Expand Down Expand Up @@ -169,6 +170,7 @@ def generate_test_results(tool, context, executable, result)
# Unity's exit code is equivalent to the number of failed tests, so we tell @tool_executor not to fail out if there are failures
# so that we can run all tests and collect all results
command = @tool_executor.build_command_line(arg_hash[:tool], [], arg_hash[:executable])
command[:line] += @unity_utils.collect_test_runner_additional_args
@streaminator.stdout_puts("Command: #{command}", Verbosity::DEBUG)
command[:options][:boom] = false
shell_result = @tool_executor.exec( command[:line], command[:options] )
Expand Down
5 changes: 5 additions & 0 deletions lib/ceedling/objects.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ project_file_loader:
- system_wrapper
- file_wrapper

unity_utils:
compose:
- configurator

project_config_manager:
compose:
- cacheinator
Expand Down Expand Up @@ -196,6 +200,7 @@ generator:
- streaminator
- plugin_manager
- file_wrapper
- unity_utils

generator_helper:
compose:
Expand Down
2 changes: 1 addition & 1 deletion lib/ceedling/rules_tests.rake
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ end

namespace TEST_SYM do
# use rules to increase efficiency for large projects (instead of iterating through all sources and creating defined tasks)

@ceedling[:unity_utils].create_test_runner_additional_args
rule(/^#{TEST_TASK_ROOT}\S+$/ => [ # test task names by regex
proc do |task_name|
test = task_name.sub(/#{TEST_TASK_ROOT}/, '')
Expand Down
101 changes: 101 additions & 0 deletions lib/ceedling/unity_utils.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# The Unity utils class,
# Store functions to enable test execution of single test case under test file
# and additional warning definitions
class UnityUtils
attr_reader :test_runner_disabled_replay, :arg_option_map
attr_accessor :test_runner_args, :not_supported

constructor :configurator

def setup
@test_runner_disabled_replay = "NOTICE: \n" \
"The option[s]: %<opt>.s \ncannot be applied." \
'To enable it, please add `:cmdline_args` under' \
' :test_runner option in your project.yml.'
@test_runner_args = ''
@not_supported = ''

# Refering to Unity implementation of the parser implemented in the unit.c :
#
# case 'l': /* list tests */
# case 'n': /* include tests with name including this string */
# case 'f': /* an alias for -n */
# case 'q': /* quiet */
# case 'v': /* verbose */
# case 'x': /* exclude tests with name including this string */
@arg_option_map =
{
'test_case' => 'n',
'list_test_cases' => 'l',
'run_tests_verbose' => 'v',
'exclude_test_case' => 'x'
}
end

# Create test runner args which can be passed to executable test file as
# filter to execute one test case from test file
#
# @param [String, #argument] argument passed after test file name
# e.g.: ceedling test:<test_file>:<argument>
# @param [String, #option] one of the supported by unity arguments.
# At current moment only "test_case_name" to
# run single test
def additional_test_run_args(argument, option)
# Confirm wherever cmdline_args is set to true
# and parsing arguments under generated test runner in Unity is enabled
# and passed argument is not nil

return nil if argument.nil?

raise TypeError, 'option expects an arg_option_map key' unless \
option.is_a?(String)
raise 'Unknown Unity argument option' unless \
@arg_option_map.key?(option)

@test_runner_args += " -#{@arg_option_map[option]} #{argument} "
end

# Return test case arguments
#
# @return [String] formatted arguments for test file
def collect_test_runner_additional_args
@test_runner_args
end

# Parse passed by user arguments
def create_test_runner_additional_args
if ENV['CEEDLING_INCLUDE_TEST_CASE_NAME']
if @configurator.project_config_hash[:test_runner_cmdline_args]
additional_test_run_args(ENV['CEEDLING_INCLUDE_TEST_CASE_NAME'],
'test_case')
else
@not_supported = "\n\t--test_case"
end
end

if ENV['CEEDLING_EXCLUDE_TEST_CASE_NAME']
if @configurator.project_config_hash[:test_runner_cmdline_args]
additional_test_run_args(ENV['CEEDLING_EXCLUDE_TEST_CASE_NAME'],
'exclude_test_case')
else
@not_supported = "\n\t--exclude_test_case"
end
end
print_warning_about_not_enabled_cmdline_args
end

# Return UNITY_USE_COMMAND_LINE_ARGS define required by Unity to
# compile unity with enabled cmd line arguments
#
# @return [Array] - empty if cmdline_args is not set
def self.update_defines_if_args_enables(in_hash)
in_hash[:test_runner_cmdline_args] ? ['UNITY_USE_COMMAND_LINE_ARGS'] : []
end

# Print on output console warning about lack of support for single test run
# if cmdline_args is not set to true in project.yml file, that
def print_warning_about_not_enabled_cmdline_args
puts(format(@test_runner_disabled_replay, opt: @not_supported)) unless \
@configurator.project_config_hash[:test_runner_cmdline_args]
end
end

0 comments on commit f522ed8

Please sign in to comment.