Class: TestInvokerHelper

Inherits:
Object show all
Defined in:
lib/ceedling/test_invoker_helper.rb

Instance Method Summary collapse

Instance Method Details

#clean_test_results(path, tests) ⇒ Object



266
267
268
269
270
# File 'lib/ceedling/test_invoker_helper.rb', line 266

def clean_test_results(path, tests)
  tests.each do |test|
    @file_wrapper.rm_f( Dir.glob( File.join( path, test + '.*' ) ) )
  end
end

#collect_test_framework_sources(mocks) ⇒ Object



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/ceedling/test_invoker_helper.rb', line 192

def collect_test_framework_sources(mocks)
  sources = []

  sources << File.join(PROJECT_BUILD_VENDOR_UNITY_PATH, UNITY_C_FILE)
  sources << File.join(PROJECT_BUILD_VENDOR_CMOCK_PATH, CMOCK_C_FILE) if @configurator.project_use_mocks and mocks
  sources << File.join(PROJECT_BUILD_VENDOR_CEXCEPTION_PATH, CEXCEPTION_C_FILE) if @configurator.project_use_exceptions

  # If we're (a) using mocks (b) a Unity helper is defined and (c) that unity helper includes a source file component,
  # then link in the unity_helper object file too.
  if @configurator.project_use_mocks
    @configurator.cmock_unity_helper_path.each do |helper|
      if @file_wrapper.exist?( helper.ext( EXTENSION_SOURCE ) )
        sources << helper
      end
    end
  end

  return sources
end

#compile_defines(context:, filepath:) ⇒ Object



152
153
154
155
156
157
158
159
160
# File 'lib/ceedling/test_invoker_helper.rb', line 152

def compile_defines(context:, filepath:)
  # If this context exists ([:defines][context]), use it. Otherwise, default to test context.
  context = TEST_SYM unless @defineinator.defines_defined?( context:context )

  defines = @defineinator.generate_test_definition( filepath:filepath )
  defines += @defineinator.defines( subkey:context, filepath:filepath )

  return defines.uniq
end

#convert_libraries_to_argumentsObject

Convert libraries configuration form YAML configuration into a string that can be given to the compiler.



274
275
276
277
278
279
280
# File 'lib/ceedling/test_invoker_helper.rb', line 274

def convert_libraries_to_arguments()
  args = ((@configurator.project_config_hash[:libraries_test] || []) + ((defined? LIBRARIES_SYSTEM) ? LIBRARIES_SYSTEM : [])).flatten
  if (defined? LIBRARIES_FLAG)
    args.map! {|v| LIBRARIES_FLAG.gsub(/\$\{1\}/, v) }
  end
  return args
end

#extract_include_directives(arg_hash) ⇒ Object



38
39
40
41
42
43
44
# File 'lib/ceedling/test_invoker_helper.rb', line 38

def extract_include_directives(arg_hash)
  # Run test file through preprocessor to parse out include statements and then collect header files, mocks, etc.
  includes = @preprocessinator.preprocess_includes( **arg_hash )

  # Store the include statements we found
  @test_context_extractor.ingest_includes( arg_hash[:filepath], includes )
end

#extract_sources(test_filepath) ⇒ Object



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/ceedling/test_invoker_helper.rb', line 212

def extract_sources(test_filepath)
  sources = []

  # Get any additional source files specified by TEST_SOURCE_FILE() in test file
  _sources = @test_context_extractor.lookup_build_directive_sources_list(test_filepath)
  _sources.each do |source|
    sources << @file_finder.find_build_input_file(filepath: source, complain: :ignore, context: TEST_SYM)
  end

  _support_headers = COLLECTION_ALL_SUPPORT.map { |filepath| File.basename(filepath).ext(EXTENSION_HEADER) }

  # Get all #include .h files from test file so we can find any source files by convention
  includes = @test_context_extractor.lookup_full_header_includes_list(test_filepath)
  includes.each do |include|
    _basename = File.basename(include)
    next if _basename == UNITY_H_FILE                # Ignore Unity in this list
    next if _basename.start_with?(CMOCK_MOCK_PREFIX) # Ignore mocks in this list
    next if _support_headers.include?(_basename)     # Ignore any sources in our support files list

    sources << @file_finder.find_build_input_file(filepath: include, complain: :ignore, context: TEST_SYM)
  end

  # Remove any nil or duplicate entries in list
  return sources.compact.uniq
