ThrowTheSwitch / Ceedling

Ruby-based unit testing and build system for C projects
http://throwtheswitch.org
Other
567 stars 241 forks source link

Issue with mocking header inline functions and defines #868

Open lafonso-gnrc opened 3 months ago

lafonso-gnrc commented 3 months ago

Hello,

I'm trying to unit test a piece of software that will use some embedded libraries for a microcontroller. Said libraries have quite a few inline functions in the header.

I tried using the CMock option :treat_inlines: :include and I do have use_test_preprocessor: TRUE. This does not work because all the defines get erased.

With use_test_preprocessor: FALSE the problem I have is that I get the following errors:

build/test/mocks/mock_stm32h5xx_ll_tim.h:2627:1: error: multiple storage classes in declaration specifiers 2627 | typedef __STATIC_INLINE void ( CMOCK_LL_TIM_GenerateEvent_BRK2_CALLBACK)(TIM_TypeDef TIMx, int cmock_num_calls);

Any advice on how to proceed? Am I missing some option?

mvandervoord commented 3 months ago

Are you in a position to try the preview release candidate? (You'd need to download the gem from releases and then install it using gem install --local <filename>). If so, I believe that the new gem fixes this issue.

The second version of the problem above is caused by having the mocked version of the function AND the original version of the function both existing when we attempt to link. The release candidate solves this by cloning the header file and removing the inline version so that it can be linked properly during tests.

lafonso-gnrc commented 3 months ago

I am currently using a snapshot 0.32.0-164e950.

Do you have any advised snapshot to get or just the latest one? I might try this over the weekend if I don't have time in the meantime.

mvandervoord commented 3 months ago

Ah. If you're already using a release candidate, that suggests that we likely still have a problem in this area. I'll add this to our priority list! Thanks!

lafonso-gnrc commented 3 months ago

let me know if you need details to replicate the issue or for me to try anything. It was using STM32 LL libraries (specifically the timer ones)

thank you

LuisAfonso95 commented 2 months ago

Hello,

I've tried this with snapshot 0.32.0-9b8d8a9.

I created a new project and attempted to mock one of the LL headers from ST. I wanted to add my project.yml to the post but it unformats everything. This is using GCC on WSL Linux (previous one was on windows with MSYS2)

I get an issue with the include:

In file included from build/test/mocks/test_gpio_ll/mock_stm32h7xx_ll_gpio.h:6, from test/test_gpio_ll.c:8: build/test/mocks/test_gpio_ll/stm32h7xx_ll_gpio.h:6:10: fatal error: STM32Cube_FW_H7_V1.11.0/Drivers/CMSIS/Device/ST/STM32H7xx/Include/stm32h7xx.h: No such file or directory 6 | #include "STM32Cube_FW_H7_V1.11.0/Drivers/CMSIS/Device/ST/STM32H7xx/Include/stm32h7xx.h" | ^~~~~~~~~~~~~~~~~~~ compilation terminated.

Other than that I can see looking at the mock files that it does create the function prototypes and the typedefs. But it doesn't include the defines.

mkarlesky commented 2 months ago

@LuisAfonso95 If you are able please do provide your configuration. You can use the code block feature of the Github comment editor to preserve the formatting. Or, you can use Markdown formatting directly by surrounding your configuration file text with ``` in a line before and after the text.

LuisAfonso95 commented 2 months ago

Hello, This is what I tried in this last one:

:project:
  # how to use ceedling. If you're not sure, leave this as `gem` and `?`
  :which_ceedling: gem
  :ceedling_version: 0.32.0

  # optional features. If you don't need them, keep them turned off for performance
  :use_mocks: TRUE
  :use_test_preprocessor: TRUE
  :use_backtrace: FALSE

  # tweak the way ceedling handles automatic tasks
  :build_root: build
  :test_file_prefix: test_
  :default_tasks:
    - test:all

  # performance options. If your tools start giving mysterious errors, consider 
  # dropping this to 1 to force single-tasking
  :test_threads: 8
  :compile_threads: 8

  # you can specify different yaml config files which modify the existing one
  :options_paths: []

  # enable release build (more details in release_build section below)
  :release_build: FALSE

# specify additional yaml files to automatically load. This is helpful if you
# want to create project files which specify your tools, and then include those
# shared tool files into each project-specific project.yml file.
:import: []

# further details to configure the way Ceedling handles test code
:test_build:
  :use_assembly: FALSE

# further details to configure the way Ceedling handles release code
:release_build:
  :output: MyApp.out
  :use_assembly: FALSE
  :artifacts: []

# Plugins are optional Ceedling features which can be enabled. Ceedling supports
# a variety of plugins which may effect the way things are compiled, reported, 
# or may provide new command options. Refer to the readme in each plugin for 
# details on how to use it.
:plugins:
  :load_paths: []
  :enabled:
    #- beep                           # beeps when finished, so you don't waste time waiting for ceedling
    - module_generator               # handy for quickly creating source, header, and test templates
    #- gcov                           # test coverage using gcov. Requires gcc, gcov, and a coverage analyzer like gcovr
    #- bullseye                       # test coverage using bullseye. Requires bullseye for your platform
    #- command_hooks                  # write custom actions to be called at different points during the build process
    #- compile_commands_json_db          # generate a compile_commands.json file
    #- dependencies                   # automatically fetch 3rd party libraries, etc.
    #- subprojects                    # managing builds and test for static libraries
    #- fake_function_framework        # use FFF instead of CMock

    # Report options (You'll want to choose one stdout option, but may choose multiple stored options if desired)
    #- test_suite_reporter
    #- report_tests_raw_output_log
    - report_tests_pretty_stdout
    #- report_tests_ide_stdout
    #- report_tests_gtestlike_stdout
    #- teamcity_tests_report
    #- warnings_report

# override the default extensions for your system and toolchain
:extension:
  #:header: .h
  #:source: .c
  #:assembly: .s
  #:dependencies: .d
  #:object: .o
  :executable: .out
  #:testpass: .pass
  #:testfail: .fail
  #:subprojects: .a

# This is where Ceedling should look for your source and test files.
# see documentation for the many options for specifying this.
:paths:
  :test:
    - +:test/**
    - -:test/support
  :source:
    - src/**
    - STM32Cube_FW_H7_V1.11.0/Drivers/**
  :include:
    - src/** # In simple projects, this entry often duplicates :source
    - STM32Cube_FW_H7_V1.11.0/Drivers/**
  :support:
    - test/support
  :libraries: []

# You can even specify specific files to add or remove from your test
# and release collections. Usually it's better to use paths and let
# Ceedling do the work for you!
:files:
  :test: []
  :source: []

# Compilation symbols to be injected into builds
# See documentation for advanced options:
#  - Test name matchers for different symbols per test executable build
#  - Referencing symbols in multiple lists using advanced YAML
#  - Specifiying symbols used during test preprocessing
:defines:
  :common: &common_defines 
    - STM32H743xx
    - USE_FULL_LL_DRIVER
  :test:
    - *common_defines
    - TEST_CEEDLING # Simple list option to add symbol 'TEST' to compilation of all files in all test executables
  :test_preprocess:
    - *common_defines
    - TEST_CEEDLING
  :release: []

  # Enable to inject name of a test as a unique compilation symbol into its respective executable build. 
  :use_test_definition: FALSE 

# Configure additional command line flags provided to tools used in each build step
# :flags:
#   :release:
#     :compile:         # Add '-Wall' and '--02' to compilation of all files in release target
#       - -Wall
#       - --O2
#   :test:
#     :compile:
#       '(_|-)special': # Add '-pedantic' to compilation of all files in all test executables with '_special' or '-special' in their names
#         - -pedantic
#       '*':            # Add '-foo' to compilation of all files in all test executables
#         - -foo

# Configuration Options specific to CMock. See CMock docs for details
: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
  :treat_inlines: :include

# Configuration options specific to Unity. 
:unity:
  :defines:
    - UNITY_EXCLUDE_FLOAT

# You can optionally have ceedling create environment variables for you before
# performing the rest of its tasks.
:environment: []

# LIBRARIES
# These libraries are automatically injected into the build process. Those specified as
# common will be used in all types of builds. Otherwise, libraries can be injected in just
# tests or releases. These options are MERGED with the options in supplemental yaml files.
:libraries:
  :placement: :end
  :flag: "-l${1}"
  :path_flag: "-L ${1}"
  :system: []    # for example, you might list 'm' to grab the math library
  :test: []
  :release: []

################################################################
# PLUGIN CONFIGURATION
################################################################

# Add -gcov to the plugins list to make sure of the gcov plugin
# You will need to have gcov and gcovr both installed to make it work.
# For more information on these options, see docs in plugins/gcov
:gcov:
  :utilities:
    - gcovr           # Use gcovr to create the specified reports (default).
    #- ReportGenerator # Use ReportGenerator to create the specified reports.
  :reports: # Specify one or more reports to generate.
    # Make an HTML summary report.
    - HtmlBasic
    # - HtmlDetailed
    # - Text
    # - Cobertura
    # - SonarQube
    # - JSON
    # - HtmlInline
    # - HtmlInlineAzure
    # - HtmlInlineAzureDark
    # - HtmlChart
    # - MHtml
    # - Badges
    # - CsvSummary
    # - Latex
    # - LatexSummary
    # - PngChart
    # - TeamCitySummary
    # - lcov
    # - Xml
    # - XmlSummary
  :gcovr:
    # :html_artifact_filename: TestCoverageReport.html
    # :html_title: Test Coverage Report
    :html_medium_threshold: 75
    :html_high_threshold: 90
    # :html_absolute_paths: TRUE
    # :html_encoding: UTF-8

# :module_generator:
#   :project_root: ./
#   :source_root: source/
#   :inc_root: includes/
#   :test_root: tests/
#   :naming: :snake #options: :bumpy, :camel, :caps, or :snake
#   :includes:
#     :tst: []
#     :src: []
#   :boilerplates:
#     :src: ""
#     :inc: ""
#     :tst: ""

# :dependencies:
#   :libraries:
#     - :name: WolfSSL
#       :source_path:   third_party/wolfssl/source
#       :build_path:    third_party/wolfssl/build
#       :artifact_path: third_party/wolfssl/install
#       :fetch:
#         :method: :zip
#         :source: \\shared_drive\third_party_libs\wolfssl\wolfssl-4.2.0.zip
#       :environment:
#         - CFLAGS+=-DWOLFSSL_DTLS_ALLOW_FUTURE
#       :build:
#         - "autoreconf -i"
#         - "./configure --enable-tls13 --enable-singlethreaded"
#         - make
#         - make install
#       :artifacts:
#         :static_libraries:
#           - lib/wolfssl.a
#         :dynamic_libraries:
#           - lib/wolfssl.so
#         :includes:
#           - include/**

# :subprojects:  
#   :paths:
#    - :name: libprojectA
#      :source:
#        - ./subprojectA/source
#      :include:
#        - ./subprojectA/include
#      :build_root: ./subprojectA/build
#      :defines: []

################################################################
# TOOLCHAIN CONFIGURATION
################################################################

#:tools:
# Ceedling defaults to using gcc for compiling, linking, etc.
# As [:tools] is blank, gcc will be used (so long as it's in your system path)
# See documentation to configure a given toolchain for use
# :tools:
#   :test_compiler: 
#     :executable:
#     :arguments: []
#     :name: 
#     :stderr_redirect: :auto
#     :optional: FALSE
#   :test_linker: 
#     :executable:
#     :arguments: []
#     :name: 
#     :stderr_redirect: :auto
#     :optional: FALSE
#   :test_assembler: 
#     :executable:
#     :arguments: []
#     :name: 
#     :stderr_redirect: :auto
#     :optional: FALSE
#   :test_fixture: 
#     :executable:
#     :arguments: []
#     :name: 
#     :stderr_redirect: :auto
#     :optional: FALSE
#   :test_includes_preprocessor: 
#     :executable:
#     :arguments: []
#     :name: 
#     :stderr_redirect: :auto
#     :optional: FALSE
#   :test_file_preprocessor: 
#     :executable:
#     :arguments: []
#     :name: 
#     :stderr_redirect: :auto
#     :optional: FALSE
#   :test_file_preprocessor_directives: 
#     :executable:
#     :arguments: []
#     :name: 
#     :stderr_redirect: :auto
#     :optional: FALSE
#   :test_dependencies_generator: 
#     :executable:
#     :arguments: []
#     :name: 
#     :stderr_redirect: :auto
#     :optional: FALSE
#   :release_compiler: 
#     :executable:
#     :arguments: []
#     :name: 
#     :stderr_redirect: :auto
#     :optional: FALSE
#   :release_linker: 
#     :executable:
#     :arguments: []
#     :name: 
#     :stderr_redirect: :auto
#     :optional: FALSE
#   :release_assembler: 
#     :executable:
#     :arguments: []
#     :name: 
#     :stderr_redirect: :auto
#     :optional: FALSE
#   :release_dependencies_generator: 
#     :executable:
#     :arguments: []
#     :name: 
#     :stderr_redirect: :auto
#     :optional: FALSE
# #These tools can be filled out when command_hooks plugin is enabled
#   :pre_mock_preprocess
#   :post_mock_preprocess
#   :pre_mock_generate
#   :post_mock_generate
#   :pre_runner_preprocess
#   :post_runner_preprocess
#   :pre_runner_generate
#   :post_runner_generate
#   :pre_compile_execute
#   :post_compile_execute
#   :pre_link_execute
#   :post_link_execute
#   :pre_test_fixture_execute
#   :pre_test
#   :post_test
#   :pre_release
#   :post_release
#   :pre_build
#   :post_build
#   :post_error
...
Letme commented 2 months ago

You have include path error, because I assume you would like to run this on x86_64 architecture, but CMSIS from ARM has few guards against compiling for wrong architecture.

So after you fix your include, you will also have to create mock for the chip (replace hardcoded addresses as variables):

stm32h7mock.h

volatile USART_TypeDef    usart0_base; /* Create global variable instead of the fixed base address */
#define USART0_BASE       (&usart0_base) /**< USART0 base address  */
volatile USART_TypeDef    usart1_base;  /* Create global variable instead of the fixed base address */
#define USART1_BASE       (&usart1_base) /**< USART1 base address  */
lafonso-gnrc commented 2 months ago

The issue only occurs in the mock with that specific snapshot. The path seems to be correct.

I do have mock registers, this one was just a quick test of the new snapshot to see if it could mock the header inline functions.

lafonso-gnrc commented 2 months ago

I believe pull request #728 seems like it does exactly what I need. Might try to pull and build it to see if it works.

M-Bab commented 1 month ago

Yeah that sounds very familiar. Have you been successful with my modified version of Ceedling @lafonso-gnrc ?

I think the fastest way to get it installed is the following:

sudo gem install specific_install
sudo gem specific_install https://github.com/KLSMartin/Ceedling working