ThrowTheSwitch / Ceedling

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

🧨 EXCEPTION: undefined method `downcase' for Hash:Class #927

Closed tyler-macinnis closed 1 week ago

tyler-macinnis commented 1 week ago

With the latest pre-release gem, I'm getting a very strange error.

🧨 EXCEPTION: undefined method `downcase' for Hash:Class

I think it might be due to how I'm doing my defines, but I'm not sure.

:defines:
  :preprocess:
    :*:
      - STM32G071xx
      - TEST
  :test:
    :*:
      - STM32G071xx
      - TEST
    :analoguedataconverter:
      - REDEFINE_VREFINT_CAL_MACROS
    :porttimer:
      - REDEFINE_TIM_MACROS
    :vsd:
      - REDEFINE_UART_MACROS
  :release: []
tyler-macinnis commented 1 week ago

Actually looking further I'm not sure what's causing it.

mvandervoord commented 1 week ago

This happens when you attempt to do anything? Does calling ceedling with ceedling -v=4 give any more information?

tyler-macinnis commented 1 week ago

Running ceedling clobber --log --verbosity=4 gives this

🚧 Application & Build Frameworks
   Ceedling => 1.0.0-d880297
      CMock => 2.5.4
      Unity => 2.6.0
 CException => 1.3.4

🚧 Validating configuration contains minimum required sections...

🚧 Project Configuration Handling
---------------------------------
Processing environment variables...
Processing Unity configuration...
Processing CMock configuration...

🚧 Plugin Handling
------------------
Discovering all plugins...
Merging configuration from plugin module_generator...
Merging configuration from plugin report_tests_pretty_stdout...

🚧 Assembling Default Settings
------------------------------
Collecting default tool configurations...
Collecting CMock defaults...
Collecting Plugin YAML defaults...
Collecting Plugin Ruby hash defaults...
Populating project configuration with collected default values...

🚧 Completing Project Configuration
-----------------------------------
Populating test runner generation settings...
Processing environment variables...
Processing path entries and expanding any string replacements...
Expanding any string replacements in :flags entries...
Expanding any string replacements in :defines entries...
Standardizing all paths...
Populating tool definition settings and expanding any string replacements...
Processing tool definition shortcuts...

🚧 Validating final project configuration...