end

#fetch_include_search_paths_for_test_file(test_filepath) ⇒ Object



242
243
244
# File 'lib/ceedling/test_invoker_helper.rb', line 242

def fetch_include_search_paths_for_test_file(test_filepath)
  return @test_context_extractor.lookup_include_paths_list(test_filepath)
end

#fetch_shallow_source_includes(test_filepath) ⇒ Object



238
239
240
# File 'lib/ceedling/test_invoker_helper.rb', line 238

def fetch_shallow_source_includes(test_filepath)
  return @test_context_extractor.lookup_source_includes_list(test_filepath)
end

#find_header_input_for_mock(mock, search_paths) ⇒ Object

TODO: Use search_paths to find/match header file from which to generate mock Today, this is just a pass-through wrapper



248
249
250
# File 'lib/ceedling/test_invoker_helper.rb', line 248

def find_header_input_for_mock(mock, search_paths)
  return @file_finder.find_header_input_for_mock( mock )
end

#flags(context:, operation:, filepath:, default: []) ⇒ Object



174
175
176
177
178
179
# File 'lib/ceedling/test_invoker_helper.rb', line 174

def flags(context:, operation:, filepath:, default:[])
  # If this context + operation exists ([:flags][context][operation]), use it. Otherwise, default to test context.
  context = TEST_SYM unless @flaginator.flags_defined?( context:context, operation:operation )

  return @flaginator.flag_down( context:context, operation:operation, filepath:filepath, default:default )
end

#form_mock_filenames(mocklist) ⇒ Object

Transform list of mock names into filenames with source extension



253
254
255
# File 'lib/ceedling/test_invoker_helper.rb', line 253

def form_mock_filenames(mocklist)
  return mocklist.map {|mock| mock + @configurator.extension_source}
end

#framework_definesObject



94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/ceedling/test_invoker_helper.rb', line 94

def framework_defines()
  defines = []

  # Unity defines
  defines += @defineinator.defines( topkey:UNITY_SYM, subkey: :defines )

  # CMock defines
  defines += @defineinator.defines( topkey:CMOCK_SYM, subkey: :defines )

  # CException defines
  defines += @defineinator.defines( topkey:CEXCEPTION_SYM, subkey: :defines )

  return defines.uniq
end

#generate_executable_now(context:, build_path:, executable:, objects:, flags:, lib_args:, lib_paths:, options:) ⇒ Object



290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/ceedling/test_invoker_helper.rb', line 290

def generate_executable_now(context:, build_path:, executable:, objects:, flags:, lib_args:, lib_paths:, options:)
  begin
    @generator.generate_executable_file(
      options[:test_linker],
      context,
      objects.map{|v| "\"#{v}\""},
      flags,
      executable,
      @file_path_utils.form_test_build_map_filepath( build_path, executable ),
      lib_args,
      lib_paths )
  rescue ShellException => ex
    if ex.shell_result[:output] =~ /symbol/i
      notice =    "If the linker reports missing symbols, the following may be to blame:\n" +
                  "  1. This test lacks #include statements corresponding to needed source files (see note below).\n" +
                  "  2. Project file paths omit source files corresponding to #include statements in this test.\n" +
                  "  3. Complex macros, #ifdefs, etc. have obscured correct #include statements in this test.\n" +
                  "  4. Your project is attempting to mix C++ and C file extensions (not supported).\n"
      if (@configurator.project_use_mocks)
        notice += "  5. This test does not #include needed mocks (that triggers their generation).\n"
      end

      notice +=   "\n"
      notice +=   "NOTE: A test file directs the build of a test executable with #include statemetns:\n" +
                  "  * By convention, Ceedling assumes header filenames correspond to source filenames.\n" +
                  "  * Which code files to compile and link are determined by #include statements.\n"
      if (@configurator.project_use_mocks)
        notice += "  * An #include statement convention directs the generation of mocks from header files.\n"
      end

      notice +=   "\n"
      notice +=   "OPTIONS:\n" +
                  "  1. Doublecheck this test's #include statements.\n" +
                  "  2. Simplify complex macros or fully specify symbols for this test in :project ↳ :defines.\n" +
                  "  3. If no header file corresponds to the needed source file, use the #{UNITY_TEST_SOURCE_FILE}()\n" +
                  "     build diective macro in this test to inject a source file into the build.\n\n" +
                  "See the docs on conventions, paths, preprocessing, compilation symbols, and build directive macros.\n\n"

      # Print helpful notice
      @loginator.log( notice, Verbosity::COMPLAIN, LogLabels::NOTICE )
    end

    # Re-raise the exception
    raise ex
  end
