Ceedling icon indicating copy to clipboard operation
Ceedling copied to clipboard

Ceedling test:all works on first build, but fails on subsequent builds

Open bigbrett opened this issue 3 years ago • 9 comments

As stated in the title. If I rm -rf the test build directory, the first time I run ceedling test:all, it sucessfully executes my tests, as expected.

However when I run the same command again right after, some of my tests run to completion, but some fail with the compiler complaining about re-definitions:

Test 'test_logic_flash_logging.c'                                                                                                                                                                  
---------------------------------                                                                                                                                                                  
Preprocessing util_clock.h...                                                                                                                                                                      
Generating include list for util_clock.h...                                                                                                                                                        
Creating mock for util_clock...                                                                                                                                                                    
Generating dependencies for mock_util_clock.c...                                                                                                                                                   
Generating runner for test_logic_flash_logging.c...                                                                                                                                                
Compiling test_logic_flash_logging_runner.c...                                                                                                                                                     
Compiling test_logic_flash_logging.c...                                                                                                                                                            
In file included from ./logic/logic_flash_logging.h:14,                                                                                                                                            
                 from test/logic/test_logic_flash_logging.c:8:                                                                                                                                     
./build/test/cache/proto_flash.h:22:5: error: redeclaration of enumerator ‘PROTO_FLASH_ERROR_NOT_IN_PROGRESS’                                                                                      
   22 |     PROTO_FLASH_ERROR_NOT_IN_PROGRESS = ((PROTO_FLASH_BASE) + (0)),                                                                                                                        
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                                                                                                                                                      
In file included from build/test/mocks/mock_proto_flash.h:6,                                                                                                                                       
                 from test/logic/test_logic_flash_logging.c:7:                                                                                                                                     
./build/test/cache/proto_flash.h:22:5: note: previous definition of ‘PROTO_FLASH_ERROR_NOT_IN_PROGRESS’ was here                                                                                   
   22 |     PROTO_FLASH_ERROR_NOT_IN_PROGRESS = ((PROTO_FLASH_BASE) + (0)),                                                                                                                        
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                        

and