🧨 EXCEPTION: undefined method `downcase' for Hash:Class
tyler-macinnis commented 1 week ago

And the log is this

<IO:$stdout> Sep 11 10:48:31 2024 | 🚧 Validating configuration contains minimum required sections...
<IO:$stdout> Sep 11 10:48:31 2024 | 🚧 Project Configuration Handling
                                    ---------------------------------
<IO:$stdout> Sep 11 10:48:31 2024 | Processing environment variables...
<IO:$stdout> Sep 11 10:48:31 2024 | Processing Unity configuration...
<IO:$stdout> Sep 11 10:48:31 2024 | Processing CMock configuration...
<IO:$stdout> Sep 11 10:48:31 2024 | 🚧 Plugin Handling
                                    ------------------
<IO:$stdout> Sep 11 10:48:31 2024 | Discovering all plugins...
<IO:$stdout> Sep 11 10:48:31 2024 | > Rake plugins: module_generator, gcov
<IO:$stdout> Sep 11 10:48:31 2024 | > Programmatic plugins: module_generator, gcov, compile_commands_json_db, report_tests_log_factory, report_tests_pretty_stdout, report_tests_raw_output_log
<IO:$stdout> Sep 11 10:48:31 2024 | > Config plugins: module_generator, report_tests_pretty_stdout
<IO:$stdout> Sep 11 10:48:31 2024 | Merging configuration from plugin module_generator...
<IO:$stdout> Sep 11 10:48:31 2024 | {:module_generator=>{:project_root=>"./", :naming=>:snake, :boilerplates=>{:src=>"", :inc=>"", :tst=>""}}}
<IO:$stdout> Sep 11 10:48:31 2024 | Merging configuration from plugin report_tests_pretty_stdout...
<IO:$stdout> Sep 11 10:48:31 2024 | {:plugins=>{:display_raw_test_results=>false}}
<IO:$stdout> Sep 11 10:48:31 2024 | 🚧 Assembling Default Settings
                                    ------------------------------
<IO:$stdout> Sep 11 10:48:31 2024 | Collecting default tool configurations...
<IO:$stdout> Sep 11 10:48:31 2024 | Collecting CMock defaults...
<IO:$stdout> Sep 11 10:48:31 2024 | Collecting Plugin YAML defaults...
<IO:$stdout> Sep 11 10:48:31 2024 | - gcov >> {:gcov=>{:summaries=>true, :report_task=>false, :utilities=>["gcovr"], :reports=>[], :gcovr=>{:report_root=>".", :merge_mode_function=>"merge-use-line-max"}, :report_generator=>{:verbosity=>"Warning", :collection_paths_source=>[], :custom_args=>[], :gcov_exclude=>[]}}}
<IO:$stdout> Sep 11 10:48:31 2024 | - report_tests_log_factory >> {:report_tests_log_factory=>{:reports=>[]}}
<IO:$stdout> Sep 11 10:48:31 2024 | Collecting Plugin Ruby hash defaults...
<IO:$stdout> Sep 11 10:48:31 2024 | - gcov >> {:tools=>{:gcov_compiler=>{:executable=>"gcc", :name=>"default_gcov_compiler", :optional=>false, :arguments=>["-g", "-fprofile-arcs", "-ftest-coverage", "-I\"${5}\"", "-D\"${6}\"", "-DGCOV_COMPILER", "-DCODE_COVERAGE", "-c \"${1}\"", "-o \"${2}\"", "-MMD", "-MF \"${4}\""]}, :gcov_linker=>{:executable=>"gcc", :name=>"default_gcov_linker", :optional=>false, :arguments=>["-g", "-fprofile-arcs", "-ftest-coverage", "${1}", "${5}", "-o \"${2}\"", "${4}"]}, :gcov_fixture=>{:executable=>"${1}", :name=>"default_gcov_fixture", :optional=>false, :arguments=>[]}, :gcov_summary=>{:executable=>"gcov", :name=>"default_gcov_summary", :optional=>true, :arguments=>["-n", "-p", "-b", "-o \"${2}\"", "\"${1}\""]}, :gcov_report=>{:executable=>"gcov", :name=>"default_gcov_report", :optional=>true, :arguments=>["-b", "-c", "-r", "-x", "${1}"]}, :gcov_gcovr_report=>{:executable=>"gcovr", :name=>"default_gcov_gcovr_report", :optional=>true, :arguments=>["${1}"]}, :gcov_reportgenerator_report=>{:executable=>"reportgenerator", :name=>"default_gcov_reportgenerator_report", :optional=>true, :arguments=>["${1}"]}}}
<IO:$stdout> Sep 11 10:48:31 2024 | Populating project configuration with collected default values...
<IO:$stdout> Sep 11 10:48:31 2024 | 🚧 Completing Project Configuration
                                    -----------------------------------
<IO:$stdout> Sep 11 10:48:31 2024 | Populating test runner generation settings...
<IO:$stdout> Sep 11 10:48:31 2024 | Unity configuration >> {:defines=>["UNITY_INCLUDE_PRINT_FORMATTED", "UNITY_INCLUDE_EXEC_TIME"], :vendor_path=>"/var/lib/gems/3.0.0/gems/ceedling-1.0.0/vendor", :use_param_tests=>false}
<IO:$stdout> Sep 11 10:48:31 2024 | CMock configuration >> {:defines=>["CMOCK_MEM_DYNAMIC"], :plugins=>[:ignore, :callback, :ignore_arg, :expect_any_args, :return_thru_ptr], :when_no_prototypes=>:warn, :treat_externs=>:include, :treat_inlines=>:exclude, :treat_as=>{"uint8"=>"HEX8", "uint16"=>"HEX16", "uint32"=>"UINT32", "int8"=>"INT8", "bool"=>"UINT8"}, :includes=>["<stdbool.h>", "<stdint.h>"], :mock_prefix=>"mock_", :vendor_path=>"/var/lib/gems/3.0.0/gems/ceedling-1.0.0/vendor", :unity_helper_path=>[], :mock_suffix=>"", :enforce_strict_ordering=>true, :mock_path=>"build/test/mocks", :verbosity=>4}
<IO:$stdout> Sep 11 10:48:31 2024 | Test Runner configuration >> {:cmdline_args=>true, :includes=>[], :defines=>["UNITY_INCLUDE_PRINT_FORMATTED", "UNITY_INCLUDE_EXEC_TIME"], :file_suffix=>"_runner", :mock_prefix=>"mock_", :mock_suffix=>"", :enforce_strict_ordering=>true, :use_param_tests=>false}
<IO:$stdout> Sep 11 10:48:31 2024 | CException configuration >> {:vendor_path=>"/var/lib/gems/3.0.0/gems/ceedling-1.0.0/vendor", :defines=>[]}
<IO:$stdout> Sep 11 10:48:31 2024 | Processing environment variables...
<IO:$stdout> Sep 11 10:48:31 2024 | Processing path entries and expanding any string replacements...
<IO:$stdout> Sep 11 10:48:31 2024 | Expanding any string replacements in :flags entries...
<IO:$stdout> Sep 11 10:48:31 2024 | Expanding any string replacements in :defines entries...
<IO:$stdout> Sep 11 10:48:31 2024 | Standardizing all paths...
<IO:$stdout> Sep 11 10:48:31 2024 | Populating tool definition settings and expanding any string replacements...
<IO:$stdout> Sep 11 10:48:31 2024 | Processing tool definition shortcuts...
<IO:$stdout> Sep 11 10:48:31 2024 | 🚧 Validating final project configuration...
<IO:$stderr> Sep 11 10:48:31 2024 | EXCEPTION: undefined method `downcase' for Hash:Class
<IO:$stdout> Sep 11 10:48:31 2024 | Backtrace ==>
<IO:$stdout> Sep 11 10:48:31 2024 | /var/lib/gems/3.0.0/gems/ceedling-1.0.0/lib/ceedling/configurator_setup.rb:230:in `block in validate_defines': undefined method `downcase' for Hash:Class (NoMethodError)
<IO:$stdout> Sep 11 10:48:31 2024 | /var/lib/gems/3.0.0/gems/ceedling-1.0.0/lib/ceedling/configurator_setup.rb:221:in `each_pair'
                                        /var/lib/gems/3.0.0/gems/ceedling-1.0.0/lib/ceedling/configurator_setup.rb:221:in `validate_defines'
                                        /var/lib/gems/3.0.0/gems/ceedling-1.0.0/lib/ceedling/configurator.rb:598:in `validate_final'
                                        /var/lib/gems/3.0.0/gems/ceedling-1.0.0/lib/ceedling/setupinator.rb:156:in `do_setup'
                                        /var/lib/gems/3.0.0/gems/ceedling-1.0.0/lib/ceedling/rakefile.rb:65:in `<top (required)>'
                                        <internal:/usr/lib/ruby/vendor_ruby/rubygems/core_ext/kernel_require.rb>:85:in `require'
                                        <internal:/usr/lib/ruby/vendor_ruby/rubygems/core_ext/kernel_require.rb>:85:in `require'
                                        /var/lib/gems/3.0.0/gems/ceedling-1.0.0/bin/cli_helper.rb:133:in `load_ceedling'
                                        /var/lib/gems/3.0.0/gems/ceedling-1.0.0/bin/cli_handler.rb:217:in `build'
                                        /var/lib/gems/3.0.0/gems/ceedling-1.0.0/bin/cli.rb:316:in `build'
                                        /var/lib/gems/3.0.0/gems/thor-1.3.2/lib/thor/command.rb:28:in `run'
                                        /var/lib/gems/3.0.0/gems/thor-1.3.2/lib/thor/invocation.rb:127:in `invoke_command'
                                        /var/lib/gems/3.0.0/gems/thor-1.3.2/lib/thor.rb:538:in `dispatch'
                                        /var/lib/gems/3.0.0/gems/ceedling-1.0.0/bin/cli.rb:93:in `start'
                                        /var/lib/gems/3.0.0/gems/ceedling-1.0.0/bin/ceedling:145:in `rescue in <top (required)>'
                                        /var/lib/gems/3.0.0/gems/ceedling-1.0.0/bin/ceedling:50:in `<top (required)>'
                                        /usr/local/bin/ceedling:25:in `load'
                                        /usr/local/bin/ceedling:25:in `<main>'