end

#get_library_paths_to_argumentsObject



282
283
284
285
286
287
288
# File 'lib/ceedling/test_invoker_helper.rb', line 282

def get_library_paths_to_arguments()
  paths = (defined? PATHS_LIBRARIES) ? (PATHS_LIBRARIES || []).clone : []
  if (defined? LIBRARIES_PATH_FLAG)
    paths.map! {|v| LIBRARIES_PATH_FLAG.gsub(/\$\{1\}/, v) }
  end
  return paths
end

#preprocess_defines(test_defines:, filepath:) ⇒ Object



162
163
164
165
166
167
168
169
170
171
172
# File 'lib/ceedling/test_invoker_helper.rb', line 162

def preprocess_defines(test_defines:, filepath:)
  # Preprocessing defines for the test file
  preprocessing_defines = @defineinator.defines( subkey:PREPROCESS_SYM, filepath:filepath, default:nil )

  # If no defines were set, default to using test_defines
  return test_defines if preprocessing_defines.nil?
    
  # Otherwise, return the defines we looked up
  # This includes an explicitly set empty list to override / clear test_defines
  return preprocessing_defines
end

#preprocess_flags(context:, compile_flags:, filepath:) ⇒ Object



181
182
183
184
185
186
187
188
189
190
# File 'lib/ceedling/test_invoker_helper.rb', line 181

def preprocess_flags(context:, compile_flags:, filepath:)
  preprocessing_flags = flags( context:context, operation:OPERATION_PREPROCESS_SYM, filepath:filepath, default:nil )

  # If no flags were set, default to using compile_flags
  return compile_flags if preprocessing_flags.nil?

  # Otherwise, return the flags we looked up
  # This includes an explicitly set empty list to override / clear compile_flags
  return preprocessing_flags
end

#process_project_include_pathsObject



32
33
34
35
36
# File 'lib/ceedling/test_invoker_helper.rb', line 32

def process_project_include_paths()
  @include_pathinator.validate_test_build_directive_paths()
  headers = @include_pathinator.validate_header_files_collection()
  @include_pathinator.augment_environment_header_files( headers )
end

#remove_mock_original_headers(filelist, mocklist) ⇒ Object



257
258
259
260
261
262
263
264
# File 'lib/ceedling/test_invoker_helper.rb', line 257

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

#run_fixture_now(context:, test_name:, test_filepath:, executable:, result:, options:) ⇒ Object



337
338
339
340
341
342
343
344
345
# File 'lib/ceedling/test_invoker_helper.rb', line 337

def run_fixture_now(context:, test_name:, test_filepath:, executable:, result:, options:)
  @generator.generate_test_results(
    tool:          options[:test_fixture], 
    context:       context,
    test_name:     test_name,
    test_filepath: test_filepath,
    executable:    executable, 
    result:        result)
end

#runner_definesObject



148
149
150
# File 'lib/ceedling/test_invoker_helper.rb', line 148

def runner_defines()
  return @test_runner_manager.collect_defines()
end

#search_paths(filepath, subdir) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/ceedling/test_invoker_helper.rb', line 78