#<Thread:0x0000555ea98d5dd0 /home/brett/workspace/firmware/link-controller/apps/link_controller/vendor/ceedling/lib/ceedling/par_map.rb:7 run> terminated with exception (report_on_exception is true):
Traceback (most recent call last):
        11: from /home/brett/workspace/firmware/link-controller/apps/link_controller/vendor/ceedling/lib/ceedling/par_map.rb:10:in `block (2 levels) in par_map'
        10: from /home/brett/workspace/firmware/link-controller/apps/link_controller/vendor/ceedling/lib/ceedling/task_invoker.rb:97:in `block in invoke_test_objects'
         9: from /usr/share/rubygems-integration/all/gems/rake-13.0.1/lib/rake/task.rb:188:in `invoke'
         8: from /usr/share/rubygems-integration/all/gems/rake-13.0.1/lib/rake/task.rb:199:in `invoke_with_call_chain'
         7: from /usr/share/rubygems-integration/all/gems/rake-13.0.1/lib/rake/task.rb:199:in `synchronize'
         6: from /usr/share/rubygems-integration/all/gems/rake-13.0.1/lib/rake/task.rb:219:in `block in invoke_with_call_chain'
         5: from /usr/share/rubygems-integration/all/gems/rake-13.0.1/lib/rake/task.rb:281:in `execute'
         4: from /usr/share/rubygems-integration/all/gems/rake-13.0.1/lib/rake/task.rb:281:in `each'
         3: from /usr/share/rubygems-integration/all/gems/rake-13.0.1/lib/rake/task.rb:281:in `block in execute'
         2: from /home/brett/workspace/firmware/link-controller/apps/link_controller/vendor/ceedling/lib/ceedling/rules_tests.rake:17:in `block in <top (required)>'
         1: from /home/brett/workspace/firmware/link-controller/apps/link_controller/vendor/ceedling/lib/ceedling/generator.rb:99:in `generate_object_file'
/home/brett/workspace/firmware/link-controller/apps/link_controller/vendor/ceedling/lib/ceedling/tool_executor.rb:88:in `exec': ShellExecutionException (ShellExecutionException)
rake aborted!
ShellExecutionException: ShellExecutionException
/home/brett/workspace/firmware/link-controller/apps/link_controller/vendor/ceedling/lib/ceedling/tool_executor.rb:88:in `exec'
/home/brett/workspace/firmware/link-controller/apps/link_controller/vendor/ceedling/lib/ceedling/generator.rb:99:in `generate_object_file'
/home/brett/workspace/firmware/link-controller/apps/link_controller/vendor/ceedling/lib/ceedling/rules_tests.rake:17:in `block in <top (required)>'
/home/brett/workspace/firmware/link-controller/apps/link_controller/vendor/ceedling/lib/ceedling/task_invoker.rb:97:in `block in invoke_test_objects'
/home/brett/workspace/firmware/link-controller/apps/link_controller/vendor/ceedling/lib/ceedling/par_map.rb:10:in `block (2 levels) in par_map'
Tasks: TOP => build/test/out/c/test_logic_flash_logging.o
(See full trace by running task with --trace)
ERROR: Ceedling Failed


Looks like a caching issue?

Additionally, when I clobber, the error persists. The only way to prevent the error from occurring again is to rm -rf the build directory.

Project File

---

:project:
  :use_exceptions: FALSE
  :use_test_preprocessor: TRUE
  :use_auxiliary_dependencies: TRUE
  :use_deep_dependencies: TRUE
  :generate_deep_dependencies: TRUE
  :build_root: build
  :test_file_prefix: test_
  :which_ceedling: vendor/ceedling
  :use_assebly: FALSE
  :default_tasks:
    - test:all

:environment:

:extension:
  :executable: .out

:module_generator:
  :project_root: ./
  :source_root: ./
  :test_root: ./test/

:paths:
  :test:
    - +:test/**
    - -:test/support
  :source:
    - ./**

    - ../../common/robot/**
    - ../../common/som
    - ../../common/proto
    - ../../common/util


    - ../../lib/CMSIS/Include/**
    - ../../lib/xip/**
    - ../../lib/lwip/port/**
    - ../../lib/lwip/src/**
    - ../../lib/lwip/src/include/**
    - ../../lib/lwip/src/include/lwip/**
    - -:../../lib/lwip/src/include/lwip/prot/**
    - ../../lib/lwip/src/include/netif/**
    - ../../lib/sbl/**
    - ../../lib/rtcesl/CM7F_RTCESL_4.5_MCUX/** # added to eliminate file-not-found error for "gflib_FP.h"
    - ../../lib/MCUX/**                        # added to eliminate file-not-found error for "cr_section_macros.h"


  :support:
    - test/support

:defines:
  :commmon: &common_defines
    - __packed="__attribute__((packed))"
  :test:
    - *common_defines
    - TEST
    - UNITY_INCLUDE_PRINT_FORMATTED
  :test_preprocess:
    - *common_defines
    - TEST

:cmock:
  :mock_prefix: mock_
  :when_no_prototypes: :warn
  :enforce_strict_ordering: FALSE # true
  :plugins:
    - :ignore
    - :callback
    - :expect_any_args
    - :ignore_arg
    - :return_thru_ptr
  :treat_as:
    uint8:    HEX8
    uint16:   HEX16
    uint32:   UINT32
    int8:     INT8
    bool:     UINT8
  :defines:
   - CMOCK_MEM_DYNAMIC

:gcov:
    :html_report_type: basic

:flags:
  :test:
    :compile:
        :*:
            - -Wno-int-to-pointer-cast
            - -Wno-pointer-to-int-cast
            - -fshort-enums 
            - -Wno-implicit-function-declaration # included to suppress issuance of warnings regarding implictly-declared functions within "arm_math.h"

:libraries:
  :placement: :end
  :flag: "${1}"  # or "-L ${1}" for example
  :common: &common_libraries []
  :test:
    - *common_libraries
  :release:
    - *common_libraries

:plugins:
  :load_paths:
    - vendor/ceedling/plugins
  :enabled:
    - stdout_pretty_tests_report
    - module_generator
    - raw_output_report
...

Ceedling Info:

      Ceedling:: 0.31.1
      Unity:: 2.5.4
      CMock:: 2.5.4
      CException:: 1.3.3

Running on Ubuntu 20.04 LTS

bigbrett avatar Jul 19 '21 19:07 bigbrett

Seeing the error logs, looks like proto_flash.h has been included twice. Can you share your source files, test_logic_flash_logging.c, logic_flash_logging.h and proto_flash.h?

maybe putting #pragma once at the top of your header file would work too.

fetiu avatar Dec 28 '21 01:12 fetiu

@bigbrett did you solved it??, because I'm facing the same problem

Diego-19xx avatar Sep 16 '23 02:09 Diego-19xx

I also have a feeling this is about missing include guards in one of the header files of proto_flash.h

Letme avatar Sep 18 '23 16:09 Letme

Well, actually after some testing i was able to replicate the problem:

This happens only when you have inline functions and includes in the header files to mock, like this

$ tree -L 3
.
|-- app
|   |-- drvs        
|   |   |-- myadc.c             --> #include "myadc.h"
|   |   |-- myadc.h             --> #include "mypwm.h"  here is where the inline functions are declared
|   |   |-- mypwm.c          
|   |   `-- mypwm.h          --> #include "myadc.h"
|   |-- dummy.c               --> #include "myadc.h"  
|   |-- dummy.h     
|   `-- main.c      
|-- makefile        
|-- project.yml     
`-- test
    `-- test_dummy.c      -- > #include "mock_myadc.h" 

the problem relays on myadc.h includes mypwm.h and mypwm.h is also inluding for some reason myadc.h again (I'm just replicating the issue), if we do:

$ ceedling test:all

Test 'test_dummy.c'
-------------------
Generating include list for myadc.h...
Creating mock for myadc...
Generating runner for test_dummy.c...
Compiling test_dummy_runner.c...
Compiling test_dummy.c...
Compiling mock_myadc.c...
Build/ceedling/test/mocks/mock_myadc.c:120:9: error: redefinition of 'adc_c_to_f'
  120 | uint8_t adc_c_to_f(uint8_t c)
      |         ^~~~~~~~~~
In file included from ./app/drvs/mypwm.h:4,
                 from Build/ceedling/test/mocks/myadc.h:1,
                 from Build/ceedling/test/mocks/mock_myadc.h:6,
                 from Build/ceedling/test/mocks/mock_myadc.c:6:
./app/drvs/myadc.h:9:55: note: previous definition of 'adc_c_to_f' with type 'uint8_t(uint8_t)' {aka 'unsigned char(unsigned char)'}
    9 | static inline __attribute__ ((always_inline)) uint8_t adc_c_to_f( uint8_t c )
      |   

ceedling build too complains about a redefinition, The ceedling/test/mocks folder contains the proper files as expected

  • mock_myadc.c
  • mock_myadc.h
  • myadc.h

if we add the same include guards to myadc.h generate by ceedling and the type

$ ceedling test:dummy


Test 'test_dummy.c'
-------------------
Compiling mock_myadc.c...
Compiling unity.c...
Compiling dummy.c...
Compiling CException.c...
Compiling cmock.c...
Linking test_dummy.exe...
Running test_dummy.exe...

--------------------
OVERALL TEST SUMMARY
--------------------
TESTED:  2
PASSED:  2
FAILED:  0
IGNORED: 0

problem is solved

Diego-19xx avatar Sep 18 '23 17:09 Diego-19xx

You can check a static inline module in CMock which you can use to mock also static inline functions. I spinout the implementation of static inline functions in separate header (lets say postfix with _inline_impl.h) which I only include when ifndef TEST. It helps in such cases - but I do not use Ceedling to build in those projects, so I can't give you example configuration.

Since you found a solution we can probably close this ticket?

Letme avatar Sep 18 '23 19:09 Letme

Is not a solution because you have to manually modify what ceedling is generating, @mvandervoord nad i don't know if this break any other stuffs in ceedling

Diego-19xx avatar Sep 18 '23 19:09 Diego-19xx

Was your solution to add protection to the generated header, like so:

#ifndef MYADC_H_ #define MYADC_H_

// all other generated code

#endif

mvandervoord avatar Sep 18 '23 19:09 mvandervoord

exactly. I can provide with the example in case some body else want to tested, make some suggestion about how to run some more exhaustive tests

inlines.zip

Diego-19xx avatar Sep 18 '23 20:09 Diego-19xx

Honestly, I thought we had this feature already... but it appears we do not. Perhaps this was because we were afraid of getting name conflicts... but that seems solveable... we could always generate the protection as something ridiculous like GUARD_HEADER_FOR_filename_H

mvandervoord avatar Sep 18 '23 20:09 mvandervoord