tyler-macinnis commented 1 week ago

I'm not sure if it is defines though because I have another project with this define for its main project.yml.

:defines:
  :test:
    :*:
      - STM32H750xx
  :release: []

and it has a mixin with

:defines:
  :test:
    :*:
      - UNIT_TEST

and it is getting the same error

mvandervoord commented 1 week ago

I don't believe you're doing anything wrong. I think the recent updates that added validated have inadvertently created some problems. In this case, lines 230 and 237 in configurator_setup.rb both have config.class.downcase but really they should be config.class.to_s.downcase or perhaps config.class.inspect.

I'm not in a position to change it this moment, but you're welcome to tweak your own files and see if it helps.

tyler-macinnis commented 1 week ago

For the time being I'm just going to use the previous pre-release gem. But this should probably stay open until the issue is resolved.

mkarlesky commented 1 week ago

Hi, @tyler-macinnis. So sorry about this. The bug was a simple if perhaps baffling mistake in formatting an error message. It's fixed in the newest pre-release build.

Incidentally, there is a problem with your :defines: configuration. Only the :test context supports the advanced matchers. The other contexts apply a list of symbols to every file processed in that context. So, for instance, you can apply a set of symbols to the compilation of a specific test executable, but the symbols defined for a :release build apply to the compilation of the entire release artifact. Another user has already requested the ability be expanded from a test executable only to per file matching for release builds. Why doesn't it exist already? The mechanisms for test executable builds are quite a bit different than from other build operations.