def search_paths(filepath, subdir)
  paths = []

  # Start with mock path to ensure any CMock-reworked header files are encountered first
  paths << File.join( @configurator.cmock_mock_path, subdir ) if @configurator.project_use_mocks
  paths += @include_pathinator.lookup_test_directive_include_paths( filepath )
  paths += @include_pathinator.collect_test_include_paths()
  paths += @configurator.collection_paths_support
  paths += @configurator.collection_paths_include
  paths += @configurator.collection_paths_libraries
  paths += @configurator.collection_paths_vendor
  paths += @configurator.collection_paths_test_toolchain_include
  
  return paths.uniq
end

#setupObject



27
28
29
30
# File 'lib/ceedling/test_invoker_helper.rb', line 27

def setup()
  # Alias for brevity
  @batchinator = @build_batchinator
end

#tailor_search_paths(filepath:, search_paths:) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/ceedling/test_invoker_helper.rb', line 109

def tailor_search_paths(filepath:, search_paths:)
  _search_paths = []

  # Unity search paths
  if filepath == File.join(PROJECT_BUILD_VENDOR_UNITY_PATH, UNITY_C_FILE)
    _search_paths += @configurator.collection_paths_support
    _search_paths << PROJECT_BUILD_VENDOR_UNITY_PATH

  # CMock search paths
  elsif @configurator.project_use_mocks and 
    (filepath == File.join(PROJECT_BUILD_VENDOR_CMOCK_PATH, CMOCK_C_FILE))
    _search_paths += @configurator.collection_paths_support
    _search_paths << PROJECT_BUILD_VENDOR_UNITY_PATH
    _search_paths << PROJECT_BUILD_VENDOR_CMOCK_PATH
    _search_paths << PROJECT_BUILD_VENDOR_CEXCEPTION_PATH if @configurator.project_use_exceptions

  # CException search paths
  elsif @configurator.project_use_exceptions and 
    (filepath == File.join(PROJECT_BUILD_VENDOR_CEXCEPTION_PATH, CEXCEPTION_C_FILE))
    _search_paths += @configurator.collection_paths_support
    _search_paths << PROJECT_BUILD_VENDOR_CEXCEPTION_PATH

  # Support files search paths
  elsif (@configurator.collection_all_support.include?(filepath))
    _search_paths = search_paths
    _search_paths += @configurator.collection_paths_support
    _search_paths << PROJECT_BUILD_VENDOR_UNITY_PATH
    _search_paths << PROJECT_BUILD_VENDOR_CMOCK_PATH if @configurator.project_use_mocks
    _search_paths << PROJECT_BUILD_VENDOR_CEXCEPTION_PATH if @configurator.project_use_exceptions
  end

  # Not a vendor file, return original search paths
  if _search_paths.length == 0
    return search_paths
  end

  return _search_paths.uniq
end

#validate_build_directive_source_files(test:, filepath:) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/ceedling/test_invoker_helper.rb', line 46

def validate_build_directive_source_files(test:, filepath:)
  sources = @test_context_extractor.lookup_build_directive_sources_list(filepath)

  ext_message = @configurator.extension_source
  if @configurator.test_build_use_assembly
    ext_message += " or #{@configurator.extension_assembly}"
  end

  sources.each do |source|
    valid_extension = true

    # Only C files in test build
    if not @configurator.test_build_use_assembly
      valid_extension = false if @file_wrapper.extname(source) != @configurator.extension_source      
    # C and assembly files in test build
    else
      ext = @file_wrapper.extname(source)
      valid_extension = false if (ext != @configurator.extension_assembly) and (ext != @configurator.extension_source)
    end

    if not valid_extension
      error = "File '#{source}' specified with #{UNITY_TEST_SOURCE_FILE}() in #{test} is not a #{ext_message} source file"
      raise CeedlingException.new(error)
    end

    if @file_finder.find_build_input_file(filepath: source, complain: :ignore, context: TEST_SYM).nil?
      error = "File '#{source}' specified with #{UNITY_TEST_SOURCE_FILE}() in #{test} cannot be found in the source file collection"
      raise CeedlingException.new(error)
    end
  end
end