From af4f1adfc799e91910e7579c2ad6577c33efac4a Mon Sep 17 00:00:00 2001 From: Michael Karlesky Date: Fri, 25 Oct 2024 16:14:29 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Extension=20handling=20now=20all?= =?UTF-8?q?ows=20dotted=20filenames?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dotted filenames are legal. Ceedling previously made too many assumptions about periods only being used for file extensions. For example, a legacy header file foo.33.h would break mocking. This is fixed. --- lib/ceedling/file_finder.rb | 45 +++++++++++++++++++---------- lib/ceedling/generator.rb | 2 +- lib/ceedling/rules_tests.rake | 6 ++-- lib/ceedling/test_invoker.rb | 15 ++++------ lib/ceedling/test_invoker_helper.rb | 19 ++++++++++-- plugins/bullseye/bullseye.rake | 2 +- plugins/gcov/gcov.rake | 2 +- 7 files changed, 59 insertions(+), 32 deletions(-) diff --git a/lib/ceedling/file_finder.rb b/lib/ceedling/file_finder.rb index 2bcdad422..b4166c060 100644 --- a/lib/ceedling/file_finder.rb +++ b/lib/ceedling/file_finder.rb @@ -14,24 +14,34 @@ class FileFinder constructor :configurator, :file_finder_helper, :cacheinator, :file_path_utils, :file_wrapper, :yaml_wrapper - def find_header_file(mock_file) - header = File.basename(mock_file).sub(/#{@configurator.cmock_mock_prefix}/, '').ext(@configurator.extension_header) + def find_header_input_for_mock(mock_name) + # Mock name =>
+ # Examples: 'Mockfoo' or 'mock_Bar' + # Note: In some rare cases, a mock name may include a dot (ex. Sensor.44) because of versioning file naming convention + # Be careful about assuming the end of the name has any sort of file extension - found_path = @file_finder_helper.find_file_in_collection(header, @configurator.collection_all_headers, :error, mock_file) + header = mock_name.sub(/#{@configurator.cmock_mock_prefix}/, '') + @configurator.extension_header + + found_path = @file_finder_helper.find_file_in_collection(header, @configurator.collection_all_headers, :error, mock_name) return found_path end - def find_header_input_for_mock_file(mock_file) - return find_header_file(mock_file) + # Find test filepath from another filepath (e.g. test executable with same base name, a/path/test_foo.exe) + def find_test_file_from_filepath(filepath) + # Strip filepath down to filename and remove file extension + name = File.basename( filepath ).ext('') + + return find_test_file_from_name( name ) end - def find_test_from_file_path(filepath) - test_file = File.basename(filepath).ext(@configurator.extension_source) + # Find test filepath from only the base name of a test file (e.g. 'test_foo') + def find_test_file_from_name(name) + test_file = name + @configurator.extension_source - found_path = @file_finder_helper.find_file_in_collection(test_file, @configurator.collection_all_tests, :error, filepath) + found_path = @file_finder_helper.find_file_in_collection(test_file, @configurator.collection_all_tests, :error, name) return found_path end @@ -42,6 +52,7 @@ def find_build_input_file(filepath:, complain: :error, context:) found_file = nil + # Strip off file extension source_file = File.basename(filepath).ext('') # We only collect files that already exist when we start up. @@ -49,9 +60,13 @@ def find_build_input_file(filepath:, complain: :error, context:) # So collect mocks and runners separately and right now. # Assume that project configuration options will have already filtered out any files that should not be searched for. + # Note: We carefully add file extensions below with string addition instead of using .ext() + # Some legacy files can include naming conventions like .##. for versioning. + # If we use .ext() below we'll clobber the dotted portion of the filename + # Generated test runners if (!release) and (source_file =~ /^#{@configurator.project_test_file_prefix}.+#{@configurator.test_runner_file_suffix}$/) - _source_file = source_file.ext(EXTENSION_CORE_SOURCE) + _source_file = source_file + EXTENSION_CORE_SOURCE found_file = @file_finder_helper.find_file_in_collection( _source_file, @@ -61,7 +76,7 @@ def find_build_input_file(filepath:, complain: :error, context:) # Generated mocks elsif (!release) and (source_file =~ /^#{@configurator.cmock_mock_prefix}/) - _source_file = source_file.ext(EXTENSION_CORE_SOURCE) + _source_file = source_file + EXTENSION_CORE_SOURCE found_file = @file_finder_helper.find_file_in_collection( _source_file, @@ -72,7 +87,7 @@ def find_build_input_file(filepath:, complain: :error, context:) # Vendor framework sources (unity.c, cmock.c, cexception.c, etc.) # Note: Taking a small chance by mixing test and release frameworks without smart checks on test/release build elsif (@configurator.collection_vendor_framework_sources.include?(source_file.ext(EXTENSION_CORE_SOURCE))) - _source_file = source_file.ext(EXTENSION_CORE_SOURCE) + _source_file = source_file + EXTENSION_CORE_SOURCE found_file = @file_finder_helper.find_file_in_collection( _source_file, @@ -97,7 +112,7 @@ def find_build_input_file(filepath:, complain: :error, context:) # Assembly files for release build if release and @configurator.release_build_use_assembly - _source_file = File.basename(filepath).ext(@configurator.extension_assembly) + _source_file = source_file + @configurator.extension_assembly found_file = @file_finder_helper.find_file_in_collection( _source_file, @@ -107,7 +122,7 @@ def find_build_input_file(filepath:, complain: :error, context:) # Assembly files for test build elsif (!release) and @configurator.test_build_use_assembly - _source_file = File.basename(filepath).ext(@configurator.extension_assembly) + _source_file = source_file + @configurator.extension_assembly found_file = @file_finder_helper.find_file_in_collection( _source_file, @@ -122,7 +137,7 @@ def find_build_input_file(filepath:, complain: :error, context:) # Release build C files if release - _source_file = File.basename(filepath).ext(@configurator.extension_source) + _source_file = source_file + @configurator.extension_source found_file = @file_finder_helper.find_file_in_collection( _source_file, @@ -132,7 +147,7 @@ def find_build_input_file(filepath:, complain: :error, context:) # Test build C files else - _source_file = File.basename(filepath).ext(@configurator.extension_source) + _source_file = source_file + @configurator.extension_source found_file = @file_finder_helper.find_file_in_collection( _source_file, diff --git a/lib/ceedling/generator.rb b/lib/ceedling/generator.rb index a51e3e553..89257ea72 100644 --- a/lib/ceedling/generator.rb +++ b/lib/ceedling/generator.rb @@ -347,7 +347,7 @@ def generate_test_results(tool:, context:, test_name:, test_filepath:, executabl executable, shell_result, arg_hash[:result_file], - @file_finder.find_test_from_file_path( arg_hash[:executable] ) + @file_finder.find_test_file_from_filepath( arg_hash[:executable] ) ) arg_hash[:result_file] = processed[:result_file] diff --git a/lib/ceedling/rules_tests.rake b/lib/ceedling/rules_tests.rake index 786acf6a6..e067a965a 100644 --- a/lib/ceedling/rules_tests.rake +++ b/lib/ceedling/rules_tests.rake @@ -36,12 +36,12 @@ namespace TEST_SYM do :test_fixture => TOOLS_TEST_FIXTURE } - # use rules to increase efficiency for large projects (instead of iterating through all sources and creating defined tasks) - rule(/^#{TEST_TASK_ROOT}\S+$/ => [ # test task names by regex + # Use rules to increase efficiency for large projects (instead of iterating through all sources and creating defined tasks) + rule(/^#{TEST_TASK_ROOT}\S+$/ => [ # Test task names by regex proc do |task_name| test = task_name.sub(/#{TEST_TASK_ROOT}/, '') test = "#{PROJECT_TEST_FILE_PREFIX}#{test}" if not (test.start_with?(PROJECT_TEST_FILE_PREFIX)) - @ceedling[:file_finder].find_test_from_file_path(test) + @ceedling[:file_finder].find_test_file_from_name(test) end ]) do |test| @ceedling[:rake_wrapper][:prepare].invoke diff --git a/lib/ceedling/test_invoker.rb b/lib/ceedling/test_invoker.rb index e3be7dea5..9119092ce 100644 --- a/lib/ceedling/test_invoker.rb +++ b/lib/ceedling/test_invoker.rb @@ -179,7 +179,7 @@ def setup_and_invoke(tests:, context:TEST_SYM, options:{}) mocks = {} mocks_list = @configurator.project_use_mocks ? @context_extractor.lookup_raw_mock_list( details[:filepath] ) : [] mocks_list.each do |name| - source = @helper.find_header_input_for_mock_file( name, details[:search_paths] ) + source = @helper.find_header_input_for_mock( name, details[:search_paths] ) preprocessed_input = @file_path_utils.form_preprocessed_file_filepath( source, details[:name] ) mocks[name.to_sym] = { :name => name, @@ -303,14 +303,11 @@ def setup_and_invoke(tests:, context:TEST_SYM, options:{}) @batchinator.build_step("Determining Artifacts to Be Built", heading: false) do @batchinator.exec(workload: :compile, things: @testables) do |test, details| # Source files referenced by conventions or specified by build directives in a test file - test_sources = @test_invoker_helper.extract_sources( details[:filepath] ) - test_core = test_sources + details[:mock_list] + test_sources = @helper.extract_sources( details[:filepath] ) + test_core = test_sources + @helper.form_mock_filenames( details[:mock_list] ) # When we have a mock and an include for the same file, the mock wins - test_core.delete_if do |v| - mock_of_this_file = "#{@configurator.cmock_mock_prefix}#{File.basename(v,'.*')}" - details[:mock_list].include?(mock_of_this_file) - end + @helper.remove_mock_original_headers( test_core, details[:mock_list] ) # CMock + Unity + CException test_frameworks = @helper.collect_test_framework_sources( !details[:mock_list].empty? ) @@ -380,7 +377,7 @@ def setup_and_invoke(tests:, context:TEST_SYM, options:{}) options: options } - @test_invoker_helper.generate_executable_now(**arg_hash) + @helper.generate_executable_now(**arg_hash) end end @@ -397,7 +394,7 @@ def setup_and_invoke(tests:, context:TEST_SYM, options:{}) options: options } - @test_invoker_helper.run_fixture_now(**arg_hash) + @helper.run_fixture_now(**arg_hash) # Handle exceptions so we can ensure post_test() is called. # A lone `ensure` includes an implicit rescuing of StandardError diff --git a/lib/ceedling/test_invoker_helper.rb b/lib/ceedling/test_invoker_helper.rb index 928ec0740..fd2711ee0 100644 --- a/lib/ceedling/test_invoker_helper.rb +++ b/lib/ceedling/test_invoker_helper.rb @@ -244,8 +244,23 @@ def fetch_include_search_paths_for_test_file(test_filepath) end # TODO: Use search_paths to find/match header file from which to generate mock - def find_header_input_for_mock_file(mock, search_paths) - return @file_finder.find_header_input_for_mock_file(mock) + # Today, this is just a pass-through wrapper + def find_header_input_for_mock(mock, search_paths) + return @file_finder.find_header_input_for_mock( mock ) + end + + # Transform list of mock names into filenames with source extension + def form_mock_filenames(mocklist) + return mocklist.map {|mock| mock + @configurator.extension_source} + end + + def remove_mock_original_headers( filelist, mocklist ) + filelist.delete_if do |filepath| + # Create a simple mock name from the filepath => mock prefix + filepath base name with no extension + mock_name = @configurator.cmock_mock_prefix + File.basename( filepath, '.*' ) + # Tell `delete_if()` logic to remove inspected filepath if simple mocklist includes the name we just generated + mocklist.include?( mock_name ) + end end def clean_test_results(path, tests) diff --git a/plugins/bullseye/bullseye.rake b/plugins/bullseye/bullseye.rake index f82a2111d..ca5335635 100755 --- a/plugins/bullseye/bullseye.rake +++ b/plugins/bullseye/bullseye.rake @@ -155,7 +155,7 @@ namespace BULLSEYE_SYM do proc do |task_name| test = task_name.sub(/#{BULLSEYE_TASK_ROOT}/, '') test = "#{PROJECT_TEST_FILE_PREFIX}#{test}" unless test.start_with?(PROJECT_TEST_FILE_PREFIX) - @ceedling[:file_finder].find_test_from_file_path(test) + @ceedling[:file_finder].find_test_file_from_name(test) end ]) do |test| @ceedling[:rake_wrapper][:prepare].invoke diff --git a/plugins/gcov/gcov.rake b/plugins/gcov/gcov.rake index 06e855cc9..44afa5484 100755 --- a/plugins/gcov/gcov.rake +++ b/plugins/gcov/gcov.rake @@ -73,7 +73,7 @@ namespace GCOV_SYM do proc do |task_name| test = task_name.sub(/#{GCOV_TASK_ROOT}/, '') test = "#{PROJECT_TEST_FILE_PREFIX}#{test}" unless test.start_with?(PROJECT_TEST_FILE_PREFIX) - @ceedling[:file_finder].find_test_from_file_path(test) + @ceedling[:file_finder].find_test_file_from_name(test) end ]) do |test| @ceedling[:rake_wrapper][:prepare].invoke