Here is your configuration corrected (note changes to :preprocess).

:defines:
  :preprocess:
    - STM32G071xx
    - TEST
  :test:
    :*:
      - STM32G071xx
      - TEST
    :analoguedataconverter:
      - REDEFINE_VREFINT_CAL_MACROS
    :porttimer:
      - REDEFINE_TIM_MACROS
    :vsd:
      - REDEFINE_UART_MACROS
  :release: []

Your configuration motivated a bit more explicit error checking along with a more specific error message to handle matchers being used where only lists are supported.

tyler-macinnis commented 1 week ago

Are flags being changed as well?

I'm getting the following error

πŸͺ² ERROR: :defines ↳ :gcov ↳ :compile entry '{:*=>["-Wno-int-to-pointer-cast", "-Wno-pointer-to-int-cast"]}' must be a list--matcher hashes only availalbe for the :test context (see docs for details)

🧨 EXCEPTION: Ceedling configuration failed validation
🚧 Loaded project configuration from working directory.
 > Using: /home/vsts/work/1/s/Tests/project.yml
 > Working directory: /home/vsts/work/1/s/Tests
πŸͺ² ERROR: :defines ↳ :gcov ↳ :compile entry '{:*=>["-Wno-int-to-pointer-cast", "-Wno-pointer-to-int-cast"]}' must be a list--matcher hashes only availalbe for the :test context (see docs for details)

🧨 EXCEPTION: Ceedling configuration failed validation
🚧 Loaded project configuration from working directory.
 > Using: /home/vsts/work/1/s/Tests/project.yml
 > Working directory: /home/vsts/work/1/s/Tests

for this

:flags:
  :test:
    :compile:
      :*:
        # Disable cast to/from pointer from/to integer of different size warnings
        # see https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
        - -Wno-int-to-pointer-cast
        - -Wno-pointer-to-int-cast
  :gcov:
    :compile:
      :*:
        # Disable cast to/from pointer from/to integer of different size warnings
        # see https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
        - -Wno-int-to-pointer-cast
        - -Wno-pointer-to-int-cast
tyler-macinnis commented 1 week ago

I'm assuming its as simple as

:flags:
  :test:
    :compile:
      :*:
        # Disable cast to/from pointer from/to integer of different size warnings
        # see https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
        - -Wno-int-to-pointer-cast
        - -Wno-pointer-to-int-cast
  :gcov:
    :compile:
      # Disable cast to/from pointer from/to integer of different size warnings
      # see https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
      - -Wno-int-to-pointer-cast
      - -Wno-pointer-to-int-cast

EDIT: Yes, that did it.

mkarlesky commented 1 week ago

@tyler-macinnis Yes, apart from the specifics of the top-level keys in :defines and :flags matcher rules and abilities are identical between themβ€”namely, only symbols or command line arguments have the sophisticated option of being matched to a test executable within the containing :test context. The idea of matcher is not available outside of :test.

For test builds, you can do this:

:flags:
  :test:
    :compile:
      :*: # Match all test executables or use another matcher for a subset of test executables
        - -Wno-int-to-pointer-cast
        - -Wno-pointer-to-int-cast

or this:

:flags:
  :test:
    :compile: # Apply these flags to all test executable compilation
      - -Wno-int-to-pointer-cast
      - -Wno-pointer-to-int-cast

The latter is a simpler syntax than the former when you want the same flags applied for all test compilation. This latter syntax is also the only option for anything other than :test.

Unfortunately, a feature Ceedling does not yet have in its plugin structure is a way to connect plugin context to specific pipelines within the core of Ceedling. What this means is that :gcov should provide the same matching as :test, but Ceedling is not presently able to do this. Your flags in a coverage build cannot presently target individual test executable builds. With a planned overhaul of its plugin system, Ceedling will eventually be able to do so.